@@ -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