add error stack accessor tests
diff --git a/features.txt b/features.txt index 3aa05db..4acec76 100644 --- a/features.txt +++ b/features.txt
@@ -93,6 +93,10 @@ # https://github.com/tc39/proposal-await-dictionary await-dictionary +# Error Stack Accessor +# https://github.com/tc39/proposal-error-stack-accessor +error-stack-accessor + ## Standard language features # # Language features that have been included in a published version of the
diff --git a/test/built-ins/Error/prototype/stack/getter-aggregate-error-prototype.js b/test/built-ins/Error/prototype/stack/getter-aggregate-error-prototype.js new file mode 100644 index 0000000..1029426 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-aggregate-error-prototype.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns undefined when called on AggregateError.prototype, which + is an ordinary object that does not have an [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, AggregateError] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(AggregateError.prototype), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-aggregate-error.js b/test/built-ins/Error/prototype/stack/getter-aggregate-error.js new file mode 100644 index 0000000..5539316 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-aggregate-error.js
@@ -0,0 +1,27 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns a String when called on an AggregateError instance. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. + + AggregateError instances are ordinary objects that inherit properties from + their AggregateError prototype object and have an [[ErrorData]] internal slot + whose value is undefined. +features: [error-stack-accessor, AggregateError] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var err = new AggregateError([new Error('inner')], 'outer'); + +assert.sameValue(typeof get.call(err), 'string', 'AggregateError instance via get.call'); +assert.sameValue(typeof err.stack, 'string', 'AggregateError instance via property access');
diff --git a/test/built-ins/Error/prototype/stack/getter-cross-realm.js b/test/built-ins/Error/prototype/stack/getter-cross-realm.js new file mode 100644 index 0000000..6aabbad --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-cross-realm.js
@@ -0,0 +1,51 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter's [[ErrorData]] check is realm-agnostic: a getter from realm A + invoked on an Error instance from realm B returns a String, since the + internal-slot check examines object identity of the slot, not realm origin. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, cross-realm] +---*/ + +var getA = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var realmB = $262.createRealm().global; + +assert.notSameValue( + Error.prototype, + realmB.Error.prototype, + 'precondition: the two realms have distinct Error.prototype objects' +); + +// (a) Realm A's getter on a realm B Error instance: the instance has +// [[ErrorData]], so the getter returns a string regardless of which realm +// owns the slot. +var errB = new realmB.Error('msg'); +assert.sameValue(typeof getA.call(errB), 'string', 'cross-realm Error instance returns a string'); + +// (b) Realm A's getter on a realm B plain object: no [[ErrorData]], returns +// undefined. +var plainB = new realmB.Object(); +assert.sameValue(getA.call(plainB), undefined, 'cross-realm plain object returns undefined'); + +// (c) Realm A's getter on realm B's Error.prototype: the prototype object +// itself has no [[ErrorData]] slot, so the getter returns undefined. +assert.sameValue(getA.call(realmB.Error.prototype), undefined, 'cross-realm Error.prototype returns undefined'); + +// (d) Realm B's getter on a realm A Error instance: same logic in reverse. +var getB = Object.getOwnPropertyDescriptor(realmB.Error.prototype, 'stack').get; +assert.sameValue(typeof getB, 'function', 'realm B has its own getter'); +assert.notSameValue(getA, getB, 'the two realms have distinct getter functions'); + +var errA = new Error('msg'); +assert.sameValue(typeof getB.call(errA), 'string', 'realm B getter on realm A Error instance returns a string');
diff --git a/test/built-ins/Error/prototype/stack/getter-data-property-shadows.js b/test/built-ins/Error/prototype/stack/getter-data-property-shadows.js new file mode 100644 index 0000000..f23e2cc --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-data-property-shadows.js
@@ -0,0 +1,46 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + An own data property named "stack" on an Error instance shadows the inherited + accessor when accessed via property lookup. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + Object.defineProperty(err, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, + }); + + assert.sameValue(err.stack, 'sentinel', Ctor.name + ': own data property is returned by [[Get]]'); + + // The inherited accessor still produces a string when invoked directly, + // because the algorithm operates on the [[ErrorData]] slot, not on properties. + assert.sameValue(typeof get.call(err), 'string', Ctor.name + ': inherited accessor still returns a string'); +}
diff --git a/test/built-ins/Error/prototype/stack/getter-error-as-prototype.js b/test/built-ins/Error/prototype/stack/getter-error-as-prototype.js new file mode 100644 index 0000000..4f67db0 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-error-as-prototype.js
@@ -0,0 +1,47 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + An object whose prototype is an Error instance does not itself have an + [[ErrorData]] internal slot, so the getter returns undefined. The slot is + not inherited through the prototype chain. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, __proto__] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var protoErr = new Ctor('outer'); + var o = { __proto__: protoErr }; + + assert.sameValue(get.call(o), undefined, Ctor.name + ': get.call on object with instance as proto'); + + // Property access walks the prototype chain to find the accessor on + // Error.prototype, then calls the getter with this set to the original + // receiver (o). Because o lacks [[ErrorData]], the result is undefined. + assert.sameValue(o.stack, undefined, Ctor.name + ': property access on object with instance as proto'); + + // The inherited instance still produces a string when the getter is invoked + // on it directly. + assert.sameValue(typeof get.call(protoErr), 'string', Ctor.name + ': get.call on the underlying instance'); +}
diff --git a/test/built-ins/Error/prototype/stack/getter-error-instance.js b/test/built-ins/Error/prototype/stack/getter-error-instance.js new file mode 100644 index 0000000..bd7e155 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-error-instance.js
@@ -0,0 +1,41 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns a String when called on an Error instance. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + + var err = new Ctor('msg'); + assert.sameValue(typeof get.call(err), 'string', Ctor.name + ': new Ctor instance via get.call'); + assert.sameValue(typeof err.stack, 'string', Ctor.name + ': new Ctor instance via property access'); + + var err2 = Ctor('msg'); + assert.sameValue(typeof get.call(err2), 'string', Ctor.name + ': Ctor called without new'); + + assert.sameValue(typeof get.call(err), 'string', Ctor.name + ': second call still returns a string'); +}
diff --git a/test/built-ins/Error/prototype/stack/getter-error-prototype.js b/test/built-ins/Error/prototype/stack/getter-error-prototype.js new file mode 100644 index 0000000..8155979 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-error-prototype.js
@@ -0,0 +1,36 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns undefined when called on Error.prototype itself, because + the Error prototype object is not an Error instance and does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. + + Properties of the Error Prototype Object + + The Error prototype object: + [...] + is not an Error instance and does not have an [[ErrorData]] internal slot. +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(Error.prototype), undefined, 'Error.prototype'); +assert.sameValue(Error.prototype.stack, undefined, 'access via property'); + +assert.sameValue(get.call(EvalError.prototype), undefined, 'EvalError.prototype'); +assert.sameValue(get.call(RangeError.prototype), undefined, 'RangeError.prototype'); +assert.sameValue(get.call(ReferenceError.prototype), undefined, 'ReferenceError.prototype'); +assert.sameValue(get.call(SyntaxError.prototype), undefined, 'SyntaxError.prototype'); +assert.sameValue(get.call(TypeError.prototype), undefined, 'TypeError.prototype'); +assert.sameValue(get.call(URIError.prototype), undefined, 'URIError.prototype');
diff --git a/test/built-ins/Error/prototype/stack/getter-foreign-new-target.js b/test/built-ins/Error/prototype/stack/getter-foreign-new-target.js new file mode 100644 index 0000000..1ddce62 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-foreign-new-target.js
@@ -0,0 +1,55 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + An Error instance constructed with a non-Error new.target acquires the + [[ErrorData]] internal slot from Error's [[Construct]], so the getter + returns a String when invoked directly. Property access via the result + does NOT find the inherited accessor, because the result's [[Prototype]] + is the new.target's prototype, not Error.prototype. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, Reflect.construct] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +function NotAnError() {} + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var e = Reflect.construct(Ctor, ['msg'], NotAnError); + + assert.sameValue( + Object.getPrototypeOf(e), + NotAnError.prototype, + Ctor.name + ': [[Prototype]] is the new.target prototype' + ); + + // The internal slot is set by Ctor's [[Construct]] regardless of new.target, + // so the getter (called directly) still produces a string. + assert.sameValue(typeof get.call(e), 'string', Ctor.name + ': via get.call'); + + // Property access on the instance does NOT find the inherited accessor: + // Error.prototype is not on e's prototype chain (it's only on Ctor.prototype, + // which is bypassed by the foreign new.target). The lookup walks + // NotAnError.prototype then Object.prototype and returns undefined. + assert.sameValue(e.stack, undefined, Ctor.name + ': property access returns undefined'); +}
diff --git a/test/built-ins/Error/prototype/stack/getter-length.js b/test/built-ins/Error/prototype/stack/getter-length.js new file mode 100644 index 0000000..375b64f --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-length.js
@@ -0,0 +1,33 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + get Error.prototype.stack.length is 0. +info: | + get Error.prototype.stack + + 17 ECMAScript Standard Built-in Objects: + Every built-in function object, including constructors, has a length + property whose value is an integer. Unless otherwise specified, this + value is equal to the largest number of named arguments shown in the + subclause headings for the function description, including optional + parameters. However, rest parameters shown using the form "...name" + are not included in the default argument count. + + Unless otherwise specified, the length property of a built-in function + object has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +verifyProperty(get, 'length', { + value: 0, + writable: false, + enumerable: false, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/getter-name.js b/test/built-ins/Error/prototype/stack/getter-name.js new file mode 100644 index 0000000..1259a17 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-name.js
@@ -0,0 +1,30 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + get Error.prototype.stack.name is "get stack". +info: | + get Error.prototype.stack + + 17 ECMAScript Standard Built-in Objects + + Functions that are specified as get or set accessor functions of built-in + properties have "get " or "set " prepended to the property name string. + + Unless otherwise specified, the name property of a built-in function object, + if it exists, has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +verifyProperty(get, 'name', { + value: 'get stack', + writable: false, + enumerable: false, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-arraybuffer.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-arraybuffer.js new file mode 100644 index 0000000..219aac3 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-arraybuffer.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on an ArrayBuffer, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, ArrayBuffer] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new ArrayBuffer(0)), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-map.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-map.js new file mode 100644 index 0000000..4f300a7 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-map.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on a Map, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, Map] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new Map()), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-promise.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-promise.js new file mode 100644 index 0000000..5fad443 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-promise.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on a Promise, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, Promise] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new Promise(function () {})), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-set.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-set.js new file mode 100644 index 0000000..77d2a37 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-set.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on a Set, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, Set] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new Set()), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-typedarray.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-typedarray.js new file mode 100644 index 0000000..a2618e0 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-typedarray.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on a TypedArray, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, TypedArray] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new Int8Array()), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-weakmap.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-weakmap.js new file mode 100644 index 0000000..227f99e --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-weakmap.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on a WeakMap, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, WeakMap] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new WeakMap()), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data-weakset.js b/test/built-ins/Error/prototype/stack/getter-no-error-data-weakset.js new file mode 100644 index 0000000..13f24ac --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data-weakset.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on a WeakSet, which does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, WeakSet] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(new WeakSet()), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-no-error-data.js b/test/built-ins/Error/prototype/stack/getter-no-error-data.js new file mode 100644 index 0000000..6eb9b4d --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-no-error-data.js
@@ -0,0 +1,41 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Returns undefined when called on an object that does not have an + [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call({}), undefined, 'plain object'); +assert.sameValue(get.call([]), undefined, 'array'); +assert.sameValue(get.call(function () {}), undefined, 'function'); +assert.sameValue(get.call(/re/), undefined, 'RegExp'); +assert.sameValue(get.call(new Date()), undefined, 'Date'); +assert.sameValue(get.call(new Boolean(true)), undefined, 'Boolean wrapper'); +assert.sameValue(get.call(new Number(0)), undefined, 'Number wrapper'); +assert.sameValue(get.call(new String('')), undefined, 'String wrapper'); + +// An object that inherits from Error.prototype but lacks [[ErrorData]] still +// returns undefined: the getter checks for the internal slot, not the prototype. +var fakeError = Object.create(Error.prototype); +assert.sameValue(get.call(fakeError), undefined, 'object with Error.prototype on its prototype chain'); + +var fakeErrorWithStack = Object.create(Error.prototype); +Object.defineProperty(fakeErrorWithStack, 'stack', { value: 'imposter', writable: true, enumerable: true, configurable: true }); +assert.sameValue( + get.call(fakeErrorWithStack), + undefined, + 'an existing own "stack" data property is ignored by the getter on a non-Error object' +);
diff --git a/test/built-ins/Error/prototype/stack/getter-not-a-constructor.js b/test/built-ins/Error/prototype/stack/getter-not-a-constructor.js new file mode 100644 index 0000000..2a59489 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-not-a-constructor.js
@@ -0,0 +1,28 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + get Error.prototype.stack does not implement [[Construct]], is not new-able +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. +includes: [isConstructor.js] +features: [error-stack-accessor, Reflect.construct] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue( + isConstructor(get), + false, + 'isConstructor(get Error.prototype.stack) must return false' +); + +assert.throws(TypeError, function () { + new get(); +});
diff --git a/test/built-ins/Error/prototype/stack/getter-null-proto-receiver.js b/test/built-ins/Error/prototype/stack/getter-null-proto-receiver.js new file mode 100644 index 0000000..5a453a5 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-null-proto-receiver.js
@@ -0,0 +1,22 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns undefined when called on a null-prototype object, since + it has no [[ErrorData]] internal slot. The check does not depend on + inherited machinery from Object.prototype. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, __proto__] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call({ __proto__: null }), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-proxy-receiver.js b/test/built-ins/Error/prototype/stack/getter-proxy-receiver.js new file mode 100644 index 0000000..4c57892 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-proxy-receiver.js
@@ -0,0 +1,61 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter operates on the receiver's [[ErrorData]] internal slot directly. + When the receiver is a Proxy, the proxy's traps are not consulted: a Proxy + has no [[ErrorData]] internal slot regardless of its target, so the getter + returns undefined. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, Proxy, Reflect] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +function noTraps(label) { + return new Proxy(new Error('inner'), { + get: function () { throw new Test262Error(label + ': get trap should not fire'); }, + getOwnPropertyDescriptor: function () { throw new Test262Error(label + ': getOwnPropertyDescriptor trap should not fire'); }, + has: function () { throw new Test262Error(label + ': has trap should not fire'); }, + getPrototypeOf: function () { throw new Test262Error(label + ': getPrototypeOf trap should not fire'); }, + ownKeys: function () { throw new Test262Error(label + ': ownKeys trap should not fire'); }, + }); +} + +// (a) Proxy wrapping an Error instance: the Proxy itself has no [[ErrorData]], +// so the getter returns undefined without consulting the wrapped target. +assert.sameValue(get.call(noTraps('a')), undefined, 'Proxy wrapping Error returns undefined'); + +// (b) Same for property access through the Proxy: the [[Get]] forwards the +// access to the proxy's get trap (which would throw), but the receiver passed +// through to the inherited accessor on Error.prototype is the proxy itself. +// Since the proxy lacks [[ErrorData]], the getter returns undefined; however, +// because [[Get]] on the proxy first invokes the get trap, this exercises a +// different code path. Use a separate proxy whose get trap forwards to the +// target so the inherited accessor still receives the proxy as `this`. +var stackTrapCalls = 0; +var pB = new Proxy(new Error('inner'), { + get: function (t, key, receiver) { + if (key === 'stack') { + stackTrapCalls += 1; + } + return Reflect.get(t, key, receiver); + }, +}); +assert.sameValue(pB.stack, undefined, 'property access on proxy: receiver is proxy, no [[ErrorData]]'); +assert.sameValue(stackTrapCalls, 1, 'get trap fired once for the property access'); + +// (c) Proxy wrapping a non-Error: still undefined. +assert.sameValue( + get.call(new Proxy({}, {})), + undefined, + 'Proxy wrapping plain object returns undefined' +);
diff --git a/test/built-ins/Error/prototype/stack/getter-subclass.js b/test/built-ins/Error/prototype/stack/getter-subclass.js new file mode 100644 index 0000000..faf184d --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-subclass.js
@@ -0,0 +1,38 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns a String when called on a subclass of Error or any + NativeError, since subclasses inherit the [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. +features: [error-stack-accessor, class] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var Sub = class extends Ctor {}; + var e = new Sub('msg'); + + assert.sameValue(typeof get.call(e), 'string', Ctor.name + ': subclass via get.call'); + assert.sameValue(typeof e.stack, 'string', Ctor.name + ': subclass via property access'); +}
diff --git a/test/built-ins/Error/prototype/stack/getter-suppressed-error-prototype.js b/test/built-ins/Error/prototype/stack/getter-suppressed-error-prototype.js new file mode 100644 index 0000000..d5c3f68 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-suppressed-error-prototype.js
@@ -0,0 +1,28 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns undefined when called on SuppressedError.prototype, which + is an ordinary object that does not have an [[ErrorData]] internal slot. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. + + Properties of the SuppressedError Prototype Object + + The SuppressedError prototype object: + [...] + is not an Error instance or a SuppressedError instance and does not have + an [[ErrorData]] internal slot. +features: [error-stack-accessor, explicit-resource-management] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(get.call(SuppressedError.prototype), undefined);
diff --git a/test/built-ins/Error/prototype/stack/getter-suppressed-error.js b/test/built-ins/Error/prototype/stack/getter-suppressed-error.js new file mode 100644 index 0000000..cade260 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-suppressed-error.js
@@ -0,0 +1,27 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + The getter returns a String when called on a SuppressedError instance. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If E does not have an [[ErrorData]] internal slot, return undefined. + 4. Return an implementation-defined string that represents the stack trace of E. + + SuppressedError instances are ordinary objects that inherit properties from + their SuppressedError prototype object and have an [[ErrorData]] internal + slot whose value is undefined. +features: [error-stack-accessor, explicit-resource-management] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +var err = new SuppressedError(new Error('inner'), new Error('suppressed'), 'msg'); + +assert.sameValue(typeof get.call(err), 'string', 'SuppressedError instance via get.call'); +assert.sameValue(typeof err.stack, 'string', 'SuppressedError instance via property access');
diff --git a/test/built-ins/Error/prototype/stack/getter-this-not-object-bigint.js b/test/built-ins/Error/prototype/stack/getter-this-not-object-bigint.js new file mode 100644 index 0000000..8329f79 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-this-not-object-bigint.js
@@ -0,0 +1,20 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Throws a TypeError if the this value is a BigInt. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. +features: [error-stack-accessor, BigInt] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.throws(TypeError, function () { + get.call(0n); +});
diff --git a/test/built-ins/Error/prototype/stack/getter-this-not-object-symbol.js b/test/built-ins/Error/prototype/stack/getter-this-not-object-symbol.js new file mode 100644 index 0000000..86ccf0a --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-this-not-object-symbol.js
@@ -0,0 +1,20 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Throws a TypeError if the this value is a Symbol. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. +features: [error-stack-accessor, Symbol] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.throws(TypeError, function () { + get.call(Symbol('s')); +});
diff --git a/test/built-ins/Error/prototype/stack/getter-this-not-object.js b/test/built-ins/Error/prototype/stack/getter-this-not-object.js new file mode 100644 index 0000000..1f367ec --- /dev/null +++ b/test/built-ins/Error/prototype/stack/getter-this-not-object.js
@@ -0,0 +1,42 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype.stack +description: > + Throws a TypeError if the this value is not an Object. +info: | + get Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +assert.sameValue(typeof get, 'function'); + +assert.throws(TypeError, function () { + get.call(undefined); +}, 'undefined'); + +assert.throws(TypeError, function () { + get.call(null); +}, 'null'); + +assert.throws(TypeError, function () { + get.call(true); +}, 'true'); + +assert.throws(TypeError, function () { + get.call(false); +}, 'false'); + +assert.throws(TypeError, function () { + get.call(1); +}, 'number'); + +assert.throws(TypeError, function () { + get.call(''); +}, 'string');
diff --git a/test/built-ins/Error/prototype/stack/instance-no-own-stack.js b/test/built-ins/Error/prototype/stack/instance-no-own-stack.js new file mode 100644 index 0000000..ae27ce6 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/instance-no-own-stack.js
@@ -0,0 +1,62 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-error-instances +description: > + A freshly-constructed Error instance has no own "stack" property; the + property is reached via the inherited accessor on Error.prototype. +info: | + Properties of Error Instances + + Error instances are ordinary objects that inherit properties from the Error + prototype object and have an [[ErrorData]] internal slot whose value is + undefined. + + Error.prototype.stack is an accessor property with attributes + { [[Enumerable]]: false, [[Configurable]]: true }. +features: [error-stack-accessor] +---*/ + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + Ctor.name + ': hasOwnProperty("stack") is false' + ); + + assert.sameValue( + Object.getOwnPropertyDescriptor(err, 'stack'), + undefined, + Ctor.name + ': getOwnPropertyDescriptor returns undefined' + ); + + // For NativeErrors, the immediate prototype is e.g. TypeError.prototype, + // which does NOT have its own "stack" property; the accessor lives only on + // Error.prototype, two links up. + if (Ctor !== Error) { + assert.sameValue( + Object.getOwnPropertyDescriptor(Object.getPrototypeOf(err), 'stack'), + undefined, + Ctor.name + ': stack is not an own property of NativeError.prototype' + ); + } + + var desc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack'); + assert.notSameValue(desc, undefined, Ctor.name + ': Error.prototype has the accessor'); + assert.sameValue(typeof desc.get, 'function', Ctor.name + ': accessor get is a function'); + assert.sameValue(typeof desc.set, 'function', Ctor.name + ': accessor set is a function'); +}
diff --git a/test/built-ins/Error/prototype/stack/instance-not-enumerable.js b/test/built-ins/Error/prototype/stack/instance-not-enumerable.js new file mode 100644 index 0000000..be7b52e --- /dev/null +++ b/test/built-ins/Error/prototype/stack/instance-not-enumerable.js
@@ -0,0 +1,84 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype-stack +description: > + "stack" is a non-enumerable accessor on Error.prototype, and Error instances + do not have an own "stack" property, so "stack" is not visible via + Object.keys, for-in, propertyIsEnumerable, or JSON.stringify. +info: | + Error.prototype.stack is an accessor property with attributes + { [[Enumerable]]: false, [[Configurable]]: true }. + + Properties of Error Instances + + Error instances are ordinary objects that inherit properties from the Error + prototype object and have an [[ErrorData]] internal slot whose value is + undefined. +features: [error-stack-accessor] +---*/ + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.sameValue( + Object.keys(err).indexOf('stack'), + -1, + Ctor.name + ': Object.keys does not include "stack"' + ); + + assert.sameValue( + Object.getOwnPropertyNames(err).indexOf('stack'), + -1, + Ctor.name + ': getOwnPropertyNames does not include "stack"' + ); + + var sawStack = false; + for (var key in err) { + if (key === 'stack') { + sawStack = true; + } + } + assert.sameValue(sawStack, false, Ctor.name + ': for-in does not yield "stack"'); + + assert.sameValue( + Object.prototype.propertyIsEnumerable.call(err, 'stack'), + false, + Ctor.name + ': propertyIsEnumerable returns false' + ); + + assert.sameValue( + Object.prototype.propertyIsEnumerable.call(Error.prototype, 'stack'), + false, + Ctor.name + ': Error.prototype.propertyIsEnumerable("stack") is false' + ); + + // JSON.stringify omits non-enumerable / non-own properties; a fresh Error + // instance has no enumerable own properties at all. + assert.sameValue( + JSON.stringify(err), + '{}', + Ctor.name + ': JSON.stringify output is empty object' + ); + + // Object.assign reads only enumerable own properties; the inherited + // accessor is not own, so "stack" is not copied. + var copy = Object.assign({}, err); + assert.sameValue( + Object.prototype.hasOwnProperty.call(copy, 'stack'), + false, + Ctor.name + ': Object.assign({}, err) does not copy "stack"' + ); +}
diff --git a/test/built-ins/Error/prototype/stack/prop-desc.js b/test/built-ins/Error/prototype/stack/prop-desc.js new file mode 100644 index 0000000..fa66bdc --- /dev/null +++ b/test/built-ins/Error/prototype/stack/prop-desc.js
@@ -0,0 +1,21 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-error.prototype-stack +description: > + Property descriptor of Error.prototype.stack +info: | + Error.prototype.stack is an accessor property with attributes + { [[Enumerable]]: false, [[Configurable]]: true }. +features: [error-stack-accessor] +---*/ + +var desc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack'); + +assert.sameValue(typeof desc.get, 'function', 'typeof desc.get'); +assert.sameValue(typeof desc.set, 'function', 'typeof desc.set'); +assert.sameValue(desc.value, undefined, 'desc.value'); +assert.sameValue(desc.writable, undefined, 'desc.writable'); +assert.sameValue(desc.enumerable, false, 'desc.enumerable'); +assert.sameValue(desc.configurable, true, 'desc.configurable');
diff --git a/test/built-ins/Error/prototype/stack/setter-aggregate-error.js b/test/built-ins/Error/prototype/stack/setter-aggregate-error.js new file mode 100644 index 0000000..c651642 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-aggregate-error.js
@@ -0,0 +1,38 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The setter installs an own "stack" data property on AggregateError instances. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. +includes: [propertyHelper.js] +features: [error-stack-accessor, AggregateError] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var err = new AggregateError([new Error('inner')], 'outer'); + +assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + 'precondition: AggregateError instance has no own "stack" property at construction' +); + +var result = set.call(err, 'sentinel'); +assert.sameValue(result, undefined, 'setter returns undefined'); + +verifyProperty(err, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-creates-own-property.js b/test/built-ins/Error/prototype/stack/setter-creates-own-property.js new file mode 100644 index 0000000..6171fcf --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-creates-own-property.js
@@ -0,0 +1,71 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When the receiver does not have an own "stack" property, the setter creates + one as a writable, enumerable, configurable data property whose value is v. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + [...] +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + Ctor.name + ': precondition: instance has no own "stack" property at construction' + ); + + var result = set.call(err, 'sentinel-' + Ctor.name); + assert.sameValue(result, undefined, Ctor.name + ': setter returns undefined'); + + verifyProperty(err, 'stack', { + value: 'sentinel-' + Ctor.name, + writable: true, + enumerable: true, + configurable: true, + }); +} + +// The same applies to a plain object that lacks [[ErrorData]]: the setter +// does not check for the internal slot before installing the property. +var plain = {}; +set.call(plain, 'on-plain'); +verifyProperty(plain, 'stack', { + value: 'on-plain', + writable: true, + enumerable: true, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-cross-realm.js b/test/built-ins/Error/prototype/stack/setter-cross-realm.js new file mode 100644 index 0000000..d699a76 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-cross-realm.js
@@ -0,0 +1,90 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The setter's home-object (SameValue) check is realm-scoped: a setter from + realm A locks against realm A's %Error.prototype% only. Cross-realm objects + are accepted as receivers (they pass the SameValue check), and an Error + instance from another realm gets an own "stack" data property installed. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 2. If SameValue(this, home) is true, then + a. Throw a TypeError exception. + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). +includes: [propertyHelper.js] +features: [error-stack-accessor, cross-realm] +---*/ + +var setA = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var realmB = $262.createRealm().global; + +assert.notSameValue( + Error.prototype, + realmB.Error.prototype, + 'precondition: the two realms have distinct Error.prototype objects' +); + +// (a) Realm A's setter on a realm B Error instance: passes SameValue (the +// instance is not realm A's Error.prototype), and the instance has no own +// "stack" property at construction, so CreateDataPropertyOrThrow installs one. +var errB = new realmB.Error('msg'); +assert.sameValue( + Object.prototype.hasOwnProperty.call(errB, 'stack'), + false, + 'precondition: cross-realm Error instance has no own "stack" property' +); + +setA.call(errB, 'sentinel'); + +verifyProperty(errB, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, +}); + +// (b) Realm A's setter on a plain object from realm B: same path, installs an +// own data property. +var plainB = new realmB.Object(); +setA.call(plainB, 'plain'); + +verifyProperty(plainB, 'stack', { + value: 'plain', + writable: true, + enumerable: true, + configurable: true, +}); + +// (c) Realm A's setter is structurally distinct from realm B's setter. +var setB = Object.getOwnPropertyDescriptor(realmB.Error.prototype, 'stack').set; +assert.sameValue(typeof setB, 'function', 'realm B has its own setter'); +assert.notSameValue(setA, setB, 'the two realms have distinct setter functions'); + +// (d) Realm A's setter on realm B's Error.prototype passes realm A's +// SameValue check (the two prototypes are distinct objects), so step 2 of +// SetterThatIgnoresPrototypeProperties does not fire. Step 3 finds realm B's +// own "stack" accessor descriptor and proceeds to step 5: Set(O, p, v, true) +// invokes that accessor's setter, which is realm B's setter. Realm B's setter +// then throws via its own SameValue check (this === realm B's Error.prototype). +// The thrown TypeError is realm B's TypeError. +assert.throws(realmB.TypeError, function () { + setA.call(realmB.Error.prototype, 'should-throw'); +}, 'realm A setter on realm B Error.prototype throws (via realm B setter)'); + +// (e) Realm A's setter on realm A's own Error.prototype throws via step 2 +// (SameValue with realm A's home). +assert.throws(TypeError, function () { + setA.call(Error.prototype, 'should-throw'); +}, 'realm A setter on realm A Error.prototype throws via SameValue');
diff --git a/test/built-ins/Error/prototype/stack/setter-delete-round-trip.js b/test/built-ins/Error/prototype/stack/setter-delete-round-trip.js new file mode 100644 index 0000000..2a13da7 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-delete-round-trip.js
@@ -0,0 +1,74 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + After the setter installs an own "stack" data property, deleting it + re-exposes the inherited accessor on Error.prototype. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + // delete on a fresh instance: nothing to remove, returns true. + assert.sameValue(delete err.stack, true, Ctor.name + ': delete on fresh instance returns true'); + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + Ctor.name + ': still no own property after delete' + ); + + // The inherited accessor still produces a string. + assert.sameValue(typeof get.call(err), 'string', Ctor.name + ': accessor still works'); + + // After setting, an own data property is installed. + set.call(err, 'sentinel'); + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + true, + Ctor.name + ': own property installed after set' + ); + assert.sameValue(err.stack, 'sentinel', Ctor.name + ': data property shadows accessor'); + + // delete removes the own data property. + assert.sameValue(delete err.stack, true, Ctor.name + ': delete removes own data property'); + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + Ctor.name + ': own property removed' + ); + + // The inherited accessor is exposed again, and [[ErrorData]] still drives it. + assert.sameValue(typeof err.stack, 'string', Ctor.name + ': inherited accessor re-exposed'); + assert.sameValue(typeof get.call(err), 'string', Ctor.name + ': accessor still returns a string'); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-empty-string.js b/test/built-ins/Error/prototype/stack/setter-empty-string.js new file mode 100644 index 0000000..92e12c7 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-empty-string.js
@@ -0,0 +1,47 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + An empty string is a String, so the setter accepts it. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + set.call(err, ''); + + // Verify the round-trip via property access before invoking verifyProperty + // (which deletes the configurable own property as part of its check). + assert.sameValue(err.stack, '', Ctor.name + ': empty string round-trips through property access'); + + verifyProperty(err, 'stack', { + value: '', + writable: true, + enumerable: true, + configurable: true, + }); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-existing-own-property.js b/test/built-ins/Error/prototype/stack/setter-existing-own-property.js new file mode 100644 index 0000000..a20846c --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-existing-own-property.js
@@ -0,0 +1,62 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When the receiver already has an own "stack" data property, the setter + performs a [[Set]] on it (preserving its current attributes). +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + 5. Else, + a. Perform ? Set(this, p, v, true). + [...] +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + Object.defineProperty(err, 'stack', { + value: 'original', + writable: true, + enumerable: false, + configurable: false, + }); + + set.call(err, 'updated'); + + // The existing descriptor is preserved; only the value changes (per [[Set]]). + verifyProperty(err, 'stack', { + value: 'updated', + writable: true, + enumerable: false, + configurable: false, + }); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-length.js b/test/built-ins/Error/prototype/stack/setter-length.js new file mode 100644 index 0000000..d85ffa8 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-length.js
@@ -0,0 +1,31 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + set Error.prototype.stack.length is 1. +info: | + set Error.prototype.stack + + 17 ECMAScript Standard Built-in Objects: + Every built-in function object, including constructors, has a length + property whose value is an integer. Unless otherwise specified, this + value is equal to the largest number of named arguments shown in the + subclause headings for the function description. + + Unless otherwise specified, the length property of a built-in function + object has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +verifyProperty(set, 'length', { + value: 1, + writable: false, + enumerable: false, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-name.js b/test/built-ins/Error/prototype/stack/setter-name.js new file mode 100644 index 0000000..2a17203 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-name.js
@@ -0,0 +1,30 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + set Error.prototype.stack.name is "set stack". +info: | + set Error.prototype.stack + + 17 ECMAScript Standard Built-in Objects + + Functions that are specified as get or set accessor functions of built-in + properties have "get " or "set " prepended to the property name string. + + Unless otherwise specified, the name property of a built-in function object, + if it exists, has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +verifyProperty(set, 'name', { + value: 'set stack', + writable: false, + enumerable: false, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-no-argument.js b/test/built-ins/Error/prototype/stack/setter-no-argument.js new file mode 100644 index 0000000..91865ad --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-no-argument.js
@@ -0,0 +1,37 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When the setter is called with no argument, v is undefined, which is not a + String, so a TypeError is thrown. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.throws(TypeError, function () { + set.call(err); + }, Ctor.name); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-non-error-receiver.js b/test/built-ins/Error/prototype/stack/setter-non-error-receiver.js new file mode 100644 index 0000000..221628e --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-non-error-receiver.js
@@ -0,0 +1,61 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The setter does not check for the [[ErrorData]] internal slot, so it works + on any extensible object that is not %Error.prototype%. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +// Plain object: installs an own data property. +var plain = {}; +set.call(plain, 'plain'); +verifyProperty(plain, 'stack', { + value: 'plain', + writable: true, + enumerable: true, + configurable: true, +}); + +// Array: installs an own data property. +var arr = []; +set.call(arr, 'array'); +verifyProperty(arr, 'stack', { + value: 'array', + writable: true, + enumerable: true, + configurable: true, +}); + +// Function: installs an own data property. +var fn = function () {}; +set.call(fn, 'function'); +verifyProperty(fn, 'stack', { + value: 'function', + writable: true, + enumerable: true, + configurable: true, +}); + +// Object whose prototype is Error.prototype but lacks [[ErrorData]]: still works. +var fakeError = Object.create(Error.prototype); +set.call(fakeError, 'fake'); +verifyProperty(fakeError, 'stack', { + value: 'fake', + writable: true, + enumerable: true, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-non-extensible-receiver.js b/test/built-ins/Error/prototype/stack/setter-non-extensible-receiver.js new file mode 100644 index 0000000..bf99d63 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-non-extensible-receiver.js
@@ -0,0 +1,85 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When the receiver lacks an own "stack" property and has any integrity level + applied (preventExtensions, seal, freeze), the setter throws a TypeError + because CreateDataPropertyOrThrow cannot add a property to a non-extensible + object. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + + CreateDataPropertyOrThrow ( O, P, V ) + + [...] + 3. Let success be ? CreateDataProperty(O, P, V). + 4. If success is false, throw a TypeError exception. +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +var integrityLevels = [ + { name: 'preventExtensions', fn: Object.preventExtensions }, + { name: 'seal', fn: Object.seal }, + { name: 'freeze', fn: Object.freeze } +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + + for (var j = 0; j < integrityLevels.length; ++j) { + var level = integrityLevels[j]; + var label = Ctor.name + '/' + level.name; + + var err = new Ctor('msg'); + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + label + ': precondition: instance has no own "stack" property at construction' + ); + + level.fn(err); + + assert.throws(TypeError, function () { + set.call(err, 'sentinel'); + }, label + ': instance without own "stack" rejects setter'); + + assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + label + ': no own "stack" property was created' + ); + } +} + +// Same behavior on plain objects at each integrity level. +for (var k = 0; k < integrityLevels.length; ++k) { + var lvl = integrityLevels[k]; + var obj = lvl.fn({}); + assert.throws(TypeError, function () { + set.call(obj, 'sentinel'); + }, lvl.name + ' plain object without own "stack"'); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-non-string-value-bigint.js b/test/built-ins/Error/prototype/stack/setter-non-string-value-bigint.js new file mode 100644 index 0000000..3366661 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-non-string-value-bigint.js
@@ -0,0 +1,36 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the value being assigned is a BigInt. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. +features: [error-stack-accessor, BigInt] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.throws(TypeError, function () { + set.call(err, 0n); + }, Ctor.name); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-non-string-value-symbol.js b/test/built-ins/Error/prototype/stack/setter-non-string-value-symbol.js new file mode 100644 index 0000000..dd01797 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-non-string-value-symbol.js
@@ -0,0 +1,36 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the value being assigned is a Symbol. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. +features: [error-stack-accessor, Symbol] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.throws(TypeError, function () { + set.call(err, Symbol('s')); + }, Ctor.name); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-non-string-value.js b/test/built-ins/Error/prototype/stack/setter-non-string-value.js new file mode 100644 index 0000000..2046a7a --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-non-string-value.js
@@ -0,0 +1,75 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the value being assigned is not a String. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + assert.throws(TypeError, function () { + set.call(err, undefined); + }, Ctor.name + ': undefined'); + + assert.throws(TypeError, function () { + set.call(err, null); + }, Ctor.name + ': null'); + + assert.throws(TypeError, function () { + set.call(err, true); + }, Ctor.name + ': true'); + + assert.throws(TypeError, function () { + set.call(err, false); + }, Ctor.name + ': false'); + + assert.throws(TypeError, function () { + set.call(err, 1); + }, Ctor.name + ': number'); + + assert.throws(TypeError, function () { + set.call(err, {}); + }, Ctor.name + ': object'); + + assert.throws(TypeError, function () { + set.call(err, []); + }, Ctor.name + ': array'); + + // An object with a toString method is not coerced; the algorithm requires v + // to already be a String. + var coercible = { + toString: function () { return 'coerced'; }, + valueOf: function () { return 'coerced'; }, + }; + assert.throws(TypeError, function () { + set.call(err, coercible); + }, Ctor.name + ': object with toString'); + + // Boxed strings are still objects, not Strings. + assert.throws(TypeError, function () { + set.call(err, new String('boxed')); + }, Ctor.name + ': String wrapper object'); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-non-writable-stack.js b/test/built-ins/Error/prototype/stack/setter-non-writable-stack.js new file mode 100644 index 0000000..0f46930 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-non-writable-stack.js
@@ -0,0 +1,80 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When the receiver has an own non-writable "stack" data property, the setter + throws a TypeError because the underlying [[Set]] is invoked with Throw=true. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + [...] + 5. Else, + a. Perform ? Set(this, p, v, true). +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + + var err = new Ctor('msg'); + Object.defineProperty(err, 'stack', { + value: 'original', + writable: false, + enumerable: false, + configurable: true, + }); + + assert.throws(TypeError, function () { + set.call(err, 'updated'); + }, Ctor.name + ': non-writable own "stack"'); + + verifyProperty(err, 'stack', { + value: 'original', + writable: false, + enumerable: false, + configurable: true, + }); + + // Frozen instance with an own "stack" data property: still throws. + var frozen = new Ctor('msg'); + Object.defineProperty(frozen, 'stack', { + value: 'frozen-original', + writable: false, + enumerable: false, + configurable: false, + }); + Object.preventExtensions(frozen); + + assert.throws(TypeError, function () { + set.call(frozen, 'updated'); + }, Ctor.name + ': frozen with non-writable own "stack"'); + + verifyProperty(frozen, 'stack', { + value: 'frozen-original', + writable: false, + enumerable: false, + configurable: false, + }); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-not-a-constructor.js b/test/built-ins/Error/prototype/stack/setter-not-a-constructor.js new file mode 100644 index 0000000..4229fb2 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-not-a-constructor.js
@@ -0,0 +1,28 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + set Error.prototype.stack does not implement [[Construct]], is not new-able +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. +includes: [isConstructor.js] +features: [error-stack-accessor, Reflect.construct] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.sameValue( + isConstructor(set), + false, + 'isConstructor(set Error.prototype.stack) must return false' +); + +assert.throws(TypeError, function () { + new set(''); +});
diff --git a/test/built-ins/Error/prototype/stack/setter-null-proto-receiver.js b/test/built-ins/Error/prototype/stack/setter-null-proto-receiver.js new file mode 100644 index 0000000..c34191b --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-null-proto-receiver.js
@@ -0,0 +1,31 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The setter installs an own "stack" data property on a null-prototype object. + The setter does not depend on inherited machinery from Object.prototype. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. +includes: [propertyHelper.js] +features: [error-stack-accessor, __proto__] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nullProto = { __proto__: null }; +set.call(nullProto, 'null-proto'); + +verifyProperty(nullProto, 'stack', { + value: 'null-proto', + writable: true, + enumerable: true, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-own-accessor.js b/test/built-ins/Error/prototype/stack/setter-own-accessor.js new file mode 100644 index 0000000..5d7dc6f --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-own-accessor.js
@@ -0,0 +1,65 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When the receiver has an own "stack" accessor property, the setter performs + [[Set]] which invokes the own setter (or throws if the accessor has no + setter). +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + [...] + 5. Else, + a. Perform ? Set(this, p, v, true). +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + + // (a) Own accessor with a setter: the own setter receives v. + var observed; + var withSetter = new Ctor('msg'); + Object.defineProperty(withSetter, 'stack', { + get: function () { return observed; }, + set: function (v) { observed = v; }, + enumerable: false, + configurable: true, + }); + + set.call(withSetter, 'sentinel'); + assert.sameValue(observed, 'sentinel', Ctor.name + ': own setter received the value'); + + // (b) Own accessor with no setter: Set with Throw=true throws TypeError. + var withoutSetter = new Ctor('msg'); + Object.defineProperty(withoutSetter, 'stack', { + get: function () { return 'getter-only'; }, + enumerable: false, + configurable: true, + }); + + assert.throws(TypeError, function () { + set.call(withoutSetter, 'sentinel'); + }, Ctor.name + ': own accessor without a setter'); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-proxy-receiver.js b/test/built-ins/Error/prototype/stack/setter-proxy-receiver.js new file mode 100644 index 0000000..5c7ab4f --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-proxy-receiver.js
@@ -0,0 +1,98 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The setter calls [[GetOwnProperty]] and either [[DefineOwnProperty]] (via + CreateDataPropertyOrThrow) or [[Set]] on the receiver, observably invoking + Proxy traps. +info: | + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + 5. Else, + a. Perform ? Set(this, p, v, true). +features: [error-stack-accessor, Proxy, Reflect] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +// (a) Proxy with no own "stack": getOwnPropertyDescriptor then defineProperty. +var trapLog = []; +var target1 = {}; +var p1 = new Proxy(target1, { + getOwnPropertyDescriptor: function (t, key) { + trapLog.push(['gOPD', key]); + return Object.getOwnPropertyDescriptor(t, key); + }, + defineProperty: function (t, key, desc) { + trapLog.push(['define', key, desc]); + return Reflect.defineProperty(t, key, desc); + }, + set: function () { + trapLog.push(['set']); + throw new Test262Error('set trap should not be invoked when no own property exists'); + }, +}); + +set.call(p1, 'sentinel'); +assert(trapLog.length >= 2, 'at least getOwnPropertyDescriptor and defineProperty were called'); +assert.sameValue(trapLog[0][0], 'gOPD', 'first trap is getOwnPropertyDescriptor'); +assert.sameValue(trapLog[0][1], 'stack', 'getOwnPropertyDescriptor was called for "stack"'); + +var lastDefine = null; +for (var i = 0; i < trapLog.length; ++i) { + if (trapLog[i][0] === 'define') { + lastDefine = trapLog[i]; + } +} +assert.notSameValue(lastDefine, null, 'defineProperty trap was invoked'); +assert.sameValue(lastDefine[1], 'stack', 'defineProperty was called for "stack"'); + +// CreateDataProperty constructs the descriptor record +// { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }; +// FromPropertyDescriptor converts that into a plain Object with exactly those +// four own properties before invoking the defineProperty trap. +var passedDesc = lastDefine[2]; +assert.sameValue(passedDesc.value, 'sentinel', 'descriptor.value'); +assert.sameValue(passedDesc.writable, true, 'descriptor.writable'); +assert.sameValue(passedDesc.enumerable, true, 'descriptor.enumerable'); +assert.sameValue(passedDesc.configurable, true, 'descriptor.configurable'); +assert.sameValue('get' in passedDesc, false, 'descriptor has no get key'); +assert.sameValue('set' in passedDesc, false, 'descriptor has no set key'); +assert.sameValue(target1.stack, 'sentinel', 'value reached the underlying target'); + +// (b) Proxy with own "stack": getOwnPropertyDescriptor then set trap. +var trapLog2 = []; +var target2 = { stack: 'old' }; +var p2 = new Proxy(target2, { + getOwnPropertyDescriptor: function (t, key) { + trapLog2.push(['gOPD', key]); + return Object.getOwnPropertyDescriptor(t, key); + }, + set: function (t, key, value) { + trapLog2.push(['set', key, value]); + t[key] = value; + return true; + }, + defineProperty: function () { + trapLog2.push(['define']); + throw new Test262Error('defineProperty trap should not be invoked when own property exists'); + }, +}); + +set.call(p2, 'updated'); +var sawSet = false; +for (var j = 0; j < trapLog2.length; ++j) { + if (trapLog2[j][0] === 'set') { + sawSet = true; + assert.sameValue(trapLog2[j][1], 'stack', 'set was called for "stack"'); + assert.sameValue(trapLog2[j][2], 'updated', 'set received the value'); + } +} +assert.sameValue(sawSet, true, 'set trap was invoked'); +assert.sameValue(target2.stack, 'updated', 'value reached the underlying target');
diff --git a/test/built-ins/Error/prototype/stack/setter-proxy-trap-rejects.js b/test/built-ins/Error/prototype/stack/setter-proxy-trap-rejects.js new file mode 100644 index 0000000..8b35620 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-proxy-trap-rejects.js
@@ -0,0 +1,59 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + When a Proxy receiver's defineProperty trap returns false during the + CreateDataPropertyOrThrow path, or its set trap returns false during the + Set-with-Throw=true path, the setter throws TypeError. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + 5. Else, + a. Perform ? Set(this, p, v, true). + + CreateDataPropertyOrThrow ( O, P, V ) + + [...] + 3. Let success be ? CreateDataProperty(O, P, V). + 4. If success is false, throw a TypeError exception. + + Set ( O, P, V, Throw ) + + [...] + 3. Let success be ? O.[[Set]](P, V, O). + 4. If success is false and Throw is true, throw a TypeError exception. +features: [error-stack-accessor, Proxy] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +// (a) defineProperty trap returns false (no own stack: CreateDataPropertyOrThrow path). +var pA = new Proxy({}, { + defineProperty: function () { + return false; + }, +}); +assert.throws(TypeError, function () { + set.call(pA, 'v'); +}, 'defineProperty returns false'); + +// (b) set trap returns false (own stack present: Set with Throw=true path). +var pB = new Proxy({ stack: 'old' }, { + set: function () { + return false; + }, +}); +assert.throws(TypeError, function () { + set.call(pB, 'v'); +}, 'set trap returns false');
diff --git a/test/built-ins/Error/prototype/stack/setter-proxy-trap-throws.js b/test/built-ins/Error/prototype/stack/setter-proxy-trap-throws.js new file mode 100644 index 0000000..89bafda --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-proxy-trap-throws.js
@@ -0,0 +1,58 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Errors thrown by Proxy traps invoked during the setter propagate out via + the ? abstract operations in SetterThatIgnoresPrototypeProperties. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + 5. Else, + a. Perform ? Set(this, p, v, true). +features: [error-stack-accessor, Proxy] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +function Sentinel() {} + +// (a) getOwnPropertyDescriptor trap throws: error propagates from step 3. +var pA = new Proxy({}, { + getOwnPropertyDescriptor: function () { + throw new Sentinel(); + }, +}); +assert.throws(Sentinel, function () { + set.call(pA, 'v'); +}, 'getOwnPropertyDescriptor trap throw'); + +// (b) defineProperty trap throws (no own stack: CreateDataPropertyOrThrow path). +var pB = new Proxy({}, { + defineProperty: function () { + throw new Sentinel(); + }, +}); +assert.throws(Sentinel, function () { + set.call(pB, 'v'); +}, 'defineProperty trap throw'); + +// (c) set trap throws (own stack present: Set path). +var pC = new Proxy({ stack: 'old' }, { + set: function () { + throw new Sentinel(); + }, +}); +assert.throws(Sentinel, function () { + set.call(pC, 'v'); +}, 'set trap throw');
diff --git a/test/built-ins/Error/prototype/stack/setter-proxy-wrapping-prototype.js b/test/built-ins/Error/prototype/stack/setter-proxy-wrapping-prototype.js new file mode 100644 index 0000000..d528fea --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-proxy-wrapping-prototype.js
@@ -0,0 +1,65 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The home-object check uses SameValue on object identity. A Proxy wrapping + %Error.prototype% is not the same object as %Error.prototype% itself, so + step 2 of SetterThatIgnoresPrototypeProperties does not fire; the algorithm + proceeds to consult the proxy's [[GetOwnProperty]] / [[Set]] traps. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 2. If SameValue(this, home) is true, then + a. Throw a TypeError exception. + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). + 5. Else, + a. Perform ? Set(this, p, v, true). +features: [error-stack-accessor, Proxy] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var trapLog = []; +var p = new Proxy(Error.prototype, { + getOwnPropertyDescriptor: function (t, key) { + trapLog.push(['gOPD', key]); + return Object.getOwnPropertyDescriptor(t, key); + }, + set: function (t, key, value) { + trapLog.push(['set', key, value]); + // Don't actually mutate Error.prototype; just acknowledge. + return true; + }, +}); + +// SameValue(p, Error.prototype) is false: a Proxy is a distinct object from +// its target. Step 2 does not throw; the algorithm proceeds to query the +// proxy's traps. +set.call(p, 'sentinel'); + +assert(trapLog.length >= 1, 'at least one trap was invoked'); +assert.sameValue(trapLog[0][0], 'gOPD', 'getOwnPropertyDescriptor trap fired first'); +assert.sameValue(trapLog[0][1], 'stack', 'getOwnPropertyDescriptor trap was called for "stack"'); + +// Error.prototype's own "stack" descriptor is an accessor, so step 5 of +// SetterThatIgnoresPrototypeProperties takes the Set path, which invokes the +// proxy's set trap. +var sawSet = false; +for (var i = 0; i < trapLog.length; ++i) { + if (trapLog[i][0] === 'set') { + sawSet = true; + assert.sameValue(trapLog[i][1], 'stack', 'set trap was called for "stack"'); + assert.sameValue(trapLog[i][2], 'sentinel', 'set trap received the value'); + } +} +assert.sameValue(sawSet, true, 'set trap was invoked');
diff --git a/test/built-ins/Error/prototype/stack/setter-receiver-is-aggregate-error-prototype.js b/test/built-ins/Error/prototype/stack/setter-receiver-is-aggregate-error-prototype.js new file mode 100644 index 0000000..d0d5efe --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-receiver-is-aggregate-error-prototype.js
@@ -0,0 +1,40 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Calling the setter with AggregateError.prototype as the receiver does NOT + throw via the home-object check (which is locked to %Error.prototype%); it + installs an own data property on AggregateError.prototype. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). +includes: [propertyHelper.js] +features: [error-stack-accessor, AggregateError] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.sameValue( + Object.getOwnPropertyDescriptor(AggregateError.prototype, 'stack'), + undefined, + 'precondition: AggregateError.prototype has no own "stack"' +); + +set.call(AggregateError.prototype, 'sentinel'); + +verifyProperty(AggregateError.prototype, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, +}); + +assert.sameValue( + Object.getOwnPropertyDescriptor(AggregateError.prototype, 'stack'), + undefined, + 'cleanup' +);
diff --git a/test/built-ins/Error/prototype/stack/setter-receiver-is-other-prototype.js b/test/built-ins/Error/prototype/stack/setter-receiver-is-other-prototype.js new file mode 100644 index 0000000..13cd021 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-receiver-is-other-prototype.js
@@ -0,0 +1,70 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The SameValue check in SetterThatIgnoresPrototypeProperties is locked to + %Error.prototype% only. Calling the setter with any other prototype object + (NativeError prototypes, AggregateError.prototype) as the receiver does NOT + throw via the home-object check; it installs an own data property on that + prototype. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + [...] + 2. If SameValue(this, home) is true, then + a. NOTE: Throwing here emulates assignment to a non-writable data property + on the home object in strict mode code. + b. Throw a TypeError exception. + 3. Let desc be ? this.[[GetOwnProperty]](p). + 4. If desc is undefined, then + a. Perform ? CreateDataPropertyOrThrow(this, p, v). +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var nativeErrors = [ + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var proto = Ctor.prototype; + + assert.sameValue( + Object.getOwnPropertyDescriptor(proto, 'stack'), + undefined, + Ctor.name + '.prototype: precondition: no own "stack" property' + ); + + set.call(proto, 'sentinel-' + Ctor.name); + + verifyProperty(proto, 'stack', { + value: 'sentinel-' + Ctor.name, + writable: true, + enumerable: true, + configurable: true, + }); + + // verifyProperty above asserts configurable: true, which causes the helper + // to delete the property as part of its check; confirm the mutation didn't + // leak. + assert.sameValue( + Object.getOwnPropertyDescriptor(proto, 'stack'), + undefined, + Ctor.name + '.prototype: cleanup' + ); +}
diff --git a/test/built-ins/Error/prototype/stack/setter-receiver-is-prototype.js b/test/built-ins/Error/prototype/stack/setter-receiver-is-prototype.js new file mode 100644 index 0000000..6dc1736 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-receiver-is-prototype.js
@@ -0,0 +1,48 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the receiver is %Error.prototype% itself. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. + + SetterThatIgnoresPrototypeProperties ( this, home, p, v ) + + 1. If this is not an Object, throw a TypeError exception. + 2. If SameValue(this, home) is true, then + a. NOTE: Throwing here emulates assignment to a non-writable data property + on the home object in strict mode code. + b. Throw a TypeError exception. + [...] +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.throws(TypeError, function () { + set.call(Error.prototype, ''); +}, 'set.call(Error.prototype, "")'); + +// Property assignment also throws. The setter throws unconditionally via +// SetterThatIgnoresPrototypeProperties; the throw originates inside the +// accessor function and propagates regardless of the caller's strict-mode +// flag. (test262 runs this file in both strict and sloppy modes by default.) +assert.throws(TypeError, function () { + Error.prototype.stack = ''; +}, 'assignment to Error.prototype.stack'); + +// The accessor descriptor on Error.prototype is unchanged. +var desc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack'); +assert.notSameValue(desc, undefined, 'Error.prototype still has its own "stack" property'); +assert.sameValue(typeof desc.get, 'function', 'getter is still installed'); +assert.sameValue(typeof desc.set, 'function', 'setter is still installed'); +assert.sameValue(desc.value, undefined, 'descriptor has no value (still an accessor)'); +assert.sameValue(desc.writable, undefined, 'descriptor has no writable (still an accessor)');
diff --git a/test/built-ins/Error/prototype/stack/setter-receiver-is-suppressed-error-prototype.js b/test/built-ins/Error/prototype/stack/setter-receiver-is-suppressed-error-prototype.js new file mode 100644 index 0000000..dbb0d46 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-receiver-is-suppressed-error-prototype.js
@@ -0,0 +1,40 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Calling the setter with SuppressedError.prototype as the receiver does NOT + throw via the home-object check (which is locked to %Error.prototype%); it + installs an own data property on SuppressedError.prototype. +info: | + set Error.prototype.stack + + [...] + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). +includes: [propertyHelper.js] +features: [error-stack-accessor, explicit-resource-management] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.sameValue( + Object.getOwnPropertyDescriptor(SuppressedError.prototype, 'stack'), + undefined, + 'precondition: SuppressedError.prototype has no own "stack"' +); + +set.call(SuppressedError.prototype, 'sentinel'); + +verifyProperty(SuppressedError.prototype, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, +}); + +assert.sameValue( + Object.getOwnPropertyDescriptor(SuppressedError.prototype, 'stack'), + undefined, + 'cleanup' +);
diff --git a/test/built-ins/Error/prototype/stack/setter-suppressed-error.js b/test/built-ins/Error/prototype/stack/setter-suppressed-error.js new file mode 100644 index 0000000..0d89411 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-suppressed-error.js
@@ -0,0 +1,38 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + The setter installs an own "stack" data property on a SuppressedError instance. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. +includes: [propertyHelper.js] +features: [error-stack-accessor, explicit-resource-management] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +var err = new SuppressedError(new Error('inner'), new Error('suppressed'), 'msg'); + +assert.sameValue( + Object.prototype.hasOwnProperty.call(err, 'stack'), + false, + 'precondition: SuppressedError instance has no own "stack" property at construction' +); + +var result = set.call(err, 'sentinel'); +assert.sameValue(result, undefined, 'setter returns undefined'); + +verifyProperty(err, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, +});
diff --git a/test/built-ins/Error/prototype/stack/setter-this-not-object-bigint.js b/test/built-ins/Error/prototype/stack/setter-this-not-object-bigint.js new file mode 100644 index 0000000..f654e4d --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-this-not-object-bigint.js
@@ -0,0 +1,20 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the this value is a BigInt. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. +features: [error-stack-accessor, BigInt] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.throws(TypeError, function () { + set.call(0n, ''); +});
diff --git a/test/built-ins/Error/prototype/stack/setter-this-not-object-symbol.js b/test/built-ins/Error/prototype/stack/setter-this-not-object-symbol.js new file mode 100644 index 0000000..99da89d --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-this-not-object-symbol.js
@@ -0,0 +1,20 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the this value is a Symbol. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. +features: [error-stack-accessor, Symbol] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.throws(TypeError, function () { + set.call(Symbol('s'), ''); +});
diff --git a/test/built-ins/Error/prototype/stack/setter-this-not-object.js b/test/built-ins/Error/prototype/stack/setter-this-not-object.js new file mode 100644 index 0000000..c910ed1 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-this-not-object.js
@@ -0,0 +1,53 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Throws a TypeError if the this value is not an Object. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. +features: [error-stack-accessor] +---*/ + +var set = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').set; + +assert.sameValue(typeof set, 'function'); + +assert.throws(TypeError, function () { + set.call(undefined, ''); +}, 'undefined'); + +assert.throws(TypeError, function () { + set.call(null, ''); +}, 'null'); + +assert.throws(TypeError, function () { + set.call(true, ''); +}, 'true'); + +assert.throws(TypeError, function () { + set.call(false, ''); +}, 'false'); + +assert.throws(TypeError, function () { + set.call(1, ''); +}, 'number'); + +assert.throws(TypeError, function () { + set.call('s', ''); +}, 'string'); + +// A non-Object this combined with a non-String v throws TypeError. The spec +// runs step 2 (this not Object) before step 3 (v not String), but both steps +// throw TypeError, so the failure is observably the same either way. +assert.throws(TypeError, function () { + set.call(undefined, 0); +}, 'undefined this with non-String v'); + +assert.throws(TypeError, function () { + set.call(null, {}); +}, 'null this with non-String v');
diff --git a/test/built-ins/Error/prototype/stack/setter-via-assignment.js b/test/built-ins/Error/prototype/stack/setter-via-assignment.js new file mode 100644 index 0000000..be32a07 --- /dev/null +++ b/test/built-ins/Error/prototype/stack/setter-via-assignment.js
@@ -0,0 +1,79 @@ +// Copyright (C) 2026 Jordan Harband. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-set-error.prototype.stack +description: > + Property assignment (e.g. err.stack = v) goes through the inherited setter. +info: | + set Error.prototype.stack + + 1. Let E be the this value. + 2. If E is not an Object, throw a TypeError exception. + 3. If v is not a String, throw a TypeError exception. + 4. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Error.prototype%, "stack", v). + 5. Return undefined. +includes: [propertyHelper.js] +features: [error-stack-accessor] +---*/ + +var get = Object.getOwnPropertyDescriptor(Error.prototype, 'stack').get; + +// Plain object inheriting the accessor: assignment installs an own data property. +var plain = Object.create(Error.prototype); +plain.stack = 'sentinel'; + +verifyProperty(plain, 'stack', { + value: 'sentinel', + writable: true, + enumerable: true, + configurable: true, +}); + +var nativeErrors = [ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError +]; + +for (var i = 0; i < nativeErrors.length; ++i) { + var Ctor = nativeErrors[i]; + var err = new Ctor('msg'); + + // Assigning a non-string still throws TypeError (step 3 of the setter). + // The `err.stack = null` form covered the "release memory" pattern in V8/JSC + // before this proposal; it now throws. + assert.throws(TypeError, function () { + err.stack = null; + }, Ctor.name + ': null assignment'); + + assert.throws(TypeError, function () { + err.stack = 0; + }, Ctor.name + ': numeric assignment'); + + assert.throws(TypeError, function () { + err.stack = undefined; + }, Ctor.name + ': undefined assignment'); + + // The failed setter does not affect the [[ErrorData]] slot: the inherited + // accessor still produces a string (Issue 13: a failed setter must not + // clobber the slot). + assert.sameValue(typeof get.call(err), 'string', Ctor.name + ': [[ErrorData]] preserved across failed setters'); + + // Assigning a string installs/updates the own data property. + err.stack = 'updated'; + assert.sameValue(err.stack, 'updated', Ctor.name + ': string assignment is observable'); +} + +// Assigning to %Error.prototype%.stack always throws, because the setter +// function itself throws via SetterThatIgnoresPrototypeProperties; the throw +// originates inside the accessor function and propagates regardless of the +// caller's strict-mode flag. (test262 runs this file in both strict and +// sloppy modes by default, so the bare assignment exercises both.) +assert.throws(TypeError, function () { + Error.prototype.stack = 'top-level'; +});