| 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.
|
|
|