| Index: pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
|
| index 411d3b900466123838faf8b5bfca698771388425..61a5ddd961408f55925d59b7b72f0c03c72b194b 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
|
| @@ -67,7 +67,8 @@ abstract class Node {
|
| /// same node.
|
| String debugString([Map annotations]) {
|
| return new SExpressionStringifier()
|
| - .withAnnotations(annotations).visit(this);
|
| + .withAnnotations(annotations)
|
| + .visit(this);
|
| }
|
|
|
| /// Prints the result of [debugString].
|
| @@ -118,70 +119,17 @@ abstract class InteriorNode extends Node {
|
| accept(BlockVisitor visitor);
|
| }
|
|
|
| -/// An expression that creates new bindings and continues evaluation in
|
| -/// a subexpression.
|
| -///
|
| -/// The interior expressions are [LetPrim], [LetCont], [LetHandler], and
|
| -/// [LetMutable].
|
| -abstract class InteriorExpression extends Expression implements InteriorNode {
|
| - Expression get next => body;
|
| -
|
| - /// Removes this expression from its current position in the IR.
|
| - ///
|
| - /// The node can be re-inserted elsewhere or remain orphaned.
|
| - ///
|
| - /// If orphaned, the caller is responsible for unlinking all references in
|
| - /// the orphaned node. Use [Reference.unlink] or [Primitive.destroy] for this.
|
| - void remove() {
|
| - assert(parent != null);
|
| - assert(parent.body == this);
|
| - assert(body.parent == this);
|
| - parent.body = body;
|
| - body.parent = parent;
|
| - parent = null;
|
| - body = null;
|
| - }
|
| -
|
| - /// Inserts this above [node].
|
| - ///
|
| - /// This node must be orphaned first.
|
| - void insertAbove(Expression node) {
|
| - insertBelow(node.parent);
|
| - }
|
| -
|
| - /// Inserts this below [node].
|
| - ///
|
| - /// This node must be orphaned first.
|
| - void insertBelow(InteriorNode newParent) {
|
| - assert(parent == null);
|
| - assert(body == null);
|
| - Expression child = newParent.body;
|
| - newParent.body = this;
|
| - this.body = child;
|
| - child.parent = this;
|
| - this.parent = newParent;
|
| - }
|
| -}
|
| -
|
| -/// An expression without a continuation or a subexpression body.
|
| -///
|
| -/// These break straight-line control flow and can be thought of as ending a
|
| -/// basic block.
|
| -abstract class TailExpression extends Expression {
|
| - Expression get next => null;
|
| -}
|
| -
|
| /// The base class of things that variables can refer to: primitives,
|
| /// continuations, function and continuation parameters, etc.
|
| abstract class Definition<T extends Definition<T>> extends Node {
|
| // The head of a linked-list of occurrences, in no particular order.
|
| Reference<T> firstRef;
|
|
|
| - bool get hasAtMostOneUse => firstRef == null || firstRef.next == null;
|
| + bool get hasAtMostOneUse => firstRef == null || firstRef.next == null;
|
| bool get hasExactlyOneUse => firstRef != null && firstRef.next == null;
|
| bool get hasNoUses => firstRef == null;
|
| bool get hasAtLeastOneUse => firstRef != null;
|
| - bool get hasMultipleUses => !hasAtMostOneUse;
|
| + bool get hasMultipleUses => !hasAtMostOneUse;
|
|
|
| void replaceUsesWith(Definition<T> newDefinition) {
|
| if (newDefinition == this) return;
|
| @@ -201,6 +149,45 @@ abstract class Definition<T extends Definition<T>> extends Node {
|
| }
|
| }
|
|
|
| +/// Operands to invocations and primitives are always variables. They point to
|
| +/// their definition and are doubly-linked into a list of occurrences.
|
| +class Reference<T extends Definition<T>> {
|
| + T definition;
|
| + Reference<T> previous;
|
| + Reference<T> next;
|
| +
|
| + /// A pointer to the parent node. Is null until set by optimization passes.
|
| + Node parent;
|
| +
|
| + Reference(this.definition) {
|
| + next = definition.firstRef;
|
| + if (next != null) next.previous = this;
|
| + definition.firstRef = this;
|
| + }
|
| +
|
| + /// Unlinks this reference from the list of occurrences.
|
| + void unlink() {
|
| + if (previous == null) {
|
| + assert(definition.firstRef == this);
|
| + definition.firstRef = next;
|
| + } else {
|
| + previous.next = next;
|
| + }
|
| + if (next != null) next.previous = previous;
|
| + }
|
| +
|
| + /// Changes the definition referenced by this object and updates
|
| + /// the reference chains accordingly.
|
| + void changeTo(Definition<T> newDefinition) {
|
| + unlink();
|
| + previous = null;
|
| + definition = newDefinition;
|
| + next = definition.firstRef;
|
| + if (next != null) next.previous = this;
|
| + definition.firstRef = this;
|
| + }
|
| +}
|
| +
|
| class EffectiveUseIterator extends Iterator<Reference<Primitive>> {
|
| Reference<Primitive> current;
|
| Reference<Primitive> next;
|
| @@ -350,171 +337,129 @@ abstract class Primitive extends Variable<Primitive> {
|
| }
|
| }
|
|
|
| -/// A primitive that is generally not safe for elimination, but may be marked
|
| -/// as safe by type propagation
|
| -abstract class UnsafePrimitive extends Primitive {
|
| - int effects = Effects.all;
|
| - bool isSafeForElimination = false;
|
| - bool isSafeForReordering = false;
|
| -}
|
| +/// Continuations are normally bound by 'let cont'. A continuation with one
|
| +/// parameter and no body is used to represent a function's return continuation.
|
| +/// The return continuation is bound by the function, not by 'let cont'.
|
| +class Continuation extends Definition<Continuation> implements InteriorNode {
|
| + final List<Parameter> parameters;
|
| + Expression body = null;
|
|
|
| -/// Operands to invocations and primitives are always variables. They point to
|
| -/// their definition and are doubly-linked into a list of occurrences.
|
| -class Reference<T extends Definition<T>> {
|
| - T definition;
|
| - Reference<T> previous;
|
| - Reference<T> next;
|
| + // A continuation is recursive if it has any recursive invocations.
|
| + bool isRecursive;
|
|
|
| - /// A pointer to the parent node. Is null until set by optimization passes.
|
| - Node parent;
|
| + /// True if this is the return continuation. The return continuation is bound
|
| + /// by [FunctionDefinition].
|
| + bool get isReturnContinuation => body == null;
|
|
|
| - Reference(this.definition) {
|
| - next = definition.firstRef;
|
| - if (next != null) next.previous = this;
|
| - definition.firstRef = this;
|
| - }
|
| + /// True if this is a branch continuation. Branch continuations are bound
|
| + /// by [LetCont] and can only have one use.
|
| + bool get isBranchContinuation => firstRef?.parent is Branch;
|
|
|
| - /// Unlinks this reference from the list of occurrences.
|
| - void unlink() {
|
| - if (previous == null) {
|
| - assert(definition.firstRef == this);
|
| - definition.firstRef = next;
|
| - } else {
|
| - previous.next = next;
|
| - }
|
| - if (next != null) next.previous = previous;
|
| - }
|
| + /// True if this is the exception handler bound by a [LetHandler].
|
| + bool get isHandlerContinuation => parent is LetHandler;
|
|
|
| - /// Changes the definition referenced by this object and updates
|
| - /// the reference chains accordingly.
|
| - void changeTo(Definition<T> newDefinition) {
|
| - unlink();
|
| - previous = null;
|
| - definition = newDefinition;
|
| - next = definition.firstRef;
|
| - if (next != null) next.previous = this;
|
| - definition.firstRef = this;
|
| + /// True if this is a non-return continuation that can be targeted by
|
| + /// [InvokeContinuation].
|
| + bool get isJoinContinuation {
|
| + return body != null &&
|
| + parent is! LetHandler &&
|
| + (firstRef == null || firstRef.parent is InvokeContinuation);
|
| }
|
| -}
|
| -
|
| -/// Evaluates a primitive and binds it to variable: `let val x = V in E`.
|
| -///
|
| -/// The bound value is in scope in the body.
|
| -///
|
| -/// During one-pass construction a LetPrim with an empty body is used to
|
| -/// represent the one-hole context `let val x = V in []`.
|
| -class LetPrim extends InteriorExpression {
|
| - Primitive primitive;
|
| - Expression body;
|
|
|
| - LetPrim(this.primitive, [this.body = null]);
|
| + Continuation(this.parameters, {this.isRecursive: false});
|
|
|
| - Expression plug(Expression expr) {
|
| - assert(body == null);
|
| - return body = expr;
|
| - }
|
| + Continuation.retrn()
|
| + : parameters = <Parameter>[new Parameter(null)],
|
| + isRecursive = false;
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitLetPrim(this);
|
| + accept(BlockVisitor visitor) => visitor.visitContinuation(this);
|
|
|
| void setParentPointers() {
|
| - primitive.parent = this;
|
| + _setParentsOnNodes(parameters, this);
|
| if (body != null) body.parent = this;
|
| }
|
| }
|
|
|
| -/// Binding continuations.
|
| -///
|
| -/// let cont k0(v0 ...) = E0
|
| -/// k1(v1 ...) = E1
|
| -/// ...
|
| -/// in E
|
| -///
|
| -/// The bound continuations are in scope in the body and the continuation
|
| -/// parameters are in scope in the respective continuation bodies.
|
| -///
|
| -/// During one-pass construction a LetCont whose first continuation has an empty
|
| -/// body is used to represent the one-hole context
|
| -/// `let cont ... k(v) = [] ... in E`.
|
| -class LetCont extends InteriorExpression {
|
| - List<Continuation> continuations;
|
| - Expression body;
|
| -
|
| - LetCont(Continuation continuation, this.body)
|
| - : continuations = <Continuation>[continuation];
|
| +/// Common interface for [Primitive] and [MutableVariable].
|
| +abstract class Variable<T extends Variable<T>> extends Definition<T> {
|
| + /// Type of value held in the variable.
|
| + ///
|
| + /// Is `null` until initialized by type propagation.
|
| + TypeMask type;
|
|
|
| - LetCont.two(Continuation first, Continuation second, this.body)
|
| - : continuations = <Continuation>[first, second];
|
| + /// The [VariableElement] or [ParameterElement] from which the variable
|
| + /// binding originated.
|
| + Entity hint;
|
|
|
| - LetCont.many(this.continuations, this.body);
|
| + Variable(this.hint);
|
|
|
| - Expression plug(Expression expr) {
|
| - assert(continuations != null &&
|
| - continuations.isNotEmpty &&
|
| - continuations.first.body == null);
|
| - return continuations.first.body = expr;
|
| + /// Use the given element as a hint for naming this primitive.
|
| + ///
|
| + /// Has no effect if this primitive already has a non-null [element].
|
| + void useElementAsHint(Entity hint) {
|
| + this.hint ??= hint;
|
| }
|
| +}
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitLetCont(this);
|
| +/// Identifies a mutable variable.
|
| +class MutableVariable extends Variable<MutableVariable> {
|
| + MutableVariable(Entity hint) : super(hint);
|
|
|
| - void setParentPointers() {
|
| - _setParentsOnNodes(continuations, this);
|
| - if (body != null) body.parent = this;
|
| - }
|
| + accept(Visitor v) => v.visitMutableVariable(this);
|
| +
|
| + void setParentPointers() {}
|
| }
|
|
|
| -// Binding an exception handler.
|
| -//
|
| -// let handler h(v0, v1) = E0 in E1
|
| -//
|
| -// The handler is a two-argument (exception, stack trace) continuation which
|
| -// is implicitly the error continuation of all the code in its body E1.
|
| -// [LetHandler] differs from a [LetCont] binding in that it (1) has the
|
| -// runtime semantics of pushing/popping a handler from the dynamic exception
|
| -// handler stack and (2) it does not have any explicit invocations.
|
| -class LetHandler extends InteriorExpression {
|
| - Continuation handler;
|
| +/// A function definition, consisting of parameters and a body.
|
| +///
|
| +/// There is an explicit parameter for the `this` argument, and a return
|
| +/// continuation to invoke when returning from the function.
|
| +class FunctionDefinition extends InteriorNode {
|
| + final ExecutableElement element;
|
| + final Parameter thisParameter;
|
| + final List<Parameter> parameters;
|
| + final Continuation returnContinuation;
|
| Expression body;
|
|
|
| - LetHandler(this.handler, this.body);
|
| + FunctionDefinition(this.element, this.thisParameter, this.parameters,
|
| + this.returnContinuation, this.body);
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitLetHandler(this);
|
| + accept(BlockVisitor visitor) => visitor.visitFunctionDefinition(this);
|
|
|
| void setParentPointers() {
|
| - handler.parent = this;
|
| + if (thisParameter != null) thisParameter.parent = this;
|
| + _setParentsOnNodes(parameters, this);
|
| + returnContinuation.parent = this;
|
| if (body != null) body.parent = this;
|
| }
|
| }
|
|
|
| -/// Binding mutable variables.
|
| -///
|
| -/// let mutable v = P in E
|
| -///
|
| -/// [MutableVariable]s can be seen as ref cells that are not first-class
|
| -/// values. They are therefore not [Primitive]s and not bound by [LetPrim]
|
| -/// to prevent unrestricted use of references to them. During one-pass
|
| -/// construction, a [LetMutable] with an empty body is use to represent the
|
| -/// one-hole context 'let mutable v = P in []'.
|
| -class LetMutable extends InteriorExpression {
|
| - final MutableVariable variable;
|
| - final Reference<Primitive> valueRef;
|
| - Expression body;
|
| +// ----------------------------------------------------------------------------
|
| +// PRIMITIVES
|
| +// ----------------------------------------------------------------------------
|
|
|
| - Primitive get value => valueRef.definition;
|
| +class Parameter extends Primitive {
|
| + Parameter(Entity hint) {
|
| + super.hint = hint;
|
| + }
|
|
|
| - LetMutable(this.variable, Primitive value)
|
| - : this.valueRef = new Reference<Primitive>(value);
|
| + accept(Visitor visitor) => visitor.visitParameter(this);
|
|
|
| - Expression plug(Expression expr) {
|
| - return body = expr;
|
| - }
|
| + String toString() => 'Parameter(${hint == null ? null : hint.name})';
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitLetMutable(this);
|
| + bool get hasValue => true;
|
| + bool get isSafeForElimination => true;
|
| + bool get isSafeForReordering => true;
|
|
|
| - void setParentPointers() {
|
| - variable.parent = this;
|
| - valueRef.parent = this;
|
| - if (body != null) body.parent = this;
|
| - }
|
| + void setParentPointers() {}
|
| +}
|
| +
|
| +/// A primitive that is generally not safe for elimination, but may be marked
|
| +/// as safe by type propagation
|
| +abstract class UnsafePrimitive extends Primitive {
|
| + int effects = Effects.all;
|
| + bool isSafeForElimination = false;
|
| + bool isSafeForReordering = false;
|
| }
|
|
|
| enum CallingConvention {
|
| @@ -594,16 +539,12 @@ class InvokeStatic extends InvocationPrimitive {
|
| final List<Reference<Primitive>> argumentRefs;
|
| final SourceInformation sourceInformation;
|
|
|
| - InvokeStatic(this.target,
|
| - this.selector,
|
| - List<Primitive> args,
|
| - [this.sourceInformation])
|
| + InvokeStatic(this.target, this.selector, List<Primitive> args,
|
| + [this.sourceInformation])
|
| : argumentRefs = _referenceList(args);
|
|
|
| - InvokeStatic.byReference(this.target,
|
| - this.selector,
|
| - this.argumentRefs,
|
| - [this.sourceInformation]);
|
| + InvokeStatic.byReference(this.target, this.selector, this.argumentRefs,
|
| + [this.sourceInformation]);
|
|
|
| accept(Visitor visitor) => visitor.visitInvokeStatic(this);
|
|
|
| @@ -638,12 +579,10 @@ class InvokeMethod extends InvocationPrimitive {
|
| : receiverRef;
|
| }
|
|
|
| - InvokeMethod(Primitive receiver,
|
| - this.selector,
|
| - this.mask,
|
| - List<Primitive> arguments,
|
| - {this.sourceInformation,
|
| - this.callingConvention: CallingConvention.Normal})
|
| + InvokeMethod(
|
| + Primitive receiver, this.selector, this.mask, List<Primitive> arguments,
|
| + {this.sourceInformation,
|
| + this.callingConvention: CallingConvention.Normal})
|
| : this.receiverRef = new Reference<Primitive>(receiver),
|
| this.argumentRefs = _referenceList(arguments);
|
|
|
| @@ -691,12 +630,9 @@ class InvokeMethodDirectly extends InvocationPrimitive {
|
| : receiverRef;
|
| }
|
|
|
| - InvokeMethodDirectly(Primitive receiver,
|
| - this.target,
|
| - this.selector,
|
| - List<Primitive> arguments,
|
| - this.sourceInformation,
|
| - {this.callingConvention: CallingConvention.Normal})
|
| + InvokeMethodDirectly(Primitive receiver, this.target, this.selector,
|
| + List<Primitive> arguments, this.sourceInformation,
|
| + {this.callingConvention: CallingConvention.Normal})
|
| : this.receiverRef = new Reference<Primitive>(receiver),
|
| this.argumentRefs = _referenceList(arguments);
|
|
|
| @@ -742,12 +678,9 @@ class InvokeConstructor extends InvocationPrimitive {
|
| /// can therefore not be inferred solely based on the call target.
|
| TypeMask allocationSiteType;
|
|
|
| - InvokeConstructor(this.dartType,
|
| - this.target,
|
| - this.selector,
|
| - List<Primitive> args,
|
| - this.sourceInformation,
|
| - {this.allocationSiteType})
|
| + InvokeConstructor(this.dartType, this.target, this.selector,
|
| + List<Primitive> args, this.sourceInformation,
|
| + {this.allocationSiteType})
|
| : argumentRefs = _referenceList(args);
|
|
|
| accept(Visitor visitor) => visitor.visitInvokeConstructor(this);
|
| @@ -771,7 +704,7 @@ class Refinement extends Primitive {
|
| final TypeMask refineType;
|
|
|
| Refinement(Primitive value, this.refineType)
|
| - : value = new Reference<Primitive>(value);
|
| + : value = new Reference<Primitive>(value);
|
|
|
| bool get hasValue => true;
|
| bool get isSafeForElimination => false;
|
| @@ -884,12 +817,13 @@ class BoundsCheck extends Primitive {
|
|
|
| String get checkString {
|
| if (hasNoChecks) return 'no-check';
|
| - return [hasUpperBoundCheck ? 'upper' : null,
|
| - hasLowerBoundCheck ? 'lower' : null,
|
| - hasEmptinessCheck ? 'emptiness' : null,
|
| - hasIntegerCheck ? 'integer' : null,
|
| - 'check']
|
| - .where((x) => x != null).join('-');
|
| + return [
|
| + hasUpperBoundCheck ? 'upper' : null,
|
| + hasLowerBoundCheck ? 'lower' : null,
|
| + hasEmptinessCheck ? 'emptiness' : null,
|
| + hasIntegerCheck ? 'integer' : null,
|
| + 'check'
|
| + ].where((x) => x != null).join('-');
|
| }
|
|
|
| bool get isSafeForElimination => checks == NONE;
|
| @@ -945,31 +879,26 @@ class ReceiverCheck extends Primitive {
|
| /// True if null is the only possible input that cannot respond to [selector].
|
| bool get isNullCheck => _flags & _NULL_CHECK != 0;
|
|
|
| -
|
| /// Constructor for creating checks in arbitrary configurations.
|
| ///
|
| /// Consider using one of the named constructors instead.
|
| ///
|
| /// [useSelector] and [isNullCheck] are mandatory named arguments.
|
| ReceiverCheck(Primitive value, this.selector, this.sourceInformation,
|
| - {Primitive condition, bool useSelector, bool isNullCheck})
|
| + {Primitive condition, bool useSelector, bool isNullCheck})
|
| : valueRef = new Reference<Primitive>(value),
|
| conditionRef = _optionalReference(condition),
|
| - _flags = (useSelector ? _USE_SELECTOR : 0) |
|
| - (isNullCheck ? _NULL_CHECK : 0);
|
| + _flags =
|
| + (useSelector ? _USE_SELECTOR : 0) | (isNullCheck ? _NULL_CHECK : 0);
|
|
|
| /// Simplified constructor for building null checks.
|
| ///
|
| /// Null must be the only possible input value that does not respond to
|
| /// [selector].
|
| ReceiverCheck.nullCheck(
|
| - Primitive value,
|
| - Selector selector,
|
| - SourceInformation sourceInformation,
|
| - {Primitive condition})
|
| - : this(value,
|
| - selector,
|
| - sourceInformation,
|
| + Primitive value, Selector selector, SourceInformation sourceInformation,
|
| + {Primitive condition})
|
| + : this(value, selector, sourceInformation,
|
| condition: condition,
|
| useSelector: condition != null,
|
| isNullCheck: true);
|
| @@ -978,17 +907,10 @@ class ReceiverCheck extends Primitive {
|
| ///
|
| /// if (condition) value.selectorName();
|
| ///
|
| - ReceiverCheck.generalCheck(
|
| - Primitive value,
|
| - Selector selector,
|
| - SourceInformation sourceInformation,
|
| - Primitive condition)
|
| - : this(value,
|
| - selector,
|
| - sourceInformation,
|
| - condition: condition,
|
| - useSelector: true,
|
| - isNullCheck: false);
|
| + ReceiverCheck.generalCheck(Primitive value, Selector selector,
|
| + SourceInformation sourceInformation, Primitive condition)
|
| + : this(value, selector, sourceInformation,
|
| + condition: condition, useSelector: true, isNullCheck: false);
|
|
|
| bool get isSafeForElimination => false;
|
| bool get isSafeForReordering => false;
|
| @@ -1038,9 +960,7 @@ class TypeTest extends Primitive {
|
| Primitive typeArgument(int n) => typeArgumentRefs[n].definition;
|
| Iterable<Primitive> get typeArguments => _dereferenceList(typeArgumentRefs);
|
|
|
| - TypeTest(Primitive value,
|
| - this.dartType,
|
| - List<Primitive> typeArguments)
|
| + TypeTest(Primitive value, this.dartType, List<Primitive> typeArguments)
|
| : this.valueRef = new Reference<Primitive>(value),
|
| this.typeArgumentRefs = _referenceList(typeArguments);
|
|
|
| @@ -1100,9 +1020,7 @@ class TypeCast extends UnsafePrimitive {
|
| Primitive typeArgument(int n) => typeArgumentRefs[n].definition;
|
| Iterable<Primitive> get typeArguments => _dereferenceList(typeArgumentRefs);
|
|
|
| - TypeCast(Primitive value,
|
| - this.dartType,
|
| - List<Primitive> typeArguments)
|
| + TypeCast(Primitive value, this.dartType, List<Primitive> typeArguments)
|
| : this.valueRef = new Reference<Primitive>(value),
|
| this.typeArgumentRefs = _referenceList(typeArguments);
|
|
|
| @@ -1127,9 +1045,8 @@ class ApplyBuiltinOperator extends Primitive {
|
| Primitive argument(int n) => argumentRefs[n].definition;
|
| Iterable<Primitive> get arguments => _dereferenceList(argumentRefs);
|
|
|
| - ApplyBuiltinOperator(this.operator,
|
| - List<Primitive> arguments,
|
| - this.sourceInformation)
|
| + ApplyBuiltinOperator(
|
| + this.operator, List<Primitive> arguments, this.sourceInformation)
|
| : this.argumentRefs = _referenceList(arguments);
|
|
|
| accept(Visitor visitor) => visitor.visitApplyBuiltinOperator(this);
|
| @@ -1156,10 +1073,8 @@ class ApplyBuiltinMethod extends Primitive {
|
| Primitive argument(int n) => argumentRefs[n].definition;
|
| Iterable<Primitive> get arguments => _dereferenceList(argumentRefs);
|
|
|
| - ApplyBuiltinMethod(this.method,
|
| - Primitive receiver,
|
| - List<Primitive> arguments,
|
| - this.sourceInformation)
|
| + ApplyBuiltinMethod(this.method, Primitive receiver, List<Primitive> arguments,
|
| + this.sourceInformation)
|
| : this.receiverRef = new Reference<Primitive>(receiver),
|
| this.argumentRefs = _referenceList(arguments);
|
|
|
| @@ -1177,43 +1092,6 @@ class ApplyBuiltinMethod extends Primitive {
|
| int get effects => getEffectsOfBuiltinMethod(method);
|
| }
|
|
|
| -/// Throw a value.
|
| -///
|
| -/// Throw is an expression, i.e., it always occurs in tail position with
|
| -/// respect to a body or expression.
|
| -class Throw extends TailExpression {
|
| - Reference<Primitive> valueRef;
|
| -
|
| - Primitive get value => valueRef.definition;
|
| -
|
| - Throw(Primitive value) : valueRef = new Reference<Primitive>(value);
|
| -
|
| - accept(BlockVisitor visitor) => visitor.visitThrow(this);
|
| -
|
| - void setParentPointers() {
|
| - valueRef.parent = this;
|
| - }
|
| -}
|
| -
|
| -/// Rethrow
|
| -///
|
| -/// Rethrow can only occur inside a continuation bound by [LetHandler]. It
|
| -/// implicitly throws the exception parameter of the enclosing handler with
|
| -/// the same stack trace as the enclosing handler.
|
| -class Rethrow extends TailExpression {
|
| - accept(BlockVisitor visitor) => visitor.visitRethrow(this);
|
| - void setParentPointers() {}
|
| -}
|
| -
|
| -/// An expression that is known to be unreachable.
|
| -///
|
| -/// This can be placed as the body of a call continuation, when the caller is
|
| -/// known never to invoke it, e.g. because the calling expression always throws.
|
| -class Unreachable extends TailExpression {
|
| - accept(BlockVisitor visitor) => visitor.visitUnreachable(this);
|
| - void setParentPointers() {}
|
| -}
|
| -
|
| /// Gets the value from a [MutableVariable].
|
| ///
|
| /// [MutableVariable]s can be seen as ref cells that are not first-class
|
| @@ -1269,100 +1147,39 @@ class SetMutable extends Primitive {
|
| }
|
| }
|
|
|
| -/// Invoke a continuation in tail position.
|
| -class InvokeContinuation extends TailExpression {
|
| - Reference<Continuation> continuationRef;
|
| - List<Reference<Primitive>> argumentRefs;
|
| - SourceInformation sourceInformation;
|
| -
|
| - Continuation get continuation => continuationRef.definition;
|
| - Primitive argument(int n) => argumentRefs[n].definition;
|
| - Iterable<Primitive> get arguments => _dereferenceList(argumentRefs);
|
| -
|
| - // An invocation of a continuation is recursive if it occurs in the body of
|
| - // the continuation itself.
|
| - bool isRecursive;
|
| -
|
| - /// True if this invocation escapes from the body of a [LetHandler]
|
| - /// (i.e. a try block). Notably, such an invocation cannot be inlined.
|
| - bool isEscapingTry;
|
| -
|
| - InvokeContinuation(Continuation cont, List<Primitive> args,
|
| - {this.isRecursive: false,
|
| - this.isEscapingTry: false,
|
| - this.sourceInformation})
|
| - : continuationRef = new Reference<Continuation>(cont),
|
| - argumentRefs = _referenceList(args) {
|
| - assert(cont.parameters == null || cont.parameters.length == args.length);
|
| - if (isRecursive) cont.isRecursive = true;
|
| - }
|
| -
|
| - /// A continuation invocation whose target and arguments will be filled
|
| - /// in later.
|
| - ///
|
| - /// Used as a placeholder for a jump whose target is not yet created
|
| - /// (e.g., in the translation of break and continue).
|
| - InvokeContinuation.uninitialized({this.isRecursive: false,
|
| - this.isEscapingTry: false})
|
| - : continuationRef = null,
|
| - argumentRefs = null,
|
| - sourceInformation = null;
|
| -
|
| - accept(BlockVisitor visitor) => visitor.visitInvokeContinuation(this);
|
| -
|
| - void setParentPointers() {
|
| - if (continuationRef != null) continuationRef.parent = this;
|
| - if (argumentRefs != null) _setParentsOnList(argumentRefs, this);
|
| - }
|
| -}
|
| -
|
| -/// Choose between a pair of continuations based on a condition value.
|
| +/// Directly reads from a field on a given object.
|
| ///
|
| -/// The two continuations must not declare any parameters.
|
| -class Branch extends TailExpression {
|
| - final Reference<Primitive> conditionRef;
|
| - final Reference<Continuation> trueContinuationRef;
|
| - final Reference<Continuation> falseContinuationRef;
|
| +/// The [object] must either be `null` or an object that has [field].
|
| +class GetField extends Primitive {
|
| + final Reference<Primitive> objectRef;
|
| + FieldElement field;
|
|
|
| - Primitive get condition => conditionRef.definition;
|
| - Continuation get trueContinuation => trueContinuationRef.definition;
|
| - Continuation get falseContinuation => falseContinuationRef.definition;
|
| + /// True if the field never changes value.
|
| + final bool isFinal;
|
|
|
| - /// If true, only the value `true` satisfies the condition. Otherwise, any
|
| - /// truthy value satisfies the check.
|
| - ///
|
| - /// Non-strict checks are preferable when the condition is known to be a
|
| - /// boolean.
|
| - bool isStrictCheck;
|
| + /// True if the object is known not to be null.
|
| + // TODO(asgerf): This is a placeholder until we agree on how to track
|
| + // side effects.
|
| + bool objectIsNotNull = false;
|
|
|
| - Branch(Primitive condition,
|
| - Continuation trueCont,
|
| - Continuation falseCont,
|
| - {bool strict})
|
| - : this.conditionRef = new Reference<Primitive>(condition),
|
| - trueContinuationRef = new Reference<Continuation>(trueCont),
|
| - falseContinuationRef = new Reference<Continuation>(falseCont),
|
| - isStrictCheck = strict {
|
| - assert(strict != null);
|
| - }
|
| + Primitive get object => objectRef.definition;
|
|
|
| - Branch.strict(Primitive condition,
|
| - Continuation trueCont,
|
| - Continuation falseCont)
|
| - : this(condition, trueCont, falseCont, strict: true);
|
| + GetField(Primitive object, this.field, {this.isFinal: false})
|
| + : this.objectRef = new Reference<Primitive>(object);
|
|
|
| - Branch.loose(Primitive condition,
|
| - Continuation trueCont,
|
| - Continuation falseCont)
|
| - : this(condition, trueCont, falseCont, strict: false);
|
| + accept(Visitor visitor) => visitor.visitGetField(this);
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitBranch(this);
|
| + bool get hasValue => true;
|
| + bool get isSafeForElimination => objectIsNotNull;
|
| + bool get isSafeForReordering => false;
|
| +
|
| + toString() => 'GetField($field)';
|
|
|
| void setParentPointers() {
|
| - conditionRef.parent = this;
|
| - trueContinuationRef.parent = this;
|
| - falseContinuationRef.parent = this;
|
| + objectRef.parent = this;
|
| }
|
| +
|
| + int get effects => isFinal ? 0 : Effects.dependsOnInstanceField;
|
| }
|
|
|
| /// Directly assigns to a field on a given object.
|
| @@ -1392,41 +1209,6 @@ class SetField extends Primitive {
|
| int get effects => Effects.changesInstanceField;
|
| }
|
|
|
| -/// Directly reads from a field on a given object.
|
| -///
|
| -/// The [object] must either be `null` or an object that has [field].
|
| -class GetField extends Primitive {
|
| - final Reference<Primitive> objectRef;
|
| - FieldElement field;
|
| -
|
| - /// True if the field never changes value.
|
| - final bool isFinal;
|
| -
|
| - /// True if the object is known not to be null.
|
| - // TODO(asgerf): This is a placeholder until we agree on how to track
|
| - // side effects.
|
| - bool objectIsNotNull = false;
|
| -
|
| - Primitive get object => objectRef.definition;
|
| -
|
| - GetField(Primitive object, this.field, {this.isFinal: false})
|
| - : this.objectRef = new Reference<Primitive>(object);
|
| -
|
| - accept(Visitor visitor) => visitor.visitGetField(this);
|
| -
|
| - bool get hasValue => true;
|
| - bool get isSafeForElimination => objectIsNotNull;
|
| - bool get isSafeForReordering => false;
|
| -
|
| - toString() => 'GetField($field)';
|
| -
|
| - void setParentPointers() {
|
| - objectRef.parent = this;
|
| - }
|
| -
|
| - int get effects => isFinal ? 0 : Effects.dependsOnInstanceField;
|
| -}
|
| -
|
| /// Get the length of a string or native list.
|
| class GetLength extends Primitive {
|
| final Reference<Primitive> objectRef;
|
| @@ -1649,8 +1431,7 @@ class CreateInstance extends Primitive {
|
| Primitive get typeInformation => typeInformationRef?.definition;
|
|
|
| CreateInstance(this.classElement, List<Primitive> arguments,
|
| - Primitive typeInformation,
|
| - this.sourceInformation)
|
| + Primitive typeInformation, this.sourceInformation)
|
| : this.argumentRefs = _referenceList(arguments),
|
| this.typeInformationRef = _optionalReference(typeInformation);
|
|
|
| @@ -1741,7 +1522,8 @@ class ForeignCode extends UnsafePrimitive {
|
| Iterable<Primitive> get arguments => _dereferenceList(argumentRefs);
|
|
|
| ForeignCode(this.codeTemplate, this.storedType, List<Primitive> arguments,
|
| - this.nativeBehavior, {this.dependency})
|
| + this.nativeBehavior,
|
| + {this.dependency})
|
| : this.argumentRefs = _referenceList(arguments) {
|
| effects = Effects.from(nativeBehavior.sideEffects);
|
| }
|
| @@ -1807,317 +1589,505 @@ class LiteralList extends Primitive {
|
| }
|
| }
|
|
|
| -class Parameter extends Primitive {
|
| - Parameter(Entity hint) {
|
| - super.hint = hint;
|
| - }
|
| +/// Converts the internal representation of a type to a Dart object of type
|
| +/// [Type].
|
| +class ReifyRuntimeType extends Primitive {
|
| + /// Reference to the internal representation of a type (as produced, for
|
| + /// example, by [ReadTypeVariable]).
|
| + final Reference<Primitive> valueRef;
|
|
|
| - accept(Visitor visitor) => visitor.visitParameter(this);
|
| + final SourceInformation sourceInformation;
|
|
|
| - String toString() => 'Parameter(${hint == null ? null : hint.name})';
|
| + Primitive get value => valueRef.definition;
|
| +
|
| + ReifyRuntimeType(Primitive value, this.sourceInformation)
|
| + : this.valueRef = new Reference<Primitive>(value);
|
| +
|
| + @override
|
| + accept(Visitor visitor) => visitor.visitReifyRuntimeType(this);
|
|
|
| bool get hasValue => true;
|
| bool get isSafeForElimination => true;
|
| bool get isSafeForReordering => true;
|
|
|
| - void setParentPointers() {}
|
| + void setParentPointers() {
|
| + valueRef.parent = this;
|
| + }
|
| }
|
|
|
| -/// Continuations are normally bound by 'let cont'. A continuation with one
|
| -/// parameter and no body is used to represent a function's return continuation.
|
| -/// The return continuation is bound by the function, not by 'let cont'.
|
| -class Continuation extends Definition<Continuation> implements InteriorNode {
|
| - final List<Parameter> parameters;
|
| - Expression body = null;
|
| +/// Read the value the type variable [variable] from the target object.
|
| +///
|
| +/// The resulting value is an internal representation (and not neccessarily a
|
| +/// Dart object), and must be reified by [ReifyRuntimeType], if it should be
|
| +/// used as a Dart value.
|
| +class ReadTypeVariable extends Primitive {
|
| + final TypeVariableType variable;
|
| + final Reference<Primitive> targetRef;
|
| + final SourceInformation sourceInformation;
|
|
|
| - // A continuation is recursive if it has any recursive invocations.
|
| - bool isRecursive;
|
| + Primitive get target => targetRef.definition;
|
|
|
| - /// True if this is the return continuation. The return continuation is bound
|
| - /// by [FunctionDefinition].
|
| - bool get isReturnContinuation => body == null;
|
| + ReadTypeVariable(this.variable, Primitive target, this.sourceInformation)
|
| + : this.targetRef = new Reference<Primitive>(target);
|
|
|
| - /// True if this is a branch continuation. Branch continuations are bound
|
| - /// by [LetCont] and can only have one use.
|
| - bool get isBranchContinuation => firstRef?.parent is Branch;
|
| + @override
|
| + accept(Visitor visitor) => visitor.visitReadTypeVariable(this);
|
|
|
| - /// True if this is the exception handler bound by a [LetHandler].
|
| - bool get isHandlerContinuation => parent is LetHandler;
|
| + bool get hasValue => true;
|
| + bool get isSafeForElimination => true;
|
| + bool get isSafeForReordering => true;
|
|
|
| - /// True if this is a non-return continuation that can be targeted by
|
| - /// [InvokeContinuation].
|
| - bool get isJoinContinuation {
|
| - return body != null &&
|
| - parent is! LetHandler &&
|
| - (firstRef == null || firstRef.parent is InvokeContinuation);
|
| + void setParentPointers() {
|
| + targetRef.parent = this;
|
| }
|
| +}
|
|
|
| - Continuation(this.parameters, {this.isRecursive: false});
|
| +enum TypeExpressionKind { COMPLETE, INSTANCE }
|
|
|
| - Continuation.retrn()
|
| - : parameters = <Parameter>[new Parameter(null)],
|
| - isRecursive = false;
|
| +/// Constructs a representation of a closed or ground-term type (that is, a type
|
| +/// without type variables).
|
| +///
|
| +/// There are two forms:
|
| +///
|
| +/// - COMPLETE: A complete form that is self contained, used for the values of
|
| +/// type parameters and non-raw is-checks.
|
| +///
|
| +/// - INSTANCE: A headless flat form for representing the sequence of values of
|
| +/// the type parameters of an instance of a generic type.
|
| +///
|
| +/// The COMPLETE form value is constructed from [dartType] by replacing the type
|
| +/// variables with consecutive values from [arguments], in the order generated
|
| +/// by [DartType.forEachTypeVariable]. The type variables in [dartType] are
|
| +/// treated as 'holes' in the term, which means that it must be ensured at
|
| +/// construction, that duplicate occurences of a type variable in [dartType]
|
| +/// are assigned the same value.
|
| +///
|
| +/// The INSTANCE form is constructed as a list of [arguments]. This is the same
|
| +/// as the COMPLETE form for the 'thisType', except the root term's type is
|
| +/// missing; this is implicit as the raw type of instance. The [dartType] of
|
| +/// the INSTANCE form must be the thisType of some class.
|
| +///
|
| +/// While we would like to remove the constrains on the INSTANCE form, we can
|
| +/// get by with a tree of TypeExpressions. Consider:
|
| +///
|
| +/// class Foo<T> {
|
| +/// ... new Set<List<T>>()
|
| +/// }
|
| +/// class Set<E1> {
|
| +/// factory Set() => new _LinkedHashSet<E1>();
|
| +/// }
|
| +/// class List<E2> { ... }
|
| +/// class _LinkedHashSet<E3> { ... }
|
| +///
|
| +/// After inlining the factory constructor for `Set<E1>`, the CreateInstance
|
| +/// should have type `_LinkedHashSet<List<T>>` and the TypeExpression should be
|
| +/// a tree:
|
| +///
|
| +/// CreateInstance(dartType: _LinkedHashSet<List<T>>,
|
| +/// [], // No arguments
|
| +/// TypeExpression(INSTANCE,
|
| +/// dartType: _LinkedHashSet<E3>, // _LinkedHashSet's thisType
|
| +/// TypeExpression(COMPLETE, // E3 = List<T>
|
| +/// dartType: List<E2>,
|
| +/// ReadTypeVariable(this, T)))) // E2 = T
|
| +//
|
| +// TODO(sra): The INSTANCE form requires the actual instance for full
|
| +// interpretation. I want to move to a representation where the INSTANCE form is
|
| +// also a complete form (possibly the same).
|
| +class TypeExpression extends Primitive {
|
| + final TypeExpressionKind kind;
|
| + final DartType dartType;
|
| + final List<Reference<Primitive>> argumentRefs;
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitContinuation(this);
|
| + Primitive argument(int n) => argumentRefs[n].definition;
|
| + Iterable<Primitive> get arguments => _dereferenceList(argumentRefs);
|
| +
|
| + TypeExpression(this.kind, this.dartType, List<Primitive> arguments)
|
| + : this.argumentRefs = _referenceList(arguments) {
|
| + assert(kind == TypeExpressionKind.INSTANCE
|
| + ? dartType == (dartType.element as ClassElement).thisType
|
| + : true);
|
| + }
|
| +
|
| + @override
|
| + accept(Visitor visitor) {
|
| + return visitor.visitTypeExpression(this);
|
| + }
|
| +
|
| + bool get hasValue => true;
|
| + bool get isSafeForElimination => true;
|
| + bool get isSafeForReordering => true;
|
|
|
| void setParentPointers() {
|
| - _setParentsOnNodes(parameters, this);
|
| - if (body != null) body.parent = this;
|
| + _setParentsOnList(argumentRefs, this);
|
| + }
|
| +
|
| + String get kindAsString {
|
| + switch (kind) {
|
| + case TypeExpressionKind.COMPLETE:
|
| + return 'COMPLETE';
|
| + case TypeExpressionKind.INSTANCE:
|
| + return 'INSTANCE';
|
| + }
|
| }
|
| }
|
|
|
| -/// Common interface for [Primitive] and [MutableVariable].
|
| -abstract class Variable<T extends Variable<T>> extends Definition<T> {
|
| - /// Type of value held in the variable.
|
| - ///
|
| - /// Is `null` until initialized by type propagation.
|
| - TypeMask type;
|
| +class Await extends UnsafePrimitive {
|
| + final Reference<Primitive> inputRef;
|
|
|
| - /// The [VariableElement] or [ParameterElement] from which the variable
|
| - /// binding originated.
|
| - Entity hint;
|
| + Primitive get input => inputRef.definition;
|
|
|
| - Variable(this.hint);
|
| + Await(Primitive input) : this.inputRef = new Reference<Primitive>(input);
|
|
|
| - /// Use the given element as a hint for naming this primitive.
|
| + @override
|
| + accept(Visitor visitor) {
|
| + return visitor.visitAwait(this);
|
| + }
|
| +
|
| + bool get hasValue => true;
|
| +
|
| + void setParentPointers() {
|
| + inputRef.parent = this;
|
| + }
|
| +}
|
| +
|
| +class Yield extends UnsafePrimitive {
|
| + final Reference<Primitive> inputRef;
|
| + final bool hasStar;
|
| +
|
| + Primitive get input => inputRef.definition;
|
| +
|
| + Yield(Primitive input, this.hasStar)
|
| + : this.inputRef = new Reference<Primitive>(input);
|
| +
|
| + @override
|
| + accept(Visitor visitor) {
|
| + return visitor.visitYield(this);
|
| + }
|
| +
|
| + bool get hasValue => true;
|
| +
|
| + void setParentPointers() {
|
| + inputRef.parent = this;
|
| + }
|
| +}
|
| +
|
| +// ---------------------------------------------------------------------------
|
| +// EXPRESSIONS
|
| +// ---------------------------------------------------------------------------
|
| +
|
| +/// An expression that creates new bindings and continues evaluation in
|
| +/// a subexpression.
|
| +///
|
| +/// The interior expressions are [LetPrim], [LetCont], [LetHandler], and
|
| +/// [LetMutable].
|
| +abstract class InteriorExpression extends Expression implements InteriorNode {
|
| + Expression get next => body;
|
| +
|
| + /// Removes this expression from its current position in the IR.
|
| ///
|
| - /// Has no effect if this primitive already has a non-null [element].
|
| - void useElementAsHint(Entity hint) {
|
| - this.hint ??= hint;
|
| + /// The node can be re-inserted elsewhere or remain orphaned.
|
| + ///
|
| + /// If orphaned, the caller is responsible for unlinking all references in
|
| + /// the orphaned node. Use [Reference.unlink] or [Primitive.destroy] for this.
|
| + void remove() {
|
| + assert(parent != null);
|
| + assert(parent.body == this);
|
| + assert(body.parent == this);
|
| + parent.body = body;
|
| + body.parent = parent;
|
| + parent = null;
|
| + body = null;
|
| + }
|
| +
|
| + /// Inserts this above [node].
|
| + ///
|
| + /// This node must be orphaned first.
|
| + void insertAbove(Expression node) {
|
| + insertBelow(node.parent);
|
| + }
|
| +
|
| + /// Inserts this below [node].
|
| + ///
|
| + /// This node must be orphaned first.
|
| + void insertBelow(InteriorNode newParent) {
|
| + assert(parent == null);
|
| + assert(body == null);
|
| + Expression child = newParent.body;
|
| + newParent.body = this;
|
| + this.body = child;
|
| + child.parent = this;
|
| + this.parent = newParent;
|
| }
|
| }
|
|
|
| -/// Identifies a mutable variable.
|
| -class MutableVariable extends Variable<MutableVariable> {
|
| - MutableVariable(Entity hint) : super(hint);
|
| +/// An expression without a continuation or a subexpression body.
|
| +///
|
| +/// These break straight-line control flow and can be thought of as ending a
|
| +/// basic block.
|
| +abstract class TailExpression extends Expression {
|
| + Expression get next => null;
|
| +}
|
|
|
| - accept(Visitor v) => v.visitMutableVariable(this);
|
| +/// Evaluates a primitive and binds it to variable: `let val x = V in E`.
|
| +///
|
| +/// The bound value is in scope in the body.
|
| +///
|
| +/// During one-pass construction a LetPrim with an empty body is used to
|
| +/// represent the one-hole context `let val x = V in []`.
|
| +class LetPrim extends InteriorExpression {
|
| + Primitive primitive;
|
| + Expression body;
|
|
|
| - void setParentPointers() {}
|
| + LetPrim(this.primitive, [this.body = null]);
|
| +
|
| + Expression plug(Expression expr) {
|
| + assert(body == null);
|
| + return body = expr;
|
| + }
|
| +
|
| + accept(BlockVisitor visitor) => visitor.visitLetPrim(this);
|
| +
|
| + void setParentPointers() {
|
| + primitive.parent = this;
|
| + if (body != null) body.parent = this;
|
| + }
|
| }
|
|
|
| -/// A function definition, consisting of parameters and a body.
|
| +/// Binding continuations.
|
| ///
|
| -/// There is an explicit parameter for the `this` argument, and a return
|
| -/// continuation to invoke when returning from the function.
|
| -class FunctionDefinition extends InteriorNode {
|
| - final ExecutableElement element;
|
| - final Parameter thisParameter;
|
| - final List<Parameter> parameters;
|
| - final Continuation returnContinuation;
|
| +/// let cont k0(v0 ...) = E0
|
| +/// k1(v1 ...) = E1
|
| +/// ...
|
| +/// in E
|
| +///
|
| +/// The bound continuations are in scope in the body and the continuation
|
| +/// parameters are in scope in the respective continuation bodies.
|
| +///
|
| +/// During one-pass construction a LetCont whose first continuation has an empty
|
| +/// body is used to represent the one-hole context
|
| +/// `let cont ... k(v) = [] ... in E`.
|
| +class LetCont extends InteriorExpression {
|
| + List<Continuation> continuations;
|
| + Expression body;
|
| +
|
| + LetCont(Continuation continuation, this.body)
|
| + : continuations = <Continuation>[continuation];
|
| +
|
| + LetCont.two(Continuation first, Continuation second, this.body)
|
| + : continuations = <Continuation>[first, second];
|
| +
|
| + LetCont.many(this.continuations, this.body);
|
| +
|
| + Expression plug(Expression expr) {
|
| + assert(continuations != null &&
|
| + continuations.isNotEmpty &&
|
| + continuations.first.body == null);
|
| + return continuations.first.body = expr;
|
| + }
|
| +
|
| + accept(BlockVisitor visitor) => visitor.visitLetCont(this);
|
| +
|
| + void setParentPointers() {
|
| + _setParentsOnNodes(continuations, this);
|
| + if (body != null) body.parent = this;
|
| + }
|
| +}
|
| +
|
| +// Binding an exception handler.
|
| +//
|
| +// let handler h(v0, v1) = E0 in E1
|
| +//
|
| +// The handler is a two-argument (exception, stack trace) continuation which
|
| +// is implicitly the error continuation of all the code in its body E1.
|
| +// [LetHandler] differs from a [LetCont] binding in that it (1) has the
|
| +// runtime semantics of pushing/popping a handler from the dynamic exception
|
| +// handler stack and (2) it does not have any explicit invocations.
|
| +class LetHandler extends InteriorExpression {
|
| + Continuation handler;
|
| Expression body;
|
|
|
| - FunctionDefinition(this.element,
|
| - this.thisParameter,
|
| - this.parameters,
|
| - this.returnContinuation,
|
| - this.body);
|
| + LetHandler(this.handler, this.body);
|
|
|
| - accept(BlockVisitor visitor) => visitor.visitFunctionDefinition(this);
|
| + accept(BlockVisitor visitor) => visitor.visitLetHandler(this);
|
|
|
| void setParentPointers() {
|
| - if (thisParameter != null) thisParameter.parent = this;
|
| - _setParentsOnNodes(parameters, this);
|
| - returnContinuation.parent = this;
|
| + handler.parent = this;
|
| if (body != null) body.parent = this;
|
| }
|
| }
|
|
|
| -/// Converts the internal representation of a type to a Dart object of type
|
| -/// [Type].
|
| -class ReifyRuntimeType extends Primitive {
|
| - /// Reference to the internal representation of a type (as produced, for
|
| - /// example, by [ReadTypeVariable]).
|
| +/// Binding mutable variables.
|
| +///
|
| +/// let mutable v = P in E
|
| +///
|
| +/// [MutableVariable]s can be seen as ref cells that are not first-class
|
| +/// values. They are therefore not [Primitive]s and not bound by [LetPrim]
|
| +/// to prevent unrestricted use of references to them. During one-pass
|
| +/// construction, a [LetMutable] with an empty body is use to represent the
|
| +/// one-hole context 'let mutable v = P in []'.
|
| +class LetMutable extends InteriorExpression {
|
| + final MutableVariable variable;
|
| final Reference<Primitive> valueRef;
|
| -
|
| - final SourceInformation sourceInformation;
|
| + Expression body;
|
|
|
| Primitive get value => valueRef.definition;
|
|
|
| - ReifyRuntimeType(Primitive value, this.sourceInformation)
|
| - : this.valueRef = new Reference<Primitive>(value);
|
| + LetMutable(this.variable, Primitive value)
|
| + : this.valueRef = new Reference<Primitive>(value);
|
|
|
| - @override
|
| - accept(Visitor visitor) => visitor.visitReifyRuntimeType(this);
|
| + Expression plug(Expression expr) {
|
| + return body = expr;
|
| + }
|
|
|
| - bool get hasValue => true;
|
| - bool get isSafeForElimination => true;
|
| - bool get isSafeForReordering => true;
|
| + accept(BlockVisitor visitor) => visitor.visitLetMutable(this);
|
|
|
| void setParentPointers() {
|
| + variable.parent = this;
|
| valueRef.parent = this;
|
| + if (body != null) body.parent = this;
|
| }
|
| }
|
|
|
| -/// Read the value the type variable [variable] from the target object.
|
| +/// Throw a value.
|
| ///
|
| -/// The resulting value is an internal representation (and not neccessarily a
|
| -/// Dart object), and must be reified by [ReifyRuntimeType], if it should be
|
| -/// used as a Dart value.
|
| -class ReadTypeVariable extends Primitive {
|
| - final TypeVariableType variable;
|
| - final Reference<Primitive> targetRef;
|
| - final SourceInformation sourceInformation;
|
| -
|
| - Primitive get target => targetRef.definition;
|
| +/// Throw is an expression, i.e., it always occurs in tail position with
|
| +/// respect to a body or expression.
|
| +class Throw extends TailExpression {
|
| + Reference<Primitive> valueRef;
|
|
|
| - ReadTypeVariable(this.variable, Primitive target, this.sourceInformation)
|
| - : this.targetRef = new Reference<Primitive>(target);
|
| + Primitive get value => valueRef.definition;
|
|
|
| - @override
|
| - accept(Visitor visitor) => visitor.visitReadTypeVariable(this);
|
| + Throw(Primitive value) : valueRef = new Reference<Primitive>(value);
|
|
|
| - bool get hasValue => true;
|
| - bool get isSafeForElimination => true;
|
| - bool get isSafeForReordering => true;
|
| + accept(BlockVisitor visitor) => visitor.visitThrow(this);
|
|
|
| void setParentPointers() {
|
| - targetRef.parent = this;
|
| + valueRef.parent = this;
|
| }
|
| }
|
|
|
| -enum TypeExpressionKind {
|
| - COMPLETE,
|
| - INSTANCE
|
| +/// Rethrow
|
| +///
|
| +/// Rethrow can only occur inside a continuation bound by [LetHandler]. It
|
| +/// implicitly throws the exception parameter of the enclosing handler with
|
| +/// the same stack trace as the enclosing handler.
|
| +class Rethrow extends TailExpression {
|
| + accept(BlockVisitor visitor) => visitor.visitRethrow(this);
|
| + void setParentPointers() {}
|
| }
|
|
|
| -/// Constructs a representation of a closed or ground-term type (that is, a type
|
| -/// without type variables).
|
| -///
|
| -/// There are two forms:
|
| -///
|
| -/// - COMPLETE: A complete form that is self contained, used for the values of
|
| -/// type parameters and non-raw is-checks.
|
| -///
|
| -/// - INSTANCE: A headless flat form for representing the sequence of values of
|
| -/// the type parameters of an instance of a generic type.
|
| -///
|
| -/// The COMPLETE form value is constructed from [dartType] by replacing the type
|
| -/// variables with consecutive values from [arguments], in the order generated
|
| -/// by [DartType.forEachTypeVariable]. The type variables in [dartType] are
|
| -/// treated as 'holes' in the term, which means that it must be ensured at
|
| -/// construction, that duplicate occurences of a type variable in [dartType]
|
| -/// are assigned the same value.
|
| -///
|
| -/// The INSTANCE form is constructed as a list of [arguments]. This is the same
|
| -/// as the COMPLETE form for the 'thisType', except the root term's type is
|
| -/// missing; this is implicit as the raw type of instance. The [dartType] of
|
| -/// the INSTANCE form must be the thisType of some class.
|
| -///
|
| -/// While we would like to remove the constrains on the INSTANCE form, we can
|
| -/// get by with a tree of TypeExpressions. Consider:
|
| -///
|
| -/// class Foo<T> {
|
| -/// ... new Set<List<T>>()
|
| -/// }
|
| -/// class Set<E1> {
|
| -/// factory Set() => new _LinkedHashSet<E1>();
|
| -/// }
|
| -/// class List<E2> { ... }
|
| -/// class _LinkedHashSet<E3> { ... }
|
| -///
|
| -/// After inlining the factory constructor for `Set<E1>`, the CreateInstance
|
| -/// should have type `_LinkedHashSet<List<T>>` and the TypeExpression should be
|
| -/// a tree:
|
| +/// An expression that is known to be unreachable.
|
| ///
|
| -/// CreateInstance(dartType: _LinkedHashSet<List<T>>,
|
| -/// [], // No arguments
|
| -/// TypeExpression(INSTANCE,
|
| -/// dartType: _LinkedHashSet<E3>, // _LinkedHashSet's thisType
|
| -/// TypeExpression(COMPLETE, // E3 = List<T>
|
| -/// dartType: List<E2>,
|
| -/// ReadTypeVariable(this, T)))) // E2 = T
|
| -//
|
| -// TODO(sra): The INSTANCE form requires the actual instance for full
|
| -// interpretation. I want to move to a representation where the INSTANCE form is
|
| -// also a complete form (possibly the same).
|
| -class TypeExpression extends Primitive {
|
| - final TypeExpressionKind kind;
|
| - final DartType dartType;
|
| - final List<Reference<Primitive>> argumentRefs;
|
| +/// This can be placed as the body of a call continuation, when the caller is
|
| +/// known never to invoke it, e.g. because the calling expression always throws.
|
| +class Unreachable extends TailExpression {
|
| + accept(BlockVisitor visitor) => visitor.visitUnreachable(this);
|
| + void setParentPointers() {}
|
| +}
|
| +
|
| +/// Invoke a continuation in tail position.
|
| +class InvokeContinuation extends TailExpression {
|
| + Reference<Continuation> continuationRef;
|
| + List<Reference<Primitive>> argumentRefs;
|
| + SourceInformation sourceInformation;
|
|
|
| + Continuation get continuation => continuationRef.definition;
|
| Primitive argument(int n) => argumentRefs[n].definition;
|
| Iterable<Primitive> get arguments => _dereferenceList(argumentRefs);
|
|
|
| - TypeExpression(this.kind,
|
| - this.dartType,
|
| - List<Primitive> arguments)
|
| - : this.argumentRefs = _referenceList(arguments) {
|
| - assert(kind == TypeExpressionKind.INSTANCE
|
| - ? dartType == (dartType.element as ClassElement).thisType
|
| - : true);
|
| - }
|
| -
|
| - @override
|
| - accept(Visitor visitor) {
|
| - return visitor.visitTypeExpression(this);
|
| - }
|
| -
|
| - bool get hasValue => true;
|
| - bool get isSafeForElimination => true;
|
| - bool get isSafeForReordering => true;
|
| + // An invocation of a continuation is recursive if it occurs in the body of
|
| + // the continuation itself.
|
| + bool isRecursive;
|
|
|
| - void setParentPointers() {
|
| - _setParentsOnList(argumentRefs, this);
|
| - }
|
| + /// True if this invocation escapes from the body of a [LetHandler]
|
| + /// (i.e. a try block). Notably, such an invocation cannot be inlined.
|
| + bool isEscapingTry;
|
|
|
| - String get kindAsString {
|
| - switch (kind) {
|
| - case TypeExpressionKind.COMPLETE: return 'COMPLETE';
|
| - case TypeExpressionKind.INSTANCE: return 'INSTANCE';
|
| - }
|
| + InvokeContinuation(Continuation cont, List<Primitive> args,
|
| + {this.isRecursive: false,
|
| + this.isEscapingTry: false,
|
| + this.sourceInformation})
|
| + : continuationRef = new Reference<Continuation>(cont),
|
| + argumentRefs = _referenceList(args) {
|
| + assert(cont.parameters == null || cont.parameters.length == args.length);
|
| + if (isRecursive) cont.isRecursive = true;
|
| }
|
| -}
|
| -
|
| -class Await extends UnsafePrimitive {
|
| - final Reference<Primitive> inputRef;
|
| -
|
| - Primitive get input => inputRef.definition;
|
|
|
| - Await(Primitive input)
|
| - : this.inputRef = new Reference<Primitive>(input);
|
| -
|
| - @override
|
| - accept(Visitor visitor) {
|
| - return visitor.visitAwait(this);
|
| - }
|
| + /// A continuation invocation whose target and arguments will be filled
|
| + /// in later.
|
| + ///
|
| + /// Used as a placeholder for a jump whose target is not yet created
|
| + /// (e.g., in the translation of break and continue).
|
| + InvokeContinuation.uninitialized(
|
| + {this.isRecursive: false, this.isEscapingTry: false})
|
| + : continuationRef = null,
|
| + argumentRefs = null,
|
| + sourceInformation = null;
|
|
|
| - bool get hasValue => true;
|
| + accept(BlockVisitor visitor) => visitor.visitInvokeContinuation(this);
|
|
|
| void setParentPointers() {
|
| - inputRef.parent = this;
|
| + if (continuationRef != null) continuationRef.parent = this;
|
| + if (argumentRefs != null) _setParentsOnList(argumentRefs, this);
|
| }
|
| }
|
|
|
| -class Yield extends UnsafePrimitive {
|
| - final Reference<Primitive> inputRef;
|
| - final bool hasStar;
|
| +/// Choose between a pair of continuations based on a condition value.
|
| +///
|
| +/// The two continuations must not declare any parameters.
|
| +class Branch extends TailExpression {
|
| + final Reference<Primitive> conditionRef;
|
| + final Reference<Continuation> trueContinuationRef;
|
| + final Reference<Continuation> falseContinuationRef;
|
|
|
| - Primitive get input => inputRef.definition;
|
| + Primitive get condition => conditionRef.definition;
|
| + Continuation get trueContinuation => trueContinuationRef.definition;
|
| + Continuation get falseContinuation => falseContinuationRef.definition;
|
|
|
| - Yield(Primitive input, this.hasStar)
|
| - : this.inputRef = new Reference<Primitive>(input);
|
| + /// If true, only the value `true` satisfies the condition. Otherwise, any
|
| + /// truthy value satisfies the check.
|
| + ///
|
| + /// Non-strict checks are preferable when the condition is known to be a
|
| + /// boolean.
|
| + bool isStrictCheck;
|
|
|
| - @override
|
| - accept(Visitor visitor) {
|
| - return visitor.visitYield(this);
|
| + Branch(Primitive condition, Continuation trueCont, Continuation falseCont,
|
| + {bool strict})
|
| + : this.conditionRef = new Reference<Primitive>(condition),
|
| + trueContinuationRef = new Reference<Continuation>(trueCont),
|
| + falseContinuationRef = new Reference<Continuation>(falseCont),
|
| + isStrictCheck = strict {
|
| + assert(strict != null);
|
| }
|
|
|
| - bool get hasValue => true;
|
| + Branch.strict(
|
| + Primitive condition, Continuation trueCont, Continuation falseCont)
|
| + : this(condition, trueCont, falseCont, strict: true);
|
| +
|
| + Branch.loose(
|
| + Primitive condition, Continuation trueCont, Continuation falseCont)
|
| + : this(condition, trueCont, falseCont, strict: false);
|
| +
|
| + accept(BlockVisitor visitor) => visitor.visitBranch(this);
|
|
|
| void setParentPointers() {
|
| - inputRef.parent = this;
|
| + conditionRef.parent = this;
|
| + trueContinuationRef.parent = this;
|
| + falseContinuationRef.parent = this;
|
| }
|
| }
|
|
|
| +// ----------------------------------------------------------------------------
|
| +// UTILITY STUFF
|
| +// ----------------------------------------------------------------------------
|
| +
|
| Reference<Primitive> _reference(Primitive definition) {
|
| return new Reference<Primitive>(definition);
|
| }
|
|
|
| Reference<Primitive> _optionalReference(Primitive definition) {
|
| - return definition == null
|
| - ? null
|
| - : new Reference<Primitive>(definition);
|
| + return definition == null ? null : new Reference<Primitive>(definition);
|
| }
|
|
|
| List<Reference<Primitive>> _referenceList(Iterable<Primitive> definitions) {
|
| @@ -2140,6 +2110,10 @@ void _setParentsOnList(List<Reference> nodes, Node parent) {
|
| }
|
| }
|
|
|
| +// ----------------------------------------------------------------------------
|
| +// VISITORS
|
| +// ----------------------------------------------------------------------------
|
| +
|
| /// Visitor for block-level traversals that do not need to dispatch on
|
| /// primitives.
|
| abstract class BlockVisitor<T> {
|
| @@ -2432,7 +2406,7 @@ class DeepRecursiveVisitor implements Visitor {
|
| }
|
|
|
| processConstant(Constant node) {}
|
| - visitConstant(Constant node) {
|
| + visitConstant(Constant node) {
|
| processConstant(node);
|
| }
|
|
|
| @@ -2766,16 +2740,15 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
| Definition getCopy(Reference reference) => _copies[reference.definition];
|
|
|
| /// Get the copy of a [Reference]'s definition from the map.
|
| - Definition getCopyOrNull(Reference reference) => reference == null
|
| - ? null
|
| - : getCopy(reference);
|
| + Definition getCopyOrNull(Reference reference) =>
|
| + reference == null ? null : getCopy(reference);
|
|
|
| /// Map a list of [Reference]s to the list of their definition's copies.
|
| List<Definition> getList(List<Reference> list) => list.map(getCopy).toList();
|
|
|
| /// Copy a non-[Continuation] [Definition].
|
| Definition copy(Definition node) {
|
| - assert (node is! Continuation);
|
| + assert(node is! Continuation);
|
| return putCopy(node, visit(node));
|
| }
|
|
|
| @@ -2807,22 +2780,22 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
|
|
| Definition visitInvokeMethodDirectly(InvokeMethodDirectly node) {
|
| return new InvokeMethodDirectly(getCopy(node.receiverRef), node.target,
|
| - node.selector,
|
| - getList(node.argumentRefs),
|
| - node.sourceInformation,
|
| + node.selector, getList(node.argumentRefs), node.sourceInformation,
|
| callingConvention: node.callingConvention);
|
| }
|
|
|
| Definition visitInvokeConstructor(InvokeConstructor node) {
|
| - return new InvokeConstructor(node.dartType, node.target, node.selector,
|
| + return new InvokeConstructor(
|
| + node.dartType,
|
| + node.target,
|
| + node.selector,
|
| getList(node.argumentRefs),
|
| - node.sourceInformation)
|
| - ..allocationSiteType = node.allocationSiteType;
|
| + node.sourceInformation)..allocationSiteType = node.allocationSiteType;
|
| }
|
|
|
| Definition visitTypeCast(TypeCast node) {
|
| - return new TypeCast(getCopy(node.valueRef), node.dartType,
|
| - getList(node.typeArgumentRefs));
|
| + return new TypeCast(
|
| + getCopy(node.valueRef), node.dartType, getList(node.typeArgumentRefs));
|
| }
|
|
|
| Definition visitSetMutable(SetMutable node) {
|
| @@ -2830,19 +2803,18 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
| }
|
|
|
| Definition visitSetStatic(SetStatic node) {
|
| - return new SetStatic(node.element, getCopy(node.valueRef),
|
| - node.sourceInformation);
|
| + return new SetStatic(
|
| + node.element, getCopy(node.valueRef), node.sourceInformation);
|
| }
|
|
|
| Definition visitSetField(SetField node) {
|
| - return new SetField(getCopy(node.objectRef), node.field,
|
| - getCopy(node.valueRef));
|
| + return new SetField(
|
| + getCopy(node.objectRef), node.field, getCopy(node.valueRef));
|
| }
|
|
|
| Definition visitGetLazyStatic(GetLazyStatic node) {
|
| return new GetLazyStatic(node.element,
|
| - isFinal: node.isFinal,
|
| - sourceInformation: node.sourceInformation);
|
| + isFinal: node.isFinal, sourceInformation: node.sourceInformation);
|
| }
|
|
|
| Definition visitAwait(Await node) {
|
| @@ -2855,7 +2827,7 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
|
|
| Definition visitLiteralList(LiteralList node) {
|
| return new LiteralList(node.dartType, getList(node.valueRefs))
|
| - ..allocationSiteType = node.allocationSiteType;
|
| + ..allocationSiteType = node.allocationSiteType;
|
| }
|
|
|
| Definition visitConstant(Constant node) {
|
| @@ -2876,27 +2848,22 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
|
|
| Definition visitGetStatic(GetStatic node) {
|
| if (node.witnessRef != null) {
|
| - return new GetStatic.witnessed(node.element,
|
| - getCopy(node.witnessRef),
|
| + return new GetStatic.witnessed(node.element, getCopy(node.witnessRef),
|
| sourceInformation: node.sourceInformation);
|
| } else {
|
| return new GetStatic(node.element,
|
| - isFinal: node.isFinal,
|
| - sourceInformation: node.sourceInformation);
|
| + isFinal: node.isFinal, sourceInformation: node.sourceInformation);
|
| }
|
| }
|
|
|
| Definition visitInterceptor(Interceptor node) {
|
| return new Interceptor(getCopy(node.inputRef), node.sourceInformation)
|
| - ..interceptedClasses.addAll(node.interceptedClasses);
|
| + ..interceptedClasses.addAll(node.interceptedClasses);
|
| }
|
|
|
| Definition visitCreateInstance(CreateInstance node) {
|
| - return new CreateInstance(
|
| - node.classElement,
|
| - getList(node.argumentRefs),
|
| - getCopyOrNull(node.typeInformationRef),
|
| - node.sourceInformation);
|
| + return new CreateInstance(node.classElement, getList(node.argumentRefs),
|
| + getCopyOrNull(node.typeInformationRef), node.sourceInformation);
|
| }
|
|
|
| Definition visitGetField(GetField node) {
|
| @@ -2913,8 +2880,8 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
| }
|
|
|
| Definition visitReadTypeVariable(ReadTypeVariable node) {
|
| - return new ReadTypeVariable(node.variable, getCopy(node.targetRef),
|
| - node.sourceInformation);
|
| + return new ReadTypeVariable(
|
| + node.variable, getCopy(node.targetRef), node.sourceInformation);
|
| }
|
|
|
| Definition visitTypeExpression(TypeExpression node) {
|
| @@ -2923,13 +2890,13 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
| }
|
|
|
| Definition visitCreateInvocationMirror(CreateInvocationMirror node) {
|
| - return new CreateInvocationMirror(node.selector,
|
| - getList(node.argumentRefs));
|
| + return new CreateInvocationMirror(
|
| + node.selector, getList(node.argumentRefs));
|
| }
|
|
|
| Definition visitTypeTest(TypeTest node) {
|
| - return new TypeTest(getCopy(node.valueRef), node.dartType,
|
| - getList(node.typeArgumentRefs));
|
| + return new TypeTest(
|
| + getCopy(node.valueRef), node.dartType, getList(node.typeArgumentRefs));
|
| }
|
|
|
| Definition visitTypeTestViaFlag(TypeTestViaFlag node) {
|
| @@ -2937,14 +2904,13 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
| }
|
|
|
| Definition visitApplyBuiltinOperator(ApplyBuiltinOperator node) {
|
| - return new ApplyBuiltinOperator(node.operator, getList(node.argumentRefs),
|
| - node.sourceInformation);
|
| + return new ApplyBuiltinOperator(
|
| + node.operator, getList(node.argumentRefs), node.sourceInformation);
|
| }
|
|
|
| Definition visitApplyBuiltinMethod(ApplyBuiltinMethod node) {
|
| return new ApplyBuiltinMethod(node.method, getCopy(node.receiverRef),
|
| - getList(node.argumentRefs),
|
| - node.sourceInformation);
|
| + getList(node.argumentRefs), node.sourceInformation);
|
| }
|
|
|
| Definition visitGetLength(GetLength node) {
|
| @@ -2966,20 +2932,17 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
|
|
| Definition visitBoundsCheck(BoundsCheck node) {
|
| if (node.hasNoChecks) {
|
| - return new BoundsCheck.noCheck(getCopy(node.objectRef),
|
| - node.sourceInformation);
|
| + return new BoundsCheck.noCheck(
|
| + getCopy(node.objectRef), node.sourceInformation);
|
| } else {
|
| return new BoundsCheck(getCopy(node.objectRef), getCopy(node.indexRef),
|
| - getCopyOrNull(node.lengthRef),
|
| - node.checks,
|
| - node.sourceInformation);
|
| + getCopyOrNull(node.lengthRef), node.checks, node.sourceInformation);
|
| }
|
| }
|
|
|
| Definition visitReceiverCheck(ReceiverCheck node) {
|
| - return new ReceiverCheck(getCopy(node.valueRef),
|
| - node.selector,
|
| - node.sourceInformation,
|
| + return new ReceiverCheck(
|
| + getCopy(node.valueRef), node.selector, node.sourceInformation,
|
| condition: getCopyOrNull(node.conditionRef),
|
| useSelector: node.useSelector,
|
| isNullCheck: node.isNullCheck);
|
| @@ -2987,8 +2950,7 @@ class DefinitionCopyingVisitor extends Visitor<Definition> {
|
|
|
| Definition visitForeignCode(ForeignCode node) {
|
| return new ForeignCode(node.codeTemplate, node.storedType,
|
| - getList(node.argumentRefs),
|
| - node.nativeBehavior,
|
| + getList(node.argumentRefs), node.nativeBehavior,
|
| dependency: node.dependency);
|
| }
|
| }
|
| @@ -3055,15 +3017,12 @@ class CopyingVisitor extends TrampolineRecursiveVisitor {
|
| // copied.
|
| Parameter returnParameter =
|
| _definitions.copy(node.returnContinuation.parameters.first);
|
| - Continuation returnContinuation = _copies[node.returnContinuation] =
|
| - new Continuation([returnParameter]);
|
| + Continuation returnContinuation =
|
| + _copies[node.returnContinuation] = new Continuation([returnParameter]);
|
|
|
| visit(node.body);
|
| - FunctionDefinition copy = new FunctionDefinition(node.element,
|
| - thisParameter,
|
| - parameters,
|
| - returnContinuation,
|
| - _first);
|
| + FunctionDefinition copy = new FunctionDefinition(
|
| + node.element, thisParameter, parameters, returnContinuation, _first);
|
| _first = _current = null;
|
| return copy;
|
| }
|
| @@ -3086,9 +3045,8 @@ class CopyingVisitor extends TrampolineRecursiveVisitor {
|
| // Continuations are copied where they are bound, before processing
|
| // expressions in the scope of their binding.
|
| push(node.handler);
|
| - Continuation handler = _copies[node.handler] =
|
| - new Continuation(node.handler.parameters.map(_definitions.copy)
|
| - .toList());
|
| + Continuation handler = _copies[node.handler] = new Continuation(
|
| + node.handler.parameters.map(_definitions.copy).toList());
|
| plug(new LetHandler(handler, null));
|
| return node.body;
|
| }
|
| @@ -3099,16 +3057,16 @@ class CopyingVisitor extends TrampolineRecursiveVisitor {
|
| }
|
|
|
| Expression traverseLetMutable(LetMutable node) {
|
| - plug(new LetMutable(_definitions.copy(node.variable),
|
| - _definitions.getCopy(node.valueRef)));
|
| + plug(new LetMutable(
|
| + _definitions.copy(node.variable), _definitions.getCopy(node.valueRef)));
|
| return node.body;
|
| }
|
|
|
| // Tail expressions do not have references, so we do not need to map them
|
| // to their copies.
|
| visitInvokeContinuation(InvokeContinuation node) {
|
| - plug(new InvokeContinuation(_copies[node.continuation],
|
| - _definitions.getList(node.argumentRefs),
|
| + plug(new InvokeContinuation(
|
| + _copies[node.continuation], _definitions.getList(node.argumentRefs),
|
| isRecursive: node.isRecursive,
|
| isEscapingTry: node.isEscapingTry,
|
| sourceInformation: node.sourceInformation));
|
| @@ -3123,10 +3081,10 @@ class CopyingVisitor extends TrampolineRecursiveVisitor {
|
| }
|
|
|
| visitBranch(Branch node) {
|
| - plug(new Branch.loose(_definitions.getCopy(node.conditionRef),
|
| + plug(new Branch.loose(
|
| + _definitions.getCopy(node.conditionRef),
|
| _copies[node.trueContinuation],
|
| - _copies[node.falseContinuation])
|
| - ..isStrictCheck = node.isStrictCheck);
|
| + _copies[node.falseContinuation])..isStrictCheck = node.isStrictCheck);
|
| }
|
|
|
| visitUnreachable(Unreachable node) {
|
|
|