Chromium Code Reviews| 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, |