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