OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library _js_helper; | 5 library _js_helper; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, | 8 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, |
9 JS, | 9 JS, |
10 JS_CALL_IN_ISOLATE, | 10 JS_CALL_IN_ISOLATE, |
(...skipping 1031 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1042 assert(typeInfo == null || isJsArray(typeInfo)); | 1042 assert(typeInfo == null || isJsArray(typeInfo)); |
1043 // We have to check for null because factories may return null. | 1043 // We have to check for null because factories may return null. |
1044 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); | 1044 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); |
1045 } | 1045 } |
1046 | 1046 |
1047 getRuntimeTypeInfo(target) { | 1047 getRuntimeTypeInfo(target) { |
1048 if (target == null) return null; | 1048 if (target == null) return null; |
1049 return JS('var', r'#.$builtinTypeInfo', target); | 1049 return JS('var', r'#.$builtinTypeInfo', target); |
1050 } | 1050 } |
1051 | 1051 |
1052 forwardRuntimeTypeInfo(target, substitution, source) { | |
1053 setRuntimeTypeInfo(target, | |
1054 substitute(substitution, getRuntimeTypeInfo(source))); | |
1055 return target; | |
1056 } | |
1057 | |
1052 getRuntimeTypeArgument(target, substitution, index) { | 1058 getRuntimeTypeArgument(target, substitution, index) { |
1053 var arguments = substitute(substitution, getRuntimeTypeInfo(target)); | 1059 var arguments = substitute(substitution, getRuntimeTypeInfo(target)); |
1054 return (arguments == null) ? null : getField(arguments, index); | 1060 return (arguments == null) ? null : getField(arguments, index); |
1055 } | 1061 } |
1056 | 1062 |
1057 /** | 1063 /** |
1058 * The following methods are called by the runtime to implement | 1064 * The following methods are called by the runtime to implement |
1059 * checked mode and casts. We specialize each primitive type (eg int, bool), and | 1065 * checked mode and casts. We specialize each primitive type (eg int, bool), and |
1060 * use the compiler's convention to do is-checks on regular objects. | 1066 * use the compiler's convention to do is-checks on regular objects. |
1061 */ | 1067 */ |
(...skipping 403 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1465 | 1471 |
1466 String getRuntimeTypeString(var object) { | 1472 String getRuntimeTypeString(var object) { |
1467 String className = isJsArray(object) ? 'List' : getClassName(object); | 1473 String className = isJsArray(object) ? 'List' : getClassName(object); |
1468 var typeInfo = JS('var', r'#.$builtinTypeInfo', object); | 1474 var typeInfo = JS('var', r'#.$builtinTypeInfo', object); |
1469 if (typeInfo == null) return className; | 1475 if (typeInfo == null) return className; |
1470 return "$className<${joinArguments(typeInfo, 0)}>"; | 1476 return "$className<${joinArguments(typeInfo, 0)}>"; |
1471 } | 1477 } |
1472 | 1478 |
1473 bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); | 1479 bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); |
1474 | 1480 |
1481 bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o); | |
1482 | |
1475 Object invoke(function, arguments) { | 1483 Object invoke(function, arguments) { |
1476 return JS('var', r'#.apply(null, #)', function, arguments); | 1484 return JS('var', r'#.apply(null, #)', function, arguments); |
1477 } | 1485 } |
1478 | 1486 |
1479 substitute(var substitution, var arguments) { | 1487 substitute(var substitution, var arguments) { |
1480 if (isJsArray(substitution)) { | 1488 if (isJsArray(substitution)) { |
1481 arguments = substitution; | 1489 arguments = substitution; |
1482 } else if (isJsFunction(substitution)) { | 1490 } else if (isJsFunction(substitution)) { |
1483 arguments = invoke(substitution, arguments); | 1491 arguments = invoke(substitution, arguments); |
1484 } | 1492 } |
1485 return arguments; | 1493 return arguments; |
1486 } | 1494 } |
1487 | 1495 |
1488 /** | 1496 /** |
1489 * Check that the types in the list [arguments] are subtypes of the types in | 1497 * Check that the types in the list [arguments] are subtypes of the types in |
1490 * list [checks] (at the respective positions), possibly applying [substitution] | 1498 * list [checks] (at the respective positions), possibly applying [substitution] |
1491 * to the arguments before the check. | 1499 * to the arguments before the check. |
1492 * | 1500 * |
1493 * See [:RuntimeTypes.getSubtypeSubstitution:] for a description of the possible | 1501 * See [:RuntimeTypes.getSubtypeSubstitution:] for a description of the possible |
1494 * values for [substitution]. | 1502 * values for [substitution]. |
1495 */ | 1503 */ |
1496 bool checkArguments(var substitution, var arguments, var checks) { | 1504 bool checkArguments(var substitution, var arguments, var checks) { |
1497 return areSubtypes(substitute(substitution, arguments), checks); | 1505 return areSubtypes(substitute(substitution, arguments), checks); |
1498 } | 1506 } |
1499 | 1507 |
1508 bool getSignature(var signature, var target) { | |
1509 return invoke(signature, getRuntimeTypeInfo(target)); | |
1510 } | |
1511 | |
1512 bool checkFunctionSubtype(var target, var typeSignature, var context) { | |
1513 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.
| |
1514 if (targetSignature == null) { | |
1515 // 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.
| |
1516 // needed. | |
1517 return true; | |
1518 } | |
1519 if (isJsFunction(targetSignature)) { | |
1520 targetSignature = invoke(targetSignature, getRuntimeTypeInfo(target)); | |
1521 } | |
1522 if (isJsFunction(typeSignature)) { | |
1523 typeSignature = invoke(typeSignature, getRuntimeTypeInfo(context)); | |
1524 } | |
1525 return isFunctionSubtype(targetSignature, typeSignature); | |
1526 } | |
1527 | |
1500 bool areSubtypes(List s, List t) { | 1528 bool areSubtypes(List s, List t) { |
1501 // [:null:] means a raw type. | 1529 // [:null:] means a raw type. |
1502 if (s == null || t == null) return true; | 1530 if (s == null || t == null) return true; |
1503 | 1531 |
1504 assert(isJsArray(s)); | 1532 assert(isJsArray(s)); |
1505 assert(isJsArray(t)); | 1533 assert(isJsArray(t)); |
1506 assert(s.length == t.length); | 1534 assert(s.length == t.length); |
1507 | 1535 |
1508 int len = s.length; | 1536 int len = s.length; |
1509 for (int i = 0; i < len; i++) { | 1537 for (int i = 0; i < len; i++) { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1564 * 4) a JavaScript array: the first entry is of type 1, 2 or 3 and contains the | 1592 * 4) a JavaScript array: the first entry is of type 1, 2 or 3 and contains the |
1565 * subtyping flags and the substitution of the type and the rest of the | 1593 * subtyping flags and the substitution of the type and the rest of the |
1566 * array are the type arguments. | 1594 * array are the type arguments. |
1567 * 5) [:null:]: the dynamic type. | 1595 * 5) [:null:]: the dynamic type. |
1568 */ | 1596 */ |
1569 bool isSubtype(var s, var t) { | 1597 bool isSubtype(var s, var t) { |
1570 // If either type is dynamic, [s] is a subtype of [t]. | 1598 // If either type is dynamic, [s] is a subtype of [t]. |
1571 if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true; | 1599 if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true; |
1572 // Subtyping is reflexive. | 1600 // Subtyping is reflexive. |
1573 if (JS('bool', '# === #', s, t)) return true; | 1601 if (JS('bool', '# === #', s, t)) return true; |
1602 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
| |
1603 return isFunctionSubtype(s, t); | |
1604 } | |
1574 // Get the object describing the class and check for the subtyping flag | 1605 // Get the object describing the class and check for the subtyping flag |
1575 // constructed from the type of [t]. | 1606 // constructed from the type of [t]. |
1576 var typeOfS = isJsArray(s) ? s[0] : s; | 1607 var typeOfS = isJsArray(s) ? s[0] : s; |
1577 var typeOfT = isJsArray(t) ? t[0] : t; | 1608 var typeOfT = isJsArray(t) ? t[0] : t; |
1578 // Check for a subtyping flag. | 1609 // Check for a subtyping flag. |
1579 var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | 1610 var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}'; |
1580 if (getField(typeOfS, test) == null) return false; | 1611 if (getField(typeOfS, test) == null) return false; |
1581 // Get the necessary substitution of the type arguments, if there is one. | 1612 // Get the necessary substitution of the type arguments, if there is one. |
1582 var substitution; | 1613 var substitution; |
1583 if (JS('bool', '# !== #', typeOfT, typeOfS)) { | 1614 if (JS('bool', '# !== #', typeOfT, typeOfS)) { |
1584 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | 1615 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; |
1585 substitution = getField(typeOfS, field); | 1616 substitution = getField(typeOfS, field); |
1586 } | 1617 } |
1587 // The class of [s] is a subclass of the class of [t]. If [s] has no type | 1618 // The class of [s] is a subclass of the class of [t]. If [s] has no type |
1588 // arguments and no substitution, it is used as raw type. If [t] has no | 1619 // arguments and no substitution, it is used as raw type. If [t] has no |
1589 // type arguments, it used as a raw type. In both cases, [s] is a subtype | 1620 // type arguments, it used as a raw type. In both cases, [s] is a subtype |
1590 // of [t]. | 1621 // of [t]. |
1591 if ((!isJsArray(s) && JS('bool', '# == null', substitution)) || | 1622 if ((!isJsArray(s) && JS('bool', '# == null', substitution)) || |
1592 !isJsArray(t)) { | 1623 !isJsArray(t)) { |
1593 return true; | 1624 return true; |
1594 } | 1625 } |
1595 // Recursively check the type arguments. | 1626 // Recursively check the type arguments. |
1596 return checkArguments(substitution, getArguments(s), getArguments(t)); | 1627 return checkArguments(substitution, getArguments(s), getArguments(t)); |
1597 } | 1628 } |
1598 | 1629 |
1630 bool isAssignable(var s, var t) { | |
1631 return isSubtype(s, t) || isSubtype(t, s); | |
1632 } | |
1633 | |
1634 bool areAssignable(List s, List t, bool allowSubset) { | |
1635 if (t == null) return true; | |
1636 if (s == null) return false; | |
1637 | |
1638 assert(isJsArray(s)); | |
1639 assert(isJsArray(t)); | |
1640 | |
1641 if (allowSubset) { | |
1642 if (s.length < t.length) return false; | |
1643 } else { | |
1644 if (s.length != t.length) return false; | |
1645 } | |
1646 | |
1647 int len = t.length; | |
1648 for (int i = 0; i < len; i++) { | |
1649 if (!isAssignable(s[i], t[i])) { | |
1650 return false; | |
1651 } | |
1652 } | |
1653 return true; | |
1654 } | |
1655 | |
1656 bool areAssignableMaps(var s, var t) { | |
1657 if (t == null) return true; | |
1658 if (s == null) return false; | |
1659 | |
1660 assert(isJsObject(s)); | |
1661 assert(isJsObject(t)); | |
1662 | |
1663 // Hack: We need to iterate over the properties in [t], and the names of these | |
1664 // properties are not statically known so they must be retrieved dynamically. | |
1665 // Therefore we (mis)use the [JS] function to generate a for-each loop on [t] | |
1666 // 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?
| |
1667 JS('', r'for (var $name in #) {', t); { | |
1668 // We need to distinguish the existing property with value [:undefined:] or | |
1669 // [:null:] from the non-existing property. The former is interpreted as a | |
1670 // named parameter of type [:dynamic:] and the later is interpreted as | |
1671 // [:$name:] not being a named parameter of [s]. | |
1672 if (JS('bool', r'!#.hasOwnProperty($name)', s)) return false; | |
1673 var tType = JS('', r'#[$name]', t); | |
1674 var sType = JS('', r'#[$name]', s); | |
1675 if (!isAssignable(sType, tType)) { | |
1676 return false; | |
1677 } | |
1678 } JS('', '}'); | |
1679 return true; | |
1680 } | |
1681 | |
1682 bool isFunctionSubtype(var s, var t) { | |
1683 if (!getField(s, 'func')) return false; | |
1684 if (getField(s, 'retvoid')) { | |
1685 if (!getField(t, 'retvoid')) return false; | |
1686 } else if (!getField(t, 'retvoid')) { | |
1687 var sReturnType = getField(s, 'ret'); | |
1688 var tReturnType = getField(t, 'ret'); | |
1689 if (!isAssignable(sReturnType, tReturnType)) return false; | |
1690 } | |
1691 var sParameterTypes = getField(s, 'args'); | |
1692 var tParameterTypes = getField(t, 'args'); | |
1693 if (!areAssignable(sParameterTypes, tParameterTypes, false)) return false; | |
1694 var sOptionalParameterTypes = getField(s, 'opt'); | |
1695 var tOptionalParameterTypes = getField(t, 'opt'); | |
1696 if (!areAssignable(sOptionalParameterTypes, tOptionalParameterTypes, true)) { | |
1697 return false; | |
1698 } | |
1699 var sNamedParameters = getField(s, 'named'); | |
1700 var tNamedParameters = getField(t, 'named'); | |
1701 if (!areAssignableMaps(sNamedParameters, tNamedParameters)) return false; | |
1702 return true; | |
1703 } | |
1704 | |
1599 createRuntimeType(String name) => new TypeImpl(name); | 1705 createRuntimeType(String name) => new TypeImpl(name); |
OLD | NEW |