Skip to content

Commit 6940eef

Browse files
committed
Added comments, reorganized
1 parent 592b67c commit 6940eef

File tree

1 file changed

+74
-60
lines changed

1 file changed

+74
-60
lines changed

src/BabelPlugin.js

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ function Plugin(babel) {
22
const t = babel.types;
33
const tryStack = [];
44
const finalStack = [];
5+
// Traverses specific expression types and marks a CallExpression in tail position
56
function markTailCall(expr) {
67
if (expr.type === "CallExpression") {
78
expr.isTailCall = true;
@@ -16,7 +17,12 @@ function Plugin(babel) {
1617
return markTailCall(expr.alternate);
1718
}
1819
}
19-
// Function call without arguments
20+
// HzTokens are unique single-instance objects for wrapping user instructions and data.
21+
// Type 1: Invocation Tokens,
22+
// Wrap userland functors and any operands needed to invoke them.
23+
// Type 2: Data Tokens,
24+
// Wrap arbitrary userland datum when returning or yielding.
25+
// Instruction Token: Function call without arguments
2026
function hzCall(callee) {
2127
return t.sequenceExpression([
2228
t.yieldExpression(
@@ -32,14 +38,14 @@ function Plugin(babel) {
3238
)
3339
]);
3440
}
35-
// Function call with arguments
41+
// Instruction Token: Function call with arguments
3642
function hzCallArgs(name, argsArray) {
3743
const seqExp = hzCall(name);
3844
seqExp.expressions[0].argument.callee.property.name = "callArgs";
3945
seqExp.expressions[0].argument.arguments.push(t.arrayExpression(argsArray));
4046
return seqExp;
4147
}
42-
// Method call without arguments
48+
// Instruction Token: Method call without arguments
4349
function hzCallMethod(object, prop) {
4450
return t.sequenceExpression([
4551
t.yieldExpression(
@@ -56,53 +62,54 @@ function Plugin(babel) {
5662
)
5763
]);
5864
}
59-
// Method call with arguments
65+
// Instruction Token: Method call with arguments
6066
function hzCallMethodArgs(object, prop, argsArray) {
6167
const seqExp = hzCallMethod(object, prop);
6268
seqExp.expressions[0].argument.callee.property.name = "callMethodArgs";
6369
seqExp.expressions[0].argument.arguments.push(t.arrayExpression(argsArray));
6470
return seqExp;
6571
}
72+
// Instruction Token: Constructor call without arguments
6673
function hzNew(callee) {
6774
const seqExp = hzCall(callee);
6875
seqExp.expressions[0].argument.callee.property.name = "new";
6976
return seqExp;
7077
}
78+
// Instruction Token: Constructor call with arguments
7179
function hzNewArgs(name, argsArray) {
7280
const seqExp = hzNew(name);
7381
seqExp.expressions[0].argument.arguments.push(t.arrayExpression(argsArray));
7482
seqExp.expressions[0].argument.callee.property.name = "newArgs";
7583
return seqExp;
7684
}
85+
// Instruction Token: Method constructor call without arguments
7786
function hzNewMethod(object, prop) {
7887
const seqExp = hzCallMethod(object, prop);
7988
seqExp.expressions[0].argument.callee.property.name = "newMethod";
8089
return seqExp;
8190
}
91+
// Instruction Token: Method constructor call with arguments
8292
function hzNewMethodArgs(object, prop, argsArray) {
8393
const seqExp = hzNewMethod(object, prop);
8494
seqExp.expressions[0].argument.arguments.push(t.arrayExpression(argsArray));
8595
seqExp.expressions[0].argument.callee.property.name = "newMethodArgs";
8696
return seqExp;
8797
}
88-
// Return without argument
98+
// Instruction Token: Return without argument
8999
function hzReturn() {
90-
return t.callExpression(
91-
t.memberExpression(
92-
t.identifier("hzUserLib"),
93-
t.identifier("return")
94-
),
95-
[]
100+
return t.memberExpression(
101+
t.identifier("hzUserLib"),
102+
t.identifier("return")
96103
);
97104
}
98-
// Return with argument
105+
// Instruction Token: Return with argument
99106
function hzReturnArg(argExp) {
100-
const callExp = hzReturn();
101-
callExp.callee.property.name = "returnValue";
102-
callExp.arguments.push(argExp);
107+
const memberExp = hzReturn();
108+
const callExp = t.callExpression(memberExp, [argExp]);
109+
memberExp.property.name = "returnValue";
103110
return callExp;
104111
}
105-
// Yield without argument
112+
// Instruction Token: Yield without argument
106113
function hzYield() {
107114
return t.callExpression(
108115
t.memberExpression(
@@ -115,14 +122,14 @@ function Plugin(babel) {
115122
])]
116123
);
117124
}
118-
// Yield with argument
125+
// Instruction Token: Yield with argument
119126
function hzYieldArg(argExp) {
120127
const callExp = hzYield();
121128
callExp.callee.property.name = "yieldValue";
122129
callExp.arguments[0].properties[0].value = argExp;
123130
return callExp;
124131
}
125-
// Spawn without arguments
132+
// Instruction Token: Spawn without arguments
126133
function hzSpawn(spawnExp) {
127134
if (spawnExp.arguments[0].type === "CallExpression") {
128135
spawnExp.arguments = [spawnExp.arguments[0].callee];
@@ -133,15 +140,15 @@ function Plugin(babel) {
133140
spawnExp
134141
);
135142
}
136-
// Spawn with arguments
143+
// Instruction Token: Spawn with arguments
137144
function hzSpawnArgs(spawnExp) {
138145
const args = spawnExp.arguments[0].arguments;
139146
spawnExp = hzSpawn(spawnExp);
140147
spawnExp.argument.arguments.push(t.arrayExpression(args));
141148
spawnExp.argument.callee.property.name = "spawnArgs";
142149
return spawnExp;
143150
}
144-
// Spawn method without arguments
151+
// Instruction Token: Spawn method without arguments
145152
function hzSpawnMethod(spawnExp) {
146153
spawnExp.arguments = [
147154
spawnExp.arguments[0].callee.object,
@@ -152,15 +159,36 @@ function Plugin(babel) {
152159
spawnExp
153160
);
154161
}
155-
// Spawn method with arguments
162+
// Instruction Token: Spawn method with arguments
156163
function hzSpawnMethodArgs(spawnExp) {
157164
const args = spawnExp.arguments[0].arguments;
158165
spawnExp = hzSpawnMethod(spawnExp);
159166
spawnExp.argument.arguments.push(t.arrayExpression(args));
160167
spawnExp.argument.callee.property.name = "spawnMethodArgs";
161168
return spawnExp;
162169
}
163-
// Coroutine factory
170+
// Instruction Token: Loop interruption token
171+
function loopInterruptor(path) {
172+
if (path.node.body.type !== "BlockStatement") {
173+
if (path.node.body.type === "EmptyStatement") {
174+
path.node.body = t.blockStatement([]);
175+
} else {
176+
if (Array.isArray(path.node.body)) {
177+
path.node.body = t.blockStatement(path.node.body);
178+
} else {
179+
path.node.body = t.blockStatement([path.node.body]);
180+
}
181+
}
182+
}
183+
path.node.body.body.unshift(t.expressionStatement(
184+
t.yieldExpression(t.memberExpression(
185+
t.identifier("hzUserLib"),
186+
t.identifier("loopYield")
187+
))
188+
));
189+
}
190+
// Function call, declaration, and expression detours enable dynamic call site interception.
191+
// FunctionExpression detour
164192
function hzCoroutine(funcExp) {
165193
return t.callExpression(
166194
t.memberExpression(
@@ -170,7 +198,7 @@ function Plugin(babel) {
170198
[funcExp]
171199
);
172200
}
173-
// ArrowFunction Coroutine factory
201+
// ArrowFunctionExpression detour
174202
function hzArrowCoroutine(funcExp) {
175203
funcExp.type = "FunctionExpression";
176204
if (funcExp.body.type !== "BlockStatement") {
@@ -190,7 +218,7 @@ function Plugin(babel) {
190218
]
191219
);
192220
}
193-
// Generator factory
221+
// Generator FunctionExpression detour
194222
function hzGenerator(funcExp) {
195223
return t.callExpression(
196224
t.memberExpression(
@@ -200,7 +228,7 @@ function Plugin(babel) {
200228
[funcExp]
201229
);
202230
}
203-
// Coroutine declaration
231+
// FunctionDeclaration detour
204232
function declareHzCoroutine(funcDec) {
205233
return t.variableDeclaration("var", [
206234
t.variableDeclarator(
@@ -214,7 +242,7 @@ function Plugin(babel) {
214242
)
215243
]);
216244
}
217-
// Generator declaration
245+
// Generator FunctionDeclaration detour
218246
function declareHzGenerator(funcDec) {
219247
return t.variableDeclaration("var", [
220248
t.variableDeclarator(
@@ -228,29 +256,9 @@ function Plugin(babel) {
228256
)
229257
]);
230258
}
231-
function loopInterruptor(path) {
232-
if (path.node.body.type !== "BlockStatement") {
233-
if (path.node.body.type === "EmptyStatement") {
234-
path.node.body = t.blockStatement([]);
235-
} else {
236-
if (Array.isArray(path.node.body)) {
237-
path.node.body = t.blockStatement(path.node.body);
238-
} else {
239-
path.node.body = t.blockStatement([path.node.body]);
240-
}
241-
}
242-
}
243-
path.node.body.body.unshift(t.expressionStatement(
244-
t.yieldExpression(t.callExpression(
245-
t.memberExpression(
246-
t.identifier("hzUserLib"),
247-
t.identifier("loopYield")
248-
),
249-
[]
250-
))
251-
));
252-
}
253259
const visitor = {
260+
// These all nsert a yield & HzToken at the top of loops
261+
// Useful for interrupting loops which make few function calls
254262
"WhileStatement": {
255263
exit: loopInterruptor
256264
},
@@ -266,6 +274,7 @@ function Plugin(babel) {
266274
"ForInStatement": {
267275
exit: loopInterruptor
268276
},
277+
// Detours a FunctionExpression
269278
"FunctionExpression": {
270279
exit: function (path) {
271280
if (path.node.generator) path.replaceWith(hzGenerator(path.node));
@@ -274,13 +283,16 @@ function Plugin(babel) {
274283
path.skip();
275284
}
276285
},
286+
// Detours an ArrowFunctionExpression
277287
"ArrowFunctionExpression": {
278288
exit: function (path) {
279289
path.replaceWith(hzArrowCoroutine(path.node));
280290
path.node.arguments[0].generator = true;
281291
path.skip();
282292
}
283293
},
294+
// Detours a FunctionDeclaration.
295+
// Changes it to a FunctionExpression and assigns it to a variable, moving it tp the top of the block
284296
"FunctionDeclaration": {
285297
exit: function (path) {
286298
if (path.node.generator) var varDec = declareHzGenerator(path.node);
@@ -292,6 +304,7 @@ function Plugin(babel) {
292304
path.remove();
293305
}
294306
},
307+
// Transforms a NewExpression into an Instruction Token
295308
"NewExpression": {
296309
exit: function (path) {
297310
if (path.node.callee.type === "MemberExpression") {
@@ -322,6 +335,9 @@ function Plugin(babel) {
322335
path.skip();
323336
}
324337
},
338+
// Checks if the CallExpression is a partially transformed "spawn" HzToken from Acorn.
339+
// If so, it completes the transformation of arguments and wraps the HzToken in a yield.
340+
// If the argument is a FunctionExpression then it is detoured.
325341
"CallExpression": {
326342
enter: function (path) {
327343
if (path.node.callee.type === "MemberExpression" &&
@@ -360,8 +376,9 @@ function Plugin(babel) {
360376
path.skip();
361377
}
362378
},
379+
// Transforms a CallExpression into an Instruction Token.
380+
// Checks if the CallExpression is a proper tail call and marks the HzToken if so.
363381
exit: function (path) {
364-
//if (path.getFunctionParent().type === "Program") return;
365382
const isTailCall = "isTailCall" in path.node;
366383
if (path.node.callee.type === "MemberExpression") {
367384
if (path.node.arguments.length === 0) {
@@ -409,14 +426,15 @@ function Plugin(babel) {
409426
}
410427
},
411428
"ReturnStatement": {
429+
// Finds and marks a CallExpression if it is in the tail position
412430
enter: function (path) {
413431
if (path.node.argument !== null) markTailCall(path.node.argument);
414432
},
433+
// Transforms a ReturnStatement into an Instruction Token
415434
exit: function (path) {
416-
const parentPath = path.getFunctionParent();
417435
if (path.node.argument === null) path.node.argument = hzReturn();
418436
else path.node.argument = hzReturnArg(path.node.argument);
419-
if (parentPath.node.generator) path.node.argument.arguments = [t.ObjectExpression([
437+
if (path.getFunctionParent().node.generator) path.node.argument.arguments = [t.ObjectExpression([
420438
t.ObjectProperty(
421439
t.identifier("value"),
422440
path.node.argument.arguments[0]
@@ -429,6 +447,7 @@ function Plugin(babel) {
429447
}
430448
},
431449
"BlockStatement": {
450+
// Records entry into the "finalizer" block of a TryStatement
432451
enter: function (path) {
433452
if (tryStack.length > 0) {
434453
const stmtParent = path.getStatementParent();
@@ -441,6 +460,7 @@ function Plugin(babel) {
441460
}
442461
}
443462
},
463+
// Records exit out of the "finalizer" block of a TryStatement
444464
exit: function (path) {
445465
if (tryStack.length > 0 && finalStack.length > 0) {
446466
const stmtParent = path.getStatementParent();
@@ -455,31 +475,25 @@ function Plugin(babel) {
455475
}
456476
},
457477
"TryStatement": {
478+
// Records entry into a TryStatement
458479
enter: function (path) {
459480
tryStack.push({
460481
node: path.node,
461482
function: path.getFunctionParent()
462483
});
463484
},
485+
// Records exit out of a TryStatement
464486
exit: function (path) {
465487
if (tryStack.length > 0) tryStack.pop();
466488
}
467489
},
468490
"YieldExpression": {
491+
// Transforms a YieldExpression into an Instruction Token
469492
exit: function (path) {
470493
if (path.node.argument === null) path.node.argument = hzYield();
471494
else path.node.argument = hzYieldArg(path.node.argument);
472495
}
473496
}
474-
/*
475-
"SpawnExpression": {
476-
exit: function (path) {
477-
if (path.node.argument === null) return;
478-
path.node.type = "YieldExpression"
479-
path.node.argument = hzSpawn(path.node.argument);
480-
}
481-
}
482-
*/
483497
};
484498
return { visitor: visitor };
485499
};

0 commit comments

Comments
 (0)