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:collection-dev'; | 8 import 'dart:collection-dev'; |
| 9 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, | 9 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, |
| 10 JS, | 10 JS, |
| 11 JS_CALL_IN_ISOLATE, | 11 JS_CALL_IN_ISOLATE, |
| 12 JS_CURRENT_ISOLATE, | 12 JS_CURRENT_ISOLATE, |
| 13 JS_OPERATOR_IS_PREFIX, | 13 JS_OPERATOR_IS_PREFIX, |
| 14 JS_OPERATOR_AS_PREFIX, | |
| 14 JS_HAS_EQUALS, | 15 JS_HAS_EQUALS, |
| 15 RAW_DART_FUNCTION_REF, | 16 RAW_DART_FUNCTION_REF, |
| 16 UNINTERCEPTED; | 17 UNINTERCEPTED; |
| 17 | 18 |
| 18 part 'constant_map.dart'; | 19 part 'constant_map.dart'; |
| 19 part 'native_helper.dart'; | 20 part 'native_helper.dart'; |
| 20 part 'regexp_helper.dart'; | 21 part 'regexp_helper.dart'; |
| 21 part 'string_helper.dart'; | 22 part 'string_helper.dart'; |
| 22 | 23 |
| 23 bool isJsArray(var value) { | 24 bool isJsArray(var value) { |
| (...skipping 1023 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1047 } | 1048 } |
| 1048 | 1049 |
| 1049 setRuntimeTypeInfo(target, typeInfo) { | 1050 setRuntimeTypeInfo(target, typeInfo) { |
| 1050 assert(typeInfo == null || isJsArray(typeInfo)); | 1051 assert(typeInfo == null || isJsArray(typeInfo)); |
| 1051 // We have to check for null because factories may return null. | 1052 // We have to check for null because factories may return null. |
| 1052 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); | 1053 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); |
| 1053 } | 1054 } |
| 1054 | 1055 |
| 1055 getRuntimeTypeInfo(target) { | 1056 getRuntimeTypeInfo(target) { |
| 1056 if (target == null) return null; | 1057 if (target == null) return null; |
| 1057 var res = JS('var', r'#.$builtinTypeInfo', target); | 1058 return JS('var', r'#.$builtinTypeInfo', target); |
| 1058 // If the object does not have runtime type information, return an | 1059 } |
| 1059 // empty literal, to avoid null checks. | 1060 |
| 1060 // TODO(ngeoffray): Make the object a top-level field to avoid | 1061 getRuntimeTypeArgument(target, index) { |
| 1061 // allocating a new object every single time. | 1062 var rti = getRuntimeTypeInfo(target); |
| 1062 return (res == null) ? JS('var', '{}') : res; | 1063 return (rti != null) ? JS('var', r'#[#]', rti, index) : null; |
| 1063 } | 1064 } |
| 1064 | 1065 |
| 1065 /** | 1066 /** |
| 1066 * The following methods are called by the runtime to implement | 1067 * The following methods are called by the runtime to implement |
| 1067 * checked mode and casts. We specialize each primitive type (eg int, bool), and | 1068 * checked mode and casts. We specialize each primitive type (eg int, bool), and |
| 1068 * use the compiler's convention to do is-checks on regular objects. | 1069 * use the compiler's convention to do is-checks on regular objects. |
| 1069 */ | 1070 */ |
| 1070 boolConversionCheck(value) { | 1071 boolConversionCheck(value) { |
| 1071 boolTypeCheck(value); | 1072 boolTypeCheck(value); |
| 1072 assert(value != null); | 1073 assert(value != null); |
| (...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1414 bool operator ==(other) { | 1415 bool operator ==(other) { |
| 1415 if (other is !TypeImpl) return false; | 1416 if (other is !TypeImpl) return false; |
| 1416 return typeName == other.typeName; | 1417 return typeName == other.typeName; |
| 1417 } | 1418 } |
| 1418 } | 1419 } |
| 1419 | 1420 |
| 1420 String getClassName(var object) { | 1421 String getClassName(var object) { |
| 1421 return JS('String', r'#.constructor.builtin$cls', object); | 1422 return JS('String', r'#.constructor.builtin$cls', object); |
| 1422 } | 1423 } |
| 1423 | 1424 |
| 1424 String getTypeArgumentAsString(List runtimeType) { | 1425 String getRuntimeTypeAsString(List runtimeType) { |
| 1425 String className = getConstructorName(runtimeType[0]); | 1426 String className = getConstructorName(runtimeType[0]); |
| 1426 if (runtimeType.length == 1) return className; | 1427 if (runtimeType.length == 1) return className; |
| 1427 return '$className<${joinArguments(runtimeType, 1)}>'; | 1428 return '$className<${joinArguments(runtimeType, 1)}>'; |
| 1428 } | 1429 } |
| 1429 | 1430 |
| 1430 String getConstructorName(type) => JS('String', r'#.builtin$cls', type); | 1431 String getConstructorName(type) => JS('String', r'#.builtin$cls', type); |
| 1431 | 1432 |
| 1432 String runtimeTypeToString(type) { | 1433 String runtimeTypeToString(type) { |
| 1433 if (type == null) { | 1434 if (type == null) { |
| 1434 return 'dynamic'; | 1435 return 'dynamic'; |
| 1435 } else if (isJsArray(type)) { | 1436 } else if (isJsArray(type)) { |
| 1436 // A list representing a type with arguments. | 1437 // A list representing a type with arguments. |
| 1437 return getTypeArgumentAsString(type); | 1438 return getRuntimeTypeAsString(type); |
| 1438 } else { | 1439 } else { |
| 1439 // A reference to the constructor. | 1440 // A reference to the constructor. |
| 1440 return getConstructorName(type); | 1441 return getConstructorName(type); |
| 1441 } | 1442 } |
| 1442 } | 1443 } |
| 1443 | 1444 |
| 1444 String joinArguments(var types, int startIndex) { | 1445 String joinArguments(var types, int startIndex) { |
| 1445 bool firstArgument = true; | 1446 bool firstArgument = true; |
| 1446 StringBuffer buffer = new StringBuffer(); | 1447 StringBuffer buffer = new StringBuffer(); |
| 1447 for (int index = startIndex; index < types.length; index++) { | 1448 for (int index = startIndex; index < types.length; index++) { |
| 1448 if (firstArgument) { | 1449 if (firstArgument) { |
| 1449 firstArgument = false; | 1450 firstArgument = false; |
| 1450 } else { | 1451 } else { |
| 1451 buffer. add(', '); | 1452 buffer. add(', '); |
| 1452 } | 1453 } |
| 1453 var argument = types[index]; | 1454 var argument = types[index]; |
| 1454 buffer.add(runtimeTypeToString(argument)); | 1455 buffer.add(runtimeTypeToString(argument)); |
| 1455 } | 1456 } |
| 1456 return buffer.toString(); | 1457 return buffer.toString(); |
| 1457 } | 1458 } |
| 1458 | 1459 |
| 1459 String getRuntimeTypeString(var object) { | 1460 String getRuntimeTypeString(var object) { |
| 1460 String className = isJsArray(object) ? 'List' : getClassName(object); | 1461 String className = isJsArray(object) ? 'List' : getClassName(object); |
| 1461 var typeInfo = JS('var', r'#.$builtinTypeInfo', object); | 1462 var typeInfo = JS('var', r'#.$builtinTypeInfo', object); |
| 1462 if (typeInfo == null) return className; | 1463 if (typeInfo == null) return className; |
| 1463 return "$className<${joinArguments(typeInfo, 0)}>"; | 1464 return "$className<${joinArguments(typeInfo, 0)}>"; |
| 1464 } | 1465 } |
| 1465 | 1466 |
| 1467 bool isJsFunction(var o) => JS('bool', r"typeof # == 'function'", o); | |
| 1468 | |
| 1469 Object invoke(function, arguments) { | |
| 1470 return JS('var', r'#.apply(null, #)', function, arguments); | |
|
ngeoffray
2013/01/30 13:06:00
"#(#)" ?
karlklose
2013/01/30 15:37:18
The arguments are in a JS array, but we want them
| |
| 1471 } | |
| 1472 | |
| 1473 /** | |
| 1474 * Check that the types in the list [arguments] are subtypes of the types in | |
| 1475 * list [checks] (at the respective positions), possibly applying [substitution] | |
| 1476 * to the arguments before the check. | |
| 1477 * | |
| 1478 * See [:RuntimeTypes.getSubtypeSubstitution:] for a description of the possible | |
| 1479 * values for [substitution]. | |
| 1480 */ | |
| 1481 bool checkArguments(var substitution, var arguments, var checks) { | |
| 1482 if (isJsArray(substitution)) { | |
| 1483 arguments = substitution; | |
| 1484 } else if (isJsFunction(substitution)) { | |
| 1485 arguments = invoke(substitution, arguments); | |
| 1486 } | |
| 1487 return areSubtypes(arguments, checks); | |
| 1488 } | |
| 1489 | |
| 1490 bool areSubtypes(var s, var t) { | |
|
ngeoffray
2013/01/30 13:06:00
(var s, var t) -> (List s, List t)
karlklose
2013/01/30 15:37:18
Done.
| |
| 1491 // [:null:] means a raw type. | |
| 1492 if (s == null || t == null) return true; | |
| 1493 | |
| 1494 assert(isJsArray(s)); | |
| 1495 assert(isJsArray(t)); | |
| 1496 assert(s.length == t.length); | |
| 1497 | |
| 1498 int len = s.length; | |
| 1499 for (int i = 0; i < len; i++) { | |
| 1500 if (!isSubtype(s[i], t[i])) { | |
| 1501 return false; | |
| 1502 } | |
| 1503 } | |
| 1504 return true; | |
| 1505 } | |
| 1506 | |
| 1507 getArguments(var type) => JS('var', r'#.slice(1)', type); | |
| 1508 | |
| 1509 getField(var object, String name) => JS('var', r'#[#]', object, name); | |
| 1510 | |
| 1466 /** | 1511 /** |
| 1467 * Check whether the type represented by [s] is a subtype of the type | 1512 * Check whether the type represented by [s] is a subtype of the type |
| 1468 * represented by [t]. | 1513 * represented by [t]. |
| 1469 * | 1514 * |
| 1470 * Type representations can be: | 1515 * Type representations can be: |
| 1471 * 1) a JavaScript constructor for a class C: the represented type is the raw | 1516 * 1) a JavaScript constructor for a class C: the represented type is the raw |
| 1472 * type C. | 1517 * type C. |
| 1473 * 2) a JavaScript object: this represents a class for which there is no | 1518 * 2) a JavaScript object: this represents a class for which there is no |
| 1474 * JavaScript constructor, because it is only used in type arguments or it | 1519 * JavaScript constructor, because it is only used in type arguments or it |
| 1475 * is native. The represented type is the raw type of this class. | 1520 * is native. The represented type is the raw type of this class. |
| 1476 * 3) a JavaScript array: the first entry is of type 1 or 2 and identifies the | 1521 * 3) a JavaScript array: the first entry is of type 1 or 2 and identifies the |
| 1477 * class of the type and the rest of the array are the type arguments. | 1522 * class of the type and the rest of the array are the type arguments. |
| 1478 * 4) [:null:]: the dynamic type. | 1523 * 4) [:null:]: the dynamic type. |
| 1479 */ | 1524 */ |
| 1480 bool isSubtype(var s, var t) { | 1525 bool isSubtype(var s, var t) { |
| 1481 // If either type is dynamic, [s] is a subtype of [t]. | 1526 // If either type is dynamic, [s] is a subtype of [t]. |
| 1482 if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true; | 1527 if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true; |
| 1483 // Subtyping is reflexive. | 1528 // Subtyping is reflexive. |
| 1484 if (JS('bool', '# === #', s, t)) return true; | 1529 if (JS('bool', '# === #', s, t)) return true; |
| 1485 // Get the object describing the class and check for the subtyping flag | 1530 // Get the object describing the class and check for the subtyping flag |
| 1486 // constructed from the type of [t]. | 1531 // constructed from the type of [t]. |
| 1487 var typeOfS = isJsArray(s) ? s[0] : s; | 1532 var typeOfS = isJsArray(s) ? s[0] : s; |
| 1488 var typeOfT = isJsArray(t) ? t[0] : t; | 1533 var typeOfT = isJsArray(t) ? t[0] : t; |
| 1534 // Check for a subtyping flag. | |
| 1489 var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | 1535 var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}'; |
| 1490 if (JS('var', r'#[#]', typeOfS, test) == null) return false; | 1536 if (getField(typeOfS, test) == null) return false; |
| 1491 // The class of [s] is a subclass of the class of [t]. If either of the types | 1537 // The class of [s] is a subclass of the class of [t]. If either of the types |
| 1492 // is raw, [s] is a subtype of [t]. | 1538 // is raw, [s] is a subtype of [t]. |
| 1493 if (!isJsArray(s) || !isJsArray(t)) return true; | 1539 if (!isJsArray(s) || !isJsArray(t)) return true; |
| 1540 // Get the necessary substitution of the type arguments, if there is one. | |
| 1541 var substitution; | |
| 1542 if (JS('bool', '# !== #', typeOfT, typeOfS)) { | |
|
ngeoffray
2013/01/30 13:06:00
Can we use Dart's "!=" for typeOfT and typeOfS?
karlklose
2013/01/30 15:37:18
I don't think so, as it would try to call $eq on a
| |
| 1543 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | |
| 1544 substitution = getField(typeOfS, field); | |
| 1545 } | |
| 1494 // Recursively check the type arguments. | 1546 // Recursively check the type arguments. |
| 1495 int len = s.length; | 1547 return checkArguments(substitution, getArguments(s), getArguments(t)); |
| 1496 if (len != t.length) return false; | |
| 1497 for (int i = 1; i < len; i++) { | |
| 1498 if (!isSubtype(s[i], t[i])) { | |
| 1499 return false; | |
| 1500 } | |
| 1501 } | |
| 1502 return true; | |
| 1503 } | 1548 } |
| 1504 | 1549 |
| 1505 createRuntimeType(String name) => new TypeImpl(name); | 1550 createRuntimeType(String name) => new TypeImpl(name); |
| OLD | NEW |