Index: compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java |
diff --git a/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java b/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java |
index b9971760c053b235d6fc61454a0f135618170fe6..459509401bb548d504d0d77fabdff200aa014672 100644 |
--- a/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java |
+++ b/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java |
@@ -148,6 +148,7 @@ import com.google.dart.compiler.resolver.MethodElement; |
import com.google.dart.compiler.resolver.SuperElement; |
import com.google.dart.compiler.resolver.VariableElement; |
import com.google.dart.compiler.type.InterfaceType; |
+import com.google.dart.compiler.type.FunctionAliasType; |
import com.google.dart.compiler.type.Type; |
import com.google.dart.compiler.type.TypeKind; |
import com.google.dart.compiler.type.Types; |
@@ -178,15 +179,34 @@ public class GenerateJavascriptAST { |
private final CoreTypeProvider typeProvider; |
private final boolean generateClosureCompatibleCode; |
+ static class FunctionAliasDiscover extends DartNodeTraverser<Void> { |
+ boolean aliasFound = false; |
+ |
+ @Override |
+ public Void visitTypeNode(DartTypeNode node) { |
+ if (node.getType() instanceof FunctionAliasType) { |
+ aliasFound = true; |
+ return null; |
+ } |
+ return visitNode(node); |
+ } |
+ } |
zundel
2011/11/16 22:17:15
style: add n/l between functions/class definitions
|
+ static boolean isFunctionAlias(DartExpression expr) { |
+ FunctionAliasDiscover visitForAlias = new FunctionAliasDiscover(); |
+ expr.accept(visitForAlias); |
+ return visitForAlias.aliasFound; |
+ } |
+ |
/** |
* Generates the Javascript AST using the names created in {@link GenerateNamesAndScopes}. |
*/ |
- static class GenerateJavascriptVisitor |
+ class GenerateJavascriptVisitor |
implements DartPlainVisitor<JsNode>, TraversalContextProvider { |
- private final boolean generateClosureCompatibleCode; |
+ private boolean isInBinaryExpression = false; |
+ private Token binaryExpressionOperator = Token.NON_TOKEN; |
- private static boolean isSuperCall(Symbol symbol) { |
+ private boolean isSuperCall(Symbol symbol) { |
return ElementKind.of(symbol).equals(ElementKind.SUPER); |
} |
@@ -194,7 +214,7 @@ public class GenerateJavascriptAST { |
* Returns <code>true</code> for members that are static or should be treated |
* as if the user had declared them as static. |
*/ |
- private static boolean isDeclaredAsStaticOrImplicitlyStatic(Element element) { |
+ private boolean isDeclaredAsStaticOrImplicitlyStatic(Element element) { |
Modifiers modifiers = element.getModifiers(); |
if (modifiers.isStatic()) { |
// Member was actually declared static |
@@ -247,11 +267,8 @@ public class GenerateJavascriptAST { |
private final RuntimeTypeInjector rtt; |
private final TranslationContext translationContext; |
- private final OptimizationStrategy optStrategy; |
- private final DartCompilerContext context; |
private final LibraryElement unitLibrary; |
private final DartMangler mangler; |
- private final CoreTypeProvider typeProvider; |
private final Types typeUtils; |
private final Deque<JsName> catchVarStack = new ArrayDeque<JsName>(); |
@@ -260,13 +277,9 @@ public class GenerateJavascriptAST { |
TranslationContext translationContext, |
OptimizationStrategy optStrategy, CoreTypeProvider typeProvider, |
boolean generateClosureCompatibleCode) { |
- this.context = context; |
this.translationContext = translationContext; |
- this.optStrategy = optStrategy; |
- this.typeProvider = typeProvider; |
this.typeUtils = Types.getInstance(typeProvider); |
this.unitLibrary = unit.getLibrary().getElement(); |
- this.generateClosureCompatibleCode = generateClosureCompatibleCode; |
// Cache the mangler in a field since it is used frequently |
mangler = translationContext.getMangler(); |
@@ -590,37 +603,82 @@ public class GenerateJavascriptAST { |
} |
/** |
- * Creates a $getter for a method of a class, returning $method as a closure |
- * bound to the current instance if it is an instance method. |
+ * Creates a $getter for a method of a class, returning $method as a closure bound to the |
+ * current instance if it is an instance method. |
*/ |
private void generateMethodGetter(MethodElement methodElement) { |
+ generateMethodGetter(methodElement, null); |
+ } |
+ |
+ private void generateMethodGetter(MethodElement methodElement, String NameOverride) { |
zundel
2011/11/16 22:17:15
style: argument name should start with lower case
|
// Generate a getter for method binding to a variable |
JsNameRef classJsNameRef = getJsName(methodElement.getEnclosingElement()).makeRef(); |
- String getterName = mangler.createGetterSyntax(methodElement, unitLibrary); |
+ String getterName = NameOverride == null ? mangler.createGetterSyntax(methodElement, |
+ unitLibrary) : NameOverride + "$getter"; |
String methodName = methodElement.getName(); |
JsName getterJsName = globalScope.declareName(getterName, getterName, methodName); |
getterJsName.setObfuscatable(false); |
- String mangledMethodName = mangler.mangleNamedMethod(methodElement, unitLibrary); |
+ String mangledMethodName = NameOverride == null ? mangler.mangleNamedMethod(methodElement, |
+ unitLibrary) : mangler.mangleNamedMethod(NameOverride, unitLibrary); |
JsFunction func = new JsFunction(globalScope); |
+ JsScope scope = func.getScope(); |
JsNameRef getterJsNameRef; |
JsExpression methodToCall; |
+ |
+ JsBlock body = new JsBlock(); |
+ List<JsStatement> stmts = body.getStatements(); |
+ |
+ JsExpression bindMethodCall; |
+ JsName seen = scope.declareFreshName("ret"); |
+ |
if (methodElement.getModifiers().isStatic()) { |
- // function() { return <class><member>$member; } |
+ // function() { var ret = $bind(<class>.prototype.<member>$member, this); |
+ // ret = $bind(<class>.prototype.<member>$lookupRTT, this); return ret; } |
zundel
2011/11/16 22:17:15
The commented code doesn't make any sense to me. I
codefu
2011/11/16 22:39:55
Bad comment. The second part should read "ret.look
|
getterJsNameRef = AstUtil.newNameRef(classJsNameRef, getterJsName); |
methodToCall = AstUtil.newNameRef(classJsNameRef, mangledMethodName); |
- func.setBody(AstUtil.newBlock(new JsReturn(methodToCall))); |
+ stmts.add(AstUtil.newVar(null, seen, methodToCall)); |
+ |
+ JsName rttLatch = scope.declareFreshName("ret.lookupRTT"); |
+ String mangledLookupName = mangler.mangleLookupMethod(methodElement, unitLibrary); |
+ methodToCall = AstUtil.newNameRef(classJsNameRef, mangledLookupName); |
+ stmts.add(AstUtil.newAssignment(rttLatch.makeRef(), methodToCall).makeStmt()); |
} else { |
- // function() { return $bind(<class>.prototype.<member>$member, this); } |
+ // function() { var ret = $bind(<class>.prototype.<member>$member, this); |
+ // ret = $bind(<class>.prototype.<member>$lookupRTT, this); return ret; } |
zundel
2011/11/16 22:17:15
Same comment as above.
codefu
2011/11/16 22:39:55
Done.
|
JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(classJsNameRef); |
getterJsNameRef = AstUtil.newNameRef(prototypeRef, getterJsName); |
- methodToCall = AstUtil.newNameRef(prototypeRef, mangledMethodName); |
- JsExpression bindMethodCall = AstUtil.newInvocation( |
- new JsNameRef("$bind"), methodToCall, new JsThisRef()); |
- func.setBody(AstUtil.newBlock(new JsReturn(bindMethodCall))); |
+ if (NameOverride == null) { |
+ methodToCall = AstUtil.newNameRef(prototypeRef, mangledMethodName); |
+ bindMethodCall = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, |
+ new JsThisRef()); |
+ } else { |
+ bindMethodCall = AstUtil.newNameRef(null, NameOverride + "$named"); |
+ } |
+ stmts.add(AstUtil.newVar(null, seen, bindMethodCall)); |
+ |
+ String mangledLookupName; |
+ if (NameOverride == null) { |
+ mangledLookupName = mangler.mangleLookupMethod(methodElement, unitLibrary); |
+ methodToCall = AstUtil.newNameRef(prototypeRef, mangledLookupName); |
+ bindMethodCall = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, |
zundel
2011/11/16 22:17:15
IMHO, re-using the variable bindMethodCall is conf
|
+ new JsThisRef()); |
+ } else { |
+ bindMethodCall = AstUtil.newNameRef(null, NameOverride + "$lookupRTT"); |
+ } |
+ JsName rttLatch = scope.declareFreshName("ret.lookupRTT"); |
+ stmts.add(AstUtil.newAssignment(rttLatch.makeRef(), bindMethodCall).makeStmt()); |
} |
- func.setName(getterJsNameRef.getName()); |
+ stmts.add(new JsReturn(seen.makeRef())); |
+ func.setBody(body); |
+ |
func.setSourceRef(methodElement.getNode()); |
- JsBinaryOperation asg = AstUtil.newAssignment(getterJsNameRef, func); |
+ JsBinaryOperation asg; |
+ if (NameOverride == null) { |
+ asg = AstUtil.newAssignment(getterJsNameRef, func); |
+ } else { |
+ getterJsNameRef = AstUtil.newNameRef(null, NameOverride + "$getter"); |
+ asg = AstUtil.newAssignment(getterJsNameRef, func); |
+ } |
asg.setSourceRef(methodElement.getNode()); |
globalBlock.getStatements().add(asg.makeStmt()); |
} |
@@ -1347,6 +1405,19 @@ public class GenerateJavascriptAST { |
asg = assign(namedProp, tramp); |
globalBlock.getStatements().add(asg.makeStmt()); |
+ |
+ // Generate a lookup method after finally writing function / named tramp |
+ assert currentScopeInfo == null : "Nested methods should be impossible"; |
+ inFactoryOrStaticContext = element.getModifiers().isFactory() |
+ || element.getModifiers().isStatic(); |
+ DartMethodDefinition x = (DartMethodDefinition) element.getNode(); |
+ currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks()); |
+ rtt.generateRuntimeTypeInfo(x); |
+ |
+ assert currentScopeInfo != null; |
+ inFactoryOrStaticContext = false; |
+ currentScopeInfo = null; |
+ |
} |
} else { |
globalBlock.getStatements().add(func.makeStmt()); |
@@ -2211,59 +2282,69 @@ public class GenerateJavascriptAST { |
public JsNode visitBinaryExpression(DartBinaryExpression x) { |
assert x == x.getNormalizedNode(); |
- Token operator = x.getOperator(); |
+ boolean oldBinaryExpression = isInBinaryExpression; |
+ Token oldBinaryExpressionOp = binaryExpressionOperator; |
+ isInBinaryExpression = true; |
+ binaryExpressionOperator = x.getOperator(); |
- if (operator == Token.IS) { |
- return generateInstanceOfComparison(x); |
- } |
+ try { |
+ Token operator = x.getOperator(); |
- DartExpression arg1 = x.getArg1(); |
- DartExpression arg2 = x.getArg2(); |
- JsExpression rhs = (JsExpression) generate(arg2); |
- if (operator == Token.ASSIGN) { |
- return arg1.accept(new Assignment(x, rhs, arg2.getType())); |
- } |
+ if (operator == Token.IS) { |
+ return generateInstanceOfComparison(x); |
+ } |
- assert !operator.isUserDefinableOperator() || !operator.isAssignmentOperator() : x; |
+ DartExpression arg1 = x.getArg1(); |
+ DartExpression arg2 = x.getArg2(); |
+ JsExpression rhs = (JsExpression) generate(arg2); |
+ if (operator == Token.ASSIGN) { |
+ return arg1.accept(new Assignment(x, rhs, arg2.getType())); |
+ } |
- // We can skip shims for non-user-definable operators (NE is a special case because it's not |
- // user-definable, but still has to be shimmed). |
- boolean skipShim = (!operator.isUserDefinableOperator() && (operator != Token.NE)); |
- if (!skipShim) { |
- // For user-defined operators, the optimization strategy can choose to skip the shim. |
- skipShim = optStrategy.canSkipOperatorShim(x); |
- } |
+ assert !operator.isUserDefinableOperator() || !operator.isAssignmentOperator() : x; |
- JsExpression lhs = (JsExpression) generate(arg1); |
- Token op = x.getOperator(); |
+ // We can skip shims for non-user-definable operators (NE is a special case because it's not |
+ // user-definable, but still has to be shimmed). |
+ boolean skipShim = (!operator.isUserDefinableOperator() && (operator != Token.NE)); |
+ if (!skipShim) { |
+ // For user-defined operators, the optimization strategy can choose to skip the shim. |
+ skipShim = optStrategy.canSkipOperatorShim(x); |
+ } |
- lhs = rtt.addTypeCheck(getCurrentClass(), lhs, getRequiredType(op), arg1.getType(), arg1); |
- rhs = rtt.addTypeCheck(getCurrentClass(), rhs, getRequiredType(op), arg2.getType(), arg2); |
- |
- if (skipShim) { |
- if (op.isEqualityOperator()) { |
- op = mapToStrictEquals(op); |
- // TODO (fabiomfv) - This optimization targets a v8 perf issue. V8 double equals |
- // comparison to undefined is up to 4 times slower than == null. It seems that it was |
- // fixed on v8 3.5. once we move to 3.5 and the fix confirmed, this should be revisited. |
- if (arg2 instanceof DartNullLiteral) { |
- op = mapToNonStrictEquals(op); |
- rhs = nulle(); |
- } |
- if (arg1 instanceof DartNullLiteral) { |
- JsExpression tmp = lhs; |
- lhs = rhs; |
- rhs = tmp; |
- op = mapToNonStrictEquals(op); |
- rhs = nulle(); |
+ JsExpression lhs = (JsExpression) generate(arg1); |
+ Token op = x.getOperator(); |
+ |
+ lhs = rtt.addTypeCheck(getCurrentClass(), lhs, getRequiredType(op), arg1.getType(), arg1); |
+ rhs = rtt.addTypeCheck(getCurrentClass(), rhs, getRequiredType(op), arg2.getType(), arg2); |
+ |
+ if (skipShim) { |
+ if (op.isEqualityOperator()) { |
+ op = mapToStrictEquals(op); |
+ // TODO (fabiomfv) - This optimization targets a v8 perf issue. V8 double equals |
+ // comparison to undefined is up to 4 times slower than == null. It seems that it was |
+ // fixed on v8 3.5. once we move to 3.5 and the fix confirmed, this should be revisited. |
+ if (arg2 instanceof DartNullLiteral) { |
+ op = mapToNonStrictEquals(op); |
+ rhs = nulle(); |
+ } |
+ if (arg1 instanceof DartNullLiteral) { |
+ JsExpression tmp = lhs; |
+ lhs = rhs; |
+ rhs = tmp; |
+ op = mapToNonStrictEquals(op); |
+ rhs = nulle(); |
+ } |
} |
+ JsExpression binOp = new JsBinaryOperation(mapBinaryOp(op), lhs, rhs); |
+ binOp.setSourceRef(x); |
+ return binOp; |
+ } else { |
+ JsNameRef ref = new JsNameRef(mangler.createOperatorSyntax(operator)); |
+ return AstUtil.newInvocation(ref, lhs, rhs).setSourceRef(x); |
} |
- JsExpression binOp = new JsBinaryOperation(mapBinaryOp(op), lhs, rhs); |
- binOp.setSourceRef(x); |
- return binOp; |
- } else { |
- JsNameRef ref = new JsNameRef(mangler.createOperatorSyntax(operator)); |
- return AstUtil.newInvocation(ref, lhs, rhs).setSourceRef(x); |
+ } finally { |
+ isInBinaryExpression = oldBinaryExpression; |
+ binaryExpressionOperator = oldBinaryExpressionOp; |
} |
} |
@@ -2958,6 +3039,11 @@ public class GenerateJavascriptAST { |
ScopeRootInfo.ClosureInfo info = currentScopeInfo.getClosureInfo(x.getFunction()); |
List<DartScope> list = info.getSortedReferencedScopeList(); |
+// TODO(codefu): Properlly support hoisted function's types TAG:codefuHoistedFunctionTypes |
+// String rootName = hoistedName.getIdent(); |
+// String getterName = mangler.createGetterSyntax(rootName, unitLibrary); |
+// String lookupName = mangler.mangleLookupMethod(rootName, unitLibrary); |
+ |
// TODO(jgw): See johnlenz' comment above about re-evaluation. This guard can go away once |
// that problem is fixed. |
if (!fnWasPreviouslyHoisted) { |
@@ -2971,15 +3057,29 @@ public class GenerateJavascriptAST { |
hoistedName = globalScope.declareName(mangled); |
tramp.setName(hoistedName); |
globalBlock.getStatements().add(tramp.makeStmt()); |
+ |
+// TODO(codefu): TAG:codefuHoistedFunctionTypes |
+// if(x.getParent() != null && !(x.getParent() instanceof DartFunctionObjectInvocation)) { |
+// rtt.generateRuntimeTypeInfo(x, rootName); |
+// generateMethodGetter(x.getSymbol(), rootName); |
+// } |
} |
// 5) Bind the necessary scope references and possibly "this". |
JsExpression replacement; |
+// TODO(codefu): TAG:codefuHoistedFunctionTypes |
+// boolean callGetter = false; |
if (list.isEmpty() && inFactoryOrStaticContext) { |
// Simply replace the function |
- replacement = new JsNameRef(hoistedName); |
+// TODO(codefu): TAG:codefuHoistedFunctionTypes |
+// if(x.getParent() != null && !(x.getParent() instanceof DartFunctionObjectInvocation)) { |
+// replacement = new JsNameRef(getterName); |
+// callGetter = true; |
+// } else { |
+ replacement = new JsNameRef(hoistedName); |
+// } |
} else { |
// Replace "function (){}" with "bind(hoistedName, this, scope1, scope2, ...)" |
@@ -3061,6 +3161,8 @@ public class GenerateJavascriptAST { |
JsNameRef scopeF = makeScopeAliasNameRef(scope, x.getSymbol()); |
JsExpression assig = AstUtil.newAssignment(scopeF, replacement); |
replacement = new JsBinaryOperation(JsBinaryOperator.COMMA, init, assig); |
+// TODO(codefu): TAG:codefuHoistedFunctionTypes |
+// callGetter = false; |
// TODO(floitsch): we need to clear the scope object. |
} |
} |
@@ -3076,10 +3178,20 @@ public class GenerateJavascriptAST { |
} else { |
// The parent expects an expression, but handles Var statements separately. |
assert !hoistedName.equals(fnDeclaredName); |
+// TODO(codefu): TAG:codefuHoistedFunctionTypes |
+// if (callGetter) { |
+// // codefu: change to invocation of getter. |
+// replacement = AstUtil.newInvocation(replacement); |
+// } |
JsVars vars = AstUtil.newVar(x.getName(), fnDeclaredName, replacement); |
return vars.setSourceRef(x); |
} |
} else { |
+// TODO(codefu): TAG:codefuHoistedFunctionTypes |
+// if (callGetter) { |
+// // codefu: change to invocation of getter. |
+// replacement = AstUtil.newInvocation(replacement); |
+// } |
return replacement.setSourceRef(x); |
} |
} |
@@ -3532,6 +3644,15 @@ public class GenerateJavascriptAST { |
@Override |
public JsNode visitFunctionTypeAlias(DartFunctionTypeAlias node) { |
+ // TODO(codefu): Optimize away if we're never the rhs of a "is" check. |
+ ClassElement classElement = node.getSymbol(); |
+ JsName classJsName = getJsName(classElement); |
+ JsFunction jsClass = new JsFunction(globalScope, classJsName).setSourceRef(node); |
+ jsClass.setIsConstructor(false); |
+ jsClass.setBody(new JsBlock()); |
+ globalBlock.getStatements().add(jsClass.makeStmt()); |
+ |
+ rtt.generateRuntimeTypeInfo(node); |
return null; |
} |
@@ -3616,22 +3737,27 @@ public class GenerateJavascriptAST { |
private JsExpression generateMethodBoundToVariable(DartIdentifier methodNode, |
MethodElement methodElement, DartNode qualifier) { |
JsExpression boundMethod; |
- String mangledName = mangler.mangleNamedMethod(methodElement, unitLibrary); |
+ |
+ String mangledName; |
+ boolean isDirectReference = methodNode.getTargetSymbol() == methodElement; |
+ mangledName = mangler.createGetterSyntax(methodElement, unitLibrary); |
boolean isSuperCall = (qualifier != null) && isSuperCall(qualifier.getSymbol()); |
if (isSuperCall) { |
boundMethod = generateSuperFieldAccess(qualifier, |
- mangler.createGetterSyntax(methodElement.getName(), unitLibrary)); |
+ mangler.createGetterSyntax(methodElement, unitLibrary)); |
} else if (Elements.isTopLevel(methodElement)) { |
+ mangledName = mangler.mangleNamedMethod(methodElement, unitLibrary); |
boundMethod = AstUtil.newNameRef(null, mangledName); |
+ // todo: can we lhs "is" top level methods? |
} else if (methodElement.isStatic()) { |
if (qualifier == null) { |
qualifier = methodElement.getEnclosingElement().getNode(); |
assert (qualifier instanceof DartClass); |
boundMethod = AstUtil.newNameRef(getJsName(qualifier.getSymbol()).makeRef(), mangledName); |
} else { |
- assert (qualifier instanceof DartIdentifier); |
boundMethod = AstUtil.newNameRef((JsExpression) generate(qualifier), mangledName); |
} |
+ boundMethod = AstUtil.newInvocation(boundMethod); |
} else { |
// Should be an invocation on an instance |
if (qualifier == null) { |
@@ -3641,8 +3767,49 @@ public class GenerateJavascriptAST { |
ClassElement classElement = (ClassElement) methodElement.getEnclosingElement(); |
String className = mangler.mangleClassName(classElement); |
JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(new JsNameRef(className)); |
- JsExpression methodToCall = AstUtil.newNameRef(prototypeRef, mangledName); |
- boundMethod = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, methodQualifier); |
+ JsExpression methodToCall; |
+ if (isDirectReference) { |
+ methodToCall = AstUtil.newNameRef(methodQualifier, mangledName); |
+ boundMethod = AstUtil.newInvocation(methodToCall); |
+ } else { |
+ methodToCall = AstUtil.newNameRef(prototypeRef, mangledName); |
+ boundMethod = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, methodQualifier); |
+ } |
+ } |
+ boundMethod.setSourceRef(methodNode); |
+ return boundMethod; |
+ } |
+ |
+ private JsExpression generateMethodLookup(DartIdentifier methodNode, |
+ MethodElement methodElement, DartNode qualifier) { |
+ JsExpression boundMethod; |
+ |
+ String mangledName; |
+ mangledName = mangler.mangleLookupMethod(methodElement, unitLibrary); |
+ boolean isSuperCall = (qualifier != null) && isSuperCall(qualifier.getSymbol()); |
+ if (isSuperCall) { |
+ boundMethod = generateSuperFieldAccess(qualifier, |
+ mangler.mangleLookupMethod(methodElement, unitLibrary)); |
+ } else if (Elements.isTopLevel(methodElement)) { |
+ boundMethod = AstUtil.newNameRef(null, mangledName); |
+ // todo: can we lhs "is" top level methods? |
+ } else if (methodElement.isStatic()) { |
+ if (qualifier == null) { |
+ qualifier = methodElement.getEnclosingElement().getNode(); |
+ assert (qualifier instanceof DartClass); |
+ boundMethod = AstUtil.newNameRef(getJsName(qualifier.getSymbol()).makeRef(), mangledName); |
+ } else { |
+ boundMethod = AstUtil.newNameRef((JsExpression) generate(qualifier), mangledName); |
+ } |
+ boundMethod = AstUtil.newInvocation(boundMethod); |
+ } else { |
+ // Should be an invocation on an instance |
+ if (qualifier == null) { |
+ qualifier = DartThisExpression.get(); |
+ } |
+ JsExpression methodQualifier = (JsExpression) generate(qualifier); |
+ JsExpression methodToCall = AstUtil.newNameRef(methodQualifier, mangledName); |
+ boundMethod = AstUtil.newInvocation(methodToCall); |
} |
boundMethod.setSourceRef(methodNode); |
return boundMethod; |
@@ -3652,6 +3819,13 @@ public class GenerateJavascriptAST { |
return referenceName(element, node); |
} |
+ private DartBinaryExpression findBinaryExpression(DartNode node) { |
+ while (node != null && !(node instanceof DartBinaryExpression)) { |
+ node = node.getParent(); |
+ } |
+ return (DartBinaryExpression) node; |
+ } |
+ |
private JsExpression generateLoad(DartNode qualifier, DartIdentifier node, Element element) { |
boolean accessThroughShim = true; |
if (element != null) { |
@@ -3666,6 +3840,15 @@ public class GenerateJavascriptAST { |
case FUNCTION_OBJECT: |
// TODO(5089961): we should not generate code for class expressions. |
case CLASS: |
+ if (isInBinaryExpression && binaryExpressionOperator == Token.IS) { |
+ DartBinaryExpression binaryExpr = findBinaryExpression(node); |
+ if (binaryExpr == node.getParent() && isFunctionAlias(binaryExpr.getArg2())) { |
+ JsExpression lookup = generateLoadTemporary(element, node); |
+ JsNameRef methodToCall = AstUtil.newNameRef(lookup, "lookupRTT"); |
+ JsInvocation bindMethodCall = AstUtil.newInvocation(methodToCall); |
+ return bindMethodCall; |
+ } |
+ } |
return generateLoadTemporary(element, node); |
case NONE: |
@@ -3673,6 +3856,13 @@ public class GenerateJavascriptAST { |
return generateSuperFieldAccess(qualifier, |
mangler.createGetterSyntax(node.getTargetName(), unitLibrary)); |
} |
+ |
+ // Unresolved element (var x = new Blah<int>()) used in a DartBinaryExpression - need to |
+ // find the rhs symbol to see if its a FunctionAliasType. |
+ if (isInBinaryExpression && isFunctionAlias(findBinaryExpression(node).getArg2())) { |
+ return generateUnresolvedAccess(qualifier, |
+ mangler.mangleLookupMethod(node.getTargetName(), unitLibrary)); |
+ } |
return generateUnresolvedAccess(qualifier, |
mangler.createGetterSyntax(node.getTargetName(), unitLibrary)); |
@@ -3693,6 +3883,9 @@ public class GenerateJavascriptAST { |
case METHOD: { |
MethodElement method = (MethodElement) element; |
+ if (isInBinaryExpression && isFunctionAlias(findBinaryExpression(node).getArg2())) { |
+ return generateMethodLookup(node, method, qualifier); |
+ } |
return generateMethodBoundToVariable(node, method, qualifier); |
} |