| Index: pkg/compiler/lib/src/js_backend/mirrors_analysis.dart
|
| diff --git a/pkg/compiler/lib/src/js_backend/mirrors_analysis.dart b/pkg/compiler/lib/src/js_backend/mirrors_analysis.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..930dba3ba0f6bcbcdac575bb0197a8a9ff7e0f46
|
| --- /dev/null
|
| +++ b/pkg/compiler/lib/src/js_backend/mirrors_analysis.dart
|
| @@ -0,0 +1,243 @@
|
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library dart2js.mirrors_handler;
|
| +
|
| +import '../common/resolution.dart';
|
| +import '../diagnostics/diagnostic_listener.dart';
|
| +import '../elements/elements.dart';
|
| +import '../enqueue.dart';
|
| +import '../universe/selector.dart';
|
| +import '../universe/use.dart';
|
| +import 'backend.dart';
|
| +
|
| +class MirrorsAnalysis {
|
| + final MirrorsHandler resolutionHandler;
|
| + final MirrorsHandler codegenHandler;
|
| +
|
| + MirrorsAnalysis(JavaScriptBackend backend, Resolution resolution)
|
| + : resolutionHandler = new MirrorsHandler(backend, resolution),
|
| + codegenHandler = new MirrorsHandler(backend, resolution);
|
| +
|
| + /// Enqueue all elements that are matched by the mirrors used
|
| + /// annotation or, in lack thereof, all elements.
|
| + // TODO(johnniwinther): Compute [WorldImpact] instead of enqueuing directly.
|
| + void enqueueReflectiveElements(
|
| + Enqueuer enqueuer,
|
| + Iterable<ClassElement> recents,
|
| + Iterable<LibraryElement> loadedLibraries) {
|
| + MirrorsHandler handler =
|
| + enqueuer.isResolutionQueue ? resolutionHandler : codegenHandler;
|
| + handler.enqueueReflectiveElements(enqueuer, recents, loadedLibraries);
|
| + }
|
| +
|
| + /// Enqueue the static fields that have been marked as used by reflective
|
| + /// usage through `MirrorsUsed`.
|
| + // TODO(johnniwinther): Compute [WorldImpact] instead of enqueuing directly.
|
| + void enqueueReflectiveStaticFields(
|
| + Enqueuer enqueuer, Iterable<Element> elements) {
|
| + MirrorsHandler handler =
|
| + enqueuer.isResolutionQueue ? resolutionHandler : codegenHandler;
|
| + handler.enqueueReflectiveStaticFields(enqueuer, elements);
|
| + }
|
| +}
|
| +
|
| +class MirrorsHandler {
|
| + static final TRACE_MIRROR_ENQUEUING =
|
| + const bool.fromEnvironment("TRACE_MIRROR_ENQUEUING");
|
| +
|
| + final JavaScriptBackend _backend;
|
| + final Resolution _resolution;
|
| +
|
| + bool hasEnqueuedReflectiveElements = false;
|
| + bool hasEnqueuedReflectiveStaticFields = false;
|
| +
|
| + MirrorsHandler(this._backend, this._resolution);
|
| +
|
| + DiagnosticReporter get _reporter => _resolution.reporter;
|
| +
|
| + void _logEnqueueReflectiveAction(action, [msg = ""]) {
|
| + if (TRACE_MIRROR_ENQUEUING) {
|
| + print("MIRROR_ENQUEUE (R): $action $msg");
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * 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 || _backend.requiredByMirrorSystem(element);
|
| + }
|
| +
|
| + /// Enqeue the constructor [ctor] if it is required for reflection.
|
| + ///
|
| + /// [enclosingWasIncluded] provides a hint whether the enclosing element was
|
| + /// needed for reflection.
|
| + void _enqueueReflectiveConstructor(
|
| + Enqueuer enqueuer, ConstructorElement constructor,
|
| + {bool enclosingWasIncluded}) {
|
| + if (_shouldIncludeElementDueToMirrors(constructor,
|
| + includedEnclosing: enclosingWasIncluded)) {
|
| + _logEnqueueReflectiveAction(constructor);
|
| + ClassElement cls = constructor.declaration.enclosingClass;
|
| + enqueuer.registerTypeUse(new TypeUse.mirrorInstantiation(cls.rawType));
|
| + enqueuer
|
| + .registerStaticUse(new StaticUse.foreignUse(constructor.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(
|
| + Enqueuer enqueuer, Element element, bool enclosingWasIncluded) {
|
| + if (_shouldIncludeElementDueToMirrors(element,
|
| + includedEnclosing: enclosingWasIncluded)) {
|
| + _logEnqueueReflectiveAction(element);
|
| + if (element.isTypedef) {
|
| + TypedefElement typedef = element;
|
| + typedef.ensureResolved(_resolution);
|
| + } else if (Elements.isStaticOrTopLevel(element)) {
|
| + enqueuer
|
| + .registerStaticUse(new StaticUse.foreignUse(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
|
| + DynamicUse dynamicUse =
|
| + new DynamicUse(new Selector.fromElement(element), null);
|
| + enqueuer.registerDynamicUse(dynamicUse);
|
| + if (element.isField) {
|
| + DynamicUse dynamicUse = new DynamicUse(
|
| + new Selector.setter(
|
| + new Name(element.name, element.library, isSetter: true)),
|
| + null);
|
| + enqueuer.registerDynamicUse(dynamicUse);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /// Enqeue the member [element] if it is required for reflection.
|
| + ///
|
| + /// [enclosingWasIncluded] provides a hint whether the enclosing element was
|
| + /// needed for reflection.
|
| + void _enqueueReflectiveElementsInClass(
|
| + Enqueuer enqueuer, 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 declaration = cls.declaration;
|
| + declaration.ensureResolved(_resolution);
|
| + enqueuer.registerTypeUse(
|
| + new TypeUse.mirrorInstantiation(declaration.rawType));
|
| + }
|
| + // 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(enqueuer, element,
|
| + enclosingWasIncluded: includeClass);
|
| + });
|
| + cls.forEachClassMember((Member member) {
|
| + _enqueueReflectiveMember(enqueuer, 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(Enqueuer enqueuer) {
|
| + Iterable<ClassElement> classes = _backend.classesRequiredForReflection;
|
| + for (ClassElement cls in classes) {
|
| + if (_backend.referencedFromMirrorSystem(cls)) {
|
| + _logEnqueueReflectiveAction(cls);
|
| + cls.ensureResolved(_resolution);
|
| + enqueuer.registerTypeUse(new TypeUse.mirrorInstantiation(cls.rawType));
|
| + }
|
| + }
|
| + }
|
| +
|
| + /// Enqeue all local members of the library [lib] if they are required for
|
| + /// reflection.
|
| + void _enqueueReflectiveElementsInLibrary(
|
| + Enqueuer enqueuer, LibraryElement lib, Iterable<ClassElement> recents) {
|
| + bool includeLibrary =
|
| + _shouldIncludeElementDueToMirrors(lib, includedEnclosing: false);
|
| + lib.forEachLocalMember((Element member) {
|
| + if (member.isInjected) return;
|
| + if (member.isClass) {
|
| + _enqueueReflectiveElementsInClass(enqueuer, member, recents,
|
| + enclosingWasIncluded: includeLibrary);
|
| + } else {
|
| + _enqueueReflectiveMember(enqueuer, member, includeLibrary);
|
| + }
|
| + });
|
| + }
|
| +
|
| + /// Enqueue all elements that are matched by the mirrors used
|
| + /// annotation or, in lack thereof, all elements.
|
| + // TODO(johnniwinther): Compute [WorldImpact] instead of enqueuing directly.
|
| + void enqueueReflectiveElements(
|
| + Enqueuer enqueuer,
|
| + Iterable<ClassElement> recents,
|
| + Iterable<LibraryElement> loadedLibraries) {
|
| + 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 = enqueuer.processedClasses.toSet();
|
| + _reporter.log('Enqueuing everything');
|
| + for (LibraryElement lib in loadedLibraries) {
|
| + _enqueueReflectiveElementsInLibrary(enqueuer, lib, recents);
|
| + }
|
| + _enqueueReflectiveSpecialClasses(enqueuer);
|
| + 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(enqueuer, cls, recents,
|
| + enclosingWasIncluded: _shouldIncludeElementDueToMirrors(cls.library,
|
| + includedEnclosing: false));
|
| + });
|
| + _logEnqueueReflectiveAction("!DONE enqueueRecents");
|
| + }
|
| + }
|
| +
|
| + /// Enqueue the static fields that have been marked as used by reflective
|
| + /// usage through `MirrorsUsed`.
|
| + // TODO(johnniwinther): Compute [WorldImpact] instead of enqueuing directly.
|
| + void enqueueReflectiveStaticFields(
|
| + Enqueuer enqueuer, Iterable<Element> elements) {
|
| + if (hasEnqueuedReflectiveStaticFields) return;
|
| + hasEnqueuedReflectiveStaticFields = true;
|
| + for (Element element in elements) {
|
| + _enqueueReflectiveMember(enqueuer, element, true);
|
| + }
|
| + }
|
| +}
|
|
|