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

Unified Diff: sdk/lib/_internal/compiler/implementation/ssa/builder.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/compiler/implementation/ssa/builder.dart
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
deleted file mode 100644
index abd5e29594869b5486b149f52854b583ddcb859b..0000000000000000000000000000000000000000
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ /dev/null
@@ -1,6578 +0,0 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of ssa;
-
-/// A synthetic local variable only used with the SSA graph.
-///
-/// For instance used for holding return value of function or the exception of a
-/// try-catch statement.
-class SyntheticLocal extends Local {
- final String name;
- final ExecutableElement executableContext;
-
- SyntheticLocal(this.name, this.executableContext);
-}
-
-class SsaBuilderTask extends CompilerTask {
- final CodeEmitterTask emitter;
- final JavaScriptBackend backend;
-
- String get name => 'SSA builder';
-
- SsaBuilderTask(JavaScriptBackend backend)
- : emitter = backend.emitter,
- backend = backend,
- super(backend.compiler);
-
- HGraph build(CodegenWorkItem work) {
- return measure(() {
- Element element = work.element.implementation;
- return compiler.withCurrentElement(element, () {
- HInstruction.idCounter = 0;
- SsaBuilder builder =
- new SsaBuilder(backend, work, emitter.nativeEmitter);
- HGraph graph;
- ElementKind kind = element.kind;
- if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
- graph = compileConstructor(builder, work);
- } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY ||
- kind == ElementKind.FUNCTION ||
- kind == ElementKind.GETTER ||
- kind == ElementKind.SETTER) {
- graph = builder.buildMethod(element);
- } else if (kind == ElementKind.FIELD) {
- if (element.isInstanceMember) {
- assert(compiler.enableTypeAssertions);
- graph = builder.buildCheckedSetter(element);
- } else {
- graph = builder.buildLazyInitializer(element);
- }
- } else {
- compiler.internalError(element, 'Unexpected element kind $kind.');
- }
- assert(graph.isValid());
- if (!identical(kind, ElementKind.FIELD)) {
- FunctionElement function = element;
- FunctionSignature signature = function.functionSignature;
- signature.forEachOptionalParameter((ParameterElement parameter) {
- // This ensures the default value will be computed.
- ConstantValue constant =
- backend.constants.getConstantForVariable(parameter).value;
- CodegenRegistry registry = work.registry;
- registry.registerCompileTimeConstant(constant);
- });
- }
- if (compiler.tracer.isEnabled) {
- String name;
- if (element.isClassMember) {
- String className = element.enclosingClass.name;
- String memberName = element.name;
- name = "$className.$memberName";
- if (element.isGenerativeConstructorBody) {
- name = "$name (body)";
- }
- } else {
- name = "${element.name}";
- }
- compiler.tracer.traceCompilation(
- name, work.compilationContext);
- compiler.tracer.traceGraph('builder', graph);
- }
- return graph;
- });
- });
- }
-
- HGraph compileConstructor(SsaBuilder builder, CodegenWorkItem work) {
- return builder.buildFactory(work.element);
- }
-}
-
-/**
- * Keeps track of locals (including parameters and phis) when building. The
- * 'this' reference is treated as parameter and hence handled by this class,
- * too.
- */
-class LocalsHandler {
- /**
- * The values of locals that can be directly accessed (without redirections
- * to boxes or closure-fields).
- *
- * [directLocals] is iterated, so it is "insertion ordered" to make the
- * iteration order a function only of insertions and not a function of
- * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements
- * don't have source locations for [Elements.compareByPosition].
- */
- Map<Local, HInstruction> directLocals =
- new Map<Local, HInstruction>();
- Map<Local, CapturedVariable> redirectionMapping =
- new Map<Local, CapturedVariable>();
- SsaBuilder builder;
- ClosureClassMap closureData;
- Map<TypeVariableType, TypeVariableLocal> typeVariableLocals =
- new Map<TypeVariableType, TypeVariableLocal>();
- final ExecutableElement executableContext;
-
- /// The class that defines the current type environment or null if no type
- /// variables are in scope.
- ClassElement get contextClass => executableContext.contextClass;
-
- LocalsHandler(this.builder, this.executableContext);
-
- /// Substituted type variables occurring in [type] into the context of
- /// [contextClass].
- DartType substInContext(DartType type) {
- if (contextClass != null) {
- ClassElement typeContext = Types.getClassContext(type);
- if (typeContext != null) {
- type = type.substByContext(
- contextClass.asInstanceOf(typeContext));
- }
- }
- return type;
- }
-
- get typesTask => builder.compiler.typesTask;
-
- /**
- * Creates a new [LocalsHandler] based on [other]. We only need to
- * copy the [directLocals], since the other fields can be shared
- * throughout the AST visit.
- */
- LocalsHandler.from(LocalsHandler other)
- : directLocals = new Map<Local, HInstruction>.from(other.directLocals),
- redirectionMapping = other.redirectionMapping,
- executableContext = other.executableContext,
- builder = other.builder,
- closureData = other.closureData;
-
- /**
- * Redirects accesses from element [from] to element [to]. The [to] element
- * must be a boxed variable or a variable that is stored in a closure-field.
- */
- void redirectElement(Local from, CapturedVariable to) {
- assert(redirectionMapping[from] == null);
- redirectionMapping[from] = to;
- assert(isStoredInClosureField(from) || isBoxed(from));
- }
-
- HInstruction createBox() {
- // TODO(floitsch): Clean up this hack. Should we create a box-object by
- // just creating an empty object literal?
- JavaScriptBackend backend = builder.backend;
- HInstruction box = new HForeign(js.js.parseForeignJS('{}'),
- backend.nonNullType,
- <HInstruction>[]);
- builder.add(box);
- return box;
- }
-
- /**
- * If the scope (function or loop) [node] has captured variables then this
- * method creates a box and sets up the redirections.
- */
- void enterScope(ast.Node node, Element element) {
- // See if any variable in the top-scope of the function is captured. If yes
- // we need to create a box-object.
- ClosureScope scopeData = closureData.capturingScopes[node];
- if (scopeData == null) return;
- HInstruction box;
- // The scope has captured variables.
- if (element != null && element.isGenerativeConstructorBody) {
- // The box is passed as a parameter to a generative
- // constructor body.
- JavaScriptBackend backend = builder.backend;
- box = builder.addParameter(scopeData.boxElement, backend.nonNullType);
- } else {
- box = createBox();
- }
- // Add the box to the known locals.
- directLocals[scopeData.boxElement] = box;
- // Make sure that accesses to the boxed locals go into the box. We also
- // need to make sure that parameters are copied into the box if necessary.
- scopeData.forEachCapturedVariable(
- (LocalVariableElement from, BoxFieldElement to) {
- // The [from] can only be a parameter for function-scopes and not
- // loop scopes.
- if (from.isParameter && !element.isGenerativeConstructorBody) {
- // Now that the redirection is set up, the update to the local will
- // write the parameter value into the box.
- // Store the captured parameter in the box. Get the current value
- // before we put the redirection in place.
- // We don't need to update the local for a generative
- // constructor body, because it receives a box that already
- // contains the updates as the last parameter.
- HInstruction instruction = readLocal(from);
- redirectElement(from, to);
- updateLocal(from, instruction);
- } else {
- redirectElement(from, to);
- }
- });
- }
-
- /**
- * Replaces the current box with a new box and copies over the given list
- * of elements from the old box into the new box.
- */
- void updateCaptureBox(BoxLocal boxElement,
- List<LocalVariableElement> toBeCopiedElements) {
- // Create a new box and copy over the values from the old box into the
- // new one.
- HInstruction oldBox = readLocal(boxElement);
- HInstruction newBox = createBox();
- for (LocalVariableElement boxedVariable in toBeCopiedElements) {
- // [readLocal] uses the [boxElement] to find its box. By replacing it
- // behind its back we can still get to the old values.
- updateLocal(boxElement, oldBox);
- HInstruction oldValue = readLocal(boxedVariable);
- updateLocal(boxElement, newBox);
- updateLocal(boxedVariable, oldValue);
- }
- updateLocal(boxElement, newBox);
- }
-
- /**
- * Documentation wanted -- johnniwinther
- *
- * Invariant: [function] must be an implementation element.
- */
- void startFunction(Element element, ast.Node node) {
- assert(invariant(element, element.isImplementation));
- Compiler compiler = builder.compiler;
- closureData = compiler.closureToClassMapper.computeClosureToClassMapping(
- element, node, builder.elements);
-
- if (element is FunctionElement) {
- FunctionElement functionElement = element;
- FunctionSignature params = functionElement.functionSignature;
- ClosureScope scopeData = closureData.capturingScopes[node];
- params.orderedForEachParameter((ParameterElement parameterElement) {
- if (element.isGenerativeConstructorBody) {
- if (scopeData != null &&
- scopeData.isCapturedVariable(parameterElement)) {
- // The parameter will be a field in the box passed as the
- // last parameter. So no need to have it.
- return;
- }
- }
- HInstruction parameter = builder.addParameter(
- parameterElement,
- TypeMaskFactory.inferredTypeForElement(parameterElement, compiler));
- builder.parameters[parameterElement] = parameter;
- directLocals[parameterElement] = parameter;
- });
- }
-
- enterScope(node, element);
-
- // If the freeVariableMapping is not empty, then this function was a
- // nested closure that captures variables. Redirect the captured
- // variables to fields in the closure.
- closureData.forEachFreeVariable((Local from, CapturedVariable to) {
- redirectElement(from, to);
- });
- JavaScriptBackend backend = compiler.backend;
- if (closureData.isClosure) {
- // Inside closure redirect references to itself to [:this:].
- HThis thisInstruction = new HThis(closureData.thisLocal,
- backend.nonNullType);
- builder.graph.thisInstruction = thisInstruction;
- builder.graph.entry.addAtEntry(thisInstruction);
- updateLocal(closureData.closureElement, thisInstruction);
- } else if (element.isInstanceMember) {
- // Once closures have been mapped to classes their instance members might
- // not have any thisElement if the closure was created inside a static
- // context.
- HThis thisInstruction = new HThis(
- closureData.thisLocal, builder.getTypeOfThis());
- builder.graph.thisInstruction = thisInstruction;
- builder.graph.entry.addAtEntry(thisInstruction);
- directLocals[closureData.thisLocal] = thisInstruction;
- }
-
- // If this method is an intercepted method, add the extra
- // parameter to it, that is the actual receiver for intercepted
- // classes, or the same as [:this:] for non-intercepted classes.
- ClassElement cls = element.enclosingClass;
-
- // When the class extends a native class, the instance is pre-constructed
- // and passed to the generative constructor factory function as a parameter.
- // Instead of allocating and initializing the object, the constructor
- // 'upgrades' the native subclass object by initializing the Dart fields.
- bool isNativeUpgradeFactory = element.isGenerativeConstructor
- && Elements.isNativeOrExtendsNative(cls);
- if (backend.isInterceptedMethod(element)) {
- bool isInterceptorClass = backend.isInterceptorClass(cls.declaration);
- String name = isInterceptorClass ? 'receiver' : '_';
- SyntheticLocal parameter = new SyntheticLocal(name, executableContext);
- HParameterValue value =
- new HParameterValue(parameter, builder.getTypeOfThis());
- builder.graph.explicitReceiverParameter = value;
- builder.graph.entry.addAfter(
- directLocals[closureData.thisLocal], value);
- if (isInterceptorClass) {
- // Only use the extra parameter in intercepted classes.
- directLocals[closureData.thisLocal] = value;
- }
- } else if (isNativeUpgradeFactory) {
- SyntheticLocal parameter =
- new SyntheticLocal('receiver', executableContext);
- // Unlike `this`, receiver is nullable since direct calls to generative
- // constructor call the constructor with `null`.
- ClassWorld classWorld = compiler.world;
- HParameterValue value =
- new HParameterValue(parameter, new TypeMask.exact(cls, classWorld));
- builder.graph.explicitReceiverParameter = value;
- builder.graph.entry.addAtEntry(value);
- }
- }
-
- /**
- * Returns true if the local can be accessed directly. Boxed variables or
- * captured variables that are stored in the closure-field return [:false:].
- */
- bool isAccessedDirectly(Local local) {
- assert(local != null);
- return !redirectionMapping.containsKey(local)
- && !closureData.usedVariablesInTry.contains(local);
- }
-
- bool isStoredInClosureField(Local local) {
- assert(local != null);
- if (isAccessedDirectly(local)) return false;
- CapturedVariable redirectTarget = redirectionMapping[local];
- if (redirectTarget == null) return false;
- return redirectTarget is ClosureFieldElement;
- }
-
- bool isBoxed(Local local) {
- if (isAccessedDirectly(local)) return false;
- if (isStoredInClosureField(local)) return false;
- return redirectionMapping.containsKey(local);
- }
-
- bool isUsedInTry(Local local) {
- return closureData.usedVariablesInTry.contains(local);
- }
-
- /**
- * Returns an [HInstruction] for the given element. If the element is
- * boxed or stored in a closure then the method generates code to retrieve
- * the value.
- */
- HInstruction readLocal(Local local) {
- if (isAccessedDirectly(local)) {
- if (directLocals[local] == null) {
- if (local is TypeVariableElement) {
- builder.compiler.internalError(builder.compiler.currentElement,
- "Runtime type information not available for $local.");
- } else {
- builder.compiler.internalError(local,
- "Cannot find value $local.");
- }
- }
- return directLocals[local];
- } else if (isStoredInClosureField(local)) {
- ClosureFieldElement redirect = redirectionMapping[local];
- HInstruction receiver = readLocal(closureData.closureElement);
- TypeMask type = local is BoxLocal
- ? builder.backend.nonNullType
- : builder.getTypeOfCapturedVariable(redirect);
- HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
- builder.add(fieldGet);
- return fieldGet;
- } else if (isBoxed(local)) {
- BoxFieldElement redirect = redirectionMapping[local];
- // In the function that declares the captured variable the box is
- // accessed as direct local. Inside the nested closure the box is
- // accessed through a closure-field.
- // Calling [readLocal] makes sure we generate the correct code to get
- // the box.
- HInstruction box = readLocal(redirect.box);
- HInstruction lookup = new HFieldGet(
- redirect, box, builder.getTypeOfCapturedVariable(redirect));
- builder.add(lookup);
- return lookup;
- } else {
- assert(isUsedInTry(local));
- HLocalValue localValue = getLocal(local);
- HInstruction instruction = new HLocalGet(
- local, localValue, builder.backend.dynamicType);
- builder.add(instruction);
- return instruction;
- }
- }
-
- HInstruction readThis() {
- HInstruction res = readLocal(closureData.thisLocal);
- if (res.instructionType == null) {
- res.instructionType = builder.getTypeOfThis();
- }
- return res;
- }
-
- HLocalValue getLocal(Local local) {
- // If the element is a parameter, we already have a
- // HParameterValue for it. We cannot create another one because
- // it could then have another name than the real parameter. And
- // the other one would not know it is just a copy of the real
- // parameter.
- if (local is ParameterElement) return builder.parameters[local];
-
- return builder.activationVariables.putIfAbsent(local, () {
- JavaScriptBackend backend = builder.backend;
- HLocalValue localValue = new HLocalValue(local, backend.nonNullType);
- builder.graph.entry.addAtExit(localValue);
- return localValue;
- });
- }
-
- Local getTypeVariableAsLocal(TypeVariableType type) {
- return typeVariableLocals.putIfAbsent(type, () {
- return new TypeVariableLocal(type, executableContext);
- });
- }
-
- /**
- * Sets the [element] to [value]. If the element is boxed or stored in a
- * closure then the method generates code to set the value.
- */
- void updateLocal(Local local, HInstruction value) {
- assert(!isStoredInClosureField(local));
- if (isAccessedDirectly(local)) {
- directLocals[local] = value;
- } else if (isBoxed(local)) {
- BoxFieldElement redirect = redirectionMapping[local];
- // The box itself could be captured, or be local. A local variable that
- // is captured will be boxed, but the box itself will be a local.
- // Inside the closure the box is stored in a closure-field and cannot
- // be accessed directly.
- HInstruction box = readLocal(redirect.box);
- builder.add(new HFieldSet(redirect, box, value));
- } else {
- assert(isUsedInTry(local));
- HLocalValue localValue = getLocal(local);
- builder.add(new HLocalSet(local, localValue, value));
- }
- }
-
- /**
- * This function, startLoop, must be called before visiting any children of
- * the loop. In particular it needs to be called before executing the
- * initializers.
- *
- * The [LocalsHandler] will make the boxes and updates at the right moment.
- * The builder just needs to call [enterLoopBody] and [enterLoopUpdates]
- * (for [ast.For] loops) at the correct places. For phi-handling
- * [beginLoopHeader] and [endLoop] must also be called.
- *
- * The correct place for the box depends on the given loop. In most cases
- * the box will be created when entering the loop-body: while, do-while, and
- * for-in (assuming the call to [:next:] is inside the body) can always be
- * constructed this way.
- *
- * Things are slightly more complicated for [ast.For] loops. If no declared
- * loop variable is boxed then the loop-body approach works here too. If a
- * loop-variable is boxed we need to introduce a new box for the
- * loop-variable before we enter the initializer so that the initializer
- * writes the values into the box. In any case we need to create the box
- * before the condition since the condition could box the variable.
- * Since the first box is created outside the actual loop we have a second
- * location where a box is created: just before the updates. This is
- * necessary since updates are considered to be part of the next iteration
- * (and can again capture variables).
- *
- * For example the following Dart code prints 1 3 -- 3 4.
- *
- * var fs = [];
- * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
- * i++;
- * }
- * print("--");
- * for (var i = 0; i < 2; i++) fs[i]();
- *
- * We solve this by emitting the following code (only for [ast.For] loops):
- * <Create box> <== move the first box creation outside the loop.
- * <initializer>;
- * loop-entry:
- * if (!<condition>) goto loop-exit;
- * <body>
- * <update box> // create a new box and copy the captured loop-variables.
- * <updates>
- * goto loop-entry;
- * loop-exit:
- */
- void startLoop(ast.Node node) {
- ClosureScope scopeData = closureData.capturingScopes[node];
- if (scopeData == null) return;
- if (scopeData.hasBoxedLoopVariables()) {
- // If there are boxed loop variables then we set up the box and
- // redirections already now. This way the initializer can write its
- // values into the box.
- // For other loops the box will be created when entering the body.
- enterScope(node, null);
- }
- }
-
- /**
- * Create phis at the loop entry for local variables (ready for the values
- * from the back edge). Populate the phis with the current values.
- */
- void beginLoopHeader(HBasicBlock loopEntry) {
- // Create a copy because we modify the map while iterating over it.
- Map<Local, HInstruction> savedDirectLocals =
- new Map<Local, HInstruction>.from(directLocals);
-
- JavaScriptBackend backend = builder.backend;
- // Create phis for all elements in the definitions environment.
- savedDirectLocals.forEach((Local local,
- HInstruction instruction) {
- if (isAccessedDirectly(local)) {
- // We know 'this' cannot be modified.
- if (local != closureData.thisLocal) {
- HPhi phi = new HPhi.singleInput(
- local, instruction, backend.dynamicType);
- loopEntry.addPhi(phi);
- directLocals[local] = phi;
- } else {
- directLocals[local] = instruction;
- }
- }
- });
- }
-
- void enterLoopBody(ast.Node node) {
- ClosureScope scopeData = closureData.capturingScopes[node];
- if (scopeData == null) return;
- // If there are no declared boxed loop variables then we did not create the
- // box before the initializer and we have to create the box now.
- if (!scopeData.hasBoxedLoopVariables()) {
- enterScope(node, null);
- }
- }
-
- void enterLoopUpdates(ast.Node node) {
- // If there are declared boxed loop variables then the updates might have
- // access to the box and we must switch to a new box before executing the
- // updates.
- // In all other cases a new box will be created when entering the body of
- // the next iteration.
- ClosureScope scopeData = closureData.capturingScopes[node];
- if (scopeData == null) return;
- if (scopeData.hasBoxedLoopVariables()) {
- updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
- }
- }
-
- /**
- * Goes through the phis created in beginLoopHeader entry and adds the
- * input from the back edge (from the current value of directLocals) to them.
- */
- void endLoop(HBasicBlock loopEntry) {
- // If the loop has an aborting body, we don't update the loop
- // phis.
- if (loopEntry.predecessors.length == 1) return;
- loopEntry.forEachPhi((HPhi phi) {
- Local element = phi.sourceElement;
- HInstruction postLoopDefinition = directLocals[element];
- phi.addInput(postLoopDefinition);
- });
- }
-
- /**
- * Merge [otherLocals] into this locals handler, creating phi-nodes when
- * there is a conflict.
- * If a phi node is necessary, it will use this handler's instruction as the
- * first input, and the otherLocals instruction as the second.
- */
- void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
- // If an element is in one map but not the other we can safely
- // ignore it. It means that a variable was declared in the
- // block. Since variable declarations are scoped the declared
- // variable cannot be alive outside the block. Note: this is only
- // true for nodes where we do joins.
- Map<Local, HInstruction> joinedLocals =
- new Map<Local, HInstruction>();
- JavaScriptBackend backend = builder.backend;
- otherLocals.directLocals.forEach((Local local,
- HInstruction instruction) {
- // We know 'this' cannot be modified.
- if (local == closureData.thisLocal) {
- assert(directLocals[local] == instruction);
- joinedLocals[local] = instruction;
- } else {
- HInstruction mine = directLocals[local];
- if (mine == null) return;
- if (identical(instruction, mine)) {
- joinedLocals[local] = instruction;
- } else {
- HInstruction phi = new HPhi.manyInputs(
- local, <HInstruction>[mine, instruction], backend.dynamicType);
- joinBlock.addPhi(phi);
- joinedLocals[local] = phi;
- }
- }
- });
- directLocals = joinedLocals;
- }
-
- /**
- * When control flow merges, this method can be used to merge several
- * localsHandlers into a new one using phis. The new localsHandler is
- * returned. Unless it is also in the list, the current localsHandler is not
- * used for its values, only for its declared variables. This is a way to
- * exclude local values from the result when they are no longer in scope.
- */
- LocalsHandler mergeMultiple(List<LocalsHandler> localsHandlers,
- HBasicBlock joinBlock) {
- assert(localsHandlers.length > 0);
- if (localsHandlers.length == 1) return localsHandlers[0];
- Map<Local, HInstruction> joinedLocals =
- new Map<Local, HInstruction>();
- HInstruction thisValue = null;
- JavaScriptBackend backend = builder.backend;
- directLocals.forEach((Local local, HInstruction instruction) {
- if (local != closureData.thisLocal) {
- HPhi phi = new HPhi.noInputs(local, backend.dynamicType);
- joinedLocals[local] = phi;
- joinBlock.addPhi(phi);
- } else {
- // We know that "this" never changes, if it's there.
- // Save it for later. While merging, there is no phi for "this",
- // so we don't have to special case it in the merge loop.
- thisValue = instruction;
- }
- });
- for (LocalsHandler handler in localsHandlers) {
- handler.directLocals.forEach((Local local,
- HInstruction instruction) {
- HPhi phi = joinedLocals[local];
- if (phi != null) {
- phi.addInput(instruction);
- }
- });
- }
- if (thisValue != null) {
- // If there was a "this" for the scope, add it to the new locals.
- joinedLocals[closureData.thisLocal] = thisValue;
- }
-
- // Remove locals that are not in all handlers.
- directLocals = new Map<Local, HInstruction>();
- joinedLocals.forEach((Local local,
- HInstruction instruction) {
- if (local != closureData.thisLocal
- && instruction.inputs.length != localsHandlers.length) {
- joinBlock.removePhi(instruction);
- } else {
- directLocals[local] = instruction;
- }
- });
- return this;
- }
-}
-
-
-// Represents a single break/continue instruction.
-class JumpHandlerEntry {
- final HJump jumpInstruction;
- final LocalsHandler locals;
- bool isBreak() => jumpInstruction is HBreak;
- bool isContinue() => jumpInstruction is HContinue;
- JumpHandlerEntry(this.jumpInstruction, this.locals);
-}
-
-
-abstract class JumpHandler {
- factory JumpHandler(SsaBuilder builder, JumpTarget target) {
- return new TargetJumpHandler(builder, target);
- }
- void generateBreak([LabelDefinition label]);
- void generateContinue([LabelDefinition label]);
- void forEachBreak(void action(HBreak instruction, LocalsHandler locals));
- void forEachContinue(void action(HContinue instruction,
- LocalsHandler locals));
- bool hasAnyContinue();
- bool hasAnyBreak();
- void close();
- final JumpTarget target;
- List<LabelDefinition> labels();
-}
-
-// Insert break handler used to avoid null checks when a target isn't
-// used as the target of a break, and therefore doesn't need a break
-// handler associated with it.
-class NullJumpHandler implements JumpHandler {
- final Compiler compiler;
-
- NullJumpHandler(this.compiler);
-
- void generateBreak([LabelDefinition label]) {
- compiler.internalError(CURRENT_ELEMENT_SPANNABLE,
- 'NullJumpHandler.generateBreak should not be called.');
- }
-
- void generateContinue([LabelDefinition label]) {
- compiler.internalError(CURRENT_ELEMENT_SPANNABLE,
- 'NullJumpHandler.generateContinue should not be called.');
- }
-
- void forEachBreak(Function ignored) { }
- void forEachContinue(Function ignored) { }
- void close() { }
- bool hasAnyContinue() => false;
- bool hasAnyBreak() => false;
-
- List<LabelDefinition> labels() => const <LabelDefinition>[];
- JumpTarget get target => null;
-}
-
-// Records breaks until a target block is available.
-// Breaks are always forward jumps.
-// Continues in loops are implemented as breaks of the body.
-// Continues in switches is currently not handled.
-class TargetJumpHandler implements JumpHandler {
- final SsaBuilder builder;
- final JumpTarget target;
- final List<JumpHandlerEntry> jumps;
-
- TargetJumpHandler(SsaBuilder builder, this.target)
- : this.builder = builder,
- jumps = <JumpHandlerEntry>[] {
- assert(builder.jumpTargets[target] == null);
- builder.jumpTargets[target] = this;
- }
-
- void generateBreak([LabelDefinition label]) {
- HInstruction breakInstruction;
- if (label == null) {
- breakInstruction = new HBreak(target);
- } else {
- breakInstruction = new HBreak.toLabel(label);
- }
- LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
- builder.close(breakInstruction);
- jumps.add(new JumpHandlerEntry(breakInstruction, locals));
- }
-
- void generateContinue([LabelDefinition label]) {
- HInstruction continueInstruction;
- if (label == null) {
- continueInstruction = new HContinue(target);
- } else {
- continueInstruction = new HContinue.toLabel(label);
- // Switch case continue statements must be handled by the
- // [SwitchCaseJumpHandler].
- assert(label.target.statement is! ast.SwitchCase);
- }
- LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
- builder.close(continueInstruction);
- jumps.add(new JumpHandlerEntry(continueInstruction, locals));
- }
-
- void forEachBreak(Function action) {
- for (JumpHandlerEntry entry in jumps) {
- if (entry.isBreak()) action(entry.jumpInstruction, entry.locals);
- }
- }
-
- void forEachContinue(Function action) {
- for (JumpHandlerEntry entry in jumps) {
- if (entry.isContinue()) action(entry.jumpInstruction, entry.locals);
- }
- }
-
- bool hasAnyContinue() {
- for (JumpHandlerEntry entry in jumps) {
- if (entry.isContinue()) return true;
- }
- return false;
- }
-
- bool hasAnyBreak() {
- for (JumpHandlerEntry entry in jumps) {
- if (entry.isBreak()) return true;
- }
- return false;
- }
-
- void close() {
- // The mapping from TargetElement to JumpHandler is no longer needed.
- builder.jumpTargets.remove(target);
- }
-
- List<LabelDefinition> labels() {
- List<LabelDefinition> result = null;
- for (LabelDefinition element in target.labels) {
- if (result == null) result = <LabelDefinition>[];
- result.add(element);
- }
- return (result == null) ? const <LabelDefinition>[] : result;
- }
-}
-
-/// Special [JumpHandler] implementation used to handle continue statements
-/// targeting switch cases.
-class SwitchCaseJumpHandler extends TargetJumpHandler {
- /// Map from switch case targets to indices used to encode the flow of the
- /// switch case loop.
- final Map<JumpTarget, int> targetIndexMap = new Map<JumpTarget, int>();
-
- SwitchCaseJumpHandler(SsaBuilder builder,
- JumpTarget target,
- ast.SwitchStatement node)
- : super(builder, target) {
- // The switch case indices must match those computed in
- // [SsaFromAstMixin.buildSwitchCaseConstants].
- // Switch indices are 1-based so we can bypass the synthetic loop when no
- // cases match simply by branching on the index (which defaults to null).
- int switchIndex = 1;
- for (ast.SwitchCase switchCase in node.cases) {
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- ast.Node label = labelOrCase.asLabel();
- if (label != null) {
- LabelDefinition labelElement =
- builder.elements.getLabelDefinition(label);
- if (labelElement != null && labelElement.isContinueTarget) {
- JumpTarget continueTarget = labelElement.target;
- targetIndexMap[continueTarget] = switchIndex;
- assert(builder.jumpTargets[continueTarget] == null);
- builder.jumpTargets[continueTarget] = this;
- }
- }
- }
- switchIndex++;
- }
- }
-
- void generateBreak([LabelDefinition label]) {
- if (label == null) {
- // Creates a special break instruction for the synthetic loop generated
- // for a switch statement with continue statements. See
- // [SsaFromAstMixin.buildComplexSwitchStatement] for detail.
-
- HInstruction breakInstruction =
- new HBreak(target, breakSwitchContinueLoop: true);
- LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
- builder.close(breakInstruction);
- jumps.add(new JumpHandlerEntry(breakInstruction, locals));
- } else {
- super.generateBreak(label);
- }
- }
-
- bool isContinueToSwitchCase(LabelDefinition label) {
- return label != null && targetIndexMap.containsKey(label.target);
- }
-
- void generateContinue([LabelDefinition label]) {
- if (isContinueToSwitchCase(label)) {
- // Creates the special instructions 'label = i; continue l;' used in
- // switch statements with continue statements. See
- // [SsaFromAstMixin.buildComplexSwitchStatement] for detail.
-
- assert(label != null);
- HInstruction value = builder.graph.addConstantInt(
- targetIndexMap[label.target],
- builder.compiler);
- builder.localsHandler.updateLocal(target, value);
-
- assert(label.target.labels.contains(label));
- HInstruction continueInstruction = new HContinue(target);
- LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
- builder.close(continueInstruction);
- jumps.add(new JumpHandlerEntry(continueInstruction, locals));
- } else {
- super.generateContinue(label);
- }
- }
-
- void close() {
- // The mapping from TargetElement to JumpHandler is no longer needed.
- for (JumpTarget target in targetIndexMap.keys) {
- builder.jumpTargets.remove(target);
- }
- super.close();
- }
-}
-
-/**
- * This class builds SSA nodes for functions represented in AST.
- */
-class SsaBuilder extends ResolvedVisitor {
- final Compiler compiler;
- final JavaScriptBackend backend;
- final ConstantSystem constantSystem;
- final CodegenWorkItem work;
- final RuntimeTypes rti;
-
- /* This field is used by the native handler. */
- final NativeEmitter nativeEmitter;
-
- final HGraph graph = new HGraph();
-
- /**
- * The current block to add instructions to. Might be null, if we are
- * visiting dead code, but see [isReachable].
- */
- HBasicBlock _current;
-
- HBasicBlock get current => _current;
-
- void set current(c) {
- isReachable = c != null;
- _current = c;
- }
-
- /**
- * The most recently opened block. Has the same value as [current] while
- * the block is open, but unlike [current], it isn't cleared when the
- * current block is closed.
- */
- HBasicBlock lastOpenedBlock;
-
- /**
- * Indicates whether the current block is dead (because it has a throw or a
- * return further up). If this is false, then [current] may be null. If the
- * block is dead then it may also be aborted, but for simplicity we only
- * abort on statement boundaries, not in the middle of expressions. See
- * isAborted.
- */
- bool isReachable = true;
-
- /**
- * True if we are visiting the expression of a throw statement; we assume this
- * is a slow path.
- */
- bool inExpressionOfThrow = false;
-
- /**
- * The loop nesting is consulted when inlining a function invocation in
- * [tryInlineMethod]. The inlining heuristics take this information into
- * account.
- */
- int loopNesting = 0;
-
- /**
- * This stack contains declaration elements of the functions being built
- * or inlined by this builder.
- */
- final List<Element> sourceElementStack = <Element>[];
-
- LocalsHandler localsHandler;
-
- HInstruction rethrowableException;
-
- HParameterValue lastAddedParameter;
-
- Map<ParameterElement, HInstruction> parameters =
- <ParameterElement, HInstruction>{};
-
- Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{};
-
- /**
- * Variables stored in the current activation. These variables are
- * being updated in try/catch blocks, and should be
- * accessed indirectly through [HLocalGet] and [HLocalSet].
- */
- Map<Local, HLocalValue> activationVariables =
- <Local, HLocalValue>{};
-
- // We build the Ssa graph by simulating a stack machine.
- List<HInstruction> stack = <HInstruction>[];
-
- SsaBuilder(JavaScriptBackend backend,
- CodegenWorkItem work,
- this.nativeEmitter)
- : this.compiler = backend.compiler,
- this.backend = backend,
- this.constantSystem = backend.constantSystem,
- this.work = work,
- this.rti = backend.rti,
- super(work.resolutionTree) {
- localsHandler = new LocalsHandler(this, work.element);
- sourceElementStack.add(work.element);
- }
-
- CodegenRegistry get registry => work.registry;
-
- /// Returns the current source element.
- ///
- /// The returned element is a declaration element.
- // TODO(johnniwinther): Check that all usages of sourceElement agree on
- // implementation/declaration distinction.
- Element get sourceElement => sourceElementStack.last;
-
- HBasicBlock addNewBlock() {
- HBasicBlock block = graph.addNewBlock();
- // If adding a new block during building of an expression, it is due to
- // conditional expressions or short-circuit logical operators.
- return block;
- }
-
- void open(HBasicBlock block) {
- block.open();
- current = block;
- lastOpenedBlock = block;
- }
-
- HBasicBlock close(HControlFlow end) {
- HBasicBlock result = current;
- current.close(end);
- current = null;
- return result;
- }
-
- HBasicBlock closeAndGotoExit(HControlFlow end) {
- HBasicBlock result = current;
- current.close(end);
- current = null;
- result.addSuccessor(graph.exit);
- return result;
- }
-
- void goto(HBasicBlock from, HBasicBlock to) {
- from.close(new HGoto());
- from.addSuccessor(to);
- }
-
- bool isAborted() {
- return current == null;
- }
-
- /**
- * Creates a new block, transitions to it from any current block, and
- * opens the new block.
- */
- HBasicBlock openNewBlock() {
- HBasicBlock newBlock = addNewBlock();
- if (!isAborted()) goto(current, newBlock);
- open(newBlock);
- return newBlock;
- }
-
- void add(HInstruction instruction) {
- current.add(instruction);
- }
-
- void addWithPosition(HInstruction instruction, ast.Node node) {
- add(attachPosition(instruction, node));
- }
-
- SourceFile currentSourceFile() {
- return sourceElement.implementation.compilationUnit.script.file;
- }
-
- void checkValidSourceFileLocation(
- SourceFileLocation location, SourceFile sourceFile, int offset) {
- if (!location.isValid()) {
- throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message(
- {'offset': offset,
- 'fileName': sourceFile.filename,
- 'length': sourceFile.length});
- }
- }
-
- /**
- * Returns a complete argument list for a call of [function].
- */
- List<HInstruction> completeSendArgumentsList(
- FunctionElement function,
- Selector selector,
- List<HInstruction> providedArguments,
- ast.Node currentNode) {
- assert(invariant(function, function.isImplementation));
- assert(providedArguments != null);
-
- bool isInstanceMember = function.isInstanceMember;
- // For static calls, [providedArguments] is complete, default arguments
- // have been included if necessary, see [addStaticSendArgumentsToList].
- if (!isInstanceMember
- || currentNode == null // In erroneous code, currentNode can be null.
- || providedArgumentsKnownToBeComplete(currentNode)
- || function.isGenerativeConstructorBody
- || selector.isGetter) {
- // For these cases, the provided argument list is known to be complete.
- return providedArguments;
- } else {
- return completeDynamicSendArgumentsList(
- selector, function, providedArguments);
- }
- }
-
- /**
- * Returns a complete argument list for a dynamic call of [function]. The
- * initial argument list [providedArguments], created by
- * [addDynamicSendArgumentsToList], does not include values for default
- * arguments used in the call. The reason is that the target function (which
- * defines the defaults) is not known.
- *
- * However, inlining can only be performed when the target function can be
- * resolved statically. The defaults can therefore be included at this point.
- *
- * The [providedArguments] list contains first all positional arguments, then
- * the provided named arguments (the named arguments that are defined in the
- * [selector]) in a specific order (see [addDynamicSendArgumentsToList]).
- */
- List<HInstruction> completeDynamicSendArgumentsList(
- Selector selector,
- FunctionElement function,
- List<HInstruction> providedArguments) {
- assert(selector.applies(function, compiler.world));
- FunctionSignature signature = function.functionSignature;
- List<HInstruction> compiledArguments = new List<HInstruction>(
- signature.parameterCount + 1); // Plus one for receiver.
-
- compiledArguments[0] = providedArguments[0]; // Receiver.
- int index = 1;
- for (; index <= signature.requiredParameterCount; index++) {
- compiledArguments[index] = providedArguments[index];
- }
- if (!signature.optionalParametersAreNamed) {
- signature.forEachOptionalParameter((element) {
- if (index < providedArguments.length) {
- compiledArguments[index] = providedArguments[index];
- } else {
- compiledArguments[index] =
- handleConstantForOptionalParameter(element);
- }
- index++;
- });
- } else {
- /* Example:
- * void foo(a, {b, d, c})
- * foo(0, d = 1, b = 2)
- *
- * providedArguments = [0, 2, 1]
- * selectorArgumentNames = [b, d]
- * signature.orderedOptionalParameters = [b, c, d]
- *
- * For each parameter name in the signature, if the argument name matches
- * we use the next provided argument, otherwise we get the default.
- */
- List<String> selectorArgumentNames = selector.getOrderedNamedArguments();
- int namedArgumentIndex = 0;
- int firstProvidedNamedArgument = index;
- signature.orderedOptionalParameters.forEach((element) {
- if (namedArgumentIndex < selectorArgumentNames.length &&
- element.name == selectorArgumentNames[namedArgumentIndex]) {
- // The named argument was provided in the function invocation.
- compiledArguments[index] = providedArguments[
- firstProvidedNamedArgument + namedArgumentIndex++];
- } else {
- compiledArguments[index] =
- handleConstantForOptionalParameter(element);
- }
- index++;
- });
- }
- return compiledArguments;
- }
-
- /**
- * Try to inline [element] within the currect context of the builder. The
- * insertion point is the state of the builder.
- */
- bool tryInlineMethod(Element element,
- Selector selector,
- List<HInstruction> providedArguments,
- ast.Node currentNode) {
- // TODO(johnniwinther): Register this on the [registry]. Currently the
- // [CodegenRegistry] calls the enqueuer, but [element] should _not_ be
- // enqueued.
- backend.registerStaticUse(element, compiler.enqueuer.codegen);
-
- // Ensure that [element] is an implementation element.
- element = element.implementation;
- FunctionElement function = element;
- bool insideLoop = loopNesting > 0 || graph.calledInLoop;
-
- // Bail out early if the inlining decision is in the cache and we can't
- // inline (no need to check the hard constraints).
- bool cachedCanBeInlined =
- backend.inlineCache.canInline(function, insideLoop: insideLoop);
- if (cachedCanBeInlined == false) return false;
-
- bool meetsHardConstraints() {
- // Don't inline from one output unit to another. If something is deferred
- // it is to save space in the loading code.
- if (!compiler.deferredLoadTask
- .inSameOutputUnit(element,compiler.currentElement)) {
- return false;
- }
- if (compiler.disableInlining) return false;
-
- assert(selector != null
- || Elements.isStaticOrTopLevel(element)
- || element.isGenerativeConstructorBody);
- if (selector != null && !selector.applies(function, compiler.world)) {
- return false;
- }
-
- // Don't inline operator== methods if the parameter can be null.
- if (element.name == '==') {
- if (element.enclosingClass != compiler.objectClass
- && providedArguments[1].canBeNull()) {
- return false;
- }
- }
-
- // Generative constructors of native classes should not be called directly
- // and have an extra argument that causes problems with inlining.
- if (element.isGenerativeConstructor
- && Elements.isNativeOrExtendsNative(element.enclosingClass)) {
- return false;
- }
-
- // A generative constructor body is not seen by global analysis,
- // so we should not query for its type.
- if (!element.isGenerativeConstructorBody) {
- // Don't inline if the return type was inferred to be non-null empty.
- // This means that the function always throws an exception.
- TypeMask returnType =
- compiler.typesTask.getGuaranteedReturnTypeOfElement(element);
- if (returnType != null
- && returnType.isEmpty
- && !returnType.isNullable) {
- isReachable = false;
- return false;
- }
- }
-
- return true;
- }
-
- bool heuristicSayGoodToGo() {
- // Don't inline recursivly
- if (inliningStack.any((entry) => entry.function == function)) {
- return false;
- }
-
- if (inExpressionOfThrow) return false;
-
- if (element.isSynthesized) return true;
-
- if (cachedCanBeInlined == true) return cachedCanBeInlined;
-
- if (backend.functionsToAlwaysInline.contains(function)) {
- // Inline this function regardless of it's size.
- assert(InlineWeeder.canBeInlined(function.node, -1, false,
- allowLoops: true));
- return true;
- }
-
- int numParameters = function.functionSignature.parameterCount;
- int maxInliningNodes;
- bool useMaxInliningNodes = true;
- if (insideLoop) {
- maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP +
- InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters;
- } else {
- maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP +
- InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters;
- }
-
- // If a method is called only once, and all the methods in the
- // inlining stack are called only once as well, we know we will
- // save on output size by inlining this method.
- TypesInferrer inferrer = compiler.typesTask.typesInferrer;
- if (inferrer.isCalledOnce(element) && allInlinedFunctionsCalledOnce) {
- useMaxInliningNodes = false;
- }
- bool canInline;
- ast.FunctionExpression functionNode = function.node;
- canInline = InlineWeeder.canBeInlined(
- functionNode, maxInliningNodes, useMaxInliningNodes);
- if (canInline) {
- backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop);
- } else {
- backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop);
- }
- return canInline;
- }
-
- void doInlining() {
- // Add an explicit null check on the receiver before doing the
- // inlining. We use [element] to get the same name in the
- // NoSuchMethodError message as if we had called it.
- if (element.isInstanceMember
- && !element.isGenerativeConstructorBody
- && (selector.mask == null || selector.mask.isNullable)) {
- addWithPosition(
- new HFieldGet(null, providedArguments[0], backend.dynamicType,
- isAssignable: false),
- currentNode);
- }
- List<HInstruction> compiledArguments = completeSendArgumentsList(
- function, selector, providedArguments, currentNode);
- enterInlinedMethod(function, currentNode, compiledArguments);
- inlinedFrom(function, () {
- if (!isReachable) {
- emitReturn(graph.addConstantNull(compiler), null);
- } else {
- doInline(function);
- }
- });
- leaveInlinedMethod();
- }
-
- if (meetsHardConstraints() && heuristicSayGoodToGo()) {
- doInlining();
- registry.registerInlining(
- element,
- compiler.currentElement);
- return true;
- }
-
- return false;
- }
-
- bool get allInlinedFunctionsCalledOnce {
- return inliningStack.isEmpty || inliningStack.last.allFunctionsCalledOnce;
- }
-
- inlinedFrom(Element element, f()) {
- assert(element is FunctionElement || element is VariableElement);
- return compiler.withCurrentElement(element, () {
- // The [sourceElementStack] contains declaration elements.
- sourceElementStack.add(element.declaration);
- var result = f();
- sourceElementStack.removeLast();
- return result;
- });
- }
-
- HInstruction handleConstantForOptionalParameter(Element parameter) {
- ConstantExpression constant =
- backend.constants.getConstantForVariable(parameter);
- assert(invariant(parameter, constant != null,
- message: 'No constant computed for $parameter'));
- return graph.addConstant(constant.value, compiler);
- }
-
- Element get currentNonClosureClass {
- ClassElement cls = sourceElement.enclosingClass;
- if (cls != null && cls.isClosure) {
- var closureClass = cls;
- return closureClass.methodElement.enclosingClass;
- } else {
- return cls;
- }
- }
-
- /**
- * Returns whether this builder is building code for [element].
- */
- bool isBuildingFor(Element element) {
- return work.element == element;
- }
-
- /// A stack of [DartType]s the have been seen during inlining of factory
- /// constructors. These types are preserved in [HInvokeStatic]s and
- /// [HForeignNews] inside the inline code and registered during code
- /// generation for these nodes.
- // TODO(karlklose): consider removing this and keeping the (substituted)
- // types of the type variables in an environment (like the [LocalsHandler]).
- final List<DartType> currentInlinedInstantiations = <DartType>[];
-
- final List<AstInliningState> inliningStack = <AstInliningState>[];
-
- Local returnLocal;
- DartType returnType;
-
- bool inTryStatement = false;
-
- ConstantValue getConstantForNode(ast.Node node) {
- ConstantExpression constant =
- backend.constants.getConstantForNode(node, elements);
- assert(invariant(node, constant != null,
- message: 'No constant computed for $node'));
- return constant.value;
- }
-
- HInstruction addConstant(ast.Node node) {
- return graph.addConstant(getConstantForNode(node), compiler);
- }
-
- bool isLazilyInitialized(VariableElement element) {
- ConstantExpression initialValue =
- backend.constants.getConstantForVariable(element);
- return initialValue == null;
- }
-
- TypeMask cachedTypeOfThis;
-
- TypeMask getTypeOfThis() {
- TypeMask result = cachedTypeOfThis;
- if (result == null) {
- ThisLocal local = localsHandler.closureData.thisLocal;
- ClassElement cls = local.enclosingClass;
- ClassWorld classWorld = compiler.world;
- if (classWorld.isUsedAsMixin(cls)) {
- // If the enclosing class is used as a mixin, [:this:] can be
- // of the class that mixins the enclosing class. These two
- // classes do not have a subclass relationship, so, for
- // simplicity, we mark the type as an interface type.
- result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world);
- } else {
- result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world);
- }
- cachedTypeOfThis = result;
- }
- return result;
- }
-
- Map<Element, TypeMask> cachedTypesOfCapturedVariables =
- new Map<Element, TypeMask>();
-
- TypeMask getTypeOfCapturedVariable(Element element) {
- assert(element.isField);
- return cachedTypesOfCapturedVariables.putIfAbsent(element, () {
- return TypeMaskFactory.inferredTypeForElement(element, compiler);
- });
- }
-
- /**
- * Documentation wanted -- johnniwinther
- *
- * Invariant: [functionElement] must be an implementation element.
- */
- HGraph buildMethod(FunctionElement functionElement) {
- assert(invariant(functionElement, functionElement.isImplementation));
- graph.calledInLoop = compiler.world.isCalledInLoop(functionElement);
- ast.FunctionExpression function = functionElement.node;
- assert(function != null);
- assert(!function.modifiers.isExternal);
- assert(elements.getFunctionDefinition(function) != null);
- openFunction(functionElement, function);
- String name = functionElement.name;
- // If [functionElement] is `operator==` we explicitely add a null check at
- // the beginning of the method. This is to avoid having call sites do the
- // null check.
- if (name == '==') {
- if (!backend.operatorEqHandlesNullArgument(functionElement)) {
- handleIf(
- function,
- () {
- HParameterValue parameter = parameters.values.first;
- push(new HIdentity(
- parameter, graph.addConstantNull(compiler), null,
- backend.boolType));
- },
- () {
- closeAndGotoExit(new HReturn(
- graph.addConstantBool(false, compiler)));
- },
- null);
- }
- }
- function.body.accept(this);
- return closeFunction();
- }
-
- HGraph buildCheckedSetter(VariableElement field) {
- openFunction(field, field.node);
- HInstruction thisInstruction = localsHandler.readThis();
- // Use dynamic type because the type computed by the inferrer is
- // narrowed to the type annotation.
- HInstruction parameter = new HParameterValue(field, backend.dynamicType);
- // Add the parameter as the last instruction of the entry block.
- // If the method is intercepted, we want the actual receiver
- // to be the first parameter.
- graph.entry.addBefore(graph.entry.last, parameter);
- HInstruction value =
- potentiallyCheckType(parameter, field.type);
- add(new HFieldSet(field, thisInstruction, value));
- return closeFunction();
- }
-
- HGraph buildLazyInitializer(VariableElement variable) {
- ast.Node node = variable.node;
- openFunction(variable, node);
- assert(variable.initializer != null);
- visit(variable.initializer);
- HInstruction value = pop();
- value = potentiallyCheckType(value, variable.type);
- closeAndGotoExit(new HReturn(value));
- return closeFunction();
- }
-
- /**
- * 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) {
- assert(constructor.isGenerativeConstructor);
- assert(invariant(constructor, 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) {
- // TODO(kasperl): Find a way of stopping the iteration
- // through the backend members.
- bodyElement = backendMember;
- }
- }
- });
- if (bodyElement == null) {
- bodyElement = new ConstructorBodyElementX(constructor);
- classElement.addBackendMember(bodyElement);
-
- 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;
- }
-
- HParameterValue addParameter(Entity parameter, TypeMask type) {
- assert(inliningStack.isEmpty);
- HParameterValue result = new HParameterValue(parameter, type);
- if (lastAddedParameter == null) {
- graph.entry.addBefore(graph.entry.first, result);
- } else {
- graph.entry.addAfter(lastAddedParameter, result);
- }
- lastAddedParameter = result;
- return result;
- }
-
- /**
- * This method sets up the local state of the builder for inlining [function].
- * The arguments of the function are inserted into the [localsHandler].
- *
- * When inlining a function, [:return:] statements are not emitted as
- * [HReturn] instructions. Instead, the value of a synthetic element is
- * updated in the [localsHandler]. This function creates such an element and
- * stores it in the [returnLocal] field.
- */
- void setupStateForInlining(FunctionElement function,
- List<HInstruction> compiledArguments) {
- localsHandler = new LocalsHandler(this, function);
- localsHandler.closureData =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- function, function.node, elements);
- returnLocal = new SyntheticLocal("result", function);
- localsHandler.updateLocal(returnLocal,
- graph.addConstantNull(compiler));
-
- inTryStatement = false; // TODO(lry): why? Document.
-
- int argumentIndex = 0;
- if (function.isInstanceMember) {
- localsHandler.updateLocal(localsHandler.closureData.thisLocal,
- compiledArguments[argumentIndex++]);
- }
-
- FunctionSignature signature = function.functionSignature;
- signature.orderedForEachParameter((ParameterElement parameter) {
- HInstruction argument = compiledArguments[argumentIndex++];
- localsHandler.updateLocal(parameter, argument);
- });
-
- ClassElement enclosing = function.enclosingClass;
- if ((function.isConstructor || function.isGenerativeConstructorBody)
- && backend.classNeedsRti(enclosing)) {
- enclosing.typeVariables.forEach((TypeVariableType typeVariable) {
- HInstruction argument = compiledArguments[argumentIndex++];
- localsHandler.updateLocal(
- localsHandler.getTypeVariableAsLocal(typeVariable), argument);
- });
- }
- assert(argumentIndex == compiledArguments.length);
-
- elements = function.resolvedAst.elements;
- assert(elements != null);
- returnType = signature.type.returnType;
- stack = <HInstruction>[];
-
- insertTraceCall(function);
- }
-
- void restoreState(AstInliningState state) {
- localsHandler = state.oldLocalsHandler;
- returnLocal = state.oldReturnLocal;
- inTryStatement = state.inTryStatement;
- elements = state.oldElements;
- returnType = state.oldReturnType;
- assert(stack.isEmpty);
- stack = state.oldStack;
- }
-
- /**
- * Run this builder on the body of the [function] to be inlined.
- */
- void visitInlinedFunction(FunctionElement function) {
- potentiallyCheckInlinedParameterTypes(function);
- if (function.isGenerativeConstructor) {
- buildFactory(function);
- } else {
- ast.FunctionExpression functionNode = function.node;
- functionNode.body.accept(this);
- }
- }
-
-
- addInlinedInstantiation(DartType type) {
- if (type != null) {
- currentInlinedInstantiations.add(type);
- }
- }
-
- removeInlinedInstantiation(DartType type) {
- if (type != null) {
- currentInlinedInstantiations.removeLast();
- }
- }
-
- bool providedArgumentsKnownToBeComplete(ast.Node currentNode) {
- /* When inlining the iterator methods generated for a [:for-in:] loop, the
- * [currentNode] is the [ForIn] tree. The compiler-generated iterator
- * invocations are known to have fully specified argument lists, no default
- * arguments are used. See invocations of [pushInvokeDynamic] in
- * [visitForIn].
- */
- return currentNode.asForIn() != null;
- }
-
- /**
- * In checked mode, generate type tests for the parameters of the inlined
- * function.
- */
- void potentiallyCheckInlinedParameterTypes(FunctionElement function) {
- if (!compiler.enableTypeAssertions) return;
-
- FunctionSignature signature = function.functionSignature;
- signature.orderedForEachParameter((ParameterElement parameter) {
- HInstruction argument = localsHandler.readLocal(parameter);
- potentiallyCheckType(argument, parameter.type);
- });
- }
-
- /**
- * Documentation wanted -- johnniwinther
- *
- * Invariant: [constructors] must contain only implementation elements.
- */
- void inlineSuperOrRedirect(FunctionElement callee,
- List<HInstruction> compiledArguments,
- List<FunctionElement> constructors,
- Map<Element, HInstruction> fieldValues,
- FunctionElement caller) {
- callee = callee.implementation;
- compiler.withCurrentElement(callee, () {
- constructors.add(callee);
- ClassElement enclosingClass = callee.enclosingClass;
- if (backend.classNeedsRti(enclosingClass)) {
- // If [enclosingClass] needs RTI, we have to give a value to its
- // type parameters.
- ClassElement currentClass = caller.enclosingClass;
- // For a super constructor call, the type is the supertype of
- // [currentClass]. For a redirecting constructor, the type is
- // the current type. [InterfaceType.asInstanceOf] takes care
- // of both.
- InterfaceType type = currentClass.thisType.asInstanceOf(enclosingClass);
- type = localsHandler.substInContext(type);
- List<DartType> arguments = type.typeArguments;
- List<DartType> typeVariables = enclosingClass.typeVariables;
- if (!type.isRaw) {
- assert(arguments.length == typeVariables.length);
- Iterator<DartType> variables = typeVariables.iterator;
- type.typeArguments.forEach((DartType argument) {
- variables.moveNext();
- TypeVariableType typeVariable = variables.current;
- localsHandler.updateLocal(
- localsHandler.getTypeVariableAsLocal(typeVariable),
- analyzeTypeArgument(argument));
- });
- } else {
- // If the supertype is a raw type, we need to set to null the
- // type variables.
- for (TypeVariableType variable in typeVariables) {
- localsHandler.updateLocal(
- localsHandler.getTypeVariableAsLocal(variable),
- graph.addConstantNull(compiler));
- }
- }
- }
-
- // For redirecting constructors, the fields have already been
- // initialized by the caller.
- if (callee.enclosingClass != caller.enclosingClass) {
- inlinedFrom(callee, () {
- buildFieldInitializers(callee.enclosingElement.implementation,
- fieldValues);
- });
- }
-
- int index = 0;
- FunctionSignature params = callee.functionSignature;
- params.orderedForEachParameter((ParameterElement parameter) {
- HInstruction argument = compiledArguments[index++];
- // Because we are inlining the initializer, we must update
- // what was given as parameter. This will be used in case
- // there is a parameter check expression in the initializer.
- parameters[parameter] = argument;
- localsHandler.updateLocal(parameter, argument);
- // Don't forget to update the field, if the parameter is of the
- // form [:this.x:].
- if (parameter.isInitializingFormal) {
- InitializingFormalElement fieldParameterElement = parameter;
- fieldValues[fieldParameterElement.fieldElement] = argument;
- }
- });
-
- // Build the initializers in the context of the new constructor.
- TreeElements oldElements = elements;
- ResolvedAst resolvedAst = callee.resolvedAst;
- elements = resolvedAst.elements;
- ClosureClassMap oldClosureData = localsHandler.closureData;
- ast.Node node = resolvedAst.node;
- ClosureClassMap newClosureData =
- compiler.closureToClassMapper.computeClosureToClassMapping(
- callee, node, elements);
- localsHandler.closureData = newClosureData;
- localsHandler.enterScope(node, callee);
- buildInitializers(callee, constructors, fieldValues);
- localsHandler.closureData = oldClosureData;
- elements = oldElements;
- });
- }
-
- /**
- * Run through the initializers and inline all field initializers. Recursively
- * inlines super initializers.
- *
- * The constructors of the inlined initializers is added to [constructors]
- * with sub constructors having a lower index than super constructors.
- *
- * Invariant: The [constructor] and elements in [constructors] must all be
- * implementation elements.
- */
- void buildInitializers(ConstructorElement constructor,
- List<FunctionElement> constructors,
- Map<Element, HInstruction> fieldValues) {
- assert(invariant(constructor, constructor.isImplementation));
- if (constructor.isSynthesized) {
- List<HInstruction> arguments = <HInstruction>[];
- HInstruction compileArgument(ParameterElement parameter) {
- return localsHandler.readLocal(parameter);
- }
-
- Element target = constructor.definingConstructor.implementation;
- bool match = Selector.addForwardingElementArgumentsToList(
- constructor,
- arguments,
- target,
- compileArgument,
- handleConstantForOptionalParameter,
- compiler.world);
- if (!match) {
- // If this fails, the selector we constructed for the call to a
- // forwarding constructor in a mixin application did not match the
- // constructor (which, for example, may happen when the libraries are
- // not compatible for private names, see issue 20394).
- compiler.internalError(constructor,
- 'forwarding constructor call does not match');
- }
- inlineSuperOrRedirect(
- target,
- arguments,
- constructors,
- fieldValues,
- constructor);
- return;
- }
- ast.FunctionExpression functionNode = constructor.node;
-
- bool foundSuperOrRedirect = false;
- if (functionNode.initializers != null) {
- Link<ast.Node> initializers = functionNode.initializers.nodes;
- for (Link<ast.Node> link = initializers; !link.isEmpty; link = link.tail) {
- assert(link.head is ast.Send);
- if (link.head is !ast.SendSet) {
- // A super initializer or constructor redirection.
- foundSuperOrRedirect = true;
- ast.Send call = link.head;
- assert(ast.Initializers.isSuperConstructorCall(call) ||
- ast.Initializers.isConstructorRedirect(call));
- FunctionElement target = elements[call].implementation;
- Selector selector = elements.getSelector(call);
- Link<ast.Node> arguments = call.arguments;
- List<HInstruction> compiledArguments = new List<HInstruction>();
- inlinedFrom(constructor, () {
- addStaticSendArgumentsToList(selector,
- arguments,
- target,
- compiledArguments);
- });
- inlineSuperOrRedirect(target,
- compiledArguments,
- constructors,
- fieldValues,
- constructor);
- } else {
- // A field initializer.
- ast.SendSet init = link.head;
- Link<ast.Node> arguments = init.arguments;
- assert(!arguments.isEmpty && arguments.tail.isEmpty);
- inlinedFrom(constructor, () {
- visit(arguments.head);
- });
- fieldValues[elements[init]] = pop();
- }
- }
- }
-
- if (!foundSuperOrRedirect) {
- // No super initializer found. Try to find the default constructor if
- // the class is not Object.
- ClassElement enclosingClass = constructor.enclosingClass;
- ClassElement superClass = enclosingClass.superclass;
- if (!enclosingClass.isObject) {
- assert(superClass != null);
- assert(superClass.resolutionState == STATE_DONE);
- Selector selector =
- new Selector.callDefaultConstructor(enclosingClass.library);
- // TODO(johnniwinther): Should we find injected constructors as well?
- FunctionElement target = superClass.lookupConstructor(selector);
- if (target == null) {
- compiler.internalError(superClass,
- "No default constructor available.");
- }
- List<HInstruction> arguments = <HInstruction>[];
- selector.addArgumentsToList(const Link<ast.Node>(),
- arguments,
- target.implementation,
- null,
- handleConstantForOptionalParameter,
- compiler.world);
- inlineSuperOrRedirect(target,
- arguments,
- constructors,
- fieldValues,
- constructor);
- }
- }
- }
-
- /**
- * Run through the fields of [cls] and add their potential
- * initializers.
- *
- * Invariant: [classElement] must be an implementation element.
- */
- void buildFieldInitializers(ClassElement classElement,
- Map<Element, HInstruction> fieldValues) {
- assert(invariant(classElement, classElement.isImplementation));
- classElement.forEachInstanceField(
- (ClassElement enclosingClass, VariableElement member) {
- compiler.withCurrentElement(member, () {
- TreeElements definitions = member.treeElements;
- ast.Node node = member.node;
- ast.Expression initializer = member.initializer;
- if (initializer == null) {
- // Unassigned fields of native classes are not initialized to
- // prevent overwriting pre-initialized native properties.
- if (!Elements.isNativeOrExtendsNative(classElement)) {
- fieldValues[member] = graph.addConstantNull(compiler);
- }
- } else {
- ast.Node right = initializer;
- TreeElements savedElements = elements;
- elements = definitions;
- // In case the field initializer uses closures, run the
- // closure to class mapper.
- compiler.closureToClassMapper.computeClosureToClassMapping(
- member, node, elements);
- inlinedFrom(member, () => right.accept(this));
- elements = savedElements;
- fieldValues[member] = pop();
- }
- });
- });
- }
-
- /**
- * Build the factory function corresponding to the constructor
- * [functionElement]:
- * - Initialize fields with the values of the field initializers of the
- * current constructor and super constructors or constructors redirected
- * to, starting from the current constructor.
- * - Call the constructor bodies, starting from the constructor(s) in the
- * super class(es).
- */
- HGraph buildFactory(FunctionElement functionElement) {
- functionElement = functionElement.implementation;
- ClassElement classElement =
- functionElement.enclosingClass.implementation;
- bool isNativeUpgradeFactory =
- Elements.isNativeOrExtendsNative(classElement);
- ast.FunctionExpression function = functionElement.node;
- // Note that constructors (like any other static function) do not need
- // to deal with optional arguments. It is the callers job to provide all
- // arguments as if they were positional.
-
- if (inliningStack.isEmpty) {
- // The initializer list could contain closures.
- openFunction(functionElement, function);
- }
-
- Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>();
-
- // Compile the possible initialization code for local fields and
- // super fields.
- buildFieldInitializers(classElement, fieldValues);
-
- // Compile field-parameters such as [:this.x:].
- FunctionSignature params = functionElement.functionSignature;
- params.orderedForEachParameter((ParameterElement parameter) {
- if (parameter.isInitializingFormal) {
- // If the [element] is a field-parameter then
- // initialize the field element with its value.
- InitializingFormalElement fieldParameter = parameter;
- HInstruction parameterValue =
- localsHandler.readLocal(fieldParameter);
- fieldValues[fieldParameter.fieldElement] = parameterValue;
- }
- });
-
- // Analyze the constructor and all referenced constructors and collect
- // initializers and constructor bodies.
- List<FunctionElement> constructors = <FunctionElement>[functionElement];
- buildInitializers(functionElement, constructors, fieldValues);
-
- // Call the JavaScript constructor with the fields as argument.
- List<HInstruction> constructorArguments = <HInstruction>[];
- List<Element> fields = <Element>[];
-
- classElement.forEachInstanceField(
- (ClassElement enclosingClass, VariableElement member) {
- HInstruction value = fieldValues[member];
- if (value == null) {
- // Uninitialized native fields are pre-initialized by the native
- // implementation.
- assert(isNativeUpgradeFactory);
- } else {
- fields.add(member);
- DartType type = localsHandler.substInContext(member.type);
- constructorArguments.add(potentiallyCheckType(value, type));
- }
- },
- includeSuperAndInjectedMembers: true);
-
- InterfaceType type = classElement.thisType;
- TypeMask ssaType =
- new TypeMask.nonNullExact(classElement.declaration, compiler.world);
- List<DartType> instantiatedTypes;
- addInlinedInstantiation(type);
- if (!currentInlinedInstantiations.isEmpty) {
- instantiatedTypes = new List<DartType>.from(currentInlinedInstantiations);
- }
-
- HInstruction newObject;
- if (!isNativeUpgradeFactory) {
- newObject = new HForeignNew(classElement,
- ssaType,
- constructorArguments,
- instantiatedTypes);
- add(newObject);
- } else {
- // Bulk assign to the initialized fields.
- newObject = graph.explicitReceiverParameter;
- // Null guard ensures an error if we are being called from an explicit
- // 'new' of the constructor instead of via an upgrade. It is optimized out
- // if there are field initializers.
- add(new HFieldGet(
- null, newObject, backend.dynamicType, isAssignable: false));
- for (int i = 0; i < fields.length; i++) {
- add(new HFieldSet(fields[i], newObject, constructorArguments[i]));
- }
- }
- removeInlinedInstantiation(type);
- // Create the runtime type information, if needed.
- if (backend.classNeedsRti(classElement)) {
- // Read the values of the type arguments and create a list to set on the
- // newly create object. We can identify the case where the new list
- // would be of the form:
- // [getTypeArgumentByIndex(this, 0), .., getTypeArgumentByIndex(this, k)]
- // and k is the number of type arguments of this. If this is the case,
- // we can simply copy the list from this.
-
- // These locals are modified by [isIndexedTypeArgumentGet].
- HThis source; // The source of the type arguments.
- bool allIndexed = true;
- int expectedIndex = 0;
- ClassElement contextClass; // The class of `this`.
- int remainingTypeVariables; // The number of 'remaining type variables'
- // of `this`.
-
- /// Helper to identify instructions that read a type variable without
- /// substitution (that is, directly use the index). These instructions
- /// are of the form:
- /// HInvokeStatic(getTypeArgumentByIndex, this, index)
- ///
- /// Return `true` if [instruction] is of that form and the index is the
- /// next index in the sequence (held in [expectedIndex]).
- bool isIndexedTypeArgumentGet(HInstruction instruction) {
- if (instruction is! HInvokeStatic) return false;
- HInvokeStatic invoke = instruction;
- if (invoke.element != backend.getGetTypeArgumentByIndex()) {
- return false;
- }
- HConstant index = invoke.inputs[1];
- HInstruction newSource = invoke.inputs[0];
- if (newSource is! HThis) {
- return false;
- }
- if (source == null) {
- // This is the first match. Extract the context class for the type
- // variables and get the list of type variables to keep track of how
- // many arguments we need to process.
- source = newSource;
- contextClass = source.sourceElement.enclosingClass;
- remainingTypeVariables = contextClass.typeVariables.length;
- } else {
- assert(source == newSource);
- }
- // If there are no more type variables, then there are more type
- // arguments for the new object than the source has, and it can't be
- // a copy. Otherwise remove one argument.
- if (remainingTypeVariables == 0) return false;
- remainingTypeVariables--;
- // Check that the index is the one we expect.
- IntConstantValue constant = index.constant;
- return constant.primitiveValue == expectedIndex++;
- }
-
- List<HInstruction> typeArguments = <HInstruction>[];
- classElement.typeVariables.forEach((TypeVariableType typeVariable) {
- HInstruction argument = localsHandler.readLocal(
- localsHandler.getTypeVariableAsLocal(typeVariable));
- if (allIndexed && !isIndexedTypeArgumentGet(argument)) {
- allIndexed = false;
- }
- typeArguments.add(argument);
- });
-
- if (source != null && allIndexed && remainingTypeVariables == 0) {
- copyRuntimeTypeInfo(source, newObject);
- } else {
- newObject =
- callSetRuntimeTypeInfo(classElement, typeArguments, newObject);
- }
- }
-
- // Generate calls to the constructor bodies.
- HInstruction interceptor = null;
- for (int index = constructors.length - 1; index >= 0; index--) {
- FunctionElement constructor = constructors[index];
- assert(invariant(functionElement, constructor.isImplementation));
- ConstructorBodyElement body = getConstructorBody(constructor);
- if (body == null) continue;
-
- List bodyCallInputs = <HInstruction>[];
- if (isNativeUpgradeFactory) {
- if (interceptor == null) {
- ConstantValue constant =
- new InterceptorConstantValue(classElement.thisType);
- interceptor = graph.addConstant(constant, compiler);
- }
- bodyCallInputs.add(interceptor);
- }
- bodyCallInputs.add(newObject);
- ResolvedAst resolvedAst = constructor.resolvedAst;
- TreeElements elements = resolvedAst.elements;
- ast.Node node = resolvedAst.node;
- ClosureClassMap parameterClosureData =
- compiler.closureToClassMapper.getMappingForNestedFunction(node);
-
- FunctionSignature functionSignature = body.functionSignature;
- // Provide the parameters to the generative constructor body.
- functionSignature.orderedForEachParameter((ParameterElement parameter) {
- // If [parameter] is boxed, it will be a field in the box passed as the
- // last parameter. So no need to directly pass it.
- if (!localsHandler.isBoxed(parameter)) {
- bodyCallInputs.add(localsHandler.readLocal(parameter));
- }
- });
-
- ClassElement currentClass = constructor.enclosingClass;
- if (backend.classNeedsRti(currentClass)) {
- // If [currentClass] needs RTI, we add the type variables as
- // parameters of the generative constructor body.
- currentClass.typeVariables.forEach((TypeVariableType argument) {
- // TODO(johnniwinther): Substitute [argument] with
- // `localsHandler.substInContext(argument)`.
- bodyCallInputs.add(localsHandler.readLocal(
- localsHandler.getTypeVariableAsLocal(argument)));
- });
- }
-
- // If there are locals that escape (ie mutated in closures), we
- // pass the box to the constructor.
- ClosureScope scopeData = parameterClosureData.capturingScopes[node];
- if (scopeData != null) {
- bodyCallInputs.add(localsHandler.readLocal(scopeData.boxElement));
- }
-
- if (!isNativeUpgradeFactory && // TODO(13836): Fix inlining.
- tryInlineMethod(body, null, bodyCallInputs, function)) {
- pop();
- } else {
- HInvokeConstructorBody invoke = new HInvokeConstructorBody(
- body.declaration, bodyCallInputs, backend.nonNullType);
- invoke.sideEffects =
- compiler.world.getSideEffectsOfElement(constructor);
- add(invoke);
- }
- }
- if (inliningStack.isEmpty) {
- closeAndGotoExit(new HReturn(newObject));
- return closeFunction();
- } else {
- localsHandler.updateLocal(returnLocal, newObject);
- return null;
- }
- }
-
- /**
- * Documentation wanted -- johnniwinther
- *
- * Invariant: [functionElement] must be the implementation element.
- */
- void openFunction(Element element, ast.Node node) {
- assert(invariant(element, element.isImplementation));
- HBasicBlock block = graph.addNewBlock();
- open(graph.entry);
-
- localsHandler.startFunction(element, node);
- close(new HGoto()).addSuccessor(block);
-
- open(block);
-
- // Add the type parameters of the class as parameters of this method. This
- // must be done before adding the normal parameters, because their types
- // may contain references to type variables.
- var enclosing = element.enclosingElement;
- if ((element.isConstructor || element.isGenerativeConstructorBody)
- && backend.classNeedsRti(enclosing)) {
- enclosing.typeVariables.forEach((TypeVariableType typeVariable) {
- HParameterValue param = addParameter(
- typeVariable.element, backend.nonNullType);
- localsHandler.directLocals[
- localsHandler.getTypeVariableAsLocal(typeVariable)] = param;
- });
- }
-
- if (element is FunctionElement) {
- FunctionElement functionElement = element;
- FunctionSignature signature = functionElement.functionSignature;
-
- // Put the type checks in the first successor of the entry,
- // because that is where the type guards will also be inserted.
- // This way we ensure that a type guard will dominate the type
- // check.
- ClosureScope scopeData =
- localsHandler.closureData.capturingScopes[node];
- signature.orderedForEachParameter((ParameterElement parameterElement) {
- if (element.isGenerativeConstructorBody) {
- if (scopeData != null &&
- scopeData.isCapturedVariable(parameterElement)) {
- // The parameter will be a field in the box passed as the
- // last parameter. So no need to have it.
- return;
- }
- }
- HInstruction newParameter =
- localsHandler.directLocals[parameterElement];
- if (!element.isConstructor ||
- !(element as ConstructorElement).isRedirectingFactory) {
- // Redirection factories must not check their argument types.
- // Example:
- //
- // class A {
- // A(String foo) = A.b;
- // A(int foo) { print(foo); }
- // }
- // main() {
- // new A(499); // valid even in checked mode.
- // new A("foo"); // invalid in checked mode.
- //
- // Only the final target is allowed to check for the argument types.
- newParameter =
- potentiallyCheckType(newParameter, parameterElement.type);
- }
- localsHandler.directLocals[parameterElement] = newParameter;
- });
-
- returnType = signature.type.returnType;
- } else {
- // Otherwise it is a lazy initializer which does not have parameters.
- assert(element is VariableElement);
- }
-
- insertTraceCall(element);
- }
-
- insertTraceCall(Element element) {
- if (JavaScriptBackend.TRACE_CALLS) {
- if (element == backend.traceHelper) return;
- n(e) => e == null ? '' : e.name;
- String name = "${n(element.library)}:${n(element.enclosingClass)}."
- "${n(element)}";
- HConstant nameConstant = addConstantString(name);
- add(new HInvokeStatic(backend.traceHelper,
- <HInstruction>[nameConstant],
- backend.dynamicType));
- }
- }
-
- /// Check that [type] is valid in the context of `localsHandler.contextClass`.
- /// This should only be called in assertions.
- bool assertTypeInContext(DartType type, [Spannable spannable]) {
- return invariant(spannable == null ? CURRENT_ELEMENT_SPANNABLE : spannable,
- () {
- ClassElement contextClass = Types.getClassContext(type);
- return contextClass == null ||
- contextClass == localsHandler.contextClass;
- },
- message: "Type '$type' is not valid context of "
- "${localsHandler.contextClass}.");
- }
-
- /// Build a [HTypeConversion] for convertion [original] to type [type].
- ///
- /// Invariant: [type] must be valid in the context.
- /// See [LocalsHandler.substInContext].
- HInstruction buildTypeConversion(HInstruction original,
- DartType type,
- int kind) {
- if (type == null) return original;
- type = type.unalias(compiler);
- assert(assertTypeInContext(type, original));
- if (type.isInterfaceType && !type.treatAsRaw) {
- TypeMask subtype = new TypeMask.subtype(type.element, compiler.world);
- HInstruction representations = buildTypeArgumentRepresentations(type);
- add(representations);
- return new HTypeConversion.withTypeRepresentation(type, kind, subtype,
- original, representations);
- } else if (type.isTypeVariable) {
- TypeMask subtype = original.instructionType;
- HInstruction typeVariable = addTypeVariableReference(type);
- return new HTypeConversion.withTypeRepresentation(type, kind, subtype,
- original, typeVariable);
- } else if (type.isFunctionType) {
- String name = kind == HTypeConversion.CAST_TYPE_CHECK
- ? '_asCheck' : '_assertCheck';
-
- List arguments = [buildFunctionType(type), original];
- pushInvokeDynamic(
- null,
- new Selector.call(name, backend.jsHelperLibrary, 1),
- arguments);
-
- return new HTypeConversion(type, kind, original.instructionType, pop());
- } else {
- return original.convertType(compiler, type, kind);
- }
- }
-
- HInstruction potentiallyBuildTypeHint(HInstruction original, DartType type) {
- if (!compiler.trustTypeAnnotations || type == null) return original;
- type = localsHandler.substInContext(type);
- if (!type.isInterfaceType) return original;
- TypeMask mask = new TypeMask.subtype(type.element, compiler.world);
- var result = new HTypeKnown.pinned(mask, original);
- return result;
- }
-
- HInstruction potentiallyCheckType(HInstruction original, DartType type,
- { int kind: HTypeConversion.CHECKED_MODE_CHECK }) {
- if (!compiler.enableTypeAssertions) return original;
- type = localsHandler.substInContext(type);
- HInstruction other = buildTypeConversion(original, type, kind);
- if (other != original) add(other);
- registry.registerIsCheck(type);
- return other;
- }
-
- void assertIsSubtype(ast.Node node, DartType subtype, DartType supertype,
- String message) {
- HInstruction subtypeInstruction =
- analyzeTypeArgument(localsHandler.substInContext(subtype));
- HInstruction supertypeInstruction =
- analyzeTypeArgument(localsHandler.substInContext(supertype));
- HInstruction messageInstruction =
- graph.addConstantString(new ast.DartString.literal(message), compiler);
- Element element = backend.getAssertIsSubtype();
- var inputs = <HInstruction>[subtypeInstruction, supertypeInstruction,
- messageInstruction];
- HInstruction assertIsSubtype = new HInvokeStatic(
- element, inputs, subtypeInstruction.instructionType);
- registry.registerTypeVariableBoundsSubtypeCheck(subtype, supertype);
- add(assertIsSubtype);
- }
-
- HGraph closeFunction() {
- // TODO(kasperl): Make this goto an implicit return.
- if (!isAborted()) closeAndGotoExit(new HGoto());
- graph.finalize();
- return graph;
- }
-
- void push(HInstruction instruction) {
- add(instruction);
- stack.add(instruction);
- }
-
- void pushWithPosition(HInstruction instruction, ast.Node node) {
- push(attachPosition(instruction, node));
- }
-
- HInstruction pop() {
- return stack.removeLast();
- }
-
- void dup() {
- stack.add(stack.last);
- }
-
- HInstruction popBoolified() {
- HInstruction value = pop();
- if (compiler.enableTypeAssertions) {
- return potentiallyCheckType(
- value,
- compiler.boolClass.rawType,
- kind: HTypeConversion.BOOLEAN_CONVERSION_CHECK);
- }
- HInstruction result = new HBoolify(value, backend.boolType);
- add(result);
- return result;
- }
-
- HInstruction attachPosition(HInstruction target, ast.Node node) {
- if (node != null) {
- target.sourcePosition = sourceFileLocationForBeginToken(node);
- }
- return target;
- }
-
- SourceFileLocation sourceFileLocationForBeginToken(ast.Node node) =>
- sourceFileLocationForToken(node, node.getBeginToken());
-
- SourceFileLocation sourceFileLocationForEndToken(ast.Node node) =>
- sourceFileLocationForToken(node, node.getEndToken());
-
- SourceFileLocation sourceFileLocationForToken(ast.Node node, Token token) {
- SourceFile sourceFile = currentSourceFile();
- SourceFileLocation location =
- new TokenSourceFileLocation(sourceFile, token, sourceElement.name);
- checkValidSourceFileLocation(location, sourceFile, token.charOffset);
- return location;
- }
-
- void visit(ast.Node node) {
- if (node != null) node.accept(this);
- }
-
- visitBlock(ast.Block node) {
- assert(!isAborted());
- if (!isReachable) return; // This can only happen when inlining.
- for (Link<ast.Node> link = node.statements.nodes;
- !link.isEmpty;
- link = link.tail) {
- visit(link.head);
- if (!isReachable) {
- // The block has been aborted by a return or a throw.
- if (!stack.isEmpty) {
- compiler.internalError(node, 'Non-empty instruction stack.');
- }
- return;
- }
- }
- assert(!current.isClosed());
- if (!stack.isEmpty) {
- compiler.internalError(node, 'Non-empty instruction stack.');
- }
- }
-
- visitClassNode(ast.ClassNode node) {
- compiler.internalError(node,
- 'SsaBuilder.visitClassNode should not be called.');
- }
-
- visitThrowExpression(ast.Expression expression) {
- bool old = inExpressionOfThrow;
- try {
- inExpressionOfThrow = true;
- visit(expression);
- } finally {
- inExpressionOfThrow = old;
- }
- }
-
- visitExpressionStatement(ast.ExpressionStatement node) {
- if (!isReachable) return;
- ast.Throw throwExpression = node.expression.asThrow();
- if (throwExpression != null && inliningStack.isEmpty) {
- visitThrowExpression(throwExpression.expression);
- handleInTryStatement();
- closeAndGotoExit(new HThrow(pop()));
- } else {
- visit(node.expression);
- pop();
- }
- }
-
- /**
- * Creates a new loop-header block. The previous [current] block
- * is closed with an [HGoto] and replaced by the newly created block.
- * Also notifies the locals handler that we're entering a loop.
- */
- JumpHandler beginLoopHeader(ast.Node node) {
- assert(!isAborted());
- HBasicBlock previousBlock = close(new HGoto());
-
- JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: true);
- HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(
- jumpHandler.target,
- jumpHandler.labels());
- previousBlock.addSuccessor(loopEntry);
- open(loopEntry);
-
- localsHandler.beginLoopHeader(loopEntry);
- return jumpHandler;
- }
-
- /**
- * Ends the loop:
- * - creates a new block and adds it as successor to the [branchExitBlock] and
- * any blocks that end in break.
- * - opens the new block (setting as [current]).
- * - notifies the locals handler that we're exiting a loop.
- * [savedLocals] are the locals from the end of the loop condition.
- * [branchExitBlock] is the exit (branching) block of the condition. Generally
- * this is not the top of the loop, since this would lead to critical edges.
- * It is null for degenerate do-while loops that have
- * no back edge because they abort (throw/return/break in the body and have
- * no continues).
- */
- void endLoop(HBasicBlock loopEntry,
- HBasicBlock branchExitBlock,
- JumpHandler jumpHandler,
- LocalsHandler savedLocals) {
- HBasicBlock loopExitBlock = addNewBlock();
-
- List<LocalsHandler> breakHandlers = <LocalsHandler>[];
- // Collect data for the successors and the phis at each break.
- jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
- breakInstruction.block.addSuccessor(loopExitBlock);
- breakHandlers.add(locals);
- });
-
- // The exit block is a successor of the loop condition if it is reached.
- // We don't add the successor in the case of a while/for loop that aborts
- // because the caller of endLoop will be wiring up a special empty else
- // block instead.
- if (branchExitBlock != null) {
- branchExitBlock.addSuccessor(loopExitBlock);
- }
- // Update the phis at the loop entry with the current values of locals.
- localsHandler.endLoop(loopEntry);
-
- // Start generating code for the exit block.
- open(loopExitBlock);
-
- // Create a new localsHandler for the loopExitBlock with the correct phis.
- if (!breakHandlers.isEmpty) {
- if (branchExitBlock != null) {
- // Add the values of the locals at the end of the condition block to
- // the phis. These are the values that flow to the exit if the
- // condition fails.
- breakHandlers.add(savedLocals);
- }
- localsHandler = savedLocals.mergeMultiple(breakHandlers, loopExitBlock);
- } else {
- localsHandler = savedLocals;
- }
- }
-
- HSubGraphBlockInformation wrapStatementGraph(SubGraph statements) {
- if (statements == null) return null;
- return new HSubGraphBlockInformation(statements);
- }
-
- HSubExpressionBlockInformation wrapExpressionGraph(SubExpression expression) {
- if (expression == null) return null;
- return new HSubExpressionBlockInformation(expression);
- }
-
- // For while loops, initializer and update are null.
- // The condition function must return a boolean result.
- // None of the functions must leave anything on the stack.
- void handleLoop(ast.Node loop,
- void initialize(),
- HInstruction condition(),
- void update(),
- void body()) {
- // Generate:
- // <initializer>
- // loop-entry:
- // if (!<condition>) goto loop-exit;
- // <body>
- // <updates>
- // goto loop-entry;
- // loop-exit:
-
- localsHandler.startLoop(loop);
-
- // The initializer.
- SubExpression initializerGraph = null;
- HBasicBlock startBlock;
- if (initialize != null) {
- HBasicBlock initializerBlock = openNewBlock();
- startBlock = initializerBlock;
- initialize();
- assert(!isAborted());
- initializerGraph =
- new SubExpression(initializerBlock, current);
- }
-
- loopNesting++;
- JumpHandler jumpHandler = beginLoopHeader(loop);
- HLoopInformation loopInfo = current.loopInformation;
- HBasicBlock conditionBlock = current;
- if (startBlock == null) startBlock = conditionBlock;
-
- HInstruction conditionInstruction = condition();
- HBasicBlock conditionEndBlock =
- close(new HLoopBranch(conditionInstruction));
- SubExpression conditionExpression =
- new SubExpression(conditionBlock, conditionEndBlock);
-
- // Save the values of the local variables at the end of the condition
- // block. These are the values that will flow to the loop exit if the
- // condition fails.
- LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
-
- // The body.
- HBasicBlock beginBodyBlock = addNewBlock();
- conditionEndBlock.addSuccessor(beginBodyBlock);
- open(beginBodyBlock);
-
- localsHandler.enterLoopBody(loop);
- body();
-
- SubGraph bodyGraph = new SubGraph(beginBodyBlock, lastOpenedBlock);
- HBasicBlock bodyBlock = current;
- if (current != null) close(new HGoto());
-
- SubExpression updateGraph;
-
- bool loopIsDegenerate = !jumpHandler.hasAnyContinue() && bodyBlock == null;
- if (!loopIsDegenerate) {
- // Update.
- // We create an update block, even when we are in a while loop. There the
- // update block is the jump-target for continue statements. We could avoid
- // the creation if there is no continue, but for now we always create it.
- HBasicBlock updateBlock = addNewBlock();
-
- List<LocalsHandler> continueHandlers = <LocalsHandler>[];
- jumpHandler.forEachContinue((HContinue instruction,
- LocalsHandler locals) {
- instruction.block.addSuccessor(updateBlock);
- continueHandlers.add(locals);
- });
-
-
- if (bodyBlock != null) {
- continueHandlers.add(localsHandler);
- bodyBlock.addSuccessor(updateBlock);
- }
-
- open(updateBlock);
- localsHandler =
- continueHandlers[0].mergeMultiple(continueHandlers, updateBlock);
-
- HLabeledBlockInformation labelInfo;
- List<LabelDefinition> labels = jumpHandler.labels();
- JumpTarget target = elements.getTargetDefinition(loop);
- if (!labels.isEmpty) {
- beginBodyBlock.setBlockFlow(
- new HLabeledBlockInformation(
- new HSubGraphBlockInformation(bodyGraph),
- jumpHandler.labels(),
- isContinue: true),
- updateBlock);
- } else if (target != null && target.isContinueTarget) {
- beginBodyBlock.setBlockFlow(
- new HLabeledBlockInformation.implicit(
- new HSubGraphBlockInformation(bodyGraph),
- target,
- isContinue: true),
- updateBlock);
- }
-
- localsHandler.enterLoopUpdates(loop);
-
- update();
-
- HBasicBlock updateEndBlock = close(new HGoto());
- // The back-edge completing the cycle.
- updateEndBlock.addSuccessor(conditionBlock);
- updateGraph = new SubExpression(updateBlock, updateEndBlock);
-
- // Avoid a critical edge from the condition to the loop-exit body.
- HBasicBlock conditionExitBlock = addNewBlock();
- open(conditionExitBlock);
- close(new HGoto());
- conditionEndBlock.addSuccessor(conditionExitBlock);
-
- endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals);
-
- conditionBlock.postProcessLoopHeader();
- HLoopBlockInformation info =
- new HLoopBlockInformation(
- HLoopBlockInformation.loopType(loop),
- wrapExpressionGraph(initializerGraph),
- wrapExpressionGraph(conditionExpression),
- wrapStatementGraph(bodyGraph),
- wrapExpressionGraph(updateGraph),
- conditionBlock.loopInformation.target,
- conditionBlock.loopInformation.labels,
- sourceFileLocationForBeginToken(loop),
- sourceFileLocationForEndToken(loop));
-
- startBlock.setBlockFlow(info, current);
- loopInfo.loopBlockInformation = info;
- } else {
- // The body of the for/while loop always aborts, so there is no back edge.
- // We turn the code into:
- // if (condition) {
- // body;
- // } else {
- // // We always create an empty else block to avoid critical edges.
- // }
- //
- // If there is any break in the body, we attach a synthetic
- // label to the if.
- HBasicBlock elseBlock = addNewBlock();
- open(elseBlock);
- close(new HGoto());
- // Pass the elseBlock as the branchBlock, because that's the block we go
- // to just before leaving the 'loop'.
- endLoop(conditionBlock, elseBlock, jumpHandler, savedLocals);
-
- SubGraph elseGraph = new SubGraph(elseBlock, elseBlock);
- // Remove the loop information attached to the header.
- conditionBlock.loopInformation = null;
-
- // Remove the [HLoopBranch] instruction and replace it with
- // [HIf].
- HInstruction condition = conditionEndBlock.last.inputs[0];
- conditionEndBlock.addAtExit(new HIf(condition));
- conditionEndBlock.addSuccessor(elseBlock);
- conditionEndBlock.remove(conditionEndBlock.last);
- HIfBlockInformation info =
- new HIfBlockInformation(
- wrapExpressionGraph(conditionExpression),
- wrapStatementGraph(bodyGraph),
- wrapStatementGraph(elseGraph));
-
- conditionEndBlock.setBlockFlow(info, current);
- HIf ifBlock = conditionEndBlock.last;
- ifBlock.blockInformation = conditionEndBlock.blockFlow;
-
- // If the body has any break, attach a synthesized label to the
- // if block.
- if (jumpHandler.hasAnyBreak()) {
- JumpTarget target = elements.getTargetDefinition(loop);
- LabelDefinition label = target.addLabel(null, 'loop');
- label.setBreakTarget();
- SubGraph labelGraph = new SubGraph(conditionBlock, current);
- HLabeledBlockInformation labelInfo = new HLabeledBlockInformation(
- new HSubGraphBlockInformation(labelGraph),
- <LabelDefinition>[label]);
-
- conditionBlock.setBlockFlow(labelInfo, current);
-
- jumpHandler.forEachBreak((HBreak breakInstruction, _) {
- HBasicBlock block = breakInstruction.block;
- block.addAtExit(new HBreak.toLabel(label));
- block.remove(breakInstruction);
- });
- }
- }
- jumpHandler.close();
- loopNesting--;
- }
-
- visitFor(ast.For node) {
- assert(isReachable);
- assert(node.body != null);
- void buildInitializer() {
- ast.Node initializer = node.initializer;
- if (initializer == null) return;
- visit(initializer);
- if (initializer.asExpression() != null) {
- pop();
- }
- }
- HInstruction buildCondition() {
- if (node.condition == null) {
- return graph.addConstantBool(true, compiler);
- }
- visit(node.condition);
- return popBoolified();
- }
- void buildUpdate() {
- for (ast.Expression expression in node.update) {
- visit(expression);
- assert(!isAborted());
- // The result of the update instruction isn't used, and can just
- // be dropped.
- HInstruction updateInstruction = pop();
- }
- }
- void buildBody() {
- visit(node.body);
- }
- handleLoop(node, buildInitializer, buildCondition, buildUpdate, buildBody);
- }
-
- visitWhile(ast.While node) {
- assert(isReachable);
- HInstruction buildCondition() {
- visit(node.condition);
- return popBoolified();
- }
- handleLoop(node,
- () {},
- buildCondition,
- () {},
- () { visit(node.body); });
- }
-
- visitDoWhile(ast.DoWhile node) {
- assert(isReachable);
- LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
- localsHandler.startLoop(node);
- loopNesting++;
- JumpHandler jumpHandler = beginLoopHeader(node);
- HLoopInformation loopInfo = current.loopInformation;
- HBasicBlock loopEntryBlock = current;
- HBasicBlock bodyEntryBlock = current;
- JumpTarget target = elements.getTargetDefinition(node);
- bool hasContinues = target != null && target.isContinueTarget;
- if (hasContinues) {
- // Add extra block to hang labels on.
- // It doesn't currently work if they are on the same block as the
- // HLoopInfo. The handling of HLabeledBlockInformation will visit a
- // SubGraph that starts at the same block again, so the HLoopInfo is
- // either handled twice, or it's handled after the labeled block info,
- // both of which generate the wrong code.
- // Using a separate block is just a simple workaround.
- bodyEntryBlock = openNewBlock();
- }
- localsHandler.enterLoopBody(node);
- visit(node.body);
-
- // If there are no continues we could avoid the creation of the condition
- // block. This could also lead to a block having multiple entries and exits.
- HBasicBlock bodyExitBlock;
- bool isAbortingBody = false;
- if (current != null) {
- bodyExitBlock = close(new HGoto());
- } else {
- isAbortingBody = true;
- bodyExitBlock = lastOpenedBlock;
- }
-
- SubExpression conditionExpression;
- bool loopIsDegenerate = isAbortingBody && !hasContinues;
- if (!loopIsDegenerate) {
- HBasicBlock conditionBlock = addNewBlock();
-
- List<LocalsHandler> continueHandlers = <LocalsHandler>[];
- jumpHandler.forEachContinue((HContinue instruction,
- LocalsHandler locals) {
- instruction.block.addSuccessor(conditionBlock);
- continueHandlers.add(locals);
- });
-
- if (!isAbortingBody) {
- bodyExitBlock.addSuccessor(conditionBlock);
- }
-
- if (!continueHandlers.isEmpty) {
- if (!isAbortingBody) continueHandlers.add(localsHandler);
- localsHandler =
- savedLocals.mergeMultiple(continueHandlers, conditionBlock);
- SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
- List<LabelDefinition> labels = jumpHandler.labels();
- HSubGraphBlockInformation bodyInfo =
- new HSubGraphBlockInformation(bodyGraph);
- HLabeledBlockInformation info;
- if (!labels.isEmpty) {
- info = new HLabeledBlockInformation(bodyInfo, labels,
- isContinue: true);
- } else {
- info = new HLabeledBlockInformation.implicit(bodyInfo, target,
- isContinue: true);
- }
- bodyEntryBlock.setBlockFlow(info, conditionBlock);
- }
- open(conditionBlock);
-
- visit(node.condition);
- assert(!isAborted());
- HInstruction conditionInstruction = popBoolified();
- HBasicBlock conditionEndBlock = close(
- new HLoopBranch(conditionInstruction, HLoopBranch.DO_WHILE_LOOP));
-
- HBasicBlock avoidCriticalEdge = addNewBlock();
- conditionEndBlock.addSuccessor(avoidCriticalEdge);
- open(avoidCriticalEdge);
- close(new HGoto());
- avoidCriticalEdge.addSuccessor(loopEntryBlock); // The back-edge.
-
- conditionExpression =
- new SubExpression(conditionBlock, conditionEndBlock);
-
- // Avoid a critical edge from the condition to the loop-exit body.
- HBasicBlock conditionExitBlock = addNewBlock();
- open(conditionExitBlock);
- close(new HGoto());
- conditionEndBlock.addSuccessor(conditionExitBlock);
-
- endLoop(loopEntryBlock, conditionExitBlock, jumpHandler, localsHandler);
-
- loopEntryBlock.postProcessLoopHeader();
- SubGraph bodyGraph = new SubGraph(loopEntryBlock, bodyExitBlock);
- HLoopBlockInformation loopBlockInfo =
- new HLoopBlockInformation(
- HLoopBlockInformation.DO_WHILE_LOOP,
- null,
- wrapExpressionGraph(conditionExpression),
- wrapStatementGraph(bodyGraph),
- null,
- loopEntryBlock.loopInformation.target,
- loopEntryBlock.loopInformation.labels,
- sourceFileLocationForBeginToken(node),
- sourceFileLocationForEndToken(node));
- loopEntryBlock.setBlockFlow(loopBlockInfo, current);
- loopInfo.loopBlockInformation = loopBlockInfo;
- } else {
- // Since the loop has no back edge, we remove the loop information on the
- // header.
- loopEntryBlock.loopInformation = null;
-
- if (jumpHandler.hasAnyBreak()) {
- // Null branchBlock because the body of the do-while loop always aborts,
- // so we never get to the condition.
- endLoop(loopEntryBlock, null, jumpHandler, localsHandler);
-
- // Since the body of the loop has a break, we attach a synthesized label
- // to the body.
- SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
- JumpTarget target = elements.getTargetDefinition(node);
- LabelDefinition label = target.addLabel(null, 'loop');
- label.setBreakTarget();
- HLabeledBlockInformation info = new HLabeledBlockInformation(
- new HSubGraphBlockInformation(bodyGraph), <LabelDefinition>[label]);
- loopEntryBlock.setBlockFlow(info, current);
- jumpHandler.forEachBreak((HBreak breakInstruction, _) {
- HBasicBlock block = breakInstruction.block;
- block.addAtExit(new HBreak.toLabel(label));
- block.remove(breakInstruction);
- });
- }
- }
- jumpHandler.close();
- loopNesting--;
- }
-
- visitFunctionExpression(ast.FunctionExpression node) {
- ClosureClassMap nestedClosureData =
- compiler.closureToClassMapper.getMappingForNestedFunction(node);
- assert(nestedClosureData != null);
- assert(nestedClosureData.closureClassElement != null);
- ClosureClassElement closureClassElement =
- nestedClosureData.closureClassElement;
- FunctionElement callElement = nestedClosureData.callElement;
- // TODO(ahe): This should be registered in codegen, not here.
- // TODO(johnniwinther): Is [registerStaticUse] equivalent to
- // [addToWorkList]?
- registry.registerStaticUse(callElement);
- // TODO(ahe): This should be registered in codegen, not here.
- registry.registerInstantiatedClass(closureClassElement);
-
- List<HInstruction> capturedVariables = <HInstruction>[];
- closureClassElement.closureFields.forEach((ClosureFieldElement field) {
- Local capturedLocal =
- nestedClosureData.getLocalVariableForClosureField(field);
- assert(capturedLocal != null);
- capturedVariables.add(localsHandler.readLocal(capturedLocal));
- });
-
- TypeMask type =
- new TypeMask.nonNullExact(compiler.functionClass, compiler.world);
- push(new HForeignNew(closureClassElement, type, capturedVariables));
-
- Element methodElement = nestedClosureData.closureElement;
- if (compiler.backend.methodNeedsRti(methodElement)) {
- registry.registerClosureWithFreeTypeVariables(methodElement);
- }
- }
-
- visitFunctionDeclaration(ast.FunctionDeclaration node) {
- assert(isReachable);
- visit(node.function);
- LocalFunctionElement localFunction =
- elements.getFunctionDefinition(node.function);
- localsHandler.updateLocal(localFunction, pop());
- }
-
- visitIdentifier(ast.Identifier node) {
- if (node.isThis()) {
- stack.add(localsHandler.readThis());
- } else {
- compiler.internalError(node,
- "SsaFromAstMixin.visitIdentifier on non-this.");
- }
- }
-
- visitIf(ast.If node) {
- assert(isReachable);
- handleIf(node,
- () => visit(node.condition),
- () => visit(node.thenPart),
- node.elsePart != null ? () => visit(node.elsePart) : null);
- }
-
- void handleIf(ast.Node diagnosticNode,
- void visitCondition(), void visitThen(), void visitElse()) {
- SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, diagnosticNode);
- branchBuilder.handleIf(visitCondition, visitThen, visitElse);
- }
-
- void visitLogicalAndOr(ast.Send node, ast.Operator op) {
- SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, node);
- branchBuilder.handleLogicalAndOrWithLeftNode(
- node.receiver,
- () { visit(node.argumentsNode); },
- isAnd: ("&&" == op.source));
- }
-
- void visitLogicalNot(ast.Send node) {
- assert(node.argumentsNode is ast.Prefix);
- visit(node.receiver);
- HNot not = new HNot(popBoolified(), backend.boolType);
- pushWithPosition(not, node);
- }
-
- void visitUnary(ast.Send node, ast.Operator op) {
- assert(node.argumentsNode is ast.Prefix);
- visit(node.receiver);
- assert(!identical(op.token.kind, PLUS_TOKEN));
- HInstruction operand = pop();
-
- // See if we can constant-fold right away. This avoids rewrites later on.
- if (operand is HConstant) {
- UnaryOperation operation = constantSystem.lookupUnary(op.source);
- HConstant constant = operand;
- ConstantValue folded = operation.fold(constant.constant);
- if (folded != null) {
- stack.add(graph.addConstant(folded, compiler));
- return;
- }
- }
-
- pushInvokeDynamic(node, elements.getSelector(node), [operand]);
- }
-
- void visitBinary(HInstruction left,
- ast.Operator op,
- HInstruction right,
- Selector selector,
- ast.Send send) {
- switch (op.source) {
- case "===":
- pushWithPosition(
- new HIdentity(left, right, null, backend.boolType), op);
- return;
- case "!==":
- HIdentity eq = new HIdentity(left, right, null, backend.boolType);
- add(eq);
- pushWithPosition(new HNot(eq, backend.boolType), op);
- return;
- }
-
- pushInvokeDynamic(send, selector, [left, right], location: op);
- if (op.source == '!=') {
- pushWithPosition(new HNot(popBoolified(), backend.boolType), op);
- }
- }
-
- HInstruction generateInstanceSendReceiver(ast.Send send) {
- assert(Elements.isInstanceSend(send, elements));
- if (send.receiver == null) {
- return localsHandler.readThis();
- }
- visit(send.receiver);
- return pop();
- }
-
- String noSuchMethodTargetSymbolString(ErroneousElement error,
- [String prefix]) {
- String result = error.name;
- if (prefix == "set") return "$result=";
- return result;
- }
-
- /**
- * Returns a set of interceptor classes that contain the given
- * [selector].
- */
- void generateInstanceGetterWithCompiledReceiver(ast.Send send,
- Selector selector,
- HInstruction receiver) {
- assert(Elements.isInstanceSend(send, elements));
- assert(selector.isGetter);
- pushInvokeDynamic(send, selector, [receiver]);
- }
-
- /// Inserts a call to checkDeferredIsLoaded if the send has a prefix that
- /// resolves to a deferred library.
- void generateIsDeferredLoadedCheckIfNeeded(ast.Send node) {
- DeferredLoadTask deferredTask = compiler.deferredLoadTask;
- PrefixElement prefixElement =
- deferredTask.deferredPrefixElement(node, elements);
- if (prefixElement != null) {
- String loadId =
- deferredTask.importDeferName[prefixElement.deferredImport];
- HInstruction loadIdConstant = addConstantString(loadId);
- String uri = prefixElement.deferredImport.uri.dartString.slowToString();
- HInstruction uriConstant = addConstantString(uri);
- Element helper = backend.getCheckDeferredIsLoaded();
- pushInvokeStatic(node, helper, [loadIdConstant, uriConstant]);
- pop();
- }
- }
-
- void generateGetter(ast.Send send, Element element) {
- if (element != null && element.isForeign(backend)) {
- visitForeignGetter(send);
- } else if (Elements.isStaticOrTopLevelField(element)) {
- ConstantExpression constant;
- if (element.isField && !element.isAssignable) {
- // A static final or const. Get its constant value and inline it if
- // the value can be compiled eagerly.
- constant = backend.constants.getConstantForVariable(element);
- }
- if (constant != null) {
- ConstantValue value = constant.value;
- HConstant instruction;
- // Constants that are referred via a deferred prefix should be referred
- // by reference.
- PrefixElement prefix = compiler.deferredLoadTask
- .deferredPrefixElement(send, elements);
- if (prefix != null) {
- instruction = graph.addDeferredConstant(value, prefix, compiler);
- } else {
- instruction = graph.addConstant(value, compiler);
- }
- stack.add(instruction);
- // The inferrer may have found a better type than the constant
- // handler in the case of lists, because the constant handler
- // does not look at elements in the list.
- TypeMask type =
- TypeMaskFactory.inferredTypeForElement(element, compiler);
- if (!type.containsAll(compiler.world) &&
- !instruction.isConstantNull()) {
- // TODO(13429): The inferrer should know that an element
- // cannot be null.
- instruction.instructionType = type.nonNullable();
- }
- } else if (element.isField && isLazilyInitialized(element)) {
- HInstruction instruction = new HLazyStatic(
- element,
- TypeMaskFactory.inferredTypeForElement(element, compiler));
- push(instruction);
- } else {
- if (element.isGetter) {
- pushInvokeStatic(send, element, <HInstruction>[]);
- } else {
- // TODO(5346): Try to avoid the need for calling [declaration] before
- // creating an [HStatic].
- HInstruction instruction = new HStatic(
- element.declaration,
- TypeMaskFactory.inferredTypeForElement(element, compiler));
- push(instruction);
- }
- }
- } else if (Elements.isInstanceSend(send, elements)) {
- HInstruction receiver = generateInstanceSendReceiver(send);
- generateInstanceGetterWithCompiledReceiver(
- send, elements.getSelector(send), receiver);
- } else if (Elements.isStaticOrTopLevelFunction(element)) {
- // TODO(5346): Try to avoid the need for calling [declaration] before
- // creating an [HStatic].
- push(new HStatic(element.declaration, backend.nonNullType));
- // TODO(ahe): This should be registered in codegen.
- registry.registerGetOfStaticFunction(element.declaration);
- } else if (Elements.isErroneousElement(element)) {
- // An erroneous element indicates an unresolved static getter.
- generateThrowNoSuchMethod(send,
- noSuchMethodTargetSymbolString(element, 'get'),
- argumentNodes: const Link<ast.Node>());
- } else {
- LocalElement local = element;
- stack.add(localsHandler.readLocal(local));
- }
- }
-
- void generateInstanceSetterWithCompiledReceiver(ast.Send send,
- HInstruction receiver,
- HInstruction value,
- {Selector selector,
- ast.Node location}) {
- assert(send == null || Elements.isInstanceSend(send, elements));
- if (selector == null) {
- assert(send != null);
- selector = elements.getSelector(send);
- }
- if (location == null) {
- assert(send != null);
- location = send;
- }
- assert(selector.isSetter);
- pushInvokeDynamic(location, selector, [receiver, value]);
- pop();
- stack.add(value);
- }
-
- void generateNonInstanceSetter(ast.SendSet send,
- Element element,
- HInstruction value,
- {ast.Node location}) {
- assert(send == null || !Elements.isInstanceSend(send, elements));
- if (location == null) {
- assert(send != null);
- location = send;
- }
- if (Elements.isStaticOrTopLevelField(element)) {
- if (element.isSetter) {
- pushInvokeStatic(location, element, <HInstruction>[value]);
- pop();
- } else {
- VariableElement field = element;
- value =
- potentiallyCheckType(value, field.type);
- addWithPosition(new HStaticStore(element, value), location);
- }
- stack.add(value);
- } else if (Elements.isErroneousElement(element)) {
- List<HInstruction> arguments =
- send == null ? const <HInstruction>[] : <HInstruction>[value];
- // An erroneous element indicates an unresolved static setter.
- generateThrowNoSuchMethod(location,
- noSuchMethodTargetSymbolString(element, 'set'),
- argumentValues: arguments);
- } else {
- stack.add(value);
- LocalElement local = element;
- // If the value does not already have a name, give it here.
- if (value.sourceElement == null) {
- value.sourceElement = local;
- }
- HInstruction checked =
- potentiallyCheckType(value, local.type);
- if (!identical(checked, value)) {
- pop();
- stack.add(checked);
- }
- HInstruction trusted =
- potentiallyBuildTypeHint(checked, local.type);
- if (!identical(trusted, checked)) {
- pop();
- push(trusted);
- }
-
- localsHandler.updateLocal(local, trusted);
- }
- }
-
- HInstruction invokeInterceptor(HInstruction receiver) {
- HInterceptor interceptor = new HInterceptor(receiver, backend.nonNullType);
- add(interceptor);
- return interceptor;
- }
-
- HForeign createForeign(js.Template code,
- TypeMask type,
- List<HInstruction> inputs) {
- return new HForeign(code, type, inputs);
- }
-
- HLiteralList buildLiteralList(List<HInstruction> inputs) {
- return new HLiteralList(inputs, backend.extendableArrayType);
- }
-
- // TODO(karlklose): change construction of the representations to be GVN'able
- // (dartbug.com/7182).
- HInstruction buildTypeArgumentRepresentations(DartType type) {
- // Compute the representation of the type arguments, including access
- // to the runtime type information for type variables as instructions.
- if (type.isTypeVariable) {
- return buildLiteralList(<HInstruction>[addTypeVariableReference(type)]);
- } else {
- assert(type.element.isClass);
- InterfaceType interface = type;
- List<HInstruction> inputs = <HInstruction>[];
- bool first = true;
- List<String> templates = <String>[];
- for (DartType argument in interface.typeArguments) {
- templates.add(rti.getTypeRepresentationWithHashes(argument, (variable) {
- HInstruction runtimeType = addTypeVariableReference(variable);
- inputs.add(runtimeType);
- }));
- }
- String template = '[${templates.join(', ')}]';
- // TODO(sra): This is a fresh template each time. We can't let the
- // template manager build them.
- js.Template code = js.js.uncachedExpressionTemplate(template);
- HInstruction representation =
- createForeign(code, backend.readableArrayType, inputs);
- return representation;
- }
- }
-
- visitOperatorSend(ast.Send node) {
- ast.Operator op = node.selector;
- if ("[]" == op.source) {
- visitDynamicSend(node);
- } else if ("&&" == op.source ||
- "||" == op.source) {
- visitLogicalAndOr(node, op);
- } else if ("!" == op.source) {
- visitLogicalNot(node);
- } else if (node.argumentsNode is ast.Prefix) {
- visitUnary(node, op);
- } else if ("is" == op.source) {
- visitIsSend(node);
- } else if ("as" == op.source) {
- visit(node.receiver);
- HInstruction expression = pop();
- DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
- if (type.isMalformed) {
- ErroneousElement element = type.element;
- generateTypeError(node, element.message);
- } else {
- HInstruction converted = buildTypeConversion(
- expression,
- localsHandler.substInContext(type),
- HTypeConversion.CAST_TYPE_CHECK);
- if (converted != expression) add(converted);
- stack.add(converted);
- }
- } else {
- visit(node.receiver);
- visit(node.argumentsNode);
- var right = pop();
- var left = pop();
- visitBinary(left, op, right, elements.getSelector(node), node);
- }
- }
-
- void visitIsSend(ast.Send node) {
- visit(node.receiver);
- HInstruction expression = pop();
- bool isNot = node.isIsNotCheck;
- DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
- HInstruction instruction = buildIsNode(node, type, expression);
- if (isNot) {
- add(instruction);
- instruction = new HNot(instruction, backend.boolType);
- }
- push(instruction);
- }
-
- HInstruction buildIsNode(ast.Node node,
- DartType type,
- HInstruction expression) {
- type = localsHandler.substInContext(type).unalias(compiler);
- if (type.isFunctionType) {
- List arguments = [buildFunctionType(type), expression];
- pushInvokeDynamic(
- node, new Selector.call('_isTest', backend.jsHelperLibrary, 1),
- arguments);
- return new HIs.compound(type, expression, pop(), backend.boolType);
- } else if (type.isTypeVariable) {
- HInstruction runtimeType = addTypeVariableReference(type);
- Element helper = backend.getCheckSubtypeOfRuntimeType();
- List<HInstruction> inputs = <HInstruction>[expression, runtimeType];
- pushInvokeStatic(null, helper, inputs, backend.boolType);
- HInstruction call = pop();
- return new HIs.variable(type, expression, call, backend.boolType);
- } else if (RuntimeTypes.hasTypeArguments(type)) {
- ClassElement element = type.element;
- Element helper = backend.getCheckSubtype();
- HInstruction representations =
- buildTypeArgumentRepresentations(type);
- add(representations);
- String operator = backend.namer.operatorIs(element);
- HInstruction isFieldName = addConstantString(operator);
- HInstruction asFieldName = compiler.world.hasAnyStrictSubtype(element)
- ? addConstantString(backend.namer.substitutionName(element))
- : graph.addConstantNull(compiler);
- List<HInstruction> inputs = <HInstruction>[expression,
- isFieldName,
- representations,
- asFieldName];
- pushInvokeStatic(node, helper, inputs, backend.boolType);
- HInstruction call = pop();
- return new HIs.compound(type, expression, call, backend.boolType);
- } else if (type.isMalformed) {
- ErroneousElement element = type.element;
- generateTypeError(node, element.message);
- HInstruction call = pop();
- return new HIs.compound(type, expression, call, backend.boolType);
- } else {
- if (backend.hasDirectCheckFor(type)) {
- return new HIs.direct(type, expression, backend.boolType);
- }
- // TODO(johnniwinther): Avoid interceptor if unneeded.
- return new HIs.raw(
- type, expression, invokeInterceptor(expression), backend.boolType);
- }
- }
-
- HInstruction buildFunctionType(FunctionType type) {
- type.accept(new TypeBuilder(compiler.world), this);
- return pop();
- }
-
- void addDynamicSendArgumentsToList(ast.Send node, List<HInstruction> list) {
- Selector selector = elements.getSelector(node);
- if (selector.namedArgumentCount == 0) {
- addGenericSendArgumentsToList(node.arguments, list);
- } else {
- // Visit positional arguments and add them to the list.
- Link<ast.Node> arguments = node.arguments;
- int positionalArgumentCount = selector.positionalArgumentCount;
- for (int i = 0;
- i < positionalArgumentCount;
- arguments = arguments.tail, i++) {
- visit(arguments.head);
- list.add(pop());
- }
-
- // Visit named arguments and add them into a temporary map.
- Map<String, HInstruction> instructions =
- new Map<String, HInstruction>();
- List<String> namedArguments = selector.namedArguments;
- int nameIndex = 0;
- for (; !arguments.isEmpty; arguments = arguments.tail) {
- visit(arguments.head);
- instructions[namedArguments[nameIndex++]] = pop();
- }
-
- // Iterate through the named arguments to add them to the list
- // of instructions, in an order that can be shared with
- // selectors with the same named arguments.
- List<String> orderedNames = selector.getOrderedNamedArguments();
- for (String name in orderedNames) {
- list.add(instructions[name]);
- }
- }
- }
-
- /**
- * Returns true if the arguments were compatible with the function signature.
- *
- * Invariant: [element] must be an implementation element.
- */
- bool addStaticSendArgumentsToList(Selector selector,
- Link<ast.Node> arguments,
- FunctionElement element,
- List<HInstruction> list) {
- assert(invariant(element, element.isImplementation));
-
- HInstruction compileArgument(ast.Node argument) {
- visit(argument);
- return pop();
- }
-
- return selector.addArgumentsToList(arguments,
- list,
- element,
- compileArgument,
- handleConstantForOptionalParameter,
- compiler.world);
- }
-
- void addGenericSendArgumentsToList(Link<ast.Node> link, List<HInstruction> list) {
- for (; !link.isEmpty; link = link.tail) {
- visit(link.head);
- list.add(pop());
- }
- }
-
- visitDynamicSend(ast.Send node) {
- Selector selector = elements.getSelector(node);
-
- List<HInstruction> inputs = <HInstruction>[];
- HInstruction receiver = generateInstanceSendReceiver(node);
- inputs.add(receiver);
- addDynamicSendArgumentsToList(node, inputs);
-
- pushInvokeDynamic(node, selector, inputs);
- if (selector.isSetter || selector.isIndexSet) {
- pop();
- stack.add(inputs.last);
- }
- }
-
- visitClosureSend(ast.Send node) {
- Selector selector = elements.getSelector(node);
- assert(node.receiver == null);
- Element element = elements[node];
- HInstruction closureTarget;
- if (element == null) {
- visit(node.selector);
- closureTarget = pop();
- } else {
- LocalElement local = element;
- closureTarget = localsHandler.readLocal(local);
- }
- var inputs = <HInstruction>[];
- inputs.add(closureTarget);
- addDynamicSendArgumentsToList(node, inputs);
- Selector closureSelector = new Selector.callClosureFrom(selector);
- pushWithPosition(
- new HInvokeClosure(closureSelector, inputs, backend.dynamicType),
- node);
- }
-
- void handleForeignJs(ast.Send node) {
- Link<ast.Node> link = node.arguments;
- // If the invoke is on foreign code, don't visit the first
- // argument, which is the type, and the second argument,
- // which is the foreign code.
- if (link.isEmpty || link.tail.isEmpty) {
- compiler.internalError(node.argumentsNode,
- 'At least two arguments expected.');
- }
- native.NativeBehavior nativeBehavior =
- compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
-
- List<HInstruction> inputs = <HInstruction>[];
- addGenericSendArgumentsToList(link.tail.tail, inputs);
-
- TypeMask ssaType =
- TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
-
- if (nativeBehavior.codeTemplate.isExpression) {
- push(new HForeign(nativeBehavior.codeTemplate, ssaType, inputs,
- effects: nativeBehavior.sideEffects,
- nativeBehavior: nativeBehavior));
- } else {
- push(new HForeign(nativeBehavior.codeTemplate, ssaType, inputs,
- isStatement: true,
- effects: nativeBehavior.sideEffects,
- nativeBehavior: nativeBehavior,
- canThrow: true));
- }
- }
-
- void handleJsStringConcat(ast.Send node) {
- List<HInstruction> inputs = <HInstruction>[];
- addGenericSendArgumentsToList(node.arguments, inputs);
- if (inputs.length != 2) {
- compiler.internalError(node.argumentsNode, 'Two arguments expected.');
- }
- push(new HStringConcat(inputs[0], inputs[1], node, backend.stringType));
- }
-
- void handleForeignJsCurrentIsolateContext(ast.Send node) {
- if (!node.arguments.isEmpty) {
- compiler.internalError(node,
- 'Too many arguments to JS_CURRENT_ISOLATE_CONTEXT.');
- }
-
- if (!compiler.hasIsolateSupport) {
- // If the isolate library is not used, we just generate code
- // to fetch the current isolate.
- String name = backend.namer.currentIsolate;
- push(new HForeign(js.js.parseForeignJS(name),
- backend.dynamicType,
- <HInstruction>[]));
- } else {
- // Call a helper method from the isolate library. The isolate
- // library uses its own isolate structure, that encapsulates
- // Leg's isolate.
- Element element = backend.isolateHelperLibrary.find('_currentIsolate');
- if (element == null) {
- compiler.internalError(node,
- 'Isolate library and compiler mismatch.');
- }
- pushInvokeStatic(null, element, [], backend.dynamicType);
- }
- }
-
- void handleForeingJsGetFlag(ast.Send node) {
- List<ast.Node> arguments = node.arguments.toList();
- ast.Node argument;
- switch (arguments.length) {
- case 0:
- compiler.reportError(
- node, MessageKind.GENERIC,
- {'text': 'Error: Expected one argument to JS_GET_FLAG.'});
- return;
- case 1:
- argument = arguments[0];
- break;
- default:
- for (int i = 1; i < arguments.length; i++) {
- compiler.reportError(
- arguments[i], MessageKind.GENERIC,
- {'text': 'Error: Extra argument to JS_GET_FLAG.'});
- }
- return;
- }
- ast.LiteralString string = argument.asLiteralString();
- if (string == null) {
- compiler.reportError(
- argument, MessageKind.GENERIC,
- {'text': 'Error: Expected a literal string.'});
- }
- String name = string.dartString.slowToString();
- bool value = false;
- if (name == 'MUST_RETAIN_METADATA') {
- value = backend.mustRetainMetadata;
- } else {
- compiler.reportError(
- node, MessageKind.GENERIC,
- {'text': 'Error: Unknown internal flag "$name".'});
- }
- stack.add(graph.addConstantBool(value, compiler));
- }
-
- void handleForeignJsGetName(ast.Send node) {
- List<ast.Node> arguments = node.arguments.toList();
- ast.Node argument;
- switch (arguments.length) {
- case 0:
- compiler.reportError(
- node, MessageKind.GENERIC,
- {'text': 'Error: Expected one argument to JS_GET_NAME.'});
- return;
- case 1:
- argument = arguments[0];
- break;
- default:
- for (int i = 1; i < arguments.length; i++) {
- compiler.reportError(
- arguments[i], MessageKind.GENERIC,
- {'text': 'Error: Extra argument to JS_GET_NAME.'});
- }
- return;
- }
- ast.LiteralString string = argument.asLiteralString();
- if (string == null) {
- compiler.reportError(
- argument, MessageKind.GENERIC,
- {'text': 'Error: Expected a literal string.'});
- }
- stack.add(
- addConstantString(
- backend.namer.getNameForJsGetName(
- argument, string.dartString.slowToString())));
- }
-
- void handleForeignJsEmbeddedGlobal(ast.Send node) {
- List<ast.Node> arguments = node.arguments.toList();
- ast.Node globalNameNode;
- switch (arguments.length) {
- case 0:
- case 1:
- compiler.reportError(
- node, MessageKind.GENERIC,
- {'text': 'Error: Expected two arguments to JS_EMBEDDED_GLOBAL.'});
- return;
- case 2:
- // The type has been extracted earlier. We are only interested in the
- // name in this function.
- globalNameNode = arguments[1];
- break;
- default:
- for (int i = 2; i < arguments.length; i++) {
- compiler.reportError(
- arguments[i], MessageKind.GENERIC,
- {'text': 'Error: Extra argument to JS_EMBEDDED_GLOBAL.'});
- }
- return;
- }
- visit(arguments[1]);
- HInstruction globalNameHNode = pop();
- if (!globalNameHNode.isConstantString()) {
- compiler.reportError(
- arguments[1], MessageKind.GENERIC,
- {'text': 'Error: Expected String as second argument '
- 'to JS_EMBEDDED_GLOBAL.'});
- return;
- }
- HConstant hConstant = globalNameHNode;
- StringConstantValue constant = hConstant.constant;
- String globalName = constant.primitiveValue.slowToString();
- js.Template expr = js.js.expressionTemplateYielding(
- backend.emitter.generateEmbeddedGlobalAccess(globalName));
- native.NativeBehavior nativeBehavior =
- compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
- TypeMask ssaType =
- TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
- push(new HForeign(expr, ssaType, const []));
- }
-
- void handleJsInterceptorConstant(ast.Send node) {
- // Single argument must be a TypeConstant which is converted into a
- // InterceptorConstant.
- if (!node.arguments.isEmpty && node.arguments.tail.isEmpty) {
- ast.Node argument = node.arguments.head;
- visit(argument);
- HInstruction argumentInstruction = pop();
- if (argumentInstruction is HConstant) {
- ConstantValue argumentConstant = argumentInstruction.constant;
- if (argumentConstant is TypeConstantValue) {
- ConstantValue constant =
- new InterceptorConstantValue(argumentConstant.representedType);
- HInstruction instruction = graph.addConstant(constant, compiler);
- stack.add(instruction);
- return;
- }
- }
- }
- compiler.reportError(node,
- MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
- stack.add(graph.addConstantNull(compiler));
- }
-
- void handleForeignJsCallInIsolate(ast.Send node) {
- Link<ast.Node> link = node.arguments;
- if (!compiler.hasIsolateSupport) {
- // If the isolate library is not used, we just invoke the
- // closure.
- visit(link.tail.head);
- Selector selector = new Selector.callClosure(0);
- push(new HInvokeClosure(selector,
- <HInstruction>[pop()],
- backend.dynamicType));
- } else {
- // Call a helper method from the isolate library.
- Element element = backend.isolateHelperLibrary.find('_callInIsolate');
- if (element == null) {
- compiler.internalError(node,
- 'Isolate library and compiler mismatch.');
- }
- List<HInstruction> inputs = <HInstruction>[];
- addGenericSendArgumentsToList(link, inputs);
- pushInvokeStatic(node, element, inputs, backend.dynamicType);
- }
- }
-
- FunctionSignature handleForeignRawFunctionRef(ast.Send node, String name) {
- if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) {
- compiler.internalError(node.argumentsNode,
- '"$name" requires exactly one argument.');
- }
- ast.Node closure = node.arguments.head;
- Element element = elements[closure];
- if (!Elements.isStaticOrTopLevelFunction(element)) {
- compiler.internalError(closure,
- '"$name" requires a static or top-level method.');
- }
- FunctionElement function = element;
- // TODO(johnniwinther): Try to eliminate the need to distinguish declaration
- // and implementation signatures. Currently it is need because the
- // signatures have different elements for parameters.
- FunctionElement implementation = function.implementation;
- FunctionSignature params = implementation.functionSignature;
- if (params.optionalParameterCount != 0) {
- compiler.internalError(closure,
- '"$name" does not handle closure with optional parameters.');
- }
-
- registry.registerStaticUse(element);
- push(new HForeign(js.js.expressionTemplateYielding(
- backend.namer.elementAccess(element)),
- backend.dynamicType,
- <HInstruction>[]));
- return params;
- }
-
- void handleForeignDartClosureToJs(ast.Send node, String name) {
- // TODO(ahe): This implements DART_CLOSURE_TO_JS and should probably take
- // care to wrap the closure in another closure that saves the current
- // isolate.
- handleForeignRawFunctionRef(node, name);
- }
-
- void handleForeignSetCurrentIsolate(ast.Send node) {
- if (node.arguments.isEmpty || !node.arguments.tail.isEmpty) {
- compiler.internalError(node.argumentsNode,
- 'Exactly one argument required.');
- }
- visit(node.arguments.head);
- String isolateName = backend.namer.currentIsolate;
- SideEffects sideEffects = new SideEffects.empty();
- sideEffects.setAllSideEffects();
- push(new HForeign(js.js.parseForeignJS("$isolateName = #"),
- backend.dynamicType,
- <HInstruction>[pop()],
- effects: sideEffects));
- }
-
- void handleForeignCreateIsolate(ast.Send node) {
- if (!node.arguments.isEmpty) {
- compiler.internalError(node.argumentsNode, 'Too many arguments.');
- }
- String constructorName = backend.namer.isolateName;
- push(new HForeign(js.js.parseForeignJS("new $constructorName()"),
- backend.dynamicType,
- <HInstruction>[]));
- }
-
- void handleForeignDartObjectJsConstructorFunction(ast.Send node) {
- if (!node.arguments.isEmpty) {
- compiler.internalError(node.argumentsNode, 'Too many arguments.');
- }
- push(new HForeign(js.js.expressionTemplateYielding(
- backend.namer.elementAccess(compiler.objectClass)),
- backend.dynamicType,
- <HInstruction>[]));
- }
-
- void handleForeignJsCurrentIsolate(ast.Send node) {
- if (!node.arguments.isEmpty) {
- compiler.internalError(node.argumentsNode, 'Too many arguments.');
- }
- push(new HForeign(js.js.parseForeignJS(backend.namer.currentIsolate),
- backend.dynamicType,
- <HInstruction>[]));
- }
-
- visitForeignSend(ast.Send node) {
- Selector selector = elements.getSelector(node);
- String name = selector.name;
- if (name == 'JS') {
- handleForeignJs(node);
- } else if (name == 'JS_CURRENT_ISOLATE_CONTEXT') {
- handleForeignJsCurrentIsolateContext(node);
- } else if (name == 'JS_CALL_IN_ISOLATE') {
- handleForeignJsCallInIsolate(node);
- } else if (name == 'DART_CLOSURE_TO_JS') {
- handleForeignDartClosureToJs(node, 'DART_CLOSURE_TO_JS');
- } else if (name == 'RAW_DART_FUNCTION_REF') {
- handleForeignRawFunctionRef(node, 'RAW_DART_FUNCTION_REF');
- } else if (name == 'JS_SET_CURRENT_ISOLATE') {
- handleForeignSetCurrentIsolate(node);
- } else if (name == 'JS_CREATE_ISOLATE') {
- handleForeignCreateIsolate(node);
- } else if (name == 'JS_OPERATOR_IS_PREFIX') {
- stack.add(addConstantString(backend.namer.operatorIsPrefix()));
- } else if (name == 'JS_OBJECT_CLASS_NAME') {
- String name = backend.namer.getRuntimeTypeName(compiler.objectClass);
- stack.add(addConstantString(name));
- } else if (name == 'JS_NULL_CLASS_NAME') {
- String name = backend.namer.getRuntimeTypeName(compiler.nullClass);
- stack.add(addConstantString(name));
- } else if (name == 'JS_FUNCTION_CLASS_NAME') {
- String name = backend.namer.getRuntimeTypeName(compiler.functionClass);
- stack.add(addConstantString(name));
- } else if (name == 'JS_OPERATOR_AS_PREFIX') {
- stack.add(addConstantString(backend.namer.operatorAsPrefix()));
- } else if (name == 'JS_SIGNATURE_NAME') {
- stack.add(addConstantString(backend.namer.operatorSignature()));
- } else if (name == 'JS_FUNCTION_TYPE_TAG') {
- stack.add(addConstantString(backend.namer.functionTypeTag()));
- } else if (name == 'JS_FUNCTION_TYPE_VOID_RETURN_TAG') {
- stack.add(addConstantString(backend.namer.functionTypeVoidReturnTag()));
- } else if (name == 'JS_FUNCTION_TYPE_RETURN_TYPE_TAG') {
- stack.add(addConstantString(backend.namer.functionTypeReturnTypeTag()));
- } else if (name ==
- 'JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG') {
- stack.add(addConstantString(
- backend.namer.functionTypeRequiredParametersTag()));
- } else if (name ==
- 'JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG') {
- stack.add(addConstantString(
- backend.namer.functionTypeOptionalParametersTag()));
- } else if (name ==
- 'JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG') {
- stack.add(addConstantString(
- backend.namer.functionTypeNamedParametersTag()));
- } else if (name == 'JS_DART_OBJECT_CONSTRUCTOR') {
- handleForeignDartObjectJsConstructorFunction(node);
- } else if (name == 'JS_IS_INDEXABLE_FIELD_NAME') {
- Element element = backend.findHelper('JavaScriptIndexingBehavior');
- stack.add(addConstantString(backend.namer.operatorIs(element)));
- } else if (name == 'JS_CURRENT_ISOLATE') {
- handleForeignJsCurrentIsolate(node);
- } else if (name == 'JS_GET_NAME') {
- handleForeignJsGetName(node);
- } else if (name == 'JS_EMBEDDED_GLOBAL') {
- handleForeignJsEmbeddedGlobal(node);
- } else if (name == 'JS_GET_FLAG') {
- handleForeingJsGetFlag(node);
- } else if (name == 'JS_EFFECT') {
- stack.add(graph.addConstantNull(compiler));
- } else if (name == 'JS_INTERCEPTOR_CONSTANT') {
- handleJsInterceptorConstant(node);
- } else if (name == 'JS_STRING_CONCAT') {
- handleJsStringConcat(node);
- } else {
- throw "Unknown foreign: ${selector}";
- }
- }
-
- visitForeignGetter(ast.Send node) {
- Element element = elements[node];
- // Until now we only handle these as getters.
- invariant(node, element.isDeferredLoaderGetter);
- FunctionElement deferredLoader = element;
- Element loadFunction = compiler.loadLibraryFunction;
- PrefixElement prefixElement = deferredLoader.enclosingElement;
- String loadId = compiler.deferredLoadTask
- .importDeferName[prefixElement.deferredImport];
- var inputs = [graph.addConstantString(
- new ast.DartString.literal(loadId), compiler)];
- push(new HInvokeStatic(loadFunction, inputs, backend.nonNullType,
- targetCanThrow: false));
- }
-
- generateSuperNoSuchMethodSend(ast.Send node,
- Selector selector,
- List<HInstruction> arguments) {
- String name = selector.name;
-
- ClassElement cls = currentNonClosureClass;
- Element element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD);
- if (compiler.enabledInvokeOn
- && element.enclosingElement.declaration != compiler.objectClass) {
- // Register the call as dynamic if [noSuchMethod] on the super
- // class is _not_ the default implementation from [Object], in
- // case the [noSuchMethod] implementation calls
- // [JSInvocationMirror._invokeOn].
- registry.registerSelectorUse(selector.asUntyped);
- }
- String publicName = name;
- if (selector.isSetter) publicName += '=';
-
- ConstantValue nameConstant = constantSystem.createString(
- new ast.DartString.literal(publicName));
-
- String internalName = backend.namer.invocationName(selector);
- ConstantValue internalNameConstant =
- constantSystem.createString(new ast.DartString.literal(internalName));
-
- Element createInvocationMirror = backend.getCreateInvocationMirror();
- var argumentsInstruction = buildLiteralList(arguments);
- add(argumentsInstruction);
-
- var argumentNames = new List<HInstruction>();
- for (String argumentName in selector.namedArguments) {
- ConstantValue argumentNameConstant =
- constantSystem.createString(new ast.DartString.literal(argumentName));
- argumentNames.add(graph.addConstant(argumentNameConstant, compiler));
- }
- var argumentNamesInstruction = buildLiteralList(argumentNames);
- add(argumentNamesInstruction);
-
- ConstantValue kindConstant =
- constantSystem.createInt(selector.invocationMirrorKind);
-
- pushInvokeStatic(null,
- createInvocationMirror,
- [graph.addConstant(nameConstant, compiler),
- graph.addConstant(internalNameConstant, compiler),
- graph.addConstant(kindConstant, compiler),
- argumentsInstruction,
- argumentNamesInstruction],
- backend.dynamicType);
-
- var inputs = <HInstruction>[pop()];
- push(buildInvokeSuper(compiler.noSuchMethodSelector, element, inputs));
- }
-
- visitSuperSend(ast.Send node) {
- Selector selector = elements.getSelector(node);
- Element element = elements[node];
- if (Elements.isUnresolved(element)) {
- List<HInstruction> arguments = <HInstruction>[];
- if (!node.isPropertyAccess) {
- addGenericSendArgumentsToList(node.arguments, arguments);
- }
- return generateSuperNoSuchMethodSend(node, selector, arguments);
- }
- List<HInstruction> inputs = <HInstruction>[];
- if (node.isPropertyAccess) {
- push(buildInvokeSuper(selector, element, inputs));
- } else if (element.isFunction || element.isGenerativeConstructor) {
- if (selector.applies(element, compiler.world)) {
- // TODO(5347): Try to avoid the need for calling [implementation] before
- // calling [addStaticSendArgumentsToList].
- FunctionElement function = element.implementation;
- bool succeeded = addStaticSendArgumentsToList(selector, node.arguments,
- function, inputs);
- assert(succeeded);
- push(buildInvokeSuper(selector, element, inputs));
- } else if (element.isGenerativeConstructor) {
- generateWrongArgumentCountError(node, element, node.arguments);
- } else {
- addGenericSendArgumentsToList(node.arguments, inputs);
- generateSuperNoSuchMethodSend(node, selector, inputs);
- }
- } else {
- HInstruction target = buildInvokeSuper(selector, element, inputs);
- add(target);
- inputs = <HInstruction>[target];
- addDynamicSendArgumentsToList(node, inputs);
- Selector closureSelector = new Selector.callClosureFrom(selector);
- push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType));
- }
- }
-
- bool needsSubstitutionForTypeVariableAccess(ClassElement cls) {
- ClassWorld classWorld = compiler.world;
- if (classWorld.isUsedAsMixin(cls)) return true;
-
- Iterable<ClassElement> subclasses = compiler.world.strictSubclassesOf(cls);
- return subclasses.any((ClassElement subclass) {
- return !rti.isTrivialSubstitution(subclass, cls);
- });
- }
-
- /**
- * Generate code to extract the type arguments from the object, substitute
- * them as an instance of the type we are testing against (if necessary), and
- * extract the type argument by the index of the variable in the list of type
- * variables for that class.
- */
- HInstruction readTypeVariable(ClassElement cls,
- TypeVariableElement variable) {
- assert(sourceElement.isInstanceMember);
-
- HInstruction target = localsHandler.readThis();
- HConstant index = graph.addConstantInt(
- RuntimeTypes.getTypeVariableIndex(variable),
- compiler);
-
- if (needsSubstitutionForTypeVariableAccess(cls)) {
- // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
- // string concatenation in the implementation), and may prevent
- // segmentation of '$'.
- String substitutionNameString = backend.namer.getNameForRti(cls);
- HInstruction substitutionName = graph.addConstantString(
- new ast.LiteralDartString(substitutionNameString), compiler);
- pushInvokeStatic(null,
- backend.getGetRuntimeTypeArgument(),
- [target, substitutionName, index],
- backend.dynamicType);
- } else {
- pushInvokeStatic(null, backend.getGetTypeArgumentByIndex(),
- [target, index],
- backend.dynamicType);
- }
- return pop();
- }
-
- // TODO(karlklose): this is needed to avoid a bug where the resolved type is
- // not stored on a type annotation in the closure translator. Remove when
- // fixed.
- bool hasDirectLocal(Local local) {
- return !localsHandler.isAccessedDirectly(local) ||
- localsHandler.directLocals[local] != null;
- }
-
- /**
- * Helper to create an instruction that gets the value of a type variable.
- */
- HInstruction addTypeVariableReference(TypeVariableType type) {
- assert(assertTypeInContext(type));
- Element member = sourceElement;
- bool isClosure = member.enclosingElement.isClosure;
- if (isClosure) {
- ClosureClassElement closureClass = member.enclosingElement;
- member = closureClass.methodElement;
- member = member.outermostEnclosingMemberOrTopLevel;
- }
- bool isInConstructorContext = member.isConstructor ||
- member.isGenerativeConstructorBody;
- Local typeVariableLocal = localsHandler.getTypeVariableAsLocal(type);
- if (isClosure) {
- if (member.isFactoryConstructor ||
- (isInConstructorContext && hasDirectLocal(typeVariableLocal))) {
- // The type variable is used from a closure in a factory constructor.
- // The value of the type argument is stored as a local on the closure
- // itself.
- return localsHandler.readLocal(typeVariableLocal);
- } else if (member.isFunction ||
- member.isGetter ||
- member.isSetter ||
- isInConstructorContext) {
- // The type variable is stored on the "enclosing object" and needs to be
- // accessed using the this-reference in the closure.
- return readTypeVariable(member.enclosingClass, type.element);
- } else {
- assert(member.isField);
- // The type variable is stored in a parameter of the method.
- return localsHandler.readLocal(typeVariableLocal);
- }
- } else if (isInConstructorContext ||
- // When [member] is a field, we can be either
- // generating a checked setter or inlining its
- // initializer in a constructor. An initializer is
- // never built standalone, so [isBuildingFor] will
- // always return true when seeing one.
- (member.isField && !isBuildingFor(member))) {
- // The type variable is stored in a parameter of the method.
- return localsHandler.readLocal(typeVariableLocal);
- } else if (member.isInstanceMember) {
- // The type variable is stored on the object.
- return readTypeVariable(member.enclosingClass,
- type.element);
- } else {
- // TODO(ngeoffray): Match the VM behavior and throw an
- // exception at runtime.
- compiler.internalError(type.element,
- 'Unimplemented unresolved type variable.');
- return null;
- }
- }
-
- HInstruction analyzeTypeArgument(DartType argument) {
- assert(assertTypeInContext(argument));
- if (argument.treatAsDynamic) {
- // Represent [dynamic] as [null].
- return graph.addConstantNull(compiler);
- }
-
- if (argument.isTypeVariable) {
- return addTypeVariableReference(argument);
- }
-
- List<HInstruction> inputs = <HInstruction>[];
-
- String template = rti.getTypeRepresentationWithHashes(argument, (variable) {
- inputs.add(addTypeVariableReference(variable));
- });
-
- js.Template code = js.js.uncachedExpressionTemplate(template);
- HInstruction result = createForeign(code, backend.stringType, inputs);
- add(result);
- return result;
- }
-
- HInstruction handleListConstructor(InterfaceType type,
- ast.Node currentNode,
- HInstruction newObject) {
- if (!backend.classNeedsRti(type.element) || type.treatAsRaw) {
- return newObject;
- }
- List<HInstruction> inputs = <HInstruction>[];
- type = localsHandler.substInContext(type);
- type.typeArguments.forEach((DartType argument) {
- inputs.add(analyzeTypeArgument(argument));
- });
- // TODO(15489): Register at codegen.
- registry.registerInstantiatedType(type);
- return callSetRuntimeTypeInfo(type.element, inputs, newObject);
- }
-
- void copyRuntimeTypeInfo(HInstruction source, HInstruction target) {
- Element copyHelper = backend.getCopyTypeArguments();
- pushInvokeStatic(null, copyHelper, [source, target]);
- pop();
- }
-
- HInstruction callSetRuntimeTypeInfo(ClassElement element,
- List<HInstruction> rtiInputs,
- HInstruction newObject) {
- if (!backend.classNeedsRti(element) || element.typeVariables.isEmpty) {
- return newObject;
- }
-
- HInstruction typeInfo = buildLiteralList(rtiInputs);
- add(typeInfo);
-
- // Set the runtime type information on the object.
- Element typeInfoSetterElement = backend.getSetRuntimeTypeInfo();
- pushInvokeStatic(
- null,
- typeInfoSetterElement,
- <HInstruction>[newObject, typeInfo],
- backend.dynamicType);
-
- // The new object will now be referenced through the
- // `setRuntimeTypeInfo` call. We therefore set the type of that
- // instruction to be of the object's type.
- assert(stack.last is HInvokeStatic || stack.last == newObject);
- stack.last.instructionType = newObject.instructionType;
- return pop();
- }
-
- handleNewSend(ast.NewExpression node) {
- ast.Send send = node.send;
- generateIsDeferredLoadedCheckIfNeeded(send);
-
- bool isFixedList = false;
- bool isFixedListConstructorCall =
- Elements.isFixedListConstructorCall(elements[send], send, compiler);
- bool isGrowableListConstructorCall =
- Elements.isGrowableListConstructorCall(elements[send], send, compiler);
-
- TypeMask computeType(element) {
- Element originalElement = elements[send];
- if (isFixedListConstructorCall
- || Elements.isFilledListConstructorCall(
- originalElement, send, compiler)) {
- isFixedList = true;
- TypeMask inferred =
- TypeMaskFactory.inferredForNode(sourceElement, send, compiler);
- return inferred.containsAll(compiler.world)
- ? backend.fixedArrayType
- : inferred;
- } else if (isGrowableListConstructorCall) {
- TypeMask inferred =
- TypeMaskFactory.inferredForNode(sourceElement, send, compiler);
- return inferred.containsAll(compiler.world)
- ? backend.extendableArrayType
- : inferred;
- } else if (Elements.isConstructorOfTypedArraySubclass(
- originalElement, compiler)) {
- isFixedList = true;
- TypeMask inferred =
- TypeMaskFactory.inferredForNode(sourceElement, send, compiler);
- ClassElement cls = element.enclosingClass;
- assert(cls.thisType.element.isNative);
- return inferred.containsAll(compiler.world)
- ? new TypeMask.nonNullExact(cls.thisType.element, compiler.world)
- : inferred;
- } else if (element.isGenerativeConstructor) {
- ClassElement cls = element.enclosingClass;
- return new TypeMask.nonNullExact(cls.thisType.element, compiler.world);
- } else {
- return TypeMaskFactory.inferredReturnTypeForElement(
- originalElement, compiler);
- }
- }
-
- Element constructor = elements[send];
- Selector selector = elements.getSelector(send);
- ConstructorElement constructorDeclaration = constructor;
- ConstructorElement constructorImplementation = constructor.implementation;
- constructor = constructorImplementation.effectiveTarget;
-
- final bool isSymbolConstructor =
- constructorDeclaration == compiler.symbolConstructor;
- final bool isJSArrayTypedConstructor =
- constructorDeclaration == backend.jsArrayTypedConstructor;
-
- if (isSymbolConstructor) {
- constructor = compiler.symbolValidatedConstructor;
- assert(invariant(send, constructor != null,
- message: 'Constructor Symbol.validated is missing'));
- selector = compiler.symbolValidatedConstructorSelector;
- assert(invariant(send, selector != null,
- message: 'Constructor Symbol.validated is missing'));
- }
-
- bool isRedirected = constructorDeclaration.isRedirectingFactory;
- InterfaceType type = elements.getType(node);
- InterfaceType expectedType =
- constructorDeclaration.computeEffectiveTargetType(type);
- expectedType = localsHandler.substInContext(expectedType);
-
- if (checkTypeVariableBounds(node, type)) return;
-
- var inputs = <HInstruction>[];
- if (constructor.isGenerativeConstructor &&
- Elements.isNativeOrExtendsNative(constructor.enclosingClass)) {
- // Native class generative constructors take a pre-constructed object.
- inputs.add(graph.addConstantNull(compiler));
- }
- // TODO(5347): Try to avoid the need for calling [implementation] before
- // calling [addStaticSendArgumentsToList].
- bool succeeded = addStaticSendArgumentsToList(selector, send.arguments,
- constructor.implementation,
- inputs);
- if (!succeeded) {
- generateWrongArgumentCountError(send, constructor, send.arguments);
- return;
- }
-
- if (constructor.isFactoryConstructor &&
- !expectedType.typeArguments.isEmpty) {
- registry.registerFactoryWithTypeArguments();
- }
-
- TypeMask elementType = computeType(constructor);
- if (isFixedListConstructorCall) {
- if (!inputs[0].isNumber(compiler)) {
- HTypeConversion conversion = new HTypeConversion(
- null, HTypeConversion.ARGUMENT_TYPE_CHECK, backend.numType,
- inputs[0], null);
- add(conversion);
- inputs[0] = conversion;
- }
- js.Template code = js.js.parseForeignJS('Array(#)');
- var behavior = new native.NativeBehavior();
- behavior.typesReturned.add(expectedType);
- // The allocation can throw only if the given length is a double
- // or negative.
- bool canThrow = true;
- if (inputs[0].isInteger(compiler) && inputs[0] is HConstant) {
- var constant = inputs[0];
- if (constant.constant.primitiveValue >= 0) canThrow = false;
- }
- HForeign foreign = new HForeign(
- code, elementType, inputs, nativeBehavior: behavior,
- canThrow: canThrow);
- push(foreign);
- TypesInferrer inferrer = compiler.typesTask.typesInferrer;
- if (inferrer.isFixedArrayCheckedForGrowable(send)) {
- js.Template code = js.js.parseForeignJS(r'#.fixed$length = init');
- // We set the instruction as [canThrow] to avoid it being dead code.
- // We need a finer grained side effect.
- add(new HForeign(
- code, backend.nullType, [stack.last], canThrow: true));
- }
- } else if (isGrowableListConstructorCall) {
- push(buildLiteralList(<HInstruction>[]));
- stack.last.instructionType = elementType;
- } else {
- ClassElement cls = constructor.enclosingClass;
- if (cls.isAbstract && constructor.isGenerativeConstructor) {
- generateAbstractClassInstantiationError(send, cls.name);
- return;
- }
- potentiallyAddTypeArguments(inputs, cls, expectedType);
-
- addInlinedInstantiation(expectedType);
- pushInvokeStatic(node, constructor, inputs, elementType);
- removeInlinedInstantiation(expectedType);
- }
- HInstruction newInstance = stack.last;
- if (isFixedList) {
- // Overwrite the element type, in case the allocation site has
- // been inlined.
- newInstance.instructionType = elementType;
- JavaScriptItemCompilationContext context = work.compilationContext;
- context.allocatedFixedLists.add(newInstance);
- }
-
- // The List constructor forwards to a Dart static method that does
- // not know about the type argument. Therefore we special case
- // this constructor to have the setRuntimeTypeInfo called where
- // the 'new' is done.
- if (backend.classNeedsRti(compiler.listClass) &&
- (isFixedListConstructorCall || isGrowableListConstructorCall ||
- isJSArrayTypedConstructor)) {
- newInstance = handleListConstructor(type, send, pop());
- stack.add(newInstance);
- }
-
- // Finally, if we called a redirecting factory constructor, check the type.
- if (isRedirected) {
- HInstruction checked = potentiallyCheckType(newInstance, type);
- if (checked != newInstance) {
- pop();
- stack.add(checked);
- }
- }
- }
-
- void potentiallyAddTypeArguments(List<HInstruction> inputs, ClassElement cls,
- InterfaceType expectedType) {
- if (!backend.classNeedsRti(cls)) return;
- assert(expectedType.typeArguments.isEmpty ||
- cls.typeVariables.length == expectedType.typeArguments.length);
- expectedType.typeArguments.forEach((DartType argument) {
- inputs.add(analyzeTypeArgument(argument));
- });
- }
-
- /// In checked mode checks the [type] of [node] to be well-bounded. The method
- /// returns [:true:] if an error can be statically determined.
- bool checkTypeVariableBounds(ast.NewExpression node, InterfaceType type) {
- if (!compiler.enableTypeAssertions) return false;
-
- Map<DartType, Set<DartType>> seenChecksMap =
- new Map<DartType, Set<DartType>>();
- bool definitelyFails = false;
-
- addTypeVariableBoundCheck(GenericType instance,
- DartType typeArgument,
- TypeVariableType typeVariable,
- DartType bound) {
- if (definitelyFails) return;
-
- int subtypeRelation = compiler.types.computeSubtypeRelation(typeArgument, bound);
- if (subtypeRelation == Types.IS_SUBTYPE) return;
-
- String message =
- "Can't create an instance of malbounded type '$type': "
- "'${typeArgument}' is not a subtype of bound '${bound}' for "
- "type variable '${typeVariable}' of type "
- "${type == instance
- ? "'${type.element.thisType}'"
- : "'${instance.element.thisType}' on the supertype "
- "'${instance}' of '${type}'"
- }.";
- if (subtypeRelation == Types.NOT_SUBTYPE) {
- generateTypeError(node, message);
- definitelyFails = true;
- return;
- } else if (subtypeRelation == Types.MAYBE_SUBTYPE) {
- Set<DartType> seenChecks =
- seenChecksMap.putIfAbsent(typeArgument, () => new Set<DartType>());
- if (!seenChecks.contains(bound)) {
- seenChecks.add(bound);
- assertIsSubtype(node, typeArgument, bound, message);
- }
- }
- }
-
- compiler.types.checkTypeVariableBounds(type, addTypeVariableBoundCheck);
- if (definitelyFails) {
- return true;
- }
- for (InterfaceType supertype in type.element.allSupertypes) {
- DartType instance = type.asInstanceOf(supertype.element);
- compiler.types.checkTypeVariableBounds(instance,
- addTypeVariableBoundCheck);
- if (definitelyFails) {
- return true;
- }
- }
- return false;
- }
-
- visitAssert(node) {
- if (!compiler.enableUserAssertions) {
- stack.add(graph.addConstantNull(compiler));
- return;
- }
- // TODO(johnniwinther): Don't handle assert like a regular static call.
- // It breaks the selector name check since the assert helper method cannot
- // be called `assert` and therefore does not match the selector like a
- // regular method.
- visitStaticSend(node);
- }
-
- visitStaticSend(ast.Send node) {
- Selector selector = elements.getSelector(node);
- Element element = elements[node];
- if (elements.isAssert(node)) {
- element = backend.assertMethod;
- }
- if (element.isForeign(backend) && element.isFunction) {
- visitForeignSend(node);
- return;
- }
- if (element.isErroneous) {
- // An erroneous element indicates that the funciton could not be resolved
- // (a warning has been issued).
- generateThrowNoSuchMethod(node,
- noSuchMethodTargetSymbolString(element),
- argumentNodes: node.arguments);
- return;
- }
- invariant(element, !element.isGenerativeConstructor);
- generateIsDeferredLoadedCheckIfNeeded(node);
- if (element.isFunction) {
- var inputs = <HInstruction>[];
- // TODO(5347): Try to avoid the need for calling [implementation] before
- // calling [addStaticSendArgumentsToList].
- bool succeeded = addStaticSendArgumentsToList(selector, node.arguments,
- element.implementation,
- inputs);
- if (!succeeded) {
- generateWrongArgumentCountError(node, element, node.arguments);
- return;
- }
-
- if (element == compiler.identicalFunction) {
- pushWithPosition(
- new HIdentity(inputs[0], inputs[1], null, backend.boolType), node);
- return;
- }
-
- pushInvokeStatic(node, element, inputs);
- } else {
- generateGetter(node, element);
- List<HInstruction> inputs = <HInstruction>[pop()];
- addDynamicSendArgumentsToList(node, inputs);
- Selector closureSelector = new Selector.callClosureFrom(selector);
- pushWithPosition(
- new HInvokeClosure(closureSelector, inputs, backend.dynamicType),
- node);
- }
- }
-
- HConstant addConstantString(String string) {
- ast.DartString dartString = new ast.DartString.literal(string);
- ConstantValue constant = constantSystem.createString(dartString);
- return graph.addConstant(constant, compiler);
- }
-
- visitTypePrefixSend(ast.Send node) {
- compiler.internalError(node, "visitTypePrefixSend should not be called.");
- }
-
- visitTypeLiteralSend(ast.Send node) {
- DartType type = elements.getTypeLiteralType(node);
- if (type.isInterfaceType || type.isTypedef || type.isDynamic) {
- // TODO(karlklose): add type representation
- if (node.isCall) {
- // The node itself is not a constant but we register the selector (the
- // identifier that refers to the class/typedef) as a constant.
- stack.add(addConstant(node.selector));
- } else {
- stack.add(addConstant(node));
- }
- } else if (type.isTypeVariable) {
- type = localsHandler.substInContext(type);
- HInstruction value = analyzeTypeArgument(type);
- pushInvokeStatic(node,
- backend.getRuntimeTypeToString(),
- [value],
- backend.stringType);
- pushInvokeStatic(node,
- backend.getCreateRuntimeType(),
- [pop()]);
- } else {
- internalError('unexpected type kind ${type.kind}', node: node);
- }
- if (node.isCall) {
- // This send is of the form 'e(...)', where e is resolved to a type
- // reference. We create a regular closure call on the result of the type
- // reference instead of creating a NoSuchMethodError to avoid pulling it
- // in if it is not used (e.g., in a try/catch).
- HInstruction target = pop();
- Selector selector = elements.getSelector(node);
- List<HInstruction> inputs = <HInstruction>[target];
- addDynamicSendArgumentsToList(node, inputs);
- Selector closureSelector = new Selector.callClosureFrom(selector);
- push(new HInvokeClosure(closureSelector, inputs, backend.dynamicType));
- }
- }
-
- visitGetterSend(ast.Send node) {
- generateIsDeferredLoadedCheckIfNeeded(node);
- generateGetter(node, elements[node]);
- }
-
- // TODO(antonm): migrate rest of SsaFromAstMixin to internalError.
- internalError(String reason, {ast.Node node}) {
- compiler.internalError(node, reason);
- }
-
- void generateError(ast.Node node, String message, Element helper) {
- HInstruction errorMessage = addConstantString(message);
- pushInvokeStatic(node, helper, [errorMessage]);
- }
-
- void generateRuntimeError(ast.Node node, String message) {
- generateError(node, message, backend.getThrowRuntimeError());
- }
-
- void generateTypeError(ast.Node node, String message) {
- generateError(node, message, backend.getThrowTypeError());
- }
-
- void generateAbstractClassInstantiationError(ast.Node node, String message) {
- generateError(node,
- message,
- backend.getThrowAbstractClassInstantiationError());
- }
-
- void generateThrowNoSuchMethod(ast.Node diagnosticNode,
- String methodName,
- {Link<ast.Node> argumentNodes,
- List<HInstruction> argumentValues,
- List<String> existingArguments}) {
- Element helper = backend.getThrowNoSuchMethod();
- ConstantValue receiverConstant =
- constantSystem.createString(new ast.DartString.empty());
- HInstruction receiver = graph.addConstant(receiverConstant, compiler);
- ast.DartString dartString = new ast.DartString.literal(methodName);
- ConstantValue nameConstant = constantSystem.createString(dartString);
- HInstruction name = graph.addConstant(nameConstant, compiler);
- if (argumentValues == null) {
- argumentValues = <HInstruction>[];
- argumentNodes.forEach((argumentNode) {
- visit(argumentNode);
- HInstruction value = pop();
- argumentValues.add(value);
- });
- }
- HInstruction arguments = buildLiteralList(argumentValues);
- add(arguments);
- HInstruction existingNamesList;
- if (existingArguments != null) {
- List<HInstruction> existingNames = <HInstruction>[];
- for (String name in existingArguments) {
- HInstruction nameConstant =
- graph.addConstantString(new ast.DartString.literal(name), compiler);
- existingNames.add(nameConstant);
- }
- existingNamesList = buildLiteralList(existingNames);
- add(existingNamesList);
- } else {
- existingNamesList = graph.addConstantNull(compiler);
- }
- pushInvokeStatic(diagnosticNode,
- helper,
- [receiver, name, arguments, existingNamesList]);
- }
-
- /**
- * Generate code to throw a [NoSuchMethodError] exception for calling a
- * method with a wrong number of arguments or mismatching named optional
- * arguments.
- */
- void generateWrongArgumentCountError(ast.Node diagnosticNode,
- FunctionElement function,
- Link<ast.Node> argumentNodes) {
- List<String> existingArguments = <String>[];
- FunctionSignature signature = function.functionSignature;
- signature.forEachParameter((Element parameter) {
- existingArguments.add(parameter.name);
- });
- generateThrowNoSuchMethod(diagnosticNode,
- function.name,
- argumentNodes: argumentNodes,
- existingArguments: existingArguments);
- }
-
- visitNewExpression(ast.NewExpression node) {
- Element element = elements[node.send];
- final bool isSymbolConstructor = element == compiler.symbolConstructor;
- if (!Elements.isErroneousElement(element)) {
- ConstructorElement function = element;
- element = function.effectiveTarget;
- }
- if (Elements.isErroneousElement(element)) {
- ErroneousElement error = element;
- if (error.messageKind == MessageKind.CANNOT_FIND_CONSTRUCTOR) {
- generateThrowNoSuchMethod(
- node.send,
- noSuchMethodTargetSymbolString(error, 'constructor'),
- argumentNodes: node.send.arguments);
- } else {
- Message message = error.messageKind.message(error.messageArguments);
- generateRuntimeError(node.send, message.toString());
- }
- } else if (node.isConst) {
- stack.add(addConstant(node));
- if (isSymbolConstructor) {
- ConstructedConstantValue symbol = getConstantForNode(node);
- StringConstantValue stringConstant = symbol.fields.single;
- String nameString = stringConstant.toDartString().slowToString();
- registry.registerConstSymbol(nameString);
- }
- } else {
- handleNewSend(node);
- }
- }
-
- void pushInvokeDynamic(ast.Node node,
- Selector selector,
- List<HInstruction> arguments,
- {ast.Node location}) {
- if (location == null) location = node;
-
- // We prefer to not inline certain operations on indexables,
- // because the constant folder will handle them better and turn
- // them into simpler instructions that allow further
- // optimizations.
- bool isOptimizableOperationOnIndexable(Selector selector, Element element) {
- bool isLength = selector.isGetter
- && selector.name == "length";
- if (isLength || selector.isIndex) {
- TypeMask type = new TypeMask.nonNullExact(
- element.enclosingClass.declaration, compiler.world);
- return type.satisfies(backend.jsIndexableClass, compiler.world);
- } else if (selector.isIndexSet) {
- TypeMask type = new TypeMask.nonNullExact(
- element.enclosingClass.declaration, compiler.world);
- return type.satisfies(backend.jsMutableIndexableClass, compiler.world);
- } else {
- return false;
- }
- }
-
- bool isOptimizableOperation(Selector selector, Element element) {
- ClassElement cls = element.enclosingClass;
- if (isOptimizableOperationOnIndexable(selector, element)) return true;
- if (!backend.interceptedClasses.contains(cls)) return false;
- if (selector.isOperator) return true;
- if (selector.isSetter) return true;
- if (selector.isIndex) return true;
- if (selector.isIndexSet) return true;
- if (element == backend.jsArrayAdd
- || element == backend.jsArrayRemoveLast
- || element == backend.jsStringSplit) {
- return true;
- }
- return false;
- }
-
- Element element = compiler.world.locateSingleElement(selector);
- if (element != null
- && !element.isField
- && !(element.isGetter && selector.isCall)
- && !(element.isFunction && selector.isGetter)
- && !isOptimizableOperation(selector, element)) {
- if (tryInlineMethod(element, selector, arguments, node)) {
- return;
- }
- }
-
- HInstruction receiver = arguments[0];
- List<HInstruction> inputs = <HInstruction>[];
- bool isIntercepted = backend.isInterceptedSelector(selector);
- if (isIntercepted) {
- inputs.add(invokeInterceptor(receiver));
- }
- inputs.addAll(arguments);
- TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler);
- if (selector.isGetter) {
- pushWithPosition(
- new HInvokeDynamicGetter(selector, null, inputs, type),
- location);
- } else if (selector.isSetter) {
- pushWithPosition(
- new HInvokeDynamicSetter(selector, null, inputs, type),
- location);
- } else {
- pushWithPosition(
- new HInvokeDynamicMethod(selector, inputs, type, isIntercepted),
- location);
- }
- }
-
- void pushInvokeStatic(ast.Node location,
- Element element,
- List<HInstruction> arguments,
- [TypeMask type]) {
- if (tryInlineMethod(element, null, arguments, location)) {
- return;
- }
-
- if (type == null) {
- type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler);
- }
- bool targetCanThrow = !compiler.world.getCannotThrow(element);
- // TODO(5346): Try to avoid the need for calling [declaration] before
- // creating an [HInvokeStatic].
- HInvokeStatic instruction = new HInvokeStatic(
- element.declaration, arguments, type, targetCanThrow: targetCanThrow);
- if (!currentInlinedInstantiations.isEmpty) {
- instruction.instantiatedTypes = new List<DartType>.from(
- currentInlinedInstantiations);
- }
- instruction.sideEffects = compiler.world.getSideEffectsOfElement(element);
- if (location == null) {
- push(instruction);
- } else {
- pushWithPosition(instruction, location);
- }
- }
-
- HInstruction buildInvokeSuper(Selector selector,
- Element element,
- List<HInstruction> arguments) {
- HInstruction receiver = localsHandler.readThis();
- // TODO(5346): Try to avoid the need for calling [declaration] before
- // creating an [HStatic].
- List<HInstruction> inputs = <HInstruction>[];
- if (backend.isInterceptedSelector(selector) &&
- // Fields don't need an interceptor; consider generating HFieldGet/Set
- // instead.
- element.kind != ElementKind.FIELD) {
- inputs.add(invokeInterceptor(receiver));
- }
- inputs.add(receiver);
- inputs.addAll(arguments);
- TypeMask type;
- if (!element.isGetter && selector.isGetter) {
- type = TypeMaskFactory.inferredTypeForElement(element, compiler);
- } else {
- type = TypeMaskFactory.inferredReturnTypeForElement(element, compiler);
- }
- HInstruction instruction = new HInvokeSuper(
- element,
- currentNonClosureClass,
- selector,
- inputs,
- type,
- isSetter: selector.isSetter || selector.isIndexSet);
- instruction.sideEffects = compiler.world.getSideEffectsOfSelector(selector);
- return instruction;
- }
-
- void handleComplexOperatorSend(ast.SendSet node,
- HInstruction receiver,
- Link<ast.Node> arguments) {
- HInstruction rhs;
- if (node.isPrefix || node.isPostfix) {
- rhs = graph.addConstantInt(1, compiler);
- } else {
- visit(arguments.head);
- assert(arguments.tail.isEmpty);
- rhs = pop();
- }
- visitBinary(receiver, node.assignmentOperator, rhs,
- elements.getOperatorSelectorInComplexSendSet(node), node);
- }
-
- visitSendSet(ast.SendSet node) {
- generateIsDeferredLoadedCheckIfNeeded(node);
- Element element = elements[node];
- if (!Elements.isUnresolved(element) && element.impliesType) {
- ast.Identifier selector = node.selector;
- generateThrowNoSuchMethod(node, selector.source,
- argumentNodes: node.arguments);
- return;
- }
- ast.Operator op = node.assignmentOperator;
- if (node.isSuperCall) {
- HInstruction result;
- List<HInstruction> setterInputs = <HInstruction>[];
- if (identical(node.assignmentOperator.source, '=')) {
- addDynamicSendArgumentsToList(node, setterInputs);
- result = setterInputs.last;
- } else {
- Element getter = elements[node.selector];
- List<HInstruction> getterInputs = <HInstruction>[];
- Link<ast.Node> arguments = node.arguments;
- if (node.isIndex) {
- // If node is of the from [:super.foo[0] += 2:], the send has
- // two arguments: the index and the left hand side. We get
- // the index and add it as input of the getter and the
- // setter.
- visit(arguments.head);
- arguments = arguments.tail;
- HInstruction index = pop();
- getterInputs.add(index);
- setterInputs.add(index);
- }
- HInstruction getterInstruction;
- Selector getterSelector =
- elements.getGetterSelectorInComplexSendSet(node);
- if (Elements.isUnresolved(getter)) {
- generateSuperNoSuchMethodSend(
- node,
- getterSelector,
- getterInputs);
- getterInstruction = pop();
- } else {
- getterInstruction = buildInvokeSuper(
- getterSelector, getter, getterInputs);
- add(getterInstruction);
- }
- handleComplexOperatorSend(node, getterInstruction, arguments);
- setterInputs.add(pop());
-
- if (node.isPostfix) {
- result = getterInstruction;
- } else {
- result = setterInputs.last;
- }
- }
- Selector setterSelector = elements.getSelector(node);
- if (Elements.isUnresolved(element)
- || !setterSelector.applies(element, compiler.world)) {
- generateSuperNoSuchMethodSend(
- node, setterSelector, setterInputs);
- pop();
- } else {
- add(buildInvokeSuper(setterSelector, element, setterInputs));
- }
- stack.add(result);
- } else if (node.isIndex) {
- if ("=" == op.source) {
- visitDynamicSend(node);
- } else {
- visit(node.receiver);
- HInstruction receiver = pop();
- Link<ast.Node> arguments = node.arguments;
- HInstruction index;
- if (node.isIndex) {
- visit(arguments.head);
- arguments = arguments.tail;
- index = pop();
- }
-
- pushInvokeDynamic(
- node,
- elements.getGetterSelectorInComplexSendSet(node),
- [receiver, index]);
- HInstruction getterInstruction = pop();
-
- handleComplexOperatorSend(node, getterInstruction, arguments);
- HInstruction value = pop();
-
- pushInvokeDynamic(
- node, elements.getSelector(node), [receiver, index, value]);
- pop();
-
- if (node.isPostfix) {
- stack.add(getterInstruction);
- } else {
- stack.add(value);
- }
- }
- } else if ("=" == op.source) {
- Link<ast.Node> link = node.arguments;
- assert(!link.isEmpty && link.tail.isEmpty);
- if (Elements.isInstanceSend(node, elements)) {
- HInstruction receiver = generateInstanceSendReceiver(node);
- visit(link.head);
- generateInstanceSetterWithCompiledReceiver(node, receiver, pop());
- } else {
- visit(link.head);
- generateNonInstanceSetter(node, element, pop());
- }
- } else if (identical(op.source, "is")) {
- compiler.internalError(op, "is-operator as SendSet.");
- } else {
- assert("++" == op.source || "--" == op.source ||
- node.assignmentOperator.source.endsWith("="));
-
- // [receiver] is only used if the node is an instance send.
- HInstruction receiver = null;
- Element getter = elements[node.selector];
-
- if (!Elements.isUnresolved(getter) && getter.impliesType) {
- ast.Identifier selector = node.selector;
- generateThrowNoSuchMethod(node, selector.source,
- argumentNodes: node.arguments);
- return;
- } else if (Elements.isInstanceSend(node, elements)) {
- receiver = generateInstanceSendReceiver(node);
- generateInstanceGetterWithCompiledReceiver(
- node, elements.getGetterSelectorInComplexSendSet(node), receiver);
- } else {
- generateGetter(node, getter);
- }
- HInstruction getterInstruction = pop();
- handleComplexOperatorSend(node, getterInstruction, node.arguments);
- HInstruction value = pop();
- assert(value != null);
- if (Elements.isInstanceSend(node, elements)) {
- assert(receiver != null);
- generateInstanceSetterWithCompiledReceiver(node, receiver, value);
- } else {
- assert(receiver == null);
- generateNonInstanceSetter(node, element, value);
- }
- if (node.isPostfix) {
- pop();
- stack.add(getterInstruction);
- }
- }
- }
-
- void visitLiteralInt(ast.LiteralInt node) {
- stack.add(graph.addConstantInt(node.value, compiler));
- }
-
- void visitLiteralDouble(ast.LiteralDouble node) {
- stack.add(graph.addConstantDouble(node.value, compiler));
- }
-
- void visitLiteralBool(ast.LiteralBool node) {
- stack.add(graph.addConstantBool(node.value, compiler));
- }
-
- void visitLiteralString(ast.LiteralString node) {
- stack.add(graph.addConstantString(node.dartString, compiler));
- }
-
- void visitLiteralSymbol(ast.LiteralSymbol node) {
- stack.add(addConstant(node));
- registry.registerConstSymbol(node.slowNameString);
- }
-
- void visitStringJuxtaposition(ast.StringJuxtaposition node) {
- if (!node.isInterpolation) {
- // This is a simple string with no interpolations.
- stack.add(graph.addConstantString(node.dartString, compiler));
- return;
- }
- StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node);
- stringBuilder.visit(node);
- stack.add(stringBuilder.result);
- }
-
- void visitLiteralNull(ast.LiteralNull node) {
- stack.add(graph.addConstantNull(compiler));
- }
-
- visitNodeList(ast.NodeList node) {
- for (Link<ast.Node> link = node.nodes; !link.isEmpty; link = link.tail) {
- if (isAborted()) {
- compiler.reportWarning(link.head,
- MessageKind.GENERIC, {'text': 'dead code'});
- } else {
- visit(link.head);
- }
- }
- }
-
- void visitParenthesizedExpression(ast.ParenthesizedExpression node) {
- visit(node.expression);
- }
-
- visitOperator(ast.Operator node) {
- // Operators are intercepted in their surrounding Send nodes.
- compiler.internalError(node,
- 'SsaBuilder.visitOperator should not be called.');
- }
-
- visitCascade(ast.Cascade node) {
- visit(node.expression);
- // Remove the result and reveal the duplicated receiver on the stack.
- pop();
- }
-
- visitCascadeReceiver(ast.CascadeReceiver node) {
- visit(node.expression);
- dup();
- }
-
- void handleInTryStatement() {
- if (!inTryStatement) return;
- HBasicBlock block = close(new HExitTry());
- HBasicBlock newBlock = graph.addNewBlock();
- block.addSuccessor(newBlock);
- open(newBlock);
- }
-
- visitRethrow(ast.Rethrow node) {
- HInstruction exception = rethrowableException;
- if (exception == null) {
- exception = graph.addConstantNull(compiler);
- compiler.internalError(node,
- 'rethrowableException should not be null.');
- }
- handleInTryStatement();
- closeAndGotoExit(new HThrow(exception, isRethrow: true));
- }
-
- visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
- ConstructorElement targetConstructor =
- elements.getRedirectingTargetConstructor(node).implementation;
- ConstructorElement redirectingConstructor = sourceElement.implementation;
- List<HInstruction> inputs = <HInstruction>[];
- FunctionSignature targetSignature = targetConstructor.functionSignature;
- FunctionSignature redirectingSignature =
- redirectingConstructor.functionSignature;
- redirectingSignature.forEachRequiredParameter((ParameterElement element) {
- inputs.add(localsHandler.readLocal(element));
- });
- List<Element> targetOptionals =
- targetSignature.orderedOptionalParameters;
- List<Element> redirectingOptionals =
- redirectingSignature.orderedOptionalParameters;
- int i = 0;
- for (; i < redirectingOptionals.length; i++) {
- ParameterElement parameter = redirectingOptionals[i];
- inputs.add(localsHandler.readLocal(parameter));
- }
- for (; i < targetOptionals.length; i++) {
- inputs.add(handleConstantForOptionalParameter(targetOptionals[i]));
- }
- ClassElement targetClass = targetConstructor.enclosingClass;
- if (backend.classNeedsRti(targetClass)) {
- ClassElement cls = redirectingConstructor.enclosingClass;
- InterfaceType targetType =
- redirectingConstructor.computeEffectiveTargetType(cls.thisType);
- targetType = localsHandler.substInContext(targetType);
- targetType.typeArguments.forEach((DartType argument) {
- inputs.add(analyzeTypeArgument(argument));
- });
- }
- pushInvokeStatic(node, targetConstructor, inputs);
- HInstruction value = pop();
- emitReturn(value, node);
- }
-
- visitReturn(ast.Return node) {
- if (identical(node.beginToken.stringValue, 'native')) {
- native.handleSsaNative(this, node.expression);
- return;
- }
- HInstruction value;
- if (node.expression == null) {
- value = graph.addConstantNull(compiler);
- } else {
- visit(node.expression);
- value = pop();
- value = potentiallyCheckType(value, returnType);
- }
-
- handleInTryStatement();
- emitReturn(value, node);
- }
-
- visitThrow(ast.Throw node) {
- visitThrowExpression(node.expression);
- if (isReachable) {
- handleInTryStatement();
- push(new HThrowExpression(pop()));
- isReachable = false;
- }
- }
-
- visitYield(ast.Yield node) {
- // Dummy implementation.
- visit(node.expression);
- pop();
- }
-
- visitAwait(ast.Await node) {
- // Dummy implementation.
- visit(node.expression);
- }
-
- visitTypeAnnotation(ast.TypeAnnotation node) {
- compiler.internalError(node,
- 'Visiting type annotation in SSA builder.');
- }
-
- visitVariableDefinitions(ast.VariableDefinitions node) {
- assert(isReachable);
- for (Link<ast.Node> link = node.definitions.nodes;
- !link.isEmpty;
- link = link.tail) {
- ast.Node definition = link.head;
- if (definition is ast.Identifier) {
- HInstruction initialValue = graph.addConstantNull(compiler);
- LocalElement local = elements[definition];
- localsHandler.updateLocal(local, initialValue);
- } else {
- assert(definition is ast.SendSet);
- visitSendSet(definition);
- pop(); // Discard value.
- }
- }
- }
-
- HInstruction setRtiIfNeeded(HInstruction object, ast.Node node) {
- InterfaceType type = localsHandler.substInContext(elements.getType(node));
- if (!backend.classNeedsRti(type.element) || type.treatAsRaw) {
- return object;
- }
- List<HInstruction> arguments = <HInstruction>[];
- for (DartType argument in type.typeArguments) {
- arguments.add(analyzeTypeArgument(argument));
- }
- // TODO(15489): Register at codegen.
- registry.registerInstantiatedType(type);
- return callSetRuntimeTypeInfo(type.element, arguments, object);
- }
-
- visitLiteralList(ast.LiteralList node) {
- HInstruction instruction;
-
- if (node.isConst) {
- instruction = addConstant(node);
- } else {
- List<HInstruction> inputs = <HInstruction>[];
- for (Link<ast.Node> link = node.elements.nodes;
- !link.isEmpty;
- link = link.tail) {
- visit(link.head);
- inputs.add(pop());
- }
- instruction = buildLiteralList(inputs);
- add(instruction);
- instruction = setRtiIfNeeded(instruction, node);
- }
-
- TypeMask type =
- TypeMaskFactory.inferredForNode(sourceElement, node, compiler);
- if (!type.containsAll(compiler.world)) instruction.instructionType = type;
- stack.add(instruction);
- }
-
- visitConditional(ast.Conditional node) {
- SsaBranchBuilder brancher = new SsaBranchBuilder(this, node);
- brancher.handleConditional(() => visit(node.condition),
- () => visit(node.thenExpression),
- () => visit(node.elseExpression));
- }
-
- visitStringInterpolation(ast.StringInterpolation node) {
- StringBuilderVisitor stringBuilder = new StringBuilderVisitor(this, node);
- stringBuilder.visit(node);
- stack.add(stringBuilder.result);
- }
-
- visitStringInterpolationPart(ast.StringInterpolationPart node) {
- // The parts are iterated in visitStringInterpolation.
- compiler.internalError(node,
- 'SsaBuilder.visitStringInterpolation should not be called.');
- }
-
- visitEmptyStatement(ast.EmptyStatement node) {
- // Do nothing, empty statement.
- }
-
- visitModifiers(ast.Modifiers node) {
- compiler.unimplemented(node, 'SsaFromAstMixin.visitModifiers.');
- }
-
- visitBreakStatement(ast.BreakStatement node) {
- assert(!isAborted());
- handleInTryStatement();
- JumpTarget target = elements.getTargetOf(node);
- assert(target != null);
- JumpHandler handler = jumpTargets[target];
- assert(handler != null);
- if (node.target == null) {
- handler.generateBreak();
- } else {
- LabelDefinition label = elements.getTargetLabel(node);
- handler.generateBreak(label);
- }
- }
-
- visitContinueStatement(ast.ContinueStatement node) {
- handleInTryStatement();
- JumpTarget target = elements.getTargetOf(node);
- assert(target != null);
- JumpHandler handler = jumpTargets[target];
- assert(handler != null);
- if (node.target == null) {
- handler.generateContinue();
- } else {
- LabelDefinition label = elements.getTargetLabel(node);
- assert(label != null);
- handler.generateContinue(label);
- }
- }
-
- /**
- * Creates a [JumpHandler] for a statement. The node must be a jump
- * target. If there are no breaks or continues targeting the statement,
- * a special "null handler" is returned.
- *
- * [isLoopJump] is [:true:] when the jump handler is for a loop. This is used
- * to distinguish the synthetized loop created for a switch statement with
- * continue statements from simple switch statements.
- */
- JumpHandler createJumpHandler(ast.Statement node, {bool isLoopJump}) {
- JumpTarget element = elements.getTargetDefinition(node);
- if (element == null || !identical(element.statement, node)) {
- // No breaks or continues to this node.
- return new NullJumpHandler(compiler);
- }
- if (isLoopJump && node is ast.SwitchStatement) {
- // Create a special jump handler for loops created for switch statements
- // with continue statements.
- return new SwitchCaseJumpHandler(this, element, node);
- }
- return new JumpHandler(this, element);
- }
-
- visitForIn(ast.ForIn node) {
- // Generate a structure equivalent to:
- // Iterator<E> $iter = <iterable>.iterator;
- // while ($iter.moveNext()) {
- // E <declaredIdentifier> = $iter.current;
- // <body>
- // }
-
- // The iterator is shared between initializer, condition and body.
- HInstruction iterator;
- void buildInitializer() {
- Selector selector = elements.getIteratorSelector(node);
- visit(node.expression);
- HInstruction receiver = pop();
- pushInvokeDynamic(node, selector, [receiver]);
- iterator = pop();
- }
- HInstruction buildCondition() {
- Selector selector = elements.getMoveNextSelector(node);
- pushInvokeDynamic(node, selector, [iterator]);
- return popBoolified();
- }
- void buildBody() {
- Selector call = elements.getCurrentSelector(node);
- pushInvokeDynamic(node, call, [iterator]);
-
- ast.Node identifier = node.declaredIdentifier;
- Element variable = elements.getForInVariable(node);
- Selector selector = elements.getSelector(identifier);
-
- HInstruction value = pop();
- if (identifier.asSend() != null
- && Elements.isInstanceSend(identifier, elements)) {
- HInstruction receiver = generateInstanceSendReceiver(identifier);
- assert(receiver != null);
- generateInstanceSetterWithCompiledReceiver(
- null,
- receiver,
- value,
- selector: selector,
- location: identifier);
- } else {
- generateNonInstanceSetter(null, variable, value, location: identifier);
- }
- pop(); // Pop the value pushed by the setter call.
-
- visit(node.body);
- }
- handleLoop(node, buildInitializer, buildCondition, () {}, buildBody);
- }
-
- visitLabel(ast.Label node) {
- compiler.internalError(node, 'SsaFromAstMixin.visitLabel.');
- }
-
- visitLabeledStatement(ast.LabeledStatement node) {
- ast.Statement body = node.statement;
- if (body is ast.Loop
- || body is ast.SwitchStatement
- || Elements.isUnusedLabel(node, elements)) {
- // Loops and switches handle their own labels.
- visit(body);
- return;
- }
- JumpTarget targetElement = elements.getTargetDefinition(body);
- LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler);
- assert(targetElement.isBreakTarget);
- JumpHandler handler = new JumpHandler(this, targetElement);
- // Introduce a new basic block.
- HBasicBlock entryBlock = openNewBlock();
- visit(body);
- SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock);
-
- HBasicBlock joinBlock = graph.addNewBlock();
- List<LocalsHandler> breakHandlers = <LocalsHandler>[];
- handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
- breakInstruction.block.addSuccessor(joinBlock);
- breakHandlers.add(locals);
- });
- bool hasBreak = breakHandlers.length > 0;
- if (!isAborted()) {
- goto(current, joinBlock);
- breakHandlers.add(localsHandler);
- }
- open(joinBlock);
- localsHandler = beforeLocals.mergeMultiple(breakHandlers, joinBlock);
-
- if (hasBreak) {
- // There was at least one reachable break, so the label is needed.
- entryBlock.setBlockFlow(
- new HLabeledBlockInformation(new HSubGraphBlockInformation(bodyGraph),
- handler.labels()),
- joinBlock);
- }
- handler.close();
- }
-
- visitLiteralMap(ast.LiteralMap node) {
- if (node.isConst) {
- stack.add(addConstant(node));
- return;
- }
- List<HInstruction> listInputs = <HInstruction>[];
- for (Link<ast.Node> link = node.entries.nodes;
- !link.isEmpty;
- link = link.tail) {
- visit(link.head);
- listInputs.add(pop());
- listInputs.add(pop());
- }
-
- ConstructorElement constructor;
- List<HInstruction> inputs = <HInstruction>[];
-
- if (listInputs.isEmpty) {
- constructor = backend.mapLiteralConstructorEmpty;
- } else {
- constructor = backend.mapLiteralConstructor;
- HLiteralList keyValuePairs = buildLiteralList(listInputs);
- add(keyValuePairs);
- inputs.add(keyValuePairs);
- }
-
- assert(constructor.isFactoryConstructor);
-
- ConstructorElement functionElement = constructor;
- constructor = functionElement.effectiveTarget;
-
- InterfaceType type = elements.getType(node);
- InterfaceType expectedType =
- functionElement.computeEffectiveTargetType(type);
- expectedType = localsHandler.substInContext(expectedType);
-
- if (constructor.isFactoryConstructor) {
- registry.registerFactoryWithTypeArguments();
- }
-
- ClassElement cls = constructor.enclosingClass;
-
- if (backend.classNeedsRti(cls)) {
- List<DartType> typeVariable = cls.typeVariables;
- expectedType.typeArguments.forEach((DartType argument) {
- inputs.add(analyzeTypeArgument(argument));
- });
- }
-
- // The instruction type will always be a subtype of the mapLiteralClass, but
- // type inference might discover a more specific type, or find nothing (in
- // dart2js unit tests).
- TypeMask mapType =
- new TypeMask.nonNullSubtype(backend.mapLiteralClass, compiler.world);
- TypeMask returnTypeMask = TypeMaskFactory.inferredReturnTypeForElement(
- constructor, compiler);
- TypeMask instructionType =
- mapType.intersection(returnTypeMask, compiler.world);
-
- addInlinedInstantiation(expectedType);
- pushInvokeStatic(node, constructor, inputs, instructionType);
- removeInlinedInstantiation(expectedType);
- }
-
- visitLiteralMapEntry(ast.LiteralMapEntry node) {
- visit(node.value);
- visit(node.key);
- }
-
- visitNamedArgument(ast.NamedArgument node) {
- visit(node.expression);
- }
-
- Map<ast.CaseMatch, ConstantValue> buildSwitchCaseConstants(
- ast.SwitchStatement node) {
-
- Map<ast.CaseMatch, ConstantValue> constants =
- new Map<ast.CaseMatch, ConstantValue>();
- for (ast.SwitchCase switchCase in node.cases) {
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- if (labelOrCase is ast.CaseMatch) {
- ast.CaseMatch match = labelOrCase;
- ConstantValue constant = getConstantForNode(match.expression);
- constants[labelOrCase] = constant;
- }
- }
- }
- return constants;
- }
-
- visitSwitchStatement(ast.SwitchStatement node) {
- Map<ast.CaseMatch, ConstantValue> constants =
- buildSwitchCaseConstants(node);
-
- // The switch case indices must match those computed in
- // [SwitchCaseJumpHandler].
- bool hasContinue = false;
- Map<ast.SwitchCase, int> caseIndex = new Map<ast.SwitchCase, int>();
- int switchIndex = 1;
- bool hasDefault = false;
- for (ast.SwitchCase switchCase in node.cases) {
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- ast.Node label = labelOrCase.asLabel();
- if (label != null) {
- LabelDefinition labelElement = elements.getLabelDefinition(label);
- if (labelElement != null && labelElement.isContinueTarget) {
- hasContinue = true;
- }
- }
- }
- if (switchCase.isDefaultCase) {
- hasDefault = true;
- }
- caseIndex[switchCase] = switchIndex;
- switchIndex++;
- }
- if (!hasContinue) {
- // If the switch statement has no switch cases targeted by continue
- // statements we encode the switch statement directly.
- buildSimpleSwitchStatement(node, constants);
- } else {
- buildComplexSwitchStatement(node, constants, caseIndex, hasDefault);
- }
- }
-
- /**
- * Builds a simple switch statement which does not handle uses of continue
- * statements to labeled switch cases.
- */
- void buildSimpleSwitchStatement(ast.SwitchStatement node,
- Map<ast.CaseMatch, ConstantValue> constants) {
- JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
- HInstruction buildExpression() {
- visit(node.expression);
- return pop();
- }
- Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) {
- List<ConstantValue> constantList = <ConstantValue>[];
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- if (labelOrCase is ast.CaseMatch) {
- constantList.add(constants[labelOrCase]);
- }
- }
- return constantList;
- }
- bool isDefaultCase(ast.SwitchCase switchCase) {
- return switchCase.isDefaultCase;
- }
- void buildSwitchCase(ast.SwitchCase node) {
- visit(node.statements);
- }
- handleSwitch(node,
- jumpHandler,
- buildExpression,
- node.cases,
- getConstants,
- isDefaultCase,
- buildSwitchCase);
- jumpHandler.close();
- }
-
- /**
- * Builds a switch statement that can handle arbitrary uses of continue
- * statements to labeled switch cases.
- */
- void buildComplexSwitchStatement(ast.SwitchStatement node,
- Map<ast.CaseMatch, ConstantValue> constants,
- Map<ast.SwitchCase, int> caseIndex,
- bool hasDefault) {
- // If the switch statement has switch cases targeted by continue
- // statements we create the following encoding:
- //
- // switch (e) {
- // l_1: case e0: s_1; break;
- // l_2: case e1: s_2; continue l_i;
- // ...
- // l_n: default: s_n; continue l_j;
- // }
- //
- // is encoded as
- //
- // var target;
- // switch (e) {
- // case e1: target = 1; break;
- // case e2: target = 2; break;
- // ...
- // default: target = n; break;
- // }
- // l: while (true) {
- // switch (target) {
- // case 1: s_1; break l;
- // case 2: s_2; target = i; continue l;
- // ...
- // case n: s_n; target = j; continue l;
- // }
- // }
-
- JumpTarget switchTarget = elements.getTargetDefinition(node);
- HInstruction initialValue = graph.addConstantNull(compiler);
- localsHandler.updateLocal(switchTarget, initialValue);
-
- JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
- var switchCases = node.cases;
- if (!hasDefault) {
- // Use [:null:] as the marker for a synthetic default clause.
- // The synthetic default is added because otherwise, there would be no
- // good place to give a default value to the local.
- switchCases = node.cases.nodes.toList()..add(null);
- }
- HInstruction buildExpression() {
- visit(node.expression);
- return pop();
- }
- Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) {
- List<ConstantValue> constantList = <ConstantValue>[];
- if (switchCase != null) {
- for (ast.Node labelOrCase in switchCase.labelsAndCases) {
- if (labelOrCase is ast.CaseMatch) {
- constantList.add(constants[labelOrCase]);
- }
- }
- }
- return constantList;
- }
- bool isDefaultCase(ast.SwitchCase switchCase) {
- return switchCase == null || switchCase.isDefaultCase;
- }
- void buildSwitchCase(ast.SwitchCase switchCase) {
- if (switchCase != null) {
- // Generate 'target = i; break;' for switch case i.
- int index = caseIndex[switchCase];
- HInstruction value = graph.addConstantInt(index, compiler);
- localsHandler.updateLocal(switchTarget, value);
- } else {
- // Generate synthetic default case 'target = null; break;'.
- HInstruction value = graph.addConstantNull(compiler);
- localsHandler.updateLocal(switchTarget, value);
- }
- jumpTargets[switchTarget].generateBreak();
- }
- handleSwitch(node,
- jumpHandler,
- buildExpression,
- switchCases,
- getConstants,
- isDefaultCase,
- buildSwitchCase);
- jumpHandler.close();
-
- HInstruction buildCondition() =>
- graph.addConstantBool(true, compiler);
-
- void buildSwitch() {
- HInstruction buildExpression() {
- return localsHandler.readLocal(switchTarget);
- }
- Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase) {
- return <ConstantValue>[constantSystem.createInt(caseIndex[switchCase])];
- }
- void buildSwitchCase(ast.SwitchCase switchCase) {
- visit(switchCase.statements);
- if (!isAborted()) {
- // Ensure that we break the loop if the case falls through. (This
- // is only possible for the last case.)
- jumpTargets[switchTarget].generateBreak();
- }
- }
- // Pass a [NullJumpHandler] because the target for the contained break
- // is not the generated switch statement but instead the loop generated
- // in the call to [handleLoop] below.
- handleSwitch(node,
- new NullJumpHandler(compiler),
- buildExpression, node.cases, getConstants,
- (_) => false, // No case is default.
- buildSwitchCase);
- }
-
- void buildLoop() {
- handleLoop(node,
- () {},
- buildCondition,
- () {},
- buildSwitch);
- }
-
- if (hasDefault) {
- buildLoop();
- } else {
- // If the switch statement has no default case, surround the loop with
- // a test of the target.
- void buildCondition() {
- js.Template code = js.js.parseForeignJS('#');
- push(createForeign(code,
- backend.boolType,
- [localsHandler.readLocal(switchTarget)]));
- }
- handleIf(node, buildCondition, buildLoop, () => {});
- }
- }
-
- /**
- * Creates a switch statement.
- *
- * [jumpHandler] is the [JumpHandler] for the created switch statement.
- * [buildExpression] creates the switch expression.
- * [switchCases] must be either an [Iterable] of [ast.SwitchCase] nodes or
- * a [Link] or a [ast.NodeList] of [ast.SwitchCase] nodes.
- * [getConstants] returns the set of constants for a switch case.
- * [isDefaultCase] returns [:true:] if the provided switch case should be
- * considered default for the created switch statement.
- * [buildSwitchCase] creates the statements for the switch case.
- */
- void handleSwitch(
- ast.Node errorNode,
- JumpHandler jumpHandler,
- HInstruction buildExpression(),
- var switchCases,
- Iterable<ConstantValue> getConstants(ast.SwitchCase switchCase),
- bool isDefaultCase(ast.SwitchCase switchCase),
- void buildSwitchCase(ast.SwitchCase switchCase)) {
-
- Map<ast.CaseMatch, ConstantValue> constants =
- new Map<ast.CaseMatch, ConstantValue>();
-
- HBasicBlock expressionStart = openNewBlock();
- HInstruction expression = buildExpression();
- if (switchCases.isEmpty) {
- return;
- }
-
- HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]);
- HBasicBlock expressionEnd = close(switchInstruction);
- LocalsHandler savedLocals = localsHandler;
-
- List<HStatementInformation> statements = <HStatementInformation>[];
- bool hasDefault = false;
- Element getFallThroughErrorElement = backend.getFallThroughError();
- HasNextIterator<ast.Node> caseIterator =
- new HasNextIterator<ast.Node>(switchCases.iterator);
- while (caseIterator.hasNext) {
- ast.SwitchCase switchCase = caseIterator.next();
- HBasicBlock block = graph.addNewBlock();
- for (ConstantValue constant in getConstants(switchCase)) {
- HConstant hConstant = graph.addConstant(constant, compiler);
- switchInstruction.inputs.add(hConstant);
- hConstant.usedBy.add(switchInstruction);
- expressionEnd.addSuccessor(block);
- }
-
- if (isDefaultCase(switchCase)) {
- // An HSwitch has n inputs and n+1 successors, the last being the
- // default case.
- expressionEnd.addSuccessor(block);
- hasDefault = true;
- }
- open(block);
- localsHandler = new LocalsHandler.from(savedLocals);
- buildSwitchCase(switchCase);
- if (!isAborted()) {
- if (caseIterator.hasNext) {
- pushInvokeStatic(switchCase, getFallThroughErrorElement, []);
- HInstruction error = pop();
- closeAndGotoExit(new HThrow(error));
- } else if (!isDefaultCase(switchCase)) {
- // If there is no default, we will add one later to avoid
- // the critical edge. So we generate a break statement to make
- // sure the last case does not fall through to the default case.
- jumpHandler.generateBreak();
- }
- }
- statements.add(
- new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock)));
- }
-
- // Add a join-block if necessary.
- // We create [joinBlock] early, and then go through the cases that might
- // want to jump to it. In each case, if we add [joinBlock] as a successor
- // of another block, we also add an element to [caseHandlers] that is used
- // to create the phis in [joinBlock].
- // If we never jump to the join block, [caseHandlers] will stay empty, and
- // the join block is never added to the graph.
- HBasicBlock joinBlock = new HBasicBlock();
- List<LocalsHandler> caseHandlers = <LocalsHandler>[];
- jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
- instruction.block.addSuccessor(joinBlock);
- caseHandlers.add(locals);
- });
- jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
- assert(invariant(errorNode, false,
- message: 'Continue cannot target a switch.'));
- });
- if (!isAborted()) {
- current.close(new HGoto());
- lastOpenedBlock.addSuccessor(joinBlock);
- caseHandlers.add(localsHandler);
- }
- if (!hasDefault) {
- // Always create a default case, to avoid a critical edge in the
- // graph.
- HBasicBlock defaultCase = addNewBlock();
- expressionEnd.addSuccessor(defaultCase);
- open(defaultCase);
- close(new HGoto());
- defaultCase.addSuccessor(joinBlock);
- caseHandlers.add(savedLocals);
- statements.add(new HSubGraphBlockInformation(new SubGraph(
- defaultCase, defaultCase)));
- }
- assert(caseHandlers.length == joinBlock.predecessors.length);
- if (caseHandlers.length != 0) {
- graph.addBlock(joinBlock);
- open(joinBlock);
- if (caseHandlers.length == 1) {
- localsHandler = caseHandlers[0];
- } else {
- localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock);
- }
- } else {
- // The joinblock is not used.
- joinBlock = null;
- }
-
- HSubExpressionBlockInformation expressionInfo =
- new HSubExpressionBlockInformation(new SubExpression(expressionStart,
- expressionEnd));
- expressionStart.setBlockFlow(
- new HSwitchBlockInformation(expressionInfo,
- statements,
- jumpHandler.target,
- jumpHandler.labels()),
- joinBlock);
-
- jumpHandler.close();
- }
-
- visitSwitchCase(ast.SwitchCase node) {
- compiler.internalError(node, 'SsaFromAstMixin.visitSwitchCase.');
- }
-
- visitCaseMatch(ast.CaseMatch node) {
- compiler.internalError(node, 'SsaFromAstMixin.visitCaseMatch.');
- }
-
- visitTryStatement(ast.TryStatement node) {
- // Save the current locals. The catch block and the finally block
- // must not reuse the existing locals handler. None of the variables
- // that have been defined in the body-block will be used, but for
- // loops we will add (unnecessary) phis that will reference the body
- // variables. This makes it look as if the variables were used
- // in a non-dominated block.
- LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
- HBasicBlock enterBlock = openNewBlock();
- HTry tryInstruction = new HTry();
- close(tryInstruction);
- bool oldInTryStatement = inTryStatement;
- inTryStatement = true;
-
- HBasicBlock startTryBlock;
- HBasicBlock endTryBlock;
- HBasicBlock startCatchBlock;
- HBasicBlock endCatchBlock;
- HBasicBlock startFinallyBlock;
- HBasicBlock endFinallyBlock;
-
- startTryBlock = graph.addNewBlock();
- open(startTryBlock);
- visit(node.tryBlock);
- // We use a [HExitTry] instead of a [HGoto] for the try block
- // because it will have multiple successors: the join block, and
- // the catch or finally block.
- if (!isAborted()) endTryBlock = close(new HExitTry());
- SubGraph bodyGraph = new SubGraph(startTryBlock, lastOpenedBlock);
- SubGraph catchGraph = null;
- HLocalValue exception = null;
-
- if (!node.catchBlocks.isEmpty) {
- localsHandler = new LocalsHandler.from(savedLocals);
- startCatchBlock = graph.addNewBlock();
- open(startCatchBlock);
- // Note that the name of this local is irrelevant.
- SyntheticLocal local =
- new SyntheticLocal('exception', localsHandler.executableContext);
- exception = new HLocalValue(local, backend.nonNullType);
- add(exception);
- HInstruction oldRethrowableException = rethrowableException;
- rethrowableException = exception;
-
- pushInvokeStatic(node, backend.getExceptionUnwrapper(), [exception]);
- HInvokeStatic unwrappedException = pop();
- tryInstruction.exception = exception;
- Link<ast.Node> link = node.catchBlocks.nodes;
-
- void pushCondition(ast.CatchBlock catchBlock) {
- if (catchBlock.onKeyword != null) {
- DartType type = elements.getType(catchBlock.type);
- if (type == null) {
- compiler.internalError(catchBlock.type, 'On with no type.');
- }
- HInstruction condition =
- buildIsNode(catchBlock.type, type, unwrappedException);
- push(condition);
- } else {
- ast.VariableDefinitions declaration = catchBlock.formals.nodes.head;
- HInstruction condition = null;
- if (declaration.type == null) {
- condition = graph.addConstantBool(true, compiler);
- stack.add(condition);
- } else {
- // TODO(aprelev@gmail.com): Once old catch syntax is removed
- // "if" condition above and this "else" branch should be deleted as
- // type of declared variable won't matter for the catch
- // condition.
- DartType type = elements.getType(declaration.type);
- if (type == null) {
- compiler.internalError(catchBlock, 'Catch with unresolved type.');
- }
- condition = buildIsNode(declaration.type, type, unwrappedException);
- push(condition);
- }
- }
- }
-
- void visitThen() {
- ast.CatchBlock catchBlock = link.head;
- link = link.tail;
- if (catchBlock.exception != null) {
- LocalVariableElement exceptionVariable =
- elements[catchBlock.exception];
- localsHandler.updateLocal(exceptionVariable,
- unwrappedException);
- }
- ast.Node trace = catchBlock.trace;
- if (trace != null) {
- pushInvokeStatic(trace, backend.getTraceFromException(), [exception]);
- HInstruction traceInstruction = pop();
- LocalVariableElement traceVariable = elements[trace];
- localsHandler.updateLocal(traceVariable, traceInstruction);
- }
- visit(catchBlock);
- }
-
- void visitElse() {
- if (link.isEmpty) {
- closeAndGotoExit(new HThrow(exception, isRethrow: true));
- } else {
- ast.CatchBlock newBlock = link.head;
- handleIf(node,
- () { pushCondition(newBlock); },
- visitThen, visitElse);
- }
- }
-
- ast.CatchBlock firstBlock = link.head;
- handleIf(node, () { pushCondition(firstBlock); }, visitThen, visitElse);
- if (!isAborted()) endCatchBlock = close(new HGoto());
-
- rethrowableException = oldRethrowableException;
- tryInstruction.catchBlock = startCatchBlock;
- catchGraph = new SubGraph(startCatchBlock, lastOpenedBlock);
- }
-
- SubGraph finallyGraph = null;
- if (node.finallyBlock != null) {
- localsHandler = new LocalsHandler.from(savedLocals);
- startFinallyBlock = graph.addNewBlock();
- open(startFinallyBlock);
- visit(node.finallyBlock);
- if (!isAborted()) endFinallyBlock = close(new HGoto());
- tryInstruction.finallyBlock = startFinallyBlock;
- finallyGraph = new SubGraph(startFinallyBlock, lastOpenedBlock);
- }
-
- HBasicBlock exitBlock = graph.addNewBlock();
-
- addOptionalSuccessor(b1, b2) { if (b2 != null) b1.addSuccessor(b2); }
- addExitTrySuccessor(successor) {
- if (successor == null) return;
- // Iterate over all blocks created inside this try/catch, and
- // attach successor information to blocks that end with
- // [HExitTry].
- for (int i = startTryBlock.id; i < successor.id; i++) {
- HBasicBlock block = graph.blocks[i];
- var last = block.last;
- if (last is HExitTry) {
- block.addSuccessor(successor);
- }
- }
- }
-
- // Setup all successors. The entry block that contains the [HTry]
- // has 1) the body, 2) the catch, 3) the finally, and 4) the exit
- // blocks as successors.
- enterBlock.addSuccessor(startTryBlock);
- addOptionalSuccessor(enterBlock, startCatchBlock);
- addOptionalSuccessor(enterBlock, startFinallyBlock);
- enterBlock.addSuccessor(exitBlock);
-
- // The body has either the catch or the finally block as successor.
- if (endTryBlock != null) {
- assert(startCatchBlock != null || startFinallyBlock != null);
- endTryBlock.addSuccessor(
- startCatchBlock != null ? startCatchBlock : startFinallyBlock);
- endTryBlock.addSuccessor(exitBlock);
- }
-
- // The catch block has either the finally or the exit block as
- // successor.
- if (endCatchBlock != null) {
- endCatchBlock.addSuccessor(
- startFinallyBlock != null ? startFinallyBlock : exitBlock);
- }
-
- // The finally block has the exit block as successor.
- if (endFinallyBlock != null) {
- endFinallyBlock.addSuccessor(exitBlock);
- }
-
- // If a block inside try/catch aborts (eg with a return statement),
- // we explicitely mark this block a predecessor of the catch
- // block and the finally block.
- addExitTrySuccessor(startCatchBlock);
- addExitTrySuccessor(startFinallyBlock);
-
- // Use the locals handler not altered by the catch and finally
- // blocks.
- localsHandler = savedLocals;
- open(exitBlock);
- enterBlock.setBlockFlow(
- new HTryBlockInformation(
- wrapStatementGraph(bodyGraph),
- exception,
- wrapStatementGraph(catchGraph),
- wrapStatementGraph(finallyGraph)),
- exitBlock);
- inTryStatement = oldInTryStatement;
- }
-
- visitCatchBlock(ast.CatchBlock node) {
- visit(node.block);
- }
-
- visitTypedef(ast.Typedef node) {
- compiler.unimplemented(node, 'SsaFromAstMixin.visitTypedef.');
- }
-
- visitTypeVariable(ast.TypeVariable node) {
- compiler.internalError(node, 'SsaFromAstMixin.visitTypeVariable.');
- }
-
- /**
- * This method is invoked before inlining the body of [function] into this
- * [SsaBuilder].
- */
- void enterInlinedMethod(FunctionElement function,
- ast.Node _,
- List<HInstruction> compiledArguments) {
- TypesInferrer inferrer = compiler.typesTask.typesInferrer;
- AstInliningState state = new AstInliningState(
- function, returnLocal, returnType, elements, stack, localsHandler,
- inTryStatement,
- allInlinedFunctionsCalledOnce && inferrer.isCalledOnce(function));
- inliningStack.add(state);
-
- // Setting up the state of the (AST) builder is performed even when the
- // inlined function is in IR, because the irInliner uses the [returnElement]
- // of the AST builder.
- setupStateForInlining(function, compiledArguments);
- }
-
- void leaveInlinedMethod() {
- HInstruction result = localsHandler.readLocal(returnLocal);
- AstInliningState state = inliningStack.removeLast();
- restoreState(state);
- stack.add(result);
- }
-
- void doInline(FunctionElement function) {
- visitInlinedFunction(function);
- }
-
- void emitReturn(HInstruction value, ast.Node node) {
- if (inliningStack.isEmpty) {
- closeAndGotoExit(attachPosition(new HReturn(value), node));
- } else {
- localsHandler.updateLocal(returnLocal, value);
- }
- }
-}
-
-/**
- * Visitor that handles generation of string literals (LiteralString,
- * StringInterpolation), and otherwise delegates to the given visitor for
- * non-literal subexpressions.
- */
-class StringBuilderVisitor extends ast.Visitor {
- final SsaBuilder builder;
- final ast.Node diagnosticNode;
-
- /**
- * The string value generated so far.
- */
- HInstruction result = null;
-
- StringBuilderVisitor(this.builder, this.diagnosticNode);
-
- Compiler get compiler => builder.compiler;
-
- void visit(ast.Node node) {
- node.accept(this);
- }
-
- visitNode(ast.Node node) {
- builder.compiler.internalError(node, 'Unexpected node.');
- }
-
- void visitExpression(ast.Node node) {
- node.accept(builder);
- HInstruction expression = builder.pop();
-
- // We want to use HStringify when:
- // 1. The value is known to be a primitive type, because it might get
- // constant-folded and codegen has some tricks with JavaScript
- // conversions.
- // 2. The value can be primitive, because the library stringifier has
- // fast-path code for most primitives.
- if (expression.canBePrimitive(compiler)) {
- append(stringify(node, expression));
- return;
- }
-
- // If the `toString` method is guaranteed to return a string we can call it
- // directly.
- Selector selector =
- new TypedSelector(expression.instructionType,
- new Selector.call('toString', null, 0), compiler.world);
- TypeMask type = TypeMaskFactory.inferredTypeForSelector(selector, compiler);
- if (type.containsOnlyString(compiler.world)) {
- builder.pushInvokeDynamic(node, selector, <HInstruction>[expression]);
- append(builder.pop());
- return;
- }
-
- append(stringify(node, expression));
- }
-
- void visitStringInterpolation(ast.StringInterpolation node) {
- node.visitChildren(this);
- }
-
- void visitStringInterpolationPart(ast.StringInterpolationPart node) {
- visit(node.expression);
- visit(node.string);
- }
-
- void visitStringJuxtaposition(ast.StringJuxtaposition node) {
- node.visitChildren(this);
- }
-
- void visitNodeList(ast.NodeList node) {
- node.visitChildren(this);
- }
-
- void append(HInstruction expression) {
- result = (result == null) ? expression : concat(result, expression);
- }
-
- HInstruction concat(HInstruction left, HInstruction right) {
- HInstruction instruction = new HStringConcat(
- left, right, diagnosticNode, builder.backend.stringType);
- builder.add(instruction);
- return instruction;
- }
-
- HInstruction stringify(ast.Node node, HInstruction expression) {
- HInstruction instruction =
- new HStringify(expression, node, builder.backend.stringType);
- builder.add(instruction);
- return instruction;
- }
-}
-
-/**
- * This class visits the method that is a candidate for inlining and
- * finds whether it is too difficult to inline.
- */
-// TODO(karlklose): refactor to make it possible to distinguish between
-// implementation restrictions (for example, we *can't* inline multiple returns)
-// and heuristics (we *shouldn't* inline large functions).
-class InlineWeeder extends ast.Visitor {
- // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP*
- static const INLINING_NODES_OUTSIDE_LOOP = 18;
- static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3;
- static const INLINING_NODES_INSIDE_LOOP = 42;
- static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4;
-
- bool seenReturn = false;
- bool tooDifficult = false;
- int nodeCount = 0;
- final int maxInliningNodes;
- final bool useMaxInliningNodes;
- final bool allowLoops;
-
- InlineWeeder(this.maxInliningNodes,
- this.useMaxInliningNodes,
- this.allowLoops);
-
- static bool canBeInlined(ast.FunctionExpression functionExpression,
- int maxInliningNodes,
- bool useMaxInliningNodes,
- {bool allowLoops: false}) {
- InlineWeeder weeder =
- new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops);
- weeder.visit(functionExpression.initializers);
- weeder.visit(functionExpression.body);
- return !weeder.tooDifficult;
- }
-
- bool registerNode() {
- if (!useMaxInliningNodes) return true;
- if (nodeCount++ > maxInliningNodes) {
- tooDifficult = true;
- return false;
- } else {
- return true;
- }
- }
-
- void visit(ast.Node node) {
- if (node != null) node.accept(this);
- }
-
- void visitNode(ast.Node node) {
- if (!registerNode()) return;
- if (seenReturn) {
- tooDifficult = true;
- } else {
- node.visitChildren(this);
- }
- }
-
- void visitFunctionExpression(ast.Node node) {
- if (!registerNode()) return;
- tooDifficult = true;
- }
-
- void visitFunctionDeclaration(ast.Node node) {
- if (!registerNode()) return;
- tooDifficult = true;
- }
-
- void visitSend(ast.Send node) {
- if (!registerNode()) return;
- node.visitChildren(this);
- }
-
- visitLoop(ast.Node node) {
- // It's actually not difficult to inline a method with a loop, but
- // our measurements show that it's currently better to not inline a
- // method that contains a loop.
- if (!allowLoops) tooDifficult = true;
- }
-
- void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
- if (!registerNode()) return;
- tooDifficult = true;
- }
-
- void visitRethrow(ast.Rethrow node) {
- if (!registerNode()) return;
- tooDifficult = true;
- }
-
- void visitReturn(ast.Return node) {
- if (!registerNode()) return;
- if (seenReturn
- || identical(node.beginToken.stringValue, 'native')) {
- tooDifficult = true;
- return;
- }
- node.visitChildren(this);
- seenReturn = true;
- }
-
- void visitTryStatement(ast.Node node) {
- if (!registerNode()) return;
- tooDifficult = true;
- }
-
- void visitThrow(ast.Throw node) {
- if (!registerNode()) return;
- // For now, we don't want to handle throw after a return even if
- // it is in an "if".
- if (seenReturn) {
- tooDifficult = true;
- } else {
- node.visitChildren(this);
- }
- }
-}
-
-abstract class InliningState {
- /**
- * Invariant: [function] must be an implementation element.
- */
- final FunctionElement function;
-
- InliningState(this.function) {
- assert(function.isImplementation);
- }
-}
-
-class AstInliningState extends InliningState {
- final Local oldReturnLocal;
- final DartType oldReturnType;
- final TreeElements oldElements;
- final List<HInstruction> oldStack;
- final LocalsHandler oldLocalsHandler;
- final bool inTryStatement;
- final bool allFunctionsCalledOnce;
-
- AstInliningState(FunctionElement function,
- this.oldReturnLocal,
- this.oldReturnType,
- this.oldElements,
- this.oldStack,
- this.oldLocalsHandler,
- this.inTryStatement,
- this.allFunctionsCalledOnce)
- : super(function);
-}
-
-class SsaBranch {
- final SsaBranchBuilder branchBuilder;
- final HBasicBlock block;
- LocalsHandler startLocals;
- LocalsHandler exitLocals;
- SubGraph graph;
-
- SsaBranch(this.branchBuilder) : block = new HBasicBlock();
-}
-
-class SsaBranchBuilder {
- final SsaBuilder builder;
- final ast.Node diagnosticNode;
-
- SsaBranchBuilder(this.builder, [this.diagnosticNode]);
-
- Compiler get compiler => builder.compiler;
-
- void checkNotAborted() {
- if (builder.isAborted()) {
- compiler.unimplemented(diagnosticNode, "aborted control flow");
- }
- }
-
- void buildCondition(void visitCondition(),
- SsaBranch conditionBranch,
- SsaBranch thenBranch,
- SsaBranch elseBranch) {
- startBranch(conditionBranch);
- visitCondition();
- checkNotAborted();
- assert(identical(builder.current, builder.lastOpenedBlock));
- HInstruction conditionValue = builder.popBoolified();
- HIf branch = new HIf(conditionValue);
- HBasicBlock conditionExitBlock = builder.current;
- builder.close(branch);
- conditionBranch.exitLocals = builder.localsHandler;
- conditionExitBlock.addSuccessor(thenBranch.block);
- conditionExitBlock.addSuccessor(elseBranch.block);
- bool conditionBranchLocalsCanBeReused =
- mergeLocals(conditionBranch, thenBranch, mayReuseFromLocals: true);
- mergeLocals(conditionBranch, elseBranch,
- mayReuseFromLocals: conditionBranchLocalsCanBeReused);
-
- conditionBranch.graph =
- new SubExpression(conditionBranch.block, conditionExitBlock);
- }
-
- /**
- * Returns true if the locals of the [fromBranch] may be reused. A [:true:]
- * return value implies that [mayReuseFromLocals] was set to [:true:].
- */
- bool mergeLocals(SsaBranch fromBranch, SsaBranch toBranch,
- {bool mayReuseFromLocals}) {
- LocalsHandler fromLocals = fromBranch.exitLocals;
- if (toBranch.startLocals == null) {
- if (mayReuseFromLocals) {
- toBranch.startLocals = fromLocals;
- return false;
- } else {
- toBranch.startLocals = new LocalsHandler.from(fromLocals);
- return true;
- }
- } else {
- toBranch.startLocals.mergeWith(fromLocals, toBranch.block);
- return true;
- }
- }
-
- void startBranch(SsaBranch branch) {
- builder.graph.addBlock(branch.block);
- builder.localsHandler = branch.startLocals;
- builder.open(branch.block);
- }
-
- HInstruction buildBranch(SsaBranch branch,
- void visitBranch(),
- SsaBranch joinBranch,
- bool isExpression) {
- startBranch(branch);
- visitBranch();
- branch.graph = new SubGraph(branch.block, builder.lastOpenedBlock);
- branch.exitLocals = builder.localsHandler;
- if (!builder.isAborted()) {
- builder.goto(builder.current, joinBranch.block);
- mergeLocals(branch, joinBranch, mayReuseFromLocals: true);
- }
- if (isExpression) {
- checkNotAborted();
- return builder.pop();
- }
- return null;
- }
-
- handleIf(void visitCondition(), void visitThen(), void visitElse()) {
- if (visitElse == null) {
- // Make sure to have an else part to avoid a critical edge. A
- // critical edge is an edge that connects a block with multiple
- // successors to a block with multiple predecessors. We avoid
- // such edges because they prevent inserting copies during code
- // generation of phi instructions.
- visitElse = () {};
- }
-
- _handleDiamondBranch(visitCondition, visitThen, visitElse, false);
- }
-
- handleConditional(void visitCondition(), void visitThen(), void visitElse()) {
- assert(visitElse != null);
- _handleDiamondBranch(visitCondition, visitThen, visitElse, true);
- }
-
- void handleLogicalAndOr(void left(), void right(), {bool isAnd}) {
- // x && y is transformed into:
- // t0 = boolify(x);
- // if (t0) {
- // t1 = boolify(y);
- // }
- // result = phi(t1, false);
- //
- // x || y is transformed into:
- // t0 = boolify(x);
- // if (not(t0)) {
- // t1 = boolify(y);
- // }
- // result = phi(t1, true);
- HInstruction boolifiedLeft;
- HInstruction boolifiedRight;
-
- void visitCondition() {
- left();
- boolifiedLeft = builder.popBoolified();
- builder.stack.add(boolifiedLeft);
- if (!isAnd) {
- builder.push(new HNot(builder.pop(), builder.backend.boolType));
- }
- }
-
- void visitThen() {
- right();
- boolifiedRight = builder.popBoolified();
- }
-
- handleIf(visitCondition, visitThen, null);
- HConstant notIsAnd =
- builder.graph.addConstantBool(!isAnd, builder.compiler);
- JavaScriptBackend backend = builder.backend;
- HPhi result = new HPhi.manyInputs(null,
- <HInstruction>[boolifiedRight, notIsAnd],
- backend.dynamicType);
- builder.current.addPhi(result);
- builder.stack.add(result);
- }
-
- void handleLogicalAndOrWithLeftNode(ast.Node left,
- void visitRight(),
- {bool isAnd}) {
- // This method is similar to [handleLogicalAndOr] but optimizes the case
- // where left is a logical "and" or logical "or".
- //
- // For example (x && y) && z is transformed into x && (y && z):
- // t0 = boolify(x);
- // if (t0) {
- // t1 = boolify(y);
- // if (t1) {
- // t2 = boolify(z);
- // }
- // t3 = phi(t2, false);
- // }
- // result = phi(t3, false);
-
- ast.Send send = left.asSend();
- if (send != null &&
- (isAnd ? send.isLogicalAnd : send.isLogicalOr)) {
- ast.Node newLeft = send.receiver;
- Link<ast.Node> link = send.argumentsNode.nodes;
- assert(link.tail.isEmpty);
- ast.Node middle = link.head;
- handleLogicalAndOrWithLeftNode(
- newLeft,
- () => handleLogicalAndOrWithLeftNode(middle, visitRight,
- isAnd: isAnd),
- isAnd: isAnd);
- } else {
- handleLogicalAndOr(() => builder.visit(left), visitRight, isAnd: isAnd);
- }
- }
-
- void _handleDiamondBranch(void visitCondition(),
- void visitThen(),
- void visitElse(),
- bool isExpression) {
- SsaBranch conditionBranch = new SsaBranch(this);
- SsaBranch thenBranch = new SsaBranch(this);
- SsaBranch elseBranch = new SsaBranch(this);
- SsaBranch joinBranch = new SsaBranch(this);
-
- conditionBranch.startLocals = builder.localsHandler;
- builder.goto(builder.current, conditionBranch.block);
-
- buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch);
- HInstruction thenValue =
- buildBranch(thenBranch, visitThen, joinBranch, isExpression);
- HInstruction elseValue =
- buildBranch(elseBranch, visitElse, joinBranch, isExpression);
-
- if (isExpression) {
- assert(thenValue != null && elseValue != null);
- JavaScriptBackend backend = builder.backend;
- HPhi phi = new HPhi.manyInputs(
- null, <HInstruction>[thenValue, elseValue], backend.dynamicType);
- joinBranch.block.addPhi(phi);
- builder.stack.add(phi);
- }
-
- HBasicBlock thenBlock = thenBranch.block;
- HBasicBlock elseBlock = elseBranch.block;
- HBasicBlock joinBlock;
- // If at least one branch did not abort, open the joinBranch.
- if (!joinBranch.block.predecessors.isEmpty) {
- startBranch(joinBranch);
- joinBlock = joinBranch.block;
- }
-
- HIfBlockInformation info =
- new HIfBlockInformation(
- new HSubExpressionBlockInformation(conditionBranch.graph),
- new HSubGraphBlockInformation(thenBranch.graph),
- new HSubGraphBlockInformation(elseBranch.graph));
-
- HBasicBlock conditionStartBlock = conditionBranch.block;
- conditionStartBlock.setBlockFlow(info, joinBlock);
- SubGraph conditionGraph = conditionBranch.graph;
- HIf branch = conditionGraph.end.last;
- assert(branch is HIf);
- branch.blockInformation = conditionStartBlock.blockFlow;
- }
-}
-
-class TypeBuilder implements DartTypeVisitor<dynamic, SsaBuilder> {
- final ClassWorld classWorld;
-
- TypeBuilder(this.classWorld);
-
- void visitType(DartType type, _) {
- throw 'Internal error $type';
- }
-
- void visitVoidType(VoidType type, SsaBuilder builder) {
- ClassElement cls = builder.backend.findHelper('VoidRuntimeType');
- builder.push(new HVoidType(type, new TypeMask.exact(cls, classWorld)));
- }
-
- void visitTypeVariableType(TypeVariableType type,
- SsaBuilder builder) {
- ClassElement cls = builder.backend.findHelper('RuntimeType');
- TypeMask instructionType = new TypeMask.subclass(cls, classWorld);
- if (!builder.sourceElement.enclosingElement.isClosure &&
- builder.sourceElement.isInstanceMember) {
- HInstruction receiver = builder.localsHandler.readThis();
- builder.push(new HReadTypeVariable(type, receiver, instructionType));
- } else {
- builder.push(
- new HReadTypeVariable.noReceiver(
- type, builder.addTypeVariableReference(type), instructionType));
- }
- }
-
- void visitFunctionType(FunctionType type, SsaBuilder builder) {
- type.returnType.accept(this, builder);
- HInstruction returnType = builder.pop();
- List<HInstruction> inputs = <HInstruction>[returnType];
-
- for (DartType parameter in type.parameterTypes) {
- parameter.accept(this, builder);
- inputs.add(builder.pop());
- }
-
- for (DartType parameter in type.optionalParameterTypes) {
- parameter.accept(this, builder);
- inputs.add(builder.pop());
- }
-
- List<DartType> namedParameterTypes = type.namedParameterTypes;
- List<String> names = type.namedParameters;
- for (int index = 0; index < names.length; index++) {
- ast.DartString dartString = new ast.DartString.literal(names[index]);
- inputs.add(
- builder.graph.addConstantString(dartString, builder.compiler));
- namedParameterTypes[index].accept(this, builder);
- inputs.add(builder.pop());
- }
-
- ClassElement cls = builder.backend.findHelper('RuntimeFunctionType');
- builder.push(new HFunctionType(inputs, type,
- new TypeMask.exact(cls, classWorld)));
- }
-
- void visitMalformedType(MalformedType type, SsaBuilder builder) {
- visitDynamicType(const DynamicType(), builder);
- }
-
- void visitStatementType(StatementType type, SsaBuilder builder) {
- throw 'not implemented visitStatementType($type)';
- }
-
- void visitGenericType(GenericType type, SsaBuilder builder) {
- throw 'not implemented visitGenericType($type)';
- }
-
- void visitInterfaceType(InterfaceType type, SsaBuilder builder) {
- List<HInstruction> inputs = <HInstruction>[];
- for (DartType typeArgument in type.typeArguments) {
- typeArgument.accept(this, builder);
- inputs.add(builder.pop());
- }
- ClassElement cls;
- if (type.typeArguments.isEmpty) {
- cls = builder.backend.findHelper('RuntimeTypePlain');
- } else {
- cls = builder.backend.findHelper('RuntimeTypeGeneric');
- }
- builder.push(new HInterfaceType(inputs, type,
- new TypeMask.exact(cls, classWorld)));
- }
-
- void visitTypedefType(TypedefType type, SsaBuilder builder) {
- DartType unaliased = type.unalias(builder.compiler);
- if (unaliased is TypedefType) throw 'unable to unalias $type';
- unaliased.accept(this, builder);
- }
-
- void visitDynamicType(DynamicType type, SsaBuilder builder) {
- JavaScriptBackend backend = builder.compiler.backend;
- ClassElement cls = backend.findHelper('DynamicRuntimeType');
- builder.push(new HDynamicType(type, new TypeMask.exact(cls, classWorld)));
- }
-}

Powered by Google App Engine
This is Rietveld 408576698