| 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..d994ae31fed58c7c0bd34f89bcd5cfde318464d7 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].
|
| @@ -107,6 +118,17 @@ class CodeEmitterTask extends CompilerTask {
|
|
|
| final bool generateSourceMap;
|
|
|
| + Iterable<ClassElement> cachedClassesUsingTypeVariableTests;
|
| +
|
| + Iterable<ClassElement> get classesUsingTypeVariableTests {
|
| + if (cachedClassesUsingTypeVariableTests == null) {
|
| + cachedClassesUsingTypeVariableTests = backend.rti.isChecks
|
| + .where((DartType t) => t is TypeVariableType)
|
| + .map((TypeVariableType v) => v.element.getEnclosingClass());
|
| + }
|
| + return cachedClassesUsingTypeVariableTests;
|
| + }
|
| +
|
| CodeEmitterTask(Compiler compiler, Namer namer, this.generateSourceMap)
|
| : boundClosureBuffer = new CodeBuffer(),
|
| mainBuffer = new CodeBuffer(),
|
| @@ -119,7 +141,44 @@ 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 = 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.
|
| + // TODO(karlklose): replace this with code that uses a subtype lookup
|
| + // datastructure in the world.
|
| + 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 +1134,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 +1195,7 @@ class CodeEmitterTask extends CompilerTask {
|
| }
|
| }
|
|
|
| - void emitRuntimeClassesAndTests(CodeBuffer buffer) {
|
| + void emitRuntimeTypeSupport(CodeBuffer buffer) {
|
| RuntimeTypeInformation rti = backend.rti;
|
| TypeChecks typeChecks = rti.getRequiredChecks();
|
|
|
| @@ -1155,8 +1214,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 +1235,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 +1633,11 @@ class CodeEmitterTask extends CompilerTask {
|
| emitted.add(superclass);
|
| }
|
| for (DartType supertype in cls.allSupertypes) {
|
| + ClassElement superclass = supertype.element;
|
| + if (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
|
| @@ -2582,7 +2655,6 @@ if (typeof document !== 'undefined' && document.readyState !== 'complete') {
|
| String assembleProgram() {
|
| measure(() {
|
| computeNeededClasses();
|
| -
|
| mainBuffer.add(GENERATED_BY);
|
| if (!compiler.enableMinification) mainBuffer.add(HOOKS_API_USAGE);
|
| mainBuffer.add('function ${namer.isolateName}()$_{}\n');
|
| @@ -2603,7 +2675,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.
|
|
|