public class ClosureExtractor extends CAstRewriterExt
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.
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 rewriting
this to thi$ 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 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 contain break,
continue, or return 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 or continue statement within the extracted loop body that refers
to the loop itself or an enclosing loop would become invalid in the transformed code. A return
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
property type indicating whether this is a 'goto' (i.e., break or return)
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 into
for(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 and continue are represented as goto
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.
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.
CAstRewriterExt.Edgecom.ibm.wala.cast.tree.rewrite.CAstRewriter.CopyKey<Self extends com.ibm.wala.cast.tree.rewrite.CAstRewriter.CopyKey>, 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>| Constructor and Description |
|---|
ClosureExtractor(com.ibm.wala.cast.tree.CAst Ast,
ExtractionPolicyFactory policyFactory) |
| Modifier and Type | Method and Description |
|---|---|
protected com.ibm.wala.cast.tree.CAstNode |
copyNodes(com.ibm.wala.cast.tree.CAstNode root,
com.ibm.wala.cast.tree.CAstControlFlowMap cfg,
NodePos context,
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 void |
enterEntity(com.ibm.wala.cast.tree.CAstEntity entity) |
protected void |
leaveEntity(com.ibm.wala.cast.tree.CAstEntity entity) |
addEntity, addFlow, addNode, copyChildren, copyFlow, deleteFlow, flowOutTo, getCurrentEntity, getEnclosingEntities, isFlowDeleted, rewritepublic ClosureExtractor(com.ibm.wala.cast.tree.CAst Ast,
ExtractionPolicyFactory policyFactory)
protected void enterEntity(com.ibm.wala.cast.tree.CAstEntity entity)
enterEntity in class CAstRewriterExtprotected void leaveEntity(com.ibm.wala.cast.tree.CAstEntity entity)
leaveEntity in class CAstRewriterExtprotected com.ibm.wala.cast.tree.CAstNode copyNodes(com.ibm.wala.cast.tree.CAstNode root,
com.ibm.wala.cast.tree.CAstControlFlowMap cfg,
NodePos context,
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)
copyNodes in class com.ibm.wala.cast.tree.rewrite.CAstRewriter<NodePos,com.ibm.wala.cast.tree.rewrite.CAstBasicRewriter.NoKey>Copyright © 2017. All rights reserved.