summaryrefslogtreecommitdiff
path: root/quickjs.c
diff options
context:
space:
mode:
authorFabrice Bellard <fabrice@bellard.org>2023-12-09 12:30:31 +0100
committerFabrice Bellard <fabrice@bellard.org>2023-12-09 12:30:31 +0100
commit58f374ef424f02bde22dbc844f2e6f6c4fb6e1a6 (patch)
treefc86f48f9282db0cc2348704c116f050be4f6bac /quickjs.c
parent20a57f90d7c3b9916424d7db55d17dc354f3a636 (diff)
downloadquickjs-58f374ef424f02bde22dbc844f2e6f6c4fb6e1a6.tar.gz
quickjs-58f374ef424f02bde22dbc844f2e6f6c4fb6e1a6.zip
reworked set property and fixed corner cases of typed array set property
Diffstat (limited to 'quickjs.c')
-rw-r--r--quickjs.c276
1 files changed, 110 insertions, 166 deletions
diff --git a/quickjs.c b/quickjs.c
index 83c8b87..31f62aa 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -8293,119 +8293,14 @@ static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
JS_FreeValue(ctx, desc->value);
}
-/* generic (and slower) version of JS_SetProperty() for
- * Reflect.set(). 'obj' must be an object. */
-static int JS_SetPropertyGeneric(JSContext *ctx,
- JSValueConst obj, JSAtom prop,
- JSValue val, JSValueConst this_obj,
- int flags)
-{
- int ret;
- JSPropertyDescriptor desc;
- JSValue obj1;
- JSObject *p;
-
- obj1 = JS_DupValue(ctx, obj);
- for(;;) {
- p = JS_VALUE_GET_OBJ(obj1);
- if (p->is_exotic) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->set_property) {
- ret = em->set_property(ctx, obj1, prop,
- val, this_obj, flags);
- JS_FreeValue(ctx, obj1);
- JS_FreeValue(ctx, val);
- return ret;
- }
- }
-
- ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
- if (ret < 0) {
- JS_FreeValue(ctx, obj1);
- JS_FreeValue(ctx, val);
- return ret;
- }
- if (ret) {
- if (desc.flags & JS_PROP_GETSET) {
- JSObject *setter;
- if (JS_IsUndefined(desc.setter))
- setter = NULL;
- else
- setter = JS_VALUE_GET_OBJ(desc.setter);
- ret = call_setter(ctx, setter, this_obj, val, flags);
- JS_FreeValue(ctx, desc.getter);
- JS_FreeValue(ctx, desc.setter);
- JS_FreeValue(ctx, obj1);
- return ret;
- } else {
- JS_FreeValue(ctx, desc.value);
- if (!(desc.flags & JS_PROP_WRITABLE)) {
- JS_FreeValue(ctx, obj1);
- goto read_only_error;
- }
- }
- break;
- }
- /* Note: at this point 'obj1' cannot be a proxy. XXX: may have
- to check recursion */
- obj1 = JS_GetPrototypeFree(ctx, obj1);
- if (JS_IsNull(obj1))
- break;
- }
- JS_FreeValue(ctx, obj1);
-
- if (!JS_IsObject(this_obj)) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");
- }
-
- p = JS_VALUE_GET_OBJ(this_obj);
-
- /* modify the property in this_obj if it already exists */
- ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
- if (ret < 0) {
- JS_FreeValue(ctx, val);
- return ret;
- }
- if (ret) {
- if (desc.flags & JS_PROP_GETSET) {
- JS_FreeValue(ctx, desc.getter);
- JS_FreeValue(ctx, desc.setter);
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
- } else {
- JS_FreeValue(ctx, desc.value);
- if (!(desc.flags & JS_PROP_WRITABLE) ||
- p->class_id == JS_CLASS_MODULE_NS) {
- read_only_error:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
- }
- }
- ret = JS_DefineProperty(ctx, this_obj, prop, val,
- JS_UNDEFINED, JS_UNDEFINED,
- JS_PROP_HAS_VALUE);
- JS_FreeValue(ctx, val);
- return ret;
- }
-
- ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
- flags |
- JS_PROP_HAS_VALUE |
- JS_PROP_HAS_ENUMERABLE |
- JS_PROP_HAS_WRITABLE |
- JS_PROP_HAS_CONFIGURABLE |
- JS_PROP_C_W_E);
- JS_FreeValue(ctx, val);
- return ret;
-}
-
/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
- the new property is not added and an error is raised. */
-int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
- JSAtom prop, JSValue val, int flags)
+ the new property is not added and an error is raised. 'this_obj' is
+ the receiver. If obj != this_obj, then obj must be an object
+ (Reflect.set case). */
+int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValue val, JSValueConst this_obj, int flags)
{
JSObject *p, *p1;
JSShapeProperty *prs;
@@ -8418,25 +8313,37 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
#endif
tag = JS_VALUE_GET_TAG(this_obj);
if (unlikely(tag != JS_TAG_OBJECT)) {
- switch(tag) {
- case JS_TAG_NULL:
- JS_FreeValue(ctx, val);
- JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
- return -1;
- case JS_TAG_UNDEFINED:
- JS_FreeValue(ctx, val);
- JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
- return -1;
- default:
- /* even on a primitive type we can have setters on the prototype */
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
p = NULL;
- p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));
+ p1 = JS_VALUE_GET_OBJ(obj);
goto prototype_lookup;
+ } else {
+ switch(tag) {
+ case JS_TAG_NULL:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
+ return -1;
+ case JS_TAG_UNDEFINED:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
+ return -1;
+ default:
+ /* even on a primitive type we can have setters on the prototype */
+ p = NULL;
+ p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
+ goto prototype_lookup;
+ }
}
+ } else {
+ p = JS_VALUE_GET_OBJ(this_obj);
+ p1 = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p != p1))
+ goto retry2;
}
- p = JS_VALUE_GET_OBJ(this_obj);
-retry:
- prs = find_own_property(&pr, p, prop);
+
+ /* fast path if obj == this_obj */
+ retry:
+ prs = find_own_property(&pr, p1, prop);
if (prs) {
if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
@@ -8468,8 +8375,7 @@ retry:
goto read_only_prop;
}
}
-
- p1 = p;
+
for(;;) {
if (p1->is_exotic) {
if (p1->fast_array) {
@@ -8493,11 +8399,19 @@ retry:
return -1;
}
typed_array_oob:
- val = JS_ToNumberFree(ctx, val);
- JS_FreeValue(ctx, val);
- if (JS_IsException(val))
- return -1;
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
+ /* must convert the argument even if out of bound access */
+ if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY ||
+ p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
+ int64_t v;
+ if (JS_ToBigInt64Free(ctx, &v, val))
+ return -1;
+ } else {
+ val = JS_ToNumberFree(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ }
+ return TRUE;
}
}
} else {
@@ -8569,9 +8483,7 @@ retry:
return -1;
goto retry2;
} else if (!(prs->flags & JS_PROP_WRITABLE)) {
- read_only_prop:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
+ goto read_only_prop;
}
}
}
@@ -8592,17 +8504,57 @@ retry:
return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
}
- if (p->is_exotic) {
- if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
- __JS_AtomIsTaggedInt(prop)) {
- uint32_t idx = __JS_AtomToUInt32(prop);
- if (idx == p->u.array.count) {
- /* fast case */
- return add_fast_array_element(ctx, p, val, flags);
+ if (likely(p == JS_VALUE_GET_OBJ(obj))) {
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
+ __JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = __JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ /* fast case */
+ return add_fast_array_element(ctx, p, val, flags);
+ } else {
+ goto generic_create_prop;
+ }
} else {
goto generic_create_prop;
}
} else {
+ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
+ if (unlikely(!pr)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ pr->u.value = val;
+ return TRUE;
+ }
+ } else {
+ /* generic case: modify the property in this_obj if it already exists */
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE) ||
+ p->class_id == JS_CLASS_MODULE_NS) {
+ read_only_prop:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
+ }
+ }
+ ret = JS_DefineProperty(ctx, this_obj, prop, val,
+ JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
+ JS_FreeValue(ctx, val);
+ return ret;
+ } else {
generic_create_prop:
ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
flags |
@@ -8615,14 +8567,6 @@ retry:
return ret;
}
}
-
- pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
- if (unlikely(!pr)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- pr->u.value = val;
- return TRUE;
}
/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
@@ -8731,7 +8675,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
return -1;
if (unlikely(idx >= (uint32_t)p->u.array.count)) {
ta_out_of_bound:
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
+ return TRUE;
}
p->u.array.u.double_ptr[idx] = d;
break;
@@ -8749,7 +8693,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
JS_FreeValue(ctx, val);
return -1;
}
- ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);
+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags);
JS_FreeAtom(ctx, atom);
return ret;
}
@@ -8789,7 +8733,7 @@ int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
JSAtom atom;
int ret;
atom = JS_NewAtom(ctx, prop);
- ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);
+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW);
JS_FreeAtom(ctx, atom);
return ret;
}
@@ -9246,7 +9190,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
}
idx = __JS_AtomToUInt32(prop);
/* if the typed array is detached, p->u.array.count = 0 */
- if (idx >= typed_array_get_length(ctx, p)) {
+ if (idx >= p->u.array.count) {
typed_array_oob:
return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
}
@@ -9640,7 +9584,7 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
flags = JS_PROP_THROW_STRICT;
if (is_strict_mode(ctx))
flags |= JS_PROP_NO_ADD;
- return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
+ return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags);
}
/* return -1, FALSE or TRUE. return FALSE if not configurable or
@@ -14840,7 +14784,7 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
goto fail;
for(i = 0; i < tab_atom_count; i++) {
- JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);
+ JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, enum_obj, 0);
}
js_free_prop_enum(ctx, tab_atom, tab_atom_count);
}
@@ -17260,7 +17204,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
atom = get_u32(pc);
pc += 4;
- ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],
+ ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2],
JS_PROP_THROW_STRICT);
JS_FreeValue(ctx, sp[-2]);
sp -= 2;
@@ -17559,8 +17503,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
atom = JS_ValueToAtom(ctx, sp[-2]);
if (unlikely(atom == JS_ATOM_NULL))
goto exception;
- ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4],
- JS_PROP_THROW_STRICT);
+ ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4],
+ JS_PROP_THROW_STRICT);
JS_FreeAtom(ctx, atom);
JS_FreeValue(ctx, sp[-4]);
JS_FreeValue(ctx, sp[-3]);
@@ -18239,7 +18183,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
break;
case OP_with_put_var:
/* XXX: check if strict mode */
- ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],
+ ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj,
JS_PROP_THROW_STRICT);
JS_FreeValue(ctx, sp[-1]);
sp -= 2;
@@ -44112,8 +44056,8 @@ static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
atom = JS_ValueToAtom(ctx, prop);
if (unlikely(atom == JS_ATOM_NULL))
return JS_EXCEPTION;
- ret = JS_SetPropertyGeneric(ctx, obj, atom,
- JS_DupValue(ctx, val), receiver, 0);
+ ret = JS_SetPropertyInternal(ctx, obj, atom,
+ JS_DupValue(ctx, val), receiver, 0);
JS_FreeAtom(ctx, atom);
if (ret < 0)
return JS_EXCEPTION;
@@ -44460,9 +44404,9 @@ static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
if (!s)
return -1;
if (JS_IsUndefined(method)) {
- return JS_SetPropertyGeneric(ctx, s->target, atom,
- JS_DupValue(ctx, value), receiver,
- flags);
+ return JS_SetPropertyInternal(ctx, s->target, atom,
+ JS_DupValue(ctx, value), receiver,
+ flags);
}
atom_val = JS_AtomToValue(ctx, atom);
if (JS_IsException(atom_val)) {
@@ -51483,7 +51427,7 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val,
idx = len + idx;
if (idx < 0 || idx >= len)
return JS_UNDEFINED;
- return JS_GetPropertyUint32(ctx, this_val, idx);
+ return JS_GetPropertyInt64(ctx, this_val, idx);
}
static JSValue js_typed_array_set(JSContext *ctx,