Index: sdk/lib/_internal/compiler/implementation/enqueue.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart |
index 796fb92fe5325ac452fc2f7412613d8bd882b602..eff52a963ce4d627bbbe0cf6e210b79d5689c973 100644 |
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart |
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart |
@@ -10,43 +10,6 @@ class EnqueueTask extends CompilerTask { |
final ResolutionEnqueuer resolution; |
final CodegenEnqueuer codegen; |
- /// A reverse map from name to *all* elements with that name, not |
- /// just instance members of instantiated classes. |
- final Map<String, Link<Element>> allElementsByName |
- = new Map<String, Link<Element>>(); |
- |
- void ensureAllElementsByName() { |
- if (!allElementsByName.isEmpty) return; |
- |
- void addMemberByName(Element element) { |
- element = element.declaration; |
- String name = element.name; |
- ScopeContainerElement container = null; |
- if (element.isLibrary) { |
- LibraryElement library = element; |
- container = library; |
- // TODO(ahe): Is this right? Is this necessary? |
- name = library.getLibraryOrScriptName(); |
- } else if (element.isClass) { |
- ClassElement cls = element; |
- cls.ensureResolved(compiler); |
- container = cls; |
- for (var link = cls.computeTypeParameters(compiler); |
- !link.isEmpty; |
- link = link.tail) { |
- addMemberByName(link.head.element); |
- } |
- } |
- allElementsByName[name] = allElementsByName.putIfAbsent( |
- name, () => const Link<Element>()).prepend(element); |
- if (container != null) { |
- container.forEachLocalMember(addMemberByName); |
- } |
- } |
- |
- compiler.libraryLoader.libraries.forEach(addMemberByName); |
- } |
- |
String get name => 'Enqueue'; |
EnqueueTask(Compiler compiler) |
@@ -73,13 +36,17 @@ abstract class Enqueuer { |
final Map<String, Link<Element>> instanceFunctionsByName |
= new Map<String, Link<Element>>(); |
final Set<ClassElement> seenClasses = new Set<ClassElement>(); |
+ Set<ClassElement> recentClasses = new Setlet<ClassElement>(); |
final Universe universe = new Universe(); |
+ static final TRACE_MIRROR_ENQUEUING = |
+ const bool.fromEnvironment("TRACE_MIRROR_ENQUEUING"); |
+ |
bool queueIsClosed = false; |
EnqueueTask task; |
native.NativeEnqueuer nativeEnqueuer; // Set by EnqueueTask |
- bool hasEnqueuedEverything = false; |
+ bool hasEnqueuedReflectiveElements = false; |
bool hasEnqueuedReflectiveStaticFields = false; |
Enqueuer(this.name, this.compiler, this.itemCompilationContextCreator); |
@@ -108,7 +75,8 @@ abstract class Enqueuer { |
*/ |
void internalAddToWorkList(Element element); |
- void registerInstantiatedType(InterfaceType type, Registry registry) { |
+ void registerInstantiatedType(InterfaceType type, Registry registry, |
+ {mirrorUsage: false}) { |
task.measure(() { |
ClassElement cls = type.element; |
registry.registerDependency(cls); |
@@ -119,16 +87,21 @@ abstract class Enqueuer { |
// classes; a native abstract class may have non-abstract subclasses |
// not declared to the program. Instances of these classes are |
// indistinguishable from the abstract class. |
- || cls.isNative) { |
+ || cls.isNative |
+ // Likewise, if this registration comes from the mirror system, |
+ // all bets are off. |
+ // TODO(herhut): Track classes required by mirrors seperately. |
+ || mirrorUsage) { |
universe.instantiatedClasses.add(cls); |
} |
onRegisterInstantiatedClass(cls); |
}); |
} |
- void registerInstantiatedClass(ClassElement cls, Registry registry) { |
+ void registerInstantiatedClass(ClassElement cls, Registry registry, |
+ {mirrorUsage: false}) { |
cls.ensureResolved(compiler); |
- registerInstantiatedType(cls.rawType, registry); |
+ registerInstantiatedType(cls.rawType, registry, mirrorUsage: mirrorUsage); |
} |
bool checkNoEnqueuedInvokedInstanceMethods() { |
@@ -260,6 +233,7 @@ abstract class Enqueuer { |
if (seenClasses.contains(cls)) return; |
seenClasses.add(cls); |
+ recentClasses.add(cls); |
cls.ensureResolved(compiler); |
cls.implementation.forEachMember(processInstantiatedClassMember); |
if (isResolutionQueue) { |
@@ -308,48 +282,159 @@ abstract class Enqueuer { |
}); |
} |
- void pretendElementWasUsed(Element element, Registry registry) { |
- if (!compiler.backend.isNeededForReflection(element)) return; |
- if (Elements.isUnresolved(element)) { |
- // Ignore. |
- } else if (element.isSynthesized |
- && element.library.isPlatformLibrary) { |
- // TODO(ahe): Work-around for http://dartbug.com/11205. |
- } else if (element.isConstructor) { |
- ClassElement cls = element.declaration.enclosingClass; |
- registerInstantiatedType(cls.rawType, registry); |
- registerStaticUse(element.declaration); |
- } else if (element.isClass) { |
- ClassElement cls = element.declaration; |
- registerInstantiatedClass(cls, registry); |
- // Make sure that even abstract classes are considered instantiated. |
- universe.instantiatedClasses.add(cls); |
- } else if (element.impliesType) { |
- // Don't enqueue typedefs, and type variables. |
- } else if (Elements.isStaticOrTopLevel(element)) { |
- registerStaticUse(element.declaration); |
- } else if (element.isInstanceMember) { |
- Selector selector = new Selector.fromElement(element, compiler); |
- registerSelectorUse(selector); |
- if (element.isField) { |
- Selector selector = |
- new Selector.setter(element.name, element.library); |
- registerInvokedSetter(selector); |
+ /** |
+ * Decides whether an element should be included to satisfy requirements |
+ * of the mirror system. [includedEnclosing] provides a hint whether the |
+ * enclosing element was included. |
+ * |
+ * The actual implementation depends on the current compiler phase. |
+ */ |
+ bool shouldIncludeElementDueToMirrors(Element element, |
+ {bool includedEnclosing}); |
+ |
+ void logEnqueueReflectiveAction(action, [msg = ""]) { |
+ if (TRACE_MIRROR_ENQUEUING) { |
+ print("MIRROR_ENQUEUE (${isResolutionQueue ? "R" : "C"}): $action $msg"); |
+ } |
+ } |
+ |
+ /// Enqeue the constructor [ctor] if it is required for reflection. |
+ /// |
+ /// [enclosingWasIncluded] provides a hint whether the enclosing element was |
+ /// needed for reflection. |
+ void enqueueReflectiveConstructor(ConstructorElement ctor, |
+ bool enclosingWasIncluded) { |
+ if (shouldIncludeElementDueToMirrors(ctor, |
+ includedEnclosing: enclosingWasIncluded)) { |
+ logEnqueueReflectiveAction(ctor); |
+ ClassElement cls = ctor.declaration.enclosingClass; |
+ registerInstantiatedType(cls.rawType, compiler.mirrorDependencies, |
+ mirrorUsage: true); |
+ registerStaticUse(ctor.declaration); |
+ } |
+ } |
+ |
+ /// Enqeue the member [element] if it is required for reflection. |
+ /// |
+ /// [enclosingWasIncluded] provides a hint whether the enclosing element was |
+ /// needed for reflection. |
+ void enqueueReflectiveMember(Element element, bool enclosingWasIncluded) { |
+ if (shouldIncludeElementDueToMirrors(element, |
+ includedEnclosing: enclosingWasIncluded) |
+ // Do not enqueue typedefs. |
+ && !element.impliesType) { |
+ logEnqueueReflectiveAction(element); |
+ if (Elements.isStaticOrTopLevel(element)) { |
+ registerStaticUse(element.declaration); |
+ } else if (element.isInstanceMember) { |
+ // We need to enqueue all members matching this one in subclasses, as |
+ // well. |
+ // TODO(herhut): Use TypedSelector.subtype for enqueueing |
+ Selector selector = new Selector.fromElement(element, compiler); |
+ registerSelectorUse(selector); |
+ if (element.isField) { |
+ Selector selector = |
+ new Selector.setter(element.name, element.library); |
+ registerInvokedSetter(selector); |
+ } |
} |
} |
} |
- void enqueueEverything() { |
- if (hasEnqueuedEverything) return; |
- compiler.log('Enqueuing everything'); |
- task.ensureAllElementsByName(); |
- for (Link link in task.allElementsByName.values) { |
- for (Element element in link) { |
- pretendElementWasUsed(element, compiler.mirrorDependencies); |
+ /// Enqeue the member [element] if it is required for reflection. |
+ /// |
+ /// [enclosingWasIncluded] provides a hint whether the enclosing element was |
+ /// needed for reflection. |
+ void enqueueReflectiveElementsInClass(ClassElement cls, |
+ Iterable<ClassElement> recents, |
+ bool enclosingWasIncluded) { |
+ if (cls.library.isInternalLibrary || cls.isInjected) return; |
+ bool includeClass = shouldIncludeElementDueToMirrors(cls, |
+ includedEnclosing: enclosingWasIncluded); |
+ if (includeClass) { |
+ logEnqueueReflectiveAction(cls, "register"); |
+ ClassElement decl = cls.declaration; |
+ registerInstantiatedClass(decl, compiler.mirrorDependencies, |
+ mirrorUsage: true); |
+ } |
+ // If the class is never instantiated, we know nothing of it can possibly |
+ // be reflected upon. |
+ // TODO(herhut): Add a warning if a mirrors annotation cannot hit. |
+ if (recents.contains(cls.declaration)) { |
+ logEnqueueReflectiveAction(cls, "members"); |
+ cls.constructors.forEach((Element element) { |
+ enqueueReflectiveConstructor(element, includeClass); |
+ }); |
+ cls.forEachClassMember((Member member) { |
+ enqueueReflectiveMember(member.element, includeClass); |
+ }); |
+ } |
+ } |
+ |
+ /// Enqeue special classes that might not be visible by normal means or that |
+ /// would not normally be enqueued: |
+ /// |
+ /// [Closure] is treated specially as it is the superclass of all closures. |
+ /// Although it is in an internal library, we mark it as reflectable. Note |
+ /// that none of its methods are reflectable, unless reflectable by |
+ /// inheritance. |
+ void enqueueReflectiveSpecialClasses() { |
+ Iterable<ClassElement> classes = |
+ compiler.backend.classesRequiredForReflection; |
+ for (ClassElement cls in classes) { |
+ if (compiler.backend.referencedFromMirrorSystem(cls)) { |
+ logEnqueueReflectiveAction(cls); |
+ registerInstantiatedClass(cls, compiler.mirrorDependencies, |
+ mirrorUsage: true); |
} |
} |
- hasEnqueuedEverything = true; |
- hasEnqueuedReflectiveStaticFields = true; |
+ } |
+ |
+ /// Enqeue all local members of the library [lib] if they are required for |
+ /// reflection. |
+ void enqueueReflectiveElementsInLibrary(LibraryElement lib, |
+ Iterable<ClassElement> recents) { |
+ bool includeLibrary = shouldIncludeElementDueToMirrors(lib, |
+ includedEnclosing: false); |
+ lib.forEachLocalMember((Element member) { |
+ if (member.isClass) { |
+ enqueueReflectiveElementsInClass(member, recents, includeLibrary); |
+ } else { |
+ enqueueReflectiveMember(member, includeLibrary); |
+ } |
+ }); |
+ } |
+ |
+ /// Enqueue all elements that are matched by the mirrors used |
+ /// annotation or, in lack thereof, all elements. |
+ void enqueueReflectiveElements(Iterable<ClassElement> recents) { |
+ if (!hasEnqueuedReflectiveElements) { |
+ logEnqueueReflectiveAction("!START enqueueAll"); |
+ // First round of enqueuing, visit everything that is visible to |
+ // also pick up static top levels, etc. |
+ // Also, during the first round, consider all classes that have been seen |
+ // as recently seen, as we do not know how many rounds of resolution might |
+ // have run before tree shaking is disabled and thus everything is |
+ // enqueued. |
+ recents = seenClasses.toSet(); |
+ compiler.log('Enqueuing everything'); |
+ for (LibraryElement lib in compiler.libraryLoader.libraries) { |
+ enqueueReflectiveElementsInLibrary(lib, recents); |
+ } |
+ enqueueReflectiveSpecialClasses(); |
+ hasEnqueuedReflectiveElements = true; |
+ hasEnqueuedReflectiveStaticFields = true; |
+ logEnqueueReflectiveAction("!DONE enqueueAll"); |
+ } else if (recents.isNotEmpty) { |
+ // Keep looking at new classes until fixpoint is reached. |
+ logEnqueueReflectiveAction("!START enqueueRecents"); |
+ recents.forEach((ClassElement cls) { |
+ enqueueReflectiveElementsInClass(cls, recents, |
+ shouldIncludeElementDueToMirrors(cls.library, |
+ includedEnclosing: false)); |
+ }); |
+ logEnqueueReflectiveAction("!DONE enqueueRecents"); |
+ } |
} |
/// Enqueue the static fields that have been marked as used by reflective |
@@ -358,7 +443,7 @@ abstract class Enqueuer { |
if (hasEnqueuedReflectiveStaticFields) return; |
hasEnqueuedReflectiveStaticFields = true; |
for (Element element in elements) { |
- pretendElementWasUsed(element, compiler.mirrorDependencies); |
+ enqueueReflectiveMember(element, true); |
} |
} |
@@ -517,22 +602,31 @@ abstract class Enqueuer { |
} |
} |
- void registerClosure(Element element, Registry registry) { |
+ void registerClosure(FunctionElement element, Registry registry) { |
+ universe.allClosures.add(element); |
registerIfGeneric(element, registry); |
} |
void forEach(f(WorkItem work)) { |
do { |
- while (!queue.isEmpty) { |
+ while (queue.isNotEmpty) { |
// TODO(johnniwinther): Find an optimal process order. |
f(queue.removeLast()); |
} |
- onQueueEmpty(); |
- } while (!queue.isEmpty); |
+ List recents = recentClasses.toList(growable: false); |
+ recentClasses.clear(); |
+ if (!onQueueEmpty(recents)) recentClasses.addAll(recents); |
+ } while (queue.isNotEmpty || recentClasses.isNotEmpty); |
} |
- void onQueueEmpty() { |
- compiler.backend.onQueueEmpty(this); |
+ /// [onQueueEmpty] is called whenever the queue is drained. [recentClasses] |
+ /// contains the set of all classes seen for the first time since |
+ /// [onQueueEmpty] was called last. A return value of [true] indicates that |
+ /// the [recentClasses] have been processed and may be cleared. If [false] is |
+ /// returned, [onQueueEmpty] will be called once the queue is empty again (or |
+ /// still empty) and [recentClasses] will be a superset of the current value. |
+ bool onQueueEmpty(Iterable<ClassElement> recentClasses) { |
+ return compiler.backend.onQueueEmpty(this, recentClasses); |
} |
void logSummary(log(message)) { |
@@ -592,6 +686,19 @@ class ResolutionEnqueuer extends Enqueuer { |
return false; |
} |
+ /** |
+ * Decides whether an element should be included to satisfy requirements |
+ * of the mirror system. |
+ * |
+ * During resolution, we have to resort to matching elements against the |
+ * [MirrorsUsed] pattern, as we do not have a complete picture of the world, |
+ * yet. |
+ */ |
+ bool shouldIncludeElementDueToMirrors(Element element, |
+ {bool includedEnclosing}) { |
+ return includedEnclosing || compiler.backend.requiredByMirrorSystem(element); |
+ } |
+ |
void internalAddToWorkList(Element element) { |
assert(invariant(element, element is AnalyzableElement, |
message: 'Element $element is not analyzable.')); |
@@ -670,9 +777,9 @@ class ResolutionEnqueuer extends Enqueuer { |
deferredTaskQueue.add(new DeferredTask(element, action)); |
} |
- void onQueueEmpty() { |
+ bool onQueueEmpty(Iterable<ClassElement> recentClasses) { |
emptyDeferredTaskQueue(); |
- super.onQueueEmpty(); |
+ return super.onQueueEmpty(recentClasses); |
} |
void emptyDeferredTaskQueue() { |
@@ -708,6 +815,18 @@ class CodegenEnqueuer extends Enqueuer { |
bool isProcessed(Element member) => |
member.isAbstract || generatedCode.containsKey(member); |
+ /** |
+ * Decides whether an element should be included to satisfy requirements |
+ * of the mirror system. |
+ * |
+ * For code generation, we rely on the precomputed set of elements that takes |
+ * subtyping constraints into account. |
+ */ |
+ bool shouldIncludeElementDueToMirrors(Element element, |
+ {bool includedEnclosing}) { |
+ return compiler.backend.isAccessibleByReflection(element); |
+ } |
+ |
void internalAddToWorkList(Element element) { |
if (compiler.hasIncrementalSupport) { |
newlyEnqueuedElements.add(element); |