| 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..44d3be38efe79babbd177b20d6c70f5f8b9f2a6c 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,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<ClassElement> 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<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);
|
| + 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<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 +617,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
|
| + // 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 =
|
|
|