objects directly used as properties now instead of shadow symbol table
This commit is contained in:
389
source/quickjs.c
389
source/quickjs.c
@@ -602,15 +602,6 @@ struct JSString {
|
||||
} u;
|
||||
};
|
||||
|
||||
/* Extended symbol atom structure with object-key payload */
|
||||
typedef struct JSAtomSymbol {
|
||||
JSString s; /* base atom struct */
|
||||
JSValue obj_key; /* JS_NULL for normal symbols; strong ref for object-key symbols */
|
||||
} JSAtomSymbol;
|
||||
|
||||
static inline JSAtomSymbol *js_atom_as_symbol(JSAtomStruct *p) {
|
||||
return (JSAtomSymbol *)p;
|
||||
}
|
||||
|
||||
#ifdef RC_TRACE
|
||||
static void rc_dump_atom(JSRuntime *rt, uint32_t atom)
|
||||
@@ -656,9 +647,7 @@ static void rc_dump_atom(JSRuntime *rt, uint32_t atom)
|
||||
}
|
||||
fprintf(stderr, "\"\n");
|
||||
} else if (p->atom_type == JS_ATOM_TYPE_SYMBOL) {
|
||||
JSAtomSymbol *sp = js_atom_as_symbol(p);
|
||||
fprintf(stderr, "type=symbol obj_key=%d\n",
|
||||
JS_VALUE_GET_TAG(sp->obj_key) != JS_TAG_NULL);
|
||||
fprintf(stderr, "type=symbol\n");
|
||||
} else {
|
||||
fprintf(stderr, "type=unknown(%u)\n", (unsigned)p->atom_type);
|
||||
}
|
||||
@@ -911,7 +900,10 @@ struct JSObject {
|
||||
};
|
||||
JSShape *shape; /* prototype and property names + flag */
|
||||
JSProperty *prop; /* array of properties */
|
||||
JSAtom object_key_atom; /* cached atom index for object-as-key (non-owning hint) */
|
||||
/* Object-key map for using objects as property keys (identity-based) */
|
||||
JSValue *objkey_tab; /* hash table: even indices are keys, odd are values */
|
||||
uint32_t objkey_count; /* number of entries */
|
||||
uint32_t objkey_size; /* allocated size (power of 2, 0 if none) */
|
||||
union {
|
||||
void *opaque;
|
||||
struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
|
||||
@@ -2531,16 +2523,13 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
|
||||
js_free_string(rt, str);
|
||||
}
|
||||
} else {
|
||||
/* Allocate extended JSAtomSymbol for symbol atoms */
|
||||
JSAtomSymbol *sp;
|
||||
sp = js_malloc_rt(rt, sizeof(JSAtomSymbol));
|
||||
if (!sp)
|
||||
/* Allocate simple JSString for symbol atoms (no payload needed) */
|
||||
p = js_malloc_rt(rt, sizeof(JSString));
|
||||
if (!p)
|
||||
return JS_ATOM_NULL;
|
||||
p = &sp->s;
|
||||
p->header.ref_count = 1;
|
||||
p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
|
||||
p->len = 0;
|
||||
sp->obj_key = JS_NULL;
|
||||
#ifdef DUMP_LEAKS
|
||||
list_add_tail(&p->link, &rt->string_list);
|
||||
#endif
|
||||
@@ -2651,14 +2640,6 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
|
||||
/* live weak references are still present on this object: keep
|
||||
it */
|
||||
} else {
|
||||
/* Free object-key payload for symbol atoms before freeing struct */
|
||||
if (p->atom_type == JS_ATOM_TYPE_SYMBOL) {
|
||||
JSAtomSymbol *sp = js_atom_as_symbol(p);
|
||||
if (!JS_IsNull(sp->obj_key)) {
|
||||
JS_FreeValueRT(rt, sp->obj_key);
|
||||
sp->obj_key = JS_NULL;
|
||||
}
|
||||
}
|
||||
js_free_rt(rt, p);
|
||||
}
|
||||
rt->atom_count--;
|
||||
@@ -2675,66 +2656,191 @@ static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
|
||||
JS_FreeAtomStruct(rt, p);
|
||||
}
|
||||
|
||||
/* Get or create a unique symbol atom for using an object as a property key.
|
||||
Returns JS_ATOM_NULL on allocation failure. */
|
||||
static JSAtom js_get_object_key_atom(JSContext *ctx, JSObject *key_obj)
|
||||
/* Object-key map helpers for direct object key storage */
|
||||
|
||||
static inline uint32_t objkey_hash(JSObject *key_obj)
|
||||
{
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSAtom atom = key_obj->object_key_atom;
|
||||
return (uint32_t)((uintptr_t)key_obj >> 3);
|
||||
}
|
||||
|
||||
/* Validate cached atom (non-owning; may be stale) */
|
||||
if (atom != JS_ATOM_NULL &&
|
||||
atom < (JSAtom)rt->atom_size &&
|
||||
rt->atom_array[atom] != NULL &&
|
||||
!atom_is_free(rt->atom_array[atom])) {
|
||||
/* Find slot for key_obj in objkey_tab. Returns index of key slot (even index).
|
||||
If found is set to TRUE, the key exists at that slot.
|
||||
If found is FALSE, the slot is either empty (JS_TAG_NULL) or a tombstone (JS_TAG_UNINITIALIZED)
|
||||
and can be used for insertion. */
|
||||
static uint32_t objkey_find_slot(JSValue *tab, uint32_t size, JSObject *key_obj, BOOL *found)
|
||||
{
|
||||
uint32_t mask = size - 1;
|
||||
uint32_t h = objkey_hash(key_obj) & mask;
|
||||
uint32_t i = h * 2; /* key at even index, value at odd */
|
||||
uint32_t first_tombstone = UINT32_MAX;
|
||||
|
||||
JSAtomStruct *ap = rt->atom_array[atom];
|
||||
if (ap->atom_type == JS_ATOM_TYPE_SYMBOL) {
|
||||
JSAtomSymbol *sp = js_atom_as_symbol(ap);
|
||||
for (;;) {
|
||||
int tag = JS_VALUE_GET_TAG(tab[i]);
|
||||
if (tag == JS_TAG_NULL) {
|
||||
/* Empty slot */
|
||||
*found = FALSE;
|
||||
return (first_tombstone != UINT32_MAX) ? first_tombstone : i;
|
||||
}
|
||||
if (tag == JS_TAG_UNINITIALIZED) {
|
||||
/* Tombstone - remember first one for insertion */
|
||||
if (first_tombstone == UINT32_MAX)
|
||||
first_tombstone = i;
|
||||
} else if (JS_VALUE_GET_OBJ(tab[i]) == key_obj) {
|
||||
/* Found */
|
||||
*found = TRUE;
|
||||
return i;
|
||||
}
|
||||
/* Linear probe */
|
||||
h = (h + 1) & mask;
|
||||
i = h * 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (JS_VALUE_GET_TAG(sp->obj_key) == JS_TAG_OBJECT &&
|
||||
JS_VALUE_GET_OBJ(sp->obj_key) == key_obj) {
|
||||
return atom;
|
||||
/* Get value for object key from this object only (no prototype chain) */
|
||||
static JSValue js_get_objkey_own(JSContext *ctx, JSObject *p, JSObject *key_obj)
|
||||
{
|
||||
if (p->objkey_size == 0)
|
||||
return JS_UNINITIALIZED; /* Not found marker */
|
||||
|
||||
BOOL found;
|
||||
uint32_t slot = objkey_find_slot(p->objkey_tab, p->objkey_size, key_obj, &found);
|
||||
if (found)
|
||||
return JS_DupValue(ctx, p->objkey_tab[slot + 1]);
|
||||
return JS_UNINITIALIZED;
|
||||
}
|
||||
|
||||
/* Get value for object key, walking prototype chain */
|
||||
static JSValue js_get_objkey_proto(JSContext *ctx, JSObject *p, JSObject *key_obj)
|
||||
{
|
||||
for (;;) {
|
||||
JSValue val = js_get_objkey_own(ctx, p, key_obj);
|
||||
if (!JS_IsUninitialized(val))
|
||||
return val;
|
||||
/* Walk prototype chain */
|
||||
p = p->shape->proto;
|
||||
if (!p)
|
||||
return JS_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize objkey table */
|
||||
static int objkey_resize(JSContext *ctx, JSObject *p, uint32_t new_size)
|
||||
{
|
||||
JSValue *new_tab = js_mallocz(ctx, new_size * 2 * sizeof(JSValue));
|
||||
if (!new_tab)
|
||||
return -1;
|
||||
|
||||
/* Initialize all slots to JS_NULL */
|
||||
for (uint32_t i = 0; i < new_size * 2; i += 2) {
|
||||
new_tab[i] = JS_NULL;
|
||||
new_tab[i + 1] = JS_NULL;
|
||||
}
|
||||
|
||||
/* Rehash existing entries */
|
||||
if (p->objkey_tab) {
|
||||
for (uint32_t i = 0; i < p->objkey_size * 2; i += 2) {
|
||||
int tag = JS_VALUE_GET_TAG(p->objkey_tab[i]);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
JSObject *key = JS_VALUE_GET_OBJ(p->objkey_tab[i]);
|
||||
BOOL found;
|
||||
uint32_t slot = objkey_find_slot(new_tab, new_size, key, &found);
|
||||
new_tab[slot] = p->objkey_tab[i];
|
||||
new_tab[slot + 1] = p->objkey_tab[i + 1];
|
||||
}
|
||||
}
|
||||
js_free(ctx, p->objkey_tab);
|
||||
}
|
||||
|
||||
p->objkey_tab = new_tab;
|
||||
p->objkey_size = new_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set value for object key on this object. Returns -1 on error. */
|
||||
static int js_set_objkey_own(JSContext *ctx, JSObject *p, JSObject *key_obj, JSValue val)
|
||||
{
|
||||
if (p->stone) {
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_ThrowTypeError(ctx, "cannot modify frozen object");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate or resize if needed */
|
||||
if (p->objkey_size == 0) {
|
||||
if (objkey_resize(ctx, p, 4) < 0) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return -1;
|
||||
}
|
||||
} else if (p->objkey_count * 2 >= p->objkey_size) {
|
||||
/* Load factor > 0.5, resize */
|
||||
if (objkey_resize(ctx, p, p->objkey_size * 2) < 0) {
|
||||
JS_FreeValue(ctx, val);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a fresh symbol atom (passing NULL for symbol str) */
|
||||
atom = __JS_NewAtom(rt, NULL, JS_ATOM_TYPE_SYMBOL);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
return JS_ATOM_NULL;
|
||||
BOOL found;
|
||||
uint32_t slot = objkey_find_slot(p->objkey_tab, p->objkey_size, key_obj, &found);
|
||||
|
||||
{
|
||||
JSAtomStruct *ap = rt->atom_array[atom];
|
||||
JSAtomSymbol *sp = js_atom_as_symbol(ap);
|
||||
sp->obj_key = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, key_obj));
|
||||
if (found) {
|
||||
/* Overwrite existing value */
|
||||
JS_FreeValue(ctx, p->objkey_tab[slot + 1]);
|
||||
p->objkey_tab[slot + 1] = val;
|
||||
} else {
|
||||
/* Insert new entry */
|
||||
p->objkey_tab[slot] = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, key_obj));
|
||||
p->objkey_tab[slot + 1] = val;
|
||||
p->objkey_count++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delete object key from this object. Returns TRUE if deleted, FALSE if not found, -1 on error. */
|
||||
static int js_del_objkey_own(JSContext *ctx, JSObject *p, JSObject *key_obj)
|
||||
{
|
||||
if (p->objkey_size == 0)
|
||||
return FALSE;
|
||||
|
||||
if (p->stone) {
|
||||
JS_ThrowTypeError(ctx, "cannot modify frozen object");
|
||||
return -1;
|
||||
}
|
||||
|
||||
key_obj->object_key_atom = atom;
|
||||
return atom;
|
||||
BOOL found;
|
||||
uint32_t slot = objkey_find_slot(p->objkey_tab, p->objkey_size, key_obj, &found);
|
||||
|
||||
if (!found)
|
||||
return FALSE;
|
||||
|
||||
/* Free key and value, mark as tombstone */
|
||||
JS_FreeValue(ctx, p->objkey_tab[slot]);
|
||||
JS_FreeValue(ctx, p->objkey_tab[slot + 1]);
|
||||
p->objkey_tab[slot] = JS_UNINITIALIZED;
|
||||
p->objkey_tab[slot + 1] = JS_NULL;
|
||||
p->objkey_count--;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Get or create a symbol value for using an object as a property key.
|
||||
Returns JS_EXCEPTION on allocation failure. */
|
||||
static JSValue js_get_object_key_symbol(JSContext *ctx, JSObject *key_obj)
|
||||
/* Check if object has object key (own property only) */
|
||||
static BOOL js_has_objkey_own(JSObject *p, JSObject *key_obj)
|
||||
{
|
||||
JSAtom atom = js_get_object_key_atom(ctx, key_obj);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
return JS_ThrowOutOfMemory(ctx);
|
||||
|
||||
return JS_MKPTR(JS_TAG_SYMBOL, ctx->rt->atom_array[atom]);
|
||||
if (p->objkey_size == 0)
|
||||
return FALSE;
|
||||
BOOL found;
|
||||
objkey_find_slot(p->objkey_tab, p->objkey_size, key_obj, &found);
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Check if a symbol atom is an object-key symbol (has obj_key payload) */
|
||||
static BOOL js_atom_is_object_key_symbol(JSRuntime *rt, JSAtom atom)
|
||||
/* Check if object has object key (including prototype chain) */
|
||||
static BOOL js_has_objkey_proto(JSObject *p, JSObject *key_obj)
|
||||
{
|
||||
if (atom >= (JSAtom)rt->atom_size)
|
||||
return FALSE;
|
||||
JSAtomStruct *ap = rt->atom_array[atom];
|
||||
if (!ap || ap->atom_type != JS_ATOM_TYPE_SYMBOL)
|
||||
return FALSE;
|
||||
JSAtomSymbol *sp = js_atom_as_symbol(ap);
|
||||
return !JS_IsNull(sp->obj_key);
|
||||
for (;;) {
|
||||
if (js_has_objkey_own(p, key_obj))
|
||||
return TRUE;
|
||||
p = p->shape->proto;
|
||||
if (!p)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Warning: 'p' is freed */
|
||||
@@ -4678,7 +4784,9 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
|
||||
p->stone = FALSE;
|
||||
p->free_mark = 0;
|
||||
p->tmp_mark = 0;
|
||||
p->object_key_atom = JS_ATOM_NULL;
|
||||
p->objkey_tab = NULL;
|
||||
p->objkey_count = 0;
|
||||
p->objkey_size = 0;
|
||||
p->u.opaque = NULL;
|
||||
p->shape = sh;
|
||||
p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
|
||||
@@ -5242,9 +5350,22 @@ static void free_object(JSRuntime *rt, JSObject *p)
|
||||
putting it in gc_zero_ref_count_list */
|
||||
js_free_shape(rt, sh);
|
||||
|
||||
/* Free object-key map entries */
|
||||
if (p->objkey_tab) {
|
||||
for (uint32_t j = 0; j < p->objkey_size * 2; j += 2) {
|
||||
int tag = JS_VALUE_GET_TAG(p->objkey_tab[j]);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
JS_FreeValueRT(rt, p->objkey_tab[j]);
|
||||
JS_FreeValueRT(rt, p->objkey_tab[j + 1]);
|
||||
}
|
||||
}
|
||||
js_free_rt(rt, p->objkey_tab);
|
||||
}
|
||||
|
||||
/* fail safe */
|
||||
p->shape = NULL;
|
||||
p->prop = NULL;
|
||||
p->objkey_tab = NULL;
|
||||
|
||||
finalizer = rt->class_array[p->class_id].finalizer;
|
||||
if (finalizer)
|
||||
@@ -5434,6 +5555,17 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
|
||||
if (gc_mark)
|
||||
gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
|
||||
}
|
||||
|
||||
/* Mark object-key map entries */
|
||||
if (p->objkey_tab) {
|
||||
for (uint32_t j = 0; j < p->objkey_size * 2; j += 2) {
|
||||
int tag = JS_VALUE_GET_TAG(p->objkey_tab[j]);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
JS_MarkValue(rt, p->objkey_tab[j], mark_func);
|
||||
JS_MarkValue(rt, p->objkey_tab[j + 1], mark_func);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||
@@ -6849,9 +6981,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
|
||||
for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
|
||||
atom = prs->atom;
|
||||
if (atom != JS_ATOM_NULL) {
|
||||
/* Skip object-key symbols (private access tokens) */
|
||||
if (js_atom_is_object_key_symbol(ctx->rt, atom))
|
||||
continue;
|
||||
kind = JS_AtomGetKind(ctx, atom);
|
||||
if ((kind == JS_ATOM_KIND_STRING && want_strings) ||
|
||||
(kind == JS_ATOM_KIND_SYMBOL && want_symbols)) {
|
||||
@@ -6877,9 +7006,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
|
||||
for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
|
||||
atom = prs->atom;
|
||||
if (atom != JS_ATOM_NULL) {
|
||||
/* Skip object-key symbols (private access tokens) */
|
||||
if (js_atom_is_object_key_symbol(ctx->rt, atom))
|
||||
continue;
|
||||
kind = JS_AtomGetKind(ctx, atom);
|
||||
if ((kind == JS_ATOM_KIND_STRING && want_strings) ||
|
||||
(kind == JS_ATOM_KIND_SYMBOL && want_symbols)) {
|
||||
@@ -6999,6 +7125,8 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
|
||||
if (tag == JS_TAG_INT) {
|
||||
/* Convert integer to string atom */
|
||||
atom = JS_NewAtomUInt32(ctx, (uint32_t)JS_VALUE_GET_INT(val));
|
||||
} else if (tag == JS_TAG_OBJECT) {
|
||||
return JS_ATOM_NULL;
|
||||
} else if (tag == JS_TAG_SYMBOL) {
|
||||
JSAtomStruct *p = JS_VALUE_GET_PTR(val);
|
||||
atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
|
||||
@@ -7008,7 +7136,6 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
|
||||
if (JS_IsException(str))
|
||||
return JS_ATOM_NULL;
|
||||
if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
|
||||
/* Must dup atom for proper ownership (especially for object-key symbols) */
|
||||
atom = JS_DupAtom(ctx, js_symbol_to_atom(ctx, str));
|
||||
} else {
|
||||
atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
|
||||
@@ -7058,7 +7185,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
|
||||
return JS_GetPropertyNumber(ctx, this_obj, idx);
|
||||
}
|
||||
|
||||
if (prop_tag == JS_TAG_STRING || prop_tag == JS_TAG_STRING_ROPE || prop_tag == JS_TAG_OBJECT) {
|
||||
if (prop_tag == JS_TAG_STRING || prop_tag == JS_TAG_STRING_ROPE) {
|
||||
atom = JS_ValueToAtom(ctx, prop);
|
||||
JS_FreeValue(ctx, prop);
|
||||
ret = JS_GetProperty(ctx, this_obj, atom);
|
||||
@@ -7066,6 +7193,21 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle object keys directly via objkey map */
|
||||
if (prop_tag == JS_TAG_OBJECT) {
|
||||
if (this_tag != JS_TAG_OBJECT) {
|
||||
JS_FreeValue(ctx, prop);
|
||||
return JS_NULL;
|
||||
}
|
||||
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
|
||||
JSObject *key_obj = JS_VALUE_GET_OBJ(prop);
|
||||
JS_FreeValue(ctx, prop);
|
||||
JSValue val = js_get_objkey_proto(ctx, p, key_obj);
|
||||
if (JS_IsUninitialized(val))
|
||||
return JS_NULL;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Unknown type -> null */
|
||||
JS_FreeValue(ctx, prop);
|
||||
return JS_NULL;
|
||||
@@ -7361,6 +7503,14 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue pr
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Handle object keys directly via objkey map */
|
||||
if (prop_tag == JS_TAG_OBJECT) {
|
||||
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
|
||||
JSObject *key_obj = JS_VALUE_GET_OBJ(prop);
|
||||
JS_FreeValue(ctx, prop);
|
||||
return js_set_objkey_own(ctx, p, key_obj, val);
|
||||
}
|
||||
|
||||
atom = JS_ValueToAtom(ctx, prop);
|
||||
JS_FreeValue(ctx, prop);
|
||||
if (unlikely(atom == JS_ATOM_NULL)) {
|
||||
@@ -7411,9 +7561,21 @@ int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Property access with JSValue key - supports object keys via symbols */
|
||||
/* Property access with JSValue key - supports object keys directly */
|
||||
JSValue JS_GetPropertyKey(JSContext *ctx, JSValueConst this_obj, JSValueConst key)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(key);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
|
||||
return JS_NULL;
|
||||
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
|
||||
JSObject *key_obj = JS_VALUE_GET_OBJ(key);
|
||||
JSValue val = js_get_objkey_proto(ctx, p, key_obj);
|
||||
if (JS_IsUninitialized(val))
|
||||
return JS_NULL;
|
||||
return val;
|
||||
}
|
||||
|
||||
JSAtom atom;
|
||||
JSValue ret;
|
||||
atom = JS_ValueToAtom(ctx, key);
|
||||
@@ -7426,6 +7588,18 @@ JSValue JS_GetPropertyKey(JSContext *ctx, JSValueConst this_obj, JSValueConst ke
|
||||
|
||||
int JS_SetPropertyKey(JSContext *ctx, JSValueConst this_obj, JSValueConst key, JSValue val)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(key);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_ThrowTypeError(ctx, "cannot set property on this value");
|
||||
return -1;
|
||||
}
|
||||
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
|
||||
JSObject *key_obj = JS_VALUE_GET_OBJ(key);
|
||||
return js_set_objkey_own(ctx, p, key_obj, val);
|
||||
}
|
||||
|
||||
JSAtom atom;
|
||||
int ret;
|
||||
atom = JS_ValueToAtom(ctx, key);
|
||||
@@ -7438,6 +7612,42 @@ int JS_SetPropertyKey(JSContext *ctx, JSValueConst this_obj, JSValueConst key, J
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Property existence check with JSValue key (supports object keys) */
|
||||
int JS_HasPropertyKey(JSContext *ctx, JSValueConst obj, JSValueConst key)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(key);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return FALSE;
|
||||
return js_has_objkey_proto(JS_VALUE_GET_OBJ(obj), JS_VALUE_GET_OBJ(key));
|
||||
}
|
||||
|
||||
JSAtom atom = JS_ValueToAtom(ctx, key);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
return -1;
|
||||
int ret = JS_HasProperty(ctx, obj, atom);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Property deletion with JSValue key (supports object keys) */
|
||||
int JS_DeletePropertyKey(JSContext *ctx, JSValueConst obj, JSValueConst key)
|
||||
{
|
||||
uint32_t tag = JS_VALUE_GET_TAG(key);
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
||||
return FALSE;
|
||||
return js_del_objkey_own(ctx, JS_VALUE_GET_OBJ(obj), JS_VALUE_GET_OBJ(key));
|
||||
}
|
||||
|
||||
JSAtom atom = JS_ValueToAtom(ctx, key);
|
||||
if (atom == JS_ATOM_NULL)
|
||||
return -1;
|
||||
int ret = JS_DeleteProperty(ctx, obj, atom);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* compute the property flags. For each flag: (JS_PROP_HAS_x forces
|
||||
it, otherwise def_flags is used)
|
||||
Note: makes assumption about the bit pattern of the flags
|
||||
@@ -8612,9 +8822,9 @@ JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
|
||||
{
|
||||
int tag = JS_VALUE_GET_TAG(val);
|
||||
|
||||
/* Objects become their cached object-key symbol (identity-based key) */
|
||||
/* Objects are handled directly via objkey map, not through atoms */
|
||||
if (tag == JS_TAG_OBJECT) {
|
||||
return js_get_object_key_symbol(ctx, JS_VALUE_GET_OBJ(val));
|
||||
return JS_DupValue(ctx, val);
|
||||
}
|
||||
|
||||
return JS_ToStringInternal(ctx, val, TRUE);
|
||||
@@ -9709,7 +9919,6 @@ static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
|
||||
static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
|
||||
{
|
||||
JSValue op1, op2;
|
||||
JSAtom atom;
|
||||
int ret;
|
||||
|
||||
op1 = sp[-2];
|
||||
@@ -9719,11 +9928,7 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
|
||||
JS_ThrowTypeError(ctx, "invalid 'in' operand");
|
||||
return -1;
|
||||
}
|
||||
atom = JS_ValueToAtom(ctx, op1);
|
||||
if (unlikely(atom == JS_ATOM_NULL))
|
||||
return -1;
|
||||
ret = JS_HasProperty(ctx, op2, atom);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
ret = JS_HasPropertyKey(ctx, op2, op1);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
JS_FreeValue(ctx, op1);
|
||||
@@ -9735,16 +9940,12 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
|
||||
static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
|
||||
{
|
||||
JSValue op1, op2;
|
||||
JSAtom atom;
|
||||
int ret;
|
||||
|
||||
op1 = sp[-2];
|
||||
op2 = sp[-1];
|
||||
atom = JS_ValueToAtom(ctx, op2);
|
||||
if (unlikely(atom == JS_ATOM_NULL))
|
||||
return -1;
|
||||
ret = JS_DeleteProperty(ctx, op1, atom);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
|
||||
ret = JS_DeletePropertyKey(ctx, op1, op2);
|
||||
if (unlikely(ret < 0))
|
||||
return -1;
|
||||
JS_FreeValue(ctx, op1);
|
||||
|
||||
@@ -3501,12 +3501,4 @@ return {
|
||||
if (obj.beta[1] != obj) throw "text key cycle failed"
|
||||
},
|
||||
|
||||
test_gc_array_object_key_mix: function() {
|
||||
var obj = {}
|
||||
var arr = [obj]
|
||||
obj[arr] = "anchor"
|
||||
obj.self = obj
|
||||
if (obj[arr] != "anchor") throw "array object key failed"
|
||||
if (arr[0] != obj) throw "array object mix failed"
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user