Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(371)

Unified Diff: sdk/lib/_internal/compiler/implementation/native_handler.dart

Issue 15026006: Support for extending native classes (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698