1- import { CancellationToken } from "vscode" ;
2- import { DatabaseItem } from "../databases/local-databases" ;
31import { basename } from "path" ;
4- import { QueryRunner } from "../query-server" ;
5- import { CodeQLCliServer } from "../codeql-cli/cli" ;
2+ import { BaseLogger } from "../common/logging" ;
63import {
7- NotificationLogger ,
8- showAndLogExceptionWithTelemetry ,
9- } from "../common/logging" ;
10- import { getModelsAsDataLanguage } from "./languages" ;
11- import { ProgressCallback } from "../common/vscode/progress" ;
12- import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders" ;
13- import { ModeledMethod , ModeledMethodType } from "./modeled-method" ;
14- import { redactableError } from "../common/errors" ;
15- import { telemetryListener } from "../common/vscode/telemetry" ;
16- import { runQuery } from "../local-queries/run-query" ;
17- import { resolveQueries } from "../local-queries" ;
4+ getModelsAsDataLanguage ,
5+ ModelsAsDataLanguage ,
6+ ModelsAsDataLanguageModelType ,
7+ } from "./languages" ;
8+ import { ModeledMethod } from "./modeled-method" ;
189import { QueryLanguage } from "../common/query-language" ;
10+ import { GenerateQueriesOptions , runGenerateQueries } from "./generate" ;
11+ import { DecodedBqrs } from "../common/bqrs-cli-types" ;
1912
2013const FLOW_MODEL_SUPPORTED_LANGUAGES = [
2114 QueryLanguage . CSharp ,
@@ -28,159 +21,47 @@ export function isFlowModelGenerationSupported(
2821 return FLOW_MODEL_SUPPORTED_LANGUAGES . includes ( language ) ;
2922}
3023
31- type FlowModelOptions = {
32- cliServer : CodeQLCliServer ;
33- queryRunner : QueryRunner ;
34- logger : NotificationLogger ;
35- queryStorageDir : string ;
36- databaseItem : DatabaseItem ;
24+ type FlowModelOptions = GenerateQueriesOptions & {
25+ logger : BaseLogger ;
3726 language : QueryLanguage ;
38- progress : ProgressCallback ;
39- token : CancellationToken ;
40- onResults : ( results : ModeledMethod [ ] ) => void | Promise < void > ;
4127} ;
4228
43- export async function runFlowModelQueries ( {
44- onResults,
45- ...options
46- } : FlowModelOptions ) {
47- const queries = await resolveFlowQueries (
48- options . cliServer ,
49- options . databaseItem ,
50- ) ;
51-
52- const queriesByBasename : Record < string , string > = { } ;
53- for ( const query of queries ) {
54- queriesByBasename [ basename ( query ) ] = query ;
55- }
56-
57- const summaryResults = await runSingleFlowQuery (
58- "summary" ,
59- queriesByBasename [ "CaptureSummaryModels.ql" ] ,
60- 0 ,
61- options ,
62- ) ;
63- if ( summaryResults ) {
64- await onResults ( summaryResults ) ;
65- }
66-
67- const sinkResults = await runSingleFlowQuery (
68- "sink" ,
69- queriesByBasename [ "CaptureSinkModels.ql" ] ,
70- 1 ,
71- options ,
72- ) ;
73- if ( sinkResults ) {
74- await onResults ( sinkResults ) ;
75- }
76-
77- const sourceResults = await runSingleFlowQuery (
78- "source" ,
79- queriesByBasename [ "CaptureSourceModels.ql" ] ,
80- 2 ,
81- options ,
82- ) ;
83- if ( sourceResults ) {
84- await onResults ( sourceResults ) ;
85- }
86-
87- const neutralResults = await runSingleFlowQuery (
88- "neutral" ,
89- queriesByBasename [ "CaptureNeutralModels.ql" ] ,
90- 3 ,
91- options ,
92- ) ;
93- if ( neutralResults ) {
94- await onResults ( neutralResults ) ;
95- }
96- }
97-
98- async function resolveFlowQueries (
99- cliServer : CodeQLCliServer ,
100- databaseItem : DatabaseItem ,
101- ) : Promise < string [ ] > {
102- const packsToSearch = [ `codeql/${ databaseItem . language } -queries` ] ;
103-
104- return await resolveQueries (
105- cliServer ,
106- packsToSearch ,
107- "flow model generator" ,
108- {
109- "tags contain" : [ "modelgenerator" ] ,
110- } ,
111- ) ;
112- }
29+ const queriesToModel : Record < string , ModelsAsDataLanguageModelType > = {
30+ "CaptureSummaryModels.ql" : "summary" ,
31+ "CaptureSinkModels.ql" : "sink" ,
32+ "CaptureSourceModels.ql" : "source" ,
33+ "CaptureNeutralModels.ql" : "neutral" ,
34+ } ;
11335
114- async function runSingleFlowQuery (
115- type : Exclude < ModeledMethodType , "none" > ,
116- queryPath : string | undefined ,
117- queryStep : number ,
118- {
119- cliServer,
120- queryRunner,
121- logger,
122- queryStorageDir,
123- databaseItem,
124- language,
125- progress,
126- token,
127- } : Omit < FlowModelOptions , "onResults" > ,
128- ) : Promise < ModeledMethod [ ] > {
129- // Check that the right query was found
130- if ( queryPath === undefined ) {
131- void showAndLogExceptionWithTelemetry (
132- logger ,
133- telemetryListener ,
134- redactableError `Failed to find ${ type } query` ,
36+ function parseFlowModelResults (
37+ queryPath : string ,
38+ bqrs : DecodedBqrs ,
39+ modelsAsDataLanguage : ModelsAsDataLanguage ,
40+ logger : BaseLogger ,
41+ ) : ModeledMethod [ ] {
42+ if ( Object . keys ( bqrs ) . length !== 1 ) {
43+ throw new Error (
44+ `Expected exactly one result set from ${ queryPath } , but got ${
45+ Object . keys ( bqrs ) . length
46+ } `,
13547 ) ;
136- return [ ] ;
13748 }
13849
139- // Run the query
140- const completedQuery = await runQuery ( {
141- queryRunner,
142- databaseItem,
143- queryPath,
144- queryStorageDir,
145- additionalPacks : getOnDiskWorkspaceFolders ( ) ,
146- extensionPacks : undefined ,
147- progress : ( { step, message } ) =>
148- progress ( {
149- message : `Generating ${ type } model: ${ message } ` ,
150- step : queryStep * 1000 + step ,
151- maxStep : 4000 ,
152- } ) ,
153- token,
154- } ) ;
155-
156- if ( ! completedQuery ) {
50+ const modelType = queriesToModel [ basename ( queryPath ) ] ;
51+ if ( ! modelType ) {
52+ void logger . log ( `Unknown model type for ${ queryPath } ` ) ;
15753 return [ ] ;
15854 }
15955
160- // Interpret the results
161- const modelsAsDataLanguage = getModelsAsDataLanguage ( language ) ;
162-
163- const definition = modelsAsDataLanguage . predicates [ type ] ;
56+ const resultSet = bqrs [ Object . keys ( bqrs ) [ 0 ] ] ;
16457
165- const bqrsPath = completedQuery . outputDir . bqrsPath ;
58+ const results = resultSet . tuples ;
16659
167- const bqrsInfo = await cliServer . bqrsInfo ( bqrsPath ) ;
168- if ( bqrsInfo [ "result-sets" ] . length !== 1 ) {
169- void showAndLogExceptionWithTelemetry (
170- logger ,
171- telemetryListener ,
172- redactableError `Expected exactly one result set, got ${
173- bqrsInfo [ "result-sets" ] . length
174- } for ${ basename ( queryPath ) } `,
175- ) ;
60+ const definition = modelsAsDataLanguage . predicates [ modelType ] ;
61+ if ( ! definition ) {
62+ throw new Error ( `No definition for ${ modelType } ` ) ;
17663 }
17764
178- const resultSet = bqrsInfo [ "result-sets" ] [ 0 ] ;
179-
180- const decodedResults = await cliServer . bqrsDecode ( bqrsPath , resultSet . name ) ;
181-
182- const results = decodedResults . tuples ;
183-
18465 return (
18566 results
18667 // This is just a sanity check. The query should only return strings.
@@ -192,3 +73,37 @@ async function runSingleFlowQuery(
19273 } )
19374 ) ;
19475}
76+
77+ export async function runFlowModelQueries ( {
78+ cliServer,
79+ queryRunner,
80+ logger,
81+ queryStorageDir,
82+ databaseItem,
83+ language,
84+ progress,
85+ token,
86+ onResults,
87+ } : FlowModelOptions ) {
88+ const modelsAsDataLanguage = getModelsAsDataLanguage ( language ) ;
89+
90+ return runGenerateQueries (
91+ {
92+ queryConstraints : {
93+ "tags contain" : [ "modelgenerator" ] ,
94+ } ,
95+ filterQueries : ( queryPath ) => basename ( queryPath ) in queriesToModel ,
96+ parseResults : ( queryPath , results ) =>
97+ parseFlowModelResults ( queryPath , results , modelsAsDataLanguage , logger ) ,
98+ } ,
99+ {
100+ cliServer,
101+ queryRunner,
102+ queryStorageDir,
103+ databaseItem,
104+ progress,
105+ token,
106+ onResults,
107+ } ,
108+ ) ;
109+ }
0 commit comments