Skip to content

Commit a1872ad

Browse files
committed
simplify loop protection to single ast pass
1 parent a0b8d98 commit a1872ad

1 file changed

Lines changed: 62 additions & 93 deletions

File tree

client/modules/Preview/jsPreprocess.js

Lines changed: 62 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
7732
function 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

209180
function 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

Comments
 (0)