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); |