| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer.src.dart.ast.utilities; | 5 library analyzer.src.dart.ast.utilities; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/visitor.dart'; | 10 import 'package:analyzer/dart/ast/visitor.dart'; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 * properties associated with the nodes. | 22 * properties associated with the nodes. |
| 23 */ | 23 */ |
| 24 class AstCloner implements AstVisitor<AstNode> { | 24 class AstCloner implements AstVisitor<AstNode> { |
| 25 /** | 25 /** |
| 26 * A flag indicating whether tokens should be cloned while cloning an AST | 26 * A flag indicating whether tokens should be cloned while cloning an AST |
| 27 * structure. | 27 * structure. |
| 28 */ | 28 */ |
| 29 final bool cloneTokens; | 29 final bool cloneTokens; |
| 30 | 30 |
| 31 /** | 31 /** |
| 32 * Mapping from original tokes to cloned. |
| 33 */ |
| 34 final Map<Token, Token> _clonedTokens = new Map<Token, Token>.identity(); |
| 35 |
| 36 /** |
| 37 * The next original token to clone. |
| 38 */ |
| 39 Token _nextToClone; |
| 40 |
| 41 /** |
| 42 * The last cloned token. |
| 43 */ |
| 44 Token _lastCloned; |
| 45 |
| 46 /** |
| 47 * The offset of the last cloned token. |
| 48 */ |
| 49 int _lastClonedOffset = -1; |
| 50 |
| 51 /** |
| 32 * Initialize a newly created AST cloner to optionally clone tokens while | 52 * Initialize a newly created AST cloner to optionally clone tokens while |
| 33 * cloning AST nodes if [cloneTokens] is `true`. | 53 * cloning AST nodes if [cloneTokens] is `true`. |
| 54 * |
| 55 * TODO(brianwilkerson) Change this to be a named parameter. |
| 34 */ | 56 */ |
| 35 AstCloner( | 57 AstCloner([this.cloneTokens = false]); |
| 36 [this.cloneTokens = | |
| 37 false]); // TODO(brianwilkerson) Change this to be a named parameter. | |
| 38 | 58 |
| 39 /** | 59 /** |
| 40 * Return a clone of the given [node]. | 60 * Return a clone of the given [node]. |
| 41 */ | 61 */ |
| 42 AstNode cloneNode(AstNode node) { | 62 AstNode cloneNode(AstNode node) { |
| 43 if (node == null) { | 63 if (node == null) { |
| 44 return null; | 64 return null; |
| 45 } | 65 } |
| 46 return node.accept(this) as AstNode; | 66 return node.accept(this) as AstNode; |
| 47 } | 67 } |
| 48 | 68 |
| 49 /** | 69 /** |
| 50 * Return a list containing cloned versions of the nodes in the given list of | 70 * Return a list containing cloned versions of the nodes in the given list of |
| 51 * [nodes]. | 71 * [nodes]. |
| 52 */ | 72 */ |
| 53 List<AstNode> cloneNodeList(NodeList nodes) { | 73 List<AstNode> cloneNodeList(NodeList nodes) { |
| 54 int count = nodes.length; | 74 int count = nodes.length; |
| 55 List clonedNodes = new List(); | 75 List clonedNodes = new List(); |
| 56 for (int i = 0; i < count; i++) { | 76 for (int i = 0; i < count; i++) { |
| 57 clonedNodes.add((nodes[i]).accept(this) as AstNode); | 77 clonedNodes.add((nodes[i]).accept(this) as AstNode); |
| 58 } | 78 } |
| 59 return clonedNodes; | 79 return clonedNodes; |
| 60 } | 80 } |
| 61 | 81 |
| 62 /** | 82 /** |
| 63 * Clone the given [token] if tokens are supposed to be cloned. | 83 * Clone the given [token] if tokens are supposed to be cloned. |
| 64 */ | 84 */ |
| 65 Token cloneToken(Token token) { | 85 Token cloneToken(Token token) { |
| 66 if (cloneTokens) { | 86 if (cloneTokens) { |
| 67 return (token == null ? null : token.copy()); | 87 if (token == null) { |
| 88 return null; |
| 89 } |
| 90 if (_lastClonedOffset <= token.offset) { |
| 91 _cloneTokens(_nextToClone ?? token, token.offset); |
| 92 } |
| 93 Token clone = _clonedTokens[token]; |
| 94 assert(clone != null); |
| 95 return clone; |
| 68 } else { | 96 } else { |
| 69 return token; | 97 return token; |
| 70 } | 98 } |
| 71 } | 99 } |
| 72 | 100 |
| 73 /** | 101 /** |
| 74 * Clone the given [tokens] if tokens are supposed to be cloned. | 102 * Clone the given [tokens] if tokens are supposed to be cloned. |
| 75 */ | 103 */ |
| 76 List<Token> cloneTokenList(List<Token> tokens) { | 104 List<Token> cloneTokenList(List<Token> tokens) { |
| 77 if (cloneTokens) { | 105 if (cloneTokens) { |
| 78 return tokens.map((Token token) => token.copy()).toList(); | 106 return tokens.map(cloneToken).toList(); |
| 79 } | 107 } |
| 80 return tokens; | 108 return tokens; |
| 81 } | 109 } |
| 82 | 110 |
| 83 @override | 111 @override |
| 84 AdjacentStrings visitAdjacentStrings(AdjacentStrings node) => | 112 AdjacentStrings visitAdjacentStrings(AdjacentStrings node) => |
| 85 new AdjacentStrings(cloneNodeList(node.strings)); | 113 new AdjacentStrings(cloneNodeList(node.strings)); |
| 86 | 114 |
| 87 @override | 115 @override |
| 88 Annotation visitAnnotation(Annotation node) => new Annotation( | 116 Annotation visitAnnotation(Annotation node) => new Annotation( |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 cloneNode(node.withClause), | 206 cloneNode(node.withClause), |
| 179 cloneNode(node.implementsClause), | 207 cloneNode(node.implementsClause), |
| 180 cloneToken(node.leftBracket), | 208 cloneToken(node.leftBracket), |
| 181 cloneNodeList(node.members), | 209 cloneNodeList(node.members), |
| 182 cloneToken(node.rightBracket)); | 210 cloneToken(node.rightBracket)); |
| 183 copy.nativeClause = cloneNode(node.nativeClause); | 211 copy.nativeClause = cloneNode(node.nativeClause); |
| 184 return copy; | 212 return copy; |
| 185 } | 213 } |
| 186 | 214 |
| 187 @override | 215 @override |
| 188 ClassTypeAlias visitClassTypeAlias(ClassTypeAlias node) => new ClassTypeAlias( | 216 ClassTypeAlias visitClassTypeAlias(ClassTypeAlias node) { |
| 217 cloneToken(node.abstractKeyword); |
| 218 return new ClassTypeAlias( |
| 189 cloneNode(node.documentationComment), | 219 cloneNode(node.documentationComment), |
| 190 cloneNodeList(node.metadata), | 220 cloneNodeList(node.metadata), |
| 191 cloneToken(node.typedefKeyword), | 221 cloneToken(node.typedefKeyword), |
| 192 cloneNode(node.name), | 222 cloneNode(node.name), |
| 193 cloneNode(node.typeParameters), | 223 cloneNode(node.typeParameters), |
| 194 cloneToken(node.equals), | 224 cloneToken(node.equals), |
| 195 cloneToken(node.abstractKeyword), | 225 cloneToken(node.abstractKeyword), |
| 196 cloneNode(node.superclass), | 226 cloneNode(node.superclass), |
| 197 cloneNode(node.withClause), | 227 cloneNode(node.withClause), |
| 198 cloneNode(node.implementsClause), | 228 cloneNode(node.implementsClause), |
| 199 cloneToken(node.semicolon)); | 229 cloneToken(node.semicolon)); |
| 230 } |
| 200 | 231 |
| 201 @override | 232 @override |
| 202 Comment visitComment(Comment node) { | 233 Comment visitComment(Comment node) { |
| 203 if (node.isDocumentation) { | 234 if (node.isDocumentation) { |
| 204 return Comment.createDocumentationCommentWithReferences( | 235 return Comment.createDocumentationCommentWithReferences( |
| 205 cloneTokenList(node.tokens), cloneNodeList(node.references)); | 236 cloneTokenList(node.tokens), cloneNodeList(node.references)); |
| 206 } else if (node.isBlock) { | 237 } else if (node.isBlock) { |
| 207 return Comment.createBlockComment(cloneTokenList(node.tokens)); | 238 return Comment.createBlockComment(cloneTokenList(node.tokens)); |
| 208 } | 239 } |
| 209 return Comment.createEndOfLineComment(cloneTokenList(node.tokens)); | 240 return Comment.createEndOfLineComment(cloneTokenList(node.tokens)); |
| 210 } | 241 } |
| 211 | 242 |
| 212 @override | 243 @override |
| 213 CommentReference visitCommentReference(CommentReference node) => | 244 CommentReference visitCommentReference(CommentReference node) => |
| 214 new CommentReference( | 245 new CommentReference( |
| 215 cloneToken(node.newKeyword), cloneNode(node.identifier)); | 246 cloneToken(node.newKeyword), cloneNode(node.identifier)); |
| 216 | 247 |
| 217 @override | 248 @override |
| 218 CompilationUnit visitCompilationUnit(CompilationUnit node) { | 249 CompilationUnit visitCompilationUnit(CompilationUnit node) { |
| 250 ScriptTag scriptTag = cloneNode(node.scriptTag); |
| 251 List<Directive> directives = cloneNodeList(node.directives); |
| 252 List<CompilationUnitMember> declarations = cloneNodeList(node.declarations); |
| 253 Token endToken = cloneToken(node.endToken); |
| 254 Token beginToken = scriptTag?.beginToken ?? |
| 255 (directives.isEmpty ? null : directives.first.beginToken) ?? |
| 256 (declarations.isEmpty ? null : declarations.first.beginToken) ?? |
| 257 endToken; |
| 219 CompilationUnit clone = new CompilationUnit( | 258 CompilationUnit clone = new CompilationUnit( |
| 220 cloneToken(node.beginToken), | 259 beginToken, scriptTag, directives, declarations, endToken); |
| 221 cloneNode(node.scriptTag), | |
| 222 cloneNodeList(node.directives), | |
| 223 cloneNodeList(node.declarations), | |
| 224 cloneToken(node.endToken)); | |
| 225 clone.lineInfo = node.lineInfo; | 260 clone.lineInfo = node.lineInfo; |
| 226 return clone; | 261 return clone; |
| 227 } | 262 } |
| 228 | 263 |
| 229 @override | 264 @override |
| 230 ConditionalExpression visitConditionalExpression( | 265 ConditionalExpression visitConditionalExpression( |
| 231 ConditionalExpression node) => | 266 ConditionalExpression node) => |
| 232 new ConditionalExpression( | 267 new ConditionalExpression( |
| 233 cloneNode(node.condition), | 268 cloneNode(node.condition), |
| 234 cloneToken(node.question), | 269 cloneToken(node.question), |
| (...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 880 cloneToken(node.withKeyword), cloneNodeList(node.mixinTypes)); | 915 cloneToken(node.withKeyword), cloneNodeList(node.mixinTypes)); |
| 881 | 916 |
| 882 @override | 917 @override |
| 883 YieldStatement visitYieldStatement(YieldStatement node) => new YieldStatement( | 918 YieldStatement visitYieldStatement(YieldStatement node) => new YieldStatement( |
| 884 cloneToken(node.yieldKeyword), | 919 cloneToken(node.yieldKeyword), |
| 885 cloneToken(node.star), | 920 cloneToken(node.star), |
| 886 cloneNode(node.expression), | 921 cloneNode(node.expression), |
| 887 cloneToken(node.semicolon)); | 922 cloneToken(node.semicolon)); |
| 888 | 923 |
| 889 /** | 924 /** |
| 925 * Clone all token starting from the given [token] up to a token that has |
| 926 * offset greater then [stopAfter], and put mapping from originals to clones |
| 927 * into [_clonedTokens]. |
| 928 * |
| 929 * We cannot clone tokens as we visit nodes because not every token is a part |
| 930 * of a node, E.g. commas in argument lists are not represented in AST. But |
| 931 * we need to the sequence of tokens that is identical to the original one. |
| 932 */ |
| 933 void _cloneTokens(Token token, int stopAfter) { |
| 934 if (token == null) { |
| 935 return; |
| 936 } |
| 937 if (token is CommentToken) { |
| 938 token = (token as CommentToken).parent; |
| 939 } |
| 940 while (token != null) { |
| 941 Token clone = token.copy(); |
| 942 { |
| 943 CommentToken c1 = token.precedingComments; |
| 944 CommentToken c2 = clone.precedingComments; |
| 945 while (c1 != null && c2 != null) { |
| 946 _clonedTokens[c1] = c2; |
| 947 if (c1 is DocumentationCommentToken && |
| 948 c2 is DocumentationCommentToken) { |
| 949 for (int i = 0; i < c1.references.length; i++) { |
| 950 _clonedTokens[c1.references[i]] = c2.references[i]; |
| 951 } |
| 952 } |
| 953 c1 = c1.next; |
| 954 c2 = c2.next; |
| 955 } |
| 956 } |
| 957 _clonedTokens[token] = clone; |
| 958 _lastCloned?.setNext(clone); |
| 959 _lastCloned = clone; |
| 960 if (token.type == TokenType.EOF) { |
| 961 break; |
| 962 } |
| 963 if (token.offset > stopAfter) { |
| 964 _nextToClone = token.next; |
| 965 _lastClonedOffset = token.offset; |
| 966 break; |
| 967 } |
| 968 token = token.next; |
| 969 } |
| 970 } |
| 971 |
| 972 /** |
| 890 * Return a clone of the given [node]. | 973 * Return a clone of the given [node]. |
| 891 */ | 974 */ |
| 892 static AstNode clone(AstNode node) { | 975 static AstNode clone(AstNode node) { |
| 893 return node.accept(new AstCloner()); | 976 return node.accept(new AstCloner()); |
| 894 } | 977 } |
| 895 } | 978 } |
| 896 | 979 |
| 897 /** | 980 /** |
| 898 * An AstVisitor that compares the structure of two AstNodes to see whether they | 981 * An AstVisitor that compares the structure of two AstNodes to see whether they |
| 899 * are equal. | 982 * are equal. |
| (...skipping 5363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6263 * Safely visit the given [token], printing the [suffix] after the token if it | 6346 * Safely visit the given [token], printing the [suffix] after the token if it |
| 6264 * is non-`null`. | 6347 * is non-`null`. |
| 6265 */ | 6348 */ |
| 6266 void _visitTokenWithSuffix(Token token, String suffix) { | 6349 void _visitTokenWithSuffix(Token token, String suffix) { |
| 6267 if (token != null) { | 6350 if (token != null) { |
| 6268 _writer.print(token.lexeme); | 6351 _writer.print(token.lexeme); |
| 6269 _writer.print(suffix); | 6352 _writer.print(suffix); |
| 6270 } | 6353 } |
| 6271 } | 6354 } |
| 6272 } | 6355 } |
| OLD | NEW |