Chromium Code Reviews| Index: pkg/compiler/lib/src/native/enqueue.dart |
| diff --git a/pkg/compiler/lib/src/native/enqueue.dart b/pkg/compiler/lib/src/native/enqueue.dart |
| index 7c3f125ac0852cb220e35cd2ff53f416b2dd3400..3784271ae3dbd9c0fe35ae8d34fa14d7fb450649 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<LibraryElement> libraries) {} |
| + void processNativeClasses( |
| + WorldImpactBuilder impactBuilder, Iterable<LibraryElement> 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<LibraryElement> libraries) { |
| + void onInstantiatedType(InterfaceType type) { |
| + if (unusedClasses.remove(type.element)) { |
| + registeredClasses.add(type.element); |
| + } |
| + } |
| + |
| + void processNativeClasses( |
| + WorldImpactBuilder impactBuilder, Iterable<LibraryElement> 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,20 @@ 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<ClassElement> classes, cause) { |
| + bool maybeFirstTime = registeredClasses.isEmpty; |
|
Harry Terkelsen
2016/11/11 21:24:27
is this used anywhere?
Johnni Winther
2016/11/14 09:08:36
No.
|
| + 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 +436,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 instantiated(InterfaceType type) { |
| + impactBuilder.registerTypeUse(new TypeUse.nativeInstantiation(type)); |
| + } |
| + |
| + int unusedBefore = unusedClasses.length; |
| + Set<ClassElement> matchingClasses = new Set<ClassElement>(); |
| 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); |
| + instantiated(compiler.coreTypes.objectType); |
| } |
| continue; |
| } |
| if (type is InterfaceType) { |
| if (type == coreTypes.intType) { |
| - backend.registerInstantiatedType(type, world, registry); |
| + instantiated(type); |
| } else if (type == coreTypes.doubleType) { |
| - backend.registerInstantiatedType(type, world, registry); |
| + instantiated(type); |
| } else if (type == coreTypes.numType) { |
| - backend.registerInstantiatedType( |
| - coreTypes.doubleType, world, registry); |
| - backend.registerInstantiatedType(coreTypes.intType, world, registry); |
| + instantiated(coreTypes.doubleType); |
| + instantiated(coreTypes.doubleType); |
|
Harry Terkelsen
2016/11/11 21:24:27
doubleType -> intType
Johnni Winther
2016/11/14 09:08:36
Good catch!
|
| } else if (type == coreTypes.stringType) { |
| - backend.registerInstantiatedType(type, world, registry); |
| + instantiated(type); |
| } else if (type == coreTypes.nullType) { |
| - backend.registerInstantiatedType(type, world, registry); |
| + instantiated(type); |
| } else if (type == coreTypes.boolType) { |
| - backend.registerInstantiatedType(type, world, registry); |
| + instantiated(type); |
| } else if (compiler.types.isSubtype( |
| type, backend.backendClasses.listImplementation.rawType)) { |
| - backend.registerInstantiatedType(type, world, registry); |
| + instantiated(type); |
| } |
| // TODO(johnniwinther): Improve spec string precision to handle type |
| // arguments and implements relations that preserve generics. Currently |
| @@ -494,60 +483,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<ClassElement> _findUnusedClassesMatching( |
| + bool predicate(classElement)) { |
| + return unusedClasses.where(predicate); |
| } |
| - onFirstNativeClass() { |
| - staticUse(name) { |
| - backend.enqueue( |
| - world, helpers.findHelper(name), compiler.globalDependencies); |
| + Iterable<ClassElement> _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<ClassElement> _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<String, ClassElement> tagOwner = new Map<String, ClassElement>(); |
| - 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 +618,46 @@ class NativeCodegenEnqueuer extends NativeEnqueuerBase { |
| final Set<ClassElement> doneAddSubtypes = new Set<ClassElement>(); |
| - 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<LibraryElement> libraries) { |
| - super.processNativeClasses(libraries); |
| + void processNativeClasses( |
| + WorldImpactBuilder impactBuilder, Iterable<LibraryElement> libraries) { |
| + super.processNativeClasses(impactBuilder, libraries); |
| // HACK HACK - add all the resolved classes. |
| NativeEnqueuerBase enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; |
| + Set<ClassElement> matchingClasses = new Set<ClassElement>(); |
| 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<ClassElement> 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 |
|
Harry Terkelsen
2016/11/11 21:24:27
long line
Johnni Winther
2016/11/14 09:08:36
Done.
|
| + // 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<Element> subtypes = |