From 028b25c9fc82b18014dc95d6465a804955840f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 4 Aug 2026 14:09:03 +0200 Subject: [PATCH] Propagate exceptions from the get/set descriptor accessors Fixes: #1627 --- quickjs.c | 10 ++++++---- tests/bug1627.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 tests/bug1627.js diff --git a/quickjs.c b/quickjs.c index bbac00c33..9b76b3378 100644 --- a/quickjs.c +++ b/quickjs.c @@ -41178,8 +41178,9 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, if (present) { flags |= JS_PROP_HAS_GET; getter = JS_GetProperty(ctx, desc, JS_ATOM_get); - if (JS_IsException(getter) || - !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { + if (JS_IsException(getter)) + goto fail; + if (!(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { JS_ThrowTypeError(ctx, "Getter must be a function"); goto fail; } @@ -41190,8 +41191,9 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, if (present) { flags |= JS_PROP_HAS_SET; setter = JS_GetProperty(ctx, desc, JS_ATOM_set); - if (JS_IsException(setter) || - !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { + if (JS_IsException(setter)) + goto fail; + if (!(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { JS_ThrowTypeError(ctx, "Setter must be a function"); goto fail; } diff --git a/tests/bug1627.js b/tests/bug1627.js new file mode 100644 index 000000000..8571f5d16 --- /dev/null +++ b/tests/bug1627.js @@ -0,0 +1,33 @@ +import { assert, assertThrows } from "./assert.js"; + +// ToPropertyDescriptor must propagate an abrupt completion from Get(Obj, "get") +// or Get(Obj, "set") instead of reporting the callability check. + +assertThrows(ReferenceError, () => { + Object.create([], { x: { get get() { unresolvable; } } }); +}); + +assertThrows(ReferenceError, () => { + Object.create([], { x: { get set() { unresolvable; } } }); +}); + +for (const key of ["get", "set"]) { + const sentinel = new Error("thrown from the " + key + " accessor"); + const desc = { get [key]() { throw sentinel; } }; + let caught; + try { + Object.defineProperty({}, "p", desc); + } catch (e) { + caught = e; + } + assert(caught, sentinel, "abrupt completion of Get(Obj, \"" + key + "\")"); +} + +// A non-callable getter/setter still yields a TypeError. +assertThrows(TypeError, () => Object.defineProperty({}, "p", { get: 1 })); +assertThrows(TypeError, () => Object.defineProperty({}, "p", { set: 1 })); + +// Accessors that do return a function keep working. +const o = {}; +Object.defineProperty(o, "p", { get get() { return () => 42; } }); +assert(o.p, 42);