Index: pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
index 8e8a2cd130b78152f2ee78e6406ea2a16a3590eb..e3492c26da036582534094cd962c29c00c9ee084 100644 |
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
@@ -617,37 +617,70 @@ assert_(condition, [message]) => JS( |
if (!$condition) $throwAssertionError(message); |
})()'''); |
-var _stack = null; |
-@JSExportName('throw') |
-throw_(obj) => JS( |
- '', |
- '''(() => { |
- $_stack = new Error(); |
- throw $obj; |
-})()'''); |
+/// Store a JS error for an exception. For non-primitives, we store as an |
+/// expando. For primitive, we use a side cache. To limit memory leakage, we |
+/// only keep the last [_maxTraceCache] entries. |
+final _error = JS('', 'Symbol("_error")'); |
+Map _primitiveErrorCache; |
+const _maxErrorCache = 10; |
+ |
+bool _isJsError(exception) { |
+ return JS('bool', '#.Error != null && # instanceof #.Error', global_, |
+ exception, global_); |
+} |
-getError(exception) => JS( |
- '', |
- '''(() => { |
- var stack = $_stack; |
- return stack !== null ? stack : $exception; |
-})()'''); |
+// Record/return the JS error for an exception. If an error was already |
+// recorded, prefer that to [newError]. |
+recordJsError(exception, [newError]) { |
+ if (_isJsError(exception)) return exception; |
+ |
+ var useExpando = |
+ exception != null && JS('bool', 'typeof # == "object"', exception); |
+ var error; |
+ if (useExpando) { |
+ error = JS('', '#[#]', exception, _error); |
+ } else { |
+ if (_primitiveErrorCache == null) _primitiveErrorCache = {}; |
+ error = _primitiveErrorCache[exception]; |
+ } |
+ if (error != null) return error; |
+ if (newError != null) { |
+ error = newError; |
+ } else { |
+ // We should only hit this path when a non-Error was thrown from JS. In |
+ // case, there is no stack trace on the exception, so we create one: |
+ error = JS('', 'new Error()'); |
+ } |
+ if (useExpando) { |
+ JS('', '#[#] = #', exception, _error, error); |
+ } else { |
+ _primitiveErrorCache[exception] = error; |
+ if (_primitiveErrorCache.length > _maxErrorCache) { |
+ _primitiveErrorCache.remove(_primitiveErrorCache.keys.first); |
+ } |
+ } |
+ return error; |
+} |
+ |
+@JSExportName('throw') |
+throw_(obj) { |
+ // Note, we create the error here to avoid the extra frame. |
+ // package:stack_trace and tests appear to assume this. We could fix use |
+ // cases instead, but we're already on the exceptional path here. |
+ recordJsError(obj, JS('', 'new Error()')); |
+ JS('', 'throw #', obj); |
+} |
// This is a utility function: it is only intended to be called from dev |
// tools. |
-stackPrint(exception) => JS( |
- '', |
- '''(() => { |
- var error = $getError($exception); |
- console.log(error.stack ? error.stack : 'No stack trace for: ' + error); |
-})()'''); |
+stackPrint(exception) { |
+ var error = recordJsError(exception); |
+ JS('', 'console.log(#.stack ? #.stack : "No stack trace for: " + #)', error, |
+ error, error); |
+} |
-stackTrace(exception) => JS( |
- '', |
- '''(() => { |
- var error = $getError($exception); |
- return $getTraceFromException(error); |
-})()'''); |
+// Forward to dart:_js_helper to create a _StackTrace object. |
+stackTrace(exception) => getTraceFromException(exception); |
/// |
/// Implements a sequence of .? operations. |