e30cd0322c
This separates VariableDeclaration from Statement. VariableDeclaration no longer implements Statement and variable declared in a block or in a for-statement are now wrapped by a VariableStatement. Currently there are two VariableStatement implementations; LegacyVariableStatement for variables in the current model, called LegacyVariable, and VariableInitialization for variables used in the new, still experimental, encoding that supports scope computation. This CL is a step towards realigning the AST nodes to the new model in which each kind of variable has its own distinct subclass. (LocalVariable, PositionalParameter, NamedParameter, SyntheticVariable, etc.) Note that it is not the intent to use VariableStatement in ForStatement going forward but that will be handled in a follow-up. TEST=existing. Change-Id: I5b309cd62c9b138f95b74fb054686edffa49a393 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/502681 Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
1647 lines
46 KiB
Dart
1647 lines
46 KiB
Dart
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
library kernel.clone;
|
|
|
|
import 'ast.dart';
|
|
import 'type_algebra.dart';
|
|
|
|
/// Visitor that return a clone of a tree, maintaining references to cloned
|
|
/// objects.
|
|
///
|
|
/// This class does not clone members. For that, use the
|
|
/// [CloneVisitorWithMembers] and setup references properly.
|
|
class CloneVisitorNotMembers
|
|
with
|
|
TreeVisitorExperimentExclusionMixin<TreeNode>,
|
|
ExpressionVisitorExperimentExclusionMixin<TreeNode>,
|
|
StatementVisitorExperimentExclusionMixin<TreeNode>
|
|
implements TreeVisitor<TreeNode> {
|
|
final Map<VariableDeclaration, VariableDeclaration> _variables =
|
|
<VariableDeclaration, VariableDeclaration>{};
|
|
final Map<LabeledStatement, LabeledStatement> labels =
|
|
<LabeledStatement, LabeledStatement>{};
|
|
final Map<SwitchCase, SwitchCase> switchCases = <SwitchCase, SwitchCase>{};
|
|
final Map<TypeParameter, DartType> typeSubstitution;
|
|
final Map<TypeParameter, TypeParameter> typeParams;
|
|
bool cloneAnnotations;
|
|
|
|
/// Creates an instance of the cloning visitor for Kernel ASTs.
|
|
///
|
|
/// The boolean value of [cloneAnnotations] tells if the annotations on the
|
|
/// outline elements in the source AST should be cloned to the target AST. The
|
|
/// annotations in procedure bodies are cloned unconditionally.
|
|
CloneVisitorNotMembers({
|
|
Map<TypeParameter, DartType>? typeSubstitution,
|
|
Map<TypeParameter, TypeParameter>? typeParams,
|
|
Map<StructuralParameter, StructuralParameter>? structuralParameters,
|
|
this.cloneAnnotations = true,
|
|
}) : this.typeSubstitution = ensureMutable(typeSubstitution),
|
|
this.typeParams = typeParams ?? <TypeParameter, TypeParameter>{};
|
|
|
|
static Map<TypeParameter, DartType> ensureMutable(
|
|
Map<TypeParameter, DartType>? map,
|
|
) {
|
|
// We need to mutate this map, so make sure we don't use a constant map.
|
|
if (map == null || map.isEmpty) {
|
|
return <TypeParameter, DartType>{};
|
|
}
|
|
return map;
|
|
}
|
|
|
|
/// Returns the clone of [variable] or `null` if no clone has been created
|
|
/// for variable.
|
|
VariableDeclaration? getVariableClone(VariableDeclaration variable) {
|
|
return _variables[variable];
|
|
}
|
|
|
|
/// Registers [clone] as the clone for [variable].
|
|
///
|
|
/// Returns the [clone].
|
|
VariableDeclaration setVariableClone(
|
|
VariableDeclaration variable,
|
|
VariableDeclaration clone,
|
|
) {
|
|
return _variables[variable] = clone;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLibrary(Library node) {
|
|
throw 'Cloning of libraries is not implemented';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitClass(Class node) {
|
|
throw 'Cloning of classes is not implemented';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitExtension(Extension node) {
|
|
throw 'Cloning of extensions is not implemented';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
|
|
throw 'Cloning of extension type declarations is not implemented';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConstructor(Constructor node) {
|
|
throw 'Cloning of constructors is not implemented here';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitProcedure(Procedure node) {
|
|
throw 'Cloning of procedures is not implemented here';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitField(Field node) {
|
|
throw 'Cloning of fields is not implemented here';
|
|
}
|
|
|
|
// The currently active file uri where we are cloning [TreeNode]s from. If
|
|
// this is set to `null` we cannot clone file offsets to newly created nodes.
|
|
// The [_cloneFileOffset] helper function will ensure this.
|
|
Uri? _activeFileUri;
|
|
|
|
// If we don't know the file uri we are cloning elements from, it's not safe
|
|
// to clone file offsets either.
|
|
int _cloneFileOffset(int fileOffset) {
|
|
return _activeFileUri == null ? TreeNode.noOffset : fileOffset;
|
|
}
|
|
|
|
bool _assertFileUriTarget(TreeNode node, TreeNode clone) {
|
|
if (node is FileUriNode && clone is FileUriNode) {
|
|
if (node.fileUri != clone.fileUri) {
|
|
assert(
|
|
false,
|
|
"Original and clone disagrees on file uri: "
|
|
"${node.fileUri} vs ${clone.fileUri}",
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
} else if (node is! FileUriNode && clone is! FileUriNode) {
|
|
return true;
|
|
}
|
|
assert(false, "Original and clone disagrees on being a file uri node.");
|
|
return false;
|
|
}
|
|
|
|
T clone<T extends TreeNode>(T node) {
|
|
final Uri? activeFileUriSaved = _activeFileUri;
|
|
if (node is FileUriNode) {
|
|
_activeFileUri = node.fileUri;
|
|
}
|
|
final TreeNode result = node.accept(this)
|
|
..fileOffset = _cloneFileOffset(node.fileOffset);
|
|
|
|
assert(_assertFileUriTarget(node, result));
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
return result as T;
|
|
}
|
|
|
|
T? cloneOptional<T extends TreeNode>(T? node) {
|
|
if (node == null) return null;
|
|
final Uri? activeFileUriSaved = _activeFileUri;
|
|
if (node is FileUriNode) {
|
|
_activeFileUri = node.fileUri;
|
|
}
|
|
TreeNode? result = node.accept(this);
|
|
if (result != null) {
|
|
result.fileOffset = _cloneFileOffset(node.fileOffset);
|
|
assert(_assertFileUriTarget(node, result));
|
|
}
|
|
_activeFileUri = activeFileUriSaved;
|
|
return result as T?;
|
|
}
|
|
|
|
/// Root entry point for cloning a subtree within the same context where the
|
|
/// file offsets are valid.
|
|
T cloneInContext<T extends TreeNode>(T node) {
|
|
assert(_activeFileUri == null);
|
|
_activeFileUri = _activeFileUriFromContext(node);
|
|
final TreeNode result = clone<T>(node);
|
|
_activeFileUri = null;
|
|
return result as T;
|
|
}
|
|
|
|
Uri? _activeFileUriFromContext(TreeNode? node) {
|
|
while (node != null) {
|
|
if (node is FileUriNode) {
|
|
return node.fileUri;
|
|
}
|
|
node = node.parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
DartType visitType(DartType type) {
|
|
return substitute(type, typeSubstitution);
|
|
}
|
|
|
|
Constant visitConstant(Constant constant) {
|
|
return constant;
|
|
}
|
|
|
|
DartType? visitOptionalType(DartType? type) {
|
|
return type == null ? null : substitute(type, typeSubstitution);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInvalidExpression(InvalidExpression node) {
|
|
return new InvalidExpression(
|
|
node.message,
|
|
node.expression != null ? clone(node.expression!) : null,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitVariableGet(VariableGet node) {
|
|
VariableDeclaration? variable = getVariableClone(node.variable);
|
|
assert(variable != null, "Missing clone for variable ${node.variable}.");
|
|
return new VariableGet(variable!, visitOptionalType(node.promotedType));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitVariableSet(VariableSet node) {
|
|
VariableDeclaration? variable = getVariableClone(node.variable);
|
|
assert(variable != null, "Missing clone for variable ${node.variable}.");
|
|
return new VariableSet(variable!, clone(node.value));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAbstractSuperPropertyGet(AbstractSuperPropertyGet node) {
|
|
return new AbstractSuperPropertyGet.byReference(
|
|
clone(node.receiver),
|
|
node.name,
|
|
node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAbstractSuperPropertySet(AbstractSuperPropertySet node) {
|
|
return new AbstractSuperPropertySet.byReference(
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.value),
|
|
node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSuperPropertyGet(SuperPropertyGet node) {
|
|
return new SuperPropertyGet.byReference(
|
|
clone(node.receiver),
|
|
node.name,
|
|
node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSuperPropertySet(SuperPropertySet node) {
|
|
return new SuperPropertySet.byReference(
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.value),
|
|
node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitStaticGet(StaticGet node) {
|
|
return new StaticGet.byReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitStaticSet(StaticSet node) {
|
|
return new StaticSet.byReference(node.targetReference, clone(node.value));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAbstractSuperMethodInvocation(
|
|
AbstractSuperMethodInvocation node,
|
|
) {
|
|
return new AbstractSuperMethodInvocation.byReference(
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.arguments),
|
|
node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSuperMethodInvocation(SuperMethodInvocation node) {
|
|
return new SuperMethodInvocation.byReference(
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.arguments),
|
|
node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitStaticInvocation(StaticInvocation node) {
|
|
return new StaticInvocation.byReference(
|
|
node.targetReference,
|
|
clone(node.arguments),
|
|
isConst: node.isConst,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConstructorInvocation(ConstructorInvocation node) {
|
|
return new ConstructorInvocation.byReference(
|
|
node.targetReference,
|
|
clone(node.arguments),
|
|
isConst: node.isConst,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRedirectingFactoryInvocation(
|
|
RedirectingFactoryInvocation node,
|
|
) {
|
|
return new RedirectingFactoryInvocation.byReference(
|
|
node.redirectingFactoryTargetReference,
|
|
clone(node.expression),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNot(Not node) {
|
|
return new Not(clone(node.operand));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNullCheck(NullCheck node) {
|
|
return new NullCheck(clone(node.operand));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLogicalExpression(LogicalExpression node) {
|
|
return new LogicalExpression(
|
|
clone(node.left),
|
|
node.operatorEnum,
|
|
clone(node.right),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConditionalExpression(ConditionalExpression node) {
|
|
return new ConditionalExpression(
|
|
clone(node.condition),
|
|
clone(node.then),
|
|
clone(node.otherwise),
|
|
visitType(node.staticType),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitStringConcatenation(StringConcatenation node) {
|
|
return new StringConcatenation(node.expressions.map(clone).toList());
|
|
}
|
|
|
|
@override
|
|
TreeNode visitListConcatenation(ListConcatenation node) {
|
|
return new ListConcatenation(
|
|
node.lists.map(clone).toList(),
|
|
typeArgument: visitType(node.typeArgument),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSetConcatenation(SetConcatenation node) {
|
|
return new SetConcatenation(
|
|
node.sets.map(clone).toList(),
|
|
typeArgument: visitType(node.typeArgument),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitMapConcatenation(MapConcatenation node) {
|
|
return new MapConcatenation(
|
|
node.maps.map(clone).toList(),
|
|
keyType: visitType(node.keyType),
|
|
valueType: visitType(node.valueType),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstanceCreation(InstanceCreation node) {
|
|
final Map<Reference, Expression> fieldValues = <Reference, Expression>{};
|
|
node.fieldValues.forEach((Reference fieldRef, Expression value) {
|
|
fieldValues[fieldRef] = clone(value);
|
|
});
|
|
return new InstanceCreation(
|
|
node.classReference,
|
|
node.typeArguments.map(visitType).toList(),
|
|
fieldValues,
|
|
node.asserts.map(clone).toList(),
|
|
node.unusedArguments.map(clone).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFileUriExpression(FileUriExpression node) {
|
|
return new FileUriExpression(clone(node.expression), _activeFileUri!);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitIsExpression(IsExpression node) {
|
|
return new IsExpression(clone(node.operand), visitType(node.type));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAsExpression(AsExpression node) {
|
|
return new AsExpression(clone(node.operand), visitType(node.type))
|
|
..flags = node.flags;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSymbolLiteral(SymbolLiteral node) {
|
|
return new SymbolLiteral(node.value);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitTypeLiteral(TypeLiteral node) {
|
|
return new TypeLiteral(visitType(node.type));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitThisExpression(ThisExpression node) {
|
|
return new ThisExpression();
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRethrow(Rethrow node) {
|
|
return new Rethrow();
|
|
}
|
|
|
|
@override
|
|
TreeNode visitThrow(Throw node) {
|
|
return new Throw(clone(node.expression))..flags = node.flags;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitListLiteral(ListLiteral node) {
|
|
return new ListLiteral(
|
|
node.expressions.map(clone).toList(),
|
|
typeArgument: visitType(node.typeArgument),
|
|
isConst: node.isConst,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSetLiteral(SetLiteral node) {
|
|
return new SetLiteral(
|
|
node.expressions.map(clone).toList(),
|
|
typeArgument: visitType(node.typeArgument),
|
|
isConst: node.isConst,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitMapLiteral(MapLiteral node) {
|
|
return new MapLiteral(
|
|
node.entries.map(clone).toList(),
|
|
keyType: visitType(node.keyType),
|
|
valueType: visitType(node.valueType),
|
|
isConst: node.isConst,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitMapLiteralEntry(MapLiteralEntry node) {
|
|
return new MapLiteralEntry(clone(node.key), clone(node.value));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRecordLiteral(RecordLiteral node) {
|
|
return new RecordLiteral(
|
|
node.positional.map(clone).toList(),
|
|
node.named.map(clone).toList(),
|
|
visitType(node.recordType) as RecordType,
|
|
isConst: node.isConst,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAwaitExpression(AwaitExpression node) {
|
|
return new AwaitExpression(clone(node.operand))
|
|
..runtimeCheckType = node.runtimeCheckType != null
|
|
? visitType(node.runtimeCheckType!)
|
|
: null;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFunctionExpression(FunctionExpression node) {
|
|
return new FunctionExpression(clone(node.function))..id = node.id;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConstantExpression(ConstantExpression node) {
|
|
if (node is FileUriConstantExpression) {
|
|
return new FileUriConstantExpression(
|
|
visitConstant(node.constant),
|
|
type: visitType(node.type),
|
|
fileUri: node.fileUri,
|
|
);
|
|
}
|
|
return new ConstantExpression(
|
|
visitConstant(node.constant),
|
|
visitType(node.type),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitStringLiteral(StringLiteral node) {
|
|
return new StringLiteral(node.value);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitIntLiteral(IntLiteral node) {
|
|
return new IntLiteral(node.value);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitDoubleLiteral(DoubleLiteral node) {
|
|
return new DoubleLiteral(node.value);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitBoolLiteral(BoolLiteral node) {
|
|
return new BoolLiteral(node.value);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNullLiteral(NullLiteral node) {
|
|
return new NullLiteral();
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLet(Let node) {
|
|
VariableDeclaration newVariable = clone(node.variable);
|
|
return new Let(newVariable, clone(node.body));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitBlockExpression(BlockExpression node) {
|
|
return new BlockExpression(clone(node.body), clone(node.value));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRecordIndexGet(RecordIndexGet node) {
|
|
return new RecordIndexGet(
|
|
clone(node.receiver),
|
|
visitType(node.receiverType) as RecordType,
|
|
node.index,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRecordNameGet(RecordNameGet node) {
|
|
return new RecordNameGet(
|
|
clone(node.receiver),
|
|
visitType(node.receiverType) as RecordType,
|
|
node.name,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitExpressionStatement(ExpressionStatement node) {
|
|
return new ExpressionStatement(clone(node.expression));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitBlock(Block node) {
|
|
return new Block(node.statements.map(clone).toList())
|
|
..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAssertBlock(AssertBlock node) {
|
|
return new AssertBlock(node.statements.map(clone).toList());
|
|
}
|
|
|
|
@override
|
|
TreeNode visitEmptyStatement(EmptyStatement node) {
|
|
return new EmptyStatement();
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAssertStatement(AssertStatement node) {
|
|
return new AssertStatement(
|
|
clone(node.condition),
|
|
conditionStartOffset: node.conditionStartOffset,
|
|
conditionEndOffset: node.conditionEndOffset,
|
|
message: cloneOptional(node.message),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLabeledStatement(LabeledStatement node) {
|
|
LabeledStatement newNode = new LabeledStatement(null);
|
|
labels[node] = newNode;
|
|
newNode.body = clone(node.body)..parent = newNode;
|
|
return newNode;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitBreakStatement(BreakStatement node) {
|
|
return new BreakStatement(labels[node.target]!);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitWhileStatement(WhileStatement node) {
|
|
return new WhileStatement(clone(node.condition), clone(node.body));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitDoStatement(DoStatement node) {
|
|
return new DoStatement(clone(node.body), clone(node.condition));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitForStatement(ForStatement node) {
|
|
List<VariableStatement> variables = node.variables.map(clone).toList();
|
|
return new ForStatement(
|
|
variables,
|
|
cloneOptional(node.condition),
|
|
node.updates.map(clone).toList(),
|
|
clone(node.body),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitForInStatement(ForInStatement node) {
|
|
VariableDeclaration newVariable = clone(node.variable);
|
|
return new ForInStatement(
|
|
newVariable,
|
|
clone(node.iterable),
|
|
clone(node.body),
|
|
isAsync: node.isAsync,
|
|
)..bodyOffset = node.bodyOffset;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSwitchStatement(SwitchStatement node) {
|
|
for (SwitchCase switchCase in node.cases) {
|
|
switchCases[switchCase] = new SwitchCase(
|
|
switchCase.expressions.map(clone).toList(),
|
|
new List<int>.of(switchCase.expressionOffsets),
|
|
dummyStatement,
|
|
isDefault: switchCase.isDefault,
|
|
);
|
|
}
|
|
return new SwitchStatement(
|
|
clone(node.expression),
|
|
node.cases.map(clone).toList(),
|
|
isExplicitlyExhaustive: node.isExplicitlyExhaustive,
|
|
)..expressionTypeInternal = visitOptionalType(node.expressionTypeInternal);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSwitchCase(SwitchCase node) {
|
|
SwitchCase switchCase = switchCases[node]!;
|
|
switchCase.body = clone(node.body)..parent = switchCase;
|
|
return switchCase;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitContinueSwitchStatement(ContinueSwitchStatement node) {
|
|
return new ContinueSwitchStatement(switchCases[node.target]!);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitIfStatement(IfStatement node) {
|
|
return new IfStatement(
|
|
clone(node.condition),
|
|
clone(node.then),
|
|
cloneOptional(node.otherwise),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitReturnStatement(ReturnStatement node) {
|
|
return new ReturnStatement(cloneOptional(node.expression));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitTryCatch(TryCatch node) {
|
|
return new TryCatch(
|
|
clone(node.body),
|
|
node.catches.map(clone).toList(),
|
|
isSynthetic: node.isSynthetic,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitCatch(Catch node) {
|
|
VariableDeclaration? newException = cloneOptional(node.exception);
|
|
VariableDeclaration? newStackTrace = cloneOptional(node.stackTrace);
|
|
return new Catch(
|
|
newException,
|
|
clone(node.body),
|
|
stackTrace: newStackTrace,
|
|
guard: visitType(node.guard),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitTryFinally(TryFinally node) {
|
|
return new TryFinally(clone(node.body), clone(node.finalizer));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitYieldStatement(YieldStatement node) {
|
|
return new YieldStatement(clone(node.expression))..flags = node.flags;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitVariableDeclaration(VariableDeclaration node) {
|
|
throw new UnimplementedError(
|
|
"${this.runtimeType}.visitVariableDeclaration",
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitPositionalParameter(PositionalParameter node) {
|
|
return setVariableClone(
|
|
node,
|
|
new PositionalParameter(
|
|
cosmeticName: node.cosmeticName,
|
|
type: visitType(node.type),
|
|
defaultValue: cloneOptional(node.defaultValue),
|
|
)
|
|
..flags = node.flags
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNamedParameter(NamedParameter node) {
|
|
return setVariableClone(
|
|
node,
|
|
new NamedParameter(
|
|
parameterName: node.parameterName,
|
|
type: visitType(node.type),
|
|
defaultValue: cloneOptional(node.defaultValue),
|
|
)
|
|
..flags = node.flags
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitCatchVariable(CatchVariable node) {
|
|
return setVariableClone(
|
|
node,
|
|
new CatchVariable(
|
|
name: node.catchVariableName,
|
|
type: visitOptionalType(node.type),
|
|
)
|
|
..flags = node.flags
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLocalVariable(LocalVariable node) {
|
|
return setVariableClone(
|
|
node,
|
|
new LocalVariable(
|
|
cosmeticName: node.cosmeticName,
|
|
type: visitOptionalType(node.type),
|
|
)
|
|
..flags = node.flags
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSyntheticVariable(SyntheticVariable node) {
|
|
return setVariableClone(
|
|
node,
|
|
SyntheticVariable(
|
|
cosmeticName: node.cosmeticName,
|
|
type: visitType(node.type),
|
|
)
|
|
..flags = node.flags
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitThisVariable(ThisVariable node) {
|
|
return setVariableClone(
|
|
node,
|
|
new ThisVariable(type: visitType(node.type))
|
|
..flags = node.flags
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLegacyVariableStatement(VariableStatement node) {
|
|
return new LegacyVariableStatement(clone(node.variable))
|
|
..fileOffset = _cloneFileOffset(node.fileOffset);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLegacyVariable(LegacyVariable node) {
|
|
return setVariableClone(
|
|
node,
|
|
new LegacyVariable(
|
|
node.name,
|
|
initializer: cloneOptional(node.initializer),
|
|
type: visitType(node.type),
|
|
flags: node.flags,
|
|
)
|
|
..annotations = _cloneAnnotations(node)
|
|
..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset),
|
|
);
|
|
}
|
|
|
|
List<Expression> _cloneAnnotations(Annotatable node) {
|
|
return cloneAnnotations && !node.annotations.isEmpty
|
|
? node.annotations.map(clone).toList()
|
|
: const <Expression>[];
|
|
}
|
|
|
|
@override
|
|
TreeNode visitVariableInitialization(VariableInitialization node) {
|
|
return new VariableInitialization(
|
|
variable: clone(node.variable),
|
|
initializer: cloneOptional(node.initializer),
|
|
)
|
|
..flags = node.flags
|
|
..annotations = cloneAnnotations && !node.annotations.isEmpty
|
|
? node.annotations.map(clone).toList()
|
|
: const <Expression>[];
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFunctionDeclaration(FunctionDeclaration node) {
|
|
VariableDeclaration newVariable = clone(node.variable);
|
|
// Create the declaration before cloning the body to support recursive
|
|
// [LocalFunctionInvocation] nodes.
|
|
FunctionDeclaration declaration = new FunctionDeclaration(
|
|
newVariable,
|
|
dummyFunctionNode,
|
|
)..id = node.id;
|
|
FunctionNode functionNode = clone(node.function);
|
|
declaration.function = functionNode..parent = declaration;
|
|
return declaration;
|
|
}
|
|
|
|
void prepareTypeParameters(List<TypeParameter> typeParameters) {
|
|
for (int i = 0; i < typeParameters.length; i++) {
|
|
TypeParameter node = typeParameters[i];
|
|
TypeParameter? newNode = typeParams[node];
|
|
if (newNode == null) {
|
|
newNode = new TypeParameter(node.name);
|
|
typeParams[node] = newNode;
|
|
typeSubstitution[node] = new TypeParameterType(
|
|
newNode,
|
|
node.computeNullabilityFromBound(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
TypeParameter visitTypeParameter(TypeParameter node) {
|
|
TypeParameter newNode = typeParams[node]!;
|
|
newNode.bound = visitType(node.bound);
|
|
newNode.defaultType = visitType(node.defaultType);
|
|
return newNode
|
|
..annotations = cloneAnnotations && !node.annotations.isEmpty
|
|
? node.annotations.map(clone).toList()
|
|
: const <Expression>[]
|
|
..flags = node.flags;
|
|
}
|
|
|
|
Statement? cloneFunctionNodeBody(FunctionNode node) {
|
|
bool savedCloneAnnotations = this.cloneAnnotations;
|
|
try {
|
|
this.cloneAnnotations = true;
|
|
return cloneOptional(node.body);
|
|
} finally {
|
|
this.cloneAnnotations = savedCloneAnnotations;
|
|
}
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFunctionNode(FunctionNode node) {
|
|
prepareTypeParameters(node.typeParameters);
|
|
List<TypeParameter> typeParameters = node.typeParameters
|
|
.map(clone)
|
|
.toList();
|
|
List<VariableDeclaration> positional = node.positionalParameters
|
|
.map(clone)
|
|
.toList();
|
|
List<VariableDeclaration> named = node.namedParameters.map(clone).toList();
|
|
VariableDeclaration? thisVariable = cloneOptional(node.thisVariable);
|
|
final DartType? futureValueType = node.emittedValueType != null
|
|
? visitType(node.emittedValueType!)
|
|
: null;
|
|
return new FunctionNode(
|
|
cloneFunctionNodeBody(node),
|
|
typeParameters: typeParameters,
|
|
positionalParameters: positional,
|
|
namedParameters: named,
|
|
thisVariable: thisVariable,
|
|
requiredParameterCount: node.requiredParameterCount,
|
|
returnType: visitType(node.returnType),
|
|
asyncMarker: node.asyncMarker,
|
|
dartAsyncMarker: node.dartAsyncMarker,
|
|
emittedValueType: futureValueType,
|
|
)..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitArguments(Arguments node) {
|
|
return new Arguments(
|
|
node.positional.map(clone).toList(),
|
|
types: node.types.map(visitType).toList(),
|
|
named: node.named.map(clone).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNamedExpression(NamedExpression node) {
|
|
return new NamedExpression(node.name, clone(node.value));
|
|
}
|
|
|
|
TreeNode _unsupportedNode(TreeNode node) {
|
|
throw 'Cloning Kernel non-members is not supported. '
|
|
'Tried cloning $node';
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAssertInitializer(AssertInitializer node) {
|
|
return new AssertInitializer(clone(node.statement));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
|
|
return new CheckLibraryIsLoaded(node.import);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitCombinator(Combinator node) {
|
|
return _unsupportedNode(node);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFieldInitializer(FieldInitializer node) {
|
|
return new FieldInitializer.byReference(
|
|
node.fieldReference,
|
|
clone(node.value),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstantiation(Instantiation node) {
|
|
return new Instantiation(
|
|
clone(node.expression),
|
|
node.typeArguments.map(visitType).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInvalidInitializer(InvalidInitializer node) {
|
|
return new InvalidInitializer(node.message)..flags = node.flags;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLibraryDependency(LibraryDependency node) {
|
|
return _unsupportedNode(node);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLibraryPart(LibraryPart node) {
|
|
return _unsupportedNode(node);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLoadLibrary(LoadLibrary node) {
|
|
return new LoadLibrary(node.import);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLocalInitializer(LocalInitializer node) {
|
|
return new LocalInitializer(clone(node.variable));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitComponent(Component node) {
|
|
return _unsupportedNode(node);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRedirectingInitializer(RedirectingInitializer node) {
|
|
return new RedirectingInitializer.byReference(
|
|
node.targetReference,
|
|
clone(node.arguments),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSuperInitializer(SuperInitializer node) {
|
|
return new SuperInitializer.byReference(
|
|
node.targetReference,
|
|
clone(node.arguments),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitTypedef(Typedef node) {
|
|
return _unsupportedNode(node);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitDynamicGet(DynamicGet node) {
|
|
return new DynamicGet(node.kind, clone(node.receiver), node.name);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitDynamicInvocation(DynamicInvocation node) {
|
|
return new DynamicInvocation(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.arguments),
|
|
)..flags = node.flags;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitDynamicSet(DynamicSet node) {
|
|
return new DynamicSet(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.value),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitEqualsCall(EqualsCall node) {
|
|
return new EqualsCall.byReference(
|
|
clone(node.left),
|
|
clone(node.right),
|
|
functionType: visitType(node.functionType) as FunctionType,
|
|
interfaceTargetReference: node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitEqualsNull(EqualsNull node) {
|
|
return new EqualsNull(clone(node.expression));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFunctionInvocation(FunctionInvocation node) {
|
|
return new FunctionInvocation(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
clone(node.arguments),
|
|
functionType: visitOptionalType(node.functionType) as FunctionType?,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstanceGet(InstanceGet node) {
|
|
return new InstanceGet.byReference(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
resultType: visitType(node.resultType),
|
|
interfaceTargetReference: node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstanceInvocation(InstanceInvocation node) {
|
|
return new InstanceInvocation.byReference(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.arguments),
|
|
functionType: visitType(node.functionType) as FunctionType,
|
|
interfaceTargetReference: node.interfaceTargetReference,
|
|
)..flags = node.flags;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstanceGetterInvocation(InstanceGetterInvocation node) {
|
|
return new InstanceGetterInvocation.byReference(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.arguments),
|
|
functionType: visitOptionalType(node.functionType) as FunctionType?,
|
|
interfaceTargetReference: node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstanceSet(InstanceSet node) {
|
|
return new InstanceSet.byReference(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
clone(node.value),
|
|
interfaceTargetReference: node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInstanceTearOff(InstanceTearOff node) {
|
|
return new InstanceTearOff.byReference(
|
|
node.kind,
|
|
clone(node.receiver),
|
|
node.name,
|
|
resultType: visitType(node.resultType),
|
|
interfaceTargetReference: node.interfaceTargetReference,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitLocalFunctionInvocation(LocalFunctionInvocation node) {
|
|
return new LocalFunctionInvocation(
|
|
getVariableClone(node.variable)!,
|
|
clone(node.arguments),
|
|
functionType: visitType(node.functionType) as FunctionType,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitStaticTearOff(StaticTearOff node) {
|
|
return new StaticTearOff.byReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitFunctionTearOff(FunctionTearOff node) {
|
|
return new FunctionTearOff(clone(node.receiver));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConstructorTearOff(ConstructorTearOff node) {
|
|
return new ConstructorTearOff.byReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRedirectingFactoryTearOff(RedirectingFactoryTearOff node) {
|
|
return new RedirectingFactoryTearOff.byReference(node.targetReference);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitTypedefTearOff(TypedefTearOff node) {
|
|
return new TypedefTearOff(
|
|
node.structuralParameters,
|
|
clone(node.expression),
|
|
node.typeArguments.map(visitType).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAndPattern(AndPattern node) {
|
|
return new AndPattern(clone(node.left), clone(node.right));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAssignedVariablePattern(AssignedVariablePattern node) {
|
|
return new AssignedVariablePattern(getVariableClone(node.variable)!)
|
|
..matchedValueType = visitOptionalType(node.matchedValueType)
|
|
..needsCast = node.needsCast;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitCastPattern(CastPattern node) {
|
|
return new CastPattern(clone(node.pattern), visitType(node.type));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConstantPattern(ConstantPattern node) {
|
|
return new ConstantPattern(clone(node.expression));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitInvalidPattern(InvalidPattern node) {
|
|
return new InvalidPattern(
|
|
clone(node.invalidExpression),
|
|
declaredVariables: node.declaredVariables
|
|
.map((e) => getVariableClone(e)!)
|
|
.toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitListPattern(ListPattern node) {
|
|
return new ListPattern(
|
|
visitOptionalType(node.typeArgument),
|
|
node.patterns.map(clone).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitMapPattern(MapPattern node) {
|
|
return new MapPattern(
|
|
visitOptionalType(node.keyType),
|
|
visitOptionalType(node.valueType),
|
|
node.entries.map(clone).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitMapPatternEntry(MapPatternEntry node) {
|
|
return new MapPatternEntry(clone(node.key), clone(node.value));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitMapPatternRestEntry(MapPatternRestEntry node) {
|
|
return new MapPatternRestEntry();
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNamedPattern(NamedPattern node) {
|
|
return new NamedPattern(node.name, clone(node.pattern));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNullAssertPattern(NullAssertPattern node) {
|
|
return new NullAssertPattern(clone(node.pattern));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitNullCheckPattern(NullCheckPattern node) {
|
|
return new NullCheckPattern(clone(node.pattern));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitObjectPattern(ObjectPattern node) {
|
|
return new ObjectPattern(
|
|
visitType(node.requiredType),
|
|
node.fields.map(clone).toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitOrPattern(OrPattern node) {
|
|
return new OrPattern(
|
|
clone(node.left),
|
|
clone(node.right),
|
|
orPatternJointVariables: node.orPatternJointVariables
|
|
.map((e) => getVariableClone(e)!)
|
|
.toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRecordPattern(RecordPattern node) {
|
|
return new RecordPattern(node.patterns.map(clone).toList());
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRelationalPattern(RelationalPattern node) {
|
|
return new RelationalPattern(node.kind, clone(node.expression))
|
|
..expressionType = visitOptionalType(node.expressionType);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitRestPattern(RestPattern node) {
|
|
return new RestPattern(cloneOptional(node.subPattern));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitVariablePattern(VariablePattern node) {
|
|
return new VariablePattern(
|
|
visitOptionalType(node.type),
|
|
clone(node.variable),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitWildcardPattern(WildcardPattern node) {
|
|
return new WildcardPattern(visitOptionalType(node.type));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitPatternGuard(PatternGuard node) {
|
|
return new PatternGuard(node.pattern, node.guard);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitPatternSwitchCase(PatternSwitchCase node) {
|
|
return new PatternSwitchCase(
|
|
new List<int>.of(node.caseOffsets),
|
|
node.patternGuards.map(clone).toList(),
|
|
clone(node.body),
|
|
isDefault: node.isDefault,
|
|
hasLabel: node.hasLabel,
|
|
jointVariables: node.jointVariables
|
|
.map((e) => getVariableClone(e)!)
|
|
.toList(),
|
|
jointVariableFirstUseOffsets: node.jointVariableFirstUseOffsets == null
|
|
? null
|
|
: new List<int>.of(node.jointVariableFirstUseOffsets!),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitPatternSwitchStatement(PatternSwitchStatement node) {
|
|
return new PatternSwitchStatement(
|
|
clone(node.expression),
|
|
node.cases.map(clone).toList(),
|
|
)..expressionTypeInternal = visitOptionalType(node.expressionTypeInternal);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSwitchExpression(SwitchExpression node) {
|
|
return new SwitchExpression(
|
|
clone(node.expression),
|
|
node.cases.map(clone).toList(),
|
|
)
|
|
..expressionType = visitOptionalType(node.expressionType)
|
|
..staticType = visitOptionalType(node.staticType);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitSwitchExpressionCase(SwitchExpressionCase node) {
|
|
return new SwitchExpressionCase(
|
|
clone(node.patternGuard),
|
|
clone(node.expression),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitPatternVariableDeclaration(PatternVariableDeclaration node) {
|
|
return new PatternVariableDeclaration(
|
|
clone(node.pattern),
|
|
clone(node.initializer),
|
|
isFinal: node.isFinal,
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitPatternAssignment(PatternAssignment node) {
|
|
return new PatternAssignment(clone(node.pattern), clone(node.expression));
|
|
}
|
|
|
|
@override
|
|
TreeNode visitIfCaseStatement(IfCaseStatement node) {
|
|
return new IfCaseStatement(
|
|
clone(node.expression),
|
|
clone(node.patternGuard),
|
|
clone(node.then),
|
|
cloneOptional(node.otherwise),
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAuxiliaryExpression(AuxiliaryExpression node) {
|
|
throw new UnsupportedError(
|
|
"Unsupported auxiliary expression ${node} (${node.runtimeType}).",
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAuxiliaryStatement(AuxiliaryStatement node) {
|
|
throw new UnsupportedError(
|
|
"Unsupported auxiliary statement ${node} (${node.runtimeType}).",
|
|
);
|
|
}
|
|
|
|
@override
|
|
TreeNode visitAuxiliaryInitializer(AuxiliaryInitializer node) {
|
|
throw new UnsupportedError(
|
|
"Unsupported auxiliary initializer ${node} (${node.runtimeType}).",
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Visitor that return a clone of a tree, maintaining references to cloned
|
|
/// objects.
|
|
///
|
|
/// It is safe to clone members, but cloning a class or library is not
|
|
/// supported.
|
|
class CloneVisitorWithMembers extends CloneVisitorNotMembers {
|
|
CloneVisitorWithMembers({
|
|
Map<TypeParameter, DartType>? typeSubstitution,
|
|
Map<TypeParameter, TypeParameter>? typeParams,
|
|
bool cloneAnnotations = true,
|
|
}) : super(
|
|
typeSubstitution: typeSubstitution,
|
|
typeParams: typeParams,
|
|
cloneAnnotations: cloneAnnotations,
|
|
);
|
|
|
|
@override
|
|
@Deprecated("When cloning with members one should use the specific cloneX")
|
|
T clone<T extends TreeNode>(T node) {
|
|
return super.clone(node);
|
|
}
|
|
|
|
Constructor cloneConstructor(Constructor node, Reference? reference) {
|
|
final Uri? activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = node.fileUri;
|
|
|
|
Constructor result =
|
|
new Constructor(
|
|
super.clone(node.function),
|
|
name: node.name,
|
|
isConst: node.isConst,
|
|
isExternal: node.isExternal,
|
|
isSynthetic: node.isSynthetic,
|
|
initializers: node.initializers.map(super.clone).toList(),
|
|
transformerFlags: node.transformerFlags,
|
|
fileUri: node.fileUri,
|
|
reference: reference,
|
|
)
|
|
..annotations = cloneAnnotations && !node.annotations.isEmpty
|
|
? node.annotations.map(super.clone).toList()
|
|
: const <Expression>[]
|
|
..fileOffset = _cloneFileOffset(node.fileOffset)
|
|
..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
|
|
setParents(result.annotations, result);
|
|
|
|
assert(_assertFileUriTarget(node, result));
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
return result;
|
|
}
|
|
|
|
Procedure cloneProcedure(Procedure node, Reference? reference) {
|
|
final Uri? activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = node.fileUri;
|
|
Procedure result =
|
|
new Procedure(
|
|
node.name,
|
|
node.kind,
|
|
super.clone(node.function),
|
|
reference: reference,
|
|
transformerFlags: node.transformerFlags,
|
|
fileUri: node.fileUri,
|
|
stubKind: node.stubKind,
|
|
stubTarget: node.stubTarget,
|
|
)
|
|
..annotations = cloneAnnotations && !node.annotations.isEmpty
|
|
? node.annotations.map(super.clone).toList()
|
|
: const <Expression>[]
|
|
..fileStartOffset = _cloneFileOffset(node.fileStartOffset)
|
|
..fileOffset = _cloneFileOffset(node.fileOffset)
|
|
..fileEndOffset = _cloneFileOffset(node.fileEndOffset)
|
|
..flags = node.flags;
|
|
setParents(result.annotations, result);
|
|
|
|
assert(_assertFileUriTarget(node, result));
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
return result;
|
|
}
|
|
|
|
Field cloneField(
|
|
Field node,
|
|
Reference? fieldReference,
|
|
Reference? getterReference,
|
|
Reference? setterReference,
|
|
) {
|
|
final Uri? activeFileUriSaved = _activeFileUri;
|
|
_activeFileUri = node.fileUri;
|
|
|
|
Field result;
|
|
if (node.hasSetter) {
|
|
result = new Field.mutable(
|
|
node.name,
|
|
type: visitType(node.type),
|
|
initializer: cloneOptional(node.initializer),
|
|
transformerFlags: node.transformerFlags,
|
|
fileUri: node.fileUri,
|
|
fieldReference: fieldReference,
|
|
getterReference: getterReference,
|
|
setterReference: setterReference,
|
|
);
|
|
} else {
|
|
assert(
|
|
setterReference == null,
|
|
"Cannot use setter reference $setterReference "
|
|
"for clone of an immutable field.",
|
|
);
|
|
result = new Field.immutable(
|
|
node.name,
|
|
type: visitType(node.type),
|
|
initializer: cloneOptional(node.initializer),
|
|
transformerFlags: node.transformerFlags,
|
|
fileUri: node.fileUri,
|
|
fieldReference: fieldReference,
|
|
getterReference: getterReference,
|
|
);
|
|
}
|
|
result
|
|
..annotations = cloneAnnotations && !node.annotations.isEmpty
|
|
? node.annotations.map(super.clone).toList()
|
|
: const <Expression>[]
|
|
..fileOffset = _cloneFileOffset(node.fileOffset)
|
|
..fileEndOffset = _cloneFileOffset(node.fileEndOffset)
|
|
..flags = node.flags;
|
|
setParents(result.annotations, result);
|
|
|
|
assert(_assertFileUriTarget(node, result));
|
|
|
|
_activeFileUri = activeFileUriSaved;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// Cloner that resolves super calls in mixin declarations.
|
|
class MixinApplicationCloner extends CloneVisitorWithMembers {
|
|
final Class mixinApplicationClass;
|
|
Map<Name, Member>? _getterMap;
|
|
Map<Name, Member>? _setterMap;
|
|
|
|
MixinApplicationCloner(
|
|
this.mixinApplicationClass, {
|
|
Map<TypeParameter, DartType>? typeSubstitution,
|
|
Map<TypeParameter, TypeParameter>? typeParams,
|
|
bool cloneAnnotations = true,
|
|
}) : super(
|
|
typeSubstitution: typeSubstitution,
|
|
typeParams: typeParams,
|
|
cloneAnnotations: cloneAnnotations,
|
|
);
|
|
|
|
Member? _findSuperMember(Name name, {required bool isSetter}) {
|
|
Map<Name, Member> cache;
|
|
if (isSetter) {
|
|
cache = _setterMap ??= {};
|
|
} else {
|
|
cache = _getterMap ??= {};
|
|
}
|
|
Member? member = cache[name];
|
|
if (member != null) {
|
|
return member;
|
|
}
|
|
Class? superClass = mixinApplicationClass.superclass;
|
|
while (superClass != null) {
|
|
for (Procedure procedure in superClass.procedures) {
|
|
if (procedure.name == name) {
|
|
if (isSetter) {
|
|
if (procedure.kind == ProcedureKind.Setter &&
|
|
!procedure.isAbstract) {
|
|
return cache[name] = procedure;
|
|
}
|
|
} else {
|
|
if (procedure.kind != ProcedureKind.Setter &&
|
|
!procedure.isAbstract) {
|
|
return cache[name] = procedure;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (Field field in superClass.fields) {
|
|
if (field.name == name) {
|
|
if (isSetter) {
|
|
if (field.hasSetter) {
|
|
return cache[name] = field;
|
|
}
|
|
} else {
|
|
return cache[name] = field;
|
|
}
|
|
}
|
|
}
|
|
|
|
superClass = superClass.superclass;
|
|
}
|
|
// TODO(johnniwinther): Throw instead when the CFE reports missing concrete
|
|
// super members.
|
|
// throw new StateError(
|
|
// 'No super member found for $name in $mixinApplicationClass');
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
SuperMethodInvocation visitSuperMethodInvocation(SuperMethodInvocation node) {
|
|
SuperMethodInvocation cloned =
|
|
super.visitSuperMethodInvocation(node) as SuperMethodInvocation;
|
|
cloned.interfaceTarget =
|
|
_findSuperMember(node.name, isSetter: false) as Procedure? ??
|
|
// TODO(johnniwinther): Remove this when an error is reported instead.
|
|
cloned.interfaceTarget;
|
|
return cloned;
|
|
}
|
|
|
|
@override
|
|
SuperPropertyGet visitSuperPropertyGet(SuperPropertyGet node) {
|
|
SuperPropertyGet cloned =
|
|
super.visitSuperPropertyGet(node) as SuperPropertyGet;
|
|
cloned.interfaceTarget =
|
|
_findSuperMember(node.name, isSetter: false) ??
|
|
// TODO(johnniwinther): Remove this when an error is reported instead.
|
|
cloned.interfaceTarget;
|
|
return cloned;
|
|
}
|
|
|
|
@override
|
|
SuperPropertySet visitSuperPropertySet(SuperPropertySet node) {
|
|
SuperPropertySet cloned =
|
|
super.visitSuperPropertySet(node) as SuperPropertySet;
|
|
cloned.interfaceTarget =
|
|
_findSuperMember(node.name, isSetter: true) ??
|
|
// TODO(johnniwinther): Remove this when an error is reported instead.
|
|
cloned.interfaceTarget;
|
|
return cloned;
|
|
}
|
|
}
|
|
|
|
class CloneProcedureWithoutBody extends CloneVisitorWithMembers {
|
|
CloneProcedureWithoutBody({
|
|
Map<TypeParameter, DartType>? typeSubstitution,
|
|
bool cloneAnnotations = true,
|
|
}) : super(
|
|
typeSubstitution: typeSubstitution,
|
|
cloneAnnotations: cloneAnnotations,
|
|
);
|
|
|
|
/// Clones procedure and replaces its parts with those passed as arguments
|
|
///
|
|
/// [cloneProcedureWith] is a shortcut that can be helpful, for example, for
|
|
/// transforming external procedures.
|
|
///
|
|
/// Since this cloner clones procedures without the body, it's safe to replace
|
|
/// the parameters of the cloned procedure, since they aren't referenced
|
|
/// anywhere. If either [positionalParameters] or [namedParameters] are
|
|
/// passed in, they are used in place of the freshly cloned
|
|
/// [FunctionNode.positionalParameters] and [FunctionNode.namedParameters].
|
|
Procedure cloneProcedureWith(
|
|
Procedure node,
|
|
Reference? reference, {
|
|
List<VariableDeclaration>? positionalParameters,
|
|
List<VariableDeclaration>? namedParameters,
|
|
}) {
|
|
Procedure cloned = cloneProcedure(node, reference);
|
|
if (positionalParameters != null) {
|
|
cloned.function.positionalParameters = positionalParameters;
|
|
setParents(positionalParameters, cloned.function);
|
|
}
|
|
if (namedParameters != null) {
|
|
cloned.function.namedParameters = namedParameters;
|
|
setParents(namedParameters, cloned.function);
|
|
}
|
|
return cloned;
|
|
}
|
|
|
|
@override
|
|
Statement? cloneFunctionNodeBody(FunctionNode node) => null;
|
|
}
|