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 | 8 |
| 9 part 'constant_map.dart'; | 9 part 'constant_map.dart'; |
| 10 part 'native_helper.dart'; | 10 part 'native_helper.dart'; |
| (...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 687 if (object == null || object is bool || object is num || object is String) { | 687 if (object == null || object is bool || object is num || object is String) { |
| 688 throw new ArgumentError(object); | 688 throw new ArgumentError(object); |
| 689 } | 689 } |
| 690 JS('void', '#[#] = #', object, key, value); | 690 JS('void', '#[#] = #', object, key, value); |
| 691 } | 691 } |
| 692 | 692 |
| 693 static applyFunction(Function function, | 693 static applyFunction(Function function, |
| 694 List positionalArguments, | 694 List positionalArguments, |
| 695 Map<String, dynamic> namedArguments) { | 695 Map<String, dynamic> namedArguments) { |
| 696 int argumentCount = 0; | 696 int argumentCount = 0; |
| 697 StringBuffer buffer = new StringBuffer(); | |
| 698 List arguments = []; | 697 List arguments = []; |
| 699 | 698 |
| 700 if (positionalArguments != null) { | 699 if (positionalArguments != null) { |
| 701 argumentCount += positionalArguments.length; | 700 argumentCount += positionalArguments.length; |
| 702 arguments.addAll(positionalArguments); | 701 arguments.addAll(positionalArguments); |
| 703 } | 702 } |
| 704 | 703 |
| 704 String selectorName = 'call\$$argumentCount'; | |
| 705 | |
| 705 // Sort the named arguments to get the right selector name and | 706 // Sort the named arguments to get the right selector name and |
| 706 // arguments order. | 707 // arguments order. |
| 707 if (namedArguments != null && !namedArguments.isEmpty) { | 708 if (namedArguments != null && !namedArguments.isEmpty) { |
| 708 // Call new List.from to make sure we get a JavaScript array. | 709 // Call new List.from to make sure we get a JavaScript array. |
| 709 List<String> listOfNamedArguments = | 710 List<String> listOfNamedArguments = |
| 710 new List<String>.from(namedArguments.keys); | 711 new List<String>.from(namedArguments.keys); |
| 711 argumentCount += namedArguments.length; | 712 argumentCount += namedArguments.length; |
| 712 // We're sorting on strings, and the behavior is the same between | 713 // We're sorting on strings, and the behavior is the same between |
| 713 // Dart string sort and JS string sort. To avoid needing the Dart | 714 // Dart string sort and JS string sort. To avoid needing the Dart |
| 714 // sort implementation, we use the JavaScript one instead. | 715 // sort implementation, we use the JavaScript one instead. |
| 715 JS('void', '#.sort()', listOfNamedArguments); | 716 JS('void', '#.sort()', listOfNamedArguments); |
| 716 listOfNamedArguments.forEach((String name) { | 717 listOfNamedArguments.forEach((String name) { |
| 717 buffer.add('\$$name'); | 718 selectorName = '$selectorName\$$name'; |
| 718 arguments.add(namedArguments[name]); | 719 arguments.add(namedArguments[name]); |
| 719 }); | 720 }); |
| 720 } | 721 } |
| 721 | 722 |
| 722 String selectorName = 'call\$$argumentCount$buffer'; | |
| 723 var jsFunction = JS('var', '#[#]', function, selectorName); | 723 var jsFunction = JS('var', '#[#]', function, selectorName); |
| 724 if (jsFunction == null) { | 724 if (jsFunction == null) { |
| 725 throw new NoSuchMethodError(function, selectorName, arguments, {}); | 725 throw new NoSuchMethodError(function, selectorName, arguments, {}); |
| 726 } | 726 } |
| 727 // We bound 'this' to [function] because of how we compile | 727 // We bound 'this' to [function] because of how we compile |
| 728 // closures: escaped local variables are stored and accessed through | 728 // closures: escaped local variables are stored and accessed through |
| 729 // [function]. | 729 // [function]. |
| 730 return JS('var', '#.apply(#, #)', jsFunction, function, arguments); | 730 return JS('var', '#.apply(#, #)', jsFunction, function, arguments); |
| 731 } | 731 } |
| 732 | 732 |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 955 * in a subtype test. | 955 * in a subtype test. |
| 956 */ | 956 */ |
| 957 throwMalformedSubtypeError(value, type, reasons) { | 957 throwMalformedSubtypeError(value, type, reasons) { |
| 958 throw new TypeErrorImplementation.malformedSubtype(value, type, reasons); | 958 throw new TypeErrorImplementation.malformedSubtype(value, type, reasons); |
| 959 } | 959 } |
| 960 | 960 |
| 961 throwAbstractClassInstantiationError(className) { | 961 throwAbstractClassInstantiationError(className) { |
| 962 throw new AbstractClassInstantiationError(className); | 962 throw new AbstractClassInstantiationError(className); |
| 963 } | 963 } |
| 964 | 964 |
| 965 class TypeErrorDecoder { | |
| 966 static final noSuchMethodPattern = | |
|
kasperl
2013/01/21 13:54:09
I find that this method is really hard to read and
sra1
2013/01/29 20:54:16
In addition to Kaspers suggestions, don't forget t
ahe
2013/01/29 22:06:46
Stephen, I'm sorry you spent so much time reviewin
| |
| 967 extractPattern(JS('', | |
| 968 r'{ toString: function() { return "$receiver$"; } }')); | |
| 969 static final nullErrorPattern = extractPattern(JS('', 'null')); | |
| 970 static final undefinedErrorPattern = extractPattern(JS('', 'void 0')); | |
| 971 | |
| 972 static extractPattern(expression) { | |
| 973 var function = JS('', r"""function($expr$) { | |
|
sra1
2013/01/29 20:54:16
Why are these names $name$?
If they are not substi
ahe
2013/01/29 22:06:46
I haven't documented why I use "$name$", not "name
| |
| 974 var $argumentsExpr$ = '$arguments$' | |
| 975 try { | |
| 976 $expr$.$method$($argumentsExpr$); | |
| 977 } catch (e) { | |
| 978 return (e.message""" | |
| 979 // Subtle bug/feature in V8: it no longer calls toString. | |
|
sra1
2013/01/29 20:54:16
it? Can you be more specific about what no longer
ahe
2013/01/29 22:06:46
I looked at my new comment. I can still clarify it
| |
| 980 r""" | |
| 981 .replace(String({}), '$receiver$') | |
|
sra1
2013/01/29 20:54:16
Surely there are other receivers?
Try with some DO
ahe
2013/01/29 22:06:46
I don't understand what you're saying.
| |
| 982 .replace(new RegExp(#, 'g'), '\\$&')); | |
| 983 } | |
| 984 }""", ESCAPE_REGEXP); | |
| 985 String message = JS('String', '(#)(#)', function, expression); | |
| 986 List<String> match = | |
| 987 JS('=List', r"#.match(/\\\$[a-zA-Z]+\\\$/g)", message); | |
| 988 int arguments = JS('int', '#.indexOf(#)', match, r'\$arguments\$'); | |
| 989 int argumentsExpr = JS('int', '#.indexOf(#)', match, r'\$argumentsExpr\$'); | |
| 990 int expr = JS('int', '#.indexOf(#)', match, r'\$expr\$'); | |
| 991 int method = JS('int', '#.indexOf(#)', match, r'\$method\$'); | |
| 992 int receiver = JS('int', '#.indexOf(#)', match, r'\$receiver\$'); | |
| 993 | |
| 994 String pattern = JS('String', | |
| 995 r"#.replace('\\$arguments\\$', '(.*)')" | |
|
sra1
2013/01/29 20:54:16
It will be slightly better if emitted JavaScript s
ahe
2013/01/29 22:06:46
Why is that?
| |
| 996 r".replace('\\$argumentsExpr\\$', '(.*)')" | |
| 997 r".replace('\\$expr\\$', '(.*)')" | |
| 998 r".replace('\\$method\\$', '(.*)')" | |
| 999 r".replace('\\$receiver\\$', '(.*)')", | |
| 1000 message); | |
| 1001 | |
| 1002 return JS('', | |
| 1003 r'{ arguments: #, argumentsExpr: #, expr: #, method: #, ' | |
| 1004 r'receiver: #, pattern: #, ' | |
| 1005 r"""match: function(msg) { | |
| 1006 var match = new RegExp(this.pattern).exec(msg); | |
| 1007 if (!match) return void 0; | |
| 1008 var result = {}; | |
| 1009 if (this.arguments != -1) result.arguments = match[this.arguments + 1]; | |
| 1010 if (this.argumentsExpr != -1) result.argumentsExpr = match[this.argumentsExpr + 1]; | |
| 1011 if (this.expr != -1) result.expr = match[this.expr + 1]; | |
| 1012 if (this.method != -1) result.method = match[this.method + 1]; | |
| 1013 if (this.receiver != -1) result.receiver = match[this.receiver + 1]; | |
| 1014 return result; | |
| 1015 }}""", | |
| 1016 arguments, | |
| 1017 argumentsExpr, | |
| 1018 expr, | |
| 1019 method, | |
| 1020 receiver, | |
| 1021 pattern); | |
| 1022 } | |
| 1023 } | |
| 1024 | |
| 1025 class NullError implements NoSuchMethodError { | |
| 1026 final String _message; | |
| 1027 final String _method; | |
| 1028 | |
| 1029 NullError(this._message, match) | |
| 1030 : this._method = JS('', '#.method', match); | |
| 1031 | |
| 1032 String toString() { | |
| 1033 if (_method == null) return 'NullError: $_message'; | |
| 1034 return 'NullError: Cannot call "$_method" on null'; | |
| 1035 } | |
| 1036 } | |
| 1037 | |
| 1038 class JsNoSuchMethodError implements NoSuchMethodError { | |
| 1039 final String _message; | |
| 1040 final String _method; | |
| 1041 final String _receiver; | |
| 1042 | |
| 1043 JsNoSuchMethodError(this._message, match) | |
| 1044 : this._method = JS('', '#.method', match), | |
| 1045 this._receiver = JS('', '#.receiver', match); | |
| 1046 | |
| 1047 String toString() { | |
| 1048 if (_method == null) return 'NoSuchMethodError: $_message'; | |
| 1049 if (_receiver == null) { | |
| 1050 return 'NoSuchMethodError: Cannot call "$_method" ($_message)'; | |
| 1051 } | |
| 1052 return 'NoSuchMethodError: Cannot call "$_method" on "$_receiver" ' | |
| 1053 '($_message)'; | |
| 1054 } | |
| 1055 } | |
| 1056 | |
| 965 /** | 1057 /** |
| 966 * Called from catch blocks in generated code to extract the Dart | 1058 * Called from catch blocks in generated code to extract the Dart |
| 967 * exception from the thrown value. The thrown value may have been | 1059 * exception from the thrown value. The thrown value may have been |
| 968 * created by [$throw] or it may be a 'native' JS exception. | 1060 * created by [$throw] or it may be a 'native' JS exception. |
| 969 * | 1061 * |
| 970 * Some native exceptions are mapped to new Dart instances, others are | 1062 * Some native exceptions are mapped to new Dart instances, others are |
| 971 * returned unmodified. | 1063 * returned unmodified. |
| 972 */ | 1064 */ |
| 973 unwrapException(ex) { | 1065 unwrapException(ex) { |
| 974 // Note that we are checking if the object has the property. If it | 1066 // Note that we are checking if the object has the property. If it |
| 975 // has, it could be set to null if the thrown value is null. | 1067 // has, it could be set to null if the thrown value is null. |
| 976 if (JS('bool', r'"dartException" in #', ex)) { | 1068 if (JS('bool', r'"dartException" in #', ex)) { |
| 977 return JS('', r'#.dartException', ex); | 1069 return JS('', r'#.dartException', ex); |
| 978 } | 1070 } |
| 979 | 1071 |
| 980 // Grab hold of the exception message. This field is available on | 1072 // Grab hold of the exception message. This field is available on |
| 981 // all supported browsers. | 1073 // all supported browsers. |
| 982 var message = JS('var', r'#.message', ex); | 1074 var message = JS('var', r'#.message', ex); |
| 983 | 1075 |
| 984 if (JS('bool', r'# instanceof TypeError', ex)) { | 1076 if (JS('bool', r'# instanceof TypeError', ex)) { |
| 985 // The type and arguments fields are Chrome specific but they | 1077 var match; |
| 986 // allow us to get very detailed information about what kind of | 1078 var nsme = TypeErrorDecoder.noSuchMethodPattern; |
| 987 // exception occurred. | 1079 var nul = TypeErrorDecoder.nullErrorPattern; |
| 988 var type = JS('var', r'#.type', ex); | 1080 var undef = TypeErrorDecoder.undefinedErrorPattern; |
| 989 var name = JS('var', r'#.arguments ? #.arguments[0] : ""', ex, ex); | 1081 if ((match = JS('', '#.match(#)', nsme, message)) != null) { |
|
ahe
2013/01/17 01:08:05
These fields have been removed from V8 (and Chrome
| |
| 990 if (contains(message, 'JSNull') || | 1082 return new JsNoSuchMethodError(message, match); |
| 991 type == 'property_not_function' || | 1083 } else if ((match = JS('', '#.match(#)', nul, message)) != null || |
| 992 type == 'called_non_callable' || | 1084 (match = JS('', '#.match(#)', undef, message)) != null) { |
| 993 type == 'non_object_property_call' || | 1085 return new NullError(message, match); |
| 994 type == 'non_object_property_load') { | |
| 995 return new NoSuchMethodError(null, name, [], {}); | |
| 996 } else if (type == 'undefined_method') { | |
| 997 return new NoSuchMethodError('', name, [], {}); | |
| 998 } | 1086 } |
| 999 | 1087 |
| 1000 var ieErrorCode = JS('int', '#.number & 0xffff', ex); | 1088 var ieErrorCode = JS('int', '#.number & 0xffff', ex); |
| 1001 var ieFacilityNumber = JS('int', '#.number>>16 & 0x1FFF', ex); | 1089 var ieFacilityNumber = JS('int', '#.number>>16 & 0x1FFF', ex); |
| 1002 // If we cannot use [type] to determine what kind of exception | 1090 if (ieErrorCode == 438 && ieFacilityNumber == 10) { |
| 1003 // we're dealing with we fall back on looking at the exception | 1091 // Object doesn't support property or method 'foo' which sets the error |
| 1004 // message if it is available and a string. | 1092 // code 438 in IE. |
| 1005 if (message is String) { | 1093 return new NoSuchMethodError('', '<unknown>', [], {}); |
| 1006 if (message.endsWith('is null') || | |
| 1007 message.endsWith('is undefined') || | |
| 1008 message.endsWith('is null or undefined') || | |
| 1009 message.endsWith('of undefined') || | |
| 1010 message.endsWith('of null')) { | |
| 1011 return new NoSuchMethodError(null, '<unknown>', [], {}); | |
| 1012 } else if (contains(message, ' has no method ') || | |
| 1013 contains(message, ' is not a function') || | |
| 1014 (ieErrorCode == 438 && ieFacilityNumber == 10)) { | |
| 1015 // Examples: | |
| 1016 // x.foo is not a function | |
| 1017 // 'undefined' is not a function (evaluating 'x.foo(1,2,3)') | |
| 1018 // Object doesn't support property or method 'foo' which sets the error | |
| 1019 // code 438 in IE. | |
| 1020 // TODO(kasperl): Compute the right name if possible. | |
| 1021 return new NoSuchMethodError('', '<unknown>', [], {}); | |
| 1022 } | |
| 1023 } | 1094 } |
| 1024 | 1095 |
| 1025 // If we cannot determine what kind of error this is, we fall back | 1096 // If we cannot determine what kind of error this is, we fall back |
| 1026 // to reporting this as a generic exception. It's probably better | 1097 // to reporting this as a generic exception. It's probably better |
| 1027 // than nothing. | 1098 // than nothing. |
| 1028 return new Exception(message is String ? message : ''); | 1099 return new Exception(message is String ? message : ''); |
| 1029 } | 1100 } |
| 1030 | 1101 |
| 1031 if (JS('bool', r'# instanceof RangeError', ex)) { | 1102 if (JS('bool', r'# instanceof RangeError', ex)) { |
| 1032 if (message is String && contains(message, 'call stack')) { | 1103 if (message is String && contains(message, 'call stack')) { |
| (...skipping 615 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1648 // A list representing a type with arguments. | 1719 // A list representing a type with arguments. |
| 1649 return getTypeArgumentAsString(type); | 1720 return getTypeArgumentAsString(type); |
| 1650 } else { | 1721 } else { |
| 1651 // A reference to the constructor. | 1722 // A reference to the constructor. |
| 1652 return getConstructorName(type); | 1723 return getConstructorName(type); |
| 1653 } | 1724 } |
| 1654 } | 1725 } |
| 1655 | 1726 |
| 1656 String joinArguments(var types, int startIndex) { | 1727 String joinArguments(var types, int startIndex) { |
| 1657 bool firstArgument = true; | 1728 bool firstArgument = true; |
| 1658 StringBuffer buffer = new StringBuffer(); | 1729 String result = ''; |
| 1659 for (int index = startIndex; index < types.length; index++) { | 1730 for (int index = startIndex; index < types.length; index++) { |
| 1660 if (firstArgument) { | 1731 if (firstArgument) { |
| 1661 firstArgument = false; | 1732 firstArgument = false; |
| 1662 } else { | 1733 } else { |
| 1663 buffer. add(', '); | 1734 result = '$result, '; |
| 1664 } | 1735 } |
| 1665 var argument = types[index]; | 1736 var argument = types[index]; |
| 1666 buffer.add(runtimeTypeToString(argument)); | 1737 result = '$result${runtimeTypeToString(argument)}'; |
| 1667 } | 1738 } |
| 1668 return buffer.toString(); | 1739 return result; |
| 1669 } | 1740 } |
| 1670 | 1741 |
| 1671 String getRuntimeTypeString(var object) { | 1742 String getRuntimeTypeString(var object) { |
| 1672 String className = isJsArray(object) ? 'List' : getClassName(object); | 1743 String className = isJsArray(object) ? 'List' : getClassName(object); |
| 1673 var typeInfo = JS('var', r'#.builtin$typeInfo', object); | 1744 var typeInfo = JS('var', r'#.builtin$typeInfo', object); |
| 1674 if (typeInfo == null) return className; | 1745 if (typeInfo == null) return className; |
| 1675 return "$className<${joinArguments(typeInfo, 0)}>"; | 1746 return "$className<${joinArguments(typeInfo, 0)}>"; |
| 1676 } | 1747 } |
| 1677 | 1748 |
| 1678 /** | 1749 /** |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 1708 if (len != t.length) return false; | 1779 if (len != t.length) return false; |
| 1709 for (int i = 1; i < len; i++) { | 1780 for (int i = 1; i < len; i++) { |
| 1710 if (!isSubtype(s[i], t[i])) { | 1781 if (!isSubtype(s[i], t[i])) { |
| 1711 return false; | 1782 return false; |
| 1712 } | 1783 } |
| 1713 } | 1784 } |
| 1714 return true; | 1785 return true; |
| 1715 } | 1786 } |
| 1716 | 1787 |
| 1717 createRuntimeType(String name) => new TypeImpl(name); | 1788 createRuntimeType(String name) => new TypeImpl(name); |
| OLD | NEW |