Skip to content

Commit c3b2e9d

Browse files
committed
Add experimental use of cli bqrs parsing.
When `codeQL.experimentalBqrsParsing` is set to true, parse raw results from the bqrs file using the cli, rather than doing it in the webview.
1 parent c20bbd9 commit c3b2e9d

File tree

6 files changed

+139
-22
lines changed

6 files changed

+139
-22
lines changed

extensions/ql-vscode/src/adapt.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { DecodedBqrsChunk, ResultSetSchema, ColumnKind, Column, ColumnValue } from "./bqrs-cli-types";
2+
import { LocationValue, ResultSetSchema as AdaptedSchema, ColumnSchema, ColumnType, LocationStyle } from 'semmle-bqrs';
3+
4+
// FIXME: This is a temporary bit of impedance matching to convert
5+
// from the types provided by ./bqrs-cli-types, to the types used by
6+
// the view layer.
7+
//
8+
// The reason that it is benign for now is that it is only used by
9+
// feature-flag-guarded codepaths that won't be encountered by normal
10+
// users. It is not yet guaranteed to produce correct output for raw
11+
// results.
12+
//
13+
// Eventually, the view layer should be refactored to directly accept data
14+
// of types coming from bqrs-cli-types, and this file can be deleted.
15+
16+
export type ResultRow = ResultValue[];
17+
18+
export interface ResultElement {
19+
label: string;
20+
location?: LocationValue;
21+
}
22+
23+
export interface ResultUri {
24+
uri: string;
25+
}
26+
27+
export type ResultValue = ResultElement | ResultUri | string;
28+
29+
export interface RawResultSet {
30+
readonly schema: AdaptedSchema;
31+
readonly rows: readonly ResultRow[];
32+
}
33+
34+
function adaptKind(kind: ColumnKind): ColumnType {
35+
// XXX what about 'u'?
36+
if (kind == 'e') {
37+
return { type: 'e', primitiveType: 's', locationStyle: LocationStyle.FivePart, hasLabel: true }
38+
}
39+
else {
40+
return { type: kind };
41+
}
42+
}
43+
44+
function adaptColumn(col: Column): ColumnSchema {
45+
return { name: col.name!, type: adaptKind(col.kind) };
46+
}
47+
48+
export function adaptSchema(schema: ResultSetSchema): AdaptedSchema {
49+
return {
50+
columns: schema.columns.map(adaptColumn),
51+
name: schema.name,
52+
tupleCount: schema.rows,
53+
version: 0,
54+
}
55+
}
56+
57+
export function adaptValue(val: ColumnValue): ResultValue {
58+
// XXX taking a lot of incorrect shortcuts here
59+
60+
if (typeof val == 'string') {
61+
return val;
62+
}
63+
64+
if (typeof val == 'number' || typeof val == 'boolean') {
65+
return val + '';
66+
}
67+
68+
const url = val.url;
69+
70+
if (typeof url == 'string') {
71+
return url;
72+
}
73+
74+
if (url == undefined) {
75+
return 'none';
76+
}
77+
78+
return {
79+
label: val.label || '',
80+
location: {
81+
t: LocationStyle.FivePart,
82+
lineStart: url.startLine,
83+
lineEnd: url.endLine,
84+
colStart: url.startColumn,
85+
colEnd: url.endColumn,
86+
// FIXME: This seems definitely wrong. Should we be using
87+
// something like the code in sarif-utils.ts?
88+
file: url.uri.replace(/file:/, ''),
89+
}
90+
}
91+
92+
}
93+
94+
export function adaptRow(row: ColumnValue[]): ResultRow {
95+
return row.map(adaptValue);
96+
}
97+
98+
export function adaptBqrs(schema: AdaptedSchema, page: DecodedBqrsChunk): RawResultSet {
99+
return {
100+
schema,
101+
rows: page.tuples.map(adaptRow),
102+
}
103+
}

