@@ -7,15 +7,21 @@ import type { CFile, Symbol } from "../symbolRegistry/types.ts";
77import type { ExportedFile } from "./types.ts" ;
88import { C_DECLARATION_QUERY } from "../headerResolver/queries.ts" ;
99import { C_IFDEF_QUERY } from "./queries.ts" ;
10+ import { CInvocationResolver } from "../invocationResolver/index.ts" ;
11+ import type z from "npm:zod" ;
12+ import type { localConfigSchema } from "../../../config/localConfig.ts" ;
13+ import { join } from "@std/path" ;
1014
1115export class CExtractor {
1216 manifest : DependencyManifest ;
1317 registry : Map < string , CFile > ;
1418 includeResolver : CIncludeResolver ;
19+ invocationResolver : CInvocationResolver ;
1520
1621 constructor (
1722 files : Map < string , { path : string ; content : string } > ,
1823 manifest : DependencyManifest ,
24+ napiConfig : z . infer < typeof localConfigSchema > ,
1925 ) {
2026 this . manifest = manifest ;
2127 const parsedFiles = new Map <
@@ -30,7 +36,13 @@ export class CExtractor {
3036 }
3137 const symbolRegistry = new CSymbolRegistry ( parsedFiles ) ;
3238 this . registry = symbolRegistry . getRegistry ( ) ;
33- this . includeResolver = new CIncludeResolver ( symbolRegistry ) ;
39+ const outDir = napiConfig . outDir ;
40+ const includeDirs = napiConfig [ "c" ] ?. includedirs ?? [ ] ;
41+ if ( outDir ) {
42+ includeDirs . push ( ...includeDirs . map ( ( i ) => join ( outDir , i ) ) ) ;
43+ }
44+ this . includeResolver = new CIncludeResolver ( symbolRegistry , includeDirs ) ;
45+ this . invocationResolver = new CInvocationResolver ( this . includeResolver ) ;
3446 }
3547
3648 /**
@@ -100,7 +112,8 @@ export class CExtractor {
100112 const ifdefs = C_IFDEF_QUERY . captures ( originalFile . rootNode ) . map ( ( n ) =>
101113 n . node . text
102114 ) ;
103- const definesToKeep = ifdefs . map ( ( i ) => fileInRegistry . symbols . get ( i ) ! ) ;
115+ const definesToKeep = ifdefs . map ( ( i ) => fileInRegistry . symbols . get ( i ) ! )
116+ . filter ( ( i ) => i ) ;
104117 const symbols = new Map < string , Symbol > ( ) ;
105118 for ( const define of definesToKeep ) {
106119 symbols . set ( define . name , define ) ;
@@ -116,6 +129,36 @@ export class CExtractor {
116129 if ( ! exportedFile . symbols . has ( symbolName ) ) {
117130 exportedFile . symbols . set ( symbolName , symbol ) ;
118131 }
132+ // Keep the files that recursively lead to a symbol we need
133+ const invocations = this . invocationResolver . getInvocationsForSymbol (
134+ symbol ,
135+ ) ;
136+ const filestokeep = Array . from (
137+ invocations . resolved . values ( ) . map ( ( s ) =>
138+ this . includeResolver . findInclusionChain (
139+ symbol . declaration . filepath ,
140+ s . symbol ,
141+ )
142+ ) . filter ( ( c ) => c !== undefined ) ,
143+ ) . flatMap ( ( c ) => c . flatMap ( ( f ) => this . registry . get ( f ) ! ) ) ;
144+ for ( const f of filestokeep ) {
145+ if ( ! exportedFiles . has ( f . file . path ) ) {
146+ const ifdefs = C_IFDEF_QUERY . captures ( f . file . rootNode ) . map ( ( n ) =>
147+ n . node . text
148+ ) ;
149+ const definesToKeep = ifdefs . map ( ( i ) => f . symbols . get ( i ) ! )
150+ . filter ( ( i ) => i ) ;
151+ const symbols = new Map < string , Symbol > ( ) ;
152+ for ( const define of definesToKeep ) {
153+ symbols . set ( define . name , define ) ;
154+ }
155+ exportedFiles . set ( f . file . path , {
156+ symbols,
157+ originalFile : f . file ,
158+ strippedFile : f . file ,
159+ } ) ;
160+ }
161+ }
119162 }
120163 return exportedFiles ;
121164 }
@@ -127,30 +170,100 @@ export class CExtractor {
127170 #stripFiles( files : Map < string , ExportedFile > ) {
128171 for ( const [ , file ] of files ) {
129172 const rootNode = file . originalFile . rootNode ;
130- const matches = C_DECLARATION_QUERY . captures ( rootNode ) ;
173+ const originalText = rootNode . text ; // Original file content
131174 const symbolsToKeep = new Set (
132175 file . symbols . values ( ) . map ( ( s ) => s . declaration . node ) ,
133176 ) ;
134177 const symbolsToRemove = new Set < Parser . SyntaxNode > ( ) ;
178+ const matches = C_DECLARATION_QUERY . captures ( rootNode ) ;
179+
135180 for ( const match of matches ) {
136181 const symbolNode = match . node ;
137182 if ( ! symbolsToKeep . has ( symbolNode ) ) {
138183 symbolsToRemove . add ( symbolNode ) ;
139184 }
140185 }
141- let filetext = rootNode . text ;
142- for ( const symbolNode of symbolsToRemove ) {
143- const symbolText = symbolNode . text ;
144- filetext = filetext . replace ( symbolText , "" ) ;
145- }
146- const strippedFile = cParser . parse ( filetext ) ;
186+
187+ // Helper function to recursively filter nodes
188+ const filterNodes = ( node : Parser . SyntaxNode ) : string => {
189+ if ( symbolsToRemove . has ( node ) ) {
190+ return "" ; // Skip this node
191+ }
192+
193+ // If the node has children, process them recursively
194+ if ( [ "translation_unit" , "preproc_ifdef" ] . includes ( node . type ) ) {
195+ let result = "" ;
196+ let lastEndIndex = node . startIndex ;
197+
198+ for ( const child of node . children ) {
199+ // Append the text between the last node and the current child
200+ result += originalText . slice ( lastEndIndex , child . startIndex ) ;
201+ result += filterNodes ( child ) ; // Process the child
202+ lastEndIndex = child . endIndex ;
203+ }
204+
205+ // Append the text after the last child
206+ result += originalText . slice ( lastEndIndex , node . endIndex ) ;
207+ return result ;
208+ }
209+
210+ // If the node has no children, return its text
211+ return originalText . slice ( node . startIndex , node . endIndex ) ;
212+ } ;
213+
214+ // Rebuild the file content by filtering nodes
215+ const newFileContent = filterNodes ( rootNode ) ;
216+
217+ // Compactify the file content
218+ const compactedContent = this . #compactifyFile( newFileContent ) ;
219+
220+ // Parse the new content and update the stripped file
221+ const strippedFile = cParser . parse ( compactedContent ) ;
147222 file . strippedFile = {
148223 path : file . originalFile . path ,
149224 rootNode : strippedFile . rootNode ,
150225 } ;
151226 }
152227 }
153228
229+ #removeDeletedIncludes(
230+ files : Map < string , ExportedFile > ,
231+ ) {
232+ const newproject : Map <
233+ string ,
234+ { path : string ; rootNode : Parser . SyntaxNode }
235+ > = new Map ( ) ;
236+ for ( const [ key , value ] of files ) {
237+ newproject . set ( key , value . strippedFile ) ;
238+ }
239+ const newregistry = new CSymbolRegistry ( newproject ) ;
240+ const newincluderes = new CIncludeResolver (
241+ newregistry ,
242+ this . includeResolver . includeDirs ,
243+ ) ;
244+ newincluderes . getInclusions ( ) ;
245+ for ( const [ key , value ] of files ) {
246+ const unresolved = newincluderes . unresolvedDirectives . get ( key ) ;
247+ if ( unresolved ) {
248+ let filetext = value . strippedFile . rootNode . text ;
249+ for ( const path of unresolved ) {
250+ filetext = filetext . replace ( `#include "${ path } "` , "" ) ;
251+ }
252+ filetext = this . #compactifyFile( filetext ) ;
253+ value . strippedFile . rootNode = cParser . parse ( filetext ) . rootNode ;
254+ }
255+ }
256+ }
257+
258+ #compactifyFile(
259+ filetext : string ,
260+ ) : string {
261+ // Remove empty lines and useless semicolons
262+ filetext = filetext . replace ( / ^ \s * ; \s * $ / gm, "" ) ; // Remove empty lines with semicolons
263+ filetext = filetext . replace ( / ^ \s * [ \r \n ] + / gm, "\n" ) ; // Remove empty lines
264+ return filetext ;
265+ }
266+
154267 /**
155268 * Finds the dependencies for a map of symbols.
156269 * @param symbolsMap - A map of file paths to their corresponding symbols.
@@ -196,6 +309,7 @@ export class CExtractor {
196309 const symbolsToExtract = this . #findDependenciesForMap( symbolsMap ) ;
197310 const filesToExport = this . #buildFileMap( symbolsToExtract ) ;
198311 this . #stripFiles( filesToExport ) ;
312+ this . #removeDeletedIncludes( filesToExport ) ;
199313 const exportedFiles = new Map < string , { path : string ; content : string } > ( ) ;
200314 for ( const [ filePath , file ] of filesToExport ) {
201315 const content = file . strippedFile . rootNode . text ;
0 commit comments