| // Copyright (c) 2014, 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 tree_ir_nodes; |
| |
| import '../constants/values.dart' as values; |
| import '../dart_types.dart' show DartType, InterfaceType, TypeVariableType; |
| import '../elements/elements.dart'; |
| import '../io/source_information.dart' show SourceInformation; |
| import '../types/types.dart' show TypeMask; |
| import '../universe/selector.dart' show Selector; |
| |
| import '../cps_ir/builtin_operator.dart'; |
| export '../cps_ir/builtin_operator.dart'; |
| |
| // These imports are only used for the JavaScript specific nodes. If we want to |
| // support more than one native backend, we should probably create better |
| // abstractions for native code and its type and effect system. |
| import '../js/js.dart' as js show Template; |
| import '../native/native.dart' as native show NativeBehavior; |
| import '../types/types.dart' as types show TypeMask; |
| |
| // The Tree language is the target of translation out of the CPS-based IR. |
| // |
| // The translation from CPS to Dart consists of several stages. Among the |
| // stages are translation to direct style, translation out of SSA, eliminating |
| // unnecessary names, recognizing high-level control constructs. Combining |
| // these separate concerns is complicated and the constraints of the CPS-based |
| // language do not permit a multi-stage translation. |
| // |
| // For that reason, CPS is translated to the direct-style language Tree. |
| // Translation out of SSA, unnaming, and control-flow, as well as 'instruction |
| // selection' are performed on the Tree language. |
| // |
| // In contrast to the CPS-based IR, non-primitive expressions can be named and |
| // arguments (to calls, primitives, and blocks) can be arbitrary expressions. |
| // |
| // Additionally, variables are considered in scope within inner functions; |
| // closure variables are thus handled directly instead of using ref cells. |
| |
| /** |
| * The base class of all Tree nodes. |
| */ |
| abstract class Node { |
| /// Workaround for a slow Object.hashCode in the VM. |
| static int _usedHashCodes = 0; |
| final int hashCode = ++_usedHashCodes; |
| } |
| |
| /** |
| * The base class of [Expression]s. |
| */ |
| abstract class Expression extends Node { |
| accept(ExpressionVisitor v); |
| accept1(ExpressionVisitor1 v, arg); |
| } |
| |
| abstract class Statement extends Node { |
| Statement get next; |
| void set next(Statement s); |
| accept(StatementVisitor v); |
| accept1(StatementVisitor1 v, arg); |
| } |
| |
| /** |
| * Labels name [LabeledStatement]s. |
| */ |
| class Label { |
| // A counter used to generate names. The counter is reset to 0 for each |
| // function emitted. |
| static int counter = 0; |
| static String _newName() => 'L${counter++}'; |
| |
| String cachedName; |
| |
| String get name { |
| if (cachedName == null) cachedName = _newName(); |
| return cachedName; |
| } |
| |
| /// Number of [Break] or [Continue] statements that target this label. |
| /// The [Break] constructor will increment this automatically, but the |
| /// counter must be decremented by hand when a [Break] becomes orphaned. |
| int useCount = 0; |
| |
| /// The [LabeledStatement] or [WhileTrue] binding this label. |
| JumpTarget binding; |
| } |
| |
| /** |
| * A local variable in the tree IR. |
| * |
| * All tree IR variables are mutable. |
| * |
| * To use a variable as an expression, reference it from a [VariableUse], with |
| * one [VariableUse] per expression. |
| * |
| * [Variable]s are reference counted. The node constructors [VariableUse], |
| * [Assign], [FunctionDefinition], and [Try] automatically update the reference |
| * count for their variables, but when transforming the tree, the transformer |
| * is responsible for updating reference counts. |
| */ |
| class Variable extends Node { |
| /// Function that declares this variable. |
| ExecutableElement host; |
| |
| /// [Entity] used for synthesizing a name for the variable. |
| /// Different variables may have the same entity. May be null. |
| Entity element; |
| |
| /// Number of places where this variable occurs in a [VariableUse]. |
| int readCount = 0; |
| |
| /// Number of places where this variable occurs as: |
| /// - left-hand of an [Assign] |
| /// - left-hand of a [FunctionDeclaration] |
| /// - parameter in a [FunctionDefinition] |
| /// - catch parameter in a [Try] |
| int writeCount = 0; |
| |
| /// True if a nested function reads or writes this variable. |
| /// |
| /// Always false in JS-mode because closure conversion eliminated nested |
| /// functions. |
| bool isCaptured = false; |
| |
| Variable(this.host, this.element) { |
| assert(host != null); |
| } |
| |
| String toString() => element == null ? 'Variable' : element.toString(); |
| } |
| |
| /// Read the value of a variable. |
| class VariableUse extends Expression { |
| Variable variable; |
| |
| /// Creates a use of [variable] and updates its `readCount`. |
| VariableUse(this.variable) { |
| variable.readCount++; |
| } |
| |
| accept(ExpressionVisitor visitor) => visitor.visitVariableUse(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitVariableUse(this, arg); |
| } |
| } |
| |
| class Assign extends Expression { |
| Variable variable; |
| Expression value; |
| |
| Assign(this.variable, this.value) { |
| variable.writeCount++; |
| } |
| |
| accept(ExpressionVisitor v) => v.visitAssign(this); |
| accept1(ExpressionVisitor1 v, arg) => v.visitAssign(this, arg); |
| |
| static ExpressionStatement makeStatement(Variable variable, |
| Expression value, |
| [Statement next]) { |
| return new ExpressionStatement(new Assign(variable, value), next); |
| } |
| } |
| |
| /** |
| * Common interface for invocations with arguments. |
| */ |
| abstract class Invoke { |
| List<Expression> get arguments; |
| } |
| |
| /** |
| * A call to a static function or getter/setter to a static field. |
| * |
| * In contrast to the CPS-based IR, the arguments can be arbitrary expressions. |
| */ |
| class InvokeStatic extends Expression implements Invoke { |
| final Entity target; |
| final List<Expression> arguments; |
| final Selector selector; |
| final SourceInformation sourceInformation; |
| |
| InvokeStatic(this.target, this.selector, this.arguments, |
| [this.sourceInformation]); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitInvokeStatic(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitInvokeStatic(this, arg); |
| } |
| } |
| |
| /** |
| * A call to a method, operator, getter, setter or index getter/setter. |
| * |
| * If [receiver] is `null`, an error is thrown before the arguments are |
| * evaluated. This corresponds to the JS evaluation order. |
| */ |
| class InvokeMethod extends Expression implements Invoke { |
| Expression receiver; |
| final Selector selector; |
| final TypeMask mask; |
| final List<Expression> arguments; |
| final SourceInformation sourceInformation; |
| |
| /// If true, it is known that the receiver cannot be `null`. |
| bool receiverIsNotNull = false; |
| |
| InvokeMethod(this.receiver, |
| this.selector, |
| this.mask, |
| this.arguments, |
| this.sourceInformation) { |
| assert(receiver != null); |
| } |
| |
| accept(ExpressionVisitor visitor) => visitor.visitInvokeMethod(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitInvokeMethod(this, arg); |
| } |
| } |
| |
| /// Invoke [target] on [receiver], bypassing ordinary dispatch semantics. |
| /// |
| /// Since the [receiver] is not used for method lookup, it may be `null` |
| /// without an error being thrown. |
| class InvokeMethodDirectly extends Expression implements Invoke { |
| Expression receiver; |
| final Element target; |
| final Selector selector; |
| final List<Expression> arguments; |
| final SourceInformation sourceInformation; |
| |
| InvokeMethodDirectly(this.receiver, this.target, this.selector, |
| this.arguments, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitInvokeMethodDirectly(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitInvokeMethodDirectly(this, arg); |
| } |
| } |
| |
| /** |
| * Call to a factory or generative constructor. |
| */ |
| class InvokeConstructor extends Expression implements Invoke { |
| final DartType type; |
| final FunctionElement target; |
| final List<Expression> arguments; |
| final Selector selector; |
| final SourceInformation sourceInformation; |
| /// TODO(karlklose): get rid of this field. Instead use the constant's |
| /// expression to find the constructor to be called in dart2dart. |
| final values.ConstantValue constant; |
| |
| InvokeConstructor(this.type, this.target, this.selector, this.arguments, |
| this.sourceInformation, [this.constant]); |
| |
| ClassElement get targetClass => target.enclosingElement; |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitInvokeConstructor(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitInvokeConstructor(this, arg); |
| } |
| } |
| |
| /** |
| * A constant. |
| */ |
| class Constant extends Expression { |
| final values.ConstantValue value; |
| final SourceInformation sourceInformation; |
| |
| Constant(this.value, {this.sourceInformation}); |
| |
| Constant.bool(values.BoolConstantValue constantValue) |
| : value = constantValue, |
| sourceInformation = null; |
| |
| accept(ExpressionVisitor visitor) => visitor.visitConstant(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitConstant(this, arg); |
| |
| String toString() => 'Constant(value=${value.toStructuredString()})'; |
| } |
| |
| class This extends Expression { |
| accept(ExpressionVisitor visitor) => visitor.visitThis(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitThis(this, arg); |
| } |
| |
| class LiteralList extends Expression { |
| final InterfaceType type; |
| final List<Expression> values; |
| |
| LiteralList(this.type, this.values); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitLiteralList(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitLiteralList(this, arg); |
| } |
| } |
| |
| class LiteralMapEntry { |
| Expression key; |
| Expression value; |
| |
| LiteralMapEntry(this.key, this.value); |
| } |
| |
| class LiteralMap extends Expression { |
| final InterfaceType type; |
| final List<LiteralMapEntry> entries; |
| |
| LiteralMap(this.type, this.entries); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitLiteralMap(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitLiteralMap(this, arg); |
| } |
| } |
| |
| /// Type test or type cast. |
| /// |
| /// Note that if this is a type test, then [type] cannot be `Object`, `dynamic`, |
| /// or the `Null` type. These cases are compiled to other node types. |
| class TypeOperator extends Expression { |
| Expression value; |
| final DartType type; |
| final List<Expression> typeArguments; |
| final bool isTypeTest; |
| |
| TypeOperator(this.value, this.type, this.typeArguments, |
| {bool this.isTypeTest}); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitTypeOperator(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitTypeOperator(this, arg); |
| } |
| |
| String get operator => isTypeTest ? 'is' : 'as'; |
| } |
| |
| /** |
| * Apply a built-in operator. |
| * |
| * It must be known that the arguments have the proper types. |
| * Null is not a valid argument to any of the built-in operators. |
| */ |
| class ApplyBuiltinOperator extends Expression { |
| BuiltinOperator operator; |
| List<Expression> arguments; |
| |
| ApplyBuiltinOperator(this.operator, this.arguments); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitApplyBuiltinOperator(this); |
| } |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitApplyBuiltinOperator(this, arg); |
| } |
| } |
| |
| class ApplyBuiltinMethod extends Expression { |
| BuiltinMethod method; |
| Expression receiver; |
| List<Expression> arguments; |
| |
| bool receiverIsNotNull; |
| |
| ApplyBuiltinMethod(this.method, |
| this.receiver, |
| this.arguments, |
| {this.receiverIsNotNull: false}); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitApplyBuiltinMethod(this); |
| } |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitApplyBuiltinMethod(this, arg); |
| } |
| } |
| |
| /// A conditional expression. |
| class Conditional extends Expression { |
| Expression condition; |
| Expression thenExpression; |
| Expression elseExpression; |
| |
| Conditional(this.condition, this.thenExpression, this.elseExpression); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitConditional(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitConditional(this, arg); |
| } |
| |
| String toString() => 'Conditional(condition=$condition,thenExpression=' |
| '$thenExpression,elseExpression=$elseExpression)'; |
| } |
| |
| /// An && or || expression. The operator is internally represented as a boolean |
| /// [isAnd] to simplify rewriting of logical operators. |
| class LogicalOperator extends Expression { |
| Expression left; |
| bool isAnd; |
| Expression right; |
| |
| LogicalOperator(this.left, this.right, this.isAnd); |
| LogicalOperator.and(this.left, this.right) : isAnd = true; |
| LogicalOperator.or(this.left, this.right) : isAnd = false; |
| |
| String get operator => isAnd ? '&&' : '||'; |
| |
| accept(ExpressionVisitor visitor) => visitor.visitLogicalOperator(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitLogicalOperator(this, arg); |
| } |
| |
| String toString() => 'LogicalOperator(left=$left,right=$right,isAnd=$isAnd)'; |
| } |
| |
| /// Logical negation. |
| // TODO(asgerf): Replace this class with the IsFalsy builtin operator? |
| // Right now the tree builder compiles IsFalsy to Not. |
| class Not extends Expression { |
| Expression operand; |
| |
| Not(this.operand); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitNot(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitNot(this, arg); |
| } |
| |
| /// Currently unused. |
| /// |
| /// See CreateFunction in the cps_ir_nodes.dart. |
| class FunctionExpression extends Expression { |
| final FunctionDefinition definition; |
| |
| FunctionExpression(this.definition); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitFunctionExpression(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitFunctionExpression(this, arg); |
| } |
| } |
| |
| /// A [LabeledStatement] or [WhileTrue] or [For]. |
| abstract class JumpTarget extends Statement { |
| Label get label; |
| Statement get body; |
| } |
| |
| /** |
| * A labeled statement. Breaks to the label within the labeled statement |
| * target the successor statement. |
| */ |
| class LabeledStatement extends JumpTarget { |
| Statement next; |
| final Label label; |
| Statement body; |
| |
| LabeledStatement(this.label, this.body, this.next) { |
| assert(label.binding == null); |
| label.binding = this; |
| } |
| |
| accept(StatementVisitor visitor) => visitor.visitLabeledStatement(this); |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitLabeledStatement(this, arg); |
| } |
| } |
| |
| /// A [WhileTrue] or [For] loop. |
| abstract class Loop extends JumpTarget { |
| } |
| |
| /** |
| * A labeled while(true) loop. |
| */ |
| class WhileTrue extends Loop { |
| final Label label; |
| Statement body; |
| |
| WhileTrue(this.label, this.body) { |
| assert(label.binding == null); |
| label.binding = this; |
| } |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| accept(StatementVisitor visitor) => visitor.visitWhileTrue(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitWhileTrue(this, arg); |
| } |
| |
| /** |
| * A loop with a condition and update expressions. If there are any update |
| * expressions, this generates a for loop, otherwise a while loop. |
| * |
| * When the condition is false, control resumes at the [next] statement. |
| * |
| * It is NOT valid to target this statement with a [Break]. |
| * The only way to reach [next] is for the condition to evaluate to false. |
| * |
| * [For] statements are introduced in the [LoopRewriter] and are |
| * assumed not to occur before then. |
| */ |
| class For extends Loop { |
| final Label label; |
| Expression condition; |
| List<Expression> updates; |
| Statement body; |
| Statement next; |
| |
| For(this.label, |
| this.condition, |
| this.updates, |
| this.body, |
| this.next) { |
| assert(label.binding == null); |
| label.binding = this; |
| } |
| |
| accept(StatementVisitor visitor) => visitor.visitFor(this); |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitFor(this, arg); |
| } |
| } |
| |
| /// A [Break] or [Continue] statement. |
| abstract class Jump extends Statement { |
| Label get target; |
| } |
| |
| /** |
| * A break from an enclosing [LabeledStatement]. The break targets the |
| * labeled statement's successor statement. |
| */ |
| class Break extends Jump { |
| final Label target; |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| Break(this.target) { |
| ++target.useCount; |
| } |
| |
| accept(StatementVisitor visitor) => visitor.visitBreak(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitBreak(this, arg); |
| } |
| |
| /** |
| * A continue to an enclosing [WhileTrue] or [For] loop. |
| * The continue targets the loop's body. |
| */ |
| class Continue extends Jump { |
| final Label target; |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| Continue(this.target) { |
| ++target.useCount; |
| } |
| |
| accept(StatementVisitor visitor) => visitor.visitContinue(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitContinue(this, arg); |
| } |
| |
| /** |
| * A return exit from the function. |
| * |
| * In contrast to the CPS-based IR, the return value is an arbitrary |
| * expression. |
| */ |
| class Return extends Statement { |
| /// Should not be null. Use [Constant] with [NullConstantValue] for void |
| /// returns. |
| /// Even in constructors this holds true. Take special care when translating |
| /// back to dart, where `return null;` in a constructor is an error. |
| Expression value; |
| SourceInformation sourceInformation; |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| Return(this.value, {this.sourceInformation}); |
| |
| accept(StatementVisitor visitor) => visitor.visitReturn(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitReturn(this, arg); |
| } |
| |
| /// A throw statement. |
| /// |
| /// In the Tree IR, throw is a statement (like JavaScript and unlike Dart). |
| /// It does not have a successor statement. |
| class Throw extends Statement { |
| Expression value; |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| Throw(this.value); |
| |
| accept(StatementVisitor visitor) => visitor.visitThrow(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitThrow(this, arg); |
| } |
| |
| /// A rethrow of an exception. |
| /// |
| /// Rethrow can only occur nested inside a catch block. It implicitly throws |
| /// the block's caught exception value without changing the caught stack |
| /// trace. It does not have a successor statement. |
| class Rethrow extends Statement { |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| Rethrow(); |
| |
| accept(StatementVisitor visitor) => visitor.visitRethrow(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitRethrow(this, arg); |
| } |
| |
| /** |
| * A conditional branch based on the true value of an [Expression]. |
| */ |
| class If extends Statement { |
| Expression condition; |
| Statement thenStatement; |
| Statement elseStatement; |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| If(this.condition, this.thenStatement, this.elseStatement); |
| |
| accept(StatementVisitor visitor) => visitor.visitIf(this); |
| accept1(StatementVisitor1 visitor, arg) => visitor.visitIf(this, arg); |
| } |
| |
| class ExpressionStatement extends Statement { |
| Statement next; |
| Expression expression; |
| |
| ExpressionStatement(this.expression, this.next); |
| |
| accept(StatementVisitor visitor) => visitor.visitExpressionStatement(this); |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitExpressionStatement(this, arg); |
| } |
| } |
| |
| class Try extends Statement { |
| Statement tryBody; |
| List<Variable> catchParameters; |
| Statement catchBody; |
| |
| Statement get next => null; |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| |
| Try(this.tryBody, this.catchParameters, this.catchBody) { |
| for (Variable variable in catchParameters) { |
| variable.writeCount++; // Being a catch parameter counts as a write. |
| } |
| } |
| |
| accept(StatementVisitor visitor) => visitor.visitTry(this); |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitTry(this, arg); |
| } |
| } |
| |
| /// A statement that is known to be unreachable. |
| class Unreachable extends Statement { |
| Statement get next => null; |
| void set next(Statement value) => throw 'UNREACHABLE'; |
| |
| accept(StatementVisitor visitor) => visitor.visitUnreachable(this); |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitUnreachable(this, arg); |
| } |
| } |
| |
| class FunctionDefinition extends Node { |
| final ExecutableElement element; |
| final List<Variable> parameters; |
| Statement body; |
| |
| /// Creates a function definition and updates `writeCount` for [parameters]. |
| FunctionDefinition(this.element, this.parameters, this.body) { |
| for (Variable param in parameters) { |
| param.writeCount++; // Being a parameter counts as a write. |
| } |
| } |
| } |
| |
| class CreateBox extends Expression { |
| accept(ExpressionVisitor visitor) => visitor.visitCreateBox(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitCreateBox(this, arg); |
| } |
| |
| class CreateInstance extends Expression { |
| ClassElement classElement; |
| List<Expression> arguments; |
| List<Expression> typeInformation; |
| SourceInformation sourceInformation; |
| |
| CreateInstance(this.classElement, this.arguments, |
| this.typeInformation, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitCreateInstance(this); |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitCreateInstance(this, arg); |
| } |
| } |
| |
| class GetField extends Expression { |
| Expression object; |
| Element field; |
| bool objectIsNotNull; |
| |
| GetField(this.object, this.field, {this.objectIsNotNull: false}); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitGetField(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitGetField(this, arg); |
| } |
| |
| class SetField extends Expression { |
| Expression object; |
| Element field; |
| Expression value; |
| |
| SetField(this.object, this.field, this.value); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitSetField(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitSetField(this, arg); |
| } |
| |
| /// Read the value of a field, possibly provoking its initializer to evaluate, |
| /// or tear off a static method. |
| class GetStatic extends Expression { |
| Element element; |
| SourceInformation sourceInformation; |
| |
| GetStatic(this.element, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitGetStatic(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitGetStatic(this, arg); |
| } |
| |
| class SetStatic extends Expression { |
| Element element; |
| Expression value; |
| SourceInformation sourceInformation; |
| |
| SetStatic(this.element, this.value, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) => visitor.visitSetStatic(this); |
| accept1(ExpressionVisitor1 visitor, arg) => visitor.visitSetStatic(this, arg); |
| } |
| |
| class GetLength extends Expression { |
| Expression object; |
| |
| GetLength(this.object); |
| |
| accept(ExpressionVisitor v) => v.visitGetLength(this); |
| accept1(ExpressionVisitor1 v, arg) => v.visitGetLength(this, arg); |
| } |
| |
| class GetIndex extends Expression { |
| Expression object; |
| Expression index; |
| |
| GetIndex(this.object, this.index); |
| |
| accept(ExpressionVisitor v) => v.visitGetIndex(this); |
| accept1(ExpressionVisitor1 v, arg) => v.visitGetIndex(this, arg); |
| } |
| |
| class SetIndex extends Expression { |
| Expression object; |
| Expression index; |
| Expression value; |
| |
| SetIndex(this.object, this.index, this.value); |
| |
| accept(ExpressionVisitor v) => v.visitSetIndex(this); |
| accept1(ExpressionVisitor1 v, arg) => v.visitSetIndex(this, arg); |
| } |
| |
| class ReifyRuntimeType extends Expression { |
| Expression value; |
| SourceInformation sourceInformation; |
| |
| ReifyRuntimeType(this.value, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitReifyRuntimeType(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitReifyRuntimeType(this, arg); |
| } |
| } |
| |
| class ReadTypeVariable extends Expression { |
| final TypeVariableType variable; |
| Expression target; |
| final SourceInformation sourceInformation; |
| |
| ReadTypeVariable(this.variable, this.target, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitReadTypeVariable(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitReadTypeVariable(this, arg); |
| } |
| } |
| |
| class CreateInvocationMirror extends Expression { |
| final Selector selector; |
| final List<Expression> arguments; |
| |
| CreateInvocationMirror(this.selector, this.arguments); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitCreateInvocationMirror(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitCreateInvocationMirror(this, arg); |
| } |
| } |
| |
| class Interceptor extends Expression { |
| Expression input; |
| Set<ClassElement> interceptedClasses; |
| final SourceInformation sourceInformation; |
| |
| Interceptor(this.input, this.interceptedClasses, this.sourceInformation); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitInterceptor(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitInterceptor(this, arg); |
| } |
| } |
| |
| class ForeignCode extends Node { |
| final js.Template codeTemplate; |
| final types.TypeMask type; |
| final List<Expression> arguments; |
| final native.NativeBehavior nativeBehavior; |
| final Element dependency; |
| |
| ForeignCode(this.codeTemplate, this.type, this.arguments, this.nativeBehavior, |
| this.dependency); |
| } |
| |
| class ForeignExpression extends ForeignCode implements Expression { |
| ForeignExpression(js.Template codeTemplate, types.TypeMask type, |
| List<Expression> arguments, native.NativeBehavior nativeBehavior, |
| Element dependency) |
| : super(codeTemplate, type, arguments, nativeBehavior, |
| dependency); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitForeignExpression(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitForeignExpression(this, arg); |
| } |
| } |
| |
| class ForeignStatement extends ForeignCode implements Statement { |
| ForeignStatement(js.Template codeTemplate, types.TypeMask type, |
| List<Expression> arguments, native.NativeBehavior nativeBehavior, |
| Element dependency) |
| : super(codeTemplate, type, arguments, nativeBehavior, |
| dependency); |
| |
| accept(StatementVisitor visitor) { |
| return visitor.visitForeignStatement(this); |
| } |
| |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitForeignStatement(this, arg); |
| } |
| |
| @override |
| Statement get next => null; |
| |
| @override |
| void set next(Statement s) => throw 'UNREACHABLE'; |
| } |
| |
| /// Denotes the internal representation of [dartType], where all type variables |
| /// are replaced by the values in [arguments]. |
| /// (See documentation on the TypeExpression CPS node for more details.) |
| class TypeExpression extends Expression { |
| final DartType dartType; |
| final List<Expression> arguments; |
| |
| TypeExpression(this.dartType, this.arguments); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitTypeExpression(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitTypeExpression(this, arg); |
| } |
| } |
| |
| class Await extends Expression { |
| Expression input; |
| |
| Await(this.input); |
| |
| accept(ExpressionVisitor visitor) { |
| return visitor.visitAwait(this); |
| } |
| |
| accept1(ExpressionVisitor1 visitor, arg) { |
| return visitor.visitAwait(this, arg); |
| } |
| } |
| |
| class Yield extends Statement { |
| Statement next; |
| Expression input; |
| final bool hasStar; |
| |
| Yield(this.input, this.hasStar, this.next); |
| |
| accept(StatementVisitor visitor) { |
| return visitor.visitYield(this); |
| } |
| |
| accept1(StatementVisitor1 visitor, arg) { |
| return visitor.visitYield(this, arg); |
| } |
| } |
| |
| abstract class ExpressionVisitor<E> { |
| E visitExpression(Expression node) => node.accept(this); |
| E visitVariableUse(VariableUse node); |
| E visitAssign(Assign node); |
| E visitInvokeStatic(InvokeStatic node); |
| E visitInvokeMethod(InvokeMethod node); |
| E visitInvokeMethodDirectly(InvokeMethodDirectly node); |
| E visitInvokeConstructor(InvokeConstructor node); |
| E visitConstant(Constant node); |
| E visitThis(This node); |
| E visitConditional(Conditional node); |
| E visitLogicalOperator(LogicalOperator node); |
| E visitNot(Not node); |
| E visitLiteralList(LiteralList node); |
| E visitLiteralMap(LiteralMap node); |
| E visitTypeOperator(TypeOperator node); |
| E visitFunctionExpression(FunctionExpression node); |
| E visitGetField(GetField node); |
| E visitSetField(SetField node); |
| E visitGetStatic(GetStatic node); |
| E visitSetStatic(SetStatic node); |
| E visitCreateBox(CreateBox node); |
| E visitCreateInstance(CreateInstance node); |
| E visitReifyRuntimeType(ReifyRuntimeType node); |
| E visitReadTypeVariable(ReadTypeVariable node); |
| E visitTypeExpression(TypeExpression node); |
| E visitCreateInvocationMirror(CreateInvocationMirror node); |
| E visitInterceptor(Interceptor node); |
| E visitApplyBuiltinOperator(ApplyBuiltinOperator node); |
| E visitApplyBuiltinMethod(ApplyBuiltinMethod node); |
| E visitForeignExpression(ForeignExpression node); |
| E visitGetLength(GetLength node); |
| E visitGetIndex(GetIndex node); |
| E visitSetIndex(SetIndex node); |
| E visitAwait(Await node); |
| } |
| |
| abstract class ExpressionVisitor1<E, A> { |
| E visitExpression(Expression node, A arg) => node.accept1(this, arg); |
| E visitVariableUse(VariableUse node, A arg); |
| E visitAssign(Assign node, A arg); |
| E visitInvokeStatic(InvokeStatic node, A arg); |
| E visitInvokeMethod(InvokeMethod node, A arg); |
| E visitInvokeMethodDirectly(InvokeMethodDirectly node, A arg); |
| E visitInvokeConstructor(InvokeConstructor node, A arg); |
| E visitConstant(Constant node, A arg); |
| E visitThis(This node, A arg); |
| E visitConditional(Conditional node, A arg); |
| E visitLogicalOperator(LogicalOperator node, A arg); |
| E visitNot(Not node, A arg); |
| E visitLiteralList(LiteralList node, A arg); |
| E visitLiteralMap(LiteralMap node, A arg); |
| E visitTypeOperator(TypeOperator node, A arg); |
| E visitFunctionExpression(FunctionExpression node, A arg); |
| E visitGetField(GetField node, A arg); |
| E visitSetField(SetField node, A arg); |
| E visitGetStatic(GetStatic node, A arg); |
| E visitSetStatic(SetStatic node, A arg); |
| E visitCreateBox(CreateBox node, A arg); |
| E visitCreateInstance(CreateInstance node, A arg); |
| E visitReifyRuntimeType(ReifyRuntimeType node, A arg); |
| E visitReadTypeVariable(ReadTypeVariable node, A arg); |
| E visitTypeExpression(TypeExpression node, A arg); |
| E visitCreateInvocationMirror(CreateInvocationMirror node, A arg); |
| E visitInterceptor(Interceptor node, A arg); |
| E visitApplyBuiltinOperator(ApplyBuiltinOperator node, A arg); |
| E visitApplyBuiltinMethod(ApplyBuiltinMethod node, A arg); |
| E visitForeignExpression(ForeignExpression node, A arg); |
| E visitGetLength(GetLength node, A arg); |
| E visitGetIndex(GetIndex node, A arg); |
| E visitSetIndex(SetIndex node, A arg); |
| E visitAwait(Await node, A arg); |
| } |
| |
| abstract class StatementVisitor<S> { |
| S visitStatement(Statement node) => node.accept(this); |
| S visitLabeledStatement(LabeledStatement node); |
| S visitReturn(Return node); |
| S visitThrow(Throw node); |
| S visitRethrow(Rethrow node); |
| S visitBreak(Break node); |
| S visitContinue(Continue node); |
| S visitIf(If node); |
| S visitWhileTrue(WhileTrue node); |
| S visitFor(For node); |
| S visitExpressionStatement(ExpressionStatement node); |
| S visitTry(Try node); |
| S visitUnreachable(Unreachable node); |
| S visitForeignStatement(ForeignStatement node); |
| S visitYield(Yield node); |
| } |
| |
| abstract class StatementVisitor1<S, A> { |
| S visitStatement(Statement node, A arg) => node.accept1(this, arg); |
| S visitLabeledStatement(LabeledStatement node, A arg); |
| S visitReturn(Return node, A arg); |
| S visitThrow(Throw node, A arg); |
| S visitRethrow(Rethrow node, A arg); |
| S visitBreak(Break node, A arg); |
| S visitContinue(Continue node, A arg); |
| S visitIf(If node, A arg); |
| S visitWhileTrue(WhileTrue node, A arg); |
| S visitFor(For node, A arg); |
| S visitExpressionStatement(ExpressionStatement node, A arg); |
| S visitTry(Try node, A arg); |
| S visitUnreachable(Unreachable node, A arg); |
| S visitForeignStatement(ForeignStatement node, A arg); |
| S visitYield(Yield node, A arg); |
| } |
| |
| abstract class RecursiveVisitor implements StatementVisitor, ExpressionVisitor { |
| visitExpression(Expression e) => e.accept(this); |
| visitStatement(Statement s) => s.accept(this); |
| |
| visitInnerFunction(FunctionDefinition node); |
| |
| visitVariable(Variable variable) {} |
| |
| visitVariableUse(VariableUse node) { |
| visitVariable(node.variable); |
| } |
| |
| visitAssign(Assign node) { |
| visitVariable(node.variable); |
| visitExpression(node.value); |
| } |
| |
| visitInvokeStatic(InvokeStatic node) { |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitInvokeMethod(InvokeMethod node) { |
| visitExpression(node.receiver); |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| visitExpression(node.receiver); |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitInvokeConstructor(InvokeConstructor node) { |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitConstant(Constant node) {} |
| |
| visitThis(This node) {} |
| |
| visitConditional(Conditional node) { |
| visitExpression(node.condition); |
| visitExpression(node.thenExpression); |
| visitExpression(node.elseExpression); |
| } |
| |
| visitLogicalOperator(LogicalOperator node) { |
| visitExpression(node.left); |
| visitExpression(node.right); |
| } |
| |
| visitNot(Not node) { |
| visitExpression(node.operand); |
| } |
| |
| visitLiteralList(LiteralList node) { |
| node.values.forEach(visitExpression); |
| } |
| |
| visitLiteralMap(LiteralMap node) { |
| node.entries.forEach((LiteralMapEntry entry) { |
| visitExpression(entry.key); |
| visitExpression(entry.value); |
| }); |
| } |
| |
| visitTypeOperator(TypeOperator node) { |
| visitExpression(node.value); |
| node.typeArguments.forEach(visitExpression); |
| } |
| |
| visitFunctionExpression(FunctionExpression node) { |
| visitInnerFunction(node.definition); |
| } |
| |
| visitLabeledStatement(LabeledStatement node) { |
| visitStatement(node.body); |
| visitStatement(node.next); |
| } |
| |
| visitReturn(Return node) { |
| visitExpression(node.value); |
| } |
| |
| visitThrow(Throw node) { |
| visitExpression(node.value); |
| } |
| |
| visitRethrow(Rethrow node) {} |
| |
| visitBreak(Break node) {} |
| |
| visitContinue(Continue node) {} |
| |
| visitIf(If node) { |
| visitExpression(node.condition); |
| visitStatement(node.thenStatement); |
| visitStatement(node.elseStatement); |
| } |
| |
| visitWhileTrue(WhileTrue node) { |
| visitStatement(node.body); |
| } |
| |
| visitFor(For node) { |
| visitExpression(node.condition); |
| node.updates.forEach(visitExpression); |
| visitStatement(node.body); |
| visitStatement(node.next); |
| } |
| |
| visitExpressionStatement(ExpressionStatement inputNode) { |
| // Iterate over chains of expression statements to avoid deep recursion. |
| Statement node = inputNode; |
| while (node is ExpressionStatement) { |
| ExpressionStatement stmt = node; |
| visitExpression(stmt.expression); |
| node = stmt.next; |
| } |
| visitStatement(node); |
| } |
| |
| visitTry(Try node) { |
| visitStatement(node.tryBody); |
| visitStatement(node.catchBody); |
| } |
| |
| visitGetField(GetField node) { |
| visitExpression(node.object); |
| } |
| |
| visitSetField(SetField node) { |
| visitExpression(node.object); |
| visitExpression(node.value); |
| } |
| |
| visitGetStatic(GetStatic node) { |
| } |
| |
| visitSetStatic(SetStatic node) { |
| visitExpression(node.value); |
| } |
| |
| visitCreateBox(CreateBox node) { |
| } |
| |
| visitCreateInstance(CreateInstance node) { |
| node.arguments.forEach(visitExpression); |
| node.typeInformation.forEach(visitExpression); |
| } |
| |
| visitReifyRuntimeType(ReifyRuntimeType node) { |
| visitExpression(node.value); |
| } |
| |
| visitReadTypeVariable(ReadTypeVariable node) { |
| visitExpression(node.target); |
| } |
| |
| visitTypeExpression(TypeExpression node) { |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitCreateInvocationMirror(CreateInvocationMirror node) { |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitUnreachable(Unreachable node) { |
| } |
| |
| visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
| visitExpression(node.receiver); |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitInterceptor(Interceptor node) { |
| visitExpression(node.input); |
| } |
| |
| visitForeignCode(ForeignCode node) { |
| node.arguments.forEach(visitExpression); |
| } |
| |
| visitForeignExpression(ForeignExpression node) => visitForeignCode(node); |
| visitForeignStatement(ForeignStatement node) => visitForeignCode(node); |
| |
| visitGetLength(GetLength node) { |
| visitExpression(node.object); |
| } |
| |
| visitGetIndex(GetIndex node) { |
| visitExpression(node.object); |
| visitExpression(node.index); |
| } |
| |
| visitSetIndex(SetIndex node) { |
| visitExpression(node.object); |
| visitExpression(node.index); |
| visitExpression(node.value); |
| } |
| |
| visitAwait(Await node) { |
| visitExpression(node.input); |
| } |
| |
| visitYield(Yield node) { |
| visitExpression(node.input); |
| visitStatement(node.next); |
| } |
| } |
| |
| abstract class Transformer implements ExpressionVisitor<Expression>, |
| StatementVisitor<Statement> { |
| Expression visitExpression(Expression e) => e.accept(this); |
| Statement visitStatement(Statement s) => s.accept(this); |
| } |
| |
| class RecursiveTransformer extends Transformer { |
| void visitInnerFunction(FunctionDefinition node) { |
| node.body = visitStatement(node.body); |
| } |
| |
| void _replaceExpressions(List<Expression> list) { |
| for (int i = 0; i < list.length; i++) { |
| list[i] = visitExpression(list[i]); |
| } |
| } |
| |
| visitVariableUse(VariableUse node) => node; |
| |
| visitAssign(Assign node) { |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitInvokeStatic(InvokeStatic node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitInvokeMethod(InvokeMethod node) { |
| node.receiver = visitExpression(node.receiver); |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| node.receiver = visitExpression(node.receiver); |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitInvokeConstructor(InvokeConstructor node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitConstant(Constant node) => node; |
| |
| visitThis(This node) => node; |
| |
| visitConditional(Conditional node) { |
| node.condition = visitExpression(node.condition); |
| node.thenExpression = visitExpression(node.thenExpression); |
| node.elseExpression = visitExpression(node.elseExpression); |
| return node; |
| } |
| |
| visitLogicalOperator(LogicalOperator node) { |
| node.left = visitExpression(node.left); |
| node.right = visitExpression(node.right); |
| return node; |
| } |
| |
| visitNot(Not node) { |
| node.operand = visitExpression(node.operand); |
| return node; |
| } |
| |
| visitLiteralList(LiteralList node) { |
| _replaceExpressions(node.values); |
| return node; |
| } |
| |
| visitLiteralMap(LiteralMap node) { |
| node.entries.forEach((LiteralMapEntry entry) { |
| entry.key = visitExpression(entry.key); |
| entry.value = visitExpression(entry.value); |
| }); |
| return node; |
| } |
| |
| visitTypeOperator(TypeOperator node) { |
| node.value = visitExpression(node.value); |
| _replaceExpressions(node.typeArguments); |
| return node; |
| } |
| |
| visitFunctionExpression(FunctionExpression node) { |
| visitInnerFunction(node.definition); |
| return node; |
| } |
| |
| visitLabeledStatement(LabeledStatement node) { |
| node.body = visitStatement(node.body); |
| node.next = visitStatement(node.next); |
| return node; |
| } |
| |
| visitReturn(Return node) { |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitThrow(Throw node) { |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitRethrow(Rethrow node) => node; |
| |
| visitBreak(Break node) => node; |
| |
| visitContinue(Continue node) => node; |
| |
| visitIf(If node) { |
| node.condition = visitExpression(node.condition); |
| node.thenStatement = visitStatement(node.thenStatement); |
| node.elseStatement = visitStatement(node.elseStatement); |
| return node; |
| } |
| |
| visitWhileTrue(WhileTrue node) { |
| node.body = visitStatement(node.body); |
| return node; |
| } |
| |
| visitFor(For node) { |
| node.condition = visitExpression(node.condition); |
| _replaceExpressions(node.updates); |
| node.body = visitStatement(node.body); |
| node.next = visitStatement(node.next); |
| return node; |
| } |
| |
| visitExpressionStatement(ExpressionStatement node) { |
| // Iterate over chains of expression statements to avoid deep recursion. |
| Statement first = node; |
| while (true) { |
| node.expression = visitExpression(node.expression); |
| if (node.next is ExpressionStatement) { |
| node = node.next; |
| } else { |
| break; |
| } |
| } |
| node.next = visitStatement(node.next); |
| return first; |
| } |
| |
| visitTry(Try node) { |
| node.tryBody = visitStatement(node.tryBody); |
| node.catchBody = visitStatement(node.catchBody); |
| return node; |
| } |
| |
| visitGetField(GetField node) { |
| node.object = visitExpression(node.object); |
| return node; |
| } |
| |
| visitSetField(SetField node) { |
| node.object = visitExpression(node.object); |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitGetStatic(GetStatic node) => node; |
| |
| visitSetStatic(SetStatic node) { |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitCreateBox(CreateBox node) => node; |
| |
| visitCreateInstance(CreateInstance node) { |
| _replaceExpressions(node.arguments); |
| _replaceExpressions(node.typeInformation); |
| return node; |
| } |
| |
| visitReifyRuntimeType(ReifyRuntimeType node) { |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitReadTypeVariable(ReadTypeVariable node) { |
| node.target = visitExpression(node.target); |
| return node; |
| } |
| |
| visitTypeExpression(TypeExpression node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitCreateInvocationMirror(CreateInvocationMirror node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitForeignExpression(ForeignExpression node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitForeignStatement(ForeignStatement node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitUnreachable(Unreachable node) { |
| return node; |
| } |
| |
| visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
| node.receiver = visitExpression(node.receiver); |
| _replaceExpressions(node.arguments); |
| return node; |
| } |
| |
| visitInterceptor(Interceptor node) { |
| node.input = visitExpression(node.input); |
| return node; |
| } |
| |
| visitGetLength(GetLength node) { |
| node.object = visitExpression(node.object); |
| return node; |
| } |
| |
| visitGetIndex(GetIndex node) { |
| node.object = visitExpression(node.object); |
| node.index = visitExpression(node.index); |
| return node; |
| } |
| |
| visitSetIndex(SetIndex node) { |
| node.object = visitExpression(node.object); |
| node.index = visitExpression(node.index); |
| node.value = visitExpression(node.value); |
| return node; |
| } |
| |
| visitAwait(Await node) { |
| node.input = visitExpression(node.input); |
| return node; |
| } |
| |
| visitYield(Yield node) { |
| node.input = visitExpression(node.input); |
| return node; |
| } |
| } |
| |
| class FallthroughTarget { |
| final Statement target; |
| int useCount = 0; |
| |
| FallthroughTarget(this.target); |
| } |
| |
| /// A stack machine for tracking fallthrough while traversing the Tree IR. |
| class FallthroughStack { |
| final List<FallthroughTarget> _stack = |
| <FallthroughTarget>[new FallthroughTarget(null)]; |
| |
| /// Set a new fallthrough target. |
| void push(Statement newFallthrough) { |
| _stack.add(new FallthroughTarget(newFallthrough)); |
| } |
| |
| /// Remove the current fallthrough target. |
| void pop() { |
| _stack.removeLast(); |
| } |
| |
| /// The current fallthrough target, or `null` if control will fall over |
| /// the end of the method. |
| Statement get target => _stack.last.target; |
| |
| /// Number of uses of the current fallthrough target. |
| int get useCount => _stack.last.useCount; |
| |
| /// Indicate that a statement will fall through to the current fallthrough |
| /// target. |
| void use() { |
| ++_stack.last.useCount; |
| } |
| } |