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..7b9b8f6f6f0fabf5c53a127c4257ab0fffba6dd0 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,119 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
classElement.ensureResolved(compiler); |
} |
+ void processSubclassesOfNativeClasses(Iterable<LibraryElement> libraries) { |
+ // class B extends foo.A {} |
ahe
2013/08/13 18:31:47
This comment is weird.
sra1
2013/08/14 04:36:47
Done.
|
+ |
+ 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); |
+ } |
+ } |
+ }); |
+ }); |
+ |
+ ClassElement nativeSuperclassOf(ClassElement classElement) { |
+ if (classElement.isNative()) return classElement; |
+ if (classElement.superclass == null) return null; |
+ return nativeSuperclassOf(classElement.superclass); |
ahe
2013/08/13 18:31:47
How does this cope with cycles in the inheritance
sra1
2013/08/14 04:36:47
Called after resolving.
ahe
2013/08/14 10:52:47
Sorry, I forgot we had solved this problem.
|
+ } |
+ |
+ void walkPotentialSubclasses(ClassElement element) { |
+ if (nativeClassesAndSubclasses.contains(element)) return; |
+ //print('Force resolve $element'); |
ahe
2013/08/13 18:31:47
Remove comment.
sra1
2013/08/14 04:36:47
Done.
|
+ 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); |
+ |
+ // print(nativeClassesAndSubclasses); |
+ |
+ nativeClasses.addAll(nativeClassesAndSubclasses); |
+ unusedClasses.addAll(nativeClassesAndSubclasses); |
+ |
+ // NEXT: make subclassed classes use different register function; add in a |
ahe
2013/08/13 18:31:47
We normally use // TODO(username): ...
sra1
2013/08/14 04:36:47
Removed, since it is done.
|
+ // hook that the DOM runtime can use. |
+ } |
+ |
+ /** |
+ * Returns the source string of the class named in the extends clause, or |
+ * `null` if there is no extends clause. |
+ */ |
+ SourceString findExtendsNameOfClass(ClassElement classElement) { |
ahe
2013/08/13 18:31:47
I'm concerned about this method, and I would reall
|
+ // "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) { |
ahe
2013/08/13 18:31:47
This should be:
BeginGroupToken beginGroupTok
|
+ 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; |
ahe
2013/08/13 18:31:47
You need to use identical, not ==. Otherwise, you
sra1
2013/08/14 04:36:47
What guarantees they are the same instance of Stri
ahe
2013/08/14 10:52:47
That is guaranteed by the language specification (
|
+ if (token.stringValue != 'class') return null; |
ahe
2013/08/13 18:31:47
Ditto.
|
+ token = token.next; |
+ if (!token.isIdentifier()) return null; |
+ token = token.next; |
+ // class F<X extends B<X>> extends ... |
+ if (token.stringValue == '<') { |
ahe
2013/08/13 18:31:47
Ditto.
|
+ token = skipTypeParameters(token); |
+ } |
+ if (token.stringValue != 'extends') return null; |
ahe
2013/08/13 18:31:47
Ditto.
|
+ token = token.next; |
+ Token id = token; |
+ for (;;) { |
ahe
2013/08/13 18:31:47
As a general rule, infinite loops on user data is
sra1
2013/08/14 04:36:47
Done.
|
+ token = token.next; |
+ if (token.stringValue != '.') break; |
ahe
2013/08/13 18:31:47
identical.
|
+ 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 +506,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 |