extensions/ql-vscode/src/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ const ROOT_SETTING = new Setting('codeQL');
5050
*/
5151
export const EXPERIMENTAL_FEATURES_SETTING = new Setting('experimentalFeatures', ROOT_SETTING);
5252

53+
/* Advanced setting: used to enable bqrs parsing in the cli instead of in the webview. */
54+
export const EXPERIMENTAL_BQRS_SETTING = new Setting('experimentalBqrsParsing', ROOT_SETTING);
55+
5356
// Distribution configuration
5457

5558
const DISTRIBUTION_SETTING = new Setting('cli', ROOT_SETTING);

extensions/ql-vscode/src/interface-types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as sarif from 'sarif';
22
import { ResolvableLocationValue } from 'semmle-bqrs';
3+
import { RawResultSet } from './adapt';
34

45
/**
56
* Only ever show this many results per run in interpreted results.
@@ -77,6 +78,12 @@ export interface SetStateMsg {
7778
* This is useful to prevent properties like scroll state being lost when rendering the sorted results after sorting a column.
7879
*/
7980
shouldKeepOldResultsWhileRendering: boolean;
81+
82+
/**
83+
* An experimental way of providing results from the extension.
84+
* Should be undefined unless config.EXPERIMENTAL_BQRS_SETTING is set to true.
85+
*/
86+
resultSets?: RawResultSet[];
8087
}
8188

8289
/** Advance to the next or previous path no in the path viewer */

extensions/ql-vscode/src/interface.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import * as messages from './messages';
1616
import { CompletedQuery, interpretResults } from './query-results';
1717
import { QueryInfo, tmpDir } from './run-queries';
1818
import { parseSarifLocation, parseSarifPlainTextMessage } from './sarif-utils';
19+
import { adaptSchema, adaptBqrs, RawResultSet } from './adapt';
20+
import { EXPERIMENTAL_BQRS_SETTING } from './config';
1921

