| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| deleted file mode 100644
|
| index bfd273a53bd82b30b83f8a853c8f1554975bc548..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| +++ /dev/null
|
| @@ -1,2932 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library dart2js.ir_builder;
|
| -
|
| -import '../closure.dart' as closure;
|
| -import '../common.dart';
|
| -import '../common/names.dart' show Selectors;
|
| -import '../compile_time_constants.dart' show BackendConstantEnvironment;
|
| -import '../constants/constant_system.dart';
|
| -import '../constants/values.dart' show ConstantValue;
|
| -import '../dart_types.dart';
|
| -import '../elements/elements.dart';
|
| -import '../io/source_information.dart';
|
| -import '../js/js.dart' as js
|
| - show
|
| - js,
|
| - objectLiteral,
|
| - Expression,
|
| - LiteralStatement,
|
| - Template,
|
| - InterpolatedExpression,
|
| - isIdentityTemplate;
|
| -import '../native/native.dart' show NativeBehavior;
|
| -import '../tree/tree.dart' as ast;
|
| -import '../types/types.dart' show TypeMask;
|
| -import '../universe/call_structure.dart' show CallStructure;
|
| -import '../universe/selector.dart' show Selector, SelectorKind;
|
| -import 'cps_ir_builder_task.dart' show GlobalProgramInformation;
|
| -import 'cps_ir_nodes.dart' as ir;
|
| -
|
| -/// A mapping from variable elements to their compile-time values.
|
| -///
|
| -/// Map elements denoted by parameters and local variables to the
|
| -/// [ir.Primitive] that is their value. Parameters and locals are
|
| -/// assigned indexes which can be used to refer to them.
|
| -class Environment {
|
| - /// A map from locals to their environment index.
|
| - final Map<Local, int> variable2index;
|
| -
|
| - /// A reverse map from environment indexes to the variable.
|
| - final List<Local> index2variable;
|
| -
|
| - /// A map from environment indexes to their value.
|
| - final List<ir.Primitive> index2value;
|
| -
|
| - Environment.empty()
|
| - : variable2index = <Local, int>{},
|
| - index2variable = <Local>[],
|
| - index2value = <ir.Primitive>[];
|
| -
|
| - /// Construct an environment that is a copy of another one.
|
| - ///
|
| - /// The mapping from elements to indexes is shared, not copied.
|
| - Environment.from(Environment other)
|
| - : variable2index = other.variable2index,
|
| - index2variable = new List<Local>.from(other.index2variable),
|
| - index2value = new List<ir.Primitive>.from(other.index2value);
|
| -
|
| - /// Construct an environment that is shaped like another one but with a
|
| - /// fresh parameter for each variable.
|
| - ///
|
| - /// The mapping from elements to indexes is shared, not copied.
|
| - Environment.fresh(Environment other)
|
| - : variable2index = other.variable2index,
|
| - index2variable = new List<Local>.from(other.index2variable),
|
| - index2value = other.index2variable.map((Local local) {
|
| - return new ir.Parameter(local);
|
| - }).toList();
|
| -
|
| - get length => index2variable.length;
|
| -
|
| - ir.Primitive operator [](int index) => index2value[index];
|
| -
|
| - void extend(Local element, ir.Primitive value) {
|
| - // Assert that the name is not already in the environment. `null` is used
|
| - // as the name of anonymous variables.
|
| - assert(!variable2index.containsKey(element));
|
| - if (element != null) variable2index[element] = index2variable.length;
|
| - index2variable.add(element);
|
| - index2value.add(value);
|
| - }
|
| -
|
| - /// Drop [count] values from the environment.
|
| - ///
|
| - /// Return the previous last value in the environment for convenience.
|
| - ir.Primitive discard(int count) {
|
| - assert(count > 0);
|
| - assert(count <= index2variable.length);
|
| - ir.Primitive value = index2value.last;
|
| - // The map from variables to their index are shared, so we cannot remove
|
| - // the mapping in `variable2index`.
|
| - index2variable.length -= count;
|
| - index2value.length -= count;
|
| - return value;
|
| - }
|
| -
|
| - ir.Primitive lookup(Local element) {
|
| - assert(invariant(element, variable2index.containsKey(element),
|
| - message: "Unknown variable: $element."));
|
| - return index2value[variable2index[element]];
|
| - }
|
| -
|
| - void update(Local element, ir.Primitive value) {
|
| - index2value[variable2index[element]] = value;
|
| - }
|
| -
|
| - /// Verify that the variable2index and index2variable maps agree up to the
|
| - /// index [length] exclusive.
|
| - bool sameDomain(int length, Environment other) {
|
| - assert(this.length >= length);
|
| - assert(other.length >= length);
|
| - for (int i = 0; i < length; ++i) {
|
| - // An index maps to the same variable in both environments.
|
| - Local variable = index2variable[i];
|
| - if (variable != other.index2variable[i]) return false;
|
| -
|
| - // A named variable maps to the same index in both environments.
|
| - if (variable != null) {
|
| - int index = variable2index[variable];
|
| - if (index == null || index != other.variable2index[variable]) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool contains(Local local) => variable2index.containsKey(local);
|
| -}
|
| -
|
| -/// The abstract base class of objects that emit jumps to a continuation and
|
| -/// give a handle to the continuation and its environment.
|
| -abstract class JumpCollector {
|
| - final JumpTarget target;
|
| -
|
| - ir.Continuation _continuation = null;
|
| - final Environment _continuationEnvironment;
|
| -
|
| - final List<Iterable<LocalVariableElement>> _boxedTryVariables =
|
| - <Iterable<LocalVariableElement>>[];
|
| -
|
| - /// Construct a collector for a given environment and optionally a target.
|
| - ///
|
| - /// The environment is the one in effect at the point where the jump's
|
| - /// continuation will be bound. Continuations can take an extra argument
|
| - /// (see [addJump]).
|
| - JumpCollector(
|
| - this._continuationEnvironment, this.target, bool hasExtraArgument) {
|
| - if (hasExtraArgument) _continuationEnvironment.extend(null, null);
|
| - }
|
| -
|
| - /// Construct a collector for collecting only return jumps.
|
| - ///
|
| - /// There is no jump target, it is implicitly the exit from the function.
|
| - /// There is no environment at the destination.
|
| - JumpCollector.retrn(this._continuation)
|
| - : _continuationEnvironment = null,
|
| - target = null;
|
| -
|
| - /// Construct a collector for collecting goto jumps.
|
| - ///
|
| - /// There is no continuation or environment at the destination.
|
| - JumpCollector.goto(this.target) : _continuationEnvironment = null;
|
| -
|
| - /// True if the collector has not recorded any jumps to its continuation.
|
| - bool get isEmpty;
|
| -
|
| - /// The continuation encapsulated by this collector.
|
| - ir.Continuation get continuation;
|
| -
|
| - /// The compile-time environment to be used for translating code in the body
|
| - /// of the continuation.
|
| - Environment get environment;
|
| -
|
| - /// Emit a jump to the continuation for a given [IrBuilder].
|
| - ///
|
| - /// Jumps can take a single extra argument. This is used to pass return
|
| - /// values to finally blocks for returns inside try/finally and to pass
|
| - /// values of expressions that have internal control flow to their join-point
|
| - /// continuations.
|
| - void addJump(IrBuilder builder,
|
| - [ir.Primitive value, SourceInformation sourceInformation]);
|
| -
|
| - /// Add a set of variables that were boxed on entry to a try block.
|
| - ///
|
| - /// All jumps from a try block to targets outside have to unbox the
|
| - /// variables that were boxed on entry before invoking the target
|
| - /// continuation. Call this function before translating a try block and
|
| - /// call [leaveTry] after translating it.
|
| - void enterTry(Iterable<LocalVariableElement> boxedOnEntry) {
|
| - // The boxed variables are maintained as a stack to make leaving easy.
|
| - _boxedTryVariables.add(boxedOnEntry);
|
| - }
|
| -
|
| - /// Remove the most recently added set of variables boxed on entry to a try
|
| - /// block.
|
| - ///
|
| - /// Call [enterTry] before translating a try block and call this function
|
| - /// after translating it.
|
| - void leaveTry() {
|
| - _boxedTryVariables.removeLast();
|
| - }
|
| -
|
| - void _buildTryExit(IrBuilder builder) {
|
| - for (Iterable<LocalVariableElement> boxedOnEntry in _boxedTryVariables) {
|
| - for (LocalVariableElement variable in boxedOnEntry) {
|
| - assert(builder.isInMutableVariable(variable));
|
| - ir.Primitive value = builder.buildLocalGet(variable);
|
| - builder.environment.update(variable, value);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// True if a jump inserted now will escape from a try block.
|
| - ///
|
| - /// Concretely, this is true when [enterTry] has been called without
|
| - /// its corresponding [leaveTry] call.
|
| - bool get isEscapingTry => _boxedTryVariables.isNotEmpty;
|
| -}
|
| -
|
| -/// A class to collect 'forward' jumps.
|
| -///
|
| -/// A forward jump to a continuation in the sense of the CPS translation is
|
| -/// a jump where the jump is emitted before any code in the body of the
|
| -/// continuation is translated. They have the property that continuation
|
| -/// parameters and the environment for the translation of the body can be
|
| -/// determined based on the invocations, before translating the body. A
|
| -/// [ForwardJumpCollector] can encapsulate a continuation where all the
|
| -/// jumps are forward ones.
|
| -///
|
| -/// Examples of forward jumps in the translation are join points of
|
| -/// if-then-else and breaks from loops.
|
| -///
|
| -/// The implementation strategy is that the collector collects invocation
|
| -/// sites and the environments at those sites. Then it constructs a
|
| -/// continuation 'on demand' after all the jumps are seen. It determines
|
| -/// continuation parameters, the environment for the translation of code in
|
| -/// the continuation body, and the arguments at the invocation site only
|
| -/// after all the jumps to the continuation are seen.
|
| -class ForwardJumpCollector extends JumpCollector {
|
| - final List<ir.InvokeContinuation> _invocations = <ir.InvokeContinuation>[];
|
| - final List<Environment> _invocationEnvironments = <Environment>[];
|
| -
|
| - /// Construct a collector with a given base environment.
|
| - ///
|
| - /// The base environment is the one in scope at the site that the
|
| - /// continuation represented by this collector will be bound. The
|
| - /// environment is copied by the collector. Subsequent mutation of the
|
| - /// original environment will not affect the collector.
|
| - ForwardJumpCollector(Environment environment,
|
| - {JumpTarget target, bool hasExtraArgument: false})
|
| - : super(new Environment.from(environment), target, hasExtraArgument);
|
| -
|
| - bool get isEmpty => _invocations.isEmpty;
|
| -
|
| - ir.Continuation get continuation {
|
| - if (_continuation == null) _setContinuation();
|
| - return _continuation;
|
| - }
|
| -
|
| - Environment get environment {
|
| - if (_continuation == null) _setContinuation();
|
| - return _continuationEnvironment;
|
| - }
|
| -
|
| - void addJump(IrBuilder builder,
|
| - [ir.Primitive value, SourceInformation sourceInformation]) {
|
| - assert(_continuation == null);
|
| - _buildTryExit(builder);
|
| - ir.InvokeContinuation invoke =
|
| - new ir.InvokeContinuation.uninitialized(isEscapingTry: isEscapingTry);
|
| - builder.add(invoke);
|
| - _invocations.add(invoke);
|
| - // Truncate the environment at the invocation site so it only includes
|
| - // values that will be continuation arguments. If an extra value is passed
|
| - // it will already be included in the continuation environment, but it is
|
| - // not present in the invocation environment.
|
| - int delta = builder.environment.length - _continuationEnvironment.length;
|
| - if (value != null) ++delta;
|
| - if (delta > 0) builder.environment.discard(delta);
|
| - if (value != null) builder.environment.extend(null, value);
|
| - _invocationEnvironments.add(builder.environment);
|
| - builder._current = null;
|
| - // TODO(kmillikin): Can we set builder.environment to null to make it
|
| - // less likely to mutate it?
|
| - }
|
| -
|
| - void _setContinuation() {
|
| - assert(_continuation == null);
|
| - // We have seen all invocations of this continuation, and recorded the
|
| - // environment in effect at each invocation site.
|
| -
|
| - // Compute the union of the assigned variables reaching the continuation.
|
| - //
|
| - // There is a continuation parameter for each environment variable
|
| - // that has a different value (from the environment in scope at the
|
| - // continuation binding) on some path. `_environment` is initially a copy
|
| - // of the environment in scope at the continuation binding. Compute the
|
| - // continuation parameters and add them to `_environment` so it will become
|
| - // the one in scope for the continuation body.
|
| - List<ir.Parameter> parameters = <ir.Parameter>[];
|
| - if (_invocationEnvironments.isNotEmpty) {
|
| - int length = _continuationEnvironment.length;
|
| - for (int varIndex = 0; varIndex < length; ++varIndex) {
|
| - for (Environment invocationEnvironment in _invocationEnvironments) {
|
| - assert(invocationEnvironment.sameDomain(
|
| - length, _continuationEnvironment));
|
| - if (invocationEnvironment[varIndex] !=
|
| - _continuationEnvironment[varIndex]) {
|
| - ir.Parameter parameter = new ir.Parameter(
|
| - _continuationEnvironment.index2variable[varIndex]);
|
| - _continuationEnvironment.index2value[varIndex] = parameter;
|
| - parameters.add(parameter);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - _continuation = new ir.Continuation(parameters);
|
| -
|
| - // Compute the intersection of the parameters with the environments at
|
| - // each continuation invocation. Initialize the invocations.
|
| - for (int jumpIndex = 0; jumpIndex < _invocations.length; ++jumpIndex) {
|
| - Environment invocationEnvironment = _invocationEnvironments[jumpIndex];
|
| - List<ir.Reference> arguments = <ir.Reference>[];
|
| - int varIndex = 0;
|
| - for (ir.Parameter parameter in parameters) {
|
| - varIndex =
|
| - _continuationEnvironment.index2value.indexOf(parameter, varIndex);
|
| - arguments.add(new ir.Reference(invocationEnvironment[varIndex]));
|
| - }
|
| - ir.InvokeContinuation invocation = _invocations[jumpIndex];
|
| - invocation.continuationRef = new ir.Reference(_continuation);
|
| - invocation.argumentRefs = arguments;
|
| - }
|
| - }
|
| -}
|
| -
|
| -/// A class to collect 'backward' jumps.
|
| -///
|
| -/// A backward jump to a continuation in the sense of the CPS translation is
|
| -/// a jump where some code in the body of the continuation is translated
|
| -/// before the jump is emitted. They have the property that the
|
| -/// continuation parameters and the environment for the translation of the
|
| -/// body must be determined before emitting all the invocations. A
|
| -/// [BackwardJumpCollector] can ecapsulate a continuation where some jumps
|
| -/// are backward ones.
|
| -///
|
| -/// Examples of backward jumps in the translation are the recursive
|
| -/// invocations of loop continuations.
|
| -///
|
| -/// The implementation strategy is that the collector inserts a continuation
|
| -/// parameter for each variable in scope at the entry to the continuation,
|
| -/// before emitting any jump to the continuation. When a jump is added, it
|
| -/// is given an argument for each continuation parameter.
|
| -class BackwardJumpCollector extends JumpCollector {
|
| - /// Construct a collector with a given base environment.
|
| - ///
|
| - /// The base environment is the one in scope at the site that the
|
| - /// continuation represented by this collector will be bound. The
|
| - /// translation of the continuation body will use an environment with the
|
| - /// same shape, but with fresh continuation parameters for each variable.
|
| - BackwardJumpCollector(Environment environment,
|
| - {JumpTarget target, bool hasExtraArgument: false})
|
| - : super(new Environment.fresh(environment), target, hasExtraArgument) {
|
| - List<ir.Parameter> parameters =
|
| - new List<ir.Parameter>.from(_continuationEnvironment.index2value);
|
| - _continuation = new ir.Continuation(parameters, isRecursive: true);
|
| - }
|
| -
|
| - bool isEmpty = true;
|
| -
|
| - ir.Continuation get continuation => _continuation;
|
| - Environment get environment => _continuationEnvironment;
|
| -
|
| - void addJump(IrBuilder builder,
|
| - [ir.Primitive value, SourceInformation sourceInformation]) {
|
| - assert(_continuation.parameters.length <= builder.environment.length);
|
| - isEmpty = false;
|
| - _buildTryExit(builder);
|
| - // Truncate the environment at the invocation site so it only includes
|
| - // values that will be continuation arguments. If an extra value is passed
|
| - // it will already be included in the continuation environment, but it is
|
| - // not present in the invocation environment.
|
| - int delta = builder.environment.length - _continuationEnvironment.length;
|
| - if (value != null) ++delta;
|
| - if (delta > 0) builder.environment.discard(delta);
|
| - if (value != null) builder.environment.extend(null, value);
|
| - builder.add(new ir.InvokeContinuation(
|
| - _continuation, builder.environment.index2value,
|
| - isRecursive: true, isEscapingTry: isEscapingTry));
|
| - builder._current = null;
|
| - }
|
| -}
|
| -
|
| -/// Collect 'return' jumps.
|
| -///
|
| -/// A return jump is one that targets the return continuation of a function.
|
| -/// Thus, returns from inside try/finally are not return jumps because they are
|
| -/// intercepted by a block that contains the finally handler code.
|
| -class ReturnJumpCollector extends JumpCollector {
|
| - bool isEmpty = true;
|
| - ir.Continuation get continuation => _continuation;
|
| - Environment environment = null;
|
| -
|
| - /// Construct a return jump collector for a given return continuation.
|
| - ReturnJumpCollector(ir.Continuation continuation) : super.retrn(continuation);
|
| -
|
| - void addJump(IrBuilder builder,
|
| - [ir.Primitive value, SourceInformation sourceInformation]) {
|
| - isEmpty = false;
|
| - builder.add(new ir.InvokeContinuation(continuation, <ir.Primitive>[value],
|
| - isEscapingTry: isEscapingTry, sourceInformation: sourceInformation));
|
| - builder._current = null;
|
| - }
|
| -}
|
| -
|
| -/// Collect 'goto' jumps, continue to a labeled case from within a switch.
|
| -///
|
| -/// These jumps are unrestricted within the switch. They can be forward or
|
| -/// backward. They are implemented by assigning to a state variable.
|
| -class GotoJumpCollector extends JumpCollector {
|
| - bool isEmpty = true;
|
| - final ir.Continuation continuation = null;
|
| - final Environment environment = null;
|
| -
|
| - int _stateVariableIndex;
|
| - int _stateValue;
|
| - JumpCollector _breakJoin;
|
| -
|
| - GotoJumpCollector(JumpTarget target, this._stateVariableIndex,
|
| - this._stateValue, this._breakJoin)
|
| - : super.goto(target);
|
| -
|
| - void addJump(IrBuilder builder,
|
| - [ir.Primitive value, SourceInformation sourceInformation]) {
|
| - isEmpty = false;
|
| - ir.Primitive constant = builder.buildIntegerConstant(_stateValue);
|
| - builder.environment.index2value[_stateVariableIndex] = constant;
|
| - builder.jumpTo(_breakJoin);
|
| - }
|
| -}
|
| -
|
| -/// Function for building a node in the context of the current builder.
|
| -typedef ir.Node BuildFunction(node);
|
| -
|
| -/// Function for building nodes in the context of the provided [builder].
|
| -typedef ir.Node SubbuildFunction(IrBuilder builder);
|
| -
|
| -/// Mixin that provides encapsulated access to nested builders.
|
| -abstract class IrBuilderMixin<N> {
|
| - IrBuilder _irBuilder;
|
| -
|
| - /// Execute [f] with [builder] as the current builder.
|
| - withBuilder(IrBuilder builder, f()) {
|
| - assert(builder != null);
|
| - IrBuilder prev = _irBuilder;
|
| - _irBuilder = builder;
|
| - var result = f();
|
| - _irBuilder = prev;
|
| - return result;
|
| - }
|
| -
|
| - /// The current builder.
|
| - IrBuilder get irBuilder {
|
| - assert(_irBuilder != null);
|
| - return _irBuilder;
|
| - }
|
| -
|
| - /// Visits the [node].
|
| - ir.Primitive visit(N node);
|
| -
|
| - /// Builds and returns the [ir.Node] for [node] or returns `null` if
|
| - /// [node] is `null`.
|
| - ir.Node build(N node) => node != null ? visit(node) : null;
|
| -
|
| - /// Returns a closure that takes an [IrBuilder] and builds [node] in its
|
| - /// context using [build].
|
| - SubbuildFunction subbuild(N node) {
|
| - return (IrBuilder builder) => withBuilder(builder, () => build(node));
|
| - }
|
| -
|
| - /// Returns a closure that takes an [IrBuilder] and runs [f] in its context.
|
| - SubbuildFunction nested(f()) {
|
| - return (IrBuilder builder) => withBuilder(builder, f);
|
| - }
|
| -
|
| - /// Returns a closure that takes an [IrBuilder] and builds the sequence of
|
| - /// [nodes] in its context using [build].
|
| - // TODO(johnniwinther): Type [nodes] as `Iterable<N>` when `NodeList` uses
|
| - // `List` instead of `Link`.
|
| - SubbuildFunction subbuildSequence(/*Iterable<N>*/ nodes) {
|
| - return (IrBuilder builder) {
|
| - return withBuilder(builder, () => builder.buildSequence(nodes, build));
|
| - };
|
| - }
|
| -}
|
| -
|
| -/// Shared state between delimited IrBuilders within the same function.
|
| -class IrBuilderSharedState {
|
| - final GlobalProgramInformation program;
|
| -
|
| - final BackendConstantEnvironment constants;
|
| -
|
| - ConstantSystem get constantSystem => constants.constantSystem;
|
| -
|
| - /// A stack of collectors for breaks.
|
| - List<JumpCollector> breakCollectors = <JumpCollector>[];
|
| -
|
| - /// A stack of collectors for continues.
|
| - List<JumpCollector> continueCollectors = <JumpCollector>[];
|
| -
|
| - final ExecutableElement currentElement;
|
| -
|
| - final ir.Continuation returnContinuation = new ir.Continuation.retrn();
|
| -
|
| - /// The target of a return from the function.
|
| - ///
|
| - /// A null value indicates that the target is the function's return
|
| - /// continuation. Otherwise, when inside the try block of try/finally
|
| - /// a return is intercepted to give a place to generate the finally code.
|
| - JumpCollector returnCollector;
|
| -
|
| - /// Parameter holding the internal value of 'this' passed to the function.
|
| - ///
|
| - /// For nested functions, this is *not* captured receiver, but the function
|
| - /// object itself.
|
| - ir.Parameter thisParameter;
|
| -
|
| - /// If non-null, this refers to the receiver (`this`) in the enclosing method.
|
| - ir.Primitive enclosingThis;
|
| -
|
| - final List<ir.Parameter> functionParameters = <ir.Parameter>[];
|
| -
|
| - /// Maps boxed locals to their location. These locals are not part of
|
| - /// the environment.
|
| - final Map<Local, ClosureLocation> boxedVariables = {};
|
| -
|
| - IrBuilderSharedState(this.program, this.constants, this.currentElement) {
|
| - returnCollector = new ReturnJumpCollector(returnContinuation);
|
| - }
|
| -}
|
| -
|
| -class ThisParameterLocal implements Local {
|
| - final ExecutableElement executableContext;
|
| - ThisParameterLocal(this.executableContext);
|
| - String get name => 'this';
|
| - toString() => 'ThisParameterLocal($executableContext)';
|
| -}
|
| -
|
| -/// The IR builder maintains an environment and an IR fragment.
|
| -///
|
| -/// The IR fragment is an expression with a hole in it. The hole represents
|
| -/// the focus where new expressions can be added. The fragment is implemented
|
| -/// by [root] which is the root of the expression and [_current] which is the
|
| -/// expression that immediately contains the hole. Not all expressions have a
|
| -/// hole (e.g., invocations, which always occur in tail position, do not have a
|
| -/// hole). Expressions with a hole have a plug method.
|
| -///
|
| -/// The environment maintains the reaching definition of each local variable,
|
| -/// including some synthetic locals such as [TypeVariableLocal].
|
| -///
|
| -/// Internally, IR builders also maintains a [JumpCollector] stack and tracks
|
| -/// which variables are currently boxed or held in a mutable local variable.
|
| -class IrBuilder {
|
| - final List<ir.Parameter> _parameters = <ir.Parameter>[];
|
| -
|
| - final IrBuilderSharedState state;
|
| -
|
| - /// A map from variable indexes to their values.
|
| - ///
|
| - /// [BoxLocal]s map to their box. [LocalElement]s that are boxed are not
|
| - /// in the map; look up their [BoxLocal] instead.
|
| - Environment environment;
|
| -
|
| - /// A map from mutable local variables to their [ir.MutableVariable]s.
|
| - ///
|
| - /// Mutable variables are treated as boxed. Writes to them are observable
|
| - /// side effects.
|
| - Map<Local, ir.MutableVariable> mutableVariables;
|
| -
|
| - ir.Expression root = null;
|
| - ir.Expression _current = null;
|
| -
|
| - GlobalProgramInformation get program => state.program;
|
| -
|
| - IrBuilder(GlobalProgramInformation program,
|
| - BackendConstantEnvironment constants, ExecutableElement currentElement)
|
| - : state = new IrBuilderSharedState(program, constants, currentElement),
|
| - environment = new Environment.empty(),
|
| - mutableVariables = <Local, ir.MutableVariable>{};
|
| -
|
| - IrBuilder._internal(this.state, this.environment, this.mutableVariables);
|
| -
|
| - /// Construct a delimited visitor for visiting a subtree.
|
| - ///
|
| - /// Build a subterm that is not (yet) connected to the CPS term. The
|
| - /// delimited visitor has its own has its own context for building an IR
|
| - /// expression, so the built expression is not plugged into the parent's
|
| - /// context. It has its own compile-time environment mapping local
|
| - /// variables to their values. If an optional environment argument is
|
| - /// supplied, it is used as the builder's initial environment. Otherwise
|
| - /// the environment is initially a copy of the parent builder's environment.
|
| - IrBuilder makeDelimitedBuilder([Environment env = null]) {
|
| - return new IrBuilder._internal(
|
| - state,
|
| - env != null ? env : new Environment.from(environment),
|
| - mutableVariables);
|
| - }
|
| -
|
| - /// True if [local] should currently be accessed from a [ir.MutableVariable].
|
| - bool isInMutableVariable(Local local) {
|
| - return mutableVariables.containsKey(local);
|
| - }
|
| -
|
| - /// Creates a [ir.MutableVariable] for the given local.
|
| - void makeMutableVariable(Local local) {
|
| - mutableVariables[local] = new ir.MutableVariable(local);
|
| - }
|
| -
|
| - /// Remove an [ir.MutableVariable] for a local.
|
| - ///
|
| - /// Subsequent access to the local will be direct rather than through the
|
| - /// mutable variable.
|
| - void removeMutableVariable(Local local) {
|
| - mutableVariables.remove(local);
|
| - }
|
| -
|
| - /// Gets the [MutableVariable] containing the value of [local].
|
| - ir.MutableVariable getMutableVariable(Local local) {
|
| - return mutableVariables[local];
|
| - }
|
| -
|
| - bool get isOpen => root == null || _current != null;
|
| -
|
| - List<ir.Primitive> buildFunctionHeader(Iterable<Local> parameters,
|
| - {ClosureScope closureScope, ClosureEnvironment env}) {
|
| - _createThisParameter();
|
| - _enterClosureEnvironment(env);
|
| - _enterScope(closureScope);
|
| - parameters.forEach(_createFunctionParameter);
|
| - return _parameters;
|
| - }
|
| -
|
| - /// Creates a parameter for [local] and adds it to the current environment.
|
| - ir.Parameter _createLocalParameter(Local local) {
|
| - ir.Parameter parameter = new ir.Parameter(local);
|
| - _parameters.add(parameter);
|
| - environment.extend(local, parameter);
|
| - return parameter;
|
| - }
|
| -
|
| - /// Plug an expression into the 'hole' in the context being accumulated. The
|
| - /// empty context (just a hole) is represented by root (and current) being
|
| - /// null. Since the hole in the current context is filled by this function,
|
| - /// the new hole must be in the newly added expression---which becomes the
|
| - /// new value of current.
|
| - void add(ir.Expression expr) {
|
| - assert(isOpen);
|
| - if (root == null) {
|
| - root = _current = expr;
|
| - } else {
|
| - _current = _current.plug(expr);
|
| - }
|
| - }
|
| -
|
| - /// Create and add a new [LetPrim] for [primitive].
|
| - ir.Primitive addPrimitive(ir.Primitive primitive) {
|
| - add(new ir.LetPrim(primitive));
|
| - return primitive;
|
| - }
|
| -
|
| - ir.Primitive buildInvokeStatic(Element element, Selector selector,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - assert(!element.isLocal);
|
| - assert(!element.isInstanceMember);
|
| - assert(isOpen);
|
| - if (program.isJsInterop(element)) {
|
| - return buildInvokeJsInteropMember(element, arguments, sourceInformation);
|
| - }
|
| - return addPrimitive(
|
| - new ir.InvokeStatic(element, selector, arguments, sourceInformation));
|
| - }
|
| -
|
| - ir.Primitive _buildInvokeSuper(Element target, Selector selector,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - assert(target.isInstanceMember);
|
| - assert(isOpen);
|
| - return addPrimitive(new ir.InvokeMethodDirectly(
|
| - buildThis(), target, selector, arguments, sourceInformation));
|
| - }
|
| -
|
| - ir.Primitive _buildInvokeDynamic(
|
| - ir.Primitive receiver,
|
| - Selector selector,
|
| - TypeMask mask,
|
| - List<ir.Primitive> arguments,
|
| - SourceInformation sourceInformation) {
|
| - assert(isOpen);
|
| - return addPrimitive(new ir.InvokeMethod(receiver, selector, mask, arguments,
|
| - sourceInformation: sourceInformation));
|
| - }
|
| -
|
| - ir.Primitive _buildInvokeCall(
|
| - ir.Primitive target,
|
| - CallStructure callStructure,
|
| - TypeMask mask,
|
| - List<ir.Definition> arguments,
|
| - SourceInformation sourceInformation) {
|
| - Selector selector = callStructure.callSelector;
|
| - return _buildInvokeDynamic(
|
| - target, selector, mask, arguments, sourceInformation);
|
| - }
|
| -
|
| - ir.Primitive buildStaticNoSuchMethod(Selector selector,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - ir.Primitive receiver = buildStringConstant('');
|
| - ir.Primitive name = buildStringConstant(selector.name);
|
| - ir.Primitive argumentList = buildListLiteral(null, arguments);
|
| - ir.Primitive expectedArgumentNames = buildNullConstant();
|
| - return buildStaticFunctionInvocation(
|
| - program.throwNoSuchMethod,
|
| - <ir.Primitive>[receiver, name, argumentList, expectedArgumentNames],
|
| - sourceInformation);
|
| - }
|
| -
|
| - /// Create a [ir.Constant] from [value] and add it to the CPS term.
|
| - ir.Constant buildConstant(ConstantValue value,
|
| - {SourceInformation sourceInformation}) {
|
| - assert(isOpen);
|
| - return addPrimitive(
|
| - new ir.Constant(value, sourceInformation: sourceInformation));
|
| - }
|
| -
|
| - /// Create an integer constant and add it to the CPS term.
|
| - ir.Constant buildIntegerConstant(int value) {
|
| - return buildConstant(state.constantSystem.createInt(value));
|
| - }
|
| -
|
| - /// Create a double constant and add it to the CPS term.
|
| - ir.Constant buildDoubleConstant(double value) {
|
| - return buildConstant(state.constantSystem.createDouble(value));
|
| - }
|
| -
|
| - /// Create a Boolean constant and add it to the CPS term.
|
| - ir.Constant buildBooleanConstant(bool value) {
|
| - return buildConstant(state.constantSystem.createBool(value));
|
| - }
|
| -
|
| - /// Create a null constant and add it to the CPS term.
|
| - ir.Constant buildNullConstant() {
|
| - return buildConstant(state.constantSystem.createNull());
|
| - }
|
| -
|
| - /// Create a string constant and add it to the CPS term.
|
| - ir.Constant buildStringConstant(String value) {
|
| - return buildConstant(
|
| - state.constantSystem.createString(new ast.DartString.literal(value)));
|
| - }
|
| -
|
| - /// Create a string constant and add it to the CPS term.
|
| - ir.Constant buildDartStringConstant(ast.DartString value) {
|
| - return buildConstant(state.constantSystem.createString(value));
|
| - }
|
| -
|
| - /// Creates a non-constant list literal of the provided [type] and with the
|
| - /// provided [values].
|
| - ir.Primitive buildListLiteral(
|
| - InterfaceType type, Iterable<ir.Primitive> values,
|
| - {TypeMask allocationSiteType}) {
|
| - assert(isOpen);
|
| - return addPrimitive(new ir.LiteralList(type, values.toList(),
|
| - allocationSiteType: allocationSiteType));
|
| - }
|
| -
|
| - /// Creates a conditional expression with the provided [condition] where the
|
| - /// then and else expression are created through the [buildThenExpression]
|
| - /// and [buildElseExpression] functions, respectively.
|
| - ir.Primitive buildConditional(
|
| - ir.Primitive condition,
|
| - ir.Primitive buildThenExpression(IrBuilder builder),
|
| - ir.Primitive buildElseExpression(IrBuilder builder),
|
| - SourceInformation sourceInformation) {
|
| - assert(isOpen);
|
| -
|
| - // The then and else expressions are delimited.
|
| - IrBuilder thenBuilder = makeDelimitedBuilder();
|
| - IrBuilder elseBuilder = makeDelimitedBuilder();
|
| - ir.Primitive thenValue = buildThenExpression(thenBuilder);
|
| - ir.Primitive elseValue = buildElseExpression(elseBuilder);
|
| -
|
| - // Treat the values of the subexpressions as named values in the
|
| - // environment, so they will be treated as arguments to the join-point
|
| - // continuation. We know the environments are the right size because
|
| - // expressions cannot introduce variable bindings.
|
| - assert(environment.length == thenBuilder.environment.length);
|
| - assert(environment.length == elseBuilder.environment.length);
|
| - JumpCollector join =
|
| - new ForwardJumpCollector(environment, hasExtraArgument: true);
|
| - thenBuilder.jumpTo(join, thenValue);
|
| - elseBuilder.jumpTo(join, elseValue);
|
| -
|
| - // Build the term
|
| - // let cont join(x, ..., result) = [] in
|
| - // let cont then() = [[thenPart]]; join(v, ...)
|
| - // and else() = [[elsePart]]; join(v, ...)
|
| - // in
|
| - // if condition (then, else)
|
| - ir.Continuation thenContinuation = new ir.Continuation([]);
|
| - ir.Continuation elseContinuation = new ir.Continuation([]);
|
| - thenContinuation.body = thenBuilder.root;
|
| - elseContinuation.body = elseBuilder.root;
|
| - add(new ir.LetCont(
|
| - join.continuation,
|
| - new ir.LetCont.two(
|
| - thenContinuation,
|
| - elseContinuation,
|
| - new ir.Branch.strict(condition, thenContinuation, elseContinuation,
|
| - sourceInformation))));
|
| - environment = join.environment;
|
| - return environment.discard(1);
|
| - }
|
| -
|
| - /**
|
| - * Add an explicit `return null` for functions that don't have a return
|
| - * statement on each branch. This includes functions with an empty body,
|
| - * such as `foo(){ }`.
|
| - */
|
| - void _ensureReturn() {
|
| - if (!isOpen) return;
|
| - ir.Constant constant = buildNullConstant();
|
| - add(new ir.InvokeContinuation(state.returnContinuation, [constant]));
|
| - _current = null;
|
| - }
|
| -
|
| - /// Create a [ir.FunctionDefinition] using [root] as the body.
|
| - ///
|
| - /// The protocol for building a function is:
|
| - /// 1. Call [buildFunctionHeader].
|
| - /// 2. Call `buildXXX` methods to build the body.
|
| - /// 3. Call [makeFunctionDefinition] to finish.
|
| - ir.FunctionDefinition makeFunctionDefinition(
|
| - SourceInformation sourceInformation) {
|
| - _ensureReturn();
|
| - return new ir.FunctionDefinition(state.currentElement, state.thisParameter,
|
| - state.functionParameters, state.returnContinuation, root,
|
| - sourceInformation: sourceInformation);
|
| - }
|
| -
|
| - /// Create a invocation of the [method] on the super class where the call
|
| - /// structure is defined [callStructure] and the argument values are defined
|
| - /// by [arguments].
|
| - ir.Primitive buildSuperMethodInvocation(
|
| - MethodElement method,
|
| - CallStructure callStructure,
|
| - List<ir.Primitive> arguments,
|
| - SourceInformation sourceInformation) {
|
| - // TODO(johnniwinther): This shouldn't be necessary.
|
| - SelectorKind kind = Elements.isOperatorName(method.name)
|
| - ? SelectorKind.OPERATOR
|
| - : SelectorKind.CALL;
|
| - Selector selector = new Selector(kind, method.memberName, callStructure);
|
| - return _buildInvokeSuper(method, selector, arguments, sourceInformation);
|
| - }
|
| -
|
| - /// Create a read access of the [method] on the super class, i.e. a
|
| - /// closurization of [method].
|
| - ir.Primitive buildSuperMethodGet(
|
| - MethodElement method, SourceInformation sourceInformation) {
|
| - // TODO(johnniwinther): This should have its own ir node.
|
| - return _buildInvokeSuper(method, new Selector.getter(method.memberName),
|
| - const <ir.Primitive>[], sourceInformation);
|
| - }
|
| -
|
| - /// Create a getter invocation of the [getter] on the super class.
|
| - ir.Primitive buildSuperGetterGet(
|
| - MethodElement getter, SourceInformation sourceInformation) {
|
| - // TODO(johnniwinther): This should have its own ir node.
|
| - return _buildInvokeSuper(getter, new Selector.getter(getter.memberName),
|
| - const <ir.Primitive>[], sourceInformation);
|
| - }
|
| -
|
| - /// Create an setter invocation of the [setter] on the super class with
|
| - /// [value].
|
| - ir.Primitive buildSuperSetterSet(MethodElement setter, ir.Primitive value,
|
| - SourceInformation sourceInformation) {
|
| - // TODO(johnniwinther): This should have its own ir node.
|
| - _buildInvokeSuper(setter, new Selector.setter(setter.memberName),
|
| - <ir.Primitive>[value], sourceInformation);
|
| - return value;
|
| - }
|
| -
|
| - /// Create an invocation of the index [method] on the super class with
|
| - /// the provided [index].
|
| - ir.Primitive buildSuperIndex(MethodElement method, ir.Primitive index,
|
| - SourceInformation sourceInformation) {
|
| - return _buildInvokeSuper(
|
| - method, new Selector.index(), <ir.Primitive>[index], sourceInformation);
|
| - }
|
| -
|
| - /// Create an invocation of the index set [method] on the super class with
|
| - /// the provided [index] and [value].
|
| - ir.Primitive buildSuperIndexSet(MethodElement method, ir.Primitive index,
|
| - ir.Primitive value, SourceInformation sourceInformation) {
|
| - _buildInvokeSuper(method, new Selector.indexSet(),
|
| - <ir.Primitive>[index, value], sourceInformation);
|
| - return value;
|
| - }
|
| -
|
| - /// Create a dynamic invocation on [receiver] where the method name and
|
| - /// argument structure are defined by [selector] and the argument values are
|
| - /// defined by [arguments].
|
| - ir.Primitive buildDynamicInvocation(
|
| - ir.Primitive receiver,
|
| - Selector selector,
|
| - TypeMask mask,
|
| - List<ir.Primitive> arguments,
|
| - SourceInformation sourceInformation) {
|
| - return _buildInvokeDynamic(
|
| - receiver, selector, mask, arguments, sourceInformation);
|
| - }
|
| -
|
| - /// Create a dynamic getter invocation on [receiver] where the getter name is
|
| - /// defined by [selector].
|
| - ir.Primitive buildDynamicGet(ir.Primitive receiver, Selector selector,
|
| - TypeMask mask, SourceInformation sourceInformation) {
|
| - assert(selector.isGetter);
|
| - FieldElement field = program.locateSingleField(selector, mask);
|
| - if (field != null) {
|
| - // If the world says this resolves to a unique field, then it MUST be
|
| - // treated as a field access, since the getter might not be emitted.
|
| - return buildFieldGet(receiver, field, sourceInformation);
|
| - } else {
|
| - return _buildInvokeDynamic(
|
| - receiver, selector, mask, const <ir.Primitive>[], sourceInformation);
|
| - }
|
| - }
|
| -
|
| - /// Create a dynamic setter invocation on [receiver] where the setter name and
|
| - /// argument are defined by [selector] and [value], respectively.
|
| - ir.Primitive buildDynamicSet(ir.Primitive receiver, Selector selector,
|
| - TypeMask mask, ir.Primitive value, SourceInformation sourceInformation) {
|
| - assert(selector.isSetter);
|
| - FieldElement field = program.locateSingleField(selector, mask);
|
| - if (field != null) {
|
| - // If the world says this resolves to a unique field, then it MUST be
|
| - // treated as a field access, since the setter might not be emitted.
|
| - buildFieldSet(receiver, field, value, sourceInformation);
|
| - } else {
|
| - _buildInvokeDynamic(
|
| - receiver, selector, mask, <ir.Primitive>[value], sourceInformation);
|
| - }
|
| - return value;
|
| - }
|
| -
|
| - /// Create a dynamic index set invocation on [receiver] with the provided
|
| - /// [index] and [value].
|
| - ir.Primitive buildDynamicIndexSet(
|
| - ir.Primitive receiver,
|
| - TypeMask mask,
|
| - ir.Primitive index,
|
| - ir.Primitive value,
|
| - SourceInformation sourceInformation) {
|
| - _buildInvokeDynamic(receiver, new Selector.indexSet(), mask,
|
| - <ir.Primitive>[index, value], sourceInformation);
|
| - return value;
|
| - }
|
| -
|
| - /// Create an invocation of the local [function] where argument structure is
|
| - /// defined by [callStructure] and the argument values are defined by
|
| - /// [arguments].
|
| - ir.Primitive buildLocalFunctionInvocation(
|
| - LocalFunctionElement function,
|
| - CallStructure callStructure,
|
| - List<ir.Primitive> arguments,
|
| - SourceInformation sourceInformation) {
|
| - // TODO(johnniwinther): Maybe this should have its own ir node.
|
| - return buildCallInvocation(
|
| - buildLocalGet(function), callStructure, arguments, sourceInformation);
|
| - }
|
| -
|
| - /// Create a static invocation of [function].
|
| - ///
|
| - /// The arguments are not named and their values are defined by [arguments].
|
| - ir.Primitive buildStaticFunctionInvocation(MethodElement function,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - Selector selector = new Selector.call(
|
| - function.memberName, new CallStructure(arguments.length));
|
| - return buildInvokeStatic(function, selector, arguments, sourceInformation);
|
| - }
|
| -
|
| - /// Create a getter invocation of the static [getter].
|
| - ir.Primitive buildStaticGetterGet(
|
| - MethodElement getter, SourceInformation sourceInformation) {
|
| - Selector selector = new Selector.getter(getter.memberName);
|
| - return buildInvokeStatic(
|
| - getter, selector, const <ir.Primitive>[], sourceInformation);
|
| - }
|
| -
|
| - /// Create a write access to the static [field] with the [value].
|
| - ir.Primitive buildStaticFieldSet(FieldElement field, ir.Primitive value,
|
| - SourceInformation sourceInformation) {
|
| - addPrimitive(new ir.SetStatic(field, value, sourceInformation));
|
| - return value;
|
| - }
|
| -
|
| - /// Create a setter invocation of the static [setter] with the [value].
|
| - ir.Primitive buildStaticSetterSet(MethodElement setter, ir.Primitive value,
|
| - SourceInformation sourceInformation) {
|
| - Selector selector = new Selector.setter(setter.memberName);
|
| - buildInvokeStatic(
|
| - setter, selector, <ir.Primitive>[value], sourceInformation);
|
| - return value;
|
| - }
|
| -
|
| - /// Create an erroneous invocation where argument structure is defined by
|
| - /// [selector] and the argument values are defined by [arguments].
|
| - // TODO(johnniwinther): Make this more fine-grained.
|
| - ir.Primitive buildErroneousInvocation(Element element, Selector selector,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - // TODO(johnniwinther): This should have its own ir node.
|
| - return buildInvokeStatic(element, selector, arguments, sourceInformation);
|
| - }
|
| -
|
| - /// Concatenate string values. The arguments must be strings.
|
| - ir.Primitive buildStringConcatenation(
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - assert(isOpen);
|
| - return addPrimitive(new ir.ApplyBuiltinOperator(
|
| - ir.BuiltinOperator.StringConcatenate, arguments, sourceInformation));
|
| - }
|
| -
|
| - /// Create an invocation of the `call` method of [functionExpression], where
|
| - /// the structure of arguments are given by [callStructure].
|
| - // TODO(johnniwinther): This should take a [TypeMask].
|
| - ir.Primitive buildCallInvocation(
|
| - ir.Primitive functionExpression,
|
| - CallStructure callStructure,
|
| - List<ir.Definition> arguments,
|
| - SourceInformation sourceInformation) {
|
| - return _buildInvokeCall(
|
| - functionExpression, callStructure, null, arguments, sourceInformation);
|
| - }
|
| -
|
| - /// Creates an if-then-else statement with the provided [condition] where the
|
| - /// then and else branches are created through the [buildThenPart] and
|
| - /// [buildElsePart] functions, respectively.
|
| - ///
|
| - /// An if-then statement is created if [buildElsePart] is a no-op.
|
| - // TODO(johnniwinther): Unify implementation with [buildConditional] and
|
| - // [_buildLogicalOperator].
|
| - void buildIf(
|
| - ir.Primitive condition,
|
| - void buildThenPart(IrBuilder builder),
|
| - void buildElsePart(IrBuilder builder),
|
| - SourceInformation sourceInformation) {
|
| - assert(isOpen);
|
| -
|
| - // The then and else parts are delimited.
|
| - IrBuilder thenBuilder = makeDelimitedBuilder();
|
| - IrBuilder elseBuilder = makeDelimitedBuilder();
|
| - buildThenPart(thenBuilder);
|
| - buildElsePart(elseBuilder);
|
| -
|
| - // Build the term
|
| - // (Result =) let cont then() = [[thenPart]]
|
| - // and else() = [[elsePart]]
|
| - // in
|
| - // if condition (then, else)
|
| - ir.Continuation thenContinuation = new ir.Continuation([]);
|
| - ir.Continuation elseContinuation = new ir.Continuation([]);
|
| - // If exactly one of the then and else continuation bodies is open (i.e.,
|
| - // the other one has an exit on all paths), then Continuation.plug expects
|
| - // that continuation to be listed first. Arbitrarily use [then, else]
|
| - // order otherwise.
|
| - List<ir.Continuation> arms = !thenBuilder.isOpen && elseBuilder.isOpen
|
| - ? <ir.Continuation>[elseContinuation, thenContinuation]
|
| - : <ir.Continuation>[thenContinuation, elseContinuation];
|
| -
|
| - ir.Expression result = new ir.LetCont.many(
|
| - arms,
|
| - new ir.Branch.strict(
|
| - condition, thenContinuation, elseContinuation, sourceInformation));
|
| -
|
| - JumpCollector join; // Null if there is no join.
|
| - if (thenBuilder.isOpen && elseBuilder.isOpen) {
|
| - // There is a join-point continuation. Build the term
|
| - // 'let cont join(x, ...) = [] in Result' and plug invocations of the
|
| - // join-point continuation into the then and else continuations.
|
| - join = new ForwardJumpCollector(environment);
|
| - thenBuilder.jumpTo(join);
|
| - elseBuilder.jumpTo(join);
|
| - result = new ir.LetCont(join.continuation, result);
|
| - }
|
| -
|
| - // The then or else term root could be null, but not both. If there is
|
| - // a join then an InvokeContinuation was just added to both of them. If
|
| - // there is no join, then at least one of them is closed and thus has a
|
| - // non-null root by the definition of the predicate isClosed. In the
|
| - // case that one of them is null, it must be the only one that is open
|
| - // and thus contains the new hole in the context. This case is handled
|
| - // after the branch is plugged into the current hole.
|
| - thenContinuation.body = thenBuilder.root;
|
| - elseContinuation.body = elseBuilder.root;
|
| -
|
| - add(result);
|
| - if (join == null) {
|
| - // At least one subexpression is closed.
|
| - if (thenBuilder.isOpen) {
|
| - if (thenBuilder.root != null) _current = thenBuilder._current;
|
| - environment = thenBuilder.environment;
|
| - } else if (elseBuilder.isOpen) {
|
| - if (elseBuilder.root != null) _current = elseBuilder._current;
|
| - environment = elseBuilder.environment;
|
| - } else {
|
| - _current = null;
|
| - }
|
| - } else {
|
| - environment = join.environment;
|
| - }
|
| - }
|
| -
|
| - void jumpTo(JumpCollector collector,
|
| - [ir.Primitive value, SourceInformation sourceInformation]) {
|
| - collector.addJump(this, value, sourceInformation);
|
| - }
|
| -
|
| - void addRecursiveContinuation(BackwardJumpCollector collector) {
|
| - assert(environment.length == collector.environment.length);
|
| - add(new ir.LetCont(
|
| - collector.continuation,
|
| - new ir.InvokeContinuation(
|
| - collector.continuation, environment.index2value)));
|
| - environment = collector.environment;
|
| - }
|
| -
|
| - /// Creates a for loop in which the initializer, condition, body, update are
|
| - /// created by [buildInitializer], [buildCondition], [buildBody] and
|
| - /// [buildUpdate], respectively.
|
| - ///
|
| - /// The jump [target] is used to identify which `break` and `continue`
|
| - /// statements that have this `for` statement as their target.
|
| - ///
|
| - /// The [closureScope] identifies variables that should be boxed in this loop.
|
| - /// This includes variables declared inside the body of the loop as well as
|
| - /// in the for-loop initializer.
|
| - ///
|
| - /// [loopVariables] is the list of variables declared in the for-loop
|
| - /// initializer.
|
| - void buildFor(
|
| - {SubbuildFunction buildInitializer,
|
| - SubbuildFunction buildCondition,
|
| - SourceInformation conditionSourceInformation,
|
| - SubbuildFunction buildBody,
|
| - SubbuildFunction buildUpdate,
|
| - JumpTarget target,
|
| - ClosureScope closureScope,
|
| - List<LocalElement> loopVariables}) {
|
| - assert(isOpen);
|
| -
|
| - // For loops use four named continuations: the entry to the condition,
|
| - // the entry to the body, the loop exit, and the loop successor (break).
|
| - // The CPS translation of
|
| - // [[for (initializer; condition; update) body; successor]] is:
|
| - //
|
| - // _enterForLoopInitializer();
|
| - // [[initializer]];
|
| - // let cont loop(x, ...) =
|
| - // let prim cond = [[condition]] in
|
| - // let cont break(x, ...) = [[successor]] in
|
| - // let cont exit() = break(v, ...) in
|
| - // let cont body() =
|
| - // _enterForLoopBody();
|
| - // let cont continue(x, ...) =
|
| - // _enterForLoopUpdate();
|
| - // [[update]];
|
| - // loop(v, ...) in
|
| - // [[body]];
|
| - // continue(v, ...) in
|
| - // branch cond (body, exit) in
|
| - // loop(v, ...)
|
| - //
|
| - // If there are no breaks in the body, the break continuation is inlined
|
| - // in the exit continuation (i.e., the translation of the successor
|
| - // statement occurs in the exit continuation). If there is only one
|
| - // invocation of the continue continuation (i.e., no continues in the
|
| - // body), the continue continuation is inlined in the body.
|
| - _enterForLoopInitializer(closureScope, loopVariables);
|
| - buildInitializer(this);
|
| -
|
| - JumpCollector loop = new BackwardJumpCollector(environment);
|
| - addRecursiveContinuation(loop);
|
| -
|
| - ir.Primitive condition = buildCondition(this);
|
| - if (condition == null) {
|
| - // If the condition is empty then the body is entered unconditionally.
|
| - condition = buildBooleanConstant(true);
|
| - }
|
| - JumpCollector breakCollector =
|
| - new ForwardJumpCollector(environment, target: target);
|
| -
|
| - // Use a pair of builders for the body, one for the entry code if any
|
| - // and one for the body itself. We only decide whether to insert a
|
| - // continue continuation until after translating the body and there is no
|
| - // way to insert such a continuation between the entry code and the body
|
| - // if they are translated together.
|
| - IrBuilder outerBodyBuilder = makeDelimitedBuilder();
|
| - outerBodyBuilder._enterForLoopBody(closureScope, loopVariables);
|
| - JumpCollector continueCollector =
|
| - new ForwardJumpCollector(outerBodyBuilder.environment, target: target);
|
| -
|
| - IrBuilder innerBodyBuilder = outerBodyBuilder.makeDelimitedBuilder();
|
| - state.breakCollectors.add(breakCollector);
|
| - state.continueCollectors.add(continueCollector);
|
| - buildBody(innerBodyBuilder);
|
| - assert(state.breakCollectors.last == breakCollector);
|
| - assert(state.continueCollectors.last == continueCollector);
|
| - state.breakCollectors.removeLast();
|
| - state.continueCollectors.removeLast();
|
| -
|
| - // The binding of the continue continuation should occur as late as
|
| - // possible, that is, at the nearest common ancestor of all the continue
|
| - // sites in the body. However, that is difficult to compute here, so it
|
| - // is instead placed just outside the translation of the loop body. In
|
| - // the case where there are no continues in the body, the updates are
|
| - // translated immediately after the body.
|
| - bool hasContinues = !continueCollector.isEmpty;
|
| - IrBuilder updateBuilder;
|
| - if (hasContinues) {
|
| - if (innerBodyBuilder.isOpen) innerBodyBuilder.jumpTo(continueCollector);
|
| - updateBuilder = makeDelimitedBuilder(continueCollector.environment);
|
| - } else {
|
| - updateBuilder = innerBodyBuilder;
|
| - }
|
| - updateBuilder._enterForLoopUpdate(closureScope, loopVariables);
|
| - buildUpdate(updateBuilder);
|
| - if (updateBuilder.isOpen) updateBuilder.jumpTo(loop);
|
| - // Connect the inner and outer body builders. This is done only after
|
| - // it is guaranteed that the updateBuilder has a non-empty term.
|
| - if (hasContinues) {
|
| - outerBodyBuilder.add(new ir.LetCont(
|
| - continueCollector.continuation, innerBodyBuilder.root));
|
| - continueCollector.continuation.body = updateBuilder.root;
|
| - } else {
|
| - outerBodyBuilder.add(innerBodyBuilder.root);
|
| - }
|
| -
|
| - // Create loop exit and body entry continuations and a branch to them.
|
| - ir.Continuation exitContinuation = new ir.Continuation([]);
|
| - ir.Continuation bodyContinuation = new ir.Continuation([]);
|
| - bodyContinuation.body = outerBodyBuilder.root;
|
| - // Note the order of continuations: the first one is the one that will
|
| - // be filled by LetCont.plug.
|
| - ir.LetCont branch = new ir.LetCont.two(
|
| - exitContinuation,
|
| - bodyContinuation,
|
| - new ir.Branch.strict(condition, bodyContinuation, exitContinuation,
|
| - conditionSourceInformation));
|
| - // If there are breaks in the body, then there must be a join-point
|
| - // continuation for the normal exit and the breaks. Otherwise, the
|
| - // successor is translated in the hole in the exit continuation.
|
| - bool hasBreaks = !breakCollector.isEmpty;
|
| - ir.LetCont letBreak;
|
| - if (hasBreaks) {
|
| - IrBuilder exitBuilder = makeDelimitedBuilder();
|
| - exitBuilder.jumpTo(breakCollector);
|
| - exitContinuation.body = exitBuilder.root;
|
| - letBreak = new ir.LetCont(breakCollector.continuation, branch);
|
| - add(letBreak);
|
| - environment = breakCollector.environment;
|
| - } else {
|
| - add(branch);
|
| - }
|
| - }
|
| -
|
| - /// Creates a for-in loop, `for (v in e) b`.
|
| - ///
|
| - /// [buildExpression] creates the expression, `e`. The variable, `v`, can
|
| - /// take one of three forms:
|
| - /// 1) `v` can be declared within the for-in statement, like in
|
| - /// `for (var v in e)`, in which case, [buildVariableDeclaration]
|
| - /// creates its declaration and [variableElement] is the element for
|
| - /// the declared variable,
|
| - /// 2) `v` is predeclared statically known variable, that is top-level,
|
| - /// static, or local variable, in which case [variableElement] is the
|
| - /// variable element, and [variableSelector] defines its write access,
|
| - /// 3) `v` is an instance variable in which case [variableSelector]
|
| - /// defines its write access.
|
| - /// [buildBody] creates the body, `b`, of the loop. The jump [target] is used
|
| - /// to identify which `break` and `continue` statements that have this for-in
|
| - /// statement as their target.
|
| - void buildForIn(
|
| - {SubbuildFunction buildExpression,
|
| - SubbuildFunction buildVariableDeclaration,
|
| - Element variableElement,
|
| - Selector variableSelector,
|
| - TypeMask variableMask,
|
| - SourceInformation variableSetSourceInformation,
|
| - TypeMask currentMask,
|
| - SourceInformation currentSourceInformation,
|
| - TypeMask iteratorMask,
|
| - SourceInformation iteratorSourceInformation,
|
| - TypeMask moveNextMask,
|
| - SourceInformation moveNextSourceInformation,
|
| - SubbuildFunction buildBody,
|
| - JumpTarget target,
|
| - ClosureScope closureScope,
|
| - SourceInformation conditionSourceInformation}) {
|
| - // The for-in loop
|
| - //
|
| - // for (a in e) s;
|
| - //
|
| - // Is compiled analogously to:
|
| - //
|
| - // it = e.iterator;
|
| - // while (it.moveNext()) {
|
| - // var a = it.current;
|
| - // s;
|
| - // }
|
| -
|
| - // Fill the current hole with:
|
| - // let prim expressionReceiver = [[e]] in
|
| - // let cont iteratorInvoked(iterator) =
|
| - // [ ]
|
| - // in expressionReceiver.iterator () iteratorInvoked
|
| - ir.Primitive expressionReceiver = buildExpression(this);
|
| - List<ir.Primitive> emptyArguments = <ir.Primitive>[];
|
| - ir.Primitive iterator = addPrimitive(new ir.InvokeMethod(
|
| - expressionReceiver, Selectors.iterator, iteratorMask, emptyArguments));
|
| -
|
| - // Fill with:
|
| - // let cont loop(x, ...) =
|
| - // let cont moveNextInvoked(condition) =
|
| - // [ ]
|
| - // in iterator.moveNext () moveNextInvoked
|
| - // in loop(v, ...)
|
| - JumpCollector loop = new BackwardJumpCollector(environment, target: target);
|
| - addRecursiveContinuation(loop);
|
| - ir.Primitive condition = addPrimitive(new ir.InvokeMethod(
|
| - iterator, Selectors.moveNext, moveNextMask, emptyArguments));
|
| -
|
| - // As a delimited term, build:
|
| - // <<BODY>> =
|
| - // _enterScope();
|
| - // [[variableDeclaration]]
|
| - // let cont currentInvoked(currentValue) =
|
| - // [[a = currentValue]];
|
| - // [ ]
|
| - // in iterator.current () currentInvoked
|
| - IrBuilder bodyBuilder = makeDelimitedBuilder();
|
| - bodyBuilder._enterScope(closureScope);
|
| - if (buildVariableDeclaration != null) {
|
| - buildVariableDeclaration(bodyBuilder);
|
| - }
|
| - ir.Primitive currentValue = bodyBuilder.addPrimitive(new ir.InvokeMethod(
|
| - iterator, Selectors.current, currentMask, emptyArguments,
|
| - sourceInformation: currentSourceInformation));
|
| - // TODO(johnniwinther): Extract this as a provided strategy.
|
| - if (Elements.isLocal(variableElement)) {
|
| - bodyBuilder.buildLocalVariableSet(
|
| - variableElement, currentValue, variableSetSourceInformation);
|
| - } else if (Elements.isError(variableElement) ||
|
| - Elements.isMalformed(variableElement)) {
|
| - Selector selector = new Selector.setter(
|
| - new Name(variableElement.name, variableElement.library));
|
| - List<ir.Primitive> value = <ir.Primitive>[currentValue];
|
| - // Note the comparison below. It can be the case that an element isError
|
| - // and isMalformed.
|
| - if (Elements.isError(variableElement)) {
|
| - bodyBuilder.buildStaticNoSuchMethod(
|
| - selector, value, variableSetSourceInformation);
|
| - } else {
|
| - bodyBuilder.buildErroneousInvocation(
|
| - variableElement, selector, value, variableSetSourceInformation);
|
| - }
|
| - } else if (Elements.isStaticOrTopLevel(variableElement)) {
|
| - if (variableElement.isField) {
|
| - bodyBuilder.addPrimitive(new ir.SetStatic(
|
| - variableElement, currentValue, variableSetSourceInformation));
|
| - } else {
|
| - bodyBuilder.buildStaticSetterSet(
|
| - variableElement, currentValue, variableSetSourceInformation);
|
| - }
|
| - } else {
|
| - ir.Primitive receiver = bodyBuilder.buildThis();
|
| - assert(receiver != null);
|
| - bodyBuilder.buildDynamicSet(receiver, variableSelector, variableMask,
|
| - currentValue, variableSetSourceInformation);
|
| - }
|
| -
|
| - // Translate the body in the hole in the delimited term above, and add
|
| - // a jump to the loop if control flow is live after the body.
|
| - JumpCollector breakCollector =
|
| - new ForwardJumpCollector(environment, target: target);
|
| - state.breakCollectors.add(breakCollector);
|
| - state.continueCollectors.add(loop);
|
| - buildBody(bodyBuilder);
|
| - assert(state.breakCollectors.last == breakCollector);
|
| - assert(state.continueCollectors.last == loop);
|
| - state.breakCollectors.removeLast();
|
| - state.continueCollectors.removeLast();
|
| - if (bodyBuilder.isOpen) bodyBuilder.jumpTo(loop);
|
| -
|
| - // Create body entry and loop exit continuations and a branch to them.
|
| - //
|
| - // let cont exit() = [ ]
|
| - // and body() = <<BODY>>
|
| - // in branch condition (body, exit)
|
| - ir.Continuation exitContinuation = new ir.Continuation([]);
|
| - ir.Continuation bodyContinuation = new ir.Continuation([]);
|
| - bodyContinuation.body = bodyBuilder.root;
|
| - // Note the order of continuations: the first one is the one that will
|
| - // be filled by LetCont.plug.
|
| - ir.LetCont branch = new ir.LetCont.two(
|
| - exitContinuation,
|
| - bodyContinuation,
|
| - new ir.Branch.strict(condition, bodyContinuation, exitContinuation,
|
| - conditionSourceInformation));
|
| - // If there are breaks in the body, then there must be a join-point
|
| - // continuation for the normal exit and the breaks. Otherwise, the
|
| - // successor is translated in the hole in the exit continuation.
|
| - bool hasBreaks = !breakCollector.isEmpty;
|
| - ir.LetCont letBreak;
|
| - if (hasBreaks) {
|
| - IrBuilder exitBuilder = makeDelimitedBuilder();
|
| - exitBuilder.jumpTo(breakCollector);
|
| - exitContinuation.body = exitBuilder.root;
|
| - letBreak = new ir.LetCont(breakCollector.continuation, branch);
|
| - add(letBreak);
|
| - environment = breakCollector.environment;
|
| - } else {
|
| - add(branch);
|
| - }
|
| - }
|
| -
|
| - /// Creates a while loop in which the condition and body are created by
|
| - /// [buildCondition] and [buildBody], respectively.
|
| - ///
|
| - /// The jump [target] is used to identify which `break` and `continue`
|
| - /// statements that have this `while` statement as their target.
|
| - void buildWhile(
|
| - {SubbuildFunction buildCondition,
|
| - SubbuildFunction buildBody,
|
| - JumpTarget target,
|
| - ClosureScope closureScope,
|
| - SourceInformation sourceInformation}) {
|
| - assert(isOpen);
|
| - // While loops use four named continuations: the entry to the body, the
|
| - // loop exit, the loop back edge (continue), and the loop exit (break).
|
| - // The CPS translation of [[while (condition) body; successor]] is:
|
| - //
|
| - // let cont continue(x, ...) =
|
| - // let prim cond = [[condition]] in
|
| - // let cont break(x, ...) = [[successor]] in
|
| - // let cont exit() = break(v, ...)
|
| - // and body() =
|
| - // _enterScope();
|
| - // [[body]];
|
| - // continue(v, ...)
|
| - // in branch cond (body, exit)
|
| - // in continue(v, ...)
|
| - //
|
| - // If there are no breaks in the body, the break continuation is inlined
|
| - // in the exit continuation (i.e., the translation of the successor
|
| - // statement occurs in the exit continuation).
|
| - JumpCollector loop = new BackwardJumpCollector(environment, target: target);
|
| - addRecursiveContinuation(loop);
|
| -
|
| - ir.Primitive condition = buildCondition(this);
|
| -
|
| - JumpCollector breakCollector =
|
| - new ForwardJumpCollector(environment, target: target);
|
| -
|
| - IrBuilder bodyBuilder = makeDelimitedBuilder();
|
| - bodyBuilder._enterScope(closureScope);
|
| - state.breakCollectors.add(breakCollector);
|
| - state.continueCollectors.add(loop);
|
| - buildBody(bodyBuilder);
|
| - assert(state.breakCollectors.last == breakCollector);
|
| - assert(state.continueCollectors.last == loop);
|
| - state.breakCollectors.removeLast();
|
| - state.continueCollectors.removeLast();
|
| - if (bodyBuilder.isOpen) bodyBuilder.jumpTo(loop);
|
| -
|
| - // Create body entry and loop exit continuations and a branch to them.
|
| - ir.Continuation exitContinuation = new ir.Continuation([]);
|
| - ir.Continuation bodyContinuation = new ir.Continuation([]);
|
| - bodyContinuation.body = bodyBuilder.root;
|
| - // Note the order of continuations: the first one is the one that will
|
| - // be filled by LetCont.plug.
|
| - ir.LetCont branch = new ir.LetCont.two(
|
| - exitContinuation,
|
| - bodyContinuation,
|
| - new ir.Branch.strict(
|
| - condition, bodyContinuation, exitContinuation, sourceInformation));
|
| - // If there are breaks in the body, then there must be a join-point
|
| - // continuation for the normal exit and the breaks. Otherwise, the
|
| - // successor is translated in the hole in the exit continuation.
|
| - bool hasBreaks = !breakCollector.isEmpty;
|
| - ir.LetCont letBreak;
|
| - if (hasBreaks) {
|
| - IrBuilder exitBuilder = makeDelimitedBuilder();
|
| - exitBuilder.jumpTo(breakCollector);
|
| - exitContinuation.body = exitBuilder.root;
|
| - letBreak = new ir.LetCont(breakCollector.continuation, branch);
|
| - add(letBreak);
|
| - environment = breakCollector.environment;
|
| - } else {
|
| - add(branch);
|
| - }
|
| - }
|
| -
|
| - /// Creates a do-while loop.
|
| - ///
|
| - /// The body and condition are created by [buildBody] and [buildCondition].
|
| - /// The jump target [target] is the target of `break` and `continue`
|
| - /// statements in the body that have the loop as their target.
|
| - /// [closureScope] contains all the variables declared in the loop (but not
|
| - /// declared in some inner closure scope).
|
| - void buildDoWhile(
|
| - {SubbuildFunction buildBody,
|
| - SubbuildFunction buildCondition,
|
| - JumpTarget target,
|
| - ClosureScope closureScope,
|
| - SourceInformation sourceInformation}) {
|
| - assert(isOpen);
|
| - // The CPS translation of [[do body; while (condition); successor]] is:
|
| - //
|
| - // let cont break(x, ...) = [[successor]] in
|
| - // let cont rec loop(x, ...) =
|
| - // let cont continue(x, ...) =
|
| - // let prim cond = [[condition]] in
|
| - // let cont exit() = break(v, ...)
|
| - // and repeat() = loop(v, ...)
|
| - // in branch cond (repeat, exit)
|
| - // in [[body]]; continue(v, ...)
|
| - // in loop(v, ...)
|
| - IrBuilder loopBuilder = makeDelimitedBuilder();
|
| - JumpCollector loop =
|
| - new BackwardJumpCollector(loopBuilder.environment, target: target);
|
| - loopBuilder.addRecursiveContinuation(loop);
|
| -
|
| - // Translate the body.
|
| - JumpCollector breakCollector =
|
| - new ForwardJumpCollector(environment, target: target);
|
| - JumpCollector continueCollector =
|
| - new ForwardJumpCollector(loopBuilder.environment, target: target);
|
| - IrBuilder bodyBuilder = loopBuilder.makeDelimitedBuilder();
|
| - bodyBuilder._enterScope(closureScope);
|
| - state.breakCollectors.add(breakCollector);
|
| - state.continueCollectors.add(continueCollector);
|
| - buildBody(bodyBuilder);
|
| - assert(state.breakCollectors.last == breakCollector);
|
| - assert(state.continueCollectors.last == continueCollector);
|
| - state.breakCollectors.removeLast();
|
| - state.continueCollectors.removeLast();
|
| - if (bodyBuilder.isOpen) bodyBuilder.jumpTo(continueCollector);
|
| -
|
| - // Construct the body of the continue continuation (i.e., the condition).
|
| - // <Continue> =
|
| - // let prim cond = [[condition]] in
|
| - // let cont exit() = break(v, ...)
|
| - // and repeat() = loop(v, ...)
|
| - // in branch cond (repeat, exit)
|
| - IrBuilder continueBuilder = loopBuilder.makeDelimitedBuilder();
|
| - continueBuilder.environment = continueCollector.environment;
|
| - ir.Primitive condition = buildCondition(continueBuilder);
|
| -
|
| - ir.Continuation exitContinuation = new ir.Continuation([]);
|
| - IrBuilder exitBuilder = continueBuilder.makeDelimitedBuilder();
|
| - exitBuilder.jumpTo(breakCollector);
|
| - exitContinuation.body = exitBuilder.root;
|
| - ir.Continuation repeatContinuation = new ir.Continuation([]);
|
| - IrBuilder repeatBuilder = continueBuilder.makeDelimitedBuilder();
|
| - repeatBuilder.jumpTo(loop);
|
| - repeatContinuation.body = repeatBuilder.root;
|
| -
|
| - continueBuilder.add(new ir.LetCont.two(
|
| - exitContinuation,
|
| - repeatContinuation,
|
| - new ir.Branch.strict(condition, repeatContinuation, exitContinuation,
|
| - sourceInformation)));
|
| - continueCollector.continuation.body = continueBuilder.root;
|
| -
|
| - // Construct the loop continuation (i.e., the body and condition).
|
| - // <Loop> =
|
| - // let cont continue(x, ...) =
|
| - // <Continue>
|
| - // in [[body]]; continue(v, ...)
|
| - loopBuilder
|
| - .add(new ir.LetCont(continueCollector.continuation, bodyBuilder.root));
|
| -
|
| - // And tie it all together.
|
| - add(new ir.LetCont(breakCollector.continuation, loopBuilder.root));
|
| - environment = breakCollector.environment;
|
| - }
|
| -
|
| - void buildSimpleSwitch(JumpCollector join, List<SwitchCaseInfo> cases,
|
| - SubbuildFunction buildDefaultBody) {
|
| - IrBuilder casesBuilder = makeDelimitedBuilder();
|
| - for (SwitchCaseInfo caseInfo in cases) {
|
| - ir.Primitive condition = caseInfo.buildCondition(casesBuilder);
|
| - IrBuilder thenBuilder = makeDelimitedBuilder();
|
| - caseInfo.buildBody(thenBuilder);
|
| - ir.Continuation thenContinuation = new ir.Continuation([]);
|
| - thenContinuation.body = thenBuilder.root;
|
| - ir.Continuation elseContinuation = new ir.Continuation([]);
|
| - // A LetCont.two term has a hole as the body of the first listed
|
| - // continuation, to be plugged by the translation. Therefore put the
|
| - // else continuation first.
|
| - casesBuilder.add(new ir.LetCont.two(
|
| - elseContinuation,
|
| - thenContinuation,
|
| - new ir.Branch.strict(condition, thenContinuation, elseContinuation,
|
| - caseInfo.sourceInformation)));
|
| - }
|
| -
|
| - if (buildDefaultBody == null) {
|
| - casesBuilder.jumpTo(join);
|
| - } else {
|
| - buildDefaultBody(casesBuilder);
|
| - }
|
| -
|
| - if (!join.isEmpty) {
|
| - add(new ir.LetCont(join.continuation, casesBuilder.root));
|
| - environment = join.environment;
|
| - } else if (casesBuilder.root != null) {
|
| - add(casesBuilder.root);
|
| - _current = casesBuilder._current;
|
| - environment = casesBuilder.environment;
|
| - } else {
|
| - // The translation of the cases did not emit any code.
|
| - }
|
| - }
|
| -
|
| - /// Utility function to translate try/catch into the IR.
|
| - ///
|
| - /// The translation treats try/finally and try/catch/finally as if they
|
| - /// were macro-expanded into try/catch. This utility function generates
|
| - /// that try/catch. The function is parameterized over a list of variables
|
| - /// that should be boxed on entry to the try, and over functions to emit
|
| - /// code for entering the try, building the try body, leaving the try body,
|
| - /// building the catch body, and leaving the entire try/catch.
|
| - ///
|
| - /// Please see the function's implementation for where these functions are
|
| - /// called.
|
| - void _helpBuildTryCatch(
|
| - TryStatementInfo variables,
|
| - void enterTry(IrBuilder builder),
|
| - SubbuildFunction buildTryBlock,
|
| - void leaveTry(IrBuilder builder),
|
| - List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join),
|
| - void leaveTryCatch(
|
| - IrBuilder builder, JumpCollector join, ir.Expression body)) {
|
| - JumpCollector join = new ForwardJumpCollector(environment);
|
| - IrBuilder tryCatchBuilder = makeDelimitedBuilder();
|
| -
|
| - // Variables treated as mutable in a try are not mutable outside of it.
|
| - // Work with a copy of the outer builder's mutable variables.
|
| - tryCatchBuilder.mutableVariables =
|
| - new Map<Local, ir.MutableVariable>.from(mutableVariables);
|
| - for (LocalVariableElement variable in variables.boxedOnEntry) {
|
| - assert(!tryCatchBuilder.isInMutableVariable(variable));
|
| - ir.Primitive value = tryCatchBuilder.buildLocalGet(variable);
|
| - tryCatchBuilder.makeMutableVariable(variable);
|
| - tryCatchBuilder.declareLocalVariable(variable, initialValue: value);
|
| - }
|
| -
|
| - IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder();
|
| - enterTry(tryBuilder);
|
| - buildTryBlock(tryBuilder);
|
| - if (tryBuilder.isOpen) {
|
| - join.enterTry(variables.boxedOnEntry);
|
| - tryBuilder.jumpTo(join);
|
| - join.leaveTry();
|
| - }
|
| - leaveTry(tryBuilder);
|
| -
|
| - IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder();
|
| - for (LocalVariableElement variable in variables.boxedOnEntry) {
|
| - assert(catchBuilder.isInMutableVariable(variable));
|
| - ir.Primitive value = catchBuilder.buildLocalGet(variable);
|
| - // After this point, the variables that were boxed on entry to the try
|
| - // are no longer treated as mutable.
|
| - catchBuilder.removeMutableVariable(variable);
|
| - catchBuilder.environment.update(variable, value);
|
| - }
|
| -
|
| - List<ir.Parameter> catchParameters = buildCatch(catchBuilder, join);
|
| - ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
|
| - catchContinuation.body = catchBuilder.root;
|
| - tryCatchBuilder.add(new ir.LetHandler(catchContinuation, tryBuilder.root));
|
| -
|
| - leaveTryCatch(this, join, tryCatchBuilder.root);
|
| - }
|
| -
|
| - /// Translates a try/catch.
|
| - ///
|
| - /// [variables] provides information on local variables declared and boxed
|
| - /// within the try body.
|
| - /// [buildTryBlock] builds the try block.
|
| - /// [catchClauseInfos] provides access to the catch type, exception variable,
|
| - /// and stack trace variable, and a function for building the catch block.
|
| - void buildTryCatch(TryStatementInfo variables, SubbuildFunction buildTryBlock,
|
| - List<CatchClauseInfo> catchClauseInfos) {
|
| - assert(isOpen);
|
| - // Catch handlers are in scope for their body. The CPS translation of
|
| - // [[try tryBlock catch (ex, st) catchBlock; successor]] is:
|
| - //
|
| - // let cont join(v0, v1, ...) = [[successor]] in
|
| - // let mutable m0 = x0 in
|
| - // let mutable m1 = x1 in
|
| - // ...
|
| - // let handler catch_(ex, st) =
|
| - // let prim p0 = GetMutable(m0) in
|
| - // let prim p1 = GetMutable(m1) in
|
| - // ...
|
| - // [[catchBlock]]
|
| - // join(p0, p1, ...)
|
| - // in
|
| - // [[tryBlock]]
|
| - // let prim p0' = GetMutable(m0) in
|
| - // let prim p1' = GetMutable(m1) in
|
| - // ...
|
| - // join(p0', p1', ...)
|
| - //
|
| - // In other words, both the try and catch block are in the scope of the
|
| - // join-point continuation, and they are both in the scope of a sequence
|
| - // of mutable bindings for the variables assigned in the try. The join-
|
| - // point continuation is not in the scope of these mutable bindings.
|
| - // The tryBlock is in the scope of a binding for the catch handler. Each
|
| - // instruction (specifically, each call) in the tryBlock is in the dynamic
|
| - // scope of the handler. The mutable bindings are dereferenced at the end
|
| - // of the try block and at the beginning of the catch block, so the
|
| - // variables are unboxed in the catch block and at the join point.
|
| -
|
| - void enterTry(IrBuilder builder) {
|
| - // On entry to try of try/catch, update the builder's state to reflect the
|
| - // variables that have been boxed.
|
| - void interceptJump(JumpCollector collector) {
|
| - collector.enterTry(variables.boxedOnEntry);
|
| - }
|
| -
|
| - builder.state.breakCollectors.forEach(interceptJump);
|
| - builder.state.continueCollectors.forEach(interceptJump);
|
| - interceptJump(builder.state.returnCollector);
|
| - }
|
| -
|
| - void leaveTry(IrBuilder builder) {
|
| - // On exit from try of try/catch, update the builder's state to reflect
|
| - // the variables that are no longer boxed.
|
| - void restoreJump(JumpCollector collector) {
|
| - collector.leaveTry();
|
| - }
|
| -
|
| - builder.state.breakCollectors.forEach(restoreJump);
|
| - builder.state.continueCollectors.forEach(restoreJump);
|
| - restoreJump(builder.state.returnCollector);
|
| - }
|
| -
|
| - List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join) {
|
| - // Translate the catch clauses. Multiple clauses are translated as if
|
| - // they were explicitly cascaded if/else type tests.
|
| -
|
| - // Handlers are always translated as having both exception and stack trace
|
| - // parameters. Multiple clauses do not have to use the same names for
|
| - // them. Choose the first of each as the name hint for the respective
|
| - // handler parameter.
|
| - ir.Parameter exceptionParameter =
|
| - new ir.Parameter(catchClauseInfos.first.exceptionVariable);
|
| - LocalVariableElement traceVariable;
|
| - CatchClauseInfo catchAll;
|
| - for (int i = 0; i < catchClauseInfos.length; ++i) {
|
| - CatchClauseInfo info = catchClauseInfos[i];
|
| - if (info.type == null) {
|
| - catchAll = info;
|
| - catchClauseInfos.length = i;
|
| - break;
|
| - }
|
| - if (traceVariable == null) {
|
| - traceVariable = info.stackTraceVariable;
|
| - }
|
| - }
|
| - ir.Parameter traceParameter = new ir.Parameter(traceVariable);
|
| -
|
| - ir.Expression buildCatchClause(CatchClauseInfo clause) {
|
| - IrBuilder clauseBuilder = builder.makeDelimitedBuilder();
|
| - if (clause.exceptionVariable != null) {
|
| - clauseBuilder.declareLocalVariable(clause.exceptionVariable,
|
| - initialValue: exceptionParameter);
|
| - }
|
| - if (clause.stackTraceVariable != null) {
|
| - clauseBuilder.declareLocalVariable(clause.stackTraceVariable,
|
| - initialValue: traceParameter);
|
| - }
|
| - clause.buildCatchBlock(clauseBuilder);
|
| - if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
|
| - return clauseBuilder.root;
|
| - }
|
| -
|
| - // Expand multiple catch clauses into an explicit if/then/else. Iterate
|
| - // them in reverse so the current block becomes the next else block.
|
| - ir.Expression catchBody =
|
| - (catchAll == null) ? new ir.Rethrow() : buildCatchClause(catchAll);
|
| - for (CatchClauseInfo clause in catchClauseInfos.reversed) {
|
| - ir.Continuation thenContinuation = new ir.Continuation([]);
|
| - ir.Continuation elseContinuation = new ir.Continuation([]);
|
| - thenContinuation.body = buildCatchClause(clause);
|
| - elseContinuation.body = catchBody;
|
| -
|
| - // Build the type test guarding this clause. We can share the
|
| - // environment with the nested builder because this part cannot mutate
|
| - // it.
|
| - IrBuilder checkBuilder = builder.makeDelimitedBuilder(environment);
|
| - ir.Primitive typeMatches = checkBuilder.buildTypeOperator(
|
| - exceptionParameter, clause.type, clause.sourceInformation,
|
| - isTypeTest: true);
|
| - checkBuilder.add(new ir.LetCont.two(
|
| - thenContinuation,
|
| - elseContinuation,
|
| - new ir.Branch.strict(typeMatches, thenContinuation,
|
| - elseContinuation, clause.sourceInformation)));
|
| - catchBody = checkBuilder.root;
|
| - }
|
| - builder.add(catchBody);
|
| -
|
| - return <ir.Parameter>[exceptionParameter, traceParameter];
|
| - }
|
| -
|
| - void leaveTryCatch(
|
| - IrBuilder builder, JumpCollector join, ir.Expression body) {
|
| - // Add the binding for the join-point continuation and continue the
|
| - // translation in its body.
|
| - builder.add(new ir.LetCont(join.continuation, body));
|
| - builder.environment = join.environment;
|
| - }
|
| -
|
| - _helpBuildTryCatch(variables, enterTry, buildTryBlock, leaveTry, buildCatch,
|
| - leaveTryCatch);
|
| - }
|
| -
|
| - /// Translates a try/finally.
|
| - ///
|
| - /// [variables] provides information on local variables declared and boxed
|
| - /// within the try body.
|
| - /// [buildTryBlock] builds the try block.
|
| - /// [buildFinallyBlock] builds the finally block.
|
| - void buildTryFinally(TryStatementInfo variables,
|
| - SubbuildFunction buildTryBlock, SubbuildFunction buildFinallyBlock) {
|
| - assert(isOpen);
|
| - // Try/finally is implemented in terms of try/catch and by duplicating the
|
| - // code for finally at all exits. The encoding is:
|
| - //
|
| - // try tryBlock finally finallyBlock
|
| - // ==>
|
| - // try tryBlock catch (ex, st) { finallyBlock; rethrow } finallyBlock
|
| - //
|
| - // Where in tryBlock, all of the break, continue, and return exits are
|
| - // translated as jumps to continuations (bound outside the catch handler)
|
| - // that include the finally code followed by a break, continue, or
|
| - // return respectively.
|
| -
|
| - List<JumpCollector> savedBreaks, newBreaks, savedContinues, newContinues;
|
| - JumpCollector savedReturn, newReturn;
|
| - void enterTry(IrBuilder builder) {
|
| - // On entry to the try of try/finally, update the builder's state to
|
| - // relfect the variables that have been boxed. Then intercept all break,
|
| - // continue, and return jumps out of the try so that they can go to
|
| - // continuations that include the finally code.
|
| - JumpCollector interceptJump(JumpCollector collector) {
|
| - JumpCollector result =
|
| - new ForwardJumpCollector(environment, target: collector.target);
|
| - result.enterTry(variables.boxedOnEntry);
|
| - return result;
|
| - }
|
| -
|
| - savedBreaks = builder.state.breakCollectors;
|
| - savedContinues = builder.state.continueCollectors;
|
| - savedReturn = builder.state.returnCollector;
|
| -
|
| - builder.state.breakCollectors =
|
| - newBreaks = savedBreaks.map(interceptJump).toList();
|
| - builder.state.continueCollectors =
|
| - newContinues = savedContinues.map(interceptJump).toList();
|
| - builder.state.returnCollector = newReturn =
|
| - new ForwardJumpCollector(environment, hasExtraArgument: true)
|
| - ..enterTry(variables.boxedOnEntry);
|
| - }
|
| -
|
| - void leaveTry(IrBuilder builder) {
|
| - // On exit from the try of try/finally, update the builder's state to
|
| - // reflect the variables that are no longer boxed and restore the
|
| - // original, unintercepted break, continue, and return targets.
|
| - void restoreJump(JumpCollector collector) {
|
| - collector.leaveTry();
|
| - }
|
| -
|
| - newBreaks.forEach(restoreJump);
|
| - newContinues.forEach(restoreJump);
|
| - newReturn.leaveTry();
|
| - builder.state.breakCollectors = savedBreaks;
|
| - builder.state.continueCollectors = savedContinues;
|
| - builder.state.returnCollector = savedReturn;
|
| - }
|
| -
|
| - List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join) {
|
| - // The catch block of the try/catch used for try/finally is the finally
|
| - // code followed by a rethrow.
|
| - buildFinallyBlock(builder);
|
| - if (builder.isOpen) {
|
| - builder.add(new ir.Rethrow());
|
| - builder._current = null;
|
| - }
|
| - return <ir.Parameter>[new ir.Parameter(null), new ir.Parameter(null)];
|
| - }
|
| -
|
| - void leaveTryCatch(
|
| - IrBuilder builder, JumpCollector join, ir.Expression body) {
|
| - // Build a list of continuations for jumps from the try block and
|
| - // duplicate the finally code before jumping to the actual target.
|
| - List<ir.Continuation> exits = <ir.Continuation>[join.continuation];
|
| - void addJump(
|
| - JumpCollector newCollector, JumpCollector originalCollector) {
|
| - if (newCollector.isEmpty) return;
|
| - IrBuilder builder = makeDelimitedBuilder(newCollector.environment);
|
| - buildFinallyBlock(builder);
|
| - if (builder.isOpen) builder.jumpTo(originalCollector);
|
| - newCollector.continuation.body = builder.root;
|
| - exits.add(newCollector.continuation);
|
| - }
|
| -
|
| - for (int i = 0; i < newBreaks.length; ++i) {
|
| - addJump(newBreaks[i], savedBreaks[i]);
|
| - }
|
| - for (int i = 0; i < newContinues.length; ++i) {
|
| - addJump(newContinues[i], savedContinues[i]);
|
| - }
|
| - if (!newReturn.isEmpty) {
|
| - IrBuilder builder = makeDelimitedBuilder(newReturn.environment);
|
| - ir.Primitive value = builder.environment.discard(1);
|
| - buildFinallyBlock(builder);
|
| - if (builder.isOpen) builder.buildReturn(value: value);
|
| - newReturn.continuation.body = builder.root;
|
| - exits.add(newReturn.continuation);
|
| - }
|
| - builder.add(new ir.LetCont.many(exits, body));
|
| - builder.environment = join.environment;
|
| - buildFinallyBlock(builder);
|
| - }
|
| -
|
| - _helpBuildTryCatch(variables, enterTry, buildTryBlock, leaveTry, buildCatch,
|
| - leaveTryCatch);
|
| - }
|
| -
|
| - /// Create a return statement `return value;` or `return;` if [value] is
|
| - /// null.
|
| - void buildReturn({ir.Primitive value, SourceInformation sourceInformation}) {
|
| - // Build(Return(e), C) = C'[InvokeContinuation(return, x)]
|
| - // where (C', x) = Build(e, C)
|
| - //
|
| - // Return without a subexpression is translated as if it were return null.
|
| - assert(isOpen);
|
| - if (value == null) {
|
| - value = buildNullConstant();
|
| - }
|
| - jumpTo(state.returnCollector, value, sourceInformation);
|
| - }
|
| -
|
| - /// Generate the body for a native function [function] that is annotated with
|
| - /// an implementation in JavaScript (provided as string in [javaScriptCode]).
|
| - void buildNativeFunctionBody(FunctionElement function, String javaScriptCode,
|
| - SourceInformation sourceInformation) {
|
| - NativeBehavior behavior = new NativeBehavior();
|
| - behavior.sideEffects.setAllSideEffects();
|
| - // Generate a [ForeignCode] statement from the given native code.
|
| - buildForeignCode(
|
| - js.js
|
| - .statementTemplateYielding(new js.LiteralStatement(javaScriptCode)),
|
| - <ir.Primitive>[],
|
| - behavior,
|
| - sourceInformation);
|
| - }
|
| -
|
| - /// Generate the body for a native function that redirects to a native
|
| - /// JavaScript function, getter, or setter.
|
| - ///
|
| - /// Generates a call to the real target, which is given by [functions]'s
|
| - /// `fixedBackendName`, passing all parameters as arguments. The target can
|
| - /// be the JavaScript implementation of a function, getter, or setter.
|
| - void buildRedirectingNativeFunctionBody(FunctionElement function, String name,
|
| - SourceInformation sourceInformation) {
|
| - List<ir.Primitive> arguments = <ir.Primitive>[];
|
| - NativeBehavior behavior = new NativeBehavior();
|
| - behavior.sideEffects.setAllSideEffects();
|
| - program.addNativeMethod(function);
|
| - // Construct the access of the target element.
|
| - String code = function.isInstanceMember ? '#.$name' : name;
|
| - if (function.isInstanceMember) {
|
| - arguments.add(state.thisParameter);
|
| - }
|
| - // Collect all parameters of the function and templates for them to be
|
| - // inserted into the JavaScript code.
|
| - List<String> argumentTemplates = <String>[];
|
| - function.functionSignature.forEachParameter((ParameterElement parameter) {
|
| - ir.Primitive input = environment.lookup(parameter);
|
| - DartType type = program.unaliasType(parameter.type);
|
| - if (type is FunctionType) {
|
| - // The parameter type is a function type either directly or through
|
| - // typedef(s).
|
| - ir.Constant arity = buildIntegerConstant(type.computeArity());
|
| - input = buildStaticFunctionInvocation(program.closureConverter,
|
| - <ir.Primitive>[input, arity], sourceInformation);
|
| - }
|
| - arguments.add(input);
|
| - argumentTemplates.add('#');
|
| - });
|
| - // Construct the application of parameters for functions and setters.
|
| - if (function.kind == ElementKind.FUNCTION) {
|
| - code = "$code(${argumentTemplates.join(', ')})";
|
| - } else if (function.kind == ElementKind.SETTER) {
|
| - code = "$code = ${argumentTemplates.single}";
|
| - } else {
|
| - assert(argumentTemplates.isEmpty);
|
| - assert(function.kind == ElementKind.GETTER);
|
| - }
|
| - // Generate the [ForeignCode] expression and a return statement to return
|
| - // its value.
|
| - ir.Primitive value = buildForeignCode(
|
| - js.js.uncachedExpressionTemplate(code),
|
| - arguments,
|
| - behavior,
|
| - sourceInformation,
|
| - type: program.getTypeMaskForNativeFunction(function));
|
| - buildReturn(value: value, sourceInformation: sourceInformation);
|
| - }
|
| -
|
| - static _isNotNull(ir.Primitive value) =>
|
| - !(value is ir.Constant && value.value.isNull);
|
| -
|
| - /// Builds a call to a resolved js-interop element.
|
| - ir.Primitive buildInvokeJsInteropMember(FunctionElement element,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - program.addNativeMethod(element);
|
| - String target = program.getJsInteropTargetPath(element);
|
| - // Strip off trailing arguments that were not specified.
|
| - // TODO(jacobr,sigmund): assert that the trailing arguments are all null.
|
| - // TODO(jacobr): rewrite named arguments to an object literal matching
|
| - // the factory constructor case.
|
| - var inputs = arguments.where(_isNotNull).toList();
|
| -
|
| - var behavior = new NativeBehavior()..sideEffects.setAllSideEffects();
|
| - DartType type = element.isConstructor
|
| - ? element.enclosingClass.thisType
|
| - : element.type.returnType;
|
| - // Native behavior effects here are similar to native/behavior.dart.
|
| - // The return type is dynamic if we don't trust js-interop type
|
| - // declarations.
|
| - behavior.typesReturned.add(
|
| - program.trustJSInteropTypeAnnotations ? type : const DynamicType());
|
| -
|
| - // The allocation effects include the declared type if it is native (which
|
| - // includes js interop types).
|
| - if (type.element != null && program.isNative(type.element)) {
|
| - behavior.typesInstantiated.add(type);
|
| - }
|
| -
|
| - // It also includes any other JS interop type if we don't trust the
|
| - // annotation or if is declared too broad.
|
| - if (!program.trustJSInteropTypeAnnotations ||
|
| - type.isObject ||
|
| - type.isDynamic) {
|
| - behavior.typesInstantiated.add(program.jsJavascriptObjectType);
|
| - }
|
| -
|
| - String code;
|
| - if (element.isGetter) {
|
| - code = target;
|
| - } else if (element.isSetter) {
|
| - code = "$target = #";
|
| - } else {
|
| - var args = new List.filled(inputs.length, '#').join(',');
|
| - code = element.isConstructor ? "new $target($args)" : "$target($args)";
|
| - }
|
| - return buildForeignCode(
|
| - js.js.parseForeignJS(code), inputs, behavior, sourceInformation);
|
| - // TODO(sigmund): should we record the source-information here?
|
| - }
|
| -
|
| - /// Builds an object literal that results from invoking a factory constructor
|
| - /// of a js-interop anonymous type.
|
| - ir.Primitive buildJsInteropObjectLiteral(ConstructorElement constructor,
|
| - List<ir.Primitive> arguments, SourceInformation sourceInformation) {
|
| - assert(program.isJsInteropAnonymous(constructor));
|
| - program.addNativeMethod(constructor);
|
| - FunctionSignature params = constructor.functionSignature;
|
| - int i = 0;
|
| - var filteredArguments = <ir.Primitive>[];
|
| - var entries = new Map<String, js.Expression>();
|
| - params.orderedForEachParameter((ParameterElement parameter) {
|
| - // TODO(jacobr): throw if parameter names do not match names of property
|
| - // names in the class.
|
| - assert(parameter.isNamed);
|
| - ir.Primitive argument = arguments[i++];
|
| - if (_isNotNull(argument)) {
|
| - filteredArguments.add(argument);
|
| - entries[parameter.name] =
|
| - new js.InterpolatedExpression(filteredArguments.length - 1);
|
| - }
|
| - });
|
| - var code = new js.Template(null, js.objectLiteral(entries));
|
| - var behavior = new NativeBehavior();
|
| - if (program.trustJSInteropTypeAnnotations) {
|
| - behavior.typesReturned.add(constructor.enclosingClass.thisType);
|
| - }
|
| -
|
| - return buildForeignCode(
|
| - code, filteredArguments, behavior, sourceInformation);
|
| - }
|
| -
|
| - /// Create a blocks of [statements] by applying [build] to all reachable
|
| - /// statements. The first statement is assumed to be reachable.
|
| - // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses
|
| - // `List` instead of `Link`.
|
| - void buildBlock(var statements, BuildFunction build) {
|
| - // Build(Block(stamements), C) = C'
|
| - // where C' = statements.fold(Build, C)
|
| - assert(isOpen);
|
| - return buildSequence(statements, build);
|
| - }
|
| -
|
| - /// Creates a sequence of [nodes] by applying [build] to all reachable nodes.
|
| - ///
|
| - /// The first node in the sequence does not need to be reachable.
|
| - // TODO(johnniwinther): Type [nodes] as `Iterable` when `NodeList` uses
|
| - // `List` instead of `Link`.
|
| - void buildSequence(var nodes, BuildFunction build) {
|
| - for (var node in nodes) {
|
| - if (!isOpen) return;
|
| - build(node);
|
| - }
|
| - }
|
| -
|
| - /// Creates a labeled statement
|
| - void buildLabeledStatement({SubbuildFunction buildBody, JumpTarget target}) {
|
| - JumpCollector join = new ForwardJumpCollector(environment, target: target);
|
| - IrBuilder innerBuilder = makeDelimitedBuilder();
|
| - innerBuilder.state.breakCollectors.add(join);
|
| - buildBody(innerBuilder);
|
| - innerBuilder.state.breakCollectors.removeLast();
|
| - bool hasBreaks = !join.isEmpty;
|
| - if (hasBreaks) {
|
| - if (innerBuilder.isOpen) innerBuilder.jumpTo(join);
|
| - add(new ir.LetCont(join.continuation, innerBuilder.root));
|
| - environment = join.environment;
|
| - } else if (innerBuilder.root != null) {
|
| - add(innerBuilder.root);
|
| - _current = innerBuilder._current;
|
| - environment = innerBuilder.environment;
|
| - } else {
|
| - // The translation of the body did not emit any CPS term.
|
| - }
|
| - }
|
| -
|
| - // Build(BreakStatement L, C) = C[InvokeContinuation(...)]
|
| - //
|
| - // The continuation and arguments are filled in later after translating
|
| - // the body containing the break.
|
| - bool buildBreak(JumpTarget target) {
|
| - return buildJumpInternal(target, state.breakCollectors);
|
| - }
|
| -
|
| - // Build(ContinueStatement L, C) = C[InvokeContinuation(...)]
|
| - //
|
| - // The continuation and arguments are filled in later after translating
|
| - // the body containing the continue.
|
| - bool buildContinue(JumpTarget target) {
|
| - return buildJumpInternal(target, state.continueCollectors);
|
| - }
|
| -
|
| - bool buildJumpInternal(
|
| - JumpTarget target, Iterable<JumpCollector> collectors) {
|
| - assert(isOpen);
|
| - for (JumpCollector collector in collectors) {
|
| - if (target == collector.target) {
|
| - jumpTo(collector);
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - void buildThrow(ir.Primitive value) {
|
| - assert(isOpen);
|
| - add(new ir.Throw(value));
|
| - _current = null;
|
| - }
|
| -
|
| - ir.Primitive buildNonTailThrow(ir.Primitive value) {
|
| - assert(isOpen);
|
| - ir.Parameter param = new ir.Parameter(null);
|
| - ir.Continuation cont = new ir.Continuation(<ir.Parameter>[param]);
|
| - add(new ir.LetCont(cont, new ir.Throw(value)));
|
| - return param;
|
| - }
|
| -
|
| - void buildRethrow() {
|
| - assert(isOpen);
|
| - add(new ir.Rethrow());
|
| - _current = null;
|
| - }
|
| -
|
| - /// Create a negation of [condition].
|
| - ir.Primitive buildNegation(
|
| - ir.Primitive condition, SourceInformation sourceInformation) {
|
| - // ! e is translated as e ? false : true
|
| -
|
| - // Add a continuation parameter for the result of the expression.
|
| - ir.Parameter resultParameter = new ir.Parameter(null);
|
| -
|
| - ir.Continuation joinContinuation = new ir.Continuation([resultParameter]);
|
| - ir.Continuation thenContinuation = new ir.Continuation([]);
|
| - ir.Continuation elseContinuation = new ir.Continuation([]);
|
| -
|
| - ir.Constant makeBoolConstant(bool value) {
|
| - return new ir.Constant(state.constantSystem.createBool(value));
|
| - }
|
| -
|
| - ir.Constant trueConstant = makeBoolConstant(true);
|
| - ir.Constant falseConstant = makeBoolConstant(false);
|
| -
|
| - thenContinuation.body = new ir.LetPrim(falseConstant)
|
| - ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant]));
|
| - elseContinuation.body = new ir.LetPrim(trueConstant)
|
| - ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant]));
|
| -
|
| - add(new ir.LetCont(
|
| - joinContinuation,
|
| - new ir.LetCont.two(
|
| - thenContinuation,
|
| - elseContinuation,
|
| - new ir.Branch.strict(condition, thenContinuation, elseContinuation,
|
| - sourceInformation))));
|
| - return resultParameter;
|
| - }
|
| -
|
| - /// Create a lazy and/or expression. [leftValue] is the value of the left
|
| - /// operand and [buildRightValue] is called to process the value of the right
|
| - /// operand in the context of its own [IrBuilder].
|
| - ir.Primitive buildLogicalOperator(
|
| - ir.Primitive leftValue,
|
| - ir.Primitive buildRightValue(IrBuilder builder),
|
| - SourceInformation sourceInformation,
|
| - {bool isLazyOr: false}) {
|
| - // e0 && e1 is translated as if e0 ? (e1 == true) : false.
|
| - // e0 || e1 is translated as if e0 ? true : (e1 == true).
|
| - // The translation must convert both e0 and e1 to booleans and handle
|
| - // local variable assignments in e1.
|
| - IrBuilder rightBuilder = makeDelimitedBuilder();
|
| - ir.Primitive rightValue = buildRightValue(rightBuilder);
|
| - // A dummy empty target for the branch on the left subexpression branch.
|
| - // This enables using the same infrastructure for join-point continuations
|
| - // as in visitIf and visitConditional. It will hold a definition of the
|
| - // appropriate constant and an invocation of the join-point continuation.
|
| - IrBuilder emptyBuilder = makeDelimitedBuilder();
|
| - // Dummy empty targets for right true and right false. They hold
|
| - // definitions of the appropriate constant and an invocation of the
|
| - // join-point continuation.
|
| - IrBuilder rightTrueBuilder = rightBuilder.makeDelimitedBuilder();
|
| - IrBuilder rightFalseBuilder = rightBuilder.makeDelimitedBuilder();
|
| -
|
| - // If we don't evaluate the right subexpression, the value of the whole
|
| - // expression is this constant.
|
| - ir.Constant leftBool = emptyBuilder.buildBooleanConstant(isLazyOr);
|
| - // If we do evaluate the right subexpression, the value of the expression
|
| - // is a true or false constant.
|
| - ir.Constant rightTrue = rightTrueBuilder.buildBooleanConstant(true);
|
| - ir.Constant rightFalse = rightFalseBuilder.buildBooleanConstant(false);
|
| -
|
| - // Result values are passed as continuation arguments, which are
|
| - // constructed based on environments. These assertions are a sanity check.
|
| - assert(environment.length == emptyBuilder.environment.length);
|
| - assert(environment.length == rightTrueBuilder.environment.length);
|
| - assert(environment.length == rightFalseBuilder.environment.length);
|
| -
|
| - // Wire up two continuations for the left subexpression, two continuations
|
| - // for the right subexpression, and a three-way join continuation.
|
| - JumpCollector join =
|
| - new ForwardJumpCollector(environment, hasExtraArgument: true);
|
| - emptyBuilder.jumpTo(join, leftBool);
|
| - rightTrueBuilder.jumpTo(join, rightTrue);
|
| - rightFalseBuilder.jumpTo(join, rightFalse);
|
| - ir.Continuation leftTrueContinuation = new ir.Continuation([]);
|
| - ir.Continuation leftFalseContinuation = new ir.Continuation([]);
|
| - ir.Continuation rightTrueContinuation = new ir.Continuation([]);
|
| - ir.Continuation rightFalseContinuation = new ir.Continuation([]);
|
| - rightTrueContinuation.body = rightTrueBuilder.root;
|
| - rightFalseContinuation.body = rightFalseBuilder.root;
|
| - // The right subexpression has two continuations.
|
| - rightBuilder.add(new ir.LetCont.two(
|
| - rightTrueContinuation,
|
| - rightFalseContinuation,
|
| - new ir.Branch.strict(rightValue, rightTrueContinuation,
|
| - rightFalseContinuation, sourceInformation)));
|
| - // Depending on the operator, the left subexpression's continuations are
|
| - // either the right subexpression or an invocation of the join-point
|
| - // continuation.
|
| - if (isLazyOr) {
|
| - leftTrueContinuation.body = emptyBuilder.root;
|
| - leftFalseContinuation.body = rightBuilder.root;
|
| - } else {
|
| - leftTrueContinuation.body = rightBuilder.root;
|
| - leftFalseContinuation.body = emptyBuilder.root;
|
| - }
|
| -
|
| - add(new ir.LetCont(
|
| - join.continuation,
|
| - new ir.LetCont.two(
|
| - leftTrueContinuation,
|
| - leftFalseContinuation,
|
| - new ir.Branch.strict(leftValue, leftTrueContinuation,
|
| - leftFalseContinuation, sourceInformation))));
|
| - environment = join.environment;
|
| - return environment.discard(1);
|
| - }
|
| -
|
| - ir.Primitive buildIdentical(ir.Primitive x, ir.Primitive y,
|
| - {SourceInformation sourceInformation}) {
|
| - return addPrimitive(new ir.ApplyBuiltinOperator(
|
| - ir.BuiltinOperator.Identical, <ir.Primitive>[x, y], sourceInformation));
|
| - }
|
| -
|
| - /// Called when entering a nested function with free variables.
|
| - ///
|
| - /// The free variables must subsequently be accessible using [buildLocalGet]
|
| - /// and [buildLocalSet].
|
| - void _enterClosureEnvironment(ClosureEnvironment env) {
|
| - if (env == null) return;
|
| -
|
| - // Obtain a reference to the function object (this).
|
| - ir.Parameter thisPrim = state.thisParameter;
|
| -
|
| - // Obtain access to the free variables.
|
| - env.freeVariables.forEach((Local local, ClosureLocation location) {
|
| - if (location.isBox) {
|
| - // Boxed variables are loaded from their box on-demand.
|
| - state.boxedVariables[local] = location;
|
| - } else {
|
| - // Unboxed variables are loaded from the function object immediately.
|
| - // This includes BoxLocals which are themselves unboxed variables.
|
| - environment.extend(
|
| - local, addPrimitive(new ir.GetField(thisPrim, location.field)));
|
| - }
|
| - });
|
| -
|
| - // If the function captures a reference to the receiver from the
|
| - // enclosing method, remember which primitive refers to the receiver object.
|
| - if (env.thisLocal != null && env.freeVariables.containsKey(env.thisLocal)) {
|
| - state.enclosingThis = environment.lookup(env.thisLocal);
|
| - }
|
| -
|
| - // If the function has a self-reference, use the value of `this`.
|
| - if (env.selfReference != null) {
|
| - environment.extend(env.selfReference, thisPrim);
|
| - }
|
| - }
|
| -
|
| - /// Creates a box for [scope.box] and binds the captured variables to
|
| - /// that box.
|
| - ///
|
| - /// The captured variables can subsequently be manipulated with
|
| - /// [declareLocalVariable], [buildLocalGet], and [buildLocalSet].
|
| - void enterScope(ClosureScope scope) => _enterScope(scope);
|
| -
|
| - /// Called when entering a function body or loop body.
|
| - ///
|
| - /// This is not called for for-loops, which instead use the methods
|
| - /// [_enterForLoopInitializer], [_enterForLoopBody], and [_enterForLoopUpdate]
|
| - /// due to their special scoping rules.
|
| - ///
|
| - /// The boxed variables declared in this scope must subsequently be available
|
| - /// using [buildLocalGet], [buildLocalSet], etc.
|
| - void _enterScope(ClosureScope scope) {
|
| - if (scope == null) return;
|
| - ir.CreateBox boxPrim = addPrimitive(new ir.CreateBox());
|
| - environment.extend(scope.box, boxPrim);
|
| - boxPrim.useElementAsHint(scope.box);
|
| - scope.capturedVariables.forEach((Local local, ClosureLocation location) {
|
| - assert(!state.boxedVariables.containsKey(local));
|
| - if (location.isBox) {
|
| - state.boxedVariables[local] = location;
|
| - }
|
| - });
|
| - }
|
| -
|
| - /// Add the given function parameter to the IR, and bind it in the environment
|
| - /// or put it in its box, if necessary.
|
| - void _createFunctionParameter(Local parameterElement) {
|
| - ir.Parameter parameter = new ir.Parameter(parameterElement);
|
| - _parameters.add(parameter);
|
| - state.functionParameters.add(parameter);
|
| - ClosureLocation location = state.boxedVariables[parameterElement];
|
| - if (location != null) {
|
| - addPrimitive(new ir.SetField(
|
| - environment.lookup(location.box), location.field, parameter));
|
| - } else {
|
| - environment.extend(parameterElement, parameter);
|
| - }
|
| - }
|
| -
|
| - void _createThisParameter() {
|
| - assert(state.thisParameter == null);
|
| - if (Elements.isStaticOrTopLevel(state.currentElement)) return;
|
| - if (state.currentElement.isLocal) return;
|
| - state.thisParameter =
|
| - new ir.Parameter(new ThisParameterLocal(state.currentElement));
|
| - }
|
| -
|
| - void declareLocalVariable(LocalElement variableElement,
|
| - {ir.Primitive initialValue}) {
|
| - assert(isOpen);
|
| - if (initialValue == null) {
|
| - initialValue = buildNullConstant();
|
| - }
|
| - ClosureLocation location = state.boxedVariables[variableElement];
|
| - if (location != null) {
|
| - addPrimitive(new ir.SetField(
|
| - environment.lookup(location.box), location.field, initialValue));
|
| - } else if (isInMutableVariable(variableElement)) {
|
| - add(new ir.LetMutable(getMutableVariable(variableElement), initialValue));
|
| - } else {
|
| - initialValue.useElementAsHint(variableElement);
|
| - environment.extend(variableElement, initialValue);
|
| - }
|
| - }
|
| -
|
| - /// Add [functionElement] to the environment with provided [definition].
|
| - void declareLocalFunction(
|
| - LocalFunctionElement functionElement,
|
| - closure.ClosureClassElement classElement,
|
| - SourceInformation sourceInformation) {
|
| - ir.Primitive closure =
|
| - buildFunctionExpression(classElement, sourceInformation);
|
| - declareLocalVariable(functionElement, initialValue: closure);
|
| - }
|
| -
|
| - ir.Primitive buildFunctionExpression(closure.ClosureClassElement classElement,
|
| - SourceInformation sourceInformation) {
|
| - List<ir.Primitive> arguments = <ir.Primitive>[];
|
| - for (closure.ClosureFieldElement field in classElement.closureFields) {
|
| - // Captured 'this' and type variables are not always available as locals
|
| - // in the environment, so treat those specially.
|
| - ir.Primitive value;
|
| - if (field.local is closure.ThisLocal) {
|
| - value = buildThis();
|
| - } else if (field.local is closure.TypeVariableLocal) {
|
| - closure.TypeVariableLocal variable = field.local;
|
| - value = buildTypeVariableAccess(variable.typeVariable);
|
| - } else {
|
| - value = environment.lookup(field.local);
|
| - }
|
| - arguments.add(value);
|
| - }
|
| - return addPrimitive(new ir.CreateInstance(
|
| - classElement, arguments, null, sourceInformation));
|
| - }
|
| -
|
| - /// Create a read access of [local] function, variable, or parameter.
|
| - // TODO(johnniwinther): Make [sourceInformation] mandatory.
|
| - ir.Primitive buildLocalGet(LocalElement local,
|
| - {SourceInformation sourceInformation}) {
|
| - assert(isOpen);
|
| - ClosureLocation location = state.boxedVariables[local];
|
| - if (location != null) {
|
| - ir.Primitive result = new ir.GetField(
|
| - environment.lookup(location.box), location.field,
|
| - sourceInformation: sourceInformation);
|
| - result.useElementAsHint(local);
|
| - return addPrimitive(result);
|
| - } else if (isInMutableVariable(local)) {
|
| - return addPrimitive(new ir.GetMutable(getMutableVariable(local),
|
| - sourceInformation: sourceInformation));
|
| - } else {
|
| - return environment.lookup(local);
|
| - }
|
| - }
|
| -
|
| - /// Create a write access to [local] variable or parameter with the provided
|
| - /// [value].
|
| - ir.Primitive buildLocalVariableSet(LocalElement local, ir.Primitive value,
|
| - SourceInformation sourceInformation) {
|
| - assert(isOpen);
|
| - ClosureLocation location = state.boxedVariables[local];
|
| - if (location != null) {
|
| - addPrimitive(new ir.SetField(
|
| - environment.lookup(location.box), location.field, value,
|
| - sourceInformation: sourceInformation));
|
| - } else if (isInMutableVariable(local)) {
|
| - addPrimitive(new ir.SetMutable(getMutableVariable(local), value,
|
| - sourceInformation: sourceInformation));
|
| - } else {
|
| - value.useElementAsHint(local);
|
| - environment.update(local, value);
|
| - }
|
| - return value;
|
| - }
|
| -
|
| - /// Called before building the initializer of a for-loop.
|
| - ///
|
| - /// The loop variables will subsequently be declared using
|
| - /// [declareLocalVariable].
|
| - void _enterForLoopInitializer(
|
| - ClosureScope scope, List<LocalElement> loopVariables) {
|
| - if (scope == null) return;
|
| - // If there are no boxed loop variables, don't create the box here, let
|
| - // it be created inside the body instead.
|
| - if (scope.boxedLoopVariables.isEmpty) return;
|
| - _enterScope(scope);
|
| - }
|
| -
|
| - /// Called before building the body of a for-loop.
|
| - void _enterForLoopBody(ClosureScope scope, List<LocalElement> loopVariables) {
|
| - if (scope == null) return;
|
| - // If there are boxed loop variables, the box has already been created
|
| - // at the initializer.
|
| - if (!scope.boxedLoopVariables.isEmpty) return;
|
| - _enterScope(scope);
|
| - }
|
| -
|
| - /// Called before building the update of a for-loop.
|
| - void _enterForLoopUpdate(
|
| - ClosureScope scope, List<LocalElement> loopVariables) {
|
| - if (scope == null) return;
|
| - // If there are no boxed loop variables, then the box is created inside the
|
| - // body, so there is no need to explicitly renew it.
|
| - if (scope.boxedLoopVariables.isEmpty) return;
|
| - ir.Primitive box = environment.lookup(scope.box);
|
| - ir.Primitive newBox = addPrimitive(new ir.CreateBox());
|
| - newBox.useElementAsHint(scope.box);
|
| - for (VariableElement loopVar in scope.boxedLoopVariables) {
|
| - ClosureLocation location = scope.capturedVariables[loopVar];
|
| - ir.Primitive value = addPrimitive(new ir.GetField(box, location.field));
|
| - addPrimitive(new ir.SetField(newBox, location.field, value));
|
| - }
|
| - environment.update(scope.box, newBox);
|
| - }
|
| -
|
| - /// Creates an access to the receiver from the current (or enclosing) method.
|
| - ///
|
| - /// If inside a closure class, [buildThis] will redirect access through
|
| - /// closure fields in order to access the receiver from the enclosing method.
|
| - ir.Primitive buildThis() {
|
| - if (state.enclosingThis != null) return state.enclosingThis;
|
| - assert(state.thisParameter != null);
|
| - return state.thisParameter;
|
| - }
|
| -
|
| - ir.Primitive buildFieldGet(ir.Primitive receiver, FieldElement target,
|
| - SourceInformation sourceInformation) {
|
| - return addPrimitive(new ir.GetField(receiver, target,
|
| - sourceInformation: sourceInformation,
|
| - isFinal: program.fieldNeverChanges(target)));
|
| - }
|
| -
|
| - void buildFieldSet(ir.Primitive receiver, FieldElement target,
|
| - ir.Primitive value, SourceInformation sourceInformation) {
|
| - addPrimitive(new ir.SetField(receiver, target, value,
|
| - sourceInformation: sourceInformation));
|
| - }
|
| -
|
| - ir.Primitive buildSuperFieldGet(
|
| - FieldElement target, SourceInformation sourceInformation) {
|
| - return addPrimitive(new ir.GetField(buildThis(), target,
|
| - sourceInformation: sourceInformation));
|
| - }
|
| -
|
| - ir.Primitive buildSuperFieldSet(FieldElement target, ir.Primitive value,
|
| - SourceInformation sourceInformation) {
|
| - addPrimitive(new ir.SetField(buildThis(), target, value,
|
| - sourceInformation: sourceInformation));
|
| - return value;
|
| - }
|
| -
|
| - /// Loads parameters to a constructor body into the environment.
|
| - ///
|
| - /// The header for a constructor body differs from other functions in that
|
| - /// some parameters are already boxed, and the box is passed as an argument
|
| - /// instead of being created in the header.
|
| - void buildConstructorBodyHeader(
|
| - Iterable<Local> parameters, ClosureScope closureScope) {
|
| - _createThisParameter();
|
| - for (Local param in parameters) {
|
| - ir.Parameter parameter = _createLocalParameter(param);
|
| - state.functionParameters.add(parameter);
|
| - }
|
| - if (closureScope != null) {
|
| - state.boxedVariables.addAll(closureScope.capturedVariables);
|
| - }
|
| - }
|
| -
|
| - /// Create a constructor invocation of [element] on [type] where the
|
| - /// constructor name and argument structure are defined by [callStructure] and
|
| - /// the argument values are defined by [arguments].
|
| - ir.Primitive buildConstructorInvocation(
|
| - ConstructorElement element,
|
| - CallStructure callStructure,
|
| - DartType type,
|
| - List<ir.Primitive> arguments,
|
| - SourceInformation sourceInformation,
|
| - {TypeMask allocationSiteType}) {
|
| - assert(isOpen);
|
| - Selector selector =
|
| - new Selector(SelectorKind.CALL, element.memberName, callStructure);
|
| - ClassElement cls = element.enclosingClass;
|
| - if (program.isJsInterop(element)) {
|
| - if (program.isJsInteropAnonymous(element)) {
|
| - return buildJsInteropObjectLiteral(
|
| - element, arguments, sourceInformation);
|
| - }
|
| - return buildInvokeJsInteropMember(element, arguments, sourceInformation);
|
| - }
|
| - if (program.requiresRuntimeTypesFor(cls)) {
|
| - InterfaceType interface = type;
|
| - Iterable<ir.Primitive> typeArguments =
|
| - interface.typeArguments.map((DartType argument) {
|
| - return type.treatAsRaw
|
| - ? buildNullConstant()
|
| - : buildTypeExpression(argument);
|
| - });
|
| - arguments = new List<ir.Primitive>.from(arguments)..addAll(typeArguments);
|
| - }
|
| - return addPrimitive(new ir.InvokeConstructor(
|
| - type, element, selector, arguments, sourceInformation,
|
| - allocationSiteType: allocationSiteType));
|
| - }
|
| -
|
| - ir.Primitive buildTypeExpression(DartType type) {
|
| - type = program.unaliasType(type);
|
| - if (type is TypeVariableType) {
|
| - return buildTypeVariableAccess(type);
|
| - } else if (type is InterfaceType || type is FunctionType) {
|
| - List<ir.Primitive> arguments = <ir.Primitive>[];
|
| - type.forEachTypeVariable((TypeVariableType variable) {
|
| - ir.Primitive value = buildTypeVariableAccess(variable);
|
| - arguments.add(value);
|
| - });
|
| - return addPrimitive(new ir.TypeExpression(
|
| - ir.TypeExpressionKind.COMPLETE, type, arguments));
|
| - } else if (type.treatAsDynamic) {
|
| - return buildNullConstant();
|
| - } else {
|
| - // TypedefType can reach here, and possibly other things.
|
| - throw 'unimplemented translation of type expression $type (${type.kind})';
|
| - }
|
| - }
|
| -
|
| - /// Obtains the internal type representation of the type held in [variable].
|
| - ///
|
| - /// The value of [variable] is taken from the current receiver object, or
|
| - /// if we are currently building a constructor field initializer, from the
|
| - /// corresponding type argument (field initializers are evaluated before the
|
| - /// receiver object is created).
|
| - ir.Primitive buildTypeVariableAccess(TypeVariableType variable,
|
| - {SourceInformation sourceInformation}) {
|
| - // If the local exists in the environment, use that.
|
| - // This is put here when we are inside a constructor or field initializer,
|
| - // (or possibly a closure inside one of these).
|
| - Local local = new closure.TypeVariableLocal(variable, state.currentElement);
|
| - if (environment.contains(local)) {
|
| - return environment.lookup(local);
|
| - }
|
| -
|
| - // If the type variable is not in a local, read its value from the
|
| - // receiver object.
|
| - ir.Primitive target = buildThis();
|
| - return addPrimitive(
|
| - new ir.ReadTypeVariable(variable, target, sourceInformation));
|
| - }
|
| -
|
| - /// Make the given type variable accessible through the local environment
|
| - /// with the value of [binding].
|
| - void declareTypeVariable(TypeVariableType variable, DartType binding) {
|
| - environment.extend(
|
| - new closure.TypeVariableLocal(variable, state.currentElement),
|
| - buildTypeExpression(binding));
|
| - }
|
| -
|
| - /// Reifies the value of [variable] on the current receiver object.
|
| - ir.Primitive buildReifyTypeVariable(
|
| - TypeVariableType variable, SourceInformation sourceInformation) {
|
| - ir.Primitive typeArgument =
|
| - buildTypeVariableAccess(variable, sourceInformation: sourceInformation);
|
| - return addPrimitive(
|
| - new ir.ReifyRuntimeType(typeArgument, sourceInformation));
|
| - }
|
| -
|
| - ir.Primitive buildInvocationMirror(
|
| - Selector selector, List<ir.Primitive> arguments) {
|
| - return addPrimitive(new ir.CreateInvocationMirror(selector, arguments));
|
| - }
|
| -
|
| - ir.Primitive buildForeignCode(
|
| - js.Template codeTemplate,
|
| - List<ir.Primitive> arguments,
|
| - NativeBehavior behavior,
|
| - SourceInformation sourceInformation,
|
| - {Element dependency,
|
| - TypeMask type}) {
|
| - assert(behavior != null);
|
| - if (type == null) {
|
| - type = program.getTypeMaskForForeign(behavior);
|
| - }
|
| - if (js.isIdentityTemplate(codeTemplate) && !program.isArrayType(type)) {
|
| - // JS expression is just a refinement.
|
| - // Do not do this for arrays - those are special because array types can
|
| - // change after creation. The input and output must therefore be modeled
|
| - // as distinct values.
|
| - return addPrimitive(new ir.Refinement(arguments.single, type));
|
| - }
|
| - ir.Primitive result = addPrimitive(new ir.ForeignCode(
|
| - codeTemplate, type, arguments, behavior, sourceInformation,
|
| - dependency: dependency));
|
| - if (!codeTemplate.isExpression) {
|
| - // Close the term if this is a "throw" expression or native body.
|
| - add(new ir.Unreachable());
|
| - _current = null;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /// Creates a type test or type cast of [value] against [type].
|
| - ir.Primitive buildTypeOperator(
|
| - ir.Primitive value, DartType type, SourceInformation sourceInformation,
|
| - {bool isTypeTest}) {
|
| - assert(isOpen);
|
| - assert(isTypeTest != null);
|
| -
|
| - type = program.unaliasType(type);
|
| -
|
| - if (type.isMalformed) {
|
| - String message;
|
| - if (type is MalformedType) {
|
| - ErroneousElement element = type.element;
|
| - message = element.message;
|
| - } else {
|
| - assert(type is MethodTypeVariableType);
|
| - message = "Method type variables are not reified, "
|
| - "so they cannot be tested dynamically";
|
| - }
|
| - ir.Primitive irMessage = buildStringConstant(message);
|
| - return buildStaticFunctionInvocation(program.throwTypeErrorHelper,
|
| - <ir.Primitive>[irMessage], sourceInformation);
|
| - }
|
| -
|
| - List<ir.Primitive> typeArguments = const <ir.Primitive>[];
|
| - if (type is GenericType && type.typeArguments.isNotEmpty) {
|
| - typeArguments = type.typeArguments.map(buildTypeExpression).toList();
|
| - } else if (type is TypeVariableType) {
|
| - typeArguments = <ir.Primitive>[buildTypeVariableAccess(type)];
|
| - } else if (type is FunctionType) {
|
| - typeArguments = <ir.Primitive>[buildTypeExpression(type)];
|
| - }
|
| -
|
| - if (isTypeTest) {
|
| - // For type tests, we must treat specially the rare cases where `null`
|
| - // satisfies the test (which otherwise never satisfies a type test).
|
| - // This is not an optimization: the TypeOperator assumes that `null`
|
| - // cannot satisfy the type test unless the type is a type variable.
|
| - if (type.isObject || type.isDynamic) {
|
| - // `x is Object` and `x is dynamic` are always true, even if x is null.
|
| - return buildBooleanConstant(true);
|
| - }
|
| - if (type is InterfaceType && type.element == program.nullClass) {
|
| - // `x is Null` is true if and only if x is null.
|
| - return _buildCheckNull(value, sourceInformation);
|
| - }
|
| - return addPrimitive(new ir.TypeTest(value, type, typeArguments));
|
| - } else {
|
| - if (type.isObject || type.isDynamic) {
|
| - // `x as Object` and `x as dynamic` are the same as `x`.
|
| - return value;
|
| - }
|
| - return addPrimitive(new ir.TypeCast(value, type, typeArguments));
|
| - }
|
| - }
|
| -
|
| - /// Create an if-null expression. This is equivalent to a conditional
|
| - /// expression whose result is either [value] if [value] is not null, or
|
| - /// `right` if [value] is null. Only when [value] is null, [buildRight] is
|
| - /// evaluated to produce the `right` value.
|
| - ir.Primitive buildIfNull(
|
| - ir.Primitive value,
|
| - ir.Primitive buildRight(IrBuilder builder),
|
| - SourceInformation sourceInformation) {
|
| - ir.Primitive condition = _buildCheckNull(value, sourceInformation);
|
| - return buildConditional(
|
| - condition, buildRight, (_) => value, sourceInformation);
|
| - }
|
| -
|
| - /// Create a conditional send. This is equivalent to a conditional expression
|
| - /// that checks if [receiver] is null, if so, it returns null, otherwise it
|
| - /// evaluates the [buildSend] expression.
|
| - ir.Primitive buildIfNotNullSend(
|
| - ir.Primitive receiver,
|
| - ir.Primitive buildSend(IrBuilder builder),
|
| - SourceInformation sourceInformation) {
|
| - ir.Primitive condition = _buildCheckNull(receiver, sourceInformation);
|
| - return buildConditional(
|
| - condition, (_) => receiver, buildSend, sourceInformation);
|
| - }
|
| -
|
| - /// Creates a type test checking whether [value] is null.
|
| - ir.Primitive _buildCheckNull(
|
| - ir.Primitive value, SourceInformation sourceInformation) {
|
| - assert(isOpen);
|
| - return buildIdentical(value, buildNullConstant(),
|
| - sourceInformation: sourceInformation);
|
| - }
|
| -}
|
| -
|
| -/// Location of a variable relative to a given closure.
|
| -class ClosureLocation {
|
| - /// If not `null`, this location is [box].[field].
|
| - /// The location of [box] can be obtained separately from an
|
| - /// enclosing [ClosureEnvironment] or [ClosureScope].
|
| - /// If `null`, then the location is [field] on the enclosing function object.
|
| - final closure.BoxLocal box;
|
| -
|
| - /// The field in which the variable is stored.
|
| - final Entity field;
|
| -
|
| - bool get isBox => box != null;
|
| -
|
| - ClosureLocation(this.box, this.field);
|
| -
|
| - /// Converts a map containing closure.dart's [CapturedVariable]s into one
|
| - /// containing [ClosureLocation]s.
|
| - ///
|
| - /// There is a 1:1 corresponce between these; we do this because the
|
| - /// IR builder should not depend on synthetic elements.
|
| - static Map<Local, ClosureLocation> mapFrom(
|
| - Map<Local, closure.CapturedVariable> map) {
|
| - Map result = {};
|
| - map.forEach((Local k, closure.CapturedVariable v) {
|
| - closure.BoxLocal box = v is closure.BoxFieldElement ? v.box : null;
|
| - result[k] = new ClosureLocation(box, v);
|
| - });
|
| - return result;
|
| - }
|
| -}
|
| -
|
| -/// Introduces a new box and binds local variables to this box.
|
| -///
|
| -/// A [ClosureScope] may exist for each function and for each loop.
|
| -/// Generally, one may pass `null` to the [IrBuilder] instead of a
|
| -/// [ClosureScope] when a given scope has no boxed variables.
|
| -class ClosureScope {
|
| - /// This box is now in scope and [capturedVariables] may use it.
|
| - final closure.BoxLocal box;
|
| -
|
| - /// Maps [LocalElement]s to their location.
|
| - final Map<Local, ClosureLocation> capturedVariables;
|
| -
|
| - /// If this is the scope of a for-loop, [boxedLoopVariables] is the list
|
| - /// of boxed variables that are declared in the initializer.
|
| - final List<VariableElement> boxedLoopVariables;
|
| -
|
| - factory ClosureScope(closure.ClosureScope scope) {
|
| - return scope == null ? null : new ClosureScope._internal(scope);
|
| - }
|
| -
|
| - ClosureScope._internal(closure.ClosureScope scope)
|
| - : box = scope.boxElement,
|
| - capturedVariables = ClosureLocation.mapFrom(scope.capturedVariables),
|
| - boxedLoopVariables = scope.boxedLoopVariables;
|
| -}
|
| -
|
| -/// Environment passed when building a nested function, describing how
|
| -/// to access variables from the enclosing scope.
|
| -class ClosureEnvironment {
|
| - /// References to this local should be treated as recursive self-reference.
|
| - /// (This is *not* in [freeVariables]).
|
| - final LocalFunctionElement selfReference;
|
| -
|
| - /// If non-null, [thisLocal] has an entry in [freeVariables] describing where
|
| - /// to find the captured value of `this`.
|
| - final closure.ThisLocal thisLocal;
|
| -
|
| - /// Maps [LocalElement]s, [BoxLocal]s and [ThisLocal] to their location.
|
| - final Map<Local, ClosureLocation> freeVariables;
|
| -
|
| - factory ClosureEnvironment(closure.ClosureClassMap closureClassMap) {
|
| - if (closureClassMap.closureElement == null) return null;
|
| - return new ClosureEnvironment._internal(closureClassMap);
|
| - }
|
| -
|
| - ClosureEnvironment._internal(closure.ClosureClassMap closureClassMap)
|
| - : selfReference = closureClassMap.closureElement,
|
| - thisLocal = closureClassMap.thisLocal,
|
| - freeVariables =
|
| - ClosureLocation.mapFrom(closureClassMap.freeVariableMap);
|
| -}
|
| -
|
| -class TryStatementInfo {
|
| - final Set<LocalVariableElement> declared = new Set<LocalVariableElement>();
|
| - final Set<LocalVariableElement> boxedOnEntry =
|
| - new Set<LocalVariableElement>();
|
| -}
|
| -
|
| -class CatchClauseInfo {
|
| - final DartType type;
|
| - final LocalVariableElement exceptionVariable;
|
| - final LocalVariableElement stackTraceVariable;
|
| - final SubbuildFunction buildCatchBlock;
|
| - final SourceInformation sourceInformation;
|
| -
|
| - CatchClauseInfo(
|
| - {this.type,
|
| - this.exceptionVariable,
|
| - this.stackTraceVariable,
|
| - this.buildCatchBlock,
|
| - this.sourceInformation});
|
| -}
|
| -
|
| -class SwitchCaseInfo {
|
| - final SubbuildFunction buildCondition;
|
| - final SubbuildFunction buildBody;
|
| - final SourceInformation sourceInformation;
|
| -
|
| - SwitchCaseInfo(this.buildCondition, this.buildBody, this.sourceInformation);
|
| -}
|
|
|