Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/js_backend/backend.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart |
| index fde02b450a584b5c36a7c8c80daaa77182da543f..4c2c62b72f48262fe3b250eb68fce6281aadd976 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart |
| @@ -612,6 +612,119 @@ class JavaScriptItemCompilationContext extends ItemCompilationContext { |
| : boundsChecked = new Set<HInstruction>(); |
| } |
| + |
| +class CheckedModeHelper { |
| + final SourceString name; |
| + |
| + const CheckedModeHelper(SourceString this.name); |
| + |
| + Element getElement(Compiler compiler) => compiler.findHelper(name); |
| + |
| + jsAst.Expression generateCall(SsaCodeGenerator codegen, |
| + HTypeConversion node) { |
| + Element helperElement = getElement(codegen.compiler); |
| + codegen.world.registerStaticUse(helperElement); |
| + List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| + codegen.use(node.checkedInput); |
| + arguments.add(codegen.pop()); |
| + generateAdditionalArguments(codegen, node, arguments); |
| + String helperName = codegen.backend.namer.isolateAccess(helperElement); |
| + return new jsAst.Call(new jsAst.VariableUse(helperName), arguments); |
| + } |
| + |
| + void generateAdditionalArguments(SsaCodeGenerator codegen, |
| + HTypeConversion node, |
| + List<jsAst.Expression> arguments) { |
| + assert(!node.typeExpression.isMalformed); |
| + // No additional arguments needed. |
| + } |
| +} |
| + |
| +class PropertyCheckedModeHelper extends CheckedModeHelper { |
| + const PropertyCheckedModeHelper(SourceString name) : super(name); |
| + |
| + void generateAdditionalArguments(SsaCodeGenerator codegen, |
| + HTypeConversion node, |
| + List<jsAst.Expression> arguments) { |
| + DartType type = node.typeExpression; |
| + assert(!type.isMalformed); |
| + String additionalArgument = codegen.backend.namer.operatorIsType(type); |
| + arguments.add(js.string(additionalArgument)); |
| + } |
| +} |
| + |
| +class TypeVariableCheckedModeHelper extends CheckedModeHelper { |
| + const TypeVariableCheckedModeHelper(SourceString name) : super(name); |
| + |
| + void generateAdditionalArguments(SsaCodeGenerator codegen, |
| + HTypeConversion node, |
| + List<jsAst.Expression> arguments) { |
| + assert(node.typeExpression.kind == TypeKind.TYPE_VARIABLE); |
| + codegen.use(node.typeRepresentation); |
| + arguments.add(codegen.pop()); |
| + } |
| +} |
| + |
| +class SubtypeCheckedModeHelper extends CheckedModeHelper { |
| + const SubtypeCheckedModeHelper(SourceString name) : super(name); |
| + |
| + void generateAdditionalArguments(SsaCodeGenerator codegen, |
| + HTypeConversion node, |
| + List<jsAst.Expression> arguments) { |
| + DartType type = node.typeExpression; |
| + Element element = type.element; |
| + String isField = codegen.backend.namer.operatorIs(element); |
| + arguments.add(js.string(isField)); |
| + codegen.use(node.typeRepresentation); |
| + arguments.add(codegen.pop()); |
| + String asField = codegen.backend.namer.substitutionName(element); |
| + arguments.add(js.string(asField)); |
| + } |
| +} |
| + |
| +class FunctionTypeCheckedModeHelper extends CheckedModeHelper { |
| + const FunctionTypeCheckedModeHelper(SourceString name) : super(name); |
| + |
| + void generateAdditionalArguments(SsaCodeGenerator codegen, |
| + HTypeConversion node, |
| + List<jsAst.Expression> arguments) { |
| + DartType type = node.typeExpression; |
| + String signatureName = codegen.backend.namer.getFunctionTypeName(type); |
| + arguments.add(js.string(signatureName)); |
| + |
| + if (type.containsTypeVariables) { |
| + ClassElement contextClass = Types.getClassContext(type); |
| + String contextName = codegen.backend.namer.getName(contextClass); |
| + arguments.add(js.string(contextName)); |
| + |
| + if (node.contextIsTypeArguments) { |
| + arguments.add(new jsAst.LiteralNull()); |
| + codegen.use(node.context); |
| + arguments.add(codegen.pop()); |
| + } else { |
| + codegen.use(node.context); |
| + arguments.add(codegen.pop()); |
| + } |
| + } |
| + } |
| +} |
| + |
| +class MalformedCheckedModeHelper extends CheckedModeHelper { |
| + const MalformedCheckedModeHelper(SourceString name) : super(name); |
| + |
| + void generateAdditionalArguments(SsaCodeGenerator codegen, |
| + HTypeConversion node, |
| + List<jsAst.Expression> arguments) { |
| + DartType type = node.typeExpression; |
| + assert(type.isMalformed); |
| + String reasons = Types.fetchReasonsFromMalformedType(type); |
| + arguments.add(js.string('$type')); |
| + // TODO(johnniwinther): Handle escaping correctly. |
| + arguments.add(js.string(reasons)); |
| + } |
| +} |
| + |
| + |
| class JavaScriptBackend extends Backend { |
| SsaBuilderTask builder; |
| SsaOptimizerTask optimizer; |
| @@ -1226,25 +1339,80 @@ class JavaScriptBackend extends Backend { |
| enqueueInResolution(getGetRuntimeTypeArgument(), elements); |
| } |
| - void registerRuntimeType(TreeElements elements) { |
| + void registerGenericCallMethod(Element callMethod, |
| + Enqueuer enqueuer, TreeElements elements) { |
| + if (enqueuer.isResolutionQueue || methodNeedsRti(callMethod)) { |
| + registerApplySignature(enqueuer, elements); |
| + } |
| + } |
| + |
| + void registerGenericClosure(Element closure, |
| + Enqueuer enqueuer, TreeElements elements) { |
| + if (enqueuer.isResolutionQueue || methodNeedsRti(closure)) { |
| + registerApplySignature(enqueuer, elements); |
| + } |
| + } |
| + |
| + void registerApplySignature(Enqueuer enqueuer, TreeElements elements) { |
| + // Calls to [:applySignature:] are generated by the emitter and we therefore |
| + // need to enqueue the used elements in the codegen enqueuer as well as in |
| + // the resolution enqueuer. |
| + enqueue(enqueuer, getSetRuntimeTypeInfo(), elements); |
| + enqueue(enqueuer, getGetRuntimeTypeInfo(), elements); |
| + enqueue(enqueuer, getApplySignature(), elements); |
| + enqueue(enqueuer, getGetRuntimeTypeArguments(), elements); |
| + enqueuer.registerInstantiatedClass(compiler.listClass, elements); |
| + } |
| + |
| + void registerRuntimeType(Enqueuer enqueuer, TreeElements elements) { |
| + registerApplySignature(enqueuer, elements); |
| enqueueInResolution(getSetRuntimeTypeInfo(), elements); |
| enqueueInResolution(getGetRuntimeTypeInfo(), elements); |
| - enqueueInResolution(getGetRuntimeTypeArgument(), elements); |
| + registerGetRuntimeTypeArgument(elements); |
| compiler.enqueuer.resolution.registerInstantiatedClass( |
| compiler.listClass, elements); |
| } |
| void registerTypeVariableExpression(TreeElements elements) { |
| - registerRuntimeType(elements); |
| + enqueueInResolution(getSetRuntimeTypeInfo(), elements); |
| + enqueueInResolution(getGetRuntimeTypeInfo(), elements); |
| + registerGetRuntimeTypeArgument(elements); |
| + compiler.enqueuer.resolution.registerInstantiatedClass( |
| + compiler.listClass, elements); |
| enqueueInResolution(getRuntimeTypeToString(), elements); |
| enqueueInResolution(getCreateRuntimeType(), elements); |
| } |
| void registerIsCheck(DartType type, Enqueuer world, TreeElements elements) { |
| world.registerInstantiatedClass(compiler.boolClass, elements); |
| - bool isTypeVariable = type.kind == TypeKind.TYPE_VARIABLE; |
| bool inCheckedMode = compiler.enableTypeAssertions; |
| - if (!type.isRaw || isTypeVariable) { |
| + // [registerIsCheck] is also called for checked mode checks, so we |
| + // need to register checked mode helpers. |
| + if (inCheckedMode) { |
| + CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: false); |
| + if (helper != null) world.addToWorkList(helper.getElement(compiler)); |
| + // We also need the native variant of the check (for DOM types). |
| + helper = getNativeCheckedModeHelper(type, typeCast: false); |
| + if (helper != null) world.addToWorkList(helper.getElement(compiler)); |
| + if (type.isMalformed) { |
| + enqueueInResolution(getThrowMalformedSubtypeError(), elements); |
| + return; |
| + } |
| + } else { |
| + if (type.isMalformed) { |
| + registerThrowRuntimeError(elements); |
| + return; |
| + } |
| + } |
| + if (type.element.isNative()) { |
| + // We will neeed to add the "$is" and "$as" properties on the |
| + // JavaScript object prototype, so we make sure |
| + // [:defineProperty:] is compiled. |
| + world.addToWorkList( |
| + compiler.findHelper(const SourceString('defineProperty'))); |
|
karlklose
2013/06/19 14:37:05
Why is that necessary? Shouldn't it be enough to r
Johnni Winther
2013/06/21 12:19:15
I have no idea. The code was just moved from below
|
| + } |
| + bool isTypeVariable = type.kind == TypeKind.TYPE_VARIABLE; |
| + if (!type.isRaw || type.containsTypeVariables) { |
| enqueueInResolution(getSetRuntimeTypeInfo(), elements); |
| enqueueInResolution(getGetRuntimeTypeInfo(), elements); |
| enqueueInResolution(getGetRuntimeTypeArgument(), elements); |
| @@ -1260,30 +1428,19 @@ class JavaScriptBackend extends Backend { |
| } |
| world.registerInstantiatedClass(compiler.listClass, elements); |
| } |
| - // [registerIsCheck] is also called for checked mode checks, so we |
| - // need to register checked mode helpers. |
| - if (inCheckedMode) { |
| - Element e = getCheckedModeHelper(type, typeCast: false); |
| - if (e != null) world.addToWorkList(e); |
| - // We also need the native variant of the check (for DOM types). |
| - e = getNativeCheckedModeHelper(type, typeCast: false); |
| - if (e != null) world.addToWorkList(e); |
| + if (type is FunctionType) { |
| + enqueueInResolution(getCheckFunctionSubtype(), elements); |
| } |
| - if (type.element.isNative()) { |
| - // We will neeed to add the "$is" and "$as" properties on the |
| - // JavaScript object prototype, so we make sure |
| - // [:defineProperty:] is compiled. |
| - world.addToWorkList( |
| - compiler.findHelper(const SourceString('defineProperty'))); |
| - } |
| - } |
| + } |
| void registerAsCheck(DartType type, TreeElements elements) { |
| - Element e = getCheckedModeHelper(type, typeCast: true); |
| - enqueueInResolution(e, elements); |
| + CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: true); |
| + enqueueInResolution(helper.getElement(compiler), elements); |
| // We also need the native variant of the check (for DOM types). |
| - e = getNativeCheckedModeHelper(type, typeCast: true); |
| - enqueueInResolution(e, elements); |
| + helper = getNativeCheckedModeHelper(type, typeCast: true); |
| + if (helper != null) { |
| + enqueueInResolution(helper.getElement(compiler), elements); |
| + } |
| } |
| void registerThrowNoSuchMethod(TreeElements elements) { |
| @@ -1367,11 +1524,19 @@ class JavaScriptBackend extends Backend { |
| || classElement == jsNullClass; |
| } |
| + bool methodNeedsRti(Element cls) { |
| + return rti.methodsNeedingRti.contains(cls) || compiler.enabledRuntimeType; |
| + } |
| + |
| + void enqueue(Enqueuer enqueuer, Element e, TreeElements elements) { |
| + enqueuer.addToWorkList(e); |
| + elements.registerDependency(e); |
| + } |
| + |
| void enqueueInResolution(Element e, TreeElements elements) { |
| if (e == null) return; |
| ResolutionEnqueuer enqueuer = compiler.enqueuer.resolution; |
| - enqueuer.addToWorkList(e); |
| - elements.registerDependency(e); |
| + enqueue(enqueuer, e, elements); |
| } |
| void registerConstantMap(TreeElements elements) { |
| @@ -1613,10 +1778,9 @@ class JavaScriptBackend extends Backend { |
| * the resolver with interface types (int, String, ...), and by the SSA |
| * backend with implementation types (JSInt, JSString, ...). |
| */ |
| - Element getCheckedModeHelper(DartType type, {bool typeCast}) { |
| - SourceString name = getCheckedModeHelperName( |
| + CheckedModeHelper getCheckedModeHelper(DartType type, {bool typeCast}) { |
| + return getCheckedModeHelperInternal( |
| type, typeCast: typeCast, nativeCheckOnly: false); |
| - return compiler.findHelper(name); |
| } |
| /** |
| @@ -1624,20 +1788,18 @@ class JavaScriptBackend extends Backend { |
| * check/type cast on [type] at runtime. If no native helper exists for |
| * [type], [:null:] is returned. |
| */ |
| - Element getNativeCheckedModeHelper(DartType type, {bool typeCast}) { |
| - SourceString sourceName = getCheckedModeHelperName( |
| + CheckedModeHelper getNativeCheckedModeHelper(DartType type, {bool typeCast}) { |
| + return getCheckedModeHelperInternal( |
| type, typeCast: typeCast, nativeCheckOnly: true); |
| - if (sourceName == null) return null; |
| - return compiler.findHelper(sourceName); |
| } |
| /** |
| - * Returns the name of the type check/type cast helper method for [type]. If |
| + * Returns the checked mode helper for the type check/type cast for [type]. If |
| * [nativeCheckOnly] is [:true:], only names for native helpers are returned. |
| */ |
| - SourceString getCheckedModeHelperName(DartType type, |
| - {bool typeCast, |
| - bool nativeCheckOnly}) { |
| + CheckedModeHelper getCheckedModeHelperInternal(DartType type, |
| + {bool typeCast, |
| + bool nativeCheckOnly}) { |
| Element element = type.element; |
| bool nativeCheck = nativeCheckOnly || |
| emitter.nativeEmitter.requiresNativeIsCheck(element); |
| @@ -1646,94 +1808,98 @@ class JavaScriptBackend extends Backend { |
| // with a malformed argument type. |
| if (nativeCheckOnly) return null; |
| return typeCast |
| - ? const SourceString('malformedTypeCast') |
| - : const SourceString('malformedTypeCheck'); |
| + ? const MalformedCheckedModeHelper(const SourceString('malformedTypeCast')) |
| + : const MalformedCheckedModeHelper(const SourceString('malformedTypeCheck')); |
| } else if (type == compiler.types.voidType) { |
| assert(!typeCast); // Cannot cast to void. |
| if (nativeCheckOnly) return null; |
| - return const SourceString('voidTypeCheck'); |
| + return const CheckedModeHelper(const SourceString('voidTypeCheck')); |
| } else if (element == jsStringClass || element == compiler.stringClass) { |
| if (nativeCheckOnly) return null; |
| return typeCast |
| - ? const SourceString("stringTypeCast") |
| - : const SourceString('stringTypeCheck'); |
| + ? const CheckedModeHelper(const SourceString("stringTypeCast")) |
| + : const CheckedModeHelper(const SourceString('stringTypeCheck')); |
| } else if (element == jsDoubleClass || element == compiler.doubleClass) { |
| if (nativeCheckOnly) return null; |
| return typeCast |
| - ? const SourceString("doubleTypeCast") |
| - : const SourceString('doubleTypeCheck'); |
| + ? const CheckedModeHelper(const SourceString("doubleTypeCast")) |
| + : const CheckedModeHelper(const SourceString('doubleTypeCheck')); |
| } else if (element == jsNumberClass || element == compiler.numClass) { |
| if (nativeCheckOnly) return null; |
| return typeCast |
| - ? const SourceString("numTypeCast") |
| - : const SourceString('numTypeCheck'); |
| + ? const CheckedModeHelper(const SourceString("numTypeCast")) |
| + : const CheckedModeHelper(const SourceString('numTypeCheck')); |
| } else if (element == jsBoolClass || element == compiler.boolClass) { |
| if (nativeCheckOnly) return null; |
| return typeCast |
| - ? const SourceString("boolTypeCast") |
| - : const SourceString('boolTypeCheck'); |
| + ? const CheckedModeHelper(const SourceString("boolTypeCast")) |
| + : const CheckedModeHelper(const SourceString('boolTypeCheck')); |
| } else if (element == jsIntClass || element == compiler.intClass) { |
| if (nativeCheckOnly) return null; |
| - return typeCast ? |
| - const SourceString("intTypeCast") : |
| - const SourceString('intTypeCheck'); |
| + return typeCast |
| + ? const CheckedModeHelper(const SourceString("intTypeCast")) |
| + : const CheckedModeHelper(const SourceString('intTypeCheck')); |
| } else if (Elements.isNumberOrStringSupertype(element, compiler)) { |
| if (nativeCheck) { |
| return typeCast |
| - ? const SourceString("numberOrStringSuperNativeTypeCast") |
| - : const SourceString('numberOrStringSuperNativeTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("numberOrStringSuperNativeTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('numberOrStringSuperNativeTypeCheck')); |
| } else { |
| return typeCast |
| - ? const SourceString("numberOrStringSuperTypeCast") |
| - : const SourceString('numberOrStringSuperTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("numberOrStringSuperTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('numberOrStringSuperTypeCheck')); |
| } |
| } else if (Elements.isStringOnlySupertype(element, compiler)) { |
| if (nativeCheck) { |
| return typeCast |
| - ? const SourceString("stringSuperNativeTypeCast") |
| - : const SourceString('stringSuperNativeTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("stringSuperNativeTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('stringSuperNativeTypeCheck')); |
| } else { |
| return typeCast |
| - ? const SourceString("stringSuperTypeCast") |
| - : const SourceString('stringSuperTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("stringSuperTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('stringSuperTypeCheck')); |
| } |
| } else if ((element == compiler.listClass || element == jsArrayClass) && |
| type.isRaw) { |
| if (nativeCheckOnly) return null; |
| return typeCast |
| - ? const SourceString("listTypeCast") |
| - : const SourceString('listTypeCheck'); |
| + ? const CheckedModeHelper(const SourceString("listTypeCast")) |
| + : const CheckedModeHelper(const SourceString('listTypeCheck')); |
| } else { |
| if (Elements.isListSupertype(element, compiler)) { |
| if (nativeCheck) { |
| return typeCast |
| - ? const SourceString("listSuperNativeTypeCast") |
| - : const SourceString('listSuperNativeTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("listSuperNativeTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('listSuperNativeTypeCheck')); |
| } else { |
| return typeCast |
| - ? const SourceString("listSuperTypeCast") |
| - : const SourceString('listSuperTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("listSuperTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('listSuperTypeCheck')); |
| } |
| } else { |
| if (nativeCheck) { |
| // TODO(karlklose): can we get rid of this branch when we use |
| // interceptors? |
| return typeCast |
| - ? const SourceString("interceptedTypeCast") |
| - : const SourceString('interceptedTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString("interceptedTypeCast")) |
| + : const PropertyCheckedModeHelper(const SourceString('interceptedTypeCheck')); |
| } else { |
| if (type.kind == TypeKind.INTERFACE && !type.isRaw) { |
| return typeCast |
| - ? const SourceString('subtypeCast') |
| - : const SourceString('assertSubtype'); |
| + ? const SubtypeCheckedModeHelper(const SourceString('subtypeCast')) |
| + : const SubtypeCheckedModeHelper(const SourceString('assertSubtype')); |
| } else if (type.kind == TypeKind.TYPE_VARIABLE) { |
| return typeCast |
| - ? const SourceString('subtypeOfRuntimeTypeCast') |
| - : const SourceString('assertSubtypeOfRuntimeType'); |
| + ? const TypeVariableCheckedModeHelper(const SourceString('subtypeOfRuntimeTypeCast')) |
| + : const TypeVariableCheckedModeHelper(const SourceString('assertSubtypeOfRuntimeType')); |
| + } else if (type.kind == TypeKind.FUNCTION) { |
| + return typeCast |
| + ? const FunctionTypeCheckedModeHelper(const SourceString('functionSubtypeCast')) |
| + : const FunctionTypeCheckedModeHelper(const SourceString('assertFunctionSubtype')); |
| } else { |
| return typeCast |
| - ? const SourceString('propertyTypeCast') |
| - : const SourceString('propertyTypeCheck'); |
| + ? const PropertyCheckedModeHelper(const SourceString('propertyTypeCast')) |
| + : const PropertyCheckedModeHelper(const SourceString('propertyTypeCheck')); |
| } |
| } |
| } |
| @@ -1763,6 +1929,10 @@ class JavaScriptBackend extends Backend { |
| return compiler.findHelper(const SourceString('throwRuntimeError')); |
| } |
| + Element getMalformedTypeCheck() { |
| + return compiler.findHelper(const SourceString('malformedTypeCheck')); |
| + } |
| + |
| Element getThrowMalformedSubtypeError() { |
| return compiler.findHelper( |
| const SourceString('throwMalformedSubtypeError')); |
| @@ -1805,6 +1975,14 @@ class JavaScriptBackend extends Backend { |
| return compiler.findHelper(const SourceString('getRuntimeTypeInfo')); |
| } |
| + Element getApplySignature() { |
| + return compiler.findHelper(const SourceString('applySignature')); |
| + } |
| + |
| + Element getGetRuntimeTypeArguments() { |
| + return compiler.findHelper(const SourceString('getRuntimeTypeArguments')); |
| + } |
| + |
| Element getGetRuntimeTypeArgument() { |
| return compiler.findHelper(const SourceString('getRuntimeTypeArgument')); |
| } |
| @@ -1830,6 +2008,10 @@ class JavaScriptBackend extends Backend { |
| const SourceString('assertSubtypeOfRuntimeType')); |
| } |
| + Element getCheckFunctionSubtype() { |
| + return compiler.findHelper(const SourceString('checkFunctionSubtype')); |
| + } |
| + |
| Element getThrowNoSuchMethod() { |
| return compiler.findHelper(const SourceString('throwNoSuchMethod')); |
| } |