... instead of the native data property. This fixes the JS spec violation when reading a data property does not expect any observable side effects. For example, OrdinaryGetOwnProperty (see https://tc39.es/ecma262/#sec-ordinarygetownproperty, step 4a). Differences to previous behaviour: - Error.stack is defined as a JavaScript accessor property. - all Error objects get a private "error_stack_symbol" field where the captured stack trace is stored (previously it was added only when the error was actually thrown which caused unnecessary transitions). - Error.captureStackTrace(obj) adds public "stack" accessor property and private "error_stack_symbol" property to given "obj". - calling "stack" getter/setter is a no-op in case receiver is not an "Error-like" object, i.e. it doesn't have a "error_stack_symbol" property and it doesn't have a prototype with such a property (the lookup stops at JSProxy or interceptor). - the "stack" getter walks the prototype chain from receiver until it finds a holder with the "error_stack_symbol" property which is then used for computing the result. This is slightly different from the previous behaviour in case receiver's prototype chain contains multiple error objects. - the "stack" setter walks the prototype chain from receiver until it finds a holder with the "error_stack_symbol" property and stores the value there. Drive-by: handle lazy accessor pairs in ObjectVisitorDeepFreezer. Bug: v8:5962 Change-Id: I109d6fbbbdb9588d13ca86d217efd9a4a11d8447 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4459251 Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Shu-yu Guo <syg@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/main@{#87286}
104 lines
3.0 KiB
JavaScript
104 lines
3.0 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", { get: undefined, 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.
|
|
})();
|
|
|
|
(function TestFormatStackPropertyInDictionaryMode() {
|
|
Error.prepareStackTrace = (error, frames) => {
|
|
return "<formatted stack trace>";
|
|
};
|
|
const error = new Error("foo");
|
|
error[%MaxSmi()] = 42;
|
|
|
|
assertTrue(%HasDictionaryElements(error));
|
|
|
|
// Check it twice.
|
|
assertEquals(error.stack, "<formatted stack trace>");
|
|
assertEquals(error.stack, "<formatted stack trace>");
|
|
})();
|
|
|
|
(function TestTransitionToDictionaryModeAfterFormatting() {
|
|
Error.prepareStackTrace = (error, frames) => {
|
|
return "<formatted stack trace>";
|
|
};
|
|
const error = new Error("foo");
|
|
assertFalse(%HasDictionaryElements(error));
|
|
|
|
assertEquals(error.stack, "<formatted stack trace>");
|
|
|
|
error[%MaxSmi()] = 42;
|
|
assertTrue(%HasDictionaryElements(error));
|
|
assertEquals(error.stack, "<formatted stack trace>");
|
|
})();
|