| // Copyright (c) 2023, 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. |
| |
| /// Helper library for creating external AST nodes during inference. |
| |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/core_types.dart'; |
| import 'package:kernel/names.dart'; |
| |
| import 'internal_ast.dart'; |
| |
| /// Creates an invocation of the [target] constructor with the given |
| /// [arguments]. |
| ConstructorInvocation createConstructorInvocation( |
| Constructor target, |
| Arguments arguments, { |
| required int fileOffset, |
| bool isConst = false, |
| }) { |
| return new ConstructorInvocation(target, arguments, isConst: isConst) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a static invocation of [target] with the given arguments. |
| StaticInvocation createStaticInvocation( |
| Procedure target, |
| Arguments arguments, { |
| required int fileOffset, |
| bool isConst = false, |
| }) { |
| return new StaticInvocation(target, arguments, isConst: isConst) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a super method invocation of [target] with the given arguments. |
| SuperMethodInvocation createSuperMethodInvocation( |
| Expression receiver, |
| Name name, |
| Procedure target, |
| Arguments arguments, { |
| required int fileOffset, |
| }) { |
| return new SuperMethodInvocation(receiver, name, arguments, target) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a `== null` test on [expression]. |
| EqualsNull createEqualsNull(Expression expression, {required int fileOffset}) { |
| return new EqualsNull(expression)..fileOffset = fileOffset; |
| } |
| |
| /// Creates a boolean literal of [value]. |
| BoolLiteral createBoolLiteral(bool value, {required int fileOffset}) { |
| return new BoolLiteral(value)..fileOffset = fileOffset; |
| } |
| |
| /// Creates an integer literal of [value]. |
| Expression createIntLiteral( |
| CoreTypes coreTypes, |
| int value, { |
| required int fileOffset, |
| }) { |
| if (value < 0) { |
| /// The web backends need this to be encoded as a unary minus on the |
| /// positive value. |
| return new InstanceInvocation( |
| InstanceAccessKind.Instance, |
| new IntLiteral(-value)..fileOffset = fileOffset, |
| unaryMinusName, |
| new Arguments([])..fileOffset = fileOffset, |
| interfaceTarget: coreTypes.intUnaryMinus, |
| functionType: coreTypes.intUnaryMinus.getterType as FunctionType, |
| )..fileOffset = fileOffset; |
| } else { |
| return new IntLiteral(value)..fileOffset = fileOffset; |
| } |
| } |
| |
| /// Creates a string literal of [value]. |
| StringLiteral createStringLiteral(String value, {required int fileOffset}) { |
| return new StringLiteral(value)..fileOffset = fileOffset; |
| } |
| |
| /// Creates a null literal. |
| NullLiteral createNullLiteral({required int fileOffset}) { |
| return new NullLiteral()..fileOffset = fileOffset; |
| } |
| |
| /// Creates a conditional expression of the [condition] and the [then] and |
| /// [otherwise] branches with the given [staticType] of the resulting |
| /// expression. |
| ConditionalExpression createConditionalExpression( |
| Expression condition, |
| Expression then, |
| Expression otherwise, { |
| required DartType staticType, |
| required int fileOffset, |
| }) { |
| return new ConditionalExpression(condition, then, otherwise, staticType) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a [Let] of [variable] with the given [body] using |
| /// `variable.fileOffset` as the file offset for the let. |
| Let createLet(VariableDeclaration variable, Expression body) { |
| return new Let(variable, body)..fileOffset = variable.fileOffset; |
| } |
| |
| /// Creates a [Let] with the [effect] as the variable initializer and the |
| /// [result] as the body of the [Let] expression and using |
| /// `effect.fileOffset` as the file offset for the let. |
| Let createLetEffect({required Expression effect, required Expression result}) { |
| return new Let(createVariableCache(effect, const DynamicType()), result) |
| ..fileOffset = effect.fileOffset; |
| } |
| |
| /// Creates a [VariableDeclaration] for caching [expression] of the static |
| /// [type] using `expression.fileOffset` as the file offset for the declaration. |
| VariableDeclaration createVariableCache(Expression expression, DartType type) { |
| return new VariableDeclaration.forValue(expression, type: type) |
| ..fileOffset = expression.fileOffset; |
| } |
| |
| /// Creates an uninitialized [VariableDeclaration] of the static [type]. |
| VariableDeclaration createUninitializedVariable( |
| DartType type, { |
| required int fileOffset, |
| }) { |
| return new VariableDeclaration(null, type: type, isSynthesized: true) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates an initialized (but mutable) [VariableDeclaration] of the static |
| /// [type]. |
| VariableDeclaration createInitializedVariable( |
| Expression expression, |
| DartType type, { |
| required int fileOffset, |
| String? name, |
| }) { |
| return new VariableDeclaration( |
| name, |
| initializer: expression, |
| type: type, |
| isSynthesized: true, |
| )..fileOffset = fileOffset; |
| } |
| |
| /// Creates a [VariableDeclaration] for [expression] with the static [type] |
| /// using `expression.fileOffset` as the file offset for the declaration. |
| // TODO(johnniwinther): Merge the use of this with [createVariableCache]. |
| VariableDeclaration createVariable(Expression expression, DartType type) { |
| assert(expression is! ThisExpression); |
| return new VariableDeclaration.forValue(expression, type: type) |
| ..fileOffset = expression.fileOffset; |
| } |
| |
| /// Creates a [VariableGet] of [variable] using `variable.fileOffset` as the |
| /// file offset for the expression. |
| VariableGet createVariableGet( |
| VariableDeclaration variable, { |
| DartType? promotedType, |
| }) { |
| return new VariableGet(variable) |
| ..fileOffset = variable.fileOffset |
| ..promotedType = promotedType != variable.type ? promotedType : null; |
| } |
| |
| /// Creates a [VariableSet] of [variable] with the [value]. |
| Expression createVariableSet( |
| VariableDeclaration variable, |
| Expression value, { |
| bool allowFinalAssignment = false, |
| required int fileOffset, |
| }) { |
| if (variable is VariableDeclarationImpl && variable.lateSetter != null) { |
| return createLocalFunctionInvocation( |
| variable.lateSetter!, |
| arguments: createArguments([value], fileOffset: fileOffset), |
| fileOffset: fileOffset, |
| ); |
| } else { |
| assert( |
| allowFinalAssignment || variable.isAssignable, |
| "Cannot assign to variable $variable", |
| ); |
| return new VariableSet(variable, value)..fileOffset = fileOffset; |
| } |
| } |
| |
| /// Creates an invocation of the local function [variable] with the provided |
| /// [arguments]. |
| LocalFunctionInvocation createLocalFunctionInvocation( |
| VariableDeclaration variable, { |
| Arguments? arguments, |
| required int fileOffset, |
| }) { |
| return new LocalFunctionInvocation( |
| variable, |
| arguments ?? // Coverage-ignore(suite): Not run. |
| createArguments([], fileOffset: fileOffset) |
| ..fileOffset = fileOffset, |
| functionType: variable.type as FunctionType, |
| )..fileOffset = fileOffset; |
| } |
| |
| /// Creates a [Not] of [operand]. |
| Not createNot(Expression operand) { |
| return new Not(operand)..fileOffset = operand.fileOffset; |
| } |
| |
| /// Creates a logical and expression of [left] and [right]. |
| LogicalExpression createAndExpression( |
| Expression left, |
| Expression right, { |
| required int fileOffset, |
| }) { |
| return new LogicalExpression(left, LogicalExpressionOperator.AND, right) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a logical or expression of [left] and [right]. |
| LogicalExpression createOrExpression( |
| Expression left, |
| Expression right, { |
| required int fileOffset, |
| }) { |
| return new LogicalExpression(left, LogicalExpressionOperator.OR, right) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates an is-test on [operand] against [type]. |
| IsExpression createIsExpression( |
| Expression operand, |
| DartType type, { |
| required int fileOffset, |
| }) { |
| return new IsExpression(operand, type)..fileOffset = fileOffset; |
| } |
| |
| /// Creates an as-cast on [operand] against [type]. |
| AsExpression createAsExpression( |
| Expression operand, |
| DartType type, { |
| required bool forNonNullableByDefault, |
| bool isUnchecked = false, |
| bool isCovarianceCheck = false, |
| required int fileOffset, |
| }) { |
| return new AsExpression(operand, type) |
| ..fileOffset = fileOffset |
| ..isUnchecked = isUnchecked |
| ..isCovarianceCheck = isCovarianceCheck; |
| } |
| |
| /// Creates a [NullCheck] of [expression]. |
| NullCheck createNullCheck(Expression expression, {required int fileOffset}) { |
| return new NullCheck(expression)..fileOffset = fileOffset; |
| } |
| |
| /// Creates a block expression using [body] as the body and [value] as the |
| /// resulting value. |
| BlockExpression createBlockExpression( |
| Block body, |
| Expression value, { |
| required int fileOffset, |
| }) { |
| return new BlockExpression(body, value)..fileOffset = fileOffset; |
| } |
| |
| /// Creates a throw of [expression] using the file offset of [expression] for |
| /// the throw expression. |
| Throw createThrow(Expression expression, {bool forErrorHandling = false}) { |
| return new Throw(expression) |
| ..fileOffset = expression.fileOffset |
| ..forErrorHandling = forErrorHandling; |
| } |
| |
| /// Creates an [ExpressionStatement] of [expression] using the file offset of |
| /// [expression] for the file offset of the statement. |
| ExpressionStatement createExpressionStatement(Expression expression) { |
| return new ExpressionStatement(expression) |
| ..fileOffset = expression.fileOffset; |
| } |
| |
| /// Creates an if statement with the [condition], [then] branch and [otherwise] |
| /// as the optional else branch. |
| IfStatement createIfStatement( |
| Expression condition, |
| Statement then, { |
| Statement? otherwise, |
| required int fileOffset, |
| }) { |
| return new IfStatement(condition, then, otherwise)..fileOffset = fileOffset; |
| } |
| |
| /// Creates a break statement with the given [target]. |
| BreakStatement createBreakStatement( |
| LabeledStatement target, { |
| required int fileOffset, |
| }) { |
| return new BreakStatement(target)..fileOffset = fileOffset; |
| } |
| |
| /// Creates a block containing the [statements]. |
| Block createBlock(List<Statement> statements, {required int fileOffset}) { |
| return new Block(statements) |
| ..fileOffset = fileOffset |
| ..fileEndOffset = fileOffset; |
| } |
| |
| /// Creates an [Arguments] object for the [positional] and [named] arguments, |
| /// and [types] as the type arguments. |
| Arguments createArguments( |
| List<Expression> positional, { |
| List<DartType>? types, |
| List<NamedExpression>? named, |
| required int fileOffset, |
| }) { |
| return new Arguments(positional, types: types, named: named) |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a switch case for the case [expressions] and their corresponding |
| /// file offsets in [expressionOffsets] with the given [body]. |
| SwitchCase createSwitchCase( |
| List<Expression> expressions, |
| List<int> expressionOffsets, |
| Statement body, { |
| required bool isDefault, |
| required int fileOffset, |
| }) { |
| return new SwitchCase( |
| expressions, |
| expressionOffsets, |
| body, |
| isDefault: isDefault, |
| )..fileOffset = fileOffset; |
| } |
| |
| /// Create a switch statement on the [expression] with the given [cases]. If |
| /// the switch is known to be exhaustive and without a default case, |
| /// [isExplicitlyExhaustive] should be set to `true`. |
| /// |
| /// The [expressionType] is the static type of the switch expression. |
| SwitchStatement createSwitchStatement( |
| Expression expression, |
| List<SwitchCase> cases, { |
| required bool isExplicitlyExhaustive, |
| required int fileOffset, |
| required DartType expressionType, |
| }) { |
| return new SwitchStatement( |
| expression, |
| cases, |
| isExplicitlyExhaustive: isExplicitlyExhaustive, |
| ) |
| ..expressionType = expressionType |
| ..fileOffset = fileOffset; |
| } |
| |
| /// Creates a labeled statement that serves as a label for [statement]. |
| LabeledStatement createLabeledStatement( |
| Statement statement, { |
| required int fileOffset, |
| }) { |
| return new LabeledStatement(statement)..fileOffset = fileOffset; |
| } |
| |
| InvalidInitializer createInvalidInitializer( |
| InvalidExpression expression, { |
| bool isSuperInitializer = false, |
| bool isRedirectingInitializer = false, |
| }) { |
| return new InvalidInitializer(expression.message) |
| ..fileOffset = expression.fileOffset |
| ..isSuperInitializer = isSuperInitializer |
| ..isRedirectingInitializer = isRedirectingInitializer; |
| } |