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 a3c030fac11aff91bbb3889144f4d164386bb247..f16a813514bff6385d9daa611f95ede7f33d90a7 100644 |
--- a/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java |
+++ b/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
+// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
@@ -278,7 +278,7 @@ public class GenerateJavascriptAST { |
jsNewDeclarationsStack.push(new HashSet<JsName>()); |
currentHolder = unit.getLibrary().getElement(); |
rtt = new RuntimeTypeInjector(this, typeProvider, translationContext, |
- context.getCompilerConfiguration().developerModeChecks()); |
+ context.getCompilerConfiguration().developerModeChecks(), mangler, unitLibrary); |
} |
/** |
@@ -595,32 +595,56 @@ public class GenerateJavascriptAST { |
*/ |
private void generateMethodGetter(MethodElement methodElement) { |
// Generate a getter for method binding to a variable |
- JsNameRef classJsNameRef = getJsName(methodElement.getEnclosingElement()).makeRef(); |
+ boolean isTopLevel = Elements.isTopLevel(methodElement); |
+ if (methodElement.getModifiers().isGetter() || methodElement.getModifiers().isSetter()) { |
+ return; |
+ } |
+ JsNameRef classJsNameRef = isTopLevel ? null : |
+ getJsName(methodElement.getEnclosingElement()).makeRef(); |
String getterName = mangler.createGetterSyntax(methodElement, unitLibrary); |
String methodName = methodElement.getName(); |
JsName getterJsName = globalScope.declareName(getterName, getterName, methodName); |
getterJsName.setObfuscatable(false); |
String mangledMethodName = mangler.mangleNamedMethod(methodElement, unitLibrary); |
+ String mangledRttMethodName = mangler.mangleRttLookupMethod(methodElement, unitLibrary); |
JsFunction func = new JsFunction(globalScope); |
JsNameRef getterJsNameRef; |
- JsExpression methodToCall; |
- if (methodElement.getModifiers().isStatic()) { |
- // function() { return <class><member>$member; } |
+ if (isTopLevel || methodElement.getModifiers().isStatic()) { |
+ // function() { |
+ // var ret = <class><member>$named; |
+ // ret.$lookupRTT = <class><member>$named_$lookupRTT; |
+ // return ret; |
+ // } |
+ func.setBody(new JsBlock()); |
+ List<JsStatement> stmts = func.getBody().getStatements(); |
+ JsName returnVar = func.getScope().declareFreshName("ret"); |
getterJsNameRef = AstUtil.newNameRef(classJsNameRef, getterJsName); |
- methodToCall = AstUtil.newNameRef(classJsNameRef, mangledMethodName); |
- func.setBody(AstUtil.newBlock(new JsReturn(methodToCall))); |
+ stmts.add(AstUtil.newVar(null, returnVar, |
+ AstUtil.newNameRef(classJsNameRef, mangledMethodName))); |
+ JsBinaryOperation varLookup = AstUtil.newAssignment( |
+ AstUtil.newNameRef(returnVar.makeRef(), "$lookupRTT"), |
+ AstUtil.newNameRef(classJsNameRef, mangledRttMethodName)); |
+ stmts.add(varLookup.makeStmt()); |
+ stmts.add(new JsReturn(returnVar.makeRef())); |
} else { |
- // function() { return $bind(<class>.prototype.<member>$member, this); } |
+ // function() { return $bind(<class>.prototype.<member>$named, |
+ // $bind(<class>.prototype.<member>$named_$lookupRTT, this); } |
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()); |
+ JsExpression bindMethodCall = AstUtil.newInvocation(new JsNameRef("$bind"), |
+ AstUtil.newNameRef(prototypeRef, mangledMethodName), |
+ AstUtil.newNameRef(prototypeRef, mangledRttMethodName), new JsThisRef()); |
func.setBody(AstUtil.newBlock(new JsReturn(bindMethodCall))); |
} |
- func.setName(getterJsNameRef.getName()); |
- func.setSourceRef(methodElement.getNode()); |
- JsBinaryOperation asg = AstUtil.newAssignment(getterJsNameRef, func); |
+ |
+ JsExpression asg; |
+ if (isTopLevel) { |
+ func.setName(getterJsNameRef.getName()); |
+ asg = func; |
+ } else { |
+ func.setSourceRef(methodElement.getNode()); |
+ asg = AstUtil.newAssignment(getterJsNameRef, func); |
+ } |
asg.setSourceRef(methodElement.getNode()); |
globalBlock.getStatements().add(asg.makeStmt()); |
} |
@@ -1095,12 +1119,10 @@ public class GenerateJavascriptAST { |
} |
assert currentScopeInfo == null : "Nested methods should be impossible"; |
- inFactoryOrStaticContext = x.getModifiers().isFactory() |
- || x.getModifiers().isStatic(); |
+ inFactoryOrStaticContext = isFactoryOrStaticContext(x.getModifiers()); |
currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks()); |
JsFunction func = (JsFunction) generate(x.getFunction()); |
- |
assert currentScopeInfo != null; |
inFactoryOrStaticContext = false; |
currentScopeInfo = null; |
@@ -1113,6 +1135,10 @@ public class GenerateJavascriptAST { |
globalBlock.getStatements().add(func.makeStmt()); |
globalBlock.getStatements().add(tramp.makeStmt()); |
+ |
+ // special case for top level methods |
+ rtt.generateRuntimeTypeInfo(x); |
+ generateMethodGetter(x.getSymbol()); |
} |
return func; |
@@ -1335,9 +1361,13 @@ public class GenerateJavascriptAST { |
return prop; |
} |
+ private boolean isFactoryOrStaticContext(Modifiers modifiers) { |
+ return modifiers.isFactory() || modifiers.isStatic(); |
+ } |
+ |
/** |
- * Turns a method into a prototype assignment on the JS class. Clears the |
- * name from the given function. |
+ * Turns a method into a prototype assignment on the JS class. Clears the name from the given |
+ * function. |
*/ |
private void makeMethod(Element element, JsFunction func) { |
if (element.getEnclosingElement().getKind().equals(ElementKind.CLASS)) { |
@@ -1350,10 +1380,8 @@ public class GenerateJavascriptAST { |
globalBlock.getStatements().add(asg.makeStmt()); |
// If it's a (non-operator, non-property) method, generate its named trampoline. |
- if (element.getKind().equals(ElementKind.METHOD) && |
- !element.getModifiers().isOperator() && |
- !element.getModifiers().isGetter() && |
- !element.getModifiers().isSetter()) { |
+ if (element.getKind().equals(ElementKind.METHOD) && !element.getModifiers().isOperator() |
+ && !element.getModifiers().isGetter() && !element.getModifiers().isSetter()) { |
// Declare the mangled trampoline's name in the same scope as its target. |
String mangled = mangler.mangleNamedMethod((MethodElement) element, unitLibrary); |
JsName namedName = prop.getName().getEnclosing().declareName(mangled); |
@@ -1364,6 +1392,17 @@ 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 = isFactoryOrStaticContext(element.getModifiers()); |
+ 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()); |
@@ -2942,6 +2981,7 @@ public class GenerateJavascriptAST { |
JsName fnDeclaredName; |
JsName hoistedName; |
+ String hoistedRttName = null; |
// TODO(johnlenz): values used in super class init methods are currently |
// evaluated twice (once for the init and once for the constructor), but |
@@ -2951,6 +2991,7 @@ public class GenerateJavascriptAST { |
if (fnWasPreviouslyHoisted) { |
fnDeclaredName = fn.getName(); |
hoistedName = fn.getName(); |
+ hoistedRttName = mangler.mangleRttLookupMethod(hoistedName.toString(), unitLibrary); |
} else { |
// 0) Save off the original name |
@@ -2988,16 +3029,28 @@ public class GenerateJavascriptAST { |
hoistedName = globalScope.declareName(mangled); |
tramp.setName(hoistedName); |
globalBlock.getStatements().add(tramp.makeStmt()); |
+ |
+ if (x.getParent() != null && !(x.getParent() instanceof DartFunctionObjectInvocation)) { |
+ hoistedRttName = mangler.mangleRttLookupMethod(hoistedName.toString(), unitLibrary); |
+ rtt.generateRuntimeTypeInfo(x, hoistedRttName); |
+ } |
+ } else { |
+ String mangled = mangler.mangleNamedMethod(hoistedName.getIdent(), unitLibrary); |
+ hoistedName = globalScope.declareName(mangled); |
+ hoistedRttName = mangler.mangleRttLookupMethod(hoistedName.toString(), unitLibrary); |
+ } |
+ |
+ if (x.getParent() == null || (x.getParent() instanceof DartFunctionObjectInvocation)) { |
+ hoistedRttName = null; |
} |
// 5) Bind the necessary scope references and possibly "this". |
JsExpression replacement; |
if (list.isEmpty() && inFactoryOrStaticContext) { |
- |
// Simply replace the function |
- replacement = new JsNameRef(hoistedName); |
- |
+ replacement = AstUtil.newInvocation(new JsNameRef("$bind"), new JsNameRef(hoistedName), |
+ hoistedRttName != null ? new JsNameRef(hoistedRttName) : nulle(), undefined()); |
} else { |
// Replace "function (){}" with "bind(hoistedName, this, scope1, scope2, ...)" |
// so that references to class fields can be resolved. |
@@ -3021,8 +3074,9 @@ public class GenerateJavascriptAST { |
} |
} |
- JsInvocation invoke = |
- AstUtil.newInvocation(new JsNameRef(jsBindName), new JsNameRef(hoistedName), thisRef); |
+ JsInvocation invoke = AstUtil.newInvocation(new JsNameRef(jsBindName), |
+ new JsNameRef(hoistedName), |
+ hoistedRttName != null ? new JsNameRef(hoistedRttName) : nulle(), thisRef); |
// Add the scope alias to the bind call and function parameter list |
int parameterIndex = 0; |
@@ -3549,6 +3603,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; |
} |
@@ -3634,21 +3697,23 @@ public class GenerateJavascriptAST { |
MethodElement methodElement, DartNode qualifier) { |
JsExpression boundMethod; |
String mangledName = mangler.mangleNamedMethod(methodElement, unitLibrary); |
+ String mangledGetter = mangler.createGetterSyntax(methodElement, unitLibrary); |
boolean isSuperCall = (qualifier != null) && isSuperCall(qualifier.getSymbol()); |
if (isSuperCall) { |
boundMethod = generateSuperFieldAccess(qualifier, |
- mangler.createGetterSyntax(methodElement.getName(), unitLibrary)); |
+ mangler.createGetterSyntax(methodElement.getName(), unitLibrary)); |
} else if (Elements.isTopLevel(methodElement)) { |
- boundMethod = AstUtil.newNameRef(null, mangledName); |
+ boundMethod = AstUtil.newInvocation(AstUtil.newNameRef(null, mangledGetter)); |
} else if (methodElement.isStatic()) { |
if (qualifier == null) { |
qualifier = methodElement.getEnclosingElement().getNode(); |
assert (qualifier instanceof DartClass); |
- boundMethod = AstUtil.newNameRef(getJsName(qualifier.getSymbol()).makeRef(), mangledName); |
+ boundMethod = AstUtil.newNameRef(getJsName(qualifier.getSymbol()).makeRef(), |
+ mangledGetter); |
} else { |
- assert (qualifier instanceof DartIdentifier); |
- boundMethod = AstUtil.newNameRef((JsExpression) generate(qualifier), mangledName); |
+ boundMethod = AstUtil.newNameRef((JsExpression) generate(qualifier), mangledGetter); |
} |
+ boundMethod = AstUtil.newInvocation(boundMethod); |
} else { |
// Should be an invocation on an instance |
if (qualifier == null) { |
@@ -3659,7 +3724,9 @@ public class GenerateJavascriptAST { |
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 methodRtt = AstUtil.newNameRef(prototypeRef, |
+ mangler.mangleRttLookupMethod(methodElement, unitLibrary)); |
+ boundMethod = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, methodRtt, methodQualifier); |
} |
boundMethod.setSourceRef(methodNode); |
return boundMethod; |