OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 part of dart2js; | |
6 | |
7 /** | |
8 * If true, print a warning for each method that was resolved, but not | |
9 * compiled. | |
10 */ | |
11 const bool REPORT_EXCESS_RESOLUTION = false; | |
12 | |
13 /** | |
14 * Contains backend-specific data that is used throughout the compilation of | |
15 * one work item. | |
16 */ | |
17 class ItemCompilationContext { | |
18 } | |
19 | |
20 abstract class WorkItem { | |
21 final ItemCompilationContext compilationContext; | |
22 /** | |
23 * Documentation wanted -- johnniwinther | |
24 * | |
25 * Invariant: [element] must be a declaration element. | |
26 */ | |
27 final AstElement element; | |
28 TreeElements get resolutionTree; | |
29 | |
30 WorkItem(this.element, this.compilationContext) { | |
31 assert(invariant(element, element.isDeclaration)); | |
32 } | |
33 | |
34 void run(Compiler compiler, Enqueuer world); | |
35 } | |
36 | |
37 /// [WorkItem] used exclusively by the [ResolutionEnqueuer]. | |
38 class ResolutionWorkItem extends WorkItem { | |
39 TreeElements resolutionTree; | |
40 | |
41 ResolutionWorkItem(AstElement element, | |
42 ItemCompilationContext compilationContext) | |
43 : super(element, compilationContext); | |
44 | |
45 void run(Compiler compiler, ResolutionEnqueuer world) { | |
46 compiler.analyze(this, world); | |
47 resolutionTree = element.resolvedAst.elements; | |
48 } | |
49 | |
50 bool isAnalyzed() => resolutionTree != null; | |
51 } | |
52 | |
53 // TODO(johnniwinther): Split this class into interface and implementation. | |
54 // TODO(johnniwinther): Move this implementation to the JS backend. | |
55 class CodegenRegistry extends Registry { | |
56 final Compiler compiler; | |
57 final TreeElements treeElements; | |
58 | |
59 CodegenRegistry(this.compiler, this.treeElements); | |
60 | |
61 bool get isForResolution => false; | |
62 | |
63 Element get currentElement => treeElements.analyzedElement; | |
64 | |
65 // TODO(johnniwinther): Remove this getter when [Registry] creates a | |
66 // dependency node. | |
67 Setlet<Element> get otherDependencies => treeElements.otherDependencies; | |
68 | |
69 CodegenEnqueuer get world => compiler.enqueuer.codegen; | |
70 js_backend.JavaScriptBackend get backend => compiler.backend; | |
71 | |
72 void registerDependency(Element element) { | |
73 treeElements.registerDependency(element); | |
74 } | |
75 | |
76 void registerInlining(Element inlinedElement, Element context) { | |
77 if (compiler.dumpInfo) { | |
78 compiler.dumpInfoTask.registerInlined(inlinedElement, context); | |
79 } | |
80 } | |
81 | |
82 void registerInstantiatedClass(ClassElement element) { | |
83 world.registerInstantiatedClass(element, this); | |
84 } | |
85 | |
86 void registerInstantiatedType(InterfaceType type) { | |
87 world.registerInstantiatedType(type, this); | |
88 } | |
89 | |
90 void registerStaticUse(Element element) { | |
91 world.registerStaticUse(element); | |
92 } | |
93 | |
94 void registerDynamicInvocation(Selector selector) { | |
95 world.registerDynamicInvocation(selector); | |
96 compiler.dumpInfoTask.elementUsesSelector(currentElement, selector); | |
97 } | |
98 | |
99 void registerDynamicSetter(Selector selector) { | |
100 world.registerDynamicSetter(selector); | |
101 compiler.dumpInfoTask.elementUsesSelector(currentElement, selector); | |
102 } | |
103 | |
104 void registerDynamicGetter(Selector selector) { | |
105 world.registerDynamicGetter(selector); | |
106 compiler.dumpInfoTask.elementUsesSelector(currentElement, selector); | |
107 } | |
108 | |
109 void registerGetterForSuperMethod(Element element) { | |
110 world.registerGetterForSuperMethod(element); | |
111 } | |
112 | |
113 void registerFieldGetter(Element element) { | |
114 world.registerFieldGetter(element); | |
115 } | |
116 | |
117 void registerFieldSetter(Element element) { | |
118 world.registerFieldSetter(element); | |
119 } | |
120 | |
121 void registerIsCheck(DartType type) { | |
122 world.registerIsCheck(type, this); | |
123 backend.registerIsCheckForCodegen(type, world, this); | |
124 } | |
125 | |
126 void registerCompileTimeConstant(ConstantValue constant) { | |
127 backend.registerCompileTimeConstant(constant, this); | |
128 backend.constants.addCompileTimeConstantForEmission(constant); | |
129 } | |
130 | |
131 void registerTypeVariableBoundsSubtypeCheck(DartType subtype, | |
132 DartType supertype) { | |
133 backend.registerTypeVariableBoundsSubtypeCheck(subtype, supertype); | |
134 } | |
135 | |
136 void registerClosureWithFreeTypeVariables(FunctionElement element) { | |
137 backend.registerClosureWithFreeTypeVariables(element, world, this); | |
138 } | |
139 | |
140 void registerGetOfStaticFunction(FunctionElement element) { | |
141 world.registerGetOfStaticFunction(element); | |
142 } | |
143 | |
144 void registerSelectorUse(Selector selector) { | |
145 world.registerSelectorUse(selector); | |
146 } | |
147 | |
148 void registerFactoryWithTypeArguments() { | |
149 world.registerFactoryWithTypeArguments(this); | |
150 } | |
151 | |
152 void registerConstSymbol(String name) { | |
153 backend.registerConstSymbol(name, this); | |
154 } | |
155 | |
156 void registerSpecializedGetInterceptor(Set<ClassElement> classes) { | |
157 backend.registerSpecializedGetInterceptor(classes); | |
158 } | |
159 | |
160 void registerUseInterceptor() { | |
161 backend.registerUseInterceptor(world); | |
162 } | |
163 | |
164 void registerTypeConstant(ClassElement element) { | |
165 backend.customElementsAnalysis.registerTypeConstant(element, world); | |
166 } | |
167 | |
168 void registerStaticInvocation(Element element) { | |
169 world.registerStaticUse(element); | |
170 } | |
171 | |
172 void registerInstantiation(InterfaceType type) { | |
173 world.registerInstantiatedType(type, this); | |
174 } | |
175 } | |
176 | |
177 /// [WorkItem] used exclusively by the [CodegenEnqueuer]. | |
178 class CodegenWorkItem extends WorkItem { | |
179 Registry registry; | |
180 final TreeElements resolutionTree; | |
181 | |
182 CodegenWorkItem(AstElement element, | |
183 ItemCompilationContext compilationContext) | |
184 : this.resolutionTree = element.resolvedAst.elements, | |
185 super(element, compilationContext) { | |
186 assert(invariant(element, resolutionTree != null, | |
187 message: 'Resolution tree is null for $element in codegen work item')); | |
188 } | |
189 | |
190 void run(Compiler compiler, CodegenEnqueuer world) { | |
191 if (world.isProcessed(element)) return; | |
192 | |
193 registry = new CodegenRegistry(compiler, resolutionTree); | |
194 compiler.codegen(this, world); | |
195 } | |
196 } | |
197 | |
198 typedef void DeferredAction(); | |
199 | |
200 class DeferredTask { | |
201 final Element element; | |
202 final DeferredAction action; | |
203 | |
204 DeferredTask(this.element, this.action); | |
205 } | |
206 | |
207 /// Interface for registration of element dependencies. | |
208 abstract class Registry { | |
209 // TODO(johnniwinther): Remove this getter when [Registry] creates a | |
210 // dependency node. | |
211 Iterable<Element> get otherDependencies; | |
212 | |
213 void registerDependency(Element element); | |
214 | |
215 bool get isForResolution; | |
216 | |
217 void registerStaticInvocation(Element element); | |
218 | |
219 void registerInstantiation(InterfaceType type); | |
220 | |
221 void registerGetOfStaticFunction(FunctionElement element); | |
222 } | |
223 | |
224 abstract class Backend { | |
225 final Compiler compiler; | |
226 | |
227 Backend(this.compiler); | |
228 | |
229 /// The [ConstantSystem] used to interpret compile-time constants for this | |
230 /// backend. | |
231 ConstantSystem get constantSystem; | |
232 | |
233 /// The constant environment for the backend interpretation of compile-time | |
234 /// constants. | |
235 BackendConstantEnvironment get constants; | |
236 | |
237 /// The compiler task responsible for the compilation of constants for both | |
238 /// the frontend and the backend. | |
239 ConstantCompilerTask get constantCompilerTask; | |
240 | |
241 /// Backend callback methods for the resolution phase. | |
242 ResolutionCallbacks get resolutionCallbacks; | |
243 | |
244 /// Set of classes that need to be considered for reflection although not | |
245 /// otherwise visible during resolution. | |
246 Iterable<ClassElement> classesRequiredForReflection = const []; | |
247 | |
248 // Given a [FunctionElement], return a buffer with the code generated for it | |
249 // or null if no code was generated. | |
250 CodeBuffer codeOf(Element element) => null; | |
251 | |
252 void initializeHelperClasses() {} | |
253 | |
254 void enqueueHelpers(ResolutionEnqueuer world, Registry registry); | |
255 void codegen(CodegenWorkItem work); | |
256 | |
257 // The backend determines the native resolution enqueuer, with a no-op | |
258 // default, so tools like dart2dart can ignore the native classes. | |
259 native.NativeEnqueuer nativeResolutionEnqueuer(world) { | |
260 return new native.NativeEnqueuer(); | |
261 } | |
262 native.NativeEnqueuer nativeCodegenEnqueuer(world) { | |
263 return new native.NativeEnqueuer(); | |
264 } | |
265 | |
266 void assembleProgram(); | |
267 List<CompilerTask> get tasks; | |
268 | |
269 void onResolutionComplete() {} | |
270 | |
271 ItemCompilationContext createItemCompilationContext() { | |
272 return new ItemCompilationContext(); | |
273 } | |
274 | |
275 bool classNeedsRti(ClassElement cls); | |
276 bool methodNeedsRti(FunctionElement function); | |
277 | |
278 /// Called during codegen when [constant] has been used. | |
279 void registerCompileTimeConstant(ConstantValue constant, Registry registry) {} | |
280 | |
281 /// Called during resolution when a constant value for [metadata] on | |
282 /// [annotatedElement] has been evaluated. | |
283 void registerMetadataConstant(MetadataAnnotation metadata, | |
284 Element annotatedElement, | |
285 Registry registry) {} | |
286 | |
287 /// Called during resolution to notify to the backend that a class is | |
288 /// being instantiated. | |
289 void registerInstantiatedClass(ClassElement cls, | |
290 Enqueuer enqueuer, | |
291 Registry registry) {} | |
292 | |
293 /// Register an is check to the backend. | |
294 void registerIsCheckForCodegen(DartType type, | |
295 Enqueuer enqueuer, | |
296 Registry registry) {} | |
297 | |
298 /// Register a runtime type variable bound tests between [typeArgument] and | |
299 /// [bound]. | |
300 void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, | |
301 DartType bound) {} | |
302 | |
303 /// Returns `true` if [element] represent the assert function. | |
304 bool isAssertMethod(Element element) => false; | |
305 | |
306 /** | |
307 * Call this to register that an instantiated generic class has a call | |
308 * method. | |
309 */ | |
310 void registerCallMethodWithFreeTypeVariables( | |
311 Element callMethod, | |
312 Enqueuer enqueuer, | |
313 Registry registry) {} | |
314 | |
315 /** | |
316 * Call this to register that a getter exists for a function on an | |
317 * instantiated generic class. | |
318 */ | |
319 void registerClosureWithFreeTypeVariables( | |
320 Element closure, | |
321 Enqueuer enqueuer, | |
322 Registry registry) {} | |
323 | |
324 /// Call this to register that a member has been closurized. | |
325 void registerBoundClosure(Enqueuer enqueuer) {} | |
326 | |
327 /// Call this to register that a static function has been closurized. | |
328 void registerGetOfStaticFunction(Enqueuer enqueuer) {} | |
329 | |
330 /** | |
331 * Call this to register that the [:runtimeType:] property has been accessed. | |
332 */ | |
333 void registerRuntimeType(Enqueuer enqueuer, Registry registry) {} | |
334 | |
335 /** | |
336 * Call this method to enable [noSuchMethod] handling in the | |
337 * backend. | |
338 */ | |
339 void enableNoSuchMethod(Element context, Enqueuer enqueuer) { | |
340 enqueuer.registerInvocation(compiler.noSuchMethodSelector); | |
341 } | |
342 | |
343 /// Call this method to enable support for isolates. | |
344 void enableIsolateSupport(Enqueuer enqueuer) {} | |
345 | |
346 void registerRequiredType(DartType type, Element enclosingElement) {} | |
347 void registerClassUsingVariableExpression(ClassElement cls) {} | |
348 | |
349 void registerConstSymbol(String name, Registry registry) {} | |
350 void registerNewSymbol(Registry registry) {} | |
351 | |
352 bool isNullImplementation(ClassElement cls) { | |
353 return cls == compiler.nullClass; | |
354 } | |
355 | |
356 ClassElement get intImplementation => compiler.intClass; | |
357 ClassElement get doubleImplementation => compiler.doubleClass; | |
358 ClassElement get numImplementation => compiler.numClass; | |
359 ClassElement get stringImplementation => compiler.stringClass; | |
360 ClassElement get listImplementation => compiler.listClass; | |
361 ClassElement get growableListImplementation => compiler.listClass; | |
362 ClassElement get fixedListImplementation => compiler.listClass; | |
363 ClassElement get constListImplementation => compiler.listClass; | |
364 ClassElement get mapImplementation => compiler.mapClass; | |
365 ClassElement get constMapImplementation => compiler.mapClass; | |
366 ClassElement get functionImplementation => compiler.functionClass; | |
367 ClassElement get typeImplementation => compiler.typeClass; | |
368 ClassElement get boolImplementation => compiler.boolClass; | |
369 ClassElement get nullImplementation => compiler.nullClass; | |
370 ClassElement get uint32Implementation => compiler.intClass; | |
371 ClassElement get uint31Implementation => compiler.intClass; | |
372 ClassElement get positiveIntImplementation => compiler.intClass; | |
373 | |
374 ClassElement defaultSuperclass(ClassElement element) => compiler.objectClass; | |
375 | |
376 bool isDefaultNoSuchMethodImplementation(Element element) { | |
377 assert(element.name == Compiler.NO_SUCH_METHOD); | |
378 ClassElement classElement = element.enclosingClass; | |
379 return classElement == compiler.objectClass; | |
380 } | |
381 | |
382 bool isInterceptorClass(ClassElement element) => false; | |
383 | |
384 /// Returns `true` if [element] is a foreign element, that is, that the | |
385 /// backend has specialized handling for the element. | |
386 bool isForeign(Element element) => false; | |
387 | |
388 /// Returns `true` if [library] is a backend specific library whose members | |
389 /// have special treatment, such as being allowed to extends blacklisted | |
390 /// classes or member being eagerly resolved. | |
391 bool isBackendLibrary(LibraryElement library) { | |
392 // TODO(johnnwinther): Remove this when patching is only done by the | |
393 // JavaScript backend. | |
394 Uri canonicalUri = library.canonicalUri; | |
395 if (canonicalUri == js_backend.JavaScriptBackend.DART_JS_HELPER || | |
396 canonicalUri == js_backend.JavaScriptBackend.DART_INTERCEPTORS) { | |
397 return true; | |
398 } | |
399 return false; | |
400 } | |
401 | |
402 void registerStaticUse(Element element, Enqueuer enqueuer) {} | |
403 | |
404 /// This method is called immediately after the [LibraryElement] [library] has | |
405 /// been created. | |
406 void onLibraryCreated(LibraryElement library) {} | |
407 | |
408 /// This method is called immediately after the [library] and its parts have | |
409 /// been scanned. | |
410 Future onLibraryScanned(LibraryElement library, LibraryLoader loader) { | |
411 if (library.isPlatformLibrary && !library.isPatched) { | |
412 // Apply patch, if any. | |
413 Uri patchUri = compiler.resolvePatchUri(library.canonicalUri.path); | |
414 if (patchUri != null) { | |
415 return compiler.patchParser.patchLibrary(loader, patchUri, library); | |
416 } | |
417 } | |
418 if (library.canUseNative) { | |
419 library.forEachLocalMember((Element element) { | |
420 if (element.isClass) { | |
421 checkNativeAnnotation(compiler, element); | |
422 } | |
423 }); | |
424 } | |
425 return new Future.value(); | |
426 } | |
427 | |
428 /// This method is called when all new libraries loaded through | |
429 /// [LibraryLoader.loadLibrary] has been loaded and their imports/exports | |
430 /// have been computed. | |
431 Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { | |
432 return new Future.value(); | |
433 } | |
434 | |
435 /// Called by [MirrorUsageAnalyzerTask] after it has merged all @MirrorsUsed | |
436 /// annotations. The arguments corresponds to the unions of the corresponding | |
437 /// fields of the annotations. | |
438 void registerMirrorUsage(Set<String> symbols, | |
439 Set<Element> targets, | |
440 Set<Element> metaTargets) {} | |
441 | |
442 /// Returns true if this element needs reflection information at runtime. | |
443 bool isAccessibleByReflection(Element element) => true; | |
444 | |
445 /// Returns true if this element is covered by a mirrorsUsed annotation. | |
446 /// | |
447 /// Note that it might still be ok to tree shake the element away if no | |
448 /// reflection is used in the program (and thus [isTreeShakingDisabled] is | |
449 /// still false). Therefore _do not_ use this predicate to decide inclusion | |
450 /// in the tree, use [requiredByMirrorSystem] instead. | |
451 bool referencedFromMirrorSystem(Element element, [recursive]) => false; | |
452 | |
453 /// Returns true if this element has to be enqueued due to | |
454 /// mirror usage. Might be a subset of [referencedFromMirrorSystem] if | |
455 /// normal tree shaking is still active ([isTreeShakingDisabled] is false). | |
456 bool requiredByMirrorSystem(Element element) => false; | |
457 | |
458 /// Returns true if global optimizations such as type inferencing | |
459 /// can apply to this element. One category of elements that do not | |
460 /// apply is runtime helpers that the backend calls, but the | |
461 /// optimizations don't see those calls. | |
462 bool canBeUsedForGlobalOptimizations(Element element) => true; | |
463 | |
464 /// Called when [enqueuer]'s queue is empty, but before it is closed. | |
465 /// This is used, for example, by the JS backend to enqueue additional | |
466 /// elements needed for reflection. [recentClasses] is a collection of | |
467 /// all classes seen for the first time by the [enqueuer] since the last call | |
468 /// to [onQueueEmpty]. | |
469 /// | |
470 /// A return value of [:true:] indicates that [recentClasses] has been | |
471 /// processed and its elements do not need to be seen in the next round. When | |
472 /// [:false:] is returned, [onQueueEmpty] will be called again once the | |
473 /// resolution queue has drained and [recentClasses] will be a superset of the | |
474 /// current value. | |
475 /// | |
476 /// There is no guarantee that a class is only present once in | |
477 /// [recentClasses], but every class seen by the [enqueuer] will be present in | |
478 /// [recentClasses] at least once. | |
479 bool onQueueEmpty(Enqueuer enqueuer, Iterable<ClassElement> recentClasses) { | |
480 return true; | |
481 } | |
482 | |
483 /// Called after [element] has been resolved. | |
484 // TODO(johnniwinther): Change [TreeElements] to [Registry] or a dependency | |
485 // node. [elements] is currently unused by the implementation. | |
486 void onElementResolved(Element element, TreeElements elements) {} | |
487 | |
488 // Does this element belong in the output | |
489 bool shouldOutput(Element element) => true; | |
490 | |
491 FunctionElement helperForBadMain() => null; | |
492 | |
493 FunctionElement helperForMissingMain() => null; | |
494 | |
495 FunctionElement helperForMainArity() => null; | |
496 | |
497 void forgetElement(Element element) {} | |
498 | |
499 void registerMainHasArguments(Enqueuer enqueuer) {} | |
500 } | |
501 | |
502 /// Backend callbacks function specific to the resolution phase. | |
503 class ResolutionCallbacks { | |
504 /// Register that [node] is a call to `assert`. | |
505 void onAssert(Send node, Registry registry) {} | |
506 | |
507 /// Called during resolution to notify to the backend that the | |
508 /// program uses string interpolation. | |
509 void onStringInterpolation(Registry registry) {} | |
510 | |
511 /// Called during resolution to notify to the backend that the | |
512 /// program has a catch statement. | |
513 void onCatchStatement(Registry registry) {} | |
514 | |
515 /// Called during resolution to notify to the backend that the | |
516 /// program explicitly throws an exception. | |
517 void onThrowExpression(Registry registry) {} | |
518 | |
519 /// Called during resolution to notify to the backend that the | |
520 /// program has a global variable with a lazy initializer. | |
521 void onLazyField(Registry registry) {} | |
522 | |
523 /// Called during resolution to notify to the backend that the | |
524 /// program uses a type variable as an expression. | |
525 void onTypeVariableExpression(Registry registry) {} | |
526 | |
527 /// Called during resolution to notify to the backend that the | |
528 /// program uses a type literal. | |
529 void onTypeLiteral(DartType type, Registry registry) {} | |
530 | |
531 /// Called during resolution to notify to the backend that the | |
532 /// program has a catch statement with a stack trace. | |
533 void onStackTraceInCatch(Registry registry) {} | |
534 | |
535 /// Register an is check to the backend. | |
536 void onIsCheck(DartType type, Registry registry) {} | |
537 | |
538 /// Register an as check to the backend. | |
539 void onAsCheck(DartType type, Registry registry) {} | |
540 | |
541 /// Registers that a type variable bounds check might occur at runtime. | |
542 void onTypeVariableBoundCheck(Registry registry) {} | |
543 | |
544 /// Register that the application may throw a [NoSuchMethodError]. | |
545 void onThrowNoSuchMethod(Registry registry) {} | |
546 | |
547 /// Register that the application may throw a [RuntimeError]. | |
548 void onThrowRuntimeError(Registry registry) {} | |
549 | |
550 /// Register that the application may throw an | |
551 /// [AbstractClassInstantiationError]. | |
552 void onAbstractClassInstantiation(Registry registry) {} | |
553 | |
554 /// Register that the application may throw a [FallThroughError]. | |
555 void onFallThroughError(Registry registry) {} | |
556 | |
557 /// Register that a super call will end up calling | |
558 /// [: super.noSuchMethod :]. | |
559 void onSuperNoSuchMethod(Registry registry) {} | |
560 | |
561 /// Register that the application creates a constant map. | |
562 void onConstantMap(Registry registry) {} | |
563 | |
564 /// Called when resolving the `Symbol` constructor. | |
565 void onSymbolConstructor(Registry registry) {} | |
566 } | |
567 | |
568 /** | |
569 * Key class used in [TokenMap] in which the hash code for a token is based | |
570 * on the [charOffset]. | |
571 */ | |
572 class TokenKey { | |
573 final Token token; | |
574 TokenKey(this.token); | |
575 int get hashCode => token.charOffset; | |
576 operator==(other) => other is TokenKey && token == other.token; | |
577 } | |
578 | |
579 /// Map of tokens and the first associated comment. | |
580 /* | |
581 * This implementation was chosen among several candidates for its space/time | |
582 * efficiency by empirical tests of running dartdoc on dartdoc itself. Time | |
583 * measurements for the use of [Compiler.commentMap]: | |
584 * | |
585 * 1) Using [TokenKey] as key (this class): ~80 msec | |
586 * 2) Using [TokenKey] as key + storing a separate map in each script: ~120 msec | |
587 * 3) Using [Token] as key in a [Map]: ~38000 msec | |
588 * 4) Storing comments is new field in [Token]: ~20 msec | |
589 * (Abandoned due to the increased memory usage) | |
590 * 5) Storing comments in an [Expando]: ~14000 msec | |
591 * 6) Storing token/comments pairs in a linked list: ~5400 msec | |
592 */ | |
593 class TokenMap { | |
594 Map<TokenKey,Token> comments = new Map<TokenKey,Token>(); | |
595 | |
596 Token operator[] (Token key) { | |
597 if (key == null) return null; | |
598 return comments[new TokenKey(key)]; | |
599 } | |
600 | |
601 void operator[]= (Token key, Token value) { | |
602 if (key == null) return; | |
603 comments[new TokenKey(key)] = value; | |
604 } | |
605 } | |
606 | |
607 abstract class Compiler implements DiagnosticListener { | |
608 static final Uri DART_CORE = new Uri(scheme: 'dart', path: 'core'); | |
609 static final Uri DART_MIRRORS = new Uri(scheme: 'dart', path: 'mirrors'); | |
610 static final Uri DART_NATIVE_TYPED_DATA = | |
611 new Uri(scheme: 'dart', path: '_native_typed_data'); | |
612 static final Uri DART_INTERNAL = new Uri(scheme: 'dart', path: '_internal'); | |
613 static final Uri DART_ASYNC = new Uri(scheme: 'dart', path: 'async'); | |
614 | |
615 final Stopwatch totalCompileTime = new Stopwatch(); | |
616 int nextFreeClassId = 0; | |
617 World world; | |
618 String assembledCode; | |
619 Types types; | |
620 | |
621 final CacheStrategy cacheStrategy; | |
622 | |
623 /** | |
624 * Map from token to the first preceeding comment token. | |
625 */ | |
626 final TokenMap commentMap = new TokenMap(); | |
627 | |
628 /** | |
629 * Records global dependencies, that is, dependencies that don't | |
630 * correspond to a particular element. | |
631 * | |
632 * We should get rid of this and ensure that all dependencies are | |
633 * associated with a particular element. | |
634 */ | |
635 Registry globalDependencies; | |
636 | |
637 /** | |
638 * Dependencies that are only included due to mirrors. | |
639 * | |
640 * We should get rid of this and ensure that all dependencies are | |
641 * associated with a particular element. | |
642 */ | |
643 // TODO(johnniwinther): This should not be a [ResolutionRegistry]. | |
644 final Registry mirrorDependencies = | |
645 new ResolutionRegistry.internal(null, new TreeElementMapping(null)); | |
646 | |
647 final bool enableMinification; | |
648 | |
649 /// When `true` emits URIs in the reflection metadata. | |
650 final bool preserveUris; | |
651 | |
652 final bool enableTypeAssertions; | |
653 final bool enableUserAssertions; | |
654 final bool trustTypeAnnotations; | |
655 final bool enableConcreteTypeInference; | |
656 final bool disableTypeInferenceFlag; | |
657 final bool dumpInfo; | |
658 final bool useContentSecurityPolicy; | |
659 | |
660 /** | |
661 * The maximum size of a concrete type before it widens to dynamic during | |
662 * concrete type inference. | |
663 */ | |
664 final int maxConcreteTypeSize; | |
665 final bool analyzeAllFlag; | |
666 final bool analyzeOnly; | |
667 | |
668 /// If true, disable tree-shaking for the main script. | |
669 final bool analyzeMain; | |
670 | |
671 /** | |
672 * If true, skip analysis of method bodies and field initializers. Implies | |
673 * [analyzeOnly]. | |
674 */ | |
675 final bool analyzeSignaturesOnly; | |
676 final bool enableNativeLiveTypeAnalysis; | |
677 | |
678 /** | |
679 * If true, stop compilation after type inference is complete. Used for | |
680 * debugging and testing purposes only. | |
681 */ | |
682 bool stopAfterTypeInference = false; | |
683 | |
684 /** | |
685 * If [:true:], comment tokens are collected in [commentMap] during scanning. | |
686 */ | |
687 final bool preserveComments; | |
688 | |
689 /** | |
690 * Is the compiler in verbose mode. | |
691 */ | |
692 final bool verbose; | |
693 | |
694 /** | |
695 * URI of the main source map if the compiler is generating source | |
696 * maps. | |
697 */ | |
698 final Uri sourceMapUri; | |
699 | |
700 /** | |
701 * URI of the main output if the compiler is generating source maps. | |
702 */ | |
703 final Uri outputUri; | |
704 | |
705 /// Emit terse diagnostics without howToFix. | |
706 final bool terseDiagnostics; | |
707 | |
708 /// If `true`, warnings and hints not from user code are reported. | |
709 final bool showPackageWarnings; | |
710 | |
711 /// `true` if the last diagnostic was filtered, in which case the | |
712 /// accompanying info message should be filtered as well. | |
713 bool lastDiagnosticWasFiltered = false; | |
714 | |
715 /// Map containing information about the warnings and hints that have been | |
716 /// suppressed for each library. | |
717 Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{}; | |
718 | |
719 final bool suppressWarnings; | |
720 | |
721 /// If `true`, some values are cached for reuse in incremental compilation. | |
722 /// Incremental compilation is basically calling [run] more than once. | |
723 final bool hasIncrementalSupport; | |
724 | |
725 api.CompilerOutputProvider outputProvider; | |
726 | |
727 bool disableInlining = false; | |
728 | |
729 /// True if compilation was aborted with a [CompilerCancelledException]. Only | |
730 /// set after Future retuned by [run] has completed. | |
731 bool compilerWasCancelled = false; | |
732 | |
733 List<Uri> librariesToAnalyzeWhenRun; | |
734 | |
735 Tracer tracer; | |
736 | |
737 CompilerTask measuredTask; | |
738 Element _currentElement; | |
739 LibraryElement coreLibrary; | |
740 | |
741 LibraryElement mainApp; | |
742 FunctionElement mainFunction; | |
743 | |
744 /// Initialized when dart:mirrors is loaded. | |
745 LibraryElement mirrorsLibrary; | |
746 | |
747 /// Initialized when dart:typed_data is loaded. | |
748 LibraryElement typedDataLibrary; | |
749 | |
750 ClassElement objectClass; | |
751 ClassElement boolClass; | |
752 ClassElement numClass; | |
753 ClassElement intClass; | |
754 ClassElement doubleClass; | |
755 ClassElement stringClass; | |
756 ClassElement functionClass; | |
757 ClassElement nullClass; | |
758 ClassElement listClass; | |
759 ClassElement typeClass; | |
760 ClassElement mapClass; | |
761 ClassElement symbolClass; | |
762 ClassElement stackTraceClass; | |
763 ClassElement typedDataClass; | |
764 | |
765 /// The constant for the [proxy] variable defined in dart:core. | |
766 ConstantValue proxyConstant; | |
767 | |
768 // TODO(johnniwinther): Move this to the JavaScriptBackend. | |
769 /// The constant for the [patch] variable defined in dart:_js_helper. | |
770 ConstantValue patchConstant; | |
771 | |
772 // TODO(johnniwinther): Move this to the JavaScriptBackend. | |
773 ClassElement nativeAnnotationClass; | |
774 | |
775 // Initialized after symbolClass has been resolved. | |
776 FunctionElement symbolConstructor; | |
777 | |
778 // Initialized when dart:mirrors is loaded. | |
779 ClassElement mirrorSystemClass; | |
780 | |
781 // Initialized when dart:mirrors is loaded. | |
782 ClassElement mirrorsUsedClass; | |
783 | |
784 // Initialized after mirrorSystemClass has been resolved. | |
785 FunctionElement mirrorSystemGetNameFunction; | |
786 | |
787 // Initialized when dart:_internal is loaded. | |
788 ClassElement symbolImplementationClass; | |
789 | |
790 // Initialized when symbolImplementationClass has been resolved. | |
791 FunctionElement symbolValidatedConstructor; | |
792 | |
793 // Initialized when mirrorsUsedClass has been resolved. | |
794 FunctionElement mirrorsUsedConstructor; | |
795 | |
796 // Initialized when dart:mirrors is loaded. | |
797 ClassElement deferredLibraryClass; | |
798 | |
799 /// Document class from dart:mirrors. | |
800 ClassElement documentClass; | |
801 Element identicalFunction; | |
802 Element loadLibraryFunction; | |
803 Element functionApplyMethod; | |
804 Element intEnvironment; | |
805 Element boolEnvironment; | |
806 Element stringEnvironment; | |
807 | |
808 fromEnvironment(String name) => null; | |
809 | |
810 Element get currentElement => _currentElement; | |
811 | |
812 String tryToString(object) { | |
813 try { | |
814 return object.toString(); | |
815 } catch (_) { | |
816 return '<exception in toString()>'; | |
817 } | |
818 } | |
819 | |
820 /** | |
821 * Perform an operation, [f], returning the return value from [f]. If an | |
822 * error occurs then report it as having occurred during compilation of | |
823 * [element]. Can be nested. | |
824 */ | |
825 withCurrentElement(Element element, f()) { | |
826 Element old = currentElement; | |
827 _currentElement = element; | |
828 try { | |
829 return f(); | |
830 } on SpannableAssertionFailure catch (ex) { | |
831 if (!hasCrashed) { | |
832 reportAssertionFailure(ex); | |
833 pleaseReportCrash(); | |
834 } | |
835 hasCrashed = true; | |
836 rethrow; | |
837 } on CompilerCancelledException catch (ex) { | |
838 rethrow; | |
839 } on StackOverflowError catch (ex) { | |
840 // We cannot report anything useful in this case, because we | |
841 // do not have enough stack space. | |
842 rethrow; | |
843 } catch (ex) { | |
844 if (hasCrashed) rethrow; | |
845 try { | |
846 unhandledExceptionOnElement(element); | |
847 } catch (doubleFault) { | |
848 // Ignoring exceptions in exception handling. | |
849 } | |
850 rethrow; | |
851 } finally { | |
852 _currentElement = old; | |
853 } | |
854 } | |
855 | |
856 List<CompilerTask> tasks; | |
857 ScannerTask scanner; | |
858 DietParserTask dietParser; | |
859 ParserTask parser; | |
860 PatchParserTask patchParser; | |
861 LibraryLoaderTask libraryLoader; | |
862 ResolverTask resolver; | |
863 closureMapping.ClosureTask closureToClassMapper; | |
864 TypeCheckerTask checker; | |
865 IrBuilderTask irBuilder; | |
866 ti.TypesTask typesTask; | |
867 Backend backend; | |
868 | |
869 GenericTask reuseLibraryTask; | |
870 | |
871 /// The constant environment for the frontend interpretation of compile-time | |
872 /// constants. | |
873 ConstantEnvironment constants; | |
874 | |
875 EnqueueTask enqueuer; | |
876 DeferredLoadTask deferredLoadTask; | |
877 MirrorUsageAnalyzerTask mirrorUsageAnalyzerTask; | |
878 DumpInfoTask dumpInfoTask; | |
879 String buildId; | |
880 | |
881 /// A customizable filter that is applied to enqueued work items. | |
882 QueueFilter enqueuerFilter = new QueueFilter(); | |
883 | |
884 static const String MAIN = 'main'; | |
885 static const String CALL_OPERATOR_NAME = 'call'; | |
886 static const String NO_SUCH_METHOD = 'noSuchMethod'; | |
887 static const int NO_SUCH_METHOD_ARG_COUNT = 1; | |
888 static const String CREATE_INVOCATION_MIRROR = | |
889 'createInvocationMirror'; | |
890 | |
891 static const String RUNTIME_TYPE = 'runtimeType'; | |
892 | |
893 static const String UNDETERMINED_BUILD_ID = | |
894 "build number could not be determined"; | |
895 | |
896 final Selector iteratorSelector = | |
897 new Selector.getter('iterator', null); | |
898 final Selector currentSelector = | |
899 new Selector.getter('current', null); | |
900 final Selector moveNextSelector = | |
901 new Selector.call('moveNext', null, 0); | |
902 final Selector noSuchMethodSelector = new Selector.call( | |
903 Compiler.NO_SUCH_METHOD, null, Compiler.NO_SUCH_METHOD_ARG_COUNT); | |
904 final Selector symbolValidatedConstructorSelector = new Selector.call( | |
905 'validated', null, 1); | |
906 final Selector fromEnvironmentSelector = new Selector.callConstructor( | |
907 'fromEnvironment', null, 2); | |
908 | |
909 bool enabledNoSuchMethod = false; | |
910 bool enabledRuntimeType = false; | |
911 bool enabledFunctionApply = false; | |
912 bool enabledInvokeOn = false; | |
913 bool hasIsolateSupport = false; | |
914 | |
915 Stopwatch progress; | |
916 | |
917 bool get shouldPrintProgress { | |
918 return verbose && progress.elapsedMilliseconds > 500; | |
919 } | |
920 | |
921 static const int PHASE_SCANNING = 0; | |
922 static const int PHASE_RESOLVING = 1; | |
923 static const int PHASE_DONE_RESOLVING = 2; | |
924 static const int PHASE_COMPILING = 3; | |
925 int phase; | |
926 | |
927 bool compilationFailed = false; | |
928 | |
929 bool hasCrashed = false; | |
930 | |
931 /// Set by the backend if real reflection is detected in use of dart:mirrors. | |
932 bool disableTypeInferenceForMirrors = false; | |
933 | |
934 Compiler({this.enableTypeAssertions: false, | |
935 this.enableUserAssertions: false, | |
936 this.trustTypeAnnotations: false, | |
937 this.enableConcreteTypeInference: false, | |
938 bool disableTypeInferenceFlag: false, | |
939 this.maxConcreteTypeSize: 5, | |
940 this.enableMinification: false, | |
941 this.preserveUris: false, | |
942 this.enableNativeLiveTypeAnalysis: false, | |
943 bool emitJavaScript: true, | |
944 bool dart2dartMultiFile: false, | |
945 bool generateSourceMap: true, | |
946 bool analyzeAllFlag: false, | |
947 bool analyzeOnly: false, | |
948 this.analyzeMain: false, | |
949 bool analyzeSignaturesOnly: false, | |
950 this.preserveComments: false, | |
951 this.verbose: false, | |
952 this.sourceMapUri: null, | |
953 this.outputUri: null, | |
954 this.buildId: UNDETERMINED_BUILD_ID, | |
955 this.terseDiagnostics: false, | |
956 this.dumpInfo: false, | |
957 this.showPackageWarnings: false, | |
958 this.useContentSecurityPolicy: false, | |
959 this.suppressWarnings: false, | |
960 bool hasIncrementalSupport: false, | |
961 api.CompilerOutputProvider outputProvider, | |
962 List<String> strips: const []}) | |
963 : this.disableTypeInferenceFlag = | |
964 disableTypeInferenceFlag || !emitJavaScript, | |
965 this.analyzeOnly = | |
966 analyzeOnly || analyzeSignaturesOnly || analyzeAllFlag, | |
967 this.analyzeSignaturesOnly = analyzeSignaturesOnly, | |
968 this.analyzeAllFlag = analyzeAllFlag, | |
969 this.hasIncrementalSupport = hasIncrementalSupport, | |
970 cacheStrategy = new CacheStrategy(hasIncrementalSupport), | |
971 this.outputProvider = (outputProvider == null) | |
972 ? NullSink.outputProvider | |
973 : outputProvider { | |
974 if (hasIncrementalSupport) { | |
975 // TODO(ahe): This is too much. Any method from platform and package | |
976 // libraries can be inlined. | |
977 disableInlining = true; | |
978 } | |
979 world = new World(this); | |
980 types = new Types(this); | |
981 tracer = new Tracer(this, this.outputProvider); | |
982 | |
983 if (verbose) { | |
984 progress = new Stopwatch()..start(); | |
985 } | |
986 | |
987 // TODO(johnniwinther): Separate the dependency tracking from the enqueueing | |
988 // for global dependencies. | |
989 globalDependencies = | |
990 new CodegenRegistry(this, new TreeElementMapping(null)); | |
991 | |
992 closureMapping.ClosureNamer closureNamer; | |
993 if (emitJavaScript) { | |
994 js_backend.JavaScriptBackend jsBackend = | |
995 new js_backend.JavaScriptBackend(this, generateSourceMap); | |
996 closureNamer = jsBackend.namer; | |
997 backend = jsBackend; | |
998 } else { | |
999 closureNamer = new closureMapping.ClosureNamer(); | |
1000 backend = new dart_backend.DartBackend(this, strips, | |
1001 multiFile: dart2dartMultiFile); | |
1002 } | |
1003 | |
1004 tasks = [ | |
1005 libraryLoader = new LibraryLoaderTask(this), | |
1006 scanner = new ScannerTask(this), | |
1007 dietParser = new DietParserTask(this), | |
1008 parser = new ParserTask(this), | |
1009 patchParser = new PatchParserTask(this), | |
1010 resolver = new ResolverTask(this, backend.constantCompilerTask), | |
1011 closureToClassMapper = new closureMapping.ClosureTask(this, closureNamer), | |
1012 checker = new TypeCheckerTask(this), | |
1013 irBuilder = new IrBuilderTask(this), | |
1014 typesTask = new ti.TypesTask(this), | |
1015 constants = backend.constantCompilerTask, | |
1016 deferredLoadTask = new DeferredLoadTask(this), | |
1017 mirrorUsageAnalyzerTask = new MirrorUsageAnalyzerTask(this), | |
1018 enqueuer = new EnqueueTask(this), | |
1019 dumpInfoTask = new DumpInfoTask(this), | |
1020 reuseLibraryTask = new GenericTask('Reuse library', this), | |
1021 ]; | |
1022 | |
1023 tasks.addAll(backend.tasks); | |
1024 } | |
1025 | |
1026 Universe get resolverWorld => enqueuer.resolution.universe; | |
1027 Universe get codegenWorld => enqueuer.codegen.universe; | |
1028 | |
1029 bool get hasBuildId => buildId != UNDETERMINED_BUILD_ID; | |
1030 | |
1031 bool get analyzeAll => analyzeAllFlag || compileAll; | |
1032 | |
1033 bool get compileAll => false; | |
1034 | |
1035 bool get disableTypeInference => disableTypeInferenceFlag; | |
1036 | |
1037 int getNextFreeClassId() => nextFreeClassId++; | |
1038 | |
1039 void unimplemented(Spannable spannable, String methodName) { | |
1040 internalError(spannable, "$methodName not implemented."); | |
1041 } | |
1042 | |
1043 void internalError(Spannable node, reason) { | |
1044 assembledCode = null; // Compilation failed. Make sure that we | |
1045 // don't return a bogus result. | |
1046 String message = tryToString(reason); | |
1047 reportDiagnosticInternal( | |
1048 node, MessageKind.GENERIC, {'text': message}, api.Diagnostic.CRASH); | |
1049 throw 'Internal Error: $message'; | |
1050 } | |
1051 | |
1052 void unhandledExceptionOnElement(Element element) { | |
1053 if (hasCrashed) return; | |
1054 hasCrashed = true; | |
1055 reportDiagnostic(element, | |
1056 MessageKind.COMPILER_CRASHED.message(), | |
1057 api.Diagnostic.CRASH); | |
1058 pleaseReportCrash(); | |
1059 } | |
1060 | |
1061 void pleaseReportCrash() { | |
1062 print(MessageKind.PLEASE_REPORT_THE_CRASH.message({'buildId': buildId})); | |
1063 } | |
1064 | |
1065 SourceSpan spanFromSpannable(Spannable node) { | |
1066 // TODO(johnniwinther): Disallow `node == null` ? | |
1067 if (node == null) return null; | |
1068 if (node == CURRENT_ELEMENT_SPANNABLE) { | |
1069 node = currentElement; | |
1070 } else if (node == NO_LOCATION_SPANNABLE) { | |
1071 if (currentElement == null) return null; | |
1072 node = currentElement; | |
1073 } | |
1074 if (node is SourceSpan) { | |
1075 return node; | |
1076 } else if (node is Node) { | |
1077 return spanFromNode(node); | |
1078 } else if (node is Token) { | |
1079 return spanFromTokens(node, node); | |
1080 } else if (node is HInstruction) { | |
1081 return spanFromHInstruction(node); | |
1082 } else if (node is Element) { | |
1083 return spanFromElement(node); | |
1084 } else if (node is MetadataAnnotation) { | |
1085 Uri uri = node.annotatedElement.compilationUnit.script.readableUri; | |
1086 return spanFromTokens(node.beginToken, node.endToken, uri); | |
1087 } else if (node is Local) { | |
1088 Local local = node; | |
1089 return spanFromElement(local.executableContext); | |
1090 } else { | |
1091 throw 'No error location.'; | |
1092 } | |
1093 } | |
1094 | |
1095 Element _elementFromHInstruction(HInstruction instruction) { | |
1096 return instruction.sourceElement is Element | |
1097 ? instruction.sourceElement : null; | |
1098 } | |
1099 | |
1100 /// Finds the approximate [Element] for [node]. [currentElement] is used as | |
1101 /// the default value. | |
1102 Element elementFromSpannable(Spannable node) { | |
1103 Element element; | |
1104 if (node is Element) { | |
1105 element = node; | |
1106 } else if (node is HInstruction) { | |
1107 element = _elementFromHInstruction(node); | |
1108 } else if (node is MetadataAnnotation) { | |
1109 element = node.annotatedElement; | |
1110 } | |
1111 return element != null ? element : currentElement; | |
1112 } | |
1113 | |
1114 void log(message) { | |
1115 reportDiagnostic(null, | |
1116 MessageKind.GENERIC.message({'text': '$message'}), | |
1117 api.Diagnostic.VERBOSE_INFO); | |
1118 } | |
1119 | |
1120 Future<bool> run(Uri uri) { | |
1121 totalCompileTime.start(); | |
1122 | |
1123 return new Future.sync(() => runCompiler(uri)).catchError((error) { | |
1124 if (error is CompilerCancelledException) { | |
1125 compilerWasCancelled = true; | |
1126 log('Error: $error'); | |
1127 return false; | |
1128 } | |
1129 | |
1130 try { | |
1131 if (!hasCrashed) { | |
1132 hasCrashed = true; | |
1133 if (error is SpannableAssertionFailure) { | |
1134 reportAssertionFailure(error); | |
1135 } else { | |
1136 reportDiagnostic(new SourceSpan(uri, 0, 0), | |
1137 MessageKind.COMPILER_CRASHED.message(), | |
1138 api.Diagnostic.CRASH); | |
1139 } | |
1140 pleaseReportCrash(); | |
1141 } | |
1142 } catch (doubleFault) { | |
1143 // Ignoring exceptions in exception handling. | |
1144 } | |
1145 throw error; | |
1146 }).whenComplete(() { | |
1147 tracer.close(); | |
1148 totalCompileTime.stop(); | |
1149 }).then((_) { | |
1150 return !compilationFailed; | |
1151 }); | |
1152 } | |
1153 | |
1154 /// This method is called immediately after the [LibraryElement] [library] has | |
1155 /// been created. | |
1156 /// | |
1157 /// Use this callback method to store references to specific libraries. | |
1158 /// Note that [library] has not been scanned yet, nor has its imports/exports | |
1159 /// been resolved. | |
1160 void onLibraryCreated(LibraryElement library) { | |
1161 Uri uri = library.canonicalUri; | |
1162 if (uri == DART_CORE) { | |
1163 coreLibrary = library; | |
1164 } else if (uri == DART_NATIVE_TYPED_DATA) { | |
1165 typedDataLibrary = library; | |
1166 } else if (uri == DART_MIRRORS) { | |
1167 mirrorsLibrary = library; | |
1168 } | |
1169 backend.onLibraryCreated(library); | |
1170 } | |
1171 | |
1172 /// This method is called immediately after the [library] and its parts have | |
1173 /// been scanned. | |
1174 /// | |
1175 /// Use this callback method to store references to specific member declared | |
1176 /// in certain libraries. Note that [library] has not been patched yet, nor | |
1177 /// has its imports/exports been resolved. | |
1178 /// | |
1179 /// Use [loader] to register the creation and scanning of a patch library | |
1180 /// for [library]. | |
1181 Future onLibraryScanned(LibraryElement library, LibraryLoader loader) { | |
1182 Uri uri = library.canonicalUri; | |
1183 if (uri == DART_CORE) { | |
1184 initializeCoreClasses(); | |
1185 identicalFunction = coreLibrary.find('identical'); | |
1186 } else if (uri == DART_INTERNAL) { | |
1187 symbolImplementationClass = findRequiredElement(library, 'Symbol'); | |
1188 } else if (uri == DART_MIRRORS) { | |
1189 mirrorSystemClass = findRequiredElement(library, 'MirrorSystem'); | |
1190 mirrorsUsedClass = findRequiredElement(library, 'MirrorsUsed'); | |
1191 } else if (uri == DART_ASYNC) { | |
1192 deferredLibraryClass = findRequiredElement(library, 'DeferredLibrary'); | |
1193 } else if (uri == DART_NATIVE_TYPED_DATA) { | |
1194 typedDataClass = findRequiredElement(library, 'NativeTypedData'); | |
1195 } else if (uri == js_backend.JavaScriptBackend.DART_JS_HELPER) { | |
1196 nativeAnnotationClass = findRequiredElement(library, 'Native'); | |
1197 } | |
1198 return backend.onLibraryScanned(library, loader); | |
1199 } | |
1200 | |
1201 /// This method is called when all new libraries loaded through | |
1202 /// [LibraryLoader.loadLibrary] has been loaded and their imports/exports | |
1203 /// have been computed. | |
1204 /// | |
1205 /// [loadedLibraries] contains the newly loaded libraries. | |
1206 /// | |
1207 /// The method returns a [Future] allowing for the loading of additional | |
1208 /// libraries. | |
1209 Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { | |
1210 return new Future.sync(() { | |
1211 if (!loadedLibraries.containsKey(DART_CORE)) return new Future.value(); | |
1212 | |
1213 functionClass.ensureResolved(this); | |
1214 functionApplyMethod = functionClass.lookupLocalMember('apply'); | |
1215 | |
1216 proxyConstant = | |
1217 resolver.constantCompiler.compileConstant( | |
1218 coreLibrary.find('proxy')).value; | |
1219 | |
1220 // TODO(johnniwinther): Move this to the JavaScript backend. | |
1221 LibraryElement jsHelperLibrary = | |
1222 loadedLibraries[js_backend.JavaScriptBackend.DART_JS_HELPER]; | |
1223 if (jsHelperLibrary != null) { | |
1224 patchConstant = resolver.constantCompiler.compileConstant( | |
1225 jsHelperLibrary.find('patch')).value; | |
1226 } | |
1227 | |
1228 if (preserveComments) { | |
1229 return libraryLoader.loadLibrary(DART_MIRRORS) | |
1230 .then((LibraryElement libraryElement) { | |
1231 documentClass = libraryElement.find('Comment'); | |
1232 }); | |
1233 } | |
1234 }).then((_) => backend.onLibrariesLoaded(loadedLibraries)); | |
1235 } | |
1236 | |
1237 Element findRequiredElement(LibraryElement library, String name) { | |
1238 var element = library.find(name); | |
1239 if (element == null) { | |
1240 internalError(library, | |
1241 "The library '${library.canonicalUri}' does not contain required " | |
1242 "element: '$name'."); | |
1243 } | |
1244 return element; | |
1245 } | |
1246 | |
1247 void onClassResolved(ClassElement cls) { | |
1248 if (mirrorSystemClass == cls) { | |
1249 mirrorSystemGetNameFunction = | |
1250 cls.lookupLocalMember('getName'); | |
1251 } else if (symbolClass == cls) { | |
1252 symbolConstructor = cls.constructors.head; | |
1253 } else if (symbolImplementationClass == cls) { | |
1254 symbolValidatedConstructor = symbolImplementationClass.lookupConstructor( | |
1255 symbolValidatedConstructorSelector); | |
1256 } else if (mirrorsUsedClass == cls) { | |
1257 mirrorsUsedConstructor = cls.constructors.head; | |
1258 } else if (intClass == cls) { | |
1259 intEnvironment = intClass.lookupConstructor(fromEnvironmentSelector); | |
1260 } else if (stringClass == cls) { | |
1261 stringEnvironment = | |
1262 stringClass.lookupConstructor(fromEnvironmentSelector); | |
1263 } else if (boolClass == cls) { | |
1264 boolEnvironment = boolClass.lookupConstructor(fromEnvironmentSelector); | |
1265 } | |
1266 } | |
1267 | |
1268 void initializeCoreClasses() { | |
1269 final List missingCoreClasses = []; | |
1270 ClassElement lookupCoreClass(String name) { | |
1271 ClassElement result = coreLibrary.find(name); | |
1272 if (result == null) { | |
1273 missingCoreClasses.add(name); | |
1274 } | |
1275 return result; | |
1276 } | |
1277 objectClass = lookupCoreClass('Object'); | |
1278 boolClass = lookupCoreClass('bool'); | |
1279 numClass = lookupCoreClass('num'); | |
1280 intClass = lookupCoreClass('int'); | |
1281 doubleClass = lookupCoreClass('double'); | |
1282 stringClass = lookupCoreClass('String'); | |
1283 functionClass = lookupCoreClass('Function'); | |
1284 listClass = lookupCoreClass('List'); | |
1285 typeClass = lookupCoreClass('Type'); | |
1286 mapClass = lookupCoreClass('Map'); | |
1287 nullClass = lookupCoreClass('Null'); | |
1288 stackTraceClass = lookupCoreClass('StackTrace'); | |
1289 symbolClass = lookupCoreClass('Symbol'); | |
1290 if (!missingCoreClasses.isEmpty) { | |
1291 internalError(coreLibrary, | |
1292 'dart:core library does not contain required classes: ' | |
1293 '$missingCoreClasses'); | |
1294 } | |
1295 } | |
1296 | |
1297 Element _unnamedListConstructor; | |
1298 Element get unnamedListConstructor { | |
1299 if (_unnamedListConstructor != null) return _unnamedListConstructor; | |
1300 Selector callConstructor = new Selector.callConstructor( | |
1301 "", listClass.library); | |
1302 return _unnamedListConstructor = | |
1303 listClass.lookupConstructor(callConstructor); | |
1304 } | |
1305 | |
1306 Element _filledListConstructor; | |
1307 Element get filledListConstructor { | |
1308 if (_filledListConstructor != null) return _filledListConstructor; | |
1309 Selector callConstructor = new Selector.callConstructor( | |
1310 "filled", listClass.library); | |
1311 return _filledListConstructor = | |
1312 listClass.lookupConstructor(callConstructor); | |
1313 } | |
1314 | |
1315 /** | |
1316 * Get an [Uri] pointing to a patch for the dart: library with | |
1317 * the given path. Returns null if there is no patch. | |
1318 */ | |
1319 Uri resolvePatchUri(String dartLibraryPath); | |
1320 | |
1321 Future runCompiler(Uri uri) { | |
1322 // TODO(ahe): This prevents memory leaks when invoking the compiler | |
1323 // multiple times. Implement a better mechanism where we can store | |
1324 // such caches in the compiler and get access to them through a | |
1325 // suitably maintained static reference to the current compiler. | |
1326 StringToken.canonicalizedSubstrings.clear(); | |
1327 Selector.canonicalizedValues.clear(); | |
1328 TypedSelector.canonicalizedValues.clear(); | |
1329 | |
1330 assert(uri != null || analyzeOnly || hasIncrementalSupport); | |
1331 return new Future.sync(() { | |
1332 if (librariesToAnalyzeWhenRun != null) { | |
1333 return Future.forEach(librariesToAnalyzeWhenRun, (libraryUri) { | |
1334 log('Analyzing $libraryUri ($buildId)'); | |
1335 return libraryLoader.loadLibrary(libraryUri); | |
1336 }); | |
1337 } | |
1338 }).then((_) { | |
1339 if (uri != null) { | |
1340 if (analyzeOnly) { | |
1341 log('Analyzing $uri ($buildId)'); | |
1342 } else { | |
1343 log('Compiling $uri ($buildId)'); | |
1344 } | |
1345 return libraryLoader.loadLibrary(uri).then((LibraryElement library) { | |
1346 mainApp = library; | |
1347 }); | |
1348 } | |
1349 }).then((_) { | |
1350 if (!compilationFailed) { | |
1351 // TODO(johnniwinther): Reenable analysis of programs with load failures | |
1352 // when these are handled as erroneous libraries/compilation units. | |
1353 compileLoadedLibraries(); | |
1354 } | |
1355 }); | |
1356 } | |
1357 | |
1358 void computeMain() { | |
1359 if (mainApp == null) return; | |
1360 | |
1361 Element main = mainApp.findExported(MAIN); | |
1362 ErroneousElement errorElement = null; | |
1363 if (main == null) { | |
1364 if (analyzeOnly) { | |
1365 if (!analyzeAll) { | |
1366 errorElement = new ErroneousElementX( | |
1367 MessageKind.CONSIDER_ANALYZE_ALL, {'main': MAIN}, MAIN, mainApp); | |
1368 } | |
1369 } else { | |
1370 // Compilation requires a main method. | |
1371 errorElement = new ErroneousElementX( | |
1372 MessageKind.MISSING_MAIN, {'main': MAIN}, MAIN, mainApp); | |
1373 } | |
1374 mainFunction = backend.helperForMissingMain(); | |
1375 } else if (main.isErroneous && main.isSynthesized) { | |
1376 if (main is ErroneousElement) { | |
1377 errorElement = main; | |
1378 } else { | |
1379 internalError(main, 'Problem with $MAIN.'); | |
1380 } | |
1381 mainFunction = backend.helperForBadMain(); | |
1382 } else if (!main.isFunction) { | |
1383 errorElement = new ErroneousElementX( | |
1384 MessageKind.MAIN_NOT_A_FUNCTION, {'main': MAIN}, MAIN, main); | |
1385 mainFunction = backend.helperForBadMain(); | |
1386 } else { | |
1387 mainFunction = main; | |
1388 FunctionSignature parameters = mainFunction.computeSignature(this); | |
1389 if (parameters.requiredParameterCount > 2) { | |
1390 int index = 0; | |
1391 parameters.orderedForEachParameter((Element parameter) { | |
1392 if (index++ < 2) return; | |
1393 errorElement = new ErroneousElementX( | |
1394 MessageKind.MAIN_WITH_EXTRA_PARAMETER, {'main': MAIN}, MAIN, | |
1395 parameter); | |
1396 mainFunction = backend.helperForMainArity(); | |
1397 // Don't warn about main not being used: | |
1398 enqueuer.resolution.registerStaticUse(main); | |
1399 }); | |
1400 } | |
1401 } | |
1402 if (mainFunction == null) { | |
1403 if (errorElement == null && !analyzeOnly && !analyzeAll) { | |
1404 internalError(mainApp, "Problem with '$MAIN'."); | |
1405 } else { | |
1406 mainFunction = errorElement; | |
1407 } | |
1408 } | |
1409 if (errorElement != null && errorElement.isSynthesized) { | |
1410 reportWarning( | |
1411 errorElement, errorElement.messageKind, | |
1412 errorElement.messageArguments); | |
1413 } | |
1414 } | |
1415 | |
1416 /// Performs the compilation when all libraries have been loaded. | |
1417 void compileLoadedLibraries() { | |
1418 computeMain(); | |
1419 | |
1420 mirrorUsageAnalyzerTask.analyzeUsage(mainApp); | |
1421 | |
1422 // In order to see if a library is deferred, we must compute the | |
1423 // compile-time constants that are metadata. This means adding | |
1424 // something to the resolution queue. So we cannot wait with | |
1425 // this until after the resolution queue is processed. | |
1426 deferredLoadTask.ensureMetadataResolved(this); | |
1427 | |
1428 phase = PHASE_RESOLVING; | |
1429 if (analyzeAll) { | |
1430 libraryLoader.libraries.forEach((LibraryElement library) { | |
1431 log('Enqueuing ${library.canonicalUri}'); | |
1432 fullyEnqueueLibrary(library, enqueuer.resolution); | |
1433 }); | |
1434 } else if (analyzeMain && mainApp != null) { | |
1435 fullyEnqueueLibrary(mainApp, enqueuer.resolution); | |
1436 } | |
1437 // Elements required by enqueueHelpers are global dependencies | |
1438 // that are not pulled in by a particular element. | |
1439 backend.enqueueHelpers(enqueuer.resolution, globalDependencies); | |
1440 resolveLibraryMetadata(); | |
1441 log('Resolving...'); | |
1442 processQueue(enqueuer.resolution, mainFunction); | |
1443 enqueuer.resolution.logSummary(log); | |
1444 | |
1445 if (compilationFailed) return; | |
1446 if (!showPackageWarnings && !suppressWarnings) { | |
1447 suppressedWarnings.forEach((Uri uri, SuppressionInfo info) { | |
1448 MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS; | |
1449 if (info.warnings == 0) { | |
1450 kind = MessageKind.HIDDEN_HINTS; | |
1451 } else if (info.hints == 0) { | |
1452 kind = MessageKind.HIDDEN_WARNINGS; | |
1453 } | |
1454 reportDiagnostic(null, | |
1455 kind.message({'warnings': info.warnings, | |
1456 'hints': info.hints, | |
1457 'uri': uri}, | |
1458 terseDiagnostics), | |
1459 api.Diagnostic.HINT); | |
1460 }); | |
1461 } | |
1462 if (analyzeOnly) { | |
1463 if (!analyzeAll) { | |
1464 // No point in reporting unused code when [analyzeAll] is true: all | |
1465 // code is artificially used. | |
1466 reportUnusedCode(); | |
1467 } | |
1468 return; | |
1469 } | |
1470 assert(mainFunction != null); | |
1471 phase = PHASE_DONE_RESOLVING; | |
1472 | |
1473 world.populate(); | |
1474 // Compute whole-program-knowledge that the backend needs. (This might | |
1475 // require the information computed in [world.populate].) | |
1476 backend.onResolutionComplete(); | |
1477 | |
1478 deferredLoadTask.onResolutionComplete(mainFunction); | |
1479 | |
1480 log('Building IR...'); | |
1481 irBuilder.buildNodes(); | |
1482 | |
1483 log('Inferring types...'); | |
1484 typesTask.onResolutionComplete(mainFunction); | |
1485 | |
1486 if (stopAfterTypeInference) return; | |
1487 | |
1488 log('Compiling...'); | |
1489 phase = PHASE_COMPILING; | |
1490 // TODO(johnniwinther): Move these to [CodegenEnqueuer]. | |
1491 if (hasIsolateSupport) { | |
1492 backend.enableIsolateSupport(enqueuer.codegen); | |
1493 } | |
1494 if (enabledNoSuchMethod) { | |
1495 backend.enableNoSuchMethod(null, enqueuer.codegen); | |
1496 } | |
1497 if (compileAll) { | |
1498 libraryLoader.libraries.forEach((LibraryElement library) { | |
1499 fullyEnqueueLibrary(library, enqueuer.codegen); | |
1500 }); | |
1501 } | |
1502 processQueue(enqueuer.codegen, mainFunction); | |
1503 enqueuer.codegen.logSummary(log); | |
1504 | |
1505 if (compilationFailed) return; | |
1506 | |
1507 backend.assembleProgram(); | |
1508 | |
1509 if (dumpInfo) { | |
1510 dumpInfoTask.dumpInfo(); | |
1511 } | |
1512 | |
1513 checkQueues(); | |
1514 | |
1515 if (compilationFailed) { | |
1516 assembledCode = null; // Signals failure. | |
1517 } | |
1518 } | |
1519 | |
1520 void fullyEnqueueLibrary(LibraryElement library, Enqueuer world) { | |
1521 void enqueueAll(Element element) { | |
1522 fullyEnqueueTopLevelElement(element, world); | |
1523 } | |
1524 library.implementation.forEachLocalMember(enqueueAll); | |
1525 } | |
1526 | |
1527 void fullyEnqueueTopLevelElement(Element element, Enqueuer world) { | |
1528 if (element.isClass) { | |
1529 ClassElement cls = element; | |
1530 cls.ensureResolved(this); | |
1531 cls.forEachLocalMember(enqueuer.resolution.addToWorkList); | |
1532 world.registerInstantiatedClass(element, globalDependencies); | |
1533 } else { | |
1534 world.addToWorkList(element); | |
1535 } | |
1536 } | |
1537 | |
1538 // Resolves metadata on library elements. This is necessary in order to | |
1539 // resolve metadata classes referenced only from metadata on library tags. | |
1540 // TODO(ahe): Figure out how to do this lazily. | |
1541 void resolveLibraryMetadata() { | |
1542 for (LibraryElement library in libraryLoader.libraries) { | |
1543 if (library.metadata != null) { | |
1544 for (MetadataAnnotation metadata in library.metadata) { | |
1545 metadata.ensureResolved(this); | |
1546 } | |
1547 } | |
1548 } | |
1549 } | |
1550 | |
1551 void processQueue(Enqueuer world, Element main) { | |
1552 world.nativeEnqueuer.processNativeClasses(libraryLoader.libraries); | |
1553 if (main != null && !main.isErroneous) { | |
1554 FunctionElement mainMethod = main; | |
1555 if (mainMethod.computeSignature(this).parameterCount != 0) { | |
1556 // The first argument could be a list of strings. | |
1557 world.registerInstantiatedClass( | |
1558 backend.listImplementation, globalDependencies); | |
1559 world.registerInstantiatedClass( | |
1560 backend.stringImplementation, globalDependencies); | |
1561 | |
1562 backend.registerMainHasArguments(world); | |
1563 } | |
1564 world.addToWorkList(main); | |
1565 } | |
1566 if (verbose) { | |
1567 progress.reset(); | |
1568 } | |
1569 world.forEach((WorkItem work) { | |
1570 withCurrentElement(work.element, () => work.run(this, world)); | |
1571 }); | |
1572 world.queueIsClosed = true; | |
1573 if (compilationFailed) return; | |
1574 assert(world.checkNoEnqueuedInvokedInstanceMethods()); | |
1575 } | |
1576 | |
1577 /** | |
1578 * Perform various checks of the queues. This includes checking that | |
1579 * the queues are empty (nothing was added after we stopped | |
1580 * processing the queues). Also compute the number of methods that | |
1581 * were resolved, but not compiled (aka excess resolution). | |
1582 */ | |
1583 checkQueues() { | |
1584 for (Enqueuer world in [enqueuer.resolution, enqueuer.codegen]) { | |
1585 world.forEach((WorkItem work) { | |
1586 internalError(work.element, "Work list is not empty."); | |
1587 }); | |
1588 } | |
1589 if (!REPORT_EXCESS_RESOLUTION) return; | |
1590 var resolved = new Set.from(enqueuer.resolution.resolvedElements); | |
1591 for (Element e in enqueuer.codegen.generatedCode.keys) { | |
1592 resolved.remove(e); | |
1593 } | |
1594 for (Element e in new Set.from(resolved)) { | |
1595 if (e.isClass || | |
1596 e.isField || | |
1597 e.isTypeVariable || | |
1598 e.isTypedef || | |
1599 identical(e.kind, ElementKind.ABSTRACT_FIELD)) { | |
1600 resolved.remove(e); | |
1601 } | |
1602 if (identical(e.kind, ElementKind.GENERATIVE_CONSTRUCTOR)) { | |
1603 ClassElement enclosingClass = e.enclosingClass; | |
1604 resolved.remove(e); | |
1605 | |
1606 } | |
1607 if (backend.isBackendLibrary(e.library)) { | |
1608 resolved.remove(e); | |
1609 } | |
1610 } | |
1611 log('Excess resolution work: ${resolved.length}.'); | |
1612 for (Element e in resolved) { | |
1613 reportWarning(e, | |
1614 MessageKind.GENERIC, | |
1615 {'text': 'Warning: $e resolved but not compiled.'}); | |
1616 } | |
1617 } | |
1618 | |
1619 void analyzeElement(Element element) { | |
1620 assert(invariant(element, | |
1621 element.impliesType || | |
1622 element.isField || | |
1623 element.isFunction || | |
1624 element.isGenerativeConstructor || | |
1625 element.isGetter || | |
1626 element.isSetter, | |
1627 message: 'Unexpected element kind: ${element.kind}')); | |
1628 assert(invariant(element, element is AnalyzableElement, | |
1629 message: 'Element $element is not analyzable.')); | |
1630 assert(invariant(element, element.isDeclaration)); | |
1631 ResolutionEnqueuer world = enqueuer.resolution; | |
1632 if (world.hasBeenResolved(element)) return; | |
1633 assert(parser != null); | |
1634 Node tree = parser.parse(element); | |
1635 assert(invariant(element, !element.isSynthesized || tree == null)); | |
1636 TreeElements elements = resolver.resolve(element); | |
1637 if (elements != null) { | |
1638 if (tree != null && !analyzeSignaturesOnly && | |
1639 !suppressWarnings) { | |
1640 // Only analyze nodes with a corresponding [TreeElements]. | |
1641 checker.check(elements); | |
1642 } | |
1643 world.registerResolvedElement(element); | |
1644 } | |
1645 } | |
1646 | |
1647 void analyze(ResolutionWorkItem work, ResolutionEnqueuer world) { | |
1648 assert(invariant(work.element, identical(world, enqueuer.resolution))); | |
1649 assert(invariant(work.element, !work.isAnalyzed(), | |
1650 message: 'Element ${work.element} has already been analyzed')); | |
1651 if (shouldPrintProgress) { | |
1652 // TODO(ahe): Add structured diagnostics to the compiler API and | |
1653 // use it to separate this from the --verbose option. | |
1654 if (phase == PHASE_RESOLVING) { | |
1655 log('Resolved ${enqueuer.resolution.resolvedElements.length} ' | |
1656 'elements.'); | |
1657 progress.reset(); | |
1658 } | |
1659 } | |
1660 AstElement element = work.element; | |
1661 if (world.hasBeenResolved(element)) return; | |
1662 analyzeElement(element); | |
1663 backend.onElementResolved(element, element.resolvedAst.elements); | |
1664 } | |
1665 | |
1666 void codegen(CodegenWorkItem work, CodegenEnqueuer world) { | |
1667 assert(invariant(work.element, identical(world, enqueuer.codegen))); | |
1668 if (shouldPrintProgress) { | |
1669 // TODO(ahe): Add structured diagnostics to the compiler API and | |
1670 // use it to separate this from the --verbose option. | |
1671 log('Compiled ${enqueuer.codegen.generatedCode.length} methods.'); | |
1672 progress.reset(); | |
1673 } | |
1674 backend.codegen(work); | |
1675 } | |
1676 | |
1677 void reportError(Spannable node, | |
1678 MessageKind messageKind, | |
1679 [Map arguments = const {}]) { | |
1680 reportDiagnosticInternal( | |
1681 node, messageKind, arguments, api.Diagnostic.ERROR); | |
1682 } | |
1683 | |
1684 /** | |
1685 * Reports an error and then aborts the compiler. Avoid using this method. | |
1686 * | |
1687 * In order to support incremental compilation, it is preferable to use | |
1688 * [reportError]. However, care must be taken to leave the compiler in a | |
1689 * consistent state, for example, by creating synthetic erroneous objects. | |
1690 * | |
1691 * If there's absolutely no way to leave the compiler in a consistent state, | |
1692 * calling this method is preferred as it will set [compilerWasCancelled] to | |
1693 * true which alerts the incremental compiler to discard all state and start | |
1694 * a new compiler. Throwing an exception is also better, as this will set | |
1695 * [hasCrashed] which the incremental compiler also listens too (but don't | |
1696 * throw exceptions, it creates a really bad user experience). | |
1697 * | |
1698 * In any case, calling this method is a last resort, as it essentially | |
1699 * breaks the user experience of the incremental compiler. The purpose of the | |
1700 * incremental compiler is to improve developer productivity. Developers | |
1701 * frequently make mistakes, so syntax errors and spelling errors are | |
1702 * considered normal to the incremental compiler. | |
1703 */ | |
1704 void reportFatalError(Spannable node, MessageKind messageKind, | |
1705 [Map arguments = const {}]) { | |
1706 reportError(node, messageKind, arguments); | |
1707 // TODO(ahe): Make this only abort the current method. | |
1708 throw new CompilerCancelledException( | |
1709 'Error: Cannot continue due to previous error.'); | |
1710 } | |
1711 | |
1712 void reportWarning(Spannable node, MessageKind messageKind, | |
1713 [Map arguments = const {}]) { | |
1714 reportDiagnosticInternal( | |
1715 node, messageKind, arguments, api.Diagnostic.WARNING); | |
1716 } | |
1717 | |
1718 void reportInfo(Spannable node, MessageKind messageKind, | |
1719 [Map arguments = const {}]) { | |
1720 reportDiagnosticInternal(node, messageKind, arguments, api.Diagnostic.INFO); | |
1721 } | |
1722 | |
1723 void reportHint(Spannable node, MessageKind messageKind, | |
1724 [Map arguments = const {}]) { | |
1725 reportDiagnosticInternal(node, messageKind, arguments, api.Diagnostic.HINT); | |
1726 } | |
1727 | |
1728 void reportDiagnosticInternal(Spannable node, | |
1729 MessageKind messageKind, | |
1730 Map arguments, | |
1731 api.Diagnostic kind) { | |
1732 if (!showPackageWarnings && node != NO_LOCATION_SPANNABLE) { | |
1733 switch (kind) { | |
1734 case api.Diagnostic.WARNING: | |
1735 case api.Diagnostic.HINT: | |
1736 Element element = elementFromSpannable(node); | |
1737 if (!inUserCode(element, assumeInUserCode: true)) { | |
1738 Uri uri = getCanonicalUri(element); | |
1739 SuppressionInfo info = | |
1740 suppressedWarnings.putIfAbsent(uri, () => new SuppressionInfo()); | |
1741 if (kind == api.Diagnostic.WARNING) { | |
1742 info.warnings++; | |
1743 } else { | |
1744 info.hints++; | |
1745 } | |
1746 lastDiagnosticWasFiltered = true; | |
1747 return; | |
1748 } | |
1749 break; | |
1750 case api.Diagnostic.INFO: | |
1751 if (lastDiagnosticWasFiltered) { | |
1752 return; | |
1753 } | |
1754 break; | |
1755 } | |
1756 } | |
1757 lastDiagnosticWasFiltered = false; | |
1758 reportDiagnostic( | |
1759 node, messageKind.message(arguments, terseDiagnostics), kind); | |
1760 } | |
1761 | |
1762 void reportDiagnostic(Spannable span, | |
1763 Message message, | |
1764 api.Diagnostic kind); | |
1765 | |
1766 void reportAssertionFailure(SpannableAssertionFailure ex) { | |
1767 String message = (ex.message != null) ? tryToString(ex.message) | |
1768 : tryToString(ex); | |
1769 SourceSpan span = spanFromSpannable(ex.node); | |
1770 reportDiagnosticInternal( | |
1771 ex.node, MessageKind.GENERIC, {'text': message}, api.Diagnostic.CRASH); | |
1772 } | |
1773 | |
1774 SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { | |
1775 if (begin == null || end == null) { | |
1776 // TODO(ahe): We can almost always do better. Often it is only | |
1777 // end that is null. Otherwise, we probably know the current | |
1778 // URI. | |
1779 throw 'Cannot find tokens to produce error message.'; | |
1780 } | |
1781 if (uri == null && currentElement != null) { | |
1782 uri = currentElement.compilationUnit.script.readableUri; | |
1783 } | |
1784 return SourceSpan.withCharacterOffsets(begin, end, | |
1785 (beginOffset, endOffset) => new SourceSpan(uri, beginOffset, endOffset)); | |
1786 } | |
1787 | |
1788 SourceSpan spanFromNode(Node node) { | |
1789 return spanFromTokens(node.getBeginToken(), node.getEndToken()); | |
1790 } | |
1791 | |
1792 SourceSpan spanFromElement(Element element) { | |
1793 while (element != null && element.isSynthesized) { | |
1794 element = element.enclosingElement; | |
1795 } | |
1796 if (element != null && | |
1797 element.position == null && | |
1798 !element.isLibrary && | |
1799 !element.isCompilationUnit) { | |
1800 // Sometimes, the backend fakes up elements that have no | |
1801 // position. So we use the enclosing element instead. It is | |
1802 // not a good error location, but cancel really is "internal | |
1803 // error" or "not implemented yet", so the vicinity is good | |
1804 // enough for now. | |
1805 element = element.enclosingElement; | |
1806 // TODO(ahe): I plan to overhaul this infrastructure anyways. | |
1807 } | |
1808 if (element == null) { | |
1809 element = currentElement; | |
1810 } | |
1811 Token position = element.position; | |
1812 Uri uri = element.compilationUnit.script.readableUri; | |
1813 return (position == null) | |
1814 ? new SourceSpan(uri, 0, 0) | |
1815 : spanFromTokens(position, position, uri); | |
1816 } | |
1817 | |
1818 SourceSpan spanFromHInstruction(HInstruction instruction) { | |
1819 Element element = _elementFromHInstruction(instruction); | |
1820 if (element == null) element = currentElement; | |
1821 var position = instruction.sourcePosition; | |
1822 if (position == null) return spanFromElement(element); | |
1823 Token token = position.token; | |
1824 if (token == null) return spanFromElement(element); | |
1825 Uri uri = element.compilationUnit.script.readableUri; | |
1826 return spanFromTokens(token, token, uri); | |
1827 } | |
1828 | |
1829 /** | |
1830 * Translates the [resolvedUri] into a readable URI. | |
1831 * | |
1832 * The [importingLibrary] holds the library importing [resolvedUri] or | |
1833 * [:null:] if [resolvedUri] is loaded as the main library. The | |
1834 * [importingLibrary] is used to grant access to internal libraries from | |
1835 * platform libraries and patch libraries. | |
1836 * | |
1837 * If the [resolvedUri] is not accessible from [importingLibrary], this method | |
1838 * is responsible for reporting errors. | |
1839 * | |
1840 * See [LibraryLoader] for terminology on URIs. | |
1841 */ | |
1842 Uri translateResolvedUri(LibraryElement importingLibrary, | |
1843 Uri resolvedUri, Node node) { | |
1844 unimplemented(importingLibrary, 'Compiler.translateResolvedUri'); | |
1845 return null; | |
1846 } | |
1847 | |
1848 /** | |
1849 * Reads the script specified by the [readableUri]. | |
1850 * | |
1851 * See [LibraryLoader] for terminology on URIs. | |
1852 */ | |
1853 Future<Script> readScript(Spannable node, Uri readableUri) { | |
1854 unimplemented(node, 'Compiler.readScript'); | |
1855 return null; | |
1856 } | |
1857 | |
1858 Element lookupElementIn(ScopeContainerElement container, String name) { | |
1859 Element element = container.localLookup(name); | |
1860 if (element == null) { | |
1861 throw 'Could not find $name in $container'; | |
1862 } | |
1863 return element; | |
1864 } | |
1865 | |
1866 bool get isMockCompilation => false; | |
1867 | |
1868 Token processAndStripComments(Token currentToken) { | |
1869 Token firstToken = currentToken; | |
1870 Token prevToken; | |
1871 while (currentToken.kind != EOF_TOKEN) { | |
1872 if (identical(currentToken.kind, COMMENT_TOKEN)) { | |
1873 Token firstCommentToken = currentToken; | |
1874 while (identical(currentToken.kind, COMMENT_TOKEN)) { | |
1875 currentToken = currentToken.next; | |
1876 } | |
1877 commentMap[currentToken] = firstCommentToken; | |
1878 if (prevToken == null) { | |
1879 firstToken = currentToken; | |
1880 } else { | |
1881 prevToken.next = currentToken; | |
1882 } | |
1883 } | |
1884 prevToken = currentToken; | |
1885 currentToken = currentToken.next; | |
1886 } | |
1887 return firstToken; | |
1888 } | |
1889 | |
1890 void reportUnusedCode() { | |
1891 void checkLive(member) { | |
1892 if (member.isFunction) { | |
1893 if (!enqueuer.resolution.hasBeenResolved(member)) { | |
1894 reportHint(member, MessageKind.UNUSED_METHOD, | |
1895 {'name': member.name}); | |
1896 } | |
1897 } else if (member.isClass) { | |
1898 if (!member.isResolved) { | |
1899 reportHint(member, MessageKind.UNUSED_CLASS, | |
1900 {'name': member.name}); | |
1901 } else { | |
1902 member.forEachLocalMember(checkLive); | |
1903 } | |
1904 } else if (member.isTypedef) { | |
1905 if (!member.isResolved) { | |
1906 reportHint(member, MessageKind.UNUSED_TYPEDEF, | |
1907 {'name': member.name}); | |
1908 } | |
1909 } | |
1910 } | |
1911 libraryLoader.libraries.forEach((LibraryElement library) { | |
1912 // TODO(ahe): Implement better heuristics to discover entry points of | |
1913 // packages and use that to discover unused implementation details in | |
1914 // packages. | |
1915 if (library.isPlatformLibrary || library.isPackageLibrary) return; | |
1916 library.compilationUnits.forEach((unit) { | |
1917 unit.forEachLocalMember(checkLive); | |
1918 }); | |
1919 }); | |
1920 } | |
1921 | |
1922 /// Helper for determining whether the current element is declared within | |
1923 /// 'user code'. | |
1924 /// | |
1925 /// See [inUserCode] for what defines 'user code'. | |
1926 bool currentlyInUserCode() { | |
1927 return inUserCode(currentElement); | |
1928 } | |
1929 | |
1930 /// Helper for determining whether [element] is declared within 'user code'. | |
1931 /// | |
1932 /// What constitutes 'user code' is defined by the URI(s) provided by the | |
1933 /// entry point(s) of compilation or analysis: | |
1934 /// | |
1935 /// If an entrypoint URI uses the 'package' scheme then every library from | |
1936 /// that same package is considered to be in user code. For instance, if | |
1937 /// an entry point URI is 'package:foo/bar.dart' then every library whose | |
1938 /// canonical URI starts with 'package:foo/' is in user code. | |
1939 /// | |
1940 /// If an entrypoint URI uses another scheme than 'package' then every library | |
1941 /// with that scheme is in user code. For instance, an entry point URI is | |
1942 /// 'file:///foo.dart' then every library whose canonical URI scheme is | |
1943 /// 'file' is in user code. | |
1944 /// | |
1945 /// If [assumeInUserCode] is `true`, [element] is assumed to be in user code | |
1946 /// if no entrypoints have been set. | |
1947 bool inUserCode(Element element, {bool assumeInUserCode: false}) { | |
1948 List<Uri> entrypoints = <Uri>[]; | |
1949 if (mainApp != null) { | |
1950 entrypoints.add(mainApp.canonicalUri); | |
1951 } | |
1952 if (librariesToAnalyzeWhenRun != null) { | |
1953 entrypoints.addAll(librariesToAnalyzeWhenRun); | |
1954 } | |
1955 if (entrypoints.isEmpty && assumeInUserCode) { | |
1956 // Assume in user code since [mainApp] has not been set yet. | |
1957 return true; | |
1958 } | |
1959 if (element == null) return false; | |
1960 Uri libraryUri = element.library.canonicalUri; | |
1961 if (libraryUri.scheme == 'package') { | |
1962 for (Uri uri in entrypoints) { | |
1963 if (uri.scheme != 'package') continue; | |
1964 int slashPos = libraryUri.path.indexOf('/'); | |
1965 if (slashPos != -1) { | |
1966 String packageName = libraryUri.path.substring(0, slashPos + 1); | |
1967 if (uri.path.startsWith(packageName)) { | |
1968 return true; | |
1969 } | |
1970 } else { | |
1971 if (libraryUri.path == uri.path) { | |
1972 return true; | |
1973 } | |
1974 } | |
1975 } | |
1976 } else { | |
1977 for (Uri uri in entrypoints) { | |
1978 if (libraryUri.scheme == uri.scheme) return true; | |
1979 } | |
1980 } | |
1981 return false; | |
1982 } | |
1983 | |
1984 /// Return a canonical URI for the source of [element]. | |
1985 /// | |
1986 /// For a package library with canonical URI 'package:foo/bar/baz.dart' the | |
1987 /// return URI is 'package:foo'. For non-package libraries the returned URI is | |
1988 /// the canonical URI of the library itself. | |
1989 Uri getCanonicalUri(Element element) { | |
1990 if (element == null) return null; | |
1991 Uri libraryUri = element.library.canonicalUri; | |
1992 if (libraryUri.scheme == 'package') { | |
1993 int slashPos = libraryUri.path.indexOf('/'); | |
1994 if (slashPos != -1) { | |
1995 String packageName = libraryUri.path.substring(0, slashPos); | |
1996 return new Uri(scheme: 'package', path: packageName); | |
1997 } | |
1998 } | |
1999 return libraryUri; | |
2000 } | |
2001 | |
2002 void diagnoseCrashInUserCode(String message, exception, stackTrace) { | |
2003 // Overridden by Compiler in apiimpl.dart. | |
2004 } | |
2005 | |
2006 void forgetElement(Element element) { | |
2007 enqueuer.forgetElement(element); | |
2008 if (element is MemberElement) { | |
2009 for (Element closure in element.nestedClosures) { | |
2010 // TODO(ahe): It would be nice to reuse names of nested closures. | |
2011 closureToClassMapper.forgetElement(closure); | |
2012 } | |
2013 } | |
2014 backend.forgetElement(element); | |
2015 } | |
2016 } | |
2017 | |
2018 class CompilerTask { | |
2019 final Compiler compiler; | |
2020 final Stopwatch watch; | |
2021 UserTag profilerTag; | |
2022 | |
2023 CompilerTask(Compiler compiler) | |
2024 : this.compiler = compiler, | |
2025 watch = (compiler.verbose) ? new Stopwatch() : null; | |
2026 | |
2027 String get name => 'Unknown task'; | |
2028 int get timing => (watch != null) ? watch.elapsedMilliseconds : 0; | |
2029 | |
2030 int get timingMicroseconds => (watch != null) ? watch.elapsedMicroseconds : 0; | |
2031 | |
2032 UserTag getProfilerTag() { | |
2033 if (profilerTag == null) profilerTag = new UserTag(name); | |
2034 return profilerTag; | |
2035 } | |
2036 | |
2037 measure(action()) { | |
2038 // In verbose mode when watch != null. | |
2039 if (watch == null) return action(); | |
2040 CompilerTask previous = compiler.measuredTask; | |
2041 if (identical(this, previous)) return action(); | |
2042 compiler.measuredTask = this; | |
2043 if (previous != null) previous.watch.stop(); | |
2044 watch.start(); | |
2045 UserTag oldTag = getProfilerTag().makeCurrent(); | |
2046 try { | |
2047 return action(); | |
2048 } finally { | |
2049 watch.stop(); | |
2050 oldTag.makeCurrent(); | |
2051 if (previous != null) previous.watch.start(); | |
2052 compiler.measuredTask = previous; | |
2053 } | |
2054 } | |
2055 | |
2056 measureElement(Element element, action()) { | |
2057 compiler.withCurrentElement(element, () => measure(action)); | |
2058 } | |
2059 } | |
2060 | |
2061 class CompilerCancelledException implements Exception { | |
2062 final String reason; | |
2063 CompilerCancelledException(this.reason); | |
2064 | |
2065 String toString() { | |
2066 String banner = 'compiler cancelled'; | |
2067 return (reason != null) ? '$banner: $reason' : '$banner'; | |
2068 } | |
2069 } | |
2070 | |
2071 class SourceSpan implements Spannable { | |
2072 final Uri uri; | |
2073 final int begin; | |
2074 final int end; | |
2075 | |
2076 const SourceSpan(this.uri, this.begin, this.end); | |
2077 | |
2078 static withCharacterOffsets(Token begin, Token end, | |
2079 f(int beginOffset, int endOffset)) { | |
2080 final beginOffset = begin.charOffset; | |
2081 final endOffset = end.charOffset + end.charCount; | |
2082 | |
2083 // [begin] and [end] might be the same for the same empty token. This | |
2084 // happens for instance when scanning '$$'. | |
2085 assert(endOffset >= beginOffset); | |
2086 return f(beginOffset, endOffset); | |
2087 } | |
2088 | |
2089 String toString() => 'SourceSpan($uri, $begin, $end)'; | |
2090 } | |
2091 | |
2092 /// Flag that can be used in assertions to assert that a code path is only | |
2093 /// executed as part of development. | |
2094 /// | |
2095 /// This flag is automatically set to true if helper methods like, [debugPrint], | |
2096 /// [debugWrapPrint], [trace], and [reportHere] are called. | |
2097 bool DEBUG_MODE = false; | |
2098 | |
2099 /// Assert that [DEBUG_MODE] is `true` and provide [message] as part of the | |
2100 /// error message. | |
2101 assertDebugMode(String message) { | |
2102 assert(invariant(NO_LOCATION_SPANNABLE, DEBUG_MODE, | |
2103 message: 'Debug mode is not enabled: $message')); | |
2104 } | |
2105 | |
2106 /** | |
2107 * Throws a [SpannableAssertionFailure] if [condition] is | |
2108 * [:false:]. [condition] must be either a [:bool:] or a no-arg | |
2109 * function returning a [:bool:]. | |
2110 * | |
2111 * Use this method to provide better information for assertion by calling | |
2112 * [invariant] as the argument to an [:assert:] statement: | |
2113 * | |
2114 * assert(invariant(position, isValid)); | |
2115 * | |
2116 * [spannable] must be non-null and will be used to provide positional | |
2117 * information in the generated error message. | |
2118 */ | |
2119 bool invariant(Spannable spannable, var condition, {var message: null}) { | |
2120 // TODO(johnniwinther): Use [spannable] and [message] to provide better | |
2121 // information on assertion errors. | |
2122 if (spannable == null) { | |
2123 throw new SpannableAssertionFailure(CURRENT_ELEMENT_SPANNABLE, | |
2124 "Spannable was null for invariant. Use CURRENT_ELEMENT_SPANNABLE."); | |
2125 } | |
2126 if (condition is Function){ | |
2127 condition = condition(); | |
2128 } | |
2129 if (!condition) { | |
2130 if (message is Function) { | |
2131 message = message(); | |
2132 } | |
2133 throw new SpannableAssertionFailure(spannable, message); | |
2134 } | |
2135 return true; | |
2136 } | |
2137 | |
2138 /// Returns `true` when [s] is private if used as an identifier. | |
2139 bool isPrivateName(String s) => !s.isEmpty && s.codeUnitAt(0) == $_; | |
2140 | |
2141 /// A sink that drains into /dev/null. | |
2142 class NullSink implements EventSink<String> { | |
2143 final String name; | |
2144 | |
2145 NullSink(this.name); | |
2146 | |
2147 add(String value) {} | |
2148 | |
2149 void addError(Object error, [StackTrace stackTrace]) {} | |
2150 | |
2151 void close() {} | |
2152 | |
2153 toString() => name; | |
2154 | |
2155 /// Convenience method for getting an [api.CompilerOutputProvider]. | |
2156 static NullSink outputProvider(String name, String extension) { | |
2157 return new NullSink('$name.$extension'); | |
2158 } | |
2159 } | |
2160 | |
2161 /// Information about suppressed warnings and hints for a given library. | |
2162 class SuppressionInfo { | |
2163 int warnings = 0; | |
2164 int hints = 0; | |
2165 } | |
2166 | |
2167 class GenericTask extends CompilerTask { | |
2168 final String name; | |
2169 | |
2170 GenericTask(this.name, Compiler compiler) | |
2171 : super(compiler); | |
2172 } | |
OLD | NEW |