From 26581b94ced49297f1e604f05b97a075d631b5e0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 2 Feb 2025 10:40:27 +0100 Subject: [PATCH] Add JS_NewObjectFrom and JS_NewObjectFromStr (#871) Combines JS_NewObject + JS_SetProperty into a single, optimized API call. Much more efficient when creating many objects or objects with many properties. Refs: https://github.com/quickjs-ng/quickjs/discussions/868 --- quickjs.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ quickjs.h | 6 ++++ run-test262.c | 41 +++++++++++++-------------- 3 files changed, 103 insertions(+), 22 deletions(-) diff --git a/quickjs.c b/quickjs.c index 361af53e..bb2491d8 100644 --- a/quickjs.c +++ b/quickjs.c @@ -5038,6 +5038,84 @@ 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) +{ + JSShapeProperty *pr; + uint32_t *hash; + JSRuntime *rt; + JSObject *p; + JSShape *sh; + JSValue obj; + JSAtom atom; + intptr_t h; + int i; + + rt = ctx->rt; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (count > 0) { + 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); + if (resize_properties(ctx, &sh, p, count)) { + js_shape_hash_link(rt, sh); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p->shape = sh; + for (i = 0; i < count; 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; +} + +JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, + const JSValue *values) +{ + JSAtom atoms_s[16], *atoms = atoms_s; + JSValue ret; + int i; + + i = 0; + ret = JS_EXCEPTION; + 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 out; + } + ret = JS_NewObjectFrom(ctx, count, atoms, values); +out: + while (i-- > 0) + JS_FreeAtom(ctx, atoms[i]); + if (atoms != atoms_s) + 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;