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 ed7fb1f15879fc346ecd2567a693e06517b32403..724383c717a564801033b202bd0b614426cecbbb 100644 |
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart |
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
@@ -86,6 +86,11 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
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. |
@@ -115,6 +120,9 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
void processNativeClasses(Iterable<LibraryElement> libraries) { |
libraries.forEach(processNativeClassesInLibrary); |
processNativeClassesInLibrary(compiler.isolateHelperLibrary); |
+ |
+ processSubclassesOfNativeClasses(libraries); |
+ |
if (!enableLiveTypeAnalysis) { |
nativeClasses.forEach((c) => enqueueClass(c, 'forced')); |
flushQueue(); |
@@ -137,6 +145,124 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
classElement.ensureResolved(compiler); |
} |
+ void processSubclassesOfNativeClasses(Iterable<LibraryElement> libraries) { |
+ // Collect potential subclasses, e.g. |
+ // |
+ // class B extends foo.A {} |
+ // |
+ // SourceString "A" has a potential subclass B. |
+ |
+ var potentialExtends = new Map<SourceString, Set<ClassElement>>(); |
+ |
+ libraries.forEach((library) { |
+ library.implementation.forEachLocalMember((element) { |
+ if (element.isClass()) { |
+ SourceString name = element.name; |
+ SourceString 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. |
+ */ |
+ SourceString 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); |
+ //} |
+ } |
+ |
+ SourceString 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; |
@@ -385,6 +511,7 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
staticUse(const SourceString('convertDartClosureToJS')); |
staticUse(const SourceString('defineNativeMethods')); |
staticUse(const SourceString('defineNativeMethodsNonleaf')); |
+ staticUse(const SourceString('defineNativeMethodsExtended')); |
// TODO(9577): Registering `defineNativeMethodsFinish` seems redundant with |
// respect to the registering in the backend. When the backend registering |
// is removed as part of fixing Issue 9577, this can be the sole place |