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

Unified Diff: dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart

Issue 11973018: Improve decoding of JS TypeError. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | dart/sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
« no previous file with comments | « no previous file | dart/sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698