@@ -29,51 +29,6 @@ function collectShaderFunctionNames(ast) {
2929 return names ;
3030}
3131
32- function collectLoopsToProtect ( ast , shaderNames ) {
33- const loops = [ ] ;
34-
35- function visitNode ( node , ancestors ) {
36- const isInsideShader = ancestors . some ( ( ancestor , idx ) => {
37- if (
38- ancestor . type === 'FunctionDeclaration' &&
39- shaderNames . has ( ancestor . id ?. name )
40- ) {
41- return true ;
42- }
43- if (
44- ancestor . type === 'FunctionExpression' ||
45- ancestor . type === 'ArrowFunctionExpression'
46- ) {
47- const parent = ancestors [ idx - 1 ] ;
48- if (
49- parent ?. type === 'CallExpression' &&
50- isShaderCall ( parent ) &&
51- parent . arguments . includes ( ancestor )
52- ) {
53- return true ;
54- }
55- if (
56- parent ?. type === 'VariableDeclarator' &&
57- shaderNames . has ( parent . id ?. name )
58- ) {
59- return true ;
60- }
61- }
62- return false ;
63- } ) ;
64-
65- if ( ! isInsideShader ) loops . push ( node ) ;
66- }
67-
68- walk . ancestor ( ast , {
69- ForStatement : visitNode ,
70- WhileStatement : visitNode ,
71- DoWhileStatement : visitNode
72- } ) ;
73-
74- return loops ;
75- }
76-
7732function makeVarDecl ( varName ) {
7833 return {
7934 type : 'VariableDeclaration' ,
@@ -152,58 +107,74 @@ function makeCheckStatement(varName, line) {
152107 } ;
153108}
154109
155- function injectProtection ( loop , idx ) {
156- const varName = `_LP${ idx } ` ;
157- const { line } = loop . loc . start ;
158- const check = makeCheckStatement ( varName , line ) ;
110+ function protectLoops ( ast , shaderNames ) {
111+ let loopCount = 0 ;
159112
160- if ( loop . body . type === 'BlockStatement' ) {
161- loop . body . body . unshift ( check ) ;
162- } else {
163- loop . body = {
164- type : 'BlockStatement' ,
165- body : [ check , loop . body ]
166- } ;
167- }
113+ function visitNode ( node , ancestors ) {
114+ const isInsideShader = ancestors . some ( ( ancestor , idx ) => {
115+ if (
116+ ancestor . type === 'FunctionDeclaration' &&
117+ shaderNames . has ( ancestor . id ?. name )
118+ ) {
119+ return true ;
120+ }
121+ if (
122+ ancestor . type === 'FunctionExpression' ||
123+ ancestor . type === 'ArrowFunctionExpression'
124+ ) {
125+ const parent = ancestors [ idx - 1 ] ;
126+ if (
127+ parent ?. type === 'CallExpression' &&
128+ isShaderCall ( parent ) &&
129+ parent . arguments . includes ( ancestor )
130+ ) {
131+ return true ;
132+ }
133+ if (
134+ parent ?. type === 'VariableDeclarator' &&
135+ shaderNames . has ( parent . id ?. name )
136+ ) {
137+ return true ;
138+ }
139+ }
140+ return false ;
141+ } ) ;
168142
169- return makeVarDecl ( varName ) ;
170- }
143+ if ( isInsideShader ) return ;
171144
172- function insertVarDeclsIntoBlock ( blockBody , loopsWithVarDecls ) {
173- loopsWithVarDecls . forEach ( ( { loop, varDecl } ) => {
174- const idx = blockBody . indexOf ( loop ) ;
175- if ( idx !== - 1 ) {
176- blockBody . splice ( idx , 0 , varDecl ) ;
177- }
178- } ) ;
179- }
145+ const varName = `_LP${ loopCount ++ } ` ;
146+ const { line } = node . loc . start ;
147+ const check = makeCheckStatement ( varName , line ) ;
180148
181- function injectVarDeclsIntoAst ( ast , loops ) {
182- const loopToVarDecl = new Map ( ) ;
183- loops . forEach ( ( loop , idx ) => {
184- loopToVarDecl . set ( loop , injectProtection ( loop , idx ) ) ;
185- } ) ;
149+ if ( node . body . type === 'BlockStatement' ) {
150+ node . body . body . unshift ( check ) ;
151+ } else {
152+ node . body = { type : 'BlockStatement' , body : [ check , node . body ] } ;
153+ }
186154
187- walk . simple ( ast , {
188- BlockStatement ( node ) {
189- const loopsWithVarDecls = node . body
190- . filter ( ( child ) => loopToVarDecl . has ( child ) )
191- . map ( ( loop ) => ( { loop, varDecl : loopToVarDecl . get ( loop ) } ) ) ;
192- if ( loopsWithVarDecls . length > 0 ) {
193- insertVarDeclsIntoBlock ( node . body , loopsWithVarDecls ) ;
194- loopsWithVarDecls . forEach ( ( { loop } ) => loopToVarDecl . delete ( loop ) ) ;
195- }
196- } ,
197- Program ( node ) {
198- const loopsWithVarDecls = node . body
199- . filter ( ( child ) => loopToVarDecl . has ( child ) )
200- . map ( ( loop ) => ( { loop, varDecl : loopToVarDecl . get ( loop ) } ) ) ;
201- if ( loopsWithVarDecls . length > 0 ) {
202- insertVarDeclsIntoBlock ( node . body , loopsWithVarDecls ) ;
203- loopsWithVarDecls . forEach ( ( { loop } ) => loopToVarDecl . delete ( loop ) ) ;
155+ const varDecl = makeVarDecl ( varName ) ;
156+ for ( let i = ancestors . length - 1 ; i >= 0 ; i -- ) {
157+ const ancestor = ancestors [ i ] ;
158+ if (
159+ ancestor !== node &&
160+ ( ancestor . type === 'BlockStatement' || ancestor . type === 'Program' )
161+ ) {
162+ const nodeIdx = ancestor . body . indexOf ( node ) ;
163+ if ( nodeIdx !== - 1 ) {
164+ ancestor . body . splice ( nodeIdx , 0 , varDecl ) ;
165+ break ;
166+ }
204167 }
205168 }
169+ }
170+
171+ walk . ancestor ( ast , {
172+ ForStatement : visitNode ,
173+ WhileStatement : visitNode ,
174+ DoWhileStatement : visitNode
206175 } ) ;
176+
177+ return loopCount ;
207178}
208179
209180function parseJs ( jsText ) {
@@ -228,11 +199,9 @@ export function jsPreprocess(jsText) {
228199 if ( ! ast ) return jsText ;
229200
230201 const shaderNames = collectShaderFunctionNames ( ast ) ;
231- const loops = collectLoopsToProtect ( ast , shaderNames ) ;
232-
233- if ( loops . length === 0 ) return jsText ;
202+ const loopCount = protectLoops ( ast , shaderNames ) ;
234203
235- injectVarDeclsIntoAst ( ast , loops ) ;
204+ if ( loopCount === 0 ) return jsText ;
236205
237206 return escodegen . generate ( ast ) ;
238207}
0 commit comments