Skip to content

Commit f01b1fc

Browse files
committed
Graph viewer support
1 parent 4e94f70 commit f01b1fc

17 files changed

Lines changed: 1127 additions & 116 deletions

extensions/ql-vscode/package-lock.json

Lines changed: 695 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/ql-vscode/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"onCommand:codeQLDatabases.chooseDatabaseLgtm",
3535
"onCommand:codeQL.setCurrentDatabase",
3636
"onCommand:codeQL.viewAst",
37+
"onCommand:codeQL.viewCfg",
3738
"onCommand:codeQL.chooseDatabaseFolder",
3839
"onCommand:codeQL.chooseDatabaseArchive",
3940
"onCommand:codeQL.chooseDatabaseInternet",
@@ -251,6 +252,10 @@
251252
"command": "codeQL.viewAst",
252253
"title": "CodeQL: View AST"
253254
},
255+
{
256+
"command": "codeQL.viewCfg",
257+
"title": "CodeQL: View CFG"
258+
},
254259
{
255260
"command": "codeQL.upgradeCurrentDatabase",
256261
"title": "CodeQL: Upgrade Current Database"
@@ -537,6 +542,11 @@
537542
"group": "9_qlCommands",
538543
"when": "resourceScheme == codeql-zip-archive"
539544
},
545+
{
546+
"command": "codeQL.viewCfg",
547+
"group": "9_qlCommands",
548+
"when": "resourceScheme == codeql-zip-archive"
549+
},
540550
{
541551
"command": "codeQL.runQueries",
542552
"group": "9_qlCommands",
@@ -564,6 +574,10 @@
564574
"command": "codeQL.viewAst",
565575
"when": "resourceScheme == codeql-zip-archive"
566576
},
577+
{
578+
"command": "codeQL.viewCfg",
579+
"when": "resourceScheme == codeql-zip-archive"
580+
},
567581
{
568582
"command": "codeQLDatabases.setCurrentDatabase",
569583
"when": "false"
@@ -736,6 +750,8 @@
736750
"dependencies": {
737751
"child-process-promise": "^2.2.1",
738752
"classnames": "~2.2.6",
753+
"d3": "^6.3.1",
754+
"d3-graphviz": "^2.6.1",
739755
"fs-extra": "^9.0.1",
740756
"glob-promise": "^3.4.0",
741757
"js-yaml": "^3.14.0",
@@ -760,6 +776,8 @@
760776
"@types/chai-as-promised": "~7.1.2",
761777
"@types/child-process-promise": "^2.2.1",
762778
"@types/classnames": "~2.2.9",
779+
"@types/d3": "^6.2.0",
780+
"@types/d3-graphviz": "^2.6.6",
763781
"@types/fs-extra": "^9.0.6",
764782
"@types/glob": "^7.1.1",
765783
"@types/google-protobuf": "^3.2.7",

extensions/ql-vscode/src/blob.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
* succeeds.
99
*/
1010

11-
declare type Blob = string;
11+
//declare type Blob = string; // TODO: Check this

extensions/ql-vscode/src/cli.ts

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
/* eslint-disable @typescript-eslint/camelcase */
22
import * as cpp from 'child-process-promise';
33
import * as child_process from 'child_process';
4-
import * as fs from 'fs-extra';
54
import * as path from 'path';
6-
import * as sarif from 'sarif';
75
import { SemVer } from 'semver';
86
import { Readable } from 'stream';
97
import { StringDecoder } from 'string_decoder';
@@ -12,19 +10,13 @@ import { promisify } from 'util';
1210
import { CancellationToken, Disposable } from 'vscode';
1311

1412
import { BQRSInfo, DecodedBqrsChunk } from './pure/bqrs-cli-types';
15-
import * as config from './config';
1613
import { CliConfig } from './config';
1714
import { DistributionProvider, FindDistributionResultKind } from './distribution';
1815
import { assertNever } from './pure/helpers-pure';
1916
import { QueryMetadata, SortDirection } from './pure/interface-types';
2017
import { Logger, ProgressReporter } from './logging';
2118
import { CompilationMessage } from './pure/messages';
2219

23-
/**
24-
* The version of the SARIF format that we are using.
25-
*/
26-
const SARIF_FORMAT = 'sarifv2.1.0';
27-
2820
/**
2921
* Flags to pass to all cli commands.
3022
*/
@@ -575,21 +567,13 @@ export class CodeQLCliServer implements Disposable {
575567
return await this.runJsonCodeQlCliCommand<DecodedBqrsChunk>(['bqrs', 'decode'], subcommandArgs, 'Reading bqrs data');
576568
}
577569

578-
async interpretBqrs(metadata: { kind: string; id: string; scored?: string }, resultsPath: string, interpretedResultsPath: string, sourceInfo?: SourceInfo): Promise<sarif.Log> {
570+
async interpretBqrs(metadata: { kind: string; id: string }, format: string, additonalArgs: string[], resultsPath: string, interpretedResultsPath: string, sourceInfo?: SourceInfo): Promise<void> {
579571
const args = [
580572
`-t=kind=${metadata.kind}`,
581573
`-t=id=${metadata.id}`,
582574
'--output', interpretedResultsPath,
583-
'--format', SARIF_FORMAT,
584-
// TODO: This flag means that we don't group interpreted results
585-
// by primary location. We may want to revisit whether we call
586-
// interpretation with and without this flag, or do some
587-
// grouping client-side.
588-
'--no-group-results',
589-
];
590-
if (config.isCanary() && metadata.scored !== undefined) {
591-
args.push(`-t=scored=${metadata.scored}`);
592-
}
575+
'--format', format,
576+
].concat(additonalArgs);
593577
if (sourceInfo !== undefined) {
594578
args.push(
595579
'--source-archive', sourceInfo.sourceArchive,
@@ -598,18 +582,6 @@ export class CodeQLCliServer implements Disposable {
598582
}
599583
args.push(resultsPath);
600584
await this.runCodeQlCliCommand(['bqrs', 'interpret'], args, 'Interpreting query results');
601-
602-
let output: string;
603-
try {
604-
output = await fs.readFile(interpretedResultsPath, 'utf8');
605-
} catch (err) {
606-
throw new Error(`Reading output of interpretation failed: ${err.stderr || err}`);
607-
}
608-
try {
609-
return JSON.parse(output) as sarif.Log;
610-
} catch (err) {
611-
throw new Error(`Parsing output of interpretation failed: ${err.stderr || err}`);
612-
}
613585
}
614586

615587

extensions/ql-vscode/src/contextual/keyType.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export enum KeyType {
22
DefinitionQuery = 'DefinitionQuery',
33
ReferenceQuery = 'ReferenceQuery',
44
PrintAstQuery = 'PrintAstQuery',
5+
PrintCfgQuery = 'PrintCfgQuery',
56
}
67

78
export function tagOfKeyType(keyType: KeyType): string {
@@ -12,6 +13,8 @@ export function tagOfKeyType(keyType: KeyType): string {
1213
return 'ide-contextual-queries/local-references';
1314
case KeyType.PrintAstQuery:
1415
return 'ide-contextual-queries/print-ast';
16+
case KeyType.PrintCfgQuery:
17+
return 'ide-contextual-queries/print-cfg';
1518
}
1619
}
1720

@@ -23,6 +26,8 @@ export function nameOfKeyType(keyType: KeyType): string {
2326
return 'references';
2427
case KeyType.PrintAstQuery:
2528
return 'print AST';
29+
case KeyType.PrintCfgQuery:
30+
return 'print CFG';
2631
}
2732
}
2833

@@ -32,6 +37,7 @@ export function kindOfKeyType(keyType: KeyType): string {
3237
case KeyType.ReferenceQuery:
3338
return 'definitions';
3439
case KeyType.PrintAstQuery:
40+
case KeyType.PrintCfgQuery:
3541
return 'graph';
3642
}
3743
}

extensions/ql-vscode/src/contextual/templateProvider.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,62 @@ export class TemplatePrintAstProvider {
188188
);
189189
}
190190
}
191+
192+
export class TemplatePrintCfgProvider {
193+
private cache: CachedOperation<[vscode.Uri, messages.TemplateDefinitions] | undefined>;
194+
195+
constructor(
196+
private cli: CodeQLCliServer,
197+
private dbm: DatabaseManager
198+
) {
199+
this.cache = new CachedOperation<[vscode.Uri, messages.TemplateDefinitions] | undefined>(this.getCfgUri.bind(this));
200+
}
201+
202+
async provideCfgUri(document?: vscode.TextDocument): Promise<[vscode.Uri, messages.TemplateDefinitions] | undefined> {
203+
if (!document) {
204+
return;
205+
}
206+
return await this.cache.get(document.uri.toString());
207+
}
208+
209+
private async getCfgUri(uriString: string): Promise<[vscode.Uri, messages.TemplateDefinitions]> {
210+
const uri = vscode.Uri.parse(uriString, true);
211+
if (uri.scheme !== zipArchiveScheme) {
212+
throw new Error('CFG Viewing is only available for databases with zipped source archives.');
213+
}
214+
215+
const zippedArchive = decodeSourceArchiveUri(uri);
216+
const sourceArchiveUri = encodeArchiveBasePath(zippedArchive.sourceArchiveZipPath);
217+
const db = this.dbm.findDatabaseItemBySourceArchive(sourceArchiveUri);
218+
219+
if (!db) {
220+
throw new Error('Can\'t infer database from the provided source.');
221+
}
222+
223+
const qlpack = await qlpackOfDatabase(this.cli, db);
224+
if (!qlpack) {
225+
throw new Error('Can\'t infer qlpack from database source archive');
226+
}
227+
const queries = await resolveQueries(this.cli, qlpack, KeyType.PrintCfgQuery);
228+
if (queries.length > 1) {
229+
throw new Error('Found multiple Print CFG queries. Can\'t continue');
230+
}
231+
if (queries.length === 0) {
232+
throw new Error('Did not find any Print CFG queries. Can\'t continue');
233+
}
234+
235+
const queryUri = vscode.Uri.file(queries[0]);
236+
237+
const templates: messages.TemplateDefinitions = {
238+
[TEMPLATE_NAME]: {
239+
values: {
240+
tuples: [[{
241+
stringValue: zippedArchive.pathWithinSourceArchive
242+
}]]
243+
}
244+
}
245+
};
246+
247+
return [queryUri, templates];
248+
}
249+
}

