Leave the error marker out of own property names (#1476)

This commit is contained in:
Chris Tate
2026-10-19 23:56:01 -05:00
committed by GitHub
7 changed files with 116 additions and 1 deletions
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/6401-dyn-error-own-property-names.ts"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/6402-dyn-error-own-property-names-js.js"
],
"diags": []
}
+14 -1
View File
@@ -11478,7 +11478,20 @@ ScrDyn *scr_dyn_get_own_property_names(const ScrDyn *value) {
scr_dyn_release(table);
return out;
}
if (value->kind == SCR_DYN_OBJ) return scr_dyn_obj_own_keys(value);
if (value->kind == SCR_DYN_OBJ) {
ScrDyn *keys = scr_dyn_obj_own_keys(value);
if (!scr_dyn_obj_get(value, "%error", 6)) return keys;
/* The checked-dynamic tree's error marker is not a property. */
ScrDyn *names = scr_dyn_new_arr();
scr_dyn_arr_reserve(names, keys->v.arr.len);
for (size_t i = 0; i < keys->v.arr.len; i++) {
const ScrStr *key = keys->v.arr.items[i]->v.str;
if (key->len == 6 && memcmp(key->data, "%error", 6) == 0) continue;
scr_dyn_arr_push(names, scr_dyn_retain(keys->v.arr.items[i]));
}
scr_dyn_release(keys);
return names;
}
if (value->kind == SCR_DYN_ARR || value->kind == SCR_DYN_STR || value->kind == SCR_DYN_BYTES) {
ScrDyn *keys = scr_dyn_new_arr();
size_t length = value->kind == SCR_DYN_ARR ? value->v.arr.len :
@@ -0,0 +1,44 @@
// The own property names of errors held as `any` or `unknown` match
// Node's: Object.getOwnPropertyNames, Reflect.ownKeys and
// Object.getOwnPropertyDescriptors list `stack`, `message` and the
// properties the error has, in order, and nothing else.
class PlainSubclass extends Error {}
function dyn(value: unknown): any {
return value;
}
function names(label: string, error: any): void {
console.log(
label,
"|",
Object.getOwnPropertyNames(error).join(","),
"|",
Reflect.ownKeys(error).map(String).join(","),
"|",
Object.keys(Object.getOwnPropertyDescriptors(error)).join(","),
);
}
names("error", dyn(new Error("plain")));
names("type error", dyn(new TypeError("bad thing")));
names("no message", dyn(new RangeError()));
names("cause", dyn(new Error("outer", { cause: new Error("inner") })));
names("subclass", dyn(new PlainSubclass("sub")));
const unknownError: unknown = new SyntaxError("unexpected");
names("unknown", unknownError);
const coded = dyn(new Error("with code"));
coded.code = "E_CODE";
coded.detail = 1;
names("own properties", coded);
try {
throw new URIError("thrown");
} catch (error) {
names("caught", error);
}
console.log("marker", Object.getOwnPropertyNames(dyn(new Error("x"))).some((name) => name.startsWith("%")));
@@ -0,0 +1,44 @@
// In JavaScript sources, the own property names of errors match
// Node's: Object.getOwnPropertyNames, Reflect.ownKeys and
// Object.getOwnPropertyDescriptors list `stack`, `message` and the
// properties the error has, in order, and nothing else.
class PlainSubclass extends Error {}
function dyn(value) {
return value;
}
function names(label, error) {
console.log(
label,
"|",
Object.getOwnPropertyNames(error).join(","),
"|",
Reflect.ownKeys(error).map(String).join(","),
"|",
Object.keys(Object.getOwnPropertyDescriptors(error)).join(","),
);
}
names("error", dyn(new Error("plain")));
names("type error", dyn(new TypeError("bad thing")));
names("no message", dyn(new RangeError()));
names("cause", dyn(new Error("outer", { cause: new Error("inner") })));
names("subclass", dyn(new PlainSubclass("sub")));
const unknownError = new SyntaxError("unexpected");
names("unknown", unknownError);
const coded = dyn(new Error("with code"));
coded.code = "E_CODE";
coded.detail = 1;
names("own properties", coded);
try {
throw new URIError("thrown");
} catch (error) {
names("caught", error);
}
console.log("marker", Object.getOwnPropertyNames(dyn(new Error("x"))).some((name) => name.startsWith("%")));
@@ -0,0 +1 @@
{ "plain": 3175, "san": 4362 }
@@ -0,0 +1 @@
{ "plain": 3056, "san": 4620 }