Skip to content

Commit

Permalink
Add JS_NewObjectFrom and JS_NewObjectFromStr (#871)
Browse files Browse the repository at this point in the history
Combines JS_NewObject + JS_SetProperty into a single, optimized API
call. Much more efficient when creating many objects or objects with
many properties.

Refs: #868
  • Loading branch information
bnoordhuis authored Feb 2, 2025
1 parent 0b38708 commit 26581b9
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 22 deletions.
78 changes: 78 additions & 0 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
6 changes: 6 additions & 0 deletions quickjs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
41 changes: 19 additions & 22 deletions run-test262.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 26581b9

Please sign in to comment.