| Index: frog/leg/ssa/builder.dart
|
| ===================================================================
|
| --- frog/leg/ssa/builder.dart (revision 5925)
|
| +++ frog/leg/ssa/builder.dart (working copy)
|
| @@ -1,2971 +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.
|
| -
|
| -class Interceptors {
|
| - Compiler compiler;
|
| - Interceptors(Compiler this.compiler);
|
| -
|
| - SourceString mapOperatorToMethodName(Operator op) {
|
| - String name = op.source.stringValue;
|
| - if (name === '+') return const SourceString('add');
|
| - if (name === '-') return const SourceString('sub');
|
| - if (name === '*') return const SourceString('mul');
|
| - if (name === '/') return const SourceString('div');
|
| - if (name === '~/') return const SourceString('tdiv');
|
| - if (name === '%') return const SourceString('mod');
|
| - if (name === '<<') return const SourceString('shl');
|
| - if (name === '>>') return const SourceString('shr');
|
| - if (name === '|') return const SourceString('or');
|
| - if (name === '&') return const SourceString('and');
|
| - if (name === '^') return const SourceString('xor');
|
| - if (name === '<') return const SourceString('lt');
|
| - if (name === '<=') return const SourceString('le');
|
| - if (name === '>') return const SourceString('gt');
|
| - if (name === '>=') return const SourceString('ge');
|
| - if (name === '==') return const SourceString('eq');
|
| - if (name === '!=') return const SourceString('eq');
|
| - if (name === '===') return const SourceString('eqq');
|
| - if (name === '!==') return const SourceString('eqq');
|
| - if (name === '+=') return const SourceString('add');
|
| - if (name === '-=') return const SourceString('sub');
|
| - if (name === '*=') return const SourceString('mul');
|
| - if (name === '/=') return const SourceString('div');
|
| - if (name === '~/=') return const SourceString('tdiv');
|
| - if (name === '%=') return const SourceString('mod');
|
| - if (name === '<<=') return const SourceString('shl');
|
| - if (name === '>>=') return const SourceString('shr');
|
| - if (name === '|=') return const SourceString('or');
|
| - if (name === '&=') return const SourceString('and');
|
| - if (name === '^=') return const SourceString('xor');
|
| - if (name === '++') return const SourceString('add');
|
| - if (name === '--') return const SourceString('sub');
|
| - compiler.unimplemented('Unknown operator', node: op);
|
| - }
|
| -
|
| - Element getStaticInterceptor(SourceString name, int parameters) {
|
| - String mangledName = "builtin\$${name.slowToString()}\$${parameters}";
|
| - Element result = compiler.findHelper(new SourceString(mangledName));
|
| - return result;
|
| - }
|
| -
|
| - Element getStaticGetInterceptor(SourceString name) {
|
| - String mangledName = "builtin\$get\$${name.slowToString()}";
|
| - Element result = compiler.findHelper(new SourceString(mangledName));
|
| - return result;
|
| - }
|
| -
|
| - Element getStaticSetInterceptor(SourceString name) {
|
| - String mangledName = "builtin\$set\$${name.slowToString()}";
|
| - Element result = compiler.findHelper(new SourceString(mangledName));
|
| - return result;
|
| - }
|
| -
|
| - Element getOperatorInterceptor(Operator op) {
|
| - SourceString name = mapOperatorToMethodName(op);
|
| - Element result = compiler.findHelper(name);
|
| - return result;
|
| - }
|
| -
|
| - Element getPrefixOperatorInterceptor(Operator op) {
|
| - String name = op.source.stringValue;
|
| - if (name === '~') {
|
| - return compiler.findHelper(const SourceString('not'));
|
| - }
|
| - if (name === '-') {
|
| - return compiler.findHelper(const SourceString('neg'));
|
| - }
|
| - compiler.unimplemented('Unknown operator', node: op);
|
| - }
|
| -
|
| - Element getIndexInterceptor() {
|
| - return compiler.findHelper(const SourceString('index'));
|
| - }
|
| -
|
| - Element getIndexAssignmentInterceptor() {
|
| - return compiler.findHelper(const SourceString('indexSet'));
|
| - }
|
| -
|
| - Element getEqualsNullInterceptor() {
|
| - return compiler.findHelper(const SourceString('eqNull'));
|
| - }
|
| -
|
| - Element getExceptionUnwrapper() {
|
| - return compiler.findHelper(const SourceString('unwrapException'));
|
| - }
|
| -
|
| - Element getClosureConverter() {
|
| - return compiler.findHelper(const SourceString('convertDartClosureToJS'));
|
| - }
|
| -
|
| - Element getTraceFromException() {
|
| - return compiler.findHelper(const SourceString('getTraceFromException'));
|
| - }
|
| -
|
| - Element getEqualsInterceptor() {
|
| - return compiler.findHelper(const SourceString('eq'));
|
| - }
|
| -
|
| - Element getMapMaker() {
|
| - return compiler.findHelper(const SourceString('makeLiteralMap'));
|
| - }
|
| -}
|
| -
|
| -class SsaBuilderTask extends CompilerTask {
|
| - final Interceptors interceptors;
|
| - final Map<Node, ClosureData> closureDataCache;
|
| -
|
| - String get name() => 'SSA builder';
|
| -
|
| - SsaBuilderTask(Compiler compiler)
|
| - : interceptors = new Interceptors(compiler),
|
| - closureDataCache = new HashMap<Node, ClosureData>(),
|
| - super(compiler);
|
| -
|
| - HGraph build(WorkItem work) {
|
| - return measure(() {
|
| - FunctionElement element = work.element;
|
| - HInstruction.idCounter = 0;
|
| - SsaBuilder builder = new SsaBuilder(compiler, work);
|
| - HGraph graph;
|
| - switch (element.kind) {
|
| - case ElementKind.GENERATIVE_CONSTRUCTOR:
|
| - graph = compileConstructor(builder, work);
|
| - break;
|
| - case ElementKind.GENERATIVE_CONSTRUCTOR_BODY:
|
| - case ElementKind.FUNCTION:
|
| - case ElementKind.GETTER:
|
| - case ElementKind.SETTER:
|
| - graph = builder.buildMethod(work.element);
|
| - break;
|
| - }
|
| - assert(graph.isValid());
|
| - if (compiler.tracer.enabled) {
|
| - String name;
|
| - if (element.enclosingElement !== null &&
|
| - element.enclosingElement.kind == ElementKind.CLASS) {
|
| - String className = element.enclosingElement.name.slowToString();
|
| - String memberName = element.name.slowToString();
|
| - name = "$className.$memberName";
|
| - if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
|
| - name = "$name (body)";
|
| - }
|
| - } else {
|
| - name = "${element.name.slowToString()}";
|
| - }
|
| - compiler.tracer.traceCompilation(name);
|
| - compiler.tracer.traceGraph('builder', graph);
|
| - }
|
| - return graph;
|
| - });
|
| - }
|
| -
|
| - HGraph compileConstructor(SsaBuilder builder, WorkItem work) {
|
| - // The body of the constructor will be generated in a separate function.
|
| - final ClassElement classElement = work.element.enclosingElement;
|
| - return builder.buildFactory(classElement, 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).
|
| - */
|
| - Map<Element, HInstruction> directLocals;
|
| - Map<Element, Element> redirectionMapping;
|
| - SsaBuilder builder;
|
| - ClosureData closureData;
|
| -
|
| - LocalsHandler(this.builder)
|
| - : directLocals = new Map<Element, HInstruction>(),
|
| - redirectionMapping = new Map<Element, Element>();
|
| -
|
| - /**
|
| - * 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<Element, HInstruction>.from(other.directLocals),
|
| - redirectionMapping = other.redirectionMapping,
|
| - 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(Element from, Element 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?
|
| - HInstruction box = new HForeign(const LiteralDartString("{}"),
|
| - const LiteralDartString('Object'),
|
| - <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(Node node) {
|
| - // 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) {
|
| - // The scope has captured variables. Create a box.
|
| - // TODO(floitsch): Clean up this hack. Should we create a box-object by
|
| - // just creating an empty object literal?
|
| - HInstruction 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.capturedVariableMapping.forEach((Element from, Element to) {
|
| - // The [from] can only be a parameter for function-scopes and not
|
| - // loop scopes.
|
| - if (from.kind == ElementKind.PARAMETER) {
|
| - // Store the captured parameter in the box. Get the current value
|
| - // before we put the redirection in place.
|
| - HInstruction instruction = readLocal(from);
|
| - redirectElement(from, to);
|
| - // Now that the redirection is set up, the update to the local will
|
| - // write the parameter value into the box.
|
| - 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(Element boxElement, List<Element> 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 (Element 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);
|
| - }
|
| -
|
| - void startFunction(FunctionElement function,
|
| - FunctionExpression node) {
|
| -
|
| - ClosureTranslator translator =
|
| - new ClosureTranslator(builder.compiler, builder.elements);
|
| - closureData = translator.translate(node);
|
| -
|
| - FunctionParameters params = function.computeParameters(builder.compiler);
|
| - params.forEachParameter((Element element) {
|
| - HParameterValue parameter = new HParameterValue(element);
|
| - builder.add(parameter);
|
| - directLocals[element] = parameter;
|
| - });
|
| - if (closureData.thisElement !== null) {
|
| - // Once closures have been mapped to classes their instance members might
|
| - // not have any thisElement if the closure was created inside a static
|
| - // context.
|
| - assert(function.isInstanceMember() || function.isGenerativeConstructor());
|
| - // We have to introduce 'this' before we enter the scope, since it might
|
| - // need to be copied into a box (if it is captured). This is similar
|
| - // to all other parameters that are introduced.
|
| - HInstruction thisInstruction = new HThis();
|
| - builder.add(thisInstruction);
|
| - directLocals[closureData.thisElement] = thisInstruction;
|
| - }
|
| -
|
| - enterScope(node);
|
| -
|
| - // 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.freeVariableMapping.forEach((Element from, Element to) {
|
| - redirectElement(from, to);
|
| - });
|
| - if (closureData.isClosure()) {
|
| - // Inside closure redirect references to itself to [:this:].
|
| - HInstruction thisInstruction = new HThis();
|
| - builder.add(thisInstruction);
|
| - updateLocal(closureData.closureElement, thisInstruction);
|
| - }
|
| - }
|
| -
|
| - bool hasValueForDirectLocal(Element element) {
|
| - assert(element !== null);
|
| - assert(isAccessedDirectly(element));
|
| - return directLocals[element] !== null;
|
| - }
|
| -
|
| - /**
|
| - * 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(Element element) {
|
| - assert(element !== null);
|
| - return redirectionMapping[element] === null
|
| - && !closureData.usedVariablesInTry.contains(element);
|
| - }
|
| -
|
| - bool isStoredInClosureField(Element element) {
|
| - assert(element !== null);
|
| - if (isAccessedDirectly(element)) return false;
|
| - Element redirectTarget = redirectionMapping[element];
|
| - if (redirectTarget == null) return false;
|
| - if (redirectTarget.enclosingElement.kind == ElementKind.CLASS) {
|
| - assert(redirectTarget is ClosureFieldElement);
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool isBoxed(Element element) {
|
| - if (isAccessedDirectly(element)) return false;
|
| - if (isStoredInClosureField(element)) return false;
|
| - return redirectionMapping[element] !== null;
|
| - }
|
| -
|
| - bool isUsedInTry(Element element) {
|
| - return closureData.usedVariablesInTry.contains(element);
|
| - }
|
| -
|
| - /**
|
| - * 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(Element element) {
|
| - if (isAccessedDirectly(element)) {
|
| - if (directLocals[element] == null) {
|
| - builder.compiler.internalError("Cannot find value $element",
|
| - element: element);
|
| - }
|
| - return directLocals[element];
|
| - } else if (isStoredInClosureField(element)) {
|
| - Element redirect = redirectionMapping[element];
|
| - // We must not use the [LocalsHandler.readThis()] since that could
|
| - // point to a captured this which would be stored in a closure-field
|
| - // itself.
|
| - HInstruction receiver = new HThis();
|
| - builder.add(receiver);
|
| - HInstruction fieldGet = new HFieldGet(redirect, receiver);
|
| - builder.add(fieldGet);
|
| - return fieldGet;
|
| - } else if (isBoxed(element)) {
|
| - Element redirect = redirectionMapping[element];
|
| - // 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.
|
| - assert(redirect.enclosingElement.kind == ElementKind.VARIABLE);
|
| - HInstruction box = readLocal(redirect.enclosingElement);
|
| - HInstruction lookup = new HFieldGet(redirect, box);
|
| - builder.add(lookup);
|
| - return lookup;
|
| - } else {
|
| - assert(isUsedInTry(element));
|
| - HInstruction variable = new HFieldGet.fromActivation(element);
|
| - builder.add(variable);
|
| - return variable;
|
| - }
|
| - }
|
| -
|
| - HInstruction readThis() {
|
| - return readLocal(closureData.thisElement);
|
| - }
|
| -
|
| - /**
|
| - * 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(Element element, HInstruction value) {
|
| - if (isAccessedDirectly(element)) {
|
| - directLocals[element] = value;
|
| - } else if (isStoredInClosureField(element)) {
|
| - Element redirect = redirectionMapping[element];
|
| - // We must not use the [LocalsHandler.readThis()] since that could
|
| - // point to a captured this which would be stored in a closure-field
|
| - // itself.
|
| - HInstruction receiver = new HThis();
|
| - builder.add(receiver);
|
| - builder.add(new HFieldSet(redirect, receiver, value));
|
| - } else if (isBoxed(element)) {
|
| - Element redirect = redirectionMapping[element];
|
| - // 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.
|
| - assert(redirect.enclosingElement.kind == ElementKind.VARIABLE);
|
| - HInstruction box = readLocal(redirect.enclosingElement);
|
| - builder.add(new HFieldSet(redirect, box, value));
|
| - } else {
|
| - assert(isUsedInTry(element));
|
| - builder.add(new HFieldSet.fromActivation(element,value));
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * This function 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
|
| - * [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 [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 [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(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);
|
| - }
|
| - }
|
| -
|
| - void beginLoopHeader(Node node, HBasicBlock loopEntry) {
|
| - // Create a copy because we modify the map while iterating over
|
| - // it.
|
| - Map<Element, HInstruction> saved =
|
| - new Map<Element, HInstruction>.from(directLocals);
|
| -
|
| - // Create phis for all elements in the definitions environment.
|
| - saved.forEach((Element element, HInstruction instruction) {
|
| - // We know 'this' cannot be modified.
|
| - if (element !== closureData.thisElement) {
|
| - HPhi phi = new HPhi.singleInput(element, instruction);
|
| - loopEntry.addPhi(phi);
|
| - directLocals[element] = phi;
|
| - } else {
|
| - directLocals[element] = instruction;
|
| - }
|
| - });
|
| - }
|
| -
|
| - void enterLoopBody(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);
|
| - }
|
| - }
|
| -
|
| - void enterLoopUpdates(Loop 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);
|
| - }
|
| - }
|
| -
|
| - void endLoop(HBasicBlock loopEntry) {
|
| - loopEntry.forEachPhi((HPhi phi) {
|
| - Element element = phi.element;
|
| - 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 the otherLocals instruction as the
|
| - * first input, and this handler's instruction as the second.
|
| - * NOTICE: This means that the predecessor corresponding to [otherLocals]
|
| - * should be the first predecessor of the current block, and the one
|
| - * corresponding to this locals handler should be 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<Element, HInstruction> joinedLocals = new Map<Element, HInstruction>();
|
| - otherLocals.directLocals.forEach((element, instruction) {
|
| - // We know 'this' cannot be modified.
|
| - if (element === closureData.thisElement) {
|
| - assert(directLocals[element] == instruction);
|
| - joinedLocals[element] = instruction;
|
| - } else {
|
| - HInstruction mine = directLocals[element];
|
| - if (mine === null) return;
|
| - if (instruction === mine) {
|
| - joinedLocals[element] = instruction;
|
| - } else {
|
| - HInstruction phi =
|
| - new HPhi.manyInputs(element, <HInstruction>[instruction, mine]);
|
| - joinBlock.addPhi(phi);
|
| - joinedLocals[element] = phi;
|
| - }
|
| - }
|
| - });
|
| - directLocals = joinedLocals;
|
| - }
|
| -
|
| - /**
|
| - * 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.
|
| - * Returns the new LocalsHandler to use (may not be [this]).
|
| - */
|
| - LocalsHandler mergeMultiple(List<LocalsHandler> locals,
|
| - HBasicBlock joinBlock) {
|
| - assert(locals.length > 0);
|
| - if (locals.length == 1) return locals[0];
|
| - Map<Element, HInstruction> joinedLocals = new Map<Element,HInstruction>();
|
| - HInstruction thisValue = null;
|
| - directLocals.forEach((Element element, HInstruction instruction) {
|
| - if (element !== closureData.thisElement) {
|
| - HPhi phi = new HPhi.noInputs(element);
|
| - joinedLocals[element] = 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 local in locals) {
|
| - local.directLocals.forEach((Element element, HInstruction instruction) {
|
| - HPhi phi = joinedLocals[element];
|
| - 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.thisElement] = thisValue;
|
| - }
|
| - directLocals = joinedLocals;
|
| - return this;
|
| - }
|
| -}
|
| -
|
| -
|
| -// Represents a single break/continue instruction.
|
| -class JumpHandlerEntry {
|
| - final HGoto jumpInstruction;
|
| - final LocalsHandler locals;
|
| - bool isBreak() => jumpInstruction is HBreak;
|
| - bool isContinue() => jumpInstruction is HContinue;
|
| - JumpHandlerEntry(this.jumpInstruction, this.locals);
|
| -}
|
| -
|
| -
|
| -interface JumpHandler default JumpHandlerImpl {
|
| - JumpHandler(SsaBuilder builder, TargetElement target);
|
| - void generateBreak([LabelElement label]);
|
| - void generateContinue([LabelElement label]);
|
| - void forEachBreak(void action(HBreak instruction, LocalsHandler locals));
|
| - void forEachContinue(void action(HBreak instruction, LocalsHandler locals));
|
| - void close();
|
| - List<LabelElement> 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 {
|
| - const NullJumpHandler();
|
| - void generateBreak([LabelElement label]) { unreachable(); }
|
| - void generateContinue([LabelElement label]) { unreachable(); }
|
| - void forEachBreak(Function ignored) { }
|
| - void forEachContinue(Function ignored) { }
|
| - void close() { }
|
| - List<LabelElement> labels() => const <LabelElement>[];
|
| -}
|
| -
|
| -// 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 JumpHandlerImpl implements JumpHandler {
|
| - final SsaBuilder builder;
|
| - final TargetElement target;
|
| - final List<JumpHandlerEntry> jumps;
|
| -
|
| - JumpHandlerImpl(SsaBuilder builder, this.target)
|
| - : this.builder = builder,
|
| - jumps = <JumpHandlerEntry>[] {
|
| - assert(builder.jumpTargets[target] === null);
|
| - builder.jumpTargets[target] = this;
|
| - }
|
| -
|
| - void generateBreak([LabelElement 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([LabelElement label]) {
|
| - HInstruction continueInstruction;
|
| - if (label === null) {
|
| - continueInstruction = new HContinue(target);
|
| - } else {
|
| - continueInstruction = new HContinue.toLabel(label);
|
| - }
|
| - 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);
|
| - }
|
| - }
|
| -
|
| - void close() {
|
| - // The mapping from TargetElement to JumpHandler is no longer needed.
|
| - builder.jumpTargets.remove(target);
|
| - }
|
| -
|
| - List<LabelElement> labels() {
|
| - List<LabelElement> result = null;
|
| - for (LabelElement element in target.labels) {
|
| - if (result === null) result = <LabelElement>[];
|
| - result.add(element);
|
| - }
|
| - return (result === null) ? const <LabelElement>[] : result;
|
| - }
|
| -}
|
| -
|
| -class SsaBuilder implements Visitor {
|
| - final Compiler compiler;
|
| - TreeElements elements;
|
| - final Interceptors interceptors;
|
| - final WorkItem work;
|
| - bool methodInterceptionEnabled;
|
| - HGraph graph;
|
| - LocalsHandler localsHandler;
|
| - HInstruction rethrowableException;
|
| -
|
| - Map<TargetElement, JumpHandler> jumpTargets;
|
| -
|
| - // We build the Ssa graph by simulating a stack machine.
|
| - List<HInstruction> stack;
|
| -
|
| - // The current block to add instructions to. Might be null, if we are
|
| - // visiting dead code.
|
| - HBasicBlock current;
|
| - // 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;
|
| -
|
| - LibraryElement get currentLibrary() => work.element.getLibrary();
|
| -
|
| - SsaBuilder(Compiler compiler, WorkItem work)
|
| - : this.compiler = compiler,
|
| - this.work = work,
|
| - interceptors = compiler.builder.interceptors,
|
| - methodInterceptionEnabled = true,
|
| - elements = work.resolutionTree,
|
| - graph = new HGraph(),
|
| - stack = new List<HInstruction>(),
|
| - jumpTargets = new Map<TargetElement, JumpHandler>() {
|
| - localsHandler = new LocalsHandler(this);
|
| - }
|
| -
|
| - void disableMethodInterception() {
|
| - assert(methodInterceptionEnabled);
|
| - methodInterceptionEnabled = false;
|
| - }
|
| -
|
| - void enableMethodInterception() {
|
| - assert(!methodInterceptionEnabled);
|
| - methodInterceptionEnabled = true;
|
| - }
|
| -
|
| - HGraph buildMethod(FunctionElement functionElement) {
|
| - FunctionExpression function = functionElement.parseNode(compiler);
|
| - openFunction(functionElement, function);
|
| - function.body.accept(this);
|
| - return closeFunction();
|
| - }
|
| -
|
| - /**
|
| - * Returns the constructor body associated with the given constructor or
|
| - * creates a new constructor body, if none can be found.
|
| - */
|
| - ConstructorBodyElement getConstructorBody(ClassElement classElement,
|
| - FunctionElement constructor) {
|
| - assert(constructor.kind === ElementKind.GENERATIVE_CONSTRUCTOR);
|
| - ConstructorBodyElement bodyElement;
|
| - for (Link<Element> backendMembers = classElement.backendMembers;
|
| - !backendMembers.isEmpty();
|
| - backendMembers = backendMembers.tail) {
|
| - Element backendMember = backendMembers.head;
|
| - if (backendMember.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
|
| - ConstructorBodyElement body = backendMember;
|
| - if (body.constructor == constructor) {
|
| - bodyElement = backendMember;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - if (bodyElement === null) {
|
| - bodyElement = new ConstructorBodyElement(constructor);
|
| - TreeElements treeElements =
|
| - compiler.resolver.resolveMethodElement(constructor);
|
| - compiler.enqueue(new WorkItem.toCodegen(bodyElement, treeElements));
|
| - classElement.backendMembers =
|
| - classElement.backendMembers.prepend(bodyElement);
|
| - }
|
| - assert(bodyElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY);
|
| - return bodyElement;
|
| - }
|
| -
|
| - /**
|
| - * 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.
|
| - */
|
| - void inlineInitializers(FunctionElement constructor,
|
| - List<FunctionElement> constructors,
|
| - Map<Element, HInstruction> fieldValues) {
|
| - TreeElements oldElements = elements;
|
| - constructors.addLast(constructor);
|
| - bool initializedSuper = false;
|
| - elements = compiler.resolver.resolveMethodElement(constructor);
|
| - FunctionExpression functionNode = constructor.parseNode(compiler);
|
| -
|
| - if (functionNode.initializers !== null) {
|
| - Link<Node> initializers = functionNode.initializers.nodes;
|
| - for (Link<Node> link = initializers; !link.isEmpty(); link = link.tail) {
|
| - assert(link.head is Send);
|
| - if (link.head is !SendSet) {
|
| - // A super initializer or constructor redirection.
|
| - Send call = link.head;
|
| - assert(Initializers.isSuperConstructorCall(call) ||
|
| - Initializers.isConstructorRedirect(call));
|
| - FunctionElement nextConstructor = elements[call];
|
| - // Visit arguments and map the corresponding parameter value to
|
| - // the resulting HInstruction value.
|
| - List<HInstruction> arguments = new List<HInstruction>();
|
| - addStaticSendArgumentsToList(call, nextConstructor, arguments);
|
| - int index = 0;
|
| - FunctionParameters parameters =
|
| - nextConstructor.computeParameters(compiler);
|
| - parameters.forEachParameter((Element parameter) {
|
| - HInstruction argument = arguments[index++];
|
| - localsHandler.updateLocal(parameter, argument);
|
| - // Don't forget to update the field, if the parameter is of the
|
| - // form [:this.x:].
|
| - if (parameter.kind == ElementKind.FIELD_PARAMETER) {
|
| - FieldParameterElement fieldParameterElement = parameter;
|
| - fieldValues[fieldParameterElement.fieldElement] = argument;
|
| - }
|
| - });
|
| - inlineInitializers(nextConstructor, constructors, fieldValues);
|
| - initializedSuper = true;
|
| - } else {
|
| - // A field initializer.
|
| - SendSet init = link.head;
|
| - Link<Node> arguments = init.arguments;
|
| - assert(!arguments.isEmpty() && arguments.tail.isEmpty());
|
| - visit(arguments.head);
|
| - fieldValues[elements[init]] = pop();
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (!initializedSuper) {
|
| - // No super initializer found. Try to find the default constructor if
|
| - // the class is not Object.
|
| - ClassElement enclosingClass = constructor.enclosingElement;
|
| - ClassElement superClass = enclosingClass.superclass;
|
| - if (enclosingClass != compiler.objectClass) {
|
| - assert(superClass !== null);
|
| - assert(superClass.isResolved);
|
| - FunctionElement nextConstructor =
|
| - superClass.lookupConstructor(superClass.name);
|
| - if (nextConstructor === null) {
|
| - compiler.internalError("no default constructor available");
|
| - }
|
| - inlineInitializers(nextConstructor, constructors, fieldValues);
|
| - }
|
| - }
|
| -
|
| - elements = oldElements;
|
| - }
|
| -
|
| - /**
|
| - * 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 the constructor bodies, starting from the constructor(s) in the
|
| - * super class(es).
|
| - */
|
| - HGraph buildFactory(ClassElement classElement,
|
| - FunctionElement functionElement) {
|
| - FunctionExpression function = functionElement.parseNode(compiler);
|
| - // 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.
|
| -
|
| - // The initializer list could contain closures.
|
| - openFunction(functionElement, function);
|
| -
|
| - Map<Element, HInstruction> fieldValues = new Map<Element, HInstruction>();
|
| - FunctionParameters parameters = functionElement.computeParameters(compiler);
|
| - parameters.forEachParameter((Element element) {
|
| - if (element.kind == ElementKind.FIELD_PARAMETER) {
|
| - // If the [element] is a field-parameter (such as [:this.x:] then
|
| - // initialize the field element with its value.
|
| - FieldParameterElement fieldParameterElement = element;
|
| - HInstruction parameterValue = localsHandler.readLocal(element);
|
| - fieldValues[fieldParameterElement.fieldElement] = parameterValue;
|
| - }
|
| - });
|
| -
|
| - final Map<FunctionElement, TreeElements> constructorElements =
|
| - compiler.resolver.constructorElements;
|
| - List<FunctionElement> constructors = new List<FunctionElement>();
|
| -
|
| - // Analyze the constructor and all referenced constructors and collect
|
| - // initializers and constructor bodies.
|
| - inlineInitializers(functionElement, constructors, fieldValues);
|
| -
|
| - // Call the JavaScript constructor with the fields as argument.
|
| - // TODO(floitsch,karlklose): move this code to ClassElement and share with
|
| - // the emitter.
|
| - List<HInstruction> constructorArguments = <HInstruction>[];
|
| - ClassElement element = classElement;
|
| - while (element != null) {
|
| - for (Element member in element.members) {
|
| - if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
|
| - HInstruction value = fieldValues[member];
|
| - if (value === null) {
|
| - // The field has no value in the initializer list. Initialize it
|
| - // with the declaration-site constant (if any).
|
| - Constant fieldValue =
|
| - compiler.constantHandler.compileVariable(member);
|
| - value = graph.addConstant(fieldValue);
|
| - }
|
| - constructorArguments.add(value);
|
| - }
|
| - }
|
| - element = element.superclass;
|
| - }
|
| - HForeignNew newObject = new HForeignNew(classElement, constructorArguments);
|
| - add(newObject);
|
| - // Generate calls to the constructor bodies.
|
| - for (int index = constructors.length - 1; index >= 0; index--) {
|
| - FunctionElement constructor = constructors[index];
|
| - // TODO(floitsch): find better way to detect that constructor body is
|
| - // empty.
|
| - if (constructor is SynthesizedConstructorElement) continue;
|
| - ConstructorBodyElement body = getConstructorBody(classElement,
|
| - constructor);
|
| - List bodyCallInputs = <HInstruction>[];
|
| - bodyCallInputs.add(newObject);
|
| - body.functionParameters.forEachParameter((parameter) {
|
| - bodyCallInputs.add(localsHandler.readLocal(parameter));
|
| - });
|
| - // TODO(ahe): The constructor name is statically resolved. See
|
| - // SsaCodeGenerator.visitInvokeDynamicMethod. Is there a cleaner
|
| - // way to do this?
|
| - SourceString methodName = new SourceString(compiler.namer.getName(body));
|
| - add(new HInvokeDynamicMethod(null, methodName, bodyCallInputs));
|
| - }
|
| - close(new HReturn(newObject)).addSuccessor(graph.exit);
|
| - return closeFunction();
|
| - }
|
| -
|
| - void openFunction(FunctionElement functionElement,
|
| - FunctionExpression node) {
|
| - HBasicBlock block = graph.addNewBlock();
|
| - open(graph.entry);
|
| -
|
| - localsHandler.startFunction(functionElement, node);
|
| - close(new HGoto()).addSuccessor(block);
|
| -
|
| - open(block);
|
| - }
|
| -
|
| - HGraph closeFunction() {
|
| - // TODO(kasperl): Make this goto an implicit return.
|
| - if (!isAborted()) close(new HGoto()).addSuccessor(graph.exit);
|
| - graph.finalize();
|
| - return graph;
|
| - }
|
| -
|
| - 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;
|
| - }
|
| -
|
| - void goto(HBasicBlock from, HBasicBlock to) {
|
| - from.close(new HGoto());
|
| - from.addSuccessor(to);
|
| - }
|
| -
|
| - bool isAborted() {
|
| - return current === null;
|
| - }
|
| -
|
| - void add(HInstruction instruction) {
|
| - current.add(instruction);
|
| - }
|
| -
|
| - void push(HInstruction instruction) {
|
| - add(instruction);
|
| - stack.add(instruction);
|
| - }
|
| -
|
| - HInstruction pop() {
|
| - return stack.removeLast();
|
| - }
|
| -
|
| - HBoolify popBoolified() {
|
| - HBoolify boolified = new HBoolify(pop());
|
| - add(boolified);
|
| - return boolified;
|
| - }
|
| -
|
| - void visit(Node node) {
|
| - if (node !== null) node.accept(this);
|
| - }
|
| -
|
| - visitBlock(Block node) {
|
| - for (Link<Node> link = node.statements.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - visit(link.head);
|
| - if (isAborted()) {
|
| - // The block has been aborted by a return or a throw.
|
| - if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack');
|
| - return;
|
| - }
|
| - }
|
| - assert(!current.isClosed());
|
| - if (!stack.isEmpty()) compiler.cancel('non-empty instruction stack');
|
| - }
|
| -
|
| - visitClassNode(ClassNode node) {
|
| - unreachable();
|
| - }
|
| -
|
| - visitExpressionStatement(ExpressionStatement node) {
|
| - 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(Node node) {
|
| - assert(!isAborted());
|
| - HBasicBlock previousBlock = close(new HGoto());
|
| -
|
| - JumpHandler jumpHandler = createJumpHandler(node);
|
| - HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(jumpHandler.labels());
|
| - previousBlock.addSuccessor(loopEntry);
|
| - open(loopEntry);
|
| -
|
| - localsHandler.beginLoopHeader(node, loopEntry);
|
| - return jumpHandler;
|
| - }
|
| -
|
| - /**
|
| - * Ends the loop:
|
| - * - creates a new block and adds it as successor to the [branchBlock].
|
| - * - opens the new block (setting as [current]).
|
| - * - notifies the locals handler that we're exiting a loop.
|
| - */
|
| - void endLoop(HBasicBlock loopEntry,
|
| - HBasicBlock branchBlock,
|
| - JumpHandler jumpHandler,
|
| - LocalsHandler savedLocals) {
|
| - HBasicBlock loopExitBlock = addNewBlock();
|
| - assert(branchBlock.successors.length == 1);
|
| - List<LocalsHandler> breakLocals = <LocalsHandler>[];
|
| - jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
|
| - breakInstruction.block.addSuccessor(loopExitBlock);
|
| - breakLocals.add(locals);
|
| - });
|
| - branchBlock.addSuccessor(loopExitBlock);
|
| - open(loopExitBlock);
|
| - localsHandler.endLoop(loopEntry);
|
| - if (!breakLocals.isEmpty()) {
|
| - breakLocals.add(savedLocals);
|
| - localsHandler = savedLocals.mergeMultiple(breakLocals, loopExitBlock);
|
| - } else {
|
| - localsHandler = savedLocals;
|
| - }
|
| - }
|
| -
|
| - // 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.
|
| - handleLoop(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.
|
| - initialize();
|
| - assert(!isAborted());
|
| -
|
| - JumpHandler jumpHandler = beginLoopHeader(loop);
|
| - HBasicBlock conditionBlock = current;
|
| -
|
| - HInstruction conditionInstruction = condition();
|
| - HBasicBlock conditionExitBlock =
|
| - close(new HLoopBranch(conditionInstruction));
|
| -
|
| - LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| -
|
| - // The body.
|
| - HBasicBlock beginBodyBlock = addNewBlock();
|
| - conditionExitBlock.addSuccessor(beginBodyBlock);
|
| - open(beginBodyBlock);
|
| -
|
| - localsHandler.enterLoopBody(loop);
|
| - hackAroundPossiblyAbortingBody(loop, body);
|
| -
|
| - SubGraph bodyGraph = new SubGraph(beginBodyBlock, current);
|
| - HBasicBlock bodyBlock = close(new HGoto());
|
| -
|
| - // 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> continueLocals = <LocalsHandler>[];
|
| - jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
|
| - instruction.block.addSuccessor(updateBlock);
|
| - continueLocals.add(locals);
|
| - });
|
| - bodyBlock.addSuccessor(updateBlock);
|
| - continueLocals.add(localsHandler);
|
| -
|
| - open(updateBlock);
|
| -
|
| - localsHandler = localsHandler.mergeMultiple(continueLocals, updateBlock);
|
| -
|
| - HLabeledBlockInformation labelInfo;
|
| - List<LabelElement> labels = jumpHandler.labels();
|
| - TargetElement target = elements[loop];
|
| - if (!labels.isEmpty()) {
|
| - beginBodyBlock.labeledBlockInformation =
|
| - new HLabeledBlockInformation(bodyGraph, updateBlock,
|
| - jumpHandler.labels(), isContinue: true);
|
| - } else if (target !== null && target.isContinueTarget) {
|
| - beginBodyBlock.labeledBlockInformation =
|
| - new HLabeledBlockInformation.implicit(bodyGraph, updateBlock,
|
| - target, isContinue: true);
|
| - }
|
| -
|
| - localsHandler.enterLoopUpdates(loop);
|
| -
|
| - update();
|
| -
|
| - updateBlock = close(new HGoto());
|
| - // The back-edge completing the cycle.
|
| - updateBlock.addSuccessor(conditionBlock);
|
| - conditionBlock.postProcessLoopHeader();
|
| -
|
| - endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals);
|
| - }
|
| -
|
| - visitFor(For node) {
|
| - assert(node.body !== null);
|
| - void buildInitializer() {
|
| - if (node.initializer === null) return;
|
| - Node initializer = node.initializer;
|
| - if (initializer !== null) {
|
| - visit(initializer);
|
| - if (initializer.asExpression() !== null) {
|
| - pop();
|
| - }
|
| - }
|
| - }
|
| - HInstruction buildCondition() {
|
| - if (node.condition === null) {
|
| - return graph.addConstantBool(true);
|
| - }
|
| - visit(node.condition);
|
| - return popBoolified();
|
| - }
|
| - void buildUpdate() {
|
| - for (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(While node) {
|
| - HInstruction buildCondition() {
|
| - visit(node.condition);
|
| - return popBoolified();
|
| - }
|
| - handleLoop(node,
|
| - () {},
|
| - buildCondition,
|
| - () {},
|
| - () { visit(node.body); });
|
| - }
|
| -
|
| - visitDoWhile(DoWhile node) {
|
| - LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| - localsHandler.startLoop(node);
|
| - JumpHandler jumpHandler = beginLoopHeader(node);
|
| - HBasicBlock loopEntryBlock = current;
|
| - HBasicBlock bodyEntryBlock = current;
|
| - TargetElement target = elements[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 = graph.addNewBlock();
|
| - goto(current, bodyEntryBlock);
|
| - open(bodyEntryBlock);
|
| - }
|
| - localsHandler.enterLoopBody(node);
|
| - hackAroundPossiblyAbortingBody(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 = close(new HGoto());
|
| - HBasicBlock conditionBlock = addNewBlock();
|
| -
|
| - List<LocalsHandler> continueLocals = <LocalsHandler>[];
|
| - jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
|
| - instruction.block.addSuccessor(conditionBlock);
|
| - continueLocals.add(locals);
|
| - });
|
| - bodyExitBlock.addSuccessor(conditionBlock);
|
| - if (!continueLocals.isEmpty()) {
|
| - continueLocals.add(localsHandler);
|
| - localsHandler = savedLocals.mergeMultiple(continueLocals, conditionBlock);
|
| - SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
|
| - List<LabelElement> labels = jumpHandler.labels();
|
| - if (!labels.isEmpty()) {
|
| - bodyEntryBlock.labeledBlockInformation =
|
| - new HLabeledBlockInformation(bodyGraph,
|
| - conditionBlock,
|
| - labels,
|
| - isContinue: true);
|
| - } else {
|
| - bodyEntryBlock.labeledBlockInformation =
|
| - new HLabeledBlockInformation.implicit(bodyGraph,
|
| - conditionBlock,
|
| - target,
|
| - isContinue: true);
|
| - }
|
| - }
|
| - open(conditionBlock);
|
| -
|
| - visit(node.condition);
|
| - assert(!isAborted());
|
| - conditionBlock = close(new HLoopBranch(popBoolified(),
|
| - HLoopBranch.DO_WHILE_LOOP));
|
| -
|
| - conditionBlock.addSuccessor(loopEntryBlock); // The back-edge.
|
| - loopEntryBlock.postProcessLoopHeader();
|
| -
|
| - endLoop(loopEntryBlock, conditionBlock, jumpHandler, localsHandler);
|
| - jumpHandler.close();
|
| - }
|
| -
|
| - visitFunctionExpression(FunctionExpression node) {
|
| - ClosureData nestedClosureData = compiler.builder.closureDataCache[node];
|
| - if (nestedClosureData === null) {
|
| - // TODO(floitsch): we can only assume that the reason for not having a
|
| - // closure data here is, because the function is inside an initializer.
|
| - compiler.unimplemented("Closures inside initializers", node: node);
|
| - }
|
| - assert(nestedClosureData !== null);
|
| - assert(nestedClosureData.closureClassElement !== null);
|
| - ClassElement closureClassElement =
|
| - nestedClosureData.closureClassElement;
|
| - FunctionElement callElement = nestedClosureData.callElement;
|
| - compiler.enqueue(new WorkItem.toCodegen(callElement, elements));
|
| - compiler.registerInstantiatedClass(closureClassElement);
|
| - assert(closureClassElement.members.isEmpty());
|
| -
|
| - List<HInstruction> capturedVariables = <HInstruction>[];
|
| - for (Element member in closureClassElement.backendMembers) {
|
| - // The backendMembers also contains the call method(s). We are only
|
| - // interested in the fields.
|
| - if (member.kind == ElementKind.FIELD) {
|
| - Element capturedLocal = nestedClosureData.capturedFieldMapping[member];
|
| - assert(capturedLocal != null);
|
| - capturedVariables.add(localsHandler.readLocal(capturedLocal));
|
| - }
|
| - }
|
| -
|
| - push(new HForeignNew(closureClassElement, capturedVariables));
|
| - }
|
| -
|
| - visitFunctionDeclaration(FunctionDeclaration node) {
|
| - visit(node.function);
|
| - localsHandler.updateLocal(elements[node], pop());
|
| - }
|
| -
|
| - visitIdentifier(Identifier node) {
|
| - if (node.isThis()) {
|
| - stack.add(localsHandler.readThis());
|
| - } else {
|
| - compiler.internalError("SsaBuilder.visitIdentifier on non-this",
|
| - node: node);
|
| - }
|
| - }
|
| -
|
| - visitIf(If node) {
|
| - visit(node.condition);
|
| - Function visitElse;
|
| - if (node.elsePart != null) {
|
| - visitElse = () {
|
| - visit(node.elsePart);
|
| - };
|
| - }
|
| - handleIf(() => visit(node.thenPart), visitElse);
|
| - }
|
| -
|
| - void handleIf(void visitThen(), void visitElse()) {
|
| - bool hasElse = visitElse != null;
|
| - HIf condition = new HIf(popBoolified(), hasElse);
|
| - HBasicBlock conditionBlock = close(condition);
|
| -
|
| - LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| -
|
| - // The then part.
|
| - HBasicBlock thenBlock = addNewBlock();
|
| - conditionBlock.addSuccessor(thenBlock);
|
| - open(thenBlock);
|
| - visitThen();
|
| - SubGraph thenGraph = new SubGraph(thenBlock, lastOpenedBlock);
|
| - thenBlock = current;
|
| -
|
| - // Reset the locals state to the state after the condition and keep the
|
| - // current state in [thenLocals].
|
| - LocalsHandler thenLocals = localsHandler;
|
| -
|
| - // Now the else part.
|
| - localsHandler = savedLocals;
|
| - HBasicBlock elseBlock = null;
|
| - SubGraph elseGraph = null;
|
| - if (hasElse) {
|
| - elseBlock = addNewBlock();
|
| - conditionBlock.addSuccessor(elseBlock);
|
| - open(elseBlock);
|
| - visitElse();
|
| - elseGraph = new SubGraph(elseBlock, lastOpenedBlock);
|
| - elseBlock = current;
|
| - }
|
| -
|
| - HBasicBlock joinBlock = null;
|
| - if (thenBlock !== null || elseBlock !== null || !hasElse) {
|
| - joinBlock = addNewBlock();
|
| - if (thenBlock !== null) goto(thenBlock, joinBlock);
|
| - if (elseBlock !== null) goto(elseBlock, joinBlock);
|
| - else if (!hasElse) conditionBlock.addSuccessor(joinBlock);
|
| - // If the join block has two predecessors we have to merge the
|
| - // locals. The current locals is what either the
|
| - // condition or the else block left us with, so we merge that
|
| - // with the set of locals we got after visiting the then
|
| - // part of the if.
|
| - open(joinBlock);
|
| - if (joinBlock.predecessors.length == 2) {
|
| - localsHandler.mergeWith(thenLocals, joinBlock);
|
| - } else if (thenBlock !== null) {
|
| - // The only predecessor is the then branch.
|
| - localsHandler = thenLocals;
|
| - }
|
| - }
|
| - condition.blockInformation =
|
| - new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock);
|
| - }
|
| -
|
| - void visitLogicalAndOr(Send node, Operator op) {
|
| - handleLogicalAndOr(() { visit(node.receiver); },
|
| - () { visit(node.argumentsNode); },
|
| - isAnd: (const SourceString("&&") == op.source));
|
| - }
|
| -
|
| -
|
| - void handleLogicalAndOr(void left(), void right(), [bool isAnd = true]) {
|
| - // x && y is transformed into:
|
| - // t0 = boolify(x);
|
| - // if (t0) t1 = boolify(y);
|
| - // result = phi(t0, t1);
|
| - //
|
| - // x || y is transformed into:
|
| - // t0 = boolify(x);
|
| - // if (not(t0)) t1 = boolify(y);
|
| - // result = phi(t0, t1);
|
| - left();
|
| - HInstruction boolifiedLeft = popBoolified();
|
| - HInstruction condition;
|
| - if (isAnd) {
|
| - condition = boolifiedLeft;
|
| - } else {
|
| - condition = new HNot(boolifiedLeft);
|
| - add(condition);
|
| - }
|
| - HIf branch = new HIf(condition, false);
|
| - HBasicBlock leftBlock = close(branch);
|
| - LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| -
|
| - HBasicBlock rightBlock = addNewBlock();
|
| - leftBlock.addSuccessor(rightBlock);
|
| - open(rightBlock);
|
| -
|
| - right();
|
| - HInstruction boolifiedRight = popBoolified();
|
| - SubGraph rightGraph = new SubGraph(rightBlock, current);
|
| - rightBlock = close(new HGoto());
|
| -
|
| - HBasicBlock joinBlock = addNewBlock();
|
| - leftBlock.addSuccessor(joinBlock);
|
| - rightBlock.addSuccessor(joinBlock);
|
| - open(joinBlock);
|
| -
|
| - branch.blockInformation =
|
| - new HIfBlockInformation(branch, rightGraph, null, joinBlock);
|
| -
|
| - localsHandler.mergeWith(savedLocals, joinBlock);
|
| - HPhi result = new HPhi.manyInputs(null,
|
| - <HInstruction>[boolifiedLeft, boolifiedRight]);
|
| - joinBlock.addPhi(result);
|
| - stack.add(result);
|
| - }
|
| -
|
| - void visitLogicalNot(Send node) {
|
| - assert(node.argumentsNode is Prefix);
|
| - visit(node.receiver);
|
| - HNot not = new HNot(popBoolified());
|
| - push(not);
|
| - }
|
| -
|
| - void visitUnary(Send node, Operator op) {
|
| - assert(node.argumentsNode is Prefix);
|
| - visit(node.receiver);
|
| - assert(op.token.kind !== PLUS_TOKEN);
|
| - HInstruction operand = pop();
|
| -
|
| - HInstruction target =
|
| - new HStatic(interceptors.getPrefixOperatorInterceptor(op));
|
| - add(target);
|
| - HInvokeUnary result;
|
| - switch (op.source.stringValue) {
|
| - case "-": result = new HNegate(target, operand); break;
|
| - case "~": result = new HBitNot(target, operand); break;
|
| - default: unreachable();
|
| - }
|
| - // See if we can constant-fold right away. This avoids rewrites later on.
|
| - if (operand is HConstant) {
|
| - HConstant constant = operand;
|
| - Constant folded = result.operation.fold(constant.constant);
|
| - if (folded !== null) {
|
| - stack.add(graph.addConstant(folded));
|
| - return;
|
| - }
|
| - }
|
| - push(result);
|
| - }
|
| -
|
| - void visitBinary(HInstruction left, Operator op, HInstruction right) {
|
| - Element element = interceptors.getOperatorInterceptor(op);
|
| - assert(element != null);
|
| - HInstruction target = new HStatic(element);
|
| - add(target);
|
| - switch (op.source.stringValue) {
|
| - case "+":
|
| - case "++":
|
| - case "+=":
|
| - push(new HAdd(target, left, right));
|
| - break;
|
| - case "-":
|
| - case "--":
|
| - case "-=":
|
| - push(new HSubtract(target, left, right));
|
| - break;
|
| - case "*":
|
| - case "*=":
|
| - push(new HMultiply(target, left, right));
|
| - break;
|
| - case "/":
|
| - case "/=":
|
| - push(new HDivide(target, left, right));
|
| - break;
|
| - case "~/":
|
| - case "~/=":
|
| - push(new HTruncatingDivide(target, left, right));
|
| - break;
|
| - case "%":
|
| - case "%=":
|
| - push(new HModulo(target, left, right));
|
| - break;
|
| - case "<<":
|
| - case "<<=":
|
| - push(new HShiftLeft(target, left, right));
|
| - break;
|
| - case ">>":
|
| - case ">>=":
|
| - push(new HShiftRight(target, left, right));
|
| - break;
|
| - case "|":
|
| - case "|=":
|
| - push(new HBitOr(target, left, right));
|
| - break;
|
| - case "&":
|
| - case "&=":
|
| - push(new HBitAnd(target, left, right));
|
| - break;
|
| - case "^":
|
| - case "^=":
|
| - push(new HBitXor(target, left, right));
|
| - break;
|
| - case "==":
|
| - push(new HEquals(target, left, right));
|
| - break;
|
| - case "===":
|
| - push(new HIdentity(target, left, right));
|
| - break;
|
| - case "!==":
|
| - HIdentity eq = new HIdentity(target, left, right);
|
| - add(eq);
|
| - push(new HNot(eq));
|
| - break;
|
| - case "<":
|
| - push(new HLess(target, left, right));
|
| - break;
|
| - case "<=":
|
| - push(new HLessEqual(target, left, right));
|
| - break;
|
| - case ">":
|
| - push(new HGreater(target, left, right));
|
| - break;
|
| - case ">=":
|
| - push(new HGreaterEqual(target, left, right));
|
| - break;
|
| - case "!=":
|
| - HEquals eq = new HEquals(target, left, right);
|
| - add(eq);
|
| - HBoolify bl = new HBoolify(eq);
|
| - add(bl);
|
| - push(new HNot(bl));
|
| - break;
|
| - default: compiler.unimplemented("SsaBuilder.visitBinary");
|
| - }
|
| - }
|
| -
|
| - void generateGetter(Send send, Element element) {
|
| - Selector selector = elements.getSelector(send);
|
| - if (Elements.isStaticOrTopLevelField(element)) {
|
| - push(new HStatic(element));
|
| - if (element.kind == ElementKind.GETTER) {
|
| - push(new HInvokeStatic(selector, <HInstruction>[pop()]));
|
| - }
|
| - } else if (Elements.isInstanceSend(send, elements)) {
|
| - HInstruction receiver;
|
| - if (send.receiver == null) {
|
| - receiver = localsHandler.readThis();
|
| - } else {
|
| - visit(send.receiver);
|
| - receiver = pop();
|
| - }
|
| - SourceString getterName = send.selector.asIdentifier().source;
|
| - Element staticInterceptor = null;
|
| - if (methodInterceptionEnabled) {
|
| - staticInterceptor = interceptors.getStaticGetInterceptor(getterName);
|
| - }
|
| - if (staticInterceptor != null) {
|
| - HStatic target = new HStatic(staticInterceptor);
|
| - add(target);
|
| - List<HInstruction> inputs = <HInstruction>[target, receiver];
|
| - push(new HInvokeInterceptor(selector, getterName, true, inputs));
|
| - } else {
|
| - push(new HInvokeDynamicGetter(selector, null, getterName, receiver));
|
| - }
|
| - } else if (Elements.isStaticOrTopLevelFunction(element)) {
|
| - push(new HStatic(element));
|
| - compiler.registerGetOfStaticFunction(element);
|
| - } else {
|
| - stack.add(localsHandler.readLocal(element));
|
| - }
|
| - }
|
| -
|
| - void generateSetter(SendSet send, Element element, HInstruction value) {
|
| - Selector selector = elements.getSelector(send);
|
| - if (Elements.isStaticOrTopLevelField(element)) {
|
| - if (element.kind == ElementKind.SETTER) {
|
| - HStatic target = new HStatic(element);
|
| - add(target);
|
| - add(new HInvokeStatic(selector, <HInstruction>[target, value]));
|
| - } else {
|
| - add(new HStaticStore(element, value));
|
| - }
|
| - stack.add(value);
|
| - } else if (element === null || Elements.isInstanceField(element)) {
|
| - SourceString dartSetterName = send.selector.asIdentifier().source;
|
| - HInstruction receiver;
|
| - if (send.receiver == null) {
|
| - receiver = localsHandler.readThis();
|
| - } else {
|
| - visit(send.receiver);
|
| - receiver = pop();
|
| - }
|
| - Element staticInterceptor = null;
|
| - if (methodInterceptionEnabled) {
|
| - staticInterceptor =
|
| - interceptors.getStaticSetInterceptor(dartSetterName);
|
| - }
|
| - if (staticInterceptor != null) {
|
| - HStatic target = new HStatic(staticInterceptor);
|
| - add(target);
|
| - List<HInstruction> inputs = <HInstruction>[target, receiver, value];
|
| - add(new HInvokeInterceptor(selector, dartSetterName, false, inputs));
|
| - } else {
|
| - add(new HInvokeDynamicSetter(selector, null, dartSetterName,
|
| - receiver, value));
|
| - }
|
| - stack.add(value);
|
| - } else {
|
| - localsHandler.updateLocal(element, value);
|
| - stack.add(value);
|
| - }
|
| - }
|
| -
|
| - visitOperatorSend(node) {
|
| - assert(node.selector is Operator);
|
| - Operator op = node.selector;
|
| - if (const SourceString("[]") == op.source) {
|
| - HStatic target = new HStatic(interceptors.getIndexInterceptor());
|
| - add(target);
|
| - visit(node.receiver);
|
| - HInstruction receiver = pop();
|
| - visit(node.argumentsNode);
|
| - HInstruction index = pop();
|
| - push(new HIndex(target, receiver, index));
|
| - } else if (const SourceString("&&") == op.source ||
|
| - const SourceString("||") == op.source) {
|
| - visitLogicalAndOr(node, op);
|
| - } else if (const SourceString("!") == op.source) {
|
| - visitLogicalNot(node);
|
| - } else if (node.argumentsNode is Prefix) {
|
| - visitUnary(node, op);
|
| - } else if (const SourceString("is") == op.source) {
|
| - visit(node.receiver);
|
| - HInstruction expression = pop();
|
| - Node argument = node.arguments.head;
|
| - TypeAnnotation type = argument.asTypeAnnotation();
|
| - bool isNot = false;
|
| - // TODO(ngeoffray): Duplicating pattern in resolver. We should
|
| - // add a new kind of node.
|
| - if (type == null) {
|
| - type = argument.asSend().receiver;
|
| - isNot = true;
|
| - }
|
| - HInstruction instruction = new HIs(elements[type], expression);
|
| - if (isNot) {
|
| - add(instruction);
|
| - instruction = new HNot(instruction);
|
| - }
|
| - push(instruction);
|
| - } else {
|
| - visit(node.receiver);
|
| - visit(node.argumentsNode);
|
| - var right = pop();
|
| - var left = pop();
|
| - visitBinary(left, op, right);
|
| - }
|
| - }
|
| -
|
| - void addDynamicSendArgumentsToList(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<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<SourceString, HInstruction> instructions =
|
| - new Map<SourceString, HInstruction>();
|
| - List<SourceString> 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<SourceString> orderedNames = selector.getOrderedNamedArguments();
|
| - for (SourceString name in orderedNames) {
|
| - list.add(instructions[name]);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void addStaticSendArgumentsToList(Send node,
|
| - FunctionElement element,
|
| - List<HInstruction> list) {
|
| - HInstruction compileArgument(Node argument) {
|
| - visit(argument);
|
| - return pop();
|
| - }
|
| -
|
| - HInstruction compileConstant(Element constantElement) {
|
| - Constant constant = compiler.compileVariable(constantElement);
|
| - return graph.addConstant(constant);
|
| - }
|
| -
|
| - Selector selector = elements.getSelector(node);
|
| - FunctionParameters parameters = element.computeParameters(compiler);
|
| - bool succeeded = selector.addSendArgumentsToList(node, list, parameters,
|
| - compileArgument,
|
| - compileConstant);
|
| - if (!succeeded) {
|
| - // TODO(ngeoffray): Match the VM behavior and throw an
|
| - // exception at runtime.
|
| - compiler.cancel('Unimplemented non-matching static call', node: node);
|
| - }
|
| - }
|
| -
|
| - void addGenericSendArgumentsToList(Link<Node> link, List<HInstruction> list) {
|
| - for (; !link.isEmpty(); link = link.tail) {
|
| - visit(link.head);
|
| - list.add(pop());
|
| - }
|
| - }
|
| -
|
| - visitDynamicSend(Send node) {
|
| - Selector selector = elements.getSelector(node);
|
| - var inputs = <HInstruction>[];
|
| -
|
| - SourceString dartMethodName;
|
| - bool isNotEquals = false;
|
| - if (node.isIndex && !node.arguments.tail.isEmpty()) {
|
| - dartMethodName = Elements.constructOperatorName(
|
| - const SourceString('operator'),
|
| - const SourceString('[]='));
|
| - } else if (node.selector.asOperator() != null) {
|
| - SourceString name = node.selector.asIdentifier().source;
|
| - isNotEquals = name.stringValue === '!=';
|
| - dartMethodName = Elements.constructOperatorName(
|
| - const SourceString('operator'),
|
| - name,
|
| - node.argumentsNode is Prefix);
|
| - } else {
|
| - dartMethodName = node.selector.asIdentifier().source;
|
| - }
|
| -
|
| - Element interceptor = null;
|
| - if (methodInterceptionEnabled && node.receiver !== null) {
|
| - interceptor = interceptors.getStaticInterceptor(dartMethodName,
|
| - node.argumentCount());
|
| - }
|
| - if (interceptor != null) {
|
| - HStatic target = new HStatic(interceptor);
|
| - add(target);
|
| - inputs.add(target);
|
| - visit(node.receiver);
|
| - inputs.add(pop());
|
| - addGenericSendArgumentsToList(node.arguments, inputs);
|
| - push(new HInvokeInterceptor(selector, dartMethodName, false, inputs));
|
| - return;
|
| - }
|
| -
|
| - if (node.receiver === null) {
|
| - inputs.add(localsHandler.readThis());
|
| - } else {
|
| - visit(node.receiver);
|
| - inputs.add(pop());
|
| - }
|
| -
|
| - addDynamicSendArgumentsToList(node, inputs);
|
| -
|
| - // The first entry in the inputs list is the receiver.
|
| - push(new HInvokeDynamicMethod(selector, dartMethodName, inputs));
|
| -
|
| - if (isNotEquals) {
|
| - HNot not = new HNot(popBoolified());
|
| - push(not);
|
| - }
|
| - }
|
| -
|
| - visitClosureSend(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 {
|
| - assert(Elements.isLocal(element));
|
| - closureTarget = localsHandler.readLocal(element);
|
| - }
|
| - var inputs = <HInstruction>[];
|
| - inputs.add(closureTarget);
|
| - addDynamicSendArgumentsToList(node, inputs);
|
| - push(new HInvokeClosure(selector, inputs));
|
| - }
|
| -
|
| - void handleForeignJs(Send node) {
|
| - Link<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.isEmpty()) {
|
| - compiler.cancel('At least two arguments expected', node: node.arguments);
|
| - }
|
| - link = link.tail.tail;
|
| - List<HInstruction> inputs = <HInstruction>[];
|
| - addGenericSendArgumentsToList(link, inputs);
|
| - Node type = node.arguments.head;
|
| - Node literal = node.arguments.tail.head;
|
| - if (literal is !StringNode || literal.dynamic.isInterpolation) {
|
| - compiler.cancel('JS code must be a string literal', node: literal);
|
| - }
|
| - if (type is !LiteralString) {
|
| - compiler.cancel(
|
| - 'The type of a JS expression must be a string literal', node: type);
|
| - }
|
| - push(new HForeign(
|
| - literal.dynamic.dartString, type.dynamic.dartString, inputs));
|
| - }
|
| -
|
| - void handleForeignUnintercepted(Send node) {
|
| - Link<Node> link = node.arguments;
|
| - if (!link.tail.isEmpty()) {
|
| - compiler.cancel(
|
| - 'More than one expression in UNINTERCEPTED()', node: node);
|
| - }
|
| - Expression expression = link.head;
|
| - disableMethodInterception();
|
| - visit(expression);
|
| - enableMethodInterception();
|
| - }
|
| -
|
| - void handleForeignJsHasEquals(Send node) {
|
| - List<HInstruction> inputs = <HInstruction>[];
|
| - if (!node.arguments.tail.isEmpty()) {
|
| - compiler.cancel(
|
| - 'More than one expression in JS_HAS_EQUALS()', node: node);
|
| - }
|
| - addGenericSendArgumentsToList(node.arguments, inputs);
|
| - String name = compiler.namer.instanceMethodName(
|
| - currentLibrary, Namer.OPERATOR_EQUALS, 1);
|
| - push(new HForeign(new DartString.literal('!!#.$name'),
|
| - const LiteralDartString('bool'),
|
| - inputs));
|
| - }
|
| -
|
| - void handleForeignJsCurrentIsolate(Send node) {
|
| - if (!node.arguments.isEmpty()) {
|
| - compiler.cancel(
|
| - 'Too many arguments to JS_CURRENT_ISOLATE', node: node);
|
| - }
|
| -
|
| - if (!compiler.hasIsolateSupport()) {
|
| - // If the isolate library is not used, we just generate code
|
| - // to fetch the Leg's current isolate.
|
| - String name = compiler.namer.CURRENT_ISOLATE;
|
| - push(new HForeign(new DartString.literal(name),
|
| - const LiteralDartString('var'),
|
| - <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 = compiler.isolateLibrary.find(
|
| - const SourceString('_currentIsolate'));
|
| - if (element === null) {
|
| - compiler.cancel(
|
| - 'Isolate library and compiler mismatch', node: node);
|
| - }
|
| - HStatic target = new HStatic(element);
|
| - add(target);
|
| - push(new HInvokeStatic(Selector.INVOCATION_0,
|
| - <HInstruction>[target]));
|
| - }
|
| - }
|
| -
|
| - void handleForeignJsCallInIsolate(Send node) {
|
| - Link<Node> link = node.arguments;
|
| - if (!compiler.hasIsolateSupport()) {
|
| - // If the isolate library is not used, we just invoke the
|
| - // closure.
|
| - visit(link.tail.head);
|
| - push(new HInvokeClosure(Selector.INVOCATION_0,
|
| - <HInstruction>[pop()]));
|
| - } else {
|
| - // Call a helper method from the isolate library.
|
| - Element element = compiler.isolateLibrary.find(
|
| - const SourceString('_callInIsolate'));
|
| - if (element === null) {
|
| - compiler.cancel(
|
| - 'Isolate library and compiler mismatch', node: node);
|
| - }
|
| - HStatic target = new HStatic(element);
|
| - add(target);
|
| - List<HInstruction> inputs = <HInstruction>[target];
|
| - addGenericSendArgumentsToList(link, inputs);
|
| - push(new HInvokeStatic(Selector.INVOCATION_0, inputs));
|
| - }
|
| - }
|
| -
|
| - void handleForeignDartClosureToJs(Send node) {
|
| - if (node.arguments.isEmpty() || !node.arguments.tail.isEmpty()) {
|
| - compiler.cancel('Exactly one argument required', node: node.arguments);
|
| - }
|
| - Node closure = node.arguments.head;
|
| - Element element = elements[closure];
|
| - if (!Elements.isStaticOrTopLevelFunction(element)) {
|
| - compiler.cancel(
|
| - 'JS_TO_CLOSURE requires a static or top-level method',
|
| - node: closure);
|
| - }
|
| - FunctionElement function = element;
|
| - FunctionParameters parameters = element.computeParameters(compiler);
|
| - if (parameters.optionalParameterCount !== 0) {
|
| - compiler.cancel(
|
| - 'JS_TO_CLOSURE does not handle closure with optional parameters',
|
| - node: closure);
|
| - }
|
| - visit(closure);
|
| - List<HInstruction> inputs = <HInstruction>[pop()];
|
| - String invocationName = compiler.namer.closureInvocationName(
|
| - new Selector(SelectorKind.INVOCATION,
|
| - parameters.requiredParameterCount));
|
| - push(new HForeign(new DartString.literal('#.$invocationName'),
|
| - const LiteralDartString('var'),
|
| - inputs));
|
| - }
|
| -
|
| - handleForeignSend(Send node) {
|
| - Element element = elements[node];
|
| - if (element.name == const SourceString('JS')) {
|
| - handleForeignJs(node);
|
| - } else if (element.name == const SourceString('UNINTERCEPTED')) {
|
| - handleForeignUnintercepted(node);
|
| - } else if (element.name == const SourceString('JS_HAS_EQUALS')) {
|
| - handleForeignJsHasEquals(node);
|
| - } else if (element.name == const SourceString('JS_CURRENT_ISOLATE')) {
|
| - handleForeignJsCurrentIsolate(node);
|
| - } else if (element.name == const SourceString('JS_CALL_IN_ISOLATE')) {
|
| - handleForeignJsCallInIsolate(node);
|
| - } else if (element.name == const SourceString('DART_CLOSURE_TO_JS')) {
|
| - handleForeignDartClosureToJs(node);
|
| - } else if (element.name == const SourceString('native')) {
|
| - native.handleSsaNative(this, node);
|
| - } else {
|
| - throw "Unknown foreign: ${node.selector}";
|
| - }
|
| - }
|
| -
|
| - visitSuperSend(Send node) {
|
| - Selector selector = elements.getSelector(node);
|
| - Element element = elements[node];
|
| - if (element === null) {
|
| - ClassElement cls = work.element.getEnclosingClass();
|
| - element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD);
|
| - HStatic target = new HStatic(element);
|
| - add(target);
|
| - HInstruction self = localsHandler.readThis();
|
| - Identifier identifier = node.selector.asIdentifier();
|
| - String name = identifier.source.slowToString();
|
| - // TODO(ahe): Add the arguments to this list.
|
| - push(new HLiteralList([], true));
|
| - var inputs = <HInstruction>[
|
| - target,
|
| - self,
|
| - graph.addConstantString(new DartString.literal(name)),
|
| - pop()];
|
| - push(new HInvokeSuper(const Selector(SelectorKind.INVOCATION, 2),
|
| - inputs));
|
| - return;
|
| - }
|
| - HInstruction target = new HStatic(element);
|
| - HInstruction context = localsHandler.readThis();
|
| - add(target);
|
| - var inputs = <HInstruction>[target, context];
|
| - if (element.kind == ElementKind.FUNCTION ||
|
| - element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
|
| - addStaticSendArgumentsToList(node, element, inputs);
|
| - push(new HInvokeSuper(selector, inputs));
|
| - } else {
|
| - target = new HInvokeSuper(Selector.GETTER, inputs);
|
| - add(target);
|
| - inputs = <HInstruction>[target];
|
| - addDynamicSendArgumentsToList(node, inputs);
|
| - push(new HInvokeClosure(selector, inputs));
|
| - }
|
| - }
|
| -
|
| - visitStaticSend(Send node) {
|
| - Selector selector = elements.getSelector(node);
|
| - Element element = elements[node];
|
| - if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR) {
|
| - compiler.resolver.resolveMethodElement(element);
|
| - FunctionElement functionElement = element;
|
| - element = functionElement.defaultImplementation;
|
| - }
|
| - HInstruction target = new HStatic(element);
|
| - add(target);
|
| - var inputs = <HInstruction>[];
|
| - inputs.add(target);
|
| - if (element.kind == ElementKind.FUNCTION ||
|
| - element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
|
| - addStaticSendArgumentsToList(node, element, inputs);
|
| - push(new HInvokeStatic(selector, inputs));
|
| - } else {
|
| - if (element.kind == ElementKind.GETTER) {
|
| - target = new HInvokeStatic(Selector.GETTER, inputs);
|
| - add(target);
|
| - inputs = <HInstruction>[target];
|
| - }
|
| - addDynamicSendArgumentsToList(node, inputs);
|
| - push(new HInvokeClosure(selector, inputs));
|
| - }
|
| - }
|
| -
|
| - visitSend(Send node) {
|
| - if (node.isSuperCall) {
|
| - if (node.isPropertyAccess) {
|
| - compiler.unimplemented('super property read', node: node);
|
| - }
|
| - visitSuperSend(node);
|
| - } else if (node.selector is Operator && methodInterceptionEnabled) {
|
| - visitOperatorSend(node);
|
| - } else if (node.isPropertyAccess) {
|
| - generateGetter(node, elements[node]);
|
| - } else if (Elements.isClosureSend(node, elements)) {
|
| - visitClosureSend(node);
|
| - } else {
|
| - Element element = elements[node];
|
| - if (element === null) {
|
| - // Example: f() with 'f' unbound.
|
| - // This can only happen inside an instance method.
|
| - visitDynamicSend(node);
|
| - } else if (element.kind == ElementKind.CLASS) {
|
| - compiler.internalError("Cannot generate code for send", node: node);
|
| - } else if (element.isInstanceMember()) {
|
| - // Example: f() with 'f' bound to instance method.
|
| - visitDynamicSend(node);
|
| - } else if (element.kind === ElementKind.FOREIGN) {
|
| - handleForeignSend(node);
|
| - } else if (!element.isInstanceMember()) {
|
| - // Example: A.f() or f() with 'f' bound to a static function.
|
| - // Also includes new A() or new A.named() which is treated like a
|
| - // static call to a factory.
|
| - visitStaticSend(node);
|
| - } else {
|
| - compiler.internalError("Cannot generate code for send", node: node);
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitNewExpression(NewExpression node) {
|
| - if (node.isConst()) {
|
| - ConstantHandler handler = compiler.constantHandler;
|
| - Constant constant = handler.compileNodeWithDefinitions(node, elements);
|
| - stack.add(graph.addConstant(constant));
|
| - } else {
|
| - visitSend(node.send);
|
| - }
|
| - }
|
| -
|
| - visitSendSet(SendSet node) {
|
| - Operator op = node.assignmentOperator;
|
| - if (node.isSuperCall) {
|
| - compiler.unimplemented('super property store', node: node);
|
| - } else if (node.isIndex) {
|
| - if (!methodInterceptionEnabled) {
|
| - assert(op.source.stringValue === '=');
|
| - visitDynamicSend(node);
|
| - } else {
|
| - HStatic target = new HStatic(
|
| - interceptors.getIndexAssignmentInterceptor());
|
| - add(target);
|
| - visit(node.receiver);
|
| - HInstruction receiver = pop();
|
| - visit(node.argumentsNode);
|
| - if (const SourceString("=") == op.source) {
|
| - HInstruction value = pop();
|
| - HInstruction index = pop();
|
| - add(new HIndexAssign(target, receiver, index, value));
|
| - stack.add(value);
|
| - } else {
|
| - HInstruction value;
|
| - HInstruction index;
|
| - bool isCompoundAssignment = op.source.stringValue.endsWith('=');
|
| - // Compound assignments are considered as being prefix.
|
| - bool isPrefix = !node.isPostfix;
|
| - Element getter = elements[node.selector];
|
| - if (isCompoundAssignment) {
|
| - value = pop();
|
| - index = pop();
|
| - } else {
|
| - index = pop();
|
| - value = graph.addConstantInt(1);
|
| - }
|
| - HStatic indexMethod = new HStatic(interceptors.getIndexInterceptor());
|
| - add(indexMethod);
|
| - HInstruction left = new HIndex(indexMethod, receiver, index);
|
| - add(left);
|
| - Element opElement = elements[op];
|
| - visitBinary(left, op, value);
|
| - value = pop();
|
| - HInstruction assign = new HIndexAssign(
|
| - target, receiver, index, value);
|
| - add(assign);
|
| - if (isPrefix) {
|
| - stack.add(value);
|
| - } else {
|
| - stack.add(left);
|
| - }
|
| - }
|
| - }
|
| - } else if (const SourceString("=") == op.source) {
|
| - Element element = elements[node];
|
| - Link<Node> link = node.arguments;
|
| - assert(!link.isEmpty() && link.tail.isEmpty());
|
| - visit(link.head);
|
| - HInstruction value = pop();
|
| - generateSetter(node, element, value);
|
| - } else if (op.source.stringValue === "is") {
|
| - compiler.internalError("is-operator as SendSet", node: op);
|
| - } else {
|
| - assert(const SourceString("++") == op.source ||
|
| - const SourceString("--") == op.source ||
|
| - node.assignmentOperator.source.stringValue.endsWith("="));
|
| - Element element = elements[node];
|
| - bool isCompoundAssignment = !node.arguments.isEmpty();
|
| - bool isPrefix = !node.isPostfix; // Compound assignments are prefix.
|
| - generateGetter(node, elements[node.selector]);
|
| - HInstruction left = pop();
|
| - HInstruction right;
|
| - if (isCompoundAssignment) {
|
| - visit(node.argumentsNode);
|
| - right = pop();
|
| - } else {
|
| - right = graph.addConstantInt(1);
|
| - }
|
| - visitBinary(left, op, right);
|
| - HInstruction operation = pop();
|
| - assert(operation !== null);
|
| - generateSetter(node, element, operation);
|
| - if (!isPrefix) {
|
| - pop();
|
| - stack.add(left);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitLiteralInt(LiteralInt node) {
|
| - stack.add(graph.addConstantInt(node.value));
|
| - }
|
| -
|
| - void visitLiteralDouble(LiteralDouble node) {
|
| - stack.add(graph.addConstantDouble(node.value));
|
| - }
|
| -
|
| - void visitLiteralBool(LiteralBool node) {
|
| - stack.add(graph.addConstantBool(node.value));
|
| - }
|
| -
|
| - void visitLiteralString(LiteralString node) {
|
| - stack.add(graph.addConstantString(node.dartString));
|
| - }
|
| -
|
| - void visitStringJuxtaposition(StringJuxtaposition node) {
|
| - if (!node.isInterpolation) {
|
| - // This is a simple string with no interpolations.
|
| - stack.add(graph.addConstantString(node.dartString));
|
| - return;
|
| - }
|
| - int offset = node.getBeginToken().charOffset;
|
| - StringBuilderVisitor stringBuilder =
|
| - new StringBuilderVisitor(this, offset);
|
| - stringBuilder.visit(node);
|
| - stack.add(stringBuilder.result());
|
| - }
|
| -
|
| - void visitLiteralNull(LiteralNull node) {
|
| - stack.add(graph.addConstantNull());
|
| - }
|
| -
|
| - visitNodeList(NodeList node) {
|
| - for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
|
| - if (isAborted()) {
|
| - compiler.reportWarning(link.head, 'dead code');
|
| - } else {
|
| - visit(link.head);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitParenthesizedExpression(ParenthesizedExpression node) {
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitOperator(Operator node) {
|
| - // Operators are intercepted in their surrounding Send nodes.
|
| - unreachable();
|
| - }
|
| -
|
| - visitReturn(Return node) {
|
| - HInstruction value;
|
| - if (node.expression === null) {
|
| - value = graph.addConstantNull();
|
| - } else {
|
| - visit(node.expression);
|
| - value = pop();
|
| - }
|
| - close(new HReturn(value)).addSuccessor(graph.exit);
|
| - }
|
| -
|
| - visitThrow(Throw node) {
|
| - if (node.expression === null) {
|
| - HInstruction exception = rethrowableException;
|
| - if (exception === null) {
|
| - exception = graph.addConstantNull();
|
| - compiler.reportError(node,
|
| - 'throw without expression outside catch block');
|
| - }
|
| - close(new HThrow(exception, isRethrow: true));
|
| - } else {
|
| - visit(node.expression);
|
| - close(new HThrow(pop()));
|
| - }
|
| - }
|
| -
|
| - visitTypeAnnotation(TypeAnnotation node) {
|
| - compiler.internalError('visiting type annotation in SSA builder',
|
| - node: node);
|
| - }
|
| -
|
| - visitVariableDefinitions(VariableDefinitions node) {
|
| - for (Link<Node> link = node.definitions.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - Node definition = link.head;
|
| - if (definition is Identifier) {
|
| - HInstruction initialValue = graph.addConstantNull();
|
| - localsHandler.updateLocal(elements[definition], initialValue);
|
| - } else {
|
| - assert(definition is SendSet);
|
| - visitSendSet(definition);
|
| - pop(); // Discard value.
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitLiteralList(LiteralList node) {
|
| - List<HInstruction> inputs = <HInstruction>[];
|
| - for (Link<Node> link = node.elements.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - visit(link.head);
|
| - inputs.add(pop());
|
| - }
|
| - push(new HLiteralList(inputs, node.isConst()));
|
| - }
|
| -
|
| - visitConditional(Conditional node) {
|
| - visit(node.condition);
|
| - HIf condition = new HIf(popBoolified(), true);
|
| - HBasicBlock conditionBlock = close(condition);
|
| - LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| -
|
| - HBasicBlock thenBlock = addNewBlock();
|
| - conditionBlock.addSuccessor(thenBlock);
|
| - open(thenBlock);
|
| - visit(node.thenExpression);
|
| - HInstruction thenInstruction = pop();
|
| - SubGraph thenGraph = new SubGraph(thenBlock, current);
|
| - thenBlock = close(new HGoto());
|
| - LocalsHandler thenLocals = localsHandler;
|
| - localsHandler = savedLocals;
|
| -
|
| - HBasicBlock elseBlock = addNewBlock();
|
| - conditionBlock.addSuccessor(elseBlock);
|
| - open(elseBlock);
|
| - visit(node.elseExpression);
|
| - HInstruction elseInstruction = pop();
|
| - SubGraph elseGraph = new SubGraph(elseBlock, current);
|
| - elseBlock = close(new HGoto());
|
| -
|
| - HBasicBlock joinBlock = addNewBlock();
|
| - thenBlock.addSuccessor(joinBlock);
|
| - elseBlock.addSuccessor(joinBlock);
|
| - condition.blockInformation =
|
| - new HIfBlockInformation(condition, thenGraph, elseGraph, joinBlock);
|
| - open(joinBlock);
|
| -
|
| - localsHandler.mergeWith(thenLocals, joinBlock);
|
| - HPhi phi = new HPhi.manyInputs(null,
|
| - <HInstruction>[thenInstruction, elseInstruction]);
|
| - joinBlock.addPhi(phi);
|
| - stack.add(phi);
|
| - }
|
| -
|
| - visitStringInterpolation(StringInterpolation node) {
|
| - int offset = node.getBeginToken().charOffset;
|
| - StringBuilderVisitor stringBuilder =
|
| - new StringBuilderVisitor(this, offset);
|
| - stringBuilder.visit(node);
|
| - stack.add(stringBuilder.result());
|
| - }
|
| -
|
| - visitStringInterpolationPart(StringInterpolationPart node) {
|
| - // The parts are iterated in visitStringInterpolation.
|
| - unreachable();
|
| - }
|
| -
|
| - visitEmptyStatement(EmptyStatement node) {
|
| - // Do nothing, empty statement.
|
| - }
|
| -
|
| - visitModifiers(Modifiers node) {
|
| - compiler.unimplemented('SsaBuilder.visitModifiers', node: node);
|
| - }
|
| -
|
| - visitBreakStatement(BreakStatement node) {
|
| - work.allowSpeculativeOptimization = false;
|
| - assert(!isAborted());
|
| - TargetElement target = elements[node];
|
| - assert(target !== null);
|
| - JumpHandler handler = jumpTargets[target];
|
| - assert(handler !== null);
|
| - if (node.target === null) {
|
| - handler.generateBreak();
|
| - } else {
|
| - LabelElement label = elements[node.target];
|
| - handler.generateBreak(label);
|
| - }
|
| - }
|
| -
|
| - visitContinueStatement(ContinueStatement node) {
|
| - work.allowSpeculativeOptimization = false;
|
| - TargetElement target = elements[node];
|
| - assert(target !== null);
|
| - JumpHandler handler = jumpTargets[target];
|
| - assert(handler !== null);
|
| - if (node.target === null) {
|
| - handler.generateContinue();
|
| - } else {
|
| - LabelElement label = elements[node.target];
|
| - 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.
|
| - */
|
| - JumpHandler createJumpHandler(Statement node) {
|
| - TargetElement element = elements[node];
|
| - if (element === null || element.statement !== node) {
|
| - // No breaks or continues to this node.
|
| - return const NullJumpHandler();
|
| - }
|
| - return new JumpHandler(this, element);
|
| - }
|
| -
|
| - visitForInStatement(ForInStatement node) {
|
| - // Generate a structure equivalent to:
|
| - // Iterator<E> $iter = <iterable>.iterator()
|
| - // while ($iter.hasNext()) {
|
| - // E <declaredIdentifier> = $iter.next();
|
| - // <body>
|
| - // }
|
| -
|
| - // All the generated calls are to zero-argument functions.
|
| - Selector selector = Selector.INVOCATION_0;
|
| - // The iterator is shared between initializer, condition and body.
|
| - HInstruction iterator;
|
| - void buildInitializer() {
|
| - SourceString iteratorName = const SourceString("iterator");
|
| - Element interceptor = interceptors.getStaticInterceptor(iteratorName, 0);
|
| - assert(interceptor != null);
|
| - HStatic target = new HStatic(interceptor);
|
| - add(target);
|
| - visit(node.expression);
|
| - List<HInstruction> inputs = <HInstruction>[target, pop()];
|
| - iterator = new HInvokeInterceptor(selector, iteratorName, false, inputs);
|
| - add(iterator);
|
| - }
|
| - HInstruction buildCondition() {
|
| - push(new HInvokeDynamicMethod(
|
| - selector, const SourceString('hasNext'), <HInstruction>[iterator]));
|
| - return popBoolified();
|
| - }
|
| - void buildBody() {
|
| - push(new HInvokeDynamicMethod(
|
| - selector, const SourceString('next'), <HInstruction>[iterator]));
|
| -
|
| - Element variable;
|
| - if (node.declaredIdentifier.asSend() !== null) {
|
| - variable = elements[node.declaredIdentifier];
|
| - } else {
|
| - assert(node.declaredIdentifier.asVariableDefinitions() !== null);
|
| - VariableDefinitions variableDefinitions = node.declaredIdentifier;
|
| - variable = elements[variableDefinitions.definitions.nodes.head];
|
| - }
|
| - localsHandler.updateLocal(variable, pop());
|
| -
|
| - visit(node.body);
|
| - }
|
| - handleLoop(node, buildInitializer, buildCondition, () {}, buildBody);
|
| - }
|
| -
|
| - visitLabeledStatement(LabeledStatement node) {
|
| - Statement body = node.getBody();
|
| - if (body is Loop || body is SwitchStatement) {
|
| - // Loops and switches handle their own labels.
|
| - visit(body);
|
| - return;
|
| - }
|
| - // Non-loop statements can only be break targets, not continue targets.
|
| - TargetElement targetElement = elements[body];
|
| - if (targetElement === null || targetElement.statement !== body) {
|
| - // Labeled statements with no element on the body have no breaks.
|
| - // A different target statement only happens if the body is itself
|
| - // a break or continue for a different target. In that case, this
|
| - // label is also always unused.
|
| - visit(body);
|
| - return;
|
| - }
|
| - LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler);
|
| - assert(targetElement.isBreakTarget);
|
| - JumpHandler handler = new JumpHandler(this, targetElement);
|
| - // Introduce a new basic block.
|
| - HBasicBlock entryBlock = graph.addNewBlock();
|
| - goto(current, entryBlock);
|
| - open(entryBlock);
|
| - hackAroundPossiblyAbortingBody(node, () { visit(body); });
|
| - SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock);
|
| -
|
| - HBasicBlock joinBlock = graph.addNewBlock();
|
| - List<LocalsHandler> breakLocals = <LocalsHandler>[];
|
| - handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
|
| - breakInstruction.block.addSuccessor(joinBlock);
|
| - breakLocals.add(locals);
|
| - });
|
| - bool hasBreak = breakLocals.length > 0;
|
| - if (!isAborted()) {
|
| - goto(current, joinBlock);
|
| - breakLocals.add(localsHandler);
|
| - }
|
| - open(joinBlock);
|
| - localsHandler = beforeLocals.mergeMultiple(breakLocals, joinBlock);
|
| -
|
| - if (hasBreak) {
|
| - // There was at least one reachable break, so the label is needed.
|
| - HLabeledBlockInformation blockInfo =
|
| - new HLabeledBlockInformation(bodyGraph, joinBlock, handler.labels());
|
| - entryBlock.labeledBlockInformation = blockInfo;
|
| - }
|
| - handler.close();
|
| - }
|
| -
|
| - visitLiteralMap(LiteralMap node) {
|
| - List<HInstruction> inputs = <HInstruction>[];
|
| - for (Link<Node> link = node.entries.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - visit(link.head);
|
| - inputs.addLast(pop());
|
| - inputs.addLast(pop());
|
| - }
|
| - HLiteralList keyValuePairs = new HLiteralList(inputs, node.isConst());
|
| - HStatic mapMaker = new HStatic(interceptors.getMapMaker());
|
| - add(keyValuePairs);
|
| - add(mapMaker);
|
| - inputs = <HInstruction>[mapMaker, keyValuePairs];
|
| - push(new HInvokeStatic(Selector.INVOCATION_1, inputs));
|
| - }
|
| -
|
| - visitLiteralMapEntry(LiteralMapEntry node) {
|
| - visit(node.value);
|
| - visit(node.key);
|
| - }
|
| -
|
| - visitNamedArgument(NamedArgument node) {
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitSwitchStatement(SwitchStatement node) {
|
| - work.allowSpeculativeOptimization = false;
|
| - LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| - HBasicBlock startBlock = graph.addNewBlock();
|
| - goto(current, startBlock);
|
| - open(startBlock);
|
| - visit(node.expression);
|
| - HInstruction expression = pop();
|
| - if (node.cases.isEmpty()) {
|
| - return;
|
| - }
|
| - Link<Node> cases = node.cases.nodes;
|
| -
|
| - JumpHandler jumpHandler = createJumpHandler(node);
|
| -
|
| - buildSwitchCases(cases, expression);
|
| -
|
| - HBasicBlock lastBlock = lastOpenedBlock;
|
| -
|
| - // Create merge block for break targets.
|
| - HBasicBlock joinBlock = new HBasicBlock();
|
| - List<LocalsHandler> caseLocals = <LocalsHandler>[];
|
| - jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
|
| - instruction.block.addSuccessor(joinBlock);
|
| - caseLocals.add(locals);
|
| - });
|
| - if (!isAborted()) {
|
| - // The current flow is only aborted if the switch has a default that
|
| - // aborts (all previous cases must abort, and if there is no default,
|
| - // it's possible to miss all the cases).
|
| - caseLocals.add(localsHandler);
|
| - goto(current, joinBlock);
|
| - }
|
| - if (caseLocals.length != 0) {
|
| - graph.addBlock(joinBlock);
|
| - open(joinBlock);
|
| - if (caseLocals.length == 1) {
|
| - localsHandler = caseLocals[0];
|
| - } else {
|
| - localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock);
|
| - }
|
| - } else {
|
| - // The joinblock is not used.
|
| - joinBlock = null;
|
| - }
|
| - startBlock.labeledBlockInformation = new HLabeledBlockInformation.implicit(
|
| - new SubGraph(startBlock, lastBlock),
|
| - joinBlock,
|
| - elements[node]);
|
| - jumpHandler.close();
|
| - }
|
| -
|
| -
|
| - // Recursively build an if/else structure to match the cases.
|
| - buildSwitchCases(Link<Node> cases, HInstruction expression) {
|
| - SwitchCase node = cases.head;
|
| -
|
| - // Called for the statements on all but the last case block.
|
| - // Ensures that a user expecting a fallthrough gets an error.
|
| - void visitStatementsAndAbort() {
|
| - visit(node.statements);
|
| - if (!isAborted()) {
|
| - compiler.reportWarning(node, 'Missing break at end of switch case');
|
| - Element element =
|
| - compiler.findHelper(const SourceString("getFallThroughError"));
|
| - push(new HStatic(element));
|
| - HInstruction error = new HInvokeStatic(
|
| - Selector.INVOCATION_0, <HInstruction>[pop()]);
|
| - add(error);
|
| - close(new HThrow(error));
|
| - }
|
| - }
|
| -
|
| - Link<Node> expressions = node.expressions.nodes;
|
| - if (expressions.isEmpty()) {
|
| - // Default case with no expressions.
|
| - if (!node.isDefaultCase) {
|
| - compiler.internalError("Case with no expression and not default",
|
| - node: node);
|
| - }
|
| - visit(node.statements);
|
| - // This must be the final case (otherwise "default" would be invalid),
|
| - // so we don't need to check for fallthrough.
|
| - return;
|
| - }
|
| -
|
| - // Recursively build the test conditions. Leaves the result on the
|
| - // expression stack.
|
| - void buildTests(Link<Node> expressions) {
|
| - // Build comparison for one case expression.
|
| - void left() {
|
| - Element equalsHelper = interceptors.getEqualsInterceptor();
|
| - HInstruction target = new HStatic(equalsHelper);
|
| - add(target);
|
| - visit(expressions.head);
|
| - push(new HEquals(target, pop(), expression));
|
| - }
|
| -
|
| - // If this is the last expression, just return it.
|
| - if (expressions.tail.isEmpty()) {
|
| - left();
|
| - return;
|
| - }
|
| -
|
| - void right() {
|
| - buildTests(expressions.tail);
|
| - }
|
| - handleLogicalAndOr(left, right, isAnd: false);
|
| - }
|
| -
|
| - buildTests(expressions);
|
| - HInstruction result = popBoolified();
|
| -
|
| - if (node.isDefaultCase) {
|
| - // Don't actually use the condition result.
|
| - // This must be final case, so don't check for abort.
|
| - visit(node.statements);
|
| - } else {
|
| - stack.add(result);
|
| - if (cases.tail.isEmpty()) {
|
| - handleIf(() { visit(node.statements); }, null);
|
| - } else {
|
| - handleIf(() { visitStatementsAndAbort(); },
|
| - () { buildSwitchCases(cases.tail, expression); });
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitSwitchCase(SwitchCase node) {
|
| - unreachable();
|
| - }
|
| -
|
| - visitTryStatement(TryStatement node) {
|
| - work.allowSpeculativeOptimization = false;
|
| - HBasicBlock enterBlock = graph.addNewBlock();
|
| - close(new HGoto()).addSuccessor(enterBlock);
|
| - open(enterBlock);
|
| - HTry tryInstruction = new HTry();
|
| - List<HBasicBlock> blocks = <HBasicBlock>[];
|
| - blocks.add(close(tryInstruction));
|
| -
|
| - HBasicBlock tryBody = graph.addNewBlock();
|
| - enterBlock.addSuccessor(tryBody);
|
| - open(tryBody);
|
| - visit(node.tryBlock);
|
| - if (!isAborted()) blocks.add(close(new HGoto()));
|
| -
|
| - if (!node.catchBlocks.isEmpty()) {
|
| - HBasicBlock block = graph.addNewBlock();
|
| - enterBlock.addSuccessor(block);
|
| - open(block);
|
| - // Note that the name of this element is irrelevant.
|
| - Element element = new Element(
|
| - const SourceString('exception'), ElementKind.PARAMETER, work.element);
|
| - HParameterValue exception = new HParameterValue(element);
|
| - add(exception);
|
| - HInstruction oldRethrowableException = rethrowableException;
|
| - rethrowableException = exception;
|
| - push(new HStatic(interceptors.getExceptionUnwrapper()));
|
| - List<HInstruction> inputs = <HInstruction>[pop(), exception];
|
| - HInvokeStatic unwrappedException =
|
| - new HInvokeStatic(Selector.INVOCATION_1, inputs);
|
| - add(unwrappedException);
|
| - tryInstruction.exception = exception;
|
| -
|
| - Link<Node> link = node.catchBlocks.nodes;
|
| -
|
| - void pushCondition(CatchBlock catchBlock) {
|
| - VariableDefinitions declaration = catchBlock.formals.nodes.head;
|
| - HInstruction condition = null;
|
| - if (declaration.type == null) {
|
| - condition = graph.addConstantBool(true);
|
| - stack.add(condition);
|
| - } else {
|
| - Element typeElement = elements[declaration.type];
|
| - if (typeElement == null) {
|
| - compiler.cancel('Catch with unresolved type', node: catchBlock);
|
| - }
|
| - condition = new HIs(typeElement, unwrappedException, nullOk: true);
|
| - push(condition);
|
| - }
|
| - }
|
| -
|
| - void visitThen() {
|
| - CatchBlock catchBlock = link.head;
|
| - link = link.tail;
|
| - localsHandler.updateLocal(elements[catchBlock.exception],
|
| - unwrappedException);
|
| - Node trace = catchBlock.trace;
|
| - if (trace != null) {
|
| - push(new HStatic(interceptors.getTraceFromException()));
|
| - HInstruction traceInstruction = new HInvokeStatic(
|
| - Selector.INVOCATION_1, <HInstruction>[pop(), exception]);
|
| - add(traceInstruction);
|
| - localsHandler.updateLocal(elements[trace], traceInstruction);
|
| - }
|
| - visit(catchBlock);
|
| - }
|
| -
|
| - void visitElse() {
|
| - if (link.isEmpty()) {
|
| - close(new HThrow(exception, isRethrow: true));
|
| - } else {
|
| - CatchBlock newBlock = link.head;
|
| - pushCondition(newBlock);
|
| - handleIf(visitThen, visitElse);
|
| - }
|
| - }
|
| -
|
| - CatchBlock firstBlock = link.head;
|
| - pushCondition(firstBlock);
|
| - handleIf(visitThen, visitElse);
|
| - if (!isAborted()) blocks.add(close(new HGoto()));
|
| - rethrowableException = oldRethrowableException;
|
| - }
|
| -
|
| - if (node.finallyBlock != null) {
|
| - HBasicBlock finallyBlock = graph.addNewBlock();
|
| - enterBlock.addSuccessor(finallyBlock);
|
| - open(finallyBlock);
|
| - visit(node.finallyBlock);
|
| - if (!isAborted()) blocks.add(close(new HGoto()));
|
| - tryInstruction.finallyBlock = finallyBlock;
|
| - }
|
| -
|
| - HBasicBlock exitBlock = graph.addNewBlock();
|
| -
|
| - for (HBasicBlock block in blocks) {
|
| - block.addSuccessor(exitBlock);
|
| - }
|
| -
|
| - open(exitBlock);
|
| - }
|
| -
|
| - visitScriptTag(ScriptTag node) {
|
| - compiler.unimplemented('SsaBuilder.visitScriptTag', node: node);
|
| - }
|
| -
|
| - visitCatchBlock(CatchBlock node) {
|
| - visit(node.block);
|
| - }
|
| -
|
| - visitTypedef(Typedef node) {
|
| - compiler.unimplemented('SsaBuilder.visitTypedef', node: node);
|
| - }
|
| -
|
| - visitTypeVariable(TypeVariable node) {
|
| - compiler.internalError('SsaBuilder.visitTypeVariable');
|
| - }
|
| -
|
| - generateUnimplemented(String reason, [bool isExpression = false]) {
|
| - DartString string = new DartString.literal(reason);
|
| - HInstruction message = graph.addConstantString(string);
|
| -
|
| - // Normally, we would call [close] here. However, then we hit
|
| - // another unimplemented feature: aborting loop body. Simply
|
| - // calling [add] does not work as it asserts that the instruction
|
| - // isn't a control flow instruction. So we inline parts of [add].
|
| - current.addAfter(current.last, new HThrow(message));
|
| - if (isExpression) {
|
| - stack.add(graph.addConstantNull());
|
| - }
|
| - }
|
| -
|
| - /** HACK HACK HACK */
|
| - void hackAroundPossiblyAbortingBody(Node statement, void body()) {
|
| - stack.add(graph.addConstantBool(true));
|
| - buildBody() {
|
| - // TODO(lrn): Make sure to take continue into account.
|
| - body();
|
| - if (isAborted()) {
|
| - compiler.reportWarning(statement, "aborting loop body");
|
| - }
|
| - }
|
| - handleIf(buildBody, null);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Visitor that handles generation of string literals (LiteralString,
|
| - * StringInterpolation), and otherwise delegates to the given visitor for
|
| - * non-literal subexpressions.
|
| - * TODO(lrn): Consider whether to handle compile time constant int/boolean
|
| - * expressions as well.
|
| - */
|
| -class StringBuilderVisitor extends AbstractVisitor {
|
| - final SsaBuilder builder;
|
| -
|
| - /**
|
| - * Offset used for the synthetic operator token used by concat.
|
| - * Can probably be removed when we stop using String.operator+.
|
| - */
|
| - final int offset;
|
| -
|
| - /**
|
| - * Used to collect concatenated string literals into a single literal
|
| - * instead of introducing unnecessary concatenations.
|
| - */
|
| - DartString literalAccumulator = const LiteralDartString("");
|
| -
|
| - /**
|
| - * The string value generated so far (not including that which is still
|
| - * in [literalAccumulator]).
|
| - */
|
| - HInstruction prefix = null;
|
| -
|
| - StringBuilderVisitor(this.builder, this.offset);
|
| -
|
| - void visit(Node node) {
|
| - node.accept(this);
|
| - }
|
| -
|
| - visitNode(Node node) {
|
| - compiler.internalError('unexpected node', node: node);
|
| - }
|
| -
|
| - void visitExpression(Node node) {
|
| - flushLiterals();
|
| - node.accept(builder);
|
| - HInstruction asString = buildToString(node, builder.pop());
|
| - prefix = concat(prefix, asString);
|
| - }
|
| -
|
| - void visitLiteralNull(LiteralNull node) {
|
| - addLiteral(const LiteralDartString("null"));
|
| - }
|
| -
|
| - void visitLiteralInt(LiteralInt node) {
|
| - addLiteral(new DartString.literal(node.value.toString()));
|
| - }
|
| -
|
| - void visitLiteralDouble(LiteralDouble node) {
|
| - addLiteral(new DartString.literal(node.value.toString()));
|
| - }
|
| -
|
| - void visitLiteralBool(LiteralBool node) {
|
| - addLiteral(node.value ? const LiteralDartString("true")
|
| - : const LiteralDartString("false"));
|
| - }
|
| -
|
| - void visitStringInterpolation(StringInterpolation node) {
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - void visitStringInterpolationPart(StringInterpolationPart node) {
|
| - visit(node.expression);
|
| - visit(node.string);
|
| - }
|
| -
|
| - void visitLiteralString(LiteralString node) {
|
| - addLiteral(node.dartString);
|
| - }
|
| -
|
| - void visitStringJuxtaposition(StringJuxtaposition node) {
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - void visitNodeList(NodeList node) {
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - /**
|
| - * Add another literal string to the literalAccumulator.
|
| - */
|
| - void addLiteral(DartString dartString) {
|
| - literalAccumulator = new DartString.concat(literalAccumulator, dartString);
|
| - }
|
| -
|
| - /**
|
| - * Combine the strings in [literalAccumulator] into the prefix instruction.
|
| - * After this, the [literalAccumulator] is empty and [prefix] is non-null.
|
| - */
|
| - void flushLiterals() {
|
| - if (literalAccumulator.isEmpty()) {
|
| - if (prefix === null) {
|
| - prefix = builder.graph.addConstantString(literalAccumulator);
|
| - }
|
| - return;
|
| - }
|
| - HInstruction string = builder.graph.addConstantString(literalAccumulator);
|
| - literalAccumulator = new DartString.empty();
|
| - if (prefix !== null) {
|
| - prefix = concat(prefix, string);
|
| - } else {
|
| - prefix = string;
|
| - }
|
| - }
|
| -
|
| - HInstruction concat(HInstruction left, HInstruction right) {
|
| - SourceString dartMethodName = const SourceString("concat");
|
| - if (!builder.methodInterceptionEnabled) {
|
| - builder.compiler.internalError(
|
| - "Using string interpolations in non-intercepted code.",
|
| - instruction: right);
|
| - }
|
| - Element interceptor =
|
| - builder.interceptors.getStaticInterceptor(dartMethodName, 1);
|
| - if (interceptor === null) {
|
| - builder.compiler.internalError(
|
| - "concat not intercepted.", instruction: left);
|
| - }
|
| - HStatic target = new HStatic(interceptor);
|
| - builder.add(target);
|
| - builder.push(new HInvokeInterceptor(Selector.INVOCATION_1,
|
| - dartMethodName,
|
| - false,
|
| - <HInstruction>[target, left, right]));
|
| - return builder.pop();
|
| - }
|
| -
|
| - HInstruction buildToString(Node node, HInstruction input) {
|
| - SourceString dartMethodName = const SourceString("toString");
|
| - if (!builder.methodInterceptionEnabled) {
|
| - builder.compiler.internalError(
|
| - "Using string interpolations in non-intercepted code.", node: node);
|
| - }
|
| - Element interceptor =
|
| - builder.interceptors.getStaticInterceptor(dartMethodName, 0);
|
| - if (interceptor === null) {
|
| - builder.compiler.internalError(
|
| - "toString not intercepted.", node: node);
|
| - }
|
| - HStatic target = new HStatic(interceptor);
|
| - builder.add(target);
|
| - builder.push(new HInvokeInterceptor(Selector.INVOCATION_0,
|
| - dartMethodName,
|
| - false,
|
| - <HInstruction>[target, input]));
|
| - return builder.pop();
|
| - }
|
| -
|
| - HInstruction result() {
|
| - flushLiterals();
|
| - return prefix;
|
| - }
|
| -}
|
|
|