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 |