Chromium Code Reviews| 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 559ccfbfd6e1136ee6ee497296aa649013502a95..59bbd67be4e87a5c7b402073494e261d7dbacd0d 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/lib/js_rti.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/lib/js_rti.dart |
| @@ -15,8 +15,23 @@ getRuntimeTypeInfo(target) { |
| return JS('var', r'#.$builtinTypeInfo', target); |
| } |
| +forwardRuntimeTypeInfo(target, substitution, source) { |
|
karlklose
2013/03/22 13:17:46
Is this still used?
Johnni Winther
2013/06/21 12:19:14
Removed.
|
| + setRuntimeTypeInfo(target, |
| + substitute(substitution, getRuntimeTypeInfo(source))); |
| + return 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); |
| } |
| @@ -79,17 +94,28 @@ String getRuntimeTypeString(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 (isJsArray(substitution)) { |
| arguments = substitution; |
| } else if (isJsFunction(substitution)) { |
| - arguments = invoke(substitution, arguments); |
| + substitution = invoke(substitution, arguments); |
| + if (isJsArray(substitution)) { |
| + arguments = substitution; |
| + } else if (isJsFunction(substitution)) { |
|
karlklose
2013/03/22 13:17:46
Is this path used?
Johnni Winther
2013/06/21 12:19:14
Not sure. Added a TODO.
|
| + arguments = invoke(substitution, arguments); |
| + } |
| } |
| return arguments; |
| } |
| @@ -134,6 +160,49 @@ bool checkArguments(var substitution, var arguments, var checks) { |
| return areSubtypes(substitute(substitution, arguments), checks); |
| } |
| +/** |
| + * Checks that the type of [target] is a subtype of the function type denoted by |
| + * [signatureName]. The |
|
karlklose
2013/03/22 13:17:46
Please finish the
Johnni Winther
2013/06/21 12:19:14
Now it
|
| + */ |
| +bool checkFunctionSubtype(var target, String signatureName, |
| + var context, String contextName) { |
| + if (isNull(target)) return false; |
| + if (hasField(target, '${JS_OPERATOR_IS_PREFIX()}_$signatureName')) { |
| + return true; |
| + } |
| + var signatureLocation = JS_CURRENT_ISOLATE(); |
| + if (isNotNull(context)) { |
| + 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); |
|
karlklose
2013/03/22 13:17:46
remove space.
Johnni Winther
2013/06/21 12:19:14
Done.
|
| + if (isJsFunction(typeSignature)) { |
| + if (context != null) { |
| + 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; |
| @@ -157,6 +226,12 @@ getArguments(var type) { |
| getField(var object, var name) => JS('var', r'#[#]', object, 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 |
| @@ -207,6 +282,9 @@ bool isSubtype(var s, var t) { |
| if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true; |
| // Subtyping is reflexive. |
| if (JS('bool', '# === #', s, t)) return true; |
| + if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { |
| + return isFunctionSubtype(s, t); |
| + } |
| // Get the object describing the class and check for the subtyping flag |
| // constructed from the type of [t]. |
| var typeOfS = isJsArray(s) ? s[0] : s; |
| @@ -232,4 +310,85 @@ bool isSubtype(var s, var t) { |
| return checkArguments(substitution, getArguments(s), getArguments(t)); |
| } |
| +bool isAssignable(var s, var t) { |
| + return isSubtype(s, t) || isSubtype(t, s); |
| +} |
| + |
| +bool areAssignable(List s, List t, bool allowSubset) { |
| + if (t == null) return true; |
|
karlklose
2013/03/22 13:17:46
What does null mean here?
Johnni Winther
2013/06/21 12:19:14
Empty lists, i.e. not arguments of that kind.
|
| + if (s == null) return false; |
| + |
| + assert(isJsArray(s)); |
| + assert(isJsArray(t)); |
| + |
| + if (allowSubset) { |
| + if (s.length < t.length) return false; |
| + } else { |
| + if (s.length != t.length) return false; |
| + } |
| + |
| + int len = t.length; |
| + for (int i = 0; i < len; i++) { |
| + if (!isAssignable(s[i], t[i])) { |
| + return false; |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +bool areAssignableMaps(var s, var t) { |
| + if (t == null) return true; |
| + if (s == null) return false; |
| + |
| + assert(isJsObject(s)); |
| + assert(isJsObject(t)); |
| + |
| + // Hack: We need to iterate over the properties in [t], and the names of these |
| + // properties are not statically known so they must be retrieved dynamically. |
| + // Therefore we (mis)use the [JS] function to generate a for-each loop on [t] |
| + // using the JavaScript variable [:$name:] to hold the property names. |
| + JS('', r'for (var $name in #) {', t); { |
| + // We need to distinguish the existing property with value [:undefined:] or |
| + // [:null:] from the non-existing property. The former is interpreted as a |
| + // named parameter of type [:dynamic:] and the later is interpreted as |
| + // [:$name:] not being a named parameter of [s]. |
| + if (JS('bool', r'!#.hasOwnProperty($name)', s)) return false; |
| + var tType = JS('', r'#[$name]', t); |
| + var sType = JS('', r'#[$name]', s); |
| + if (!isAssignable(sType, tType)) { |
| + return false; |
| + } |
| + } JS('', '}'); |
| + return true; |
| +} |
| + |
| +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); |