// 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. /// /// It is safe to clone members, but cloning a class or library is not /// supported. class CloneVisitor extends TreeVisitor { final Map variables = {}; final Map labels = {}; final Map switchCases = {}; final Map typeSubstitution; CloneVisitor({Map typeSubstitution}) : this.typeSubstitution = ensureMutable(typeSubstitution); static Map ensureMutable( Map map) { // We need to mutate this map, so make sure we don't use a constant map. if (map == null || map.isEmpty) { return {}; } return map; } TreeNode visitLibrary(Library node) { throw 'Cloning of libraries is not implemented'; } TreeNode visitClass(Class node) { throw 'Cloning of classes is not implemented'; } TreeNode clone(TreeNode node) => node.accept(this)..fileOffset = node.fileOffset; TreeNode cloneOptional(TreeNode node) { TreeNode result = node?.accept(this); if (result != null) result.fileOffset = node.fileOffset; return result; } DartType visitType(DartType type) { return substitute(type, typeSubstitution); } DartType visitOptionalType(DartType type) { return type == null ? null : substitute(type, typeSubstitution); } visitInvalidExpression(InvalidExpression node) => new InvalidExpression(); visitVariableGet(VariableGet node) { return new VariableGet( variables[node.variable], visitOptionalType(node.promotedType)); } visitVariableSet(VariableSet node) { return new VariableSet(variables[node.variable], clone(node.value)); } visitPropertyGet(PropertyGet node) { return new PropertyGet.byReference( clone(node.receiver), node.name, node.interfaceTargetReference); } visitPropertySet(PropertySet node) { return new PropertySet.byReference(clone(node.receiver), node.name, clone(node.value), node.interfaceTargetReference); } visitDirectPropertyGet(DirectPropertyGet node) { return new DirectPropertyGet.byReference( clone(node.receiver), node.targetReference); } visitDirectPropertySet(DirectPropertySet node) { return new DirectPropertySet.byReference( clone(node.receiver), node.targetReference, clone(node.value)); } visitSuperPropertyGet(SuperPropertyGet node) { return new SuperPropertyGet.byReference( node.name, node.interfaceTargetReference); } visitSuperPropertySet(SuperPropertySet node) { return new SuperPropertySet.byReference( node.name, clone(node.value), node.interfaceTargetReference); } visitStaticGet(StaticGet node) { return new StaticGet.byReference(node.targetReference); } visitStaticSet(StaticSet node) { return new StaticSet.byReference(node.targetReference, clone(node.value)); } visitMethodInvocation(MethodInvocation node) { return new MethodInvocation.byReference(clone(node.receiver), node.name, clone(node.arguments), node.interfaceTargetReference); } visitDirectMethodInvocation(DirectMethodInvocation node) { return new DirectMethodInvocation.byReference( clone(node.receiver), node.targetReference, clone(node.arguments)); } visitSuperMethodInvocation(SuperMethodInvocation node) { return new SuperMethodInvocation.byReference( node.name, clone(node.arguments), node.interfaceTargetReference); } visitStaticInvocation(StaticInvocation node) { return new StaticInvocation.byReference( node.targetReference, clone(node.arguments), isConst: node.isConst); } visitConstructorInvocation(ConstructorInvocation node) { return new ConstructorInvocation.byReference( node.targetReference, clone(node.arguments), isConst: node.isConst); } visitNot(Not node) { return new Not(clone(node.operand)); } visitLogicalExpression(LogicalExpression node) { return new LogicalExpression( clone(node.left), node.operator, clone(node.right)); } visitConditionalExpression(ConditionalExpression node) { return new ConditionalExpression(clone(node.condition), clone(node.then), clone(node.otherwise), visitOptionalType(node.staticType)); } visitStringConcatenation(StringConcatenation node) { return new StringConcatenation(node.expressions.map(clone).toList()); } visitIsExpression(IsExpression node) { return new IsExpression(clone(node.operand), visitType(node.type)); } visitAsExpression(AsExpression node) { return new AsExpression(clone(node.operand), visitType(node.type)); } visitSymbolLiteral(SymbolLiteral node) { return new SymbolLiteral(node.value); } visitTypeLiteral(TypeLiteral node) { return new TypeLiteral(visitType(node.type)); } visitThisExpression(ThisExpression node) { return new ThisExpression(); } visitRethrow(Rethrow node) { return new Rethrow(); } visitThrow(Throw node) { return new Throw(cloneOptional(node.expression)); } visitListLiteral(ListLiteral node) { return new ListLiteral(node.expressions.map(clone).toList(), typeArgument: visitType(node.typeArgument), isConst: node.isConst); } visitMapLiteral(MapLiteral node) { return new MapLiteral(node.entries.map(clone).toList(), keyType: visitType(node.keyType), valueType: visitType(node.valueType), isConst: node.isConst); } visitMapEntry(MapEntry node) { return new MapEntry(clone(node.key), clone(node.value)); } visitAwaitExpression(AwaitExpression node) { return new AwaitExpression(clone(node.operand)); } visitFunctionExpression(FunctionExpression node) { return new FunctionExpression(clone(node.function)); } visitStringLiteral(StringLiteral node) { return new StringLiteral(node.value); } visitIntLiteral(IntLiteral node) { return new IntLiteral(node.value); } visitDoubleLiteral(DoubleLiteral node) { return new DoubleLiteral(node.value); } visitBoolLiteral(BoolLiteral node) { return new BoolLiteral(node.value); } visitNullLiteral(NullLiteral node) { return new NullLiteral(); } visitLet(Let node) { var newVariable = clone(node.variable); return new Let(newVariable, clone(node.body)); } visitVectorCreation(VectorCreation node) { return new VectorCreation(node.length); } visitClosureCreation(ClosureCreation node) { return new ClosureCreation.byReference( node.topLevelFunctionReference, cloneOptional(node.contextVector), visitOptionalType(node.functionType), node.typeArguments.map(visitType).toList()); } visitVectorSet(VectorSet node) { return new VectorSet( clone(node.vectorExpression), node.index, clone(node.value)); } visitVectorGet(VectorGet node) { return new VectorGet(clone(node.vectorExpression), node.index); } visitVectorCopy(VectorCopy node) { return new VectorCopy(clone(node.vectorExpression)); } // Statements visitInvalidStatement(InvalidStatement node) { return new InvalidStatement(); } visitExpressionStatement(ExpressionStatement node) { return new ExpressionStatement(clone(node.expression)); } visitBlock(Block node) { return new Block(node.statements.map(clone).toList()); } visitEmptyStatement(EmptyStatement node) { return new EmptyStatement(); } visitAssertStatement(AssertStatement node) { return new AssertStatement(clone(node.condition), conditionStartOffset: node.conditionStartOffset, conditionEndOffset: node.conditionEndOffset, message: cloneOptional(node.message)); } visitLabeledStatement(LabeledStatement node) { LabeledStatement newNode = new LabeledStatement(null); labels[node] = newNode; newNode.body = clone(node.body)..parent = newNode; return newNode; } visitBreakStatement(BreakStatement node) { return new BreakStatement(labels[node.target]); } visitWhileStatement(WhileStatement node) { return new WhileStatement(clone(node.condition), clone(node.body)); } visitDoStatement(DoStatement node) { return new DoStatement(clone(node.body), clone(node.condition)); } visitForStatement(ForStatement node) { var variables = node.variables.map(clone).toList(); return new ForStatement(variables, cloneOptional(node.condition), node.updates.map(clone).toList(), clone(node.body)); } visitForInStatement(ForInStatement node) { var newVariable = clone(node.variable); return new ForInStatement( newVariable, clone(node.iterable), clone(node.body)); } visitSwitchStatement(SwitchStatement node) { for (SwitchCase switchCase in node.cases) { switchCases[switchCase] = new SwitchCase( switchCase.expressions.map(clone).toList(), new List.from(switchCase.expressionOffsets), null); } return new SwitchStatement( clone(node.expression), node.cases.map(clone).toList()); } visitSwitchCase(SwitchCase node) { var switchCase = switchCases[node]; switchCase.body = clone(node.body)..parent = switchCase; return switchCase; } visitContinueSwitchStatement(ContinueSwitchStatement node) { return new ContinueSwitchStatement(switchCases[node.target]); } visitIfStatement(IfStatement node) { return new IfStatement( clone(node.condition), clone(node.then), cloneOptional(node.otherwise)); } visitReturnStatement(ReturnStatement node) { return new ReturnStatement(cloneOptional(node.expression)); } visitTryCatch(TryCatch node) { return new TryCatch(clone(node.body), node.catches.map(clone).toList()); } visitCatch(Catch node) { var newException = cloneOptional(node.exception); var newStackTrace = cloneOptional(node.stackTrace); return new Catch(newException, clone(node.body), stackTrace: newStackTrace, guard: visitType(node.guard)); } visitTryFinally(TryFinally node) { return new TryFinally(clone(node.body), clone(node.finalizer)); } visitYieldStatement(YieldStatement node) { return new YieldStatement(clone(node.expression)); } visitVariableDeclaration(VariableDeclaration node) { return variables[node] = new VariableDeclaration(node.name, initializer: cloneOptional(node.initializer), type: visitType(node.type), isCovariant: node.isCovariant, isFinal: node.isFinal, isConst: node.isConst, isFieldFormal: node.isFieldFormal); } visitFunctionDeclaration(FunctionDeclaration node) { var newVariable = clone(node.variable); return new FunctionDeclaration(newVariable, clone(node.function)); } // Members visitConstructor(Constructor node) { return new Constructor(clone(node.function), name: node.name, isConst: node.isConst, isExternal: node.isExternal, initializers: node.initializers.map(clone).toList(), transformerFlags: node.transformerFlags) ..fileEndOffset = node.fileEndOffset; } visitProcedure(Procedure node) { return new Procedure(node.name, node.kind, clone(node.function), isAbstract: node.isAbstract, isStatic: node.isStatic, isExternal: node.isExternal, isConst: node.isConst, isForwardingStub: node.isForwardingStub, transformerFlags: node.transformerFlags, fileUri: node.fileUri) ..fileEndOffset = node.fileEndOffset; } visitField(Field node) { return new Field(node.name, type: visitType(node.type), initializer: cloneOptional(node.initializer), isCovariant: node.isCovariant, isFinal: node.isFinal, isConst: node.isConst, isStatic: node.isStatic, hasImplicitGetter: node.hasImplicitGetter, hasImplicitSetter: node.hasImplicitSetter, transformerFlags: node.transformerFlags, fileUri: node.fileUri) ..fileEndOffset = node.fileEndOffset; } visitTypeParameter(TypeParameter node) { var newNode = new TypeParameter(node.name); typeSubstitution[node] = new TypeParameterType(newNode); newNode.bound = visitType(node.bound); return newNode; } TreeNode cloneFunctionNodeBody(FunctionNode node) => cloneOptional(node.body); visitFunctionNode(FunctionNode node) { var typeParameters = node.typeParameters.map(clone).toList(); var positional = node.positionalParameters.map(clone).toList(); var named = node.namedParameters.map(clone).toList(); return new FunctionNode(cloneFunctionNodeBody(node), typeParameters: typeParameters, positionalParameters: positional, namedParameters: named, requiredParameterCount: node.requiredParameterCount, returnType: visitType(node.returnType), asyncMarker: node.asyncMarker, dartAsyncMarker: node.dartAsyncMarker) ..fileEndOffset = node.fileEndOffset; } visitArguments(Arguments node) { return new Arguments(node.positional.map(clone).toList(), types: node.types.map(visitType).toList(), named: node.named.map(clone).toList()); } visitNamedExpression(NamedExpression node) { return new NamedExpression(node.name, clone(node.value)); } }