|  | // 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. | 
|  |  | 
|  | import 'arguments.dart'; | 
|  | import 'elements.dart'; | 
|  | import 'proto.dart'; | 
|  | import 'record_fields.dart'; | 
|  | import 'references.dart'; | 
|  | import 'string_literal_parts.dart'; | 
|  | import 'type_annotations.dart'; | 
|  | import 'util.dart'; | 
|  |  | 
|  | /// Superclass for all expression nodes. | 
|  | sealed class Expression { | 
|  | /// Returns the [Expression] corresponding to this [Expression] in which all | 
|  | /// [UnresolvedIdentifier]s have been resolved within their scope. | 
|  | /// | 
|  | /// If this didn't create a new [Expression], `null` is returned. | 
|  | Expression? resolve(); | 
|  | } | 
|  |  | 
|  | class InvalidExpression extends Expression { | 
|  | @override | 
|  | String toString() => 'InvalidExpression()'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class StaticGet extends Expression { | 
|  | final FieldReference reference; | 
|  |  | 
|  | StaticGet(this.reference); | 
|  |  | 
|  | @override | 
|  | String toString() => 'StaticGet($reference)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class FunctionTearOff extends Expression { | 
|  | final FunctionReference reference; | 
|  |  | 
|  | FunctionTearOff(this.reference); | 
|  |  | 
|  | @override | 
|  | String toString() => 'FunctionTearOff($reference)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class ConstructorTearOff extends Expression { | 
|  | final TypeAnnotation type; | 
|  | final ConstructorReference reference; | 
|  |  | 
|  | ConstructorTearOff(this.type, this.reference); | 
|  |  | 
|  | @override | 
|  | String toString() => 'ConstructorTearOff($type,$reference)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | TypeAnnotation? newType = type.resolve(); | 
|  | return newType == null ? null : new ConstructorTearOff(newType, reference); | 
|  | } | 
|  | } | 
|  |  | 
|  | class ConstructorInvocation extends Expression { | 
|  | final TypeAnnotation type; | 
|  | final Reference constructor; | 
|  | final List<Argument> arguments; | 
|  |  | 
|  | ConstructorInvocation(this.type, this.constructor, this.arguments); | 
|  |  | 
|  | @override | 
|  | String toString() => 'ConstructorInvocation($type,$constructor,$arguments)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | TypeAnnotation? newType = type.resolve(); | 
|  | List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | 
|  | return newType == null && newArguments == null | 
|  | ? null | 
|  | : new ConstructorInvocation( | 
|  | newType ?? type, constructor, newArguments ?? arguments); | 
|  | } | 
|  | } | 
|  |  | 
|  | class IntegerLiteral extends Expression { | 
|  | final String? text; | 
|  | final int? value; | 
|  |  | 
|  | IntegerLiteral.fromText(String this.text, [this.value]); | 
|  |  | 
|  | IntegerLiteral.fromValue(int this.value) : text = null; | 
|  |  | 
|  | @override | 
|  | String toString() => 'IntegerLiteral(${value ?? text})'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class DoubleLiteral extends Expression { | 
|  | final String text; | 
|  | final double value; | 
|  |  | 
|  | DoubleLiteral(this.text, this.value); | 
|  |  | 
|  | @override | 
|  | String toString() => 'DoubleLiteral($text)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class BooleanLiteral extends Expression { | 
|  | final bool value; | 
|  |  | 
|  | BooleanLiteral(this.value); | 
|  |  | 
|  | @override | 
|  | String toString() => 'BooleanLiteral($value)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class NullLiteral extends Expression { | 
|  | NullLiteral(); | 
|  |  | 
|  | @override | 
|  | String toString() => 'NullLiteral()'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class SymbolLiteral extends Expression { | 
|  | final List<String> parts; | 
|  |  | 
|  | SymbolLiteral(this.parts); | 
|  |  | 
|  | @override | 
|  | String toString() => 'SymbolLiteral($parts)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() => null; | 
|  | } | 
|  |  | 
|  | class StringLiteral extends Expression { | 
|  | final List<StringLiteralPart> parts; | 
|  |  | 
|  | StringLiteral(this.parts); | 
|  |  | 
|  | @override | 
|  | String toString() => 'StringLiteral($parts)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | List<StringLiteralPart>? newParts = parts.resolve((p) => p.resolve()); | 
|  | return newParts == null ? null : new StringLiteral(newParts); | 
|  | } | 
|  | } | 
|  |  | 
|  | class AdjacentStringLiterals extends Expression { | 
|  | final List<Expression> expressions; | 
|  |  | 
|  | AdjacentStringLiterals(this.expressions); | 
|  |  | 
|  | @override | 
|  | String toString() => 'AdjacentStringLiterals($expressions)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | List<Expression>? newExpressions = expressions.resolve((e) => e.resolve()); | 
|  | return newExpressions == null | 
|  | ? null | 
|  | : new AdjacentStringLiterals(newExpressions); | 
|  | } | 
|  | } | 
|  |  | 
|  | class ImplicitInvocation extends Expression { | 
|  | final Expression receiver; | 
|  | final List<TypeAnnotation> typeArguments; | 
|  | final List<Argument> arguments; | 
|  |  | 
|  | ImplicitInvocation(this.receiver, this.typeArguments, this.arguments); | 
|  |  | 
|  | @override | 
|  | String toString() => | 
|  | 'ImplicitInvocation($receiver,$typeArguments,$arguments)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newReceiver = receiver.resolve(); | 
|  | List<TypeAnnotation>? newTypeArguments = | 
|  | typeArguments.resolve((t) => t.resolve()); | 
|  | List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | 
|  | return newReceiver == null && | 
|  | newTypeArguments == null && | 
|  | newArguments == null | 
|  | ? null | 
|  | : new ImplicitInvocation(newReceiver ?? receiver, | 
|  | newTypeArguments ?? typeArguments, newArguments ?? arguments); | 
|  | } | 
|  | } | 
|  |  | 
|  | class StaticInvocation extends Expression { | 
|  | final FunctionReference function; | 
|  | final List<TypeAnnotation> typeArguments; | 
|  | final List<Argument> arguments; | 
|  |  | 
|  | StaticInvocation(this.function, this.typeArguments, this.arguments); | 
|  |  | 
|  | @override | 
|  | String toString() => 'StaticInvocation($function,$typeArguments,$arguments)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | List<TypeAnnotation>? newTypeArguments = | 
|  | typeArguments.resolve((t) => t.resolve()); | 
|  | List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | 
|  | return newTypeArguments == null && newArguments == null | 
|  | ? null | 
|  | : new StaticInvocation(function, newTypeArguments ?? typeArguments, | 
|  | newArguments ?? arguments); | 
|  | } | 
|  | } | 
|  |  | 
|  | class Instantiation extends Expression { | 
|  | final Expression receiver; | 
|  | final List<TypeAnnotation> typeArguments; | 
|  |  | 
|  | Instantiation(this.receiver, this.typeArguments); | 
|  |  | 
|  | @override | 
|  | String toString() => 'Instantiation($receiver,$typeArguments)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newReceiver = receiver.resolve(); | 
|  | List<TypeAnnotation>? newTypeArguments = | 
|  | typeArguments.resolve((t) => t.resolve()); | 
|  | return newReceiver == null && newTypeArguments == null | 
|  | ? null | 
|  | : new Instantiation( | 
|  | newReceiver ?? receiver, newTypeArguments ?? typeArguments); | 
|  | } | 
|  | } | 
|  |  | 
|  | class MethodInvocation extends Expression { | 
|  | final Expression receiver; | 
|  | final String name; | 
|  | final List<TypeAnnotation> typeArguments; | 
|  | final List<Argument> arguments; | 
|  |  | 
|  | MethodInvocation( | 
|  | this.receiver, this.name, this.typeArguments, this.arguments); | 
|  |  | 
|  | @override | 
|  | String toString() => | 
|  | 'MethodInvocation($receiver,$name,$typeArguments,$arguments)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newReceiver = receiver.resolve(); | 
|  | List<TypeAnnotation>? newTypeArguments = | 
|  | typeArguments.resolve((t) => t.resolve()); | 
|  | List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | 
|  | return newReceiver == null && | 
|  | newTypeArguments == null && | 
|  | newArguments == null | 
|  | ? null | 
|  | : new MethodInvocation(newReceiver ?? receiver, name, | 
|  | newTypeArguments ?? typeArguments, newArguments ?? arguments); | 
|  | } | 
|  | } | 
|  |  | 
|  | class PropertyGet extends Expression { | 
|  | final Expression receiver; | 
|  | final String name; | 
|  |  | 
|  | PropertyGet(this.receiver, this.name); | 
|  |  | 
|  | @override | 
|  | String toString() => 'PropertyGet($receiver,$name)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newReceiver = receiver.resolve(); | 
|  | return newReceiver == null ? null : new PropertyGet(newReceiver, name); | 
|  | } | 
|  | } | 
|  |  | 
|  | class NullAwarePropertyGet extends Expression { | 
|  | final Expression receiver; | 
|  | final String name; | 
|  |  | 
|  | NullAwarePropertyGet(this.receiver, this.name); | 
|  |  | 
|  | @override | 
|  | String toString() => 'NullAwarePropertyGet($receiver,$name)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newReceiver = receiver.resolve(); | 
|  | return newReceiver == null | 
|  | ? null | 
|  | : new NullAwarePropertyGet(newReceiver, name); | 
|  | } | 
|  | } | 
|  |  | 
|  | class TypeLiteral extends Expression { | 
|  | final TypeAnnotation typeAnnotation; | 
|  |  | 
|  | TypeLiteral(this.typeAnnotation); | 
|  |  | 
|  | @override | 
|  | String toString() => 'TypeLiteral($typeAnnotation)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | TypeAnnotation? newTypeAnnotation = typeAnnotation.resolve(); | 
|  | return newTypeAnnotation == null | 
|  | ? null | 
|  | : new TypeLiteral(newTypeAnnotation); | 
|  | } | 
|  | } | 
|  |  | 
|  | class ParenthesizedExpression extends Expression { | 
|  | final Expression expression; | 
|  |  | 
|  | ParenthesizedExpression(this.expression); | 
|  |  | 
|  | @override | 
|  | String toString() => 'ParenthesizedExpression($expression)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newExpression = expression.resolve(); | 
|  | return newExpression == null | 
|  | ? null | 
|  | : new ParenthesizedExpression(newExpression); | 
|  | } | 
|  | } | 
|  |  | 
|  | class ConditionalExpression extends Expression { | 
|  | final Expression condition; | 
|  | final Expression then; | 
|  | final Expression otherwise; | 
|  |  | 
|  | ConditionalExpression(this.condition, this.then, this.otherwise); | 
|  |  | 
|  | @override | 
|  | String toString() => 'ConditionalExpression($condition,$then,$otherwise)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newCondition = condition.resolve(); | 
|  | Expression? newThen = then.resolve(); | 
|  | Expression? newOtherwise = otherwise.resolve(); | 
|  | return newCondition == null && newThen == null && newOtherwise == null | 
|  | ? null | 
|  | : new ConditionalExpression(newCondition ?? condition, newThen ?? then, | 
|  | newOtherwise ?? otherwise); | 
|  | } | 
|  | } | 
|  |  | 
|  | class ListLiteral extends Expression { | 
|  | final List<TypeAnnotation> typeArguments; | 
|  | final List<Element> elements; | 
|  |  | 
|  | ListLiteral(this.typeArguments, this.elements); | 
|  |  | 
|  | @override | 
|  | String toString() => 'ListLiteral($typeArguments,$elements)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | List<TypeAnnotation>? newTypeArguments = | 
|  | typeArguments.resolve((t) => t.resolve()); | 
|  | List<Element>? newElements = elements.resolve((e) => e.resolve()); | 
|  | return newTypeArguments == null && newElements == null | 
|  | ? null | 
|  | : new ListLiteral( | 
|  | newTypeArguments ?? typeArguments, newElements ?? elements); | 
|  | } | 
|  | } | 
|  |  | 
|  | class SetOrMapLiteral extends Expression { | 
|  | final List<TypeAnnotation> typeArguments; | 
|  | final List<Element> elements; | 
|  |  | 
|  | SetOrMapLiteral(this.typeArguments, this.elements); | 
|  |  | 
|  | @override | 
|  | String toString() => 'SetOrMapLiteral($typeArguments,$elements)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | List<TypeAnnotation>? newTypeArguments = | 
|  | typeArguments.resolve((t) => t.resolve()); | 
|  | List<Element>? newElements = elements.resolve((e) => e.resolve()); | 
|  | return newTypeArguments == null && newElements == null | 
|  | ? null | 
|  | : new SetOrMapLiteral( | 
|  | newTypeArguments ?? typeArguments, newElements ?? elements); | 
|  | } | 
|  | } | 
|  |  | 
|  | class RecordLiteral extends Expression { | 
|  | final List<RecordField> fields; | 
|  |  | 
|  | RecordLiteral(this.fields); | 
|  |  | 
|  | @override | 
|  | String toString() => 'RecordLiteral($fields)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | List<RecordField>? newFields = fields.resolve((e) => e.resolve()); | 
|  | return newFields == null ? null : new RecordLiteral(newFields); | 
|  | } | 
|  | } | 
|  |  | 
|  | class IfNull extends Expression { | 
|  | final Expression left; | 
|  | final Expression right; | 
|  |  | 
|  | IfNull(this.left, this.right); | 
|  |  | 
|  | @override | 
|  | String toString() => 'IfNull($left,$right)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newLeft = left.resolve(); | 
|  | Expression? newRight = right.resolve(); | 
|  | return newLeft == null && newRight == null | 
|  | ? null | 
|  | : new IfNull(newLeft ?? left, newRight ?? right); | 
|  | } | 
|  | } | 
|  |  | 
|  | enum LogicalOperator { | 
|  | and('&&'), | 
|  | or('||'), | 
|  | ; | 
|  |  | 
|  | final String text; | 
|  |  | 
|  | const LogicalOperator(this.text); | 
|  | } | 
|  |  | 
|  | class LogicalExpression extends Expression { | 
|  | final Expression left; | 
|  | final LogicalOperator operator; | 
|  | final Expression right; | 
|  |  | 
|  | LogicalExpression(this.left, this.operator, this.right); | 
|  |  | 
|  | @override | 
|  | String toString() => 'LogicalExpression($left,$operator,$right)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newLeft = left.resolve(); | 
|  | Expression? newRight = right.resolve(); | 
|  | return newLeft == null && newRight == null | 
|  | ? null | 
|  | : new LogicalExpression(newLeft ?? left, operator, newRight ?? right); | 
|  | } | 
|  | } | 
|  |  | 
|  | class EqualityExpression extends Expression { | 
|  | final Expression left; | 
|  | final Expression right; | 
|  | final bool isNotEquals; | 
|  |  | 
|  | EqualityExpression(this.left, this.right, {required this.isNotEquals}); | 
|  |  | 
|  | @override | 
|  | String toString() => | 
|  | 'EqualityExpression($left,$right,isNotEquals=$isNotEquals)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newLeft = left.resolve(); | 
|  | Expression? newRight = right.resolve(); | 
|  | return newLeft == null && newRight == null | 
|  | ? null | 
|  | : new EqualityExpression(newLeft ?? left, newRight ?? right, | 
|  | isNotEquals: isNotEquals); | 
|  | } | 
|  | } | 
|  |  | 
|  | enum BinaryOperator { | 
|  | greaterThan('>'), | 
|  | greaterThanOrEqual('>='), | 
|  | lessThan('<'), | 
|  | lessThanOrEqual('<='), | 
|  | shiftLeft('<<'), | 
|  | signedShiftRight('>>'), | 
|  | unsignedShiftRight('>>>'), | 
|  | plus('+'), | 
|  | minus('-'), | 
|  | times('*'), | 
|  | divide('/'), | 
|  | integerDivide('~/'), | 
|  | modulo('%'), | 
|  | bitwiseOr('|'), | 
|  | bitwiseAnd('&'), | 
|  | bitwiseXor('^'), | 
|  | ; | 
|  |  | 
|  | final String text; | 
|  |  | 
|  | const BinaryOperator(this.text); | 
|  | } | 
|  |  | 
|  | class BinaryExpression extends Expression { | 
|  | final Expression left; | 
|  | final BinaryOperator operator; | 
|  | final Expression right; | 
|  |  | 
|  | BinaryExpression(this.left, this.operator, this.right); | 
|  |  | 
|  | @override | 
|  | String toString() => 'BinaryExpression($left,$operator,$right)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newLeft = left.resolve(); | 
|  | Expression? newRight = right.resolve(); | 
|  | return newLeft == null && newRight == null | 
|  | ? null | 
|  | : new BinaryExpression(newLeft ?? left, operator, newRight ?? right); | 
|  | } | 
|  | } | 
|  |  | 
|  | enum UnaryOperator { | 
|  | minus('-'), | 
|  | bang('!'), | 
|  | tilde('~'), | 
|  | ; | 
|  |  | 
|  | final String text; | 
|  |  | 
|  | const UnaryOperator(this.text); | 
|  | } | 
|  |  | 
|  | class UnaryExpression extends Expression { | 
|  | final UnaryOperator operator; | 
|  | final Expression expression; | 
|  |  | 
|  | UnaryExpression(this.operator, this.expression); | 
|  |  | 
|  | @override | 
|  | String toString() => 'UnaryExpression($operator,$expression)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newExpression = expression.resolve(); | 
|  | return newExpression == null | 
|  | ? null | 
|  | : new UnaryExpression(operator, newExpression); | 
|  | } | 
|  | } | 
|  |  | 
|  | class IsTest extends Expression { | 
|  | final Expression expression; | 
|  | final TypeAnnotation type; | 
|  | final bool isNot; | 
|  |  | 
|  | IsTest(this.expression, this.type, {required this.isNot}); | 
|  |  | 
|  | @override | 
|  | String toString() => 'IsTest($expression,$type,isNot=$isNot)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | TypeAnnotation? newType = type.resolve(); | 
|  | Expression? newExpression = expression.resolve(); | 
|  | return newType == null && newExpression == null | 
|  | ? null | 
|  | : new IsTest(newExpression ?? expression, newType ?? type, | 
|  | isNot: isNot); | 
|  | } | 
|  | } | 
|  |  | 
|  | class AsExpression extends Expression { | 
|  | final Expression expression; | 
|  | final TypeAnnotation type; | 
|  |  | 
|  | AsExpression(this.expression, this.type); | 
|  |  | 
|  | @override | 
|  | String toString() => 'AsExpression($expression,$type)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | TypeAnnotation? newType = type.resolve(); | 
|  | Expression? newExpression = expression.resolve(); | 
|  | return newType == null && newExpression == null | 
|  | ? null | 
|  | : new AsExpression(newExpression ?? expression, newType ?? type); | 
|  | } | 
|  | } | 
|  |  | 
|  | class NullCheck extends Expression { | 
|  | final Expression expression; | 
|  |  | 
|  | NullCheck(this.expression); | 
|  |  | 
|  | @override | 
|  | String toString() => 'NullCheck($expression)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | Expression? newExpression = expression.resolve(); | 
|  | return newExpression == null ? null : new NullCheck(newExpression); | 
|  | } | 
|  | } | 
|  |  | 
|  | class UnresolvedExpression extends Expression { | 
|  | final Unresolved unresolved; | 
|  |  | 
|  | UnresolvedExpression(this.unresolved); | 
|  |  | 
|  | @override | 
|  | String toString() => 'UnresolvedExpression($unresolved)'; | 
|  |  | 
|  | @override | 
|  | Expression? resolve() { | 
|  | return unresolved.resolveAsExpression(); | 
|  | } | 
|  | } |