while (declarations.hasNext()) {
Symbol symbol = declarations.next();
if (!(symbol instanceof Property)) {
continue;
}
Property prop = (Property) symbol;
// We should only move a property across modules if:
// 1) We can move it deeper in the module graph, and
// 2) it's a function.
// 3) it is not a get or a set.
//
// #1 should be obvious. #2 is more subtle. It's possible
// to copy off of a prototype, as in the code:
// for (var k in Foo.prototype) {
// doSomethingWith(Foo.prototype[k]);
// }
// This is a common way to implement pseudo-multiple inheritance in JS.
//
// So if we move a prototype method into a deeper module, we must
// replace it with a stub function so that it preserves its original
// behavior.
Node value = prop.getValue();
if (moduleGraph.dependsOn(deepestCommonModuleRef, prop.getModule()) &&
value.getType() == Token.FUNCTION) {
Node valueParent = value.getParent();
if (valueParent.getType() == Token.GET
|| valueParent.getType() == Token.SET) {
// TODO(johnlenz): a GET or SET can't be deferred like a normal
// FUNCTION property definition as a mix-in would get the result
// of a GET instead of the function itself.
continue;
}
Node proto = prop.getPrototype();
int stubId = idGenerator.newId();
// example: JSCompiler_stubMethod(id);
Node stubCall = new Node(Token.CALL,
Node.newString(Token.NAME, STUB_METHOD_NAME),
Node.newNumber(stubId))
.copyInformationFromForTree(value);
stubCall.putBooleanProp(Node.FREE_CALL, true);
// stub out the method in the original module
// A.prototype.b = JSCompiler_stubMethod(id);
valueParent.replaceChild(value, stubCall);
// unstub the function body in the deeper module
Node unstubParent = compiler.getNodeForCodeInsertion(
deepestCommonModuleRef);
Node unstubCall = new Node(Token.CALL,
Node.newString(Token.NAME, UNSTUB_METHOD_NAME),
Node.newNumber(stubId),
value);
unstubCall.putBooleanProp(Node.FREE_CALL, true);
unstubParent.addChildToFront(
// A.prototype.b = JSCompiler_unstubMethod(id, body);
new Node(Token.EXPR_RESULT,
new Node(Token.ASSIGN,
new Node(Token.GETPROP,
proto.cloneTree(),
Node.newString(Token.STRING, nameInfo.name)),
unstubCall))
.copyInformationFromForTree(value));
compiler.reportCodeChange();
logger.fine("Moved method: " +
proto.getQualifiedName() + "." + nameInfo.name +
" from module " + prop.getModule() + " to module " +
deepestCommonModuleRef);
}
}
}