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

Unified Diff: pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart

Issue 1567403004: dart2js: Eliminate the JsIrBuilderVisitor. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
index 9723f6e59d7caa658adb17e1727c3709b1caad23..dfb59eb46268e49b5a89fdec9619b75ac18f08fb 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
@@ -4,8 +4,7 @@
library dart2js.ir_builder_task;
-import '../closure.dart' as closurelib;
-import '../closure.dart' hide ClosureScope;
+import '../closure.dart' as closure;
import '../common.dart';
import '../common/names.dart' show
Names,
@@ -91,7 +90,7 @@ class IrBuilderTask extends CompilerTask {
sourceInformationStrategy.createBuilderForContext(element);
IrBuilderVisitor builder =
- new JsIrBuilderVisitor(
+ new IrBuilderVisitor(
elementsMapping, compiler, sourceInformationBuilder,
typeMaskSystem);
ir.FunctionDefinition irNode = builder.buildExecutable(element);
@@ -115,7 +114,7 @@ class IrBuilderTask extends CompilerTask {
/// For expressions, the primitive holding the resulting value is returned.
/// For statements, `null` is returned.
// TODO(johnniwinther): Implement [SemanticDeclVisitor].
-abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
+class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
with IrBuilderMixin<ast.Node>,
SemanticSendResolvedMixin<ir.Primitive, dynamic>,
ErrorBulkMixin<ir.Primitive, dynamic>,
@@ -165,3560 +164,3466 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
this.sourceInformationBuilder,
this.typeMaskSystem);
+ JavaScriptBackend get backend => compiler.backend;
+ BackendHelpers get helpers => backend.helpers;
DiagnosticReporter get reporter => compiler.reporter;
String bailoutMessage = null;
+ ir.Primitive visit(ast.Node node) => node.accept(this);
+
@override
ir.Primitive apply(ast.Node node, _) => node.accept(this);
- @override
SemanticSendVisitor get sendVisitor => this;
- /**
- * Builds the [ir.FunctionDefinition] for an executable element. In case the
- * function uses features that cannot be expressed in the IR, this element
- * returns `null`.
- */
- ir.FunctionDefinition buildExecutable(ExecutableElement element);
-
- ClosureClassMap get closureClassMap;
- ClosureScope getClosureScopeForNode(ast.Node node);
- ClosureEnvironment getClosureEnvironment();
-
- /// Normalizes the argument list of a static invocation.
+ /// Result of closure conversion for the current body of code.
///
- /// A static invocation is one where the target is known. The argument list
- /// [arguments] is normalized by adding default values for optional arguments
- /// that are not passed, and by sorting it in place so that named arguments
- /// appear in a canonical order. A [CallStructure] reflecting this order
- /// is returned.
- CallStructure normalizeStaticArguments(
- CallStructure callStructure,
- FunctionElement target,
- List<ir.Primitive> arguments);
+ /// Will be initialized upon entering the body of a function.
+ /// It is computed by the [ClosureTranslator].
+ closure.ClosureClassMap closureClassMap;
- /// Normalizes the argument list of a dynamic invocation.
+ /// If [node] has declarations for variables that should be boxed,
+ /// returns a [ClosureScope] naming a box to create, and enumerating the
+ /// variables that should be stored in the box.
///
- /// A dynamic invocation is one where the target is not known. The argument
- /// list [arguments] is normalized by sorting it in place so that the named
- /// arguments appear in a canonical order. A [CallStructure] reflecting this
- /// order is returned.
- CallStructure normalizeDynamicArguments(
- CallStructure callStructure,
- List<ir.Primitive> arguments);
-
- /// Read the value of [field].
- ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src);
-
- ir.FunctionDefinition _makeFunctionBody(FunctionElement element,
- ast.FunctionExpression node) {
- FunctionSignature signature = element.functionSignature;
- List<Local> parameters = <Local>[];
- signature.orderedForEachParameter(
- (LocalParameterElement e) => parameters.add(e));
-
- if (element.isFactoryConstructor) {
- // Type arguments are passed in as extra parameters.
- for (DartType typeVariable in element.enclosingClass.typeVariables) {
- parameters.add(new TypeVariableLocal(typeVariable, element));
- }
- }
+ /// Also see [ClosureScope].
+ ClosureScope getClosureScopeForNode(ast.Node node) {
+ // We translate a ClosureScope from closure.dart into IR builder's variant
+ // because the IR builder should not depend on the synthetic elements
+ // created in closure.dart.
+ return new ClosureScope(closureClassMap.capturingScopes[node]);
+ }
- irBuilder.buildFunctionHeader(parameters,
- closureScope: getClosureScopeForNode(node),
- env: getClosureEnvironment());
+ /// Returns the [ClosureScope] for any function, possibly different from the
+ /// one currently being built.
+ ClosureScope getClosureScopeForFunction(FunctionElement function) {
+ closure.ClosureClassMap map =
+ compiler.closureToClassMapper.computeClosureToClassMapping(
+ function,
+ function.node,
+ elements);
+ return new ClosureScope(map.capturingScopes[function.node]);
+ }
- visit(node.body);
- return irBuilder.makeFunctionDefinition();
+ /// If the current function is a nested function with free variables (or a
+ /// captured reference to `this`), returns a [ClosureEnvironment]
+ /// indicating how to access these.
+ ClosureEnvironment getClosureEnvironment() {
+ return new ClosureEnvironment(closureClassMap);
}
- /// Returns the allocation site-specific type for a given allocation.
- ///
- /// Currently, it is an error to call this with anything that is not the
- /// allocation site for a List object (a literal list or a call to one
- /// of the List constructors).
- TypeMask getAllocationSiteType(ast.Node node) {
- return compiler.typesTask.getGuaranteedTypeOfNode(
- elements.analyzedElement, node);
+ IrBuilder getBuilderFor(Element element) {
+ return new IrBuilder(
+ new GlobalProgramInformation(compiler),
+ backend.constants,
+ element);
}
- ir.Primitive visit(ast.Node node) => node.accept(this);
+ /// Builds the [ir.FunctionDefinition] for an executable element. In case the
+ /// function uses features that cannot be expressed in the IR, this element
+ /// returns `null`.
+ ir.FunctionDefinition buildExecutable(ExecutableElement element) {
+ return nullIfGiveup(() {
+ ir.FunctionDefinition root;
+ switch (element.kind) {
+ case ElementKind.GENERATIVE_CONSTRUCTOR:
+ root = buildConstructor(element);
+ break;
- // ## Statements ##
- visitBlock(ast.Block node) {
- irBuilder.buildBlock(node.statements.nodes, build);
- }
+ case ElementKind.GENERATIVE_CONSTRUCTOR_BODY:
+ root = buildConstructorBody(element);
+ break;
- ir.Primitive visitBreakStatement(ast.BreakStatement node) {
- if (!irBuilder.buildBreak(elements.getTargetOf(node))) {
- reporter.internalError(node, "'break' target not found");
- }
- return null;
- }
+ case ElementKind.FACTORY_CONSTRUCTOR:
+ case ElementKind.FUNCTION:
+ case ElementKind.GETTER:
+ case ElementKind.SETTER:
+ root = buildFunction(element);
+ break;
- ir.Primitive visitContinueStatement(ast.ContinueStatement node) {
- if (!irBuilder.buildContinue(elements.getTargetOf(node))) {
- reporter.internalError(node, "'continue' target not found");
- }
- return null;
- }
+ case ElementKind.FIELD:
+ if (Elements.isStaticOrTopLevel(element)) {
+ root = buildStaticFieldInitializer(element);
+ } else {
+ // Instance field initializers are inlined in the constructor,
+ // so we shouldn't need to build anything here.
+ // TODO(asgerf): But what should we return?
+ return null;
+ }
+ break;
- // Build(EmptyStatement, C) = C
- ir.Primitive visitEmptyStatement(ast.EmptyStatement node) {
- assert(irBuilder.isOpen);
- return null;
+ default:
+ reporter.internalError(element, "Unexpected element type $element");
+ }
+ return root;
+ });
}
- // Build(ExpressionStatement(e), C) = C'
- // where (C', _) = Build(e, C)
- ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) {
- assert(irBuilder.isOpen);
- if (node.expression is ast.Throw) {
- // Throw expressions that occur as statements are translated differently
- // from ones that occur as subexpressions. This is achieved by peeking
- // at statement-level expressions here.
- irBuilder.buildThrow(visit(node.expression));
- } else {
- visit(node.expression);
+ /// Loads the type variables for all super classes of [superClass] into the
+ /// IR builder's environment with their corresponding values.
+ ///
+ /// The type variables for [currentClass] must already be in the IR builder's
+ /// environment.
+ ///
+ /// Type variables are stored as [TypeVariableLocal] in the environment.
+ ///
+ /// This ensures that access to type variables mentioned inside the
+ /// constructors and initializers will happen through the local environment
+ /// instead of using 'this'.
+ void loadTypeVariablesForSuperClasses(ClassElement currentClass) {
+ if (currentClass.isObject) return;
+ loadTypeVariablesForType(currentClass.supertype);
+ if (currentClass is MixinApplicationElement) {
+ loadTypeVariablesForType(currentClass.mixinType);
}
- return null;
}
- ir.Primitive visitRethrow(ast.Rethrow node) {
- assert(irBuilder.isOpen);
- irBuilder.buildRethrow();
- return null;
+ /// Loads all type variables for [type] and all of its super classes into
+ /// the environment. All type variables mentioned in [type] must already
+ /// be in the environment.
+ void loadTypeVariablesForType(InterfaceType type) {
+ ClassElement clazz = type.element;
+ assert(clazz.typeVariables.length == type.typeArguments.length);
+ for (int i = 0; i < clazz.typeVariables.length; ++i) {
+ irBuilder.declareTypeVariable(clazz.typeVariables[i],
+ type.typeArguments[i]);
+ }
+ loadTypeVariablesForSuperClasses(clazz);
}
- /// Construct a method that executes the forwarding call to the target
- /// constructor. This is only required, if the forwarding factory
- /// constructor can potentially be the target of a reflective call, because
- /// the builder shortcuts calls to redirecting factories at the call site
- /// (see [JsIrBuilderVisitor.handleConstructorInvoke]).
- visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
- ConstructorElement targetConstructor =
- elements.getRedirectingTargetConstructor(node).implementation;
- ConstructorElement redirectingConstructor =
- irBuilder.state.currentElement.implementation;
- List<ir.Primitive> arguments = <ir.Primitive>[];
- FunctionSignature redirectingSignature =
- redirectingConstructor.functionSignature;
- List<String> namedParameters = <String>[];
- redirectingSignature.forEachParameter((ParameterElement parameter) {
- arguments.add(irBuilder.environment.lookup(parameter));
- if (parameter.isNamed) {
- namedParameters.add(parameter.name);
+ /// Returns the constructor body associated with the given constructor or
+ /// creates a new constructor body, if none can be found.
+ ///
+ /// Returns `null` if the constructor does not have a body.
+ ConstructorBodyElement getConstructorBody(FunctionElement constructor) {
+ // TODO(asgerf): This is largely inherited from the SSA builder.
+ // The ConstructorBodyElement has an invalid function signature, but we
+ // cannot add a BoxLocal as parameter, because BoxLocal is not an element.
+ // Instead of forging ParameterElements to forge a FunctionSignature, we
+ // need a way to create backend methods without creating more fake elements.
+ assert(constructor.isGenerativeConstructor);
+ assert(constructor.isImplementation);
+ if (constructor.isSynthesized) return null;
+ ast.FunctionExpression node = constructor.node;
+ // If we know the body doesn't have any code, we don't generate it.
+ if (!node.hasBody()) return null;
+ if (node.hasEmptyBody()) return null;
+ ClassElement classElement = constructor.enclosingClass;
+ ConstructorBodyElement bodyElement;
+ classElement.forEachBackendMember((Element backendMember) {
+ if (backendMember.isGenerativeConstructorBody) {
+ ConstructorBodyElement body = backendMember;
+ if (body.constructor == constructor) {
+ bodyElement = backendMember;
+ }
}
});
- ClassElement cls = redirectingConstructor.enclosingClass;
- InterfaceType targetType =
- redirectingConstructor.computeEffectiveTargetType(cls.thisType);
- CallStructure callStructure = new CallStructure(
- redirectingSignature.parameterCount,
- namedParameters);
- callStructure =
- normalizeStaticArguments(callStructure, targetConstructor, arguments);
- ir.Primitive instance = irBuilder.buildConstructorInvocation(
- targetConstructor,
- callStructure,
- targetType,
- arguments,
- sourceInformationBuilder.buildNew(node));
- irBuilder.buildReturn(
- value: instance,
- sourceInformation: sourceInformationBuilder.buildReturn(node));
- }
+ if (bodyElement == null) {
+ bodyElement = new ConstructorBodyElementX(constructor);
+ classElement.addBackendMember(bodyElement);
- visitFor(ast.For node) {
- List<LocalElement> loopVariables = <LocalElement>[];
- if (node.initializer is ast.VariableDefinitions) {
- ast.VariableDefinitions definitions = node.initializer;
- for (ast.Node node in definitions.definitions.nodes) {
- LocalElement loopVariable = elements[node];
- loopVariables.add(loopVariable);
+ if (constructor.isPatch) {
+ // Create origin body element for patched constructors.
+ ConstructorBodyElementX patch = bodyElement;
+ ConstructorBodyElementX origin =
+ new ConstructorBodyElementX(constructor.origin);
+ origin.applyPatch(patch);
+ classElement.origin.addBackendMember(bodyElement.origin);
}
}
-
- JumpTarget target = elements.getTargetDefinition(node);
- irBuilder.buildFor(
- buildInitializer: subbuild(node.initializer),
- buildCondition: subbuild(node.condition),
- buildBody: subbuild(node.body),
- buildUpdate: subbuildSequence(node.update),
- closureScope: getClosureScopeForNode(node),
- loopVariables: loopVariables,
- target: target);
+ assert(bodyElement.isGenerativeConstructorBody);
+ return bodyElement;
}
- visitIf(ast.If node) {
- irBuilder.buildIf(
- build(node.condition),
- subbuild(node.thenPart),
- subbuild(node.elsePart));
- }
-
- visitLabeledStatement(ast.LabeledStatement node) {
- ast.Statement body = node.statement;
- if (body is ast.Loop) {
- visit(body);
- } else {
- JumpTarget target = elements.getTargetDefinition(body);
- irBuilder.buildLabeledStatement(
- buildBody: subbuild(body),
- target: target);
+ /// The list of parameters to send from the generative constructor
+ /// to the generative constructor body.
+ ///
+ /// Boxed parameters are not in the list, instead, a [BoxLocal] is passed
+ /// containing the boxed parameters.
+ ///
+ /// For example, given the following constructor,
+ ///
+ /// Foo(x, y) : field = (() => ++x) { print(x + y) }
+ ///
+ /// the argument `x` would be replaced by a [BoxLocal]:
+ ///
+ /// Foo_body(box0, y) { print(box0.x + y) }
+ ///
+ List<Local> getConstructorBodyParameters(ConstructorBodyElement body) {
+ List<Local> parameters = <Local>[];
+ ClosureScope scope = getClosureScopeForFunction(body.constructor);
+ if (scope != null) {
+ parameters.add(scope.box);
}
+ body.functionSignature.orderedForEachParameter((ParameterElement param) {
+ if (scope != null && scope.capturedVariables.containsKey(param)) {
+ // Do not pass this parameter; the box will carry its value.
+ } else {
+ parameters.add(param);
+ }
+ });
+ return parameters;
}
- visitDoWhile(ast.DoWhile node) {
- irBuilder.buildDoWhile(
- buildBody: subbuild(node.body),
- buildCondition: subbuild(node.condition),
- target: elements.getTargetDefinition(node),
- closureScope: getClosureScopeForNode(node));
- }
+ /// Builds the IR for a given constructor.
+ ///
+ /// 1. Computes the type held in all own or "inherited" type variables.
+ /// 2. Evaluates all own or inherited field initializers.
+ /// 3. Creates the object and assigns its fields and runtime type.
+ /// 4. Calls constructor body and super constructor bodies.
+ /// 5. Returns the created object.
+ ir.FunctionDefinition buildConstructor(ConstructorElement constructor) {
+ // TODO(asgerf): Optimization: If constructor is redirecting, then just
+ // evaluate arguments and call the target constructor.
+ constructor = constructor.implementation;
+ ClassElement classElement = constructor.enclosingClass.implementation;
- visitWhile(ast.While node) {
- irBuilder.buildWhile(
- buildCondition: subbuild(node.condition),
- buildBody: subbuild(node.body),
- target: elements.getTargetDefinition(node),
- closureScope: getClosureScopeForNode(node));
- }
+ IrBuilder builder = getBuilderFor(constructor);
- visitAsyncForIn(ast.AsyncForIn node) {
- // await for is not yet implemented.
- return giveup(node, 'await for');
- }
+ final bool requiresTypeInformation =
+ builder.program.requiresRuntimeTypesFor(classElement);
- visitAwait(ast.Await node) {
- ir.Primitive value = visit(node.expression);
- return irBuilder.buildAwait(value);
- }
+ return withBuilder(builder, () {
+ // Setup parameters and create a box if anything is captured.
+ List<Local> parameters = <Local>[];
+ constructor.functionSignature.orderedForEachParameter(
+ (ParameterElement p) => parameters.add(p));
- visitYield(ast.Yield node) {
- ir.Primitive value = visit(node.expression);
- return irBuilder.buildYield(value, node.hasStar);
- }
+ int firstTypeArgumentParameterIndex;
- visitSyncForIn(ast.SyncForIn node) {
- // [node.declaredIdentifier] can be either an [ast.VariableDefinitions]
- // (defining a new local variable) or a send designating some existing
- // variable.
- ast.Node identifier = node.declaredIdentifier;
- ast.VariableDefinitions variableDeclaration =
- identifier.asVariableDefinitions();
- Element variableElement = elements.getForInVariable(node);
- Selector selector = elements.getSelector(identifier);
+ // If instances of the class may need runtime type information, we add a
+ // synthetic parameter for each type parameter.
+ if (requiresTypeInformation) {
+ firstTypeArgumentParameterIndex = parameters.length;
+ classElement.typeVariables.forEach((TypeVariableType variable) {
+ parameters.add(new closure.TypeVariableLocal(variable, constructor));
+ });
+ } else {
+ classElement.typeVariables.forEach((TypeVariableType variable) {
+ irBuilder.declareTypeVariable(variable, const DynamicType());
+ });
+ }
- irBuilder.buildForIn(
- buildExpression: subbuild(node.expression),
- buildVariableDeclaration: subbuild(variableDeclaration),
- variableElement: variableElement,
- variableSelector: selector,
- variableMask: elements.getTypeMask(identifier),
- currentMask: elements.getCurrentTypeMask(node),
- moveNextMask: elements.getMoveNextTypeMask(node),
- iteratorMask: elements.getIteratorTypeMask(node),
- buildBody: subbuild(node.body),
- target: elements.getTargetDefinition(node),
- closureScope: getClosureScopeForNode(node));
+ // Create IR parameters and setup the environment.
+ List<ir.Parameter> irParameters = builder.buildFunctionHeader(parameters,
+ closureScope: getClosureScopeForFunction(constructor));
+
+ // Create a list of the values of all type argument parameters, if any.
+ List<ir.Primitive> typeInformation;
+ if (requiresTypeInformation) {
+ typeInformation = irParameters.sublist(firstTypeArgumentParameterIndex);
+ } else {
+ typeInformation = const <ir.Primitive>[];
+ }
+
+ // -- Load values for type variables declared on super classes --
+ // Field initializers for super classes can reference these, so they
+ // must be available before evaluating field initializers.
+ // This could be interleaved with field initialization, but we choose do
+ // get it out of the way here to avoid complications with mixins.
+ loadTypeVariablesForSuperClasses(classElement);
+
+ /// Maps each field from this class or a superclass to its initial value.
+ Map<FieldElement, ir.Primitive> fieldValues =
+ <FieldElement, ir.Primitive>{};
+
+ // -- Evaluate field initializers ---
+ // Evaluate field initializers in constructor and super constructors.
+ List<ConstructorElement> constructorList = <ConstructorElement>[];
+ evaluateConstructorFieldInitializers(
+ constructor, constructorList, fieldValues);
+
+ // All parameters in all constructors are now bound in the environment.
+ // BoxLocals for captured parameters are also in the environment.
+ // The initial value of all fields are now bound in [fieldValues].
+
+ // --- Create the object ---
+ // Get the initial field values in the canonical order.
+ List<ir.Primitive> instanceArguments = <ir.Primitive>[];
+ classElement.forEachInstanceField((ClassElement c, FieldElement field) {
+ ir.Primitive value = fieldValues[field];
+ if (value != null) {
+ instanceArguments.add(value);
+ } else {
+ assert(backend.isNativeOrExtendsNative(c));
+ // Native fields are initialized elsewhere.
+ }
+ }, includeSuperAndInjectedMembers: true);
+
+ ir.Primitive instance = new ir.CreateInstance(
+ classElement,
+ instanceArguments,
+ typeInformation,
+ constructor.hasNode
+ ? sourceInformationBuilder.buildCreate(constructor.node)
+ // TODO(johnniwinther): Provide source information for creation
+ // through synthetic constructors.
+ : null);
+ irBuilder.add(new ir.LetPrim(instance));
+
+ // --- Call constructor bodies ---
+ for (ConstructorElement target in constructorList) {
+ ConstructorBodyElement bodyElement = getConstructorBody(target);
+ if (bodyElement == null) continue; // Skip if constructor has no body.
+ List<ir.Primitive> bodyArguments = <ir.Primitive>[];
+ for (Local param in getConstructorBodyParameters(bodyElement)) {
+ bodyArguments.add(irBuilder.environment.lookup(param));
+ }
+ irBuilder.buildInvokeDirectly(bodyElement, instance, bodyArguments);
+ }
+
+ // --- step 4: return the created object ----
+ irBuilder.buildReturn(
+ value: instance,
+ sourceInformation:
+ sourceInformationBuilder.buildImplicitReturn(constructor));
+
+ return irBuilder.makeFunctionDefinition();
+ });
}
- /// If compiling with trusted type annotations, assumes that [value] is
- /// now known to be `null` or an instance of [type].
+ /// Make a visitor suitable for translating ASTs taken from [context].
///
- /// This is also where we should add type checks in checked mode, but this
- /// is not supported yet.
- ir.Primitive checkType(ir.Primitive value, DartType dartType) {
- if (!compiler.trustTypeAnnotations) return value;
- TypeMask type = typeMaskSystem.subtypesOf(dartType).nullable();
- return irBuilder.buildRefinement(value, type);
+ /// Every visitor can only be applied to nodes in one context, because
+ /// the [elements] field is specific to that context.
+ IrBuilderVisitor makeVisitorForContext(AstElement context) {
+ return new IrBuilderVisitor(
+ context.resolvedAst.elements,
+ compiler,
+ sourceInformationBuilder.forContext(context),
+ typeMaskSystem);
}
- ir.Primitive checkTypeVsElement(ir.Primitive value, TypedElement element) {
- return checkType(value, element.type);
+ /// Builds the IR for an [expression] taken from a different [context].
+ ///
+ /// Such expressions need to be compiled with a different [sourceFile] and
+ /// [elements] mapping.
+ ir.Primitive inlineExpression(AstElement context, ast.Expression expression) {
+ IrBuilderVisitor visitor = makeVisitorForContext(context);
+ return visitor.withBuilder(irBuilder, () => visitor.visit(expression));
}
- ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) {
- assert(irBuilder.isOpen);
- for (ast.Node definition in node.definitions.nodes) {
- Element element = elements[definition];
- ir.Primitive initialValue;
- // Definitions are either SendSets if there is an initializer, or
- // Identifiers if there is no initializer.
- if (definition is ast.SendSet) {
- assert(!definition.arguments.isEmpty);
- assert(definition.arguments.tail.isEmpty);
- initialValue = visit(definition.arguments.head);
- initialValue = checkTypeVsElement(initialValue, element);
- } else {
- assert(definition is ast.Identifier);
- }
- irBuilder.declareLocalVariable(element, initialValue: initialValue);
- }
- return null;
+ /// Evaluate the implicit super call in the given mixin constructor.
+ void forwardSynthesizedMixinConstructor(
+ ConstructorElement constructor,
+ List<ConstructorElement> supers,
+ Map<FieldElement, ir.Primitive> fieldValues) {
+ assert(constructor.enclosingClass.implementation.isMixinApplication);
+ assert(constructor.isSynthesized);
+ ConstructorElement target =
+ constructor.definingConstructor.implementation;
+ // The resolver gives us the exact same FunctionSignature for the two
+ // constructors. The parameters for the synthesized constructor
+ // are already in the environment, so the target constructor's parameters
+ // are also in the environment since their elements are the same.
+ assert(constructor.functionSignature == target.functionSignature);
+ IrBuilderVisitor visitor = makeVisitorForContext(target);
+ visitor.withBuilder(irBuilder, () {
+ visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
+ });
}
- static final RegExp nativeRedirectionRegExp =
- new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$');
+ /// In preparation of inlining (part of) [target], the [arguments] are moved
+ /// into the environment bindings for the corresponding parameters.
+ ///
+ /// Defaults for optional arguments are evaluated in order to ensure
+ /// all parameters are available in the environment.
+ void loadArguments(ConstructorElement target,
+ CallStructure call,
+ List<ir.Primitive> arguments) {
+ assert(target.isImplementation);
+ assert(target == elements.analyzedElement);
+ FunctionSignature signature = target.functionSignature;
- // Build(Return(e), C) = C'[InvokeContinuation(return, x)]
- // where (C', x) = Build(e, C)
- //
- // Return without a subexpression is translated as if it were return null.
- visitReturn(ast.Return node) {
- assert(irBuilder.isOpen);
- SourceInformation source = sourceInformationBuilder.buildReturn(node);
- if (node.beginToken.value == 'native') {
- FunctionElement function = irBuilder.state.currentElement;
- assert(compiler.backend.isNative(function));
- ast.Node nativeBody = node.expression;
- if (nativeBody != null) {
- ast.LiteralString jsCode = nativeBody.asLiteralString();
- String javaScriptCode = jsCode.dartString.slowToString();
- assert(invariant(nativeBody,
- !nativeRedirectionRegExp.hasMatch(javaScriptCode),
- message: "Deprecated syntax, use @JSName('name') instead."));
- assert(invariant(nativeBody,
- function.functionSignature.parameterCount == 0,
- message: 'native "..." syntax is restricted to '
- 'functions with zero parameters.'));
- irBuilder.buildNativeFunctionBody(function, javaScriptCode);
- } else {
- JavaScriptBackend backend = compiler.backend;
- String name = backend.getFixedBackendName(function);
- irBuilder.buildRedirectingNativeFunctionBody(function, name, source);
- }
- } else {
- irBuilder.buildReturn(
- value: build(node.expression),
- sourceInformation: source);
- }
- }
+ // Establish a scope in case parameters are captured.
+ ClosureScope scope = getClosureScopeForFunction(target);
+ irBuilder.enterScope(scope);
- visitSwitchStatement(ast.SwitchStatement node) {
- assert(irBuilder.isOpen);
- // We do not handle switch statements with continue to labeled cases.
- for (ast.SwitchCase switchCase in node.cases) {
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- if (labelOrCase is ast.Label) {
- LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
- if (definition != null && definition.isContinueTarget) {
- return giveup(node, "continue to a labeled switch case");
- }
+ // Load required parameters
+ int index = 0;
+ signature.forEachRequiredParameter((ParameterElement param) {
+ irBuilder.declareLocalVariable(param, initialValue: arguments[index]);
+ index++;
+ });
+
+ // Load optional parameters, evaluating default values for omitted ones.
+ signature.forEachOptionalParameter((ParameterElement param) {
+ ir.Primitive value;
+ // Load argument if provided.
+ if (signature.optionalParametersAreNamed) {
+ int nameIndex = call.namedArguments.indexOf(param.name);
+ if (nameIndex != -1) {
+ int translatedIndex = call.positionalArgumentCount + nameIndex;
+ value = arguments[translatedIndex];
}
+ } else if (index < arguments.length) {
+ value = arguments[index];
}
- }
-
- // Each switch case contains a list of interleaved labels and expressions
- // and a non-empty body. We can ignore the labels because they are not
- // jump targets.
- List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
- SwitchCaseInfo defaultCase;
- for (ast.SwitchCase switchCase in node.cases) {
- SwitchCaseInfo caseInfo =
- new SwitchCaseInfo(subbuildSequence(switchCase.statements));
- if (switchCase.isDefaultCase) {
- defaultCase = caseInfo;
- } else {
- cases.add(caseInfo);
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- if (labelOrCase is ast.CaseMatch) {
- ir.Primitive constant = translateConstant(labelOrCase.expression);
- caseInfo.addConstant(constant);
- }
+ // Load default if argument was not provided.
+ if (value == null) {
+ if (param.initializer != null) {
+ value = visit(param.initializer);
+ } else {
+ value = irBuilder.buildNullConstant();
}
}
- }
- ir.Primitive value = visit(node.expression);
- JumpTarget target = elements.getTargetDefinition(node);
- Element error =
- (compiler.backend as JavaScriptBackend).helpers.fallThroughError;
- irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error,
- sourceInformationBuilder.buildGeneric(node));
+ irBuilder.declareLocalVariable(param, initialValue: value);
+ index++;
+ });
}
- visitTryStatement(ast.TryStatement node) {
- List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[];
- for (ast.CatchBlock catchClause in node.catchBlocks.nodes) {
- LocalVariableElement exceptionVariable;
- if (catchClause.exception != null) {
- exceptionVariable = elements[catchClause.exception];
- }
- LocalVariableElement stackTraceVariable;
- if (catchClause.trace != null) {
- stackTraceVariable = elements[catchClause.trace];
+ /// Evaluates a call to the given constructor from an initializer list.
+ ///
+ /// Calls [loadArguments] and [evaluateConstructorFieldInitializers] in a
+ /// visitor that has the proper [TreeElements] mapping.
+ void evaluateConstructorCallFromInitializer(
+ ConstructorElement target,
+ CallStructure call,
+ List<ir.Primitive> arguments,
+ List<ConstructorElement> supers,
+ Map<FieldElement, ir.Primitive> fieldValues) {
+ IrBuilderVisitor visitor = makeVisitorForContext(target);
+ visitor.withBuilder(irBuilder, () {
+ visitor.loadArguments(target, call, arguments);
+ visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
+ });
+ }
+
+ /// Evaluates all field initializers on [constructor] and all constructors
+ /// invoked through `this()` or `super()` ("superconstructors").
+ ///
+ /// The resulting field values will be available in [fieldValues]. The values
+ /// are not stored in any fields.
+ ///
+ /// This procedure assumes that the parameters to [constructor] are available
+ /// in the IR builder's environment.
+ ///
+ /// The parameters to superconstructors are, however, assumed *not* to be in
+ /// the environment, but will be put there by this procedure.
+ ///
+ /// All constructors will be added to [supers], with superconstructors first.
+ void evaluateConstructorFieldInitializers(
+ ConstructorElement constructor,
+ List<ConstructorElement> supers,
+ Map<FieldElement, ir.Primitive> fieldValues) {
+ assert(constructor.isImplementation);
+ assert(constructor == elements.analyzedElement);
+ ClassElement enclosingClass = constructor.enclosingClass.implementation;
+ // Evaluate declaration-site field initializers, unless this constructor
+ // redirects to another using a `this()` initializer. In that case, these
+ // will be initialized by the effective target constructor.
+ if (!constructor.isRedirectingGenerative) {
+ enclosingClass.forEachInstanceField((ClassElement c, FieldElement field) {
+ if (field.initializer != null) {
+ fieldValues[field] = inlineExpression(field, field.initializer);
+ } else {
+ if (backend.isNativeOrExtendsNative(c)) {
+ // Native field is initialized elsewhere.
+ } else {
+ // Fields without an initializer default to null.
+ // This value will be overwritten below if an initializer is found.
+ fieldValues[field] = irBuilder.buildNullConstant();
+ }
+ }
+ });
+ }
+ // If this is a mixin constructor, it does not have its own parameter list
+ // or initializer list. Directly forward to the super constructor.
+ // Note that the declaration-site initializers originating from the
+ // mixed-in class were handled above.
+ if (enclosingClass.isMixinApplication) {
+ forwardSynthesizedMixinConstructor(constructor, supers, fieldValues);
+ return;
+ }
+ // Evaluate initializing parameters, e.g. `Foo(this.x)`.
+ constructor.functionSignature.orderedForEachParameter(
+ (ParameterElement parameter) {
+ if (parameter.isInitializingFormal) {
+ InitializingFormalElement fieldParameter = parameter;
+ fieldValues[fieldParameter.fieldElement] =
+ irBuilder.buildLocalVariableGet(parameter);
}
- DartType type;
- if (catchClause.onKeyword != null) {
- type = elements.getType(catchClause.type);
+ });
+ // Evaluate constructor initializers, e.g. `Foo() : x = 50`.
+ ast.FunctionExpression node = constructor.node;
+ bool hasConstructorCall = false; // Has this() or super() initializer?
+ if (node != null && node.initializers != null) {
+ for(ast.Node initializer in node.initializers) {
+ if (initializer is ast.SendSet) {
+ // Field initializer.
+ FieldElement field = elements[initializer];
+ fieldValues[field] = visit(initializer.arguments.head);
+ } else if (initializer is ast.Send) {
+ // Super or this initializer.
+ ConstructorElement target = elements[initializer].implementation;
+ Selector selector = elements.getSelector(initializer);
+ List<ir.Primitive> arguments = initializer.arguments.mapToList(visit);
+ evaluateConstructorCallFromInitializer(
+ target,
+ selector.callStructure,
+ arguments,
+ supers,
+ fieldValues);
+ hasConstructorCall = true;
+ } else {
+ reporter.internalError(initializer,
+ "Unexpected initializer type $initializer");
+ }
}
- catchClauseInfos.add(new CatchClauseInfo(
- type: type,
- exceptionVariable: exceptionVariable,
- stackTraceVariable: stackTraceVariable,
- buildCatchBlock: subbuild(catchClause.block)));
}
-
- assert(!node.catchBlocks.isEmpty || node.finallyBlock != null);
- if (!node.catchBlocks.isEmpty && node.finallyBlock != null) {
- // Try/catch/finally is encoded in terms of try/catch and try/finally:
- //
- // try tryBlock catch (ex, st) catchBlock finally finallyBlock
- // ==>
- // try { try tryBlock catch (ex, st) catchBlock } finally finallyBlock
- irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
- (IrBuilder inner) {
- inner.buildTryCatch(tryStatements[node.catchBlocks],
- subbuild(node.tryBlock),
- catchClauseInfos);
- },
- subbuild(node.finallyBlock));
- } else if (!node.catchBlocks.isEmpty) {
- irBuilder.buildTryCatch(tryStatements[node.catchBlocks],
- subbuild(node.tryBlock),
- catchClauseInfos);
- } else {
- irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
- subbuild(node.tryBlock),
- subbuild(node.finallyBlock));
+ // If no super() or this() was found, also call default superconstructor.
+ if (!hasConstructorCall && !enclosingClass.isObject) {
+ ClassElement superClass = enclosingClass.superclass;
+ FunctionElement target = superClass.lookupDefaultConstructor();
+ if (target == null) {
+ reporter.internalError(superClass, "No default constructor available.");
+ }
+ target = target.implementation;
+ evaluateConstructorCallFromInitializer(
+ target,
+ CallStructure.NO_ARGS,
+ const [],
+ supers,
+ fieldValues);
}
+ // Add this constructor after the superconstructors.
+ supers.add(constructor);
}
- // ## Expressions ##
- ir.Primitive visitConditional(ast.Conditional node) {
- return irBuilder.buildConditional(
- build(node.condition),
- subbuild(node.thenExpression),
- subbuild(node.elseExpression));
- }
-
- // For all simple literals:
- // Build(Literal(c), C) = C[let val x = Constant(c) in [], x]
- ir.Primitive visitLiteralBool(ast.LiteralBool node) {
- assert(irBuilder.isOpen);
- return irBuilder.buildBooleanConstant(node.value);
- }
-
- ir.Primitive visitLiteralDouble(ast.LiteralDouble node) {
- assert(irBuilder.isOpen);
- return irBuilder.buildDoubleConstant(node.value);
- }
-
- ir.Primitive visitLiteralInt(ast.LiteralInt node) {
- assert(irBuilder.isOpen);
- return irBuilder.buildIntegerConstant(node.value);
+ TryBoxedVariables _analyzeTryBoxedVariables(ast.Node node) {
+ TryBoxedVariables variables = new TryBoxedVariables(elements);
+ try {
+ variables.analyze(node);
+ } catch (e) {
+ bailoutMessage = variables.bailoutMessage;
+ rethrow;
+ }
+ return variables;
}
- ir.Primitive visitLiteralNull(ast.LiteralNull node) {
- assert(irBuilder.isOpen);
- return irBuilder.buildNullConstant();
- }
+ /// Builds the IR for the body of a constructor.
+ ///
+ /// This function is invoked from one or more "factory" constructors built by
+ /// [buildConstructor].
+ ir.FunctionDefinition buildConstructorBody(ConstructorBodyElement body) {
+ ConstructorElement constructor = body.constructor;
+ ast.FunctionExpression node = constructor.node;
+ closureClassMap =
+ compiler.closureToClassMapper.computeClosureToClassMapping(
+ constructor,
+ node,
+ elements);
- ir.Primitive visitLiteralString(ast.LiteralString node) {
- assert(irBuilder.isOpen);
- return irBuilder.buildDartStringConstant(node.dartString);
- }
+ // We compute variables boxed in mutable variables on entry to each try
+ // block, not including variables captured by a closure (which are boxed
+ // in the heap). This duplicates some of the work of closure conversion
+ // without directly using the results. This duplication is wasteful and
+ // error-prone.
+ // TODO(kmillikin): We should combine closure conversion and try/catch
+ // variable analysis in some way.
+ TryBoxedVariables variables = _analyzeTryBoxedVariables(node);
+ tryStatements = variables.tryStatements;
+ IrBuilder builder = getBuilderFor(body);
- ConstantValue getConstantForNode(ast.Node node) {
- return irBuilder.state.constants.getConstantValueForNode(node, elements);
+ return withBuilder(builder, () {
+ irBuilder.buildConstructorBodyHeader(getConstructorBodyParameters(body),
+ getClosureScopeForNode(node));
+ visit(node.body);
+ return irBuilder.makeFunctionDefinition();
+ });
}
- ConstantValue getConstantForVariable(VariableElement element) {
- return irBuilder.state.constants.getConstantValueForVariable(element);
- }
+ ir.FunctionDefinition buildFunction(FunctionElement element) {
+ assert(invariant(element, element.isImplementation));
+ ast.FunctionExpression node = element.node;
- ir.Primitive buildConstantExpression(ConstantExpression expression,
- SourceInformation sourceInformation) {
- return irBuilder.buildConstant(
- irBuilder.state.constants.getConstantValue(expression),
- sourceInformation: sourceInformation);
- }
+ assert(!element.isSynthesized);
+ assert(node != null);
+ assert(elements[node] != null);
- ir.Primitive visitLiteralList(ast.LiteralList node) {
- if (node.isConst) {
- return translateConstant(node);
- }
- List<ir.Primitive> values = node.elements.nodes.mapToList(visit);
- InterfaceType type = elements.getType(node);
- return irBuilder.buildListLiteral(type, values,
- allocationSiteType: getAllocationSiteType(node));
+ closureClassMap =
+ compiler.closureToClassMapper.computeClosureToClassMapping(
+ element,
+ node,
+ elements);
+ TryBoxedVariables variables = _analyzeTryBoxedVariables(node);
+ tryStatements = variables.tryStatements;
+ IrBuilder builder = getBuilderFor(element);
+ return withBuilder(builder, () => _makeFunctionBody(element, node));
}
- ir.Primitive visitLiteralMap(ast.LiteralMap node) {
- if (node.isConst) {
- return translateConstant(node);
+ ir.FunctionDefinition buildStaticFieldInitializer(FieldElement element) {
+ if (!backend.constants.lazyStatics.contains(element)) {
+ return null; // Nothing to do.
}
- InterfaceType type = elements.getType(node);
- return irBuilder.buildMapLiteral(
- type,
- node.entries.nodes.map((e) => e.key),
- node.entries.nodes.map((e) => e.value),
- build);
- }
-
- ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) {
- assert(irBuilder.isOpen);
- return translateConstant(node);
- }
-
- ir.Primitive visitParenthesizedExpression(
- ast.ParenthesizedExpression node) {
- assert(irBuilder.isOpen);
- return visit(node.expression);
- }
-
- // Stores the result of visiting a CascadeReceiver, so we can return it from
- // its enclosing Cascade.
- ir.Primitive _currentCascadeReceiver;
-
- ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) {
- assert(irBuilder.isOpen);
- return _currentCascadeReceiver = visit(node.expression);
+ closureClassMap =
+ compiler.closureToClassMapper.computeClosureToClassMapping(
+ element,
+ element.node,
+ elements);
+ IrBuilder builder = getBuilderFor(element);
+ return withBuilder(builder, () {
+ irBuilder.buildFunctionHeader(<Local>[]);
+ ir.Primitive initialValue = visit(element.initializer);
+ ast.VariableDefinitions node = element.node;
+ ast.SendSet sendSet = node.definitions.nodes.head;
+ irBuilder.buildReturn(
+ value: initialValue,
+ sourceInformation:
+ sourceInformationBuilder.buildReturn(sendSet.assignmentOperator));
+ return irBuilder.makeFunctionDefinition();
+ });
}
- ir.Primitive visitCascade(ast.Cascade node) {
- assert(irBuilder.isOpen);
- var oldCascadeReceiver = _currentCascadeReceiver;
- // Throw away the result of visiting the expression.
- // Instead we return the result of visiting the CascadeReceiver.
- visit(node.expression);
- ir.Primitive receiver = _currentCascadeReceiver;
- _currentCascadeReceiver = oldCascadeReceiver;
- return receiver;
+ /// Builds the IR for a constant taken from a different [context].
+ ///
+ /// Such constants need to be compiled with a different [sourceFile] and
+ /// [elements] mapping.
+ ir.Primitive inlineConstant(AstElement context, ast.Expression exp) {
+ IrBuilderVisitor visitor = makeVisitorForContext(context);
+ return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp));
}
- @override
- ir.Primitive visitAssert(ast.Assert node) {
- assert(irBuilder.isOpen);
- if (compiler.enableUserAssertions) {
- return giveup(node, 'assert in checked mode not implemented');
+ /// Creates a primitive for the default value of [parameter].
+ ir.Primitive translateDefaultValue(ParameterElement parameter) {
+ if (parameter.initializer == null) {
+ return irBuilder.buildNullConstant();
} else {
- // The call to assert and its argument expression must be ignored
- // in production mode.
- // Assertions can only occur in expression statements, so no value needs
- // to be returned.
- return null;
+ return inlineConstant(parameter.executableContext, parameter.initializer);
}
}
- // ## Sends ##
- @override
- void previsitDeferredAccess(ast.Send node, PrefixElement prefix, _) {
- if (prefix != null) buildCheckDeferredIsLoaded(prefix, node);
- }
-
- /// Create a call to check that a deferred import has already been loaded.
- ir.Primitive buildCheckDeferredIsLoaded(PrefixElement prefix, ast.Send node) {
- SourceInformation sourceInformation =
- sourceInformationBuilder.buildCall(node, node.selector);
- JavaScriptBackend backend = compiler.backend;
- return irBuilder.buildStaticFunctionInvocation(
- backend.helpers.checkDeferredIsLoaded,
- CallStructure.TWO_ARGS, <ir.Primitive>[
- irBuilder.buildStringConstant(
- compiler.deferredLoadTask.getImportDeferName(node, prefix)),
- irBuilder.buildStringConstant('${prefix.deferredImport.uri}'),
- ], sourceInformation: sourceInformation);
- }
+ /// Normalizes the argument list of a static invocation.
+ ///
+ /// A static invocation is one where the target is known. The argument list
+ /// [arguments] is normalized by adding default values for optional arguments
+ /// that are not passed, and by sorting it in place so that named arguments
+ /// appear in a canonical order. A [CallStructure] reflecting this order
+ /// is returned.
+ CallStructure normalizeStaticArguments(CallStructure callStructure,
+ FunctionElement target,
+ List<ir.Primitive> arguments) {
+ target = target.implementation;
+ FunctionSignature signature = target.functionSignature;
+ if (!signature.optionalParametersAreNamed &&
+ signature.parameterCount == arguments.length) {
+ return callStructure;
+ }
- ir.Primitive visitNamedArgument(ast.NamedArgument node) {
- assert(irBuilder.isOpen);
- return visit(node.expression);
- }
+ if (!signature.optionalParametersAreNamed) {
+ int i = signature.requiredParameterCount;
+ signature.forEachOptionalParameter((ParameterElement element) {
+ if (i < callStructure.positionalArgumentCount) {
+ ++i;
+ } else {
+ arguments.add(translateDefaultValue(element));
+ }
+ });
+ return new CallStructure(signature.parameterCount);
+ }
- @override
- ir.Primitive visitExpressionInvoke(ast.Send node,
- ast.Node expression,
- ast.NodeList argumentsNode,
- CallStructure callStructure, _) {
- ir.Primitive receiver = visit(expression);
- List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
- callStructure = normalizeDynamicArguments(callStructure, arguments);
- return irBuilder.buildCallInvocation(receiver, callStructure, arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, argumentsNode));
+ int offset = signature.requiredParameterCount;
+ List<ir.Primitive> namedArguments = arguments.sublist(offset);
+ arguments.length = offset;
+ List<String> normalizedNames = <String>[];
+ // Iterate over the optional parameters of the signature, and try to
+ // find them in the callStructure's named arguments. If found, we use the
+ // value in the temporary list, otherwise the default value.
+ signature.orderedOptionalParameters.forEach((ParameterElement element) {
+ int nameIndex = callStructure.namedArguments.indexOf(element.name);
+ arguments.add(nameIndex == -1 ? translateDefaultValue(element)
+ : namedArguments[nameIndex]);
+ normalizedNames.add(element.name);
+ });
+ return new CallStructure(signature.parameterCount, normalizedNames);
}
- /// Returns `true` if [node] is a super call.
- // TODO(johnniwinther): Remove the need for this.
- bool isSuperCall(ast.Send node) {
- return node != null && node.receiver != null && node.receiver.isSuper();
+ /// Normalizes the argument list of a dynamic invocation.
+ ///
+ /// A dynamic invocation is one where the target is not known. The argument
+ /// list [arguments] is normalized by sorting it in place so that the named
+ /// arguments appear in a canonical order. A [CallStructure] reflecting this
+ /// order is returned.
+ CallStructure normalizeDynamicArguments(CallStructure callStructure,
+ List<ir.Primitive> arguments) {
+ assert(arguments.length == callStructure.argumentCount);
+ if (callStructure.namedArguments.isEmpty) return callStructure;
+ int destinationIndex = callStructure.positionalArgumentCount;
+ List<ir.Primitive> namedArguments = arguments.sublist(destinationIndex);
+ for (String argName in callStructure.getOrderedNamedArguments()) {
+ int sourceIndex = callStructure.namedArguments.indexOf(argName);
+ arguments[destinationIndex++] = namedArguments[sourceIndex];
+ }
+ return new CallStructure(callStructure.argumentCount,
+ callStructure.getOrderedNamedArguments());
}
- @override
- ir.Primitive handleConstantGet(
- ast.Node node,
- ConstantExpression constant, _) {
- return buildConstantExpression(constant,
- sourceInformationBuilder.buildGet(node));
+ /// Read the value of [field].
+ ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src) {
+ ConstantValue constant = getConstantForVariable(field);
+ if (constant != null && !field.isAssignable) {
+ typeMaskSystem.associateConstantValueWithElement(constant, field);
+ return irBuilder.buildConstant(constant, sourceInformation: src);
+ } else if (backend.constants.lazyStatics.contains(field)) {
+ return irBuilder.buildStaticFieldLazyGet(field, src);
+ } else {
+ return irBuilder.buildStaticFieldGet(field, src);
+ }
}
- /// If [node] is null, returns this.
- /// Otherwise visits [node] and returns the result.
- ir.Primitive translateReceiver(ast.Expression node) {
- return node != null ? visit(node) : irBuilder.buildThis();
- }
+ ir.FunctionDefinition _makeFunctionBody(FunctionElement element,
+ ast.FunctionExpression node) {
+ FunctionSignature signature = element.functionSignature;
+ List<Local> parameters = <Local>[];
+ signature.orderedForEachParameter(
+ (LocalParameterElement e) => parameters.add(e));
- @override
- ir.Primitive handleDynamicGet(
- ast.Send node,
- ast.Node receiver,
- Name name,
- _) {
- return irBuilder.buildDynamicGet(
- translateReceiver(receiver),
- new Selector.getter(name),
- elements.getTypeMask(node),
- sourceInformationBuilder.buildGet(node));
+ if (element.isFactoryConstructor) {
+ // Type arguments are passed in as extra parameters.
+ for (DartType typeVariable in element.enclosingClass.typeVariables) {
+ parameters.add(new closure.TypeVariableLocal(typeVariable, element));
+ }
+ }
+
+ irBuilder.buildFunctionHeader(parameters,
+ closureScope: getClosureScopeForNode(node),
+ env: getClosureEnvironment());
+
+ visit(node.body);
+ return irBuilder.makeFunctionDefinition();
}
- @override
- ir.Primitive visitIfNotNullDynamicPropertyGet(
- ast.Send node,
- ast.Node receiver,
- Name name,
- _) {
- ir.Primitive target = visit(receiver);
- return irBuilder.buildIfNotNullSend(
- target,
- nested(() => irBuilder.buildDynamicGet(
- target,
- new Selector.getter(name),
- elements.getTypeMask(node),
- sourceInformationBuilder.buildGet(node))));
+ /// Builds the IR for creating an instance of the closure class corresponding
+ /// to the given nested function.
+ closure.ClosureClassElement makeSubFunction(ast.FunctionExpression node) {
+ closure.ClosureClassMap innerMap =
+ compiler.closureToClassMapper.getMappingForNestedFunction(node);
+ closure.ClosureClassElement closureClass = innerMap.closureClassElement;
+ return closureClass;
}
- @override
- ir.Primitive visitDynamicTypeLiteralGet(
- ast.Send node,
- ConstantExpression constant,
- _) {
- return buildConstantExpression(constant,
- sourceInformationBuilder.buildGet(node));
+ ir.Primitive visitFunctionExpression(ast.FunctionExpression node) {
+ return irBuilder.buildFunctionExpression(makeSubFunction(node),
+ sourceInformationBuilder.buildCreate(node));
}
- @override
- ir.Primitive visitLocalVariableGet(
- ast.Send node,
- LocalVariableElement element,
- _) {
- return element.isConst
- ? irBuilder.buildConstant(getConstantForVariable(element),
- sourceInformation: sourceInformationBuilder.buildGet(node))
- : irBuilder.buildLocalVariableGet(element);
+ visitFunctionDeclaration(ast.FunctionDeclaration node) {
+ LocalFunctionElement element = elements[node.function];
+ Object inner = makeSubFunction(node.function);
+ irBuilder.declareLocalFunction(element, inner,
+ sourceInformationBuilder.buildCreate(node.function));
}
- @override
- ir.Primitive handleLocalGet(
- ast.Send node,
- LocalElement element,
- _) {
- return irBuilder.buildLocalVariableGet(element);
+ // ## Statements ##
+ visitBlock(ast.Block node) {
+ irBuilder.buildBlock(node.statements.nodes, build);
}
- @override
- ir.Primitive visitLocalFunctionGet(
- ast.Send node,
- LocalFunctionElement function,
- _) {
- return irBuilder.buildLocalFunctionGet(function);
+ ir.Primitive visitBreakStatement(ast.BreakStatement node) {
+ if (!irBuilder.buildBreak(elements.getTargetOf(node))) {
+ reporter.internalError(node, "'break' target not found");
+ }
+ return null;
}
- @override
- ir.Primitive handleStaticFunctionGet(
- ast.Send node,
- MethodElement function,
- _) {
- return irBuilder.buildStaticFunctionGet(function);
+ ir.Primitive visitContinueStatement(ast.ContinueStatement node) {
+ if (!irBuilder.buildContinue(elements.getTargetOf(node))) {
+ reporter.internalError(node, "'continue' target not found");
+ }
+ return null;
}
- @override
- ir.Primitive handleStaticGetterGet(
- ast.Send node,
- FunctionElement getter,
- _) {
- return buildStaticGetterGet(getter, node);
+ // Build(EmptyStatement, C) = C
+ ir.Primitive visitEmptyStatement(ast.EmptyStatement node) {
+ assert(irBuilder.isOpen);
+ return null;
}
- /// Create a getter invocation of the static getter [getter]. This also
- /// handles the special case where [getter] is the `loadLibrary`
- /// pseudo-function on library prefixes of deferred imports.
- ir.Primitive buildStaticGetterGet(MethodElement getter, ast.Send node) {
- SourceInformation sourceInformation =
- sourceInformationBuilder.buildGet(node);
- if (getter.isDeferredLoaderGetter) {
- PrefixElement prefix = getter.enclosingElement;
- ir.Primitive loadId = irBuilder.buildStringConstant(
- compiler.deferredLoadTask.getImportDeferName(node, prefix));
- return irBuilder.buildStaticFunctionInvocation(
- compiler.loadLibraryFunction,
- CallStructure.ONE_ARG, <ir.Primitive>[loadId],
- sourceInformation: sourceInformation);
+ // Build(ExpressionStatement(e), C) = C'
+ // where (C', _) = Build(e, C)
+ ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) {
+ assert(irBuilder.isOpen);
+ if (node.expression is ast.Throw) {
+ // Throw expressions that occur as statements are translated differently
+ // from ones that occur as subexpressions. This is achieved by peeking
+ // at statement-level expressions here.
+ irBuilder.buildThrow(visit(node.expression));
} else {
- return irBuilder.buildStaticGetterGet(getter, sourceInformation);
+ visit(node.expression);
}
+ return null;
}
- @override
- ir.Primitive visitSuperFieldGet(
- ast.Send node,
- FieldElement field,
- _) {
- return irBuilder.buildSuperFieldGet(field);
+ ir.Primitive visitRethrow(ast.Rethrow node) {
+ assert(irBuilder.isOpen);
+ irBuilder.buildRethrow();
+ return null;
}
- @override
- ir.Primitive visitSuperGetterGet(
- ast.Send node,
- FunctionElement getter,
- _) {
- return irBuilder.buildSuperGetterGet(
- getter, sourceInformationBuilder.buildGet(node));
+ /// Construct a method that executes the forwarding call to the target
+ /// constructor. This is only required, if the forwarding factory
+ /// constructor can potentially be the target of a reflective call, because
+ /// the builder shortcuts calls to redirecting factories at the call site
+ /// (see [handleConstructorInvoke]).
+ visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
+ ConstructorElement targetConstructor =
+ elements.getRedirectingTargetConstructor(node).implementation;
+ ConstructorElement redirectingConstructor =
+ irBuilder.state.currentElement.implementation;
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ FunctionSignature redirectingSignature =
+ redirectingConstructor.functionSignature;
+ List<String> namedParameters = <String>[];
+ redirectingSignature.forEachParameter((ParameterElement parameter) {
+ arguments.add(irBuilder.environment.lookup(parameter));
+ if (parameter.isNamed) {
+ namedParameters.add(parameter.name);
+ }
+ });
+ ClassElement cls = redirectingConstructor.enclosingClass;
+ InterfaceType targetType =
+ redirectingConstructor.computeEffectiveTargetType(cls.thisType);
+ CallStructure callStructure = new CallStructure(
+ redirectingSignature.parameterCount,
+ namedParameters);
+ callStructure =
+ normalizeStaticArguments(callStructure, targetConstructor, arguments);
+ ir.Primitive instance = irBuilder.buildConstructorInvocation(
+ targetConstructor,
+ callStructure,
+ targetType,
+ arguments,
+ sourceInformationBuilder.buildNew(node));
+ irBuilder.buildReturn(
+ value: instance,
+ sourceInformation: sourceInformationBuilder.buildReturn(node));
}
- @override
- ir.Primitive visitSuperMethodGet(
- ast.Send node,
- MethodElement method,
- _) {
- return irBuilder.buildSuperMethodGet(method);
+ visitFor(ast.For node) {
+ List<LocalElement> loopVariables = <LocalElement>[];
+ if (node.initializer is ast.VariableDefinitions) {
+ ast.VariableDefinitions definitions = node.initializer;
+ for (ast.Node node in definitions.definitions.nodes) {
+ LocalElement loopVariable = elements[node];
+ loopVariables.add(loopVariable);
+ }
+ }
+
+ JumpTarget target = elements.getTargetDefinition(node);
+ irBuilder.buildFor(
+ buildInitializer: subbuild(node.initializer),
+ buildCondition: subbuild(node.condition),
+ buildBody: subbuild(node.body),
+ buildUpdate: subbuildSequence(node.update),
+ closureScope: getClosureScopeForNode(node),
+ loopVariables: loopVariables,
+ target: target);
}
- @override
- ir.Primitive visitUnresolvedSuperGet(
- ast.Send node,
- Element element, _) {
- return buildInstanceNoSuchMethod(
- elements.getSelector(node), elements.getTypeMask(node), []);
+ visitIf(ast.If node) {
+ irBuilder.buildIf(
+ build(node.condition),
+ subbuild(node.thenPart),
+ subbuild(node.elsePart));
}
- @override
- ir.Primitive visitThisGet(ast.Identifier node, _) {
- if (irBuilder.state.thisParameter == null) {
- // TODO(asgerf,johnniwinther): Should be in a visitInvalidThis method.
- // 'this' in static context. Just translate to null.
- assert(compiler.compilationFailed);
- return irBuilder.buildNullConstant();
+ visitLabeledStatement(ast.LabeledStatement node) {
+ ast.Statement body = node.statement;
+ if (body is ast.Loop) {
+ visit(body);
+ } else {
+ JumpTarget target = elements.getTargetDefinition(body);
+ irBuilder.buildLabeledStatement(
+ buildBody: subbuild(body),
+ target: target);
}
- return irBuilder.buildThis();
}
- ir.Primitive translateTypeVariableTypeLiteral(
- TypeVariableElement element,
- SourceInformation sourceInformation) {
- return irBuilder.buildReifyTypeVariable(element.type, sourceInformation);
+ visitDoWhile(ast.DoWhile node) {
+ irBuilder.buildDoWhile(
+ buildBody: subbuild(node.body),
+ buildCondition: subbuild(node.condition),
+ target: elements.getTargetDefinition(node),
+ closureScope: getClosureScopeForNode(node));
}
- @override
- ir.Primitive visitTypeVariableTypeLiteralGet(ast.Send node,
- TypeVariableElement element, _) {
- return translateTypeVariableTypeLiteral(element,
- sourceInformationBuilder.buildGet(node));
+ visitWhile(ast.While node) {
+ irBuilder.buildWhile(
+ buildCondition: subbuild(node.condition),
+ buildBody: subbuild(node.body),
+ target: elements.getTargetDefinition(node),
+ closureScope: getClosureScopeForNode(node));
}
- ir.Primitive translateLogicalOperator(ast.Expression left,
- ast.Expression right,
- {bool isLazyOr}) {
- ir.Primitive leftValue = visit(left);
-
- ir.Primitive buildRightValue(IrBuilder rightBuilder) {
- return withBuilder(rightBuilder, () => visit(right));
- }
-
- return irBuilder.buildLogicalOperator(
- leftValue, buildRightValue, isLazyOr: isLazyOr);
+ visitAsyncForIn(ast.AsyncForIn node) {
+ // await for is not yet implemented.
+ return giveup(node, 'await for');
}
- @override
- ir.Primitive visitIfNull(
- ast.Send node, ast.Node left, ast.Node right, _) {
- return irBuilder.buildIfNull(build(left), subbuild(right));
+ visitAwait(ast.Await node) {
+ ir.Primitive value = visit(node.expression);
+ return irBuilder.buildAwait(value);
}
- @override
- ir.Primitive visitLogicalAnd(
- ast.Send node, ast.Node left, ast.Node right, _) {
- return translateLogicalOperator(left, right, isLazyOr: false);
+ visitYield(ast.Yield node) {
+ ir.Primitive value = visit(node.expression);
+ return irBuilder.buildYield(value, node.hasStar);
}
- @override
- ir.Primitive visitLogicalOr(
- ast.Send node, ast.Node left, ast.Node right, _) {
- return translateLogicalOperator(left, right, isLazyOr: true);
+ visitSyncForIn(ast.SyncForIn node) {
+ // [node.declaredIdentifier] can be either an [ast.VariableDefinitions]
+ // (defining a new local variable) or a send designating some existing
+ // variable.
+ ast.Node identifier = node.declaredIdentifier;
+ ast.VariableDefinitions variableDeclaration =
+ identifier.asVariableDefinitions();
+ Element variableElement = elements.getForInVariable(node);
+ Selector selector = elements.getSelector(identifier);
+
+ irBuilder.buildForIn(
+ buildExpression: subbuild(node.expression),
+ buildVariableDeclaration: subbuild(variableDeclaration),
+ variableElement: variableElement,
+ variableSelector: selector,
+ variableMask: elements.getTypeMask(identifier),
+ currentMask: elements.getCurrentTypeMask(node),
+ moveNextMask: elements.getMoveNextTypeMask(node),
+ iteratorMask: elements.getIteratorTypeMask(node),
+ buildBody: subbuild(node.body),
+ target: elements.getTargetDefinition(node),
+ closureScope: getClosureScopeForNode(node));
}
- @override
- ir.Primitive visitAs(
- ast.Send node,
- ast.Node expression,
- DartType type,
- _) {
- ir.Primitive receiver = visit(expression);
- return irBuilder.buildTypeOperator(receiver, type, isTypeTest: false);
+ /// If compiling with trusted type annotations, assumes that [value] is
+ /// now known to be `null` or an instance of [type].
+ ///
+ /// This is also where we should add type checks in checked mode, but this
+ /// is not supported yet.
+ ir.Primitive checkType(ir.Primitive value, DartType dartType) {
+ if (!compiler.trustTypeAnnotations) return value;
+ TypeMask type = typeMaskSystem.subtypesOf(dartType).nullable();
+ return irBuilder.buildRefinement(value, type);
}
- @override
- ir.Primitive visitIs(
- ast.Send node,
- ast.Node expression,
- DartType type,
- _) {
- ir.Primitive value = visit(expression);
- return irBuilder.buildTypeOperator(value, type, isTypeTest: true);
+ ir.Primitive checkTypeVsElement(ir.Primitive value, TypedElement element) {
+ return checkType(value, element.type);
}
- @override
- ir.Primitive visitIsNot(ast.Send node,
- ast.Node expression, DartType type, _) {
- ir.Primitive value = visit(expression);
- ir.Primitive check = irBuilder.buildTypeOperator(
- value, type, isTypeTest: true);
- return irBuilder.buildNegation(check);
+ ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) {
+ assert(irBuilder.isOpen);
+ for (ast.Node definition in node.definitions.nodes) {
+ Element element = elements[definition];
+ ir.Primitive initialValue;
+ // Definitions are either SendSets if there is an initializer, or
+ // Identifiers if there is no initializer.
+ if (definition is ast.SendSet) {
+ assert(!definition.arguments.isEmpty);
+ assert(definition.arguments.tail.isEmpty);
+ initialValue = visit(definition.arguments.head);
+ initialValue = checkTypeVsElement(initialValue, element);
+ } else {
+ assert(definition is ast.Identifier);
+ }
+ irBuilder.declareLocalVariable(element, initialValue: initialValue);
+ }
+ return null;
}
- ir.Primitive translateBinary(ast.Send node,
- ast.Node left,
- op.BinaryOperator operator,
- ast.Node right) {
- ir.Primitive receiver = visit(left);
- Selector selector = new Selector.binaryOperator(operator.selectorName);
- List<ir.Primitive> arguments = <ir.Primitive>[visit(right)];
- CallStructure callStructure =
- normalizeDynamicArguments(selector.callStructure, arguments);
- return irBuilder.buildDynamicInvocation(
- receiver,
- new Selector(selector.kind, selector.memberName, callStructure),
- elements.getTypeMask(node),
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, node.selector));
+ static final RegExp nativeRedirectionRegExp =
+ new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$');
+
+ // Build(Return(e), C) = C'[InvokeContinuation(return, x)]
+ // where (C', x) = Build(e, C)
+ //
+ // Return without a subexpression is translated as if it were return null.
+ visitReturn(ast.Return node) {
+ assert(irBuilder.isOpen);
+ SourceInformation source = sourceInformationBuilder.buildReturn(node);
+ if (node.beginToken.value == 'native') {
+ FunctionElement function = irBuilder.state.currentElement;
+ assert(backend.isNative(function));
+ ast.Node nativeBody = node.expression;
+ if (nativeBody != null) {
+ ast.LiteralString jsCode = nativeBody.asLiteralString();
+ String javaScriptCode = jsCode.dartString.slowToString();
+ assert(invariant(nativeBody,
+ !nativeRedirectionRegExp.hasMatch(javaScriptCode),
+ message: "Deprecated syntax, use @JSName('name') instead."));
+ assert(invariant(nativeBody,
+ function.functionSignature.parameterCount == 0,
+ message: 'native "..." syntax is restricted to '
+ 'functions with zero parameters.'));
+ irBuilder.buildNativeFunctionBody(function, javaScriptCode);
+ } else {
+ String name = backend.getFixedBackendName(function);
+ irBuilder.buildRedirectingNativeFunctionBody(function, name, source);
+ }
+ } else {
+ irBuilder.buildReturn(
+ value: build(node.expression),
+ sourceInformation: source);
+ }
}
- @override
- ir.Primitive visitBinary(ast.Send node,
- ast.Node left,
- op.BinaryOperator operator,
- ast.Node right, _) {
- return translateBinary(node, left, operator, right);
+ visitSwitchStatement(ast.SwitchStatement node) {
+ assert(irBuilder.isOpen);
+ // We do not handle switch statements with continue to labeled cases.
+ for (ast.SwitchCase switchCase in node.cases) {
+ for (ast.Node labelOrCase in switchCase.labelsAndCases) {
+ if (labelOrCase is ast.Label) {
+ LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
+ if (definition != null && definition.isContinueTarget) {
+ return giveup(node, "continue to a labeled switch case");
+ }
+ }
+ }
+ }
+
+ // Each switch case contains a list of interleaved labels and expressions
+ // and a non-empty body. We can ignore the labels because they are not
+ // jump targets.
+ List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
+ SwitchCaseInfo defaultCase;
+ for (ast.SwitchCase switchCase in node.cases) {
+ SwitchCaseInfo caseInfo =
+ new SwitchCaseInfo(subbuildSequence(switchCase.statements));
+ if (switchCase.isDefaultCase) {
+ defaultCase = caseInfo;
+ } else {
+ cases.add(caseInfo);
+ for (ast.Node labelOrCase in switchCase.labelsAndCases) {
+ if (labelOrCase is ast.CaseMatch) {
+ ir.Primitive constant = translateConstant(labelOrCase.expression);
+ caseInfo.addConstant(constant);
+ }
+ }
+ }
+ }
+ ir.Primitive value = visit(node.expression);
+ JumpTarget target = elements.getTargetDefinition(node);
+ Element error = helpers.fallThroughError;
+ irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error,
+ sourceInformationBuilder.buildGeneric(node));
}
- @override
- ir.Primitive visitIndex(ast.Send node,
- ast.Node receiver,
- ast.Node index, _) {
- ir.Primitive target = visit(receiver);
- Selector selector = new Selector.index();
- List<ir.Primitive> arguments = <ir.Primitive>[visit(index)];
- CallStructure callStructure =
- normalizeDynamicArguments(selector.callStructure, arguments);
- return irBuilder.buildDynamicInvocation(
- target,
- new Selector(selector.kind, selector.memberName, callStructure),
- elements.getTypeMask(node),
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(receiver, node.selector));
- }
+ visitTryStatement(ast.TryStatement node) {
+ List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[];
+ for (ast.CatchBlock catchClause in node.catchBlocks.nodes) {
+ LocalVariableElement exceptionVariable;
+ if (catchClause.exception != null) {
+ exceptionVariable = elements[catchClause.exception];
+ }
+ LocalVariableElement stackTraceVariable;
+ if (catchClause.trace != null) {
+ stackTraceVariable = elements[catchClause.trace];
+ }
+ DartType type;
+ if (catchClause.onKeyword != null) {
+ type = elements.getType(catchClause.type);
+ }
+ catchClauseInfos.add(new CatchClauseInfo(
+ type: type,
+ exceptionVariable: exceptionVariable,
+ stackTraceVariable: stackTraceVariable,
+ buildCatchBlock: subbuild(catchClause.block)));
+ }
- ir.Primitive translateSuperBinary(FunctionElement function,
- op.BinaryOperator operator,
- ast.Node argument) {
- List<ir.Primitive> arguments = <ir.Primitive>[visit(argument)];
- return irBuilder.buildSuperMethodInvocation(function,
- CallStructure.ONE_ARG, arguments);
+ assert(!node.catchBlocks.isEmpty || node.finallyBlock != null);
+ if (!node.catchBlocks.isEmpty && node.finallyBlock != null) {
+ // Try/catch/finally is encoded in terms of try/catch and try/finally:
+ //
+ // try tryBlock catch (ex, st) catchBlock finally finallyBlock
+ // ==>
+ // try { try tryBlock catch (ex, st) catchBlock } finally finallyBlock
+ irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
+ (IrBuilder inner) {
+ inner.buildTryCatch(tryStatements[node.catchBlocks],
+ subbuild(node.tryBlock),
+ catchClauseInfos);
+ },
+ subbuild(node.finallyBlock));
+ } else if (!node.catchBlocks.isEmpty) {
+ irBuilder.buildTryCatch(tryStatements[node.catchBlocks],
+ subbuild(node.tryBlock),
+ catchClauseInfos);
+ } else {
+ irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
+ subbuild(node.tryBlock),
+ subbuild(node.finallyBlock));
+ }
}
- @override
- ir.Primitive visitSuperBinary(
- ast.Send node,
- FunctionElement function,
- op.BinaryOperator operator,
- ast.Node argument,
- _) {
- return translateSuperBinary(function, operator, argument);
+ // ## Expressions ##
+ ir.Primitive visitConditional(ast.Conditional node) {
+ return irBuilder.buildConditional(
+ build(node.condition),
+ subbuild(node.thenExpression),
+ subbuild(node.elseExpression));
}
- @override
- ir.Primitive visitSuperIndex(
- ast.Send node,
- FunctionElement function,
- ast.Node index,
- _) {
- return irBuilder.buildSuperIndex(function, visit(index));
+ // For all simple literals:
+ // Build(Literal(c), C) = C[let val x = Constant(c) in [], x]
+ ir.Primitive visitLiteralBool(ast.LiteralBool node) {
+ assert(irBuilder.isOpen);
+ return irBuilder.buildBooleanConstant(node.value);
}
- @override
- ir.Primitive visitEquals(
- ast.Send node,
- ast.Node left,
- ast.Node right,
- _) {
- return translateBinary(node, left, op.BinaryOperator.EQ, right);
+ ir.Primitive visitLiteralDouble(ast.LiteralDouble node) {
+ assert(irBuilder.isOpen);
+ return irBuilder.buildDoubleConstant(node.value);
}
- @override
- ir.Primitive visitSuperEquals(
- ast.Send node,
- FunctionElement function,
- ast.Node argument,
- _) {
- return translateSuperBinary(function, op.BinaryOperator.EQ, argument);
+ ir.Primitive visitLiteralInt(ast.LiteralInt node) {
+ assert(irBuilder.isOpen);
+ return irBuilder.buildIntegerConstant(node.value);
}
- @override
- ir.Primitive visitNot(
- ast.Send node,
- ast.Node expression,
- _) {
- return irBuilder.buildNegation(visit(expression));
+ ir.Primitive visitLiteralNull(ast.LiteralNull node) {
+ assert(irBuilder.isOpen);
+ return irBuilder.buildNullConstant();
}
- @override
- ir.Primitive visitNotEquals(
- ast.Send node,
- ast.Node left,
- ast.Node right,
- _) {
- return irBuilder.buildNegation(
- translateBinary(node, left, op.BinaryOperator.NOT_EQ, right));
+ ir.Primitive visitLiteralString(ast.LiteralString node) {
+ assert(irBuilder.isOpen);
+ return irBuilder.buildDartStringConstant(node.dartString);
}
- @override
- ir.Primitive visitSuperNotEquals(
- ast.Send node,
- FunctionElement function,
- ast.Node argument,
- _) {
- return irBuilder.buildNegation(
- translateSuperBinary(function, op.BinaryOperator.NOT_EQ, argument));
+ ConstantValue getConstantForNode(ast.Node node) {
+ return irBuilder.state.constants.getConstantValueForNode(node, elements);
}
- @override
- ir.Primitive visitUnary(ast.Send node,
- op.UnaryOperator operator, ast.Node expression, _) {
- // TODO(johnniwinther): Clean up the creation of selectors.
- Selector selector = operator.selector;
- ir.Primitive receiver = translateReceiver(expression);
- return irBuilder.buildDynamicInvocation(
- receiver, selector, elements.getTypeMask(node), const [],
- sourceInformation: sourceInformationBuilder.buildCall(
- expression, node));
+ ConstantValue getConstantForVariable(VariableElement element) {
+ return irBuilder.state.constants.getConstantValueForVariable(element);
}
- @override
- ir.Primitive visitSuperUnary(
- ast.Send node,
- op.UnaryOperator operator,
- FunctionElement function,
- _) {
- return irBuilder.buildSuperMethodInvocation(
- function, CallStructure.NO_ARGS, const []);
+ ir.Primitive buildConstantExpression(ConstantExpression expression,
+ SourceInformation sourceInformation) {
+ return irBuilder.buildConstant(
+ irBuilder.state.constants.getConstantValue(expression),
+ sourceInformation: sourceInformation);
}
- // TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
- // semantic correlation between arguments and invocation.
- CallStructure translateDynamicArguments(ast.NodeList nodeList,
- CallStructure callStructure,
- List<ir.Primitive> arguments) {
- assert(arguments.isEmpty);
- for (ast.Node node in nodeList) arguments.add(visit(node));
- return normalizeDynamicArguments(callStructure, arguments);
+ /// Returns the allocation site-specific type for a given allocation.
+ ///
+ /// Currently, it is an error to call this with anything that is not the
+ /// allocation site for a List object (a literal list or a call to one
+ /// of the List constructors).
+ TypeMask getAllocationSiteType(ast.Node node) {
+ return compiler.typesTask.getGuaranteedTypeOfNode(
+ elements.analyzedElement, node);
}
- // TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
- // semantic correlation between arguments and invocation.
- CallStructure translateStaticArguments(ast.NodeList nodeList,
- Element element,
- CallStructure callStructure,
- List<ir.Primitive> arguments) {
- assert(arguments.isEmpty);
- for (ast.Node node in nodeList) arguments.add(visit(node));
- return normalizeStaticArguments(callStructure, element, arguments);
+ ir.Primitive visitLiteralList(ast.LiteralList node) {
+ if (node.isConst) {
+ return translateConstant(node);
+ }
+ List<ir.Primitive> values = node.elements.nodes.mapToList(visit);
+ InterfaceType type = elements.getType(node);
+ return irBuilder.buildListLiteral(type, values,
+ allocationSiteType: getAllocationSiteType(node));
}
- ir.Primitive translateCallInvoke(ir.Primitive target,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- SourceInformation sourceInformation) {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildCallInvocation(target, callStructure, arguments,
- sourceInformation: sourceInformation);
+ ir.Primitive visitLiteralMap(ast.LiteralMap node) {
+ if (node.isConst) {
+ return translateConstant(node);
+ }
+ InterfaceType type = elements.getType(node);
+ return irBuilder.buildMapLiteral(
+ type,
+ node.entries.nodes.map((e) => e.key),
+ node.entries.nodes.map((e) => e.value),
+ build);
}
- @override
- ir.Primitive handleConstantInvoke(
- ast.Send node,
- ConstantExpression constant,
- ast.NodeList arguments,
- CallStructure callStructure,
- _) {
- ir.Primitive target = buildConstantExpression(constant,
- sourceInformationBuilder.buildGet(node));
- return translateCallInvoke(target, arguments, callStructure,
- sourceInformationBuilder.buildCall(node, arguments));
+ ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) {
+ assert(irBuilder.isOpen);
+ return translateConstant(node);
}
- @override
- ir.Primitive handleDynamicInvoke(
- ast.Send node,
- ast.Node receiver,
- ast.NodeList argumentsNode,
- Selector selector,
- _) {
- ir.Primitive target = translateReceiver(receiver);
- List<ir.Primitive> arguments = <ir.Primitive>[];
- CallStructure callStructure = translateDynamicArguments(
- argumentsNode, selector.callStructure, arguments);
- return irBuilder.buildDynamicInvocation(
- target,
- new Selector(selector.kind, selector.memberName, callStructure),
- elements.getTypeMask(node),
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, node.selector));
+ ir.Primitive visitParenthesizedExpression(
+ ast.ParenthesizedExpression node) {
+ assert(irBuilder.isOpen);
+ return visit(node.expression);
}
- @override
- ir.Primitive visitIfNotNullDynamicPropertyInvoke(
- ast.Send node,
- ast.Node receiver,
- ast.NodeList argumentsNode,
- Selector selector,
- _) {
- ir.Primitive target = visit(receiver);
- return irBuilder.buildIfNotNullSend(
- target,
- nested(() {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- CallStructure callStructure = translateDynamicArguments(
- argumentsNode, selector.callStructure, arguments);
- return irBuilder.buildDynamicInvocation(
- target,
- new Selector(selector.kind, selector.memberName, callStructure),
- elements.getTypeMask(node),
- arguments);
- }));
- }
+ // Stores the result of visiting a CascadeReceiver, so we can return it from
+ // its enclosing Cascade.
+ ir.Primitive _currentCascadeReceiver;
- ir.Primitive handleLocalInvoke(
- ast.Send node,
- LocalElement element,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildLocalVariableInvocation(
- element,
- callStructure,
- arguments,
- callSourceInformation:
- sourceInformationBuilder.buildCall(node, argumentsNode));
+ ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) {
+ assert(irBuilder.isOpen);
+ return _currentCascadeReceiver = visit(node.expression);
}
- @override
- ir.Primitive visitLocalFunctionInvoke(
- ast.Send node,
- LocalFunctionElement function,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildLocalFunctionInvocation(
- function,
- callStructure,
- arguments,
- sourceInformationBuilder.buildCall(node, argumentsNode));
+ ir.Primitive visitCascade(ast.Cascade node) {
+ assert(irBuilder.isOpen);
+ var oldCascadeReceiver = _currentCascadeReceiver;
+ // Throw away the result of visiting the expression.
+ // Instead we return the result of visiting the CascadeReceiver.
+ visit(node.expression);
+ ir.Primitive receiver = _currentCascadeReceiver;
+ _currentCascadeReceiver = oldCascadeReceiver;
+ return receiver;
}
@override
- ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) {
- return buildStaticFieldGet(field, sourceInformationBuilder.buildGet(node));
+ ir.Primitive visitAssert(ast.Assert node) {
+ assert(irBuilder.isOpen);
+ if (compiler.enableUserAssertions) {
+ return giveup(node, 'assert in checked mode not implemented');
+ } else {
+ // The call to assert and its argument expression must be ignored
+ // in production mode.
+ // Assertions can only occur in expression statements, so no value needs
+ // to be returned.
+ return null;
+ }
}
+ // ## Sends ##
@override
- ir.Primitive handleStaticFieldInvoke(
- ast.Send node,
- FieldElement field,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- SourceInformation src = sourceInformationBuilder.buildGet(node);
- ir.Primitive target = buildStaticFieldGet(field, src);
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildCallInvocation(
- target,
- callStructure,
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, argumentsNode));
+ void previsitDeferredAccess(ast.Send node, PrefixElement prefix, _) {
+ if (prefix != null) buildCheckDeferredIsLoaded(prefix, node);
}
- @override
- ir.Primitive handleStaticFunctionInvoke(
- ast.Send node,
- MethodElement function,
- ast.NodeList arguments,
- CallStructure callStructure,
- _);
+ /// Create a call to check that a deferred import has already been loaded.
+ ir.Primitive buildCheckDeferredIsLoaded(PrefixElement prefix, ast.Send node) {
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildCall(node, node.selector);
+ return irBuilder.buildStaticFunctionInvocation(
+ helpers.checkDeferredIsLoaded,
+ CallStructure.TWO_ARGS, <ir.Primitive>[
+ irBuilder.buildStringConstant(
+ compiler.deferredLoadTask.getImportDeferName(node, prefix)),
+ irBuilder.buildStringConstant('${prefix.deferredImport.uri}'),
+ ], sourceInformation: sourceInformation);
+ }
- @override
- ir.Primitive handleStaticFunctionIncompatibleInvoke(
- ast.Send node,
- MethodElement function,
- ast.NodeList arguments,
- CallStructure callStructure, _) {
- return irBuilder.buildStaticNoSuchMethod(
- elements.getSelector(node),
- arguments.nodes.mapToList(visit));
+ ir.Primitive visitNamedArgument(ast.NamedArgument node) {
+ assert(irBuilder.isOpen);
+ return visit(node.expression);
}
@override
- ir.Primitive handleStaticGetterInvoke(
- ast.Send node,
- FunctionElement getter,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- ir.Primitive target = buildStaticGetterGet(getter, node);
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildCallInvocation(
- target,
- callStructure,
- arguments,
+ ir.Primitive visitExpressionInvoke(ast.Send node,
+ ast.Node expression,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure, _) {
+ ir.Primitive receiver = visit(expression);
+ List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
+ callStructure = normalizeDynamicArguments(callStructure, arguments);
+ return irBuilder.buildCallInvocation(receiver, callStructure, arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
- @override
- ir.Primitive visitSuperFieldInvoke(
- ast.Send node,
- FieldElement field,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- ir.Primitive target = irBuilder.buildSuperFieldGet(field);
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildCallInvocation(
- target,
- callStructure,
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, argumentsNode));
+ /// Returns `true` if [node] is a super call.
+ // TODO(johnniwinther): Remove the need for this.
+ bool isSuperCall(ast.Send node) {
+ return node != null && node.receiver != null && node.receiver.isSuper();
}
@override
- ir.Primitive visitSuperGetterInvoke(
- ast.Send node,
- FunctionElement getter,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- ir.Primitive target = irBuilder.buildSuperGetterGet(getter,
+ ir.Primitive handleConstantGet(
+ ast.Node node,
+ ConstantExpression constant, _) {
+ return buildConstantExpression(constant,
sourceInformationBuilder.buildGet(node));
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildCallInvocation(
- target,
- callStructure,
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, argumentsNode));
}
- @override
- ir.Primitive visitSuperMethodInvoke(
- ast.Send node,
- MethodElement method,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure = translateStaticArguments(argumentsNode, method,
- callStructure, arguments);
- return irBuilder.buildSuperMethodInvocation(
- method,
- callStructure,
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, node.selector));
+ /// If [node] is null, returns this.
+ /// Otherwise visits [node] and returns the result.
+ ir.Primitive translateReceiver(ast.Expression node) {
+ return node != null ? visit(node) : irBuilder.buildThis();
}
@override
- ir.Primitive visitSuperMethodIncompatibleInvoke(
+ ir.Primitive handleDynamicGet(
ast.Send node,
- MethodElement method,
- ast.NodeList arguments,
- CallStructure callStructure, _) {
- List<ir.Primitive> normalizedArguments = <ir.Primitive>[];
- CallStructure normalizedCallStructure =
- translateDynamicArguments(arguments, callStructure, normalizedArguments);
- return buildInstanceNoSuchMethod(
- new Selector.call(method.memberName, normalizedCallStructure),
+ ast.Node receiver,
+ Name name,
+ _) {
+ return irBuilder.buildDynamicGet(
+ translateReceiver(receiver),
+ new Selector.getter(name),
elements.getTypeMask(node),
- normalizedArguments);
+ sourceInformationBuilder.buildGet(node));
}
@override
- ir.Primitive visitUnresolvedSuperInvoke(
+ ir.Primitive visitIfNotNullDynamicPropertyGet(
ast.Send node,
- Element element,
- ast.NodeList argumentsNode,
- Selector selector, _) {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- CallStructure callStructure = translateDynamicArguments(
- argumentsNode, selector.callStructure, arguments);
- // TODO(johnniwinther): Supply a member name to the visit function instead
- // of looking it up in elements.
- return buildInstanceNoSuchMethod(
- new Selector.call(elements.getSelector(node).memberName, callStructure),
- elements.getTypeMask(node),
- arguments);
+ ast.Node receiver,
+ Name name,
+ _) {
+ ir.Primitive target = visit(receiver);
+ return irBuilder.buildIfNotNullSend(
+ target,
+ nested(() => irBuilder.buildDynamicGet(
+ target,
+ new Selector.getter(name),
+ elements.getTypeMask(node),
+ sourceInformationBuilder.buildGet(node))));
+ }
+
+ @override
+ ir.Primitive visitDynamicTypeLiteralGet(
+ ast.Send node,
+ ConstantExpression constant,
+ _) {
+ return buildConstantExpression(constant,
+ sourceInformationBuilder.buildGet(node));
}
@override
- ir.Primitive visitThisInvoke(
+ ir.Primitive visitLocalVariableGet(
ast.Send node,
- ast.NodeList arguments,
- CallStructure callStructure,
+ LocalVariableElement element,
_) {
- return translateCallInvoke(
- irBuilder.buildThis(),
- arguments,
- callStructure,
- sourceInformationBuilder.buildCall(node, arguments));
+ return element.isConst
+ ? irBuilder.buildConstant(getConstantForVariable(element),
+ sourceInformation: sourceInformationBuilder.buildGet(node))
+ : irBuilder.buildLocalVariableGet(element);
}
@override
- ir.Primitive visitTypeVariableTypeLiteralInvoke(
+ ir.Primitive handleLocalGet(
ast.Send node,
- TypeVariableElement element,
- ast.NodeList arguments,
- CallStructure callStructure,
+ LocalElement element,
_) {
- return translateCallInvoke(
- translateTypeVariableTypeLiteral(
- element, sourceInformationBuilder.buildGet(node)),
- arguments,
- callStructure,
- sourceInformationBuilder.buildCall(node, arguments));
+ return irBuilder.buildLocalVariableGet(element);
}
@override
- ir.Primitive visitIndexSet(
- ast.SendSet node,
- ast.Node receiver,
- ast.Node index,
- ast.Node rhs,
- _) {
- return irBuilder.buildDynamicIndexSet(
- visit(receiver), elements.getTypeMask(node), visit(index), visit(rhs));
+ ir.Primitive visitLocalFunctionGet(
+ ast.Send node,
+ LocalFunctionElement function,
+ _) {
+ return irBuilder.buildLocalFunctionGet(function);
}
@override
- ir.Primitive visitSuperIndexSet(
- ast.SendSet node,
- FunctionElement function,
- ast.Node index,
- ast.Node rhs,
+ ir.Primitive handleStaticFunctionGet(
+ ast.Send node,
+ MethodElement function,
_) {
- return irBuilder.buildSuperIndexSet(function, visit(index), visit(rhs));
+ return irBuilder.buildStaticFunctionGet(function);
}
- ir.Primitive translateCompounds(
- ast.SendSet node,
- {ir.Primitive getValue(),
- CompoundRhs rhs,
- void setValue(ir.Primitive value)}) {
- ir.Primitive value = getValue();
- op.BinaryOperator operator = rhs.operator;
- if (operator.kind == op.BinaryOperatorKind.IF_NULL) {
- // Unlike other compound operators if-null conditionally will not do the
- // assignment operation.
- return irBuilder.buildIfNull(value, nested(() {
- ir.Primitive newValue = build(rhs.rhs);
- setValue(newValue);
- return newValue;
- }));
- }
+ @override
+ ir.Primitive handleStaticGetterGet(
+ ast.Send node,
+ FunctionElement getter,
+ _) {
+ return buildStaticGetterGet(getter, node);
+ }
- Selector operatorSelector =
- new Selector.binaryOperator(operator.selectorName);
- ir.Primitive rhsValue;
- if (rhs.kind == CompoundKind.ASSIGNMENT) {
- rhsValue = visit(rhs.rhs);
+ /// Create a getter invocation of the static getter [getter]. This also
+ /// handles the special case where [getter] is the `loadLibrary`
+ /// pseudo-function on library prefixes of deferred imports.
+ ir.Primitive buildStaticGetterGet(MethodElement getter, ast.Send node) {
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
+ if (getter.isDeferredLoaderGetter) {
+ PrefixElement prefix = getter.enclosingElement;
+ ir.Primitive loadId = irBuilder.buildStringConstant(
+ compiler.deferredLoadTask.getImportDeferName(node, prefix));
+ return irBuilder.buildStaticFunctionInvocation(
+ compiler.loadLibraryFunction,
+ CallStructure.ONE_ARG, <ir.Primitive>[loadId],
+ sourceInformation: sourceInformation);
} else {
- rhsValue = irBuilder.buildIntegerConstant(1);
+ return irBuilder.buildStaticGetterGet(getter, sourceInformation);
}
- List<ir.Primitive> arguments = <ir.Primitive>[rhsValue];
- CallStructure callStructure =
- normalizeDynamicArguments(operatorSelector.callStructure, arguments);
- TypeMask operatorTypeMask =
- elements.getOperatorTypeMaskInComplexSendSet(node);
- SourceInformation operatorSourceInformation =
- sourceInformationBuilder.buildCall(node, node.assignmentOperator);
- ir.Primitive result = irBuilder.buildDynamicInvocation(
- value,
- new Selector(operatorSelector.kind, operatorSelector.memberName,
- callStructure),
- operatorTypeMask,
- arguments,
- sourceInformation: operatorSourceInformation);
- setValue(result);
- return rhs.kind == CompoundKind.POSTFIX ? value : result;
}
- ir.Primitive translateSetIfNull(
- ast.SendSet node,
- {ir.Primitive getValue(),
- ast.Node rhs,
- void setValue(ir.Primitive value)}) {
- ir.Primitive value = getValue();
- // Unlike other compound operators if-null conditionally will not do the
- // assignment operation.
- return irBuilder.buildIfNull(value, nested(() {
- ir.Primitive newValue = build(rhs);
- setValue(newValue);
- return newValue;
- }));
+ @override
+ ir.Primitive visitSuperFieldGet(
+ ast.Send node,
+ FieldElement field,
+ _) {
+ return irBuilder.buildSuperFieldGet(field);
}
@override
- ir.Primitive handleDynamicSet(
- ast.SendSet node,
- ast.Node receiver,
- Name name,
- ast.Node rhs,
+ ir.Primitive visitSuperGetterGet(
+ ast.Send node,
+ FunctionElement getter,
_) {
- return irBuilder.buildDynamicSet(
- translateReceiver(receiver),
- new Selector.setter(name),
- elements.getTypeMask(node),
- visit(rhs));
+ return irBuilder.buildSuperGetterGet(
+ getter, sourceInformationBuilder.buildGet(node));
}
@override
- ir.Primitive visitIfNotNullDynamicPropertySet(
- ast.SendSet node,
- ast.Node receiver,
- Name name,
- ast.Node rhs,
+ ir.Primitive visitSuperMethodGet(
+ ast.Send node,
+ MethodElement method,
_) {
- ir.Primitive target = visit(receiver);
- return irBuilder.buildIfNotNullSend(
- target,
- nested(() => irBuilder.buildDynamicSet(
- target,
- new Selector.setter(name),
- elements.getTypeMask(node),
- visit(rhs))));
+ return irBuilder.buildSuperMethodGet(method);
}
@override
- ir.Primitive handleLocalSet(
- ast.SendSet node,
- LocalElement element,
- ast.Node rhs,
- _) {
- ir.Primitive value = visit(rhs);
- value = checkTypeVsElement(value, element);
- return irBuilder.buildLocalVariableSet(element, value);
+ ir.Primitive visitUnresolvedSuperGet(
+ ast.Send node,
+ Element element, _) {
+ return buildInstanceNoSuchMethod(
+ elements.getSelector(node), elements.getTypeMask(node), []);
}
@override
- ir.Primitive handleStaticFieldSet(
- ast.SendSet node,
- FieldElement field,
- ast.Node rhs,
- _) {
- return irBuilder.buildStaticFieldSet(field, visit(rhs));
+ ir.Primitive visitThisGet(ast.Identifier node, _) {
+ if (irBuilder.state.thisParameter == null) {
+ // TODO(asgerf,johnniwinther): Should be in a visitInvalidThis method.
+ // 'this' in static context. Just translate to null.
+ assert(compiler.compilationFailed);
+ return irBuilder.buildNullConstant();
+ }
+ return irBuilder.buildThis();
+ }
+
+ ir.Primitive translateTypeVariableTypeLiteral(
+ TypeVariableElement element,
+ SourceInformation sourceInformation) {
+ return irBuilder.buildReifyTypeVariable(element.type, sourceInformation);
}
@override
- ir.Primitive visitSuperFieldSet(
- ast.SendSet node,
- FieldElement field,
- ast.Node rhs,
- _) {
- return irBuilder.buildSuperFieldSet(field, visit(rhs));
+ ir.Primitive visitTypeVariableTypeLiteralGet(ast.Send node,
+ TypeVariableElement element, _) {
+ return translateTypeVariableTypeLiteral(element,
+ sourceInformationBuilder.buildGet(node));
+ }
+
+ ir.Primitive translateLogicalOperator(ast.Expression left,
+ ast.Expression right,
+ {bool isLazyOr}) {
+ ir.Primitive leftValue = visit(left);
+
+ ir.Primitive buildRightValue(IrBuilder rightBuilder) {
+ return withBuilder(rightBuilder, () => visit(right));
+ }
+
+ return irBuilder.buildLogicalOperator(
+ leftValue, buildRightValue, isLazyOr: isLazyOr);
}
@override
- ir.Primitive visitSuperSetterSet(
- ast.SendSet node,
- FunctionElement setter,
- ast.Node rhs,
- _) {
- return irBuilder.buildSuperSetterSet(setter, visit(rhs));
+ ir.Primitive visitIfNull(
+ ast.Send node, ast.Node left, ast.Node right, _) {
+ return irBuilder.buildIfNull(build(left), subbuild(right));
}
@override
- ir.Primitive visitUnresolvedSuperIndexSet(
+ ir.Primitive visitLogicalAnd(
+ ast.Send node, ast.Node left, ast.Node right, _) {
+ return translateLogicalOperator(left, right, isLazyOr: false);
+ }
+
+ @override
+ ir.Primitive visitLogicalOr(
+ ast.Send node, ast.Node left, ast.Node right, _) {
+ return translateLogicalOperator(left, right, isLazyOr: true);
+ }
+
+ @override
+ ir.Primitive visitAs(
ast.Send node,
- Element element,
- ast.Node index,
- ast.Node rhs,
- arg) {
- return giveup(node, 'visitUnresolvedSuperIndexSet');
+ ast.Node expression,
+ DartType type,
+ _) {
+ ir.Primitive receiver = visit(expression);
+ return irBuilder.buildTypeOperator(receiver, type, isTypeTest: false);
}
@override
- ir.Primitive handleStaticSetterSet(
- ast.SendSet node,
- FunctionElement setter,
- ast.Node rhs,
+ ir.Primitive visitIs(
+ ast.Send node,
+ ast.Node expression,
+ DartType type,
_) {
- return irBuilder.buildStaticSetterSet(setter, visit(rhs));
+ ir.Primitive value = visit(expression);
+ return irBuilder.buildTypeOperator(value, type, isTypeTest: true);
}
@override
- ir.Primitive handleTypeLiteralConstantCompounds(
- ast.SendSet node,
- ConstantExpression constant,
- CompoundRhs rhs,
- arg) {
- return translateCompounds(
- node,
- getValue: () {
- return buildConstantExpression(constant,
- sourceInformationBuilder.buildGet(node));
- },
- rhs: rhs,
- setValue: (value) {}); // The binary operator will throw before this.
+ ir.Primitive visitIsNot(ast.Send node,
+ ast.Node expression, DartType type, _) {
+ ir.Primitive value = visit(expression);
+ ir.Primitive check = irBuilder.buildTypeOperator(
+ value, type, isTypeTest: true);
+ return irBuilder.buildNegation(check);
}
- @override
- ir.Primitive handleTypeLiteralConstantSetIfNulls(
- ast.SendSet node,
- ConstantExpression constant,
- ast.Node rhs,
- _) {
- // The type literal is never `null`.
- return buildConstantExpression(constant,
- sourceInformationBuilder.buildGet(node));
+ ir.Primitive translateBinary(ast.Send node,
+ ast.Node left,
+ op.BinaryOperator operator,
+ ast.Node right) {
+ ir.Primitive receiver = visit(left);
+ Selector selector = new Selector.binaryOperator(operator.selectorName);
+ List<ir.Primitive> arguments = <ir.Primitive>[visit(right)];
+ CallStructure callStructure =
+ normalizeDynamicArguments(selector.callStructure, arguments);
+ return irBuilder.buildDynamicInvocation(
+ receiver,
+ new Selector(selector.kind, selector.memberName, callStructure),
+ elements.getTypeMask(node),
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
}
@override
- ir.Primitive handleDynamicCompounds(
- ast.SendSet node,
- ast.Node receiver,
- Name name,
- CompoundRhs rhs,
- arg) {
- ir.Primitive target = translateReceiver(receiver);
- ir.Primitive helper() {
- return translateCompounds(
- node,
- getValue: () => irBuilder.buildDynamicGet(
- target,
- new Selector.getter(name),
- elements.getGetterTypeMaskInComplexSendSet(node),
- sourceInformationBuilder.buildGet(node)),
- rhs: rhs,
- setValue: (ir.Primitive result) {
- irBuilder.buildDynamicSet(
- target,
- new Selector.setter(name),
- elements.getTypeMask(node),
- result);
- });
- }
- return node.isConditional
- ? irBuilder.buildIfNotNullSend(target, nested(helper))
- : helper();
+ ir.Primitive visitBinary(ast.Send node,
+ ast.Node left,
+ op.BinaryOperator operator,
+ ast.Node right, _) {
+ return translateBinary(node, left, operator, right);
}
@override
- ir.Primitive handleDynamicSetIfNulls(
- ast.Send node,
- ast.Node receiver,
- Name name,
- ast.Node rhs,
- _) {
- ir.Primitive target = translateReceiver(receiver);
- ir.Primitive helper() {
- return translateSetIfNull(
- node,
- getValue: () => irBuilder.buildDynamicGet(
- target,
- new Selector.getter(name),
- elements.getGetterTypeMaskInComplexSendSet(node),
- sourceInformationBuilder.buildGet(node)),
- rhs: rhs,
- setValue: (ir.Primitive result) {
- irBuilder.buildDynamicSet(
- target,
- new Selector.setter(name),
- elements.getTypeMask(node),
- result);
- });
- }
- return node.isConditional
- ? irBuilder.buildIfNotNullSend(target, nested(helper))
- : helper();
+ ir.Primitive visitIndex(ast.Send node,
+ ast.Node receiver,
+ ast.Node index, _) {
+ ir.Primitive target = visit(receiver);
+ Selector selector = new Selector.index();
+ List<ir.Primitive> arguments = <ir.Primitive>[visit(index)];
+ CallStructure callStructure =
+ normalizeDynamicArguments(selector.callStructure, arguments);
+ return irBuilder.buildDynamicInvocation(
+ target,
+ new Selector(selector.kind, selector.memberName, callStructure),
+ elements.getTypeMask(node),
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(receiver, node.selector));
}
- ir.Primitive buildLocalNoSuchSetter(LocalElement local, ir.Primitive value) {
- Selector selector = new Selector.setter(
- new Name(local.name, local.library, isSetter: true));
- return irBuilder.buildStaticNoSuchMethod(selector, [value]);
+ ir.Primitive translateSuperBinary(FunctionElement function,
+ op.BinaryOperator operator,
+ ast.Node argument) {
+ List<ir.Primitive> arguments = <ir.Primitive>[visit(argument)];
+ return irBuilder.buildSuperMethodInvocation(function,
+ CallStructure.ONE_ARG, arguments);
}
@override
- ir.Primitive handleLocalCompounds(
- ast.SendSet node,
- LocalElement local,
- CompoundRhs rhs,
- arg,
- {bool isSetterValid}) {
- return translateCompounds(
- node,
- getValue: () {
- if (local.isFunction) {
- return irBuilder.buildLocalFunctionGet(local);
- } else {
- return irBuilder.buildLocalVariableGet(local);
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- if (isSetterValid) {
- irBuilder.buildLocalVariableSet(local, result);
- } else {
- return buildLocalNoSuchSetter(local, result);
- }
- });
+ ir.Primitive visitSuperBinary(
+ ast.Send node,
+ FunctionElement function,
+ op.BinaryOperator operator,
+ ast.Node argument,
+ _) {
+ return translateSuperBinary(function, operator, argument);
}
@override
- ir.Primitive handleLocalSetIfNulls(
- ast.SendSet node,
- LocalElement local,
- ast.Node rhs,
- _,
- {bool isSetterValid}) {
- return translateSetIfNull(
- node,
- getValue: () {
- if (local.isFunction) {
- return irBuilder.buildLocalFunctionGet(local);
- } else {
- return irBuilder.buildLocalVariableGet(local);
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- if (isSetterValid) {
- irBuilder.buildLocalVariableSet(local, result);
- } else {
- return buildLocalNoSuchSetter(local, result);
- }
- });
+ ir.Primitive visitSuperIndex(
+ ast.Send node,
+ FunctionElement function,
+ ast.Node index,
+ _) {
+ return irBuilder.buildSuperIndex(function, visit(index));
}
- ir.Primitive buildStaticNoSuchGetter(Element element) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.getter(new Name(element.name, element.library)),
- const <ir.Primitive>[]);
+ @override
+ ir.Primitive visitEquals(
+ ast.Send node,
+ ast.Node left,
+ ast.Node right,
+ _) {
+ return translateBinary(node, left, op.BinaryOperator.EQ, right);
}
- ir.Primitive buildStaticNoSuchSetter(Element element, ir.Primitive value) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(new Name(element.name, element.library)),
- <ir.Primitive>[value]);
+ @override
+ ir.Primitive visitSuperEquals(
+ ast.Send node,
+ FunctionElement function,
+ ast.Node argument,
+ _) {
+ return translateSuperBinary(function, op.BinaryOperator.EQ, argument);
}
@override
- ir.Primitive handleStaticCompounds(
- ast.SendSet node,
- Element getter,
- CompoundGetter getterKind,
- Element setter,
- CompoundSetter setterKind,
- CompoundRhs rhs,
- arg) {
- return translateCompounds(
- node,
- getValue: () {
- switch (getterKind) {
- case CompoundGetter.FIELD:
- SourceInformation src = sourceInformationBuilder.buildGet(node);
- return buildStaticFieldGet(getter, src);
- case CompoundGetter.GETTER:
- return buildStaticGetterGet(getter, node);
- case CompoundGetter.METHOD:
- return irBuilder.buildStaticFunctionGet(getter);
- case CompoundGetter.UNRESOLVED:
- return buildStaticNoSuchGetter(getter);
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- switch (setterKind) {
- case CompoundSetter.FIELD:
- return irBuilder.buildStaticFieldSet(setter, result);
- case CompoundSetter.SETTER:
- return irBuilder.buildStaticSetterSet(setter, result);
- case CompoundSetter.INVALID:
- return buildStaticNoSuchSetter(setter, result);
- }
- });
+ ir.Primitive visitNot(
+ ast.Send node,
+ ast.Node expression,
+ _) {
+ return irBuilder.buildNegation(visit(expression));
}
@override
- ir.Primitive handleStaticSetIfNulls(
- ast.SendSet node,
- Element getter,
- CompoundGetter getterKind,
- Element setter,
- CompoundSetter setterKind,
- ast.Node rhs,
+ ir.Primitive visitNotEquals(
+ ast.Send node,
+ ast.Node left,
+ ast.Node right,
_) {
- return translateSetIfNull(
- node,
- getValue: () {
- switch (getterKind) {
- case CompoundGetter.FIELD:
- SourceInformation src = sourceInformationBuilder.buildGet(node);
- return buildStaticFieldGet(getter, src);
- case CompoundGetter.GETTER:
- return buildStaticGetterGet(getter, node);
- case CompoundGetter.METHOD:
- return irBuilder.buildStaticFunctionGet(getter);
- case CompoundGetter.UNRESOLVED:
- return buildStaticNoSuchGetter(getter);
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- switch (setterKind) {
- case CompoundSetter.FIELD:
- return irBuilder.buildStaticFieldSet(setter, result);
- case CompoundSetter.SETTER:
- return irBuilder.buildStaticSetterSet(setter, result);
- case CompoundSetter.INVALID:
- return buildStaticNoSuchSetter(setter, result);
- }
- });
+ return irBuilder.buildNegation(
+ translateBinary(node, left, op.BinaryOperator.NOT_EQ, right));
}
- ir.Primitive buildSuperNoSuchGetter(Element element, TypeMask mask) {
- return buildInstanceNoSuchMethod(
- new Selector.getter(new Name(element.name, element.library)),
- mask,
- const <ir.Primitive>[]);
+ @override
+ ir.Primitive visitSuperNotEquals(
+ ast.Send node,
+ FunctionElement function,
+ ast.Node argument,
+ _) {
+ return irBuilder.buildNegation(
+ translateSuperBinary(function, op.BinaryOperator.NOT_EQ, argument));
}
- ir.Primitive buildSuperNoSuchSetter(Element element,
- TypeMask mask,
- ir.Primitive value) {
- return buildInstanceNoSuchMethod(
- new Selector.setter(new Name(element.name, element.library)),
- mask,
- <ir.Primitive>[value]);
+ @override
+ ir.Primitive visitUnary(ast.Send node,
+ op.UnaryOperator operator, ast.Node expression, _) {
+ // TODO(johnniwinther): Clean up the creation of selectors.
+ Selector selector = operator.selector;
+ ir.Primitive receiver = translateReceiver(expression);
+ return irBuilder.buildDynamicInvocation(
+ receiver, selector, elements.getTypeMask(node), const [],
+ sourceInformation: sourceInformationBuilder.buildCall(
+ expression, node));
}
@override
- ir.Primitive handleSuperCompounds(
- ast.SendSet node,
- Element getter,
- CompoundGetter getterKind,
- Element setter,
- CompoundSetter setterKind,
- CompoundRhs rhs,
- arg) {
- return translateCompounds(
- node,
- getValue: () {
- switch (getterKind) {
- case CompoundGetter.FIELD:
- return irBuilder.buildSuperFieldGet(getter);
- case CompoundGetter.GETTER:
- return irBuilder.buildSuperGetterGet(
- getter, sourceInformationBuilder.buildGet(node));
- case CompoundGetter.METHOD:
- return irBuilder.buildSuperMethodGet(getter);
- case CompoundGetter.UNRESOLVED:
- return buildSuperNoSuchGetter(
- getter,
- elements.getGetterTypeMaskInComplexSendSet(node));
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- switch (setterKind) {
- case CompoundSetter.FIELD:
- return irBuilder.buildSuperFieldSet(setter, result);
- case CompoundSetter.SETTER:
- return irBuilder.buildSuperSetterSet(setter, result);
- case CompoundSetter.INVALID:
- return buildSuperNoSuchSetter(
- setter, elements.getTypeMask(node), result);
- }
- });
+ ir.Primitive visitSuperUnary(
+ ast.Send node,
+ op.UnaryOperator operator,
+ FunctionElement function,
+ _) {
+ return irBuilder.buildSuperMethodInvocation(
+ function, CallStructure.NO_ARGS, const []);
+ }
+
+ // TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
+ // semantic correlation between arguments and invocation.
+ CallStructure translateDynamicArguments(ast.NodeList nodeList,
+ CallStructure callStructure,
+ List<ir.Primitive> arguments) {
+ assert(arguments.isEmpty);
+ for (ast.Node node in nodeList) arguments.add(visit(node));
+ return normalizeDynamicArguments(callStructure, arguments);
+ }
+
+ // TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
+ // semantic correlation between arguments and invocation.
+ CallStructure translateStaticArguments(ast.NodeList nodeList,
+ Element element,
+ CallStructure callStructure,
+ List<ir.Primitive> arguments) {
+ assert(arguments.isEmpty);
+ for (ast.Node node in nodeList) arguments.add(visit(node));
+ return normalizeStaticArguments(callStructure, element, arguments);
+ }
+
+ ir.Primitive translateCallInvoke(ir.Primitive target,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ SourceInformation sourceInformation) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildCallInvocation(target, callStructure, arguments,
+ sourceInformation: sourceInformation);
}
@override
- ir.Primitive handleSuperSetIfNulls(
- ast.SendSet node,
- Element getter,
- CompoundGetter getterKind,
- Element setter,
- CompoundSetter setterKind,
- ast.Node rhs,
+ ir.Primitive handleConstantInvoke(
+ ast.Send node,
+ ConstantExpression constant,
+ ast.NodeList arguments,
+ CallStructure callStructure,
_) {
- return translateSetIfNull(
- node,
- getValue: () {
- switch (getterKind) {
- case CompoundGetter.FIELD:
- return irBuilder.buildSuperFieldGet(getter);
- case CompoundGetter.GETTER:
- return irBuilder.buildSuperGetterGet(
- getter, sourceInformationBuilder.buildGet(node));
- case CompoundGetter.METHOD:
- return irBuilder.buildSuperMethodGet(getter);
- case CompoundGetter.UNRESOLVED:
- return buildSuperNoSuchGetter(
- getter,
- elements.getGetterTypeMaskInComplexSendSet(node));
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- switch (setterKind) {
- case CompoundSetter.FIELD:
- return irBuilder.buildSuperFieldSet(setter, result);
- case CompoundSetter.SETTER:
- return irBuilder.buildSuperSetterSet(setter, result);
- case CompoundSetter.INVALID:
- return buildSuperNoSuchSetter(
- setter, elements.getTypeMask(node), result);
- }
- });
+ ir.Primitive target = buildConstantExpression(constant,
+ sourceInformationBuilder.buildGet(node));
+ return translateCallInvoke(target, arguments, callStructure,
+ sourceInformationBuilder.buildCall(node, arguments));
}
@override
- ir.Primitive handleTypeVariableTypeLiteralCompounds(
- ast.SendSet node,
- TypeVariableElement typeVariable,
- CompoundRhs rhs,
- arg) {
- return translateCompounds(
- node,
- getValue: () {
- return irBuilder.buildReifyTypeVariable(
- typeVariable.type,
- sourceInformationBuilder.buildGet(node));
- },
- rhs: rhs,
- setValue: (value) {}); // The binary operator will throw before this.
+ ir.Primitive handleConstructorInvoke(
+ ast.NewExpression node,
+ ConstructorElement constructor,
+ DartType type,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+
+ // TODO(sigmund): move these checks down after visiting arguments
+ // (see issue #25355)
+ ast.Send send = node.send;
+ // If an allocation refers to a type using a deferred import prefix (e.g.
+ // `new lib.A()`), we must ensure that the deferred import has already been
+ // loaded.
+ var prefix = compiler.deferredLoadTask.deferredPrefixElement(
+ send, elements);
+ if (prefix != null) buildCheckDeferredIsLoaded(prefix, send);
+
+ // We also emit deferred import checks when using redirecting factories that
+ // refer to deferred prefixes.
+ if (constructor.isRedirectingFactory && !constructor.isCyclicRedirection) {
+ ConstructorElement current = constructor;
+ while (current.isRedirectingFactory) {
+ var prefix = current.redirectionDeferredPrefix;
+ if (prefix != null) buildCheckDeferredIsLoaded(prefix, send);
+ current = current.immediateRedirectionTarget;
+ }
+ }
+
+ List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
+ // Use default values from the effective target, not the immediate target.
+ ConstructorElement target = constructor.effectiveTarget;
+
+ callStructure = normalizeStaticArguments(callStructure, target, arguments);
+ TypeMask allocationSiteType;
+
+ if (Elements.isFixedListConstructorCall(constructor, send, compiler) ||
+ Elements.isGrowableListConstructorCall(constructor, send, compiler) ||
+ Elements.isFilledListConstructorCall(constructor, send, compiler) ||
+ Elements.isConstructorOfTypedArraySubclass(constructor, compiler)) {
+ allocationSiteType = getAllocationSiteType(send);
+ }
+ return irBuilder.buildConstructorInvocation(
+ target,
+ callStructure,
+ constructor.computeEffectiveTargetType(type),
+ arguments,
+ sourceInformationBuilder.buildNew(node),
+ allocationSiteType: allocationSiteType);
}
@override
- ir.Primitive visitTypeVariableTypeLiteralSetIfNull(
+ ir.Primitive handleDynamicInvoke(
ast.Send node,
- TypeVariableElement element,
- ast.Node rhs,
+ ast.Node receiver,
+ ast.NodeList argumentsNode,
+ Selector selector,
_) {
- // The type variable is never `null`.
- return translateTypeVariableTypeLiteral(element,
- sourceInformationBuilder.buildGet(node));
+ ir.Primitive target = translateReceiver(receiver);
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ CallStructure callStructure = translateDynamicArguments(
+ argumentsNode, selector.callStructure, arguments);
+ return irBuilder.buildDynamicInvocation(
+ target,
+ new Selector(selector.kind, selector.memberName, callStructure),
+ elements.getTypeMask(node),
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
}
@override
- ir.Primitive handleIndexCompounds(
- ast.SendSet node,
+ ir.Primitive visitIfNotNullDynamicPropertyInvoke(
+ ast.Send node,
ast.Node receiver,
- ast.Node index,
- CompoundRhs rhs,
- arg) {
+ ast.NodeList argumentsNode,
+ Selector selector,
+ _) {
ir.Primitive target = visit(receiver);
- ir.Primitive indexValue = visit(index);
- return translateCompounds(
- node,
- getValue: () {
- Selector selector = new Selector.index();
- List<ir.Primitive> arguments = <ir.Primitive>[indexValue];
- CallStructure callStructure =
- normalizeDynamicArguments(selector.callStructure, arguments);
+ return irBuilder.buildIfNotNullSend(
+ target,
+ nested(() {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ CallStructure callStructure = translateDynamicArguments(
+ argumentsNode, selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target,
new Selector(selector.kind, selector.memberName, callStructure),
- elements.getGetterTypeMaskInComplexSendSet(node),
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(receiver, node));
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- irBuilder.buildDynamicIndexSet(
- target,
elements.getTypeMask(node),
- indexValue,
- result);
- });
- }
-
- @override
- ir.Primitive handleSuperIndexCompounds(
- ast.SendSet node,
- Element indexFunction,
- Element indexSetFunction,
- ast.Node index,
- CompoundRhs rhs,
- arg,
- {bool isGetterValid,
- bool isSetterValid}) {
- ir.Primitive indexValue = visit(index);
- return translateCompounds(
- node,
- getValue: () {
- if (isGetterValid) {
- return irBuilder.buildSuperIndex(indexFunction, indexValue);
- } else {
- return buildInstanceNoSuchMethod(
- new Selector.index(),
- elements.getGetterTypeMaskInComplexSendSet(node),
- <ir.Primitive>[indexValue]);
- }
- },
- rhs: rhs,
- setValue: (ir.Primitive result) {
- if (isSetterValid) {
- irBuilder.buildSuperIndexSet(indexSetFunction, indexValue, result);
- } else {
- buildInstanceNoSuchMethod(
- new Selector.indexSet(),
- elements.getTypeMask(node),
- <ir.Primitive>[indexValue, result]);
- }
- });
- }
-
- /// Evaluates a string interpolation and appends each part to [accumulator]
- /// (after stringify conversion).
- void buildStringParts(ast.Node node, List<ir.Primitive> accumulator) {
- if (node is ast.StringJuxtaposition) {
- buildStringParts(node.first, accumulator);
- buildStringParts(node.second, accumulator);
- } else if (node is ast.StringInterpolation) {
- buildStringParts(node.string, accumulator);
- for (ast.StringInterpolationPart part in node.parts) {
- buildStringParts(part.expression, accumulator);
- buildStringParts(part.string, accumulator);
- }
- } else if (node is ast.LiteralString) {
- // Empty strings often occur at the end of a string interpolation,
- // do not bother to include them.
- if (!node.dartString.isEmpty) {
- accumulator.add(irBuilder.buildDartStringConstant(node.dartString));
- }
- } else if (node is ast.ParenthesizedExpression) {
- buildStringParts(node.expression, accumulator);
- } else {
- ir.Primitive value = visit(node);
- accumulator.add(irBuilder.buildStringify(value));
- }
- }
-
- ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) {
- assert(irBuilder.isOpen);
- List<ir.Primitive> parts = <ir.Primitive>[];
- buildStringParts(node, parts);
- return irBuilder.buildStringConcatenation(parts);
+ arguments);
+ }));
}
- ir.Primitive visitStringInterpolation(ast.StringInterpolation node) {
- assert(irBuilder.isOpen);
- List<ir.Primitive> parts = <ir.Primitive>[];
- buildStringParts(node, parts);
- return irBuilder.buildStringConcatenation(parts);
+ ir.Primitive handleLocalInvoke(
+ ast.Send node,
+ LocalElement element,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildLocalVariableInvocation(
+ element,
+ callStructure,
+ arguments,
+ callSourceInformation:
+ sourceInformationBuilder.buildCall(node, argumentsNode));
}
- ir.Primitive translateConstant(ast.Node node) {
- assert(irBuilder.isOpen);
- return irBuilder.buildConstant(
- getConstantForNode(node),
- sourceInformation: sourceInformationBuilder.buildGet(node));
+ @override
+ ir.Primitive visitLocalFunctionInvoke(
+ ast.Send node,
+ LocalFunctionElement function,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildLocalFunctionInvocation(
+ function,
+ callStructure,
+ arguments,
+ sourceInformationBuilder.buildCall(node, argumentsNode));
}
- ir.Primitive visitThrow(ast.Throw node) {
- assert(irBuilder.isOpen);
- // This function is not called for throw expressions occurring as
- // statements.
- return irBuilder.buildNonTailThrow(visit(node.expression));
+ @override
+ ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) {
+ return buildStaticFieldGet(field, sourceInformationBuilder.buildGet(node));
}
- ir.Primitive buildInstanceNoSuchMethod(
- Selector selector,
- TypeMask mask,
- List<ir.Primitive> arguments);
-
- ir.Primitive buildRuntimeError(String message);
-
- ir.Primitive buildAbstractClassInstantiationError(ClassElement element);
-
@override
- ir.Primitive visitUnresolvedCompound(
+ ir.Primitive handleStaticFieldInvoke(
ast.Send node,
- Element element,
- op.AssignmentOperator operator,
- ast.Node rhs, _) {
- // TODO(asgerf): What is unresolved? The getter and/or the setter?
- // If it was the setter, we must evaluate the right-hand side.
- return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node), []);
+ FieldElement field,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ SourceInformation src = sourceInformationBuilder.buildGet(node);
+ ir.Primitive target = buildStaticFieldGet(field, src);
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildCallInvocation(
+ target,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
- ir.Primitive visitUnresolvedClassConstructorInvoke(
- ast.NewExpression node,
- Element element,
- DartType type,
- ast.NodeList arguments,
- Selector selector, _) {
- // If the class is missing it's a runtime error.
- return buildRuntimeError("Unresolved class: '${element.name}'");
+ ir.Primitive handleStaticFunctionInvoke(ast.Send node,
+ MethodElement function,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ if (compiler.backend.isForeign(function)) {
+ return handleForeignCode(node, function, argumentsNode, callStructure);
+ } else {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure = translateStaticArguments(argumentsNode, function,
+ callStructure, arguments);
+ return irBuilder.buildStaticFunctionInvocation(function,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
+ }
}
@override
- ir.Primitive visitUnresolvedConstructorInvoke(
- ast.NewExpression node,
- Element constructor,
- DartType type,
- ast.NodeList argumentsNode,
- Selector selector, _) {
- // If the class is there but the constructor is missing, it's an NSM error.
- List<ir.Primitive> arguments = <ir.Primitive>[];
- CallStructure callStructure = translateDynamicArguments(
- argumentsNode, selector.callStructure, arguments);
+ ir.Primitive handleStaticFunctionIncompatibleInvoke(
+ ast.Send node,
+ MethodElement function,
+ ast.NodeList arguments,
+ CallStructure callStructure, _) {
return irBuilder.buildStaticNoSuchMethod(
- new Selector(selector.kind, selector.memberName, callStructure),
- arguments);
+ elements.getSelector(node),
+ arguments.nodes.mapToList(visit));
}
@override
- ir.Primitive visitConstructorIncompatibleInvoke(
- ast.NewExpression node,
- ConstructorElement constructor,
- DartType type,
+ ir.Primitive handleStaticGetterInvoke(
+ ast.Send node,
+ FunctionElement getter,
ast.NodeList argumentsNode,
- CallStructure callStructure, _) {
+ CallStructure callStructure,
+ _) {
+ ir.Primitive target = buildStaticGetterGet(getter, node);
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.call(constructor.memberName, callStructure), arguments);
+ return irBuilder.buildCallInvocation(
+ target,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
- ir.Primitive visitUnresolvedGet(
+ ir.Primitive visitSuperFieldInvoke(
ast.Send node,
- Element element, _) {
- return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node), []);
+ FieldElement field,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ ir.Primitive target = irBuilder.buildSuperFieldGet(field);
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildCallInvocation(
+ target,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
- ir.Primitive visitUnresolvedInvoke(
+ ir.Primitive visitSuperGetterInvoke(
ast.Send node,
- Element element,
- ast.NodeList arguments,
- Selector selector, _) {
- return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node),
- arguments.nodes.mapToList(visit));
- }
-
- @override
- ir.Primitive visitUnresolvedRedirectingFactoryConstructorInvoke(
- ast.NewExpression node,
- ConstructorElement constructor,
- InterfaceType type,
- ast.NodeList argumentsNode,
- CallStructure callStructure, _) {
- String nameString = Elements.reconstructConstructorName(constructor);
- Name name = new Name(nameString, constructor.library);
+ FunctionElement getter,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ ir.Primitive target = irBuilder.buildSuperGetterGet(getter,
+ sourceInformationBuilder.buildGet(node));
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.call(name, callStructure),
- arguments);
+ return irBuilder.buildCallInvocation(
+ target,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
- ir.Primitive visitUnresolvedSet(
+ ir.Primitive visitSuperMethodInvoke(
ast.Send node,
- Element element,
- ast.Node rhs, _) {
- return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node),
- [visit(rhs)]);
+ MethodElement method,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure,
+ _) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure = translateStaticArguments(argumentsNode, method,
+ callStructure, arguments);
+ return irBuilder.buildSuperMethodInvocation(
+ method,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
}
@override
- ir.Primitive visitUnresolvedSuperIndex(
+ ir.Primitive visitSuperMethodIncompatibleInvoke(
ast.Send node,
- Element function,
- ast.Node index, _) {
- // Assume the index getter is missing.
+ MethodElement method,
+ ast.NodeList arguments,
+ CallStructure callStructure, _) {
+ List<ir.Primitive> normalizedArguments = <ir.Primitive>[];
+ CallStructure normalizedCallStructure =
+ translateDynamicArguments(arguments, callStructure, normalizedArguments);
return buildInstanceNoSuchMethod(
- new Selector.index(), elements.getTypeMask(node), [visit(index)]);
+ new Selector.call(method.memberName, normalizedCallStructure),
+ elements.getTypeMask(node),
+ normalizedArguments);
}
@override
- ir.Primitive visitUnresolvedSuperBinary(
+ ir.Primitive visitUnresolvedSuperInvoke(
ast.Send node,
Element element,
- op.BinaryOperator operator,
- ast.Node argument, _) {
+ ast.NodeList argumentsNode,
+ Selector selector, _) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ CallStructure callStructure = translateDynamicArguments(
+ argumentsNode, selector.callStructure, arguments);
+ // TODO(johnniwinther): Supply a member name to the visit function instead
+ // of looking it up in elements.
return buildInstanceNoSuchMethod(
- elements.getSelector(node),
+ new Selector.call(elements.getSelector(node).memberName, callStructure),
elements.getTypeMask(node),
- [visit(argument)]);
+ arguments);
}
@override
- ir.Primitive visitUnresolvedSuperUnary(
+ ir.Primitive visitThisInvoke(
ast.Send node,
- op.UnaryOperator operator,
- Element element, _) {
- return buildInstanceNoSuchMethod(
- elements.getSelector(node), elements.getTypeMask(node), []);
- }
-
- @override
- ir.Primitive bulkHandleNode(ast.Node node, String message, _) {
- return giveup(node, "Unhandled node: ${message.replaceAll('#', '$node')}");
- }
-
- @override
- ir.Primitive bulkHandleError(ast.Node node, ErroneousElement error, _) {
- return irBuilder.buildNullConstant();
- }
-
- @override
- ir.Primitive visitClassTypeLiteralSet(
- ast.SendSet node,
- TypeConstantExpression constant,
- ast.Node rhs, _) {
- InterfaceType type = constant.type;
- ClassElement element = type.element;
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(element.memberName),
- [visit(rhs)]);
- }
-
- @override
- ir.Primitive visitTypedefTypeLiteralSet(
- ast.SendSet node,
- TypeConstantExpression constant,
- ast.Node rhs, _) {
- TypedefType type = constant.type;
- TypedefElement element = type.element;
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(element.memberName),
- [visit(rhs)]);
+ ast.NodeList arguments,
+ CallStructure callStructure,
+ _) {
+ return translateCallInvoke(
+ irBuilder.buildThis(),
+ arguments,
+ callStructure,
+ sourceInformationBuilder.buildCall(node, arguments));
}
@override
- ir.Primitive visitTypeVariableTypeLiteralSet(
- ast.SendSet node,
+ ir.Primitive visitTypeVariableTypeLiteralInvoke(
+ ast.Send node,
TypeVariableElement element,
- ast.Node rhs, _) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(element.memberName), [visit(rhs)]);
- }
-
- @override
- ir.Primitive visitDynamicTypeLiteralSet(
- ast.SendSet node,
- ConstantExpression constant,
- ast.Node rhs, _) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(Names.dynamic_), [visit(rhs)]);
+ ast.NodeList arguments,
+ CallStructure callStructure,
+ _) {
+ return translateCallInvoke(
+ translateTypeVariableTypeLiteral(
+ element, sourceInformationBuilder.buildGet(node)),
+ arguments,
+ callStructure,
+ sourceInformationBuilder.buildCall(node, arguments));
}
@override
- ir.Primitive visitAbstractClassConstructorInvoke(
- ast.NewExpression node,
- ConstructorElement element,
- InterfaceType type,
- ast.NodeList arguments,
- CallStructure callStructure, _) {
- for (ast.Node argument in arguments) visit(argument);
- return buildAbstractClassInstantiationError(element.enclosingClass);
+ ir.Primitive visitIndexSet(
+ ast.SendSet node,
+ ast.Node receiver,
+ ast.Node index,
+ ast.Node rhs,
+ _) {
+ return irBuilder.buildDynamicIndexSet(
+ visit(receiver), elements.getTypeMask(node), visit(index), visit(rhs));
}
@override
- ir.Primitive handleFinalStaticFieldSet(
+ ir.Primitive visitSuperIndexSet(
ast.SendSet node,
- FieldElement field,
- ast.Node rhs, _) {
- // TODO(asgerf): Include class name somehow for static class members?
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(field.memberName),
- [visit(rhs)]);
+ FunctionElement function,
+ ast.Node index,
+ ast.Node rhs,
+ _) {
+ return irBuilder.buildSuperIndexSet(function, visit(index), visit(rhs));
}
- @override
- ir.Primitive visitFinalSuperFieldSet(
+ ir.Primitive translateCompounds(
ast.SendSet node,
- FieldElement field,
- ast.Node rhs, _) {
- return buildInstanceNoSuchMethod(
- new Selector.setter(field.memberName),
- elements.getTypeMask(node),
- [visit(rhs)]);
+ {ir.Primitive getValue(),
+ CompoundRhs rhs,
+ void setValue(ir.Primitive value)}) {
+ ir.Primitive value = getValue();
+ op.BinaryOperator operator = rhs.operator;
+ if (operator.kind == op.BinaryOperatorKind.IF_NULL) {
+ // Unlike other compound operators if-null conditionally will not do the
+ // assignment operation.
+ return irBuilder.buildIfNull(value, nested(() {
+ ir.Primitive newValue = build(rhs.rhs);
+ setValue(newValue);
+ return newValue;
+ }));
+ }
+
+ Selector operatorSelector =
+ new Selector.binaryOperator(operator.selectorName);
+ ir.Primitive rhsValue;
+ if (rhs.kind == CompoundKind.ASSIGNMENT) {
+ rhsValue = visit(rhs.rhs);
+ } else {
+ rhsValue = irBuilder.buildIntegerConstant(1);
+ }
+ List<ir.Primitive> arguments = <ir.Primitive>[rhsValue];
+ CallStructure callStructure =
+ normalizeDynamicArguments(operatorSelector.callStructure, arguments);
+ TypeMask operatorTypeMask =
+ elements.getOperatorTypeMaskInComplexSendSet(node);
+ SourceInformation operatorSourceInformation =
+ sourceInformationBuilder.buildCall(node, node.assignmentOperator);
+ ir.Primitive result = irBuilder.buildDynamicInvocation(
+ value,
+ new Selector(operatorSelector.kind, operatorSelector.memberName,
+ callStructure),
+ operatorTypeMask,
+ arguments,
+ sourceInformation: operatorSourceInformation);
+ setValue(result);
+ return rhs.kind == CompoundKind.POSTFIX ? value : result;
}
- @override
- ir.Primitive handleImmutableLocalSet(
+ ir.Primitive translateSetIfNull(
ast.SendSet node,
- LocalElement local,
- ast.Node rhs, _) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(new Name(local.name, local.library)),
- [visit(rhs)]);
+ {ir.Primitive getValue(),
+ ast.Node rhs,
+ void setValue(ir.Primitive value)}) {
+ ir.Primitive value = getValue();
+ // Unlike other compound operators if-null conditionally will not do the
+ // assignment operation.
+ return irBuilder.buildIfNull(value, nested(() {
+ ir.Primitive newValue = build(rhs);
+ setValue(newValue);
+ return newValue;
+ }));
}
@override
- ir.Primitive handleStaticFunctionSet(
- ast.Send node,
- MethodElement function,
+ ir.Primitive handleDynamicSet(
+ ast.SendSet node,
+ ast.Node receiver,
+ Name name,
ast.Node rhs,
_) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(function.memberName),
- [visit(rhs)]);
+ return irBuilder.buildDynamicSet(
+ translateReceiver(receiver),
+ new Selector.setter(name),
+ elements.getTypeMask(node),
+ visit(rhs));
}
@override
- ir.Primitive handleStaticGetterSet(
+ ir.Primitive visitIfNotNullDynamicPropertySet(
ast.SendSet node,
- GetterElement getter,
+ ast.Node receiver,
+ Name name,
ast.Node rhs,
_) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.setter(getter.memberName),
- [visit(rhs)]);
+ ir.Primitive target = visit(receiver);
+ return irBuilder.buildIfNotNullSend(
+ target,
+ nested(() => irBuilder.buildDynamicSet(
+ target,
+ new Selector.setter(name),
+ elements.getTypeMask(node),
+ visit(rhs))));
}
@override
- ir.Primitive handleStaticSetterGet(
- ast.Send node,
- SetterElement setter,
+ ir.Primitive handleLocalSet(
+ ast.SendSet node,
+ LocalElement element,
+ ast.Node rhs,
_) {
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.getter(setter.memberName),
- []);
- }
-
- @override
- ir.Primitive handleStaticSetterInvoke(
- ast.Send node,
- SetterElement setter,
- ast.NodeList argumentsNode,
- CallStructure callStructure, _) {
- // Translate as a method call.
- List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
- return irBuilder.buildStaticNoSuchMethod(
- new Selector.call(setter.memberName, callStructure),
- arguments);
+ ir.Primitive value = visit(rhs);
+ value = checkTypeVsElement(value, element);
+ return irBuilder.buildLocalVariableSet(element, value);
}
@override
- ir.Primitive visitSuperGetterSet(
+ ir.Primitive handleStaticFieldSet(
ast.SendSet node,
- GetterElement getter,
+ FieldElement field,
ast.Node rhs,
_) {
- return buildInstanceNoSuchMethod(
- new Selector.setter(getter.memberName),
- elements.getTypeMask(node),
- [visit(rhs)]);
+ return irBuilder.buildStaticFieldSet(field, visit(rhs));
}
@override
- ir.Primitive visitSuperMethodSet(
- ast.Send node,
- MethodElement method,
+ ir.Primitive visitSuperFieldSet(
+ ast.SendSet node,
+ FieldElement field,
ast.Node rhs,
_) {
- return buildInstanceNoSuchMethod(
- new Selector.setter(method.memberName),
- elements.getTypeMask(node),
- [visit(rhs)]);
- }
-
- @override
- ir.Primitive visitSuperSetterGet(
- ast.Send node,
- SetterElement setter, _) {
- return buildInstanceNoSuchMethod(
- new Selector.setter(setter.memberName),
- elements.getTypeMask(node),
- []);
+ return irBuilder.buildSuperFieldSet(field, visit(rhs));
}
@override
- ir.Primitive visitSuperSetterInvoke(
- ast.Send node,
- SetterElement setter,
- ast.NodeList argumentsNode,
- CallStructure callStructure, _) {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure =
- translateDynamicArguments(argumentsNode, callStructure, arguments);
- return buildInstanceNoSuchMethod(
- new Selector.call(setter.memberName, callStructure),
- elements.getTypeMask(node),
- arguments);
- }
-
- ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) {
- try {
- return action();
- } catch(e) {
- if (e == ABORT_IRNODE_BUILDER) {
- return null;
- }
- rethrow;
- }
- }
-
- internalError(ast.Node node, String message) {
- reporter.internalError(node, message);
+ ir.Primitive visitSuperSetterSet(
+ ast.SendSet node,
+ FunctionElement setter,
+ ast.Node rhs,
+ _) {
+ return irBuilder.buildSuperSetterSet(setter, visit(rhs));
}
@override
- visitNode(ast.Node node) {
- giveup(node, "Unhandled node");
- }
-
- dynamic giveup(ast.Node node, [String reason]) {
- bailoutMessage = '($node): $reason';
- throw ABORT_IRNODE_BUILDER;
- }
-}
-
-final String ABORT_IRNODE_BUILDER = "IrNode builder aborted";
-
-/// Determines which local variables should be boxed in a mutable variable
-/// inside a given try block.
-class TryBoxedVariables extends ast.Visitor {
- final TreeElements elements;
- TryBoxedVariables(this.elements);
-
- FunctionElement currentFunction;
- bool insideInitializer = false;
- Set<Local> capturedVariables = new Set<Local>();
-
- /// A map containing variables boxed inside try blocks.
- ///
- /// The map is keyed by the [NodeList] of catch clauses for try/catch and
- /// by the finally block for try/finally. try/catch/finally is treated
- /// as a try/catch nested in the try block of a try/finally.
- Map<ast.Node, TryStatementInfo> tryStatements =
- <ast.Node, TryStatementInfo>{};
-
- List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[];
- bool get inTryStatement => tryNestingStack.isNotEmpty;
-
- String bailoutMessage = null;
-
- giveup(ast.Node node, [String reason]) {
- bailoutMessage = '($node): $reason';
- throw ABORT_IRNODE_BUILDER;
- }
-
- void markAsCaptured(Local local) {
- capturedVariables.add(local);
- }
-
- analyze(ast.Node node) {
- visit(node);
- // Variables that are captured by a closure are boxed for their entire
- // lifetime, so they never need to be boxed on entry to a try block.
- // They are not filtered out before this because we cannot identify all
- // of them in the same pass (they may be captured by a closure after the
- // try statement).
- for (TryStatementInfo info in tryStatements.values) {
- info.boxedOnEntry.removeAll(capturedVariables);
- }
- }
-
- visit(ast.Node node) => node.accept(this);
-
- visitNode(ast.Node node) {
- node.visitChildren(this);
- }
-
- void handleSend(ast.Send node) {
- Element element = elements[node];
- if (Elements.isLocal(element) &&
- !element.isConst &&
- element.enclosingElement != currentFunction) {
- LocalElement local = element;
- markAsCaptured(local);
- }
- }
-
- visitSend(ast.Send node) {
- handleSend(node);
- node.visitChildren(this);
- }
-
- visitSendSet(ast.SendSet node) {
- handleSend(node);
- Element element = elements[node];
- if (Elements.isLocal(element)) {
- LocalElement local = element;
- if (insideInitializer &&
- local.isParameter &&
- local.enclosingElement == currentFunction) {
- assert(local.enclosingElement.isConstructor);
- // Initializers in an initializer-list can communicate via parameters.
- // If a parameter is stored in an initializer list we box it.
- // TODO(sigurdm): Fix this.
- // Though these variables do not outlive the activation of the
- // function, they still need to be boxed. As a simplification, we
- // treat them as if they are captured by a closure (i.e., they do
- // outlive the activation of the function).
- markAsCaptured(local);
- } else if (inTryStatement) {
- assert(local.isParameter || local.isVariable);
- // Search for the position of the try block containing the variable
- // declaration, or -1 if it is declared outside the outermost try.
- int i = tryNestingStack.length - 1;
- while (i >= 0 && !tryNestingStack[i].declared.contains(local)) {
- --i;
- }
- // If there is a next inner try, then the variable should be boxed on
- // entry to it.
- if (i + 1 < tryNestingStack.length) {
- tryNestingStack[i + 1].boxedOnEntry.add(local);
- }
- }
- }
- node.visitChildren(this);
+ ir.Primitive visitUnresolvedSuperIndexSet(
+ ast.Send node,
+ Element element,
+ ast.Node index,
+ ast.Node rhs,
+ arg) {
+ return giveup(node, 'visitUnresolvedSuperIndexSet');
}
- visitFunctionExpression(ast.FunctionExpression node) {
- FunctionElement savedFunction = currentFunction;
- currentFunction = elements[node];
-
- if (currentFunction.asyncMarker != AsyncMarker.SYNC &&
- currentFunction.asyncMarker != AsyncMarker.SYNC_STAR &&
- currentFunction.asyncMarker != AsyncMarker.ASYNC) {
- giveup(node, "cannot handle async* functions");
- }
+ @override
+ ir.Primitive handleStaticSetterSet(
+ ast.SendSet node,
+ FunctionElement setter,
+ ast.Node rhs,
+ _) {
+ return irBuilder.buildStaticSetterSet(setter, visit(rhs));
+ }
- if (node.initializers != null) {
- visit(node.initializers);
- }
- visit(node.body);
- currentFunction = savedFunction;
+ @override
+ ir.Primitive handleTypeLiteralConstantCompounds(
+ ast.SendSet node,
+ ConstantExpression constant,
+ CompoundRhs rhs,
+ arg) {
+ return translateCompounds(
+ node,
+ getValue: () {
+ return buildConstantExpression(constant,
+ sourceInformationBuilder.buildGet(node));
+ },
+ rhs: rhs,
+ setValue: (value) {}); // The binary operator will throw before this.
}
- visitTryStatement(ast.TryStatement node) {
- // Try/catch/finally is treated as two simpler constructs: try/catch and
- // try/finally. The encoding is:
- //
- // try S0 catch (ex, st) S1 finally S2
- // ==>
- // try { try S0 catch (ex, st) S1 } finally S2
- //
- // The analysis associates variables assigned in S0 with the catch clauses
- // and variables assigned in S0 and S1 with the finally block.
- TryStatementInfo enterTryFor(ast.Node node) {
- TryStatementInfo info = new TryStatementInfo();
- tryStatements[node] = info;
- tryNestingStack.add(info);
- return info;
- }
- void leaveTryFor(TryStatementInfo info) {
- assert(tryNestingStack.last == info);
- tryNestingStack.removeLast();
- }
- bool hasCatch = !node.catchBlocks.isEmpty;
- bool hasFinally = node.finallyBlock != null;
- TryStatementInfo catchInfo, finallyInfo;
- // There is a nesting stack of try blocks, so the outer try/finally block
- // is added first.
- if (hasFinally) finallyInfo = enterTryFor(node.finallyBlock);
- if (hasCatch) catchInfo = enterTryFor(node.catchBlocks);
- visit(node.tryBlock);
+ @override
+ ir.Primitive handleTypeLiteralConstantSetIfNulls(
+ ast.SendSet node,
+ ConstantExpression constant,
+ ast.Node rhs,
+ _) {
+ // The type literal is never `null`.
+ return buildConstantExpression(constant,
+ sourceInformationBuilder.buildGet(node));
+ }
- if (hasCatch) {
- leaveTryFor(catchInfo);
- visit(node.catchBlocks);
- }
- if (hasFinally) {
- leaveTryFor(finallyInfo);
- visit(node.finallyBlock);
+ @override
+ ir.Primitive handleDynamicCompounds(
+ ast.SendSet node,
+ ast.Node receiver,
+ Name name,
+ CompoundRhs rhs,
+ arg) {
+ ir.Primitive target = translateReceiver(receiver);
+ ir.Primitive helper() {
+ return translateCompounds(
+ node,
+ getValue: () => irBuilder.buildDynamicGet(
+ target,
+ new Selector.getter(name),
+ elements.getGetterTypeMaskInComplexSendSet(node),
+ sourceInformationBuilder.buildGet(node)),
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ irBuilder.buildDynamicSet(
+ target,
+ new Selector.setter(name),
+ elements.getTypeMask(node),
+ result);
+ });
}
+ return node.isConditional
+ ? irBuilder.buildIfNotNullSend(target, nested(helper))
+ : helper();
}
- visitVariableDefinitions(ast.VariableDefinitions node) {
- if (inTryStatement) {
- for (ast.Node definition in node.definitions.nodes) {
- LocalVariableElement local = elements[definition];
- assert(local != null);
- // In the closure conversion pass we check for isInitializingFormal,
- // but I'm not sure it can arise.
- assert(!local.isInitializingFormal);
- tryNestingStack.last.declared.add(local);
- }
+ @override
+ ir.Primitive handleDynamicSetIfNulls(
+ ast.Send node,
+ ast.Node receiver,
+ Name name,
+ ast.Node rhs,
+ _) {
+ ir.Primitive target = translateReceiver(receiver);
+ ir.Primitive helper() {
+ return translateSetIfNull(
+ node,
+ getValue: () => irBuilder.buildDynamicGet(
+ target,
+ new Selector.getter(name),
+ elements.getGetterTypeMaskInComplexSendSet(node),
+ sourceInformationBuilder.buildGet(node)),
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ irBuilder.buildDynamicSet(
+ target,
+ new Selector.setter(name),
+ elements.getTypeMask(node),
+ result);
+ });
}
- node.visitChildren(this);
+ return node.isConditional
+ ? irBuilder.buildIfNotNullSend(target, nested(helper))
+ : helper();
}
-}
-
-/// The [IrBuilder]s view on the information about the program that has been
-/// computed in resolution and and type interence.
-class GlobalProgramInformation {
- final Compiler _compiler;
- JavaScriptBackend get _backend => _compiler.backend;
-
- GlobalProgramInformation(this._compiler);
- /// Returns [true], if the analysis could not determine that the type
- /// arguments for the class [cls] are never used in the program.
- bool requiresRuntimeTypesFor(ClassElement cls) {
- return cls.typeVariables.isNotEmpty && _backend.classNeedsRti(cls);
+ ir.Primitive buildLocalNoSuchSetter(LocalElement local, ir.Primitive value) {
+ Selector selector = new Selector.setter(
+ new Name(local.name, local.library, isSetter: true));
+ return irBuilder.buildStaticNoSuchMethod(selector, [value]);
}
- FunctionElement get stringifyFunction {
- return _backend.helpers.stringInterpolationHelper;
+ @override
+ ir.Primitive handleLocalCompounds(
+ ast.SendSet node,
+ LocalElement local,
+ CompoundRhs rhs,
+ arg,
+ {bool isSetterValid}) {
+ return translateCompounds(
+ node,
+ getValue: () {
+ if (local.isFunction) {
+ return irBuilder.buildLocalFunctionGet(local);
+ } else {
+ return irBuilder.buildLocalVariableGet(local);
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ if (isSetterValid) {
+ irBuilder.buildLocalVariableSet(local, result);
+ } else {
+ return buildLocalNoSuchSetter(local, result);
+ }
+ });
}
- FunctionElement get throwTypeErrorHelper => _backend.helpers.throwTypeError;
- Element get throwNoSuchMethod => _backend.helpers.throwNoSuchMethod;
-
- ClassElement get nullClass => _compiler.coreClasses.nullClass;
+ @override
+ ir.Primitive handleLocalSetIfNulls(
+ ast.SendSet node,
+ LocalElement local,
+ ast.Node rhs,
+ _,
+ {bool isSetterValid}) {
+ return translateSetIfNull(
+ node,
+ getValue: () {
+ if (local.isFunction) {
+ return irBuilder.buildLocalFunctionGet(local);
+ } else {
+ return irBuilder.buildLocalVariableGet(local);
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ if (isSetterValid) {
+ irBuilder.buildLocalVariableSet(local, result);
+ } else {
+ return buildLocalNoSuchSetter(local, result);
+ }
+ });
+ }
- DartType unaliasType(DartType type) => type.unaliased;
+ ir.Primitive buildStaticNoSuchGetter(Element element) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.getter(new Name(element.name, element.library)),
+ const <ir.Primitive>[]);
+ }
- TypeMask getTypeMaskForForeign(NativeBehavior behavior) {
- if (behavior == null) {
- return _backend.dynamicType;
- }
- return TypeMaskFactory.fromNativeBehavior(behavior, _compiler);
+ ir.Primitive buildStaticNoSuchSetter(Element element, ir.Primitive value) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(new Name(element.name, element.library)),
+ <ir.Primitive>[value]);
}
- FieldElement locateSingleField(Selector selector, TypeMask type) {
- return _compiler.world.locateSingleField(selector, type);
+ @override
+ ir.Primitive handleStaticCompounds(
+ ast.SendSet node,
+ Element getter,
+ CompoundGetter getterKind,
+ Element setter,
+ CompoundSetter setterKind,
+ CompoundRhs rhs,
+ arg) {
+ return translateCompounds(
+ node,
+ getValue: () {
+ switch (getterKind) {
+ case CompoundGetter.FIELD:
+ SourceInformation src = sourceInformationBuilder.buildGet(node);
+ return buildStaticFieldGet(getter, src);
+ case CompoundGetter.GETTER:
+ return buildStaticGetterGet(getter, node);
+ case CompoundGetter.METHOD:
+ return irBuilder.buildStaticFunctionGet(getter);
+ case CompoundGetter.UNRESOLVED:
+ return buildStaticNoSuchGetter(getter);
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ switch (setterKind) {
+ case CompoundSetter.FIELD:
+ return irBuilder.buildStaticFieldSet(setter, result);
+ case CompoundSetter.SETTER:
+ return irBuilder.buildStaticSetterSet(setter, result);
+ case CompoundSetter.INVALID:
+ return buildStaticNoSuchSetter(setter, result);
+ }
+ });
}
- Element get closureConverter {
- return _backend.helpers.closureConverter;
+ @override
+ ir.Primitive handleStaticSetIfNulls(
+ ast.SendSet node,
+ Element getter,
+ CompoundGetter getterKind,
+ Element setter,
+ CompoundSetter setterKind,
+ ast.Node rhs,
+ _) {
+ return translateSetIfNull(
+ node,
+ getValue: () {
+ switch (getterKind) {
+ case CompoundGetter.FIELD:
+ SourceInformation src = sourceInformationBuilder.buildGet(node);
+ return buildStaticFieldGet(getter, src);
+ case CompoundGetter.GETTER:
+ return buildStaticGetterGet(getter, node);
+ case CompoundGetter.METHOD:
+ return irBuilder.buildStaticFunctionGet(getter);
+ case CompoundGetter.UNRESOLVED:
+ return buildStaticNoSuchGetter(getter);
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ switch (setterKind) {
+ case CompoundSetter.FIELD:
+ return irBuilder.buildStaticFieldSet(setter, result);
+ case CompoundSetter.SETTER:
+ return irBuilder.buildStaticSetterSet(setter, result);
+ case CompoundSetter.INVALID:
+ return buildStaticNoSuchSetter(setter, result);
+ }
+ });
}
- void addNativeMethod(FunctionElement function) {
- _backend.emitter.nativeEmitter.nativeMethods.add(function);
+ ir.Primitive buildSuperNoSuchGetter(Element element, TypeMask mask) {
+ return buildInstanceNoSuchMethod(
+ new Selector.getter(new Name(element.name, element.library)),
+ mask,
+ const <ir.Primitive>[]);
}
-}
-
-/// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder].
-class JsIrBuilderVisitor extends IrBuilderVisitor {
- JavaScriptBackend get backend => compiler.backend;
-
- /// Result of closure conversion for the current body of code.
- ///
- /// Will be initialized upon entering the body of a function.
- /// It is computed by the [ClosureTranslator].
- ClosureClassMap closureClassMap;
- JsIrBuilderVisitor(TreeElements elements,
- Compiler compiler,
- SourceInformationBuilder sourceInformationBuilder,
- TypeMaskSystem typeMaskSystem)
- : super(elements, compiler, sourceInformationBuilder, typeMaskSystem);
-
- BackendHelpers get helpers => backend.helpers;
-
- /// Builds the IR for creating an instance of the closure class corresponding
- /// to the given nested function.
- ClosureClassElement makeSubFunction(ast.FunctionExpression node) {
- ClosureClassMap innerMap =
- compiler.closureToClassMapper.getMappingForNestedFunction(node);
- ClosureClassElement closureClass = innerMap.closureClassElement;
- return closureClass;
+ ir.Primitive buildSuperNoSuchSetter(Element element,
+ TypeMask mask,
+ ir.Primitive value) {
+ return buildInstanceNoSuchMethod(
+ new Selector.setter(new Name(element.name, element.library)),
+ mask,
+ <ir.Primitive>[value]);
}
- ir.Primitive visitFunctionExpression(ast.FunctionExpression node) {
- return irBuilder.buildFunctionExpression(makeSubFunction(node),
- sourceInformationBuilder.buildCreate(node));
+ @override
+ ir.Primitive handleSuperCompounds(
+ ast.SendSet node,
+ Element getter,
+ CompoundGetter getterKind,
+ Element setter,
+ CompoundSetter setterKind,
+ CompoundRhs rhs,
+ arg) {
+ return translateCompounds(
+ node,
+ getValue: () {
+ switch (getterKind) {
+ case CompoundGetter.FIELD:
+ return irBuilder.buildSuperFieldGet(getter);
+ case CompoundGetter.GETTER:
+ return irBuilder.buildSuperGetterGet(
+ getter, sourceInformationBuilder.buildGet(node));
+ case CompoundGetter.METHOD:
+ return irBuilder.buildSuperMethodGet(getter);
+ case CompoundGetter.UNRESOLVED:
+ return buildSuperNoSuchGetter(
+ getter,
+ elements.getGetterTypeMaskInComplexSendSet(node));
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ switch (setterKind) {
+ case CompoundSetter.FIELD:
+ return irBuilder.buildSuperFieldSet(setter, result);
+ case CompoundSetter.SETTER:
+ return irBuilder.buildSuperSetterSet(setter, result);
+ case CompoundSetter.INVALID:
+ return buildSuperNoSuchSetter(
+ setter, elements.getTypeMask(node), result);
+ }
+ });
}
- visitFunctionDeclaration(ast.FunctionDeclaration node) {
- LocalFunctionElement element = elements[node.function];
- Object inner = makeSubFunction(node.function);
- irBuilder.declareLocalFunction(element, inner,
- sourceInformationBuilder.buildCreate(node.function));
+ @override
+ ir.Primitive handleSuperSetIfNulls(
+ ast.SendSet node,
+ Element getter,
+ CompoundGetter getterKind,
+ Element setter,
+ CompoundSetter setterKind,
+ ast.Node rhs,
+ _) {
+ return translateSetIfNull(
+ node,
+ getValue: () {
+ switch (getterKind) {
+ case CompoundGetter.FIELD:
+ return irBuilder.buildSuperFieldGet(getter);
+ case CompoundGetter.GETTER:
+ return irBuilder.buildSuperGetterGet(
+ getter, sourceInformationBuilder.buildGet(node));
+ case CompoundGetter.METHOD:
+ return irBuilder.buildSuperMethodGet(getter);
+ case CompoundGetter.UNRESOLVED:
+ return buildSuperNoSuchGetter(
+ getter,
+ elements.getGetterTypeMaskInComplexSendSet(node));
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ switch (setterKind) {
+ case CompoundSetter.FIELD:
+ return irBuilder.buildSuperFieldSet(setter, result);
+ case CompoundSetter.SETTER:
+ return irBuilder.buildSuperSetterSet(setter, result);
+ case CompoundSetter.INVALID:
+ return buildSuperNoSuchSetter(
+ setter, elements.getTypeMask(node), result);
+ }
+ });
}
- Map mapValues(Map map, dynamic fn(dynamic)) {
- Map result = {};
- map.forEach((key, value) {
- result[key] = fn(value);
- });
- return result;
+ @override
+ ir.Primitive handleTypeVariableTypeLiteralCompounds(
+ ast.SendSet node,
+ TypeVariableElement typeVariable,
+ CompoundRhs rhs,
+ arg) {
+ return translateCompounds(
+ node,
+ getValue: () {
+ return irBuilder.buildReifyTypeVariable(
+ typeVariable.type,
+ sourceInformationBuilder.buildGet(node));
+ },
+ rhs: rhs,
+ setValue: (value) {}); // The binary operator will throw before this.
}
- /// Converts closure.dart's CapturedVariable into a ClosureLocation.
- /// There is a 1:1 corresponce between these; we do this because the
- /// IR builder should not depend on synthetic elements.
- ClosureLocation getLocation(CapturedVariable v) {
- if (v is BoxFieldElement) {
- return new ClosureLocation(v.box, v);
- } else {
- ClosureFieldElement field = v;
- return new ClosureLocation(null, field);
- }
+ @override
+ ir.Primitive visitTypeVariableTypeLiteralSetIfNull(
+ ast.Send node,
+ TypeVariableElement element,
+ ast.Node rhs,
+ _) {
+ // The type variable is never `null`.
+ return translateTypeVariableTypeLiteral(element,
+ sourceInformationBuilder.buildGet(node));
}
- /// If the current function is a nested function with free variables (or a
- /// captured reference to `this`), returns a [ClosureEnvironment]
- /// indicating how to access these.
- ClosureEnvironment getClosureEnvironment() {
- if (closureClassMap.closureElement == null) return null;
- return new ClosureEnvironment(
- closureClassMap.closureElement,
- closureClassMap.thisLocal,
- mapValues(closureClassMap.freeVariableMap, getLocation));
+ @override
+ ir.Primitive handleIndexCompounds(
+ ast.SendSet node,
+ ast.Node receiver,
+ ast.Node index,
+ CompoundRhs rhs,
+ arg) {
+ ir.Primitive target = visit(receiver);
+ ir.Primitive indexValue = visit(index);
+ return translateCompounds(
+ node,
+ getValue: () {
+ Selector selector = new Selector.index();
+ List<ir.Primitive> arguments = <ir.Primitive>[indexValue];
+ CallStructure callStructure =
+ normalizeDynamicArguments(selector.callStructure, arguments);
+ return irBuilder.buildDynamicInvocation(
+ target,
+ new Selector(selector.kind, selector.memberName, callStructure),
+ elements.getGetterTypeMaskInComplexSendSet(node),
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(receiver, node));
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ irBuilder.buildDynamicIndexSet(
+ target,
+ elements.getTypeMask(node),
+ indexValue,
+ result);
+ });
}
- /// If [node] has declarations for variables that should be boxed,
- /// returns a [ClosureScope] naming a box to create, and enumerating the
- /// variables that should be stored in the box.
- ///
- /// Also see [ClosureScope].
- ClosureScope getClosureScopeForNode(ast.Node node) {
- closurelib.ClosureScope scope = closureClassMap.capturingScopes[node];
- if (scope == null) return null;
- // We translate a ClosureScope from closure.dart into IR builder's variant
- // because the IR builder should not depend on the synthetic elements
- // created in closure.dart.
- return new ClosureScope(scope.boxElement,
- mapValues(scope.capturedVariables, getLocation),
- scope.boxedLoopVariables);
+ @override
+ ir.Primitive handleSuperIndexCompounds(
+ ast.SendSet node,
+ Element indexFunction,
+ Element indexSetFunction,
+ ast.Node index,
+ CompoundRhs rhs,
+ arg,
+ {bool isGetterValid,
+ bool isSetterValid}) {
+ ir.Primitive indexValue = visit(index);
+ return translateCompounds(
+ node,
+ getValue: () {
+ if (isGetterValid) {
+ return irBuilder.buildSuperIndex(indexFunction, indexValue);
+ } else {
+ return buildInstanceNoSuchMethod(
+ new Selector.index(),
+ elements.getGetterTypeMaskInComplexSendSet(node),
+ <ir.Primitive>[indexValue]);
+ }
+ },
+ rhs: rhs,
+ setValue: (ir.Primitive result) {
+ if (isSetterValid) {
+ irBuilder.buildSuperIndexSet(indexSetFunction, indexValue, result);
+ } else {
+ buildInstanceNoSuchMethod(
+ new Selector.indexSet(),
+ elements.getTypeMask(node),
+ <ir.Primitive>[indexValue, result]);
+ }
+ });
}
- /// Returns the [ClosureScope] for any function, possibly different from the
- /// one currently being built.
- ClosureScope getClosureScopeForFunction(FunctionElement function) {
- ClosureClassMap map =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- function,
- function.node,
- elements);
- closurelib.ClosureScope scope = map.capturingScopes[function.node];
- if (scope == null) return null;
- return new ClosureScope(scope.boxElement,
- mapValues(scope.capturedVariables, getLocation),
- scope.boxedLoopVariables);
- }
+ /// Build code to handle foreign code, that is, native JavaScript code, or
+ /// builtin values and operations of the backend.
+ ir.Primitive handleForeignCode(ast.Send node,
+ MethodElement function,
+ ast.NodeList argumentList,
+ CallStructure callStructure) {
- ir.FunctionDefinition buildExecutable(ExecutableElement element) {
- return nullIfGiveup(() {
- ir.FunctionDefinition root;
- switch (element.kind) {
- case ElementKind.GENERATIVE_CONSTRUCTOR:
- root = buildConstructor(element);
- break;
+ void validateArgumentCount({int minimum, int exactly}) {
+ assert((minimum == null) != (exactly == null));
+ int count = 0;
+ int maximum;
+ if (exactly != null) {
+ minimum = exactly;
+ maximum = exactly;
+ }
+ for (ast.Node argument in argumentList) {
+ count++;
+ if (maximum != null && count > maximum) {
+ internalError(argument, 'Additional argument.');
+ }
+ }
+ if (count < minimum) {
+ internalError(node, 'Expected at least $minimum arguments.');
+ }
+ }
- case ElementKind.GENERATIVE_CONSTRUCTOR_BODY:
- root = buildConstructorBody(element);
- break;
+ /// Call a helper method from the isolate library. The isolate library uses
+ /// its own isolate structure, that encapsulates dart2js's isolate.
+ ir.Primitive buildIsolateHelperInvocation(Element element,
+ CallStructure callStructure) {
+ if (element == null) {
+ reporter.internalError(node,
+ 'Isolate library and compiler mismatch.');
+ }
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure = translateStaticArguments(argumentList, element,
+ callStructure, arguments);
+ return irBuilder.buildStaticFunctionInvocation(
+ element,
+ callStructure,
+ arguments,
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
+ }
- case ElementKind.FACTORY_CONSTRUCTOR:
- case ElementKind.FUNCTION:
- case ElementKind.GETTER:
- case ElementKind.SETTER:
- root = buildFunction(element);
- break;
+ /// Lookup the value of the enum described by [node].
+ getEnumValue(ast.Node node, EnumClassElement enumClass, List values) {
+ Element element = elements[node];
+ if (element is! FieldElement || element.enclosingClass != enumClass) {
+ internalError(node, 'expected a JsBuiltin enum value');
+ }
- case ElementKind.FIELD:
- if (Elements.isStaticOrTopLevel(element)) {
- root = buildStaticFieldInitializer(element);
- } else {
- // Instance field initializers are inlined in the constructor,
- // so we shouldn't need to build anything here.
- // TODO(asgerf): But what should we return?
- return null;
- }
- break;
+ int index = enumClass.enumValues.indexOf(element);
+ return values[index];
+ }
- default:
- reporter.internalError(element, "Unexpected element type $element");
+ /// Returns the String the node evaluates to, or throws an error if the
+ /// result is not a string constant.
+ String expectStringConstant(ast.Node node) {
+ ir.Primitive nameValue = visit(node);
+ if (nameValue is ir.Constant && nameValue.value.isString) {
+ StringConstantValue constantValue = nameValue.value;
+ return constantValue.primitiveValue.slowToString();
+ } else {
+ return internalError(node, 'expected a literal string');
}
- return root;
- });
- }
-
- ir.FunctionDefinition buildStaticFieldInitializer(FieldElement element) {
- if (!backend.constants.lazyStatics.contains(element)) {
- return null; // Nothing to do.
}
- closureClassMap =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- element,
- element.node,
- elements);
- IrBuilder builder = getBuilderFor(element);
- return withBuilder(builder, () {
- irBuilder.buildFunctionHeader(<Local>[]);
- ir.Primitive initialValue = visit(element.initializer);
- ast.VariableDefinitions node = element.node;
- ast.SendSet sendSet = node.definitions.nodes.head;
- irBuilder.buildReturn(
- value: initialValue,
- sourceInformation:
- sourceInformationBuilder.buildReturn(sendSet.assignmentOperator));
- return irBuilder.makeFunctionDefinition();
- });
- }
- /// Make a visitor suitable for translating ASTs taken from [context].
- ///
- /// Every visitor can only be applied to nodes in one context, because
- /// the [elements] field is specific to that context.
- JsIrBuilderVisitor makeVisitorForContext(AstElement context) {
- return new JsIrBuilderVisitor(
- context.resolvedAst.elements,
- compiler,
- sourceInformationBuilder.forContext(context),
- typeMaskSystem);
- }
+ Link<ast.Node> argumentNodes = argumentList.nodes;
+ NativeBehavior behavior =
+ compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
+ switch (function.name) {
+ case 'JS':
+ validateArgumentCount(minimum: 2);
+ // The first two arguments are the type and the foreign code template,
+ // which already have been analyzed by the resolver and can be retrieved
+ // using [NativeBehavior]. We can ignore these arguments in the backend.
+ List<ir.Primitive> arguments =
+ argumentNodes.skip(2).mapToList(visit, growable: false);
+ if (behavior.codeTemplate.positionalArgumentCount != arguments.length) {
+ reporter.reportErrorMessage(
+ node, MessageKind.GENERIC,
+ {'text':
+ 'Mismatch between number of placeholders'
+ ' and number of arguments.'});
+ return irBuilder.buildNullConstant();
+ }
- /// Builds the IR for an [expression] taken from a different [context].
- ///
- /// Such expressions need to be compiled with a different [sourceFile] and
- /// [elements] mapping.
- ir.Primitive inlineExpression(AstElement context, ast.Expression expression) {
- JsIrBuilderVisitor visitor = makeVisitorForContext(context);
- return visitor.withBuilder(irBuilder, () => visitor.visit(expression));
- }
+ if (HasCapturedPlaceholders.check(behavior.codeTemplate.ast)) {
+ reporter.reportErrorMessage(node, MessageKind.JS_PLACEHOLDER_CAPTURE);
+ return irBuilder.buildNullConstant();
+ }
- /// Builds the IR for a constant taken from a different [context].
- ///
- /// Such constants need to be compiled with a different [sourceFile] and
- /// [elements] mapping.
- ir.Primitive inlineConstant(AstElement context, ast.Expression exp) {
- JsIrBuilderVisitor visitor = makeVisitorForContext(context);
- return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp));
- }
+ return irBuilder.buildForeignCode(behavior.codeTemplate, arguments,
+ behavior);
- IrBuilder getBuilderFor(Element element) {
- return new IrBuilder(
- new GlobalProgramInformation(compiler),
- compiler.backend.constants,
- element);
- }
+ case 'DART_CLOSURE_TO_JS':
+ // TODO(ahe): This should probably take care to wrap the closure in
+ // another closure that saves the current isolate.
+ case 'RAW_DART_FUNCTION_REF':
+ validateArgumentCount(exactly: 1);
- /// Builds the IR for a given constructor.
- ///
- /// 1. Computes the type held in all own or "inherited" type variables.
- /// 2. Evaluates all own or inherited field initializers.
- /// 3. Creates the object and assigns its fields and runtime type.
- /// 4. Calls constructor body and super constructor bodies.
- /// 5. Returns the created object.
- ir.FunctionDefinition buildConstructor(ConstructorElement constructor) {
- // TODO(asgerf): Optimization: If constructor is redirecting, then just
- // evaluate arguments and call the target constructor.
- constructor = constructor.implementation;
- ClassElement classElement = constructor.enclosingClass.implementation;
+ ast.Node argument = node.arguments.single;
+ FunctionElement closure = elements[argument].implementation;
+ if (!Elements.isStaticOrTopLevelFunction(closure)) {
+ internalError(argument,
+ 'only static or toplevel function supported');
+ }
+ if (closure.functionSignature.hasOptionalParameters) {
+ internalError(argument,
+ 'closures with optional parameters not supported');
+ }
+ return irBuilder.buildForeignCode(
+ js.js.expressionTemplateYielding(
+ backend.emitter.staticFunctionAccess(closure)),
+ <ir.Primitive>[],
+ NativeBehavior.PURE,
+ dependency: closure);
- IrBuilder builder = getBuilderFor(constructor);
+ case 'JS_BUILTIN':
+ // The first argument is a description of the type and effect of the
+ // builtin, which has already been analyzed in the frontend. The second
+ // argument must be a [JsBuiltin] value. All other arguments are
+ // values used by the JavaScript template that is associated with the
+ // builtin.
+ validateArgumentCount(minimum: 2);
- final bool requiresTypeInformation =
- builder.program.requiresRuntimeTypesFor(classElement);
+ ast.Node builtin = argumentNodes.tail.head;
+ JsBuiltin value = getEnumValue(builtin, helpers.jsBuiltinEnum,
+ JsBuiltin.values);
+ js.Template template = backend.emitter.builtinTemplateFor(value);
+ List<ir.Primitive> arguments =
+ argumentNodes.skip(2).mapToList(visit, growable: false);
+ return irBuilder.buildForeignCode(template, arguments, behavior);
- return withBuilder(builder, () {
- // Setup parameters and create a box if anything is captured.
- List<Local> parameters = <Local>[];
- constructor.functionSignature.orderedForEachParameter(
- (ParameterElement p) => parameters.add(p));
+ case 'JS_EMBEDDED_GLOBAL':
+ validateArgumentCount(exactly: 2);
- int firstTypeArgumentParameterIndex;
+ String name = expectStringConstant(argumentNodes.tail.head);
+ js.Expression access =
+ backend.emitter.generateEmbeddedGlobalAccess(name);
+ js.Template template = js.js.expressionTemplateYielding(access);
+ return irBuilder.buildForeignCode(template, <ir.Primitive>[], behavior);
- // If instances of the class may need runtime type information, we add a
- // synthetic parameter for each type parameter.
- if (requiresTypeInformation) {
- firstTypeArgumentParameterIndex = parameters.length;
- classElement.typeVariables.forEach((TypeVariableType variable) {
- parameters.add(new TypeVariableLocal(variable, constructor));
- });
- } else {
- classElement.typeVariables.forEach((TypeVariableType variable) {
- irBuilder.declareTypeVariable(variable, const DynamicType());
- });
- }
+ case 'JS_INTERCEPTOR_CONSTANT':
+ validateArgumentCount(exactly: 1);
- // Create IR parameters and setup the environment.
- List<ir.Parameter> irParameters = builder.buildFunctionHeader(parameters,
- closureScope: getClosureScopeForFunction(constructor));
+ ast.Node argument = argumentNodes.head;
+ ir.Primitive argumentValue = visit(argument);
+ if (argumentValue is ir.Constant && argumentValue.value.isType) {
+ TypeConstantValue constant = argumentValue.value;
+ ConstantValue interceptorValue =
+ new InterceptorConstantValue(constant.representedType);
+ return irBuilder.buildConstant(interceptorValue);
+ }
+ return internalError(argument, 'expected Type as argument');
- // Create a list of the values of all type argument parameters, if any.
- List<ir.Primitive> typeInformation;
- if (requiresTypeInformation) {
- typeInformation = irParameters.sublist(firstTypeArgumentParameterIndex);
- } else {
- typeInformation = const <ir.Primitive>[];
- }
+ case 'JS_EFFECT':
+ return irBuilder.buildNullConstant();
- // -- Load values for type variables declared on super classes --
- // Field initializers for super classes can reference these, so they
- // must be available before evaluating field initializers.
- // This could be interleaved with field initialization, but we choose do
- // get it out of the way here to avoid complications with mixins.
- loadTypeVariablesForSuperClasses(classElement);
+ case 'JS_GET_NAME':
+ validateArgumentCount(exactly: 1);
- /// Maps each field from this class or a superclass to its initial value.
- Map<FieldElement, ir.Primitive> fieldValues =
- <FieldElement, ir.Primitive>{};
+ ast.Node argument = argumentNodes.head;
+ JsGetName id = getEnumValue(argument, helpers.jsGetNameEnum,
+ JsGetName.values);
+ js.Name name = backend.namer.getNameForJsGetName(argument, id);
+ ConstantValue nameConstant =
+ new SyntheticConstantValue(SyntheticConstantKind.NAME,
+ js.js.quoteName(name));
- // -- Evaluate field initializers ---
- // Evaluate field initializers in constructor and super constructors.
- List<ConstructorElement> constructorList = <ConstructorElement>[];
- evaluateConstructorFieldInitializers(
- constructor, constructorList, fieldValues);
+ return irBuilder.buildConstant(nameConstant);
- // All parameters in all constructors are now bound in the environment.
- // BoxLocals for captured parameters are also in the environment.
- // The initial value of all fields are now bound in [fieldValues].
+ case 'JS_GET_FLAG':
+ validateArgumentCount(exactly: 1);
- // --- Create the object ---
- // Get the initial field values in the canonical order.
- List<ir.Primitive> instanceArguments = <ir.Primitive>[];
- classElement.forEachInstanceField((ClassElement c, FieldElement field) {
- ir.Primitive value = fieldValues[field];
- if (value != null) {
- instanceArguments.add(value);
- } else {
- assert(backend.isNativeOrExtendsNative(c));
- // Native fields are initialized elsewhere.
+ String name = expectStringConstant(argumentNodes.first);
+ bool value = false;
+ switch (name) {
+ case 'MUST_RETAIN_METADATA':
+ value = backend.mustRetainMetadata;
+ break;
+ case 'USE_CONTENT_SECURITY_POLICY':
+ value = compiler.useContentSecurityPolicy;
+ break;
+ default:
+ internalError(node, 'Unknown internal flag "$name".');
}
- }, includeSuperAndInjectedMembers: true);
+ return irBuilder.buildBooleanConstant(value);
- ir.Primitive instance = new ir.CreateInstance(
- classElement,
- instanceArguments,
- typeInformation,
- constructor.hasNode
- ? sourceInformationBuilder.buildCreate(constructor.node)
- // TODO(johnniwinther): Provide source information for creation
- // through synthetic constructors.
- : null);
- irBuilder.add(new ir.LetPrim(instance));
+ case 'JS_STRING_CONCAT':
+ validateArgumentCount(exactly: 2);
+ List<ir.Primitive> arguments = argumentNodes.mapToList(visit);
+ return irBuilder.buildStringConcatenation(arguments);
- // --- Call constructor bodies ---
- for (ConstructorElement target in constructorList) {
- ConstructorBodyElement bodyElement = getConstructorBody(target);
- if (bodyElement == null) continue; // Skip if constructor has no body.
- List<ir.Primitive> bodyArguments = <ir.Primitive>[];
- for (Local param in getConstructorBodyParameters(bodyElement)) {
- bodyArguments.add(irBuilder.environment.lookup(param));
+ case 'JS_CURRENT_ISOLATE_CONTEXT':
+ validateArgumentCount(exactly: 0);
+
+ if (!compiler.hasIsolateSupport) {
+ // If the isolate library is not used, we just generate code
+ // to fetch the current isolate.
+ continue getStaticState;
}
- irBuilder.buildInvokeDirectly(bodyElement, instance, bodyArguments);
- }
+ return buildIsolateHelperInvocation(helpers.currentIsolate,
+ CallStructure.NO_ARGS);
- // --- step 4: return the created object ----
- irBuilder.buildReturn(
- value: instance,
- sourceInformation:
- sourceInformationBuilder.buildImplicitReturn(constructor));
+ getStaticState: case 'JS_GET_STATIC_STATE':
+ validateArgumentCount(exactly: 0);
- return irBuilder.makeFunctionDefinition();
- });
- }
+ return irBuilder.buildForeignCode(
+ js.js.parseForeignJS(backend.namer.staticStateHolder),
+ const <ir.Primitive>[],
+ NativeBehavior.PURE);
- /// Evaluates all field initializers on [constructor] and all constructors
- /// invoked through `this()` or `super()` ("superconstructors").
- ///
- /// The resulting field values will be available in [fieldValues]. The values
- /// are not stored in any fields.
- ///
- /// This procedure assumes that the parameters to [constructor] are available
- /// in the IR builder's environment.
- ///
- /// The parameters to superconstructors are, however, assumed *not* to be in
- /// the environment, but will be put there by this procedure.
- ///
- /// All constructors will be added to [supers], with superconstructors first.
- void evaluateConstructorFieldInitializers(
- ConstructorElement constructor,
- List<ConstructorElement> supers,
- Map<FieldElement, ir.Primitive> fieldValues) {
- assert(constructor.isImplementation);
- assert(constructor == elements.analyzedElement);
- ClassElement enclosingClass = constructor.enclosingClass.implementation;
- // Evaluate declaration-site field initializers, unless this constructor
- // redirects to another using a `this()` initializer. In that case, these
- // will be initialized by the effective target constructor.
- if (!constructor.isRedirectingGenerative) {
- enclosingClass.forEachInstanceField((ClassElement c, FieldElement field) {
- if (field.initializer != null) {
- fieldValues[field] = inlineExpression(field, field.initializer);
- } else {
- if (backend.isNativeOrExtendsNative(c)) {
- // Native field is initialized elsewhere.
- } else {
- // Fields without an initializer default to null.
- // This value will be overwritten below if an initializer is found.
- fieldValues[field] = irBuilder.buildNullConstant();
- }
+ case 'JS_SET_STATIC_STATE':
+ validateArgumentCount(exactly: 1);
+
+ ir.Primitive value = visit(argumentNodes.single);
+ String isolateName = backend.namer.staticStateHolder;
+ return irBuilder.buildForeignCode(
+ js.js.parseForeignJS("$isolateName = #"),
+ <ir.Primitive>[value],
+ NativeBehavior.PURE);
+
+ case 'JS_CALL_IN_ISOLATE':
+ validateArgumentCount(exactly: 2);
+
+ if (!compiler.hasIsolateSupport) {
+ ir.Primitive closure = visit(argumentNodes.tail.head);
+ return irBuilder.buildCallInvocation(closure, CallStructure.NO_ARGS,
+ const <ir.Primitive>[]);
}
- });
- }
- // If this is a mixin constructor, it does not have its own parameter list
- // or initializer list. Directly forward to the super constructor.
- // Note that the declaration-site initializers originating from the
- // mixed-in class were handled above.
- if (enclosingClass.isMixinApplication) {
- forwardSynthesizedMixinConstructor(constructor, supers, fieldValues);
- return;
+ return buildIsolateHelperInvocation(helpers.callInIsolate,
+ CallStructure.TWO_ARGS);
+
+ default:
+ return giveup(node, 'unplemented native construct: ${function.name}');
}
- // Evaluate initializing parameters, e.g. `Foo(this.x)`.
- constructor.functionSignature.orderedForEachParameter(
- (ParameterElement parameter) {
- if (parameter.isInitializingFormal) {
- InitializingFormalElement fieldParameter = parameter;
- fieldValues[fieldParameter.fieldElement] =
- irBuilder.buildLocalVariableGet(parameter);
- }
- });
- // Evaluate constructor initializers, e.g. `Foo() : x = 50`.
- ast.FunctionExpression node = constructor.node;
- bool hasConstructorCall = false; // Has this() or super() initializer?
- if (node != null && node.initializers != null) {
- for(ast.Node initializer in node.initializers) {
- if (initializer is ast.SendSet) {
- // Field initializer.
- FieldElement field = elements[initializer];
- fieldValues[field] = visit(initializer.arguments.head);
- } else if (initializer is ast.Send) {
- // Super or this initializer.
- ConstructorElement target = elements[initializer].implementation;
- Selector selector = elements.getSelector(initializer);
- List<ir.Primitive> arguments = initializer.arguments.mapToList(visit);
- evaluateConstructorCallFromInitializer(
- target,
- selector.callStructure,
- arguments,
- supers,
- fieldValues);
- hasConstructorCall = true;
- } else {
- reporter.internalError(initializer,
- "Unexpected initializer type $initializer");
- }
+ }
+
+ /// Evaluates a string interpolation and appends each part to [accumulator]
+ /// (after stringify conversion).
+ void buildStringParts(ast.Node node, List<ir.Primitive> accumulator) {
+ if (node is ast.StringJuxtaposition) {
+ buildStringParts(node.first, accumulator);
+ buildStringParts(node.second, accumulator);
+ } else if (node is ast.StringInterpolation) {
+ buildStringParts(node.string, accumulator);
+ for (ast.StringInterpolationPart part in node.parts) {
+ buildStringParts(part.expression, accumulator);
+ buildStringParts(part.string, accumulator);
}
- }
- // If no super() or this() was found, also call default superconstructor.
- if (!hasConstructorCall && !enclosingClass.isObject) {
- ClassElement superClass = enclosingClass.superclass;
- FunctionElement target = superClass.lookupDefaultConstructor();
- if (target == null) {
- reporter.internalError(superClass, "No default constructor available.");
+ } else if (node is ast.LiteralString) {
+ // Empty strings often occur at the end of a string interpolation,
+ // do not bother to include them.
+ if (!node.dartString.isEmpty) {
+ accumulator.add(irBuilder.buildDartStringConstant(node.dartString));
}
- target = target.implementation;
- evaluateConstructorCallFromInitializer(
- target,
- CallStructure.NO_ARGS,
- const [],
- supers,
- fieldValues);
+ } else if (node is ast.ParenthesizedExpression) {
+ buildStringParts(node.expression, accumulator);
+ } else {
+ ir.Primitive value = visit(node);
+ accumulator.add(irBuilder.buildStringify(value));
}
- // Add this constructor after the superconstructors.
- supers.add(constructor);
}
- /// Evaluates a call to the given constructor from an initializer list.
- ///
- /// Calls [loadArguments] and [evaluateConstructorFieldInitializers] in a
- /// visitor that has the proper [TreeElements] mapping.
- void evaluateConstructorCallFromInitializer(
- ConstructorElement target,
- CallStructure call,
- List<ir.Primitive> arguments,
- List<ConstructorElement> supers,
- Map<FieldElement, ir.Primitive> fieldValues) {
- JsIrBuilderVisitor visitor = makeVisitorForContext(target);
- visitor.withBuilder(irBuilder, () {
- visitor.loadArguments(target, call, arguments);
- visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
- });
+ ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) {
+ assert(irBuilder.isOpen);
+ List<ir.Primitive> parts = <ir.Primitive>[];
+ buildStringParts(node, parts);
+ return irBuilder.buildStringConcatenation(parts);
+ }
+
+ ir.Primitive visitStringInterpolation(ast.StringInterpolation node) {
+ assert(irBuilder.isOpen);
+ List<ir.Primitive> parts = <ir.Primitive>[];
+ buildStringParts(node, parts);
+ return irBuilder.buildStringConcatenation(parts);
+ }
+
+ ir.Primitive translateConstant(ast.Node node) {
+ assert(irBuilder.isOpen);
+ return irBuilder.buildConstant(
+ getConstantForNode(node),
+ sourceInformation: sourceInformationBuilder.buildGet(node));
}
- /// Evaluate the implicit super call in the given mixin constructor.
- void forwardSynthesizedMixinConstructor(
- ConstructorElement constructor,
- List<ConstructorElement> supers,
- Map<FieldElement, ir.Primitive> fieldValues) {
- assert(constructor.enclosingClass.implementation.isMixinApplication);
- assert(constructor.isSynthesized);
- ConstructorElement target =
- constructor.definingConstructor.implementation;
- // The resolver gives us the exact same FunctionSignature for the two
- // constructors. The parameters for the synthesized constructor
- // are already in the environment, so the target constructor's parameters
- // are also in the environment since their elements are the same.
- assert(constructor.functionSignature == target.functionSignature);
- JsIrBuilderVisitor visitor = makeVisitorForContext(target);
- visitor.withBuilder(irBuilder, () {
- visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
- });
+ ir.Primitive visitThrow(ast.Throw node) {
+ assert(irBuilder.isOpen);
+ // This function is not called for throw expressions occurring as
+ // statements.
+ return irBuilder.buildNonTailThrow(visit(node.expression));
}
- /// Loads the type variables for all super classes of [superClass] into the
- /// IR builder's environment with their corresponding values.
- ///
- /// The type variables for [currentClass] must already be in the IR builder's
- /// environment.
- ///
- /// Type variables are stored as [TypeVariableLocal] in the environment.
- ///
- /// This ensures that access to type variables mentioned inside the
- /// constructors and initializers will happen through the local environment
- /// instead of using 'this'.
- void loadTypeVariablesForSuperClasses(ClassElement currentClass) {
- if (currentClass.isObject) return;
- loadTypeVariablesForType(currentClass.supertype);
- if (currentClass is MixinApplicationElement) {
- loadTypeVariablesForType(currentClass.mixinType);
- }
+ ir.Primitive buildInstanceNoSuchMethod(Selector selector,
+ TypeMask mask,
+ List<ir.Primitive> arguments) {
+ return irBuilder.buildDynamicInvocation(
+ irBuilder.buildThis(),
+ Selectors.noSuchMethod_,
+ mask,
+ [irBuilder.buildInvocationMirror(selector, arguments)]);
}
- /// Loads all type variables for [type] and all of its super classes into
- /// the environment. All type variables mentioned in [type] must already
- /// be in the environment.
- void loadTypeVariablesForType(InterfaceType type) {
- ClassElement clazz = type.element;
- assert(clazz.typeVariables.length == type.typeArguments.length);
- for (int i = 0; i < clazz.typeVariables.length; ++i) {
- irBuilder.declareTypeVariable(clazz.typeVariables[i],
- type.typeArguments[i]);
- }
- loadTypeVariablesForSuperClasses(clazz);
+ ir.Primitive buildRuntimeError(String message) {
+ return irBuilder.buildStaticFunctionInvocation(
+ helpers.throwRuntimeError,
+ new CallStructure.unnamed(1),
+ [irBuilder.buildStringConstant(message)]);
}
- /// In preparation of inlining (part of) [target], the [arguments] are moved
- /// into the environment bindings for the corresponding parameters.
- ///
- /// Defaults for optional arguments are evaluated in order to ensure
- /// all parameters are available in the environment.
- void loadArguments(ConstructorElement target,
- CallStructure call,
- List<ir.Primitive> arguments) {
- assert(target.isImplementation);
- assert(target == elements.analyzedElement);
- FunctionSignature signature = target.functionSignature;
+ ir.Primitive buildAbstractClassInstantiationError(ClassElement element) {
+ return irBuilder.buildStaticFunctionInvocation(
+ helpers.throwAbstractClassInstantiationError,
+ new CallStructure.unnamed(1),
+ [irBuilder.buildStringConstant(element.name)]);
+ }
- // Establish a scope in case parameters are captured.
- ClosureScope scope = getClosureScopeForFunction(target);
- irBuilder.enterScope(scope);
+ @override
+ ir.Primitive visitUnresolvedCompound(
+ ast.Send node,
+ Element element,
+ op.AssignmentOperator operator,
+ ast.Node rhs, _) {
+ // TODO(asgerf): What is unresolved? The getter and/or the setter?
+ // If it was the setter, we must evaluate the right-hand side.
+ return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node), []);
+ }
- // Load required parameters
- int index = 0;
- signature.forEachRequiredParameter((ParameterElement param) {
- irBuilder.declareLocalVariable(param, initialValue: arguments[index]);
- index++;
- });
+ @override
+ ir.Primitive visitUnresolvedClassConstructorInvoke(
+ ast.NewExpression node,
+ Element element,
+ DartType type,
+ ast.NodeList arguments,
+ Selector selector, _) {
+ // If the class is missing it's a runtime error.
+ return buildRuntimeError("Unresolved class: '${element.name}'");
+ }
- // Load optional parameters, evaluating default values for omitted ones.
- signature.forEachOptionalParameter((ParameterElement param) {
- ir.Primitive value;
- // Load argument if provided.
- if (signature.optionalParametersAreNamed) {
- int nameIndex = call.namedArguments.indexOf(param.name);
- if (nameIndex != -1) {
- int translatedIndex = call.positionalArgumentCount + nameIndex;
- value = arguments[translatedIndex];
- }
- } else if (index < arguments.length) {
- value = arguments[index];
- }
- // Load default if argument was not provided.
- if (value == null) {
- if (param.initializer != null) {
- value = visit(param.initializer);
- } else {
- value = irBuilder.buildNullConstant();
- }
- }
- irBuilder.declareLocalVariable(param, initialValue: value);
- index++;
- });
+ @override
+ ir.Primitive visitUnresolvedConstructorInvoke(
+ ast.NewExpression node,
+ Element constructor,
+ DartType type,
+ ast.NodeList argumentsNode,
+ Selector selector, _) {
+ // If the class is there but the constructor is missing, it's an NSM error.
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ CallStructure callStructure = translateDynamicArguments(
+ argumentsNode, selector.callStructure, arguments);
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector(selector.kind, selector.memberName, callStructure),
+ arguments);
}
- /**
- * Returns the constructor body associated with the given constructor or
- * creates a new constructor body, if none can be found.
- *
- * Returns `null` if the constructor does not have a body.
- */
- ConstructorBodyElement getConstructorBody(FunctionElement constructor) {
- // TODO(asgerf): This is largely inherited from the SSA builder.
- // The ConstructorBodyElement has an invalid function signature, but we
- // cannot add a BoxLocal as parameter, because BoxLocal is not an element.
- // Instead of forging ParameterElements to forge a FunctionSignature, we
- // need a way to create backend methods without creating more fake elements.
- assert(constructor.isGenerativeConstructor);
- assert(constructor.isImplementation);
- if (constructor.isSynthesized) return null;
- ast.FunctionExpression node = constructor.node;
- // If we know the body doesn't have any code, we don't generate it.
- if (!node.hasBody()) return null;
- if (node.hasEmptyBody()) return null;
- ClassElement classElement = constructor.enclosingClass;
- ConstructorBodyElement bodyElement;
- classElement.forEachBackendMember((Element backendMember) {
- if (backendMember.isGenerativeConstructorBody) {
- ConstructorBodyElement body = backendMember;
- if (body.constructor == constructor) {
- bodyElement = backendMember;
- }
- }
- });
- if (bodyElement == null) {
- bodyElement = new ConstructorBodyElementX(constructor);
- classElement.addBackendMember(bodyElement);
+ @override
+ ir.Primitive visitConstructorIncompatibleInvoke(
+ ast.NewExpression node,
+ ConstructorElement constructor,
+ DartType type,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure, _) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.call(constructor.memberName, callStructure), arguments);
+ }
- if (constructor.isPatch) {
- // Create origin body element for patched constructors.
- ConstructorBodyElementX patch = bodyElement;
- ConstructorBodyElementX origin =
- new ConstructorBodyElementX(constructor.origin);
- origin.applyPatch(patch);
- classElement.origin.addBackendMember(bodyElement.origin);
- }
- }
- assert(bodyElement.isGenerativeConstructorBody);
- return bodyElement;
+ @override
+ ir.Primitive visitUnresolvedGet(
+ ast.Send node,
+ Element element, _) {
+ return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node), []);
}
- /// The list of parameters to send from the generative constructor
- /// to the generative constructor body.
- ///
- /// Boxed parameters are not in the list, instead, a [BoxLocal] is passed
- /// containing the boxed parameters.
- ///
- /// For example, given the following constructor,
- ///
- /// Foo(x, y) : field = (() => ++x) { print(x + y) }
- ///
- /// the argument `x` would be replaced by a [BoxLocal]:
- ///
- /// Foo_body(box0, y) { print(box0.x + y) }
- ///
- List<Local> getConstructorBodyParameters(ConstructorBodyElement body) {
- List<Local> parameters = <Local>[];
- ClosureScope scope = getClosureScopeForFunction(body.constructor);
- if (scope != null) {
- parameters.add(scope.box);
- }
- body.functionSignature.orderedForEachParameter((ParameterElement param) {
- if (scope != null && scope.capturedVariables.containsKey(param)) {
- // Do not pass this parameter; the box will carry its value.
- } else {
- parameters.add(param);
- }
- });
- return parameters;
+ @override
+ ir.Primitive visitUnresolvedInvoke(
+ ast.Send node,
+ Element element,
+ ast.NodeList arguments,
+ Selector selector, _) {
+ return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node),
+ arguments.nodes.mapToList(visit));
+ }
+
+ @override
+ ir.Primitive visitUnresolvedRedirectingFactoryConstructorInvoke(
+ ast.NewExpression node,
+ ConstructorElement constructor,
+ InterfaceType type,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure, _) {
+ String nameString = Elements.reconstructConstructorName(constructor);
+ Name name = new Name(nameString, constructor.library);
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.call(name, callStructure),
+ arguments);
}
- TryBoxedVariables _analyzeTryBoxedVariables(ast.Node node) {
- TryBoxedVariables variables = new TryBoxedVariables(elements);
- try {
- variables.analyze(node);
- } catch (e) {
- bailoutMessage = variables.bailoutMessage;
- rethrow;
- }
- return variables;
+ @override
+ ir.Primitive visitUnresolvedSet(
+ ast.Send node,
+ Element element,
+ ast.Node rhs, _) {
+ return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node),
+ [visit(rhs)]);
}
- /// Builds the IR for the body of a constructor.
- ///
- /// This function is invoked from one or more "factory" constructors built by
- /// [buildConstructor].
- ir.FunctionDefinition buildConstructorBody(ConstructorBodyElement body) {
- ConstructorElement constructor = body.constructor;
- ast.FunctionExpression node = constructor.node;
- closureClassMap =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- constructor,
- node,
- elements);
+ @override
+ ir.Primitive visitUnresolvedSuperIndex(
+ ast.Send node,
+ Element function,
+ ast.Node index, _) {
+ // Assume the index getter is missing.
+ return buildInstanceNoSuchMethod(
+ new Selector.index(), elements.getTypeMask(node), [visit(index)]);
+ }
- // We compute variables boxed in mutable variables on entry to each try
- // block, not including variables captured by a closure (which are boxed
- // in the heap). This duplicates some of the work of closure conversion
- // without directly using the results. This duplication is wasteful and
- // error-prone.
- // TODO(kmillikin): We should combine closure conversion and try/catch
- // variable analysis in some way.
- TryBoxedVariables variables = _analyzeTryBoxedVariables(node);
- tryStatements = variables.tryStatements;
- IrBuilder builder = getBuilderFor(body);
+ @override
+ ir.Primitive visitUnresolvedSuperBinary(
+ ast.Send node,
+ Element element,
+ op.BinaryOperator operator,
+ ast.Node argument, _) {
+ return buildInstanceNoSuchMethod(
+ elements.getSelector(node),
+ elements.getTypeMask(node),
+ [visit(argument)]);
+ }
- return withBuilder(builder, () {
- irBuilder.buildConstructorBodyHeader(getConstructorBodyParameters(body),
- getClosureScopeForNode(node));
- visit(node.body);
- return irBuilder.makeFunctionDefinition();
- });
+ @override
+ ir.Primitive visitUnresolvedSuperUnary(
+ ast.Send node,
+ op.UnaryOperator operator,
+ Element element, _) {
+ return buildInstanceNoSuchMethod(
+ elements.getSelector(node), elements.getTypeMask(node), []);
}
- ir.FunctionDefinition buildFunction(FunctionElement element) {
- assert(invariant(element, element.isImplementation));
- ast.FunctionExpression node = element.node;
+ @override
+ ir.Primitive bulkHandleNode(ast.Node node, String message, _) {
+ return giveup(node, "Unhandled node: ${message.replaceAll('#', '$node')}");
+ }
- assert(!element.isSynthesized);
- assert(node != null);
- assert(elements[node] != null);
+ @override
+ ir.Primitive bulkHandleError(ast.Node node, ErroneousElement error, _) {
+ return irBuilder.buildNullConstant();
+ }
- closureClassMap =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- element,
- node,
- elements);
- TryBoxedVariables variables = _analyzeTryBoxedVariables(node);
- tryStatements = variables.tryStatements;
- IrBuilder builder = getBuilderFor(element);
- return withBuilder(builder, () => _makeFunctionBody(element, node));
+ @override
+ ir.Primitive visitClassTypeLiteralSet(
+ ast.SendSet node,
+ TypeConstantExpression constant,
+ ast.Node rhs, _) {
+ InterfaceType type = constant.type;
+ ClassElement element = type.element;
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(element.memberName),
+ [visit(rhs)]);
}
- /// Creates a primitive for the default value of [parameter].
- ir.Primitive translateDefaultValue(ParameterElement parameter) {
- if (parameter.initializer == null) {
- return irBuilder.buildNullConstant();
- } else {
- return inlineConstant(parameter.executableContext, parameter.initializer);
- }
+ @override
+ ir.Primitive visitTypedefTypeLiteralSet(
+ ast.SendSet node,
+ TypeConstantExpression constant,
+ ast.Node rhs, _) {
+ TypedefType type = constant.type;
+ TypedefElement element = type.element;
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(element.memberName),
+ [visit(rhs)]);
}
- CallStructure normalizeStaticArguments(CallStructure callStructure,
- FunctionElement target,
- List<ir.Primitive> arguments) {
- target = target.implementation;
- FunctionSignature signature = target.functionSignature;
- if (!signature.optionalParametersAreNamed &&
- signature.parameterCount == arguments.length) {
- return callStructure;
- }
+ @override
+ ir.Primitive visitTypeVariableTypeLiteralSet(
+ ast.SendSet node,
+ TypeVariableElement element,
+ ast.Node rhs, _) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(element.memberName), [visit(rhs)]);
+ }
- if (!signature.optionalParametersAreNamed) {
- int i = signature.requiredParameterCount;
- signature.forEachOptionalParameter((ParameterElement element) {
- if (i < callStructure.positionalArgumentCount) {
- ++i;
- } else {
- arguments.add(translateDefaultValue(element));
- }
- });
- return new CallStructure(signature.parameterCount);
- }
+ @override
+ ir.Primitive visitDynamicTypeLiteralSet(
+ ast.SendSet node,
+ ConstantExpression constant,
+ ast.Node rhs, _) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(Names.dynamic_), [visit(rhs)]);
+ }
- int offset = signature.requiredParameterCount;
- List<ir.Primitive> namedArguments = arguments.sublist(offset);
- arguments.length = offset;
- List<String> normalizedNames = <String>[];
- // Iterate over the optional parameters of the signature, and try to
- // find them in the callStructure's named arguments. If found, we use the
- // value in the temporary list, otherwise the default value.
- signature.orderedOptionalParameters.forEach((ParameterElement element) {
- int nameIndex = callStructure.namedArguments.indexOf(element.name);
- arguments.add(nameIndex == -1 ? translateDefaultValue(element)
- : namedArguments[nameIndex]);
- normalizedNames.add(element.name);
- });
- return new CallStructure(signature.parameterCount, normalizedNames);
+ @override
+ ir.Primitive visitAbstractClassConstructorInvoke(
+ ast.NewExpression node,
+ ConstructorElement element,
+ InterfaceType type,
+ ast.NodeList arguments,
+ CallStructure callStructure, _) {
+ for (ast.Node argument in arguments) visit(argument);
+ return buildAbstractClassInstantiationError(element.enclosingClass);
}
- CallStructure normalizeDynamicArguments(CallStructure callStructure,
- List<ir.Primitive> arguments) {
- assert(arguments.length == callStructure.argumentCount);
- if (callStructure.namedArguments.isEmpty) return callStructure;
- int destinationIndex = callStructure.positionalArgumentCount;
- List<ir.Primitive> namedArguments = arguments.sublist(destinationIndex);
- for (String argName in callStructure.getOrderedNamedArguments()) {
- int sourceIndex = callStructure.namedArguments.indexOf(argName);
- arguments[destinationIndex++] = namedArguments[sourceIndex];
- }
- return new CallStructure(callStructure.argumentCount,
- callStructure.getOrderedNamedArguments());
+ @override
+ ir.Primitive handleFinalStaticFieldSet(
+ ast.SendSet node,
+ FieldElement field,
+ ast.Node rhs, _) {
+ // TODO(asgerf): Include class name somehow for static class members?
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(field.memberName),
+ [visit(rhs)]);
}
@override
- ir.Primitive handleConstructorInvoke(
- ast.NewExpression node,
- ConstructorElement constructor,
- DartType type,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
+ ir.Primitive visitFinalSuperFieldSet(
+ ast.SendSet node,
+ FieldElement field,
+ ast.Node rhs, _) {
+ return buildInstanceNoSuchMethod(
+ new Selector.setter(field.memberName),
+ elements.getTypeMask(node),
+ [visit(rhs)]);
+ }
- // TODO(sigmund): move these checks down after visiting arguments
- // (see issue #25355)
- ast.Send send = node.send;
- // If an allocation refers to a type using a deferred import prefix (e.g.
- // `new lib.A()`), we must ensure that the deferred import has already been
- // loaded.
- var prefix = compiler.deferredLoadTask.deferredPrefixElement(
- send, elements);
- if (prefix != null) buildCheckDeferredIsLoaded(prefix, send);
+ @override
+ ir.Primitive handleImmutableLocalSet(
+ ast.SendSet node,
+ LocalElement local,
+ ast.Node rhs, _) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(new Name(local.name, local.library)),
+ [visit(rhs)]);
+ }
- // We also emit deferred import checks when using redirecting factories that
- // refer to deferred prefixes.
- if (constructor.isRedirectingFactory && !constructor.isCyclicRedirection) {
- ConstructorElement current = constructor;
- while (current.isRedirectingFactory) {
- var prefix = current.redirectionDeferredPrefix;
- if (prefix != null) buildCheckDeferredIsLoaded(prefix, send);
- current = current.immediateRedirectionTarget;
- }
- }
+ @override
+ ir.Primitive handleStaticFunctionSet(
+ ast.Send node,
+ MethodElement function,
+ ast.Node rhs,
+ _) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(function.memberName),
+ [visit(rhs)]);
+ }
- List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
- // Use default values from the effective target, not the immediate target.
- ConstructorElement target = constructor.effectiveTarget;
+ @override
+ ir.Primitive handleStaticGetterSet(
+ ast.SendSet node,
+ GetterElement getter,
+ ast.Node rhs,
+ _) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.setter(getter.memberName),
+ [visit(rhs)]);
+ }
- callStructure = normalizeStaticArguments(callStructure, target, arguments);
- TypeMask allocationSiteType;
+ @override
+ ir.Primitive handleStaticSetterGet(
+ ast.Send node,
+ SetterElement setter,
+ _) {
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.getter(setter.memberName),
+ []);
+ }
- if (Elements.isFixedListConstructorCall(constructor, send, compiler) ||
- Elements.isGrowableListConstructorCall(constructor, send, compiler) ||
- Elements.isFilledListConstructorCall(constructor, send, compiler) ||
- Elements.isConstructorOfTypedArraySubclass(constructor, compiler)) {
- allocationSiteType = getAllocationSiteType(send);
- }
- return irBuilder.buildConstructorInvocation(
- target,
- callStructure,
- constructor.computeEffectiveTargetType(type),
- arguments,
- sourceInformationBuilder.buildNew(node),
- allocationSiteType: allocationSiteType);
+ @override
+ ir.Primitive handleStaticSetterInvoke(
+ ast.Send node,
+ SetterElement setter,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure, _) {
+ // Translate as a method call.
+ List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
+ return irBuilder.buildStaticNoSuchMethod(
+ new Selector.call(setter.memberName, callStructure),
+ arguments);
}
@override
- ir.Primitive buildInstanceNoSuchMethod(Selector selector,
- TypeMask mask,
- List<ir.Primitive> arguments) {
- return irBuilder.buildDynamicInvocation(
- irBuilder.buildThis(),
- Selectors.noSuchMethod_,
- mask,
- [irBuilder.buildInvocationMirror(selector, arguments)]);
+ ir.Primitive visitSuperGetterSet(
+ ast.SendSet node,
+ GetterElement getter,
+ ast.Node rhs,
+ _) {
+ return buildInstanceNoSuchMethod(
+ new Selector.setter(getter.memberName),
+ elements.getTypeMask(node),
+ [visit(rhs)]);
}
@override
- ir.Primitive buildRuntimeError(String message) {
- return irBuilder.buildStaticFunctionInvocation(
- backend.helpers.throwRuntimeError,
- new CallStructure.unnamed(1),
- [irBuilder.buildStringConstant(message)]);
+ ir.Primitive visitSuperMethodSet(
+ ast.Send node,
+ MethodElement method,
+ ast.Node rhs,
+ _) {
+ return buildInstanceNoSuchMethod(
+ new Selector.setter(method.memberName),
+ elements.getTypeMask(node),
+ [visit(rhs)]);
}
@override
- ir.Primitive buildAbstractClassInstantiationError(ClassElement element) {
- return irBuilder.buildStaticFunctionInvocation(
- backend.helpers.throwAbstractClassInstantiationError,
- new CallStructure.unnamed(1),
- [irBuilder.buildStringConstant(element.name)]);
+ ir.Primitive visitSuperSetterGet(
+ ast.Send node,
+ SetterElement setter, _) {
+ return buildInstanceNoSuchMethod(
+ new Selector.setter(setter.memberName),
+ elements.getTypeMask(node),
+ []);
}
@override
- ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) {
- SourceInformation src = sourceInformationBuilder.buildGet(node);
- return buildStaticFieldGet(field, src);
+ ir.Primitive visitSuperSetterInvoke(
+ ast.Send node,
+ SetterElement setter,
+ ast.NodeList argumentsNode,
+ CallStructure callStructure, _) {
+ List<ir.Primitive> arguments = <ir.Primitive>[];
+ callStructure =
+ translateDynamicArguments(argumentsNode, callStructure, arguments);
+ return buildInstanceNoSuchMethod(
+ new Selector.call(setter.memberName, callStructure),
+ elements.getTypeMask(node),
+ arguments);
}
- ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src) {
- ConstantValue constant = getConstantForVariable(field);
- if (constant != null && !field.isAssignable) {
- typeMaskSystem.associateConstantValueWithElement(constant, field);
- return irBuilder.buildConstant(constant, sourceInformation: src);
- } else if (backend.constants.lazyStatics.contains(field)) {
- return irBuilder.buildStaticFieldLazyGet(field, src);
- } else {
- return irBuilder.buildStaticFieldGet(field, src);
+ ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) {
+ try {
+ return action();
+ } catch(e) {
+ if (e == ABORT_IRNODE_BUILDER) {
+ return null;
+ }
+ rethrow;
}
}
- /// Build code to handle foreign code, that is, native JavaScript code, or
- /// builtin values and operations of the backend.
- ir.Primitive handleForeignCode(ast.Send node,
- MethodElement function,
- ast.NodeList argumentList,
- CallStructure callStructure) {
+ internalError(ast.Node node, String message) {
+ reporter.internalError(node, message);
+ }
- void validateArgumentCount({int minimum, int exactly}) {
- assert((minimum == null) != (exactly == null));
- int count = 0;
- int maximum;
- if (exactly != null) {
- minimum = exactly;
- maximum = exactly;
- }
- for (ast.Node argument in argumentList) {
- count++;
- if (maximum != null && count > maximum) {
- internalError(argument, 'Additional argument.');
- }
- }
- if (count < minimum) {
- internalError(node, 'Expected at least $minimum arguments.');
- }
- }
+ @override
+ visitNode(ast.Node node) {
+ giveup(node, "Unhandled node");
+ }
- /// Call a helper method from the isolate library. The isolate library uses
- /// its own isolate structure, that encapsulates dart2js's isolate.
- ir.Primitive buildIsolateHelperInvocation(Element element,
- CallStructure callStructure) {
- if (element == null) {
- reporter.internalError(node,
- 'Isolate library and compiler mismatch.');
- }
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure = translateStaticArguments(argumentList, element,
- callStructure, arguments);
- return irBuilder.buildStaticFunctionInvocation(
- element,
- callStructure,
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, node.selector));
- }
+ dynamic giveup(ast.Node node, [String reason]) {
+ bailoutMessage = '($node): $reason';
+ throw ABORT_IRNODE_BUILDER;
+ }
+}
- /// Lookup the value of the enum described by [node].
- getEnumValue(ast.Node node, EnumClassElement enumClass, List values) {
- Element element = elements[node];
- if (element is! FieldElement || element.enclosingClass != enumClass) {
- internalError(node, 'expected a JsBuiltin enum value');
- }
+final String ABORT_IRNODE_BUILDER = "IrNode builder aborted";
- int index = enumClass.enumValues.indexOf(element);
- return values[index];
- }
+/// Determines which local variables should be boxed in a mutable variable
+/// inside a given try block.
+class TryBoxedVariables extends ast.Visitor {
+ final TreeElements elements;
+ TryBoxedVariables(this.elements);
- /// Returns the String the node evaluates to, or throws an error if the
- /// result is not a string constant.
- String expectStringConstant(ast.Node node) {
- ir.Primitive nameValue = visit(node);
- if (nameValue is ir.Constant && nameValue.value.isString) {
- StringConstantValue constantValue = nameValue.value;
- return constantValue.primitiveValue.slowToString();
- } else {
- return internalError(node, 'expected a literal string');
- }
- }
+ FunctionElement currentFunction;
+ bool insideInitializer = false;
+ Set<Local> capturedVariables = new Set<Local>();
- Link<ast.Node> argumentNodes = argumentList.nodes;
- NativeBehavior behavior =
- compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
- switch (function.name) {
- case 'JS':
- validateArgumentCount(minimum: 2);
- // The first two arguments are the type and the foreign code template,
- // which already have been analyzed by the resolver and can be retrieved
- // using [NativeBehavior]. We can ignore these arguments in the backend.
- List<ir.Primitive> arguments =
- argumentNodes.skip(2).mapToList(visit, growable: false);
- if (behavior.codeTemplate.positionalArgumentCount != arguments.length) {
- reporter.reportErrorMessage(
- node, MessageKind.GENERIC,
- {'text':
- 'Mismatch between number of placeholders'
- ' and number of arguments.'});
- return irBuilder.buildNullConstant();
- }
+ /// A map containing variables boxed inside try blocks.
+ ///
+ /// The map is keyed by the [NodeList] of catch clauses for try/catch and
+ /// by the finally block for try/finally. try/catch/finally is treated
+ /// as a try/catch nested in the try block of a try/finally.
+ Map<ast.Node, TryStatementInfo> tryStatements =
+ <ast.Node, TryStatementInfo>{};
- if (HasCapturedPlaceholders.check(behavior.codeTemplate.ast)) {
- reporter.reportErrorMessage(node, MessageKind.JS_PLACEHOLDER_CAPTURE);
- return irBuilder.buildNullConstant();
- }
+ List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[];
+ bool get inTryStatement => tryNestingStack.isNotEmpty;
- return irBuilder.buildForeignCode(behavior.codeTemplate, arguments,
- behavior);
+ String bailoutMessage = null;
- case 'DART_CLOSURE_TO_JS':
- // TODO(ahe): This should probably take care to wrap the closure in
- // another closure that saves the current isolate.
- case 'RAW_DART_FUNCTION_REF':
- validateArgumentCount(exactly: 1);
+ giveup(ast.Node node, [String reason]) {
+ bailoutMessage = '($node): $reason';
+ throw ABORT_IRNODE_BUILDER;
+ }
- ast.Node argument = node.arguments.single;
- FunctionElement closure = elements[argument].implementation;
- if (!Elements.isStaticOrTopLevelFunction(closure)) {
- internalError(argument,
- 'only static or toplevel function supported');
- }
- if (closure.functionSignature.hasOptionalParameters) {
- internalError(argument,
- 'closures with optional parameters not supported');
- }
- return irBuilder.buildForeignCode(
- js.js.expressionTemplateYielding(
- backend.emitter.staticFunctionAccess(closure)),
- <ir.Primitive>[],
- NativeBehavior.PURE,
- dependency: closure);
+ void markAsCaptured(Local local) {
+ capturedVariables.add(local);
+ }
- case 'JS_BUILTIN':
- // The first argument is a description of the type and effect of the
- // builtin, which has already been analyzed in the frontend. The second
- // argument must be a [JsBuiltin] value. All other arguments are
- // values used by the JavaScript template that is associated with the
- // builtin.
- validateArgumentCount(minimum: 2);
+ analyze(ast.Node node) {
+ visit(node);
+ // Variables that are captured by a closure are boxed for their entire
+ // lifetime, so they never need to be boxed on entry to a try block.
+ // They are not filtered out before this because we cannot identify all
+ // of them in the same pass (they may be captured by a closure after the
+ // try statement).
+ for (TryStatementInfo info in tryStatements.values) {
+ info.boxedOnEntry.removeAll(capturedVariables);
+ }
+ }
- ast.Node builtin = argumentNodes.tail.head;
- JsBuiltin value = getEnumValue(builtin, helpers.jsBuiltinEnum,
- JsBuiltin.values);
- js.Template template = backend.emitter.builtinTemplateFor(value);
- List<ir.Primitive> arguments =
- argumentNodes.skip(2).mapToList(visit, growable: false);
- return irBuilder.buildForeignCode(template, arguments, behavior);
+ visit(ast.Node node) => node.accept(this);
- case 'JS_EMBEDDED_GLOBAL':
- validateArgumentCount(exactly: 2);
+ visitNode(ast.Node node) {
+ node.visitChildren(this);
+ }
- String name = expectStringConstant(argumentNodes.tail.head);
- js.Expression access =
- backend.emitter.generateEmbeddedGlobalAccess(name);
- js.Template template = js.js.expressionTemplateYielding(access);
- return irBuilder.buildForeignCode(template, <ir.Primitive>[], behavior);
+ void handleSend(ast.Send node) {
+ Element element = elements[node];
+ if (Elements.isLocal(element) &&
+ !element.isConst &&
+ element.enclosingElement != currentFunction) {
+ LocalElement local = element;
+ markAsCaptured(local);
+ }
+ }
- case 'JS_INTERCEPTOR_CONSTANT':
- validateArgumentCount(exactly: 1);
+ visitSend(ast.Send node) {
+ handleSend(node);
+ node.visitChildren(this);
+ }
- ast.Node argument = argumentNodes.head;
- ir.Primitive argumentValue = visit(argument);
- if (argumentValue is ir.Constant && argumentValue.value.isType) {
- TypeConstantValue constant = argumentValue.value;
- ConstantValue interceptorValue =
- new InterceptorConstantValue(constant.representedType);
- return irBuilder.buildConstant(interceptorValue);
+ visitSendSet(ast.SendSet node) {
+ handleSend(node);
+ Element element = elements[node];
+ if (Elements.isLocal(element)) {
+ LocalElement local = element;
+ if (insideInitializer &&
+ local.isParameter &&
+ local.enclosingElement == currentFunction) {
+ assert(local.enclosingElement.isConstructor);
+ // Initializers in an initializer-list can communicate via parameters.
+ // If a parameter is stored in an initializer list we box it.
+ // TODO(sigurdm): Fix this.
+ // Though these variables do not outlive the activation of the
+ // function, they still need to be boxed. As a simplification, we
+ // treat them as if they are captured by a closure (i.e., they do
+ // outlive the activation of the function).
+ markAsCaptured(local);
+ } else if (inTryStatement) {
+ assert(local.isParameter || local.isVariable);
+ // Search for the position of the try block containing the variable
+ // declaration, or -1 if it is declared outside the outermost try.
+ int i = tryNestingStack.length - 1;
+ while (i >= 0 && !tryNestingStack[i].declared.contains(local)) {
+ --i;
}
- return internalError(argument, 'expected Type as argument');
+ // If there is a next inner try, then the variable should be boxed on
+ // entry to it.
+ if (i + 1 < tryNestingStack.length) {
+ tryNestingStack[i + 1].boxedOnEntry.add(local);
+ }
+ }
+ }
+ node.visitChildren(this);
+ }
- case 'JS_EFFECT':
- return irBuilder.buildNullConstant();
+ visitFunctionExpression(ast.FunctionExpression node) {
+ FunctionElement savedFunction = currentFunction;
+ currentFunction = elements[node];
- case 'JS_GET_NAME':
- validateArgumentCount(exactly: 1);
+ if (currentFunction.asyncMarker != AsyncMarker.SYNC &&
+ currentFunction.asyncMarker != AsyncMarker.SYNC_STAR &&
+ currentFunction.asyncMarker != AsyncMarker.ASYNC) {
+ giveup(node, "cannot handle async* functions");
+ }
- ast.Node argument = argumentNodes.head;
- JsGetName id = getEnumValue(argument, helpers.jsGetNameEnum,
- JsGetName.values);
- js.Name name = backend.namer.getNameForJsGetName(argument, id);
- ConstantValue nameConstant =
- new SyntheticConstantValue(SyntheticConstantKind.NAME,
- js.js.quoteName(name));
+ if (node.initializers != null) {
+ visit(node.initializers);
+ }
+ visit(node.body);
+ currentFunction = savedFunction;
+ }
- return irBuilder.buildConstant(nameConstant);
+ visitTryStatement(ast.TryStatement node) {
+ // Try/catch/finally is treated as two simpler constructs: try/catch and
+ // try/finally. The encoding is:
+ //
+ // try S0 catch (ex, st) S1 finally S2
+ // ==>
+ // try { try S0 catch (ex, st) S1 } finally S2
+ //
+ // The analysis associates variables assigned in S0 with the catch clauses
+ // and variables assigned in S0 and S1 with the finally block.
+ TryStatementInfo enterTryFor(ast.Node node) {
+ TryStatementInfo info = new TryStatementInfo();
+ tryStatements[node] = info;
+ tryNestingStack.add(info);
+ return info;
+ }
+ void leaveTryFor(TryStatementInfo info) {
+ assert(tryNestingStack.last == info);
+ tryNestingStack.removeLast();
+ }
+ bool hasCatch = !node.catchBlocks.isEmpty;
+ bool hasFinally = node.finallyBlock != null;
+ TryStatementInfo catchInfo, finallyInfo;
+ // There is a nesting stack of try blocks, so the outer try/finally block
+ // is added first.
+ if (hasFinally) finallyInfo = enterTryFor(node.finallyBlock);
+ if (hasCatch) catchInfo = enterTryFor(node.catchBlocks);
+ visit(node.tryBlock);
- case 'JS_GET_FLAG':
- validateArgumentCount(exactly: 1);
+ if (hasCatch) {
+ leaveTryFor(catchInfo);
+ visit(node.catchBlocks);
+ }
+ if (hasFinally) {
+ leaveTryFor(finallyInfo);
+ visit(node.finallyBlock);
+ }
+ }
- String name = expectStringConstant(argumentNodes.first);
- bool value = false;
- switch (name) {
- case 'MUST_RETAIN_METADATA':
- value = backend.mustRetainMetadata;
- break;
- case 'USE_CONTENT_SECURITY_POLICY':
- value = compiler.useContentSecurityPolicy;
- break;
- default:
- internalError(node, 'Unknown internal flag "$name".');
- }
- return irBuilder.buildBooleanConstant(value);
+ visitVariableDefinitions(ast.VariableDefinitions node) {
+ if (inTryStatement) {
+ for (ast.Node definition in node.definitions.nodes) {
+ LocalVariableElement local = elements[definition];
+ assert(local != null);
+ // In the closure conversion pass we check for isInitializingFormal,
+ // but I'm not sure it can arise.
+ assert(!local.isInitializingFormal);
+ tryNestingStack.last.declared.add(local);
+ }
+ }
+ node.visitChildren(this);
+ }
+}
- case 'JS_STRING_CONCAT':
- validateArgumentCount(exactly: 2);
- List<ir.Primitive> arguments = argumentNodes.mapToList(visit);
- return irBuilder.buildStringConcatenation(arguments);
+/// The [IrBuilder]s view on the information about the program that has been
+/// computed in resolution and and type interence.
+class GlobalProgramInformation {
+ final Compiler _compiler;
+ JavaScriptBackend get _backend => _compiler.backend;
- case 'JS_CURRENT_ISOLATE_CONTEXT':
- validateArgumentCount(exactly: 0);
+ GlobalProgramInformation(this._compiler);
- if (!compiler.hasIsolateSupport) {
- // If the isolate library is not used, we just generate code
- // to fetch the current isolate.
- continue getStaticState;
- }
- return buildIsolateHelperInvocation(backend.helpers.currentIsolate,
- CallStructure.NO_ARGS);
+ /// Returns [true], if the analysis could not determine that the type
+ /// arguments for the class [cls] are never used in the program.
+ bool requiresRuntimeTypesFor(ClassElement cls) {
+ return cls.typeVariables.isNotEmpty && _backend.classNeedsRti(cls);
+ }
- getStaticState: case 'JS_GET_STATIC_STATE':
- validateArgumentCount(exactly: 0);
+ FunctionElement get stringifyFunction {
+ return _backend.helpers.stringInterpolationHelper;
+ }
- return irBuilder.buildForeignCode(
- js.js.parseForeignJS(backend.namer.staticStateHolder),
- const <ir.Primitive>[],
- NativeBehavior.PURE);
+ FunctionElement get throwTypeErrorHelper => _backend.helpers.throwTypeError;
+ Element get throwNoSuchMethod => _backend.helpers.throwNoSuchMethod;
- case 'JS_SET_STATIC_STATE':
- validateArgumentCount(exactly: 1);
+ ClassElement get nullClass => _compiler.coreClasses.nullClass;
- ir.Primitive value = visit(argumentNodes.single);
- String isolateName = backend.namer.staticStateHolder;
- return irBuilder.buildForeignCode(
- js.js.parseForeignJS("$isolateName = #"),
- <ir.Primitive>[value],
- NativeBehavior.PURE);
+ DartType unaliasType(DartType type) => type.unaliased;
- case 'JS_CALL_IN_ISOLATE':
- validateArgumentCount(exactly: 2);
+ TypeMask getTypeMaskForForeign(NativeBehavior behavior) {
+ if (behavior == null) {
+ return _backend.dynamicType;
+ }
+ return TypeMaskFactory.fromNativeBehavior(behavior, _compiler);
+ }
- if (!compiler.hasIsolateSupport) {
- ir.Primitive closure = visit(argumentNodes.tail.head);
- return irBuilder.buildCallInvocation(closure, CallStructure.NO_ARGS,
- const <ir.Primitive>[]);
- }
- return buildIsolateHelperInvocation(backend.helpers.callInIsolate,
- CallStructure.TWO_ARGS);
+ FieldElement locateSingleField(Selector selector, TypeMask type) {
+ return _compiler.world.locateSingleField(selector, type);
+ }
- default:
- return giveup(node, 'unplemented native construct: ${function.name}');
- }
+ Element get closureConverter {
+ return _backend.helpers.closureConverter;
}
- @override
- ir.Primitive handleStaticFunctionInvoke(ast.Send node,
- MethodElement function,
- ast.NodeList argumentsNode,
- CallStructure callStructure,
- _) {
- if (compiler.backend.isForeign(function)) {
- return handleForeignCode(node, function, argumentsNode, callStructure);
- } else {
- List<ir.Primitive> arguments = <ir.Primitive>[];
- callStructure = translateStaticArguments(argumentsNode, function,
- callStructure, arguments);
- return irBuilder.buildStaticFunctionInvocation(function,
- callStructure,
- arguments,
- sourceInformation:
- sourceInformationBuilder.buildCall(node, node.selector));
- }
+ void addNativeMethod(FunctionElement function) {
+ _backend.emitter.nativeEmitter.nativeMethods.add(function);
}
}
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698