| 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 7f4caebab48e694fa53dc67425f7feed8a805ce5..0000000000000000000000000000000000000000
|
| --- a/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java
|
| +++ /dev/null
|
| @@ -1,3879 +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) {
|
| - InterfaceType classType = classElement.getType();
|
| - return TypeKind.of(classType) == TypeKind.INTERFACE
|
| - && typeUtils.isSubtype(classType, typeProvider.getIsolateType());
|
| - }
|
| -
|
| - /**
|
| - * 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) { return parameter; }(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);
|
| - }
|
| -}
|
|
|