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 /// `true` if async/await features are supported. | |
722 final bool enableAsyncAwait; | |
723 | |
724 /// If `true`, some values are cached for reuse in incremental compilation. | |
725 /// Incremental compilation is basically calling [run] more than once. | |
726 final bool hasIncrementalSupport; | |
727 | |
728 api.CompilerOutputProvider outputProvider; | |
729 | |
730 bool disableInlining = false; | |
731 | |
732 /// True if compilation was aborted with a [CompilerCancelledException]. Only | |
733 /// set after Future retuned by [run] has completed. | |
734 bool compilerWasCancelled = false; | |
735 | |
736 List<Uri> librariesToAnalyzeWhenRun; | |
737 | |
738 Tracer tracer; | |
739 | |
740 CompilerTask measuredTask; | |
741 Element _currentElement; | |
742 LibraryElement coreLibrary; | |
743 | |
744 LibraryElement mainApp; | |
745 FunctionElement mainFunction; | |
746 | |
747 /// Initialized when dart:mirrors is loaded. | |
748 LibraryElement mirrorsLibrary; | |
749 | |
750 /// Initialized when dart:typed_data is loaded. | |
751 LibraryElement typedDataLibrary; | |
752 | |
753 ClassElement objectClass; | |
754 ClassElement boolClass; | |
755 ClassElement numClass; | |
756 ClassElement intClass; | |
757 ClassElement doubleClass; | |
758 ClassElement stringClass; | |
759 ClassElement functionClass; | |
760 ClassElement nullClass; | |
761 ClassElement listClass; | |
762 ClassElement typeClass; | |
763 ClassElement mapClass; | |
764 ClassElement symbolClass; | |
765 ClassElement stackTraceClass; | |
766 ClassElement typedDataClass; | |
767 ClassElement futureClass; | |
768 ClassElement iterableClass; | |
769 ClassElement streamClass; | |
770 | |
771 /// The constant for the [proxy] variable defined in dart:core. | |
772 ConstantValue proxyConstant; | |
773 | |
774 // TODO(johnniwinther): Move this to the JavaScriptBackend. | |
775 /// The constant for the [patch] variable defined in dart:_js_helper. | |
776 ConstantValue patchConstant; | |
777 | |
778 // TODO(johnniwinther): Move this to the JavaScriptBackend. | |
779 ClassElement nativeAnnotationClass; | |
780 | |
781 // Initialized after symbolClass has been resolved. | |
782 FunctionElement symbolConstructor; | |
783 | |
784 // Initialized when dart:mirrors is loaded. | |
785 ClassElement mirrorSystemClass; | |
786 | |
787 // Initialized when dart:mirrors is loaded. | |
788 ClassElement mirrorsUsedClass; | |
789 | |
790 // Initialized after mirrorSystemClass has been resolved. | |
791 FunctionElement mirrorSystemGetNameFunction; | |
792 | |
793 // Initialized when dart:_internal is loaded. | |
794 ClassElement symbolImplementationClass; | |
795 | |
796 // Initialized when symbolImplementationClass has been resolved. | |
797 FunctionElement symbolValidatedConstructor; | |
798 | |
799 // Initialized when mirrorsUsedClass has been resolved. | |
800 FunctionElement mirrorsUsedConstructor; | |
801 | |
802 // Initialized when dart:mirrors is loaded. | |
803 ClassElement deferredLibraryClass; | |
804 | |
805 /// Document class from dart:mirrors. | |
806 ClassElement documentClass; | |
807 Element identicalFunction; | |
808 Element loadLibraryFunction; | |
809 Element functionApplyMethod; | |
810 Element intEnvironment; | |
811 Element boolEnvironment; | |
812 Element stringEnvironment; | |
813 | |
814 fromEnvironment(String name) => null; | |
815 | |
816 Element get currentElement => _currentElement; | |
817 | |
818 String tryToString(object) { | |
819 try { | |
820 return object.toString(); | |
821 } catch (_) { | |
822 return '<exception in toString()>'; | |
823 } | |
824 } | |
825 | |
826 /** | |
827 * Perform an operation, [f], returning the return value from [f]. If an | |
828 * error occurs then report it as having occurred during compilation of | |
829 * [element]. Can be nested. | |
830 */ | |
831 withCurrentElement(Element element, f()) { | |
832 Element old = currentElement; | |
833 _currentElement = element; | |
834 try { | |
835 return f(); | |
836 } on SpannableAssertionFailure catch (ex) { | |
837 if (!hasCrashed) { | |
838 reportAssertionFailure(ex); | |
839 pleaseReportCrash(); | |
840 } | |
841 hasCrashed = true; | |
842 rethrow; | |
843 } on CompilerCancelledException catch (ex) { | |
844 rethrow; | |
845 } on StackOverflowError catch (ex) { | |
846 // We cannot report anything useful in this case, because we | |
847 // do not have enough stack space. | |
848 rethrow; | |
849 } catch (ex) { | |
850 if (hasCrashed) rethrow; | |
851 try { | |
852 unhandledExceptionOnElement(element); | |
853 } catch (doubleFault) { | |
854 // Ignoring exceptions in exception handling. | |
855 } | |
856 rethrow; | |
857 } finally { | |
858 _currentElement = old; | |
859 } | |
860 } | |
861 | |
862 List<CompilerTask> tasks; | |
863 ScannerTask scanner; | |
864 DietParserTask dietParser; | |
865 ParserTask parser; | |
866 PatchParserTask patchParser; | |
867 LibraryLoaderTask libraryLoader; | |
868 ResolverTask resolver; | |
869 closureMapping.ClosureTask closureToClassMapper; | |
870 TypeCheckerTask checker; | |
871 IrBuilderTask irBuilder; | |
872 ti.TypesTask typesTask; | |
873 Backend backend; | |
874 | |
875 GenericTask reuseLibraryTask; | |
876 | |
877 /// The constant environment for the frontend interpretation of compile-time | |
878 /// constants. | |
879 ConstantEnvironment constants; | |
880 | |
881 EnqueueTask enqueuer; | |
882 DeferredLoadTask deferredLoadTask; | |
883 MirrorUsageAnalyzerTask mirrorUsageAnalyzerTask; | |
884 DumpInfoTask dumpInfoTask; | |
885 String buildId; | |
886 | |
887 /// A customizable filter that is applied to enqueued work items. | |
888 QueueFilter enqueuerFilter = new QueueFilter(); | |
889 | |
890 static const String MAIN = 'main'; | |
891 static const String CALL_OPERATOR_NAME = 'call'; | |
892 static const String NO_SUCH_METHOD = 'noSuchMethod'; | |
893 static const int NO_SUCH_METHOD_ARG_COUNT = 1; | |
894 static const String CREATE_INVOCATION_MIRROR = | |
895 'createInvocationMirror'; | |
896 | |
897 static const String RUNTIME_TYPE = 'runtimeType'; | |
898 | |
899 static const String UNDETERMINED_BUILD_ID = | |
900 "build number could not be determined"; | |
901 | |
902 final Selector iteratorSelector = | |
903 new Selector.getter('iterator', null); | |
904 final Selector currentSelector = | |
905 new Selector.getter('current', null); | |
906 final Selector moveNextSelector = | |
907 new Selector.call('moveNext', null, 0); | |
908 final Selector noSuchMethodSelector = new Selector.call( | |
909 Compiler.NO_SUCH_METHOD, null, Compiler.NO_SUCH_METHOD_ARG_COUNT); | |
910 final Selector symbolValidatedConstructorSelector = new Selector.call( | |
911 'validated', null, 1); | |
912 final Selector fromEnvironmentSelector = new Selector.callConstructor( | |
913 'fromEnvironment', null, 2); | |
914 | |
915 bool enabledNoSuchMethod = false; | |
916 bool enabledRuntimeType = false; | |
917 bool enabledFunctionApply = false; | |
918 bool enabledInvokeOn = false; | |
919 bool hasIsolateSupport = false; | |
920 | |
921 Stopwatch progress; | |
922 | |
923 bool get shouldPrintProgress { | |
924 return verbose && progress.elapsedMilliseconds > 500; | |
925 } | |
926 | |
927 static const int PHASE_SCANNING = 0; | |
928 static const int PHASE_RESOLVING = 1; | |
929 static const int PHASE_DONE_RESOLVING = 2; | |
930 static const int PHASE_COMPILING = 3; | |
931 int phase; | |
932 | |
933 bool compilationFailed = false; | |
934 | |
935 bool hasCrashed = false; | |
936 | |
937 /// Set by the backend if real reflection is detected in use of dart:mirrors. | |
938 bool disableTypeInferenceForMirrors = false; | |
939 | |
940 Compiler({this.enableTypeAssertions: false, | |
941 this.enableUserAssertions: false, | |
942 this.trustTypeAnnotations: false, | |
943 this.enableConcreteTypeInference: false, | |
944 bool disableTypeInferenceFlag: false, | |
945 this.maxConcreteTypeSize: 5, | |
946 this.enableMinification: false, | |
947 this.preserveUris: false, | |
948 this.enableNativeLiveTypeAnalysis: false, | |
949 bool emitJavaScript: true, | |
950 bool dart2dartMultiFile: false, | |
951 bool generateSourceMap: true, | |
952 bool analyzeAllFlag: false, | |
953 bool analyzeOnly: false, | |
954 this.analyzeMain: false, | |
955 bool analyzeSignaturesOnly: false, | |
956 this.preserveComments: false, | |
957 this.verbose: false, | |
958 this.sourceMapUri: null, | |
959 this.outputUri: null, | |
960 this.buildId: UNDETERMINED_BUILD_ID, | |
961 this.terseDiagnostics: false, | |
962 this.dumpInfo: false, | |
963 this.showPackageWarnings: false, | |
964 this.useContentSecurityPolicy: false, | |
965 this.suppressWarnings: false, | |
966 bool hasIncrementalSupport: false, | |
967 this.enableAsyncAwait: false, | |
968 api.CompilerOutputProvider outputProvider, | |
969 List<String> strips: const []}) | |
970 : this.disableTypeInferenceFlag = | |
971 disableTypeInferenceFlag || !emitJavaScript, | |
972 this.analyzeOnly = | |
973 analyzeOnly || analyzeSignaturesOnly || analyzeAllFlag, | |
974 this.analyzeSignaturesOnly = analyzeSignaturesOnly, | |
975 this.analyzeAllFlag = analyzeAllFlag, | |
976 this.hasIncrementalSupport = hasIncrementalSupport, | |
977 cacheStrategy = new CacheStrategy(hasIncrementalSupport), | |
978 this.outputProvider = (outputProvider == null) | |
979 ? NullSink.outputProvider | |
980 : outputProvider { | |
981 if (hasIncrementalSupport) { | |
982 // TODO(ahe): This is too much. Any method from platform and package | |
983 // libraries can be inlined. | |
984 disableInlining = true; | |
985 } | |
986 world = new World(this); | |
987 types = new Types(this); | |
988 tracer = new Tracer(this, this.outputProvider); | |
989 | |
990 if (verbose) { | |
991 progress = new Stopwatch()..start(); | |
992 } | |
993 | |
994 // TODO(johnniwinther): Separate the dependency tracking from the enqueueing | |
995 // for global dependencies. | |
996 globalDependencies = | |
997 new CodegenRegistry(this, new TreeElementMapping(null)); | |
998 | |
999 closureMapping.ClosureNamer closureNamer; | |
1000 if (emitJavaScript) { | |
1001 js_backend.JavaScriptBackend jsBackend = | |
1002 new js_backend.JavaScriptBackend(this, generateSourceMap); | |
1003 closureNamer = jsBackend.namer; | |
1004 backend = jsBackend; | |
1005 } else { | |
1006 closureNamer = new closureMapping.ClosureNamer(); | |
1007 backend = new dart_backend.DartBackend(this, strips, | |
1008 multiFile: dart2dartMultiFile); | |
1009 } | |
1010 | |
1011 tasks = [ | |
1012 libraryLoader = new LibraryLoaderTask(this), | |
1013 scanner = new ScannerTask(this), | |
1014 dietParser = new DietParserTask(this), | |
1015 parser = new ParserTask(this), | |
1016 patchParser = new PatchParserTask(this), | |
1017 resolver = new ResolverTask(this, backend.constantCompilerTask), | |
1018 closureToClassMapper = new closureMapping.ClosureTask(this, closureNamer), | |
1019 checker = new TypeCheckerTask(this), | |
1020 irBuilder = new IrBuilderTask(this), | |
1021 typesTask = new ti.TypesTask(this), | |
1022 constants = backend.constantCompilerTask, | |
1023 deferredLoadTask = new DeferredLoadTask(this), | |
1024 mirrorUsageAnalyzerTask = new MirrorUsageAnalyzerTask(this), | |
1025 enqueuer = new EnqueueTask(this), | |
1026 dumpInfoTask = new DumpInfoTask(this), | |
1027 reuseLibraryTask = new GenericTask('Reuse library', this), | |
1028 ]; | |
1029 | |
1030 tasks.addAll(backend.tasks); | |
1031 } | |
1032 | |
1033 Universe get resolverWorld => enqueuer.resolution.universe; | |
1034 Universe get codegenWorld => enqueuer.codegen.universe; | |
1035 | |
1036 bool get hasBuildId => buildId != UNDETERMINED_BUILD_ID; | |
1037 | |
1038 bool get analyzeAll => analyzeAllFlag || compileAll; | |
1039 | |
1040 bool get compileAll => false; | |
1041 | |
1042 bool get disableTypeInference => disableTypeInferenceFlag; | |
1043 | |
1044 int getNextFreeClassId() => nextFreeClassId++; | |
1045 | |
1046 void unimplemented(Spannable spannable, String methodName) { | |
1047 internalError(spannable, "$methodName not implemented."); | |
1048 } | |
1049 | |
1050 void internalError(Spannable node, reason) { | |
1051 assembledCode = null; // Compilation failed. Make sure that we | |
1052 // don't return a bogus result. | |
1053 String message = tryToString(reason); | |
1054 reportDiagnosticInternal( | |
1055 node, MessageKind.GENERIC, {'text': message}, api.Diagnostic.CRASH); | |
1056 throw 'Internal Error: $message'; | |
1057 } | |
1058 | |
1059 void unhandledExceptionOnElement(Element element) { | |
1060 if (hasCrashed) return; | |
1061 hasCrashed = true; | |
1062 reportDiagnostic(element, | |
1063 MessageKind.COMPILER_CRASHED.message(), | |
1064 api.Diagnostic.CRASH); | |
1065 pleaseReportCrash(); | |
1066 } | |
1067 | |
1068 void pleaseReportCrash() { | |
1069 print(MessageKind.PLEASE_REPORT_THE_CRASH.message({'buildId': buildId})); | |
1070 } | |
1071 | |
1072 SourceSpan spanFromSpannable(Spannable node) { | |
1073 // TODO(johnniwinther): Disallow `node == null` ? | |
1074 if (node == null) return null; | |
1075 if (node == CURRENT_ELEMENT_SPANNABLE) { | |
1076 node = currentElement; | |
1077 } else if (node == NO_LOCATION_SPANNABLE) { | |
1078 if (currentElement == null) return null; | |
1079 node = currentElement; | |
1080 } | |
1081 if (node is SourceSpan) { | |
1082 return node; | |
1083 } else if (node is Node) { | |
1084 return spanFromNode(node); | |
1085 } else if (node is TokenPair) { | |
1086 return spanFromTokens(node.begin, node.end); | |
1087 } else if (node is Token) { | |
1088 return spanFromTokens(node, node); | |
1089 } else if (node is HInstruction) { | |
1090 return spanFromHInstruction(node); | |
1091 } else if (node is Element) { | |
1092 return spanFromElement(node); | |
1093 } else if (node is MetadataAnnotation) { | |
1094 Uri uri = node.annotatedElement.compilationUnit.script.readableUri; | |
1095 return spanFromTokens(node.beginToken, node.endToken, uri); | |
1096 } else if (node is Local) { | |
1097 Local local = node; | |
1098 return spanFromElement(local.executableContext); | |
1099 } else { | |
1100 throw 'No error location.'; | |
1101 } | |
1102 } | |
1103 | |
1104 Element _elementFromHInstruction(HInstruction instruction) { | |
1105 return instruction.sourceElement is Element | |
1106 ? instruction.sourceElement : null; | |
1107 } | |
1108 | |
1109 /// Finds the approximate [Element] for [node]. [currentElement] is used as | |
1110 /// the default value. | |
1111 Element elementFromSpannable(Spannable node) { | |
1112 Element element; | |
1113 if (node is Element) { | |
1114 element = node; | |
1115 } else if (node is HInstruction) { | |
1116 element = _elementFromHInstruction(node); | |
1117 } else if (node is MetadataAnnotation) { | |
1118 element = node.annotatedElement; | |
1119 } | |
1120 return element != null ? element : currentElement; | |
1121 } | |
1122 | |
1123 void log(message) { | |
1124 reportDiagnostic(null, | |
1125 MessageKind.GENERIC.message({'text': '$message'}), | |
1126 api.Diagnostic.VERBOSE_INFO); | |
1127 } | |
1128 | |
1129 Future<bool> run(Uri uri) { | |
1130 totalCompileTime.start(); | |
1131 | |
1132 return new Future.sync(() => runCompiler(uri)).catchError((error) { | |
1133 if (error is CompilerCancelledException) { | |
1134 compilerWasCancelled = true; | |
1135 log('Error: $error'); | |
1136 return false; | |
1137 } | |
1138 | |
1139 try { | |
1140 if (!hasCrashed) { | |
1141 hasCrashed = true; | |
1142 if (error is SpannableAssertionFailure) { | |
1143 reportAssertionFailure(error); | |
1144 } else { | |
1145 reportDiagnostic(new SourceSpan(uri, 0, 0), | |
1146 MessageKind.COMPILER_CRASHED.message(), | |
1147 api.Diagnostic.CRASH); | |
1148 } | |
1149 pleaseReportCrash(); | |
1150 } | |
1151 } catch (doubleFault) { | |
1152 // Ignoring exceptions in exception handling. | |
1153 } | |
1154 throw error; | |
1155 }).whenComplete(() { | |
1156 tracer.close(); | |
1157 totalCompileTime.stop(); | |
1158 }).then((_) { | |
1159 return !compilationFailed; | |
1160 }); | |
1161 } | |
1162 | |
1163 /// This method is called immediately after the [LibraryElement] [library] has | |
1164 /// been created. | |
1165 /// | |
1166 /// Use this callback method to store references to specific libraries. | |
1167 /// Note that [library] has not been scanned yet, nor has its imports/exports | |
1168 /// been resolved. | |
1169 void onLibraryCreated(LibraryElement library) { | |
1170 Uri uri = library.canonicalUri; | |
1171 if (uri == DART_CORE) { | |
1172 coreLibrary = library; | |
1173 } else if (uri == DART_NATIVE_TYPED_DATA) { | |
1174 typedDataLibrary = library; | |
1175 } else if (uri == DART_MIRRORS) { | |
1176 mirrorsLibrary = library; | |
1177 } | |
1178 backend.onLibraryCreated(library); | |
1179 } | |
1180 | |
1181 /// This method is called immediately after the [library] and its parts have | |
1182 /// been scanned. | |
1183 /// | |
1184 /// Use this callback method to store references to specific member declared | |
1185 /// in certain libraries. Note that [library] has not been patched yet, nor | |
1186 /// has its imports/exports been resolved. | |
1187 /// | |
1188 /// Use [loader] to register the creation and scanning of a patch library | |
1189 /// for [library]. | |
1190 Future onLibraryScanned(LibraryElement library, LibraryLoader loader) { | |
1191 Uri uri = library.canonicalUri; | |
1192 if (uri == DART_CORE) { | |
1193 initializeCoreClasses(); | |
1194 identicalFunction = coreLibrary.find('identical'); | |
1195 } else if (uri == DART_INTERNAL) { | |
1196 symbolImplementationClass = findRequiredElement(library, 'Symbol'); | |
1197 } else if (uri == DART_MIRRORS) { | |
1198 mirrorSystemClass = findRequiredElement(library, 'MirrorSystem'); | |
1199 mirrorsUsedClass = findRequiredElement(library, 'MirrorsUsed'); | |
1200 } else if (uri == DART_ASYNC) { | |
1201 deferredLibraryClass = findRequiredElement(library, 'DeferredLibrary'); | |
1202 futureClass = findRequiredElement(library, 'Future'); | |
1203 streamClass = findRequiredElement(library, 'Stream'); | |
1204 } else if (uri == DART_NATIVE_TYPED_DATA) { | |
1205 typedDataClass = findRequiredElement(library, 'NativeTypedData'); | |
1206 } else if (uri == js_backend.JavaScriptBackend.DART_JS_HELPER) { | |
1207 nativeAnnotationClass = findRequiredElement(library, 'Native'); | |
1208 } | |
1209 return backend.onLibraryScanned(library, loader); | |
1210 } | |
1211 | |
1212 /// This method is called when all new libraries loaded through | |
1213 /// [LibraryLoader.loadLibrary] has been loaded and their imports/exports | |
1214 /// have been computed. | |
1215 /// | |
1216 /// [loadedLibraries] contains the newly loaded libraries. | |
1217 /// | |
1218 /// The method returns a [Future] allowing for the loading of additional | |
1219 /// libraries. | |
1220 Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { | |
1221 return new Future.sync(() { | |
1222 if (!loadedLibraries.containsKey(DART_CORE)) return new Future.value(); | |
1223 | |
1224 functionClass.ensureResolved(this); | |
1225 functionApplyMethod = functionClass.lookupLocalMember('apply'); | |
1226 | |
1227 proxyConstant = | |
1228 resolver.constantCompiler.compileConstant( | |
1229 coreLibrary.find('proxy')).value; | |
1230 | |
1231 // TODO(johnniwinther): Move this to the JavaScript backend. | |
1232 LibraryElement jsHelperLibrary = | |
1233 loadedLibraries[js_backend.JavaScriptBackend.DART_JS_HELPER]; | |
1234 if (jsHelperLibrary != null) { | |
1235 patchConstant = resolver.constantCompiler.compileConstant( | |
1236 jsHelperLibrary.find('patch')).value; | |
1237 } | |
1238 | |
1239 if (preserveComments) { | |
1240 return libraryLoader.loadLibrary(DART_MIRRORS) | |
1241 .then((LibraryElement libraryElement) { | |
1242 documentClass = libraryElement.find('Comment'); | |
1243 }); | |
1244 } | |
1245 }).then((_) => backend.onLibrariesLoaded(loadedLibraries)); | |
1246 } | |
1247 | |
1248 Element findRequiredElement(LibraryElement library, String name) { | |
1249 var element = library.find(name); | |
1250 if (element == null) { | |
1251 internalError(library, | |
1252 "The library '${library.canonicalUri}' does not contain required " | |
1253 "element: '$name'."); | |
1254 } | |
1255 return element; | |
1256 } | |
1257 | |
1258 void onClassResolved(ClassElement cls) { | |
1259 if (mirrorSystemClass == cls) { | |
1260 mirrorSystemGetNameFunction = | |
1261 cls.lookupLocalMember('getName'); | |
1262 } else if (symbolClass == cls) { | |
1263 symbolConstructor = cls.constructors.head; | |
1264 } else if (symbolImplementationClass == cls) { | |
1265 symbolValidatedConstructor = symbolImplementationClass.lookupConstructor( | |
1266 symbolValidatedConstructorSelector); | |
1267 } else if (mirrorsUsedClass == cls) { | |
1268 mirrorsUsedConstructor = cls.constructors.head; | |
1269 } else if (intClass == cls) { | |
1270 intEnvironment = intClass.lookupConstructor(fromEnvironmentSelector); | |
1271 } else if (stringClass == cls) { | |
1272 stringEnvironment = | |
1273 stringClass.lookupConstructor(fromEnvironmentSelector); | |
1274 } else if (boolClass == cls) { | |
1275 boolEnvironment = boolClass.lookupConstructor(fromEnvironmentSelector); | |
1276 } | |
1277 } | |
1278 | |
1279 void initializeCoreClasses() { | |
1280 final List missingCoreClasses = []; | |
1281 ClassElement lookupCoreClass(String name) { | |
1282 ClassElement result = coreLibrary.find(name); | |
1283 if (result == null) { | |
1284 missingCoreClasses.add(name); | |
1285 } | |
1286 return result; | |
1287 } | |
1288 objectClass = lookupCoreClass('Object'); | |
1289 boolClass = lookupCoreClass('bool'); | |
1290 numClass = lookupCoreClass('num'); | |
1291 intClass = lookupCoreClass('int'); | |
1292 doubleClass = lookupCoreClass('double'); | |
1293 stringClass = lookupCoreClass('String'); | |
1294 functionClass = lookupCoreClass('Function'); | |
1295 listClass = lookupCoreClass('List'); | |
1296 typeClass = lookupCoreClass('Type'); | |
1297 mapClass = lookupCoreClass('Map'); | |
1298 nullClass = lookupCoreClass('Null'); | |
1299 stackTraceClass = lookupCoreClass('StackTrace'); | |
1300 iterableClass = lookupCoreClass('Iterable'); | |
1301 symbolClass = lookupCoreClass('Symbol'); | |
1302 if (!missingCoreClasses.isEmpty) { | |
1303 internalError(coreLibrary, | |
1304 'dart:core library does not contain required classes: ' | |
1305 '$missingCoreClasses'); | |
1306 } | |
1307 } | |
1308 | |
1309 Element _unnamedListConstructor; | |
1310 Element get unnamedListConstructor { | |
1311 if (_unnamedListConstructor != null) return _unnamedListConstructor; | |
1312 Selector callConstructor = new Selector.callConstructor( | |
1313 "", listClass.library); | |
1314 return _unnamedListConstructor = | |
1315 listClass.lookupConstructor(callConstructor); | |
1316 } | |
1317 | |
1318 Element _filledListConstructor; | |
1319 Element get filledListConstructor { | |
1320 if (_filledListConstructor != null) return _filledListConstructor; | |
1321 Selector callConstructor = new Selector.callConstructor( | |
1322 "filled", listClass.library); | |
1323 return _filledListConstructor = | |
1324 listClass.lookupConstructor(callConstructor); | |
1325 } | |
1326 | |
1327 /** | |
1328 * Get an [Uri] pointing to a patch for the dart: library with | |
1329 * the given path. Returns null if there is no patch. | |
1330 */ | |
1331 Uri resolvePatchUri(String dartLibraryPath); | |
1332 | |
1333 Future runCompiler(Uri uri) { | |
1334 // TODO(ahe): This prevents memory leaks when invoking the compiler | |
1335 // multiple times. Implement a better mechanism where we can store | |
1336 // such caches in the compiler and get access to them through a | |
1337 // suitably maintained static reference to the current compiler. | |
1338 StringToken.canonicalizedSubstrings.clear(); | |
1339 Selector.canonicalizedValues.clear(); | |
1340 TypedSelector.canonicalizedValues.clear(); | |
1341 | |
1342 assert(uri != null || analyzeOnly || hasIncrementalSupport); | |
1343 return new Future.sync(() { | |
1344 if (librariesToAnalyzeWhenRun != null) { | |
1345 return Future.forEach(librariesToAnalyzeWhenRun, (libraryUri) { | |
1346 log('Analyzing $libraryUri ($buildId)'); | |
1347 return libraryLoader.loadLibrary(libraryUri); | |
1348 }); | |
1349 } | |
1350 }).then((_) { | |
1351 if (uri != null) { | |
1352 if (analyzeOnly) { | |
1353 log('Analyzing $uri ($buildId)'); | |
1354 } else { | |
1355 log('Compiling $uri ($buildId)'); | |
1356 } | |
1357 return libraryLoader.loadLibrary(uri).then((LibraryElement library) { | |
1358 mainApp = library; | |
1359 }); | |
1360 } | |
1361 }).then((_) { | |
1362 if (!compilationFailed) { | |
1363 // TODO(johnniwinther): Reenable analysis of programs with load failures | |
1364 // when these are handled as erroneous libraries/compilation units. | |
1365 compileLoadedLibraries(); | |
1366 } | |
1367 }); | |
1368 } | |
1369 | |
1370 void computeMain() { | |
1371 if (mainApp == null) return; | |
1372 | |
1373 Element main = mainApp.findExported(MAIN); | |
1374 ErroneousElement errorElement = null; | |
1375 if (main == null) { | |
1376 if (analyzeOnly) { | |
1377 if (!analyzeAll) { | |
1378 errorElement = new ErroneousElementX( | |
1379 MessageKind.CONSIDER_ANALYZE_ALL, {'main': MAIN}, MAIN, mainApp); | |
1380 } | |
1381 } else { | |
1382 // Compilation requires a main method. | |
1383 errorElement = new ErroneousElementX( | |
1384 MessageKind.MISSING_MAIN, {'main': MAIN}, MAIN, mainApp); | |
1385 } | |
1386 mainFunction = backend.helperForMissingMain(); | |
1387 } else if (main.isErroneous && main.isSynthesized) { | |
1388 if (main is ErroneousElement) { | |
1389 errorElement = main; | |
1390 } else { | |
1391 internalError(main, 'Problem with $MAIN.'); | |
1392 } | |
1393 mainFunction = backend.helperForBadMain(); | |
1394 } else if (!main.isFunction) { | |
1395 errorElement = new ErroneousElementX( | |
1396 MessageKind.MAIN_NOT_A_FUNCTION, {'main': MAIN}, MAIN, main); | |
1397 mainFunction = backend.helperForBadMain(); | |
1398 } else { | |
1399 mainFunction = main; | |
1400 FunctionSignature parameters = mainFunction.computeSignature(this); | |
1401 if (parameters.requiredParameterCount > 2) { | |
1402 int index = 0; | |
1403 parameters.orderedForEachParameter((Element parameter) { | |
1404 if (index++ < 2) return; | |
1405 errorElement = new ErroneousElementX( | |
1406 MessageKind.MAIN_WITH_EXTRA_PARAMETER, {'main': MAIN}, MAIN, | |
1407 parameter); | |
1408 mainFunction = backend.helperForMainArity(); | |
1409 // Don't warn about main not being used: | |
1410 enqueuer.resolution.registerStaticUse(main); | |
1411 }); | |
1412 } | |
1413 } | |
1414 if (mainFunction == null) { | |
1415 if (errorElement == null && !analyzeOnly && !analyzeAll) { | |
1416 internalError(mainApp, "Problem with '$MAIN'."); | |
1417 } else { | |
1418 mainFunction = errorElement; | |
1419 } | |
1420 } | |
1421 if (errorElement != null && errorElement.isSynthesized) { | |
1422 reportWarning( | |
1423 errorElement, errorElement.messageKind, | |
1424 errorElement.messageArguments); | |
1425 } | |
1426 } | |
1427 | |
1428 /// Performs the compilation when all libraries have been loaded. | |
1429 void compileLoadedLibraries() { | |
1430 computeMain(); | |
1431 | |
1432 mirrorUsageAnalyzerTask.analyzeUsage(mainApp); | |
1433 | |
1434 // In order to see if a library is deferred, we must compute the | |
1435 // compile-time constants that are metadata. This means adding | |
1436 // something to the resolution queue. So we cannot wait with | |
1437 // this until after the resolution queue is processed. | |
1438 deferredLoadTask.ensureMetadataResolved(this); | |
1439 | |
1440 phase = PHASE_RESOLVING; | |
1441 if (analyzeAll) { | |
1442 libraryLoader.libraries.forEach((LibraryElement library) { | |
1443 log('Enqueuing ${library.canonicalUri}'); | |
1444 fullyEnqueueLibrary(library, enqueuer.resolution); | |
1445 }); | |
1446 } else if (analyzeMain && mainApp != null) { | |
1447 fullyEnqueueLibrary(mainApp, enqueuer.resolution); | |
1448 } | |
1449 // Elements required by enqueueHelpers are global dependencies | |
1450 // that are not pulled in by a particular element. | |
1451 backend.enqueueHelpers(enqueuer.resolution, globalDependencies); | |
1452 resolveLibraryMetadata(); | |
1453 log('Resolving...'); | |
1454 processQueue(enqueuer.resolution, mainFunction); | |
1455 enqueuer.resolution.logSummary(log); | |
1456 | |
1457 if (compilationFailed) return; | |
1458 if (!showPackageWarnings && !suppressWarnings) { | |
1459 suppressedWarnings.forEach((Uri uri, SuppressionInfo info) { | |
1460 MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS; | |
1461 if (info.warnings == 0) { | |
1462 kind = MessageKind.HIDDEN_HINTS; | |
1463 } else if (info.hints == 0) { | |
1464 kind = MessageKind.HIDDEN_WARNINGS; | |
1465 } | |
1466 reportDiagnostic(null, | |
1467 kind.message({'warnings': info.warnings, | |
1468 'hints': info.hints, | |
1469 'uri': uri}, | |
1470 terseDiagnostics), | |
1471 api.Diagnostic.HINT); | |
1472 }); | |
1473 } | |
1474 if (analyzeOnly) { | |
1475 if (!analyzeAll) { | |
1476 // No point in reporting unused code when [analyzeAll] is true: all | |
1477 // code is artificially used. | |
1478 reportUnusedCode(); | |
1479 } | |
1480 return; | |
1481 } | |
1482 assert(mainFunction != null); | |
1483 phase = PHASE_DONE_RESOLVING; | |
1484 | |
1485 world.populate(); | |
1486 // Compute whole-program-knowledge that the backend needs. (This might | |
1487 // require the information computed in [world.populate].) | |
1488 backend.onResolutionComplete(); | |
1489 | |
1490 deferredLoadTask.onResolutionComplete(mainFunction); | |
1491 | |
1492 log('Building IR...'); | |
1493 irBuilder.buildNodes(); | |
1494 | |
1495 log('Inferring types...'); | |
1496 typesTask.onResolutionComplete(mainFunction); | |
1497 | |
1498 if (stopAfterTypeInference) return; | |
1499 | |
1500 log('Compiling...'); | |
1501 phase = PHASE_COMPILING; | |
1502 // TODO(johnniwinther): Move these to [CodegenEnqueuer]. | |
1503 if (hasIsolateSupport) { | |
1504 backend.enableIsolateSupport(enqueuer.codegen); | |
1505 } | |
1506 if (enabledNoSuchMethod) { | |
1507 backend.enableNoSuchMethod(null, enqueuer.codegen); | |
1508 } | |
1509 if (compileAll) { | |
1510 libraryLoader.libraries.forEach((LibraryElement library) { | |
1511 fullyEnqueueLibrary(library, enqueuer.codegen); | |
1512 }); | |
1513 } | |
1514 processQueue(enqueuer.codegen, mainFunction); | |
1515 enqueuer.codegen.logSummary(log); | |
1516 | |
1517 if (compilationFailed) return; | |
1518 | |
1519 backend.assembleProgram(); | |
1520 | |
1521 if (dumpInfo) { | |
1522 dumpInfoTask.dumpInfo(); | |
1523 } | |
1524 | |
1525 checkQueues(); | |
1526 | |
1527 if (compilationFailed) { | |
1528 assembledCode = null; // Signals failure. | |
1529 } | |
1530 } | |
1531 | |
1532 void fullyEnqueueLibrary(LibraryElement library, Enqueuer world) { | |
1533 void enqueueAll(Element element) { | |
1534 fullyEnqueueTopLevelElement(element, world); | |
1535 } | |
1536 library.implementation.forEachLocalMember(enqueueAll); | |
1537 } | |
1538 | |
1539 void fullyEnqueueTopLevelElement(Element element, Enqueuer world) { | |
1540 if (element.isClass) { | |
1541 ClassElement cls = element; | |
1542 cls.ensureResolved(this); | |
1543 cls.forEachLocalMember(enqueuer.resolution.addToWorkList); | |
1544 world.registerInstantiatedClass(element, globalDependencies); | |
1545 } else { | |
1546 world.addToWorkList(element); | |
1547 } | |
1548 } | |
1549 | |
1550 // Resolves metadata on library elements. This is necessary in order to | |
1551 // resolve metadata classes referenced only from metadata on library tags. | |
1552 // TODO(ahe): Figure out how to do this lazily. | |
1553 void resolveLibraryMetadata() { | |
1554 for (LibraryElement library in libraryLoader.libraries) { | |
1555 if (library.metadata != null) { | |
1556 for (MetadataAnnotation metadata in library.metadata) { | |
1557 metadata.ensureResolved(this); | |
1558 } | |
1559 } | |
1560 } | |
1561 } | |
1562 | |
1563 void processQueue(Enqueuer world, Element main) { | |
1564 world.nativeEnqueuer.processNativeClasses(libraryLoader.libraries); | |
1565 if (main != null && !main.isErroneous) { | |
1566 FunctionElement mainMethod = main; | |
1567 if (mainMethod.computeSignature(this).parameterCount != 0) { | |
1568 // The first argument could be a list of strings. | |
1569 world.registerInstantiatedClass( | |
1570 backend.listImplementation, globalDependencies); | |
1571 world.registerInstantiatedClass( | |
1572 backend.stringImplementation, globalDependencies); | |
1573 | |
1574 backend.registerMainHasArguments(world); | |
1575 } | |
1576 world.addToWorkList(main); | |
1577 } | |
1578 if (verbose) { | |
1579 progress.reset(); | |
1580 } | |
1581 world.forEach((WorkItem work) { | |
1582 withCurrentElement(work.element, () => work.run(this, world)); | |
1583 }); | |
1584 world.queueIsClosed = true; | |
1585 if (compilationFailed) return; | |
1586 assert(world.checkNoEnqueuedInvokedInstanceMethods()); | |
1587 } | |
1588 | |
1589 /** | |
1590 * Perform various checks of the queues. This includes checking that | |
1591 * the queues are empty (nothing was added after we stopped | |
1592 * processing the queues). Also compute the number of methods that | |
1593 * were resolved, but not compiled (aka excess resolution). | |
1594 */ | |
1595 checkQueues() { | |
1596 for (Enqueuer world in [enqueuer.resolution, enqueuer.codegen]) { | |
1597 world.forEach((WorkItem work) { | |
1598 internalError(work.element, "Work list is not empty."); | |
1599 }); | |
1600 } | |
1601 if (!REPORT_EXCESS_RESOLUTION) return; | |
1602 var resolved = new Set.from(enqueuer.resolution.resolvedElements); | |
1603 for (Element e in enqueuer.codegen.generatedCode.keys) { | |
1604 resolved.remove(e); | |
1605 } | |
1606 for (Element e in new Set.from(resolved)) { | |
1607 if (e.isClass || | |
1608 e.isField || | |
1609 e.isTypeVariable || | |
1610 e.isTypedef || | |
1611 identical(e.kind, ElementKind.ABSTRACT_FIELD)) { | |
1612 resolved.remove(e); | |
1613 } | |
1614 if (identical(e.kind, ElementKind.GENERATIVE_CONSTRUCTOR)) { | |
1615 ClassElement enclosingClass = e.enclosingClass; | |
1616 resolved.remove(e); | |
1617 | |
1618 } | |
1619 if (backend.isBackendLibrary(e.library)) { | |
1620 resolved.remove(e); | |
1621 } | |
1622 } | |
1623 log('Excess resolution work: ${resolved.length}.'); | |
1624 for (Element e in resolved) { | |
1625 reportWarning(e, | |
1626 MessageKind.GENERIC, | |
1627 {'text': 'Warning: $e resolved but not compiled.'}); | |
1628 } | |
1629 } | |
1630 | |
1631 void analyzeElement(Element element) { | |
1632 assert(invariant(element, | |
1633 element.impliesType || | |
1634 element.isField || | |
1635 element.isFunction || | |
1636 element.isGenerativeConstructor || | |
1637 element.isGetter || | |
1638 element.isSetter, | |
1639 message: 'Unexpected element kind: ${element.kind}')); | |
1640 assert(invariant(element, element is AnalyzableElement, | |
1641 message: 'Element $element is not analyzable.')); | |
1642 assert(invariant(element, element.isDeclaration)); | |
1643 ResolutionEnqueuer world = enqueuer.resolution; | |
1644 if (world.hasBeenResolved(element)) return; | |
1645 assert(parser != null); | |
1646 Node tree = parser.parse(element); | |
1647 assert(invariant(element, !element.isSynthesized || tree == null)); | |
1648 TreeElements elements = resolver.resolve(element); | |
1649 if (elements != null) { | |
1650 if (tree != null && !analyzeSignaturesOnly && | |
1651 !suppressWarnings) { | |
1652 // Only analyze nodes with a corresponding [TreeElements]. | |
1653 checker.check(elements); | |
1654 } | |
1655 world.registerResolvedElement(element); | |
1656 } | |
1657 } | |
1658 | |
1659 void analyze(ResolutionWorkItem work, ResolutionEnqueuer world) { | |
1660 assert(invariant(work.element, identical(world, enqueuer.resolution))); | |
1661 assert(invariant(work.element, !work.isAnalyzed(), | |
1662 message: 'Element ${work.element} has already been analyzed')); | |
1663 if (shouldPrintProgress) { | |
1664 // TODO(ahe): Add structured diagnostics to the compiler API and | |
1665 // use it to separate this from the --verbose option. | |
1666 if (phase == PHASE_RESOLVING) { | |
1667 log('Resolved ${enqueuer.resolution.resolvedElements.length} ' | |
1668 'elements.'); | |
1669 progress.reset(); | |
1670 } | |
1671 } | |
1672 AstElement element = work.element; | |
1673 if (world.hasBeenResolved(element)) return; | |
1674 analyzeElement(element); | |
1675 backend.onElementResolved(element, element.resolvedAst.elements); | |
1676 } | |
1677 | |
1678 void codegen(CodegenWorkItem work, CodegenEnqueuer world) { | |
1679 assert(invariant(work.element, identical(world, enqueuer.codegen))); | |
1680 if (shouldPrintProgress) { | |
1681 // TODO(ahe): Add structured diagnostics to the compiler API and | |
1682 // use it to separate this from the --verbose option. | |
1683 log('Compiled ${enqueuer.codegen.generatedCode.length} methods.'); | |
1684 progress.reset(); | |
1685 } | |
1686 backend.codegen(work); | |
1687 } | |
1688 | |
1689 void reportError(Spannable node, | |
1690 MessageKind messageKind, | |
1691 [Map arguments = const {}]) { | |
1692 reportDiagnosticInternal( | |
1693 node, messageKind, arguments, api.Diagnostic.ERROR); | |
1694 } | |
1695 | |
1696 /** | |
1697 * Reports an error and then aborts the compiler. Avoid using this method. | |
1698 * | |
1699 * In order to support incremental compilation, it is preferable to use | |
1700 * [reportError]. However, care must be taken to leave the compiler in a | |
1701 * consistent state, for example, by creating synthetic erroneous objects. | |
1702 * | |
1703 * If there's absolutely no way to leave the compiler in a consistent state, | |
1704 * calling this method is preferred as it will set [compilerWasCancelled] to | |
1705 * true which alerts the incremental compiler to discard all state and start | |
1706 * a new compiler. Throwing an exception is also better, as this will set | |
1707 * [hasCrashed] which the incremental compiler also listens too (but don't | |
1708 * throw exceptions, it creates a really bad user experience). | |
1709 * | |
1710 * In any case, calling this method is a last resort, as it essentially | |
1711 * breaks the user experience of the incremental compiler. The purpose of the | |
1712 * incremental compiler is to improve developer productivity. Developers | |
1713 * frequently make mistakes, so syntax errors and spelling errors are | |
1714 * considered normal to the incremental compiler. | |
1715 */ | |
1716 void reportFatalError(Spannable node, MessageKind messageKind, | |
1717 [Map arguments = const {}]) { | |
1718 reportError(node, messageKind, arguments); | |
1719 // TODO(ahe): Make this only abort the current method. | |
1720 throw new CompilerCancelledException( | |
1721 'Error: Cannot continue due to previous error.'); | |
1722 } | |
1723 | |
1724 void reportWarning(Spannable node, MessageKind messageKind, | |
1725 [Map arguments = const {}]) { | |
1726 reportDiagnosticInternal( | |
1727 node, messageKind, arguments, api.Diagnostic.WARNING); | |
1728 } | |
1729 | |
1730 void reportInfo(Spannable node, MessageKind messageKind, | |
1731 [Map arguments = const {}]) { | |
1732 reportDiagnosticInternal(node, messageKind, arguments, api.Diagnostic.INFO); | |
1733 } | |
1734 | |
1735 void reportHint(Spannable node, MessageKind messageKind, | |
1736 [Map arguments = const {}]) { | |
1737 reportDiagnosticInternal(node, messageKind, arguments, api.Diagnostic.HINT); | |
1738 } | |
1739 | |
1740 void reportDiagnosticInternal(Spannable node, | |
1741 MessageKind messageKind, | |
1742 Map arguments, | |
1743 api.Diagnostic kind) { | |
1744 if (!showPackageWarnings && node != NO_LOCATION_SPANNABLE) { | |
1745 switch (kind) { | |
1746 case api.Diagnostic.WARNING: | |
1747 case api.Diagnostic.HINT: | |
1748 Element element = elementFromSpannable(node); | |
1749 if (!inUserCode(element, assumeInUserCode: true)) { | |
1750 Uri uri = getCanonicalUri(element); | |
1751 SuppressionInfo info = | |
1752 suppressedWarnings.putIfAbsent(uri, () => new SuppressionInfo()); | |
1753 if (kind == api.Diagnostic.WARNING) { | |
1754 info.warnings++; | |
1755 } else { | |
1756 info.hints++; | |
1757 } | |
1758 lastDiagnosticWasFiltered = true; | |
1759 return; | |
1760 } | |
1761 break; | |
1762 case api.Diagnostic.INFO: | |
1763 if (lastDiagnosticWasFiltered) { | |
1764 return; | |
1765 } | |
1766 break; | |
1767 } | |
1768 } | |
1769 lastDiagnosticWasFiltered = false; | |
1770 reportDiagnostic( | |
1771 node, messageKind.message(arguments, terseDiagnostics), kind); | |
1772 } | |
1773 | |
1774 void reportDiagnostic(Spannable span, | |
1775 Message message, | |
1776 api.Diagnostic kind); | |
1777 | |
1778 void reportAssertionFailure(SpannableAssertionFailure ex) { | |
1779 String message = (ex.message != null) ? tryToString(ex.message) | |
1780 : tryToString(ex); | |
1781 SourceSpan span = spanFromSpannable(ex.node); | |
1782 reportDiagnosticInternal( | |
1783 ex.node, MessageKind.GENERIC, {'text': message}, api.Diagnostic.CRASH); | |
1784 } | |
1785 | |
1786 SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { | |
1787 if (begin == null || end == null) { | |
1788 // TODO(ahe): We can almost always do better. Often it is only | |
1789 // end that is null. Otherwise, we probably know the current | |
1790 // URI. | |
1791 throw 'Cannot find tokens to produce error message.'; | |
1792 } | |
1793 if (uri == null && currentElement != null) { | |
1794 uri = currentElement.compilationUnit.script.readableUri; | |
1795 } | |
1796 return SourceSpan.withCharacterOffsets(begin, end, | |
1797 (beginOffset, endOffset) => new SourceSpan(uri, beginOffset, endOffset)); | |
1798 } | |
1799 | |
1800 SourceSpan spanFromNode(Node node) { | |
1801 return spanFromTokens(node.getBeginToken(), node.getEndToken()); | |
1802 } | |
1803 | |
1804 SourceSpan spanFromElement(Element element) { | |
1805 while (element != null && element.isSynthesized) { | |
1806 element = element.enclosingElement; | |
1807 } | |
1808 if (element != null && | |
1809 element.position == null && | |
1810 !element.isLibrary && | |
1811 !element.isCompilationUnit) { | |
1812 // Sometimes, the backend fakes up elements that have no | |
1813 // position. So we use the enclosing element instead. It is | |
1814 // not a good error location, but cancel really is "internal | |
1815 // error" or "not implemented yet", so the vicinity is good | |
1816 // enough for now. | |
1817 element = element.enclosingElement; | |
1818 // TODO(ahe): I plan to overhaul this infrastructure anyways. | |
1819 } | |
1820 if (element == null) { | |
1821 element = currentElement; | |
1822 } | |
1823 Token position = element.position; | |
1824 Uri uri = element.compilationUnit.script.readableUri; | |
1825 return (position == null) | |
1826 ? new SourceSpan(uri, 0, 0) | |
1827 : spanFromTokens(position, position, uri); | |
1828 } | |
1829 | |
1830 SourceSpan spanFromHInstruction(HInstruction instruction) { | |
1831 Element element = _elementFromHInstruction(instruction); | |
1832 if (element == null) element = currentElement; | |
1833 var position = instruction.sourcePosition; | |
1834 if (position == null) return spanFromElement(element); | |
1835 Token token = position.token; | |
1836 if (token == null) return spanFromElement(element); | |
1837 Uri uri = element.compilationUnit.script.readableUri; | |
1838 return spanFromTokens(token, token, uri); | |
1839 } | |
1840 | |
1841 /** | |
1842 * Translates the [resolvedUri] into a readable URI. | |
1843 * | |
1844 * The [importingLibrary] holds the library importing [resolvedUri] or | |
1845 * [:null:] if [resolvedUri] is loaded as the main library. The | |
1846 * [importingLibrary] is used to grant access to internal libraries from | |
1847 * platform libraries and patch libraries. | |
1848 * | |
1849 * If the [resolvedUri] is not accessible from [importingLibrary], this method | |
1850 * is responsible for reporting errors. | |
1851 * | |
1852 * See [LibraryLoader] for terminology on URIs. | |
1853 */ | |
1854 Uri translateResolvedUri(LibraryElement importingLibrary, | |
1855 Uri resolvedUri, Node node) { | |
1856 unimplemented(importingLibrary, 'Compiler.translateResolvedUri'); | |
1857 return null; | |
1858 } | |
1859 | |
1860 /** | |
1861 * Reads the script specified by the [readableUri]. | |
1862 * | |
1863 * See [LibraryLoader] for terminology on URIs. | |
1864 */ | |
1865 Future<Script> readScript(Spannable node, Uri readableUri) { | |
1866 unimplemented(node, 'Compiler.readScript'); | |
1867 return null; | |
1868 } | |
1869 | |
1870 Element lookupElementIn(ScopeContainerElement container, String name) { | |
1871 Element element = container.localLookup(name); | |
1872 if (element == null) { | |
1873 throw 'Could not find $name in $container'; | |
1874 } | |
1875 return element; | |
1876 } | |
1877 | |
1878 bool get isMockCompilation => false; | |
1879 | |
1880 Token processAndStripComments(Token currentToken) { | |
1881 Token firstToken = currentToken; | |
1882 Token prevToken; | |
1883 while (currentToken.kind != EOF_TOKEN) { | |
1884 if (identical(currentToken.kind, COMMENT_TOKEN)) { | |
1885 Token firstCommentToken = currentToken; | |
1886 while (identical(currentToken.kind, COMMENT_TOKEN)) { | |
1887 currentToken = currentToken.next; | |
1888 } | |
1889 commentMap[currentToken] = firstCommentToken; | |
1890 if (prevToken == null) { | |
1891 firstToken = currentToken; | |
1892 } else { | |
1893 prevToken.next = currentToken; | |
1894 } | |
1895 } | |
1896 prevToken = currentToken; | |
1897 currentToken = currentToken.next; | |
1898 } | |
1899 return firstToken; | |
1900 } | |
1901 | |
1902 void reportUnusedCode() { | |
1903 void checkLive(member) { | |
1904 if (member.isFunction) { | |
1905 if (!enqueuer.resolution.hasBeenResolved(member)) { | |
1906 reportHint(member, MessageKind.UNUSED_METHOD, | |
1907 {'name': member.name}); | |
1908 } | |
1909 } else if (member.isClass) { | |
1910 if (!member.isResolved) { | |
1911 reportHint(member, MessageKind.UNUSED_CLASS, | |
1912 {'name': member.name}); | |
1913 } else { | |
1914 member.forEachLocalMember(checkLive); | |
1915 } | |
1916 } else if (member.isTypedef) { | |
1917 if (!member.isResolved) { | |
1918 reportHint(member, MessageKind.UNUSED_TYPEDEF, | |
1919 {'name': member.name}); | |
1920 } | |
1921 } | |
1922 } | |
1923 libraryLoader.libraries.forEach((LibraryElement library) { | |
1924 // TODO(ahe): Implement better heuristics to discover entry points of | |
1925 // packages and use that to discover unused implementation details in | |
1926 // packages. | |
1927 if (library.isPlatformLibrary || library.isPackageLibrary) return; | |
1928 library.compilationUnits.forEach((unit) { | |
1929 unit.forEachLocalMember(checkLive); | |
1930 }); | |
1931 }); | |
1932 } | |
1933 | |
1934 /// Helper for determining whether the current element is declared within | |
1935 /// 'user code'. | |
1936 /// | |
1937 /// See [inUserCode] for what defines 'user code'. | |
1938 bool currentlyInUserCode() { | |
1939 return inUserCode(currentElement); | |
1940 } | |
1941 | |
1942 /// Helper for determining whether [element] is declared within 'user code'. | |
1943 /// | |
1944 /// What constitutes 'user code' is defined by the URI(s) provided by the | |
1945 /// entry point(s) of compilation or analysis: | |
1946 /// | |
1947 /// If an entrypoint URI uses the 'package' scheme then every library from | |
1948 /// that same package is considered to be in user code. For instance, if | |
1949 /// an entry point URI is 'package:foo/bar.dart' then every library whose | |
1950 /// canonical URI starts with 'package:foo/' is in user code. | |
1951 /// | |
1952 /// If an entrypoint URI uses another scheme than 'package' then every library | |
1953 /// with that scheme is in user code. For instance, an entry point URI is | |
1954 /// 'file:///foo.dart' then every library whose canonical URI scheme is | |
1955 /// 'file' is in user code. | |
1956 /// | |
1957 /// If [assumeInUserCode] is `true`, [element] is assumed to be in user code | |
1958 /// if no entrypoints have been set. | |
1959 bool inUserCode(Element element, {bool assumeInUserCode: false}) { | |
1960 List<Uri> entrypoints = <Uri>[]; | |
1961 if (mainApp != null) { | |
1962 entrypoints.add(mainApp.canonicalUri); | |
1963 } | |
1964 if (librariesToAnalyzeWhenRun != null) { | |
1965 entrypoints.addAll(librariesToAnalyzeWhenRun); | |
1966 } | |
1967 if (entrypoints.isEmpty && assumeInUserCode) { | |
1968 // Assume in user code since [mainApp] has not been set yet. | |
1969 return true; | |
1970 } | |
1971 if (element == null) return false; | |
1972 Uri libraryUri = element.library.canonicalUri; | |
1973 if (libraryUri.scheme == 'package') { | |
1974 for (Uri uri in entrypoints) { | |
1975 if (uri.scheme != 'package') continue; | |
1976 int slashPos = libraryUri.path.indexOf('/'); | |
1977 if (slashPos != -1) { | |
1978 String packageName = libraryUri.path.substring(0, slashPos + 1); | |
1979 if (uri.path.startsWith(packageName)) { | |
1980 return true; | |
1981 } | |
1982 } else { | |
1983 if (libraryUri.path == uri.path) { | |
1984 return true; | |
1985 } | |
1986 } | |
1987 } | |
1988 } else { | |
1989 for (Uri uri in entrypoints) { | |
1990 if (libraryUri.scheme == uri.scheme) return true; | |
1991 } | |
1992 } | |
1993 return false; | |
1994 } | |
1995 | |
1996 /// Return a canonical URI for the source of [element]. | |
1997 /// | |
1998 /// For a package library with canonical URI 'package:foo/bar/baz.dart' the | |
1999 /// return URI is 'package:foo'. For non-package libraries the returned URI is | |
2000 /// the canonical URI of the library itself. | |
2001 Uri getCanonicalUri(Element element) { | |
2002 if (element == null) return null; | |
2003 Uri libraryUri = element.library.canonicalUri; | |
2004 if (libraryUri.scheme == 'package') { | |
2005 int slashPos = libraryUri.path.indexOf('/'); | |
2006 if (slashPos != -1) { | |
2007 String packageName = libraryUri.path.substring(0, slashPos); | |
2008 return new Uri(scheme: 'package', path: packageName); | |
2009 } | |
2010 } | |
2011 return libraryUri; | |
2012 } | |
2013 | |
2014 void diagnoseCrashInUserCode(String message, exception, stackTrace) { | |
2015 // Overridden by Compiler in apiimpl.dart. | |
2016 } | |
2017 | |
2018 void forgetElement(Element element) { | |
2019 enqueuer.forgetElement(element); | |
2020 if (element is MemberElement) { | |
2021 for (Element closure in element.nestedClosures) { | |
2022 // TODO(ahe): It would be nice to reuse names of nested closures. | |
2023 closureToClassMapper.forgetElement(closure); | |
2024 } | |
2025 } | |
2026 backend.forgetElement(element); | |
2027 } | |
2028 } | |
2029 | |
2030 class CompilerTask { | |
2031 final Compiler compiler; | |
2032 final Stopwatch watch; | |
2033 UserTag profilerTag; | |
2034 | |
2035 CompilerTask(Compiler compiler) | |
2036 : this.compiler = compiler, | |
2037 watch = (compiler.verbose) ? new Stopwatch() : null; | |
2038 | |
2039 String get name => 'Unknown task'; | |
2040 int get timing => (watch != null) ? watch.elapsedMilliseconds : 0; | |
2041 | |
2042 int get timingMicroseconds => (watch != null) ? watch.elapsedMicroseconds : 0; | |
2043 | |
2044 UserTag getProfilerTag() { | |
2045 if (profilerTag == null) profilerTag = new UserTag(name); | |
2046 return profilerTag; | |
2047 } | |
2048 | |
2049 measure(action()) { | |
2050 // In verbose mode when watch != null. | |
2051 if (watch == null) return action(); | |
2052 CompilerTask previous = compiler.measuredTask; | |
2053 if (identical(this, previous)) return action(); | |
2054 compiler.measuredTask = this; | |
2055 if (previous != null) previous.watch.stop(); | |
2056 watch.start(); | |
2057 UserTag oldTag = getProfilerTag().makeCurrent(); | |
2058 try { | |
2059 return action(); | |
2060 } finally { | |
2061 watch.stop(); | |
2062 oldTag.makeCurrent(); | |
2063 if (previous != null) previous.watch.start(); | |
2064 compiler.measuredTask = previous; | |
2065 } | |
2066 } | |
2067 | |
2068 measureElement(Element element, action()) { | |
2069 compiler.withCurrentElement(element, () => measure(action)); | |
2070 } | |
2071 } | |
2072 | |
2073 class CompilerCancelledException implements Exception { | |
2074 final String reason; | |
2075 CompilerCancelledException(this.reason); | |
2076 | |
2077 String toString() { | |
2078 String banner = 'compiler cancelled'; | |
2079 return (reason != null) ? '$banner: $reason' : '$banner'; | |
2080 } | |
2081 } | |
2082 | |
2083 class SourceSpan implements Spannable { | |
2084 final Uri uri; | |
2085 final int begin; | |
2086 final int end; | |
2087 | |
2088 const SourceSpan(this.uri, this.begin, this.end); | |
2089 | |
2090 static withCharacterOffsets(Token begin, Token end, | |
2091 f(int beginOffset, int endOffset)) { | |
2092 final beginOffset = begin.charOffset; | |
2093 final endOffset = end.charOffset + end.charCount; | |
2094 | |
2095 // [begin] and [end] might be the same for the same empty token. This | |
2096 // happens for instance when scanning '$$'. | |
2097 assert(endOffset >= beginOffset); | |
2098 return f(beginOffset, endOffset); | |
2099 } | |
2100 | |
2101 String toString() => 'SourceSpan($uri, $begin, $end)'; | |
2102 } | |
2103 | |
2104 /// Flag that can be used in assertions to assert that a code path is only | |
2105 /// executed as part of development. | |
2106 /// | |
2107 /// This flag is automatically set to true if helper methods like, [debugPrint], | |
2108 /// [debugWrapPrint], [trace], and [reportHere] are called. | |
2109 bool DEBUG_MODE = false; | |
2110 | |
2111 /// Assert that [DEBUG_MODE] is `true` and provide [message] as part of the | |
2112 /// error message. | |
2113 assertDebugMode(String message) { | |
2114 assert(invariant(NO_LOCATION_SPANNABLE, DEBUG_MODE, | |
2115 message: 'Debug mode is not enabled: $message')); | |
2116 } | |
2117 | |
2118 /** | |
2119 * Throws a [SpannableAssertionFailure] if [condition] is | |
2120 * [:false:]. [condition] must be either a [:bool:] or a no-arg | |
2121 * function returning a [:bool:]. | |
2122 * | |
2123 * Use this method to provide better information for assertion by calling | |
2124 * [invariant] as the argument to an [:assert:] statement: | |
2125 * | |
2126 * assert(invariant(position, isValid)); | |
2127 * | |
2128 * [spannable] must be non-null and will be used to provide positional | |
2129 * information in the generated error message. | |
2130 */ | |
2131 bool invariant(Spannable spannable, var condition, {var message: null}) { | |
2132 // TODO(johnniwinther): Use [spannable] and [message] to provide better | |
2133 // information on assertion errors. | |
2134 if (spannable == null) { | |
2135 throw new SpannableAssertionFailure(CURRENT_ELEMENT_SPANNABLE, | |
2136 "Spannable was null for invariant. Use CURRENT_ELEMENT_SPANNABLE."); | |
2137 } | |
2138 if (condition is Function){ | |
2139 condition = condition(); | |
2140 } | |
2141 if (!condition) { | |
2142 if (message is Function) { | |
2143 message = message(); | |
2144 } | |
2145 throw new SpannableAssertionFailure(spannable, message); | |
2146 } | |
2147 return true; | |
2148 } | |
2149 | |
2150 /// Returns `true` when [s] is private if used as an identifier. | |
2151 bool isPrivateName(String s) => !s.isEmpty && s.codeUnitAt(0) == $_; | |
2152 | |
2153 /// A sink that drains into /dev/null. | |
2154 class NullSink implements EventSink<String> { | |
2155 final String name; | |
2156 | |
2157 NullSink(this.name); | |
2158 | |
2159 add(String value) {} | |
2160 | |
2161 void addError(Object error, [StackTrace stackTrace]) {} | |
2162 | |
2163 void close() {} | |
2164 | |
2165 toString() => name; | |
2166 | |
2167 /// Convenience method for getting an [api.CompilerOutputProvider]. | |
2168 static NullSink outputProvider(String name, String extension) { | |
2169 return new NullSink('$name.$extension'); | |
2170 } | |
2171 } | |
2172 | |
2173 /// Information about suppressed warnings and hints for a given library. | |
2174 class SuppressionInfo { | |
2175 int warnings = 0; | |
2176 int hints = 0; | |
2177 } | |
2178 | |
2179 class GenericTask extends CompilerTask { | |
2180 final String name; | |
2181 | |
2182 GenericTask(this.name, Compiler compiler) | |
2183 : super(compiler); | |
2184 } | |
OLD | NEW |