Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
index f805355f634bccc366b0574aedde8def7f8eedd6..a08680b423779ef4879d1bf8f6231a468a7745bf 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
@@ -99,6 +99,17 @@ class CodeEmitterTask extends CompilerTask { |
Set<ClassElement> checkedClasses; |
/** |
+ * Set of JS native classes (or 'Object') that need a [:$nativeCheck:] method, |
+ * because they could be used as a type argument in an is-check. |
+ * |
+ * For example, in the following program, the class int needs a native check |
+ * to correctly match integers in the is-check: |
+ * class Check<T> { foo(o) => o is T; } |
+ * main() => new Check<int>().foo(3); |
+ */ |
+ Set<ClassElement> requiredNativeChecks; |
+ |
+ /** |
* Raw Typedef symbols occuring in is-checks and type assertions. If the |
* program contains `x is F<int>` and `x is F<bool>` then the TypedefElement |
* `F` will occur once in [checkedTypedefs]. |
@@ -119,7 +130,42 @@ class CodeEmitterTask extends CompilerTask { |
} |
void computeRequiredTypeChecks() { |
- assert(checkedClasses == null); |
+ assert(checkedClasses == null && |
+ checkedTypedefs == null && |
+ requiredNativeChecks == null); |
+ |
+ // Compute type arguments of classes that potentially use their type |
+ // variables in is-checks and compute the (small) set of classes that |
+ // need native check methods (consult the documentation of |
+ // [requiredNativeChecks] for more information). |
+ requiredNativeChecks = new Set<ClassElement>(); |
+ Iterable<ClassElement> classes = backend.classesUsingTypeVariableTests; |
+ if (!classes.isEmpty) { |
+ // Find all instantiated types that are a subtype of a class that uses |
+ // one of its type arguments in an is-check and add the arguments to the |
+ // set of is-checks. |
ngeoffray
2013/02/21 10:26:18
We have a [subtypes] set in the world. Couldn't yo
karlklose
2013/02/21 14:48:44
No, we want to have the instantiated types to now
|
+ for (DartType type in compiler.codegenWorld.instantiatedTypes) { |
+ if (type.kind != TypeKind.INTERFACE) continue; |
+ InterfaceType classType = type; |
+ for (ClassElement cls in classes) { |
+ // We need the type as instance of its superclass anyway, so we just |
+ // try to compute the substitution; if the result is [:null:], the |
+ // classes are not related. |
+ InterfaceType instance = classType.asInstanceOf(cls); |
+ if (instance == null) continue; |
+ Link<DartType> typeArguments = instance.typeArguments; |
+ for (DartType argument in typeArguments) { |
+ Element element = argument.element; |
+ JavaScriptBackend backend = compiler.backend; |
+ if (backend.rti.needsNativeCheck(element)) { |
+ requiredNativeChecks.add(element); |
+ } |
+ compiler.codegenWorld.isChecks.add(argument); |
+ } |
+ } |
+ } |
+ } |
+ |
checkedClasses = new Set<ClassElement>(); |
checkedTypedefs = new Set<TypedefElement>(); |
compiler.codegenWorld.isChecks.forEach((DartType t) { |
@@ -1075,7 +1121,7 @@ class CodeEmitterTask extends CompilerTask { |
void generateIsTest(Element other) { |
jsAst.Expression code; |
- if (compiler.objectClass == other) return; |
+ if (other == compiler.objectClass) return; |
if (nativeEmitter.requiresNativeIsCheck(other)) { |
code = js.fun([], [js.return_(true)]); |
} else { |
@@ -1136,7 +1182,7 @@ class CodeEmitterTask extends CompilerTask { |
} |
} |
- void emitRuntimeClassesAndTests(CodeBuffer buffer) { |
+ void emitRuntimeTypeSupport(CodeBuffer buffer) { |
RuntimeTypeInformation rti = backend.rti; |
TypeChecks typeChecks = rti.getRequiredChecks(); |
@@ -1155,8 +1201,7 @@ class CodeEmitterTask extends CompilerTask { |
if (!needsHolder(cls)) return; |
String holder = namer.isolateAccess(cls); |
String name = namer.getName(cls); |
- buffer.add("$holder$_=$_{builtin\$cls:$_'$name'"); |
- buffer.add('}$N'); |
+ buffer.add("$holder$_=$_{builtin\$cls:$_'$name'}$N"); |
} |
// Create representation objects for classes that we do not have a class |
@@ -1177,6 +1222,16 @@ class CodeEmitterTask extends CompilerTask { |
} |
}; |
} |
+ |
+ // Emit native check methods for the class representations of native types |
+ // that could be used in an is-check against a type variable. |
+ requiredNativeChecks.forEach((ClassElement cls) { |
+ jsAst.Expression nativeCheck = rti.getNativeCheck(cls); |
+ String holder = namer.isolateAccess(cls); |
+ buffer.add('$holder.${namer.getNativeCheckName()}$_=$_'); |
+ buffer.addBuffer(jsAst.prettyPrint(nativeCheck, compiler)); |
+ buffer.add('$N'); |
+ }); |
} |
void visitNativeMixins(ClassElement classElement, |
@@ -1565,6 +1620,11 @@ class CodeEmitterTask extends CompilerTask { |
emitted.add(superclass); |
} |
for (DartType supertype in cls.allSupertypes) { |
+ ClassElement superclass = supertype.element; |
+ if (backend.classesUsingTypeVariableTests.contains(superclass)) { |
+ emitSubstitution(superclass, emitNull: true); |
+ emitted.add(superclass); |
+ } |
for (ClassElement check in checkedClasses) { |
if (supertype.element == check && !emitted.contains(check)) { |
// Generate substitution. If no substitution is necessary, emit |
@@ -2603,7 +2663,7 @@ if (typeof document !== 'undefined' && document.readyState !== 'complete') { |
// We need to finish the classes before we construct compile time |
// constants. |
emitFinishClassesInvocationIfNecessary(mainBuffer); |
- emitRuntimeClassesAndTests(mainBuffer); |
+ emitRuntimeTypeSupport(mainBuffer); |
emitCompileTimeConstants(mainBuffer); |
// Static field initializations require the classes and compile-time |
// constants to be set up. |