| 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 c04ff051db9c9ff6222ff049cd3f03351f233e30..fecfb7e685058c2fc0bd28dbdecde6b0308a34dd 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
|
| @@ -48,6 +48,13 @@ class ClassBuilder {
|
| }
|
| }
|
|
|
| +// Function signatures used in the generation of runtime type information.
|
| +typedef void FunctionTypeSignatureEmitter(Element method,
|
| + FunctionType methodType);
|
| +// TODO(johnniwinther): Clean up terminology for rti in the emitter.
|
| +typedef void FunctionTypeTestEmitter(FunctionType functionType);
|
| +typedef void SubstitutionEmitter(Element element, {bool emitNull});
|
| +
|
| /**
|
| * Generates the code for all used classes in the program. Static fields (even
|
| * in classes) are ignored, since they can be treated as non-class elements.
|
| @@ -112,11 +119,26 @@ class CodeEmitterTask extends CompilerTask {
|
| Set<ClassElement> checkedClasses;
|
|
|
| /**
|
| - * 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].
|
| + * The set of function types that checked, both explicity through tests of
|
| + * typedefs and implicitly through type annotations in checked mode.
|
| */
|
| - Set<TypedefElement> checkedTypedefs;
|
| + Set<FunctionType> checkedFunctionTypes;
|
| +
|
| + Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes =
|
| + new Map<ClassElement, Set<FunctionType>>();
|
| +
|
| + Set<FunctionType> checkedNonGenericFunctionTypes =
|
| + new Set<FunctionType>();
|
| +
|
| + void registerDynamicFunctionTypeCheck(FunctionType functionType) {
|
| + ClassElement classElement = Types.getClassContext(functionType);
|
| + if (classElement != null) {
|
| + checkedGenericFunctionTypes.putIfAbsent(classElement,
|
| + () => new Set<FunctionType>()).add(functionType);
|
| + } else {
|
| + checkedNonGenericFunctionTypes.add(functionType);
|
| + }
|
| + }
|
|
|
| final bool generateSourceMap;
|
|
|
| @@ -147,18 +169,20 @@ class CodeEmitterTask extends CompilerTask {
|
| }
|
|
|
| void computeRequiredTypeChecks() {
|
| - assert(checkedClasses == null && checkedTypedefs == null);
|
| + assert(checkedClasses == null && checkedFunctionTypes == null);
|
|
|
| backend.rti.addImplicitChecks(compiler.codegenWorld,
|
| classesUsingTypeVariableTests);
|
|
|
| checkedClasses = new Set<ClassElement>();
|
| - checkedTypedefs = new Set<TypedefElement>();
|
| + checkedFunctionTypes = new Set<FunctionType>();
|
| compiler.codegenWorld.isChecks.forEach((DartType t) {
|
| - if (t is InterfaceType) {
|
| - checkedClasses.add(t.element);
|
| - } else if (t is TypedefType) {
|
| - checkedTypedefs.add(t.element);
|
| + if (!t.isMalformed) {
|
| + if (t is InterfaceType) {
|
| + checkedClasses.add(t.element);
|
| + } else if (t is FunctionType) {
|
| + checkedFunctionTypes.add(t);
|
| + }
|
| }
|
| });
|
| }
|
| @@ -1319,9 +1343,34 @@ class CodeEmitterTask extends CompilerTask {
|
| builder.addProperty(namer.operatorIs(other), js('true'));
|
| }
|
|
|
| + void generateIsFunctionTypeTest(FunctionType type) {
|
| + String operator = namer.operatorIsType(type);
|
| + builder.addProperty(operator, new jsAst.LiteralBool(true));
|
| + }
|
| +
|
| + void generateFunctionTypeSignature(Element method, FunctionType type) {
|
| + assert(method.isImplementation);
|
| + String thisAccess = 'this';
|
| + Node node = method.parseNode(compiler);
|
| + ClosureClassMap closureData =
|
| + compiler.closureToClassMapper.closureMappingCache[node];
|
| + if (closureData != null) {
|
| + Element thisElement =
|
| + closureData.freeVariableMapping[closureData.thisElement];
|
| + if (thisElement != null) {
|
| + String thisName = backend.namer.getName(thisElement);
|
| + thisAccess = 'this.$thisName';
|
| + }
|
| + }
|
| + RuntimeTypes rti = backend.rti;
|
| + String encoding = rti.getSignatureEncoding(type, () => '$thisAccess');
|
| + String operatorSignature = namer.operatorSignature();
|
| + builder.addProperty(operatorSignature,
|
| + new jsAst.LiteralExpression(encoding));
|
| + }
|
| +
|
| void generateSubstitution(Element other, {bool emitNull: false}) {
|
| RuntimeTypes rti = backend.rti;
|
| - // TODO(karlklose): support typedefs with variables.
|
| jsAst.Expression expression;
|
| bool needsNativeCheck = nativeEmitter.requiresNativeIsCheck(other);
|
| if (other.kind == ElementKind.CLASS) {
|
| @@ -1338,7 +1387,9 @@ class CodeEmitterTask extends CompilerTask {
|
| }
|
| }
|
|
|
| - generateIsTestsOn(classElement, generateIsTest, generateSubstitution);
|
| + generateIsTestsOn(classElement, generateIsTest,
|
| + generateIsFunctionTypeTest, generateFunctionTypeSignature,
|
| + generateSubstitution);
|
| }
|
|
|
| void emitRuntimeTypeSupport(CodeBuffer buffer) {
|
| @@ -1358,6 +1409,17 @@ class CodeEmitterTask extends CompilerTask {
|
| }
|
| };
|
| }
|
| +
|
| + void addSignature(FunctionType type) {
|
| + String encoding = rti.getTypeEncoding(type);
|
| + buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N');
|
| + }
|
| +
|
| + checkedNonGenericFunctionTypes.forEach(addSignature);
|
| +
|
| + checkedGenericFunctionTypes.forEach((_, Set<FunctionType> functionTypes) {
|
| + functionTypes.forEach(addSignature);
|
| + });
|
| }
|
|
|
| /**
|
| @@ -1489,12 +1551,14 @@ class CodeEmitterTask extends CompilerTask {
|
| DartType type = member.computeType(compiler);
|
| // TODO(ahe): Generate a dynamic type error here.
|
| if (type.element.isErroneous()) return;
|
| - FunctionElement helperElement
|
| - = backend.getCheckedModeHelper(type, typeCast: false);
|
| + type = type.unalias(compiler);
|
| + CheckedModeHelper helper =
|
| + backend.getCheckedModeHelper(type, typeCast: false);
|
| + FunctionElement helperElement = helper.getElement(compiler);
|
| String helperName = namer.isolateAccess(helperElement);
|
| List<jsAst.Expression> arguments = <jsAst.Expression>[js('v')];
|
| if (helperElement.computeSignature(compiler).parameterCount != 1) {
|
| - arguments.add(js.string(namer.operatorIs(type.element)));
|
| + arguments.add(js.string(namer.operatorIsType(type)));
|
| }
|
|
|
| String setterName = namer.setterNameFromAccessorName(accessorName);
|
| @@ -1735,14 +1799,27 @@ class CodeEmitterTask extends CompilerTask {
|
| return _selectorRank(selector1) - _selectorRank(selector2);
|
| }
|
|
|
| - Iterable<Element> getTypedefChecksOn(DartType type) {
|
| - bool isSubtype(TypedefElement typedef) {
|
| - FunctionType typedefType =
|
| - typedef.computeType(compiler).unalias(compiler);
|
| - return compiler.types.isSubtype(type, typedefType);
|
| + /**
|
| + * Returns a mapping containing all checked function types for which [type]
|
| + * can be a subtype. A function type is mapped to [:true:] if [type] is
|
| + * statically known to be a subtype of it and to [:false:] if [type] might
|
| + * be a subtype, provided with the right type arguments.
|
| + */
|
| + // TODO(johnniwinther): Change to return a mapping from function types to
|
| + // a set of variable points and use this to detect statically/dynamically
|
| + // known subtype relations.
|
| + Map<FunctionType, bool> getFunctionTypeChecksOn(DartType type) {
|
| + Map<FunctionType, bool> functionTypeMap =
|
| + new LinkedHashMap<FunctionType, bool>();
|
| + for (FunctionType functionType in checkedFunctionTypes) {
|
| + if (compiler.types.isSubtype(type, functionType)) {
|
| + functionTypeMap[functionType] = true;
|
| + } else if (compiler.types.isPotentialSubtype(type, functionType)) {
|
| + functionTypeMap[functionType] = false;
|
| + }
|
| }
|
| - return checkedTypedefs.where(isSubtype).toList()
|
| - ..sort(Elements.compareByPosition);
|
| + // TODO(johnniwinther): Ensure stable ordering of the keys.
|
| + return functionTypeMap;
|
| }
|
|
|
| /**
|
| @@ -1754,7 +1831,9 @@ class CodeEmitterTask extends CompilerTask {
|
| */
|
| void generateIsTestsOn(ClassElement cls,
|
| void emitIsTest(Element element),
|
| - void emitSubstitution(Element element, {emitNull})) {
|
| + FunctionTypeTestEmitter emitIsFunctionTypeTest,
|
| + FunctionTypeSignatureEmitter emitFunctionTypeSignature,
|
| + SubstitutionEmitter emitSubstitution) {
|
| if (checkedClasses.contains(cls)) {
|
| emitIsTest(cls);
|
| emitSubstitution(cls);
|
| @@ -1777,7 +1856,7 @@ class CodeEmitterTask extends CompilerTask {
|
| Set<ClassElement> emitted = new Set<ClassElement>();
|
| // TODO(karlklose): move the computation of these checks to
|
| // RuntimeTypeInformation.
|
| - if (backend.needsRti(cls)) {
|
| + if (backend.classNeedsRti(cls)) {
|
| emitSubstitution(superclass, emitNull: true);
|
| emitted.add(superclass);
|
| }
|
| @@ -1805,7 +1884,7 @@ class CodeEmitterTask extends CompilerTask {
|
| // A class that defines a [:call:] method implicitly implements
|
| // [Function] and needs checks for all typedefs that are used in is-checks.
|
| if (checkedClasses.contains(compiler.functionClass) ||
|
| - !checkedTypedefs.isEmpty) {
|
| + !checkedFunctionTypes.isEmpty) {
|
| Element call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME);
|
| if (call == null) {
|
| // If [cls] is a closure, it has a synthetic call operator method.
|
| @@ -1816,8 +1895,12 @@ class CodeEmitterTask extends CompilerTask {
|
| emitIsTest,
|
| emitSubstitution,
|
| generated);
|
| - getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest);
|
| - }
|
| + FunctionType callType = call.computeType(compiler);
|
| + Map<FunctionType, bool> functionTypeChecks =
|
| + getFunctionTypeChecksOn(callType);
|
| + generateFunctionTypeTests(call, callType, functionTypeChecks,
|
| + emitFunctionTypeSignature, emitIsFunctionTypeTest);
|
| + }
|
| }
|
|
|
| for (DartType interfaceType in cls.interfaces) {
|
| @@ -1831,7 +1914,7 @@ class CodeEmitterTask extends CompilerTask {
|
| */
|
| void generateInterfacesIsTests(ClassElement cls,
|
| void emitIsTest(ClassElement element),
|
| - void emitSubstitution(ClassElement element),
|
| + SubstitutionEmitter emitSubstitution,
|
| Set<Element> alreadyGenerated) {
|
| void tryEmitTest(ClassElement check) {
|
| if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) {
|
| @@ -1859,6 +1942,45 @@ class CodeEmitterTask extends CompilerTask {
|
| }
|
| }
|
|
|
| + static const int MAX_FUNCTION_TYPE_PREDICATES = 10;
|
| +
|
| + /**
|
| + * Generates function type checks on [method] with type [methodType] against
|
| + * the function type checks in [functionTypeChecks].
|
| + */
|
| + void generateFunctionTypeTests(
|
| + Element method,
|
| + FunctionType methodType,
|
| + Map<FunctionType, bool> functionTypeChecks,
|
| + FunctionTypeSignatureEmitter emitFunctionTypeSignature,
|
| + FunctionTypeTestEmitter emitIsFunctionTypeTest) {
|
| + bool hasDynamicFunctionTypeCheck = false;
|
| + int neededPredicates = 0;
|
| + functionTypeChecks.forEach((FunctionType functionType, bool knownSubtype) {
|
| + if (!knownSubtype) {
|
| + registerDynamicFunctionTypeCheck(functionType);
|
| + hasDynamicFunctionTypeCheck = true;
|
| + } else {
|
| + neededPredicates++;
|
| + }
|
| + });
|
| + bool alwaysUseSignature = false;
|
| + if (hasDynamicFunctionTypeCheck ||
|
| + neededPredicates > MAX_FUNCTION_TYPE_PREDICATES) {
|
| + emitFunctionTypeSignature(method, methodType);
|
| + alwaysUseSignature = true;
|
| + }
|
| + functionTypeChecks.forEach((FunctionType functionType, bool knownSubtype) {
|
| + if (knownSubtype) {
|
| + if (alwaysUseSignature) {
|
| + registerDynamicFunctionTypeCheck(functionType);
|
| + } else {
|
| + emitIsFunctionTypeTest(functionType);
|
| + }
|
| + }
|
| + });
|
| + }
|
| +
|
| /**
|
| * Return a function that returns true if its argument is a class
|
| * that needs to be emitted.
|
| @@ -2021,12 +2143,6 @@ class CodeEmitterTask extends CompilerTask {
|
|
|
| addParameterStubs(callElement, closureBuilder.addProperty);
|
|
|
| - DartType type = element.computeType(compiler);
|
| - getTypedefChecksOn(type).forEach((Element typedef) {
|
| - String operator = namer.operatorIs(typedef);
|
| - closureBuilder.addProperty(operator, js('true'));
|
| - });
|
| -
|
| // TODO(ngeoffray): Cache common base classes for closures, bound
|
| // closures, and static closures that have common type checks.
|
| boundClosures.add(
|
| @@ -2034,6 +2150,28 @@ class CodeEmitterTask extends CompilerTask {
|
| closureBuilder.toObjectInitializer()));
|
|
|
| staticGetters[element] = closureClassElement;
|
| +
|
| + void emitFunctionTypeSignature(Element method, FunctionType methodType) {
|
| + RuntimeTypes rti = backend.rti;
|
| + // [:() => null:] is dummy encoding of [this] which is never needed for
|
| + // the encoding of the type of the static [method].
|
| + String encoding = rti.getSignatureEncoding(methodType, () => 'null');
|
| + String operatorSignature = namer.operatorSignature();
|
| + // TODO(johnniwinther): Make MiniJsParser support function expressions.
|
| + closureBuilder.addProperty(operatorSignature,
|
| + new jsAst.LiteralExpression(encoding));
|
| + }
|
| +
|
| + void emitIsFunctionTypeTest(FunctionType functionType) {
|
| + String operator = namer.operatorIsType(functionType);
|
| + closureBuilder.addProperty(operator, js('true'));
|
| + }
|
| +
|
| + FunctionType methodType = element.computeType(compiler);
|
| + Map<FunctionType, bool> functionTypeChecks =
|
| + getFunctionTypeChecksOn(methodType);
|
| + generateFunctionTypeTests(element, methodType, functionTypeChecks,
|
| + emitFunctionTypeSignature, emitIsFunctionTypeTest);
|
| }
|
| }
|
|
|
| @@ -2091,12 +2229,14 @@ class CodeEmitterTask extends CompilerTask {
|
| fieldNames.add(namer.getName(field));
|
| });
|
|
|
| - Iterable<Element> typedefChecks =
|
| - getTypedefChecksOn(member.computeType(compiler));
|
| - bool hasTypedefChecks = !typedefChecks.isEmpty;
|
| + DartType memberType = member.computeType(compiler);
|
| + Map<FunctionType, bool> functionTypeChecks =
|
| + getFunctionTypeChecksOn(memberType);
|
| + bool hasFunctionTypeChecks = !functionTypeChecks.isEmpty;
|
|
|
| - bool canBeShared = !hasOptionalParameters && !hasTypedefChecks;
|
| + bool canBeShared = !hasOptionalParameters && !hasFunctionTypeChecks;
|
|
|
| + ClassElement classElement = member.getEnclosingClass();
|
| String closureClass = canBeShared ? cache[parameterCount] : null;
|
| if (closureClass == null) {
|
| // Either the class was not cached yet, or there are optional parameters.
|
| @@ -2152,10 +2292,23 @@ class CodeEmitterTask extends CompilerTask {
|
| boundClosureBuilder.addProperty(invocationName, fun);
|
|
|
| addParameterStubs(callElement, boundClosureBuilder.addProperty);
|
| - typedefChecks.forEach((Element typedef) {
|
| - String operator = namer.operatorIs(typedef);
|
| - boundClosureBuilder.addProperty(operator, js('true'));
|
| - });
|
| +
|
| + void emitFunctionTypeSignature(Element method, FunctionType methodType) {
|
| + String encoding = backend.rti.getSignatureEncoding(
|
| + methodType, () => 'this.${fieldNames[0]}');
|
| + String operatorSignature = namer.operatorSignature();
|
| + boundClosureBuilder.addProperty(operatorSignature,
|
| + new jsAst.LiteralExpression(encoding));
|
| + }
|
| +
|
| + void emitIsFunctionTypeTest(FunctionType functionType) {
|
| + String operator = namer.operatorIsType(functionType);
|
| + boundClosureBuilder.addProperty(operator,
|
| + new jsAst.LiteralBool(true));
|
| + }
|
| +
|
| + generateFunctionTypeTests(member, memberType, functionTypeChecks,
|
| + emitFunctionTypeSignature, emitIsFunctionTypeTest);
|
|
|
| boundClosures.add(
|
| js('$classesCollector.$mangledName = #',
|
| @@ -2187,8 +2340,7 @@ class CodeEmitterTask extends CompilerTask {
|
| }
|
|
|
| jsAst.Expression getterFunction = js.fun(
|
| - parameters,
|
| - js.return_(js(closureClass).newWith(arguments)));
|
| + parameters, js.return_(js(closureClass).newWith(arguments)));
|
|
|
| defineStub(getterName, getterFunction);
|
| }
|
| @@ -2228,7 +2380,6 @@ class CodeEmitterTask extends CompilerTask {
|
| // identical stubs for each we track untyped selectors which already have
|
| // stubs.
|
| Set<Selector> generatedSelectors = new Set<Selector>();
|
| -
|
| for (Selector selector in selectors) {
|
| if (selector.applies(member, compiler)) {
|
| selector = selector.asUntyped;
|
| @@ -2833,6 +2984,15 @@ if (typeof document !== "undefined" && document.readyState !== "complete") {
|
| rti.getClassesUsedInSubstitutions(backend, requiredChecks);
|
| addClassesWithSuperclasses(classesUsedInSubstitutions);
|
|
|
| + // 3c. Add classes that contain checked generic function types. These are
|
| + // needed to store the signature encoding.
|
| + for (FunctionType type in checkedFunctionTypes) {
|
| + ClassElement contextClass = Types.getClassContext(type);
|
| + if (contextClass != null) {
|
| + neededClasses.add(contextClass);
|
| + }
|
| + }
|
| +
|
| // 4. Finally, sort the classes.
|
| List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses);
|
|
|
|
|