Class ClosureExtractor
- java.lang.Object
-
- com.ibm.wala.cast.tree.rewrite.CAstRewriter<NodePos,com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NoKey>
-
- com.ibm.wala.cast.js.ipa.callgraph.correlations.extraction.CAstRewriterExt
-
- com.ibm.wala.cast.js.ipa.callgraph.correlations.extraction.ClosureExtractor
-
public class ClosureExtractor extends CAstRewriterExt
A CAst rewriter for extracting bits of code into one-shot closures. What to extract is determined by anExtractionPolicy.For instance, a
ForInBodyExtractionPolicyextracts the body of every for-in loop in the program, whereas aCorrelatedPairExtractionPolicyextracts 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
ForInBodyExtractionPolicyandCorrelatedPairExtractionPolicy, this should be transformed intofunction 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 rewritingthistothi$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
newexpressions, and it must not containbreak,continue, orreturnstatements. 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
breakorcontinuestatement within the extracted loop body that refers to the loop itself or an enclosing loop would become invalid in the transformed code. Areturnstatement would no longer return from the enclosing function, but instead from the extracted function.We transform all three statements into
returnstatements returning an object literal with a propertytypeindicating whether this is a 'goto' (i.e.,breakorreturn) 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,
breakandcontinueare represented asgotostatements, 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.
- References to
-
-
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
com.ibm.wala.cast.tree.rewrite.CAstRewriter.CopyKey<Self extends com.ibm.wala.cast.tree.rewrite.CAstRewriter.CopyKey<Self>>, com.ibm.wala.cast.tree.rewrite.CAstRewriter.Rewrite, com.ibm.wala.cast.tree.rewrite.CAstRewriter.RewriteContext<K extends com.ibm.wala.cast.tree.rewrite.CAstRewriter.CopyKey<K>>
-
-
Constructor Summary
Constructors Constructor Description ClosureExtractor(com.ibm.wala.cast.tree.CAst Ast, ExtractionPolicyFactory policyFactory)
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description protected com.ibm.wala.cast.tree.CAstNodecopyNodes(com.ibm.wala.cast.tree.CAstNode root, com.ibm.wala.cast.tree.CAstControlFlowMap cfg, NodePos context, java.util.Map<com.ibm.wala.util.collections.Pair<com.ibm.wala.cast.tree.CAstNode,com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NoKey>,com.ibm.wala.cast.tree.CAstNode> nodeMap)protected voidenterEntity(com.ibm.wala.cast.tree.CAstEntity entity)protected voidleaveEntity()-
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
-
-
-
-
Constructor Detail
-
ClosureExtractor
public ClosureExtractor(com.ibm.wala.cast.tree.CAst Ast, ExtractionPolicyFactory policyFactory)
-
-
Method Detail
-
enterEntity
protected void enterEntity(com.ibm.wala.cast.tree.CAstEntity entity)
- Overrides:
enterEntityin classCAstRewriterExt
-
leaveEntity
protected void leaveEntity()
- Overrides:
leaveEntityin classCAstRewriterExt
-
copyNodes
protected com.ibm.wala.cast.tree.CAstNode copyNodes(com.ibm.wala.cast.tree.CAstNode root, com.ibm.wala.cast.tree.CAstControlFlowMap cfg, NodePos context, java.util.Map<com.ibm.wala.util.collections.Pair<com.ibm.wala.cast.tree.CAstNode,com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NoKey>,com.ibm.wala.cast.tree.CAstNode> nodeMap)- Specified by:
copyNodesin classcom.ibm.wala.cast.tree.rewrite.CAstRewriter<NodePos,com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NoKey>
-
-