Class ClosureExtractor
ExtractionPolicy
.
For instance, a ForInBodyExtractionPolicy
extracts the body of every for-in loop in
the program, whereas a CorrelatedPairExtractionPolicy
extracts pieces of code containing
correlated property reads and writes of the same property.
As an example, consider the following function:
function extend(dest, src) { for(var p in src) dest[p] = src[p]; }
Under both ForInBodyExtractionPolicy
and CorrelatedPairExtractionPolicy
, this
should be transformed into
function extend(dest, src) { for(var p in src) (function _forin_body_0(p) { dest[p] = src[p]; })(p); }
There are four issues to be considered here.
- References to
this
:If the code to extract contains references to
this
, these references have to be rewritten; otherwise they would refer to the global object in the transformed code.We do this by giving the extracted function an extra parameter
thi$
, and rewritingthis
tothi$
within the extracted code.For instance,
Object.prototype.extend = function(src) { for(var p in src) this[p] = src[p]; }
becomes
Object.prototype.extend = function(src) { for(var p in src) (function _forin_body_0(p, thi$) { thi$[p] = src[p]; })(p, this); }
- Local variable declarations:
Local variable declarations inside the extracted code have to be hoisted to the enclosing function; otherwise they would become local variables of the extracted function instead.
This is already taken care of by the translation from Rhino's AST to CAst.
Optionally, the policy can request that one local variable of the surrounding function be turned into a local variable of the extracted closure. The rewriter checks that this is possible: the code to extract must not contain function calls or
new
expressions, and it must not containbreak
,continue
, orreturn
statements. The former requirement prevents a called function from observing a different value of the local variable than before. The latter requirement is necessary because the final value of the localised variable needs to be returned and assigned to its counterpart in the surrounding function; since non-local jumps are encoded by special return values (see next item), this would no longer be possible. break
,continue
,return
:A
break
orcontinue
statement within the extracted loop body that refers to the loop itself or an enclosing loop would become invalid in the transformed code. Areturn
statement would no longer return from the enclosing function, but instead from the extracted function.We transform all three statements into
return
statements returning an object literal with a propertytype
indicating whether this is a 'goto' (i.e.,break
orreturn
) or a 'return'. In the former case, the 'target' property contains an integer identifying the jump target; in the latter case, the 'value' property contains the value to return.The return value of the extracted function is then examined to determine whether it completed normally (i.e., returned
undefined
), or whether it returned an object indicating special control flow.For example, consider this code from MooTools:
for(var style in Element.ShortStyles) { if(property != style) continue; for(var s in Element.ShortStyles[style]) result.push(this.getStyle(s)); return result.join(' '); }
Under
ForInBodyExtractionPolicy
, this is transformed intofor(var style in Element.ShortStyles) { var s; re$ = (function _forin_body_0(style, thi$) { if(property != style) return { type: 'goto', target: 1 }; for(s in Element.ShortStyles[style]) { (function _forin_body_2(s) { result.push(thi$.getStyle(s)); })(s); } return { type: 'return', value: result.join(' ') }; })(style, this); if(re$) { if(re$.type == 'return') return re$.value; if(re$.type == 'goto') { if(re$.target == 1) continue; } } }
Note that at the CAst level,
break
andcontinue
are represented asgoto
statements, which simplifies the translation somewhat. The numerical encoding of jump targets does not matter as long as the extracted function and the fixup code agree on which number represents which label.- Assignment to loop variable:
The loop body may assign to the loop variable. If the variable is referenced after the loop, this assignment needs to be propagated back to the enclosing function in the extracted code.
TODO: This is not handled at the moment.
Finally, note that exceptions do not need to be handled specially.
- Author:
- mschaefer
-
Nested Class Summary
Nested classes/interfaces inherited from class com.ibm.wala.cast.js.ipa.callgraph.correlations.extraction.CAstRewriterExt
CAstRewriterExt.Edge
Nested classes/interfaces inherited from class com.ibm.wala.cast.tree.rewrite.CAstRewriter
CAstRewriter.CopyKey<Self extends CAstRewriter.CopyKey<Self>>, CAstRewriter.Rewrite, CAstRewriter.RewriteContext<K extends CAstRewriter.CopyKey<K>>
-
Field Summary
Fields inherited from class com.ibm.wala.cast.tree.rewrite.CAstRewriter
Ast, DEBUG, recursive, rootContext
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionprotected CAstNode
copyNodes
(CAstNode root, CAstControlFlowMap cfg, NodePos context, Map<Pair<CAstNode, CAstBasicRewriter.NoKey>, CAstNode> nodeMap) rewrite the CAst rooted at root under some context, returning the node at the root of the rewritten tree.protected void
enterEntity
(CAstEntity entity) protected void
Methods inherited from class com.ibm.wala.cast.js.ipa.callgraph.correlations.extraction.CAstRewriterExt
addEntity, addFlow, addNode, copyChildren, copyFlow, deleteFlow, flowOutTo, getCurrentEntity, getEnclosingEntities, isFlowDeleted, rewrite
Methods inherited from class com.ibm.wala.cast.tree.rewrite.CAstRewriter
copyChildrenArray, copyChildrenArrayAndTargets, copySource, copySubtreesIntoNewNode, copySubtreesIntoNewNode, copyTypes, rewrite
-
Constructor Details
-
ClosureExtractor
-
-
Method Details
-
enterEntity
- Overrides:
enterEntity
in classCAstRewriterExt
-
leaveEntity
protected void leaveEntity()- Overrides:
leaveEntity
in classCAstRewriterExt
-
copyNodes
protected CAstNode copyNodes(CAstNode root, CAstControlFlowMap cfg, NodePos context, Map<Pair<CAstNode, CAstBasicRewriter.NoKey>, CAstNode> nodeMap) Description copied from class:CAstRewriter
rewrite the CAst rooted at root under some context, returning the node at the root of the rewritten tree. mutate nodeMap in the process, indicating how (original node, copy key) pairs are mapped to nodes in the rewritten tree.- Specified by:
copyNodes
in classCAstRewriter<NodePos,
CAstBasicRewriter.NoKey>
-