| // Copyright (c) 2024, 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. | |
| part of '../../ast.dart'; | |
| // ------------------------------------------------------------------------ | |
| // EXPRESSIONS | |
| // ------------------------------------------------------------------------ | |
| /// A fixed-length list of [Expression]s. | |
| extension type const ExpressionList._(List<Expression> _list) | |
| implements List<Expression> { | |
| static const ExpressionList empty = ExpressionList._(const <Expression>[]); | |
| /// Creates a fixed-length list of 1–7 elements without allocating a | |
| /// temporary growable list from a literal. | |
| @pragma('vm:prefer-inline') | |
| factory( | |
| Expression e1, [ | |
| Expression? e2, | |
| Expression? e3, | |
| Expression? e4, | |
| Expression? e5, | |
| Expression? e6, | |
| Expression? e7, | |
| ]) { | |
| if (e2 == null) { | |
| assert( | |
| e3 == null && e4 == null && e5 == null && e6 == null && e7 == null, | |
| ); | |
| return ExpressionList._1(e1); | |
| } | |
| if (e3 == null) { | |
| assert(e4 == null && e5 == null && e6 == null && e7 == null); | |
| return ExpressionList._2(e1, e2); | |
| } | |
| if (e4 == null) { | |
| assert(e5 == null && e6 == null && e7 == null); | |
| return ExpressionList._3(e1, e2, e3); | |
| } | |
| if (e5 == null) { | |
| assert(e6 == null && e7 == null); | |
| return ExpressionList._4(e1, e2, e3, e4); | |
| } | |
| if (e6 == null) { | |
| assert(e7 == null); | |
| return ExpressionList._5(e1, e2, e3, e4, e5); | |
| } | |
| if (e7 == null) { | |
| return ExpressionList._6(e1, e2, e3, e4, e5, e6); | |
| } | |
| return ExpressionList._7(e1, e2, e3, e4, e5, e6, e7); | |
| } | |
| factory _1(Expression e1) => ExpressionList._(List<Expression>.filled(1, e1)); | |
| factory _2(Expression e1, Expression e2) => | |
| ExpressionList._(List<Expression>.filled(2, e1)..[1] = e2); | |
| factory _3(Expression e1, Expression e2, Expression e3) => ExpressionList._( | |
| List<Expression>.filled(3, e1) | |
| ..[1] = e2 | |
| ..[2] = e3, | |
| ); | |
| factory _4(Expression e1, Expression e2, Expression e3, Expression e4) => | |
| ExpressionList._( | |
| List<Expression>.filled(4, e1) | |
| ..[1] = e2 | |
| ..[2] = e3 | |
| ..[3] = e4, | |
| ); | |
| factory _5( | |
| Expression e1, | |
| Expression e2, | |
| Expression e3, | |
| Expression e4, | |
| Expression e5, | |
| ) => ExpressionList._( | |
| List<Expression>.filled(5, e1) | |
| ..[1] = e2 | |
| ..[2] = e3 | |
| ..[3] = e4 | |
| ..[4] = e5, | |
| ); | |
| factory _6( | |
| Expression e1, | |
| Expression e2, | |
| Expression e3, | |
| Expression e4, | |
| Expression e5, | |
| Expression e6, | |
| ) => ExpressionList._( | |
| List<Expression>.filled(6, e1) | |
| ..[1] = e2 | |
| ..[2] = e3 | |
| ..[3] = e4 | |
| ..[4] = e5 | |
| ..[5] = e6, | |
| ); | |
| factory _7( | |
| Expression e1, | |
| Expression e2, | |
| Expression e3, | |
| Expression e4, | |
| Expression e5, | |
| Expression e6, | |
| Expression e7, | |
| ) => ExpressionList._( | |
| List<Expression>.filled(7, e1) | |
| ..[1] = e2 | |
| ..[2] = e3 | |
| ..[3] = e4 | |
| ..[4] = e5 | |
| ..[5] = e6 | |
| ..[6] = e7, | |
| ); | |
| factory filled(int length, Expression fill) { | |
| if (length == 0) return empty; | |
| return ExpressionList._(List<Expression>.filled(length, fill)); | |
| } | |
| @pragma('vm:prefer-inline') | |
| factory generate(int length, Expression Function(int index) generator) { | |
| if (length == 0) return empty; | |
| return ExpressionList._( | |
| List<Expression>.generate(length, generator, growable: false), | |
| ); | |
| } | |
| @pragma('vm:prefer-inline') | |
| static ExpressionList mapped<T>(List<T> list, Expression Function(T) func) { | |
| final int length = list.length; | |
| if (length == 0) return empty; | |
| return ExpressionList._( | |
| List<Expression>.generate(length, (i) => func(list[i]), growable: false), | |
| ); | |
| } | |
| /// Copies [expressions] into a new fixed-length list. | |
| factory from(List<Expression> expressions) { | |
| if (expressions.isEmpty) return empty; | |
| return ExpressionList._( | |
| List<Expression>.generate( | |
| expressions.length, | |
| (i) => expressions[i], | |
| growable: false, | |
| ), | |
| ); | |
| } | |
| /// Returns a fixed-length list containing the elements of [a] followed by | |
| /// the elements of [b]. | |
| /// | |
| /// May return the existing [a] / [b] if the other is empty. It's therefore | |
| /// not guaranteed to return a new list. | |
| factory concat(ExpressionList a, ExpressionList b) { | |
| if (a.isEmpty) return b; | |
| if (b.isEmpty) return a; | |
| final int aLength = a.length; | |
| return ExpressionList.generate( | |
| aLength + b.length, | |
| (i) => i < aLength ? a[i] : b[i - aLength], | |
| ); | |
| } | |
| ExpressionList skip(int count) { | |
| assert(0 <= count); | |
| if (count == 0) return this; | |
| if (count >= _list.length) return empty; | |
| return ExpressionList.generate( | |
| _list.length - count, | |
| (i) => _list[count + i], | |
| ); | |
| } | |
| } | |
| /// A fixed-length list of [NamedExpression]s. | |
| extension type const NamedExpressionList._(List<NamedExpression> _list) | |
| implements List<NamedExpression> { | |
| static const NamedExpressionList empty = NamedExpressionList._( | |
| const <NamedExpression>[], | |
| ); | |
| /// Creates a fixed-length list of 1–4 elements without allocating a | |
| /// temporary growable list from a literal. | |
| @pragma('vm:prefer-inline') | |
| factory( | |
| NamedExpression e1, [ | |
| NamedExpression? e2, | |
| NamedExpression? e3, | |
| NamedExpression? e4, | |
| ]) { | |
| if (e2 == null) { | |
| assert(e3 == null && e4 == null); | |
| return NamedExpressionList._1(e1); | |
| } | |
| if (e3 == null) { | |
| assert(e4 == null); | |
| return NamedExpressionList._2(e1, e2); | |
| } | |
| if (e4 == null) { | |
| return NamedExpressionList._3(e1, e2, e3); | |
| } | |
| return NamedExpressionList._4(e1, e2, e3, e4); | |
| } | |
| factory _1(NamedExpression e1) => | |
| NamedExpressionList._(List<NamedExpression>.filled(1, e1)); | |
| factory _2(NamedExpression e1, NamedExpression e2) => | |
| NamedExpressionList._(List<NamedExpression>.filled(2, e1)..[1] = e2); | |
| factory _3(NamedExpression e1, NamedExpression e2, NamedExpression e3) => | |
| NamedExpressionList._( | |
| List<NamedExpression>.filled(3, e1) | |
| ..[1] = e2 | |
| ..[2] = e3, | |
| ); | |
| factory _4( | |
| NamedExpression e1, | |
| NamedExpression e2, | |
| NamedExpression e3, | |
| NamedExpression e4, | |
| ) => NamedExpressionList._( | |
| List<NamedExpression>.filled(4, e1) | |
| ..[1] = e2 | |
| ..[2] = e3 | |
| ..[3] = e4, | |
| ); | |
| factory filled(int length, NamedExpression fill) { | |
| if (length == 0) return empty; | |
| return NamedExpressionList._(List<NamedExpression>.filled(length, fill)); | |
| } | |
| @pragma('vm:prefer-inline') | |
| factory generate(int length, NamedExpression Function(int index) generator) { | |
| if (length == 0) return empty; | |
| return NamedExpressionList._( | |
| List<NamedExpression>.generate(length, generator, growable: false), | |
| ); | |
| } | |
| @pragma('vm:prefer-inline') | |
| static NamedExpressionList mapped<T>( | |
| List<T> list, | |
| NamedExpression Function(T) func, | |
| ) { | |
| final int length = list.length; | |
| if (length == 0) return empty; | |
| return NamedExpressionList._( | |
| List<NamedExpression>.generate( | |
| length, | |
| (i) => func(list[i]), | |
| growable: false, | |
| ), | |
| ); | |
| } | |
| /// Copies [expressions] into a new fixed-length list. | |
| factory from(List<NamedExpression> expressions) { | |
| if (expressions.isEmpty) return empty; | |
| return NamedExpressionList._( | |
| List<NamedExpression>.generate( | |
| expressions.length, | |
| (i) => expressions[i], | |
| growable: false, | |
| ), | |
| ); | |
| } | |
| } | |
| sealed class Expression extends TreeNode { | |
| /// Returns the static type of the expression. | |
| /// | |
| /// This calls `StaticTypeContext.getExpressionType` which calls | |
| /// [getStaticTypeInternal] to compute the type of not already cached in | |
| /// [context]. | |
| DartType getStaticType(StaticTypeContext context) { | |
| return context.getExpressionType(this); | |
| } | |
| /// Computes the static type of this expression. | |
| /// | |
| /// This is called by `StaticTypeContext.getExpressionType` if the static | |
| /// type of this expression is not already cached in [context]. | |
| DartType getStaticTypeInternal(StaticTypeContext context); | |
| /// Returns the static type of the expression as an instantiation of | |
| /// [superclass]. | |
| /// | |
| /// Shouldn't be used on code compiled in legacy mode, as this method assumes | |
| /// the IR is strongly typed. | |
| /// | |
| /// This method furthermore assumes that the type of the expression actually | |
| /// is a subtype of (some instantiation of) the given [superclass]. | |
| /// If this is not the case, either an exception is thrown or the raw type of | |
| /// [superclass] is returned. | |
| InterfaceType getStaticTypeAsInstanceOf( | |
| Class superclass, | |
| StaticTypeContext context, | |
| ) { | |
| // This method assumes the program is correctly typed, so if the superclass | |
| // is not generic, we can just return its raw type without computing the | |
| // type of this expression. It also ensures that all types are considered | |
| // subtypes of Object (not just interface types), and function types are | |
| // considered subtypes of Function. | |
| if (superclass.typeParameters.isEmpty) { | |
| return context.typeEnvironment.coreTypes.rawType( | |
| superclass, | |
| context.nonNullable, | |
| ); | |
| } | |
| DartType type = getStaticType(context).nonTypeParameterBound; | |
| if (type is NullType) { | |
| return context.typeEnvironment.coreTypes.bottomInterfaceType( | |
| superclass, | |
| context.nullable, | |
| ); | |
| } else if (type is NeverType) { | |
| return context.typeEnvironment.coreTypes.bottomInterfaceType( | |
| superclass, | |
| type.nullability, | |
| ); | |
| } | |
| if (type is TypeDeclarationType) { | |
| DartTypeList? upcastTypeArguments = context.typeEnvironment | |
| .getTypeArgumentsAsInstanceOf(type, superclass); | |
| if (upcastTypeArguments != null) { | |
| return new InterfaceType( | |
| superclass, | |
| type.nullability, | |
| upcastTypeArguments, | |
| ); | |
| } | |
| } | |
| // The static type of this expression is not a subtype of [superclass]. The | |
| // means that the static type of this expression is not the same as when | |
| // the parent [PropertyGet] or [MethodInvocation] was created. | |
| // | |
| // For instance when cloning generic mixin methods, the substitution can | |
| // render some of the code paths as dead code: | |
| // | |
| // mixin M<T> { | |
| // int method(T t) => t is String ? t.length : 0; | |
| // } | |
| // class C with M<int> {} | |
| // | |
| // The mixin transformation will clone the `M.method` method into the | |
| // unnamed mixin application for `Object&M<int>` as this: | |
| // | |
| // int method(int t) => t is String ? t.length : 0; | |
| // | |
| // Now `t.length`, which was originally an access to `String.length` on a | |
| // receiver of type `T & String`, is an access to `String.length` on `int`. | |
| // When computing the static type of `t.length` we will try to compute the | |
| // type of `int` as an instance of `String`, and we do not find it to be | |
| // an instance of `String`. | |
| // | |
| // To resolve this case we compute the type of `t.length` to be the type | |
| // as if accessed on an unknown subtype `String`. | |
| return context.typeEnvironment.coreTypes.rawType( | |
| superclass, | |
| context.nonNullable, | |
| ); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg); | |
| int get precedence => astToText.Precedence.of(this); | |
| @override | |
| String toText(AstTextStrategy strategy) { | |
| AstPrinter printer = new AstPrinter(strategy); | |
| printer.writeExpression(this); | |
| return printer.getText(); | |
| } | |
| } | |
| /// Abstract subclass of [Expression] that can be used to add [Expression] | |
| /// subclasses from outside `package:kernel`. | |
| abstract class AuxiliaryExpression extends Expression { | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAuxiliaryExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitAuxiliaryExpression(this, arg); | |
| } | |
| /// An expression containing compile-time errors. | |
| /// | |
| /// Should throw a runtime error when evaluated. | |
| /// | |
| /// The [fileOffset] of an [InvalidExpression] indicates the location in the | |
| /// tree where the expression occurs, rather than the location of the error. | |
| class InvalidExpression extends Expression { | |
| String message; | |
| /// The expression containing the error. | |
| Expression? expression; | |
| new(this.message, [this.expression]) { | |
| expression?.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| const NeverType.nonNullable(); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInvalidExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInvalidExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| expression?.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| if (expression != null) { | |
| expression = v.transform(expression!); | |
| expression?.parent = this; | |
| } | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| if (expression != null) { | |
| expression = v.transformOrRemoveExpression(expression!); | |
| expression?.parent = this; | |
| } | |
| } | |
| @override | |
| String toString() { | |
| return "InvalidExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('<invalid:'); | |
| printer.write(message); | |
| if (expression != null) { | |
| printer.write(', '); | |
| printer.writeExpression(expression!); | |
| } | |
| printer.write('>'); | |
| } | |
| } | |
| class VariableGet extends Expression { | |
| /// The target variable. | |
| Variable variable; | |
| /// Null if not promoted. | |
| DartType? promotedType; | |
| new(this.variable, [this.promotedType]); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return promotedType ?? variable.type; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitVariableGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitVariableGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| promotedType?.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| if (promotedType != null) { | |
| promotedType = v.visitDartType(promotedType!); | |
| } | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| if (promotedType != null) { | |
| DartType newPromotedType = v.visitDartType(promotedType!, dummyDartType); | |
| if (identical(newPromotedType, dummyDartType)) { | |
| promotedType = null; | |
| } else { | |
| promotedType = newPromotedType; | |
| } | |
| } | |
| } | |
| @override | |
| String toString() { | |
| return "VariableGet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write(printer.getVariableName(variable)); | |
| if (promotedType != null) { | |
| printer.write('{'); | |
| printer.writeType(promotedType!); | |
| printer.write('}'); | |
| } | |
| } | |
| } | |
| /// Assign a local variable or function parameter. | |
| /// | |
| /// Evaluates to the value of [value]. | |
| class VariableSet extends Expression { | |
| /// The target variable. | |
| Variable variable; | |
| Expression value; | |
| new(this.variable, this.value) { | |
| value.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitVariableSet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitVariableSet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "VariableSet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write(printer.getVariableName(variable)); | |
| printer.write(' = '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| class RecordIndexGet extends Expression { | |
| Expression receiver; | |
| RecordType receiverType; | |
| final int index; | |
| new(this.receiver, this.receiverType, this.index) | |
| : assert(0 <= index && index < receiverType.positional.length) { | |
| receiver.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| assert(index < receiverType.positional.length); | |
| return receiverType.positional[index]; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRecordIndexGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitRecordIndexGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| receiverType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver)..parent = this; | |
| receiverType = v.visitDartType(receiverType) as RecordType; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver)..parent = this; | |
| receiverType = | |
| v.visitDartType(receiverType, cannotRemoveSentinel) as RecordType; | |
| } | |
| @override | |
| String toString() { | |
| return "RecordIndexGet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression(receiver); | |
| printer.write(".\$${index + 1}"); | |
| } | |
| } | |
| class RecordNameGet extends Expression { | |
| Expression receiver; | |
| RecordType receiverType; | |
| final String name; | |
| new(this.receiver, this.receiverType, this.name) | |
| : assert( | |
| receiverType.named | |
| .singleWhere((element) => element.name == name) | |
| .name == | |
| name, | |
| ) { | |
| receiver.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| DartType? result; | |
| for (NamedType namedType in receiverType.named) { | |
| if (namedType.name == name) { | |
| result = namedType.type; | |
| break; | |
| } | |
| } | |
| assert(result != null); | |
| return result!; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRecordNameGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitRecordNameGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| receiverType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver)..parent = this; | |
| receiverType = v.visitDartType(receiverType) as RecordType; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver)..parent = this; | |
| receiverType = | |
| v.visitDartType(receiverType, cannotRemoveSentinel) as RecordType; | |
| } | |
| @override | |
| String toString() { | |
| return "RecordNameGet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression(receiver); | |
| printer.write(".${name}"); | |
| } | |
| } | |
| enum DynamicAccessKind { | |
| /// An access on a receiver of type dynamic. | |
| /// | |
| /// An access of this kind always results in a value of static type dynamic. | |
| /// | |
| /// Valid accesses to Object members on receivers of type dynamic are encoded | |
| /// as an [InstanceInvocation] of kind [InstanceAccessKind.Object]. | |
| Dynamic, | |
| /// An access on a receiver of type Never. | |
| /// | |
| /// An access of this kind always results in a value of static type Never. | |
| /// | |
| /// Valid accesses to Object members on receivers of type Never are also | |
| /// encoded as [DynamicInvocation] of kind [DynamicAccessKind.Never] and _not_ | |
| /// as an [InstanceInvocation] of kind [InstanceAccessKind.Object]. | |
| Never, | |
| /// An access on a receiver of an invalid type. | |
| /// | |
| /// An access of this kind always results in a value of an invalid static | |
| /// type. | |
| Invalid, | |
| /// An access of an unresolved target. | |
| /// | |
| /// An access of this kind always results in a value of an invalid static | |
| /// type. | |
| Unresolved, | |
| } | |
| class DynamicGet extends Expression { | |
| final DynamicAccessKind kind; | |
| Expression receiver; | |
| Name name; | |
| new(this.kind, this.receiver, this.name) { | |
| receiver.parent = this; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitDynamicGet(this, arg); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| switch (kind) { | |
| case DynamicAccessKind.Dynamic: | |
| return const DynamicType(); | |
| case DynamicAccessKind.Never: | |
| return const NeverType.nonNullable(); | |
| case DynamicAccessKind.Invalid: | |
| case DynamicAccessKind.Unresolved: | |
| return const InvalidType(); | |
| } | |
| } | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| name.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "DynamicGet($kind,${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeName(name); | |
| } | |
| } | |
| /// A property read of an instance getter or field with a statically known | |
| /// interface target. | |
| class InstanceGet extends Expression { | |
| final InstanceAccessKind kind; | |
| Expression receiver; | |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? | |
| Name name; | |
| /// The static type of result of the property read. | |
| /// | |
| /// This includes substituted type parameters from the static receiver type. | |
| /// | |
| /// For instance | |
| /// | |
| /// class A<T> { | |
| /// T get t; | |
| /// } | |
| /// m(A<String> a) { | |
| /// a.t; // The result type is `String`. | |
| /// } | |
| /// | |
| /// Note that [resultType] can be overwritten with a more general type in case | |
| /// the type parameters of either the class or the generic method occur in the | |
| /// return type contravariantly, so that [resultType] is always a supertype of | |
| /// the runtime type of the invocation value. In that case an as-check is | |
| /// generated to make sure the runtime type of the value is the subtype of the | |
| /// return type of the method. If that happens, [resultType] and | |
| /// [getStaticType] yield the type that might be different from the static | |
| /// type of the expression as defined by the language specification. | |
| DartType resultType; | |
| Reference interfaceTargetReference; | |
| new( | |
| InstanceAccessKind kind, | |
| Expression receiver, | |
| Name name, { | |
| required Member interfaceTarget, | |
| required DartType resultType, | |
| }) : this.byReference( | |
| kind, | |
| receiver, | |
| name, | |
| interfaceTargetReference: getNonNullableMemberReferenceGetter( | |
| interfaceTarget, | |
| ), | |
| resultType: resultType, | |
| ); | |
| new byReference( | |
| this.kind, | |
| this.receiver, | |
| this.name, { | |
| required this.interfaceTargetReference, | |
| required this.resultType, | |
| }) { | |
| receiver.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => resultType; | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member member) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(member); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstanceGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| resultType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| resultType = v.visitDartType(resultType); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| resultType = v.visitDartType(resultType, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "InstanceGet($kind,${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| } | |
| } | |
| /// A tear-off of the 'call' method on an expression whose static type is | |
| /// a function type or the type 'Function'. | |
| class FunctionTearOff extends Expression { | |
| Expression receiver; | |
| new(this.receiver) { | |
| receiver.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| receiver.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionTearOff(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitFunctionTearOff(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "FunctionTearOff(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeName(Name.callName); | |
| } | |
| } | |
| /// A tear-off of an instance method with a statically known interface target. | |
| class InstanceTearOff extends Expression { | |
| final InstanceAccessKind kind; | |
| Expression receiver; | |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? | |
| Name name; | |
| /// The static type of result of the tear-off. | |
| /// | |
| /// This includes substituted type parameters from the static receiver type. | |
| /// | |
| /// For instance | |
| /// | |
| /// class A<T, S> { | |
| /// T method<U>(S s, U u) { ... } | |
| /// } | |
| /// m(A<String, int> a) { | |
| /// a.method; // The result type is `String Function<U>(int, U)`. | |
| /// } | |
| /// | |
| /// Note that [resultType] can be overwritten with a more general type in case | |
| /// the type parameters of either the class or the generic method occur in the | |
| /// return type contravariantly, so that [resultType] is always a supertype of | |
| /// the runtime type of the invocation value. In that case an as-check is | |
| /// generated to make sure the runtime type of the value is the subtype of the | |
| /// return type of the method. If that happens, [resultType] and | |
| /// [getStaticType] yield the type that might be different from the static | |
| /// type of the expression as defined by the language specification. | |
| DartType resultType; | |
| Reference interfaceTargetReference; | |
| new( | |
| InstanceAccessKind kind, | |
| Expression receiver, | |
| Name name, { | |
| required Procedure interfaceTarget, | |
| required DartType resultType, | |
| }) : this.byReference( | |
| kind, | |
| receiver, | |
| name, | |
| interfaceTargetReference: getNonNullableMemberReferenceGetter( | |
| interfaceTarget, | |
| ), | |
| resultType: resultType, | |
| ); | |
| new byReference( | |
| this.kind, | |
| this.receiver, | |
| this.name, { | |
| required this.interfaceTargetReference, | |
| required this.resultType, | |
| }) { | |
| receiver.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => resultType; | |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; | |
| void set interfaceTarget(Procedure procedure) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(procedure); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceTearOff(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstanceTearOff(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| resultType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| resultType = v.visitDartType(resultType); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| resultType = v.visitDartType(resultType, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "InstanceTearOff($kind, ${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| } | |
| } | |
| class DynamicSet extends Expression { | |
| final DynamicAccessKind kind; | |
| Expression receiver; | |
| Name name; | |
| Expression value; | |
| new(this.kind, this.receiver, this.name, this.value) { | |
| receiver.parent = this; | |
| value.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicSet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitDynamicSet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| name.accept(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "DynamicSet($kind,${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeName(name); | |
| printer.write(' = '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// An property write of an instance setter or field with a statically known | |
| /// interface target. | |
| class InstanceSet extends Expression { | |
| final InstanceAccessKind kind; | |
| Expression receiver; | |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? | |
| Name name; | |
| Expression value; | |
| Reference interfaceTargetReference; | |
| new( | |
| InstanceAccessKind kind, | |
| Expression receiver, | |
| Name name, | |
| Expression value, { | |
| required Member interfaceTarget, | |
| }) : this.byReference( | |
| kind, | |
| receiver, | |
| name, | |
| value, | |
| interfaceTargetReference: getNonNullableMemberReferenceSetter( | |
| interfaceTarget, | |
| ), | |
| ); | |
| new byReference( | |
| this.kind, | |
| this.receiver, | |
| this.name, | |
| this.value, { | |
| required this.interfaceTargetReference, | |
| }) { | |
| receiver.parent = this; | |
| value.parent = this; | |
| } | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member member) { | |
| interfaceTargetReference = getNonNullableMemberReferenceSetter(member); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceSet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstanceSet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "InstanceSet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.write(' = '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// Expression of form `super.foo` occurring in a mixin declaration. | |
| /// | |
| /// In this setting, the target is looked up on the types in the mixin 'on' | |
| /// clause and are therefore not necessary the runtime targets of the read. An | |
| /// [AbstractSuperPropertyGet] must be converted into a [SuperPropertyGet] to | |
| /// statically bind the target. | |
| /// | |
| /// For instance | |
| /// | |
| /// abstract class Interface { | |
| /// get getter; | |
| /// } | |
| /// mixin Mixin on Interface { | |
| /// get getter { | |
| /// // This is an [AbstractSuperPropertyGet] with interface target | |
| /// // `Interface.getter`. | |
| /// return super.getter; | |
| /// } | |
| /// } | |
| /// class Super implements Interface { | |
| /// // This is the target when `Mixin` is applied to `Class`. | |
| /// get getter => 42; | |
| /// } | |
| /// class Class extends Super with Mixin {} | |
| /// | |
| /// This may invoke a getter, read a field, or tear off a method. | |
| class AbstractSuperPropertyGet extends Expression { | |
| Expression receiver; | |
| Name name; | |
| Reference interfaceTargetReference; | |
| new(Expression receiver, Name name, Member interfaceTarget) | |
| : this.byReference( | |
| receiver, | |
| name, | |
| getNonNullableMemberReferenceGetter(interfaceTarget), | |
| ); | |
| new byReference(this.receiver, this.name, this.interfaceTargetReference) { | |
| receiver.parent = this; | |
| } | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member member) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(member); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| Class declaringClass = interfaceTarget.enclosingClass!; | |
| if (declaringClass.typeParameters.isEmpty) { | |
| return interfaceTarget.getterType; | |
| } | |
| DartTypeList? receiverArguments = context.typeEnvironment | |
| .getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass); | |
| return Substitution.fromPairs( | |
| declaringClass.typeParameters, | |
| receiverArguments!, | |
| ).substituteType(interfaceTarget.getterType); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAbstractSuperPropertyGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitAbstractSuperPropertyGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "AbstractSuperPropertyGet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('super.{abstract}'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| } | |
| } | |
| /// Expression of form `super.field`. | |
| /// | |
| /// This may invoke a getter, read a field, or tear off a method. | |
| class SuperPropertyGet extends Expression { | |
| Expression receiver; | |
| Name name; | |
| Reference interfaceTargetReference; | |
| new(Expression receiver, Name name, Member interfaceTarget) | |
| : this.byReference( | |
| receiver, | |
| name, | |
| getNonNullableMemberReferenceGetter(interfaceTarget), | |
| ); | |
| new byReference(this.receiver, this.name, this.interfaceTargetReference) { | |
| receiver.parent = this; | |
| } | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member member) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(member); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| Class declaringClass = interfaceTarget.enclosingClass!; | |
| if (declaringClass.typeParameters.isEmpty) { | |
| return interfaceTarget.getterType; | |
| } | |
| DartTypeList? receiverArguments = context.typeEnvironment | |
| .getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass); | |
| return Substitution.fromPairs( | |
| declaringClass.typeParameters, | |
| receiverArguments!, | |
| ).substituteType(interfaceTarget.getterType); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertyGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitSuperPropertyGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "SuperPropertyGet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('super.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| } | |
| } | |
| /// Expression of form `super.foo = x` occurring in a mixin declaration. | |
| /// | |
| /// In this setting, the target is looked up on the types in the mixin 'on' | |
| /// clause and are therefore not necessary the runtime targets of the | |
| /// assignment. An [AbstractSuperPropertySet] must be converted into a | |
| /// [SuperPropertySet] to statically bind the target. | |
| /// | |
| /// For instance | |
| /// | |
| /// abstract class Interface { | |
| /// void set setter(value); | |
| /// } | |
| /// mixin Mixin on Interface { | |
| /// void set setter(value) { | |
| /// // This is an [AbstractSuperPropertySet] with interface target | |
| /// // `Interface.setter`. | |
| /// super.setter = value; | |
| /// } | |
| /// } | |
| /// class Super implements Interface { | |
| /// // This is the target when `Mixin` is applied to `Class`. | |
| /// void set setter(value) {} | |
| /// } | |
| /// class Class extends Super with Mixin {} | |
| /// | |
| /// This may invoke a setter or assign a field. | |
| class AbstractSuperPropertySet extends Expression { | |
| Expression receiver; | |
| Name name; | |
| Expression value; | |
| Reference interfaceTargetReference; | |
| new(Expression receiver, Name name, Expression value, Member interfaceTarget) | |
| : this.byReference( | |
| receiver, | |
| name, | |
| value, | |
| getNonNullableMemberReferenceSetter(interfaceTarget), | |
| ); | |
| new byReference( | |
| this.receiver, | |
| this.name, | |
| this.value, | |
| this.interfaceTargetReference, | |
| ) { | |
| receiver.parent = this; | |
| value.parent = this; | |
| } | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member member) { | |
| interfaceTargetReference = getNonNullableMemberReferenceSetter(member); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAbstractSuperPropertySet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitAbstractSuperPropertySet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "AbstractSuperPropertySet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('super.{abstract}'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.write(' = '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// Expression of form `super.field = value`. | |
| /// | |
| /// This may invoke a setter or assign a field. | |
| /// | |
| /// Evaluates to the value of [value]. | |
| class SuperPropertySet extends Expression { | |
| Expression receiver; | |
| Name name; | |
| Expression value; | |
| Reference interfaceTargetReference; | |
| new(Expression receiver, Name name, Expression value, Member interfaceTarget) | |
| : this.byReference( | |
| receiver, | |
| name, | |
| value, | |
| getNonNullableMemberReferenceSetter(interfaceTarget), | |
| ); | |
| new byReference( | |
| this.receiver, | |
| this.name, | |
| this.value, | |
| this.interfaceTargetReference, | |
| ) { | |
| receiver.parent = this; | |
| value.parent = this; | |
| } | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member member) { | |
| interfaceTargetReference = getNonNullableMemberReferenceSetter(member); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertySet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitSuperPropertySet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "SuperPropertySet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('super.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.write(' = '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// Read a static field, call a static getter, or tear off a static method. | |
| class StaticGet extends Expression { | |
| /// A static field, getter, or method (for tear-off). | |
| Reference targetReference; | |
| new(Member target) | |
| : assert(target is Field || (target is Procedure && target.isGetter)), | |
| this.targetReference = getNonNullableMemberReferenceGetter(target); | |
| new byReference(this.targetReference); | |
| Member get target => targetReference.asMember; | |
| void set target(Member target) { | |
| targetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| target.getterType; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticGet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitStaticGet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "StaticGet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeMemberName(targetReference); | |
| } | |
| } | |
| /// Tear-off of a static method. | |
| class StaticTearOff extends Expression { | |
| Reference targetReference; | |
| new(Procedure target) | |
| : assert(target.isStatic, "Unexpected static tear off target: $target"), | |
| assert( | |
| target.kind == ProcedureKind.Method, | |
| "Unexpected static tear off target: $target", | |
| ), | |
| this.targetReference = getNonNullableMemberReferenceGetter(target); | |
| new byReference(this.targetReference); | |
| Procedure get target => targetReference.asProcedure; | |
| void set target(Procedure target) { | |
| targetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| target.getterType; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticTearOff(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitStaticTearOff(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "StaticTearOff(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeMemberName(targetReference); | |
| } | |
| } | |
| /// Assign a static field or call a static setter. | |
| /// | |
| /// Evaluates to the value of [value]. | |
| class StaticSet extends Expression { | |
| /// A mutable static field or a static setter. | |
| Reference targetReference; | |
| Expression value; | |
| new(Member target, Expression value) | |
| : this.byReference(getNonNullableMemberReferenceSetter(target), value); | |
| new byReference(this.targetReference, this.value) { | |
| value.parent = this; | |
| } | |
| Member get target => targetReference.asMember; | |
| void set target(Member target) { | |
| targetReference = getNonNullableMemberReferenceSetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticSet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitStaticSet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "StaticSet(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeMemberName(targetReference); | |
| printer.write(' = '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// The arguments to a function call, divided into type arguments, | |
| /// positional arguments, and named arguments. | |
| class Arguments extends TreeNode { | |
| DartTypeList types; | |
| final ExpressionList positional; | |
| NamedExpressionList named; | |
| new(this.positional, {DartTypeList? types, NamedExpressionList? named}) | |
| : this.types = types ?? DartTypeList.empty, | |
| this.named = named ?? NamedExpressionList.empty { | |
| setParents(this.positional, this); | |
| setParents(this.named, this); | |
| } | |
| new empty() | |
| : types = DartTypeList.empty, | |
| positional = ExpressionList.empty, | |
| named = NamedExpressionList.empty; | |
| factory forwarded(FunctionNode function) { | |
| return new Arguments( | |
| ExpressionList.mapped(function.positionalParameters, VariableGet.new), | |
| named: NamedExpressionList.mapped( | |
| function.namedParameters, | |
| (p) => new NamedExpression(p.parameterName, new VariableGet(p)), | |
| ), | |
| types: DartTypeList.generate( | |
| function.typeParameters.length, | |
| (i) => new TypeParameterType.withDefaultNullability( | |
| function.typeParameters[i], | |
| ), | |
| ), | |
| ); | |
| } | |
| @override | |
| R accept<R>(TreeVisitor<R> v) => v.visitArguments(this); | |
| @override | |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitArguments(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| visitList(types, v); | |
| visitList(positional, v); | |
| visitList(named, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| types = v.transformDartTypeList(types); | |
| v.transformList(positional, this); | |
| v.transformList(named, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| types = v.transformDartTypeList(types); | |
| v.transformExpressionList(positional, this); | |
| v.transformNamedExpressionList(named, this); | |
| } | |
| @override | |
| String toString() { | |
| return "Arguments(${toStringInternal()})"; | |
| } | |
| @override | |
| String toText(AstTextStrategy strategy) { | |
| AstPrinter printer = new AstPrinter(strategy); | |
| printer.writeArguments(this); | |
| return printer.getText(); | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer, {bool includeTypeArguments = true}) { | |
| if (includeTypeArguments) { | |
| printer.writeTypeArguments(types); | |
| } | |
| printer.write('('); | |
| for (int index = 0; index < positional.length; index++) { | |
| if (index > 0) { | |
| printer.write(', '); | |
| } | |
| printer.writeExpression(positional[index]); | |
| } | |
| if (named.isNotEmpty) { | |
| if (positional.isNotEmpty) { | |
| printer.write(', '); | |
| } | |
| for (int index = 0; index < named.length; index++) { | |
| if (index > 0) { | |
| printer.write(', '); | |
| } | |
| printer.writeNamedExpression(named[index]); | |
| } | |
| } | |
| printer.write(')'); | |
| } | |
| } | |
| /// A named argument, `name: value`. | |
| class NamedExpression extends TreeNode { | |
| String name; | |
| Expression value; | |
| new(this.name, this.value) { | |
| value.parent = this; | |
| } | |
| @override | |
| R accept<R>(TreeVisitor<R> v) => v.visitNamedExpression(this); | |
| @override | |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => | |
| v.visitNamedExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "NamedExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| String toText(AstTextStrategy strategy) { | |
| AstPrinter printer = new AstPrinter(strategy); | |
| toTextInternal(printer); | |
| return printer.getText(); | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write(name); | |
| printer.write(': '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// Common super class for [DirectMethodInvocation], [MethodInvocation], | |
| /// [SuperMethodInvocation], [StaticInvocation], and [ConstructorInvocation]. | |
| abstract class InvocationExpression extends Expression { | |
| Arguments get arguments; | |
| /// Name of the invoked method. | |
| Name get name; | |
| } | |
| abstract class InstanceInvocationExpression extends InvocationExpression { | |
| Expression get receiver; | |
| } | |
| class DynamicInvocation extends InstanceInvocationExpression { | |
| // Must match serialized bit positions. | |
| static const int FlagImplicitCall = 1 << 0; | |
| final DynamicAccessKind kind; | |
| @override | |
| Expression receiver; | |
| @override | |
| Name name; | |
| @override | |
| Arguments arguments; | |
| int flags = 0; | |
| new(this.kind, this.receiver, this.name, this.arguments) { | |
| receiver.parent = this; | |
| arguments.parent = this; | |
| } | |
| /// If `true` this is an implicit call to 'call'. For instance | |
| /// | |
| /// method(dynamic d) { | |
| /// d(); // Implicit call. | |
| /// d.call(); // Explicit call. | |
| /// | |
| bool get isImplicitCall => flags & FlagImplicitCall != 0; | |
| void set isImplicitCall(bool value) { | |
| flags = value ? (flags | FlagImplicitCall) : (flags & ~FlagImplicitCall); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| switch (kind) { | |
| case DynamicAccessKind.Dynamic: | |
| return const DynamicType(); | |
| case DynamicAccessKind.Never: | |
| return const NeverType.nonNullable(); | |
| case DynamicAccessKind.Invalid: | |
| case DynamicAccessKind.Unresolved: | |
| return const InvalidType(); | |
| } | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitDynamicInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| name.accept(v); | |
| arguments.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "DynamicInvocation($kind,${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| if (!isImplicitCall) { | |
| printer.write('.'); | |
| printer.writeName(name); | |
| } | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// Access kind used by [InstanceInvocation], [InstanceGet], [InstanceSet], | |
| /// and [InstanceTearOff]. | |
| enum InstanceAccessKind { | |
| /// An access to a member on a static receiver type which is an interface | |
| /// type. | |
| /// | |
| /// In null safe libraries the static receiver type is non-nullable. | |
| /// | |
| /// For instance: | |
| /// | |
| /// class C { void method() {} } | |
| /// main() => new C().method(); | |
| /// | |
| Instance, | |
| /// An access to a member defined on Object on a static receiver type that | |
| /// is either a non-interface type or a nullable type. | |
| /// | |
| /// For instance: | |
| /// | |
| /// test1(String? s) => s.toString(); | |
| /// test1(dynamic s) => s.hashCode; | |
| /// | |
| Object, | |
| /// An access to a method on a static receiver type which is an interface | |
| /// type which is inapplicable, that is, whose arguments don't match the | |
| /// required parameter structure. | |
| /// | |
| /// This is an error case which is only used on expression nested within | |
| /// [InvalidExpression]s. | |
| /// | |
| /// For instance: | |
| /// | |
| /// class C { void method() {} } | |
| /// main() => new C().method(0); // Too many arguments. | |
| /// | |
| Inapplicable, | |
| /// An access to a non-Object member on a static receiver type which is a | |
| /// nullable interface type. | |
| /// | |
| /// This is an error case which is only used on expression nested within | |
| /// [InvalidExpression]s. | |
| /// | |
| /// For instance: | |
| /// | |
| /// class C { void method() {} } | |
| /// test(C? c) => c.method(0); // 'c' is nullable. | |
| /// | |
| Nullable, | |
| } | |
| /// An invocation of an instance method with a statically known interface | |
| /// target. | |
| class InstanceInvocation extends InstanceInvocationExpression { | |
| // Must match serialized bit positions. | |
| static const int FlagInvariant = 1 << 0; | |
| static const int FlagBoundsSafe = 1 << 1; | |
| final InstanceAccessKind kind; | |
| @override | |
| Expression receiver; | |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? | |
| @override | |
| Name name; | |
| @override | |
| Arguments arguments; | |
| int flags = 0; | |
| /// The static type of the invocation. | |
| /// | |
| /// This includes substituted type parameters from the static receiver type | |
| /// and generic type arguments. | |
| /// | |
| /// For instance | |
| /// | |
| /// class A<T> { | |
| /// Map<T, S> map<S>(S s) { ... } | |
| /// } | |
| /// m(A<String> a) { | |
| /// a.map(0); // The function type is `Map<String, int> Function(int)`. | |
| /// } | |
| /// | |
| FunctionType functionType; | |
| /// The static type of the result of the invocation. | |
| /// | |
| /// This includes substituted type parameters from the static type of the | |
| /// receiver and the generic type arguments. | |
| /// | |
| /// For instance | |
| /// | |
| /// class A<T> { | |
| /// Map<T, S> map<S>(S s) { ... } | |
| /// } | |
| /// m(A<String> a) { | |
| /// a.map(0); // The result type is `Map<String, int>`. | |
| /// } | |
| /// | |
| /// Note that [resultType] can be overwritten with a more general type in case | |
| /// the type parameters of either the class or the generic method occur in the | |
| /// return type contravariantly, so that [resultType] is always a supertype of | |
| /// the runtime type of the invocation value. In that case an as-check is | |
| /// generated to make sure the runtime type of the value is the subtype of the | |
| /// return type of the method. If that happens, [resultType] and | |
| /// [getStaticType] yield the type that might be different from the static | |
| /// type of the expression as defined by the language specification. | |
| DartType resultType; | |
| Reference interfaceTargetReference; | |
| new( | |
| InstanceAccessKind kind, | |
| Expression receiver, | |
| Name name, | |
| Arguments arguments, { | |
| required Procedure interfaceTarget, | |
| required FunctionType functionType, | |
| DartType? resultType, | |
| }) : this.byReference( | |
| kind, | |
| receiver, | |
| name, | |
| arguments, | |
| interfaceTargetReference: getNonNullableMemberReferenceGetter( | |
| interfaceTarget, | |
| ), | |
| functionType: functionType, | |
| resultType: resultType, | |
| ); | |
| new byReference( | |
| this.kind, | |
| this.receiver, | |
| this.name, | |
| this.arguments, { | |
| required this.interfaceTargetReference, | |
| required this.functionType, | |
| DartType? resultType, | |
| }) : resultType = resultType ?? functionType.returnType, | |
| assert(functionType.typeParameters.isEmpty) { | |
| receiver.parent = this; | |
| arguments.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => resultType; | |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; | |
| void set interfaceTarget(Procedure target) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. | |
| /// | |
| /// This is for instance the case in code patterns like this | |
| /// | |
| /// List<int> list = <int>[]; | |
| /// list.add(0); | |
| /// | |
| /// where the `list` variable is known to hold a value of the same type as | |
| /// the static type. In contrast the would not be the case in code patterns | |
| /// like this | |
| /// | |
| /// List<num> list = <double>[]; | |
| /// list.add(0); // Runtime error `int` is not a subtype of `double`. | |
| /// | |
| bool get isInvariant => flags & FlagInvariant != 0; | |
| void set isInvariant(bool value) { | |
| flags = value ? (flags | FlagInvariant) : (flags & ~FlagInvariant); | |
| } | |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. | |
| /// | |
| /// This is for instance the case in code patterns like this | |
| /// | |
| /// List list = new List.filled(2, 0); | |
| /// list[1] = 42; | |
| /// | |
| /// where the `list` is known to have a sufficient length for the update | |
| /// in `list[1] = 42`. | |
| bool get isBoundsSafe => flags & FlagBoundsSafe != 0; | |
| void set isBoundsSafe(bool value) { | |
| flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstanceInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| arguments.accept(v); | |
| functionType.accept(v); | |
| resultType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| functionType = v.visitDartType(functionType) as FunctionType; | |
| resultType = v.visitDartType(resultType); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| functionType = | |
| v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType; | |
| resultType = v.visitDartType(resultType, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "InstanceInvocation($kind, ${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// An invocation of an instance getter or field with a statically known | |
| /// interface target. | |
| /// | |
| /// This is used only for web backend in order to support invocation of | |
| /// native properties as functions. This node will be removed when this | |
| /// invocation style is no longer supported. | |
| class InstanceGetterInvocation extends InstanceInvocationExpression { | |
| // Must match serialized bit positions. | |
| static const int FlagInvariant = 1 << 0; | |
| static const int FlagBoundsSafe = 1 << 1; | |
| final InstanceAccessKind kind; | |
| @override | |
| Expression receiver; | |
| @override | |
| Name name; | |
| @override | |
| Arguments arguments; | |
| int flags = 0; | |
| /// The static type of the invocation, or `dynamic` is of the type is unknown. | |
| /// | |
| /// This includes substituted type parameters from the static receiver type | |
| /// and generic type arguments. | |
| /// | |
| /// For instance | |
| /// | |
| /// class A<T> { | |
| /// Map<T, S> Function<S>(S) get map => ... | |
| /// dynamic get dyn => ... | |
| /// } | |
| /// m(A<String> a) { | |
| /// a.map(0); // The function type is `Map<String, int> Function(int)`. | |
| /// a.dyn(0); // The function type is `null`. | |
| /// } | |
| /// | |
| FunctionType? functionType; | |
| Reference interfaceTargetReference; | |
| new( | |
| InstanceAccessKind kind, | |
| Expression receiver, | |
| Name name, | |
| Arguments arguments, { | |
| required Member interfaceTarget, | |
| required FunctionType? functionType, | |
| }) : this.byReference( | |
| kind, | |
| receiver, | |
| name, | |
| arguments, | |
| interfaceTargetReference: getNonNullableMemberReferenceGetter( | |
| interfaceTarget, | |
| ), | |
| functionType: functionType, | |
| ); | |
| new byReference( | |
| this.kind, | |
| this.receiver, | |
| this.name, | |
| this.arguments, { | |
| required this.interfaceTargetReference, | |
| required this.functionType, | |
| }) : assert(functionType == null || functionType.typeParameters.isEmpty) { | |
| receiver.parent = this; | |
| arguments.parent = this; | |
| } | |
| Member get interfaceTarget => interfaceTargetReference.asMember; | |
| void set interfaceTarget(Member target) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. | |
| /// | |
| /// This is for instance the case in code patterns like this | |
| /// | |
| /// List<int> list = <int>[]; | |
| /// list.add(0); | |
| /// | |
| /// where the `list` variable is known to hold a value of the same type as | |
| /// the static type. In contrast the would not be the case in code patterns | |
| /// like this | |
| /// | |
| /// List<num> list = <double>[]; | |
| /// list.add(0); // Runtime error `int` is not a subtype of `double`. | |
| /// | |
| bool get isInvariant => flags & FlagInvariant != 0; | |
| void set isInvariant(bool value) { | |
| flags = value ? (flags | FlagInvariant) : (flags & ~FlagInvariant); | |
| } | |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. | |
| /// | |
| /// This is for instance the case in code patterns like this | |
| /// | |
| /// List list = new List.filled(2, 0); | |
| /// list[1] = 42; | |
| /// | |
| /// where the `list` is known to have a sufficient length for the update | |
| /// in `list[1] = 42`. | |
| bool get isBoundsSafe => flags & FlagBoundsSafe != 0; | |
| void set isBoundsSafe(bool value) { | |
| flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| functionType?.returnType ?? const DynamicType(); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGetterInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstanceGetterInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| arguments.accept(v); | |
| functionType?.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| if (functionType != null) { | |
| functionType = v.visitDartType(functionType!) as FunctionType; | |
| } | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| if (functionType != null) { | |
| functionType = | |
| v.visitDartType(functionType!, cannotRemoveSentinel) as FunctionType; | |
| } | |
| } | |
| @override | |
| String toString() { | |
| return "InstanceGetterInvocation($kind, ${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.write('.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// Access kind used by [FunctionInvocation] and [FunctionTearOff]. | |
| enum FunctionAccessKind { | |
| /// An access to the 'call' method on an expression of static type `Function`. | |
| /// | |
| /// For instance | |
| /// | |
| /// method(Function f) => f(); | |
| /// | |
| Function, | |
| /// An access to the 'call' method on an expression whose static type is a | |
| /// function type. | |
| /// | |
| /// For instance | |
| /// | |
| /// method(void Function() f) => f(); | |
| /// | |
| FunctionType, | |
| /// An access to the 'call' method on an expression whose static type is a | |
| /// function type which is inapplicable, that is, whose arguments don't match | |
| /// the required parameter structure. | |
| /// | |
| /// This is an error case which is only used on expression nested within | |
| /// [InvalidExpression]s. | |
| /// | |
| /// For instance: | |
| /// | |
| /// test(void Function() f) => f(0); // Too many arguments. | |
| /// | |
| Inapplicable, | |
| /// An access to the 'call' method on an expression whose static type is a | |
| /// nullable function type or `Function?`. | |
| /// | |
| /// This is an error case which is only used on expression nested within | |
| /// [InvalidExpression]s. | |
| /// | |
| /// For instance: | |
| /// | |
| /// test(void Function()? f) => f(); // 'f' is nullable. | |
| /// | |
| Nullable, | |
| } | |
| /// An invocation of the 'call' method on an expression whose static type is | |
| /// a function type or the type 'Function'. | |
| class FunctionInvocation extends InstanceInvocationExpression { | |
| final FunctionAccessKind kind; | |
| @override | |
| Expression receiver; | |
| @override | |
| Arguments arguments; | |
| /// The static type of the invocation. | |
| /// | |
| /// This is `null` if the static type of the receiver is not a function type | |
| /// or is not bounded by a function type. | |
| /// | |
| /// For instance | |
| /// | |
| /// m<T extends Function, S extends int Function()>(T t, S s, Function f) { | |
| /// X local<X>(X t) => t; | |
| /// t(); // The function type is `null`. | |
| /// s(); // The function type is `int Function()`. | |
| /// f(); // The function type is `null`. | |
| /// local(0); // The function type is `int Function(int)`. | |
| /// } | |
| /// | |
| FunctionType? functionType; | |
| new(this.kind, this.receiver, this.arguments, {required this.functionType}) { | |
| receiver.parent = this; | |
| arguments.parent = this; | |
| } | |
| @override | |
| Name get name => Name.callName; | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| functionType?.returnType ?? const DynamicType(); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitFunctionInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| name.accept(v); | |
| arguments.accept(v); | |
| functionType?.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| FunctionType? type = functionType; | |
| if (type != null) { | |
| functionType = v.visitDartType(type) as FunctionType; | |
| } | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| FunctionType? type = functionType; | |
| if (type != null) { | |
| functionType = | |
| v.visitDartType(type, cannotRemoveSentinel) as FunctionType; | |
| } | |
| } | |
| @override | |
| String toString() { | |
| return "FunctionInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| receiver, | |
| minimumPrecedence: astToText.Precedence.PRIMARY, | |
| ); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// An invocation of a local function declaration. | |
| class LocalFunctionInvocation extends InvocationExpression { | |
| /// The variable declaration for the function declaration. | |
| LocalFunctionVariable variable; | |
| @override | |
| Arguments arguments; | |
| /// The static type of the invocation. | |
| /// | |
| /// This might differ from the static type of [variable] for generic | |
| /// functions. | |
| /// | |
| /// For instance | |
| /// | |
| /// m() { | |
| /// T local<T>(T t) => t; | |
| /// local(0); // The static type is `int Function(int)`. | |
| /// } | |
| /// | |
| FunctionType functionType; | |
| new(this.variable, this.arguments, {required this.functionType}) { | |
| arguments.parent = this; | |
| } | |
| /// The declaration for the invoked local function. | |
| FunctionDeclaration get localFunction => | |
| variable.parent as FunctionDeclaration; | |
| @override | |
| Name get name => Name.callName; | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| functionType.returnType; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLocalFunctionInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitLocalFunctionInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| arguments.accept(v); | |
| functionType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| functionType = v.visitDartType(functionType) as FunctionType; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| functionType = | |
| v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType; | |
| } | |
| @override | |
| String toString() { | |
| return "LocalFunctionInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write(printer.getVariableName(variable)); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// Nullness test of an expression, that is `e == null`. | |
| /// | |
| /// This is generated for code like `e1 == e2` where `e1` or `e2` is `null`. | |
| class EqualsNull extends Expression { | |
| /// The expression tested for nullness. | |
| Expression expression; | |
| new(this.expression) { | |
| expression.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsNull(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitEqualsNull(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| expression.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "EqualsNull(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression(expression, minimumPrecedence: precedence); | |
| printer.write(' == null'); | |
| } | |
| } | |
| /// A test of equality, that is `e1 == e2`. | |
| /// | |
| /// This is generated for code like `e1 == e2` where neither `e1` nor `e2` is | |
| /// `null`. | |
| class EqualsCall extends Expression { | |
| Expression left; | |
| Expression right; | |
| /// The static type of the invocation. | |
| /// | |
| /// This might differ from the static type of [Object.==] for covariant | |
| /// parameters. | |
| /// | |
| /// For instance | |
| /// | |
| /// class C<T> { | |
| /// bool operator(covariant C<T> other) { ... } | |
| /// } | |
| /// // The function type is `bool Function(C<num>)`. | |
| /// method(C<num> a, C<int> b) => a == b; | |
| /// | |
| FunctionType functionType; | |
| Reference interfaceTargetReference; | |
| new( | |
| Expression left, | |
| Expression right, { | |
| required FunctionType functionType, | |
| required Procedure interfaceTarget, | |
| }) : this.byReference( | |
| left, | |
| right, | |
| functionType: functionType, | |
| interfaceTargetReference: getNonNullableMemberReferenceGetter( | |
| interfaceTarget, | |
| ), | |
| ); | |
| new byReference( | |
| this.left, | |
| this.right, { | |
| required this.functionType, | |
| required this.interfaceTargetReference, | |
| }) { | |
| left.parent = this; | |
| right.parent = this; | |
| } | |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; | |
| void set interfaceTarget(Procedure target) { | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return functionType.returnType; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsCall(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitEqualsCall(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| left.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| right.accept(v); | |
| functionType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| left = v.transform(left); | |
| left.parent = this; | |
| right = v.transform(right); | |
| right.parent = this; | |
| functionType = v.visitDartType(functionType) as FunctionType; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| left = v.transform(left); | |
| left.parent = this; | |
| right = v.transform(right); | |
| right.parent = this; | |
| functionType = | |
| v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType; | |
| } | |
| @override | |
| String toString() { | |
| return "EqualsCall(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| int minimumPrecedence = precedence; | |
| printer.writeExpression(left, minimumPrecedence: minimumPrecedence); | |
| printer.write(' == '); | |
| printer.writeExpression(right, minimumPrecedence: minimumPrecedence + 1); | |
| } | |
| } | |
| /// Expression of form `super.foo(x)` occurring in a mixin declaration. | |
| /// | |
| /// In this setting, the target is looked up on the types in the mixin 'on' | |
| /// clause and are therefore not necessary the runtime targets of the | |
| /// invocation. An [AbstractSuperMethodInvocation] must be converted into | |
| /// a [SuperMethodInvocation] to statically bind the target. | |
| /// | |
| /// For instance | |
| /// | |
| /// abstract class Interface { | |
| /// void method(); | |
| /// } | |
| /// mixin Mixin on Interface { | |
| /// void method() { | |
| /// // This is an [AbstractSuperMethodInvocation] with interface target | |
| /// // `Interface.method`. | |
| /// super.method(); // This targets Super.method. | |
| /// } | |
| /// } | |
| /// class Super implements Interface { | |
| /// // This is the target when `Mixin` is applied to `Class`. | |
| /// void method() {} | |
| /// } | |
| /// class Class extends Super with Mixin {} | |
| /// | |
| class AbstractSuperMethodInvocation extends InvocationExpression { | |
| Expression receiver; | |
| @override | |
| Name name; | |
| @override | |
| Arguments arguments; | |
| Reference interfaceTargetReference; | |
| new( | |
| Expression receiver, | |
| Name name, | |
| Arguments arguments, | |
| Procedure interfaceTarget, | |
| ) : this.byReference( | |
| receiver, | |
| name, | |
| arguments, | |
| // An invocation doesn't refer to the setter. | |
| getNonNullableMemberReferenceGetter(interfaceTarget), | |
| ); | |
| new byReference( | |
| this.receiver, | |
| this.name, | |
| this.arguments, | |
| this.interfaceTargetReference, | |
| ) { | |
| receiver.parent = this; | |
| arguments.parent = this; | |
| } | |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; | |
| void set interfaceTarget(Procedure target) { | |
| // An invocation doesn't refer to the setter. | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| Class superclass = interfaceTarget.enclosingClass!; | |
| DartTypeList? receiverTypeArguments = context.typeEnvironment | |
| .getTypeArgumentsAsInstanceOf(context.thisType!, superclass); | |
| DartType returnType = Substitution.fromPairs( | |
| superclass.typeParameters, | |
| receiverTypeArguments!, | |
| ).substituteType(interfaceTarget.function.returnType); | |
| return Substitution.fromPairs( | |
| interfaceTarget.function.typeParameters, | |
| arguments.types, | |
| ).substituteType(returnType); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => | |
| v.visitAbstractSuperMethodInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitAbstractSuperMethodInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| arguments.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "AbstractSuperMethodInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('super.{abstract}'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// Expression of form `super.foo(x)`. | |
| /// | |
| /// The provided arguments might not match the parameters of the target. | |
| class SuperMethodInvocation extends InvocationExpression { | |
| Expression receiver; | |
| @override | |
| Name name; | |
| @override | |
| Arguments arguments; | |
| Reference interfaceTargetReference; | |
| new( | |
| Expression receiver, | |
| Name name, | |
| Arguments arguments, | |
| Procedure interfaceTarget, | |
| ) : this.byReference( | |
| receiver, | |
| name, | |
| arguments, | |
| // An invocation doesn't refer to the setter. | |
| getNonNullableMemberReferenceGetter(interfaceTarget), | |
| ); | |
| new byReference( | |
| this.receiver, | |
| this.name, | |
| this.arguments, | |
| this.interfaceTargetReference, | |
| ) { | |
| receiver.parent = this; | |
| arguments.parent = this; | |
| } | |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; | |
| void set interfaceTarget(Procedure target) { | |
| // An invocation doesn't refer to the setter. | |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| Class superclass = interfaceTarget.enclosingClass!; | |
| DartTypeList? receiverTypeArguments = context.typeEnvironment | |
| .getTypeArgumentsAsInstanceOf(context.thisType!, superclass); | |
| DartType returnType = Substitution.fromPairs( | |
| superclass.typeParameters, | |
| receiverTypeArguments!, | |
| ).substituteType(interfaceTarget.function.returnType); | |
| return Substitution.fromPairs( | |
| interfaceTarget.function.typeParameters, | |
| arguments.types, | |
| ).substituteType(returnType); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSuperMethodInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitSuperMethodInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| receiver.accept(v); | |
| interfaceTarget.acceptReference(v); | |
| name.accept(v); | |
| arguments.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| receiver = v.transform(receiver); | |
| receiver.parent = this; | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "SuperMethodInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('super.'); | |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// Expression of form `foo(x)`, or `const foo(x)` if the target is an | |
| /// external constant factory. | |
| /// | |
| /// The provided arguments might not match the parameters of the target. | |
| class StaticInvocation extends InvocationExpression { | |
| Reference targetReference; | |
| @override | |
| Arguments arguments; | |
| /// True if this is a constant call to an external constant factory. | |
| bool isConst; | |
| @override | |
| Name get name => target.name; | |
| new(Procedure target, Arguments arguments, {bool isConst = false}) | |
| : this.byReference( | |
| // An invocation doesn't refer to the setter. | |
| getNonNullableMemberReferenceGetter(target), | |
| arguments, | |
| isConst: isConst, | |
| ); | |
| new byReference( | |
| this.targetReference, | |
| this.arguments, { | |
| this.isConst = false, | |
| }) { | |
| arguments.parent = this; | |
| } | |
| Procedure get target => targetReference.asProcedure; | |
| void set target(Procedure target) { | |
| // An invocation doesn't refer to the setter. | |
| targetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return Substitution.fromPairs( | |
| target.function.typeParameters, | |
| arguments.types, | |
| ).substituteType(target.function.returnType); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitStaticInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| arguments.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "StaticInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeMemberName(targetReference); | |
| printer.writeArguments(arguments); | |
| } | |
| } | |
| /// Expression of form `new Foo(x)` or `const Foo(x)`. | |
| /// | |
| /// The provided arguments might not match the parameters of the target. | |
| // | |
| // DESIGN TODO: Should we pass type arguments in a separate field | |
| // `classTypeArguments`? They are quite different from type arguments to | |
| // generic functions. | |
| class ConstructorInvocation extends InvocationExpression { | |
| Reference targetReference; | |
| @override | |
| Arguments arguments; | |
| bool isConst; | |
| @override | |
| Name get name => target.name; | |
| new(Constructor target, Arguments arguments, {bool isConst = false}) | |
| : this.byReference( | |
| // A constructor doesn't refer to the setter. | |
| getNonNullableMemberReferenceGetter(target), | |
| arguments, | |
| isConst: isConst, | |
| ); | |
| new byReference( | |
| this.targetReference, | |
| this.arguments, { | |
| this.isConst = false, | |
| }) { | |
| arguments.parent = this; | |
| } | |
| Constructor get target => targetReference.asConstructor; | |
| void set target(Constructor target) { | |
| // A constructor doesn't refer to the setter. | |
| targetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return arguments.types.isEmpty | |
| ? context.typeEnvironment.coreTypes.rawType( | |
| target.enclosingClass, | |
| context.nonNullable, | |
| ) | |
| : new InterfaceType( | |
| target.enclosingClass, | |
| context.nonNullable, | |
| arguments.types, | |
| ); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitConstructorInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| arguments.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| arguments = v.transform(arguments); | |
| arguments.parent = this; | |
| } | |
| // TODO(cstefantsova): Change the getter into a method that accepts a | |
| // CoreTypes. | |
| InterfaceType get constructedType { | |
| Class enclosingClass = target.enclosingClass; | |
| // TODO(cstefantsova): Get raw type from a CoreTypes object if arguments is | |
| // empty. | |
| return new InterfaceType( | |
| enclosingClass, | |
| target.enclosingLibrary.nonNullable, | |
| arguments.types, | |
| ); | |
| } | |
| @override | |
| String toString() { | |
| return "ConstructorInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| if (isConst) { | |
| printer.write('const '); | |
| } else { | |
| printer.write('new '); | |
| } | |
| printer.writeClassName(target.enclosingClass.reference); | |
| printer.writeTypeArguments(arguments.types); | |
| if (target.name.text.isNotEmpty) { | |
| printer.write('.'); | |
| printer.write(target.name.text); | |
| } | |
| printer.writeArguments(arguments, includeTypeArguments: false); | |
| } | |
| } | |
| /// Invocation of a redirecting factory constructor. | |
| /// | |
| /// This wraps the actual invocation [expression] with the original redirecting | |
| /// factory target. This node is available only in outlines and during | |
| /// pre-modular transformation, and is replaced with [expression] during | |
| /// constant evaluation. | |
| class RedirectingFactoryInvocation extends Expression { | |
| /// The original target of the redirecting factory invocation. | |
| Reference redirectingFactoryTargetReference; | |
| /// The invocation of the effective target. | |
| InvocationExpression expression; | |
| factory(Procedure redirectingFactoryTarget, InvocationExpression expression) { | |
| assert(redirectingFactoryTarget.isRedirectingFactory); | |
| return new RedirectingFactoryInvocation.byReference( | |
| redirectingFactoryTarget.reference, | |
| expression, | |
| ); | |
| } | |
| new byReference(this.redirectingFactoryTargetReference, this.expression) { | |
| expression.parent = this; | |
| } | |
| Procedure get redirectingFactoryTarget => | |
| redirectingFactoryTargetReference.asProcedure; | |
| void set redirectingFactoryTarget(Procedure value) { | |
| assert(redirectingFactoryTarget.isRedirectingFactory); | |
| redirectingFactoryTargetReference = value.reference; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return expression.getStaticTypeInternal(context); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => | |
| v.visitRedirectingFactoryInvocation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitRedirectingFactoryInvocation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| redirectingFactoryTarget.acceptReference(v); | |
| expression.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "RedirectingFactoryInvocation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('/*original='); | |
| printer.writeMemberName(redirectingFactoryTargetReference); | |
| printer.write('*/'); | |
| expression.toTextInternal(printer); | |
| } | |
| } | |
| /// An explicit type instantiation of a generic function. | |
| class Instantiation extends Expression { | |
| Expression expression; | |
| DartTypeList typeArguments; | |
| new(this.expression, this.typeArguments) { | |
| expression.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| DartType type = expression.getStaticType(context); | |
| if (type is FunctionType) { | |
| return FunctionTypeInstantiator.instantiate(type, typeArguments); | |
| } | |
| assert( | |
| type is InvalidType || type is NeverType, | |
| "Unexpected operand type $type for $expression", | |
| ); | |
| return type; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstantiation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstantiation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| expression.accept(v); | |
| visitList(typeArguments, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| typeArguments = v.transformDartTypeList(typeArguments); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| typeArguments = v.transformDartTypeList(typeArguments); | |
| } | |
| @override | |
| String toString() { | |
| return "Instantiation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression(expression); | |
| printer.writeTypeArguments(typeArguments); | |
| } | |
| } | |
| /// Expression of form `!x`. | |
| /// | |
| /// The `is!` and `!=` operators are desugared into [Not] nodes with `is` and | |
| /// `==` expressions inside, respectively. | |
| class Not extends Expression { | |
| Expression operand; | |
| new(this.operand) { | |
| operand.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitNot(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitNot(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| operand.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "Not(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('!'); | |
| printer.writeExpression( | |
| operand, | |
| minimumPrecedence: astToText.Precedence.PREFIX, | |
| ); | |
| } | |
| } | |
| enum LogicalExpressionOperator { AND, OR } | |
| String logicalExpressionOperatorToString(LogicalExpressionOperator operator) { | |
| switch (operator) { | |
| case LogicalExpressionOperator.AND: | |
| return "&&"; | |
| case LogicalExpressionOperator.OR: | |
| return "||"; | |
| } | |
| } | |
| /// Expression of form `x && y` or `x || y` | |
| class LogicalExpression extends Expression { | |
| Expression left; | |
| LogicalExpressionOperator operatorEnum; // AND (&&) or OR (||). | |
| Expression right; | |
| new(this.left, this.operatorEnum, this.right) { | |
| left.parent = this; | |
| right.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLogicalExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitLogicalExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| left.accept(v); | |
| right.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| left = v.transform(left); | |
| left.parent = this; | |
| right = v.transform(right); | |
| right.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| left = v.transform(left); | |
| left.parent = this; | |
| right = v.transform(right); | |
| right.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "LogicalExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| int minimumPrecedence = precedence; | |
| printer.writeExpression(left, minimumPrecedence: minimumPrecedence); | |
| printer.write(' ${logicalExpressionOperatorToString(operatorEnum)} '); | |
| printer.writeExpression(right, minimumPrecedence: minimumPrecedence + 1); | |
| } | |
| } | |
| /// Expression of form `x ? y : z`. | |
| class ConditionalExpression extends Expression { | |
| Expression condition; | |
| Expression then; | |
| Expression otherwise; | |
| /// The static type of the expression. | |
| DartType staticType; | |
| new(this.condition, this.then, this.otherwise, this.staticType) { | |
| condition.parent = this; | |
| then.parent = this; | |
| otherwise.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => staticType; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConditionalExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitConditionalExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| condition.accept(v); | |
| then.accept(v); | |
| otherwise.accept(v); | |
| staticType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| condition = v.transform(condition); | |
| condition.parent = this; | |
| then = v.transform(then); | |
| then.parent = this; | |
| otherwise = v.transform(otherwise); | |
| otherwise.parent = this; | |
| staticType = v.visitDartType(staticType); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| condition = v.transform(condition); | |
| condition.parent = this; | |
| then = v.transform(then); | |
| then.parent = this; | |
| otherwise = v.transform(otherwise); | |
| otherwise.parent = this; | |
| staticType = v.visitDartType(staticType, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "ConditionalExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| condition, | |
| minimumPrecedence: astToText.Precedence.LOGICAL_OR, | |
| ); | |
| printer.write(' ?'); | |
| printer.write('{'); | |
| printer.writeType(staticType); | |
| printer.write('}'); | |
| printer.write(' '); | |
| printer.writeExpression(then); | |
| printer.write(' : '); | |
| printer.writeExpression(otherwise); | |
| } | |
| } | |
| /// Convert expressions to strings and concatenate them. Semantically, calls | |
| /// `toString` on every argument, checks that a string is returned, and returns | |
| /// the concatenation of all the strings. | |
| /// | |
| /// If [expressions] is empty then an empty string is returned. | |
| /// | |
| /// These arise from string interpolations and adjacent string literals. | |
| class StringConcatenation extends Expression { | |
| final ExpressionList expressions; | |
| new(this.expressions) { | |
| setParents(expressions, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.stringRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStringConcatenation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitStringConcatenation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| visitList(expressions, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| v.transformList(expressions, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| v.transformExpressionList(expressions, this); | |
| } | |
| @override | |
| String toString() { | |
| return "StringConcatenation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('"'); | |
| for (Expression part in expressions) { | |
| if (part is StringLiteral) { | |
| printer.write(escapeString(part.value)); | |
| } else { | |
| printer.write(r'${'); | |
| printer.writeExpression(part); | |
| printer.write('}'); | |
| } | |
| } | |
| printer.write('"'); | |
| } | |
| } | |
| /// Concatenate lists into a single list. | |
| /// | |
| /// If [lists] is empty then an empty list is returned. | |
| /// | |
| /// These arise from spread and control-flow elements in const list literals. | |
| /// They are only present before constant evaluation, or within unevaluated | |
| /// constants in constant expressions. | |
| class ListConcatenation extends Expression { | |
| DartType typeArgument; | |
| final ExpressionList lists; | |
| new(this.lists, {this.typeArgument = const DynamicType()}) { | |
| setParents(lists, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.listType(typeArgument, context.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitListConcatenation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitListConcatenation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| typeArgument.accept(v); | |
| visitList(lists, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| typeArgument = v.visitDartType(typeArgument); | |
| v.transformList(lists, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); | |
| v.transformExpressionList(lists, this); | |
| } | |
| @override | |
| String toString() { | |
| return "ListConcatenation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| bool first = true; | |
| for (Expression part in lists) { | |
| if (!first) { | |
| printer.write(' + '); | |
| } | |
| printer.writeExpression(part); | |
| first = false; | |
| } | |
| } | |
| } | |
| /// Concatenate sets into a single set. | |
| /// | |
| /// If [sets] is empty then an empty set is returned. | |
| /// | |
| /// These arise from spread and control-flow elements in const set literals. | |
| /// They are only present before constant evaluation, or within unevaluated | |
| /// constants in constant expressions. | |
| /// | |
| /// Duplicated values in or across the sets will result in a compile-time error | |
| /// during constant evaluation. | |
| class SetConcatenation extends Expression { | |
| DartType typeArgument; | |
| final ExpressionList sets; | |
| new(this.sets, {this.typeArgument = const DynamicType()}) { | |
| setParents(sets, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.setType(typeArgument, context.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSetConcatenation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitSetConcatenation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| typeArgument.accept(v); | |
| visitList(sets, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| typeArgument = v.visitDartType(typeArgument); | |
| v.transformList(sets, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); | |
| v.transformExpressionList(sets, this); | |
| } | |
| @override | |
| String toString() { | |
| return "SetConcatenation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| bool first = true; | |
| for (Expression part in sets) { | |
| if (!first) { | |
| printer.write(' + '); | |
| } | |
| printer.writeExpression(part); | |
| first = false; | |
| } | |
| } | |
| } | |
| /// Concatenate maps into a single map. | |
| /// | |
| /// If [maps] is empty then an empty map is returned. | |
| /// | |
| /// These arise from spread and control-flow elements in const map literals. | |
| /// They are only present before constant evaluation, or within unevaluated | |
| /// constants in constant expressions. | |
| /// | |
| /// Duplicated keys in or across the maps will result in a compile-time error | |
| /// during constant evaluation. | |
| class MapConcatenation extends Expression { | |
| DartType keyType; | |
| DartType valueType; | |
| final ExpressionList maps; | |
| new( | |
| this.maps, { | |
| this.keyType = const DynamicType(), | |
| this.valueType = const DynamicType(), | |
| }) { | |
| setParents(maps, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.mapType( | |
| keyType, | |
| valueType, | |
| context.nonNullable, | |
| ); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitMapConcatenation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitMapConcatenation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| keyType.accept(v); | |
| valueType.accept(v); | |
| visitList(maps, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| keyType = v.visitDartType(keyType); | |
| valueType = v.visitDartType(valueType); | |
| v.transformList(maps, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| keyType = v.visitDartType(keyType, cannotRemoveSentinel); | |
| valueType = v.visitDartType(valueType, cannotRemoveSentinel); | |
| v.transformExpressionList(maps, this); | |
| } | |
| @override | |
| String toString() { | |
| return "MapConcatenation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| bool first = true; | |
| for (Expression part in maps) { | |
| if (!first) { | |
| printer.write(' + '); | |
| } | |
| printer.writeExpression(part); | |
| first = false; | |
| } | |
| } | |
| } | |
| /// Create an instance directly from the field values. | |
| /// | |
| /// These expressions arise from const constructor calls when one or more field | |
| /// initializing expressions, field initializers, assert initializers or unused | |
| /// arguments contain unevaluated expressions. They only ever occur within | |
| /// unevaluated constants in constant expressions. | |
| class InstanceCreation extends Expression { | |
| final Reference classReference; | |
| DartTypeList typeArguments; | |
| final Map<Reference, Expression> fieldValues; | |
| final List<AssertStatement> asserts; | |
| final ExpressionList unusedArguments; | |
| new( | |
| this.classReference, | |
| this.typeArguments, | |
| this.fieldValues, | |
| this.asserts, | |
| this.unusedArguments, | |
| ) { | |
| setParents(fieldValues.values.toList(), this); | |
| setParents(asserts, this); | |
| setParents(unusedArguments, this); | |
| } | |
| Class get classNode => classReference.asClass; | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return typeArguments.isEmpty | |
| ? context.typeEnvironment.coreTypes.rawType( | |
| classNode, | |
| context.nonNullable, | |
| ) | |
| : new InterfaceType(classNode, context.nonNullable, typeArguments); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceCreation(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitInstanceCreation(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| classReference.asClass.acceptReference(v); | |
| visitList(typeArguments, v); | |
| for (final Reference reference in fieldValues.keys) { | |
| reference.asField.acceptReference(v); | |
| } | |
| for (final Expression value in fieldValues.values) { | |
| value.accept(v); | |
| } | |
| visitList(asserts, v); | |
| visitList(unusedArguments, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| typeArguments = v.transformDartTypeList(typeArguments); | |
| fieldValues.forEach((Reference fieldRef, Expression value) { | |
| Expression transformed = v.transform(value); | |
| if (!identical(value, transformed)) { | |
| fieldValues[fieldRef] = transformed; | |
| transformed.parent = this; | |
| } | |
| }); | |
| v.transformList(asserts, this); | |
| v.transformList(unusedArguments, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| typeArguments = v.transformDartTypeList(typeArguments); | |
| fieldValues.forEach((Reference fieldRef, Expression value) { | |
| Expression transformed = v.transform(value); | |
| if (!identical(value, transformed)) { | |
| fieldValues[fieldRef] = transformed; | |
| transformed.parent = this; | |
| } | |
| }); | |
| v.transformList(asserts, this, dummyAssertStatement); | |
| v.transformExpressionList(unusedArguments, this); | |
| } | |
| @override | |
| String toString() { | |
| return "InstanceCreation(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeClassName(classReference); | |
| printer.writeTypeArguments(typeArguments); | |
| printer.write('{'); | |
| bool first = true; | |
| fieldValues.forEach((Reference fieldRef, Expression value) { | |
| if (!first) { | |
| printer.write(', '); | |
| } | |
| printer.writeName(fieldRef.asField.name); | |
| printer.write(': '); | |
| printer.writeExpression(value); | |
| first = false; | |
| }); | |
| for (AssertStatement assert_ in asserts) { | |
| if (!first) { | |
| printer.write(', '); | |
| } | |
| printer.write('assert('); | |
| printer.writeExpression(assert_.condition); | |
| if (assert_.message != null) { | |
| printer.write(', '); | |
| printer.writeExpression(assert_.message!); | |
| } | |
| printer.write(')'); | |
| first = false; | |
| } | |
| for (Expression unusedArgument in unusedArguments) { | |
| if (!first) { | |
| printer.write(', '); | |
| } | |
| printer.writeExpression(unusedArgument); | |
| first = false; | |
| } | |
| printer.write('}'); | |
| } | |
| } | |
| /// A marker indicating that a subexpression originates in a different source | |
| /// file than the surrounding context. | |
| /// | |
| /// These expressions arise from inlining of const variables during constant | |
| /// evaluation. They only ever occur within unevaluated constants in constant | |
| /// expressions. | |
| class FileUriExpression extends Expression implements FileUriNode { | |
| /// The URI of the source file in which the subexpression is located. | |
| /// Can be different from the file containing the [FileUriExpression]. | |
| @override | |
| Uri fileUri; | |
| Expression expression; | |
| new(this.expression, this.fileUri) { | |
| expression.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| expression.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFileUriExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitFileUriExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| expression.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| expression = v.transform(expression)..parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| expression = v.transform(expression)..parent = this; | |
| } | |
| @override | |
| Location? _getLocationInEnclosingFile(int offset) { | |
| return _getLocationInComponent( | |
| enclosingComponent, | |
| fileUri, | |
| offset, | |
| viaForErrorMessage: "File uri expression", | |
| ); | |
| } | |
| @override | |
| String toString() { | |
| return "FileUriExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| if (printer.includeAuxiliaryProperties) { | |
| printer.write('{'); | |
| printer.write(fileUri.toString()); | |
| printer.write('}'); | |
| } | |
| printer.writeExpression(expression); | |
| } | |
| } | |
| /// Expression of form `x is T`. | |
| class IsExpression extends Expression { | |
| Expression operand; | |
| DartType type; | |
| new(this.operand, this.type) { | |
| operand.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitIsExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitIsExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| operand.accept(v); | |
| type.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| type = v.visitDartType(type); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| type = v.visitDartType(type, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "IsExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| operand, | |
| minimumPrecedence: astToText.Precedence.BITWISE_OR, | |
| ); | |
| printer.write(' is '); | |
| printer.writeType(type); | |
| } | |
| } | |
| /// Expression of form `x as T`. | |
| class AsExpression extends Expression { | |
| int flags = 0; | |
| Expression operand; | |
| DartType type; | |
| new(this.operand, this.type) { | |
| operand.parent = this; | |
| } | |
| // Must match serialized bit positions. | |
| static const int FlagTypeError = 1 << 0; | |
| static const int FlagCovarianceCheck = 1 << 1; | |
| static const int FlagForDynamic = 1 << 2; | |
| static const int FlagUnchecked = 1 << 3; | |
| /// If `true`, this test is an implicit down cast. | |
| /// | |
| /// If `true` a TypeError should be thrown. If `false` a CastError should be | |
| /// thrown. | |
| bool get isTypeError => flags & FlagTypeError != 0; | |
| void set isTypeError(bool value) { | |
| flags = value ? (flags | FlagTypeError) : (flags & ~FlagTypeError); | |
| } | |
| /// If `true`, this test is needed to ensure soundness of covariant type | |
| /// variables using in contravariant positions. | |
| /// | |
| /// For instance | |
| /// | |
| /// class Class<T> { | |
| /// void Function(T) field; | |
| /// Class(this.field); | |
| /// } | |
| /// main() { | |
| /// Class<num> c = new Class<int>((int i) {}); | |
| /// void Function<num> field = c.field; // Check needed on `c.field` | |
| /// field(0.5); | |
| /// } | |
| /// | |
| /// Here a covariant check `c.field as void Function(num)` is needed because | |
| /// the field could be (and indeed is) not a subtype of the static type of | |
| /// the expression. | |
| bool get isCovarianceCheck => flags & FlagCovarianceCheck != 0; | |
| void set isCovarianceCheck(bool value) { | |
| flags = value | |
| ? (flags | FlagCovarianceCheck) | |
| : (flags & ~FlagCovarianceCheck); | |
| } | |
| /// If `true`, this is an implicit down cast from an expression of type | |
| /// `dynamic`. | |
| bool get isForDynamic => flags & FlagForDynamic != 0; | |
| void set isForDynamic(bool value) { | |
| flags = value ? (flags | FlagForDynamic) : (flags & ~FlagForDynamic); | |
| } | |
| /// If `true`, this test is added to show the known static type of the | |
| /// expression and should not be performed at runtime. | |
| /// | |
| /// This is the case for instance for access to extension type representation | |
| /// fields on an extension type, where this node shows that the static type | |
| /// changes from the extension type of the declared representation type. | |
| /// | |
| /// This is also the case when a field access undergoes type promotion. | |
| bool get isUnchecked => flags & FlagUnchecked != 0; | |
| void set isUnchecked(bool value) { | |
| flags = value ? (flags | FlagUnchecked) : (flags & ~FlagUnchecked); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => type; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAsExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitAsExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| operand.accept(v); | |
| type.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| type = v.visitDartType(type); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| type = v.visitDartType(type, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "AsExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| operand, | |
| minimumPrecedence: astToText.Precedence.BITWISE_OR, | |
| ); | |
| printer.write(' as'); | |
| if (printer.includeAuxiliaryProperties) { | |
| List<String> flags = <String>[]; | |
| if (isTypeError) { | |
| flags.add('TypeError'); | |
| } | |
| if (isCovarianceCheck) { | |
| flags.add('CovarianceCheck'); | |
| } | |
| if (isForDynamic) { | |
| flags.add('ForDynamic'); | |
| } | |
| if (flags.isNotEmpty) { | |
| printer.write('{${flags.join(',')}}'); | |
| } | |
| } | |
| printer.write(' '); | |
| printer.writeType(type); | |
| } | |
| } | |
| /// Null check expression of form `x!`. | |
| /// | |
| /// This expression was added as part of NNBD and is currently only created when | |
| /// the 'non-nullable' experimental feature is enabled. | |
| class NullCheck extends Expression { | |
| Expression operand; | |
| new(this.operand) { | |
| operand.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| DartType operandType = operand.getStaticType(context); | |
| return operandType is NullType | |
| ? const NeverType.nonNullable() | |
| : operandType.withDeclaredNullability(Nullability.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitNullCheck(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitNullCheck(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| operand.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "NullCheck(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression( | |
| operand, | |
| minimumPrecedence: astToText.Precedence.POSTFIX, | |
| ); | |
| printer.write('!'); | |
| } | |
| } | |
| /// An integer, double, boolean, string, or null constant. | |
| abstract class BasicLiteral extends Expression { | |
| Object? get value; | |
| @override | |
| void visitChildren(Visitor v) {} | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| } | |
| class StringLiteral extends BasicLiteral { | |
| @override | |
| String value; | |
| new(this.value); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.stringRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStringLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitStringLiteral(this, arg); | |
| @override | |
| String toString() { | |
| return "StringLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('"'); | |
| printer.write(escapeString(value)); | |
| printer.write('"'); | |
| } | |
| } | |
| class IntLiteral extends BasicLiteral { | |
| /// Note that this value holds a uint64 value. | |
| /// E.g. "0x8000000000000000" will be saved as "-9223372036854775808" despite | |
| /// technically (on some platforms, particularly JavaScript) being positive. | |
| /// If the number is meant to be negative it will be wrapped in a "unary-". | |
| @override | |
| int value; | |
| new(this.value); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.intRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitIntLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitIntLiteral(this, arg); | |
| @override | |
| String toString() { | |
| return "IntLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('$value'); | |
| } | |
| } | |
| class DoubleLiteral extends BasicLiteral { | |
| @override | |
| double value; | |
| new(this.value); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.doubleRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDoubleLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitDoubleLiteral(this, arg); | |
| @override | |
| String toString() { | |
| return "DoubleLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('$value'); | |
| } | |
| } | |
| class BoolLiteral extends BasicLiteral { | |
| @override | |
| bool value; | |
| new(this.value); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitBoolLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitBoolLiteral(this, arg); | |
| @override | |
| String toString() { | |
| return "BoolLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('$value'); | |
| } | |
| } | |
| class NullLiteral extends BasicLiteral { | |
| @override | |
| Object? get value => null; | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => const NullType(); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitNullLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitNullLiteral(this, arg); | |
| @override | |
| String toString() { | |
| return "NullLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('null'); | |
| } | |
| } | |
| class SymbolLiteral extends Expression { | |
| String value; // Everything strictly after the '#'. | |
| new(this.value); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.symbolRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSymbolLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitSymbolLiteral(this, arg); | |
| @override | |
| void visitChildren(Visitor v) {} | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "SymbolLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('#'); | |
| printer.write(value); | |
| } | |
| } | |
| class TypeLiteral extends Expression { | |
| DartType type; | |
| new(this.type); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.typeEnvironment.coreTypes.typeRawType(context.nonNullable); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitTypeLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitTypeLiteral(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| type.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| type = v.visitDartType(type); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| type = v.visitDartType(type, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "TypeLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeType(type); | |
| } | |
| } | |
| class ThisExpression extends Expression { | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| context.thisType!; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitThisExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitThisExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) {} | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "ThisExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('this'); | |
| } | |
| } | |
| class Rethrow extends Expression { | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| const NeverType.nonNullable(); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRethrow(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitRethrow(this, arg); | |
| @override | |
| void visitChildren(Visitor v) {} | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "Rethrow(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('rethrow'); | |
| } | |
| } | |
| class Throw extends Expression { | |
| Expression expression; | |
| int flags = 0; | |
| new(this.expression) { | |
| expression.parent = this; | |
| } | |
| // Must match serialized bit positions. | |
| static const int FlagForErrorHandling = 1 << 0; | |
| /// If `true`, this `throw` is *not* present in the source code but added | |
| /// to ensure correctness and/or soundness of the generated code. | |
| /// | |
| /// This is used for instance in the lowering for handling duplicate writes | |
| /// to a late final field or for pattern assignments that don't match. | |
| bool get forErrorHandling => flags & FlagForErrorHandling != 0; | |
| void set forErrorHandling(bool value) { | |
| flags = value | |
| ? (flags | FlagForErrorHandling) | |
| : (flags & ~FlagForErrorHandling); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| const NeverType.nonNullable(); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitThrow(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitThrow(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| expression.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "Throw(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('throw '); | |
| printer.writeExpression(expression); | |
| } | |
| } | |
| class ListLiteral extends Expression { | |
| bool isConst; | |
| DartType typeArgument; // Not null, defaults to DynamicType. | |
| final ExpressionList expressions; | |
| new( | |
| this.expressions, { | |
| this.typeArgument = const DynamicType(), | |
| this.isConst = false, | |
| }) { | |
| setParents(expressions, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.listType(typeArgument, context.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitListLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitListLiteral(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| typeArgument.accept(v); | |
| visitList(expressions, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| typeArgument = v.visitDartType(typeArgument); | |
| v.transformList(expressions, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); | |
| v.transformExpressionList(expressions, this); | |
| } | |
| @override | |
| String toString() { | |
| return "ListLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| if (isConst) { | |
| printer.write('const '); | |
| } | |
| printer.write('<'); | |
| printer.writeType(typeArgument); | |
| printer.write('>['); | |
| printer.writeExpressions(expressions); | |
| printer.write(']'); | |
| } | |
| } | |
| class SetLiteral extends Expression { | |
| bool isConst; | |
| DartType typeArgument; // Not null, defaults to DynamicType. | |
| final ExpressionList expressions; | |
| new( | |
| this.expressions, { | |
| this.typeArgument = const DynamicType(), | |
| this.isConst = false, | |
| }) { | |
| setParents(expressions, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.setType(typeArgument, context.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSetLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitSetLiteral(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| typeArgument.accept(v); | |
| visitList(expressions, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| typeArgument = v.visitDartType(typeArgument); | |
| v.transformList(expressions, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); | |
| v.transformExpressionList(expressions, this); | |
| } | |
| @override | |
| String toString() { | |
| return "SetLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| if (isConst) { | |
| printer.write('const '); | |
| } | |
| printer.write('<'); | |
| printer.writeType(typeArgument); | |
| printer.write('>{'); | |
| printer.writeExpressions(expressions); | |
| printer.write('}'); | |
| } | |
| } | |
| class MapLiteral extends Expression { | |
| bool isConst; | |
| DartType keyType; // Not null, defaults to DynamicType. | |
| DartType valueType; // Not null, defaults to DynamicType. | |
| final List<MapLiteralEntry> entries; | |
| new( | |
| this.entries, { | |
| this.keyType = const DynamicType(), | |
| this.valueType = const DynamicType(), | |
| this.isConst = false, | |
| }) { | |
| setParents(entries, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.mapType( | |
| keyType, | |
| valueType, | |
| context.nonNullable, | |
| ); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitMapLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitMapLiteral(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| keyType.accept(v); | |
| valueType.accept(v); | |
| visitList(entries, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| keyType = v.visitDartType(keyType); | |
| valueType = v.visitDartType(valueType); | |
| v.transformList(entries, this); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| keyType = v.visitDartType(keyType, cannotRemoveSentinel); | |
| valueType = v.visitDartType(valueType, cannotRemoveSentinel); | |
| v.transformMapEntryList(entries, this); | |
| } | |
| @override | |
| String toString() { | |
| return "MapLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| if (isConst) { | |
| printer.write('const '); | |
| } | |
| printer.write('<'); | |
| printer.writeType(keyType); | |
| printer.write(', '); | |
| printer.writeType(valueType); | |
| printer.write('>{'); | |
| for (int index = 0; index < entries.length; index++) { | |
| if (index > 0) { | |
| printer.write(', '); | |
| } | |
| printer.writeMapEntry(entries[index]); | |
| } | |
| printer.write('}'); | |
| } | |
| } | |
| class MapLiteralEntry extends TreeNode { | |
| Expression key; | |
| Expression value; | |
| new(this.key, this.value) { | |
| key.parent = this; | |
| value.parent = this; | |
| } | |
| @override | |
| R accept<R>(TreeVisitor<R> v) => v.visitMapLiteralEntry(this); | |
| @override | |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => | |
| v.visitMapLiteralEntry(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| key.accept(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| key = v.transform(key); | |
| key.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| key = v.transform(key); | |
| key.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "MapEntry(${toStringInternal()})"; | |
| } | |
| @override | |
| String toText(AstTextStrategy strategy) { | |
| AstPrinter printer = new AstPrinter(strategy); | |
| toTextInternal(printer); | |
| return printer.getText(); | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeExpression(key); | |
| printer.write(': '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| class RecordLiteral extends Expression { | |
| bool isConst; | |
| final ExpressionList positional; | |
| final NamedExpressionList named; | |
| RecordType recordType; | |
| new(this.positional, this.named, this.recordType, {this.isConst = false}) | |
| : assert( | |
| positional.length == recordType.positional.length && | |
| named.length == recordType.named.length && | |
| recordType.named | |
| .map((f) => f.name) | |
| .toSet() | |
| .containsAll(named.map((f) => f.name)), | |
| ), | |
| assert( | |
| () { | |
| // Assert that the named fields are sorted. | |
| for (int i = 1; i < named.length; i++) { | |
| if (named[i].name.compareTo(named[i - 1].name) < 0) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }(), | |
| "Named fields of a RecordLiterals aren't sorted lexicographically: " | |
| "${named.map((f) => f.name).join(", ")}", | |
| ) { | |
| setParents(positional, this); | |
| setParents(named, this); | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return recordType; | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRecordLiteral(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitRecordLiteral(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| visitList(positional, v); | |
| visitList(named, v); | |
| recordType.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| v.transformList(positional, this); | |
| v.transformList(named, this); | |
| recordType = v.visitDartType(recordType) as RecordType; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| v.transformExpressionList(positional, this); | |
| v.transformNamedExpressionList(named, this); | |
| recordType = | |
| v.visitDartType(recordType, cannotRemoveSentinel) as RecordType; | |
| } | |
| @override | |
| String toString() { | |
| return "RecordLiteral(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| if (isConst) { | |
| printer.write("const "); | |
| } | |
| printer.write("("); | |
| for (int index = 0; index < positional.length; index++) { | |
| if (index > 0) { | |
| printer.write(", "); | |
| } | |
| printer.writeExpression(positional[index]); | |
| } | |
| if (named.isNotEmpty) { | |
| if (positional.isNotEmpty) { | |
| printer.write(", "); | |
| } | |
| for (int index = 0; index < named.length; index++) { | |
| if (index > 0) { | |
| printer.write(", "); | |
| } | |
| printer.writeNamedExpression(named[index]); | |
| } | |
| } | |
| printer.write(")"); | |
| } | |
| } | |
| /// Expression of form `await x`. | |
| class AwaitExpression extends Expression { | |
| Expression operand; | |
| /// If non-null, the runtime should check whether the value of [operand] is a | |
| /// subtype of [runtimeCheckType], and if _not_ so, wrap the value in a call | |
| /// to the `Future.value()` constructor. | |
| /// | |
| /// For instance | |
| /// | |
| /// FutureOr<Object> future1 = Future<Object?>.value(); | |
| /// var x = await future1; // Check against `Future<Object>`. | |
| /// | |
| /// Object object = Future<Object?>.value(); | |
| /// var y = await object; // Check against `Future<Object>`. | |
| /// | |
| /// Future<Object?> future2 = Future<Object?>.value(); | |
| /// var z = await future2; // No check. | |
| /// | |
| /// This runtime checks is necessary to ensure that we don't evaluate the | |
| /// await expression to `null` when the static type of the expression is | |
| /// non-nullable. | |
| /// | |
| /// The [runtimeCheckType] is computed as `Future<T>` where `T = flatten(S)` | |
| /// and `S` is the static type of [operand]. To avoid unnecessary runtime | |
| /// checks, the [runtimeCheckType] is not set if the static type of the | |
| /// [operand] is a subtype of `Future<T>`. | |
| /// | |
| /// See https://github.com/dart-lang/sdk/issues/49396 for further discussion | |
| /// of which the check is needed. | |
| DartType? runtimeCheckType; | |
| new(this.operand) { | |
| operand.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.flatten(operand.getStaticType(context)); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAwaitExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitAwaitExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| operand.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| if (runtimeCheckType != null) { | |
| runtimeCheckType = v.visitDartType(runtimeCheckType!); | |
| } | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| operand = v.transform(operand); | |
| operand.parent = this; | |
| if (runtimeCheckType != null) { | |
| runtimeCheckType = v.visitDartType(runtimeCheckType!, null); | |
| } | |
| } | |
| @override | |
| String toString() { | |
| return "AwaitExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('await '); | |
| printer.writeExpression(operand); | |
| } | |
| } | |
| /// Common super-interface for [FunctionExpression] and [FunctionDeclaration]. | |
| abstract class LocalFunction implements GenericFunction { | |
| @override | |
| FunctionNode get function; | |
| /// Unique identifier of this function within a [Member]. | |
| /// Has a value [LocalFunctionId.invalid] until set. | |
| abstract LocalFunctionId id; | |
| } | |
| /// Unique identifier of the local function within a [Member]. | |
| extension type const LocalFunctionId(int _value) { | |
| static const LocalFunctionId invalid = LocalFunctionId(0); | |
| static const LocalFunctionId first = LocalFunctionId(1); | |
| LocalFunctionId operator +(int delta) => LocalFunctionId(_value + delta); | |
| bool operator >(LocalFunctionId other) => this._value > other._value; | |
| int toInt() => _value; // Only for serialization. | |
| } | |
| /// Counter of local functions within a [Member]. | |
| class LocalFunctionIdGenerator { | |
| LocalFunctionId _counter; | |
| new() : _counter = LocalFunctionId.first; | |
| new after(LocalFunctionId lastUsedId) : _counter = lastUsedId + 1; | |
| /// Generate a new id for a local function within a [Member]. | |
| LocalFunctionId allocateId() => _counter++; | |
| bool get isUsed => _counter != LocalFunctionId.first; | |
| bool isValidId(LocalFunctionId id) => | |
| id._value > 0 && id._value < _counter._value; | |
| } | |
| /// Expression of form `(x,y) => ...` or `(x,y) { ... }` | |
| /// | |
| /// The arrow-body form `=> e` is desugared into `return e;`. | |
| class FunctionExpression extends Expression implements LocalFunction { | |
| @override | |
| FunctionNode function; | |
| @override | |
| LocalFunctionId id = LocalFunctionId.invalid; | |
| new(this.function) { | |
| function.parent = this; | |
| } | |
| @override | |
| TypeParameterList get typeParameters => function.typeParameters; | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return function.computeFunctionType(context.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitFunctionExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| function.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| function = v.transform(function); | |
| function.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| function = v.transform(function); | |
| function.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "FunctionExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeFunctionNode(function, ''); | |
| } | |
| } | |
| class ConstantExpression extends Expression { | |
| Constant constant; | |
| DartType type; | |
| new(this.constant, [this.type = const DynamicType()]); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => type; | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConstantExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitConstantExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| constant.acceptReference(v); | |
| type.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| constant = v.visitConstant(constant); | |
| type = v.visitDartType(type); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| constant = v.visitConstant(constant, cannotRemoveSentinel); | |
| type = v.visitDartType(type, cannotRemoveSentinel); | |
| } | |
| @override | |
| String toString() { | |
| return "ConstantExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeConstant(constant); | |
| } | |
| } | |
| class FileUriConstantExpression extends ConstantExpression | |
| implements FileUriNode { | |
| @override | |
| Uri fileUri; | |
| new( | |
| Constant constant, { | |
| DartType type = const DynamicType(), | |
| required this.fileUri, | |
| }) : super(constant, type); | |
| @override | |
| Location? _getLocationInEnclosingFile(int offset) { | |
| return _getLocationInComponent( | |
| enclosingComponent, | |
| fileUri, | |
| offset, | |
| viaForErrorMessage: "File uri constant expression", | |
| ); | |
| } | |
| } | |
| /// Synthetic expression of form `let v = x in y` | |
| class Let extends Expression { | |
| SyntheticVariable variable; // Must have an initializer. | |
| Expression body; | |
| new({required this.variable, required Expression value, required this.body}) { | |
| variable.parent = this; | |
| body.parent = this; | |
| // ignore: deprecated_member_use_from_same_package | |
| variable.initializer = value..parent = variable; | |
| } | |
| // ignore: deprecated_member_use_from_same_package | |
| Expression get value => variable.initializer!; | |
| void set value(Expression value) { | |
| // ignore: deprecated_member_use_from_same_package | |
| variable.initializer = value; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| body.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLet(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitLet(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| variable.accept(v); | |
| body.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| variable = v.transform(variable); | |
| variable.parent = this; | |
| body = v.transform(body); | |
| body.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| variable = v.transform(variable); | |
| variable.parent = this; | |
| body = v.transform(body); | |
| body.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "Let(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('let '); | |
| printer.writeVariableInitialization(variable); | |
| printer.write(' in '); | |
| printer.writeExpression(body); | |
| } | |
| } | |
| class BlockExpression extends Expression implements ScopeProvider { | |
| Block body; | |
| Expression value; | |
| @override | |
| Scope? scope; | |
| new(this.body, this.value) { | |
| body.parent = this; | |
| value.parent = this; | |
| } | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) => | |
| value.getStaticType(context); | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitBlockExpression(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitBlockExpression(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| body.accept(v); | |
| value.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| body = v.transform(body); | |
| body.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| body = v.transform(body); | |
| body.parent = this; | |
| value = v.transform(value); | |
| value.parent = this; | |
| } | |
| @override | |
| String toString() { | |
| return "BlockExpression(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write('block '); | |
| printer.writeBlock(body.statements); | |
| printer.write(' => '); | |
| printer.writeExpression(value); | |
| } | |
| } | |
| /// Attempt to load the library referred to by a deferred import. | |
| /// | |
| /// This instruction is concerned with: | |
| /// - keeping track whether the deferred import is marked as 'loaded' | |
| /// - keeping track of whether the library code has already been downloaded | |
| /// - actually downloading and linking the library | |
| /// | |
| /// Should return a future. The value in this future will be the same value | |
| /// seen by callers of `loadLibrary` functions. | |
| /// | |
| /// On backends that link the entire program eagerly, this instruction needs | |
| /// to mark the deferred import as 'loaded' and return a future. | |
| class LoadLibrary extends Expression { | |
| /// Reference to a deferred import in the enclosing library. | |
| LibraryDependency import; | |
| new(this.import); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.futureType( | |
| const DynamicType(), | |
| context.nonNullable, | |
| ); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLoadLibrary(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitLoadLibrary(this, arg); | |
| @override | |
| void visitChildren(Visitor v) {} | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "LoadLibrary(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write(import.name!); | |
| printer.write('.loadLibrary()'); | |
| } | |
| } | |
| /// Checks that the given deferred import has been marked as 'loaded'. | |
| class CheckLibraryIsLoaded extends Expression { | |
| /// Reference to a deferred import in the enclosing library. | |
| LibraryDependency import; | |
| new(this.import); | |
| @override | |
| DartType getStaticType(StaticTypeContext context) => | |
| getStaticTypeInternal(context); | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return context.typeEnvironment.coreTypes.objectRawType(context.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitCheckLibraryIsLoaded(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitCheckLibraryIsLoaded(this, arg); | |
| @override | |
| void visitChildren(Visitor v) {} | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "CheckLibraryIsLoaded(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.write(import.name!); | |
| printer.write('.checkLibraryIsLoaded()'); | |
| } | |
| } | |
| /// Tearing off a constructor of a class. | |
| class ConstructorTearOff extends Expression { | |
| /// The reference to the constructor being torn off. | |
| Reference targetReference; | |
| new(Member target) | |
| : assert( | |
| target is Constructor || (target is Procedure && target.isFactory), | |
| "Unexpected constructor tear off target: $target", | |
| ), | |
| this.targetReference = getNonNullableMemberReferenceGetter(target); | |
| new byReference(this.targetReference); | |
| Member get target => targetReference.asMember; | |
| FunctionNode get function => target.function!; | |
| void set target(Member member) { | |
| assert( | |
| member is Constructor || | |
| (member is Procedure && member.kind == ProcedureKind.Factory), | |
| ); | |
| targetReference = getNonNullableMemberReferenceGetter(member); | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return target.function!.computeFunctionType(Nullability.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorTearOff(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitConstructorTearOff(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "ConstructorTearOff(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeMemberName(targetReference); | |
| } | |
| } | |
| /// Tearing off a redirecting factory constructor of a class. | |
| class RedirectingFactoryTearOff extends Expression { | |
| /// The reference to the redirecting factory constructor being torn off. | |
| Reference targetReference; | |
| new(Procedure target) | |
| : assert(target.isRedirectingFactory), | |
| this.targetReference = getNonNullableMemberReferenceGetter(target); | |
| new byReference(this.targetReference); | |
| Procedure get target => targetReference.asProcedure; | |
| void set target(Procedure target) { | |
| targetReference = getNonNullableMemberReferenceGetter(target); | |
| } | |
| FunctionNode get function => target.function; | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| return target.function.computeFunctionType(Nullability.nonNullable); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRedirectingFactoryTearOff(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitRedirectingFactoryTearOff(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| target.acceptReference(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) {} | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) {} | |
| @override | |
| String toString() { | |
| return "RedirectingFactoryTearOff(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeMemberName(targetReference); | |
| } | |
| } | |
| class TypedefTearOff extends Expression { | |
| final StructuralParameterList structuralParameters; | |
| Expression expression; | |
| DartTypeList typeArguments; | |
| new(this.structuralParameters, this.expression, this.typeArguments) { | |
| expression.parent = this; | |
| } | |
| @override | |
| DartType getStaticTypeInternal(StaticTypeContext context) { | |
| FreshStructuralParameters freshTypeParameters = | |
| getFreshStructuralParameters(structuralParameters); | |
| FunctionType type = expression.getStaticType(context) as FunctionType; | |
| type = freshTypeParameters.substitute( | |
| FunctionTypeInstantiator.instantiate(type, typeArguments), | |
| ) as FunctionType; | |
| return new FunctionType( | |
| type.positionalParameters, | |
| type.returnType, | |
| type.declaredNullability, | |
| namedParameters: type.namedParameters, | |
| typeParameters: freshTypeParameters.freshTypeParameters, | |
| requiredParameterCount: type.requiredParameterCount, | |
| ); | |
| } | |
| @override | |
| R accept<R>(ExpressionVisitor<R> v) => v.visitTypedefTearOff(this); | |
| @override | |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => | |
| v.visitTypedefTearOff(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| expression.accept(v); | |
| visitList(structuralParameters, v); | |
| visitList(typeArguments, v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| typeArguments = v.transformDartTypeList(typeArguments); | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| expression = v.transform(expression); | |
| expression.parent = this; | |
| typeArguments = v.transformDartTypeList(typeArguments); | |
| } | |
| @override | |
| String toString() { | |
| return "TypedefTearOff(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| printer.writeStructuralParameters(structuralParameters); | |
| printer.write(".("); | |
| printer.writeExpression(expression); | |
| printer.writeTypeArguments(typeArguments); | |
| printer.write(")"); | |
| } | |
| } |