Index: sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
index e4536f3622aca4ed21dc7eeb3a2b8fc849153a19..3657858d89dd7fc1a0cf1e7a9df4d46fc91ab035 100644 |
--- a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
@@ -1049,6 +1049,12 @@ getRuntimeTypeInfo(target) { |
return JS('var', r'#.$builtinTypeInfo', target); |
} |
+forwardRuntimeTypeInfo(target, substitution, source) { |
+ setRuntimeTypeInfo(target, |
+ substitute(substitution, getRuntimeTypeInfo(source))); |
+ return target; |
+} |
+ |
getRuntimeTypeArgument(target, substitution, index) { |
var arguments = substitute(substitution, getRuntimeTypeInfo(target)); |
return (arguments == null) ? null : getField(arguments, index); |
@@ -1472,6 +1478,8 @@ 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); |
} |
@@ -1497,6 +1505,26 @@ bool checkArguments(var substitution, var arguments, var checks) { |
return areSubtypes(substitute(substitution, arguments), checks); |
} |
+bool getSignature(var signature, var target) { |
+ return invoke(signature, getRuntimeTypeInfo(target)); |
+} |
+ |
+bool checkFunctionSubtype(var target, var typeSignature, var context) { |
+ var targetSignature = getField(target, r'$signature'); |
ngeoffray
2013/03/13 09:30:46
Using $signature directly is brittle, you should p
Johnni Winther
2013/03/22 07:30:24
Done.
|
+ if (targetSignature == null) { |
+ // The target is statically known to be a subtype so further check is |
ngeoffray
2013/03/13 09:30:46
further or no further? Could you add an example?
Johnni Winther
2013/03/22 07:30:24
Not needed in the new encoding.
|
+ // needed. |
+ return true; |
+ } |
+ if (isJsFunction(targetSignature)) { |
+ targetSignature = invoke(targetSignature, getRuntimeTypeInfo(target)); |
+ } |
+ if (isJsFunction(typeSignature)) { |
+ typeSignature = invoke(typeSignature, getRuntimeTypeInfo(context)); |
+ } |
+ return isFunctionSubtype(targetSignature, typeSignature); |
+} |
+ |
bool areSubtypes(List s, List t) { |
// [:null:] means a raw type. |
if (s == null || t == null) return true; |
@@ -1571,6 +1599,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 (getField(t, 'func') == true) { |
ngeoffray
2013/03/13 09:30:46
What is this? Probably needs an abstraction.
Johnni Winther
2013/03/22 07:30:24
A tag used to identify function type representatio
|
+ 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; |
@@ -1596,4 +1627,79 @@ 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; |
+ 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. |
ngeoffray
2013/03/13 09:30:46
To not make it a hack, please create a JavaScript
Johnni Winther
2013/03/22 07:30:24
How?
|
+ 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 (!getField(s, 'func')) return false; |
+ if (getField(s, 'retvoid')) { |
+ if (!getField(t, 'retvoid')) return false; |
+ } else if (!getField(t, 'retvoid')) { |
+ var sReturnType = getField(s, 'ret'); |
+ var tReturnType = getField(t, 'ret'); |
+ if (!isAssignable(sReturnType, tReturnType)) return false; |
+ } |
+ var sParameterTypes = getField(s, 'args'); |
+ var tParameterTypes = getField(t, 'args'); |
+ if (!areAssignable(sParameterTypes, tParameterTypes, false)) return false; |
+ var sOptionalParameterTypes = getField(s, 'opt'); |
+ var tOptionalParameterTypes = getField(t, 'opt'); |
+ if (!areAssignable(sOptionalParameterTypes, tOptionalParameterTypes, true)) { |
+ return false; |
+ } |
+ var sNamedParameters = getField(s, 'named'); |
+ var tNamedParameters = getField(t, 'named'); |
+ if (!areAssignableMaps(sNamedParameters, tNamedParameters)) return false; |
+ return true; |
+} |
+ |
createRuntimeType(String name) => new TypeImpl(name); |