Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(510)

Unified Diff: compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java

Issue 9479013: Remove backends. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: More clean up Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
deleted file mode 100644
index 2942b7405883f4f14b26309dd1b0aa55bec02b8e..0000000000000000000000000000000000000000
--- a/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java
+++ /dev/null
@@ -1,3877 +0,0 @@
-// Copyright (c) 2012, 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.
-
-package com.google.dart.compiler.backend.js;
-
-import com.google.common.collect.Lists;
-import com.google.dart.compiler.DartCompilationError;
-import com.google.dart.compiler.DartCompilerContext;
-import com.google.dart.compiler.InternalCompilerException;
-import com.google.dart.compiler.ast.DartArrayAccess;
-import com.google.dart.compiler.ast.DartArrayLiteral;
-import com.google.dart.compiler.ast.DartAssertion;
-import com.google.dart.compiler.ast.DartBinaryExpression;
-import com.google.dart.compiler.ast.DartBlock;
-import com.google.dart.compiler.ast.DartBooleanLiteral;
-import com.google.dart.compiler.ast.DartBreakStatement;
-import com.google.dart.compiler.ast.DartCase;
-import com.google.dart.compiler.ast.DartCatchBlock;
-import com.google.dart.compiler.ast.DartClass;
-import com.google.dart.compiler.ast.DartClassMember;
-import com.google.dart.compiler.ast.DartConditional;
-import com.google.dart.compiler.ast.DartContinueStatement;
-import com.google.dart.compiler.ast.DartDefault;
-import com.google.dart.compiler.ast.DartDoWhileStatement;
-import com.google.dart.compiler.ast.DartDoubleLiteral;
-import com.google.dart.compiler.ast.DartEmptyStatement;
-import com.google.dart.compiler.ast.DartExprStmt;
-import com.google.dart.compiler.ast.DartExpression;
-import com.google.dart.compiler.ast.DartField;
-import com.google.dart.compiler.ast.DartFieldDefinition;
-import com.google.dart.compiler.ast.DartForInStatement;
-import com.google.dart.compiler.ast.DartForStatement;
-import com.google.dart.compiler.ast.DartFunction;
-import com.google.dart.compiler.ast.DartFunctionExpression;
-import com.google.dart.compiler.ast.DartFunctionObjectInvocation;
-import com.google.dart.compiler.ast.DartFunctionTypeAlias;
-import com.google.dart.compiler.ast.DartIdentifier;
-import com.google.dart.compiler.ast.DartIfStatement;
-import com.google.dart.compiler.ast.DartImportDirective;
-import com.google.dart.compiler.ast.DartInitializer;
-import com.google.dart.compiler.ast.DartIntegerLiteral;
-import com.google.dart.compiler.ast.DartInvocation;
-import com.google.dart.compiler.ast.DartLabel;
-import com.google.dart.compiler.ast.DartLibraryDirective;
-import com.google.dart.compiler.ast.DartMapLiteral;
-import com.google.dart.compiler.ast.DartMapLiteralEntry;
-import com.google.dart.compiler.ast.DartMethodDefinition;
-import com.google.dart.compiler.ast.DartMethodInvocation;
-import com.google.dart.compiler.ast.DartNamedExpression;
-import com.google.dart.compiler.ast.DartNativeBlock;
-import com.google.dart.compiler.ast.DartNativeDirective;
-import com.google.dart.compiler.ast.DartNewExpression;
-import com.google.dart.compiler.ast.DartNode;
-import com.google.dart.compiler.ast.DartNodeTraverser;
-import com.google.dart.compiler.ast.DartNullLiteral;
-import com.google.dart.compiler.ast.DartParameter;
-import com.google.dart.compiler.ast.DartParameterizedTypeNode;
-import com.google.dart.compiler.ast.DartParenthesizedExpression;
-import com.google.dart.compiler.ast.DartPlainVisitor;
-import com.google.dart.compiler.ast.DartPropertyAccess;
-import com.google.dart.compiler.ast.DartRedirectConstructorInvocation;
-import com.google.dart.compiler.ast.DartResourceDirective;
-import com.google.dart.compiler.ast.DartReturnStatement;
-import com.google.dart.compiler.ast.DartSourceDirective;
-import com.google.dart.compiler.ast.DartStatement;
-import com.google.dart.compiler.ast.DartStringInterpolation;
-import com.google.dart.compiler.ast.DartStringLiteral;
-import com.google.dart.compiler.ast.DartSuperConstructorInvocation;
-import com.google.dart.compiler.ast.DartSuperExpression;
-import com.google.dart.compiler.ast.DartSwitchStatement;
-import com.google.dart.compiler.ast.DartSyntheticErrorExpression;
-import com.google.dart.compiler.ast.DartSyntheticErrorStatement;
-import com.google.dart.compiler.ast.DartThisExpression;
-import com.google.dart.compiler.ast.DartThrowStatement;
-import com.google.dart.compiler.ast.DartTryStatement;
-import com.google.dart.compiler.ast.DartTypeExpression;
-import com.google.dart.compiler.ast.DartTypeNode;
-import com.google.dart.compiler.ast.DartTypeParameter;
-import com.google.dart.compiler.ast.DartUnaryExpression;
-import com.google.dart.compiler.ast.DartUnit;
-import com.google.dart.compiler.ast.DartUnqualifiedInvocation;
-import com.google.dart.compiler.ast.DartVariable;
-import com.google.dart.compiler.ast.DartVariableStatement;
-import com.google.dart.compiler.ast.DartWhileStatement;
-import com.google.dart.compiler.ast.Modifiers;
-import com.google.dart.compiler.backend.js.ScopeRootInfo.DartScope;
-import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
-import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
-import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
-import com.google.dart.compiler.backend.js.ast.JsBlock;
-import com.google.dart.compiler.backend.js.ast.JsBreak;
-import com.google.dart.compiler.backend.js.ast.JsCase;
-import com.google.dart.compiler.backend.js.ast.JsCatch;
-import com.google.dart.compiler.backend.js.ast.JsConditional;
-import com.google.dart.compiler.backend.js.ast.JsContinue;
-import com.google.dart.compiler.backend.js.ast.JsDefault;
-import com.google.dart.compiler.backend.js.ast.JsDoWhile;
-import com.google.dart.compiler.backend.js.ast.JsEmpty;
-import com.google.dart.compiler.backend.js.ast.JsExprStmt;
-import com.google.dart.compiler.backend.js.ast.JsExpression;
-import com.google.dart.compiler.backend.js.ast.JsFor;
-import com.google.dart.compiler.backend.js.ast.JsFunction;
-import com.google.dart.compiler.backend.js.ast.JsIf;
-import com.google.dart.compiler.backend.js.ast.JsInvocation;
-import com.google.dart.compiler.backend.js.ast.JsLabel;
-import com.google.dart.compiler.backend.js.ast.JsLiteral;
-import com.google.dart.compiler.backend.js.ast.JsName;
-import com.google.dart.compiler.backend.js.ast.JsNameRef;
-import com.google.dart.compiler.backend.js.ast.JsNew;
-import com.google.dart.compiler.backend.js.ast.JsNode;
-import com.google.dart.compiler.backend.js.ast.JsNullLiteral;
-import com.google.dart.compiler.backend.js.ast.JsNumberLiteral;
-import com.google.dart.compiler.backend.js.ast.JsObjectLiteral;
-import com.google.dart.compiler.backend.js.ast.JsParameter;
-import com.google.dart.compiler.backend.js.ast.JsPostfixOperation;
-import com.google.dart.compiler.backend.js.ast.JsPrefixOperation;
-import com.google.dart.compiler.backend.js.ast.JsProgram;
-import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
-import com.google.dart.compiler.backend.js.ast.JsReturn;
-import com.google.dart.compiler.backend.js.ast.JsScope;
-import com.google.dart.compiler.backend.js.ast.JsStatement;
-import com.google.dart.compiler.backend.js.ast.JsStringLiteral;
-import com.google.dart.compiler.backend.js.ast.JsSwitch;
-import com.google.dart.compiler.backend.js.ast.JsSwitchMember;
-import com.google.dart.compiler.backend.js.ast.JsThisRef;
-import com.google.dart.compiler.backend.js.ast.JsThrow;
-import com.google.dart.compiler.backend.js.ast.JsTry;
-import com.google.dart.compiler.backend.js.ast.JsUnaryOperator;
-import com.google.dart.compiler.backend.js.ast.JsValueLiteral;
-import com.google.dart.compiler.backend.js.ast.JsVars;
-import com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
-import com.google.dart.compiler.backend.js.ast.JsWhile;
-import com.google.dart.compiler.common.SourceInfo;
-import com.google.dart.compiler.common.Symbol;
-import com.google.dart.compiler.parser.Token;
-import com.google.dart.compiler.resolver.ClassElement;
-import com.google.dart.compiler.resolver.ConstructorElement;
-import com.google.dart.compiler.resolver.CoreTypeProvider;
-import com.google.dart.compiler.resolver.Element;
-import com.google.dart.compiler.resolver.ElementKind;
-import com.google.dart.compiler.resolver.Elements;
-import com.google.dart.compiler.resolver.EnclosingElement;
-import com.google.dart.compiler.resolver.FieldElement;
-import com.google.dart.compiler.resolver.LibraryElement;
-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.Type;
-import com.google.dart.compiler.type.TypeKind;
-import com.google.dart.compiler.type.Types;
-import com.google.dart.compiler.util.AstUtil;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.Stack;
-import java.util.concurrent.Callable;
-
-/**
- * Visitor that generates a Javascript AST from an existing Dart AST.
- */
-public class GenerateJavascriptAST {
- private final DartCompilerContext context;
- private final DartUnit unit;
- private final CoreTypeProvider typeProvider;
-
- /**
- * Generates the Javascript AST using the names created in {@link GenerateNamesAndScopes}.
- */
- static class GenerateJavascriptVisitor
- implements DartPlainVisitor<JsNode>, TraversalContextProvider {
-
- private static boolean isSuperCall(Symbol symbol) {
- return ElementKind.of(symbol).equals(ElementKind.SUPER);
- }
-
- /**
- * 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) {
- Modifiers modifiers = element.getModifiers();
- if (modifiers.isStatic()) {
- // Member was actually declared static
- return true;
- }
-
- if (Elements.isTopLevel(element)) {
- // Top level fields and methods are implicitly static
- ElementKind elementKind = ElementKind.of(element);
- return elementKind == ElementKind.FIELD || elementKind == ElementKind.METHOD;
- }
-
- return false;
- }
-
- /**
- * The name of the javascript function used to intern compile time constants
- */
- private static final String INTERN_CONST_FUNCTION = "$intern";
-
- /**
- * The name of the function used to lookup a id for a constant object
- */
- private static final String DART_CONST_ID_JS_FUNC = "$dart_const_id";
-
- /**
- * The name of the method use to generate the id for an constant object
- */
- private static final String CONST_ID_JS_METHOD_NAME = "$const_id";
-
- private static final String STATIC_UNINITIALIZED = "static$uninitialized";
- private static final String STATIC_INITIALIZING = "static$initializing";
- private static final String ISOLATE_CURRENT = "isolate$current";
- private static final String ISOLATE_INITS = "isolate$inits";
- private static final String ISOLATE_DEFAULT_FACTORY = "default$factory";
- static final int MAX_SPECIALIZED_BIND_SCOPES = 3;
- static final int MAX_SPECIALIZED_BIND_ARGS = 5;
- private static final String ISOLATE_ISOLATE_FACTORY = "isolateFactory";
- private static final String ISOLATE_ISOLATE_FACTORY_GETTER = "getIsolateFactory";
-
- private final JsScope globalScope;
- private final JsBlock globalBlock;
- private final List<JsStatement> staticInit = Lists.newArrayList();
- private final Deque<DartFunction> functionStack = new LinkedList<DartFunction>();
- private final Deque<Set<JsName>> jsNewDeclarationsStack = new LinkedList<Set<JsName>>();
- private Element currentHolder;
- private boolean inFactory = false;
- private boolean inFactoryOrStaticContext = false;
- private ScopeRootInfo currentScopeInfo;
- private JsName traceCounter;
- private final RuntimeTypeInjector rtt;
-
- private final TranslationContext translationContext;
- 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>();
-
- public GenerateJavascriptVisitor(DartUnit unit, DartCompilerContext context,
- TranslationContext translationContext,
- CoreTypeProvider typeProvider) {
- this.context = context;
- this.translationContext = translationContext;
- this.typeProvider = typeProvider;
- this.typeUtils = Types.getInstance(typeProvider);
- this.unitLibrary = unit.getLibrary().getElement();
-
- // Cache the mangler in a field since it is used frequently
- mangler = translationContext.getMangler();
-
- JsProgram program = translationContext.getProgram();
- globalScope = program.getScope();
- globalBlock = program.getGlobalBlock();
- // setup the global scope.
- jsNewDeclarationsStack.push(new HashSet<JsName>());
- currentHolder = unit.getLibrary().getElement();
- rtt = new RuntimeTypeInjector(this, typeProvider, translationContext,
- context.getCompilerConfiguration().developerModeChecks(), mangler, unitLibrary);
- }
-
- /**
- * @param block The global block to add the static init statements too.
- */
- public void addStaticInitsToBlock(JsBlock block) {
- if (staticInit.isEmpty()) return;
- JsFunction init = new JsFunction(globalScope);
- JsBlock body = new JsBlock();
- body.getStatements().addAll(staticInit);
- init.setBody(body);
- staticInit.clear();
-
- // All the static variable initialization code belonging to the
- // current compilation unit is appended to the initialization
- // list (isolate$inits) through Array.prototype.push.
- JsNameRef pushRef = AstUtil.newNameRef(new JsNameRef(ISOLATE_INITS), "push");
- JsInvocation invokePush = AstUtil.newInvocation(pushRef);
- invokePush.getArguments().add(init);
- block.getStatements().add(new JsExprStmt(invokePush));
- }
-
- /**
- * @return the JsScope that is used to create temporary Js-variables in the current scope.
- */
- private JsScope getCurrentFunctionScope() {
- return translationContext.getMethods().get(functionStack.peek()).getScope();
- }
-
- /**
- * Adds the given name to the declarations-array. All names in the array will be declared at
- * the beginning of the function. The same name can be registered multiple times.
- * @param name
- */
- private void registerForDeclaration(JsName name) {
- jsNewDeclarationsStack.peek().add(name);
- }
-
-
- /**
- * Creates a temporary variable but does not register it for var-declaration.
- * @return the JsName of the temporary.
- */
- private JsName createNonVarTempory() {
- JsScope scope;
- if (!functionStack.isEmpty()) {
- scope = getCurrentFunctionScope();
- } else {
- scope = globalScope;
- }
- return scope.declareTemporary();
- }
-
- /**
- * Creates a temporary variable and registers it, so that an declaration statement is
- * emitted.
- * @return the JsName of the temporary.
- */
- @Override
- public JsName createTemporary() {
- JsName temp = createNonVarTempory();
- registerForDeclaration(temp);
- return temp;
- }
-
- /**
- * Returns the JsName for the given element. If the element is global and
- * hasn't been declared yet, it is done now.
- */
- private JsName getJsName(Symbol symbol) {
- return translationContext.getNames().getName(symbol);
- }
-
- /**
- * Creates a JS function with JS calling conventions and a deterministic name
- * so that it can be invoked from JS. The function will then forward
- * the call to the given method (with Dart calling conventions).
- */
- private void generateJsExportedFunction(MethodElement element, JsName name) {
- JsFunction fn = new JsFunction(globalScope);
-
- JsNameRef dartTarget = makeMethodJsReference(element, name);
- JsInvocation callIntoDart = AstUtil.newInvocation(dartTarget);
-
- List<JsParameter> parameters = fn.getParameters();
- List<JsExpression> arguments = callIntoDart.getArguments();
- for (VariableElement p : element.getParameters()) {
- JsName parameter = fn.getScope().declareFreshName(p.getName());
- parameters.add(new JsParameter(parameter));
- arguments.add(parameter.makeRef());
- }
- JsBlock jsBlock = new JsBlock();
- jsBlock.getStatements().add(new JsReturn(callIntoDart));
- jsBlock.setSourceRef(element.getNode());
-
- fn.setBody(jsBlock);
-
- String exportedFunctionName = mangler.mangleNativeMethod(element);
- JsName exportedFunctionJsName = globalScope.declareName(exportedFunctionName,
- exportedFunctionName,
- exportedFunctionName);
- fn.setName(exportedFunctionJsName);
- fn.setSourceRef(element.getNode());
- globalBlock.getStatements().add(fn.makeStmt());
- }
-
- /**
- * Makes the default-constructor accessible under a deterministic name and
- * with JavaScript calling conventions so that it can be invoked from JS.
- */
- private void generateIsolateDefaultFactoryMember(MethodElement element, JsName funcName) {
- JsName className = getJsName(element.getEnclosingElement());
- JsNameRef unmangledName = AstUtil.newNameRef(className.makeRef(), ISOLATE_DEFAULT_FACTORY);
- JsNameRef factoryName = AstUtil.newNameRef(className.makeRef(), funcName);
- JsBinaryOperation defaultAsg = AstUtil.newAssignment(unmangledName, factoryName);
- defaultAsg.setSourceRef(element.getNode());
- globalBlock.getStatements().add(defaultAsg.makeStmt());
- }
-
- @Override
- public JsNode visitClass(DartClass x) {
- assert ElementKind.of(currentHolder).equals(ElementKind.LIBRARY)
- : "Nested classes should be impossible";
- Element previousHolder = currentHolder;
- currentHolder = x.getSymbol();
-
- ClassElement classElement = x.getSymbol();
- JsName classJsName = getJsName(classElement);
-
- // If there is already a native class we must not create the JS function.
- if (classElement.getNativeName() == null) {
- JsFunction jsClass = new JsFunction(globalScope, classJsName).setSourceRef(x);
- jsClass.setIsConstructor(true);
- jsClass.setBody(new JsBlock());
- globalBlock.getStatements().add(jsClass.makeStmt());
- }
-
- if (classElement.isInterface()) {
- rtt.generateRuntimeTypeInfo(x);
-
- // Emit only static final fields for interfaces.
- for (Element member : classElement.getMembers()) {
- if (ElementKind.of(member).equals(ElementKind.FIELD)) {
- Modifiers modifiers = member.getModifiers();
- if (modifiers.isStatic() && !modifiers.isAbstractField()) {
- assert modifiers.isFinal();
- generateField((FieldElement) member);
- }
- }
- }
- } else {
- // Inherits.
- if (x.getSuperSymbol() != null) {
- JsNameRef superRef = getJsName(x.getSuperSymbol()).makeRef();
- JsInvocation inherits = AstUtil.newInvocation(
- new JsNameRef("$inherits"),
- classJsName.makeRef(),
- superRef);
- inherits.setSourceRef(x);
- globalBlock.getStatements().add(inherits.makeStmt());
- }
-
- maybeInjectIsolateMethods(classElement);
- rtt.generateRuntimeTypeInfo(x);
-
- List<Element> classMembers = new ArrayList<Element>();
- classMembers.addAll(classElement.getConstructors());
- for (Element element : classElement.getMembers()) {
- classMembers.add(element);
- }
-
- if (Elements.needsImplicitDefaultConstructor(classElement)) {
- addImplicitDefaultConstructor(x, classElement, classMembers);
- }
-
- for (Element member : classMembers) {
- switch(ElementKind.of(member)) {
- case METHOD: {
- MethodElement methodElement = (MethodElement) member;
- generateMethodDefinition(methodElement);
- if (!methodElement.getModifiers().isOperator()) {
- generateMethodGetter(methodElement);
- }
- break;
- }
-
- case CONSTRUCTOR:
- generateMethodDefinition((MethodElement) member);
- break;
-
- case FIELD:
- generateField((FieldElement) member);
- break;
-
- default:
- throw new AssertionError("Invalid member " + member);
- }
- }
-
- // TODO(johnlenz): should we create a stub method to catch
- // class without const constructors? As is, the a non-const
- // class with get the id of an "const Object".
- if (hasConstConstructor(classElement)) {
- makeConstIdMethod(classElement);
- }
-
- // Add temporary variable declarations, if any.
- // TODO(johnlenz): This isn't always correct: an incremental compile
- // might reuse temps, which we don't want. However, static initializations
- // (where the temps would be used) aren't quite right either yet. Double
- // check this when its done.
- Set<JsName> temps = jsNewDeclarationsStack.peek();
- declareTempsInBlock(globalBlock, temps);
-
- // Clear the set for the next class.
- temps.clear();
- }
-
- assert currentHolder == x.getSymbol() : "Unbalanced class visitation";
- currentHolder = previousHolder;
-
- return null;
- }
-
- private void addImplicitDefaultConstructor(DartClass x, ClassElement classElement,
- List<Element> classElementMembers) {
- for (DartNode member : x.getMembers()) {
- if (member instanceof DartMethodDefinition) {
- DartMethodDefinition method = (DartMethodDefinition) member;
- MethodElement symbol = method.getSymbol();
- if (symbol.isConstructor() && "".equals(symbol.getName())) {
- classElementMembers.add(symbol);
- }
- }
- }
- }
-
- /**
- * @param classElement
- *
- */
- private void maybeInjectIsolateMethods(ClassElement classElement) {
- if (isIsolateClass(classElement)) {
- // In order to construct an isolate in another worker, it must be
- // referrable by name, this requires a top level method.
- generateIsolateFactory(classElement);
-
- // ... and a way to get the factory from a isolate instance
- generateIsolateFactoryGetter(classElement);
- }
- }
-
- private JsName getIsolateFactoryFunctionName(ClassElement classElement) {
- String fnNameStr = getJsName(classElement).getShortIdent() + "$" + ISOLATE_ISOLATE_FACTORY;
- return globalScope.declareName(fnNameStr);
- }
-
- private JsNameRef getIsolateFactoryGetterName(ClassElement classElement) {
- return AstUtil.newNameRef(
- AstUtil.newPrototypeNameRef(getJsName(classElement).makeRef()),
- ISOLATE_ISOLATE_FACTORY_GETTER);
- }
-
- private void generateIsolateFactory(ClassElement classElement) {
- // Create static factory function:
- // function Foo$IsolateFactory() {
- // return Foo.default$Factory();
- // }
-
- // Build the function
- JsName fnName = getIsolateFactoryFunctionName(classElement);
-
- JsNameRef defaultFactory = AstUtil.newNameRef(
- getJsName(classElement).makeRef(), ISOLATE_DEFAULT_FACTORY);
- JsInvocation invokeFactory = AstUtil.newInvocation(defaultFactory);
-
- // TODO(johnlenz): Add runtime type information if necessary.
- JsFunction factoryFn = AstUtil.newFunction(globalScope, fnName, null,
- new JsReturn(invokeFactory));
-
- globalBlock.getStatements().add(factoryFn.makeStmt());
- }
-
- private void generateIsolateFactoryGetter(ClassElement classElement) {
- // Create static factory function:
- // function Foo.prototype.getFactory() {
- // return Foo$IsolateFactory;
- // }
-
- // Build the function
- JsName fnName = getIsolateFactoryFunctionName(classElement);
- JsFunction getterFn = AstUtil.newFunction(globalScope, null, null,
- new JsReturn(fnName.makeRef()));
-
- // Declare it.
- JsExpression declStmt = AstUtil.newAssignment(
- getIsolateFactoryGetterName(classElement), getterFn);
- globalBlock.getStatements().add(declStmt.makeStmt());
- }
-
- private boolean isIsolateClass(ClassElement classElement) {
- return false;
- }
-
- /**
- * 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) {
- // Generate a getter for method binding to a variable
- 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);
- JsNameRef mangledRttMethod = rtt.getRTTLookupMethodNameRef(methodElement);
- JsFunction func = new JsFunction(globalScope);
- JsNameRef getterJsNameRef;
- 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);
- stmts.add(AstUtil.newVar(null, returnVar,
- AstUtil.newNameRef(classJsNameRef, mangledMethodName)));
- JsBinaryOperation varLookup = AstUtil.newAssignment(
- AstUtil.newNameRef(returnVar.makeRef(), "$lookupRTT"),
- mangledRttMethod);
- stmts.add(varLookup.makeStmt());
- stmts.add(new JsReturn(returnVar.makeRef()));
- } else {
- // function() { return $bind(<class>.prototype.<member>$named,
- // $bind(<class>.prototype.<member>$named_$lookupRTT, this); }
- JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(classJsNameRef);
- getterJsNameRef = AstUtil.newNameRef(prototypeRef, getterJsName);
- JsExpression bindMethodCall = AstUtil.newInvocation(new JsNameRef("$bind"),
- AstUtil.newNameRef(prototypeRef, mangledMethodName),
- mangledRttMethod, new JsThisRef());
- func.setBody(AstUtil.newBlock(new JsReturn(bindMethodCall)));
- }
-
- 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());
- }
-
- private boolean hasConstConstructor(ClassElement element) {
- for (ConstructorElement ctr : element.getConstructors()) {
- if (ctr.getModifiers().isConstant()) {
- return true;
- }
- }
- return false;
- }
-
- private void makeConstIdMethod(ClassElement classElement) {
- JsNameRef methodRef = makeConstIdMethodRef(classElement);
- JsStatement decl = AstUtil.newAssignment(methodRef,
- makeConstIdMethodFunction(classElement)).makeStmt();
- this.globalBlock.getStatements().add(decl);
- }
-
- private JsNameRef makeConstIdMethodRef(ClassElement classElement) {
- // Instance methods hang from the prototype.
- JsNameRef qualifier = AstUtil.newPrototypeNameRef(getJsName(classElement).makeRef());
- JsNameRef methodRef = AstUtil.newNameRef(qualifier, CONST_ID_JS_METHOD_NAME);
- return methodRef;
- }
-
- private JsFunction makeConstIdMethodFunction(ClassElement classElement) {
- JsFunction func = new JsFunction(this.globalScope);
- // Make an id like:
- // <type>:field1:field2:...-<supertype>:field1:field2:...
- JsExpression idExpr = rtt.getRTTClassId(classElement);
- for (Element member : classElement.getMembers()) {
- if (member.getKind() == ElementKind.FIELD) {
- if (!member.getModifiers().isStatic()) {
- idExpr = addConstIdFieldExpr((FieldElement) member, idExpr);
- }
- }
- }
- idExpr = addConstIdSuperExpr(classElement, idExpr);
- func.setBody(AstUtil.newBlock(new JsReturn(idExpr)));
- return func;
- }
-
- private JsExpression addConstIdFieldExpr(
- FieldElement element, JsExpression prevPart) {
- JsExpression qualifier = getGetterSetterQualifier(element);
- JsName fieldJsName = getJsName(element);
- JsNameRef ref = AstUtil.newNameRef(qualifier, fieldJsName);
-
- // example: prevPart + ":" + $const_id(this.field)
- return add(prevPart, add(string(":"),
- AstUtil.newInvocation(new JsNameRef(DART_CONST_ID_JS_FUNC), ref)));
- }
-
- private JsExpression addConstIdSuperExpr(ClassElement element, JsExpression prevPart) {
- InterfaceType superType = element.getSupertype();
- if (superType == null || superType.getElement().isObject()) {
- // The root object doesn't add anything.
- return prevPart;
- }
- JsNameRef superConstIdRef = makeConstIdMethodRef(superType.getElement());
- JsNameRef callRef = AstUtil.newNameRef(superConstIdRef, "call");
- JsInvocation superCall = AstUtil.newInvocation(callRef, new JsThisRef());
-
- // example: prevPart + "-" + super.prototype.$const_id.call(this);
- return add(prevPart, add(string("-"), superCall));
- }
-
- private JsExpression add(JsExpression first, JsExpression second) {
- return new JsBinaryOperation(JsBinaryOperator.ADD, first, second);
- }
-
- private void generateField(FieldElement element) {
- generate(element.getNode());
- }
-
- private void generateMethodDefinition(MethodElement element) {
- JsFunction func = (JsFunction) generate(element.getNode());
-
- // makeMethod clears the name of the function.
- JsName funcName = func.getName();
- makeMethod(element, func);
-
- // If the method is the default factory we add the same method under an unmangled name.
- // This is necessary for the isolate code.
- if (Elements.isNonFactoryConstructor(element)
- && "".equals(element.getName())
- && func.getParameters().size() == 0
- && isIsolateClass((ClassElement)element.getEnclosingElement())) {
- generateIsolateDefaultFactoryMember(element, funcName);
- }
-
- // If the function is exported to JavaScript make it accessible under its
- // (more or less) unmangled name.
- DartMethodDefinition method = (DartMethodDefinition) element.getNode();
- DartBlock body = method.getFunction().getBody();
- if (element.getModifiers().isNative() && !(body instanceof DartNativeBlock)) {
- generateJsExportedFunction(element, funcName);
- }
- }
-
- private void createInlinedClassConstructor(DartClass x) {
- ClassElement classElement = x.getSymbol();
- assert classElement.getNativeName() == null;
- JsName classJsName = getJsName(classElement);
- JsFunction jsClass = new JsFunction(globalScope, classJsName).setSourceRef(x);
- jsClass.setIsConstructor(true);
- JsBlock block = new JsBlock();
- JsScope scope = new JsScope(globalScope, "temp");
- for (FieldElement fieldElement : getFieldsInClassHierarchy(classElement)) {
- String fieldName = translationContext.getMangler().mangleField(fieldElement, unitLibrary);
- JsNameRef fieldRef = AstUtil.newNameRef(new JsThisRef(), fieldName);
- JsName paramName = scope.declareName("p$" + fieldName);
- jsClass.getParameters().add(new JsParameter(paramName));
- JsBinaryOperation asg = AstUtil.newAssignment(fieldRef, new JsNameRef(paramName));
- block.getStatements().add(asg.makeStmt());
- }
- jsClass.setBody(block);
- globalBlock.getStatements().add(jsClass.makeStmt());
- }
-
- private List<FieldElement> getFieldsInClassHierarchy(ClassElement classElement) {
- InterfaceType current = classElement.getType();
- Stack<ClassElement> classes = new Stack<ClassElement>();
- while ((current != null) && !current.getElement().isObject()) {
- classElement = current.getElement();
- classes.push(classElement);
- current = classElement.getSupertype();
- }
- List<FieldElement> fields = Lists.newArrayList();
- while (!classes.isEmpty()) {
- classElement = classes.pop();
- for (Element elem : classElement.getMembers()) {
- Modifiers modifiers = elem.getModifiers();
- if (ElementKind.of(elem).equals(ElementKind.FIELD) && !modifiers.isStatic()
- && !modifiers.isAbstractField()) {
- fields.add((FieldElement) elem);
- }
- }
- }
- return fields;
- }
-
- private void generateAbstractField(FieldElement fieldElement) {
- if (fieldElement.getGetter() != null) {
- generateMethodDefinition(fieldElement.getGetter());
- }
- if (fieldElement.getSetter() != null) {
- generateMethodDefinition(fieldElement.getSetter());
- }
- }
-
- private void declareTempsInBlock(JsBlock block, Collection<JsName> tempCollection) {
- // Add temporary variable declarations, if any.
- Iterator<JsName> temps = tempCollection.iterator();
- if (temps.hasNext()) {
- JsVars jsVars = new JsVars();
- while (temps.hasNext()) {
- JsName name = temps.next();
- JsVars.JsVar jsVar = new JsVars.JsVar(name);
- jsVars.insert(jsVar);
- }
- block.getStatements().add(0, jsVars);
- }
- }
-
- private List<DartField> getInlineFieldInitializers(ConstructorElement element) {
- List<DartField> fieldInitializers = new ArrayList<DartField>();
- Iterable<Element> classMembers = element.getEnclosingElement().getMembers();
- for (Element member : classMembers) {
- Modifiers modifiers = member.getModifiers();
- if (!modifiers.isStatic()
- && !modifiers.isAbstractField()
- && ElementKind.of(member).equals(ElementKind.FIELD)) {
- DartField field = (DartField) member.getNode();
- if (field.getValue() != null) {
- fieldInitializers.add(field);
- }
- }
- }
- return fieldInitializers;
- }
-
- private JsExpression generateInlineFieldInitializer(DartField field) {
- JsNameRef fieldName = AstUtil.newNameRef(new JsThisRef(), getJsName(field.getSymbol()));
- JsExpression initExpr = (JsExpression) generate(field.getValue());
- return AstUtil.newAssignment(fieldName, initExpr);
- }
-
- /**
- * For a constructor B whose super is A we generate:
- *
- * FactoryB() {
- * var tmp = new B;
- * InitB(tmp);
- * BodyB(tmp);
- * }
- *
- * BodyB() {
- * BodyA();
- * }
- *
- * InitB() {
- * InitA();
- * }
- *
- * This method creates the InitB method and adds the BodyA call in the BodyB method.
- */
- private void addInitializers(DartMethodDefinition constructor,
- JsFunction factory,
- JsName tempVar) {
- ConstructorElement element = (ConstructorElement) constructor.getSymbol();
- JsScope classMemberScope = translationContext.getMemberScopes().get(element.getEnclosingElement());
- JsName curClassJsName = getJsName(element.getEnclosingElement());
-
- // Create the initializer function.
- String constructorName = element.getName();
- String initName = mangler.createInitializerSyntax(constructorName, unitLibrary);
- JsName initJsName = classMemberScope.declareName(initName, initName, constructorName);
- // Initializers are called from other class (as part of the super initialization).
- initJsName.setObfuscatable(false);
- JsFunction initFunction = new JsFunction(globalScope, initJsName).setSourceRef(constructor);
- initFunction.setBody(new JsBlock());
-
- // Add the initializer as a member of the current class.
- makeMethod(element, initFunction);
-
- // Add the parameters to the initializer function.
- List<DartParameter> params = constructor.getFunction().getParams();
- for (DartParameter p : params) {
- initFunction.getParameters().add(
- new JsParameter(getJsName(p.getNormalizedNode().getSymbol())));
- }
-
- // If there are initializers, or inline field initializers, populate the
- // initializer function.
- List<DartInitializer> initializers = constructor.getInitializers();
- List<DartField> fieldInitializers = getInlineFieldInitializers(element);
-
- if (!initializers.isEmpty() || !fieldInitializers.isEmpty()) {
- // TODO(johnlenz): move this block shares the some of the same setup
- // and tear down as the visitFunction method.
-
- // Give the initializer expressions access to the function parameters
- functionStack.push(constructor.getFunction());
- jsNewDeclarationsStack.push(new HashSet<JsName>());
-
- // Do the field inline initializers first. If there are any assignments in the initializer
- // list, they will be the last assignments.
- List<JsStatement> jsInitializers = initFunction.getBody().getStatements();
- Iterator<DartField> fieldIterator = fieldInitializers.iterator();
- while (fieldIterator.hasNext()) {
- jsInitializers.add(generateInlineFieldInitializer(fieldIterator.next()).makeStmt());
- }
-
- DartInvocation initInvocation = null;
- Iterator<DartInitializer> iterator = initializers.iterator();
- while (iterator.hasNext()) {
- DartInitializer initializer = iterator.next();
- if (!initializer.isInvocation()) {
- jsInitializers.add((JsStatement) generate(initializer));
- } else {
- initInvocation = (DartInvocation) initializer.getValue();
- }
- }
-
- JsInvocation constructorInvocation = maybeGenerateSuperOrRedirectCall(constructor);
- if (constructorInvocation != null) {
- // Call the super initializer function in the initializer.
- // Compute the super constructor initializer to call.
- ConstructorElement superElement = (ConstructorElement) initInvocation.getSymbol();
- // TODO(floitsch): it would be better if we had a js-name and not just a string.
- // This way the debugging information would be better.
- // We need to generate the JsName (for the initializer/factory) once only and store it
- // in some hashtable. Then instead of reusing the mangler, we should reuse those JsNames.
- // The debugging information would then contain a link from the property-access to the
- // constructor. Without JsName the debugger just assumes we access some random property.
- String mangledSuperConstructorName =
- mangler.createInitializerSyntax(superElement.getName(), unitLibrary);
- Element superClassElement = superElement.getEnclosingElement();
- JsNameRef superInitRef = AstUtil.newNameRef(getJsName(superClassElement).makeRef(),
- mangledSuperConstructorName);
- JsNameRef callRef = AstUtil.newNameRef(superInitRef, "call");
- JsInvocation superInitCall = AstUtil.newInvocation(callRef);
- initFunction.getBody().getStatements().add(0, superInitCall.makeStmt());
- // TODO(floitsch): don't copy the arguments from the super call for the initializer call.
- // This will evaluate side-effects twice, and we are reusing nodes (thereby creating a
- // DAG instead of a tree).
- superInitCall.getArguments().addAll(constructorInvocation.getArguments());
- }
-
- // Call the initializer in the factory. This must be executed
- // before calling the super constructor: <class>.<name>$Initializer.call(this, ...)
- JsNameRef initRef = AstUtil.newNameRef(curClassJsName.makeRef(), initJsName);
- JsNameRef initCallRef = AstUtil.newNameRef(initRef, "call");
- JsInvocation initCall = AstUtil.newInvocation(initCallRef, tempVar.makeRef());
- for (DartParameter p : params) {
- initCall.getArguments().add(getJsName(p.getNormalizedNode().getSymbol()).makeRef());
- }
-
- factory.getBody().getStatements().add(0, initCall.makeStmt());
-
- // Dart does not have an implicit call to a super constructor.
-
- // Add temporary variable declarations, if any.
- declareTempsInBlock(initFunction.getBody(), jsNewDeclarationsStack.pop());
-
- // setup the scope alias for the init function
- maybeAddFunctionScopeAlias(
- currentScopeInfo.getScope(constructor.getFunction()), initFunction);
-
- // Remove the containing function scope.
- functionStack.pop();
- }
- }
-
- private void addSuperOrRedirectConstructorCall(DartMethodDefinition constructor) {
- JsInvocation superCall = maybeGenerateSuperOrRedirectCall(constructor);
- if (superCall != null) {
- // If we have a super constructor call, add it as the first statement
- // in the constructor body.
- // <super-class>.<name>$Constructor.call(this, ...).
- JsFunction constructorFunction = translationContext.getMethods().get(constructor.getFunction());
- constructorFunction.getBody().getStatements().add(0, superCall.makeStmt());
- }
- }
-
- private JsInvocation maybeGenerateSuperOrRedirectCall(DartMethodDefinition constructor) {
- // If there are initializers, populate the initializer function.
- List<DartInitializer> initializers = constructor.getInitializers();
- if (!initializers.isEmpty()) {
- for (DartInitializer init : initializers) {
- if (init.isInvocation()) {
- JsExprStmt statement = (JsExprStmt) generate(init);
- if (statement != null) {
- return (JsInvocation) statement.getExpression();
- }
- }
- }
- }
- return null;
- }
-
- private JsNode generateConstructorDefinition(DartMethodDefinition x) {
- assert currentScopeInfo == null : "Nesting a constructor in a method should be impossible";
- currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
- ConstructorElement element = (ConstructorElement) x.getSymbol();
- ClassElement classElement = (ClassElement) element.getEnclosingElement();
- JsScope classMemberScope = translationContext.getMemberScopes().get(classElement);
- String constructorName = element.getName();
- JsName curClassJsName = getJsName(classElement);
-
- JsFunction dartCtor = (JsFunction) generate(x.getFunction());
-
- // Add the constructor as a member of the current class.
- makeMethod(element, dartCtor);
-
- // Create the static factory function that allocates the object
- // and calls the constructor.
- // <class>.ConstructorName$Factory = function (args ...) {
- // var tmp = new <class>();
- // tmp.$typeInfo = runtimeType;
- // <class>.ConstructorName$Constructor.call(tmp, args ...);
- // return tmp;
- // }
- // Attaching the factory to <class> is done outside this method. We just provide the
- // factory-name ("ConstructorName$Factory" here).
-
- // The factory becomes a member of <class> and should therefore be declared in the same
- // scope as all other members.
- String className = element.getConstructorType().getName();
- String factoryName = mangler.createFactorySyntax(className, constructorName, unitLibrary);
- JsName factoryJsName =
- classMemberScope.declareName(factoryName, factoryName, constructorName);
- // Factories are globally accessible.
- factoryJsName.setObfuscatable(false);
-
- JsFunction factoryFunction = new JsFunction(globalScope, factoryJsName).setSourceRef(x);
- JsScope factoryScope = factoryFunction.getScope();
-
- // We do the constructor invocation before we declare the temporary variable. This is
- // necessary to ensure that the created temporary does not conflict with the parameters.
- JsInvocation constructorInvocation = new JsInvocation();
- JsName constructorJsName = getJsName(element);
- JsNameRef constructorRef = AstUtil.newNameRef(curClassJsName.makeRef(), constructorJsName);
- constructorInvocation.setQualifier(AstUtil.newNameRef(constructorRef, "call"));
-
- // Add the arguments to the constructor invocation. Note that the constructor call is still
- // missing the 'tmp' variable. We will add it later.
- List<DartParameter> params = x.getFunction().getParams();
- List<JsName> jsArgNames = new ArrayList<JsName>();
- for (DartParameter p : params) {
- // TODO(ngeoffray): We should actually copy the arguments. See b/4424659.
- JsName argName = getJsName(p.getNormalizedNode().getSymbol());
- jsArgNames.add(argName);
- constructorInvocation.getArguments().add(argName.makeRef());
- }
-
- JsName tempVar = factoryScope.declareTemporary();
- // Add the 'tmp' var to the constructor call.
- constructorInvocation.getArguments().add(0, tempVar.makeRef());
-
- factoryFunction.setBody(AstUtil.newBlock(
- constructorInvocation.makeStmt(),
- new JsReturn(tempVar.makeRef())));
-
- addInitializers(x, factoryFunction, tempVar);
- rtt.maybeAddClassRuntimeTypeToConstructor(classElement, factoryFunction, tempVar.makeRef());
- JsNew jsNew = new JsNew(curClassJsName.makeRef());
- if (classElement.getNativeName() != null && x.getFunction().getBody() == null) {
- /*
- * For native classes with bodyless constructors, we pass the user-declared arguments of
- * the factory method to the native "new" expression.
- */
- List<JsExpression> newArguments = jsNew.getArguments();
- for (JsName jsArgName : jsArgNames) {
- newArguments.add(jsArgName.makeRef());
- }
- }
- factoryFunction.getBody().getStatements().add(0, AstUtil.newVar(x, tempVar, jsNew));
-
- generateAll(x.getFunction().getParams(), factoryFunction.getParameters(), JsParameter.class);
-
- assert currentScopeInfo != null;
- inFactory = false;
- inFactoryOrStaticContext = false;
- currentScopeInfo = null;
-
- return factoryFunction;
- }
-
- private void generateInitializersInlined(DartMethodDefinition x, JsFunction factoryFunction,
- JsScope factoryScope, JsName tempVar) {
- ConstructorElement element = (ConstructorElement) x.getSymbol();
- JsName curClassJsName = getJsName(element.getEnclosingElement());
- Map<FieldElement, JsExpression> initMap = new HashMap<FieldElement, JsExpression>();
- JsExpression superInvocation = null;
- for (DartInitializer init : x.getInitializers()) {
- JsExpression initValue = (JsExpression) generate(init.getValue());
- if (init.isInvocation()) {
- superInvocation = initValue;
- continue;
- } else {
- assert ElementKind.of(init.getName().getTargetSymbol()).equals(ElementKind.FIELD);
- FieldElement fieldElement = (FieldElement) init.getName().getTargetSymbol();
- initMap.put(fieldElement, initValue);
- }
- }
- List<JsStatement> stmts = Lists.newArrayList();
- JsNew jsNew = new JsNew(curClassJsName.makeRef());
- ClassElement classElement = (ClassElement) element.getEnclosingElement();
- for (FieldElement fieldElement : getFieldsInClassHierarchy(classElement)) {
- String fieldName = translationContext.getMangler().mangleField(fieldElement, unitLibrary);
- JsName tmp = factoryScope.declareName("init$" + fieldName);
- JsExpression initValue = initMap.get(fieldElement);
- if (initValue == null) {
- DartField fieldNode = (DartField) fieldElement.getNode();
- if (fieldNode.getValue() != null) {
- initValue = (JsExpression) generate(fieldNode.getValue());
- } else {
- initValue = undefined();
- }
- }
- stmts.add(AstUtil.newVar(x, tmp, initValue));
- jsNew.getArguments().add(new JsNameRef(tmp));
- }
- if (superInvocation != null) {
- factoryFunction.getBody().getStatements().add(0, new JsExprStmt(superInvocation));
- }
- stmts.add(AstUtil.newVar(x, tempVar, jsNew));
- factoryFunction.getBody().getStatements().addAll(0, stmts);
- }
-
- @Override
- public JsNode visitMethodDefinition(DartMethodDefinition x) {
- assert x == x.getNormalizedNode();
- if (Elements.isNonFactoryConstructor(x.getSymbol())) {
- return generateConstructorDefinition(x);
- }
-
- assert currentScopeInfo == null : "Nested methods should be impossible";
- inFactory = x.getModifiers().isFactory();
- inFactoryOrStaticContext = isFactoryOrStaticContext(x.getModifiers());
- currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
-
- JsFunction func = (JsFunction) generate(x.getFunction());
- assert currentScopeInfo != null;
- inFactory = false;
- inFactoryOrStaticContext = false;
- currentScopeInfo = null;
-
- if (Elements.isTopLevel(x.getSymbol())) {
- JsFunction tramp = generateNamedParameterMethodTrampoline(x, func.getName().makeRef());
- String mangled = mangler.mangleNamedMethod(x.getSymbol(), unitLibrary);
- JsName trampName = globalScope.declareName(mangled);
- tramp.setName(trampName);
-
- globalBlock.getStatements().add(func.makeStmt());
- globalBlock.getStatements().add(tramp.makeStmt());
-
- // special case for top level methods
- rtt.generateRuntimeTypeInfo(x);
- generateMethodGetter(x.getSymbol());
- }
-
- return func;
- }
-
- private JsFunction generateNamedParameterMethodTrampoline(DartMethodDefinition method,
- JsNameRef origJsName) {
- boolean preserveThis = !(method.getModifiers().isStatic() ||
- method.getModifiers().isFactory() ||
- Elements.isTopLevel(method.getSymbol()));
-
- return generateNamedParameterTrampoline(method.getFunction(), origJsName, 0, preserveThis);
- }
-
- private JsFunction generateNamedParameterTrampoline(DartFunction func,
- JsNameRef origJsName, int numClosureScopes, boolean preserveThis) {
- // function([$s0, $s1, ...], $n, $o, P0, P1, P2, P3, ...) {
- JsFunction tramp = new JsFunction(globalScope);
- JsScope scope = tramp.getScope();
-
- // Create fresh parameters for the explicit and synthetic parameters.
- boolean hasNamedParams = false;
- List<JsParameter> explicitJsParams = new ArrayList<JsParameter>();
- for (DartParameter dartParam : func.getParams()) {
- String paramName = ((DartIdentifier) dartParam.getName()).getTargetName();
- JsParameter param = new JsParameter(scope.declareName(paramName));
- explicitJsParams.add(param);
- if (dartParam.getModifiers().isNamed()) {
- hasNamedParams = true;
- }
- }
-
- List<JsParameter> closureScopeParams = new ArrayList<JsParameter>();
- for (int i = 0; i < numClosureScopes; ++i) {
- JsParameter param = new JsParameter(scope.declareFreshName("$s" + i));
- closureScopeParams.add(param);
- }
- JsParameter countParam = new JsParameter(scope.declareFreshName("$n"));
- JsParameter namedParam = new JsParameter(scope.declareFreshName("$o"));
-
- // Declare parameters in the proper order.
- for (int i = 0; i < numClosureScopes; ++i) {
- tramp.getParameters().add(closureScopeParams.get(i));
- }
- tramp.getParameters().add(countParam);
- tramp.getParameters().add(namedParam);
- for (int i = 0; i < explicitJsParams.size(); ++i) {
- tramp.getParameters().add(explicitJsParams.get(i));
- }
-
- JsBlock body = new JsBlock();
- tramp.setBody(body);
- List<JsStatement> stmts = body.getStatements();
-
- if (hasNamedParams) {
- // var seen = 0, def = 0;
- JsName seen = scope.declareFreshName("seen");
- JsName def = scope.declareFreshName("def");
- stmts.add(AstUtil.newVar(null, seen, number(0)));
- stmts.add(AstUtil.newVar(null, def, number(0)));
-
- // switch ($n) {
- // case 1: P0 = $o.P0 ? (++seen, $o.P0) : null; // no default value
- // case 2: P1 = $o.P1 ? (++seen, $o.P1) : (++def, DEFAULT); // explicit default value
- // ...
- // }
- JsSwitch jsSwitch = new JsSwitch();
- jsSwitch.setExpr(countParam.getName().makeRef());
- for (int i = 0; i < func.getParams().size(); ++i) {
- DartParameter param = func.getParams().get(i);
- JsParameter jsParam = tramp.getParameters().get(i + 2 + numClosureScopes);
- if (!param.getModifiers().isNamed()) {
- continue;
- }
-
- String paramNameStr = getPropNameForNamedParameter(jsParam);
- JsExpression paramName = string(getPropNameForNamedParameter(jsParam));
- JsExpression ifExpr = AstUtil.in(null, paramName, namedParam.getName().makeRef());
-
- JsExpression ppSeen = AstUtil.preinc(null, seen.makeRef());
- JsBinaryOperation thenExpr = AstUtil.comma(null, ppSeen,
- AstUtil.newNameRef(namedParam.getName().makeRef(), paramNameStr));
-
- DartExpression defaultValue = param.getDefaultExpr();
- JsExpression elseExpr = (defaultValue != null)
- ? generateDefaultValue(defaultValue)
- : undefined();
- JsExpression ppDef = AstUtil.preinc(null, def.makeRef());
- elseExpr = AstUtil.comma(null, ppDef, elseExpr);
-
- JsBinaryOperation asg = assign(
- jsParam.getName().makeRef(),
- new JsConditional(ifExpr, thenExpr, elseExpr));
-
- jsSwitch.getCases().add(AstUtil.newCase(number(i), asg.makeStmt()));
- }
- if (jsSwitch.getCases().size() > 0) {
- stmts.add(jsSwitch);
- }
-
- // if ((seen != $o.count) || (seen + def + $n != TOTAL)) {
- // $nsme();
- // }
- {
- JsBinaryOperation ifLeft = neq(seen.makeRef(),
- AstUtil.newNameRef(namedParam.getName().makeRef(), "count"));
-
- JsExpression add1 = add(seen.makeRef(), def.makeRef());
- JsExpression add2 = add(add1, countParam.getName().makeRef());
- JsExpression ifRight = neq(add2, number(func.getParams().size()));
-
- JsExpression ifExpr = or(ifLeft, ifRight);
- JsStatement thenStmt = AstUtil.newInvocation(new JsNameRef("$nsme")).makeStmt();
-
- stmts.add(new JsIf(ifExpr, thenStmt, null));
- }
- } else {
- // if ($o.count || ($n != TOTAL)) {
- // $nsme();
- // }
- {
- JsExpression ifExpr =
- or(AstUtil.newNameRef(namedParam.getName().makeRef(), "count"),
- neq(countParam.getName().makeRef(), number(func.getParams().size())));
- JsStatement thenStmt = AstUtil.newInvocation(new JsNameRef("$nsme")).makeStmt();
-
- stmts.add(new JsIf(ifExpr, thenStmt, null));
- }
- }
- JsInvocation jsInvoke = AstUtil.newInvocation(
- AstUtil.newNameRef(origJsName.getQualifier(), origJsName.getName()));
- if (preserveThis) {
- JsNameRef call = AstUtil.newNameRef(jsInvoke.getQualifier(), "call");
- jsInvoke = AstUtil.newInvocation(call, new JsThisRef());
- }
- for (int i = 0; i < numClosureScopes; ++i) {
- jsInvoke.getArguments().add(closureScopeParams.get(i).getName().makeRef());
- }
- for (JsParameter jsParam : explicitJsParams) {
- jsInvoke.getArguments().add(jsParam.getName().makeRef());
- }
- stmts.add(new JsReturn(jsInvoke));
-
- return tramp;
- }
-
- private String mangleNamedParameterName(String name) {
- return "$p_" + name;
- }
-
- private String getPropNameForNamedParameter(JsParameter jsParam) {
- return mangleNamedParameterName(jsParam.getName().getShortIdent());
- }
-
- private String getPropNameForNamedParameter(DartNamedExpression namedExpr) {
- return mangleNamedParameterName(namedExpr.getName().getTargetName());
- }
-
- /**
- * If necessary, add object holding aliases for any parameters
- * captured by function closures.
- */
- private void maybeAddFunctionScopeAlias(DartScope scope, JsFunction function) {
- if (scope.definesClosureReferencedSymbols()) {
- JsScope jsScope = function.getScope();
- JsBlock body = function.getBody();
-
- // Example:
- // function f(a,b) { ... }
- // to:
- // function f(a,b) {var s0={f:f,a:a,b:b} ... };
- JsObjectLiteral aliasInit = new JsObjectLiteral();
- for (Entry<Symbol, DartScope.DartSymbolInfo> entry : scope.getSymbols().entrySet()) {
- if (entry.getValue().isReferencedFromClosure()) {
- JsName param = getJsName(entry.getKey());
- aliasInit.getPropertyInitializers().add(
- new JsPropertyInitializer(string(param.getIdent()), new JsNameRef(param)));
- }
- }
-
- JsName aliasName = scope.getAliasForJsScope(jsScope);
- // Scope objects are declared (in the JsScope) at first use. By construction scope-objects
- // are only created when they are used. Therefore the scope-object must exist in the
- // JsScope.
- assert aliasName != null;
- JsStatement aliasDecl = AstUtil.newVar(null, aliasName, aliasInit);
- body.getStatements().add(0, aliasDecl);
- }
- }
-
- private JsName getTraceCounter() {
- if (traceCounter == null) {
- traceCounter = globalScope.declareTemporary();
- JsStatement counterDecl = AstUtil.newVar(null, traceCounter, number(0));
- globalBlock.getStatements().add(0, counterDecl);
- }
- return traceCounter;
- }
-
- private JsNameRef makeMethodJsReference(Element element, JsName name) {
- JsNameRef qualifier;
- boolean isNonFactoryConstructor = Elements.isNonFactoryConstructor(element);
- Modifiers modifiers = element.getModifiers();
- JsNameRef classJsName = getJsName(element.getEnclosingElement()).makeRef();
- if (modifiers.isStatic() || modifiers.isFactory() || isNonFactoryConstructor) {
- // Static methods hang directly from the constructor.
- qualifier = classJsName;
- } else {
- // Instance methods hang from the prototype.
- qualifier = AstUtil.newPrototypeNameRef(classJsName);
- }
-
- JsNameRef prop = AstUtil.newNameRef(qualifier, name);
- // TODO(johnlenz): This should be the name node reference
- prop.setSourceRef(element.getNode());
- 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.
- */
- private void makeMethod(Element element, JsFunction func) {
- if (element.getEnclosingElement().getKind().equals(ElementKind.CLASS)) {
- JsNameRef prop = makeMethodJsReference(element, func.getName());
- func.setName(null);
- JsBinaryOperation asg = AstUtil.newAssignment(prop, func);
-
- // TODO(johnlenz): This should be the stmt node reference
- asg.setSourceRef(element.getNode());
- 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()) {
- // 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);
- JsNameRef namedProp = makeMethodJsReference(element, namedName);
-
- DartMethodDefinition method = (DartMethodDefinition) element.getNode();
- JsFunction tramp = generateNamedParameterMethodTrampoline(method, prop);
-
- 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";
- inFactory = element.getModifiers().isFactory();
- inFactoryOrStaticContext = isFactoryOrStaticContext(element.getModifiers());
- DartMethodDefinition x = (DartMethodDefinition) element.getNode();
- currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
- rtt.generateRuntimeTypeInfo(x);
-
- assert currentScopeInfo != null;
- inFactory = false;
- inFactoryOrStaticContext = false;
- currentScopeInfo = null;
- }
- } else {
- globalBlock.getStatements().add(func.makeStmt());
- }
- }
-
- private JsExpression getGetterSetterQualifier(Element element) {
- if (isDeclaredAsStaticOrImplicitlyStatic(element)) {
- // The mangler makes sure that the mangled version of static
- // fields names encode the class name so we do not have to
- // read the fields through the class function.
- return new JsNameRef(ISOLATE_CURRENT);
- } else if (Elements.isTopLevel(element)) {
- return null;
- } else {
- return new JsThisRef();
- }
- }
-
- /**
- * Creates a getter that returns a JavaScript property.
- */
- private void makePropertyGetter(FieldElement element) {
- JsExpression qualifier = getGetterSetterQualifier(element);
- JsName fieldJsName = getJsName(element);
- JsNameRef ref = AstUtil.newNameRef(qualifier, fieldJsName);
- makeGetter(element, ref);
- }
-
- /**
- * Creates a getter that returns a constant (simple) JavaScript value.
- */
- private void makeConstantValueGetter(FieldElement element, JsExpression value) {
- assert element.getModifiers().isFinal();
- makeGetter(element, value);
- }
-
- private void makeGetter(FieldElement element, JsExpression expression) {
- String getterName = mangler.createGetterSyntax(element, unitLibrary);
- String fieldName = element.getName();
- JsName getterJsName = globalScope.declareName(getterName, getterName, fieldName);
- getterJsName.setObfuscatable(false);
- JsFunction func = new JsFunction(globalScope, getterJsName);
- func.setBody(AstUtil.newBlock(new JsReturn(expression)));
- makeMethod(element, func);
- }
-
- /**
- * Create a shim method for invoking a method through a field. Invoke the
- * getter to get the field value, then apply the shim's arguments to
- * the returned closure object.
- */
- private void makeMethodCallThroughFieldShim(DartField x) {
- FieldElement element = x.getSymbol();
- if (Elements.isTopLevel(element)) {
- // Don't bother making a call-though-field shim for global methods. They're always
- // statically resolved, so we'll never generate a call to one.
- return;
- }
-
- String shimName = mangler.mangleNamedMethod(element.getName(), unitLibrary);
- String fieldName = element.getName();
- JsName shimJsName = globalScope.declareName(shimName, shimName, fieldName);
- shimJsName.setObfuscatable(false);
- JsFunction func = new JsFunction(globalScope, shimJsName);
- JsExpression qualifier;
- if (element.getModifiers().isStatic()) {
- Element enclosingElement = element.getEnclosingElement();
- switch (enclosingElement.getKind()) {
- case CLASS:
- qualifier = AstUtil.newNameRef(null,
- mangler.mangleClassName((ClassElement) enclosingElement));
- break;
- case LIBRARY:
- qualifier = null;
- break;
- default:
- throw new InternalCompilerException(
- "Unhandled type of static element making method shim.");
- }
- } else {
- qualifier = getGetterSetterQualifier(element);
- }
- String getterName = mangler.createGetterSyntax(element, unitLibrary);
- JsExpression expression = AstUtil.newInvocation(AstUtil.newNameRef(qualifier, getterName));
- expression = AstUtil.newNameRef(expression, "apply");
- expression = AstUtil.newInvocation(expression, new JsThisRef(),
- AstUtil.newNameRef(null, "arguments"));
- func.setBody(AstUtil.newBlock(new JsReturn(expression)));
- makeMethod(element, func);
- }
-
- /**
- * Creates a getter method that lazily initializes the field (if necessary).
- */
- private void makeInitializingGetter(FieldElement element, JsExpression initExpression) {
- String getterName = mangler.createGetterSyntax(element, unitLibrary);
- String fieldName = element.getName();
- JsName getterJsName = globalScope.declareName(getterName, getterName, fieldName);
- getterJsName.setObfuscatable(false);
-
- JsFunction func = new JsFunction(globalScope, getterJsName);
- JsScope scope = new JsScope(globalScope, "temp");
-
- // Foo.x$getter = function() {
- // var t0 = isolate$current.Foo$x;
- // var t1 = $initializing;
- // if (t0 === t1) throw "circular initialization";
- // if (t0 !== $uninitialized) return t0;
- // isolate$current.Foo$x = t1;
- // var t2 = ... // initialization expression
- // isolate$current.Foo$x = t2;
- // return t2;
- // }
-
- JsExpression fieldQualifier = getGetterSetterQualifier(element);
- JsName fieldJsName = getJsName(element);
-
- JsName t0 = scope.declareTemporary();
- JsName t1 = scope.declareTemporary();
- JsName t2 = scope.declareTemporary();
-
- JsVars initializeT0 = AstUtil.newVar(
- null, t0, AstUtil.newNameRef(fieldQualifier, fieldJsName));
- JsVars initializeT1 = AstUtil.newVar(
- null, t1, new JsNameRef(STATIC_INITIALIZING));
- JsStatement checkIfCircular = new JsIf(
- new JsBinaryOperation(
- JsBinaryOperator.REF_EQ,
- t0.makeRef(),
- t1.makeRef()),
- new JsThrow(string("circular initialization")),
- null);
- JsStatement checkIfInitialized = new JsIf(
- new JsBinaryOperation(
- JsBinaryOperator.REF_NEQ,
- t0.makeRef(),
- new JsNameRef(STATIC_UNINITIALIZED)),
- new JsReturn(t0.makeRef()),
- null);
- JsStatement markField = AstUtil.newAssignment(
- AstUtil.newNameRef(fieldQualifier, fieldJsName), t1.makeRef()).makeStmt();
- JsStatement initializeT2 = AstUtil.newVar(
- null, t2, initExpression);
- JsStatement initializeField = AstUtil.newAssignment(
- AstUtil.newNameRef(fieldQualifier, fieldJsName), t2.makeRef()).makeStmt();
- JsStatement returnT2 = new JsReturn(t2.makeRef());
-
- // Construct the method from the statements.
- func.setBody(AstUtil.newBlock(
- initializeT0,
- initializeT1,
- checkIfCircular,
- checkIfInitialized,
- markField,
- initializeT2,
- initializeField,
- returnT2));
- makeMethod(element, func);
- }
-
- /**
- * Creates a setter and turns it into a prototype assignment on the
- * JS class.
- */
- private void makeSetter(FieldElement element) {
- String fieldName = element.getName();
- String setterName = mangler.createSetterSyntax(element, unitLibrary);
- JsName setterJsName = globalScope.declareName(setterName, setterName, fieldName);
- setterJsName.setObfuscatable(false);
- JsFunction func = new JsFunction(globalScope, setterJsName);
-
- JsScope scope = new JsScope(globalScope, "temp");
- JsName parameter = scope.declareTemporary();
- func.getParameters().add(0, new JsParameter(parameter));
-
- JsExpression qualifier = getGetterSetterQualifier(element);
-
- JsName fieldJsName = getJsName(element);
- JsNameRef ref = AstUtil.newNameRef(qualifier, fieldJsName);
- JsBinaryOperation asg = AstUtil.newAssignment(ref, parameter.makeRef());
- func.setBody(AstUtil.newBlock(new JsExprStmt(asg)));
-
- makeMethod(element, func);
- }
-
- @Override
- public JsNode visitInitializer(DartInitializer x) {
- JsExpression e = (JsExpression) generate(x.getValue());
- if (e != null && !x.isInvocation()) {
- JsName fieldJsName = getJsName(x.getName().getTargetSymbol());
- assert fieldJsName != null : "Field name must have been resolved.";
- JsNameRef field = AstUtil.newNameRef(new JsThisRef(), fieldJsName);
- e = AstUtil.newAssignment(field, e);
- e.setSourceRef(x);
- }
- return e != null ? new JsExprStmt(e) : null;
- }
-
- @Override
- public JsNode visitFieldDefinition(DartFieldDefinition node) {
- assert ElementKind.of(currentHolder).equals(ElementKind.LIBRARY);
- for (DartField field : node.getFields()) {
- generateTopLevelField(field);
- }
- return null;
- }
-
- private void generateTopLevelField(DartField field) {
- if (field.getSymbol().getModifiers().isAbstractField()) {
- generate(field.getAccessor());
- } else {
- generate(field);
- }
- }
-
- @Override
- public JsNode visitField(DartField x) {
- makeMethodCallThroughFieldShim(x);
- FieldElement element = x.getSymbol();
- Modifiers modifiers = element.getModifiers();
- if (modifiers.isAbstractField()) {
- generateAbstractField(element);
- return null;
- }
-
- DartExpression initializer = x.getValue();
- JsExprStmt result = null;
-
- if (initializer != null || Elements.isTopLevel(element)) {
- currentScopeInfo = ScopeRootInfo.makeScopeInfo(x, !shouldGenerateDeveloperModeChecks());
- inFactoryOrStaticContext = true;
-
- // There's an initializer, so emit an assignment statement.
- JsNameRef fieldName;
- if (isDeclaredAsStaticOrImplicitlyStatic(element)) {
- JsExpression qualifier = getGetterSetterQualifier(element);
- fieldName = AstUtil.newNameRef(qualifier, translationContext.getNames().getName(element));
- } else {
- fieldName = AstUtil.newNameRef(new JsThisRef(), getJsName(element));
- }
-
- JsExpression initExpr;
- if (initializer == null) {
- initExpr = undefined();
- } else {
- initExpr = (JsExpression) generate(initializer);
- }
-
- boolean emitStaticInitialization = true;
- if (x.getModifiers().isFinal() &&
- (initializer == null || initExpr instanceof JsValueLiteral)) {
- makeConstantValueGetter(element, initExpr);
- emitStaticInitialization = false;
- } else if (initializer == null || initExpr instanceof JsLiteral) {
- makePropertyGetter(element);
- } else {
- makeInitializingGetter(element, initExpr);
- initExpr = new JsNameRef(STATIC_UNINITIALIZED);
- }
-
- if (emitStaticInitialization) {
- JsBinaryOperation assignment = AstUtil.newAssignment(fieldName, initExpr);
- assignment.setSourceRef(x);
- result = new JsExprStmt(assignment);
- staticInit.add(result);
- }
-
- assert currentScopeInfo != null;
- currentScopeInfo = null;
- inFactoryOrStaticContext = false;
- } else {
- makePropertyGetter(element);
- }
-
- if (!element.getModifiers().isFinal()) {
- makeSetter(element);
- }
- return result;
- }
-
- @Override
- public JsNode visitFunction(DartFunction x) {
- if (x.getBody() == null) {
- if (ElementKind.of(currentHolder).equals(ElementKind.CLASS)
- && ((ClassElement) currentHolder).isInterface()) {
- return null;
- }
- }
-
- functionStack.push(x);
- jsNewDeclarationsStack.push(new HashSet<JsName>());
-
- // The JsFunction was already created and pushed in visit(DartFunction).
- JsFunction jsFunc = translationContext.getMethods().get(x);
-
- // Generate and set the body.
- JsBlock body;
- if (x.getBody() == null) {
- // The resolution has checked already that it is valid for this method
- // to not have a body.
- body = new JsBlock();
- } else {
- body = (JsBlock) generate(x.getBody());
- }
- jsFunc.setBody(body);
-
- // Create JS parameters
- List<DartParameter> params = x.getParams();
- List<JsParameter> jsParams = jsFunc.getParameters();
- if (!jsFunc.isHoisted()) {
- generateAll(params, jsParams, JsParameter.class);
- }
-
-
- // Create the runtime type checks that will be inserted later
- List<JsStatement> checks = Lists.newArrayList();
-
- // TODO(zundel): Issue 925: these runtime checks do not work in hoisted functions
- // created inside of factory methods if the parameter references type variables.
- // A bad reference to $typeArgs caused an error in the closure compiler backend.
- boolean omitTypeVariableChecks = false;
- if (inFactory) {
- Element element = (Element)x.getParent().getSymbol();
- if (!ElementKind.of(element).equals(ElementKind.CONSTRUCTOR)) {
- // The code being emitted is something other than the factory method itself.
- omitTypeVariableChecks = true;
- }
- }
-
- int numParams = params.size();
- for (int i = 0; i < numParams; ++i) {
- JsNameRef jsParam = jsParams.get(i).getName().makeRef();
- DartParameter param = params.get(i);
- Type paramType = param.getSymbol().getType();
- if (omitTypeVariableChecks && TypeKind.of(paramType).equals(TypeKind.VARIABLE)) {
- continue;
- }
- JsExpression expr = rtt.addTypeCheck(getCurrentClass(), jsParam, paramType, null, param);
- if (expr != jsParam) {
- // if the expression was returned unchanged, omit the check
- checks.add(new JsExprStmt(expr));
- }
- }
-
-
- // Add temporary variable declarations, if any.
- declareTempsInBlock(body, jsNewDeclarationsStack.pop());
-
- DartNode parent = x.getParent();
- assert parent != null;
- if (parent instanceof DartMethodDefinition) {
- DartMethodDefinition method = (DartMethodDefinition) parent;
- if (isFactory(method)) {
- rtt.maybeAddTypeParameterToFactory(method, jsFunc);
- }
- if (Elements.isNonFactoryConstructor((Element) parent.getSymbol())) {
- this.addSuperOrRedirectConstructorCall(method);
- }
- }
-
- // Call the function prologue setup functions in the reserve order that
- // their output need to appear as each adds to the front of the function
- // body.
- // 3. setup the scope aliases (after default init)
- maybeAddFunctionScopeAlias(currentScopeInfo.getScope(x),
- translationContext.getMethods().get(x));
-
- // 2. call function trace before anything else.
- maybeAddFunctionTracing(x);
-
- // 1. insert parameter type checks at the beginning of the method
- body.getStatements().addAll(0, checks);
-
- functionStack.pop();
- return jsFunc.setSourceRef(x);
- }
-
- /**
- * Get the type associated with a type node
- *
- * @param typeNode a {@link DartTypeNode}, which may be null
- * @return a {@link Type} corresponding to the type node or null to indicate unknown
- */
- private Type typeOf(DartTypeNode typeNode) {
- if (typeNode == null) {
- return null;
- }
- return typeNode.getType();
- }
-
- private boolean isFactory(DartMethodDefinition method) {
- return method.getModifiers().isFactory();
- }
-
- private void maybeAddFunctionTracing(DartFunction dartFunction) {
- // TODO(floitsch): temporary way to enable tracing is by setting a system property.
- String tracingCallTarget = System.getProperty("Trace");
- if (tracingCallTarget != null) {
- JsFunction function = translationContext.getMethods().get(dartFunction);
-
- // Example:
- // function f(a,b) { ... }
- // to:
- // function f(a, b) {
- // nestingCounter++;
- // <tracingCallTarget>(nestingCounter, "f(a, b)", a, b);
- // try { ... }
- // finally { nestingCounter--; }
- // }
- JsExpression increment = new JsPostfixOperation(JsUnaryOperator.INC,
- new JsNameRef(getTraceCounter()));
- JsExpression decrement = new JsPostfixOperation(JsUnaryOperator.DEC,
- new JsNameRef(getTraceCounter()));
-
- JsInvocation tracerCall = new JsInvocation();
- tracerCall.setQualifier(new JsNameRef(tracingCallTarget));
- List<JsExpression> traceArguments = tracerCall.getArguments();
- traceArguments.add(new JsNameRef(getTraceCounter()));
- traceArguments.add(null); // Reserve space for string description.
- StringBuffer description = new StringBuffer();
- JsName name = function.getName();
- if (name != null) {
- description.append(function.getName().toString());
- } else {
- description.append("<anonymous-" + function.hashCode() + ">");
- }
- description.append("(");
- dartFunction.getParams();
- for (DartParameter param : dartFunction.getParams()) {
- JsName paramName = getJsName(param.getSymbol());
- description.append(paramName.toString());
- traceArguments.add(new JsNameRef(paramName));
- }
- description.append(")");
- // Update string description in argument list.
- traceArguments.set(1, string(description.toString()));
-
- JsTry countingTry = new JsTry();
- countingTry.setTryBlock(function.getBody());
- countingTry.setFinallyBlock(AstUtil.newBlock(decrement.makeStmt()));
- JsBlock newBody = AstUtil.newBlock(increment.makeStmt(),
- tracerCall.makeStmt(),
- countingTry);
- function.setBody(newBody);
- }
- }
-
- @Override
- public JsNode visitParameter(DartParameter x) {
- if (x.getSymbol() != null) {
- return new JsParameter(getJsName(x.getSymbol())).setSourceRef(x);
- } else {
- // TODO(ngeoffray): A parameter in a function type does not have a symbol.
- return null;
- }
- }
-
- @Override
- public JsNode visitBlock(DartBlock x) {
- // Basic block handling
- JsBlock jsBlock = new JsBlock();
- // TODO(johnlenz): merge redundant JsBlock nodes.
- generateAll(x.getStatements(), jsBlock.getStatements(), JsStatement.class);
-
- //
- // For names defined in this scope that are captured by a function
- // closure rewrite, inject an object to hold the aliases for the
- // value for use by the closure. This simulates lexically scoped names
- // in JavaScript and once the closures are hoisted out of the scope
- // prevents the capture of value that would otherwise be protected in
- // another scope.
- //
-
- // Inject scope alias initialization and clean up.
- ScopeRootInfo methodInfo = currentScopeInfo;
- if (methodInfo != null) {
- DartScope scope = methodInfo.getScope(x);
- if (scope.definesClosureReferencedSymbols()) {
- // Make sure the alias is defined in the scope.
- JsScope currentFunctionScope = getCurrentFunctionScope();
- JsName aliasName = scope.findAliasForJsScope(currentFunctionScope);
- // Scope objects are declared (in the JsScope) at first use. By construction scope-objects
- // are only created when they are used. Therefore the scope-object must exist in the
- // JsScope.
- assert aliasName != null;
- registerForDeclaration(aliasName);
-
- // Init and clean up the scope alias
- // TODO(johnlenz): this really should be in a finally block,
- // debate the runtime cost of doing this, version the possibility of
- // a memory leak (It is only really needed if there are closures
- // outside this DartScope).
- // Alternately, once the closures have been hoisted out, the cleanup
- // code can be removed completely.
- List<JsStatement> list = jsBlock.getStatements();
- JsStatement init = AstUtil.newAssignment(
- new JsNameRef(aliasName), new JsObjectLiteral())
- .makeStmt();
- JsStatement cleanup = AstUtil.newAssignment(
- new JsNameRef(aliasName), undefined())
- .makeStmt();
- list.add(0, init);
- list.add(cleanup);
- }
- }
- return jsBlock.setSourceRef(x);
- }
-
- @Override
- public JsNode visitIfStatement(DartIfStatement x) {
- JsExpression jsCondition = (JsExpression) generate(x.getCondition());
- jsCondition = rtt.addTypeCheck(getCurrentClass(), jsCondition, typeProvider.getBoolType(),
- x.getCondition().getType(), x);
- JsStatement jsThenStmt = (JsStatement) generate(x.getThenStatement());
- JsStatement jsElseStmt = null;
- if (x.getElseStatement() != null) {
- jsElseStmt = (JsStatement) generate(x.getElseStatement());
- }
- return new JsIf(jsCondition, jsThenStmt, jsElseStmt).setSourceRef(x);
- }
-
- @Override
- public JsNode visitSwitchStatement(DartSwitchStatement x) {
- JsSwitch jsSwitch = new JsSwitch();
- jsSwitch.setExpr((JsExpression) generate(x.getExpression()));
- generateAll(x.getMembers(), jsSwitch.getCases(), JsSwitchMember.class);
- return jsSwitch.setSourceRef(x);
- }
-
- @Override
- public JsNode visitCase(DartCase x) {
- JsCase jsCase = new JsCase();
- jsCase.setCaseExpr((JsExpression) generate(x.getExpr()));
- generateAll(x.getStatements(), jsCase.getStmts(), JsStatement.class);
- return jsCase.setSourceRef(x);
- }
-
- @Override
- public JsNode visitDefault(DartDefault x) {
- JsDefault jsDefault = new JsDefault();
- generateAll(x.getStatements(), jsDefault.getStmts(), JsStatement.class);
- return jsDefault.setSourceRef(x);
- }
-
- @Override
- public JsNode visitWhileStatement(DartWhileStatement x) {
- JsExpression condition = (JsExpression) generate(x.getCondition());
- condition = rtt.addTypeCheck(getCurrentClass(), condition, typeProvider.getBoolType(),
- x.getCondition().getType(), x);
- JsBlock body = (JsBlock) generate(x.getBody());
- return new JsWhile(condition, body).setSourceRef(x);
- }
-
- @Override
- public JsNode visitDoWhileStatement(DartDoWhileStatement x) {
- JsExpression condition = (JsExpression) generate(x.getCondition());
- condition = rtt.addTypeCheck(getCurrentClass(), condition, typeProvider.getBoolType(),
- x.getCondition().getType(), x);
- JsBlock body = (JsBlock) generate(x.getBody());
- return new JsDoWhile(condition, body).setSourceRef(x);
- }
-
- @Override
- public JsNode visitForStatement(DartForStatement x) {
- // Dart AST normalization removes init expressions.
- assert x.getInit() == null;
-
- JsFor jsFor = new JsFor().setSourceRef(x);
- if (x.getCondition() != null) {
- JsExpression condExpr = (JsExpression) generate(x.getCondition());
- condExpr = rtt.addTypeCheck(getCurrentClass(), condExpr, typeProvider.getBoolType(),
- x.getCondition().getType(), x);
- jsFor.setCondition(condExpr);
- }
- if (x.getIncrement() != null) {
- jsFor.setIncrExpr((JsExpression) generate(x.getIncrement()));
- }
- jsFor.setBody((JsStatement) generate(x.getBody()));
- return jsFor.setSourceRef(x);
- }
-
- @Override
- public JsNode visitForInStatement(DartForInStatement x) {
- DartStatement normalizedNode = x.getNormalizedNode();
- if (normalizedNode == null) {
- throw new InternalCompilerException("For-in statement should have been normalized.");
- }
- return normalizedNode.accept(this);
- }
-
- @Override
- public JsNode visitContinueStatement(DartContinueStatement x) {
- JsContinue jsContinue = null;
- if (x.getTargetSymbol() != null) {
- jsContinue = new JsContinue(getJsName(x.getTargetSymbol()).makeRef());
- } else {
- jsContinue = new JsContinue();
- }
- return jsContinue.setSourceRef(x);
- }
-
- @Override
- public JsNode visitBreakStatement(DartBreakStatement x) {
- JsBreak jsBreak = null;
- if (x.getTargetSymbol() != null) {
- jsBreak = new JsBreak(getJsName(x.getTargetSymbol()).makeRef());
- } else {
- jsBreak = new JsBreak();
- }
- return jsBreak.setSourceRef(x);
- }
-
- @Override
- public JsNode visitReturnStatement(DartReturnStatement x) {
- JsReturn jsRet = new JsReturn();
- DartExpression returnValue = x.getValue();
- if (returnValue != null) {
- JsExpression expr = (JsExpression) generate(returnValue);
- DartFunction function = functionStack.peek();
- if (function != null) {
- // NOTE: FunctionExpressionInliner might be leaving return statements around
- DartTypeNode returnTypeNode = function.getReturnTypeNode();
- Type returnType = null;
- if (returnTypeNode == null) {
- // check for factory methods, which return the type of their name
- returnType = getFactoryReturnType(function);
- }
- if (returnType == null) {
- returnType = typeOf(returnTypeNode);
- }
- expr = rtt.addTypeCheck(getCurrentClass(), expr, returnType, returnValue.getType(), x);
- }
- jsRet.setExpr(expr);
- }
- return jsRet.setSourceRef(x);
- }
-
- /**
- * Get the return type of a factory method.
- *
- * @param function
- * @return {@link Type} instance or null if not a factory method or otherwise unavailable
- */
- private Type getFactoryReturnType(DartFunction function) {
- // TODO: implement
- return null;
- }
-
- @Override
- public JsNode visitTryStatement(DartTryStatement x) {
- JsTry jsTry = new JsTry();
- jsTry.setTryBlock((JsBlock) generate(x.getTryBlock()));
-
- // TODO(jgw): The Javascript AST allows multiple catch blocks for some reason,
- // even though that makes no sense. Sort this out once structured exceptions are
- // worked out in Dart.
- List<DartCatchBlock> catchBlocks = x.getCatchBlocks();
- if (catchBlocks != null && !catchBlocks.isEmpty()) {
- // Transform a sequence of catch blocks into nested if-statements.
- // Example:
- // try { <try-body>
- // } catch (SomeException e1) { <body1>
- // } catch (OtherException e2) { <body2>
- // }
- // becomes
- // try { <try-body>
- // } catch(tmpVar) {
- // if (tmpVar instanceof SomeException) { var e1 = tmpVar; <body1>
- // } else if (tmpVar instanceof OtherException) { var e2 = tmpVar; <body2>
- // } else { throw tmpVar; }
- // }
- //
- // Note that when no type is given for the Dart exception, it catches always. Then we
- // don't need a rethrow.
- // The JsCatch scope only contains one variable. There is hence no clash possible.
- JsCatch jsCatch = new JsCatch(getCurrentFunctionScope(), "e");
- JsName exceptionVar = jsCatch.getScope().findExistingName("e");
-
- jsTry.getCatches().add(jsCatch);
- JsBlock jsCatchBody = new JsBlock();
- // Tease out browser built-in exceptions
- JsExpression filterBuiltin = AstUtil.newAssignment(exceptionVar.makeRef(),
- AstUtil.newInvocation(AstUtil.newNameRef(null, "$transformBrowserException"),
- exceptionVar.makeRef()));
- jsCatchBody.getStatements().add(filterBuiltin.makeStmt());
- jsCatch.setBody(jsCatchBody);
- JsStatement jsElse = new JsThrow(new JsNameRef(exceptionVar));
-
- catchVarStack.push(exceptionVar);
- for (int i = catchBlocks.size() - 1; i >= 0; i--) {
- DartCatchBlock catchBlock = catchBlocks.get(i);
- JsBlock jsClauseBody = (JsBlock) generate(catchBlock.getBlock());
- if (catchBlock.getStackTrace() != null) {
- // TODO(ngeoffray): do something with the stackTrace.
- JsParameter jsStackParam = (JsParameter) generate(catchBlock.getStackTrace());
- registerForDeclaration(jsStackParam.getName());
- }
- JsParameter jsClauseParam = (JsParameter) generate(catchBlock.getException());
- JsName jsClauseParamName = jsClauseParam.getName();
-
- JsExpression assignment = AstUtil.newAssignment(new JsNameRef(jsClauseParamName),
- new JsNameRef(exceptionVar));
- jsClauseBody.getStatements().add(0, assignment.makeStmt());
- // The exception variable is not declared by the catch-block anymore. Register for
- // declaration so that it becomes a local variable.
- // Note that the name could already be in the list (if two catch-clauses share the same
- // name). In this case the declaration-clause will declare the same variable multiple
- // times (ex: var e, e, e;).
- registerForDeclaration(jsClauseParamName);
-
- DartParameter exception = catchBlock.getException();
- DartTypeNode exceptionType = exception.getTypeNode();
- if (exceptionType == null) {
- // No type has been given. This clause catches everything.
- jsElse = jsClauseBody;
- continue;
- }
-
- JsExpression instanceCheck = rtt.generateInstanceOfComparison(
- getCurrentClass(),
- new JsNameRef(exceptionVar),
- exceptionType,
- exceptionType).setSourceRef(exception);
- jsElse = new JsIf(instanceCheck, jsClauseBody, jsElse);
- }
- jsCatchBody.getStatements().add(jsElse);
- catchVarStack.pop();
- }
-
- if (x.getFinallyBlock() != null) {
- JsBlock jsFinallyBlock = (JsBlock) generate(x.getFinallyBlock());
- jsTry.setFinallyBlock(jsFinallyBlock);
- }
-
- // Allow a try block to be a target of a label by surrounding it with a block.
- JsNode result = jsTry.setSourceRef(x);
- if (x.getParent() instanceof DartLabel) {
- result = new JsBlock(jsTry).setSourceRef(x);
- }
- return result;
- }
-
- @Override
- public JsNode visitThrowStatement(DartThrowStatement x) {
- JsNameRef error = new JsNameRef("$Dart$ThrowException");
- JsInvocation invoc = AstUtil.newInvocation(error);
- JsExpression exception;
- if (x.getException() != null) {
- exception = (JsExpression) generate(x.getException());
- } else {
- // rethrow the exception
- JsName name = catchVarStack.peek();
- if (name != null) {
- exception = name.makeRef();
- } else {
- // TODO(johnlenz): validate this is the correct behavior
- throw new InternalCompilerException("invalid rethrow context");
- }
- }
- invoc.getArguments().add(exception);
- return new JsExprStmt(invoc.setSourceRef(x));
- }
-
-
- @Override
- public JsNode visitVariableStatement(DartVariableStatement x) {
-
- // Dart AST Normalization creates one declaration per VAR.
- assert x.getVariables().size() == 1;
-
- JsNode node = generate(x.getVariables().get(0));
- if (node instanceof JsVar) {
- JsVars jsVars = new JsVars();
- jsVars.insert((JsVar)node);
- return jsVars.setSourceRef(x);
- } else {
-
- // Variables captured by closures may be transformed to property
- // assignments.
- assert node instanceof JsStatement;
-
- return node;
- }
- }
-
- @Override
- public JsNode visitVariable(DartVariable x) {
- Symbol targetSymbol = x.getSymbol();
-
- // If the name is referenced by a closure use the scope alias.
- JsNameRef scopeAliasRef = maybeMakeScopeAliasReference(targetSymbol);
- JsNode result = null;
- DartExpression value = x.getValue();
- if (scopeAliasRef != null) {
- if (value != null) {
- JsExpression initExpr = (JsExpression) generate(value);
- Type type = getTypeOfIdentifier(x.getName());
- initExpr = rtt.addTypeCheck(getCurrentClass(), initExpr, type, value.getType(), x);
- result = AstUtil.newAssignment(scopeAliasRef, initExpr).setSourceRef(x).makeStmt();
- } else {
- // we need to put some statement in to keep the expected number
- // of values on the stack.
- result = translationContext.getProgram().getEmptyStmt();
- }
- } else {
- JsVars.JsVar jsVar = new JsVars.JsVar(getJsName(targetSymbol));
- if (value != null) {
- JsExpression initExpr = (JsExpression) generate(value);
- Type type = getTypeOfIdentifier(x.getName());
- initExpr = rtt.addTypeCheck(getCurrentClass(), initExpr, type, value.getType(), x);
- jsVar.setInitExpr(initExpr);
- } else {
- jsVar.setInitExpr(undefined());
- }
- result = jsVar.setSourceRef(x);
- }
-
- return result;
- }
-
- @Override
- public JsNode visitEmptyStatement(DartEmptyStatement x) {
- x.visitChildren(this);
- // TODO(johnlenz): Set source info?
- return translationContext.getProgram().getEmptyStmt();
- }
-
- @Override
- public JsNode visitSyntheticErrorExpression(DartSyntheticErrorExpression node) {
- String name = node.getSource().getName();
- int line = node.getSourceLine();
- int col = node.getSourceColumn();
- throw new AssertionError("Generating JS with parse error at " + name + ":"
- + line + ":" + col);
- }
-
- @Override
- public JsNode visitSyntheticErrorStatement(DartSyntheticErrorStatement node) {
- String name = node.getSource().getName();
- int line = node.getSourceLine();
- int col = node.getSourceColumn();
- throw new AssertionError("Generating JS with parse error at " + name + ":"
- + line + ":" + col);
- }
-
- @Override
- public JsNode visitLabel(DartLabel x) {
- JsStatement jsStmt = (JsStatement) generate(x.getStatement());
- JsLabel jsLabel = new JsLabel(getJsName(x.getSymbol()));
- jsLabel.setStmt(jsStmt);
- return jsLabel.setSourceRef(x);
- }
-
- @Override
- public JsNode visitExprStmt(DartExprStmt x) {
- JsNode node = generate(x.getExpression());
- if (node instanceof JsVars) {
- // Function statements maybe transformed to var statements.
- // TODO(johnlenz): Create a JsFunctionStatement so that those statements
- // aren't wrapped in expressions.
- // Note(floitsch): When removing this special case please update the comments
- // in 'endVisit(DartFunctionExpression, ...)'.
- return node;
- } else {
- JsExpression expr = (JsExpression) node;
- return new JsExprStmt(expr).setSourceRef(x);
- }
- }
-
- @Override
- public JsNode visitConditional(DartConditional x) {
- JsExpression testExpr = (JsExpression) generate(x.getCondition());
- testExpr = rtt.addTypeCheck(getCurrentClass(), testExpr, typeProvider.getBoolType(),
- x.getCondition().getType(), x);
- JsExpression thenExpr = (JsExpression) generate(x.getThenExpression());
- JsExpression elseExpr = (JsExpression) generate(x.getElseExpression());
- return new JsConditional(testExpr, thenExpr, elseExpr).setSourceRef(x);
- }
-
- @Override
- public JsNode visitBinaryExpression(DartBinaryExpression x) {
- assert x == x.getNormalizedNode();
-
- Token operator = x.getOperator();
-
- if (operator == Token.IS) {
- return generateInstanceOfComparison(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()));
- }
-
- assert !operator.isUserDefinableOperator() || !operator.isAssignmentOperator() : x;
-
- // 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));
- 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);
- }
- }
-
- /**
- * Return a type which an operator requires for its operands.
- *
- * @param op operator
- * @return a {@link Type} instance, which will be {@code null} if there are
- * no restrictions
- */
- private Type getRequiredType(Token op) {
- switch (op) {
- case OR:
- case AND:
- return typeProvider.getBoolType();
- // TODO: other operators?
- default:
- return null;
- }
- }
-
- private Type getTypeOfIdentifier(DartIdentifier ident) {
- Element element = ident.getReferencedElement();
- DartTypeNode typeNode = null;
- if (element == null) {
- DartNode parent = ident.getParent();
- if (parent instanceof DartVariable) {
- DartVariableStatement varStmt = (DartVariableStatement) parent.getParent();
- typeNode = varStmt.getTypeNode();
- }
- } else {
- switch (element.getKind()) {
- case VARIABLE:
- DartVariableStatement varStmt = (DartVariableStatement) element.getNode().getParent();
- typeNode = varStmt.getTypeNode();
- break;
- case PARAMETER:
- DartParameter param = (DartParameter) element.getNode();
- typeNode = param.getTypeNode();
- break;
- case FIELD:
- DartFieldDefinition fieldDef = (DartFieldDefinition) element.getNode().getParent();
- typeNode = fieldDef.getTypeNode();
- break;
- default:
- break;
- }
- }
- if (typeNode != null) {
- return typeNode.getType();
- }
- return null;
- }
-
- private JsExpression generateInstanceOfComparison(DartBinaryExpression x) {
- JsExpression lhs = (JsExpression) generate(x.getArg1());
- DartExpression rhs = x.getArg2();
- boolean isNot = false;
- if (rhs instanceof DartUnaryExpression) {
- isNot = true;
- rhs = ((DartUnaryExpression) rhs).getArg();
- }
- JsExpression expr = rtt.generateInstanceOfComparison(getCurrentClass(),
- lhs, ((DartTypeExpression) rhs).getTypeNode(), rhs).setSourceRef(x);
- if (isNot) {
- expr = new JsPrefixOperation(JsUnaryOperator.NOT, expr);
- }
- return expr;
- }
-
- private JsBinaryOperation assign(JsNameRef op1, JsExpression op2) {
- return AstUtil.newAssignment(op1, op2);
- }
-
- private JsBinaryOperation neq(JsExpression op1, JsExpression op2) {
- return new JsBinaryOperation(JsBinaryOperator.NEQ, op1, op2);
- }
-
- private JsBinaryOperation or(JsExpression op1, JsExpression op2) {
- return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2);
- }
-
- private JsNumberLiteral number(double num) {
- return translationContext.getProgram().getNumberLiteral(num);
- }
-
- private JsStringLiteral string(String str) {
- return translationContext.getProgram().getStringLiteral(str);
- }
-
- private JsNullLiteral nulle() {
- return translationContext.getProgram().getNullLiteral();
- }
-
- private JsNameRef undefined() {
- return translationContext.getProgram().getUndefinedLiteral();
- }
-
- private JsEmpty empty() {
- return translationContext.getProgram().getEmptyStmt();
- }
-
- @Override
- public JsNode visitTypeNode(DartTypeNode x) {
- // This backend does not need types.
- return null;
- }
-
- @Override
- public JsNode visitTypeParameter(DartTypeParameter x) {
- // This backend does not need types.
- return null;
- }
-
- @Override
- public JsNode visitTypeExpression(DartTypeExpression x) {
- throw new AssertionError("Unreachable");
- }
-
- @Override
- public JsNode visitUnaryExpression(DartUnaryExpression x) {
- assert x == x.getNormalizedNode();
- Token operator = x.getOperator();
- JsNode result;
- JsExpression arg = (JsExpression) generate(x.getArg());
- if (operator == Token.SUB) {
- JsNameRef ref =
- new JsNameRef(mangler.createOperatorSyntax(DartMangler.NEGATE_OPERATOR_NAME));
- result = (AstUtil.newInvocation(ref, arg));
- return result.setSourceRef(x);
- } else if (operator.isUserDefinableOperator()) {
- JsNameRef ref = new JsNameRef(mangler.createOperatorSyntax(operator));
- result = (AstUtil.newInvocation(ref, arg));
- return result.setSourceRef(x);
- } else {
- JsUnaryOperator jsUnaryOperator;
- switch (operator) {
- case INC:
- jsUnaryOperator = JsUnaryOperator.INC;
- break;
- case DEC:
- jsUnaryOperator = JsUnaryOperator.DEC;
- break;
- case NOT:
- jsUnaryOperator = JsUnaryOperator.NOT;
- arg = rtt.addTypeCheck(getCurrentClass(), arg, typeProvider.getBoolType(),
- x.getArg().getType(), x.getArg());
- break;
- default:
- throw new AssertionError("Unexpected unary operator " + operator.name());
- }
-
- if (x.isPrefix()) {
- result = new JsPrefixOperation(jsUnaryOperator, arg);
- } else {
- result = new JsPostfixOperation(jsUnaryOperator, arg);
- }
-
- return result.setSourceRef(x);
- }
- }
-
- @Override
- public JsNode visitPropertyAccess(DartPropertyAccess x) {
- return generateLoad(x.getQualifier(), x.getName()).setSourceRef(x);
- }
-
- @Override
- public JsNode visitArrayAccess(DartArrayAccess x) {
- JsExpression target = (JsExpression) generate(x.getTarget());
- JsExpression key = (JsExpression) generate(x.getKey());
- JsNameRef ref = AstUtil.newNameRef(target, mangler.createOperatorSyntax(Token.INDEX));
- JsInvocation invoke = AstUtil.newInvocation(ref, key);
- return invoke.setSourceRef(x);
- }
-
- @Override
- public JsNode visitUnqualifiedInvocation(DartUnqualifiedInvocation x) {
- DartIdentifier target = x.getTarget();
- Element element = target.getTargetSymbol();
- ElementKind kind = ElementKind.of(element);
- JsExpression qualifier;
- String mangledName;
- MethodElement method = null;
- switch (kind) {
- case FUNCTION_OBJECT:
- mangledName = null;
- qualifier = (JsExpression) generate(target);
- EnclosingElement enclosingElement = element.getEnclosingElement();
- if (enclosingElement != null && enclosingElement.getKind() == ElementKind.CLASS) {
- // Function-object invocations can be made directly, unless they're closures (in which
- // case their enclosing-element will be null).
- method = (MethodElement) element;
- }
- break;
- case FIELD:
- case PARAMETER:
- case VARIABLE:
- mangledName = null;
- qualifier = (JsExpression) generate(target);
- break;
-
- case NONE:
- mangledName = mangler.mangleMethod(x.getTarget().getTargetName(), unitLibrary);
- qualifier = new JsThisRef();
- break;
-
- case METHOD:
- method = (MethodElement) element;
- mangledName = mangler.mangleMethod(method, unitLibrary);
- if (element.getModifiers().isStatic()) {
- qualifier = referenceName(element.getEnclosingElement(), x.getTarget());
- } else if (Elements.isTopLevel(element)) {
- qualifier = null;
- } else {
- qualifier = new JsThisRef();
- }
- break;
-
- default:
- throw new AssertionError("Cannot be an unqualified invocation " + kind);
- }
- return generateInvocation(x, qualifier, false, mangledName, method);
- }
-
- @Override
- public JsNode visitFunctionObjectInvocation(DartFunctionObjectInvocation x) {
- DartExpression target = x.getTarget();
- if (target instanceof DartFunctionExpression) {
- DartFunctionExpression functionExpression = (DartFunctionExpression) target;
- if (functionExpression.getSymbol().getModifiers().isInlinable() &&
- !shouldGenerateDeveloperModeChecks()) {
- // TODO FunctionExpressionInliner conflics with developer mode checks
- return new FunctionExpressionInliner(functionExpression, x.getArgs()).call();
- }
- }
- JsExpression qualifier = (JsExpression) generate(target);
- return generateInvocation(x, qualifier, false, null, null);
- }
-
- /**
- * Takes a function expression and inlines it with the given arguments, for
- * example:
- * <pre>{@code
- * function(parameter) &#123; return parameter; &#125;(argument)
- * }</pre>
- * becomes:
- * <pre>{@code
- * ($1 = argument, $1)
- * }</pre>
- */
- private class FunctionExpressionInliner implements Callable<JsExpression> {
- private final List<DartExpression> arguments;
- private final List<DartParameter> parameters;
- private final Map<Symbol, Element> parameterMap = new HashMap<Symbol, Element>();
- private final List<DartStatement> statements;
- private final JsExpression[] expressions;
-
- FunctionExpressionInliner(DartFunctionExpression functionExpression,
- List<DartExpression> arguments) {
- final DartFunction function = functionExpression.getFunction();
- this.arguments = arguments;
- parameters = function.getParams();
- assert arguments.size() == parameters.size();
- statements = function.getBody().getStatements();
- expressions = new JsExpression[parameters.size() + statements.size()];
- }
-
- @Override
- public JsExpression call() {
- int i = 0;
- Iterator<DartExpression> argumentsIterator = arguments.iterator();
- for (DartParameter parameter : parameters) {
- // Assign each argument to a new temporary.
- // For example: "arg" becomes: "$i = arg"
- expressions[i++] = rewriteArgument(parameter, argumentsIterator.next());
- }
- for (DartStatement statement : statements) {
- // Inline each statement after rewriting references to the parameters.
- // For example: "return parameter_i;" becomes: "$i"
- expressions[i++] = rewriteStatement(statement);
- }
- if (i == 1) {
- return expressions[0];
- } else {
- return AstUtil.newSequence(expressions);
- }
- }
-
- private JsExpression rewriteArgument(DartParameter parameter, DartExpression argument) {
- JsName temporary = createTemporary();
- VariableElement element = Elements.makeVariable(temporary.getIdent());
- parameterMap.put(parameter.getSymbol(), element);
- translationContext.getNames().setName(element, temporary);
- return AstUtil.newAssignment(temporary.makeRef(), (JsExpression) generate(argument));
- }
-
- private JsExpression rewriteStatement(DartStatement node) {
- node.accept(new ParameterRewriter());
- JsNode jsNode = generate(node);
- if (jsNode instanceof JsExprStmt) {
- return ((JsExprStmt) jsNode).getExpression();
- } else if (jsNode instanceof JsReturn) {
- return ((JsReturn) jsNode).getExpr();
- } else {
- throw new AssertionError(node);
- }
- }
-
- private class ParameterRewriter extends DartNodeTraverser<Void> {
- @Override
- public Void visitIdentifier(DartIdentifier node) {
- Element element = parameterMap.get(node.getTargetSymbol());
- if (element != null) {
- DartIdentifier identifier = new DartIdentifier(element.getName());
- identifier.setSourceInfo(node);
- identifier.setSymbol(element);
- node.setNormalizedNode(identifier);
- }
- return null;
- }
- }
- }
-
- @Override
- public JsNode visitMethodInvocation(DartMethodInvocation x) {
- Element element = (Element) x.getTargetSymbol();
- MethodElement method = null;
- JsExpression qualifier;
- String mangledName;
-
- if (element == null) {
- mangledName = mangler.mangleNamedMethod(x.getFunctionNameString(), unitLibrary);
- qualifier = (JsExpression) generate(x.getTarget());
- } else {
- switch (element.getKind()) {
- case METHOD: {
- mangledName = mangler.mangleMethod((MethodElement) element, unitLibrary);
- if (element.getModifiers().isStatic()) {
- qualifier = referenceName(element.getEnclosingElement(), x.getTarget());
- } else if (Elements.isTopLevel(element)) {
- qualifier = null;
- } else {
- qualifier = (JsExpression) generate(x.getTarget());
- }
- method = (MethodElement) element;
- break;
- }
-
- case FIELD: {
- if(ElementKind.of(x.getTarget().getSymbol()) == ElementKind.LIBRARY) {
- // Handled very much like a unqualified invocation
- mangledName = null;
- qualifier = (JsExpression) generate(x.getFunctionName());
- } else {
- mangledName = mangler.mangleNamedMethod(x.getFunctionNameString(), unitLibrary);
- qualifier = (JsExpression) generate(x.getTarget());
- }
- break;
- }
-
- default: {
- throw new AssertionError("Unexpected invocation target.");
- }
- }
- }
-
- boolean isSuperCall = isSuperCall(x.getTarget().getSymbol());
- return generateInvocation(x, qualifier, isSuperCall, mangledName, method);
- }
-
- private JsExpression generateConstructorInvocation(
- DartNewExpression x, JsExpression qualifier,
- MethodElement method) {
- JsInvocation invoke = (JsInvocation)generateInvocation(x, qualifier, false, null, method);
- // TODO(johnlenz): if generateInvocation generates a "noSuchMethod" call. This will add
- // useless parameters to the call, this is harmless at the moment.
- rtt.mayAddRuntimeTypeToConstrutorOrFactoryCall(getCurrentClass(), x, invoke);
- return invoke;
- }
-
- private JsExpression generateInvocation(DartInvocation x,
- JsExpression qualifier,
- boolean isSuperCall,
- String mangledName,
- MethodElement method) {
- JsInvocation jsInvoke = new JsInvocation();
-
- if (method != null) {
- if (!generateDirectCallArgs(x, method, jsInvoke)) {
- // Call cannot succeed. Generate $nsme() invocation.
- return AstUtil.newInvocation(new JsNameRef("$nsme"));
- }
- } else {
- generateNamedCallArgs(x, jsInvoke);
- }
-
- JsExpression explicitReceiver = null;
- int argsLength = jsInvoke.getArguments().size();
- qualifier = referenceMethodMember(qualifier, mangledName);
-
- // If it's a super-call, and we need to adjust the 'this'.
- if (isSuperCall) {
- qualifier = AstUtil.newNameRef(qualifier, "call");
- }
-
- if (isSuperCall) {
- assert explicitReceiver == null;
- explicitReceiver = new JsThisRef();
- }
- if (explicitReceiver != null) {
- jsInvoke.getArguments().add(0, explicitReceiver);
- }
- jsInvoke.setQualifier(qualifier);
- return jsInvoke.setSourceRef(x);
- }
-
- /**
- * @return <code>false</code> if the invocation cannot succeed
- */
- private boolean generateDirectCallArgs(DartInvocation x, MethodElement target,
- JsInvocation jsInvoke) {
- // Direct call. Standard calling convention.
- List<DartExpression> args = x.getArgs();
- List<JsExpression> jsArgs = jsInvoke.getArguments();
-
- // Reorder named parameters.
- List<DartExpression> posArgs = new ArrayList<DartExpression>();
- Map<String, DartExpression> namedArgs = new HashMap<String, DartExpression>();
- for (DartExpression arg : args) {
- if (arg instanceof DartNamedExpression) {
- DartNamedExpression named = (DartNamedExpression) arg;
- namedArgs.put(named.getName().getTargetName(), named.getExpression());
- } else {
- posArgs.add(arg);
- }
- }
-
- int idx = 0, posUsed = 0;
- for (VariableElement param : target.getParameters()) {
- String name = param.getName();
- if (name != null) {
- DartExpression namedArg = namedArgs.remove(name);
- if (namedArg != null) {
- if (!param.getModifiers().isNamed()) {
- // Provided a named argument to a positional parameter.
- return false;
- }
- jsArgs.add((JsExpression) generate(namedArg));
- } else if (idx < posArgs.size()) {
- ++posUsed;
- jsArgs.add((JsExpression) generate(posArgs.get(idx)));
- } else if (param.getDefaultValue() != null) {
- assert(param.isNamed());
- jsArgs.add(generateDefaultValue(param.getDefaultValue()));
- } else {
- if (param.isNamed()) {
- jsArgs.add(undefined());
- } else {
- return false;
- }
- }
- }
- ++idx;
- }
-
- // Caller specified a named argument that wasn't declared in the method definition.
- if (!namedArgs.isEmpty()) {
- return false;
- }
-
- if (posUsed != posArgs.size()) {
- // Unused positional arguments.
- return false;
- }
-
- return true;
- }
-
- private JsExpression generateDefaultValue(DartExpression defaultValue) {
- if (defaultValue != null) {
- if (defaultValue instanceof DartFunctionExpression) {
- // This should be caught much earlier and rejected. This check avoids an NPE later.
- return nulle();
- }
- }
- return (JsExpression) generate(defaultValue);
- }
-
- private void generateNamedCallArgs(DartInvocation invoke, JsInvocation jsInvoke) {
- // Indirect call. Named-parameter calling convention.
- // method(parg_count, { na0:NA0, na1:NA1, ..., count:N }, pa0, pa1, ...);
- List<DartExpression> args = invoke.getArgs();
- List<JsExpression> jsArgs = jsInvoke.getArguments();
-
- int namedCount = 0;
- for (DartExpression arg : args) {
- if (arg instanceof DartNamedExpression) {
- ++namedCount;
- }
- }
-
- JsExpression argmap;
- if (namedCount == 0) {
- argmap = new JsNameRef("$noargs");
- } else {
- JsObjectLiteral bag = new JsObjectLiteral();
- for (DartExpression arg : args) {
- if (arg instanceof DartNamedExpression) {
- DartNamedExpression namedExpr = ((DartNamedExpression) arg);
- JsExpression targetName = string(getPropNameForNamedParameter(namedExpr));
- JsPropertyInitializer propInit = new JsPropertyInitializer(
- targetName,
- (JsExpression) generate(namedExpr.getExpression()));
- bag.getPropertyInitializers().add(propInit);
- }
- }
- JsPropertyInitializer countProp = new JsPropertyInitializer(string("count"),
- number(namedCount));
- bag.getPropertyInitializers().add(countProp);
- argmap = bag;
- }
-
- jsArgs.add(number(args.size() - namedCount));
- jsArgs.add(argmap);
- for (DartExpression arg : args) {
- if (!(arg instanceof DartNamedExpression)) {
- jsArgs.add((JsExpression) generate(arg));
- }
- }
- }
-
- private JsExpression referenceMethodMember(JsExpression qualifier,
- String mangledName) {
- if (mangledName != null) {
- qualifier = AstUtil.newNameRef(qualifier, mangledName);
- }
- return qualifier;
- }
-
- @Override
- public JsNode visitThisExpression(DartThisExpression x) {
- return new JsThisRef().setSourceRef(x);
- }
-
- @Override
- public JsNode visitSuperExpression(DartSuperExpression x) {
- ClassElement element = x.getSymbol().getClassElement();
- JsNameRef superRef = AstUtil.newPrototypeNameRef(getJsName(element).makeRef());
- return superRef.setSourceRef(x);
- }
-
- @Override
- public JsNode visitSuperConstructorInvocation(DartSuperConstructorInvocation x) {
- return generateSuperConstructorInvocation(x);
- }
-
- @Override
- public JsNode visitNativeBlock(DartNativeBlock x) {
- JsBlock jsBlock = new JsBlock();
-
- DartMethodDefinition method =
- (DartMethodDefinition) currentScopeInfo.getContainingClassMember();
- String name = mangler.mangleNativeMethod(method.getSymbol());
-
- JsNameRef nativeRef = new JsNameRef(name);
- JsInvocation nativeCall;
- if (method.getModifiers().isStatic()) {
- nativeCall = AstUtil.newInvocation(nativeRef);
- } else {
- JsNameRef callRef = AstUtil.newNameRef(nativeRef, "call");
- nativeCall = AstUtil.newInvocation(callRef, new JsThisRef());
- }
-
- for (DartParameter p : method.getFunction().getParams()) {
- nativeCall.getArguments().add(getJsName(p.getSymbol()).makeRef());
- }
-
- jsBlock.getStatements().add(new JsReturn(nativeCall));
- return jsBlock.setSourceRef(x);
- }
-
- @Override
- public JsNode visitNewExpression(DartNewExpression x) {
- ConstructorElement element = x.getSymbol();
- JsExpression newExpr;
- if (element != null && element.getConstructorType() != null) {
- String className = element.getConstructorType().getName();
- // TODO(floitsch): We should have a JsNames instead of creating the string representations.
- String name = mangler.createFactorySyntax(className, element.getName(), unitLibrary);
- // We add the class name of the holder of the constructor as a qualifier.
- JsName classJsName = getJsName(element.getEnclosingElement());
- JsNameRef consName = AstUtil.newNameRef(classJsName.makeRef(), name);
- newExpr = generateConstructorInvocation(x, consName, element);
- if (x.isConst()) {
- newExpr = maybeInternConst(newExpr, Types.constructorType(x).getArguments());
- }
- } else {
- JsInvocation jsInvocation = AstUtil.newInvocation(new JsNameRef("$nsme2"));
- JsStringLiteral ctorName = translationContext.getProgram().getStringLiteral(
- x.getConstructor().toSource());
- JsArrayLiteral jsArgsArray = new JsArrayLiteral();
- List<JsExpression> expressions = jsArgsArray.getExpressions();
- List<DartExpression> dartArgs = x.getArgs();
- for (DartExpression arg : dartArgs) {
- expressions.add((JsExpression) generate(arg));
- }
- List<JsExpression> jsInvocationArguments = jsInvocation.getArguments();
- jsInvocationArguments.add(ctorName);
- jsInvocationArguments.add(jsArgsArray);
- newExpr = jsInvocation;
- }
- return newExpr;
- }
-
-
- // Compile time constants expressions must be canonicalized.
- // We do this with the javascript native "$intern" method.
- private JsExpression maybeInternConst(JsExpression newExpr, List<Type> typeParams) {
- JsInvocation intern = AstUtil.newInvocation(new JsNameRef(INTERN_CONST_FUNCTION), newExpr);
- if (typeParams != null && typeParams.size() != 0) {
- JsArrayLiteral arr = new JsArrayLiteral();
- for (Type t : typeParams) {
- JsExpression typeName;
- if (t.getKind() != TypeKind.DYNAMIC) {
- typeName = rtt.getRTTClassId((ClassElement)t.getElement());
- } else {
- typeName = string("");
- }
- arr.getExpressions().add(typeName);
- }
- intern.getArguments().add(arr);
- }
- return intern;
- }
-
- private boolean shouldBindThis(ScopeRootInfo.ClosureInfo info) {
- if (shouldGenerateDeveloperModeChecks()) {
- return true;
- }
-
- return !inFactoryOrStaticContext && info.referencesThis;
- }
-
- private boolean shouldGenerateDeveloperModeChecks() {
- return context.getCompilerConfiguration().developerModeChecks();
- }
-
- @Override
- public JsNode visitFunctionExpression(DartFunctionExpression x) {
- JsFunction fn = (JsFunction) generate(x.getFunction());
- 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
- // this is problematic. We won't need to keep track of the hoisted
- // state once the re-evaluation problem is fixed.
- boolean fnWasPreviouslyHoisted = fn.isHoisted();
- if (fnWasPreviouslyHoisted) {
- fnDeclaredName = fn.getName();
- hoistedName = fn.getName();
- hoistedRttName = mangler.mangleRttLookupMethod(hoistedName.toString(), unitLibrary);
- } else {
-
- // 0) Save off the original name
- fnDeclaredName = fn.getName();
-
- // 1) Create a global name for this method
- hoistedName = makeClosureHoistedJsName(currentHolder, currentScopeInfo, x);
-
- // 2) Give it a unique name.
- fn.setName(hoistedName);
-
- // 3) Insert it into global scope
- fn.rebaseScope(globalScope);
-
- // 4) Make it statement, if it isn't already
- globalBlock.getStatements().add(fn.makeStmt());
-
- // 5) Mark the function as hoisted
- fn.setHoisted();
- }
-
- ScopeRootInfo.ClosureInfo info = currentScopeInfo.getClosureInfo(x.getFunction());
- List<DartScope> list = info.getSortedReferencedScopeList();
-
- // TODO(jgw): See johnlenz' comment above about re-evaluation. This guard can go away once
- // that problem is fixed.
- if (!fnWasPreviouslyHoisted) {
- // Generate the named-parameter trampoline.
- boolean includesClosureScope = !list.isEmpty();
- boolean preserveThis = shouldBindThis(info);
- JsFunction tramp = generateNamedParameterTrampoline(x.getFunction(),
- hoistedName.makeRef(),
- list.size(), preserveThis);
- String mangled = mangler.mangleNamedMethod(hoistedName.getIdent(), unitLibrary);
- 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 = 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.
-
- JsExpression thisRef = undefined();
- // Only bind 'this' if 'this' is referenced
- if (shouldBindThis(info)) {
- thisRef = new JsThisRef();
- }
-
- // Replace the definition with a reference to the
- // hoisted function, and bind the necessary values
- // to it.
- int scopeCount = list.size();
- int argCount = fn.getParameters().size() + 2; // +2 => Named-parameter calling convention
- String jsBindName = "$bind";
-
- 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;
- for (DartScope s : list) {
- // Add the scope-object as argument to the bind call. The scope-object is referenced
- // in the outer function (currentFunctionScope).
- JsName aliasJsName = s.getAliasForJsScope(getCurrentFunctionScope());
- invoke.getArguments().add(new JsNameRef(aliasJsName));
- // Add the scope-object as parameter to the hoisted signature. The scope-object is
- // referenced from the inner function.
- JsName jsName = s.findAliasForJsScope(fn.getScope());
- // Scope objects are declared (in the JsScope) at first use. By construction scope-objects
- // are only created when they are used. Therefore the scope-object must exist in the
- // JsScope.
- assert jsName != null;
- // TODO(johnlenz): remove this hoisted check once the constructor/init
- // parameters aren't reused.
- if (!fnWasPreviouslyHoisted) {
- fn.getParameters().add(parameterIndex, new JsParameter(jsName));
- }
- parameterIndex += 1;
- }
-
- replacement = invoke;
- }
-
- // 6) If this is a named function expression, then we need to build the scope object.
- if (!x.isStatement() && x.getName() != null) {
- ScopeRootInfo scopeInfo = currentScopeInfo;
- DartScope scope = scopeInfo.getScope(x);
- // If the function is not used by name, then we might not need to create a scope.
- if (scope.definesClosureReferencedSymbols()) {
- // Make sure the alias is defined in the scope.
- JsScope currentFunctionScope = getCurrentFunctionScope();
- // This must be the second use of the scope in this function scope. The first use was
- // as argument to the bind-invocation above.
- JsName aliasName = scope.findAliasForJsScope(currentFunctionScope);
- assert aliasName != null;
- registerForDeclaration(aliasName);
-
- // Assume that the initial expression was x = function f() { <body> }.
- // Up to this point the function has been hoisted and replaced by a binding call:
- // x = bind(hoistedName, this, scope1, ...)
- // The variable "replacement" is equal to the bind call.
- //
- // One of the scopes - say "scope2" - is the FunctionExpressionScope that defines 'f'
- // itself. We now need to set up this scope.
- // Transform:
- // x = bind(hoistedName, this, scope1, ...) into
- // x = (scope2 = {}, scope2.f = bind(hoistedName, this, scope1, ...)).
- JsExpression init =
- AstUtil.newAssignment(new JsNameRef(aliasName), new JsObjectLiteral());
- JsNameRef scopeF = makeScopeAliasNameRef(scope, x.getSymbol());
- JsExpression assig = AstUtil.newAssignment(scopeF, replacement);
- replacement = new JsBinaryOperation(JsBinaryOperator.COMMA, init, assig);
- // TODO(floitsch): we need to clear the scope object.
- }
- }
-
- if (x.isStatement()) {
- // If the name is referenced by a closure use the scope alias.
- JsNameRef scopeAliasRef = maybeMakeScopeAliasReference(x.getSymbol());
- if (scopeAliasRef != null) {
- JsExpression assig = AstUtil.newAssignment(scopeAliasRef, replacement);
- // We must not return a statement. The parent has a check and handles JsVars differently.
- // By default it actually expects an expression.
- return assig.setSourceRef(x);
- } else {
- // The parent expects an expression, but handles Var statements separately.
- assert !hoistedName.equals(fnDeclaredName);
- JsVars vars = AstUtil.newVar(x.getName(), fnDeclaredName, replacement);
- return vars.setSourceRef(x);
- }
- } else {
- return replacement.setSourceRef(x);
- }
- }
-
- private JsName makeClosureHoistedJsName(
- Element holder, ScopeRootInfo info, DartFunctionExpression x) {
- Element element = info.getContainingElement();
- String closureIdentifier = info.getNextClosureName();
- String closureName = x.getFunctionName();
- String hoistedName =
- mangler.createHoistedFunctionName(holder, element, closureIdentifier, closureName);
- return globalScope.declareName(hoistedName, hoistedName, closureName);
- }
-
- @Override
- public JsNode visitIdentifier(DartIdentifier x) {
- DartExpression normalizedNode = x.getNormalizedNode();
- if (normalizedNode != x) {
- return normalizedNode.accept(this);
- }
-
- return generateLoad(null, x).setSourceRef(x);
- }
-
- /**
- * @return A NameRef to the scoped alias if needed.
- */
- private JsNameRef maybeMakeScopeAliasReference(Symbol targetSymbol) {
- if (!functionStack.isEmpty()) {
- /*
- * Currently, you must be inside of a DartFunction in order to be able to generate a
- * scope alias.
- */
- ScopeRootInfo methodInfo = currentScopeInfo;
- if (methodInfo != null) {
- DartScope.DartSymbolInfo symbolInfo = methodInfo.getSymbolInfo(targetSymbol);
- if (symbolInfo != null && symbolInfo.isReferencedFromClosure()) {
- return makeScopeAliasNameRef(symbolInfo.getOwningScope(), targetSymbol);
- }
- }
- }
- return null;
- }
-
- private JsNameRef makeScopeAliasNameRef(DartScope scope, Symbol targetSymbol) {
- JsName qualifier = scope.getAliasForJsScope(getCurrentFunctionScope());
- return AstUtil.newNameRef(new JsNameRef(qualifier), getJsName(targetSymbol).getIdent());
- }
-
- @Override
- public JsNode visitNullLiteral(DartNullLiteral x) {
- // TODO(johnlenz): set source location?
- return undefined();
- }
-
- @Override
- public JsNode visitStringLiteral(DartStringLiteral x) {
- // TODO(johnlenz): properly set source location?
- return string(x.getValue()).setSourceRef(x);
- }
-
- @Override
- public JsNode visitStringInterpolation(DartStringInterpolation x) {
- List<DartStringLiteral> strings = x.getStrings();
- List<DartExpression> expressions = x.getExpressions();
-
- JsExpression res = null;
- Iterator<DartExpression> eIter = expressions.iterator();
- boolean first = true;
- for (DartStringLiteral lit : strings) {
- if (first) {
- first = false;
- res = (JsExpression) generate(lit);
- } else {
- assert eIter.hasNext() : "DartStringInterpolation invariant broken.";
- JsExpression expr = (JsExpression) generate(eIter.next());
- JsInvocation exprToString = new JsInvocation();
- exprToString.setQualifier(new JsNameRef("$toString"));
- exprToString.getArguments().add(expr);
- res = new JsBinaryOperation(JsBinaryOperator.ADD,
- new JsBinaryOperation(JsBinaryOperator.ADD, res, exprToString),
- (JsExpression) generate(lit)).setSourceRef(x);
- }
- }
- assert res != null;
- return res;
- }
-
- @Override
- public JsNode visitBooleanLiteral(DartBooleanLiteral x) {
- // TODO(johnlenz): set source location?
- return x.getValue() ? translationContext.getProgram().getTrueLiteral() : translationContext.getProgram().getFalseLiteral();
- }
-
- @Override
- public JsNode visitIntegerLiteral(DartIntegerLiteral x) {
- // TODO(johnlenz): set source location?
- return number(x.getValue().doubleValue());
- }
-
- @Override
- public JsNode visitDoubleLiteral(DartDoubleLiteral x) {
- // TODO(johnlenz): set source location?
- return number(x.getValue());
- }
-
- @Override
- public JsNode visitArrayLiteral(DartArrayLiteral x) {
- Type elementType = x.getType().getArguments().get(0);
- JsArrayLiteral jsArray = new JsArrayLiteral();
- for (DartNode node : x.getExpressions()) {
- JsExpression expr = (JsExpression) generate(node);
- JsExpression checkedExpr = rtt.addTypeCheck(getCurrentClass(), expr,
- elementType, node.getType(), node.getSourceInfo());
- jsArray.getExpressions().add(checkedExpr);
- }
- jsArray.setSourceRef(x);
- JsExpression result = rtt.maybeAddRuntimeTypeForArrayLiteral(getCurrentClass(), x, jsArray);
- if (x.isConst()) {
- result = this.maybeInternConst(result, x.getType().getArguments());
- }
- return result;
- }
-
- @Override
- @SuppressWarnings("deprecation")
- public JsNode visitMapLiteral(DartMapLiteral x) {
- // Map { 'a': 3, 'b': "foo" } to
- // (tmp = new Map(), tmp.a = 3, tmp.b = "foo", tmp)
- // TODO(floitsch): optimize map-literal creation.
- JsName tmpVar = createTemporary();
- // TODO(floitsch): hardcoded reference to "LinkedHashMapImplementation".
- // We should instead get the element from the DartMapLiteral x.
- String name = "LinkedHashMapImplementation";
- String mangledMap = mangler.mangleClassNameHack(null, name);
- String mangledFactory = mangler.createFactorySyntax(name, "", unitLibrary);
- JsNameRef runtimeMap = AstUtil.newNameRef(new JsNameRef(mangledMap), mangledFactory);
- JsInvocation invoke = AstUtil.newInvocation(runtimeMap);
- rtt.maybeAddRuntimeTypeToMapLiteralConstructor(getCurrentClass(), x, invoke);
- JsExpression assig = AstUtil.newAssignment(tmpVar.makeRef(), invoke.setSourceRef(x));
- JsExpression result = assig;
- Type actualEntryType = x.getType().getArguments().get(1);
- for (DartMapLiteralEntry entry : x.getEntries()) {
- result = AstUtil.newSequence(result, visitMapLiteralEntry(entry, tmpVar, actualEntryType));
- }
- result = AstUtil.newSequence(result, tmpVar.makeRef());
- if (x.isConst()) {
- result = this.maybeInternConst(result, x.getType().getArguments());
- }
- return result;
- }
-
- private JsExpression visitMapLiteralEntry(DartMapLiteralEntry x, JsName map, Type valueType) {
- String addMethod = mangler.createOperatorSyntax(Token.ASSIGN_INDEX);
- JsExpression value = (JsExpression) generate(x.getValue());
- value = rtt.addTypeCheck(getCurrentClass(), value, valueType, x.getType(), x.getSourceInfo());
- JsExpression key = (JsExpression) generate(x.getKey());
- JsNameRef methodName = AstUtil.newNameRef(map.makeRef(), addMethod);
- return AstUtil.newInvocation(methodName, key, value).setSourceRef(x);
- }
-
- @Override
- public JsNode visitMapLiteralEntry(DartMapLiteralEntry x) {
- throw new InternalCompilerException("MapLiteralEntries are handled by the 2-arg variant.");
- }
-
- @Override
- public JsNode visitNamedExpression(DartNamedExpression node) {
- return generate(node.getExpression());
- }
-
- @Override
- public JsNode visitRedirectConstructorInvocation(DartRedirectConstructorInvocation x) {
- return generateSuperConstructorInvocation(x);
- }
-
- private JsNode generateSuperConstructorInvocation(DartInvocation x) {
- // Must use SuperClass.call(this, ...) to get the correct 'this' context in the callee:
- // <super-class>.<name>$Constructor.call(this, ...).
- ConstructorElement element = (ConstructorElement) x.getSymbol();
- Element classElement;
- String elementName;
- if (element == null) {
- classElement = ((ClassElement) currentHolder).getSupertype().getElement();
- elementName = classElement.getName();
- } else {
- classElement = element.getEnclosingElement();
- elementName = element.getName();
- }
-
- // Skip emitting the super calls call if it is Object.
- if (classElement.equals(typeProvider.getObjectType().getElement())) {
- return null;
- }
-
- // TODO(floitsch): it would be good, if we could get a js-name instead of just a string.
- // This way the debugging information would be better.
- // We need to generate the JsName (for the initializer/factory) once only and store it
- // in some hashtable. Then instead of reusing the mangler, we should reuse those JsNames.
- // The debugging information would then contain a link from the property-access to the
- // constructor. Without JsName the debugger just assumes we access some random property.
- String name = mangler.mangleConstructor(elementName, unitLibrary);
- JsNameRef constructorRef = AstUtil.newNameRef(getJsName(classElement).makeRef(), name);
- return generateInvocation(x, constructorRef, true, null, element);
- }
-
- private JsBinaryOperator mapBinaryOp(Token operator) {
- switch (operator) {
- /* Assignment operators. */
- case ASSIGN: return JsBinaryOperator.ASG;
- case ASSIGN_BIT_OR: return JsBinaryOperator.ASG_BIT_OR;
- case ASSIGN_BIT_XOR: return JsBinaryOperator.ASG_BIT_XOR;
- case ASSIGN_BIT_AND: return JsBinaryOperator.ASG_BIT_AND;
- case ASSIGN_SHL: return JsBinaryOperator.ASG_SHL;
- case ASSIGN_SAR: return JsBinaryOperator.ASG_SHR;
- case ASSIGN_ADD: return JsBinaryOperator.ASG_ADD;
- case ASSIGN_SUB: return JsBinaryOperator.ASG_SUB;
- case ASSIGN_MUL: return JsBinaryOperator.ASG_MUL;
- case ASSIGN_DIV: return JsBinaryOperator.ASG_DIV;
- case ASSIGN_MOD: return JsBinaryOperator.ASG_MOD;
-
- /* Binary operators sorted by precedence. */
- case OR: return JsBinaryOperator.OR;
- case AND: return JsBinaryOperator.AND;
- case BIT_OR: return JsBinaryOperator.BIT_OR;
- case BIT_XOR: return JsBinaryOperator.BIT_XOR;
- case BIT_AND: return JsBinaryOperator.BIT_AND;
- case SHL: return JsBinaryOperator.SHL;
- case SAR: return JsBinaryOperator.SHR;
- case ADD: return JsBinaryOperator.ADD;
- case SUB: return JsBinaryOperator.SUB;
- case MUL: return JsBinaryOperator.MUL;
- case DIV: return JsBinaryOperator.DIV;
- case MOD: return JsBinaryOperator.MOD;
-
- /* Compare operators sorted by precedence. */
- case EQ: return JsBinaryOperator.EQ;
- case NE: return JsBinaryOperator.NEQ;
- case EQ_STRICT: return JsBinaryOperator.REF_EQ;
- case NE_STRICT: return JsBinaryOperator.REF_NEQ;
- case LT: return JsBinaryOperator.LT;
- case GT: return JsBinaryOperator.GT;
- case LTE: return JsBinaryOperator.LTE;
- case GTE: return JsBinaryOperator.GTE;
-
- // Only used by 'for'.
- case COMMA: return JsBinaryOperator.COMMA;
-
- default:
- throw new InternalCompilerException("Invalid binary operator");
- }
- }
-
- private JsUnaryOperator mapUnaryOp(Token operator) {
- switch (operator) {
- case BIT_NOT:
- return JsUnaryOperator.BIT_NOT;
- case NOT:
- return JsUnaryOperator.NOT;
- case SUB:
- return JsUnaryOperator.NEG;
- case INC:
- return JsUnaryOperator.INC;
- case DEC:
- return JsUnaryOperator.DEC;
- default:
- throw new InternalCompilerException("Invalid unary operator.");
- }
- }
-
- private Token mapToStrictEquals(Token op) {
- switch (op) {
- case EQ:
- return Token.EQ_STRICT;
- case NE:
- return Token.NE_STRICT;
- case EQ_STRICT:
- return Token.EQ_STRICT;
- case NE_STRICT:
- return Token.NE_STRICT;
- default:
- throw new InternalCompilerException("Invalid equals operator.");
- }
- }
-
- private Token mapToNonStrictEquals(Token op) {
- switch (op) {
- case EQ_STRICT:
- return Token.EQ;
- case NE_STRICT:
- return Token.NE;
- case EQ:
- return Token.EQ;
- case NE:
- return Token.NE;
- default:
- throw new InternalCompilerException("Invalid equals operator.");
- }
- }
-
- class Assignment extends DartNodeTraverser<JsNode> {
- private final DartNode info;
- private final JsExpression rhs;
- private final Type rhsType;
-
- public Assignment(DartNode info, JsExpression rhs, Type rhsType) {
- this.info = info;
- this.rhs = rhs;
- this.rhsType = rhsType;
- }
-
- @Override
- public JsNode visitNode(DartNode lhs) {
- throw new AssertionError(lhs.getClass().getSimpleName());
- }
-
- @Override
- public JsNode visitIdentifier(DartIdentifier lhs) {
- DartExpression normalizedNode = lhs.getNormalizedNode();
- if (lhs != normalizedNode) {
- return normalizedNode.accept(this);
- }
- Type type = getTypeOfIdentifier(lhs);
- JsExpression wrapped = rtt.addTypeCheck(getCurrentClass(), rhs, type, rhsType, info);
- // On the form e1.name = rhs.
- return generateStore(null, lhs, wrapped).setSourceRef(info);
- }
-
- @Override
- public JsNode visitPropertyAccess(DartPropertyAccess lhs) {
- // On the form e1.name = rhs.
- Type type = lhs.getType();
- JsExpression wrapped = rtt.addTypeCheck(getCurrentClass(), rhs, type, rhsType, info);
- return generateStore(lhs.getQualifier(), lhs.getName(), wrapped).setSourceRef(info);
- }
-
- @Override
- public JsNode visitArrayAccess(DartArrayAccess lhs) {
- // On the form e1[key] = argument.
- // Generate: e1.$set(key, $0 = argument), $0
-
- JsExpression key = (JsExpression) generate(lhs.getKey());
- JsExpression e1 = (JsExpression) generate(lhs.getTarget());
- Type type = lhs.getType();
- JsExpression wrapped = rtt.addTypeCheck(getCurrentClass(), rhs, type, rhsType, info);
- JsNameRef $0 = new JsNameRef(createTemporary());
- String $set = mangler.createOperatorSyntax(Token.ASSIGN_INDEX);
- // Generate: $0 = rhs
- JsExpression e = AstUtil.newAssignment($0, wrapped);
- // Generate: e1.$set(key, $0 = rhs)
- e = AstUtil.newInvocation(AstUtil.newNameRef(e1, $set), key, e);
- // Generate: e, $0
- return new JsBinaryOperation(JsBinaryOperator.COMMA, e, $0).setSourceRef(info);
- }
- }
-
- private final JsNode generate(DartNode node) {
- if (node != null) {
- try {
- return node.getNormalizedNode().accept(this);
- } catch (AssertionError e) {
- reportError(node, e);
- // Wrap assertion error to prevent repeated messages for the same error.
- throw new RuntimeException(e);
- }
- } else {
- return null;
- }
- }
-
- private JsExpression inlineArrayIndexCheck(JsExpression array, JsExpression index) {
- return AstUtil.newInvocation(new JsNameRef("$inlineArrayIndexCheck"), array, index);
- }
-
- private void reportError(DartNode node, Throwable exception) {
- context.onError(new DartCompilationError(node, JsErrorCode.INTERNAL_ERROR,
- exception.getLocalizedMessage()));
- }
-
- private final <T> void generateAll(List<? extends DartNode> nodes, List<T> result,
- Class<? extends T> cls) {
- for (DartNode node : nodes) {
- result.add(cls.cast(generate(node)));
- }
- }
-
- JsNameRef referenceName(Symbol symbol, SourceInfo info) {
- // If the value if captured by a closure, change the reference to
- // use the alias of the value.
- JsNameRef jsNode = maybeMakeScopeAliasReference(symbol);
- if (jsNode == null) {
- jsNode = getJsName(symbol).makeRef();
- }
- jsNode.setSourceRef(info);
- return jsNode;
- }
-
- @Override
- public void visit(List<? extends DartNode> nodes) {
- if (nodes != null) {
- for (DartNode node : nodes) {
- node.accept(this);
- }
- }
- }
-
- @Override
- public JsNode visitAssertion(DartAssertion node) {
- if (inDevMode()) {
- JsExpression expression = (JsExpression) generate(node.getExpression());
- JsNameRef assertName = new JsNameRef("assert");
- JsInvocation jsInvoke;
- jsInvoke = AstUtil.newInvocation(assertName, expression);
- return new JsExprStmt(jsInvoke).setSourceRef(node);
- }
- // Just emit an empty statement if not in checked mode.
- return empty();
- }
-
- private boolean inDevMode() {
- return context.getCompilerConfiguration().developerModeChecks();
- }
-
- @Override
- public JsNode visitParenthesizedExpression(DartParenthesizedExpression node) {
- return node.getExpression().accept(this);
- }
-
- @Override
- public JsNode visitCatchBlock(DartCatchBlock node) {
- throw new AssertionError("should never be called directly");
- }
-
- @Override
- public JsNode visitUnit(DartUnit unit) {
- throw new AssertionError("should never be called directly");
- /*
- unit.visitChildren(this);
- // Initialize static fields after declaring every method, getters &
- // setters (b/4101270)
- // TODO(johnlenz): canonicalize statics values
- globalBlock.getStatements().addAll(staticInit);
- */
- }
-
- @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;
- }
-
- private JsExpression generateQualifiedFieldAccess(DartNode qualifier,
- String accessorName) {
- // Generate this.ACCESSOR();
- JsExpression jsQualifier;
- if (qualifier == null || (qualifier instanceof DartThisExpression)) {
- jsQualifier = new JsThisRef();
- } else {
- jsQualifier = (JsExpression) generate(qualifier);
- }
-
- jsQualifier.setSourceRef(qualifier);
- JsNameRef nameRef = AstUtil.newNameRef(jsQualifier, accessorName);
- return AstUtil.newInvocation(nameRef);
- }
-
- private JsExpression generateUnresolvedAccess(DartNode qualifier,
- String accessorName) {
- if (qualifier == null) {
- return generateQualifiedFieldAccess(qualifier, accessorName);
- }
- // Generate qualifier.ACCESSOR();
- JsExpression jsQualifier = (JsExpression) generate(qualifier);
- jsQualifier.setSourceRef(qualifier);
- JsNameRef method = AstUtil.newNameRef(jsQualifier, accessorName);
- return AstUtil.newInvocation(method);
- }
-
- private JsInvocation generateSuperFieldAccess(DartNode qualifier,
- String accessorName) {
- // Generate CLASS.prototype.ACCESSOR.call(this);
- ClassElement superClass = ((SuperElement) qualifier.getSymbol()).getClassElement();
- JsExpression jsQualifier = AstUtil.newPrototypeNameRef(getJsName(superClass).makeRef());
- jsQualifier.setSourceRef(qualifier);
- JsNameRef method = AstUtil.newNameRef(jsQualifier, accessorName);
- method = AstUtil.newNameRef(method, "call");
- JsInvocation jsInvoke = AstUtil.newInvocation(method);
- jsInvoke.getArguments().add(0, new JsThisRef());
- return jsInvoke;
- }
-
- private JsInvocation generateStaticFieldAccess(FieldElement element,
- DartNode qualifier,
- String accessorName) {
- // Generate CLASS.ACCESSOR();
- JsExpression jsQualifier = referenceName(element.getEnclosingElement(), qualifier);
- jsQualifier.setSourceRef(qualifier);
- JsNameRef method = AstUtil.newNameRef(jsQualifier, accessorName);
- return AstUtil.newInvocation(method);
- }
-
- private JsInvocation generateLibraryFieldAccess(String accessorName) {
- // Generate ACCESSOR();
- return AstUtil.newInvocation(new JsNameRef(accessorName));
- }
-
- private JsExpression generateFieldAccess(FieldElement field, DartNode qualifier,
- String accessorName) {
- boolean isSuperCall = (qualifier != null) && isSuperCall(qualifier.getSymbol());
- if (isSuperCall) {
- return generateSuperFieldAccess(qualifier, accessorName);
- } else if (Elements.isTopLevel(field)) {
- return generateLibraryFieldAccess(accessorName);
- } else if (field.isStatic()) {
- return generateStaticFieldAccess(field, qualifier, accessorName);
- } else {
- return generateQualifiedFieldAccess(qualifier, accessorName);
- }
- }
-
- /*
- * A method is accessed as if a closure object
- * class A { foo() { return 1; })
- * Function f = A.foo;
- */
- private JsExpression generateMethodBoundToVariable(DartIdentifier methodNode,
- 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));
- } else if (Elements.isTopLevel(methodElement)) {
- 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(),
- mangledGetter);
- } else {
- boundMethod = AstUtil.newNameRef((JsExpression) generate(qualifier), mangledGetter);
- }
- boundMethod = AstUtil.newInvocation(boundMethod);
- } else {
- // Should be an invocation on an instance
- if (qualifier == null) {
- qualifier = DartThisExpression.get();
- }
- JsExpression methodQualifier = (JsExpression) generate(qualifier);
- ClassElement classElement = (ClassElement) methodElement.getEnclosingElement();
- String className = mangler.mangleClassName(classElement);
- JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(new JsNameRef(className));
- JsExpression methodToCall = AstUtil.newNameRef(prototypeRef, mangledName);
- JsExpression methodRtt = AstUtil.newNameRef(prototypeRef,
- mangler.mangleRttLookupMethod(methodElement, unitLibrary));
- boundMethod = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, methodRtt, methodQualifier);
- }
- boundMethod.setSourceRef(methodNode);
- return boundMethod;
- }
-
- private JsExpression generateLoadTemporary(Element element, DartIdentifier node) {
- return referenceName(element, node);
- }
-
- private JsExpression generateLoad(DartNode qualifier, DartIdentifier node) {
- Element element = node.getTargetSymbol();
-
- switch (ElementKind.of(element)) {
- case VARIABLE:
- case PARAMETER:
- case FUNCTION_OBJECT:
- // TODO(5089961): we should not generate code for class expressions.
- case CLASS:
- return generateLoadTemporary(element, node);
-
- case NONE:
- if (qualifier != null && isSuperCall(qualifier.getSymbol())) {
- return generateSuperFieldAccess(qualifier,
- mangler.createGetterSyntax(node.getTargetName(), unitLibrary));
- }
- return generateUnresolvedAccess(qualifier,
- mangler.createGetterSyntax(node.getTargetName(), unitLibrary));
-
- case FIELD: {
- FieldElement field = (FieldElement) element;
- String accessorName;
- accessorName = mangler.createGetterSyntax(field, unitLibrary);
- return generateFieldAccess(field, qualifier, accessorName);
- }
-
- case METHOD: {
- MethodElement method = (MethodElement) element;
- return generateMethodBoundToVariable(node, method, qualifier);
- }
-
- default:
- throw new AssertionError("I do not know how to load: " + ElementKind.of(element));
- }
- }
-
- private JsExpression generateStoreTemporary(Element element,
- DartIdentifier node,
- JsExpression rhs) {
- JsNameRef jsName = referenceName(element, node);
- return AstUtil.newAssignment(jsName, rhs);
- }
-
- private JsExpression generateStoreField(JsExpression fieldAccess,
- JsExpression rhs) {
- if (fieldAccess instanceof JsInvocation) {
- JsNameRef $0 = new JsNameRef(createTemporary());
- // Generate: $0 = rhs
- JsExpression e = AstUtil.newAssignment($0, rhs);
-
- // Add ($0 = rhs) as parameter of the field access.
- ((JsInvocation) fieldAccess).getArguments().add(e);
- // Generate: e1.set$name($0 = rhs), $0
- return new JsBinaryOperation(JsBinaryOperator.COMMA, fieldAccess, $0);
- } else {
- assert (fieldAccess instanceof JsNameRef);
- return AstUtil.newAssignment((JsNameRef) fieldAccess, rhs);
- }
- }
-
- private JsExpression generateStore(DartNode qualifier, DartIdentifier node, JsExpression rhs) {
- Element element = node.getTargetSymbol();
-
- switch (ElementKind.of(element)) {
- case VARIABLE:
- case PARAMETER:
- return generateStoreTemporary(element, node, rhs);
-
- case NONE: {
- JsExpression invoke =
- generateUnresolvedAccess(qualifier,
- mangler.createSetterSyntax(node.getTargetName(), unitLibrary));
- return generateStoreField(invoke, rhs);
- }
-
- case FIELD: {
- FieldElement field = (FieldElement) element;
- String accessorName;
-
- accessorName = mangler.createSetterSyntax(field, unitLibrary);
-
- JsExpression invoke = generateFieldAccess(field, qualifier, accessorName);
- return generateStoreField(invoke, rhs);
- }
-
- default:
- throw new AssertionError("I do not know how to store into: " + ElementKind.of(element));
- }
- }
-
- @Override
- public JsNode visitParameterizedTypeNode(DartParameterizedTypeNode node) {
- return node.getExpression().accept(this);
- }
-
- @Override
- public JsNode visitImportDirective(DartImportDirective node) {
- throw new AssertionError("should never be called directly");
- }
-
- @Override
- public JsNode visitLibraryDirective(DartLibraryDirective node) {
- throw new AssertionError("should never be called directly");
- }
-
- @Override
- public JsNode visitNativeDirective(DartNativeDirective node) {
- throw new AssertionError("should never be called directly");
- }
-
- @Override
- public JsNode visitResourceDirective(DartResourceDirective node) {
- throw new AssertionError("should never be called directly");
- }
-
- @Override
- public JsNode visitSourceDirective(DartSourceDirective node) {
- throw new AssertionError("should never be called directly");
- }
-
- @Override
- public DartClassMember<?> getCurrentClassMember() {
- if (this.currentScopeInfo != null) {
- return currentScopeInfo.getContainingClassMember();
- }
- return null;
- }
-
- private ClassElement getCurrentClass() {
- if (currentHolder.getKind() == ElementKind.CLASS) {
- return (ClassElement) currentHolder;
- }
- return null;
- }
- }
-
- GenerateJavascriptAST(DartUnit unit, CoreTypeProvider typeProvider, DartCompilerContext context) {
- this.unit = unit;
- this.context = context;
- this.typeProvider = typeProvider;
- }
-
- public void translateNode(TranslationContext translationContext, DartNode node,
- JsBlock blockStatics) {
- GenerateJavascriptVisitor generator =
- new GenerateJavascriptVisitor(unit, context, translationContext,
- typeProvider);
- // Generate the Javascript AST.
- node.accept(generator);
- // Set aside the static initializations
- generator.addStaticInitsToBlock(blockStatics);
- }
-}

Powered by Google App Engine
This is Rietveld 408576698