| 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 8567adb1d0d08ac55742a1b4016569174eb74638..04292612ab20375ec464bcde5c1f3546d1510745 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
|
| @@ -11,6 +11,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;
|
| @@ -613,25 +726,72 @@ class JavaScriptBackend extends Backend {
|
| enqueueInResolution(getGetRuntimeTypeArgument(), elements);
|
| }
|
|
|
| - void registerRuntimeType(TreeElements elements) {
|
| + void registerGenericCallMethod(Element callMethod,
|
| + Enqueuer enqueuer, TreeElements elements) {
|
| + if (enqueuer.isResolutionQueue || methodNeedsRti(callMethod)) {
|
| + registerComputeSignature(enqueuer, elements);
|
| + }
|
| + }
|
| +
|
| + void registerGenericClosure(Element closure,
|
| + Enqueuer enqueuer, TreeElements elements) {
|
| + if (enqueuer.isResolutionQueue || methodNeedsRti(closure)) {
|
| + registerComputeSignature(enqueuer, elements);
|
| + }
|
| + }
|
| +
|
| + void registerComputeSignature(Enqueuer enqueuer, TreeElements elements) {
|
| + // Calls to [:computeSignature:] 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, getComputeSignature(), elements);
|
| + enqueue(enqueuer, getGetRuntimeTypeArguments(), elements);
|
| + enqueuer.registerInstantiatedClass(compiler.listClass, elements);
|
| + }
|
| +
|
| + void registerRuntimeType(Enqueuer enqueuer, TreeElements elements) {
|
| + registerComputeSignature(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) {
|
| + type = type.unalias(compiler);
|
| 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;
|
| + }
|
| + bool isTypeVariable = type.kind == TypeKind.TYPE_VARIABLE;
|
| + if (!type.isRaw || type.containsTypeVariables) {
|
| enqueueInResolution(getSetRuntimeTypeInfo(), elements);
|
| enqueueInResolution(getGetRuntimeTypeInfo(), elements);
|
| enqueueInResolution(getGetRuntimeTypeArgument(), elements);
|
| @@ -647,14 +807,8 @@ 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
|
| @@ -663,14 +817,17 @@ class JavaScriptBackend extends Backend {
|
| world.addToWorkList(
|
| compiler.findHelper(const SourceString('defineProperty')));
|
| }
|
| - }
|
| + }
|
|
|
| void registerAsCheck(DartType type, TreeElements elements) {
|
| - Element e = getCheckedModeHelper(type, typeCast: true);
|
| - enqueueInResolution(e, elements);
|
| + type = type.unalias(compiler);
|
| + 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) {
|
| @@ -734,7 +891,7 @@ class JavaScriptBackend extends Backend {
|
| rti.classesUsingTypeVariableExpression.add(cls);
|
| }
|
|
|
| - bool needsRti(ClassElement cls) {
|
| + bool classNeedsRti(ClassElement cls) {
|
| return rti.classesNeedingRti.contains(cls.declaration) ||
|
| compiler.enabledRuntimeType;
|
| }
|
| @@ -754,11 +911,20 @@ class JavaScriptBackend extends Backend {
|
| || classElement == jsNullClass;
|
| }
|
|
|
| + bool methodNeedsRti(FunctionElement function) {
|
| + return rti.methodsNeedingRti.contains(function) ||
|
| + 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) {
|
| @@ -857,10 +1023,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);
|
| }
|
|
|
| /**
|
| @@ -868,20 +1033,19 @@ 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}) {
|
| + assert(type.kind != TypeKind.TYPEDEF);
|
| Element element = type.element;
|
| bool nativeCheck = nativeCheckOnly ||
|
| emitter.nativeEmitter.requiresNativeIsCheck(element);
|
| @@ -890,94 +1054,122 @@ 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'));
|
| }
|
| }
|
| }
|
| @@ -992,6 +1184,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'));
|
| @@ -1034,6 +1230,14 @@ class JavaScriptBackend extends Backend {
|
| return compiler.findHelper(const SourceString('getRuntimeTypeInfo'));
|
| }
|
|
|
| + Element getComputeSignature() {
|
| + return compiler.findHelper(const SourceString('computeSignature'));
|
| + }
|
| +
|
| + Element getGetRuntimeTypeArguments() {
|
| + return compiler.findHelper(const SourceString('getRuntimeTypeArguments'));
|
| + }
|
| +
|
| Element getGetRuntimeTypeArgument() {
|
| return compiler.findHelper(const SourceString('getRuntimeTypeArgument'));
|
| }
|
| @@ -1059,6 +1263,10 @@ class JavaScriptBackend extends Backend {
|
| const SourceString('assertSubtypeOfRuntimeType'));
|
| }
|
|
|
| + Element getCheckFunctionSubtype() {
|
| + return compiler.findHelper(const SourceString('checkFunctionSubtype'));
|
| + }
|
| +
|
| Element getThrowNoSuchMethod() {
|
| return compiler.findHelper(const SourceString('throwNoSuchMethod'));
|
| }
|
|
|