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 = |