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 cc6d052cce473a4d293d4ddbcea5d028b473526d..60853523726971f18b71eeba915b000c5a3c581b 100644 |
--- a/dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
+++ b/dart/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
@@ -951,30 +951,81 @@ class MathNatives { |
} |
/** |
- * Throws the given Dart object as an exception by wrapping it in a |
- * proper JavaScript error object and then throwing that. That gives |
- * us a reasonable stack trace on most JavaScript implementations. The |
- * code in [unwrapException] deals with getting the original Dart |
+ * Wrap the given Dart object and record a stack trace. |
+ * |
+ * The code in [unwrapException] deals with getting the original Dart |
* object out of the wrapper again. |
*/ |
$throw(ex) { |
if (ex == null) ex = const NullThrownError(); |
- var jsError = JS('var', r'new Error()'); |
- JS('void', r'#.name = #', jsError, ex); |
- JS('void', r'#.description = #', jsError, ex); |
- JS('void', r'#.dartException = #', jsError, ex); |
- JS('void', r'#.toString = #', jsError, |
- DART_CLOSURE_TO_JS(toStringWrapper)); |
- JS('void', r'throw #', jsError); |
+ var wrapper = new DartError(ex); |
+ |
+ if (JS('bool', 'Error.captureStackTrace')) { |
+ // Use V8 API for recording a "fast" stack trace (this installs a |
+ // "stack" property getter on [wrapper]). |
+ JS('void', r'Error.captureStackTrace(#, #)', |
+ wrapper, RAW_DART_FUNCTION_REF($throw)); |
+ } else { |
+ // Otherwise, produce a stack trace and record it in the wrapper. |
+ // This is a slower way to create a stack trace which works on |
+ // some browsers, but may simply evaluate to null. |
+ String stackTrace = JS('', 'new Error().stack'); |
+ JS('void', '#.stack = #', wrapper, stackTrace); |
+ } |
+ return wrapper; |
} |
/** |
- * This method is installed as JavaScript toString method on exception |
- * objects in [$throw]. So JavaScript 'this' binds to an instance of |
- * JavaScript Error to which we have added a property 'dartException' |
- * which holds a Dart object. |
+ * Wrapper class for throwing exceptions. |
*/ |
-toStringWrapper() => JS('', r'this.dartException').toString(); |
+class DartError { |
+ /// The Dart object (or primitive JavaScript value) which was thrown. |
+ final dartException; |
+ |
+ DartError(this.dartException) { |
+ // Install a toString method that the JavaScript system will call |
+ // to format uncaught exceptions. |
+ JS('void', '#.toString = #', this, DART_CLOSURE_TO_JS(toStringWrapper)); |
+ } |
+ |
+ /** |
+ * V8/Chrome installs a property getter, "stack", when calling |
+ * Error.captureStackTrace (see [$throw]). In [$throw], we make sure |
+ * that this property is always set. |
+ */ |
+ String get stack => JS('', '#.stack', this); |
+ |
+ /** |
+ * This method can be invoked by calling toString from |
+ * JavaScript. See the constructor of this class. |
+ * |
+ * We only expect this method to be called (indirectly) by the |
+ * browser when an uncaught exception occurs. Instance of this class |
+ * should never escape into Dart code (except for [$throw] above). |
+ */ |
+ String toString() { |
+ // If Error.captureStackTrace is available, accessing stack from |
+ // this method would cause recursion because the stack property |
+ // (on this object) is actually a getter which calls toString on |
+ // this object (via the wrapper installed in this class' |
+ // constructor). Fortunately, both Chrome and d8 prints the stack |
+ // trace and Chrome even applies source maps to the stack |
+ // trace. Remeber, this method is only ever invoked by the browser |
+ // when an uncaught exception occurs. |
+ if (JS('bool', 'Error.captureStackTrace') || (stack == null)) { |
+ return dartException.toString(); |
+ } else { |
+ return '$dartException\n$stack'; |
+ } |
+ } |
+ |
+ /** |
+ * This method is installed as JavaScript toString method on |
+ * [DartError]. So JavaScript 'this' binds to an instance of |
+ * DartError. |
+ */ |
+ static toStringWrapper() => JS('', r'this').toString(); |
+} |
makeLiteralListConst(list) { |
JS('bool', r'#.immutable$list = #', list, true); |