Chromium Code Reviews| 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 |