From 5344fac5a9399ddc5fcc46bc595b8e3130f267b5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2025 12:12:18 +0100 Subject: [PATCH 1/6] Add JS_NewObjectFrom and JS_NewObjectFromStr --- quickjs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ quickjs.h | 6 ++++++ run-test262.c | 41 +++++++++++++++++++---------------------- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/quickjs.c b/quickjs.c index 0f673f7b..41f333f6 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5038,6 +5038,53 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto) return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); } +JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, + const JSValue *values) +{ + JSValue obj; + int i; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + for (i = 0; i < count; i++) { + if (JS_SetProperty(ctx, obj, props[i], values[i]) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, + const JSValue *values) +{ + JSAtom *atoms; + JSValue ret; + int i; + + i = 0; + ret = JS_EXCEPTION; + count = max_int(0, count); + atoms = NULL; + if (count) { + atoms = js_malloc(ctx, count * sizeof(*atoms)); + if (!atoms) + return JS_EXCEPTION; + for (i = 0; i < count; i++) { + atoms[i] = JS_NewAtom(ctx, props[i]); + if (atoms[i] == JS_ATOM_NULL) + goto fail; + } + } + ret = JS_NewObjectFrom(ctx, count, atoms, values); +fail: + while (i-- > 0) + JS_FreeAtom(ctx, atoms[i]); + js_free(ctx, atoms); + return ret; +} + JSValue JS_NewArray(JSContext *ctx) { return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), diff --git a/quickjs.h b/quickjs.h index c5f3a763..b54154c7 100644 --- a/quickjs.h +++ b/quickjs.h @@ -717,6 +717,12 @@ JS_EXTERN JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, JSClassI JS_EXTERN JSValue JS_NewObjectClass(JSContext *ctx, int class_id); JS_EXTERN JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto); JS_EXTERN JSValue JS_NewObject(JSContext *ctx); +JS_EXTERN JSValue JS_NewObjectFrom(JSContext *ctx, int count, + const JSAtom *props, + const JSValue *values); +JS_EXTERN JSValue JS_NewObjectFromStr(JSContext *ctx, int count, + const char **props, + const JSValue *values); JS_EXTERN JSValue JS_ToObject(JSContext *ctx, JSValue val); JS_EXTERN JSValue JS_ToObjectString(JSContext *ctx, JSValue val); diff --git a/run-test262.c b/run-test262.c index 3b29b649..945919e5 100644 --- a/run-test262.c +++ b/run-test262.c @@ -931,36 +931,33 @@ static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val, static JSValue add_helpers1(JSContext *ctx) { JSValue global_obj; - JSValue obj262, obj; + JSValue obj262, is_html_dda; global_obj = JS_GetGlobalObject(ctx); JS_SetPropertyStr(ctx, global_obj, "print", JS_NewCFunction(ctx, js_print_262, "print", 1)); + is_html_dda = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0); + JS_SetIsHTMLDDA(ctx, is_html_dda); +#define N 7 + static const char *props[N] = { + "detachArrayBuffer", "evalScript", "codePointRange", + "agent", "global", "createRealm", "IsHTMLDDA", + }; + JSValue values[N] = { + JS_NewCFunction(ctx, js_detachArrayBuffer, "detachArrayBuffer", 1), + JS_NewCFunction(ctx, js_evalScript_262, "evalScript", 1), + JS_NewCFunction(ctx, js_string_codePointRange, "codePointRange", 2), + js_new_agent(ctx), + JS_DupValue(ctx, global_obj), + JS_NewCFunction(ctx, js_createRealm, "createRealm", 0), + is_html_dda, + }; /* $262 special object used by the tests */ - obj262 = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, obj262, "detachArrayBuffer", - JS_NewCFunction(ctx, js_detachArrayBuffer, - "detachArrayBuffer", 1)); - JS_SetPropertyStr(ctx, obj262, "evalScript", - JS_NewCFunction(ctx, js_evalScript_262, - "evalScript", 1)); - JS_SetPropertyStr(ctx, obj262, "codePointRange", - JS_NewCFunction(ctx, js_string_codePointRange, - "codePointRange", 2)); - JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx)); - - JS_SetPropertyStr(ctx, obj262, "global", - JS_DupValue(ctx, global_obj)); - JS_SetPropertyStr(ctx, obj262, "createRealm", - JS_NewCFunction(ctx, js_createRealm, - "createRealm", 0)); - obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0); - JS_SetIsHTMLDDA(ctx, obj); - JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj); - + obj262 = JS_NewObjectFromStr(ctx, N, props, values); JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262)); +#undef N JS_FreeValue(ctx, global_obj); return obj262; From 0e4314f616e9756675d323c8293bcc06bec534e2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2025 12:18:26 +0100 Subject: [PATCH 2/6] squash! use add_property directly --- quickjs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/quickjs.c b/quickjs.c index 41f333f6..84e95ecd 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5041,17 +5041,22 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto) JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, const JSValue *values) { + JSProperty *pr; + JSObject *p; JSValue obj; int i; obj = JS_NewObject(ctx); if (JS_IsException(obj)) return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(obj); for (i = 0; i < count; i++) { - if (JS_SetProperty(ctx, obj, props[i], values[i]) < 0) { + pr = add_property(ctx, p, props[i], JS_PROP_C_W_E); + if (!pr) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } + pr->u.value = values[i]; } return obj; } From f77830d2c155262a7e18adf422a3713a6db0bec1 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2025 13:08:35 +0100 Subject: [PATCH 3/6] squash! use add_shape_property directly --- quickjs.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/quickjs.c b/quickjs.c index 84e95ecd..6794fc5e 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5043,22 +5043,37 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, { JSProperty *pr; JSObject *p; + JSShape *sh; JSValue obj; int i; obj = JS_NewObject(ctx); if (JS_IsException(obj)) return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(obj); - for (i = 0; i < count; i++) { - pr = add_property(ctx, p, props[i], JS_PROP_C_W_E); - if (!pr) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + sh = p->shape; + assert(sh->is_hashed); + for (i = 0; i < count; i++) { + if (sh->header.ref_count > 1) { + sh = js_clone_shape(ctx, sh); + if (!sh) + goto fail; + sh->is_hashed = true; + js_shape_hash_link(ctx->rt, sh); + js_free_shape(ctx->rt, p->shape); + p->shape = sh; + } + if (add_shape_property(ctx, &p->shape, p, props[i], JS_PROP_C_W_E)) + goto fail; + pr = &p->prop[p->shape->prop_count-1]; + pr->u.value = values[i]; } - pr->u.value = values[i]; } return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; } JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, @@ -5070,9 +5085,8 @@ JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, i = 0; ret = JS_EXCEPTION; - count = max_int(0, count); atoms = NULL; - if (count) { + if (count > 0) { atoms = js_malloc(ctx, count * sizeof(*atoms)); if (!atoms) return JS_EXCEPTION; From 15c6aadf7dc0b283b423f522c1293d4d69a3d621 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2025 13:42:45 +0100 Subject: [PATCH 4/6] squash! resize_properties --- quickjs.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/quickjs.c b/quickjs.c index 6794fc5e..4f5c0ffc 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5042,11 +5042,13 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, const JSValue *values) { JSProperty *pr; + JSRuntime *rt; JSObject *p; JSShape *sh; JSValue obj; - int i; + int i, r; + rt = ctx->rt; obj = JS_NewObject(ctx); if (JS_IsException(obj)) return JS_EXCEPTION; @@ -5054,20 +5056,18 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, p = JS_VALUE_GET_OBJ(obj); sh = p->shape; assert(sh->is_hashed); + assert(sh->header.ref_count == 1); + js_shape_hash_unlink(rt, sh); + r = resize_properties(ctx, &sh, p, count); + js_shape_hash_link(rt, sh); + if (r) + goto fail; + p->shape = sh; + pr = p->prop; for (i = 0; i < count; i++) { - if (sh->header.ref_count > 1) { - sh = js_clone_shape(ctx, sh); - if (!sh) - goto fail; - sh->is_hashed = true; - js_shape_hash_link(ctx->rt, sh); - js_free_shape(ctx->rt, p->shape); - p->shape = sh; - } if (add_shape_property(ctx, &p->shape, p, props[i], JS_PROP_C_W_E)) goto fail; - pr = &p->prop[p->shape->prop_count-1]; - pr->u.value = values[i]; + pr[i].u.value = values[i]; } } return obj; From 8612cdeaaf0aa4dbebe44f8a1c34d800d6f7f4f4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2025 14:11:36 +0100 Subject: [PATCH 5/6] squash! configure property descriptor directly --- quickjs.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/quickjs.c b/quickjs.c index 4f5c0ffc..4fc18fd8 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5041,12 +5041,15 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto) JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, const JSValue *values) { - JSProperty *pr; + JSShapeProperty *pr; + uint32_t *hash; JSRuntime *rt; JSObject *p; JSShape *sh; JSValue obj; - int i, r; + JSAtom atom; + intptr_t h; + int i; rt = ctx->rt; obj = JS_NewObject(ctx); @@ -5058,22 +5061,29 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, assert(sh->is_hashed); assert(sh->header.ref_count == 1); js_shape_hash_unlink(rt, sh); - r = resize_properties(ctx, &sh, p, count); - js_shape_hash_link(rt, sh); - if (r) - goto fail; + if (resize_properties(ctx, &sh, p, count)) { + js_shape_hash_link(rt, sh); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } p->shape = sh; - pr = p->prop; for (i = 0; i < count; i++) { - if (add_shape_property(ctx, &p->shape, p, props[i], JS_PROP_C_W_E)) - goto fail; - pr[i].u.value = values[i]; + atom = props[i]; + pr = &sh->prop[i]; + sh->hash = shape_hash(shape_hash(sh->hash, atom), JS_PROP_C_W_E); + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + h = atom & sh->prop_hash_mask; + hash = &prop_hash_end(sh)[-h - 1]; + pr->hash_next = *hash; + *hash = i + 1; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = JS_PROP_C_W_E; + p->prop[i].u.value = values[i]; } + js_shape_hash_link(rt, sh); + sh->prop_count = count; } return obj; -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; } JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, From 79ed5b576e1956b1d449e5a24cfb286ce185f60f Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 1 Feb 2025 19:21:57 +0100 Subject: [PATCH 6/6] squash! use on-stack buffer if possible --- quickjs.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/quickjs.c b/quickjs.c index 4fc18fd8..569d40a5 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5089,28 +5089,30 @@ JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, const JSValue *values) { - JSAtom *atoms; + JSAtom atoms_s[16], *atoms = atoms_s; JSValue ret; int i; i = 0; ret = JS_EXCEPTION; - atoms = NULL; - if (count > 0) { + if (count < 1) + goto out; + if (count > (int)countof(atoms_s)) { atoms = js_malloc(ctx, count * sizeof(*atoms)); if (!atoms) return JS_EXCEPTION; - for (i = 0; i < count; i++) { - atoms[i] = JS_NewAtom(ctx, props[i]); - if (atoms[i] == JS_ATOM_NULL) - goto fail; - } + } + for (i = 0; i < count; i++) { + atoms[i] = JS_NewAtom(ctx, props[i]); + if (atoms[i] == JS_ATOM_NULL) + goto out; } ret = JS_NewObjectFrom(ctx, count, atoms, values); -fail: +out: while (i-- > 0) JS_FreeAtom(ctx, atoms[i]); - js_free(ctx, atoms); + if (atoms != atoms_s) + js_free(ctx, atoms); return ret; }