| Index: pkg/fletchc/lib/src/codegen_visitor.dart
|
| diff --git a/pkg/fletchc/lib/src/codegen_visitor.dart b/pkg/fletchc/lib/src/codegen_visitor.dart
|
| deleted file mode 100644
|
| index cb450ac180cdf8aafbf7ad87ea9ba9994b344f29..0000000000000000000000000000000000000000
|
| --- a/pkg/fletchc/lib/src/codegen_visitor.dart
|
| +++ /dev/null
|
| @@ -1,3286 +0,0 @@
|
| -// Copyright (c) 2015, the Dartino 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.md file.
|
| -
|
| -library fletchc.codegen_visitor;
|
| -
|
| -import 'package:compiler/src/resolution/semantic_visitor.dart';
|
| -
|
| -import 'package:compiler/src/resolution/operators.dart' show
|
| - AssignmentOperator,
|
| - BinaryOperator,
|
| - IncDecOperator,
|
| - UnaryOperator;
|
| -
|
| -import 'package:compiler/src/constants/expressions.dart' show
|
| - BoolFromEnvironmentConstantExpression,
|
| - IntFromEnvironmentConstantExpression,
|
| - StringFromEnvironmentConstantExpression,
|
| - ConstantExpression,
|
| - ConstructedConstantExpression,
|
| - TypeConstantExpression;
|
| -
|
| -import 'package:compiler/src/resolution/tree_elements.dart' show
|
| - TreeElements;
|
| -
|
| -import 'package:compiler/src/util/util.dart' show
|
| - Link;
|
| -
|
| -import 'package:compiler/src/common/names.dart' show
|
| - Names,
|
| - Selectors;
|
| -
|
| -import 'package:compiler/src/universe/use.dart' show DynamicUse, StaticUse;
|
| -
|
| -import 'package:compiler/src/elements/elements.dart';
|
| -import 'package:compiler/src/tree/tree.dart';
|
| -import 'package:compiler/src/universe/call_structure.dart' show
|
| - CallStructure;
|
| -import 'package:compiler/src/universe/selector.dart' show
|
| - Selector;
|
| -import 'package:compiler/src/diagnostics/spannable.dart' show
|
| - Spannable;
|
| -import 'package:compiler/src/dart_types.dart';
|
| -
|
| -import 'fletch_context.dart';
|
| -
|
| -import 'fletch_backend.dart';
|
| -
|
| -import 'fletch_constants.dart' show
|
| - FletchClassConstant,
|
| - FletchClassInstanceConstant;
|
| -
|
| -import 'fletch_function_builder.dart' show
|
| - FletchFunctionBuilder;
|
| -
|
| -import 'fletch_class_builder.dart' show
|
| - FletchClassBuilder;
|
| -
|
| -import 'fletch_selector.dart';
|
| -
|
| -import '../fletch_system.dart';
|
| -
|
| -import 'closure_environment.dart';
|
| -
|
| -import '../incremental/fletchc_incremental.dart' show
|
| - IncrementalCompilationFailed; // TODO(ahe): Remove this import.
|
| -
|
| -import 'fletch_registry.dart' show
|
| - ClosureKind,
|
| - FletchRegistry;
|
| -
|
| -import 'package:compiler/src/diagnostics/diagnostic_listener.dart' show
|
| - DiagnosticMessage;
|
| -
|
| -import 'package:compiler/src/diagnostics/messages.dart' show
|
| - MessageKind;
|
| -
|
| -import 'package:compiler/src/constants/values.dart' show
|
| - ConstantValue;
|
| -
|
| -enum VisitState {
|
| - Value,
|
| - Effect,
|
| - Test,
|
| -}
|
| -
|
| -/**
|
| - * A reference to a local value, including how it should be used
|
| - * (loaded/stored).
|
| - */
|
| -abstract class LocalValue {
|
| - final int slot;
|
| - final Element element;
|
| - LocalValue(this.slot, this.element);
|
| -
|
| - void initialize(BytecodeAssembler assembler);
|
| -
|
| - void load(BytecodeAssembler assembler);
|
| -
|
| - void loadRaw(BytecodeAssembler assembler) {
|
| - load(assembler);
|
| - }
|
| -
|
| - void store(BytecodeAssembler assembler);
|
| -}
|
| -
|
| -/**
|
| - * A reference to a local value that is boxed.
|
| - */
|
| -class BoxedLocalValue extends LocalValue {
|
| - BoxedLocalValue(int slot, Element element) : super(slot, element);
|
| -
|
| - void initialize(BytecodeAssembler assembler) {
|
| - assembler.allocateBoxed();
|
| - }
|
| -
|
| - void load(BytecodeAssembler assembler) {
|
| - assembler.loadBoxedSlot(slot);
|
| - }
|
| -
|
| - void loadRaw(BytecodeAssembler assembler) {
|
| - assembler.loadSlot(slot);
|
| - }
|
| -
|
| - void store(BytecodeAssembler assembler) {
|
| - assembler.storeBoxedSlot(slot);
|
| - }
|
| -
|
| - String toString() => "Boxed($element, $slot)";
|
| -}
|
| -
|
| -/**
|
| - * A reference to a local value that is boxed.
|
| - */
|
| -class UnboxedLocalValue extends LocalValue {
|
| - UnboxedLocalValue(int slot, Element element) : super(slot, element);
|
| -
|
| - void initialize(BytecodeAssembler assembler) {}
|
| -
|
| - void load(BytecodeAssembler assembler) {
|
| - assembler.loadSlot(slot);
|
| - }
|
| -
|
| - void store(BytecodeAssembler assembler) {
|
| - assembler.storeSlot(slot);
|
| - }
|
| -
|
| - String toString() => "Local($element, $slot)";
|
| -}
|
| -
|
| -/**
|
| - * A reference to a local value that is boxed.
|
| - */
|
| -class BoxedParameterValue extends LocalValue {
|
| - BoxedParameterValue(
|
| - int parameter,
|
| - Element element,
|
| - BytecodeAssembler assembler)
|
| - : super(assembler.computeParameterSlot(parameter), element);
|
| -
|
| - void initialize(BytecodeAssembler assembler) {
|
| - assembler.allocateBoxed();
|
| - }
|
| -
|
| - void load(BytecodeAssembler assembler) {
|
| - assembler.loadBoxedParameterSlot(slot);
|
| - }
|
| -
|
| - void loadRaw(BytecodeAssembler assembler) {
|
| - assembler.loadParameterSlot(slot);
|
| - }
|
| -
|
| - void store(BytecodeAssembler assembler) {
|
| - assembler.storeBoxedParameterSlot(slot);
|
| - }
|
| -
|
| - String toString() => "BoxedParameter($element, $slot)";
|
| -}
|
| -
|
| -/**
|
| - * A reference to a local value that is boxed.
|
| - */
|
| -class UnboxedParameterValue extends LocalValue {
|
| - UnboxedParameterValue(
|
| - int parameter,
|
| - Element element,
|
| - BytecodeAssembler assembler)
|
| - : super(assembler.computeParameterSlot(parameter), element);
|
| -
|
| - void initialize(BytecodeAssembler assembler) {}
|
| -
|
| - void load(BytecodeAssembler assembler) {
|
| - assembler.loadParameterSlot(slot);
|
| - }
|
| -
|
| - void store(BytecodeAssembler assembler) {
|
| - assembler.storeParameterSlot(slot);
|
| - }
|
| -
|
| - String toString() => "Parameter($element, $slot)";
|
| -}
|
| -
|
| -class JumpInfo {
|
| - final int stackSize;
|
| - final BytecodeLabel continueLabel;
|
| - final BytecodeLabel breakLabel;
|
| - JumpInfo(this.stackSize, this.continueLabel, this.breakLabel);
|
| -}
|
| -
|
| -class TryBlock {
|
| - final int stackSize;
|
| - final BytecodeLabel finallyLabel;
|
| - final BytecodeLabel finallyReturnLabel;
|
| - TryBlock(this.stackSize, this.finallyLabel, this.finallyReturnLabel);
|
| -}
|
| -
|
| -abstract class CodegenVisitor
|
| - extends SemanticVisitor
|
| - with GetBulkMixin,
|
| - SetBulkMixin,
|
| - ErrorBulkMixin,
|
| - InvokeBulkMixin,
|
| - IndexSetBulkMixin,
|
| - CompoundBulkMixin,
|
| - UnaryBulkMixin,
|
| - BaseBulkMixin,
|
| - BinaryBulkMixin,
|
| - PrefixBulkMixin,
|
| - PostfixBulkMixin,
|
| - NewBulkMixin,
|
| - VariableBulkMixin,
|
| - ParameterBulkMixin,
|
| - FunctionBulkMixin,
|
| - ConstructorBulkMixin,
|
| - InitializerBulkMixin,
|
| - BaseImplementationOfStaticsMixin,
|
| - BaseImplementationOfLocalsMixin,
|
| - SetIfNullBulkMixin
|
| - implements SemanticSendVisitor, SemanticDeclarationVisitor {
|
| - // A literal int can have up to 31 bits of information (32 minus sign).
|
| - static const int LITERAL_INT_MAX = 0x3FFFFFFF;
|
| - static const int MAX_INT64 = (1 << 63) - 1;
|
| - static const int MIN_INT64 = -(1 << 63);
|
| -
|
| - final FletchContext context;
|
| -
|
| - final ClosureEnvironment closureEnvironment;
|
| -
|
| - final ExecutableElement element;
|
| -
|
| - final FletchFunctionBuilder functionBuilder;
|
| -
|
| - final Map<Element, LocalValue> scope = <Element, LocalValue>{};
|
| -
|
| - final Map<Node, JumpInfo> jumpInfo = <Node, JumpInfo>{};
|
| -
|
| - // Stack of try blocks (inner-most first), in the lexical scope.
|
| - Link<TryBlock> tryBlockStack = const Link<TryBlock>();
|
| -
|
| - VisitState visitState;
|
| - BytecodeLabel trueLabel;
|
| - BytecodeLabel falseLabel;
|
| -
|
| - // TODO(ajohnsen): Merge computation into constructor.
|
| - // The slot at which 'this' is stored. In closures, this is overwritten.
|
| - LocalValue thisValue;
|
| -
|
| - TreeElements initializerElements;
|
| -
|
| - List<Element> blockLocals = <Element>[];
|
| -
|
| - /// A FunctionExpression in this set is a named local function declaration.
|
| - /// Many calls to such functions are statically bound. So if `f` is a named
|
| - /// local function declaration, `f()` doesn't need to be registered as a
|
| - /// dynamic send.
|
| - // TODO(ahe): Get rid of this by refactoring initializeLocal. See TODO there.
|
| - final Set<FunctionExpression> functionDeclarations =
|
| - new Set<FunctionExpression>();
|
| -
|
| - CodegenVisitor(this.functionBuilder,
|
| - this.context,
|
| - TreeElements elements,
|
| - this.closureEnvironment,
|
| - this.element)
|
| - : super(elements) {
|
| - if (functionBuilder.isInstanceMember) {
|
| - thisValue = new UnboxedParameterValue(0, null, assembler);
|
| - }
|
| - }
|
| -
|
| - TreeElements get elements {
|
| - if (initializerElements != null) return initializerElements;
|
| - return super.elements;
|
| - }
|
| -
|
| - BytecodeAssembler get assembler => functionBuilder.assembler;
|
| -
|
| - SemanticSendVisitor get sendVisitor => this;
|
| - SemanticDeclarationVisitor get declVisitor => this;
|
| -
|
| - void compile();
|
| -
|
| - ConstantExpression compileConstant(
|
| - Node node,
|
| - {TreeElements elements,
|
| - bool isConst}) {
|
| - if (elements == null) elements = this.elements;
|
| - return context.compileConstant(node, elements, isConst: isConst);
|
| - }
|
| -
|
| - ConstantExpression inspectConstant(
|
| - Node node,
|
| - {TreeElements elements,
|
| - bool isConst}) {
|
| - if (elements == null) elements = this.elements;
|
| - return context.inspectConstant(node, elements, isConst: isConst);
|
| - }
|
| -
|
| - bool isConstNull(Node node) {
|
| - ConstantExpression expression = inspectConstant(node, isConst: false);
|
| - if (expression == null) return false;
|
| - return context.getConstantValue(expression).isNull;
|
| - }
|
| -
|
| - int allocateConstantFromNode(Node node, {TreeElements elements}) {
|
| - ConstantExpression expression = compileConstant(
|
| - node,
|
| - elements: elements,
|
| - isConst: false);
|
| - return functionBuilder.allocateConstant(
|
| - context.getConstantValue(expression));
|
| - }
|
| -
|
| - int allocateConstantClassInstance(int classId) {
|
| - var constant = new FletchClassInstanceConstant(classId);
|
| - context.markConstantUsed(constant);
|
| - return functionBuilder.allocateConstant(constant);
|
| - }
|
| -
|
| - int allocateStringConstant(String string) {
|
| - return functionBuilder.allocateConstant(
|
| - context.backend.constantSystem.createString(
|
| - new DartString.literal(string)));
|
| - }
|
| -
|
| - ClosureInfo get closureInfo => closureEnvironment.closures[element];
|
| -
|
| - LocalValue createLocalValueFor(
|
| - LocalElement element,
|
| - {int slot,
|
| - bool isCapturedValueBoxed: true}) {
|
| - if (slot == null) slot = assembler.stackSize;
|
| - if (closureEnvironment.shouldBeBoxed(element)) {
|
| - if (isCapturedValueBoxed) {
|
| - return new BoxedLocalValue(slot, element);
|
| - }
|
| - LocalValue value = new BoxedLocalValue(assembler.stackSize, element);
|
| - assembler.loadSlot(slot);
|
| - value.initialize(assembler);
|
| - return value;
|
| - }
|
| -
|
| - return new UnboxedLocalValue(slot, element);
|
| - }
|
| -
|
| - LocalValue createLocalValueForParameter(
|
| - ParameterElement parameter,
|
| - int index,
|
| - {bool isCapturedValueBoxed: true}) {
|
| - // TODO(kasperl): Use [ParameterElement.constant] instead when
|
| - // [ConstantValue] can be computed on-the-fly from a [ConstantExpression].
|
| - Expression initializer = parameter.initializer;
|
| - if (initializer != null) {
|
| - // If the parameter has an initializer expression, we ask the context
|
| - // to compile it right away to make sure we enqueue all dependent
|
| - // elements correctly before we start assembling the program.
|
| - context.compileConstant(
|
| - initializer,
|
| - parameter.memberContext.resolvedAst.elements,
|
| - isConst: true);
|
| - }
|
| -
|
| - if (closureEnvironment.shouldBeBoxed(parameter)) {
|
| - if (isCapturedValueBoxed) {
|
| - return new BoxedParameterValue(index, parameter, assembler);
|
| - }
|
| - LocalValue value = new BoxedLocalValue(assembler.stackSize, parameter);
|
| - assembler.loadParameter(index);
|
| - value.initialize(assembler);
|
| - return value;
|
| - }
|
| - return new UnboxedParameterValue(index, parameter, assembler);
|
| - }
|
| -
|
| - void pushVariableDeclaration(LocalValue value) {
|
| - scope[value.element] = value;
|
| - }
|
| -
|
| - void popVariableDeclaration(Element local) {
|
| - scope.remove(local);
|
| - }
|
| -
|
| - void registerDynamicUse(Selector selector);
|
| -
|
| - void registerStaticUse(StaticUse use);
|
| -
|
| - void registerInstantiatedClass(ClassElement klass);
|
| -
|
| - void registerIsCheck(DartType type);
|
| -
|
| - void registerLocalInvoke(LocalElement element, Selector selector);
|
| -
|
| - /// Register that [element] is a closure. This can happen for a tear-off, or
|
| - /// for local functions. See [ClosureKind] for more information about the
|
| - /// various kinds of implicit or explicit closurizations that can occur.
|
| - void registerClosurization(FunctionElement element, ClosureKind kind);
|
| -
|
| - int compileLazyFieldInitializer(FieldElement field);
|
| -
|
| - void invokeMethod(Node node, Selector selector) {
|
| - registerDynamicUse(selector);
|
| - String symbol = context.getSymbolFromSelector(selector);
|
| - int id = context.getSymbolId(symbol);
|
| - int arity = selector.argumentCount;
|
| - int fletchSelector = FletchSelector.encodeMethod(id, arity);
|
| - assembler.invokeMethod(fletchSelector, arity, selector.name);
|
| - }
|
| -
|
| - void invokeGetter(Node node, Name name) {
|
| - registerDynamicUse(new Selector.getter(name));
|
| - String symbol = context.mangleName(name);
|
| - int id = context.getSymbolId(symbol);
|
| - int fletchSelector = FletchSelector.encodeGetter(id);
|
| - assembler.invokeMethod(fletchSelector, 0);
|
| - }
|
| -
|
| - void invokeSetter(Node node, Name name) {
|
| - registerDynamicUse(new Selector.setter(name));
|
| - String symbol = context.mangleName(name);
|
| - int id = context.getSymbolId(symbol);
|
| - int fletchSelector = FletchSelector.encodeSetter(id);
|
| - assembler.invokeMethod(fletchSelector, 1);
|
| - }
|
| -
|
| - void invokeFactory(Node node, int constId, int arity) {
|
| - assembler.invokeFactory(constId, arity);
|
| - }
|
| -
|
| - void invokeStatic(Node node, int constId, int arity) {
|
| - assembler.invokeStatic(constId, arity);
|
| - }
|
| -
|
| - void generateIdentical(Node node) {
|
| - assembler.identical();
|
| - }
|
| -
|
| - void generateIdenticalNonNumeric(Node node) {
|
| - assembler.identicalNonNumeric();
|
| - }
|
| -
|
| - void generateReturn(Node node) {
|
| - assembler.ret();
|
| - }
|
| -
|
| - void generateReturnNull(Node node) {
|
| - assembler.returnNull();
|
| - }
|
| -
|
| - void generateThrow(Node node) {
|
| - assembler.emitThrow();
|
| - }
|
| -
|
| - void generateSwitchCaseMatch(CaseMatch caseMatch, BytecodeLabel ifTrue) {
|
| - assembler.dup();
|
| - int constId = allocateConstantFromNode(caseMatch.expression);
|
| - assembler.loadConst(constId);
|
| - // For debugging, ignore the equality checks in connection
|
| - // with case matches by not associating the calls with
|
| - // any node.
|
| - invokeMethod(null, new Selector.binaryOperator('=='));
|
| - assembler.branchIfTrue(ifTrue);
|
| - }
|
| -
|
| - FletchFunctionBase requireFunction(FunctionElement element) {
|
| - // TODO(johnniwinther): More precise use.
|
| - registerStaticUse(new StaticUse.foreignUse(element));
|
| - return context.backend.getFunctionForElement(element);
|
| - }
|
| -
|
| - FletchFunctionBase requireConstructorInitializer(
|
| - ConstructorElement constructor) {
|
| - assert(constructor.isGenerativeConstructor);
|
| - registerInstantiatedClass(constructor.enclosingClass);
|
| - registerStaticUse(new StaticUse.foreignUse(constructor));
|
| - return context.backend.getConstructorInitializerFunction(constructor);
|
| - }
|
| -
|
| - void doStaticFunctionInvoke(
|
| - Node node,
|
| - FletchFunctionBase function,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - {bool factoryInvoke: false}) {
|
| - if (function.isInstanceMember) loadThis();
|
| - FunctionSignature signature = function.signature;
|
| - int functionId;
|
| - int arity;
|
| - if (signature.hasOptionalParameters &&
|
| - signature.optionalParametersAreNamed) {
|
| - if (FletchBackend.isExactParameterMatch(signature, callStructure)) {
|
| - functionId = function.functionId;
|
| - } else if (callStructure.signatureApplies(signature)) {
|
| - // TODO(ajohnsen): Inline parameter stub?
|
| - FletchFunctionBase stub = context.backend.createParameterStub(
|
| - function,
|
| - callStructure.callSelector);
|
| - functionId = stub.functionId;
|
| - } else {
|
| - doUnresolved(function.name);
|
| - return;
|
| - }
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - arity = callStructure.argumentCount;
|
| - } else if (callStructure != null &&
|
| - callStructure.namedArguments.isNotEmpty) {
|
| - doUnresolved(function.name);
|
| - return;
|
| - } else {
|
| - functionId = function.functionId;
|
| - arity = loadPositionalArguments(arguments, signature, function.name);
|
| - }
|
| - if (function.isInstanceMember) arity++;
|
| - int constId = functionBuilder.allocateConstantFromFunction(functionId);
|
| - if (factoryInvoke) {
|
| - invokeFactory(node, constId, arity);
|
| - } else {
|
| - invokeStatic(node, constId, arity);
|
| - }
|
| - }
|
| -
|
| - void loadThis() {
|
| - thisValue.load(assembler);
|
| - }
|
| -
|
| - /**
|
| - * Load the [arguments] for caling [function], with potential positional
|
| - * arguments.
|
| - *
|
| - * Return the number of arguments pushed onto the stack.
|
| - */
|
| - int loadPositionalArguments(
|
| - NodeList arguments,
|
| - FunctionSignature signature,
|
| - String name) {
|
| - int argumentCount = 0;
|
| - Iterator<Node> it = arguments.iterator;
|
| - signature.orderedForEachParameter((ParameterElement parameter) {
|
| - if (it.moveNext()) {
|
| - visitForValue(it.current);
|
| - } else {
|
| - if (parameter.isOptional) {
|
| - doParameterInitializer(parameter);
|
| - } else {
|
| - doUnresolved(name);
|
| - }
|
| - }
|
| - argumentCount++;
|
| - });
|
| - if (it.moveNext()) doUnresolved(name);
|
| - return argumentCount;
|
| - }
|
| -
|
| - void doParameterInitializer(ParameterElement parameter) {
|
| - Expression initializer = parameter.initializer;
|
| - if (initializer == null) {
|
| - assembler.loadLiteralNull();
|
| - } else {
|
| - var previousElements = initializerElements;
|
| - initializerElements = parameter.resolvedAst.elements;
|
| - visitForValue(initializer);
|
| - initializerElements = previousElements;
|
| - }
|
| - }
|
| -
|
| - void doVisitForValue(Node node) {
|
| - VisitState oldState = visitState;
|
| - visitState = VisitState.Value;
|
| - node.accept(this);
|
| - visitState = oldState;
|
| - }
|
| -
|
| - // Visit the expression [node] and push the result on top of the stack.
|
| - void visitForValue(Node node) {
|
| - doVisitForValue(node);
|
| - }
|
| -
|
| - // Visit the expression [node] and push the result on top of the stack.
|
| - // This method bypasses debug information collection and using this
|
| - // method will not generate breakpoints for the expression evaluation.
|
| - // This is useful when dealing with internal details that the programmer
|
| - // shouldn't care about such as the string concatenation aspects of
|
| - // of string interpolation.
|
| - void visitForValueNoDebugInfo(Node node) {
|
| - doVisitForValue(node);
|
| - }
|
| -
|
| - // Visit the expression [node] without pushing the result on top of the stack.
|
| - void visitForEffect(Node node) {
|
| - VisitState oldState = visitState;
|
| - visitState = VisitState.Effect;
|
| - node.accept(this);
|
| - visitState = oldState;
|
| - }
|
| -
|
| - // Visit the expression [node] with the result being a branch to either
|
| - // [trueLabel] or [falseLabel].
|
| - void visitForTest(
|
| - Node node,
|
| - BytecodeLabel trueLabel,
|
| - BytecodeLabel falseLabel) {
|
| - VisitState oldState = visitState;
|
| - visitState = VisitState.Test;
|
| - BytecodeLabel oldTrueLabel = this.trueLabel;
|
| - this.trueLabel = trueLabel;
|
| - BytecodeLabel oldFalseLabel = this.falseLabel;
|
| - this.falseLabel = falseLabel;
|
| -
|
| - assert(trueLabel != null || falseLabel != null);
|
| - node.accept(this);
|
| -
|
| - visitState = oldState;
|
| - this.trueLabel = oldTrueLabel;
|
| - this.falseLabel = oldFalseLabel;
|
| - }
|
| -
|
| - void negateTest() {
|
| - assert(visitState == VisitState.Test);
|
| - BytecodeLabel temporary = trueLabel;
|
| - trueLabel = falseLabel;
|
| - falseLabel = temporary;
|
| - }
|
| -
|
| - void applyVisitState() {
|
| - if (visitState == VisitState.Effect) {
|
| - assembler.pop();
|
| - } else if (visitState == VisitState.Test) {
|
| - if (trueLabel == null) {
|
| - assembler.branchIfFalse(falseLabel);
|
| - } else if (falseLabel == null) {
|
| - assembler.branchIfTrue(trueLabel);
|
| - } else {
|
| - assembler.branchIfTrue(trueLabel);
|
| - assembler.branch(falseLabel);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitNamedArgument(NamedArgument node) {
|
| - Expression expression = node.expression;
|
| - if (expression != null) {
|
| - visitForValue(expression);
|
| - } else {
|
| - assembler.loadLiteralNull();
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doLocalVariableCompound(
|
| - Node node,
|
| - LocalVariableElement variable,
|
| - AssignmentOperator operator,
|
| - Node rhs) {
|
| - LocalValue value = scope[variable];
|
| - value.load(assembler);
|
| - visitForValue(rhs);
|
| - String operatorName = operator.binaryOperator.name;
|
| - invokeMethod(node, new Selector.binaryOperator(operatorName));
|
| - value.store(assembler);
|
| - }
|
| -
|
| - void visitLocalVariableCompound(
|
| - Send node,
|
| - LocalVariableElement variable,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - doLocalVariableCompound(node, variable, operator, rhs);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitParameterCompound(
|
| - Send node,
|
| - LocalParameterElement parameter,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - doLocalVariableCompound(node, parameter, operator, rhs);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doStaticFieldCompound(
|
| - Node node,
|
| - FieldElement field,
|
| - AssignmentOperator operator,
|
| - Node rhs) {
|
| - doStaticFieldGet(field);
|
| - visitForValue(rhs);
|
| - Selector selector = new Selector.binaryOperator(
|
| - operator.binaryOperator.name);
|
| - invokeMethod(node, selector);
|
| - doStaticFieldSet(field);
|
| - }
|
| -
|
| - void visitTopLevelFieldCompound(
|
| - Send node,
|
| - FieldElement field,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - doStaticFieldCompound(node, field, operator, rhs);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitStaticFieldCompound(
|
| - Send node,
|
| - FieldElement field,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - doStaticFieldCompound(node, field, operator, rhs);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doBinaryOperator(
|
| - Node node,
|
| - Node left,
|
| - Node right,
|
| - BinaryOperator operator) {
|
| - visitForValue(left);
|
| - visitForValue(right);
|
| - // For '==', if either side is a null literal, use identicalNonNumeric.
|
| - if (operator == BinaryOperator.EQ &&
|
| - (isConstNull(left) || isConstNull(right))) {
|
| - generateIdenticalNonNumeric(node);
|
| - return;
|
| - }
|
| -
|
| - Selector selector = new Selector.binaryOperator(operator.name);
|
| - invokeMethod(node, selector);
|
| - }
|
| -
|
| - void visitEquals(
|
| - Send node,
|
| - Node left,
|
| - Node right,
|
| - _) {
|
| - // TODO(ajohnsen): Inject null check (in callee).
|
| - doBinaryOperator(node, left, right, BinaryOperator.EQ);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitNotEquals(
|
| - Send node,
|
| - Node left,
|
| - Node right,
|
| - _) {
|
| - doBinaryOperator(node, left, right, BinaryOperator.EQ);
|
| - if (visitState == VisitState.Test) {
|
| - negateTest();
|
| - } else {
|
| - assembler.negate();
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitBinary(
|
| - Send node,
|
| - Node left,
|
| - BinaryOperator operator,
|
| - Node right,
|
| - _) {
|
| - doBinaryOperator(node, left, right, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitUnary(
|
| - Send node,
|
| - UnaryOperator operator,
|
| - Node value,
|
| - _) {
|
| - visitForValue(value);
|
| - Selector selector = new Selector.unaryOperator(operator.name);
|
| - invokeMethod(node, selector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitNot(
|
| - Send node,
|
| - Node value,
|
| - _) {
|
| - if (visitState == VisitState.Test) {
|
| - visitForTest(value, falseLabel, trueLabel);
|
| - } else {
|
| - visitForValue(value);
|
| - assembler.negate();
|
| - applyVisitState();
|
| - }
|
| - }
|
| -
|
| - void visitIndex(
|
| - Send node,
|
| - Node receiver,
|
| - Node index,
|
| - _) {
|
| - visitForValue(receiver);
|
| - visitForValue(index);
|
| - Selector selector = new Selector.index();
|
| - invokeMethod(node, selector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIndexSet(
|
| - SendSet node,
|
| - Node receiver,
|
| - Node index,
|
| - Node value,
|
| - _) {
|
| - visitForValue(receiver);
|
| - visitForValue(index);
|
| - visitForValue(value);
|
| - Selector selector = new Selector.indexSet();
|
| - invokeMethod(node, selector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLogicalAnd(
|
| - Send node,
|
| - Node left,
|
| - Node right,
|
| - _) {
|
| - if (visitState == VisitState.Test) {
|
| - if (falseLabel == null) {
|
| - BytecodeLabel ifFalse = new BytecodeLabel();
|
| - visitForTest(left, null, ifFalse);
|
| - visitForTest(right, trueLabel, null);
|
| - assembler.bind(ifFalse);
|
| - } else {
|
| - visitForTest(left, null, falseLabel);
|
| - visitForTest(right, trueLabel, falseLabel);
|
| - }
|
| - return;
|
| - }
|
| -
|
| - BytecodeLabel isFalse = new BytecodeLabel();
|
| - assembler.loadLiteralFalse();
|
| -
|
| - visitForTest(left, null, isFalse);
|
| - visitForTest(right, null, isFalse);
|
| - assembler.pop();
|
| - assembler.loadLiteralTrue();
|
| -
|
| - assembler.bind(isFalse);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLogicalOr(
|
| - Send node,
|
| - Node left,
|
| - Node right,
|
| - _) {
|
| - if (visitState == VisitState.Test) {
|
| - if (trueLabel == null) {
|
| - BytecodeLabel ifTrue = new BytecodeLabel();
|
| - visitForTest(left, ifTrue, null);
|
| - visitForTest(right, null, falseLabel);
|
| - assembler.bind(ifTrue);
|
| - } else {
|
| - visitForTest(left, trueLabel, null);
|
| - visitForTest(right, trueLabel, falseLabel);
|
| - }
|
| - return;
|
| - }
|
| -
|
| - BytecodeLabel isTrue = new BytecodeLabel();
|
| - assembler.loadLiteralTrue();
|
| -
|
| - visitForTest(left, isTrue, null);
|
| - visitForTest(right, isTrue, null);
|
| - assembler.pop();
|
| - assembler.loadLiteralFalse();
|
| -
|
| - assembler.bind(isTrue);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitConditional(Conditional node) {
|
| - BytecodeLabel isFalse = new BytecodeLabel();
|
| - BytecodeLabel done = new BytecodeLabel();
|
| -
|
| - assembler.loadLiteralNull();
|
| -
|
| - visitForTest(node.condition, null, isFalse);
|
| -
|
| - assembler.pop();
|
| - visitForValue(node.thenExpression);
|
| - assembler.branch(done);
|
| -
|
| - assembler.bind(isFalse);
|
| - assembler.pop();
|
| - visitForValue(node.elseExpression);
|
| -
|
| - assembler.bind(done);
|
| -
|
| - applyVisitState();
|
| - }
|
| -
|
| - void callIsSelector(
|
| - Node node,
|
| - DartType type,
|
| - // TODO(ahe): Remove [diagnosticLocation] when malformed types are
|
| - // handled.
|
| - Spannable diagnosticLocation) {
|
| - if (type == null || type.isMalformed) {
|
| - assembler.pop();
|
| - generateUnimplementedError(
|
| - diagnosticLocation, "Unhandled type test for malformed $type.");
|
| - return;
|
| - }
|
| -
|
| - registerIsCheck(type);
|
| -
|
| - if (type.isDynamic) {
|
| - assembler.pop();
|
| - assembler.loadLiteralTrue();
|
| - return;
|
| - }
|
| -
|
| - if (type.isTypedef) {
|
| - // TODO(ajohnsen): This only matches with the number of arguments, not
|
| - // the actual argument types.
|
| - TypedefType typedefType = type;
|
| - int arity = typedefType.element.functionSignature.parameterCount;
|
| - int fletchSelector = context.toFletchIsSelector(
|
| - context.backend.compiler.coreClasses.functionClass, arity);
|
| - assembler.invokeTest(fletchSelector, 0);
|
| - return;
|
| - }
|
| -
|
| - if (!type.isInterfaceType) {
|
| - assembler.pop();
|
| - generateUnimplementedError(
|
| - diagnosticLocation, "Unhandled type test for $type.");
|
| - return;
|
| - }
|
| -
|
| - Element element = type.element;
|
| - int fletchSelector = context.toFletchIsSelector(element);
|
| - assembler.invokeTest(fletchSelector, 0);
|
| - }
|
| -
|
| - void doIs(
|
| - Node node,
|
| - Node expression,
|
| - DartType type,
|
| - // TODO(ahe): Remove [diagnosticLocation] when callIsSelector does not
|
| - // require it.
|
| - Spannable diagnosticLocation) {
|
| - visitForValue(expression);
|
| - callIsSelector(node, type, diagnosticLocation);
|
| - }
|
| -
|
| - void visitIs(
|
| - Send node,
|
| - Node expression,
|
| - DartType type,
|
| - _) {
|
| - doIs(node, expression, type, node.arguments.first);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIsNot(
|
| - Send node,
|
| - Node expression,
|
| - DartType type,
|
| - _) {
|
| - doIs(node, expression, type, node.arguments.first);
|
| - if (visitState == VisitState.Test) {
|
| - negateTest();
|
| - } else {
|
| - assembler.negate();
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitAs(
|
| - Send node,
|
| - Node expression,
|
| - DartType type,
|
| - _) {
|
| - // TODO(ajohnsen): To actual type check.
|
| - visitForValue(expression);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisGet(
|
| - Node node,
|
| - _) {
|
| - loadThis();
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doIdenticalCall(Node node, NodeList arguments) {
|
| - assert(arguments.slowLength() == 2);
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - generateIdentical(node);
|
| - }
|
| -
|
| - void handleStaticFunctionGet(
|
| - Send node,
|
| - MethodElement function,
|
| - _) {
|
| - registerClosurization(function, ClosureKind.tearOff);
|
| - FletchFunctionBase target = requireFunction(function);
|
| - FletchClassBuilder classBuilder =
|
| - context.backend.createTearoffClass(target);
|
| - assert(classBuilder.fields == 0);
|
| - int constId = allocateConstantClassInstance(classBuilder.classId);
|
| - assembler.loadConst(constId);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doMainCall(Send node, NodeList arguments) {
|
| - FunctionElement function = context.compiler.mainFunction;
|
| - if (function.isMalformed) {
|
| - DiagnosticMessage message =
|
| - context.compiler.elementsWithCompileTimeErrors[function];
|
| - if (message == null) {
|
| - // TODO(johnniwinther): The error should always be associated with the
|
| - // element.
|
| - // Example triggering this:
|
| - // ```
|
| - // [
|
| - // main() {}
|
| - // ```
|
| - message = context.compiler.reporter.createMessage(
|
| - function, MessageKind.GENERIC, {'text': 'main is malformed.'});
|
| - }
|
| - doCompileError(message);
|
| - return;
|
| - }
|
| - if (context.compiler.libraryLoader.libraries.any(checkCompileError)) return;
|
| -
|
| - // Load up to 'parameterCount' arguments, padding with nulls.
|
| - int parameterCount = function.functionSignature.parameterCount;
|
| - int argumentCount = 0;
|
| - for (Node argument in arguments) {
|
| - if (argumentCount == parameterCount) break;
|
| - visitForValue(argument);
|
| - argumentCount++;
|
| - }
|
| - for (int i = argumentCount; i < parameterCount; i++) {
|
| - assembler.loadLiteralNull();
|
| - }
|
| -
|
| - FletchFunctionBase base = requireFunction(function);
|
| - int constId = functionBuilder.allocateConstantFromFunction(base.functionId);
|
| - invokeStatic(node, constId, parameterCount);
|
| - }
|
| -
|
| - void doStaticallyBoundInvoke(
|
| - Send node,
|
| - MethodElement element,
|
| - NodeList arguments,
|
| - CallStructure callStructure) {
|
| - if (checkCompileError(element)) return;
|
| - if (element.declaration == context.compiler.identicalFunction) {
|
| - doIdenticalCall(node, arguments);
|
| - return;
|
| - }
|
| - if (element.isExternal) {
|
| - // Patch known functions directly.
|
| - if (element == context.backend.fletchExternalInvokeMain) {
|
| - doMainCall(node, arguments);
|
| - return;
|
| - } else if (element == context.backend.fletchExternalCoroutineChange) {
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - assembler.coroutineChange();
|
| - return;
|
| - }
|
| - // TODO(ajohnsen): Define a known set of external functions we allow
|
| - // calls to?
|
| - }
|
| - FletchFunctionBase target = requireFunction(element);
|
| - doStaticFunctionInvoke(node, target, arguments, callStructure);
|
| - }
|
| -
|
| - void handleStaticFunctionInvoke(
|
| - Send node,
|
| - MethodElement element,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - doStaticallyBoundInvoke(
|
| - node, element.declaration, arguments, callStructure);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperMethodInvoke(
|
| - Send node,
|
| - MethodElement element,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - doStaticallyBoundInvoke(node, element, arguments, callStructure);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doSuperCall(Node node, FunctionElement function) {
|
| - registerStaticUse(new StaticUse.foreignUse(function));
|
| - int arity = function.functionSignature.parameterCount + 1;
|
| - FletchFunctionBase base = requireFunction(function);
|
| - int constId = functionBuilder.allocateConstantFromFunction(base.functionId);
|
| - invokeStatic(node, constId, arity);
|
| - }
|
| -
|
| - void visitSuperGetterGet(
|
| - Send node,
|
| - FunctionElement getter,
|
| - _) {
|
| - loadThis();
|
| - doSuperCall(node, getter);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperMethodGet(
|
| - Send node,
|
| - MethodElement method,
|
| - _) {
|
| - registerClosurization(method, ClosureKind.superTearOff);
|
| - loadThis();
|
| - FletchFunctionBase target = requireFunction(method);
|
| - FletchClassBuilder classBuilder =
|
| - context.backend.createTearoffClass(target);
|
| - assert(classBuilder.fields == 1);
|
| - int constId = functionBuilder.allocateConstantFromClass(
|
| - classBuilder.classId);
|
| - assembler.allocate(constId, classBuilder.fields);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperSetterSet(
|
| - SendSet node,
|
| - FunctionElement setter,
|
| - Node rhs,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(rhs);
|
| - doSuperCall(node, setter);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperIndex(
|
| - Send node,
|
| - FunctionElement function,
|
| - Node index,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(index);
|
| - doSuperCall(node, function);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperIndexSet(
|
| - Send node,
|
| - FunctionElement function,
|
| - Node index,
|
| - Node rhs,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(index);
|
| - visitForValue(rhs);
|
| - doSuperCall(node, function);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperCompoundIndexSet(
|
| - Send node,
|
| - FunctionElement getter,
|
| - FunctionElement setter,
|
| - Node index,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(index);
|
| - loadThis();
|
| - assembler.loadLocal(1);
|
| - doSuperCall(node, getter);
|
| - loadThis();
|
| - // Load index
|
| - assembler.loadLocal(2);
|
| - // Load value from index call and call operator.
|
| - assembler.loadLocal(2);
|
| - visitForValue(rhs);
|
| - invokeMethod(node, getAssignmentSelector(operator));
|
| - doSuperCall(node, setter);
|
| - // Override 'index' with result value, and pop everything else.
|
| - assembler.storeLocal(2);
|
| - assembler.popMany(2);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperIndexPostfix(
|
| - SendSet node,
|
| - FunctionElement getter,
|
| - FunctionElement setter,
|
| - Node index,
|
| - IncDecOperator operator,
|
| - _) {
|
| - // TODO(ajohnsen): Fast-case when for effect.
|
| - visitForValue(index);
|
| - loadThis();
|
| - assembler.loadLocal(1);
|
| - doSuperCall(node, getter);
|
| - loadThis();
|
| - // Load index
|
| - assembler.loadLocal(2);
|
| - // Load value from index call and inc/dec.
|
| - assembler.loadLocal(2);
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - // We can now call []= with 'this', 'index' and 'value'.
|
| - doSuperCall(node, setter);
|
| - assembler.pop();
|
| - // Pop result, override 'index' with initial indexed value, and pop again.
|
| - assembler.storeLocal(1);
|
| - assembler.pop();
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperBinary(
|
| - Send node,
|
| - FunctionElement function,
|
| - BinaryOperator operator,
|
| - Node argument,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(argument);
|
| - doSuperCall(node, function);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperEquals(
|
| - Send node,
|
| - FunctionElement function,
|
| - Node argument,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(argument);
|
| - doSuperCall(node, function);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperUnary(
|
| - Send node,
|
| - UnaryOperator operator,
|
| - FunctionElement function,
|
| - _) {
|
| - loadThis();
|
| - doSuperCall(node, function);
|
| - applyVisitState();
|
| - }
|
| -
|
| - int computeFieldIndex(FieldElement field) {
|
| - ClassElement classElement = element.enclosingClass;
|
| - int fieldIndex;
|
| - FletchClassBuilder classBuilder;
|
| - do {
|
| - // We need to find the mixin application of the class, where the field
|
| - // is stored. Iterate until it's found.
|
| - classBuilder = context.backend.registerClassElement(classElement);
|
| - classElement = classElement.implementation;
|
| - int i = 0;
|
| - classElement.forEachInstanceField((_, FieldElement member) {
|
| - if (member == field) {
|
| - assert(fieldIndex == null);
|
| - fieldIndex = i;
|
| - }
|
| - i++;
|
| - });
|
| - classElement = classElement.superclass;
|
| - } while (fieldIndex == null);
|
| - fieldIndex += classBuilder.superclassFields;
|
| - return fieldIndex;
|
| - }
|
| -
|
| - void visitSuperFieldGet(
|
| - Send node,
|
| - FieldElement field,
|
| - _) {
|
| - loadThis();
|
| - assembler.loadField(computeFieldIndex(field));
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitSuperFieldSet(
|
| - SendSet node,
|
| - FieldElement field,
|
| - Node rhs,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(rhs);
|
| - assembler.storeField(computeFieldIndex(field));
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticFieldInvoke(
|
| - Node node,
|
| - FieldElement field,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - doStaticFieldGet(field);
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, callStructure.callSelector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitDynamicPropertyInvoke(
|
| - Send node,
|
| - Node receiver,
|
| - NodeList arguments,
|
| - Selector selector,
|
| - _) {
|
| - visitForValue(receiver);
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, selector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIfNull(
|
| - Send node,
|
| - Node left,
|
| - Node right,
|
| - _) {
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - visitForValue(left);
|
| - assembler.dup();
|
| - assembler.loadLiteralNull();
|
| - assembler.identicalNonNumeric();
|
| - assembler.branchIfFalse(end);
|
| - assembler.pop();
|
| - visitForValue(right);
|
| - assembler.bind(end);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doIfNotNull(Node receiver, void ifNotNull()) {
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - visitForValue(receiver);
|
| - assembler.dup();
|
| - assembler.loadLiteralNull();
|
| - assembler.identicalNonNumeric();
|
| - assembler.branchIfTrue(end);
|
| - ifNotNull();
|
| - assembler.bind(end);
|
| - }
|
| -
|
| - void visitIfNotNullDynamicPropertyInvoke(
|
| - Send node,
|
| - Node receiver,
|
| - NodeList arguments,
|
| - Selector selector,
|
| - _) {
|
| - doIfNotNull(receiver, () {
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, selector);
|
| - });
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitExpressionInvoke(
|
| - Send node,
|
| - Expression receiver,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - visitForValue(receiver);
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, new Selector.call(Names.call, callStructure));
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisPropertyInvoke(
|
| - Send node,
|
| - NodeList arguments,
|
| - Selector selector,
|
| - _) {
|
| - loadThis();
|
| -
|
| - // If the property is statically known to be a field, instead invoke the
|
| - // getter and then invoke 'call(...)' on the value.
|
| - // TODO(ajohnsen): This is a fix that only works when the field is
|
| - // statically known - that is not always the case. Implement VM support?
|
| - Element target = elements[node];
|
| - if (target != null && target.isField) {
|
| - invokeGetter(node, new Name(target.name, element.library));
|
| - selector = new Selector.callClosureFrom(selector);
|
| - }
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, selector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisInvoke(
|
| - Send node,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - loadThis();
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, callStructure.callSelector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitClassTypeLiteralGet(
|
| - Send node,
|
| - ConstantExpression constant,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[visitClassTypeLiteralGet] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitDynamicPropertyGet(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - _) {
|
| - if (name.text == "runtimeType") {
|
| - // TODO(ahe): Implement runtimeType.
|
| - generateUnimplementedError(
|
| - node,
|
| - "'runtimeType' isn't supported in Fletch. See https://goo.gl/ELH6Zc");
|
| - applyVisitState();
|
| - return;
|
| - }
|
| - visitForValue(receiver);
|
| - invokeGetter(node, name);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIfNotNullDynamicPropertyGet(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - _) {
|
| - doIfNotNull(receiver, () {
|
| - invokeGetter(node, name);
|
| - });
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisPropertyGet(
|
| - Send node,
|
| - Name name,
|
| - _) {
|
| - loadThis();
|
| - invokeGetter(node, name);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisPropertySet(
|
| - Send node,
|
| - Name name,
|
| - Node rhs,
|
| - _) {
|
| - loadThis();
|
| - visitForValue(rhs);
|
| - invokeSetter(node, name);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doStaticFieldGet(FieldElement field) {
|
| - if (checkCompileError(field)) return;
|
| - if (field.isConst) {
|
| - int constId = allocateConstantFromNode(
|
| - field.initializer,
|
| - elements: field.resolvedAst.elements);
|
| - assembler.loadConst(constId);
|
| - } else {
|
| - int index = compileLazyFieldInitializer(field);
|
| - if (field.initializer != null) {
|
| - assembler.loadStaticInit(index);
|
| - } else {
|
| - assembler.loadStatic(index);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void handleStaticFieldGet(
|
| - Send node,
|
| - FieldElement field,
|
| - _) {
|
| - doStaticFieldGet(field);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitAssert(Assert node) {
|
| - // TODO(ajohnsen): Emit assert in checked mode.
|
| - }
|
| -
|
| - void visitDynamicPropertySet(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(receiver);
|
| - visitForValue(rhs);
|
| - invokeSetter(node, name);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIfNotNullDynamicPropertySet(
|
| - SendSet node,
|
| - Node receiver,
|
| - Name name,
|
| - Node rhs,
|
| - _) {
|
| - doIfNotNull(receiver, () {
|
| - visitForValue(rhs);
|
| - invokeSetter(node, name);
|
| - });
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doStaticFieldSet(
|
| - FieldElement field) {
|
| - int index = context.getStaticFieldIndex(field, element);
|
| - assembler.storeStatic(index);
|
| - }
|
| -
|
| - void handleStaticFieldSet(
|
| - SendSet node,
|
| - FieldElement field,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(rhs);
|
| - doStaticFieldSet(field);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitStringJuxtaposition(StringJuxtaposition node) {
|
| - // TODO(ajohnsen): This could probably be optimized to string constants in
|
| - // some cases.
|
| - visitForValue(node.first);
|
| - visitForValue(node.second);
|
| - // TODO(ajohnsen): Cache these in context/backend.
|
| - Selector concat = new Selector.binaryOperator('+');
|
| - invokeMethod(node, concat);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitStringInterpolation(StringInterpolation node) {
|
| - // TODO(ajohnsen): Cache this in context/backend.
|
| - Selector concat = new Selector.binaryOperator('+');
|
| - visitForValueNoDebugInfo(node.string);
|
| - for (StringInterpolationPart part in node.parts) {
|
| - visitForValue(part.expression);
|
| - invokeMethod(part.expression, Selectors.toString_);
|
| - LiteralString string = part.string;
|
| - if (string.dartString.isNotEmpty) {
|
| - visitForValueNoDebugInfo(string);
|
| - invokeMethod(null, concat);
|
| - }
|
| - invokeMethod(null, concat);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLiteralNull(LiteralNull node) {
|
| - if (visitState == VisitState.Value) {
|
| - assembler.loadLiteralNull();
|
| - } else if (visitState == VisitState.Test) {
|
| - if (falseLabel != null) assembler.branch(falseLabel);
|
| - }
|
| - }
|
| -
|
| - void visitLiteralSymbol(LiteralSymbol node) {
|
| - int constId = allocateConstantFromNode(node);
|
| - assembler.loadConst(constId);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLiteralBool(LiteralBool node) {
|
| - var expression = compileConstant(node, isConst: false);
|
| - bool isTrue = expression != null &&
|
| - context.getConstantValue(expression).isTrue;
|
| -
|
| - if (visitState == VisitState.Value) {
|
| - if (isTrue) {
|
| - assembler.loadLiteralTrue();
|
| - } else {
|
| - assembler.loadLiteralFalse();
|
| - }
|
| - } else if (visitState == VisitState.Test) {
|
| - if (isTrue) {
|
| - if (trueLabel != null) assembler.branch(trueLabel);
|
| - } else {
|
| - if (falseLabel != null) assembler.branch(falseLabel);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitLiteralInt(LiteralInt node) {
|
| - if (visitState == VisitState.Value) {
|
| - int value = node.value;
|
| - assert(value >= 0);
|
| - if (value > LITERAL_INT_MAX) {
|
| - if ((value < MIN_INT64 || value > MAX_INT64) && !context.enableBigint) {
|
| - generateUnimplementedError(
|
| - node,
|
| - 'Program compiled without support for big integers');
|
| - } else {
|
| - int constId = allocateConstantFromNode(node);
|
| - assembler.loadConst(constId);
|
| - }
|
| - } else {
|
| - assembler.loadLiteral(value);
|
| - }
|
| - } else if (visitState == VisitState.Test) {
|
| - if (falseLabel != null) assembler.branch(falseLabel);
|
| - }
|
| - }
|
| -
|
| - void visitLiteral(Literal node) {
|
| - if (visitState == VisitState.Value) {
|
| - assembler.loadConst(allocateConstantFromNode(node));
|
| - } else if (visitState == VisitState.Test) {
|
| - if (falseLabel != null) assembler.branch(falseLabel);
|
| - }
|
| - }
|
| -
|
| - void visitLiteralList(LiteralList node) {
|
| - if (node.isConst) {
|
| - int constId = allocateConstantFromNode(node);
|
| - assembler.loadConst(constId);
|
| - applyVisitState();
|
| - return;
|
| - }
|
| - ClassElement literalClass = context.backend.growableListClass;
|
| - ConstructorElement constructor = literalClass.lookupDefaultConstructor();
|
| - if (constructor == null) {
|
| - internalError(node, "Failed to lookup default list constructor");
|
| - }
|
| - // Call with empty arguments, as we call the default constructor.
|
| - callConstructor(
|
| - node, constructor, new NodeList.empty(), CallStructure.NO_ARGS);
|
| - Selector add = new Selector.call(new Name('add', null),
|
| - CallStructure.ONE_ARG);
|
| - for (Node element in node.elements) {
|
| - assembler.dup();
|
| - visitForValue(element);
|
| - invokeMethod(node, add);
|
| - assembler.pop();
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLiteralMap(LiteralMap node) {
|
| - if (node.isConst) {
|
| - int constId = allocateConstantFromNode(node);
|
| - assembler.loadConst(constId);
|
| - applyVisitState();
|
| - return;
|
| - }
|
| - ClassElement literalClass =
|
| - context.backend.mapImplementation.implementation;
|
| - ConstructorElement constructor = literalClass.lookupConstructor("");
|
| - if (constructor == null) {
|
| - internalError(literalClass, "Failed to lookup default map constructor");
|
| - return;
|
| - }
|
| - // The default constructor is a redirecting factory constructor. Follow it.
|
| - constructor = constructor.effectiveTarget;
|
| - FletchFunctionBase function = requireFunction(constructor.declaration);
|
| - doStaticFunctionInvoke(
|
| - node,
|
| - function,
|
| - new NodeList.empty(),
|
| - CallStructure.NO_ARGS,
|
| - factoryInvoke: true);
|
| -
|
| - Selector selector = new Selector.indexSet();
|
| - for (Node element in node.entries) {
|
| - assembler.dup();
|
| - visitForValue(element);
|
| - invokeMethod(node, selector);
|
| - assembler.pop();
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLiteralMapEntry(LiteralMapEntry node) {
|
| - assert(visitState == VisitState.Value);
|
| - visitForValue(node.key);
|
| - visitForValue(node.value);
|
| - }
|
| -
|
| - void visitLiteralString(LiteralString node) {
|
| - if (visitState == VisitState.Value) {
|
| - assembler.loadConst(allocateConstantFromNode(node));
|
| - registerInstantiatedClass(
|
| - context.compiler.backend.stringImplementation);
|
| - } else if (visitState == VisitState.Test) {
|
| - if (falseLabel != null) assembler.branch(falseLabel);
|
| - }
|
| - }
|
| -
|
| - void visitCascadeReceiver(CascadeReceiver node) {
|
| - visitForValue(node.expression);
|
| - assembler.dup();
|
| - assert(visitState == VisitState.Value);
|
| - }
|
| -
|
| - void visitCascade(Cascade node) {
|
| - visitForEffect(node.expression);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitParenthesizedExpression(ParenthesizedExpression node) {
|
| - // Visit expression in the same VisitState.
|
| - node.expression.accept(this);
|
| - }
|
| -
|
| - void visitLocalFunctionGet(Send node, LocalFunctionElement function, _) {
|
| - registerClosurization(function, ClosureKind.localFunction);
|
| - handleLocalGet(node, function, _);
|
| - }
|
| -
|
| - void handleLocalGet(
|
| - Send node,
|
| - LocalElement element,
|
| - _) {
|
| - scope[element].load(assembler);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleLocalSet(
|
| - SendSet node,
|
| - LocalElement element,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(rhs);
|
| - scope[element].store(assembler);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLocalFunctionInvoke(
|
| - Send node,
|
| - LocalFunctionElement function,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - // TODO(ahe): We could use loadPositionalArguments if [element] is a local
|
| - // function to avoid generating additional stubs and to avoid registering
|
| - // this as a dynamic call.
|
| - registerLocalInvoke(function, callStructure.callSelector);
|
| - handleLocalInvoke(node, function, arguments, callStructure, _);
|
| - }
|
| -
|
| - void handleLocalInvoke(
|
| - Node node,
|
| - LocalElement element,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - scope[element].load(assembler);
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, callStructure.callSelector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - static Selector getIncDecSelector(IncDecOperator operator) {
|
| - String name = operator == IncDecOperator.INC ? '+' : '-';
|
| - return new Selector.binaryOperator(name);
|
| - }
|
| -
|
| - static Selector getAssignmentSelector(AssignmentOperator operator) {
|
| - String name = operator.binaryOperator.name;
|
| - return new Selector.binaryOperator(name);
|
| - }
|
| -
|
| - void doLocalVariableIncrement(
|
| - Node node,
|
| - LocalVariableElement element,
|
| - IncDecOperator operator,
|
| - bool prefix) {
|
| - // TODO(ajohnsen): Candidate for bytecode: Inc/Dec local with non-Smi
|
| - // bailout.
|
| - LocalValue value = scope[element];
|
| - value.load(assembler);
|
| - // For postfix, keep local, unmodified version, to 'return' after store.
|
| - if (!prefix) assembler.dup();
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - value.store(assembler);
|
| - if (!prefix) assembler.pop();
|
| - }
|
| -
|
| - void visitLocalVariablePrefix(
|
| - SendSet node,
|
| - LocalVariableElement element,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doLocalVariableIncrement(node, element, operator, true);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitParameterPrefix(
|
| - Send node,
|
| - LocalParameterElement parameter,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doLocalVariableIncrement(node, parameter, operator, true);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitLocalVariablePostfix(
|
| - SendSet node,
|
| - LocalVariableElement element,
|
| - IncDecOperator operator,
|
| - _) {
|
| - // If visitState is for effect, we can ignore the return value, thus always
|
| - // generate code for the simpler 'prefix' case.
|
| - bool prefix = (visitState == VisitState.Effect);
|
| - doLocalVariableIncrement(node, element, operator, prefix);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitParameterPostfix(
|
| - SendSet node,
|
| - LocalParameterElement parameter,
|
| - IncDecOperator operator,
|
| - _) {
|
| - // If visitState is for effect, we can ignore the return value, thus always
|
| - // generate code for the simpler 'prefix' case.
|
| - bool prefix = (visitState == VisitState.Effect);
|
| - doLocalVariableIncrement(node, parameter, operator, prefix);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doStaticFieldPrefix(
|
| - Node node,
|
| - FieldElement field,
|
| - IncDecOperator operator) {
|
| - doStaticFieldGet(field);
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - doStaticFieldSet(field);
|
| - }
|
| -
|
| - void doStaticFieldPostfix(
|
| - Node node,
|
| - FieldElement field,
|
| - IncDecOperator operator) {
|
| - doStaticFieldGet(field);
|
| - // For postfix, keep local, unmodified version, to 'return' after store.
|
| - assembler.dup();
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - doStaticFieldSet(field);
|
| - assembler.pop();
|
| - }
|
| -
|
| - void visitStaticFieldPostfix(
|
| - Send node,
|
| - FieldElement field,
|
| - IncDecOperator operator,
|
| - _) {
|
| - if (visitState == VisitState.Effect) {
|
| - doStaticFieldPrefix(node, field, operator);
|
| - } else {
|
| - doStaticFieldPostfix(node, field, operator);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitStaticFieldPrefix(
|
| - Send node,
|
| - FieldElement field,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doStaticFieldPrefix(node, field, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitTopLevelFieldPostfix(
|
| - Send node,
|
| - FieldElement field,
|
| - IncDecOperator operator,
|
| - _) {
|
| - if (visitState == VisitState.Effect) {
|
| - doStaticFieldPrefix(node, field, operator);
|
| - } else {
|
| - doStaticFieldPostfix(node, field, operator);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitTopLevelFieldPrefix(
|
| - Send node,
|
| - FieldElement field,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doStaticFieldPrefix(node, field, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doDynamicPropertyCompound(
|
| - Node node,
|
| - Name name,
|
| - AssignmentOperator operator,
|
| - Node rhs) {
|
| - // Dup receiver for setter.
|
| - assembler.dup();
|
| - invokeGetter(node, name);
|
| - visitForValue(rhs);
|
| - invokeMethod(node, getAssignmentSelector(operator));
|
| - invokeSetter(node, name);
|
| - }
|
| -
|
| - void visitDynamicPropertyCompound(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(receiver);
|
| - doDynamicPropertyCompound(
|
| - node,
|
| - name,
|
| - operator,
|
| - rhs);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIfNotNullDynamicPropertyCompound(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - doIfNotNull(receiver, () {
|
| - doDynamicPropertyCompound(
|
| - node,
|
| - name,
|
| - operator,
|
| - rhs);
|
| - });
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisPropertyCompound(
|
| - Send node,
|
| - Name name,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - loadThis();
|
| - doDynamicPropertyCompound(
|
| - node,
|
| - name,
|
| - operator,
|
| - rhs);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doDynamicPrefix(
|
| - Node node,
|
| - Name name,
|
| - IncDecOperator operator) {
|
| - assembler.dup();
|
| - invokeGetter(node, name);
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - invokeSetter(node, name);
|
| - }
|
| -
|
| - void doIndexPrefix(
|
| - SendSet node,
|
| - Node receiver,
|
| - Node index,
|
| - IncDecOperator operator) {
|
| - visitForValue(receiver);
|
| - visitForValue(index);
|
| - // Load already evaluated receiver and index for '[]' call.
|
| - assembler.loadLocal(1);
|
| - assembler.loadLocal(1);
|
| - invokeMethod(node, new Selector.index());
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - // Use existing evaluated receiver and index for '[]=' call.
|
| - invokeMethod(node, new Selector.indexSet());
|
| - }
|
| -
|
| - void visitIndexPrefix(
|
| - SendSet node,
|
| - Node receiver,
|
| - Node index,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doIndexPrefix(node, receiver, index, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIndexPostfix(
|
| - Send node,
|
| - Node receiver,
|
| - Node index,
|
| - IncDecOperator operator,
|
| - _) {
|
| - if (visitState == VisitState.Effect) {
|
| - doIndexPrefix(node, receiver, index, operator);
|
| - applyVisitState();
|
| - return;
|
| - }
|
| -
|
| - // Reserve slot for result.
|
| - assembler.loadLiteralNull();
|
| - visitForValue(receiver);
|
| - visitForValue(index);
|
| - // Load already evaluated receiver and index for '[]' call.
|
| - assembler.loadLocal(1);
|
| - assembler.loadLocal(1);
|
| - invokeMethod(node, new Selector.index());
|
| - assembler.storeLocal(3);
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - // Use existing evaluated receiver and index for '[]=' call.
|
| - invokeMethod(node, new Selector.indexSet());
|
| - assembler.pop();
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitCompoundIndexSet(
|
| - Send node,
|
| - Node receiver,
|
| - Node index,
|
| - AssignmentOperator operator,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(receiver);
|
| - visitForValue(index);
|
| - // Load already evaluated receiver and index for '[]' call.
|
| - assembler.loadLocal(1);
|
| - assembler.loadLocal(1);
|
| - invokeMethod(node, new Selector.index());
|
| - visitForValue(rhs);
|
| - invokeMethod(node, getAssignmentSelector(operator));
|
| - // Use existing evaluated receiver and index for '[]=' call.
|
| - invokeMethod(node, new Selector.indexSet());
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisPropertyPrefix(
|
| - Send node,
|
| - Name name,
|
| - IncDecOperator operator,
|
| - _) {
|
| - loadThis();
|
| - doDynamicPrefix(node, name, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThisPropertyPostfix(
|
| - Send node,
|
| - Name name,
|
| - IncDecOperator operator,
|
| - _) {
|
| - // If visitState is for effect, we can ignore the return value, thus always
|
| - // generate code for the simpler 'prefix' case.
|
| - if (visitState == VisitState.Effect) {
|
| - loadThis();
|
| - doDynamicPrefix(node, name, operator);
|
| - applyVisitState();
|
| - return;
|
| - }
|
| -
|
| - loadThis();
|
| - invokeGetter(node, name);
|
| - // For postfix, keep local, unmodified version, to 'return' after store.
|
| - assembler.dup();
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - loadThis();
|
| - assembler.loadLocal(1);
|
| - invokeSetter(node, name);
|
| - assembler.popMany(2);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitDynamicPropertyPrefix(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - IncDecOperator operator,
|
| - _) {
|
| - visitForValue(receiver);
|
| - doDynamicPrefix(node, name, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIfNotNullDynamicPropertyPrefix(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doIfNotNull(receiver, () {
|
| - doDynamicPrefix(node, name, operator);
|
| - });
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doDynamicPostfix(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - IncDecOperator operator) {
|
| - int receiverSlot = assembler.stackSize - 1;
|
| - assembler.loadSlot(receiverSlot);
|
| - invokeGetter(node, name);
|
| - // For postfix, keep local, unmodified version, to 'return' after store.
|
| - assembler.dup();
|
| - assembler.loadLiteral(1);
|
| - invokeMethod(node, getIncDecSelector(operator));
|
| - assembler.loadSlot(receiverSlot);
|
| - assembler.loadLocal(1);
|
| - invokeSetter(node, name);
|
| - assembler.popMany(2);
|
| - assembler.storeLocal(1);
|
| - // Pop receiver.
|
| - assembler.pop();
|
| - }
|
| -
|
| - void visitDynamicPropertyPostfix(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - IncDecOperator operator,
|
| - _) {
|
| - // If visitState is for effect, we can ignore the return value, thus always
|
| - // generate code for the simpler 'prefix' case.
|
| - if (visitState == VisitState.Effect) {
|
| - visitForValue(receiver);
|
| - doDynamicPrefix(node, name, operator);
|
| - applyVisitState();
|
| - return;
|
| - }
|
| -
|
| - visitForValue(receiver);
|
| - doDynamicPostfix(node, receiver, name, operator);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIfNotNullDynamicPropertyPostfix(
|
| - Send node,
|
| - Node receiver,
|
| - Name name,
|
| - IncDecOperator operator,
|
| - _) {
|
| - doIfNotNull(receiver, () {
|
| - doDynamicPostfix(
|
| - node, receiver, name, operator);
|
| - });
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitThrow(Throw node) {
|
| - visitForValue(node.expression);
|
| - generateThrow(node);
|
| - // TODO(ahe): It seems suboptimal that each throw is followed by a pop.
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitRethrow(Rethrow node) {
|
| - if (tryBlockStack.isEmpty) {
|
| - doCompileError(context.compiler.reporter.createMessage(
|
| - node, MessageKind.GENERIC, {"text": "Rethrow outside try"}));
|
| - } else {
|
| - TryBlock block = tryBlockStack.head;
|
| - assembler.loadSlot(block.stackSize - 1);
|
| - // TODO(ahe): It seems suboptimal that each throw is followed by a pop.
|
| - generateThrow(node);
|
| - }
|
| - assembler.pop();
|
| - }
|
| -
|
| - void callConstructor(Node node,
|
| - ConstructorElement constructor,
|
| - NodeList arguments,
|
| - CallStructure callStructure) {
|
| - FletchFunctionBase function = requireConstructorInitializer(constructor);
|
| - doStaticFunctionInvoke(node, function, arguments, callStructure);
|
| - }
|
| -
|
| - void doConstConstructorInvoke(ConstantExpression constant) {
|
| - var value = context.getConstantValue(constant);
|
| - context.markConstantUsed(value);
|
| - int constId = functionBuilder.allocateConstant(value);
|
| - assembler.loadConst(constId);
|
| - }
|
| -
|
| - void visitConstConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructedConstantExpression constant,
|
| - _) {
|
| - // TODO(johnniwinther): We should not end up here with an bad constructor.
|
| - if (!checkCompileError(elements[node.send])) {
|
| - doConstConstructorInvoke(constant);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitBoolFromEnvironmentConstructorInvoke(
|
| - NewExpression node,
|
| - BoolFromEnvironmentConstantExpression constant,
|
| - _) {
|
| - doConstConstructorInvoke(constant);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitIntFromEnvironmentConstructorInvoke(
|
| - NewExpression node,
|
| - IntFromEnvironmentConstantExpression constant,
|
| - _) {
|
| - doConstConstructorInvoke(constant);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitStringFromEnvironmentConstructorInvoke(
|
| - NewExpression node,
|
| - StringFromEnvironmentConstantExpression constant,
|
| - _) {
|
| - doConstConstructorInvoke(constant);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitGenerativeConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructorElement constructor,
|
| - InterfaceType type,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - if (!checkCompileError(constructor)) {
|
| - callConstructor(node, constructor.declaration, arguments, callStructure);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitFactoryConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructorElement constructor,
|
| - InterfaceType type,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - // If the constructor has an implementation, the implementation is the
|
| - // factory we want to invoke. Redirect to
|
| - // visitRedirectingFactoryConstructorInvoke, so we handle both cases of
|
| - // either a factory or a redirecting factory.
|
| - if (constructor.implementation != constructor) {
|
| - ConstructorElement implementation = constructor.implementation;
|
| - visitRedirectingFactoryConstructorInvoke(
|
| - node,
|
| - constructor,
|
| - type,
|
| - implementation.effectiveTarget,
|
| - null,
|
| - arguments,
|
| - callStructure,
|
| - null);
|
| - return;
|
| - }
|
| - // TODO(ahe): Remove ".declaration" when issue 23135 is fixed.
|
| - FletchFunctionBase function = requireFunction(constructor.declaration);
|
| - doStaticFunctionInvoke(
|
| - node, function, arguments, callStructure, factoryInvoke: true);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitConstructorIncompatibleInvoke(
|
| - NewExpression node,
|
| - ConstructorElement constructor,
|
| - InterfaceType type,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - if (!checkCompileError(constructor)) {
|
| - doUnresolved(constructor.name);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitRedirectingGenerativeConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructorElement constructor,
|
| - InterfaceType type,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - visitGenerativeConstructorInvoke(
|
| - node,
|
| - constructor,
|
| - type,
|
| - arguments,
|
| - callStructure,
|
| - null);
|
| - }
|
| -
|
| - void visitRedirectingFactoryConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructorElement constructor,
|
| - InterfaceType type,
|
| - ConstructorElement effectiveTarget,
|
| - InterfaceType effectiveTargetType,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - if (effectiveTarget.isGenerativeConstructor) {
|
| - visitGenerativeConstructorInvoke(
|
| - node,
|
| - effectiveTarget,
|
| - effectiveTargetType,
|
| - arguments,
|
| - callStructure,
|
| - null);
|
| - } else {
|
| - visitFactoryConstructorInvoke(
|
| - node,
|
| - effectiveTarget,
|
| - effectiveTargetType,
|
| - arguments,
|
| - callStructure,
|
| - null);
|
| - }
|
| - }
|
| -
|
| - void visitUnresolvedConstructorInvoke(
|
| - NewExpression node,
|
| - Element constructor,
|
| - DartType type,
|
| - NodeList arguments,
|
| - Selector selector,
|
| - _) {
|
| - if (!checkCompileError(constructor.enclosingClass)) {
|
| - doUnresolved(node.send.toString());
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitUnresolvedClassConstructorInvoke(
|
| - NewExpression node,
|
| - Element element,
|
| - DartType type,
|
| - NodeList arguments,
|
| - Selector selector,
|
| - _) {
|
| - doUnresolved(node.send.toString());
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitAbstractClassConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructorElement element,
|
| - InterfaceType type,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - generateUnimplementedError(node, "Cannot allocate abstract class");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitUnresolvedRedirectingFactoryConstructorInvoke(
|
| - NewExpression node,
|
| - ConstructorElement constructor,
|
| - InterfaceType type,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - doUnresolved(node.send.toString());
|
| - applyVisitState();
|
| - }
|
| -
|
| - void doStaticGetterGet(Send node, FunctionElement getter) {
|
| - if (getter == context.backend.fletchExternalNativeError) {
|
| - assembler.loadSlot(0);
|
| - return;
|
| - }
|
| -
|
| - if (getter.isDeferredLoaderGetter) {
|
| - generateUnimplementedError(node, "Deferred loading is not supported.");
|
| - return;
|
| - }
|
| -
|
| - FletchFunctionBase base = requireFunction(getter);
|
| - int constId = functionBuilder.allocateConstantFromFunction(base.functionId);
|
| - invokeStatic(node, constId, 0);
|
| - }
|
| -
|
| - void handleStaticGetterGet(Send node, FunctionElement getter, _) {
|
| - doStaticGetterGet(node, getter);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticGetterInvoke(
|
| - Send node,
|
| - FunctionElement getter,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - doStaticGetterGet(node, getter);
|
| - for (Node argument in arguments) {
|
| - visitForValue(argument);
|
| - }
|
| - invokeMethod(node, callStructure.callSelector);
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticSetterSet(
|
| - Send node,
|
| - FunctionElement setter,
|
| - Node rhs,
|
| - _) {
|
| - visitForValue(rhs);
|
| - FletchFunctionBase base = requireFunction(setter);
|
| - int constId = functionBuilder.allocateConstantFromFunction(base.functionId);
|
| - invokeStatic(node, constId, 1);
|
| - applyVisitState();
|
| - }
|
| -
|
| - /**
|
| - * Load the captured variables of [function], expressed in [info].
|
| - *
|
| - * If [function] captures itself, its field index is returned.
|
| - */
|
| - int pushCapturedVariables(FunctionElement function) {
|
| - ClosureInfo info = closureEnvironment.closures[function];
|
| - if (info == null) {
|
| - // TODO(ahe): Do not throw here, instead fix bug in incremental compiler
|
| - // (see test closure_capture).
|
| - throw new IncrementalCompilationFailed(
|
| - "Internal error: no closure info for $function");
|
| - }
|
| - int index = 0;
|
| - if (info.isThisFree) {
|
| - loadThis();
|
| - index++;
|
| - }
|
| - int thisClosureIndex = -1;
|
| - for (LocalElement element in info.free) {
|
| - if (element == function) {
|
| - // If we capture ourself, remember index and assign into closure after
|
| - // allocation.
|
| - assembler.loadLiteralNull();
|
| - assert(thisClosureIndex == -1);
|
| - thisClosureIndex = index;
|
| - } else {
|
| - // Load the raw value (the 'Box' when by reference).
|
| - scope[element].loadRaw(assembler);
|
| - }
|
| - index++;
|
| - }
|
| - return thisClosureIndex;
|
| - }
|
| -
|
| - void visitFunctionExpression(FunctionExpression node) {
|
| - FunctionElement function = elements[node];
|
| -
|
| - // If the closure captures itself, thisClosureIndex is the field-index in
|
| - // the closure.
|
| - int thisClosureIndex = pushCapturedVariables(function);
|
| - bool needToStoreThisReference = thisClosureIndex >= 0;
|
| -
|
| - FletchClassBuilder classBuilder = context.backend.createClosureClass(
|
| - function,
|
| - closureEnvironment);
|
| - int classConstant = functionBuilder.allocateConstantFromClass(
|
| - classBuilder.classId);
|
| -
|
| - // NOTE: Currently we emit a storeField instruction in case a closure
|
| - // captures itself. Changing fields makes it a mutable object.
|
| - // We can therefore not allocate the object with `immutable = true`.
|
| - // TODO(fletchc-team): Could we restrict this limitation.
|
| - bool immutable = !closureEnvironment.closures[function].free.any(
|
| - closureEnvironment.shouldBeBoxed) && !needToStoreThisReference;
|
| -
|
| - assembler.allocate(
|
| - classConstant, classBuilder.fields, immutable: immutable);
|
| -
|
| - if (needToStoreThisReference) {
|
| - assert(!immutable);
|
| - assembler.dup();
|
| - assembler.storeField(thisClosureIndex);
|
| - }
|
| -
|
| - if (!functionDeclarations.contains(node)) {
|
| - registerClosurization(function, ClosureKind.localFunction);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitExpression(Expression node) {
|
| - generateUnimplementedError(
|
| - node, "Missing visit of expression: ${node.runtimeType}");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitStatement(Node node) {
|
| - generateUnimplementedError(
|
| - node, "Missing visit of statement: ${node.runtimeType}");
|
| - assembler.pop();
|
| - }
|
| -
|
| - void doStatements(NodeList statements) {
|
| - List<Element> oldBlockLocals = blockLocals;
|
| - blockLocals = <Element>[];
|
| - int stackSize = assembler.stackSize;
|
| -
|
| - for (Node statement in statements) {
|
| - statement.accept(this);
|
| - }
|
| -
|
| - int stackSizeDifference = assembler.stackSize - stackSize;
|
| - if (stackSizeDifference != blockLocals.length) {
|
| - internalError(
|
| - statements,
|
| - "Unbalanced number of block locals and stack slots used by block.");
|
| - }
|
| -
|
| - if (blockLocals.length > 0) assembler.popMany(blockLocals.length);
|
| -
|
| - for (int i = blockLocals.length - 1; i >= 0; --i) {
|
| - popVariableDeclaration(blockLocals[i]);
|
| - }
|
| -
|
| - blockLocals = oldBlockLocals;
|
| - }
|
| -
|
| - void visitBlock(Block node) {
|
| - var breakLabel = new BytecodeLabel();
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, breakLabel);
|
| - doStatements(node.statements);
|
| - assembler.bind(breakLabel);
|
| - }
|
| -
|
| - void visitEmptyStatement(EmptyStatement node) {
|
| - }
|
| -
|
| - void visitExpressionStatement(ExpressionStatement node) {
|
| - visitForEffect(node.expression);
|
| - }
|
| -
|
| - // Called before 'return', as an option to replace the already evaluated
|
| - // return value. One example is setters.
|
| - bool get hasAssignmentSemantics => false;
|
| - void optionalReplaceResultValue() { }
|
| -
|
| - void visitReturn(Return node) {
|
| - Expression expression = node.expression;
|
| - bool returnNull = true;
|
| - if (expression != null && !isConstNull(expression)) {
|
| - visitForValue(expression);
|
| - returnNull = false;
|
| - }
|
| -
|
| - // Avoid using the return-null bytecode if we have assignment semantics.
|
| - if (returnNull && hasAssignmentSemantics) {
|
| - assembler.loadLiteralNull();
|
| - returnNull = false;
|
| - }
|
| -
|
| - if (returnNull) {
|
| - callFinallyBlocks(0, false);
|
| - generateReturnNull(node);
|
| - } else {
|
| - callFinallyBlocks(0, true);
|
| - optionalReplaceResultValue();
|
| - generateReturn(node);
|
| - }
|
| - }
|
| -
|
| - // Find the JumpInfo matching the target of [node].
|
| - JumpInfo getJumpTargetInfo(GotoStatement node) {
|
| - JumpTarget target = elements.getTargetOf(node);
|
| - if (target == null) {
|
| - generateUnimplementedError(node, "'$node' not in loop");
|
| - assembler.pop();
|
| - return null;
|
| - }
|
| - Node statement = target.statement;
|
| - JumpInfo info = jumpInfo[statement];
|
| - if (info == null) {
|
| - generateUnimplementedError(node, "'$node' has no target");
|
| - assembler.pop();
|
| - }
|
| - return info;
|
| - }
|
| -
|
| - void callFinallyBlocks(int targetStackSize, bool preserveTop) {
|
| - int popCount = 0;
|
| - for (var block in tryBlockStack) {
|
| - // Break once all exited finally blocks are processed. Finally blocks
|
| - // are ordered by stack size which coincides with scoping. Blocks with
|
| - // stack sizes at least equal to target size are being exited.
|
| - if (block.stackSize < targetStackSize) break;
|
| - if (block.finallyLabel == null) continue;
|
| - if (preserveTop) {
|
| - // We reuse the exception slot as a temporary buffer for the top
|
| - // element, which is located -1 relative to the block's stack size.
|
| - assembler.storeSlot(block.stackSize - 1);
|
| - }
|
| - // TODO(ajohnsen): Don't pop, but let subroutineCall take a 'pop count'
|
| - // argument, just like popAndBranch.
|
| - if (assembler.stackSize > block.stackSize) {
|
| - int sizeDifference = assembler.stackSize - block.stackSize;
|
| - popCount += sizeDifference;
|
| - assembler.popMany(sizeDifference);
|
| - }
|
| - assembler.subroutineCall(block.finallyLabel, block.finallyReturnLabel);
|
| - if (preserveTop) {
|
| - assembler.loadSlot(block.stackSize - 1);
|
| - popCount--;
|
| - }
|
| - }
|
| - // Reallign stack (should be removed, according to above TODO).
|
| - for (int i = 0; i < popCount; i++) {
|
| - // Note we dup, to make sure the top element is the return value.
|
| - assembler.dup();
|
| - }
|
| - }
|
| -
|
| - void unbalancedBranch(GotoStatement node, bool isBreak) {
|
| - JumpInfo info = getJumpTargetInfo(node);
|
| - if (info == null) return;
|
| - callFinallyBlocks(info.stackSize, false);
|
| - BytecodeLabel label = isBreak ? info.breakLabel : info.continueLabel;
|
| - int diff = assembler.stackSize - info.stackSize;
|
| - assembler.popAndBranch(diff, label);
|
| - }
|
| -
|
| - void visitBreakStatement(BreakStatement node) {
|
| - var breakLabel = new BytecodeLabel();
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, breakLabel);
|
| - unbalancedBranch(node, true);
|
| - assembler.bind(breakLabel);
|
| - }
|
| -
|
| - void visitContinueStatement(ContinueStatement node) {
|
| - unbalancedBranch(node, false);
|
| - }
|
| -
|
| - void visitIf(If node) {
|
| - ConstantExpression conditionConstant =
|
| - inspectConstant(node.condition, isConst: false);
|
| -
|
| - if (conditionConstant != null) {
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end);
|
| - if (context.getConstantValue(conditionConstant).isTrue) {
|
| - doScopedStatement(node.thenPart);
|
| - } else if (node.hasElsePart) {
|
| - doScopedStatement(node.elsePart);
|
| - }
|
| - assembler.bind(end);
|
| - return;
|
| - }
|
| -
|
| - BytecodeLabel ifFalse = new BytecodeLabel();
|
| -
|
| - visitForTest(node.condition, null, ifFalse);
|
| - if (node.hasElsePart) {
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end);
|
| - doScopedStatement(node.thenPart);
|
| - assembler.branch(end);
|
| - assembler.bind(ifFalse);
|
| - doScopedStatement(node.elsePart);
|
| - assembler.bind(end);
|
| - } else {
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, ifFalse);
|
| - doScopedStatement(node.thenPart);
|
| - assembler.bind(ifFalse);
|
| - }
|
| - }
|
| -
|
| - void visitFor(For node) {
|
| - List<Element> oldBlockLocals = blockLocals;
|
| - blockLocals = <Element>[];
|
| -
|
| - BytecodeLabel start = new BytecodeLabel();
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - BytecodeLabel afterBody = new BytecodeLabel();
|
| -
|
| - Node initializer = node.initializer;
|
| - if (initializer != null) visitForEffect(initializer);
|
| -
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, afterBody, end);
|
| -
|
| - assembler.bind(start);
|
| -
|
| - Expression condition = node.condition;
|
| - if (condition != null) {
|
| - visitForTest(condition, null, end);
|
| - }
|
| -
|
| - doScopedStatement(node.body);
|
| -
|
| - assembler.bind(afterBody);
|
| -
|
| - for (int i = blockLocals.length - 1; i >= 0; --i) {
|
| - LocalElement local = blockLocals[i];
|
| - // If the locals are captured by reference, load the current value and
|
| - // store it in a new boxed.
|
| - if (closureEnvironment.shouldBeBoxed(local)) {
|
| - LocalValue value = scope[local];
|
| - value.load(assembler);
|
| - value.initialize(assembler);
|
| - assembler.storeSlot(value.slot);
|
| - assembler.pop();
|
| - }
|
| - }
|
| -
|
| - for (Node update in node.update) {
|
| - visitForEffect(update);
|
| - }
|
| - assembler.branch(start);
|
| -
|
| - assembler.bind(end);
|
| -
|
| - for (int i = blockLocals.length - 1; i >= 0; --i) {
|
| - assembler.pop();
|
| - popVariableDeclaration(blockLocals[i]);
|
| - }
|
| -
|
| - blockLocals = oldBlockLocals;
|
| - }
|
| -
|
| - void visitSyncForIn(SyncForIn node) {
|
| - visitForIn(node);
|
| - }
|
| -
|
| - void visitForIn(ForIn node) {
|
| - BytecodeLabel start = new BytecodeLabel();
|
| - BytecodeLabel end = new BytecodeLabel();
|
| -
|
| - // Evalutate expression and iterator.
|
| - visitForValue(node.expression);
|
| - invokeGetter(node.expression, Names.iterator);
|
| -
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, start, end);
|
| -
|
| - assembler.bind(start);
|
| -
|
| - assembler.dup();
|
| - invokeMethod(node, Selectors.moveNext);
|
| - assembler.branchIfFalse(end);
|
| -
|
| - bool isVariableDeclaration = node.declaredIdentifier.asSend() == null;
|
| - Element element = elements[node];
|
| - if (isVariableDeclaration) {
|
| - // Create local value and load the current element to it.
|
| - LocalValue value = createLocalValueFor(element);
|
| - assembler.dup();
|
| - invokeGetter(node, Names.current);
|
| - value.initialize(assembler);
|
| - pushVariableDeclaration(value);
|
| - } else {
|
| - if (element == null || element.isInstanceMember) {
|
| - loadThis();
|
| - assembler.loadLocal(1);
|
| - invokeGetter(node, Names.current);
|
| - Selector selector = elements.getSelector(node.declaredIdentifier);
|
| - invokeSetter(node, selector.memberName);
|
| - } else {
|
| - assembler.dup();
|
| - invokeGetter(node, Names.current);
|
| - if (element.isLocal) {
|
| - scope[element].store(assembler);
|
| - } else if (element.isField) {
|
| - doStaticFieldSet(element);
|
| - } else if (element.isMalformed) {
|
| - doUnresolved(element.name);
|
| - assembler.pop();
|
| - } else {
|
| - internalError(node, "Unhandled store in for-in");
|
| - }
|
| - }
|
| - assembler.pop();
|
| - }
|
| -
|
| - doScopedStatement(node.body);
|
| -
|
| - if (isVariableDeclaration) {
|
| - // Pop the local again.
|
| - assembler.pop();
|
| - popVariableDeclaration(element);
|
| - }
|
| -
|
| - assembler.branch(start);
|
| -
|
| - assembler.bind(end);
|
| -
|
| - // Pop iterator.
|
| - assembler.pop();
|
| - }
|
| -
|
| - void visitLabeledStatement(LabeledStatement node) {
|
| - node.statement.accept(this);
|
| - }
|
| -
|
| - // Visit the statement in a scope, where locals are popped when left.
|
| - void doScopedStatement(Node statement) {
|
| - Block block = statement.asBlock();
|
| - if (block != null) {
|
| - doStatements(block.statements);
|
| - } else {
|
| - doStatements(new NodeList.singleton(statement));
|
| - }
|
| - }
|
| -
|
| - void visitWhile(While node) {
|
| - BytecodeLabel start = new BytecodeLabel();
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, start, end);
|
| - assembler.bind(start);
|
| - visitForTest(node.condition, null, end);
|
| - doScopedStatement(node.body);
|
| - assembler.branch(start);
|
| - assembler.bind(end);
|
| - }
|
| -
|
| - void visitDoWhile(DoWhile node) {
|
| - BytecodeLabel start = new BytecodeLabel();
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - BytecodeLabel skipBody = new BytecodeLabel();
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, skipBody, end);
|
| - assembler.bind(start);
|
| - doScopedStatement(node.body);
|
| - assembler.bind(skipBody);
|
| - visitForTest(node.condition, start, null);
|
| - assembler.bind(end);
|
| - }
|
| -
|
| - LocalValue initializeLocal(LocalElement element, Expression initializer) {
|
| - int slot = assembler.stackSize;
|
| - if (initializer != null) {
|
| - // TODO(ahe): If we can move this to the caller, then we don't need
|
| - // functionDeclarations.
|
| - visitForValue(initializer);
|
| - } else {
|
| - generateEmptyInitializer(element.node);
|
| - }
|
| - LocalValue value = createLocalValueFor(element, slot: slot);
|
| - value.initialize(assembler);
|
| - pushVariableDeclaration(value);
|
| - blockLocals.add(element);
|
| - return value;
|
| - }
|
| -
|
| - void generateEmptyInitializer(Node node) {
|
| - assembler.loadLiteralNull();
|
| - }
|
| -
|
| - void visitVariableDefinitions(VariableDefinitions node) {
|
| - for (Node definition in node.definitions) {
|
| - LocalVariableElement element = elements[definition];
|
| - initializeLocal(element, element.initializer);
|
| - }
|
| - }
|
| -
|
| - void visitFunctionDeclaration(FunctionDeclaration node) {
|
| - FunctionExpression function = node.function;
|
| - functionDeclarations.add(function);
|
| - initializeLocal(elements[function], function);
|
| - }
|
| -
|
| - void visitSwitchStatement(SwitchStatement node) {
|
| - BytecodeLabel end = new BytecodeLabel();
|
| -
|
| - visitForValue(node.expression);
|
| -
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end);
|
| -
|
| - // Install cross-case jump targets.
|
| - for (SwitchCase switchCase in node.cases) {
|
| - BytecodeLabel continueLabel = new BytecodeLabel();
|
| - jumpInfo[switchCase] = new JumpInfo(
|
| - assembler.stackSize,
|
| - continueLabel,
|
| - null);
|
| - }
|
| -
|
| - for (SwitchCase switchCase in node.cases) {
|
| - BytecodeLabel ifTrue = jumpInfo[switchCase].continueLabel;
|
| - BytecodeLabel next = new BytecodeLabel();
|
| - if (!switchCase.isDefaultCase) {
|
| - for (Node labelOrCaseMatch in switchCase.labelsAndCases) {
|
| - CaseMatch caseMatch = labelOrCaseMatch.asCaseMatch();
|
| - if (caseMatch == null) continue;
|
| - generateSwitchCaseMatch(caseMatch, ifTrue);
|
| - }
|
| - assembler.branch(next);
|
| - }
|
| - assembler.bind(ifTrue);
|
| - doStatements(switchCase.statements);
|
| - assembler.branch(end);
|
| - assembler.bind(next);
|
| - }
|
| -
|
| - assembler.bind(end);
|
| - assembler.pop();
|
| - }
|
| -
|
| - void doCatchBlock(CatchBlock node, int exceptionSlot, BytecodeLabel end) {
|
| - BytecodeLabel wrongType = new BytecodeLabel();
|
| -
|
| - TypeAnnotation type = node.type;
|
| - if (type != null) {
|
| - assembler.loadSlot(exceptionSlot);
|
| - callIsSelector(type, elements.getType(type), type);
|
| - assembler.branchIfFalse(wrongType);
|
| - }
|
| -
|
| - List<Element> locals = <Element>[];
|
| - Node exception = node.exception;
|
| - if (exception != null) {
|
| - LocalVariableElement element = elements[exception];
|
| - LocalValue value = createLocalValueFor(element);
|
| - assembler.loadSlot(exceptionSlot);
|
| - value.initialize(assembler);
|
| - pushVariableDeclaration(value);
|
| - locals.add(element);
|
| -
|
| - Node trace = node.trace;
|
| - if (trace != null) {
|
| - LocalVariableElement element = elements[trace];
|
| - LocalValue value = createLocalValueFor(element);
|
| - assembler.loadLiteralNull();
|
| - value.initialize(assembler);
|
| - pushVariableDeclaration(value);
|
| - // TODO(ajohnsen): Set trace.
|
| - locals.add(element);
|
| - }
|
| - }
|
| -
|
| - node.block.accept(this);
|
| -
|
| - assembler.popMany(locals.length);
|
| - for (Element e in locals) {
|
| - popVariableDeclaration(e);
|
| - }
|
| -
|
| - assembler.branch(end);
|
| -
|
| - assembler.bind(wrongType);
|
| - }
|
| -
|
| - void visitTryStatement(TryStatement node) {
|
| - BytecodeLabel end = new BytecodeLabel();
|
| - BytecodeLabel finallyLabel = new BytecodeLabel();
|
| - BytecodeLabel finallyReturnLabel = new BytecodeLabel();
|
| -
|
| - Block finallyBlock = node.finallyBlock;
|
| - bool hasFinally = finallyBlock != null;
|
| -
|
| - // Reserve slot for exception.
|
| - int exceptionSlot = assembler.stackSize;
|
| - assembler.loadLiteralNull();
|
| -
|
| - jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end);
|
| -
|
| - int startBytecodeSize = assembler.byteSize;
|
| -
|
| - tryBlockStack = tryBlockStack.prepend(
|
| - new TryBlock(
|
| - assembler.stackSize,
|
| - hasFinally ? finallyLabel : null,
|
| - hasFinally ? finallyReturnLabel: null));
|
| -
|
| - node.tryBlock.accept(this);
|
| -
|
| - // Go to end if no exceptions was thrown.
|
| - assembler.branch(end);
|
| - int endBytecodeSize = assembler.byteSize;
|
| -
|
| - // Add catch-frame to the assembler.
|
| - assembler.addCatchFrameRange(startBytecodeSize, endBytecodeSize);
|
| -
|
| - for (Node catchBlock in node.catchBlocks) {
|
| - doCatchBlock(catchBlock, exceptionSlot, end);
|
| - }
|
| -
|
| - tryBlockStack = tryBlockStack.tail;
|
| -
|
| - if (hasFinally) {
|
| - if (!node.catchBlocks.isEmpty) {
|
| - assembler.addCatchFrameRange(endBytecodeSize, assembler.byteSize);
|
| - }
|
| - // Catch exception from catch blocks.
|
| - assembler.subroutineCall(finallyLabel, finallyReturnLabel);
|
| - }
|
| -
|
| - // The exception was not caught. Rethrow.
|
| - generateThrow(node);
|
| -
|
| - assembler.bind(end);
|
| -
|
| - if (hasFinally) {
|
| - BytecodeLabel done = new BytecodeLabel();
|
| - assembler.subroutineCall(finallyLabel, finallyReturnLabel);
|
| - assembler.branch(done);
|
| -
|
| - assembler.bind(finallyLabel);
|
| - assembler.applyStackSizeFix(1);
|
| - finallyBlock.accept(this);
|
| - assembler.subroutineReturn(finallyReturnLabel);
|
| -
|
| - assembler.bind(done);
|
| - }
|
| -
|
| - // Pop exception slot.
|
| - assembler.pop();
|
| - }
|
| -
|
| - void doUnresolved(String name) {
|
| - var constString = context.backend.constantSystem.createString(
|
| - new DartString.literal(name));
|
| - context.markConstantUsed(constString);
|
| - assembler.loadConst(functionBuilder.allocateConstant(constString));
|
| - FunctionElement function = context.backend.fletchUnresolved;
|
| - FletchFunctionBase base = requireFunction(function);
|
| - int constId = functionBuilder.allocateConstantFromFunction(base.functionId);
|
| - assembler.invokeStatic(constId, 1);
|
| - }
|
| -
|
| - bool checkCompileError(Element element) {
|
| - DiagnosticMessage message =
|
| - context.compiler.elementsWithCompileTimeErrors[element];
|
| - if (message != null) {
|
| - doCompileError(message);
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - String formatError(DiagnosticMessage diagnosticMessage) {
|
| - return diagnosticMessage.message.computeMessage();
|
| - }
|
| -
|
| -
|
| - void doCompileError(DiagnosticMessage errorMessage) {
|
| - FunctionElement function = context.backend.fletchCompileError;
|
| - FletchFunctionBase base = requireFunction(function);
|
| - int constId = functionBuilder.allocateConstantFromFunction(base.functionId);
|
| - String errorString = formatError(errorMessage);
|
| - ConstantValue stringConstant =
|
| - context.backend.constantSystem.createString(
|
| - new DartString.literal(errorString));
|
| - int messageConstId = functionBuilder.allocateConstant(stringConstant);
|
| - context.markConstantUsed(stringConstant);
|
| - assembler.loadConst(messageConstId);
|
| - registerInstantiatedClass(context.backend.stringImplementation);
|
| - assembler.invokeStatic(constId, 1);
|
| - }
|
| -
|
| - void visitUnresolvedInvoke(
|
| - Send node,
|
| - Element element,
|
| - Node arguments,
|
| - Selector selector,
|
| - _) {
|
| - if (!checkCompileError(element)) {
|
| - doUnresolved(node.selector.toString());
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitUnresolvedGet(
|
| - Send node,
|
| - Element element,
|
| - _) {
|
| - doUnresolved(node.selector.toString());
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitUnresolvedSet(
|
| - Send node,
|
| - Element element,
|
| - Node rhs,
|
| - _) {
|
| - doUnresolved(node.selector.toString());
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticFunctionIncompatibleInvoke(
|
| - Send node,
|
| - MethodElement function,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - if (!checkCompileError(function)) {
|
| - doUnresolved(function.name);
|
| - }
|
| - applyVisitState();
|
| - }
|
| -
|
| - void internalError(Spannable spannable, String reason) {
|
| - context.compiler.reporter.internalError(spannable, reason);
|
| - }
|
| -
|
| - void generateUnimplementedError(Spannable spannable, String reason) {
|
| - context.backend.generateUnimplementedError(
|
| - spannable,
|
| - reason,
|
| - functionBuilder);
|
| - }
|
| -
|
| - String toString() => "FunctionCompiler(${element.name})";
|
| -
|
| - void handleFinalStaticFieldSet(
|
| - SendSet node,
|
| - FieldElement field,
|
| - Node rhs,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[handleFinalStaticFieldSet] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleImmutableLocalSet(
|
| - SendSet node,
|
| - LocalElement element,
|
| - Node rhs,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[handleImmutableLocalSet] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticSetterGet(
|
| - Send node,
|
| - FunctionElement setter,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[handleStaticSetterGet] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticSetterInvoke(
|
| - Send node,
|
| - FunctionElement setter,
|
| - NodeList arguments,
|
| - CallStructure callStructure,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[handleStaticSetterInvoke] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticGetterSet(
|
| - Send node,
|
| - FunctionElement getter,
|
| - Node rhs,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[handleStaticGetterSet] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void handleStaticFunctionSet(
|
| - SendSet node,
|
| - MethodElement function,
|
| - Node rhs,
|
| - _) {
|
| - generateUnimplementedError(
|
| - node, "[handleStaticFunctionSet] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - @override
|
| - void bulkHandleSetIfNull(Node node, _) {
|
| - generateUnimplementedError(
|
| - node, "[bulkHandleSetIfNull] isn't implemented.");
|
| - applyVisitState();
|
| - }
|
| -
|
| - void previsitDeferredAccess(Send node, PrefixElement prefix, _) {
|
| - // We don't support deferred access, so nothing to do for now.
|
| - }
|
| -
|
| - void bulkHandleNode(Node node, String msg, _) {
|
| - generateUnimplementedError(node, msg.replaceAll('#', node.toString()));
|
| - applyVisitState();
|
| - }
|
| -
|
| - void visitNode(Node node) {
|
| - internalError(node, "[visitNode] isn't implemented.");
|
| - }
|
| -
|
| - void apply(Node node, _) {
|
| - internalError(node, "[apply] isn't implemented.");
|
| - }
|
| -
|
| - void applyInitializers(FunctionExpression initializers, _) {
|
| - internalError(initializers, "[applyInitializers] isn't implemented.");
|
| - }
|
| -
|
| - void applyParameters(NodeList parameters, _) {
|
| - internalError(parameters, "[applyParameters] isn't implemented.");
|
| - }
|
| -}
|
| -
|
| -abstract class FletchRegistryMixin {
|
| - FletchRegistry get registry;
|
| - FletchContext get context;
|
| -
|
| - void registerDynamicUse(Selector selector) {
|
| - registry.registerDynamicUse(selector);
|
| - }
|
| -
|
| - void registerStaticUse(StaticUse staticUse) {
|
| - registry.registerStaticUse(staticUse);
|
| - }
|
| -
|
| - void registerInstantiatedClass(ClassElement klass) {
|
| - registry.registerInstantiatedClass(klass);
|
| - }
|
| -
|
| - void registerIsCheck(DartType type) {
|
| - registry.registerIsCheck(type);
|
| - }
|
| -
|
| - void registerLocalInvoke(LocalElement element, Selector selector) {
|
| - registry.registerLocalInvoke(element, selector);
|
| - }
|
| -
|
| - void registerClosurization(FunctionElement element, ClosureKind kind) {
|
| - if (kind == ClosureKind.localFunction) {
|
| - // TODO(ahe): Get rid of the call to [registerStaticUse]. It is
|
| - // currently needed to ensure that local function expression closures are
|
| - // compiled correctly. For example, `[() {}].last()`, notice that `last`
|
| - // is a getter. This happens for both named and unnamed.
|
| - registerStaticUse(new StaticUse.foreignUse(element));
|
| - }
|
| - registry.registerClosurization(element, kind);
|
| - }
|
| -
|
| - int compileLazyFieldInitializer(FieldElement field) {
|
| - return context.backend.compileLazyFieldInitializer(field, registry);
|
| - }
|
| -}
|
|
|