2022
/**
2123
* interface.ts
@@ -361,13 +363,28 @@ export class InterfaceManager extends DisposableObject {
361363
});
362364
}
363365

366+
let resultSets: RawResultSet[] | undefined;
367+
368+
if (EXPERIMENTAL_BQRS_SETTING.getValue()) {
369+
resultSets = [];
370+
const pageSize = 100;
371+
const schemas = await this.cliServer.bqrsInfo(results.query.resultsPaths.resultsPath, pageSize);
372+
for (const schema of schemas["result-sets"]) {
373+
const chunk = await this.cliServer.bqrsDecode(results.query.resultsPaths.resultsPath, schema.name, pageSize, 0)
374+
const adaptedSchema = adaptSchema(schema);
375+
const resultSet = adaptBqrs(adaptedSchema, chunk);
376+
resultSets.push(resultSet);
377+
}
378+
}
379+
364380
await this.postMessage({
365381
t: "setState",
366382
interpretation,
367383
origResultsPaths: results.query.resultsPaths,
368384
resultsPath: this.convertPathToWebviewUri(
369385
results.query.resultsPaths.resultsPath
370386
),
387+
resultSets,
371388
sortedResultsMap,
372389
database: results.database,
373390
shouldKeepOldResultsWhileRendering,

extensions/ql-vscode/src/view/raw-results-table.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from "react";
22
import { renderLocation, ResultTableProps, zebraStripe, className, nextSortDirection } from "./result-table-utils";
3-
import { RawTableResultSet, ResultValue, vscode } from "./results";
3+
import { RawTableResultSet, vscode } from "./results";
4+
import { ResultValue } from "../adapt";
45
import { SortDirection, RAW_RESULTS_LIMIT, RawResultsSortState } from "../interface-types";
56

67
export type RawTableProps = ResultTableProps & {

extensions/ql-vscode/src/view/results.tsx

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import * as React from 'react';
22
import * as Rdom from 'react-dom';
33
import * as bqrs from 'semmle-bqrs';
4-
import { ElementBase, LocationValue, PrimitiveColumnValue, PrimitiveTypeKind, ResultSetSchema, tryGetResolvableLocation } from 'semmle-bqrs';
4+
import { ElementBase, PrimitiveColumnValue, PrimitiveTypeKind, ResultSetSchema, tryGetResolvableLocation } from 'semmle-bqrs';
55
import { assertNever } from '../helpers-pure';
66
import { DatabaseInfo, FromResultsViewMsg, Interpretation, IntoResultsViewMsg, SortedResultSetInfo, RawResultsSortState, NavigatePathMsg, QueryMetadata, ResultsPaths } from '../interface-types';
77
import { EventHandlers as EventHandlerList } from './event-handler-list';
88
import { ResultTables } from './result-tables';
9+
import { RawResultSet, ResultValue, ResultRow } from '../adapt';
910

1011
/**
1112
* results.tsx
@@ -23,31 +24,13 @@ interface VsCodeApi {
2324
declare const acquireVsCodeApi: () => VsCodeApi;
2425
export const vscode = acquireVsCodeApi();
2526

26-
export interface ResultElement {
27-
label: string;
28-
location?: LocationValue;
29-
}
30-
31-
export interface ResultUri {
32-
uri: string;
33-
}
34-
35-
export type ResultValue = ResultElement | ResultUri | string;
36-
37-
export type ResultRow = ResultValue[];
38-
3927
export type RawTableResultSet = { t: 'RawResultSet' } & RawResultSet;
4028
export type PathTableResultSet = { t: 'SarifResultSet'; readonly schema: ResultSetSchema; name: string } & Interpretation;
4129

4230
export type ResultSet =
4331
| RawTableResultSet
4432
| PathTableResultSet;
4533

46-
export interface RawResultSet {
47-
readonly schema: ResultSetSchema;
48-
readonly rows: readonly ResultRow[];
49-
}
50-
5134
async function* getChunkIterator(response: Response): AsyncIterableIterator<Uint8Array> {
5235
if (!response.ok) {
5336
throw new Error(`Failed to load results: (${response.status}) ${response.statusText}`);
@@ -63,7 +46,7 @@ async function* getChunkIterator(response: Response): AsyncIterableIterator<Uint
6346
}
6447

6548
function translatePrimitiveValue(value: PrimitiveColumnValue, type: PrimitiveTypeKind):
66-
ResultValue {
49+
ResultValue {
6750

6851
switch (type) {
6952
case 'i':
@@ -127,6 +110,7 @@ async function parseResultSets(response: Response): Promise<readonly ResultSet[]
127110

128111
interface ResultsInfo {
129112
resultsPath: string;
113+
resultSets: ResultSet[] | undefined;
130114
origResultsPaths: ResultsPaths;
131115
database: DatabaseInfo;
132116
interpretation: Interpretation | undefined;
@@ -187,6 +171,7 @@ class App extends React.Component<{}, ResultsViewState> {
187171
case 'setState':
188172
this.updateStateWithNewResultsInfo({
189173
resultsPath: msg.resultsPath,
174+
resultSets: msg.resultSets?.map(x => ({ t: 'RawResultSet', ...x })),
190175
origResultsPaths: msg.origResultsPaths,
191176
sortedResultsMap: new Map(Object.entries(msg.sortedResultsMap)),
192177
database: msg.database,
@@ -247,8 +232,9 @@ class App extends React.Component<{}, ResultsViewState> {
247232
let results: Results | null = null;
248233
let statusText = '';
249234
try {
235+
const resultSets = resultsInfo.resultSets || await this.getResultSets(resultsInfo);
250236
results = {
251-
resultSets: await this.getResultSets(resultsInfo),
237+
resultSets,
252238
database: resultsInfo.database,
253239
sortStates: this.getSortStates(resultsInfo)
254240
};

0 commit comments

Comments
 (0)