Chromium Code Reviews| 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); |
| } |