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

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

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_fragment.dart ('k') | pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder.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);
-}
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_fragment.dart ('k') | pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698