| 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);
|
|
|