Index: sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
index 53fc6082f135371eaa10c17f273124323e91cada..6face2cd39c16fa398382c75c169d76d5041c9ef 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
@@ -93,6 +93,12 @@ class NativeEmitter { |
return backend.namer.isolateAccess(element); |
} |
+ String get defineNativeMethodsExtendedName { |
+ Element element = compiler.findHelper( |
+ const SourceString('defineNativeMethodsExtended')); |
+ return backend.namer.isolateAccess(element); |
+ } |
+ |
String get defineNativeMethodsFinishName { |
Element element = compiler.findHelper( |
const SourceString('defineNativeMethodsFinish')); |
@@ -118,6 +124,10 @@ class NativeEmitter { |
* benefit), due to how [getNativeInterceptor] works. Finding the interceptor |
* of a leaf class in the hierarchy is more efficient that a non-leaf, so it |
* improves performance when more classes can be treated as leaves. |
+ * |
+ * [classes] contains native classes, mixin applications, and user subclasses |
+ * of native classes. ONLY the native classes are generated here. [classes] |
+ * is sorted in desired output order. |
*/ |
void generateNativeClasses(List<ClassElement> classes, |
CodeBuffer mainBuffer) { |
@@ -127,8 +137,10 @@ class NativeEmitter { |
List<ClassElement> preOrder = <ClassElement>[]; |
Set<ClassElement> seen = new Set<ClassElement>(); |
+ seen..add(compiler.objectClass) |
+ ..add(backend.jsInterceptorClass); |
void walk(ClassElement element) { |
- if (seen.contains(element) || element == compiler.objectClass) return; |
+ if (seen.contains(element)) return; |
seen.add(element); |
walk(element.superclass); |
preOrder.add(element); |
@@ -140,8 +152,10 @@ class NativeEmitter { |
Map<ClassElement, ClassBuilder> builders = |
new Map<ClassElement, ClassBuilder>(); |
for (ClassElement classElement in classes) { |
- ClassBuilder builder = generateNativeClass(classElement); |
- builders[classElement] = builder; |
+ if (classElement.isNative()) { |
+ ClassBuilder builder = generateNativeClass(classElement); |
+ builders[classElement] = builder; |
+ } |
} |
// Find which classes are needed and which are non-leaf classes. Any class |
@@ -150,6 +164,10 @@ class NativeEmitter { |
Set<ClassElement> neededClasses = new Set<ClassElement>(); |
Set<ClassElement> nonleafClasses = new Set<ClassElement>(); |
+ |
+ Map<ClassElement, List<ClassElement>> extensionPoints = |
+ computeExtensionPoints(preOrder); |
+ |
neededClasses.add(compiler.objectClass); |
Set<ClassElement> neededByConstant = |
@@ -176,6 +194,8 @@ class NativeEmitter { |
// TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
// adds information to a class prototype or constructor. |
needed = true; |
+ } else if (extensionPoints.containsKey(classElement)) { |
+ needed = true; |
} |
if (needed || neededClasses.contains(classElement)) { |
@@ -185,7 +205,7 @@ class NativeEmitter { |
} |
} |
- // Collect all the tags that map to each class. |
+ // Collect all the tags that map to each native class. |
Map<ClassElement, Set<String>> leafTags = |
new Map<ClassElement, Set<String>>(); |
@@ -193,9 +213,11 @@ class NativeEmitter { |
new Map<ClassElement, Set<String>>(); |
for (ClassElement classElement in classes) { |
+ if (!classElement.isNative()) continue; |
List<String> nativeTags = nativeTagsOfClass(classElement); |
- if (nonleafClasses.contains(classElement)) { |
+ if (nonleafClasses.contains(classElement) || |
+ extensionPoints.containsKey(classElement)) { |
nonleafTags |
.putIfAbsent(classElement, () => new Set<String>()) |
.addAll(nativeTags); |
@@ -218,10 +240,19 @@ class NativeEmitter { |
if (compiler.enqueuer.codegen.nativeEnqueuer |
.hasInstantiatedNativeClasses()) { |
void generateDefines(ClassElement classElement) { |
- generateDefineNativeMethods(leafTags[classElement], classElement, |
- defineNativeMethodsName); |
- generateDefineNativeMethods(nonleafTags[classElement], classElement, |
- defineNativeMethodsNonleafName); |
+ generateDefineNativeMethods(leafTags[classElement], |
+ null, |
+ classElement, defineNativeMethodsName); |
+ List<ClassElement> extensions = extensionPoints[classElement]; |
+ if (extensions == null) { |
+ generateDefineNativeMethods(nonleafTags[classElement], |
+ null, |
+ classElement, defineNativeMethodsNonleafName); |
+ } else { |
+ generateDefineNativeMethods(nonleafTags[classElement], |
+ makeSubclassList(extensions), |
+ classElement, defineNativeMethodsExtendedName); |
+ } |
} |
generateDefines(backend.jsInterceptorClass); |
for (ClassElement classElement in classes) { |
@@ -231,6 +262,7 @@ class NativeEmitter { |
// Emit the native class interceptors that were actually used. |
for (ClassElement classElement in classes) { |
+ if (!classElement.isNative()) continue; |
if (neededClasses.contains(classElement)) { |
// Define interceptor class for [classElement]. |
emitter.emitClassBuilderWithReflectionData( |
@@ -242,6 +274,39 @@ class NativeEmitter { |
} |
} |
+ /** |
+ * Computes the native classes that are extended (subclassed) by non-native |
+ * classes and the set non-mative classes that extend them. (A List is used |
+ * instead of a Set for out stability). |
+ */ |
+ Map<ClassElement, List<ClassElement>> computeExtensionPoints( |
+ List<ClassElement> classes) { |
+ ClassElement nativeSuperclassOf(ClassElement element) { |
+ if (element == null) return null; |
+ if (element.isNative()) return element; |
+ return nativeSuperclassOf(element.superclass); |
+ } |
+ |
+ ClassElement nativeAncestorOf(ClassElement element) { |
+ return nativeSuperclassOf(element.superclass); |
+ } |
+ |
+ Map<ClassElement, List<ClassElement>> map = |
+ new Map<ClassElement, List<ClassElement>>(); |
+ |
+ for (ClassElement classElement in classes) { |
+ if (classElement.isNative()) continue; |
+ ClassElement nativeAncestor = nativeAncestorOf(classElement); |
+ if (nativeAncestor != null) { |
+ map |
+ .putIfAbsent(nativeAncestor, () => <ClassElement>[]) |
+ .add(classElement); |
+ } |
+ } |
+ |
+ return map; |
+ } |
+ |
ClassBuilder generateNativeClass(ClassElement classElement) { |
assert(!classElement.hasBackendMembers); |
nativeClasses.add(classElement); |
@@ -249,6 +314,7 @@ class NativeEmitter { |
ClassElement superclass = classElement.superclass; |
assert(superclass != null); |
// Fix superclass. TODO(sra): make native classes inherit from Interceptor. |
+ assert(superclass != compiler.objectClass); |
if (superclass == compiler.objectClass) { |
superclass = backend.jsInterceptorClass; |
} |
@@ -275,18 +341,28 @@ class NativeEmitter { |
} |
void generateDefineNativeMethods( |
- Set<String> tags, ClassElement classElement, String definer) { |
+ Set<String> tags, jsAst.Expression extraArgument, |
+ ClassElement classElement, String definer) { |
if (tags == null) return; |
String tagsString = (tags.toList()..sort()).join('|'); |
- jsAst.Expression definition = |
- js(definer)( |
- [js.string(tagsString), |
- js(backend.namer.isolateAccess(classElement))]); |
+ |
+ List arguments = [ |
+ js.string(tagsString), |
+ js(backend.namer.isolateAccess(classElement))]; |
+ if (extraArgument != null) { |
+ arguments.add(extraArgument); |
+ } |
+ jsAst.Expression definition = js(definer)(arguments); |
nativeBuffer.add(jsAst.prettyPrint(definition, compiler)); |
nativeBuffer.add('$N$n'); |
} |
+ jsAst.Expression makeSubclassList(List<ClassElement> classes) { |
+ return new jsAst.ArrayInitializer.from( |
+ classes.map((ClassElement classElement) => |
+ js(backend.namer.isolateAccess(classElement)))); |
+ } |
void finishGenerateNativeClasses() { |
// TODO(sra): Put specialized version of getNativeMethods on |