| // 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 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, |
| 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 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'; |
| } |
| |
| @override |
| TreeNode visitRedirectingFactory(RedirectingFactory node) { |
| throw 'Cloning of redirecting factory constructors 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; |
| } |
| |
| 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); |
| _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); |
| _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) { |
| return new VariableGet( |
| getVariableClone(node.variable)!, visitOptionalType(node.promotedType)); |
| } |
| |
| @override |
| TreeNode visitVariableSet(VariableSet node) { |
| return new VariableSet(getVariableClone(node.variable)!, clone(node.value)); |
| } |
| |
| @override |
| TreeNode visitAbstractSuperPropertyGet(AbstractSuperPropertyGet node) { |
| return new AbstractSuperPropertyGet.byReference( |
| node.name, node.interfaceTargetReference); |
| } |
| |
| @override |
| TreeNode visitAbstractSuperPropertySet(AbstractSuperPropertySet node) { |
| return new AbstractSuperPropertySet.byReference( |
| node.name, clone(node.value), node.interfaceTargetReference); |
| } |
| |
| @override |
| TreeNode visitSuperPropertyGet(SuperPropertyGet node) { |
| return new SuperPropertyGet.byReference( |
| node.name, node.interfaceTargetReference); |
| } |
| |
| @override |
| TreeNode visitSuperPropertySet(SuperPropertySet node) { |
| return new SuperPropertySet.byReference( |
| 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( |
| node.name, clone(node.arguments), node.interfaceTargetReference); |
| } |
| |
| @override |
| TreeNode visitSuperMethodInvocation(SuperMethodInvocation node) { |
| return new SuperMethodInvocation.byReference( |
| 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 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)) |
| ..flags = node.flags; |
| } |
| |
| @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)); |
| } |
| |
| @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 visitAwaitExpression(AwaitExpression node) { |
| return new AwaitExpression(clone(node.operand)); |
| } |
| |
| @override |
| TreeNode visitFunctionExpression(FunctionExpression node) { |
| return new FunctionExpression(clone(node.function)); |
| } |
| |
| @override |
| TreeNode visitConstantExpression(ConstantExpression node) { |
| 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 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<VariableDeclaration> 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); |
| } |
| |
| @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) { |
| return setVariableClone( |
| node, |
| new VariableDeclaration(node.name, |
| initializer: cloneOptional(node.initializer), |
| type: visitType(node.type)) |
| ..annotations = cloneAnnotations && !node.annotations.isEmpty |
| ? node.annotations.map(clone).toList() |
| : const <Expression>[] |
| ..flags = node.flags |
| ..fileEqualsOffset = _cloneFileOffset(node.fileEqualsOffset)); |
| } |
| |
| @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); |
| FunctionNode functionNode = clone(node.function); |
| declaration.function = functionNode..parent = declaration; |
| return declaration; |
| } |
| |
| void prepareTypeParameters(List<TypeParameter> typeParameters) { |
| for (TypeParameter node in typeParameters) { |
| TypeParameter? newNode = typeParams[node]; |
| if (newNode == null) { |
| newNode = new TypeParameter(node.name); |
| typeParams[node] = newNode; |
| typeSubstitution[node] = |
| new TypeParameterType.forAlphaRenaming(node, newNode); |
| } |
| } |
| } |
| |
| @override |
| TypeParameter visitTypeParameter(TypeParameter node) { |
| TypeParameter newNode = typeParams[node]!; |
| newNode.bound = visitType(node.bound); |
| // ignore: unnecessary_null_comparison |
| if (node.defaultType != null) { |
| 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(); |
| final DartType? futureValueType = |
| node.futureValueType != null ? visitType(node.futureValueType!) : null; |
| return new FunctionNode(cloneFunctionNodeBody(node), |
| typeParameters: typeParameters, |
| positionalParameters: positional, |
| namedParameters: named, |
| requiredParameterCount: node.requiredParameterCount, |
| returnType: visitType(node.returnType), |
| asyncMarker: node.asyncMarker, |
| dartAsyncMarker: node.dartAsyncMarker, |
| futureValueType: 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)); |
| } |
| |
| @override |
| TreeNode defaultBasicLiteral(BasicLiteral node) { |
| return defaultExpression(node); |
| } |
| |
| @override |
| TreeNode defaultExpression(Expression node) { |
| throw 'Unimplemented clone for Kernel expression: $node'; |
| } |
| |
| @override |
| TreeNode defaultInitializer(Initializer node) { |
| throw 'Unimplemented clone for Kernel initializer: $node'; |
| } |
| |
| @override |
| TreeNode defaultMember(Member node) { |
| throw 'Unimplemented clone for Kernel member: $node'; |
| } |
| |
| @override |
| TreeNode defaultStatement(Statement node) { |
| throw 'Unimplemented clone for Kernel statement: $node'; |
| } |
| |
| @override |
| TreeNode defaultTreeNode(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 defaultTreeNode(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(); |
| } |
| |
| @override |
| TreeNode visitLibraryDependency(LibraryDependency node) { |
| return defaultTreeNode(node); |
| } |
| |
| @override |
| TreeNode visitLibraryPart(LibraryPart node) { |
| return defaultTreeNode(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 defaultTreeNode(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 defaultTreeNode(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)); |
| } |
| |
| @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) { |
| prepareTypeParameters(node.typeParameters); |
| return new TypedefTearOff( |
| node.typeParameters.map(visitTypeParameter).toList(), |
| clone(node.expression), |
| node.typeArguments.map(visitType).toList()); |
| } |
| } |
| |
| /// 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); |
| |
| _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; |
| |
| _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; |
| |
| _activeFileUri = activeFileUriSaved; |
| return result; |
| } |
| |
| RedirectingFactory cloneRedirectingFactory( |
| RedirectingFactory node, Reference? reference) { |
| final Uri? activeFileUriSaved = _activeFileUri; |
| _activeFileUri = node.fileUri; |
| |
| RedirectingFactory result = new RedirectingFactory(node.targetReference, |
| name: node.name, |
| isConst: node.isConst, |
| isExternal: node.isExternal, |
| transformerFlags: node.transformerFlags, |
| typeArguments: node.typeArguments.map(visitType).toList(), |
| function: super.clone(node.function), |
| fileUri: node.fileUri, |
| reference: reference) |
| ..fileOffset = _cloneFileOffset(node.fileOffset) |
| ..annotations = cloneAnnotations && !node.annotations.isEmpty |
| ? node.annotations.map(super.clone).toList() |
| : const <Expression>[]; |
| |
| _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}) { |
| // ignore: unnecessary_null_comparison |
| assert(isSetter != null); |
| 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); |
| |
| @override |
| Statement? cloneFunctionNodeBody(FunctionNode node) => null; |
| } |