Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(748)

Unified Diff: sdk/lib/_internal/compiler/implementation/enqueue.dart

Issue 340783011: Take inheritance into account when computing the elements accessible by mirrors. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: rebased + fixes Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);

Powered by Google App Engine
This is Rietveld 408576698