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 4ce3d5c93c3f325271104ded7e08941fb0829eba..8c34277e3aa891797e14455561cc840abda14fac 100644 |
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart |
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart |
@@ -73,13 +73,16 @@ 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 = false; |
floitsch
2014/06/27 08:59:15
should we start making these things const X.fromEn
herhut
2014/06/27 12:34:35
I did not know it was that easy to read an environ
|
+ |
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 +111,8 @@ abstract class Enqueuer { |
*/ |
void internalAddToWorkList(Element element); |
- void registerInstantiatedType(InterfaceType type, Registry registry) { |
+ void registerInstantiatedType(InterfaceType type, Registry registry, |
+ {mirrorUsage: false}) { |
karlklose
2014/06/27 07:45:57
Only use registerInstantiatedType for types that a
herhut
2014/06/27 12:34:35
This only replicated what the original implementat
|
task.measure(() { |
ClassElement cls = type.element; |
registry.registerDependency(cls); |
@@ -119,16 +123,20 @@ 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. |
+ || 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 +268,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 +317,130 @@ 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. |
+ * |
+ * The actual implementation depends on the current compiler phase. |
+ */ |
+ bool includeElementDueToMirrors(bool includeEnclosing, Element element); |
Johnni Winther
2014/06/27 07:43:01
Rename to [shouldIncludeElementDueToMirror] to ind
Johnni Winther
2014/06/27 07:43:01
Make [includeEnclosing] a named parameter.
herhut
2014/06/27 12:34:36
Done.
herhut
2014/06/27 12:34:36
Done.
|
+ |
+ void logEnqueueReflectiveAction(action, [msg = ""]) { |
+ if (TRACE_MIRROR_ENQUEUING) { |
+ print("MIRROR_ENQUEUE (${isResolutionQueue ? "R" : "B"}): $action $msg"); |
karlklose
2014/06/27 07:45:57
'R' -> 'resolution'?
What is 'B'?
herhut
2014/06/27 12:34:36
Backend. Probably C for codegen is better. I have
|
+ } |
+ } |
+ |
+ void enqueueReflectiveConstructor(ConstructorElement ctor, bool add) { |
Johnni Winther
2014/06/27 07:43:00
Add comment. I guess it is a helper method for [en
Johnni Winther
2014/06/27 07:43:01
Make [add] named.
karlklose
2014/06/27 07:45:57
Could you add a comment about the semantics of 'ad
floitsch
2014/06/27 08:59:15
nit: s/add/forceAdd/
I'm guessing that's the mean
herhut
2014/06/27 12:34:36
Done.
herhut
2014/06/27 12:34:36
Done.
herhut
2014/06/27 12:34:36
Done.
herhut
2014/06/27 12:34:37
Done.
|
+ if (includeElementDueToMirrors(add, ctor)) { |
+ logEnqueueReflectiveAction(ctor); |
+ ClassElement cls = ctor.declaration.enclosingClass; |
+ registerInstantiatedType(cls.rawType, compiler.mirrorDependencies); |
Johnni Winther
2014/06/27 07:43:01
Shouldn't this call have `mirrorUsage: true` ?
karlklose
2014/06/27 07:45:57
I am not sure we should register this type here.
herhut
2014/06/27 12:34:35
It will not have an effect currently but it is mor
herhut
2014/06/27 12:34:36
This implements what was done before. I will redo
|
+ registerStaticUse(ctor.declaration); |
+ } |
+ } |
+ |
+ void enqueueReflectiveMember(Element element, bool add) { |
Johnni Winther
2014/06/27 07:43:00
Make [add] named.
Johnni Winther
2014/06/27 07:43:01
Add comment.
herhut
2014/06/27 12:34:35
Done.
herhut
2014/06/27 12:34:36
Done.
|
+ add = includeElementDueToMirrors(add, element); |
karlklose
2014/06/27 07:45:57
Inline call to includeElementsDueToMirrors? (Also
herhut
2014/06/27 12:34:36
Below I have to pass the result on to the processi
|
+ if (add && !element.impliesType) { |
floitsch
2014/06/27 08:59:15
Keep comment exlpaining what "impliesType" means.
herhut
2014/06/27 12:34:36
Done.
|
+ 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); |
+ void enqueueReflectiveElementsInClass(ClassElement cls, |
Johnni Winther
2014/06/27 07:43:00
Add comment.
herhut
2014/06/27 12:34:36
Done.
|
+ Iterable<ClassElement> recents, |
+ bool add) { |
Johnni Winther
2014/06/27 07:43:00
Make [add] named.
herhut
2014/06/27 12:34:37
Done.
|
+ if (cls.library.isInternalLibrary) return; |
+ if (cls.isInjected) return; |
Johnni Winther
2014/06/27 07:43:00
Merge conditions.
herhut
2014/06/27 12:34:36
Done.
|
+ add = includeElementDueToMirrors(add, cls); |
+ if (add) { |
+ 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, add); |
+ }); |
+ cls.forEachClassMember((Member member) { |
+ enqueueReflectiveMember(member.element, add); |
+ }); |
+ } |
+ } |
+ |
+ void enqueueReflectiveSpecialClasses() { |
Johnni Winther
2014/06/27 07:43:00
Add comment.
herhut
2014/06/27 12:34:36
Done.
|
+ // [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. |
+ ClassElement closureClass = compiler.closureClass; |
+ if (compiler.backend.referencedFromMirrorSystem(closureClass)) { |
+ logEnqueueReflectiveAction(closureClass); |
+ registerInstantiatedClass(closureClass, compiler.mirrorDependencies, |
+ mirrorUsage: true); |
+ } |
+ } |
+ |
+ void enqueueReflectiveElementsInLibrary(LibraryElement lib, |
Johnni Winther
2014/06/27 07:43:00
Add comment.
herhut
2014/06/27 12:34:36
Done.
|
+ Iterable<ClassElement> recents, |
+ bool add) { |
+ add = includeElementDueToMirrors(add, lib); |
+ lib.forEachLocalMember((Element member) { |
+ if (member.isClass) { |
+ enqueueReflectiveElementsInClass(member, recents, add); |
+ } else { |
+ enqueueReflectiveMember(member, add); |
+ } |
+ }); |
+ } |
+ |
+ /// 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 a couple of rounds of resolution might have run |
floitsch
2014/06/27 08:59:15
maybe?
... as treeshaking might only be disabled
herhut
2014/06/27 12:34:35
Rephrased:
Also, during the first round, consider
|
+ // before treeshaking is disabled and we thus enqueue everything. |
+ recents = seenClasses.toSet(); |
+ compiler.log('Enqueuing everything'); |
+ for (LibraryElement lib in compiler.libraries.values) { |
+ enqueueReflectiveElementsInLibrary(lib, recents, false); |
} |
+ 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, |
+ includeElementDueToMirrors(false, cls.library)); |
+ }); |
+ logEnqueueReflectiveAction("!DONE enqueueRecents"); |
} |
- hasEnqueuedEverything = true; |
- hasEnqueuedReflectiveStaticFields = true; |
} |
/// Enqueue the static fields that have been marked as used by reflective |
@@ -358,7 +449,7 @@ abstract class Enqueuer { |
if (hasEnqueuedReflectiveStaticFields) return; |
hasEnqueuedReflectiveStaticFields = true; |
for (Element element in elements) { |
- pretendElementWasUsed(element, compiler.mirrorDependencies); |
+ enqueueReflectiveMember(element, true); |
} |
} |
@@ -529,21 +620,30 @@ abstract class Enqueuer { |
} |
void registerClosure(Element element, Registry registry) { |
Johnni Winther
2014/06/27 07:43:00
Change [Element] to [FunctionElement].
herhut
2014/06/27 12:34:36
Done.
|
+ 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)) { |
@@ -603,6 +703,18 @@ class ResolutionEnqueuer extends Enqueuer { |
return false; |
} |
+ /** |
+ * Decides whether an element should be included to satisfy requirements |
+ * of the mirror system. |
+ * |
+ * During resoltion, we have to resort to matching elements against the |
Johnni Winther
2014/06/27 07:43:01
'resoltion' -> 'resolution'.
karlklose
2014/06/27 07:45:57
'resoltion' -> 'resolution'.
herhut
2014/06/27 12:34:36
Done.
herhut
2014/06/27 12:34:37
Done.
|
+ * [MirrorsUsed] pattern, as we do not have a complete picture of the world, |
+ * yet. |
+ */ |
+ bool includeElementDueToMirrors(bool includeEnclosing, Element element) { |
karlklose
2014/06/27 07:45:57
Does it make sense to move functionality like this
herhut
2014/06/27 12:34:36
Yes, this is also on the list of further refactori
|
+ return includeEnclosing || compiler.backend.requiredByMirrorSystem(element); |
+ } |
+ |
void internalAddToWorkList(Element element) { |
assert(invariant(element, element is AnalyzableElement, |
message: 'Element $element is not analyzable.')); |
@@ -689,9 +801,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() { |
@@ -727,6 +839,17 @@ 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 includeElementDueToMirrors(bool includeEnclosing, Element element) { |
+ return compiler.backend.isAccessibleByReflection(element); |
+ } |
+ |
void internalAddToWorkList(Element element) { |
if (compiler.hasIncrementalSupport) { |
newlyEnqueuedElements.add(element); |