OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 dart2js.serialization_model_test; | 5 library dart2js.serialization_model_test; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 import 'package:async_helper/async_helper.dart'; | 9 import 'package:async_helper/async_helper.dart'; |
10 import 'package:expect/expect.dart'; | 10 import 'package:expect/expect.dart'; |
11 import 'package:compiler/src/closure.dart'; | 11 import 'package:compiler/src/closure.dart'; |
12 import 'package:compiler/src/commandline_options.dart'; | 12 import 'package:compiler/src/commandline_options.dart'; |
13 import 'package:compiler/src/common.dart'; | 13 import 'package:compiler/src/common.dart'; |
14 import 'package:compiler/src/constants/values.dart'; | 14 import 'package:compiler/src/constants/values.dart'; |
15 import 'package:compiler/src/compiler.dart'; | 15 import 'package:compiler/src/compiler.dart'; |
| 16 import 'package:compiler/src/dart_types.dart'; |
16 import 'package:compiler/src/deferred_load.dart'; | 17 import 'package:compiler/src/deferred_load.dart'; |
17 import 'package:compiler/src/elements/elements.dart'; | 18 import 'package:compiler/src/elements/elements.dart'; |
| 19 import 'package:compiler/src/enqueue.dart'; |
18 import 'package:compiler/src/filenames.dart'; | 20 import 'package:compiler/src/filenames.dart'; |
19 import 'package:compiler/src/js_backend/js_backend.dart'; | 21 import 'package:compiler/src/js_backend/js_backend.dart'; |
20 import 'package:compiler/src/serialization/equivalence.dart'; | 22 import 'package:compiler/src/serialization/equivalence.dart'; |
21 import 'package:compiler/src/tree/nodes.dart'; | 23 import 'package:compiler/src/tree/nodes.dart'; |
22 import 'package:compiler/src/universe/class_set.dart'; | 24 import 'package:compiler/src/universe/class_set.dart'; |
| 25 import 'package:compiler/src/world.dart' show ClosedWorld; |
23 import '../memory_compiler.dart'; | 26 import '../memory_compiler.dart'; |
24 import 'helper.dart'; | 27 import 'helper.dart'; |
25 import 'test_data.dart'; | 28 import 'test_data.dart'; |
26 import 'test_helper.dart'; | 29 import 'test_helper.dart'; |
27 | 30 |
28 /// Number of tests that are not part of the automatic test grouping. | 31 /// Number of tests that are not part of the automatic test grouping. |
29 int SKIP_COUNT = 2; | 32 int SKIP_COUNT = 2; |
30 | 33 |
31 /// Number of groups that the [TESTS] are split into. | 34 /// Number of groups that the [TESTS] are split into. |
32 int SPLIT_COUNT = 5; | 35 int SPLIT_COUNT = 5; |
33 | 36 |
34 main(List<String> args) { | 37 main(List<String> args) { |
35 asyncTest(() async { | 38 asyncTest(() async { |
36 Arguments arguments = new Arguments.from(args); | 39 Arguments arguments = new Arguments.from(args); |
37 SerializedData serializedData = | 40 SerializedData serializedData = |
38 await serializeDartCore(arguments: arguments); | 41 await serializeDartCore(arguments: arguments); |
39 if (arguments.filename != null) { | 42 if (arguments.uri != null) { |
40 Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.filename)); | 43 Uri entryPoint = arguments.uri; |
41 SerializationResult result = | 44 SerializationResult result = |
42 await measure('${entryPoint}', 'serialize', () { | 45 await measure('${entryPoint}', 'serialize', () { |
43 return serialize(entryPoint, | 46 return serialize(entryPoint, |
44 memorySourceFiles: serializedData.toMemorySourceFiles(), | 47 memorySourceFiles: serializedData.toMemorySourceFiles(), |
45 resolutionInputs: serializedData.toUris(), | 48 resolutionInputs: serializedData.toUris(), |
46 dataUri: Uri.parse('memory:test.data')); | 49 dataUri: Uri.parse('memory:test.data')); |
47 }); | 50 }); |
48 await checkModels(entryPoint, | 51 await checkModels(entryPoint, |
49 sourceFiles: serializedData | 52 sourceFiles: serializedData |
50 .toMemorySourceFiles(result.serializedData.toMemorySourceFiles()), | 53 .toMemorySourceFiles(result.serializedData.toMemorySourceFiles()), |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 resolutionInputs: resolutionInputs, | 86 resolutionInputs: resolutionInputs, |
84 options: [Flags.analyzeOnly]); | 87 options: [Flags.analyzeOnly]); |
85 compilerDeserialized.resolution.retainCachesForTesting = true; | 88 compilerDeserialized.resolution.retainCachesForTesting = true; |
86 await compilerDeserialized.run(entryPoint); | 89 await compilerDeserialized.run(entryPoint); |
87 compilerDeserialized.closeResolution(); | 90 compilerDeserialized.closeResolution(); |
88 return compilerDeserialized; | 91 return compilerDeserialized; |
89 }); | 92 }); |
90 | 93 |
91 return measure(title, 'check models', () async { | 94 return measure(title, 'check models', () async { |
92 checkAllImpacts(compilerNormal, compilerDeserialized, verbose: verbose); | 95 checkAllImpacts(compilerNormal, compilerDeserialized, verbose: verbose); |
93 | 96 checkResolutionEnqueuers(compilerNormal.enqueuer.resolution, |
94 checkSets( | 97 compilerDeserialized.enqueuer.resolution, |
95 compilerNormal.resolverWorld.directlyInstantiatedClasses, | |
96 compilerDeserialized.resolverWorld.directlyInstantiatedClasses, | |
97 "Directly instantiated classes mismatch", | |
98 areElementsEquivalent, | |
99 verbose: verbose); | 98 verbose: verbose); |
100 | 99 checkClosedWorlds( |
101 checkSets( | 100 compilerNormal.closedWorld, compilerDeserialized.closedWorld, |
102 compilerNormal.resolverWorld.instantiatedTypes, | |
103 compilerDeserialized.resolverWorld.instantiatedTypes, | |
104 "Instantiated types mismatch", | |
105 areTypesEquivalent, | |
106 verbose: verbose); | 101 verbose: verbose); |
107 | 102 checkBackendInfo(compilerNormal, compilerDeserialized, verbose: verbose); |
108 checkSets( | |
109 compilerNormal.resolverWorld.isChecks, | |
110 compilerDeserialized.resolverWorld.isChecks, | |
111 "Is-check mismatch", | |
112 areTypesEquivalent, | |
113 verbose: verbose); | |
114 | |
115 checkSets( | |
116 compilerNormal.enqueuer.resolution.processedElements, | |
117 compilerDeserialized.enqueuer.resolution.processedElements, | |
118 "Processed element mismatch", | |
119 areElementsEquivalent, onSameElement: (a, b) { | |
120 checkElements(compilerNormal, compilerDeserialized, a, b, | |
121 verbose: verbose); | |
122 }, verbose: verbose); | |
123 | |
124 checkClassHierarchyNodes( | |
125 compilerNormal, | |
126 compilerDeserialized, | |
127 compilerNormal.closedWorld | |
128 .getClassHierarchyNode(compilerNormal.coreClasses.objectClass), | |
129 compilerDeserialized.closedWorld.getClassHierarchyNode( | |
130 compilerDeserialized.coreClasses.objectClass), | |
131 verbose: verbose); | |
132 | |
133 Expect.equals( | |
134 compilerNormal.enabledInvokeOn, | |
135 compilerDeserialized.enabledInvokeOn, | |
136 "Compiler.enabledInvokeOn mismatch"); | |
137 Expect.equals( | |
138 compilerNormal.enabledFunctionApply, | |
139 compilerDeserialized.enabledFunctionApply, | |
140 "Compiler.enabledFunctionApply mismatch"); | |
141 Expect.equals( | |
142 compilerNormal.enabledRuntimeType, | |
143 compilerDeserialized.enabledRuntimeType, | |
144 "Compiler.enabledRuntimeType mismatch"); | |
145 Expect.equals( | |
146 compilerNormal.hasIsolateSupport, | |
147 compilerDeserialized.hasIsolateSupport, | |
148 "Compiler.hasIsolateSupport mismatch"); | |
149 Expect.equals( | |
150 compilerNormal.deferredLoadTask.isProgramSplit, | |
151 compilerDeserialized.deferredLoadTask.isProgramSplit, | |
152 "isProgramSplit mismatch"); | |
153 | |
154 Map<ConstantValue, OutputUnit> constants1 = | |
155 compilerNormal.deferredLoadTask.outputUnitForConstantsForTesting; | |
156 Map<ConstantValue, OutputUnit> constants2 = | |
157 compilerDeserialized.deferredLoadTask.outputUnitForConstantsForTesting; | |
158 checkSets( | |
159 constants1.keys, | |
160 constants2.keys, | |
161 'deferredLoadTask._outputUnitForConstants.keys', | |
162 areConstantValuesEquivalent, | |
163 failOnUnfound: false, | |
164 failOnExtra: false, | |
165 onSameElement: (ConstantValue value1, ConstantValue value2) { | |
166 OutputUnit outputUnit1 = constants1[value1]; | |
167 OutputUnit outputUnit2 = constants2[value2]; | |
168 checkOutputUnits( | |
169 outputUnit1, | |
170 outputUnit2, | |
171 'for ${value1.toStructuredText()} ' | |
172 'vs ${value2.toStructuredText()}'); | |
173 }, onUnfoundElement: (ConstantValue value1) { | |
174 OutputUnit outputUnit1 = constants1[value1]; | |
175 Expect.isTrue(outputUnit1.isMainOutput, | |
176 "Missing deferred constant: ${value1.toStructuredText()}"); | |
177 }, onExtraElement: (ConstantValue value2) { | |
178 OutputUnit outputUnit2 = constants2[value2]; | |
179 Expect.isTrue(outputUnit2.isMainOutput, | |
180 "Extra deferred constant: ${value2.toStructuredText()}"); | |
181 }, elementToString: (a) { | |
182 return '${a.toStructuredText()} -> ${constants1[a]}/${constants2[a]}'; | |
183 }); | |
184 }); | 103 }); |
185 } | 104 } |
186 | 105 |
| 106 void checkResolutionEnqueuers( |
| 107 ResolutionEnqueuer enqueuer1, ResolutionEnqueuer enqueuer2, |
| 108 {bool typeEquivalence(DartType a, DartType b): areTypesEquivalent, |
| 109 bool elementFilter(Element element), |
| 110 bool verbose: false}) { |
| 111 Iterable<Element> processedElements1 = enqueuer1.processedElements; |
| 112 Iterable<Element> processedElements2 = enqueuer2.processedElements; |
| 113 if (elementFilter != null) { |
| 114 processedElements1 = processedElements1.where(elementFilter); |
| 115 processedElements2 = processedElements2.where(elementFilter); |
| 116 } |
| 117 |
| 118 checkSets(processedElements1, processedElements2, |
| 119 "Processed element mismatch", areElementsEquivalent, |
| 120 verbose: verbose); |
| 121 |
| 122 checkSets( |
| 123 enqueuer1.universe.directlyInstantiatedClasses, |
| 124 enqueuer2.universe.directlyInstantiatedClasses, |
| 125 "Directly instantiated classes mismatch", |
| 126 areElementsEquivalent, |
| 127 verbose: verbose); |
| 128 |
| 129 checkSets( |
| 130 enqueuer1.universe.instantiatedTypes, |
| 131 enqueuer2.universe.instantiatedTypes, |
| 132 "Instantiated types mismatch", |
| 133 typeEquivalence, |
| 134 verbose: verbose); |
| 135 |
| 136 checkSets(enqueuer1.universe.isChecks, enqueuer2.universe.isChecks, |
| 137 "Is-check mismatch", typeEquivalence, |
| 138 verbose: verbose); |
| 139 |
| 140 JavaScriptBackend backend1 = enqueuer1.backend; |
| 141 JavaScriptBackend backend2 = enqueuer2.backend; |
| 142 Expect.equals(backend1.hasInvokeOnSupport, |
| 143 backend2.hasInvokeOnSupport, "Compiler.enabledInvokeOn mismatch"); |
| 144 Expect.equals( |
| 145 enqueuer1.universe.hasFunctionApplySupport, |
| 146 enqueuer2.universe.hasFunctionApplySupport, |
| 147 "ResolutionEnqueuer.universe.hasFunctionApplySupport mismatch"); |
| 148 Expect.equals( |
| 149 enqueuer1.universe.hasRuntimeTypeSupport, |
| 150 enqueuer2.universe.hasRuntimeTypeSupport, |
| 151 "ResolutionEnqueuer.universe.hasRuntimeTypeSupport mismatch"); |
| 152 Expect.equals( |
| 153 enqueuer1.universe.hasIsolateSupport, |
| 154 enqueuer2.universe.hasIsolateSupport, |
| 155 "ResolutionEnqueuer.universe.hasIsolateSupport mismatch"); |
| 156 } |
| 157 |
| 158 void checkClosedWorlds(ClosedWorld closedWorld1, ClosedWorld closedWorld2, |
| 159 {bool verbose: false}) { |
| 160 checkClassHierarchyNodes( |
| 161 closedWorld1, |
| 162 closedWorld2, |
| 163 closedWorld1.getClassHierarchyNode(closedWorld1.coreClasses.objectClass), |
| 164 closedWorld2.getClassHierarchyNode(closedWorld2.coreClasses.objectClass), |
| 165 verbose: verbose); |
| 166 } |
| 167 |
| 168 void checkBackendInfo(Compiler compilerNormal, Compiler compilerDeserialized, |
| 169 {bool verbose: false}) { |
| 170 checkSets( |
| 171 compilerNormal.enqueuer.resolution.processedElements, |
| 172 compilerDeserialized.enqueuer.resolution.processedElements, |
| 173 "Processed element mismatch", |
| 174 areElementsEquivalent, onSameElement: (a, b) { |
| 175 checkElements(compilerNormal, compilerDeserialized, a, b, verbose: verbose); |
| 176 }, verbose: verbose); |
| 177 Expect.equals( |
| 178 compilerNormal.deferredLoadTask.isProgramSplit, |
| 179 compilerDeserialized.deferredLoadTask.isProgramSplit, |
| 180 "isProgramSplit mismatch"); |
| 181 |
| 182 Map<ConstantValue, OutputUnit> constants1 = |
| 183 compilerNormal.deferredLoadTask.outputUnitForConstantsForTesting; |
| 184 Map<ConstantValue, OutputUnit> constants2 = |
| 185 compilerDeserialized.deferredLoadTask.outputUnitForConstantsForTesting; |
| 186 checkSets( |
| 187 constants1.keys, |
| 188 constants2.keys, |
| 189 'deferredLoadTask._outputUnitForConstants.keys', |
| 190 areConstantValuesEquivalent, |
| 191 failOnUnfound: false, |
| 192 failOnExtra: false, |
| 193 onSameElement: (ConstantValue value1, ConstantValue value2) { |
| 194 OutputUnit outputUnit1 = constants1[value1]; |
| 195 OutputUnit outputUnit2 = constants2[value2]; |
| 196 checkOutputUnits( |
| 197 outputUnit1, |
| 198 outputUnit2, |
| 199 'for ${value1.toStructuredText()} ' |
| 200 'vs ${value2.toStructuredText()}'); |
| 201 }, onUnfoundElement: (ConstantValue value1) { |
| 202 OutputUnit outputUnit1 = constants1[value1]; |
| 203 Expect.isTrue(outputUnit1.isMainOutput, |
| 204 "Missing deferred constant: ${value1.toStructuredText()}"); |
| 205 }, onExtraElement: (ConstantValue value2) { |
| 206 OutputUnit outputUnit2 = constants2[value2]; |
| 207 Expect.isTrue(outputUnit2.isMainOutput, |
| 208 "Extra deferred constant: ${value2.toStructuredText()}"); |
| 209 }, elementToString: (a) { |
| 210 return '${a.toStructuredText()} -> ${constants1[a]}/${constants2[a]}'; |
| 211 }); |
| 212 } |
| 213 |
187 void checkElements( | 214 void checkElements( |
188 Compiler compiler1, Compiler compiler2, Element element1, Element element2, | 215 Compiler compiler1, Compiler compiler2, Element element1, Element element2, |
189 {bool verbose: false}) { | 216 {bool verbose: false}) { |
190 if (element1.isAbstract) return; | 217 if (element1.isAbstract) return; |
191 if (element1.isFunction || | 218 if (element1.isFunction || |
192 element1.isConstructor || | 219 element1.isConstructor || |
193 (element1.isField && element1.isInstanceMember)) { | 220 (element1.isField && element1.isInstanceMember)) { |
194 AstElement astElement1 = element1; | 221 AstElement astElement1 = element1; |
195 AstElement astElement2 = element2; | 222 AstElement astElement2 = element2; |
196 ClosureClassMap closureData1 = compiler1.closureToClassMapper | 223 ClosureClassMap closureData1 = compiler1.closureToClassMapper |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 JavaScriptBackend backend1 = compiler1.backend; | 280 JavaScriptBackend backend1 = compiler1.backend; |
254 JavaScriptBackend backend2 = compiler2.backend; | 281 JavaScriptBackend backend2 = compiler2.backend; |
255 Expect.equals( | 282 Expect.equals( |
256 backend1.inlineCache.getCurrentCacheDecisionForTesting(element1), | 283 backend1.inlineCache.getCurrentCacheDecisionForTesting(element1), |
257 backend2.inlineCache.getCurrentCacheDecisionForTesting(element2), | 284 backend2.inlineCache.getCurrentCacheDecisionForTesting(element2), |
258 "Inline cache decision mismatch for $element1 vs $element2"); | 285 "Inline cache decision mismatch for $element1 vs $element2"); |
259 | 286 |
260 checkElementOutputUnits(compiler1, compiler2, element1, element2); | 287 checkElementOutputUnits(compiler1, compiler2, element1, element2); |
261 } | 288 } |
262 | 289 |
263 void checkMixinUses(Compiler compiler1, Compiler compiler2, ClassElement class1, | 290 void checkMixinUses(ClosedWorld closedWorld1, ClosedWorld closedWorld2, |
264 ClassElement class2, | 291 ClassElement class1, ClassElement class2, |
265 {bool verbose: false}) { | 292 {bool verbose: false}) { |
266 checkSets( | 293 checkSets(closedWorld1.mixinUsesOf(class1), closedWorld2.mixinUsesOf(class2), |
267 compiler1.closedWorld.mixinUsesOf(class1), | 294 "Mixin uses of $class1 vs $class2", areElementsEquivalent, |
268 compiler2.closedWorld.mixinUsesOf(class2), | |
269 "Mixin uses of $class1 vs $class2", | |
270 areElementsEquivalent, | |
271 verbose: verbose); | 295 verbose: verbose); |
272 } | 296 } |
273 | 297 |
274 void checkClassHierarchyNodes(Compiler compiler1, Compiler compiler2, | 298 void checkClassHierarchyNodes( |
275 ClassHierarchyNode node1, ClassHierarchyNode node2, | 299 ClosedWorld closedWorld1, |
| 300 ClosedWorld closedWorld2, |
| 301 ClassHierarchyNode node1, |
| 302 ClassHierarchyNode node2, |
276 {bool verbose: false}) { | 303 {bool verbose: false}) { |
277 if (verbose) { | 304 if (verbose) { |
278 print('Checking $node1 vs $node2'); | 305 print('Checking $node1 vs $node2'); |
279 } | 306 } |
280 Expect.isTrue(areElementsEquivalent(node1.cls, node2.cls), | 307 Expect.isTrue(areElementsEquivalent(node1.cls, node2.cls), |
281 "Element identity mismatch for ${node1.cls} vs ${node2.cls}."); | 308 "Element identity mismatch for ${node1.cls} vs ${node2.cls}."); |
282 Expect.equals( | 309 Expect.equals( |
283 node1.isDirectlyInstantiated, | 310 node1.isDirectlyInstantiated, |
284 node2.isDirectlyInstantiated, | 311 node2.isDirectlyInstantiated, |
285 "Value mismatch for 'isDirectlyInstantiated' " | 312 "Value mismatch for 'isDirectlyInstantiated' " |
286 "for ${node1.cls} vs ${node2.cls}."); | 313 "for ${node1.cls} vs ${node2.cls}."); |
287 Expect.equals( | 314 Expect.equals( |
288 node1.isIndirectlyInstantiated, | 315 node1.isIndirectlyInstantiated, |
289 node2.isIndirectlyInstantiated, | 316 node2.isIndirectlyInstantiated, |
290 "Value mismatch for 'isIndirectlyInstantiated' " | 317 "Value mismatch for 'isIndirectlyInstantiated' " |
291 "for ${node1.cls} vs ${node2.cls}."); | 318 "for ${node1.cls} vs ${node2.cls}."); |
292 // TODO(johnniwinther): Enforce a canonical and stable order on direct | 319 // TODO(johnniwinther): Enforce a canonical and stable order on direct |
293 // subclasses. | 320 // subclasses. |
294 for (ClassHierarchyNode child in node1.directSubclasses) { | 321 for (ClassHierarchyNode child in node1.directSubclasses) { |
295 bool found = false; | 322 bool found = false; |
296 for (ClassHierarchyNode other in node2.directSubclasses) { | 323 for (ClassHierarchyNode other in node2.directSubclasses) { |
297 if (areElementsEquivalent(child.cls, other.cls)) { | 324 if (areElementsEquivalent(child.cls, other.cls)) { |
298 checkClassHierarchyNodes(compiler1, compiler2, child, other, | 325 checkClassHierarchyNodes(closedWorld1, closedWorld2, child, other, |
299 verbose: verbose); | 326 verbose: verbose); |
300 found = true; | 327 found = true; |
301 break; | 328 break; |
302 } | 329 } |
303 } | 330 } |
304 if (!found) { | 331 if (!found) { |
305 if (child.isInstantiated) { | 332 if (child.isInstantiated) { |
306 print('Missing subclass ${child.cls} of ${node1.cls} ' | 333 print('Missing subclass ${child.cls} of ${node1.cls} ' |
307 'in ${node2.directSubclasses}'); | 334 'in ${node2.directSubclasses}'); |
308 print(compiler1.closedWorld | 335 print(closedWorld1 |
309 .dump(verbose ? compiler1.coreClasses.objectClass : node1.cls)); | 336 .dump(verbose ? closedWorld1.coreClasses.objectClass : node1.cls)); |
310 print(compiler2.closedWorld | 337 print(closedWorld2 |
311 .dump(verbose ? compiler2.coreClasses.objectClass : node2.cls)); | 338 .dump(verbose ? closedWorld2.coreClasses.objectClass : node2.cls)); |
312 } | 339 } |
313 Expect.isFalse( | 340 Expect.isFalse( |
314 child.isInstantiated, | 341 child.isInstantiated, |
315 'Missing subclass ${child.cls} of ${node1.cls} in ' | 342 'Missing subclass ${child.cls} of ${node1.cls} in ' |
316 '${node2.directSubclasses}'); | 343 '${node2.directSubclasses}'); |
317 } | 344 } |
318 } | 345 } |
319 checkMixinUses(compiler1, compiler2, node1.cls, node2.cls, verbose: verbose); | 346 checkMixinUses(closedWorld1, closedWorld2, node1.cls, node2.cls, |
| 347 verbose: verbose); |
320 } | 348 } |
321 | 349 |
322 bool areLocalsEquivalent(Local a, Local b) { | 350 bool areLocalsEquivalent(Local a, Local b) { |
323 if (a == b) return true; | 351 if (a == b) return true; |
324 if (a == null || b == null) return false; | 352 if (a == null || b == null) return false; |
325 | 353 |
326 if (a is Element) { | 354 if (a is Element) { |
327 return b is Element && areElementsEquivalent(a as Element, b as Element); | 355 return b is Element && areElementsEquivalent(a as Element, b as Element); |
328 } else { | 356 } else { |
329 return a.runtimeType == b.runtimeType && | 357 return a.runtimeType == b.runtimeType && |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 check(outputUnit1, outputUnit2, 'OutputUnit.isMainOutput $message', | 412 check(outputUnit1, outputUnit2, 'OutputUnit.isMainOutput $message', |
385 outputUnit1.isMainOutput, outputUnit2.isMainOutput); | 413 outputUnit1.isMainOutput, outputUnit2.isMainOutput); |
386 checkSetEquivalence( | 414 checkSetEquivalence( |
387 outputUnit1, | 415 outputUnit1, |
388 outputUnit2, | 416 outputUnit2, |
389 'OutputUnit.imports $message', | 417 'OutputUnit.imports $message', |
390 outputUnit1.imports, | 418 outputUnit1.imports, |
391 outputUnit2.imports, | 419 outputUnit2.imports, |
392 (a, b) => areElementsEquivalent(a.declaration, b.declaration)); | 420 (a, b) => areElementsEquivalent(a.declaration, b.declaration)); |
393 } | 421 } |
OLD | NEW |