Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1230)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/lib/js_helper.dart

Issue 12334070: Support runtime check of function types. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Register dependency Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698