Index: sdk/lib/_internal/compiler/implementation/native_handler.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
index 0ba42aea08f475ea08ac938302867d44a04d2ed6..a3e82a5b5db809e38dc1eb4df7873cb6dd062863 100644 |
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart |
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
@@ -13,66 +13,294 @@ import 'ssa/ssa.dart'; |
import 'tree/tree.dart'; |
import 'util/util.dart'; |
-void processNativeClasses(Enqueuer world, |
- CodeEmitterTask emitter, |
- Collection<LibraryElement> libraries) { |
- for (LibraryElement library in libraries) { |
- processNativeClassesInLibrary(world, emitter, library); |
- } |
+ |
+// This could be an abstract class but we use it as a stub for the dart_backend. |
+class NativeEnqueuer { |
+ void processNativeClasses(Collection<LibraryElement> libraries) {} |
+ |
+ |
+ void registerElement(Element element) {} |
+ |
+ // Method is a member of a native class. |
+ void registerMethod(Element method) {} |
+ |
+ // Field is a member of a native class. |
+ void registerFieldLoad(Element field) {} |
+ void registerFieldStore(Element field) {} |
+ |
+ // JS-form code can be an instantiation point for types. |
+ // |
+ // JS('_DOMWindowImpl', 'window'); |
+ // |
+ // TODO(sra): Who calls this? |
+ // TODO(sra): How do we parse the type? |
+ // void registerJS(type) {} |
+ |
+ void registerJSDartType(DartType type, Node locationNode) {} |
} |
-void addSubtypes(ClassElement cls, |
- NativeEmitter emitter) { |
- for (DartType type in cls.allSupertypes) { |
- List<Element> subtypes = emitter.subtypes.putIfAbsent( |
- type.element, |
- () => <ClassElement>[]); |
- subtypes.add(cls); |
+ |
+class NativeEnqueuerBase implements NativeEnqueuer { |
ngeoffray
2012/11/08 08:18:01
A couple of high-level comments and alternatives a
sra1
2012/11/15 00:09:10
Enqueuer already needs to be split into a resoluti
|
+ |
+ final Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
+ |
+ // Each class is exactly one of these sets. |
+ final Set<ClassElement> registeredClasses = new Set<ClassElement>(); |
+ final Set<ClassElement> unusedClasses = new Set<ClassElement>(); |
+ final Set<ClassElement> pendingClasses = new Set<ClassElement>(); |
+ |
+ final queue = new Queue(); // Classes in pendingClasse have thunks in queue. |
+ bool flushing = false; |
+ |
+ final Set<DartType> escapedTypes = new Set<DartType>(); |
+ final Set<DartType> capturedTypes = new Set<DartType>(); |
+ |
+ |
+ final Enqueuer world; |
+ final Compiler compiler; |
+ |
+ // constructed by backend. |
+ NativeEnqueuerBase(this.world, this.compiler); |
+ |
+ void processNativeClasses(Collection<LibraryElement> libraries) { |
+ libraries.forEach(processNativeClassesInLibrary); |
+ |
+ // nativeClasses.forEach(enqueueClass); flushQueue(); |
+ } |
+ |
+ void processNativeClassesInLibrary(LibraryElement library) { |
+ // Use implementation to ensure the inclusion of injected members. |
+ library.implementation.forEachLocalMember((Element element) { |
+ if (element.kind == ElementKind.CLASS) { |
+ ClassElement classElement = element; |
+ if (classElement.isNative()) { |
+ nativeClasses.add(classElement); |
+ unusedClasses.add(classElement); |
+ |
+ // Resolve class to ensure the class has valid inheritance info. |
+ classElement.ensureResolved(compiler); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ enqueueClass(ClassElement classElement, cause) { |
+ assert(unusedClasses.contains(classElement)); |
+ compiler.log( |
+ 'Adding native class ${classElement.name.slowToString()},' |
+ ' why: $cause'); |
+ 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) { |
+ assert(!registeredClasses.contains(classElement)); |
+ |
+ bool firstTime = registeredClasses.isEmpty; |
+ pendingClasses.remove(classElement); |
+ registeredClasses.add(classElement); |
+ |
+ // print('processClass $classElement $cause'); |
+ world.registerInstantiatedClass(classElement); |
+ |
+ // Also parse the node to know all its methods because |
+ // otherwise it will only be parsed if there is a call to |
+ // one of its constructors. |
+ classElement.parseNode(compiler); |
+ |
+ if (firstTime) { |
+ queue.add(onFirstNativeClass); |
+ } |
+ } |
+ |
+ registerElement(Element element) { |
+ if (element.isFunction()) return registerMethod(element); |
+ } |
+ |
+ registerMethod(Element method) { |
+ if (isNativeMethod(method)) { |
+ // print('Native method call $method'); |
+ captureType(method.computeType(compiler), method); |
+ flushQueue(); |
+ } |
+ } |
+ |
+ bool isNativeMethod(Element element) { |
+ // Native method? |
+ Node node = element.parseNode(compiler); |
+ if (node is! FunctionExpression) return false; |
+ node = node.body; |
+ Token token = node.getBeginToken(); |
+ if (token.stringValue == 'native') return true; |
+ return false; |
+ } |
+ |
+ registerFieldLoad(Element field) { |
+ // print('Native load field $field'); |
+ captureType(field.computeType(compiler), field); |
+ flushQueue(); |
} |
- List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( |
- cls.superclass, |
- () => <ClassElement>[]); |
- directSubtypes.add(cls); |
+ registerFieldStore(Element field) { |
+ // print('Native store field $field'); |
+ escapeType(field.computeType(compiler), field); |
+ flushQueue(); |
+ } |
+ |
+ void registerJSDartType(DartType type, Node locationNode) { |
+ captureType(type, locationNode); |
+ flushQueue(); |
+ } |
+ |
+ escapeType(DartType type, cause) { |
+ type = type.unalias(compiler); |
+ if (escapedTypes.contains(type)) return; |
+ escapedTypes.add(type); |
+ // print(' escapeType $type'); |
+ |
+ if (type is FunctionType) { |
+ escapeType(type.returnType, cause); |
+ // TODO: use FunctionSignature. |
+ for (Link<DartType> parameters = type.parameterTypes; |
+ !parameters.isEmpty; |
+ parameters = parameters.tail) { |
+ captureType(parameters.head, cause); |
+ } |
+ } |
+ } |
+ |
+ captureType(DartType type, cause) { |
+ type = type.unalias(compiler); |
+ if (capturedTypes.contains(type)) return; |
+ capturedTypes.add(type); |
+ // print(' capturedType $type'); |
+ |
+ if (type is FunctionType) { |
+ captureType(type.returnType, cause); |
+ for (Link<DartType> parameters = type.parameterTypes; |
+ !parameters.isEmpty; |
+ parameters = parameters.tail) { |
+ escapeType(parameters.head, cause); |
+ } |
+ } else { |
+ bool allUsedBefore = unusedClasses.isEmpty; |
+ enqueueUnusedClassesMatching( |
+ (nativeClass) => compiler.types.isSubtype(nativeClass.type, type), |
+ cause, |
+ 'subtypeof($type)'); |
+ if (unusedClasses.isEmpty && !allUsedBefore) { |
+ if (cause is Node) { |
+ throw 'Eugh $cause'; |
+ compiler.log('All native types marked as used due to $cause.'); |
+ } else if (cause.position() != null) { |
+ //compiler.cancel('Eugh!', token: cause.position()); |
+ compiler.log('All native types marked used due to $cause.'); |
+ } else { |
+ compiler.log('All native types marked used due to $type.'); |
+ } |
+ } |
+ } |
+ |
+ // TODO(sra): Do we need to do something for capturing List<Node>? For now, |
+ // hope that NodeListImpl matches List, causing NodeListImpl.operator[] to |
+ // match some invocation, leading to instantiating Node. |
+ |
+ // Required for: |
+ // typedef void MutationCallback(List<MutationRecord> mutations, MutationObserver observer); |
+ // No other code instantiates MutationRecord. |
+ // We can work-around by annotating the MutationObserver constructor with a list of 'also created' type. |
+ |
+ } |
+ |
+ enqueueUnusedClassesMatching(predicate, cause, [reason]) { |
+ var matches = unusedClasses.filter(predicate); |
+ // print('${matches.length} matches: $matches\n.. $reason $world'); |
+ matches.forEach((c) => enqueueClass(c, cause)); |
+ } |
+ |
+ onFirstNativeClass() { |
+ staticUse(name) => world.registerStaticUse(compiler.findHelper(name)); |
+ |
+ staticUse(const SourceString('dynamicFunction')); |
+ staticUse(const SourceString('dynamicSetMetadata')); |
+ staticUse(const SourceString('defineProperty')); |
+ staticUse(const SourceString('toStringForNativeObject')); |
+ staticUse(const SourceString('hashCodeForNativeObject')); |
+ |
+ addNativeExceptions(); |
+ } |
+ |
+ addNativeExceptions() { |
+ enqueueUnusedClassesMatching((classElement) { |
+ // TODO(sra): Annotate exception classes in dart:html. |
+ var name = classElement.name.slowToString(); |
+ if (name.contains('Exception')) return true; |
+ if (name.contains('Error')) return true; |
+ return false; |
+ }, |
+ 'native exception'); |
+ } |
} |
-void processNativeClassesInLibrary(Enqueuer world, |
- CodeEmitterTask emitter, |
- LibraryElement library) { |
- bool hasNativeClass = false; |
- final compiler = emitter.compiler; |
- // Use implementation to ensure the inclusion of injected members. |
- library.implementation.forEachLocalMember((Element element) { |
- if (element.kind == ElementKind.CLASS) { |
- ClassElement classElement = element; |
- if (classElement.isNative()) { |
- hasNativeClass = true; |
- world.registerInstantiatedClass(classElement); |
- // Also parse the node to know all its methods because |
- // otherwise it will only be parsed if there is a call to |
- // one of its constructor. |
- classElement.parseNode(compiler); |
- // Resolve to setup the inheritance. |
- classElement.ensureResolved(compiler); |
- // 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); |
+ |
+class NativeResolutionEnqueuer extends NativeEnqueuerBase { |
+ |
+ NativeResolutionEnqueuer(Enqueuer world, Compiler compiler) |
+ : super(world, compiler); |
+} |
+ |
+ |
+class NativeCodegenEnqueuer extends NativeEnqueuerBase { |
+ |
+ final CodeEmitterTask emitter; |
+ |
+ NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter) |
+ : super(world, compiler); |
+ |
+ void processNativeClasses(Collection<LibraryElement> libraries) { |
+ super.processNativeClasses(libraries); |
+ |
+ // HACK HACK - add all the resolved classes. |
+ for (final classElement |
+ in compiler.enqueuer.resolution.nativeEnqueuer.registeredClasses) { |
+ if (unusedClasses.contains(classElement)) { |
+ enqueueClass(classElement, 'was resolved'); |
} |
} |
- }); |
- if (hasNativeClass) { |
- world.registerStaticUse(compiler.findHelper( |
- const SourceString('dynamicFunction'))); |
- world.registerStaticUse(compiler.findHelper( |
- const SourceString('dynamicSetMetadata'))); |
- world.registerStaticUse(compiler.findHelper( |
- const SourceString('defineProperty'))); |
- world.registerStaticUse(compiler.findHelper( |
- const SourceString('toStringForNativeObject'))); |
- world.registerStaticUse(compiler.findHelper( |
- const SourceString('hashCodeForNativeObject'))); |
+ flushQueue(); |
+ } |
+ |
+ 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 addSubtypes(ClassElement cls, NativeEmitter emitter) { |
+ for (DartType type in cls.allSupertypes) { |
+ List<Element> subtypes = emitter.subtypes.putIfAbsent( |
+ type.element, |
+ () => <ClassElement>[]); |
+ subtypes.add(cls); |
+ } |
+ |
+ List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( |
+ cls.superclass, |
+ () => <ClassElement>[]); |
+ directSubtypes.add(cls); |
} |
+ |
} |
void maybeEnableNative(Compiler compiler, |