| 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'; | 5 import 'dart:async'; |
| 6 import 'package:compiler/src/commandline_options.dart'; | 6 import 'package:compiler/src/commandline_options.dart'; |
| 7 import 'package:compiler/src/common.dart'; | 7 import 'package:compiler/src/common.dart'; |
| 8 import 'package:compiler/src/common_elements.dart'; | 8 import 'package:compiler/src/common_elements.dart'; |
| 9 import 'package:compiler/src/compiler.dart'; | 9 import 'package:compiler/src/compiler.dart'; |
| 10 import 'package:compiler/src/elements/elements.dart'; | 10 import 'package:compiler/src/elements/elements.dart'; |
| 11 import 'package:compiler/src/elements/entities.dart'; | 11 import 'package:compiler/src/elements/entities.dart'; |
| 12 import 'package:compiler/src/resolution/tree_elements.dart'; | 12 import 'package:compiler/src/resolution/tree_elements.dart'; |
| 13 import 'package:compiler/src/tree/nodes.dart' as ast; | 13 import 'package:compiler/src/tree/nodes.dart' as ast; |
| 14 import 'package:expect/expect.dart'; | 14 import 'package:expect/expect.dart'; |
| 15 import 'package:kernel/ast.dart' as ir; | 15 import 'package:kernel/ast.dart' as ir; |
| 16 | 16 |
| 17 import '../annotated_code_helper.dart'; | 17 import '../annotated_code_helper.dart'; |
| 18 import '../memory_compiler.dart'; | 18 import '../memory_compiler.dart'; |
| 19 import '../equivalence/id_equivalence.dart'; | 19 import '../equivalence/id_equivalence.dart'; |
| 20 import '../kernel/compiler_helper.dart'; | 20 import '../kernel/compiler_helper.dart'; |
| 21 | 21 |
| 22 /// Function that compiles [code] with [options] and returns the [Compiler] obje
ct. | 22 /// Function that compiles [code] with [options] and returns the [Compiler] obje
ct. |
| 23 typedef Future<Compiler> CompileFunction( | 23 typedef Future<Compiler> CompileFunction( |
| 24 AnnotatedCode code, Uri mainUri, List<String> options); | 24 AnnotatedCode code, Uri mainUri, List<String> options); |
| 25 | 25 |
| 26 /// Function that computes a data mapping for [member]. | 26 /// Function that computes a data mapping for [member]. |
| 27 /// | 27 /// |
| 28 /// Fills [actualMap] with the data and [sourceSpanMap] with the source spans | 28 /// Fills [actualMap] with the data and [sourceSpanMap] with the source spans |
| 29 /// for the data origin. | 29 /// for the data origin. |
| 30 typedef void ComputeMemberDataFunction(Compiler compiler, MemberEntity member, | 30 typedef void ComputeMemberDataFunction( |
| 31 Map<Id, String> actualMap, Map<Id, SourceSpan> sourceSpanMap, | 31 Compiler compiler, MemberEntity member, Map<Id, ActualData> actualMap, |
| 32 {bool verbose}); | 32 {bool verbose}); |
| 33 | 33 |
| 34 /// Compile [code] from .dart sources. | 34 /// Compile [code] from .dart sources. |
| 35 Future<Compiler> compileFromSource( | 35 Future<Compiler> compileFromSource( |
| 36 AnnotatedCode code, Uri mainUri, List<String> options) async { | 36 AnnotatedCode code, Uri mainUri, List<String> options) async { |
| 37 Compiler compiler = compilerFor( | 37 Compiler compiler = compilerFor( |
| 38 memorySourceFiles: {'main.dart': code.sourceCode}, options: options); | 38 memorySourceFiles: {'main.dart': code.sourceCode}, options: options); |
| 39 compiler.stopAfterTypeInference = true; | 39 compiler.stopAfterTypeInference = true; |
| 40 await compiler.run(mainUri); | 40 await compiler.run(mainUri); |
| 41 return compiler; | 41 return compiler; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 60 /// using [compileFunction]. | 60 /// using [compileFunction]. |
| 61 Future<IdData> computeData( | 61 Future<IdData> computeData( |
| 62 String annotatedCode, | 62 String annotatedCode, |
| 63 ComputeMemberDataFunction computeMemberData, | 63 ComputeMemberDataFunction computeMemberData, |
| 64 CompileFunction compileFunction, | 64 CompileFunction compileFunction, |
| 65 {List<String> options: const <String>[], | 65 {List<String> options: const <String>[], |
| 66 bool verbose: false}) async { | 66 bool verbose: false}) async { |
| 67 AnnotatedCode code = | 67 AnnotatedCode code = |
| 68 new AnnotatedCode.fromText(annotatedCode, commentStart, commentEnd); | 68 new AnnotatedCode.fromText(annotatedCode, commentStart, commentEnd); |
| 69 Map<Id, String> expectedMap = computeExpectedMap(code); | 69 Map<Id, String> expectedMap = computeExpectedMap(code); |
| 70 Map<Id, String> actualMap = <Id, String>{}; | 70 Map<Id, ActualData> actualMap = <Id, ActualData>{}; |
| 71 Map<Id, SourceSpan> sourceSpanMap = <Id, SourceSpan>{}; | |
| 72 Uri mainUri = Uri.parse('memory:main.dart'); | 71 Uri mainUri = Uri.parse('memory:main.dart'); |
| 73 Compiler compiler = await compileFunction(code, mainUri, options); | 72 Compiler compiler = await compileFunction(code, mainUri, options); |
| 74 ElementEnvironment elementEnvironment = | 73 ElementEnvironment elementEnvironment = |
| 75 compiler.backendClosedWorldForTesting.elementEnvironment; | 74 compiler.backendClosedWorldForTesting.elementEnvironment; |
| 76 LibraryEntity mainLibrary = elementEnvironment.mainLibrary; | 75 LibraryEntity mainLibrary = elementEnvironment.mainLibrary; |
| 77 elementEnvironment.forEachClass(mainLibrary, (ClassEntity cls) { | 76 elementEnvironment.forEachClass(mainLibrary, (ClassEntity cls) { |
| 78 elementEnvironment.forEachClassMember(cls, | 77 elementEnvironment.forEachClassMember(cls, |
| 79 (ClassEntity declarer, MemberEntity member) { | 78 (ClassEntity declarer, MemberEntity member) { |
| 80 if (cls == declarer) { | 79 if (cls == declarer) { |
| 81 computeMemberData(compiler, member, actualMap, sourceSpanMap, | 80 computeMemberData(compiler, member, actualMap, verbose: verbose); |
| 82 verbose: verbose); | |
| 83 } | 81 } |
| 84 }); | 82 }); |
| 85 }); | 83 }); |
| 86 elementEnvironment.forEachLibraryMember(mainLibrary, (MemberEntity member) { | 84 elementEnvironment.forEachLibraryMember(mainLibrary, (MemberEntity member) { |
| 87 computeMemberData(compiler, member, actualMap, sourceSpanMap, | 85 computeMemberData(compiler, member, actualMap, verbose: verbose); |
| 88 verbose: verbose); | |
| 89 }); | 86 }); |
| 90 return new IdData(compiler, elementEnvironment, mainUri, expectedMap, | 87 return new IdData( |
| 91 actualMap, sourceSpanMap); | 88 code, compiler, elementEnvironment, mainUri, expectedMap, actualMap); |
| 89 } |
| 90 |
| 91 class ActualData { |
| 92 final Id id; |
| 93 final String value; |
| 94 final SourceSpan sourceSpan; |
| 95 final Object object; |
| 96 |
| 97 ActualData(this.id, this.value, this.sourceSpan, this.object); |
| 92 } | 98 } |
| 93 | 99 |
| 94 /// Data collected by [computeData]. | 100 /// Data collected by [computeData]. |
| 95 class IdData { | 101 class IdData { |
| 102 final AnnotatedCode code; |
| 96 final Compiler compiler; | 103 final Compiler compiler; |
| 97 final ElementEnvironment elementEnvironment; | 104 final ElementEnvironment elementEnvironment; |
| 98 final Uri mainUri; | 105 final Uri mainUri; |
| 99 final Map<Id, String> expectedMap; | 106 final Map<Id, String> expectedMap; |
| 100 final Map<Id, String> actualMap; | 107 final Map<Id, ActualData> actualMap; |
| 101 final Map<Id, SourceSpan> sourceSpanMap; | |
| 102 | 108 |
| 103 IdData(this.compiler, this.elementEnvironment, this.mainUri, this.expectedMap, | 109 IdData(this.code, this.compiler, this.elementEnvironment, this.mainUri, |
| 104 this.actualMap, this.sourceSpanMap); | 110 this.expectedMap, this.actualMap); |
| 111 |
| 112 String withAnnotations(Map<int, String> annotations) { |
| 113 StringBuffer sb = new StringBuffer(); |
| 114 int end = 0; |
| 115 for (int offset in annotations.keys.toList()..sort()) { |
| 116 if (offset > end) { |
| 117 sb.write(code.sourceCode.substring(end, offset)); |
| 118 } |
| 119 sb.write('/* '); |
| 120 sb.write(annotations[offset]); |
| 121 sb.write(' */'); |
| 122 end = offset; |
| 123 } |
| 124 if (end < code.sourceCode.length) { |
| 125 sb.write(code.sourceCode.substring(end)); |
| 126 } |
| 127 return sb.toString(); |
| 128 } |
| 129 |
| 130 String get actualCode { |
| 131 Map<int, String> annotations = <int, String>{}; |
| 132 actualMap.forEach((Id id, ActualData data) { |
| 133 annotations[data.sourceSpan.begin] = data.value; |
| 134 }); |
| 135 return withAnnotations(annotations); |
| 136 } |
| 137 |
| 138 String get diffCode { |
| 139 Map<int, String> annotations = <int, String>{}; |
| 140 actualMap.forEach((Id id, ActualData data) { |
| 141 String expected = expectedMap[id]; |
| 142 if (data.value != expected) { |
| 143 expected ??= '---'; |
| 144 annotations[data.sourceSpan.begin] = '${expected} | ${data.value}'; |
| 145 } |
| 146 }); |
| 147 expectedMap.forEach((Id id, String expected) { |
| 148 if (!actualMap.containsKey(id)) { |
| 149 int offset = compiler.reporter |
| 150 .spanFromSpannable( |
| 151 computeSpannable(elementEnvironment, mainUri, id)) |
| 152 .begin; |
| 153 annotations[offset] = '${expected} | ---'; |
| 154 } |
| 155 }); |
| 156 return withAnnotations(annotations); |
| 157 } |
| 105 } | 158 } |
| 106 | 159 |
| 107 /// Compiles the [annotatedCode] with the provided [options] and calls | 160 /// Compiles the [annotatedCode] with the provided [options] and calls |
| 108 /// [computeMemberData] for each member. The result is checked against the | 161 /// [computeMemberData] for each member. The result is checked against the |
| 109 /// expected data derived from [annotatedCode]. | 162 /// expected data derived from [annotatedCode]. |
| 110 Future checkCode( | 163 Future checkCode( |
| 111 String annotatedCode, | 164 String annotatedCode, |
| 112 ComputeMemberDataFunction computeMemberData, | 165 ComputeMemberDataFunction computeMemberData, |
| 113 CompileFunction compileFunction, | 166 CompileFunction compileFunction, |
| 114 {List<String> options: const <String>[], | 167 {List<String> options: const <String>[], |
| 115 bool verbose: false}) async { | 168 bool verbose: false}) async { |
| 116 IdData data = await computeData( | 169 IdData data = await computeData( |
| 117 annotatedCode, computeMemberData, compileFunction, | 170 annotatedCode, computeMemberData, compileFunction, |
| 118 options: options, verbose: verbose); | 171 options: options, verbose: verbose); |
| 119 | 172 |
| 120 data.actualMap.forEach((Id id, String actual) { | 173 data.actualMap.forEach((Id id, ActualData actualData) { |
| 174 String actual = actualData.value; |
| 121 if (!data.expectedMap.containsKey(id)) { | 175 if (!data.expectedMap.containsKey(id)) { |
| 122 if (actual != '') { | 176 if (actual != '') { |
| 123 reportHere(data.compiler.reporter, data.sourceSpanMap[id], | 177 reportHere( |
| 124 'Id $id not expected in ${data.expectedMap.keys}'); | 178 data.compiler.reporter, |
| 179 actualData.sourceSpan, |
| 180 'Id $id for ${actualData.object} ' |
| 181 '(${actualData.object.runtimeType}) ' |
| 182 'not expected in ${data.expectedMap.keys}'); |
| 183 print('--annotations diff--------------------------------------------'); |
| 184 print(data.diffCode); |
| 185 print('--------------------------------------------------------------'); |
| 125 } | 186 } |
| 126 Expect.equals('', actual); | 187 Expect.equals('', actual); |
| 127 } else { | 188 } else { |
| 128 String expected = data.expectedMap.remove(id); | 189 String expected = data.expectedMap.remove(id); |
| 129 if (actual != expected) { | 190 if (actual != expected) { |
| 130 reportHere(data.compiler.reporter, data.sourceSpanMap[id], | 191 reportHere( |
| 131 'expected:${expected},actual:${actual}'); | 192 data.compiler.reporter, |
| 193 actualData.sourceSpan, |
| 194 'Object: ${actualData.object} (${actualData.object.runtimeType}), ' |
| 195 'expected: ${expected}, actual: ${actual}'); |
| 196 print('--annotations diff--------------------------------------------'); |
| 197 print(data.diffCode); |
| 198 print('--------------------------------------------------------------'); |
| 132 } | 199 } |
| 133 Expect.equals(expected, actual); | 200 Expect.equals(expected, actual); |
| 134 } | 201 } |
| 135 }); | 202 }); |
| 136 | 203 |
| 137 data.expectedMap.forEach((Id id, String expected) { | 204 data.expectedMap.forEach((Id id, String expected) { |
| 138 reportHere( | 205 reportHere( |
| 139 data.compiler.reporter, | 206 data.compiler.reporter, |
| 140 computeSpannable(data.elementEnvironment, data.mainUri, id), | 207 computeSpannable(data.elementEnvironment, data.mainUri, id), |
| 141 'expected:${expected},actual:null'); | 208 'Expected $expected for id $id missing in ${data.actualMap.keys}'); |
| 142 }); | 209 }); |
| 143 Expect.isTrue( | 210 Expect.isTrue( |
| 144 data.expectedMap.isEmpty, "Ids not found: ${data.expectedMap}."); | 211 data.expectedMap.isEmpty, "Ids not found: ${data.expectedMap}."); |
| 145 } | 212 } |
| 146 | 213 |
| 147 /// Compute a [Spannable] from an [id] in the library [mainUri]. | 214 /// Compute a [Spannable] from an [id] in the library [mainUri]. |
| 148 Spannable computeSpannable( | 215 Spannable computeSpannable( |
| 149 ElementEnvironment elementEnvironment, Uri mainUri, Id id) { | 216 ElementEnvironment elementEnvironment, Uri mainUri, Id id) { |
| 150 if (id is NodeId) { | 217 if (id is NodeId) { |
| 151 return new SourceSpan(mainUri, id.value, id.value + 1); | 218 return new SourceSpan(mainUri, id.value, id.value + 1); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 177 id = new ElementId(text.substring(0, colonPos)); | 244 id = new ElementId(text.substring(0, colonPos)); |
| 178 expected = text.substring(colonPos + 1); | 245 expected = text.substring(colonPos + 1); |
| 179 } | 246 } |
| 180 map[id] = expected; | 247 map[id] = expected; |
| 181 } | 248 } |
| 182 return map; | 249 return map; |
| 183 } | 250 } |
| 184 | 251 |
| 185 /// Mixin used for computing [Id] data. | 252 /// Mixin used for computing [Id] data. |
| 186 abstract class ComputerMixin { | 253 abstract class ComputerMixin { |
| 187 Map<Id, String> get actualMap; | 254 Map<Id, ActualData> get actualMap; |
| 188 Map<Id, SourceSpan> get sourceSpanMap; | |
| 189 | 255 |
| 190 void registerValue(SourceSpan sourceSpan, Id id, String value) { | 256 void registerValue( |
| 257 SourceSpan sourceSpan, Id id, String value, Object object) { |
| 191 if (id != null && value != null) { | 258 if (id != null && value != null) { |
| 192 sourceSpanMap[id] = sourceSpan; | 259 actualMap[id] = new ActualData(id, value, sourceSpan, object); |
| 193 actualMap[id] = value; | |
| 194 } | 260 } |
| 195 } | 261 } |
| 196 } | 262 } |
| 197 | 263 |
| 198 /// Abstract AST visitor for computing [Id] data. | 264 /// Abstract AST visitor for computing [Id] data. |
| 199 abstract class AbstractResolvedAstComputer extends ast.Visitor | 265 abstract class AbstractResolvedAstComputer extends ast.Visitor |
| 200 with AstEnumeratorMixin, ComputerMixin { | 266 with AstEnumeratorMixin, ComputerMixin { |
| 201 final DiagnosticReporter reporter; | 267 final DiagnosticReporter reporter; |
| 202 final Map<Id, String> actualMap; | 268 final Map<Id, ActualData> actualMap; |
| 203 final Map<Id, SourceSpan> sourceSpanMap; | |
| 204 final ResolvedAst resolvedAst; | 269 final ResolvedAst resolvedAst; |
| 205 | 270 |
| 206 AbstractResolvedAstComputer( | 271 AbstractResolvedAstComputer(this.reporter, this.actualMap, this.resolvedAst); |
| 207 this.reporter, this.actualMap, this.sourceSpanMap, this.resolvedAst); | |
| 208 | 272 |
| 209 TreeElements get elements => resolvedAst.elements; | 273 TreeElements get elements => resolvedAst.elements; |
| 210 | 274 |
| 211 void computeForElement(AstElement element) { | 275 void computeForElement(AstElement element) { |
| 212 ElementId id = computeElementId(element); | 276 ElementId id = computeElementId(element); |
| 213 if (id == null) return; | 277 if (id == null) return; |
| 214 String value = computeElementValue(element); | 278 String value = computeElementValue(element); |
| 215 registerValue(element.sourcePosition, id, value); | 279 registerValue(element.sourcePosition, id, value, element); |
| 216 } | 280 } |
| 217 | 281 |
| 218 void computeForNode(ast.Node node, AstElement element) { | 282 void computeForNode(ast.Node node, AstElement element) { |
| 219 NodeId id = computeNodeId(node, element); | 283 NodeId id = computeNodeId(node, element); |
| 220 if (id == null) return; | 284 if (id == null) return; |
| 221 String value = computeNodeValue(node, element); | 285 String value = computeNodeValue(node, element); |
| 222 SourceSpan sourceSpan = new SourceSpan(resolvedAst.sourceUri, | 286 SourceSpan sourceSpan = new SourceSpan(resolvedAst.sourceUri, |
| 223 node.getBeginToken().charOffset, node.getEndToken().charEnd); | 287 node.getBeginToken().charOffset, node.getEndToken().charEnd); |
| 224 registerValue(sourceSpan, id, value); | 288 registerValue(sourceSpan, id, value, element ?? node); |
| 225 } | 289 } |
| 226 | 290 |
| 227 String computeElementValue(AstElement element); | 291 String computeElementValue(AstElement element); |
| 228 | 292 |
| 229 String computeNodeValue(ast.Node node, AstElement element); | 293 String computeNodeValue(ast.Node node, AstElement element); |
| 230 | 294 |
| 231 void run() { | 295 void run() { |
| 232 resolvedAst.node.accept(this); | 296 resolvedAst.node.accept(this); |
| 233 } | 297 } |
| 234 | 298 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 | 331 |
| 268 visitSendSet(ast.SendSet node) { | 332 visitSendSet(ast.SendSet node) { |
| 269 computeForNode(node, null); | 333 computeForNode(node, null); |
| 270 visitNode(node); | 334 visitNode(node); |
| 271 } | 335 } |
| 272 } | 336 } |
| 273 | 337 |
| 274 /// Abstract IR visitor for computing [Id] data. | 338 /// Abstract IR visitor for computing [Id] data. |
| 275 abstract class AbstractIrComputer extends ir.Visitor | 339 abstract class AbstractIrComputer extends ir.Visitor |
| 276 with IrEnumeratorMixin, ComputerMixin { | 340 with IrEnumeratorMixin, ComputerMixin { |
| 277 final Map<Id, String> actualMap; | 341 final Map<Id, ActualData> actualMap; |
| 278 final Map<Id, SourceSpan> sourceSpanMap; | |
| 279 | 342 |
| 280 AbstractIrComputer(this.actualMap, this.sourceSpanMap); | 343 AbstractIrComputer(this.actualMap); |
| 281 | 344 |
| 282 void computeForMember(ir.Member member) { | 345 void computeForMember(ir.Member member) { |
| 283 ElementId id = computeElementId(member); | 346 ElementId id = computeElementId(member); |
| 284 if (id == null) return; | 347 if (id == null) return; |
| 285 String value = computeMemberValue(member); | 348 String value = computeMemberValue(member); |
| 286 registerValue(computeSpannable(member), id, value); | 349 registerValue(computeSpannable(member), id, value, member); |
| 287 } | 350 } |
| 288 | 351 |
| 289 void computeForNode(ir.TreeNode node) { | 352 void computeForNode(ir.TreeNode node) { |
| 290 NodeId id = computeNodeId(node); | 353 NodeId id = computeNodeId(node); |
| 291 if (id == null) return; | 354 if (id == null) return; |
| 292 String value = computeNodeValue(node); | 355 String value = computeNodeValue(node); |
| 293 registerValue(computeSpannable(node), id, value); | 356 registerValue(computeSpannable(node), id, value, node); |
| 294 } | 357 } |
| 295 | 358 |
| 296 Spannable computeSpannable(ir.TreeNode node) { | 359 Spannable computeSpannable(ir.TreeNode node) { |
| 297 return new SourceSpan( | 360 return new SourceSpan( |
| 298 Uri.parse(node.location.file), node.fileOffset, node.fileOffset + 1); | 361 Uri.parse(node.location.file), node.fileOffset, node.fileOffset + 1); |
| 299 } | 362 } |
| 300 | 363 |
| 301 String computeMemberValue(ir.Member member); | 364 String computeMemberValue(ir.Member member); |
| 302 | 365 |
| 303 String computeNodeValue(ir.TreeNode node); | 366 String computeNodeValue(ir.TreeNode node); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 333 visitFunctionDeclaration(ir.FunctionDeclaration node) { | 396 visitFunctionDeclaration(ir.FunctionDeclaration node) { |
| 334 computeForNode(node); | 397 computeForNode(node); |
| 335 super.visitFunctionDeclaration(node); | 398 super.visitFunctionDeclaration(node); |
| 336 } | 399 } |
| 337 | 400 |
| 338 visitFunctionExpression(ir.FunctionExpression node) { | 401 visitFunctionExpression(ir.FunctionExpression node) { |
| 339 computeForNode(node); | 402 computeForNode(node); |
| 340 super.visitFunctionExpression(node); | 403 super.visitFunctionExpression(node); |
| 341 } | 404 } |
| 342 } | 405 } |
| OLD | NEW |