| OLD | NEW |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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 import 'dart:async'; |
| 6 import 'package:compiler/src/commandline_options.dart'; |
| 5 import 'package:compiler/src/common.dart'; | 7 import 'package:compiler/src/common.dart'; |
| 6 import 'package:compiler/src/common_elements.dart'; | 8 import 'package:compiler/src/common_elements.dart'; |
| 7 import 'package:compiler/src/compiler.dart'; | 9 import 'package:compiler/src/compiler.dart'; |
| 8 import 'package:compiler/src/elements/elements.dart'; | 10 import 'package:compiler/src/elements/elements.dart'; |
| 9 import 'package:compiler/src/elements/entities.dart'; | 11 import 'package:compiler/src/elements/entities.dart'; |
| 10 import 'package:compiler/src/resolution/tree_elements.dart'; | 12 import 'package:compiler/src/resolution/tree_elements.dart'; |
| 11 import 'package:compiler/src/tree/nodes.dart'; | 13 import 'package:compiler/src/tree/nodes.dart' as ast; |
| 12 import 'package:expect/expect.dart'; | 14 import 'package:expect/expect.dart'; |
| 15 import 'package:kernel/ast.dart' as ir; |
| 13 | 16 |
| 14 import '../annotated_code_helper.dart'; | 17 import '../annotated_code_helper.dart'; |
| 15 import '../memory_compiler.dart'; | 18 import '../memory_compiler.dart'; |
| 16 import '../equivalence/id_equivalence.dart'; | 19 import '../equivalence/id_equivalence.dart'; |
| 20 import '../kernel/compiler_helper.dart'; |
| 17 | 21 |
| 18 typedef void CheckMemberFunction( | 22 /// Function that compiles [code] with [options] and returns the [Compiler] obje
ct. |
| 19 Compiler compiler, Map<Id, String> expectedMap, MemberEntity member); | 23 typedef Future<Compiler> CompileFunction( |
| 24 AnnotatedCode code, Uri mainUri, List<String> options); |
| 20 | 25 |
| 21 /// Compiles the [annotatedCode] with the provided [options] and calls | 26 /// Function that computes a data mapping for [member]. |
| 22 /// [checkMember] for each member in the code providing the map from [Id] to | 27 /// |
| 23 /// annotation. Any [Id] left in the map will be reported as missing. | 28 /// Fills [actualMap] with the data and [sourceSpanMap] with the source spans |
| 24 checkCode(String annotatedCode, CheckMemberFunction checkMember, | 29 /// for the data origin. |
| 30 typedef void ComputeMemberDataFunction(Compiler compiler, MemberEntity member, |
| 31 Map<Id, String> actualMap, Map<Id, SourceSpan> sourceSpanMap); |
| 32 |
| 33 /// Compile [code] from .dart sources. |
| 34 Future<Compiler> compileFromSource( |
| 35 AnnotatedCode code, Uri mainUri, List<String> options) async { |
| 36 Compiler compiler = compilerFor( |
| 37 memorySourceFiles: {'main.dart': code.sourceCode}, options: options); |
| 38 compiler.stopAfterTypeInference = true; |
| 39 await compiler.run(mainUri); |
| 40 return compiler; |
| 41 } |
| 42 |
| 43 /// Compile [code] from .dill sources. |
| 44 Future<Compiler> compileFromDill( |
| 45 AnnotatedCode code, Uri mainUri, List<String> options) async { |
| 46 Compiler compiler = await compileWithDill( |
| 47 mainUri, |
| 48 {'main.dart': code.sourceCode}, |
| 49 [Flags.disableTypeInference]..addAll(options), |
| 50 beforeRun: (Compiler compiler) { |
| 51 compiler.stopAfterTypeInference = true; |
| 52 }); |
| 53 return compiler; |
| 54 } |
| 55 |
| 56 /// Compute expected and actual data for all members defined in [annotatedCode]. |
| 57 /// |
| 58 /// Actual data is computed using [computeMemberData] and [code] is compiled |
| 59 /// using [compileFunction]. |
| 60 Future<IdData> computeData( |
| 61 String annotatedCode, |
| 62 ComputeMemberDataFunction computeMemberData, |
| 63 CompileFunction compileFunction, |
| 25 {List<String> options: const <String>[]}) async { | 64 {List<String> options: const <String>[]}) async { |
| 26 AnnotatedCode code = | 65 AnnotatedCode code = |
| 27 new AnnotatedCode.fromText(annotatedCode, commentStart, commentEnd); | 66 new AnnotatedCode.fromText(annotatedCode, commentStart, commentEnd); |
| 28 Map<Id, String> expectedMap = computeExpectedMap(code); | 67 Map<Id, String> expectedMap = computeExpectedMap(code); |
| 29 Compiler compiler = compilerFor( | 68 Map<Id, String> actualMap = <Id, String>{}; |
| 30 memorySourceFiles: {'main.dart': code.sourceCode}, options: options); | 69 Map<Id, SourceSpan> sourceSpanMap = <Id, SourceSpan>{}; |
| 31 compiler.stopAfterTypeInference = true; | |
| 32 Uri mainUri = Uri.parse('memory:main.dart'); | 70 Uri mainUri = Uri.parse('memory:main.dart'); |
| 33 await compiler.run(mainUri); | 71 Compiler compiler = await compileFunction(code, mainUri, options); |
| 34 ElementEnvironment elementEnvironment = | 72 ElementEnvironment elementEnvironment = |
| 35 compiler.backendClosedWorldForTesting.elementEnvironment; | 73 compiler.backendClosedWorldForTesting.elementEnvironment; |
| 36 LibraryEntity mainLibrary = elementEnvironment.mainLibrary; | 74 LibraryEntity mainLibrary = elementEnvironment.mainLibrary; |
| 37 elementEnvironment.forEachClass(mainLibrary, (ClassEntity cls) { | 75 elementEnvironment.forEachClass(mainLibrary, (ClassEntity cls) { |
| 38 elementEnvironment.forEachClassMember(cls, | 76 elementEnvironment.forEachClassMember(cls, |
| 39 (ClassEntity declarer, MemberEntity member) { | 77 (ClassEntity declarer, MemberEntity member) { |
| 40 if (cls == declarer) { | 78 if (cls == declarer) { |
| 41 checkMember(compiler, expectedMap, member); | 79 computeMemberData(compiler, member, actualMap, sourceSpanMap); |
| 42 } | 80 } |
| 43 }); | 81 }); |
| 44 }); | 82 }); |
| 45 elementEnvironment.forEachLibraryMember(mainLibrary, (MemberEntity member) { | 83 elementEnvironment.forEachLibraryMember(mainLibrary, (MemberEntity member) { |
| 46 checkMember(compiler, expectedMap, member); | 84 computeMemberData(compiler, member, actualMap, sourceSpanMap); |
| 47 }); | 85 }); |
| 48 expectedMap.forEach((Id id, String expected) { | 86 return new IdData(compiler, elementEnvironment, mainUri, expectedMap, |
| 87 actualMap, sourceSpanMap); |
| 88 } |
| 89 |
| 90 /// Data collected by [computeData]. |
| 91 class IdData { |
| 92 final Compiler compiler; |
| 93 final ElementEnvironment elementEnvironment; |
| 94 final Uri mainUri; |
| 95 final Map<Id, String> expectedMap; |
| 96 final Map<Id, String> actualMap; |
| 97 final Map<Id, SourceSpan> sourceSpanMap; |
| 98 |
| 99 IdData(this.compiler, this.elementEnvironment, this.mainUri, this.expectedMap, |
| 100 this.actualMap, this.sourceSpanMap); |
| 101 } |
| 102 |
| 103 /// Compiles the [annotatedCode] with the provided [options] and calls |
| 104 /// [computeMemberData] for each member. The result is checked against the |
| 105 /// expected data derived from [annotatedCode]. |
| 106 Future checkCode( |
| 107 String annotatedCode, |
| 108 ComputeMemberDataFunction computeMemberData, |
| 109 CompileFunction compileFunction, |
| 110 {List<String> options: const <String>[]}) async { |
| 111 IdData data = await computeData( |
| 112 annotatedCode, computeMemberData, compileFunction, |
| 113 options: options); |
| 114 |
| 115 data.actualMap.forEach((Id id, String actual) { |
| 116 String expected = data.expectedMap.remove(id); |
| 117 if (actual != expected) { |
| 118 reportHere(data.compiler.reporter, data.sourceSpanMap[id], |
| 119 'expected:${expected},actual:${actual}'); |
| 120 } |
| 121 Expect.equals(expected, actual); |
| 122 }); |
| 123 |
| 124 data.expectedMap.forEach((Id id, String expected) { |
| 49 reportHere( | 125 reportHere( |
| 50 compiler.reporter, | 126 data.compiler.reporter, |
| 51 computeSpannable(elementEnvironment, mainUri, id), | 127 computeSpannable(data.elementEnvironment, data.mainUri, id), |
| 52 'expected:${expected},actual:null'); | 128 'expected:${expected},actual:null'); |
| 53 }); | 129 }); |
| 54 Expect.isTrue(expectedMap.isEmpty, "Ids not found: $expectedMap."); | 130 Expect.isTrue( |
| 131 data.expectedMap.isEmpty, "Ids not found: ${data.expectedMap}."); |
| 55 } | 132 } |
| 56 | 133 |
| 134 /// Compute a [Spannable] from an [id] in the library [mainUri]. |
| 57 Spannable computeSpannable( | 135 Spannable computeSpannable( |
| 58 ElementEnvironment elementEnvironment, Uri mainUri, Id id) { | 136 ElementEnvironment elementEnvironment, Uri mainUri, Id id) { |
| 59 if (id is NodeId) { | 137 if (id is NodeId) { |
| 60 return new SourceSpan(mainUri, id.value, id.value + 1); | 138 return new SourceSpan(mainUri, id.value, id.value + 1); |
| 61 } else if (id is ElementId) { | 139 } else if (id is ElementId) { |
| 62 LibraryEntity library = elementEnvironment.lookupLibrary(mainUri); | 140 LibraryEntity library = elementEnvironment.lookupLibrary(mainUri); |
| 63 if (id.className != null) { | 141 if (id.className != null) { |
| 64 ClassEntity cls = | 142 ClassEntity cls = |
| 65 elementEnvironment.lookupClass(library, id.className, required: true); | 143 elementEnvironment.lookupClass(library, id.className, required: true); |
| 66 return elementEnvironment.lookupClassMember(cls, id.memberName); | 144 return elementEnvironment.lookupClassMember(cls, id.memberName); |
| 67 } else { | 145 } else { |
| 68 return elementEnvironment.lookupLibraryMember(library, id.memberName); | 146 return elementEnvironment.lookupLibraryMember(library, id.memberName); |
| 69 } | 147 } |
| 70 } | 148 } |
| 71 throw new UnsupportedError('Unsupported id $id.'); | 149 throw new UnsupportedError('Unsupported id $id.'); |
| 72 } | 150 } |
| 73 | 151 |
| 152 /// Compute the expectancy map from [code]. |
| 74 Map<Id, String> computeExpectedMap(AnnotatedCode code) { | 153 Map<Id, String> computeExpectedMap(AnnotatedCode code) { |
| 75 Map<Id, String> map = <Id, String>{}; | 154 Map<Id, String> map = <Id, String>{}; |
| 76 for (Annotation annotation in code.annotations) { | 155 for (Annotation annotation in code.annotations) { |
| 77 String text = annotation.text; | 156 String text = annotation.text; |
| 78 int colonPos = text.indexOf(':'); | 157 int colonPos = text.indexOf(':'); |
| 79 Id id; | 158 Id id; |
| 80 String expected; | 159 String expected; |
| 81 if (colonPos == -1) { | 160 if (colonPos == -1) { |
| 82 id = new NodeId(annotation.offset); | 161 id = new NodeId(annotation.offset); |
| 83 expected = text; | 162 expected = text; |
| 84 } else { | 163 } else { |
| 85 id = new ElementId(text.substring(0, colonPos)); | 164 id = new ElementId(text.substring(0, colonPos)); |
| 86 expected = text.substring(colonPos + 1); | 165 expected = text.substring(colonPos + 1); |
| 87 } | 166 } |
| 88 map[id] = expected; | 167 map[id] = expected; |
| 89 } | 168 } |
| 90 return map; | 169 return map; |
| 91 } | 170 } |
| 92 | 171 |
| 93 abstract class AbstractResolvedAstChecker extends Visitor | 172 /// Mixin used for computing [Id] data. |
| 94 with AstEnumeratorMixin { | 173 abstract class ComputerMixin { |
| 174 Map<Id, String> get actualMap; |
| 175 Map<Id, SourceSpan> get sourceSpanMap; |
| 176 |
| 177 void registerValue(SourceSpan sourceSpan, Id id, String value) { |
| 178 if (id != null && value != null) { |
| 179 sourceSpanMap[id] = sourceSpan; |
| 180 actualMap[id] = value; |
| 181 } |
| 182 } |
| 183 } |
| 184 |
| 185 /// Abstract AST visitor for computing [Id] data. |
| 186 abstract class AbstractResolvedAstComputer extends ast.Visitor |
| 187 with AstEnumeratorMixin, ComputerMixin { |
| 95 final DiagnosticReporter reporter; | 188 final DiagnosticReporter reporter; |
| 96 final Map<Id, String> expectedMap; | 189 final Map<Id, String> actualMap; |
| 190 final Map<Id, SourceSpan> sourceSpanMap; |
| 97 final ResolvedAst resolvedAst; | 191 final ResolvedAst resolvedAst; |
| 98 | 192 |
| 99 AbstractResolvedAstChecker(this.reporter, this.expectedMap, this.resolvedAst); | 193 AbstractResolvedAstComputer( |
| 194 this.reporter, this.actualMap, this.sourceSpanMap, this.resolvedAst); |
| 100 | 195 |
| 101 TreeElements get elements => resolvedAst.elements; | 196 TreeElements get elements => resolvedAst.elements; |
| 102 | 197 |
| 103 void check() { | 198 void computeForElement(AstElement element) { |
| 104 resolvedAst.node.accept(this); | 199 ElementId id = computeElementId(element); |
| 200 if (id == null) return; |
| 201 String value = computeElementValue(element); |
| 202 registerValue(element.sourcePosition, id, value); |
| 105 } | 203 } |
| 106 | 204 |
| 107 visitNode(Node node) { | 205 void computeForNode(ast.Node node, AstElement element) { |
| 108 node.visitChildren(this); | 206 NodeId id = computeNodeId(node, element); |
| 109 } | 207 if (id == null) return; |
| 110 | 208 String value = computeNodeValue(node, element); |
| 111 void checkElement(AstElement element) { | 209 SourceSpan sourceSpan = new SourceSpan(resolvedAst.sourceUri, |
| 112 ElementId id = computeElementId(element); | 210 node.getBeginToken().charOffset, node.getEndToken().charEnd); |
| 113 String expected = annotationForId(id); | 211 registerValue(sourceSpan, id, value); |
| 114 String value = computeElementValue(element); | |
| 115 checkValue(element, expected, value); | |
| 116 } | 212 } |
| 117 | 213 |
| 118 String computeElementValue(AstElement element); | 214 String computeElementValue(AstElement element); |
| 119 | 215 |
| 120 String annotationForId(Id id) { | 216 String computeNodeValue(ast.Node node, AstElement element); |
| 121 if (id == null) return null; | 217 |
| 122 return expectedMap.remove(id); | 218 void run() { |
| 219 resolvedAst.node.accept(this); |
| 123 } | 220 } |
| 124 | 221 |
| 125 void checkValue(Spannable spannable, String expected, String value) { | 222 visitNode(ast.Node node) { |
| 126 if (value != null || expected != null) { | 223 node.visitChildren(this); |
| 127 if (value != expected) { | |
| 128 reportHere(reporter, spannable, 'expected:${expected},actual:${value}'); | |
| 129 } | |
| 130 Expect.equals(expected, value); | |
| 131 } | |
| 132 } | 224 } |
| 133 | 225 |
| 134 void checkNode(Node node, AstElement element) { | 226 visitVariableDefinitions(ast.VariableDefinitions node) { |
| 135 NodeId id = computeNodeId(node, element); | 227 for (ast.Node child in node.definitions) { |
| 136 String expected = annotationForId(id); | |
| 137 String value = computeNodeValue(node, element); | |
| 138 checkValue(node, expected, value); | |
| 139 } | |
| 140 | |
| 141 String computeNodeValue(Node node, [AstElement element]); | |
| 142 | |
| 143 visitVariableDefinitions(VariableDefinitions node) { | |
| 144 for (Node child in node.definitions) { | |
| 145 AstElement element = elements[child]; | 228 AstElement element = elements[child]; |
| 146 if (element == null) { | 229 if (element == null) { |
| 147 reportHere(reporter, child, 'No element for variable.'); | 230 reportHere(reporter, child, 'No element for variable.'); |
| 148 } else if (!element.isLocal) { | 231 } else if (!element.isLocal) { |
| 149 checkElement(element); | 232 computeForElement(element); |
| 150 } else { | 233 } else { |
| 151 checkNode(child, element); | 234 computeForNode(child, element); |
| 152 } | 235 } |
| 153 } | 236 } |
| 154 visitNode(node); | 237 visitNode(node); |
| 155 } | 238 } |
| 156 | 239 |
| 157 visitFunctionExpression(FunctionExpression node) { | 240 visitFunctionExpression(ast.FunctionExpression node) { |
| 158 AstElement element = elements.getFunctionDefinition(node); | 241 AstElement element = elements.getFunctionDefinition(node); |
| 159 if (!element.isLocal) { | 242 if (!element.isLocal) { |
| 160 checkElement(element); | 243 computeForElement(element); |
| 161 } else { | 244 } else { |
| 162 checkNode(node, element); | 245 computeForNode(node, element); |
| 163 } | 246 } |
| 164 visitNode(node); | 247 visitNode(node); |
| 165 } | 248 } |
| 166 | 249 |
| 167 visitSend(Send node) { | 250 visitSend(ast.Send node) { |
| 168 checkNode(node, null); | 251 computeForNode(node, null); |
| 169 visitNode(node); | 252 visitNode(node); |
| 170 } | 253 } |
| 171 | 254 |
| 172 visitSendSet(SendSet node) { | 255 visitSendSet(ast.SendSet node) { |
| 173 checkNode(node, null); | 256 computeForNode(node, null); |
| 174 visitNode(node); | 257 visitNode(node); |
| 175 } | 258 } |
| 176 } | 259 } |
| 260 |
| 261 /// Abstract IR visitor for computing [Id] data. |
| 262 abstract class AbstractIrComputer extends ir.Visitor |
| 263 with IrEnumeratorMixin, ComputerMixin { |
| 264 final Map<Id, String> actualMap; |
| 265 final Map<Id, SourceSpan> sourceSpanMap; |
| 266 |
| 267 AbstractIrComputer(this.actualMap, this.sourceSpanMap); |
| 268 |
| 269 void computeForMember(ir.Member member) { |
| 270 ElementId id = computeElementId(member); |
| 271 if (id == null) return; |
| 272 String value = computeMemberValue(member); |
| 273 registerValue(computeSpannable(member), id, value); |
| 274 } |
| 275 |
| 276 void computeForNode(ir.TreeNode node) { |
| 277 NodeId id = computeNodeId(node); |
| 278 if (id == null) return; |
| 279 String value = computeNodeValue(node); |
| 280 registerValue(computeSpannable(node), id, value); |
| 281 } |
| 282 |
| 283 Spannable computeSpannable(ir.TreeNode node) { |
| 284 return new SourceSpan( |
| 285 Uri.parse(node.location.file), node.fileOffset, node.fileOffset + 1); |
| 286 } |
| 287 |
| 288 String computeMemberValue(ir.Member member); |
| 289 |
| 290 String computeNodeValue(ir.TreeNode node); |
| 291 |
| 292 void run(ir.Node root) { |
| 293 root.accept(this); |
| 294 } |
| 295 |
| 296 defaultNode(ir.Node node) { |
| 297 node.visitChildren(this); |
| 298 } |
| 299 |
| 300 defaultMember(ir.Member node) { |
| 301 computeForMember(node); |
| 302 super.defaultMember(node); |
| 303 } |
| 304 |
| 305 visitMethodInvocation(ir.MethodInvocation node) { |
| 306 computeForNode(node); |
| 307 super.visitMethodInvocation(node); |
| 308 } |
| 309 |
| 310 visitPropertyGet(ir.PropertyGet node) { |
| 311 computeForNode(node); |
| 312 super.visitPropertyGet(node); |
| 313 } |
| 314 |
| 315 visitVariableDeclaration(ir.VariableDeclaration node) { |
| 316 computeForNode(node); |
| 317 super.visitVariableDeclaration(node); |
| 318 } |
| 319 |
| 320 visitFunctionDeclaration(ir.FunctionDeclaration node) { |
| 321 computeForNode(node); |
| 322 super.visitFunctionDeclaration(node); |
| 323 } |
| 324 } |
| OLD | NEW |