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 |
deleted file mode 100644 |
index 4c48f48be5311128287f0545910de015832889fb..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/native/enqueue.dart |
+++ /dev/null |
@@ -1,641 +0,0 @@ |
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of native; |
- |
-/** |
- * This could be an abstract class but we use it as a stub for the dart_backend. |
- */ |
-class NativeEnqueuer { |
- /// Initial entry point to native enqueuer. |
- void processNativeClasses(Iterable<LibraryElement> libraries) {} |
- |
- /// Notification of a main Enqueuer worklist element. For methods, adds |
- /// information from metadata attributes, and computes types instantiated due |
- /// to calling the method. |
- void registerElement(Element element) {} |
- |
- /// Notification of native field. Adds information from metadata attributes. |
- void handleFieldAnnotations(Element field) {} |
- |
- /// Computes types instantiated due to getting a native field. |
- void registerFieldLoad(Element field) {} |
- |
- /// Computes types instantiated due to setting a native field. |
- void registerFieldStore(Element field) {} |
- |
- NativeBehavior getNativeBehaviorOf(Send node) => NativeBehavior.NONE; |
- |
- /// Returns whether native classes are being used. |
- bool hasInstantiatedNativeClasses() => false; |
- |
- /** |
- * Handles JS-calls, which can be an instantiation point for types. |
- * |
- * For example, the following code instantiates and returns native classes |
- * that are `_DOMWindowImpl` or a subtype. |
- * |
- * JS('_DOMWindowImpl', 'window') |
- * |
- */ |
- // TODO(sra): The entry from codegen will not have a resolver. |
- void registerJsCall(Send node, ResolverVisitor resolver) {} |
- |
- /** |
- * Handles JS-embedded global calls, which can be an instantiation point for |
- * types. |
- * |
- * For example, the following code instantiates and returns a String class |
- * |
- * JS_EMBEDDED_GLOBAL('String', 'foo') |
- * |
- */ |
- // TODO(sra): The entry from codegen will not have a resolver. |
- void registerJsEmbeddedGlobalCall(Send node, ResolverVisitor resolver) {} |
- |
- /// Emits a summary information using the [log] function. |
- void logSummary(log(message)) {} |
- |
- // Do not use annotations in dart2dart. |
- ClassElement get annotationCreatesClass => null; |
- ClassElement get annotationReturnsClass => null; |
- ClassElement get annotationJsNameClass => null; |
-} |
- |
- |
-abstract class NativeEnqueuerBase implements NativeEnqueuer { |
- |
- /** |
- * The set of all native classes. Each native class is in [nativeClasses] and |
- * exactly one of [unusedClasses], [pendingClasses] and [registeredClasses]. |
- */ |
- final Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
- |
- final Set<ClassElement> registeredClasses = new Set<ClassElement>(); |
- final Set<ClassElement> pendingClasses = new Set<ClassElement>(); |
- final Set<ClassElement> unusedClasses = new Set<ClassElement>(); |
- |
- final Set<LibraryElement> processedLibraries; |
- |
- bool hasInstantiatedNativeClasses() => !registeredClasses.isEmpty; |
- |
- final Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); |
- |
- final Map<ClassElement, Set<ClassElement>> nonNativeSubclasses = |
- new Map<ClassElement, Set<ClassElement>>(); |
- |
- /** |
- * Records matched constraints ([SpecialType] or [DartType]). Once a type |
- * constraint has been matched, there is no need to match it again. |
- */ |
- 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; |
- |
- /// Maps JS foreign calls to their computed native behavior. |
- final Map<Node, NativeBehavior> nativeBehaviors = |
- new Map<Node, NativeBehavior>(); |
- |
- final Enqueuer world; |
- final Compiler compiler; |
- final bool enableLiveTypeAnalysis; |
- |
- ClassElement _annotationCreatesClass; |
- ClassElement _annotationReturnsClass; |
- ClassElement _annotationJsNameClass; |
- |
- /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. |
- NativeEnqueuerBase(this.world, Compiler compiler, this.enableLiveTypeAnalysis) |
- : this.compiler = compiler, |
- processedLibraries = compiler.cacheStrategy.newSet(); |
- |
- JavaScriptBackend get backend => compiler.backend; |
- |
- void processNativeClasses(Iterable<LibraryElement> libraries) { |
- if (compiler.hasIncrementalSupport) { |
- // Since [Set.add] returns bool if an element was added, this restricts |
- // [libraries] to ones that haven't already been processed. This saves |
- // time during incremental compiles. |
- libraries = libraries.where(processedLibraries.add); |
- } |
- libraries.forEach(processNativeClassesInLibrary); |
- if (backend.isolateHelperLibrary != null) { |
- processNativeClassesInLibrary(backend.isolateHelperLibrary); |
- } |
- processSubclassesOfNativeClasses(libraries); |
- if (!enableLiveTypeAnalysis) { |
- nativeClasses.forEach((c) => enqueueClass(c, 'forced')); |
- flushQueue(); |
- } |
- } |
- |
- void processNativeClassesInLibrary(LibraryElement library) { |
- // Use implementation to ensure the inclusion of injected members. |
- library.implementation.forEachLocalMember((Element element) { |
- if (element.isClass && element.isNative) { |
- processNativeClass(element); |
- } |
- }); |
- } |
- |
- void processNativeClass(ClassElement classElement) { |
- nativeClasses.add(classElement); |
- unusedClasses.add(classElement); |
- // Resolve class to ensure the class has valid inheritance info. |
- classElement.ensureResolved(compiler); |
- } |
- |
- void processSubclassesOfNativeClasses(Iterable<LibraryElement> libraries) { |
- // Collect potential subclasses, e.g. |
- // |
- // class B extends foo.A {} |
- // |
- // String "A" has a potential subclass B. |
- |
- var potentialExtends = new Map<String, Set<ClassElement>>(); |
- |
- libraries.forEach((library) { |
- library.implementation.forEachLocalMember((element) { |
- if (element.isClass) { |
- String name = element.name; |
- String extendsName = findExtendsNameOfClass(element); |
- if (extendsName != null) { |
- Set<ClassElement> potentialSubclasses = |
- potentialExtends.putIfAbsent( |
- extendsName, |
- () => new Set<ClassElement>()); |
- potentialSubclasses.add(element); |
- } |
- } |
- }); |
- }); |
- |
- // Resolve all the native classes and any classes that might extend them in |
- // [potentialExtends], and then check that the properly resolved class is in |
- // fact a subclass of a native class. |
- |
- ClassElement nativeSuperclassOf(ClassElement classElement) { |
- if (classElement.isNative) return classElement; |
- if (classElement.superclass == null) return null; |
- return nativeSuperclassOf(classElement.superclass); |
- } |
- |
- void walkPotentialSubclasses(ClassElement element) { |
- if (nativeClassesAndSubclasses.contains(element)) return; |
- element.ensureResolved(compiler); |
- ClassElement nativeSuperclass = nativeSuperclassOf(element); |
- if (nativeSuperclass != null) { |
- nativeClassesAndSubclasses.add(element); |
- if (!element.isNative) { |
- nonNativeSubclasses.putIfAbsent(nativeSuperclass, |
- () => new Set<ClassElement>()) |
- .add(element); |
- } |
- Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; |
- if (potentialSubclasses != null) { |
- potentialSubclasses.forEach(walkPotentialSubclasses); |
- } |
- } |
- } |
- |
- nativeClasses.forEach(walkPotentialSubclasses); |
- |
- nativeClasses.addAll(nativeClassesAndSubclasses); |
- unusedClasses.addAll(nativeClassesAndSubclasses); |
- } |
- |
- /** |
- * Returns the source string of the class named in the extends clause, or |
- * `null` if there is no extends clause. |
- */ |
- String findExtendsNameOfClass(ClassElement classElement) { |
- // "class B extends A ... {}" --> "A" |
- // "class B extends foo.A ... {}" --> "A" |
- // "class B<T> extends foo.A<T,T> with M1, M2 ... {}" --> "A" |
- |
- // We want to avoid calling classElement.parseNode on every class. Doing so |
- // will slightly increase parse time and size and cause compiler errors and |
- // warnings to me emitted in more unused code. |
- |
- // An alternative to this code is to extend the API of ClassElement to |
- // expose the name of the extended element. |
- |
- // Pattern match the above cases in the token stream. |
- // [abstract] class X extends [id.]* id |
- |
- Token skipTypeParameters(Token token) { |
- BeginGroupToken beginGroupToken = token; |
- Token endToken = beginGroupToken.endGroup; |
- return endToken.next; |
- //for (;;) { |
- // token = token.next; |
- // if (token.stringValue == '>') return token.next; |
- // if (token.stringValue == '<') return skipTypeParameters(token); |
- //} |
- } |
- |
- String scanForExtendsName(Token token) { |
- if (token.stringValue == 'abstract') token = token.next; |
- if (token.stringValue != 'class') return null; |
- token = token.next; |
- if (!token.isIdentifier()) return null; |
- token = token.next; |
- // class F<X extends B<X>> extends ... |
- if (token.stringValue == '<') { |
- token = skipTypeParameters(token); |
- } |
- if (token.stringValue != 'extends') return null; |
- token = token.next; |
- Token id = token; |
- while (token.kind != EOF_TOKEN) { |
- token = token.next; |
- if (token.stringValue != '.') break; |
- token = token.next; |
- if (!token.isIdentifier()) return null; |
- id = token; |
- } |
- // Should be at '{', 'with', 'implements', '<' or 'native'. |
- return id.value; |
- } |
- |
- return compiler.withCurrentElement(classElement, () { |
- return scanForExtendsName(classElement.position); |
- }); |
- } |
- |
- ClassElement get annotationCreatesClass { |
- findAnnotationClasses(); |
- return _annotationCreatesClass; |
- } |
- |
- ClassElement get annotationReturnsClass { |
- findAnnotationClasses(); |
- return _annotationReturnsClass; |
- } |
- |
- ClassElement get annotationJsNameClass { |
- findAnnotationClasses(); |
- return _annotationJsNameClass; |
- } |
- |
- void findAnnotationClasses() { |
- if (_annotationCreatesClass != null) return; |
- ClassElement find(name) { |
- Element e = backend.findHelper(name); |
- if (e == null || e is! ClassElement) { |
- compiler.internalError(NO_LOCATION_SPANNABLE, |
- "Could not find implementation class '${name}'."); |
- } |
- return e; |
- } |
- _annotationCreatesClass = find('Creates'); |
- _annotationReturnsClass = find('Returns'); |
- _annotationJsNameClass = find('JSName'); |
- } |
- |
- /// Returns the JSName annotation string or `null` if no JSName annotation is |
- /// present. |
- String findJsNameFromAnnotation(Element element) { |
- String name = null; |
- ClassElement annotationClass = annotationJsNameClass; |
- for (Link<MetadataAnnotation> link = element.metadata; |
- !link.isEmpty; |
- link = link.tail) { |
- MetadataAnnotation annotation = link.head.ensureResolved(compiler); |
- ConstantValue value = annotation.constant.value; |
- if (!value.isConstructedObject) continue; |
- ConstructedConstantValue constructedObject = value; |
- if (constructedObject.type.element != annotationClass) continue; |
- |
- List<ConstantValue> fields = constructedObject.fields; |
- // TODO(sra): Better validation of the constant. |
- if (fields.length != 1 || fields[0] is! StringConstantValue) { |
- PartialMetadataAnnotation partial = annotation; |
- compiler.internalError(annotation, |
- 'Annotations needs one string: ${partial.parseNode(compiler)}'); |
- } |
- StringConstantValue specStringConstant = fields[0]; |
- String specString = specStringConstant.toDartString().slowToString(); |
- if (name == null) { |
- name = specString; |
- } else { |
- PartialMetadataAnnotation partial = annotation; |
- compiler.internalError(annotation, |
- 'Too many JSName annotations: ${partial.parseNode(compiler)}'); |
- } |
- } |
- 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(ClassElementX classElement, cause) { |
- // TODO(ahe): Fix this assertion to work in incremental compilation. |
- assert(compiler.hasIncrementalSupport || |
- !registeredClasses.contains(classElement)); |
- |
- bool firstTime = registeredClasses.isEmpty; |
- pendingClasses.remove(classElement); |
- registeredClasses.add(classElement); |
- |
- // TODO(ahe): Is this really a global dependency? |
- world.registerInstantiatedClass(classElement, compiler.globalDependencies); |
- |
- // 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) { |
- compiler.withCurrentElement(element, () { |
- if (element.isFunction || element.isGetter || element.isSetter) { |
- handleMethodAnnotations(element); |
- if (element.isNative) { |
- registerMethodUsed(element); |
- } |
- } else if (element.isField) { |
- handleFieldAnnotations(element); |
- if (element.isNative) { |
- registerFieldLoad(element); |
- registerFieldStore(element); |
- } |
- } |
- }); |
- } |
- |
- handleFieldAnnotations(Element element) { |
- if (element.enclosingElement.isNative) { |
- // Exclude non-instance (static) fields - they not really native and are |
- // compiled as isolate globals. Access of a property of a constructor |
- // function or a non-method property in the prototype chain, must be coded |
- // using a JS-call. |
- if (element.isInstanceMember) { |
- setNativeName(element); |
- } |
- } |
- } |
- |
- handleMethodAnnotations(Element method) { |
- if (isNativeMethod(method)) { |
- setNativeName(method); |
- } |
- } |
- |
- /// Sets the native name of [element], either from an annotation, or |
- /// defaulting to the Dart name. |
- void setNativeName(Element element) { |
- String name = findJsNameFromAnnotation(element); |
- if (name == null) name = element.name; |
- element.setNative(name); |
- } |
- |
- bool isNativeMethod(FunctionElementX element) { |
- if (!element.library.canUseNative) return false; |
- // Native method? |
- return compiler.withCurrentElement(element, () { |
- Node node = element.parseNode(compiler); |
- if (node is! FunctionExpression) return false; |
- FunctionExpression functionExpression = node; |
- node = functionExpression.body; |
- Token token = node.getBeginToken(); |
- if (identical(token.stringValue, 'native')) return true; |
- return false; |
- }); |
- } |
- |
- void registerMethodUsed(Element method) { |
- processNativeBehavior( |
- NativeBehavior.ofMethod(method, compiler), |
- method); |
- flushQueue(); |
- } |
- |
- void registerFieldLoad(Element field) { |
- processNativeBehavior( |
- NativeBehavior.ofFieldLoad(field, compiler), |
- field); |
- flushQueue(); |
- } |
- |
- void registerFieldStore(Element field) { |
- processNativeBehavior( |
- NativeBehavior.ofFieldStore(field, compiler), |
- field); |
- flushQueue(); |
- } |
- |
- void registerJsCall(Send node, ResolverVisitor resolver) { |
- NativeBehavior behavior = NativeBehavior.ofJsCall(node, compiler, resolver); |
- processNativeBehavior(behavior, node); |
- nativeBehaviors[node] = behavior; |
- flushQueue(); |
- } |
- |
- void registerJsEmbeddedGlobalCall(Send node, ResolverVisitor resolver) { |
- NativeBehavior behavior = |
- NativeBehavior.ofJsEmbeddedGlobalCall(node, compiler, resolver); |
- processNativeBehavior(behavior, node); |
- nativeBehaviors[node] = behavior; |
- flushQueue(); |
- } |
- |
- NativeBehavior getNativeBehaviorOf(Send node) => nativeBehaviors[node]; |
- |
- processNativeBehavior(NativeBehavior behavior, cause) { |
- // TODO(ahe): Is this really a global dependency? |
- Registry registry = compiler.globalDependencies; |
- bool allUsedBefore = unusedClasses.isEmpty; |
- for (var type in behavior.typesInstantiated) { |
- if (matchedTypeConstraints.contains(type)) continue; |
- matchedTypeConstraints.add(type); |
- if (type is SpecialType) { |
- if (type == SpecialType.JsObject) { |
- world.registerInstantiatedClass(compiler.objectClass, registry); |
- } |
- continue; |
- } |
- if (type is InterfaceType) { |
- if (type.element == compiler.intClass) { |
- world.registerInstantiatedClass(compiler.intClass, registry); |
- } else if (type.element == compiler.doubleClass) { |
- world.registerInstantiatedClass(compiler.doubleClass, registry); |
- } else if (type.element == compiler.numClass) { |
- world.registerInstantiatedClass(compiler.doubleClass, registry); |
- world.registerInstantiatedClass(compiler.intClass, registry); |
- } else if (type.element == compiler.stringClass) { |
- world.registerInstantiatedClass(compiler.stringClass, registry); |
- } else if (type.element == compiler.nullClass) { |
- world.registerInstantiatedClass(compiler.nullClass, registry); |
- } else if (type.element == compiler.boolClass) { |
- world.registerInstantiatedClass(compiler.boolClass, registry); |
- } else if (compiler.types.isSubtype( |
- type, backend.listImplementation.rawType)) { |
- world.registerInstantiatedClass(type.element, registry); |
- } |
- } |
- assert(type is DartType); |
- enqueueUnusedClassesMatching( |
- (nativeClass) => compiler.types.isSubtype(nativeClass.thisType, type), |
- cause, |
- 'subtypeof($type)'); |
- } |
- |
- // Give an info so that library developers can compile with -v to find why |
- // all the native classes are included. |
- if (unusedClasses.isEmpty && !allUsedBefore) { |
- compiler.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)); |
- } |
- |
- onFirstNativeClass() { |
- staticUse(name) { |
- backend.enqueue( |
- world, backend.findHelper(name), compiler.globalDependencies); |
- } |
- |
- staticUse('defineProperty'); |
- staticUse('toStringForNativeObject'); |
- staticUse('hashCodeForNativeObject'); |
- staticUse('convertDartClosureToJS'); |
- addNativeExceptions(); |
- } |
- |
- addNativeExceptions() { |
- enqueueUnusedClassesMatching((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.enableNativeLiveTypeAnalysis); |
- |
- void processNativeClass(ClassElement classElement) { |
- super.processNativeClass(classElement); |
- |
- // Since we map from dispatch tags to classes, a dispatch tag must be used |
- // on only one native class. |
- for (String tag in nativeTagsOfClass(classElement)) { |
- ClassElement owner = tagOwner[tag]; |
- if (owner != null) { |
- if (owner != classElement) { |
- compiler.internalError( |
- classElement, "Tag '$tag' already in use by '${owner.name}'"); |
- } |
- } else { |
- tagOwner[tag] = classElement; |
- } |
- } |
- } |
- |
- void logSummary(log(message)) { |
- log('Resolved ${registeredClasses.length} native elements used, ' |
- '${unusedClasses.length} native elements dead.'); |
- } |
-} |
- |
- |
-class NativeCodegenEnqueuer extends NativeEnqueuerBase { |
- |
- final CodeEmitterTask emitter; |
- |
- final Set<ClassElement> doneAddSubtypes = new Set<ClassElement>(); |
- |
- NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter) |
- : super(world, compiler, compiler.enableNativeLiveTypeAnalysis); |
- |
- void processNativeClasses(Iterable<LibraryElement> libraries) { |
- super.processNativeClasses(libraries); |
- |
- // HACK HACK - add all the resolved classes. |
- NativeEnqueuerBase enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; |
- for (final classElement in enqueuer.registeredClasses) { |
- if (unusedClasses.contains(classElement)) { |
- enqueueClass(classElement, 'was resolved'); |
- } |
- } |
- 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) { |
- if (!cls.isNative) 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); |
- |
- for (DartType type in cls.allSupertypes) { |
- List<Element> subtypes = emitter.subtypes.putIfAbsent( |
- type.element, |
- () => <ClassElement>[]); |
- subtypes.add(cls); |
- } |
- |
- // Skip through all the mixin applications in the super class |
- // chain. That way, the direct subtypes set only contain the |
- // natives classes. |
- ClassElement superclass = cls.superclass; |
- while (superclass != null && superclass.isMixinApplication) { |
- assert(!superclass.isNative); |
- superclass = superclass.superclass; |
- } |
- |
- List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( |
- superclass, |
- () => <ClassElement>[]); |
- directSubtypes.add(cls); |
- } |
- |
- void logSummary(log(message)) { |
- log('Compiled ${registeredClasses.length} native classes, ' |
- '${unusedClasses.length} native classes omitted.'); |
- } |
-} |