diff --git a/pkg/compiler/lib/src/common/backend_api.dart b/pkg/compiler/lib/src/common/backend_api.dart index b8944a1f23c8..ea4ae66d3320 100644 --- a/pkg/compiler/lib/src/common/backend_api.dart +++ b/pkg/compiler/lib/src/common/backend_api.dart @@ -83,7 +83,7 @@ abstract class Backend extends Target { void initializeHelperClasses() {} - void enqueueHelpers(ResolutionEnqueuer world, Registry registry); + void enqueueHelpers(ResolutionEnqueuer world); /// Creates an [Enqueuer] for code generation specific to this backend. Enqueuer createCodegenEnqueuer(Compiler compiler); @@ -117,7 +117,8 @@ abstract class Backend extends Target { /// Enable deferred loading. Returns `true` if the backend supports deferred /// loading. - bool enableDeferredLoadingIfSupported(Spannable node, Registry registry); + bool enableDeferredLoadingIfSupported( + ResolutionEnqueuer enqueuer, Spannable node); /// Called during codegen when [constant] has been used. void computeImpactForCompileTimeConstant(ConstantValue constant, @@ -126,22 +127,15 @@ abstract class Backend extends Target { /// Called to notify to the backend that a class is being instantiated. // TODO(johnniwinther): Remove this. It's only called once for each [cls] and // only with [Compiler.globalDependencies] as [registry]. - void registerInstantiatedClass( - ClassElement cls, Enqueuer enqueuer, Registry registry) {} + void registerInstantiatedClass(ClassElement cls, Enqueuer enqueuer) {} /// Called to notify to the backend that a class is implemented by an /// instantiated class. - void registerImplementedClass( - ClassElement cls, Enqueuer enqueuer, Registry registry) {} + void registerImplementedClass(ClassElement cls, Enqueuer enqueuer) {} /// Called to instruct to the backend register [type] as instantiated on /// [enqueuer]. - void registerInstantiatedType( - InterfaceType type, Enqueuer enqueuer, Registry registry, - {bool mirrorUsage: false}) { - registry.registerDependency(type.element); - enqueuer.registerInstantiatedType(type, mirrorUsage: mirrorUsage); - } + void registerInstantiatedType(InterfaceType type) {} /// Register a runtime type variable bound tests between [typeArgument] and /// [bound]. @@ -153,12 +147,12 @@ abstract class Backend extends Target { * method. */ void registerCallMethodWithFreeTypeVariables( - Element callMethod, Enqueuer enqueuer, Registry registry) {} + Element callMethod, Enqueuer enqueuer) {} /// Called to instruct the backend to register that a closure exists for a /// function on an instantiated generic class. void registerClosureWithFreeTypeVariables( - Element closure, Enqueuer enqueuer, Registry registry) {} + Element closure, Enqueuer enqueuer) {} /// Call this to register that a member has been closurized. void registerBoundClosure(Enqueuer enqueuer) {} @@ -169,7 +163,7 @@ abstract class Backend extends Target { /** * Call this to register that the [:runtimeType:] property has been accessed. */ - void registerRuntimeType(Enqueuer enqueuer, Registry registry) {} + void registerRuntimeType(Enqueuer enqueuer) {} /// Call this to register a `noSuchMethod` implementation. void registerNoSuchMethod(FunctionElement noSuchMethodElement) {} @@ -213,7 +207,7 @@ abstract class Backend extends Target { return false; } - void registerStaticUse(Element element, {bool forResolution}) {} + void registerStaticUse(Enqueuer enqueuer, Element element) {} /// This method is called immediately after the [LibraryElement] [library] has /// been created. @@ -368,7 +362,8 @@ abstract class ForeignResolver { class ImpactTransformer { /// Transform the [ResolutionImpact] into a [WorldImpact] adding the /// backend dependencies for features used in [worldImpact]. - WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) { + WorldImpact transformResolutionImpact( + ResolutionEnqueuer enqueuer, ResolutionImpact worldImpact) { return worldImpact; } diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index 536a2794bbbb..c2dad31db863 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart @@ -76,7 +76,8 @@ import 'universe/selector.dart' show Selector; import 'universe/world_builder.dart' show ResolutionWorldBuilder, CodegenWorldBuilder; import 'universe/use.dart' show StaticUse; -import 'universe/world_impact.dart' show ImpactStrategy, WorldImpact; +import 'universe/world_impact.dart' + show ImpactStrategy, WorldImpact, WorldImpactBuilderImpl; import 'util/util.dart' show Link, Setlet; import 'world.dart' show ClosedWorld, ClosedWorldRefiner, OpenWorld, WorldImpl; @@ -114,16 +115,6 @@ abstract class Compiler implements LibraryLoaderListener { */ GlobalDependencyRegistry globalDependencies; - /** - * Dependencies that are only included due to mirrors. - * - * We should get rid of this and ensure that all dependencies are - * associated with a particular element. - */ - // TODO(johnniwinther): This should not be a [ResolutionRegistry]. - final Registry mirrorDependencies = - new ResolutionRegistry(null, new TreeElementMapping(null)); - /// Options provided from command-line arguments. final CompilerOptions options; @@ -687,7 +678,7 @@ abstract class Compiler implements LibraryLoaderListener { } // Elements required by enqueueHelpers are global dependencies // that are not pulled in by a particular element. - backend.enqueueHelpers(enqueuer.resolution, globalDependencies); + backend.enqueueHelpers(enqueuer.resolution); resolveLibraryMetadata(); reporter.log('Resolving...'); processQueue(enqueuer.resolution, mainFunction); @@ -737,7 +728,6 @@ abstract class Compiler implements LibraryLoaderListener { reporter.log('Compiling...'); phase = PHASE_COMPILING; backend.onCodegenStart(); - // TODO(johnniwinther): Move these to [CodegenEnqueuer]. if (hasIsolateSupport) { backend.enableIsolateSupport(enqueuer.codegen); } @@ -817,7 +807,7 @@ abstract class Compiler implements LibraryLoaderListener { ClassElement cls = element; cls.ensureResolved(resolution); cls.forEachLocalMember(enqueuer.resolution.addToWorkList); - backend.registerInstantiatedType(cls.rawType, world, globalDependencies); + world.registerInstantiatedType(cls.rawType); } else { world.addToWorkList(element); } @@ -853,43 +843,42 @@ abstract class Compiler implements LibraryLoaderListener { }); }); - void processQueue(Enqueuer world, Element main) => - selfTask.measureSubtask("Compiler.processQueue", () { - world.nativeEnqueuer.processNativeClasses(libraryLoader.libraries); - if (main != null && !main.isMalformed) { - FunctionElement mainMethod = main; - mainMethod.computeType(resolution); - if (mainMethod.functionSignature.parameterCount != 0) { - // The first argument could be a list of strings. - backend.backendClasses.listImplementation - .ensureResolved(resolution); - backend.registerInstantiatedType( - backend.backendClasses.listImplementation.rawType, - world, - globalDependencies); - backend.backendClasses.stringImplementation - .ensureResolved(resolution); - backend.registerInstantiatedType( - backend.backendClasses.stringImplementation.rawType, - world, - globalDependencies); - - backend.registerMainHasArguments(world); - } - world.addToWorkList(main); + void processQueue(Enqueuer enqueuer, Element main) { + selfTask.measureSubtask("Compiler.processQueue", () { + WorldImpactBuilderImpl nativeImpact = new WorldImpactBuilderImpl(); + enqueuer.nativeEnqueuer + .processNativeClasses(nativeImpact, libraryLoader.libraries); + enqueuer.applyImpact(nativeImpact); + if (main != null && !main.isMalformed) { + FunctionElement mainMethod = main; + mainMethod.computeType(resolution); + if (mainMethod.functionSignature.parameterCount != 0) { + // The first argument could be a list of strings. + backend.backendClasses.listImplementation.ensureResolved(resolution); + enqueuer.registerInstantiatedType( + backend.backendClasses.listImplementation.rawType); + backend.backendClasses.stringImplementation + .ensureResolved(resolution); + enqueuer.registerInstantiatedType( + backend.backendClasses.stringImplementation.rawType); + + backend.registerMainHasArguments(enqueuer); } - if (options.verbose) { - progress.reset(); - } - emptyQueue(world); - world.queueIsClosed = true; - // Notify the impact strategy impacts are no longer needed for this - // enqueuer. - impactStrategy.onImpactUsed(world.impactUse); - backend.onQueueClosed(); - assert( - compilationFailed || world.checkNoEnqueuedInvokedInstanceMethods()); - }); + enqueuer.addToWorkList(main); + } + if (options.verbose) { + progress.reset(); + } + emptyQueue(enqueuer); + enqueuer.queueIsClosed = true; + // Notify the impact strategy impacts are no longer needed for this + // enqueuer. + impactStrategy.onImpactUsed(enqueuer.impactUse); + backend.onQueueClosed(); + assert(compilationFailed || + enqueuer.checkNoEnqueuedInvokedInstanceMethods()); + }); + } /** * Perform various checks of the queues. This includes checking that @@ -2150,7 +2139,8 @@ class _CompilerResolution implements Resolution { WorldImpact transformResolutionImpact( Element element, ResolutionImpact resolutionImpact) { WorldImpact worldImpact = compiler.backend.impactTransformer - .transformResolutionImpact(resolutionImpact); + .transformResolutionImpact( + compiler.enqueuer.resolution, resolutionImpact); _worldImpactCache[element] = worldImpact; return worldImpact; } diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart index 6c938048a2dd..b29dae6c337a 100644 --- a/pkg/compiler/lib/src/deferred_load.dart +++ b/pkg/compiler/lib/src/deferred_load.dart @@ -346,6 +346,8 @@ class DeferredLoadTask extends CompilerTask { } break; case TypeUseKind.INSTANTIATION: + case TypeUseKind.MIRROR_INSTANTIATION: + case TypeUseKind.NATIVE_INSTANTIATION: case TypeUseKind.IS_CHECK: case TypeUseKind.AS_CAST: case TypeUseKind.CATCH_TYPE: @@ -842,7 +844,7 @@ class DeferredLoadTask extends CompilerTask { } if (isProgramSplit) { isProgramSplit = compiler.backend.enableDeferredLoadingIfSupported( - lastDeferred, compiler.globalDependencies); + compiler.enqueuer.resolution, lastDeferred); } } diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart index b254e0d75b57..660e2e866ded 100644 --- a/pkg/compiler/lib/src/enqueue.dart +++ b/pkg/compiler/lib/src/enqueue.dart @@ -19,16 +19,12 @@ import 'elements/elements.dart' AnalyzableElement, AstElement, ClassElement, - ConstructorElement, Element, - Elements, Entity, FunctionElement, LibraryElement, - Member, - Name, - TypedElement, - TypedefElement; + LocalFunctionElement, + TypedElement; import 'native/native.dart' as native; import 'types/types.dart' show TypeMaskStrategy; import 'universe/selector.dart' show Selector; @@ -100,15 +96,7 @@ abstract class Enqueuer { void enableIsolateSupport(); - /// Enqueue the static fields that have been marked as used by reflective - /// usage through `MirrorsUsed`. - void enqueueReflectiveStaticFields(Iterable elements); - - /// Enqueue all elements that are matched by the mirrors used - /// annotation or, in lack thereof, all elements. - void enqueueReflectiveElements(Iterable recents); - - void registerInstantiatedType(InterfaceType type, {bool mirrorUsage: false}); + void registerInstantiatedType(InterfaceType type); void forEach(void f(WorkItem work)); /// Apply the [worldImpact] to this enqueuer. If the [impactSource] is provided @@ -122,6 +110,8 @@ abstract class Enqueuer { bool isProcessed(Element member); Iterable get processedEntities; + + Iterable get processedClasses; } /// [Enqueuer] which is specific to resolution. @@ -138,14 +128,8 @@ class ResolutionEnqueuer extends Enqueuer { final ResolutionWorldBuilderImpl _universe = new ResolutionWorldBuilderImpl(const TypeMaskStrategy()); - static final TRACE_MIRROR_ENQUEUING = - const bool.fromEnvironment("TRACE_MIRROR_ENQUEUING"); - bool queueIsClosed = false; - bool hasEnqueuedReflectiveElements = false; - bool hasEnqueuedReflectiveStaticFields = false; - WorldImpactVisitor impactVisitor; ResolutionEnqueuer(Compiler compiler, this.strategy) @@ -186,12 +170,19 @@ class ResolutionEnqueuer extends Enqueuer { } } + void registerInstantiatedType(InterfaceType type) { + _registerInstantiatedType(type, globalDependency: true); + } + void applyImpact(WorldImpact worldImpact, {Element impactSource}) { compiler.impactStrategy .visitImpact(impactSource, worldImpact, impactVisitor, impactUse); } - void registerInstantiatedType(InterfaceType type, {bool mirrorUsage: false}) { + void _registerInstantiatedType(InterfaceType type, + {bool mirrorUsage: false, + bool nativeUsage: false, + bool globalDependency: false}) { task.measure(() { ClassElement cls = type.element; cls.ensureResolved(resolution); @@ -199,9 +190,15 @@ class ResolutionEnqueuer extends Enqueuer { _universe.registerTypeInstantiation(type, isNative: isNative, byMirrors: mirrorUsage, onImplemented: (ClassElement cls) { - compiler.backend - .registerImplementedClass(cls, this, compiler.globalDependencies); + compiler.backend.registerImplementedClass(cls, this); }); + if (globalDependency && !mirrorUsage) { + compiler.globalDependencies.registerDependency(type.element); + } + if (nativeUsage) { + nativeEnqueuer.onInstantiatedType(type); + } + compiler.backend.registerInstantiatedType(type); // TODO(johnniwinther): Share this reasoning with [Universe]. if (!cls.isAbstract || isNative || mirrorUsage) { processInstantiatedClass(cls); @@ -325,8 +322,7 @@ class ResolutionEnqueuer extends Enqueuer { // We only tell the backend once that [superclass] was instantiated, so // any additional dependencies must be treated as global // dependencies. - compiler.backend.registerInstantiatedClass( - superclass, this, compiler.globalDependencies); + compiler.backend.registerInstantiatedClass(superclass, this); } ClassElement superclass = cls; @@ -345,172 +341,6 @@ class ResolutionEnqueuer extends Enqueuer { }); } - void logEnqueueReflectiveAction(action, [msg = ""]) { - if (TRACE_MIRROR_ENQUEUING) { - print("MIRROR_ENQUEUE (R): $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; - compiler.backend.registerInstantiatedType( - cls.rawType, this, compiler.mirrorDependencies, - mirrorUsage: true); - registerStaticUse(new StaticUse.foreignUse(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)) { - logEnqueueReflectiveAction(element); - if (element.isTypedef) { - TypedefElement typedef = element; - typedef.ensureResolved(resolution); - } else if (Elements.isStaticOrTopLevel(element)) { - 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); - registerDynamicUse(dynamicUse); - if (element.isField) { - DynamicUse dynamicUse = new DynamicUse( - new Selector.setter( - new Name(element.name, element.library, isSetter: true)), - null); - 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(ClassElement cls, - Iterable 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; - decl.ensureResolved(resolution); - compiler.backend.registerInstantiatedType( - decl.rawType, this, 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 classes = - compiler.backend.classesRequiredForReflection; - for (ClassElement cls in classes) { - if (compiler.backend.referencedFromMirrorSystem(cls)) { - logEnqueueReflectiveAction(cls); - cls.ensureResolved(resolution); - compiler.backend.registerInstantiatedType( - cls.rawType, this, compiler.mirrorDependencies, - mirrorUsage: true); - } - } - } - - /// Enqeue all local members of the library [lib] if they are required for - /// reflection. - void enqueueReflectiveElementsInLibrary( - LibraryElement lib, Iterable recents) { - bool includeLibrary = - shouldIncludeElementDueToMirrors(lib, includedEnclosing: false); - lib.forEachLocalMember((Element member) { - if (member.isInjected) return; - 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 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 = _processedClasses.toSet(); - reporter.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 - /// usage through `MirrorsUsed`. - void enqueueReflectiveStaticFields(Iterable elements) { - if (hasEnqueuedReflectiveStaticFields) return; - hasEnqueuedReflectiveStaticFields = true; - for (Element element in elements) { - enqueueReflectiveMember(element, true); - } - } - void processSet( Map> map, String memberName, bool f(Element e)) { Set members = map[memberName]; @@ -576,7 +406,7 @@ class ResolutionEnqueuer extends Enqueuer { assert(invariant(element, element.isDeclaration, message: "Element ${element} is not the declaration.")); _universe.registerStaticUse(staticUse); - compiler.backend.registerStaticUse(element, forResolution: true); + compiler.backend.registerStaticUse(this, element); bool addElement = true; switch (staticUse.kind) { case StaticUseKind.STATIC_TEAR_OFF: @@ -589,6 +419,10 @@ class ResolutionEnqueuer extends Enqueuer { // [FIELD_SET] contains [BoxFieldElement]s which we cannot enqueue. // Also [CLOSURE] contains [LocalFunctionElement] which we cannot // enqueue. + LocalFunctionElement closure = staticUse.element; + if (closure.type.containsTypeVariables) { + universe.closuresWithFreeTypeVariables.add(closure); + } addElement = false; break; case StaticUseKind.SUPER_FIELD_SET: @@ -613,7 +447,15 @@ class ResolutionEnqueuer extends Enqueuer { DartType type = typeUse.type; switch (typeUse.kind) { case TypeUseKind.INSTANTIATION: - registerInstantiatedType(type); + _registerInstantiatedType(type, globalDependency: false); + break; + case TypeUseKind.MIRROR_INSTANTIATION: + _registerInstantiatedType(type, + mirrorUsage: true, globalDependency: false); + break; + case TypeUseKind.NATIVE_INSTANTIATION: + _registerInstantiatedType(type, + nativeUsage: true, globalDependency: true); break; case TypeUseKind.IS_CHECK: case TypeUseKind.AS_CAST: @@ -639,16 +481,14 @@ class ResolutionEnqueuer extends Enqueuer { } void registerCallMethodWithFreeTypeVariables(Element element) { - compiler.backend.registerCallMethodWithFreeTypeVariables( - element, this, compiler.globalDependencies); + compiler.backend.registerCallMethodWithFreeTypeVariables(element, this); _universe.callMethodsWithFreeTypeVariables.add(element); } void registerClosurizedMember(TypedElement element) { assert(element.isInstanceMember); if (element.computeType(resolution).containsTypeVariables) { - compiler.backend.registerClosureWithFreeTypeVariables( - element, this, compiler.globalDependencies); + compiler.backend.registerClosureWithFreeTypeVariables(element, this); _universe.closuresWithFreeTypeVariables.add(element); } compiler.backend.registerBoundClosure(this); @@ -705,20 +545,6 @@ class ResolutionEnqueuer extends Enqueuer { compiler.backend.onElementResolved(element); } - /** - * 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); - } - /** * Adds [element] to the work list if it has not already been processed. * @@ -765,7 +591,7 @@ class ResolutionEnqueuer extends Enqueuer { // runtime type. compiler.enabledRuntimeType = true; // TODO(ahe): Record precise dependency here. - compiler.backend.registerRuntimeType(this, compiler.globalDependencies); + compiler.backend.registerRuntimeType(this); } else if (compiler.commonElements.isFunctionApplyMethod(element)) { compiler.enabledFunctionApply = true; } diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart index 48f71398234d..2fa04484c947 100644 --- a/pkg/compiler/lib/src/js_backend/backend.dart +++ b/pkg/compiler/lib/src/js_backend/backend.dart @@ -74,6 +74,7 @@ import 'enqueuer.dart'; import 'js_interop_analysis.dart' show JsInteropAnalysis; import '../kernel/task.dart'; import 'lookup_map_analysis.dart' show LookupMapAnalysis; +import 'mirrors_analysis.dart'; import 'namer.dart'; import 'native_data.dart' show NativeData; import 'no_such_method_registry.dart'; @@ -572,6 +573,9 @@ class JavaScriptBackend extends Backend { /// Support for classifying `noSuchMethod` implementations. NoSuchMethodRegistry noSuchMethodRegistry; + /// Resolution and codegen support for computing reflectable elements. + MirrorsAnalysis mirrorsAnalysis; + /// Builds kernel representation for the program. KernelTask kernelTask; @@ -628,6 +632,7 @@ class JavaScriptBackend extends Backend { customElementsAnalysis = new CustomElementsAnalysis(this); lookupMapAnalysis = new LookupMapAnalysis(this, reporter); jsInteropAnalysis = new JsInteropAnalysis(this); + mirrorsAnalysis = new MirrorsAnalysis(this, compiler.resolution); noSuchMethodRegistry = new NoSuchMethodRegistry(this); kernelTask = new KernelTask(compiler); @@ -1053,7 +1058,7 @@ class JavaScriptBackend extends Backend { } } - void addInterceptors(ClassElement cls, Enqueuer enqueuer, Registry registry) { + void addInterceptors(ClassElement cls, Enqueuer enqueuer) { if (enqueuer.isResolutionQueue) { _interceptedClasses.add(helpers.jsInterceptorClass); _interceptedClasses.add(cls); @@ -1066,7 +1071,7 @@ class JavaScriptBackend extends Backend { set.add(member); }, includeSuperAndInjectedMembers: true); } - enqueueClass(enqueuer, cls, registry); + enqueueClass(enqueuer, cls); } Set get interceptedClasses { @@ -1159,20 +1164,17 @@ class JavaScriptBackend extends Backend { } } - void registerInstantiatedClass( - ClassElement cls, Enqueuer enqueuer, Registry registry) { - _processClass(cls, enqueuer, registry); + void registerInstantiatedClass(ClassElement cls, Enqueuer enqueuer) { + _processClass(cls, enqueuer); } - void registerImplementedClass( - ClassElement cls, Enqueuer enqueuer, Registry registry) { - _processClass(cls, enqueuer, registry); + void registerImplementedClass(ClassElement cls, Enqueuer enqueuer) { + _processClass(cls, enqueuer); } - void _processClass(ClassElement cls, Enqueuer enqueuer, Registry registry) { + void _processClass(ClassElement cls, Enqueuer enqueuer) { if (!cls.typeVariables.isEmpty) { - typeVariableHandler.registerClassWithTypeVariables( - cls, enqueuer, registry); + typeVariableHandler.registerClassWithTypeVariables(cls, enqueuer); } // Register any helper that will be needed by the backend. @@ -1182,33 +1184,32 @@ class JavaScriptBackend extends Backend { cls == coreClasses.numClass) { // The backend will try to optimize number operations and use the // `iae` helper directly. - enqueue(enqueuer, helpers.throwIllegalArgumentException, registry); + enqueue(enqueuer, helpers.throwIllegalArgumentException); } else if (cls == coreClasses.listClass || cls == coreClasses.stringClass) { // The backend will try to optimize array and string access and use the // `ioore` and `iae` helpers directly. - enqueue(enqueuer, helpers.throwIndexOutOfRangeException, registry); - enqueue(enqueuer, helpers.throwIllegalArgumentException, registry); + enqueue(enqueuer, helpers.throwIndexOutOfRangeException); + enqueue(enqueuer, helpers.throwIllegalArgumentException); } else if (cls == coreClasses.functionClass) { - enqueueClass(enqueuer, helpers.closureClass, registry); + enqueueClass(enqueuer, helpers.closureClass); } else if (cls == coreClasses.mapClass) { // The backend will use a literal list to initialize the entries // of the map. - enqueueClass(enqueuer, coreClasses.listClass, registry); - enqueueClass(enqueuer, helpers.mapLiteralClass, registry); + enqueueClass(enqueuer, coreClasses.listClass); + enqueueClass(enqueuer, helpers.mapLiteralClass); // For map literals, the dependency between the implementation class // and [Map] is not visible, so we have to add it manually. rti.registerRtiDependency(helpers.mapLiteralClass, cls); } else if (cls == helpers.boundClosureClass) { // TODO(johnniwinther): Is this a noop? - enqueueClass(enqueuer, helpers.boundClosureClass, registry); + enqueueClass(enqueuer, helpers.boundClosureClass); } else if (isNativeOrExtendsNative(cls)) { - enqueue(enqueuer, helpers.getNativeInterceptorMethod, registry); - enqueueClass( - enqueuer, helpers.jsInterceptorClass, compiler.globalDependencies); - enqueueClass(enqueuer, helpers.jsJavaScriptObjectClass, registry); - enqueueClass(enqueuer, helpers.jsPlainJavaScriptObjectClass, registry); - enqueueClass(enqueuer, helpers.jsJavaScriptFunctionClass, registry); + enqueue(enqueuer, helpers.getNativeInterceptorMethod); + enqueueClass(enqueuer, helpers.jsInterceptorClass); + enqueueClass(enqueuer, helpers.jsJavaScriptObjectClass); + enqueueClass(enqueuer, helpers.jsPlainJavaScriptObjectClass); + enqueueClass(enqueuer, helpers.jsJavaScriptFunctionClass); } else if (cls == helpers.mapLiteralClass) { // For map literals, the dependency between the implementation class // and [Map] is not visible, so we have to add it manually. @@ -1245,70 +1246,71 @@ class JavaScriptBackend extends Backend { helpers.mapLiteralConstructor = getFactory('_literal', 1); helpers.mapLiteralConstructorEmpty = getFactory('_empty', 0); - enqueueInResolution(helpers.mapLiteralConstructor, registry); - enqueueInResolution(helpers.mapLiteralConstructorEmpty, registry); + enqueue(enqueuer, helpers.mapLiteralConstructor); + enqueue(enqueuer, helpers.mapLiteralConstructorEmpty); helpers.mapLiteralUntypedMaker = getMember('_makeLiteral'); helpers.mapLiteralUntypedEmptyMaker = getMember('_makeEmpty'); - enqueueInResolution(helpers.mapLiteralUntypedMaker, registry); - enqueueInResolution(helpers.mapLiteralUntypedEmptyMaker, registry); + enqueue(enqueuer, helpers.mapLiteralUntypedMaker); + enqueue(enqueuer, helpers.mapLiteralUntypedEmptyMaker); } } if (cls == helpers.closureClass) { - enqueue(enqueuer, helpers.closureFromTearOff, registry); + enqueue(enqueuer, helpers.closureFromTearOff); } if (cls == coreClasses.stringClass || cls == helpers.jsStringClass) { - addInterceptors(helpers.jsStringClass, enqueuer, registry); + addInterceptors(helpers.jsStringClass, enqueuer); } else if (cls == coreClasses.listClass || cls == helpers.jsArrayClass || cls == helpers.jsFixedArrayClass || cls == helpers.jsExtendableArrayClass || cls == helpers.jsUnmodifiableArrayClass) { - addInterceptors(helpers.jsArrayClass, enqueuer, registry); - addInterceptors(helpers.jsMutableArrayClass, enqueuer, registry); - addInterceptors(helpers.jsFixedArrayClass, enqueuer, registry); - addInterceptors(helpers.jsExtendableArrayClass, enqueuer, registry); - addInterceptors(helpers.jsUnmodifiableArrayClass, enqueuer, registry); - // Literal lists can be translated into calls to these functions: - enqueueInResolution(helpers.jsArrayTypedConstructor, registry); - enqueueInResolution(helpers.setRuntimeTypeInfo, registry); - enqueueInResolution(helpers.getTypeArgumentByIndex, registry); + addInterceptors(helpers.jsArrayClass, enqueuer); + addInterceptors(helpers.jsMutableArrayClass, enqueuer); + addInterceptors(helpers.jsFixedArrayClass, enqueuer); + addInterceptors(helpers.jsExtendableArrayClass, enqueuer); + addInterceptors(helpers.jsUnmodifiableArrayClass, enqueuer); + if (enqueuer.isResolutionQueue) { + // Literal lists can be translated into calls to these functions: + enqueue(enqueuer, helpers.jsArrayTypedConstructor); + enqueue(enqueuer, helpers.setRuntimeTypeInfo); + enqueue(enqueuer, helpers.getTypeArgumentByIndex); + } } else if (cls == coreClasses.intClass || cls == helpers.jsIntClass) { - addInterceptors(helpers.jsIntClass, enqueuer, registry); - addInterceptors(helpers.jsPositiveIntClass, enqueuer, registry); - addInterceptors(helpers.jsUInt32Class, enqueuer, registry); - addInterceptors(helpers.jsUInt31Class, enqueuer, registry); - addInterceptors(helpers.jsNumberClass, enqueuer, registry); + addInterceptors(helpers.jsIntClass, enqueuer); + addInterceptors(helpers.jsPositiveIntClass, enqueuer); + addInterceptors(helpers.jsUInt32Class, enqueuer); + addInterceptors(helpers.jsUInt31Class, enqueuer); + addInterceptors(helpers.jsNumberClass, enqueuer); } else if (cls == coreClasses.doubleClass || cls == helpers.jsDoubleClass) { - addInterceptors(helpers.jsDoubleClass, enqueuer, registry); - addInterceptors(helpers.jsNumberClass, enqueuer, registry); + addInterceptors(helpers.jsDoubleClass, enqueuer); + addInterceptors(helpers.jsNumberClass, enqueuer); } else if (cls == coreClasses.boolClass || cls == helpers.jsBoolClass) { - addInterceptors(helpers.jsBoolClass, enqueuer, registry); + addInterceptors(helpers.jsBoolClass, enqueuer); } else if (cls == coreClasses.nullClass || cls == helpers.jsNullClass) { - addInterceptors(helpers.jsNullClass, enqueuer, registry); + addInterceptors(helpers.jsNullClass, enqueuer); } else if (cls == coreClasses.numClass || cls == helpers.jsNumberClass) { - addInterceptors(helpers.jsIntClass, enqueuer, registry); - addInterceptors(helpers.jsPositiveIntClass, enqueuer, registry); - addInterceptors(helpers.jsUInt32Class, enqueuer, registry); - addInterceptors(helpers.jsUInt31Class, enqueuer, registry); - addInterceptors(helpers.jsDoubleClass, enqueuer, registry); - addInterceptors(helpers.jsNumberClass, enqueuer, registry); + addInterceptors(helpers.jsIntClass, enqueuer); + addInterceptors(helpers.jsPositiveIntClass, enqueuer); + addInterceptors(helpers.jsUInt32Class, enqueuer); + addInterceptors(helpers.jsUInt31Class, enqueuer); + addInterceptors(helpers.jsDoubleClass, enqueuer); + addInterceptors(helpers.jsNumberClass, enqueuer); } else if (cls == helpers.jsJavaScriptObjectClass) { - addInterceptors(helpers.jsJavaScriptObjectClass, enqueuer, registry); + addInterceptors(helpers.jsJavaScriptObjectClass, enqueuer); } else if (cls == helpers.jsPlainJavaScriptObjectClass) { - addInterceptors(helpers.jsPlainJavaScriptObjectClass, enqueuer, registry); + addInterceptors(helpers.jsPlainJavaScriptObjectClass, enqueuer); } else if (cls == helpers.jsUnknownJavaScriptObjectClass) { - addInterceptors( - helpers.jsUnknownJavaScriptObjectClass, enqueuer, registry); + addInterceptors(helpers.jsUnknownJavaScriptObjectClass, enqueuer); } else if (cls == helpers.jsJavaScriptFunctionClass) { - addInterceptors(helpers.jsJavaScriptFunctionClass, enqueuer, registry); + addInterceptors(helpers.jsJavaScriptFunctionClass, enqueuer); } else if (isNativeOrExtendsNative(cls)) { addInterceptorsForNativeClassMembers(cls, enqueuer); } else if (cls == helpers.jsIndexingBehaviorInterface) { // These two helpers are used by the emitter and the codegen. // Because we cannot enqueue elements at the time of emission, // we make sure they are always generated. - enqueue(enqueuer, helpers.isJsIndexable, registry); + enqueue(enqueuer, helpers.isJsIndexable); } customElementsAnalysis.registerInstantiatedClass(cls, @@ -1318,27 +1320,23 @@ class JavaScriptBackend extends Backend { } } - void registerInstantiatedType( - InterfaceType type, Enqueuer enqueuer, Registry registry, - {bool mirrorUsage: false}) { + void registerInstantiatedType(InterfaceType type) { lookupMapAnalysis.registerInstantiatedType(type); - super.registerInstantiatedType(type, enqueuer, registry, - mirrorUsage: mirrorUsage); } - void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { + void enqueueHelpers(ResolutionEnqueuer enqueuer) { assert(helpers.interceptorsLibrary != null); // TODO(ngeoffray): Not enqueuing those two classes currently make // the compiler potentially crash. However, any reasonable program // will instantiate those two classes. - addInterceptors(helpers.jsBoolClass, world, registry); - addInterceptors(helpers.jsNullClass, world, registry); + addInterceptors(helpers.jsBoolClass, enqueuer); + addInterceptors(helpers.jsNullClass, enqueuer); if (compiler.options.enableTypeAssertions) { // Unconditionally register the helper that checks if the // expression in an if/while/for is a boolean. // TODO(ngeoffray): Should we have the resolver register those instead? Element e = helpers.boolConversionCheck; - if (e != null) enqueue(world, e, registry); + if (e != null) enqueue(enqueuer, e); } if (TRACE_CALLS) { @@ -1346,10 +1344,10 @@ class JavaScriptBackend extends Backend { ? helpers.consoleTraceHelper : helpers.postTraceHelper; assert(traceHelper != null); - enqueueInResolution(traceHelper, registry); + enqueue(enqueuer, traceHelper); } - enqueueInResolution(helpers.assertUnreachableMethod, registry); - registerCheckedModeHelpers(registry); + enqueue(enqueuer, helpers.assertUnreachableMethod); + registerCheckedModeHelpers(enqueuer); } onResolutionComplete() { @@ -1364,53 +1362,47 @@ class JavaScriptBackend extends Backend { noSuchMethodRegistry.onTypeInferenceComplete(); } - void registerGetRuntimeTypeArgument(Registry registry) { - enqueueImpact( - compiler.enqueuer.resolution, impacts.getRuntimeTypeArgument, registry); + void registerGetRuntimeTypeArgument() { + enqueueImpact(compiler.enqueuer.resolution, impacts.getRuntimeTypeArgument); } void registerCallMethodWithFreeTypeVariables( - Element callMethod, Enqueuer enqueuer, Registry registry) { + Element callMethod, Enqueuer enqueuer) { if (enqueuer.isResolutionQueue || methodNeedsRti(callMethod)) { - registerComputeSignature(enqueuer, registry); + registerComputeSignature(enqueuer); } } void registerClosureWithFreeTypeVariables( - Element closure, Enqueuer enqueuer, Registry registry) { + Element closure, Enqueuer enqueuer) { if (enqueuer.isResolutionQueue || methodNeedsRti(closure)) { - registerComputeSignature(enqueuer, registry); + registerComputeSignature(enqueuer); } } void registerBoundClosure(Enqueuer enqueuer) { helpers.boundClosureClass.ensureResolved(resolution); - registerInstantiatedType( - helpers.boundClosureClass.rawType, - enqueuer, - // Precise dependency is not important here. - compiler.globalDependencies); + enqueuer.registerInstantiatedType(helpers.boundClosureClass.rawType); } void registerGetOfStaticFunction(Enqueuer enqueuer) { helpers.closureClass.ensureResolved(resolution); - registerInstantiatedType( - helpers.closureClass.rawType, enqueuer, compiler.globalDependencies); + enqueuer.registerInstantiatedType(helpers.closureClass.rawType); } - void registerComputeSignature(Enqueuer enqueuer, Registry registry) { + void registerComputeSignature(Enqueuer enqueuer) { // Calls to [:computeSignature:] are generated by the emitter and we // therefore need to enqueue the used elements in the codegen enqueuer as // well as in the resolution enqueuer. - enqueueImpact(enqueuer, impacts.computeSignature, registry); + enqueueImpact(enqueuer, impacts.computeSignature); } - void registerRuntimeType(Enqueuer enqueuer, Registry registry) { - registerComputeSignature(enqueuer, registry); - enqueueInResolution(helpers.setRuntimeTypeInfo, registry); - registerGetRuntimeTypeArgument(registry); - enqueueInResolution(helpers.getRuntimeTypeInfo, registry); - enqueueClass(enqueuer, coreClasses.listClass, registry); + void registerRuntimeType(ResolutionEnqueuer enqueuer) { + registerComputeSignature(enqueuer); + enqueue(enqueuer, helpers.setRuntimeTypeInfo); + registerGetRuntimeTypeArgument(); + enqueue(enqueuer, helpers.getRuntimeTypeInfo); + enqueueClass(enqueuer, coreClasses.listClass); } void registerTypeVariableBoundsSubtypeCheck( @@ -1418,11 +1410,10 @@ class JavaScriptBackend extends Backend { rti.registerTypeVariableBoundsSubtypeCheck(typeArgument, bound); } - void registerCheckDeferredIsLoaded(Registry registry) { - enqueueInResolution(helpers.checkDeferredIsLoaded, registry); + void registerCheckDeferredIsLoaded(ResolutionEnqueuer enqueuer) { + enqueue(enqueuer, helpers.checkDeferredIsLoaded); // Also register the types of the arguments passed to this method. - enqueueClass( - compiler.enqueuer.resolution, coreClasses.stringClass, registry); + enqueueClass(enqueuer, coreClasses.stringClass); } void registerNoSuchMethod(FunctionElement noSuchMethod) { @@ -1464,7 +1455,7 @@ class JavaScriptBackend extends Backend { } void enableNoSuchMethod(Enqueuer world) { - enqueue(world, helpers.createInvocationMirror, compiler.globalDependencies); + enqueue(world, helpers.createInvocationMirror); world.registerDynamicUse(new DynamicUse(Selectors.noSuchMethod_, null)); } @@ -1527,26 +1518,17 @@ class JavaScriptBackend extends Backend { /// Enqueue [e] in [enqueuer]. /// /// This method calls [registerBackendUse]. - void enqueue(Enqueuer enqueuer, Element e, Registry registry) { + void enqueue(Enqueuer enqueuer, Element e) { if (e == null) return; registerBackendUse(e); enqueuer.addToWorkList(e); - registry.registerDependency(e); - } - - /// Enqueue [e] in the resolution enqueuer. - /// - /// This method calls [registerBackendUse]. - void enqueueInResolution(Element e, Registry registry) { - if (e == null) return; - ResolutionEnqueuer enqueuer = compiler.enqueuer.resolution; - enqueue(enqueuer, e, registry); + compiler.globalDependencies.registerDependency(e); } /// Register instantiation of [cls] in [enqueuer]. /// /// This method calls [registerBackendUse]. - void enqueueClass(Enqueuer enqueuer, ClassElement cls, Registry registry) { + void enqueueClass(Enqueuer enqueuer, ClassElement cls) { if (cls == null) return; registerBackendUse(cls); helpersUsed.add(cls.declaration); @@ -1554,13 +1536,13 @@ class JavaScriptBackend extends Backend { helpersUsed.add(cls.implementation); } cls.ensureResolved(resolution); - registerInstantiatedType(cls.rawType, enqueuer, registry); + enqueuer.registerInstantiatedType(cls.rawType); } /// Register instantiation of [type] in [enqueuer]. /// /// This method calls [registerBackendUse]. - void enqueueType(Enqueuer enqueuer, InterfaceType type, Registry registry) { + void enqueueType(Enqueuer enqueuer, InterfaceType type) { if (type == null) return; ClassElement cls = type.element; registerBackendUse(cls); @@ -1569,22 +1551,21 @@ class JavaScriptBackend extends Backend { helpersUsed.add(cls.implementation); } cls.ensureResolved(resolution); - registerInstantiatedType(type, enqueuer, registry); + enqueuer.registerInstantiatedType(type); } - void enqueueImpact( - Enqueuer enqueuer, BackendImpact impact, Registry registry) { + void enqueueImpact(Enqueuer enqueuer, BackendImpact impact) { for (Element staticUse in impact.staticUses) { - enqueue(enqueuer, staticUse, registry); + enqueue(enqueuer, staticUse); } for (InterfaceType type in impact.instantiatedTypes) { - enqueueType(enqueuer, type, registry); + enqueueType(enqueuer, type); } for (ClassElement cls in impact.instantiatedClasses) { - enqueueClass(enqueuer, cls, registry); + enqueueClass(enqueuer, cls); } for (BackendImpact otherImpact in impact.otherImpacts) { - enqueueImpact(enqueuer, otherImpact, registry); + enqueueImpact(enqueuer, otherImpact); } } @@ -1666,11 +1647,11 @@ class JavaScriptBackend extends Backend { } native.NativeEnqueuer nativeResolutionEnqueuer(Enqueuer world) { - return new native.NativeResolutionEnqueuer(world, compiler); + return new native.NativeResolutionEnqueuer(compiler); } native.NativeEnqueuer nativeCodegenEnqueuer(Enqueuer world) { - return new native.NativeCodegenEnqueuer(world, compiler, emitter); + return new native.NativeCodegenEnqueuer(compiler, emitter); } ClassElement defaultSuperclass(ClassElement element) { @@ -1870,11 +1851,11 @@ class JavaScriptBackend extends Backend { } } - void registerCheckedModeHelpers(Registry registry) { + void registerCheckedModeHelpers(ResolutionEnqueuer enqueuer) { // We register all the helpers in the resolution queue. // TODO(13155): Find a way to register fewer helpers. for (CheckedModeHelper helper in checkedModeHelpers) { - enqueueInResolution(helper.getStaticUse(compiler).element, registry); + enqueue(enqueuer, helper.getStaticUse(compiler).element); } } @@ -1906,7 +1887,7 @@ class JavaScriptBackend extends Backend { return compiler.closedWorld.hasOnlySubclasses(classElement); } - void registerStaticUse(Element element, {bool forResolution}) { + void registerStaticUse(Enqueuer enqueuer, Element element) { if (element == helpers.disableTreeShakingMarker) { isTreeShakingDisabled = true; } else if (element == helpers.preserveNamesMarker) { @@ -1923,14 +1904,15 @@ class JavaScriptBackend extends Backend { // TODO(sigurdm): Create a function registerLoadLibraryAccess. if (!isLoadLibraryFunctionResolved) { isLoadLibraryFunctionResolved = true; - enqueueInResolution( - helpers.loadLibraryWrapper, compiler.globalDependencies); + if (enqueuer.isResolutionQueue) { + enqueue(enqueuer, helpers.loadLibraryWrapper); + } } } else if (element == helpers.requiresPreambleMarker) { requiresPreamble = true; } customElementsAnalysis.registerStaticUse(element, - forResolution: forResolution); + forResolution: enqueuer.isResolutionQueue); } /// Called when [:const Symbol(name):] is seen. @@ -2395,12 +2377,14 @@ class JavaScriptBackend extends Backend { } if (isTreeShakingDisabled) { - enqueuer.enqueueReflectiveElements(recentClasses); + mirrorsAnalysis.enqueueReflectiveElements( + enqueuer, recentClasses, compiler.libraryLoader.libraries); } else if (!targetsUsed.isEmpty && enqueuer.isResolutionQueue) { // Add all static elements (not classes) that have been requested for // reflection. If there is no mirror-usage these are probably not // necessary, but the backend relies on them being resolved. - enqueuer.enqueueReflectiveStaticFields(_findStaticFieldTargets()); + mirrorsAnalysis.enqueueReflectiveStaticFields( + enqueuer, _findStaticFieldTargets()); } if (mustPreserveNames) reporter.log('Preserving names.'); @@ -2607,8 +2591,9 @@ class JavaScriptBackend extends Backend { } @override - bool enableDeferredLoadingIfSupported(Spannable node, Registry registry) { - registerCheckDeferredIsLoaded(registry); + bool enableDeferredLoadingIfSupported( + ResolutionEnqueuer enqueuer, Spannable node) { + registerCheckDeferredIsLoaded(enqueuer); return true; } @@ -2792,13 +2777,9 @@ class JavaScriptImpactTransformer extends ImpactTransformer { BackendImpacts get impacts => backend.impacts; - // TODO(johnniwinther): Avoid this dependency. - ResolutionEnqueuer get resolutionEnqueuer { - return backend.compiler.enqueuer.resolution; - } - @override - WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) { + WorldImpact transformResolutionImpact( + ResolutionEnqueuer enqueuer, ResolutionImpact worldImpact) { TransformedWorldImpact transformed = new TransformedWorldImpact(worldImpact); for (Feature feature in worldImpact.features) { @@ -2885,6 +2866,8 @@ class JavaScriptImpactTransformer extends ImpactTransformer { DartType type = typeUse.type; switch (typeUse.kind) { case TypeUseKind.INSTANTIATION: + case TypeUseKind.MIRROR_INSTANTIATION: + case TypeUseKind.NATIVE_INSTANTIATION: registerRequiredType(type); break; case TypeUseKind.IS_CHECK: @@ -2965,8 +2948,6 @@ class JavaScriptImpactTransformer extends ImpactTransformer { registerBackendImpact(transformed, impacts.closure); LocalFunctionElement closure = staticUse.element; if (closure.type.containsTypeVariables) { - resolutionEnqueuer.universe.closuresWithFreeTypeVariables - .add(closure); registerBackendImpact(transformed, impacts.computeSignature); } break; @@ -3002,8 +2983,8 @@ class JavaScriptImpactTransformer extends ImpactTransformer { } for (native.NativeBehavior behavior in worldImpact.nativeData) { - resolutionEnqueuer.nativeEnqueuer - .registerNativeBehavior(behavior, worldImpact); + enqueuer.nativeEnqueuer + .registerNativeBehavior(transformed, behavior, worldImpact); } return transformed; @@ -3103,8 +3084,8 @@ class JavaScriptImpactTransformer extends ImpactTransformer { // need to register checked mode helpers. if (inCheckedMode) { // All helpers are added to resolution queue in enqueueHelpers. These - // calls to enqueueInResolution serve as assertions that the helper was - // in fact added. + // calls to [enqueue] with the resolution enqueuer serve as assertions + // that the helper was in fact added. // TODO(13155): Find a way to enqueue helpers lazily. CheckedModeHelper helper = backend.getCheckedModeHelper(type, typeCast: false); diff --git a/pkg/compiler/lib/src/js_backend/enqueuer.dart b/pkg/compiler/lib/src/js_backend/enqueuer.dart index a9736b81f1ce..420dfab67432 100644 --- a/pkg/compiler/lib/src/js_backend/enqueuer.dart +++ b/pkg/compiler/lib/src/js_backend/enqueuer.dart @@ -58,16 +58,10 @@ class CodegenEnqueuer implements Enqueuer { final CodegenWorldBuilderImpl _universe = new CodegenWorldBuilderImpl(const TypeMaskStrategy()); - static final TRACE_MIRROR_ENQUEUING = - const bool.fromEnvironment("TRACE_MIRROR_ENQUEUING"); - bool queueIsClosed = false; EnqueueTask task; native.NativeEnqueuer nativeEnqueuer; // Set by EnqueueTask - bool hasEnqueuedReflectiveElements = false; - bool hasEnqueuedReflectiveStaticFields = false; - WorldImpactVisitor impactVisitor; CodegenEnqueuer(Compiler compiler, this.strategy) @@ -85,10 +79,6 @@ class CodegenEnqueuer implements Enqueuer { CompilerOptions get options => _compiler.options; - Registry get globalDependencies => _compiler.globalDependencies; - - Registry get mirrorDependencies => _compiler.mirrorDependencies; - ClosedWorld get _world => _compiler.closedWorld; bool get queueIsEmpty => queue.isEmpty; @@ -140,15 +130,24 @@ class CodegenEnqueuer implements Enqueuer { .visitImpact(impactSource, worldImpact, impactVisitor, impactUse); } - void registerInstantiatedType(InterfaceType type, {bool mirrorUsage: false}) { + void registerInstantiatedType(InterfaceType type) { + _registerInstantiatedType(type); + } + + void _registerInstantiatedType(InterfaceType type, + {bool mirrorUsage: false, bool nativeUsage: false}) { task.measure(() { ClassElement cls = type.element; bool isNative = backend.isNative(cls); _universe.registerTypeInstantiation(type, isNative: isNative, byMirrors: mirrorUsage, onImplemented: (ClassElement cls) { - backend.registerImplementedClass(cls, this, globalDependencies); + backend.registerImplementedClass(cls, this); }); + if (nativeUsage) { + nativeEnqueuer.onInstantiatedType(type); + } + backend.registerInstantiatedType(type); // TODO(johnniwinther): Share this reasoning with [Universe]. if (!cls.isAbstract || isNative || mirrorUsage) { processInstantiatedClass(cls); @@ -274,7 +273,7 @@ class CodegenEnqueuer implements Enqueuer { // We only tell the backend once that [superclass] was instantiated, so // any additional dependencies must be treated as global // dependencies. - backend.registerInstantiatedClass(superclass, this, globalDependencies); + backend.registerInstantiatedClass(superclass, this); } ClassElement superclass = cls; @@ -293,165 +292,6 @@ class CodegenEnqueuer implements Enqueuer { }); } - void logEnqueueReflectiveAction(action, [msg = ""]) { - if (TRACE_MIRROR_ENQUEUING) { - print("MIRROR_ENQUEUE (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; - backend.registerInstantiatedType(cls.rawType, this, mirrorDependencies, - mirrorUsage: true); - registerStaticUse(new StaticUse.foreignUse(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)) { - logEnqueueReflectiveAction(element); - if (element.isTypedef) { - // Do nothing. - } else if (Elements.isStaticOrTopLevel(element)) { - 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); - registerDynamicUse(dynamicUse); - if (element.isField) { - DynamicUse dynamicUse = new DynamicUse( - new Selector.setter( - new Name(element.name, element.library, isSetter: true)), - null); - 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(ClassElement cls, - Iterable 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; - backend.registerInstantiatedType(decl.rawType, this, 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 classes = backend.classesRequiredForReflection; - for (ClassElement cls in classes) { - if (backend.referencedFromMirrorSystem(cls)) { - logEnqueueReflectiveAction(cls); - backend.registerInstantiatedType(cls.rawType, this, mirrorDependencies, - mirrorUsage: true); - } - } - } - - /// Enqeue all local members of the library [lib] if they are required for - /// reflection. - void enqueueReflectiveElementsInLibrary( - LibraryElement lib, Iterable recents) { - bool includeLibrary = - shouldIncludeElementDueToMirrors(lib, includedEnclosing: false); - lib.forEachLocalMember((Element member) { - if (member.isInjected) return; - 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 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 = _processedClasses.toSet(); - reporter.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 - /// usage through `MirrorsUsed`. - void enqueueReflectiveStaticFields(Iterable elements) { - if (hasEnqueuedReflectiveStaticFields) return; - hasEnqueuedReflectiveStaticFields = true; - for (Element element in elements) { - enqueueReflectiveMember(element, true); - } - } - void processSet( Map> map, String memberName, bool f(Element e)) { Set members = map[memberName]; @@ -517,7 +357,7 @@ class CodegenEnqueuer implements Enqueuer { assert(invariant(element, element.isDeclaration, message: "Element ${element} is not the declaration.")); _universe.registerStaticUse(staticUse); - backend.registerStaticUse(element, forResolution: false); + backend.registerStaticUse(this, element); bool addElement = true; switch (staticUse.kind) { case StaticUseKind.STATIC_TEAR_OFF: @@ -553,7 +393,13 @@ class CodegenEnqueuer implements Enqueuer { DartType type = typeUse.type; switch (typeUse.kind) { case TypeUseKind.INSTANTIATION: - registerInstantiatedType(type); + _registerInstantiatedType(type); + break; + case TypeUseKind.MIRROR_INSTANTIATION: + _registerInstantiatedType(type, mirrorUsage: true); + break; + case TypeUseKind.NATIVE_INSTANTIATION: + _registerInstantiatedType(type, nativeUsage: true); break; case TypeUseKind.IS_CHECK: case TypeUseKind.AS_CAST: @@ -579,15 +425,13 @@ class CodegenEnqueuer implements Enqueuer { } void registerCallMethodWithFreeTypeVariables(Element element) { - backend.registerCallMethodWithFreeTypeVariables( - element, this, globalDependencies); + backend.registerCallMethodWithFreeTypeVariables(element, this); } void registerClosurizedMember(TypedElement element) { assert(element.isInstanceMember); if (element.type.containsTypeVariables) { - backend.registerClosureWithFreeTypeVariables( - element, this, globalDependencies); + backend.registerClosureWithFreeTypeVariables(element, this); } backend.registerBoundClosure(this); } @@ -645,18 +489,6 @@ class CodegenEnqueuer implements 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 backend.isAccessibleByReflection(element); - } - void registerNoSuchMethod(Element element) { if (!enabledNoSuchMethod && backend.enabledNoSuchMethod) { backend.enableNoSuchMethod(this); @@ -689,6 +521,9 @@ class CodegenEnqueuer implements Enqueuer { @override Iterable get processedEntities => generatedCode.keys; + + @override + Iterable get processedClasses => _processedClasses; } void removeFromSet(Map> map, Element element) { 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 000000000000..930dba3ba0f6 --- /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 recents, + Iterable 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 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 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 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 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 recents, + Iterable 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 elements) { + if (hasEnqueuedReflectiveStaticFields) return; + hasEnqueuedReflectiveStaticFields = true; + for (Element element in elements) { + _enqueueReflectiveMember(enqueuer, element, true); + } + } +} diff --git a/pkg/compiler/lib/src/js_backend/type_variable_handler.dart b/pkg/compiler/lib/src/js_backend/type_variable_handler.dart index 595da9542433..97c6011a0c3d 100644 --- a/pkg/compiler/lib/src/js_backend/type_variable_handler.dart +++ b/pkg/compiler/lib/src/js_backend/type_variable_handler.dart @@ -64,13 +64,12 @@ class TypeVariableHandler { return impactBuilder.flush(); } - void registerClassWithTypeVariables( - ClassElement cls, Enqueuer enqueuer, Registry registry) { + void registerClassWithTypeVariables(ClassElement cls, Enqueuer enqueuer) { if (enqueuer.isResolutionQueue) { // On first encounter, we have to ensure that the support classes get // resolved. if (!_seenClassesWithTypeVariables) { - _backend.enqueueClass(enqueuer, _typeVariableClass, registry); + _backend.enqueueClass(enqueuer, _typeVariableClass); _typeVariableClass.ensureResolved(_compiler.resolution); Link constructors = _typeVariableClass.constructors; if (constructors.isEmpty && constructors.tail.isEmpty) { @@ -78,9 +77,8 @@ class TypeVariableHandler { "Class '$_typeVariableClass' should only have one constructor"); } _typeVariableConstructor = _typeVariableClass.constructors.head; - _backend.enqueueInResolution(_typeVariableConstructor, registry); - _backend.registerInstantiatedType( - _typeVariableClass.rawType, enqueuer, registry); + _backend.enqueue(enqueuer, _typeVariableConstructor); + enqueuer.registerInstantiatedType(_typeVariableClass.rawType); enqueuer.registerStaticUse(new StaticUse.staticInvoke( _backend.registerBackendUse(_backend.helpers.createRuntimeType), CallStructure.ONE_ARG)); diff --git a/pkg/compiler/lib/src/native/enqueue.dart b/pkg/compiler/lib/src/native/enqueue.dart index 7c3f125ac085..44d3be38efe7 100644 --- a/pkg/compiler/lib/src/native/enqueue.dart +++ b/pkg/compiler/lib/src/native/enqueue.dart @@ -21,18 +21,25 @@ import '../js_emitter/js_emitter.dart' show CodeEmitterTask, NativeEmitter; import '../tokens/token.dart' show BeginGroupToken, Token; import '../tokens/token_constants.dart' as Tokens show EOF_TOKEN; import '../tree/tree.dart'; +import '../universe/use.dart' show StaticUse, TypeUse; +import '../universe/world_impact.dart' show WorldImpactBuilder; import 'behavior.dart'; /** * This could be an abstract class but we use it as a stub for the dart_backend. */ class NativeEnqueuer { + /// Called when a [type] has been instantiated natively. + void onInstantiatedType(InterfaceType type) {} + /// Initial entry point to native enqueuer. - void processNativeClasses(Iterable libraries) {} + void processNativeClasses( + WorldImpactBuilder impactBuilder, Iterable libraries) {} /// Registers the [nativeBehavior]. Adds the liveness of its instantiated /// types to the world. - void registerNativeBehavior(NativeBehavior nativeBehavior, cause) {} + void registerNativeBehavior( + WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) {} // TODO(johnniwinther): Move [handleFieldAnnotations] and // [handleMethodAnnotations] to [JavaScriptBackend] or [NativeData]. @@ -86,12 +93,6 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { */ final Set matchedTypeConstraints = new Set(); - /// Pending actions. Classes in [pendingClasses] have action thunks in - /// [queue] to register the class. - final queue = new Queue(); - bool flushing = false; - - final Enqueuer world; final Compiler compiler; final bool enableLiveTypeAnalysis; @@ -100,7 +101,7 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { ClassElement _annotationJsNameClass; /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. - NativeEnqueuerBase(this.world, Compiler compiler, this.enableLiveTypeAnalysis) + NativeEnqueuerBase(Compiler compiler, this.enableLiveTypeAnalysis) : this.compiler = compiler, processedLibraries = compiler.cacheStrategy.newSet(); @@ -111,7 +112,14 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { DiagnosticReporter get reporter => compiler.reporter; CoreTypes get coreTypes => compiler.coreTypes; - void processNativeClasses(Iterable libraries) { + void onInstantiatedType(InterfaceType type) { + if (unusedClasses.remove(type.element)) { + registeredClasses.add(type.element); + } + } + + void processNativeClasses( + WorldImpactBuilder impactBuilder, Iterable libraries) { if (compiler.options.hasIncrementalSupport) { // Since [Set.add] returns bool if an element was added, this restricts // [libraries] to ones that haven't already been processed. This saves @@ -124,8 +132,7 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { } processSubclassesOfNativeClasses(libraries); if (!enableLiveTypeAnalysis) { - nativeClasses.forEach((c) => enqueueClass(c, 'forced')); - flushQueue(); + _registerTypeUses(impactBuilder, nativeClasses, 'forced'); } } @@ -333,40 +340,19 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { return name; } - enqueueClass(ClassElement classElement, cause) { - assert(unusedClasses.contains(classElement)); - unusedClasses.remove(classElement); - pendingClasses.add(classElement); - queue.add(() { - processClass(classElement, cause); - }); - } - - void flushQueue() { - if (flushing) return; - flushing = true; - while (!queue.isEmpty) { - (queue.removeFirst())(); - } - flushing = false; - } - - processClass(ClassElement classElement, cause) { - // TODO(ahe): Fix this assertion to work in incremental compilation. - assert(compiler.options.hasIncrementalSupport || - !registeredClasses.contains(classElement)); - - bool firstTime = registeredClasses.isEmpty; - pendingClasses.remove(classElement); - registeredClasses.add(classElement); - - // TODO(ahe): Is this really a global dependency? - classElement.ensureResolved(resolution); - compiler.backend.registerInstantiatedType( - classElement.rawType, world, compiler.globalDependencies); - - if (firstTime) { - queue.add(onFirstNativeClass); + /// Register [classes] as natively instantiated in [impactBuilder]. + void _registerTypeUses( + WorldImpactBuilder impactBuilder, Set classes, cause) { + for (ClassElement cls in classes) { + if (!unusedClasses.contains(cls)) { + // No need to add [classElement] to [impactBuilder]: it has already been + // instantiated and we don't track origins of native instantiations + // precisely. + continue; + } + cls.ensureResolved(resolution); + impactBuilder + .registerTypeUse(new TypeUse.nativeInstantiation(cls.rawType)); } } @@ -449,43 +435,45 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { }); } - void registerNativeBehavior(NativeBehavior nativeBehavior, cause) { - processNativeBehavior(nativeBehavior, cause); - flushQueue(); + void registerNativeBehavior( + WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) { + _processNativeBehavior(impactBuilder, nativeBehavior, cause); } - processNativeBehavior(NativeBehavior behavior, cause) { - // TODO(ahe): Is this really a global dependency? - Registry registry = compiler.globalDependencies; - bool allUsedBefore = unusedClasses.isEmpty; + void _processNativeBehavior( + WorldImpactBuilder impactBuilder, NativeBehavior behavior, cause) { + void registerInstantiation(InterfaceType type) { + impactBuilder.registerTypeUse(new TypeUse.nativeInstantiation(type)); + } + + int unusedBefore = unusedClasses.length; + Set matchingClasses = new Set(); for (var type in behavior.typesInstantiated) { if (matchedTypeConstraints.contains(type)) continue; matchedTypeConstraints.add(type); if (type is SpecialType) { if (type == SpecialType.JsObject) { - backend.registerInstantiatedType( - compiler.coreTypes.objectType, world, registry); + registerInstantiation(compiler.coreTypes.objectType); } continue; } if (type is InterfaceType) { if (type == coreTypes.intType) { - backend.registerInstantiatedType(type, world, registry); + registerInstantiation(type); } else if (type == coreTypes.doubleType) { - backend.registerInstantiatedType(type, world, registry); + registerInstantiation(type); } else if (type == coreTypes.numType) { - backend.registerInstantiatedType( - coreTypes.doubleType, world, registry); - backend.registerInstantiatedType(coreTypes.intType, world, registry); + registerInstantiation(coreTypes.doubleType); + registerInstantiation(coreTypes.intType); } else if (type == coreTypes.stringType) { - backend.registerInstantiatedType(type, world, registry); + registerInstantiation(type); } else if (type == coreTypes.nullType) { - backend.registerInstantiatedType(type, world, registry); + registerInstantiation(type); } else if (type == coreTypes.boolType) { - backend.registerInstantiatedType(type, world, registry); + registerInstantiation(type); } else if (compiler.types.isSubtype( type, backend.backendClasses.listImplementation.rawType)) { - backend.registerInstantiatedType(type, world, registry); + registerInstantiation(type); } // TODO(johnniwinther): Improve spec string precision to handle type // arguments and implements relations that preserve generics. Currently @@ -494,60 +482,66 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { // any native subclasses of generic classes. // TODO(johnniwinther,sra): Find and replace uses of `List` with the // actual implementation classes such as `JSArray` et al. - enqueueUnusedClassesMatching((ClassElement nativeClass) { + matchingClasses + .addAll(_findUnusedClassesMatching((ClassElement nativeClass) { InterfaceType nativeType = nativeClass.thisType; InterfaceType specType = type.element.thisType; return compiler.types.isSubtype(nativeType, specType); - }, cause, 'subtypeof($type)'); + })); } else if (type.isDynamic) { - enqueueUnusedClassesMatching((_) => true, cause, 'subtypeof($type)'); + matchingClasses.addAll(unusedClasses); } else { assert(type is VoidType); } } + if (matchingClasses.isNotEmpty && registeredClasses.isEmpty) { + matchingClasses.addAll(_onFirstNativeClass(impactBuilder)); + } + _registerTypeUses(impactBuilder, matchingClasses, cause); // Give an info so that library developers can compile with -v to find why // all the native classes are included. - if (unusedClasses.isEmpty && !allUsedBefore) { + if (unusedBefore == matchingClasses.length) { reporter.log('All native types marked as used due to $cause.'); } } - enqueueUnusedClassesMatching(bool predicate(classElement), cause, - [String reason]) { - Iterable matches = unusedClasses.where(predicate); - matches.toList().forEach((c) => enqueueClass(c, cause)); + Iterable _findUnusedClassesMatching( + bool predicate(classElement)) { + return unusedClasses.where(predicate); } - onFirstNativeClass() { - staticUse(name) { - backend.enqueue( - world, helpers.findHelper(name), compiler.globalDependencies); + Iterable _onFirstNativeClass(WorldImpactBuilder impactBuilder) { + void staticUse(name) { + Element element = helpers.findHelper(name); + impactBuilder.registerStaticUse(new StaticUse.foreignUse(element)); + backend.registerBackendUse(element); + compiler.globalDependencies.registerDependency(element); } staticUse('defineProperty'); staticUse('toStringForNativeObject'); staticUse('hashCodeForNativeObject'); staticUse('convertDartClosureToJS'); - addNativeExceptions(); + return _findNativeExceptions(); } - addNativeExceptions() { - enqueueUnusedClassesMatching((classElement) { + Iterable _findNativeExceptions() { + return _findUnusedClassesMatching((classElement) { // TODO(sra): Annotate exception classes in dart:html. String name = classElement.name; if (name.contains('Exception')) return true; if (name.contains('Error')) return true; return false; - }, 'native exception'); + }); } } class NativeResolutionEnqueuer extends NativeEnqueuerBase { Map tagOwner = new Map(); - NativeResolutionEnqueuer(Enqueuer world, Compiler compiler) - : super(world, compiler, compiler.options.enableNativeLiveTypeAnalysis); + NativeResolutionEnqueuer(Compiler compiler) + : super(compiler, compiler.options.enableNativeLiveTypeAnalysis); void processNativeClass(ClassElement classElement) { super.processNativeClass(classElement); @@ -623,37 +617,46 @@ class NativeCodegenEnqueuer extends NativeEnqueuerBase { final Set doneAddSubtypes = new Set(); - NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter) - : super(world, compiler, compiler.options.enableNativeLiveTypeAnalysis); + NativeCodegenEnqueuer(Compiler compiler, this.emitter) + : super(compiler, compiler.options.enableNativeLiveTypeAnalysis); - void processNativeClasses(Iterable libraries) { - super.processNativeClasses(libraries); + void processNativeClasses( + WorldImpactBuilder impactBuilder, Iterable libraries) { + super.processNativeClasses(impactBuilder, libraries); // HACK HACK - add all the resolved classes. NativeEnqueuerBase enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; + Set matchingClasses = new Set(); for (final classElement in enqueuer.registeredClasses) { if (unusedClasses.contains(classElement)) { - enqueueClass(classElement, 'was resolved'); + matchingClasses.add(classElement); } } - flushQueue(); + if (matchingClasses.isNotEmpty && registeredClasses.isEmpty) { + matchingClasses.addAll(_onFirstNativeClass(impactBuilder)); + } + _registerTypeUses(impactBuilder, matchingClasses, 'was resolved'); } - processClass(ClassElement classElement, cause) { - super.processClass(classElement, cause); - // Add the information that this class is a subtype of its supertypes. The - // code emitter and the ssa builder use that information. - addSubtypes(classElement, emitter.nativeEmitter); + void _registerTypeUses( + WorldImpactBuilder impactBuilder, Set classes, cause) { + super._registerTypeUses(impactBuilder, classes, cause); + + for (ClassElement classElement in classes) { + // Add the information that this class is a subtype of its supertypes. The + // code emitter and the ssa builder use that information. + _addSubtypes(classElement, emitter.nativeEmitter); + } } - void addSubtypes(ClassElement cls, NativeEmitter emitter) { + void _addSubtypes(ClassElement cls, NativeEmitter emitter) { if (!backend.isNative(cls)) return; if (doneAddSubtypes.contains(cls)) return; doneAddSubtypes.add(cls); // Walk the superclass chain since classes on the superclass chain might not // be instantiated (abstract or simply unused). - addSubtypes(cls.superclass, emitter); + _addSubtypes(cls.superclass, emitter); for (DartType type in cls.allSupertypes) { List subtypes = diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart index 90a5c04c737d..14fd016fcbf3 100644 --- a/pkg/compiler/lib/src/ssa/builder.dart +++ b/pkg/compiler/lib/src/ssa/builder.dart @@ -400,7 +400,7 @@ class SsaBuilder extends ast.Visitor // TODO(johnniwinther): Register this on the [registry]. Currently the // [CodegenRegistry] calls the enqueuer, but [element] should _not_ be // enqueued. - backend.registerStaticUse(element, forResolution: false); + backend.registerStaticUse(compiler.enqueuer.codegen, element); if (backend.isJsInterop(element) && !element.isFactoryConstructor) { // We only inline factory JavaScript interop constructors. diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index 968f9e05b552..5248f915e10c 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -1933,7 +1933,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { void registerForeignTypes(HForeign node) { native.NativeBehavior nativeBehavior = node.nativeBehavior; if (nativeBehavior == null) return; - nativeEnqueuer.registerNativeBehavior(nativeBehavior, node); + nativeEnqueuer.registerNativeBehavior( + registry.worldImpact, nativeBehavior, node); } visitForeignCode(HForeignCode node) { diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart index 7cb642eae11b..5876a1814f49 100644 --- a/pkg/compiler/lib/src/universe/use.dart +++ b/pkg/compiler/lib/src/universe/use.dart @@ -323,6 +323,8 @@ enum TypeUseKind { CATCH_TYPE, TYPE_LITERAL, INSTANTIATION, + MIRROR_INSTANTIATION, + NATIVE_INSTANTIATION, } /// Use of a [DartType]. @@ -366,6 +368,16 @@ class TypeUse { return new TypeUse.internal(type, TypeUseKind.INSTANTIATION); } + /// [type] used in an instantiation through mirrors. + factory TypeUse.mirrorInstantiation(InterfaceType type) { + return new TypeUse.internal(type, TypeUseKind.MIRROR_INSTANTIATION); + } + + /// [type] used in a native instantiation. + factory TypeUse.nativeInstantiation(InterfaceType type) { + return new TypeUse.internal(type, TypeUseKind.NATIVE_INSTANTIATION); + } + bool operator ==(other) { if (identical(this, other)) return true; if (other is! TypeUse) return false; diff --git a/pkg/compiler/tool/perf.dart b/pkg/compiler/tool/perf.dart index 2cd9d0c38d45..348570d0cb93 100644 --- a/pkg/compiler/tool/perf.dart +++ b/pkg/compiler/tool/perf.dart @@ -382,7 +382,7 @@ class MyCompiler extends CompilerImpl { fullyEnqueueLibrary(library, enqueuer.resolution); }); - backend.enqueueHelpers(enqueuer.resolution, globalDependencies); + backend.enqueueHelpers(enqueuer.resolution); resolveLibraryMetadata(); reporter.log('Resolving...'); processQueue(enqueuer.resolution, mainFunction); diff --git a/pkg/dart2js_incremental/lib/caching_compiler.dart b/pkg/dart2js_incremental/lib/caching_compiler.dart index 5582bab2098b..8c6357fd5cd9 100644 --- a/pkg/dart2js_incremental/lib/caching_compiler.dart +++ b/pkg/dart2js_incremental/lib/caching_compiler.dart @@ -35,17 +35,19 @@ Future reuseCompiler( environment = {}; } CompilerImpl compiler = cachedCompiler; + JavaScriptBackend backend = compiler?.backend; if (compiler == null || compiler.libraryRoot != libraryRoot || !compiler.options.hasIncrementalSupport || compiler.hasCrashed || - compiler.enqueuer.resolution.hasEnqueuedReflectiveElements || + backend.mirrorsAnalysis.resolutionHandler.hasEnqueuedReflectiveElements || compiler.deferredLoadTask.isProgramSplit) { if (compiler != null && compiler.options.hasIncrementalSupport) { print('***FLUSH***'); if (compiler.hasCrashed) { print('Unable to reuse compiler due to crash.'); - } else if (compiler.enqueuer.resolution.hasEnqueuedReflectiveElements) { + } else if (backend.mirrorsAnalysis.resolutionHandler + .hasEnqueuedReflectiveElements) { print('Unable to reuse compiler due to dart:mirrors.'); } else if (compiler.deferredLoadTask.isProgramSplit) { print('Unable to reuse compiler due to deferred loading.'); @@ -64,7 +66,7 @@ Future reuseCompiler( packageConfig: packageConfig, options: options, environment: environment)); - JavaScriptBackend backend = compiler.backend; + backend = compiler.backend; full.Emitter emitter = backend.emitter.emitter; @@ -83,8 +85,7 @@ Future reuseCompiler( // Likewise, always be prepared for runtimeType support. // TODO(johnniwinther): Add global switch to force RTI. compiler.enabledRuntimeType = true; - backend.registerRuntimeType( - compiler.enqueuer.resolution, compiler.globalDependencies); + backend.registerRuntimeType(compiler.enqueuer.resolution); return compiler; }); }); diff --git a/tests/compiler/dart2js/compiler_helper.dart b/tests/compiler/dart2js/compiler_helper.dart index c9bd50801dff..a81af12005e6 100644 --- a/tests/compiler/dart2js/compiler_helper.dart +++ b/tests/compiler/dart2js/compiler_helper.dart @@ -72,8 +72,7 @@ Future compile(String code, lego.Element element = compiler.mainApp.find(entry); if (element == null) return null; compiler.phase = Compiler.PHASE_RESOLVING; - compiler.backend.enqueueHelpers( - compiler.enqueuer.resolution, compiler.globalDependencies); + compiler.backend.enqueueHelpers(compiler.enqueuer.resolution); compiler.processQueue(compiler.enqueuer.resolution, element); compiler.openWorld.closeWorld(); compiler.backend.onResolutionComplete(); diff --git a/tests/compiler/dart2js/deferred_load_graph_segmentation_test.dart b/tests/compiler/dart2js/deferred_load_graph_segmentation_test.dart index 5e67cc33635e..0563a637886f 100644 --- a/tests/compiler/dart2js/deferred_load_graph_segmentation_test.dart +++ b/tests/compiler/dart2js/deferred_load_graph_segmentation_test.dart @@ -24,7 +24,6 @@ void main() { var main = compiler.mainFunction; Expect.isNotNull(main, "Could not find 'main'"); - compiler.deferredLoadTask.onResolutionComplete(main); var outputUnitForElement = compiler.deferredLoadTask.outputUnitForElement; diff --git a/tests/compiler/dart2js/mirror_tree_shaking_test.dart b/tests/compiler/dart2js/mirror_tree_shaking_test.dart index 3618c816fdac..744d36eee996 100644 --- a/tests/compiler/dart2js/mirror_tree_shaking_test.dart +++ b/tests/compiler/dart2js/mirror_tree_shaking_test.dart @@ -16,15 +16,18 @@ main() { CompilationResult result = await runCompiler( memorySourceFiles: MEMORY_SOURCE_FILES, diagnosticHandler: collector); Compiler compiler = result.compiler; + JavaScriptBackend backend = compiler.backend; Expect.isTrue(collector.errors.isEmpty); Expect.isTrue(collector.infos.isEmpty); Expect.isFalse(compiler.compilationFailed); - Expect.isFalse(compiler.enqueuer.resolution.hasEnqueuedReflectiveElements); + Expect.isFalse(backend + .mirrorsAnalysis.resolutionHandler.hasEnqueuedReflectiveElements); + Expect.isFalse(backend + .mirrorsAnalysis.resolutionHandler.hasEnqueuedReflectiveStaticFields); Expect.isFalse( - compiler.enqueuer.resolution.hasEnqueuedReflectiveStaticFields); - JavaScriptBackend backend = compiler.backend; - Expect.isFalse(backend.codegenEnqueuer.hasEnqueuedReflectiveElements); - Expect.isFalse(backend.codegenEnqueuer.hasEnqueuedReflectiveStaticFields); + backend.mirrorsAnalysis.codegenHandler.hasEnqueuedReflectiveElements); + Expect.isFalse(backend + .mirrorsAnalysis.codegenHandler.hasEnqueuedReflectiveStaticFields); Expect.isFalse(compiler.disableTypeInference); Expect.isFalse(backend.hasRetainedMetadata); }); diff --git a/tests/compiler/dart2js/type_combination_test.dart b/tests/compiler/dart2js/type_combination_test.dart index f15d471f1ad7..5ca7197b4751 100644 --- a/tests/compiler/dart2js/type_combination_test.dart +++ b/tests/compiler/dart2js/type_combination_test.dart @@ -740,19 +740,18 @@ void main() { helpers.interceptorsLibrary.forEachLocalMember((element) { if (element.isClass) { element.ensureResolved(compiler.resolution); - backend.registerInstantiatedType(element.rawType, - compiler.enqueuer.resolution, compiler.globalDependencies); + compiler.enqueuer.resolution.registerInstantiatedType(element.rawType); } }); ClassElement patternImplClass = compiler.mainApp.find('PatternImpl'); patternImplClass.ensureResolved(compiler.resolution); - backend.registerInstantiatedType(compiler.coreTypes.mapType(), - compiler.enqueuer.resolution, compiler.globalDependencies); - backend.registerInstantiatedType(compiler.coreTypes.functionType, - compiler.enqueuer.resolution, compiler.globalDependencies); - backend.registerInstantiatedType(patternImplClass.rawType, - compiler.enqueuer.resolution, compiler.globalDependencies); + compiler.enqueuer.resolution + .registerInstantiatedType(compiler.coreTypes.mapType()); + compiler.enqueuer.resolution + .registerInstantiatedType(compiler.coreTypes.functionType); + compiler.enqueuer.resolution + .registerInstantiatedType(patternImplClass.rawType); compiler.openWorld.closeWorld(); // Grab hold of a supertype for String so we can produce potential