| Index: pkg/compiler/lib/src/js_backend/enqueuer.dart
|
| diff --git a/pkg/compiler/lib/src/js_backend/enqueuer.dart b/pkg/compiler/lib/src/js_backend/enqueuer.dart
|
| index 4784df02cf69b31160c2a68d7a8e4d8efb5820ac..673d186adcd58f3a2c891b9e6ca9d5883fa5a7e3 100644
|
| --- a/pkg/compiler/lib/src/js_backend/enqueuer.dart
|
| +++ b/pkg/compiler/lib/src/js_backend/enqueuer.dart
|
| @@ -9,49 +9,34 @@ import 'dart:collection' show Queue;
|
| import '../cache_strategy.dart' show CacheStrategy;
|
| import '../common/backend_api.dart' show Backend;
|
| import '../common/codegen.dart' show CodegenWorkItem;
|
| -import '../common/names.dart' show Identifiers;
|
| import '../common/tasks.dart' show CompilerTask;
|
| import '../common/work.dart' show WorkItem;
|
| import '../common.dart';
|
| import '../compiler.dart' show Compiler;
|
| import '../dart_types.dart' show DartType, InterfaceType;
|
| -import '../dump_info.dart';
|
| import '../elements/elements.dart'
|
| - show
|
| - ClassElement,
|
| - Element,
|
| - Entity,
|
| - FunctionElement,
|
| - MemberElement,
|
| - MethodElement,
|
| - TypedElement;
|
| + show ClassElement, Element, Entity, MemberElement, TypedElement;
|
| import '../elements/entities.dart';
|
| import '../enqueue.dart';
|
| import '../js/js.dart' as js;
|
| import '../native/native.dart' as native;
|
| import '../options.dart';
|
| import '../types/types.dart' show TypeMaskStrategy;
|
| -import '../universe/selector.dart' show Selector;
|
| import '../universe/world_builder.dart';
|
| import '../universe/use.dart'
|
| show DynamicUse, StaticUse, StaticUseKind, TypeUse, TypeUseKind;
|
| import '../universe/world_impact.dart'
|
| show ImpactUseCase, WorldImpact, WorldImpactVisitor;
|
| +import '../util/enumset.dart';
|
| import '../util/util.dart' show Setlet;
|
| -import '../world.dart';
|
|
|
| /// [Enqueuer] which is specific to code generation.
|
| class CodegenEnqueuer extends EnqueuerImpl {
|
| final String name;
|
| final EnqueuerStrategy strategy;
|
| - final Map<String, Set<Element>> _instanceMembersByName =
|
| - new Map<String, Set<Element>>();
|
| - final Map<String, Set<Element>> _instanceFunctionsByName =
|
| - new Map<String, Set<Element>>();
|
| - final Set<ClassElement> _processedClasses = new Set<ClassElement>();
|
| +
|
| Set<ClassElement> _recentClasses = new Setlet<ClassElement>();
|
| - final CodegenWorldBuilderImpl _universe =
|
| - new CodegenWorldBuilderImpl(const TypeMaskStrategy());
|
| + final CodegenWorldBuilderImpl _universe;
|
|
|
| bool queueIsClosed = false;
|
| final CompilerTask task;
|
| @@ -68,14 +53,14 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|
|
| final Set<DynamicUse> newlySeenSelectors;
|
|
|
| - bool _enabledNoSuchMethod = false;
|
| -
|
| static const ImpactUseCase IMPACT_USE =
|
| const ImpactUseCase('CodegenEnqueuer');
|
|
|
| CodegenEnqueuer(this.task, CacheStrategy cacheStrategy, Backend backend,
|
| this._options, this.strategy)
|
| - : newlyEnqueuedElements = cacheStrategy.newSet(),
|
| + : _universe =
|
| + new CodegenWorldBuilderImpl(backend, const TypeMaskStrategy()),
|
| + newlyEnqueuedElements = cacheStrategy.newSet(),
|
| newlySeenSelectors = cacheStrategy.newSet(),
|
| nativeEnqueuer = backend.nativeCodegenEnqueuer(),
|
| this._backend = backend,
|
| @@ -85,10 +70,6 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|
|
| CodegenWorldBuilder get universe => _universe;
|
|
|
| - // TODO(johnniwinther): Remove these hacks:
|
| - ClosedWorld get _world => _backend.compiler.closedWorld;
|
| - DumpInfoTask get _dumpInfoTask => _backend.compiler.dumpInfoTask;
|
| -
|
| bool get queueIsEmpty => _queue.isEmpty;
|
|
|
| /// Returns [:true:] if this enqueuer is the resolution enqueuer.
|
| @@ -122,9 +103,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
| element, "Codegen work list is closed. Trying to add $element");
|
| }
|
| _queue.add(new CodegenWorkItem(_backend, element));
|
| - // TODO(sigmund): add other missing dependencies (internals, selectors
|
| - // enqueued after allocations).
|
| - _dumpInfoTask.registerDependency(element);
|
| + applyImpact(_backend.registerUsedElement(element, forResolution: false));
|
| }
|
|
|
| void applyImpact(WorldImpact worldImpact, {Element impactSource}) {
|
| @@ -136,22 +115,12 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
| 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) {
|
| - applyImpact(
|
| - _backend.registerImplementedClass(cls, forResolution: false));
|
| - });
|
| + _universe.registerTypeInstantiation(type, _applyClassUse,
|
| + byMirrors: mirrorUsage);
|
| if (nativeUsage) {
|
| nativeEnqueuer.onInstantiatedType(type);
|
| }
|
| _backend.registerInstantiatedType(type);
|
| - // TODO(johnniwinther): Share this reasoning with [Universe].
|
| - if (!cls.isAbstract || isNative || mirrorUsage) {
|
| - _processInstantiatedClass(cls);
|
| - }
|
| });
|
| }
|
|
|
| @@ -160,224 +129,67 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
| }
|
|
|
| void checkClass(ClassElement cls) {
|
| - cls.implementation.forEachMember(processInstantiatedClassMember);
|
| - }
|
| -
|
| - void processInstantiatedClassMember(ClassElement cls, Element member) {
|
| - assert(invariant(member, member.isDeclaration));
|
| - if (isProcessed(member)) return;
|
| - if (!member.isInstanceMember) return;
|
| - String memberName = member.name;
|
| -
|
| - if (member.isField) {
|
| - // The obvious thing to test here would be "member.isNative",
|
| - // however, that only works after metadata has been parsed/analyzed,
|
| - // and that may not have happened yet.
|
| - // So instead we use the enclosing class, which we know have had
|
| - // its metadata parsed and analyzed.
|
| - // Note: this assumes that there are no non-native fields on native
|
| - // classes, which may not be the case when a native class is subclassed.
|
| - if (_backend.isNative(cls)) {
|
| - if (_universe.hasInvokedGetter(member, _world) ||
|
| - _universe.hasInvocation(member, _world)) {
|
| - _addToWorkList(member);
|
| - return;
|
| - } else if (universe.hasInvokedSetter(member, _world)) {
|
| - _addToWorkList(member);
|
| - return;
|
| - }
|
| - // Native fields need to go into instanceMembersByName as they
|
| - // are virtual instantiation points and escape points.
|
| - } else {
|
| - // All field initializers must be resolved as they could
|
| - // have an observable side-effect (and cannot be tree-shaken
|
| - // away).
|
| - _addToWorkList(member);
|
| - return;
|
| - }
|
| - } else if (member.isFunction) {
|
| - FunctionElement function = member;
|
| - if (function.name == Identifiers.noSuchMethod_) {
|
| - _registerNoSuchMethod(function);
|
| - }
|
| - if (function.name == Identifiers.call && !cls.typeVariables.isEmpty) {
|
| - _registerCallMethodWithFreeTypeVariables(function);
|
| - }
|
| - // If there is a property access with the same name as a method we
|
| - // need to emit the method.
|
| - if (_universe.hasInvokedGetter(function, _world)) {
|
| - _registerClosurizedMember(function);
|
| - _addToWorkList(function);
|
| - return;
|
| - }
|
| - _registerInstanceMethod(function);
|
| - if (_universe.hasInvocation(function, _world)) {
|
| - _addToWorkList(function);
|
| - return;
|
| - }
|
| - } else if (member.isGetter) {
|
| - FunctionElement getter = member;
|
| - if (_universe.hasInvokedGetter(getter, _world)) {
|
| - _addToWorkList(getter);
|
| - return;
|
| - }
|
| - // We don't know what selectors the returned closure accepts. If
|
| - // the set contains any selector we have to assume that it matches.
|
| - if (_universe.hasInvocation(getter, _world)) {
|
| - _addToWorkList(getter);
|
| - return;
|
| - }
|
| - } else if (member.isSetter) {
|
| - FunctionElement setter = member;
|
| - if (_universe.hasInvokedSetter(setter, _world)) {
|
| - _addToWorkList(setter);
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // The element is not yet used. Add it to the list of instance
|
| - // members to still be processed.
|
| - _instanceMembersByName
|
| - .putIfAbsent(memberName, () => new Set<Element>())
|
| - .add(member);
|
| - }
|
| -
|
| - // Store the member in [instanceFunctionsByName] to catch
|
| - // getters on the function.
|
| - void _registerInstanceMethod(MethodElement element) {
|
| - _instanceFunctionsByName
|
| - .putIfAbsent(element.name, () => new Set<Element>())
|
| - .add(element);
|
| - }
|
| -
|
| - void _processInstantiatedClass(ClassElement cls) {
|
| - task.measure(() {
|
| - if (_processedClasses.contains(cls)) return;
|
| -
|
| - void processClass(ClassElement superclass) {
|
| - if (_processedClasses.contains(superclass)) return;
|
| - // TODO(johnniwinther): Re-insert this invariant when unittests don't
|
| - // fail. There is already a similar invariant on the members.
|
| - /*assert(invariant(superclass,
|
| - superclass.isClosure ||
|
| - _compiler.enqueuer.resolution.isClassProcessed(superclass),
|
| - message: "Class $superclass has not been "
|
| - "processed in resolution."));
|
| - */
|
| -
|
| - _processedClasses.add(superclass);
|
| - _recentClasses.add(superclass);
|
| - superclass.implementation.forEachMember(processInstantiatedClassMember);
|
| - // We only tell the backend once that [superclass] was instantiated, so
|
| - // any additional dependencies must be treated as global
|
| - // dependencies.
|
| - applyImpact(_backend.registerInstantiatedClass(superclass,
|
| - forResolution: false));
|
| - }
|
| -
|
| - ClassElement superclass = cls;
|
| - while (superclass != null) {
|
| - processClass(superclass);
|
| - superclass = superclass.superclass;
|
| - }
|
| - });
|
| - }
|
| -
|
| - void processDynamicUse(DynamicUse dynamicUse) {
|
| - task.measure(() {
|
| - if (_universe.registerDynamicUse(dynamicUse)) {
|
| - _handleUnseenSelector(dynamicUse);
|
| + _universe.processClassMembers(cls, (MemberElement member, useSet) {
|
| + if (useSet.isNotEmpty) {
|
| + _backend.compiler.reporter.internalError(member,
|
| + 'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
|
| }
|
| });
|
| }
|
|
|
| - void _processSet(
|
| - Map<String, Set<Element>> map, String memberName, bool f(Element e)) {
|
| - Set<Element> members = map[memberName];
|
| - if (members == null) return;
|
| - // [f] might add elements to [: map[memberName] :] during the loop below
|
| - // so we create a new list for [: map[memberName] :] and prepend the
|
| - // [remaining] members after the loop.
|
| - map[memberName] = new Set<Element>();
|
| - Set<Element> remaining = new Set<Element>();
|
| - for (Element member in members) {
|
| - if (!f(member)) remaining.add(member);
|
| + /// Callback for applying the use of a [cls].
|
| + void _applyClassUse(ClassEntity cls, EnumSet<ClassUse> useSet) {
|
| + if (useSet.contains(ClassUse.INSTANTIATED)) {
|
| + _recentClasses.add(cls);
|
| + _universe.processClassMembers(cls, _applyMemberUse);
|
| + // We only tell the backend once that [cls] was instantiated, so
|
| + // any additional dependencies must be treated as global
|
| + // dependencies.
|
| + applyImpact(
|
| + _backend.registerInstantiatedClass(cls, forResolution: false));
|
| + }
|
| + if (useSet.contains(ClassUse.IMPLEMENTED)) {
|
| + applyImpact(_backend.registerImplementedClass(cls, forResolution: false));
|
| }
|
| - map[memberName].addAll(remaining);
|
| - }
|
| -
|
| - void _processInstanceMembers(String n, bool f(Element e)) {
|
| - _processSet(_instanceMembersByName, n, f);
|
| }
|
|
|
| - void _processInstanceFunctions(String n, bool f(Element e)) {
|
| - _processSet(_instanceFunctionsByName, n, f);
|
| + /// Callback for applying the use of a [member].
|
| + void _applyMemberUse(Entity member, EnumSet<MemberUse> useSet) {
|
| + if (useSet.contains(MemberUse.NORMAL)) {
|
| + _addToWorkList(member);
|
| + }
|
| + if (useSet.contains(MemberUse.CLOSURIZE_INSTANCE)) {
|
| + _registerClosurizedMember(member);
|
| + }
|
| + if (useSet.contains(MemberUse.CLOSURIZE_STATIC)) {
|
| + applyImpact(_backend.registerGetOfStaticFunction());
|
| + }
|
| }
|
|
|
| - void _handleUnseenSelector(DynamicUse dynamicUse) {
|
| - if (_options.hasIncrementalSupport) {
|
| - newlySeenSelectors.add(dynamicUse);
|
| - }
|
| - Selector selector = dynamicUse.selector;
|
| - String methodName = selector.name;
|
| - _processInstanceMembers(methodName, (Element member) {
|
| - if (dynamicUse.appliesUnnamed(member, _world)) {
|
| - if (member.isFunction && selector.isGetter) {
|
| - _registerClosurizedMember(member);
|
| + void processDynamicUse(DynamicUse dynamicUse) {
|
| + task.measure(() {
|
| + if (_universe.registerDynamicUse(dynamicUse, _applyMemberUse)) {
|
| + if (_options.hasIncrementalSupport) {
|
| + newlySeenSelectors.add(dynamicUse);
|
| }
|
| - _addToWorkList(member);
|
| - return true;
|
| }
|
| - return false;
|
| });
|
| - if (selector.isGetter) {
|
| - _processInstanceFunctions(methodName, (Element member) {
|
| - if (dynamicUse.appliesUnnamed(member, _world)) {
|
| - _registerClosurizedMember(member);
|
| - return true;
|
| - }
|
| - return false;
|
| - });
|
| - }
|
| }
|
|
|
| void processStaticUse(StaticUse staticUse) {
|
| Element element = staticUse.element;
|
| assert(invariant(element, element.isDeclaration,
|
| message: "Element ${element} is not the declaration."));
|
| - _universe.registerStaticUse(staticUse);
|
| - applyImpact(_backend.registerUsedElement(element, forResolution: false));
|
| - bool addElement = true;
|
| + _universe.registerStaticUse(staticUse, _applyMemberUse);
|
| switch (staticUse.kind) {
|
| - case StaticUseKind.STATIC_TEAR_OFF:
|
| - applyImpact(_backend.registerGetOfStaticFunction());
|
| - break;
|
| - case StaticUseKind.FIELD_GET:
|
| - case StaticUseKind.FIELD_SET:
|
| - case StaticUseKind.CLOSURE:
|
| - // TODO(johnniwinther): Avoid this. Currently [FIELD_GET] and
|
| - // [FIELD_SET] contains [BoxFieldElement]s which we cannot enqueue.
|
| - // Also [CLOSURE] contains [LocalFunctionElement] which we cannot
|
| - // enqueue.
|
| - addElement = false;
|
| - break;
|
| - case StaticUseKind.SUPER_FIELD_SET:
|
| - case StaticUseKind.SUPER_TEAR_OFF:
|
| - case StaticUseKind.GENERAL:
|
| - case StaticUseKind.DIRECT_USE:
|
| - break;
|
| case StaticUseKind.CONSTRUCTOR_INVOKE:
|
| case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
| case StaticUseKind.REDIRECTION:
|
| processTypeUse(new TypeUse.instantiation(staticUse.type));
|
| break;
|
| - case StaticUseKind.DIRECT_INVOKE:
|
| - _registerInstanceMethod(staticUse.element);
|
| + default:
|
| break;
|
| }
|
| - if (addElement) {
|
| - _addToWorkList(element);
|
| - }
|
| }
|
|
|
| void processTypeUse(TypeUse typeUse) {
|
| @@ -415,11 +227,6 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
| assert(!type.isTypeVariable || !type.element.enclosingElement.isTypedef);
|
| }
|
|
|
| - void _registerCallMethodWithFreeTypeVariables(Element element) {
|
| - applyImpact(_backend.registerCallMethodWithFreeTypeVariables(element,
|
| - forResolution: false));
|
| - }
|
| -
|
| void _registerClosurizedMember(TypedElement element) {
|
| assert(element.isInstanceMember);
|
| if (element.type.containsTypeVariables) {
|
| @@ -468,24 +275,12 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
| bool isProcessed(Element member) =>
|
| member.isAbstract || generatedCode.containsKey(member);
|
|
|
| - void _registerNoSuchMethod(Element element) {
|
| - if (!_enabledNoSuchMethod && _backend.enabledNoSuchMethod) {
|
| - applyImpact(_backend.enableNoSuchMethod());
|
| - _enabledNoSuchMethod = true;
|
| - }
|
| - }
|
| -
|
| void forgetEntity(Element element, Compiler compiler) {
|
| _universe.forgetElement(element, compiler);
|
| - _processedClasses.remove(element);
|
| - _instanceMembersByName[element.name]?.remove(element);
|
| - _instanceFunctionsByName[element.name]?.remove(element);
|
| generatedCode.remove(element);
|
| if (element is MemberElement) {
|
| for (Element closure in element.nestedClosures) {
|
| generatedCode.remove(closure);
|
| - removeFromSet(_instanceMembersByName, closure);
|
| - removeFromSet(_instanceFunctionsByName, closure);
|
| }
|
| }
|
| }
|
| @@ -494,11 +289,5 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
| Iterable<Entity> get processedEntities => generatedCode.keys;
|
|
|
| @override
|
| - Iterable<ClassEntity> get processedClasses => _processedClasses;
|
| -}
|
| -
|
| -void removeFromSet(Map<String, Set<Element>> map, Element element) {
|
| - Set<Element> set = map[element.name];
|
| - if (set == null) return;
|
| - set.remove(element);
|
| + Iterable<ClassEntity> get processedClasses => _universe.processedClasses;
|
| }
|
|
|