Chromium Code Reviews| Index: dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
| diff --git a/dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart b/dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
| index 1b78b32d343cd25eb64cb1492c4c62de784d16d8..ceec5c42d5c0bf5eba82064aaf4a8e6730db176c 100644 |
| --- a/dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
| +++ b/dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
| @@ -694,7 +694,6 @@ class Primitives { |
| List positionalArguments, |
| Map<String, dynamic> namedArguments) { |
| int argumentCount = 0; |
| - StringBuffer buffer = new StringBuffer(); |
| List arguments = []; |
| if (positionalArguments != null) { |
| @@ -702,6 +701,8 @@ class Primitives { |
| arguments.addAll(positionalArguments); |
| } |
| + String selectorName = 'call\$$argumentCount'; |
| + |
| // Sort the named arguments to get the right selector name and |
| // arguments order. |
| if (namedArguments != null && !namedArguments.isEmpty) { |
| @@ -714,12 +715,11 @@ class Primitives { |
| // sort implementation, we use the JavaScript one instead. |
| JS('void', '#.sort()', listOfNamedArguments); |
| listOfNamedArguments.forEach((String name) { |
| - buffer.add('\$$name'); |
| + selectorName = '$selectorName\$$name'; |
| arguments.add(namedArguments[name]); |
| }); |
| } |
| - String selectorName = 'call\$$argumentCount$buffer'; |
| var jsFunction = JS('var', '#[#]', function, selectorName); |
| if (jsFunction == null) { |
| throw new NoSuchMethodError(function, selectorName, arguments, {}); |
| @@ -962,6 +962,98 @@ throwAbstractClassInstantiationError(className) { |
| throw new AbstractClassInstantiationError(className); |
| } |
| +class TypeErrorDecoder { |
| + 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
|
| + extractPattern(JS('', |
| + r'{ toString: function() { return "$receiver$"; } }')); |
| + static final nullErrorPattern = extractPattern(JS('', 'null')); |
| + static final undefinedErrorPattern = extractPattern(JS('', 'void 0')); |
| + |
| + static extractPattern(expression) { |
| + 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
|
| + var $argumentsExpr$ = '$arguments$' |
| + try { |
| + $expr$.$method$($argumentsExpr$); |
| + } catch (e) { |
| + return (e.message""" |
| +// 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
|
| +r""" |
| + .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.
|
| + .replace(new RegExp(#, 'g'), '\\$&')); |
| + } |
| +}""", ESCAPE_REGEXP); |
| + String message = JS('String', '(#)(#)', function, expression); |
| + List<String> match = |
| + JS('=List', r"#.match(/\\\$[a-zA-Z]+\\\$/g)", message); |
| + int arguments = JS('int', '#.indexOf(#)', match, r'\$arguments\$'); |
| + int argumentsExpr = JS('int', '#.indexOf(#)', match, r'\$argumentsExpr\$'); |
| + int expr = JS('int', '#.indexOf(#)', match, r'\$expr\$'); |
| + int method = JS('int', '#.indexOf(#)', match, r'\$method\$'); |
| + int receiver = JS('int', '#.indexOf(#)', match, r'\$receiver\$'); |
| + |
| + String pattern = JS('String', |
| + 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?
|
| + r".replace('\\$argumentsExpr\\$', '(.*)')" |
| + r".replace('\\$expr\\$', '(.*)')" |
| + r".replace('\\$method\\$', '(.*)')" |
| + r".replace('\\$receiver\\$', '(.*)')", |
| + message); |
| + |
| + return JS('', |
| + r'{ arguments: #, argumentsExpr: #, expr: #, method: #, ' |
| + r'receiver: #, pattern: #, ' |
| + r"""match: function(msg) { |
| +var match = new RegExp(this.pattern).exec(msg); |
| +if (!match) return void 0; |
| +var result = {}; |
| +if (this.arguments != -1) result.arguments = match[this.arguments + 1]; |
| +if (this.argumentsExpr != -1) result.argumentsExpr = match[this.argumentsExpr + 1]; |
| +if (this.expr != -1) result.expr = match[this.expr + 1]; |
| +if (this.method != -1) result.method = match[this.method + 1]; |
| +if (this.receiver != -1) result.receiver = match[this.receiver + 1]; |
| +return result; |
| +}}""", |
| + arguments, |
| + argumentsExpr, |
| + expr, |
| + method, |
| + receiver, |
| + pattern); |
| + } |
| +} |
| + |
| +class NullError implements NoSuchMethodError { |
| + final String _message; |
| + final String _method; |
| + |
| + NullError(this._message, match) |
| + : this._method = JS('', '#.method', match); |
| + |
| + String toString() { |
| + if (_method == null) return 'NullError: $_message'; |
| + return 'NullError: Cannot call "$_method" on null'; |
| + } |
| +} |
| + |
| +class JsNoSuchMethodError implements NoSuchMethodError { |
| + final String _message; |
| + final String _method; |
| + final String _receiver; |
| + |
| + JsNoSuchMethodError(this._message, match) |
| + : this._method = JS('', '#.method', match), |
| + this._receiver = JS('', '#.receiver', match); |
| + |
| + String toString() { |
| + if (_method == null) return 'NoSuchMethodError: $_message'; |
| + if (_receiver == null) { |
| + return 'NoSuchMethodError: Cannot call "$_method" ($_message)'; |
| + } |
| + return 'NoSuchMethodError: Cannot call "$_method" on "$_receiver" ' |
| + '($_message)'; |
| + } |
| +} |
| + |
| /** |
| * Called from catch blocks in generated code to extract the Dart |
| * exception from the thrown value. The thrown value may have been |
| @@ -982,44 +1074,23 @@ unwrapException(ex) { |
| var message = JS('var', r'#.message', ex); |
| if (JS('bool', r'# instanceof TypeError', ex)) { |
| - // The type and arguments fields are Chrome specific but they |
| - // allow us to get very detailed information about what kind of |
| - // exception occurred. |
| - var type = JS('var', r'#.type', ex); |
| - var name = JS('var', r'#.arguments ? #.arguments[0] : ""', ex, ex); |
|
ahe
2013/01/17 01:08:05
These fields have been removed from V8 (and Chrome
|
| - if (contains(message, 'JSNull') || |
| - type == 'property_not_function' || |
| - type == 'called_non_callable' || |
| - type == 'non_object_property_call' || |
| - type == 'non_object_property_load') { |
| - return new NoSuchMethodError(null, name, [], {}); |
| - } else if (type == 'undefined_method') { |
| - return new NoSuchMethodError('', name, [], {}); |
| + var match; |
| + var nsme = TypeErrorDecoder.noSuchMethodPattern; |
| + var nul = TypeErrorDecoder.nullErrorPattern; |
| + var undef = TypeErrorDecoder.undefinedErrorPattern; |
| + if ((match = JS('', '#.match(#)', nsme, message)) != null) { |
| + return new JsNoSuchMethodError(message, match); |
| + } else if ((match = JS('', '#.match(#)', nul, message)) != null || |
| + (match = JS('', '#.match(#)', undef, message)) != null) { |
| + return new NullError(message, match); |
| } |
| var ieErrorCode = JS('int', '#.number & 0xffff', ex); |
| var ieFacilityNumber = JS('int', '#.number>>16 & 0x1FFF', ex); |
| - // If we cannot use [type] to determine what kind of exception |
| - // we're dealing with we fall back on looking at the exception |
| - // message if it is available and a string. |
| - if (message is String) { |
| - if (message.endsWith('is null') || |
| - message.endsWith('is undefined') || |
| - message.endsWith('is null or undefined') || |
| - message.endsWith('of undefined') || |
| - message.endsWith('of null')) { |
| - return new NoSuchMethodError(null, '<unknown>', [], {}); |
| - } else if (contains(message, ' has no method ') || |
| - contains(message, ' is not a function') || |
| - (ieErrorCode == 438 && ieFacilityNumber == 10)) { |
| - // Examples: |
| - // x.foo is not a function |
| - // 'undefined' is not a function (evaluating 'x.foo(1,2,3)') |
| - // Object doesn't support property or method 'foo' which sets the error |
| - // code 438 in IE. |
| - // TODO(kasperl): Compute the right name if possible. |
| - return new NoSuchMethodError('', '<unknown>', [], {}); |
| - } |
| + if (ieErrorCode == 438 && ieFacilityNumber == 10) { |
| + // Object doesn't support property or method 'foo' which sets the error |
| + // code 438 in IE. |
| + return new NoSuchMethodError('', '<unknown>', [], {}); |
| } |
| // If we cannot determine what kind of error this is, we fall back |
| @@ -1655,17 +1726,17 @@ String runtimeTypeToString(type) { |
| String joinArguments(var types, int startIndex) { |
| bool firstArgument = true; |
| - StringBuffer buffer = new StringBuffer(); |
| + String result = ''; |
| for (int index = startIndex; index < types.length; index++) { |
| if (firstArgument) { |
| firstArgument = false; |
| } else { |
| - buffer. add(', '); |
| + result = '$result, '; |
| } |
| var argument = types[index]; |
| - buffer.add(runtimeTypeToString(argument)); |
| + result = '$result${runtimeTypeToString(argument)}'; |
| } |
| - return buffer.toString(); |
| + return result; |
| } |
| String getRuntimeTypeString(var object) { |