| Index: sdk/lib/_internal/compiler/implementation/lib/js_rti.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_rti.dart b/sdk/lib/_internal/compiler/implementation/lib/js_rti.dart
|
| index 0cd37e1cd1031abe633b22be37ac8a73cf60b5a9..e1641d65d6f3c7a01f6d86c5a54c89e6896372dc 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/lib/js_rti.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/lib/js_rti.dart
|
| @@ -15,8 +15,17 @@ getRuntimeTypeInfo(target) {
|
| return JS('var', r'#.$builtinTypeInfo', target);
|
| }
|
|
|
| +/**
|
| + * Returns the type arguments of [target] as an instance of [substitutionName].
|
| + */
|
| +getRuntimeTypeArguments(target, substitutionName) {
|
| + var substitution =
|
| + getField(target, '${JS_OPERATOR_AS_PREFIX()}$substitutionName');
|
| + return substitute(substitution, getRuntimeTypeInfo(target));
|
| +}
|
| +
|
| getRuntimeTypeArgument(target, substitution, index) {
|
| - var arguments = substitute(substitution, getRuntimeTypeInfo(target));
|
| + var arguments = getRuntimeTypeArguments(target, substitution);
|
| return (arguments == null) ? null : getField(arguments, index);
|
| }
|
|
|
| @@ -63,7 +72,7 @@ String joinArguments(var types, int startIndex) {
|
| bool firstArgument = true;
|
| bool allDynamic = true;
|
| StringBuffer buffer = new StringBuffer();
|
| - for (int index = startIndex; index < types.length; index++) {
|
| + for (int index = startIndex; index < getLength(types); index++) {
|
| if (firstArgument) {
|
| firstArgument = false;
|
| } else {
|
| @@ -91,17 +100,28 @@ Type getRuntimeType(var object) {
|
|
|
| bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o);
|
|
|
| +bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o);
|
| +
|
| Object invoke(function, arguments) {
|
| return JS('var', r'#.apply(null, #)', function, arguments);
|
| }
|
|
|
| +Object invokeOn(function, receiver, arguments) {
|
| + return JS('var', r'#.apply(#, #)', function, receiver, arguments);
|
| +}
|
| +
|
| Object call(target, name) => JS('var', r'#[#]()', target, name);
|
|
|
| substitute(var substitution, var arguments) {
|
| if (substitution is JSArray) {
|
| arguments = substitution;
|
| } else if (isJsFunction(substitution)) {
|
| - arguments = invoke(substitution, arguments);
|
| + substitution = invoke(substitution, arguments);
|
| + if (isJsArray(substitution)) {
|
| + arguments = substitution;
|
| + } else if (isJsFunction(substitution)) {
|
| + arguments = invoke(substitution, arguments);
|
| + }
|
| }
|
| return arguments;
|
| }
|
| @@ -146,7 +166,7 @@ Object subtypeCast(Object object, String isField, List checks, String asField) {
|
| if (object != null && !checkSubtype(object, isField, checks, asField)) {
|
| String actualType = Primitives.objectTypeName(object);
|
| String typeName = computeTypeName(isField, checks);
|
| - throw new CastErrorImplementation(object, typeName);
|
| + throw new CastErrorImplementation(actualType, typeName);
|
| }
|
| return object;
|
| }
|
| @@ -172,17 +192,88 @@ bool checkArguments(var substitution, var arguments, var checks) {
|
| return areSubtypes(substitute(substitution, arguments), checks);
|
| }
|
|
|
| +Object functionSubtypeCast(Object object, String signatureName,
|
| + String contextName, var context,
|
| + var typeArguments) {
|
| + if (!checkFunctionSubtype(object, signatureName,
|
| + contextName, context, typeArguments)) {
|
| + String actualType = Primitives.objectTypeName(object);
|
| + // TODO(johnniwinther): Provide better function type naming.
|
| + String typeName = signatureName;
|
| + throw new CastErrorImplementation(actualType, typeName);
|
| + }
|
| + return object;
|
| +}
|
| +
|
| +Object assertFunctionSubtype(Object object, String signatureName,
|
| + String contextName, var context,
|
| + var typeArguments) {
|
| + if (!checkFunctionSubtype(object, signatureName,
|
| + contextName, context, typeArguments)) {
|
| + // TODO(johnniwinther): Provide better function type naming.
|
| + String typeName = signatureName;
|
| + throw new TypeErrorImplementation(object, typeName);
|
| + }
|
| + return object;
|
| +}
|
| +
|
| +/**
|
| + * Checks that the type of [target] is a subtype of the function type denoted by
|
| + * [signatureName]. The
|
| + */
|
| +bool checkFunctionSubtype(var target, String signatureName,
|
| + String contextName, var context,
|
| + var typeArguments) {
|
| + if (isNull(target)) return true;
|
| + if (hasField(target, '${JS_OPERATOR_IS_PREFIX()}_$signatureName')) {
|
| + return true;
|
| + }
|
| + var signatureLocation = JS_SETUP_OBJECT();
|
| + if (isNotNull(contextName)) {
|
| + signatureLocation = getField(signatureLocation, contextName);
|
| + }
|
| + var typeSignature =
|
| + getField(signatureLocation, '${JS_SIGNATURE_NAME()}_$signatureName');
|
| + if (isNull(typeSignature)) {
|
| + // All checks can be determined statically so the type signature has not
|
| + // been computed.
|
| + return false;
|
| + }
|
| + var targetSignatureFunction = getField(target, '${JS_SIGNATURE_NAME()}');
|
| + var targetSignature = invokeOn(targetSignatureFunction, target, null);
|
| + if (isJsFunction(typeSignature)) {
|
| + if (isNotNull(typeArguments)) {
|
| + typeSignature = invoke(typeSignature, typeArguments);
|
| + } else if (isNotNull(context)) {
|
| + typeSignature =
|
| + invoke(typeSignature, getRuntimeTypeArguments(context, contextName));
|
| + } else {
|
| + typeSignature = invoke(typeSignature, null);
|
| + }
|
| + }
|
| + return isFunctionSubtype(targetSignature, typeSignature);
|
| +}
|
| +
|
| +/**
|
| + * Applies the type arguments of [context] as an instance of [contextName] to
|
| + * the signature function [signature].
|
| + */
|
| +applySignature(var signature, var context, var contextName) {
|
| + var typeArguments = getRuntimeTypeArguments(context, contextName);
|
| + return invokeOn(signature, context, typeArguments);
|
| +}
|
| +
|
| bool areSubtypes(List s, List t) {
|
| // [:null:] means a raw type.
|
| - if (s == null || t == null) return true;
|
| + if (isNull(s) || isNull(t)) return true;
|
|
|
| - assert(s is JSArray);
|
| - assert(t is JSArray);
|
| - assert(s.length == t.length);
|
| + assert(isJsArray(s));
|
| + assert(isJsArray(t));
|
| + assert(getLength(s) == getLength(t));
|
|
|
| - int len = s.length;
|
| + int len = getLength(s);
|
| for (int i = 0; i < len; i++) {
|
| - if (!isSubtype(s[i], t[i])) {
|
| + if (!isSubtype(getIndex(s, i), getIndex(t, i))) {
|
| return false;
|
| }
|
| }
|
| @@ -200,6 +291,12 @@ bool isSubtypeOfNull(type) {
|
| return type == null || getConstructorName(type) == JS_OBJECT_CLASS_NAME();
|
| }
|
|
|
| +hasField(var object, var name) => JS('bool', r'#[#]', object, name);
|
| +
|
| +isNull(var object) => JS('bool', '# == null', object);
|
| +
|
| +isNotNull(var object) => JS('bool', '# != null', object);
|
| +
|
| /**
|
| * Tests whether the Dart object [o] is a subtype of the runtime type
|
| * representation [t], which is a type representation as described in the
|
| @@ -262,23 +359,30 @@ Object assertSubtypeOfRuntimeType(Object object, var type) {
|
| */
|
| bool isSubtype(var s, var t) {
|
| // If either type is dynamic, [s] is a subtype of [t].
|
| - if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true;
|
| + if (isNull(s) || isNull(t)) return true;
|
| // Subtyping is reflexive.
|
| - if (JS('bool', '# === #', s, t)) return true;
|
| + if (isIdentical(s, t)) return true;
|
| + if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) {
|
| + if (!hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) {
|
| + var signatureName =
|
| + '${JS_OPERATOR_IS_PREFIX()}_${getField(t, JS_FUNCTION_TYPE_TAG())}';
|
| + if (hasField(s, signatureName)) return true;
|
| + var targetSignatureFunction = getField(s, '${JS_SIGNATURE_NAME()}');
|
| + if (isNull(targetSignatureFunction)) return false;
|
| + s = invokeOn(targetSignatureFunction, s, null);
|
| + }
|
| + return isFunctionSubtype(s, t);
|
| + }
|
| // Get the object describing the class and check for the subtyping flag
|
| // constructed from the type of [t].
|
| - var typeOfS = s is JSArray ? s[0] : s;
|
| - var typeOfT = t is JSArray ? t[0] : t;
|
| - // TODO(johnniwinther): replace this with the real function subtype test.
|
| - if (JS('bool', '#.func', s) == true || JS('bool', '#.func', t) == true ) {
|
| - return true;
|
| - }
|
| + var typeOfS = isJsArray(s) ? getIndex(s, 0) : s;
|
| + var typeOfT = isJsArray(t) ? getIndex(t, 0) : t;
|
| // Check for a subtyping flag.
|
| var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}';
|
| - if (getField(typeOfS, test) == null) return false;
|
| + if (!hasField(typeOfS, test)) return false;
|
| // Get the necessary substitution of the type arguments, if there is one.
|
| var substitution;
|
| - if (JS('bool', '# !== #', typeOfT, typeOfS)) {
|
| + if (isNotIdentical(typeOfT, typeOfS)) {
|
| var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}';
|
| substitution = getField(typeOfS, field);
|
| }
|
| @@ -286,12 +390,106 @@ bool isSubtype(var s, var t) {
|
| // arguments and no substitution, it is used as raw type. If [t] has no
|
| // type arguments, it used as a raw type. In both cases, [s] is a subtype
|
| // of [t].
|
| - if ((s is! JSArray && JS('bool', '# == null', substitution)) ||
|
| - t is! JSArray) {
|
| + if ((!isJsArray(s) && isNull(substitution)) ||
|
| + !isJsArray(t)) {
|
| return true;
|
| }
|
| // Recursively check the type arguments.
|
| return checkArguments(substitution, getArguments(s), getArguments(t));
|
| }
|
|
|
| +bool isAssignable(var s, var t) {
|
| + return isSubtype(s, t) || isSubtype(t, s);
|
| +}
|
| +
|
| +getIndex(var array, int index) {
|
| + assert(isJsArray(array));
|
| + return JS('int', '#[#]', array, index);
|
| +}
|
| +
|
| +int getLength(var array) {
|
| + assert(isJsArray(array));
|
| + return JS('int', '#.length', array);
|
| +}
|
| +
|
| +bool areAssignable(List s, List t, bool allowSubset) {
|
| + if (isNull(t)) return true;
|
| + if (isNull(s)) return false;
|
| +
|
| + assert(isJsArray(s));
|
| + assert(isJsArray(t));
|
| +
|
| + int sLength = getLength(s);
|
| + int tLength = getLength(t);
|
| + if (allowSubset) {
|
| + if (sLength< tLength) return false;
|
| + } else {
|
| + if (sLength != tLength) return false;
|
| + }
|
| +
|
| + for (int i = 0; i < tLength; i++) {
|
| + if (!isAssignable(getIndex(s, i), getIndex(t, i))) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool areAssignableMaps(var s, var t) {
|
| + if (isNull(t)) return true;
|
| + if (isNull(s)) return false;
|
| +
|
| + assert(isJsObject(s));
|
| + assert(isJsObject(t));
|
| +
|
| + return JS('bool', r'''
|
| + function (t, s, isAssignable) {
|
| + for (var $name in t) {
|
| + if (!s.hasOwnProperty($name)) {
|
| + return false;
|
| + }
|
| + var tType = t[$name];
|
| + var sType = s[$name];
|
| + if (!isAssignable.call$2(sType, tType)) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }(#, #, #)
|
| + ''', t, s, RAW_DART_FUNCTION_REF(isAssignable));
|
| +}
|
| +
|
| +bool isFunctionSubtype(var s, var t) {
|
| + if (!hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) return false;
|
| + if (hasField(s, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) {
|
| + if (!hasField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) return false;
|
| + } else if (!hasField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) {
|
| + var sReturnType = getField(s, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}');
|
| + var tReturnType = getField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}');
|
| + if (!isAssignable(sReturnType, tReturnType)) return false;
|
| + }
|
| + var sParameterTypes =
|
| + getField(s, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}');
|
| + var tParameterTypes =
|
| + getField(t, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}');
|
| + if (!areAssignable(sParameterTypes, tParameterTypes, false)) return false;
|
| + var sOptionalParameterTypes =
|
| + getField(s, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}');
|
| + var tOptionalParameterTypes =
|
| + getField(t, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}');
|
| + if (!areAssignable(sOptionalParameterTypes, tOptionalParameterTypes, true)) {
|
| + return false;
|
| + }
|
| + var sNamedParameters =
|
| + getField(s, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}');
|
| + var tNamedParameters =
|
| + getField(t, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}');
|
| + if (!areAssignableMaps(sNamedParameters, tNamedParameters)) return false;
|
| + return true;
|
| +}
|
| +
|
| createRuntimeType(String name) => new TypeImpl(name);
|
| +
|
| +bool isIdentical(var s, var t) => JS('bool', '# === #', s, t);
|
| +
|
| +bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t);
|
|
|