extensions/ql-vscode/src/extension.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import { DatabaseUI } from './databases-ui';
3232
import {
3333
TemplateQueryDefinitionProvider,
3434
TemplateQueryReferenceProvider,
35-
TemplatePrintAstProvider
35+
TemplatePrintAstProvider,
36+
TemplatePrintCfgProvider
3637
} from './contextual/templateProvider';
3738
import {
3839
DEFAULT_DISTRIBUTION_VERSION_RANGE,
@@ -67,6 +68,7 @@ import {
6768
withProgress,
6869
ProgressUpdate
6970
} from './commandRunner';
71+
import * as messages from './pure/messages';
7072

7173
/**
7274
* extension.ts
@@ -444,6 +446,7 @@ async function activateWithInstalledDistribution(
444446
selectedQuery: Uri | undefined,
445447
progress: ProgressCallback,
446448
token: CancellationToken,
449+
templates?: messages.TemplateDefinitions,
447450
): Promise<void> {
448451
if (qs !== undefined) {
449452
const dbItem = await databaseUI.getDatabaseItem(progress, token);
@@ -457,7 +460,8 @@ async function activateWithInstalledDistribution(
457460
quickEval,
458461
selectedQuery,
459462
progress,
460-
token
463+
token,
464+
templates
461465
);
462466
const item = qhm.addQuery(info);
463467
await showResultsForCompletedQuery(item, WebviewReveal.NotForced);
@@ -659,6 +663,26 @@ async function activateWithInstalledDistribution(
659663
})
660664
);
661665

666+
ctx.subscriptions.push(
667+
commandRunnerWithProgress(
668+
'codeQL.viewCfg',
669+
async (
670+
progress: ProgressCallback,
671+
token: CancellationToken
672+
) => {
673+
const res = await new TemplatePrintCfgProvider(cliServer, dbm)
674+
.provideCfgUri(window.activeTextEditor?.document);
675+
if (res) {
676+
await compileAndRunQuery(false, res[0], progress, token, res[1]);
677+
}
678+
},
679+
{
680+
title: 'Calculate CFG',
681+
cancellable: true
682+
}
683+
)
684+
);
685+
662686
logger.log('Starting language server.');
663687
ctx.subscriptions.push(client.start());
664688

0 commit comments

Comments
 (0)