v8/test/mjsunit/error-stack.js
Simon Zünd c8206043e1 [stack-trace] Use ErrorStack accessor for formatted stack traces
When a stack trace is captured, it is stored in a private symbol on
the respective Error object. The first access to "Error.stack"  will
then format the stack trace, with a possible call into user JS via
the Error.prepareStackTrace callback.

Until now, the accessor converted ".stack" to a normal data
property containing the formatted stack trace. This causes a new Map
with a new DescriptorArray to be created, which will not be shared
with anything else (also not other error objects with formated
stack traces).

This CL changes the accessor to store the formatted stack trace in
the same symbol (stack_trace_symbol) as the structured data. The
result is that an error object will have the same Map before and
after "Error.stack" is accessed.

Bug: v8:9115
Change-Id: I7d6bf49be76d63b57fbbaf904cc6ed7dbdbfb96b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1564061
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60865}
2019-04-16 12:02:40 +00:00

76 lines
2.2 KiB
JavaScript

// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --allow-natives-syntax
(function TestErrorObjectsRetainMap() {
const error1 = new Error("foo");
const error2 = new Error("bar");
assertTrue(%HaveSameMap(error1, error2));
// Trigger serialization of the stack-trace.
error1.stack;
assertTrue(%HaveSameMap(error1, error2));
error2.stack;
assertTrue(%HaveSameMap(error1, error2));
})();
(function TestPrepareStackTraceCallback() {
Error.prepareStackTrace = (error, frame) => {
return "custom stack trace No. 42";
};
const error = new Error("foo");
// Check it twice, so both code paths in the accessor are exercised.
assertEquals(error.stack, "custom stack trace No. 42");
assertEquals(error.stack, "custom stack trace No. 42");
})();
(function TestPrepareStackTraceCallbackMessesWithProperty() {
Error.prepareStackTrace = (error, frames) => {
error.stack = "Yes, we can write to this!";
return 42;
};
const error = new Error("foo");
// Check it twice. The first returns the formatting result,
// the second the value of the private symbol.
assertEquals(error.stack, 42);
assertEquals(error.stack, 42);
})();
(function TestPrepareStackTraceCallbackInstallsGetter() {
Error.prepareStackTrace = (error, frames) => {
Object.defineProperty(error, "stack", { get: () => 42 });
return "<formatted stack trace>";
};
const error = new Error("foo");
// Check it twice. The second time the accessor should be used.
assertEquals(error.stack, "<formatted stack trace>");
assertEquals(error.stack, 42);
})();
(function TestPrepareStackTraceCallbackInstallsSetter() {
Error.prepareStackTrace = (error, frames) => {
Object.defineProperty(error, "stack", { set: (x) => {
error[42] = x;
}});
return "<formatted stack trace>";
};
const error = new Error("foo");
// Cause the accessor to get installed.
error.stack;
error.stack = "Who needs stack traces anyway?";
assertEquals(error[42], "Who needs stack traces anyway?");
assertEquals(error.stack, undefined); // No getter.
})();