object cannot use anything but string and obj as keys

This commit is contained in:
2026-01-06 09:11:47 -06:00
parent b0f0a5f63f
commit eba0727247
2 changed files with 203 additions and 6 deletions

View File

@@ -7560,16 +7560,24 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
{
JSAtom atom;
JSValue ret;
uint32_t prop_tag = JS_VALUE_GET_TAG(prop);
if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
prop_tag == JS_TAG_INT)) {
JSObject *p;
uint32_t idx;
int32_t signed_idx;
/* fast path for array access */
p = JS_VALUE_GET_OBJ(this_obj);
idx = JS_VALUE_GET_INT(prop);
signed_idx = JS_VALUE_GET_INT(prop);
switch(p->class_id) {
case JS_CLASS_ARRAY:
/* arrays require non-negative numeric index */
if (signed_idx < 0) {
JS_FreeValue(ctx, prop);
return JS_ThrowTypeError(ctx, "array index must be non-negative");
}
idx = (uint32_t)signed_idx;
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_DupValue(ctx, p->u.array.u.values[idx]);
default:
@@ -7577,8 +7585,39 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
}
} else {
slow_path:
/* Type checking for array vs object indexing */
if (JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
if (p->class_id == JS_CLASS_ARRAY) {
/* Arrays require numeric index */
if (prop_tag != JS_TAG_INT && !JS_TAG_IS_FLOAT64(prop_tag)) {
JS_FreeValue(ctx, prop);
return JS_ThrowTypeError(ctx, "array index must be a number");
}
/* Check for negative index */
if (prop_tag == JS_TAG_INT) {
if (JS_VALUE_GET_INT(prop) < 0) {
JS_FreeValue(ctx, prop);
return JS_ThrowTypeError(ctx, "array index must be non-negative");
}
} else {
double d = JS_VALUE_GET_FLOAT64(prop);
if (d < 0) {
JS_FreeValue(ctx, prop);
return JS_ThrowTypeError(ctx, "array index must be non-negative");
}
}
} else {
/* Objects require text or object (symbol) key - NOT numbers */
if (prop_tag != JS_TAG_STRING && prop_tag != JS_TAG_STRING_ROPE &&
prop_tag != JS_TAG_SYMBOL && prop_tag != JS_TAG_OBJECT) {
JS_FreeValue(ctx, prop);
return JS_ThrowTypeError(ctx, "object key must be text or object");
}
}
}
/* ToObject() must be done before ToPropertyKey() */
atom = JS_ValueToAtom(ctx, prop);
atom = JS_ValueToAtom(ctx, prop);
if (JS_IsNull(this_obj)) {
JS_FreeValue(ctx, prop);
JSValue ret = JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", atom);
@@ -8263,16 +8302,27 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
JSValue prop, JSValue val, int flags)
{
uint32_t prop_tag = JS_VALUE_GET_TAG(prop);
if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
prop_tag == JS_TAG_INT)) {
JSObject *p;
uint32_t idx;
int32_t signed_idx;
/* fast path for array access */
p = JS_VALUE_GET_OBJ(this_obj);
idx = JS_VALUE_GET_INT(prop);
signed_idx = JS_VALUE_GET_INT(prop);
switch(p->class_id) {
case JS_CLASS_ARRAY:
/* arrays require non-negative numeric index */
if (signed_idx < 0) {
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, val);
JS_ThrowTypeError(ctx, "array index must be non-negative");
return -1;
}
idx = (uint32_t)signed_idx;
if (unlikely(idx >= (uint32_t)p->u.array.count)) {
JSObject *p1;
JSShape *sh1;
@@ -8309,6 +8359,45 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
JSAtom atom;
int ret;
slow_path:
/* Type checking for array vs object indexing */
if (JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
if (p->class_id == JS_CLASS_ARRAY) {
/* Arrays require numeric index */
if (prop_tag != JS_TAG_INT && !JS_TAG_IS_FLOAT64(prop_tag)) {
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, val);
JS_ThrowTypeError(ctx, "array index must be a number");
return -1;
}
/* Check for negative index */
if (prop_tag == JS_TAG_INT) {
if (JS_VALUE_GET_INT(prop) < 0) {
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, val);
JS_ThrowTypeError(ctx, "array index must be non-negative");
return -1;
}
} else {
double d = JS_VALUE_GET_FLOAT64(prop);
if (d < 0) {
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, val);
JS_ThrowTypeError(ctx, "array index must be non-negative");
return -1;
}
}
} else {
/* Objects require text or object (symbol) key - NOT numbers */
if (prop_tag != JS_TAG_STRING && prop_tag != JS_TAG_STRING_ROPE &&
prop_tag != JS_TAG_SYMBOL && prop_tag != JS_TAG_OBJECT) {
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, val);
JS_ThrowTypeError(ctx, "object key must be text or object");
return -1;
}
}
}
atom = JS_ValueToAtom(ctx, prop);
JS_FreeValue(ctx, prop);
if (unlikely(atom == JS_ATOM_NULL)) {
@@ -10282,7 +10371,6 @@ JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
return js_get_object_key_symbol(ctx, JS_VALUE_GET_OBJ(val));
}
/* Preserve existing behavior for everything else */
return JS_ToStringInternal(ctx, val, TRUE);
}

View File

@@ -1722,4 +1722,113 @@ return {
outer[k1] = inner
if (outer[k1][k2] != "nested") throw "nested object keys failed"
},
test_array_number_key: function() {
var a = []
a[1] = 1
if (a[1] != 1) throw "array should be able to use number as key"
},
test_array_string_key_throws: function() {
var a = []
var caught = false
try {
a["a"] = 1
} catch(e) {
caught = true
}
if (!caught) throw "array should not be able to use string as key"
},
test_array_object_key_throws: function() {
var a = []
var b = {}
var caught = false
try {
a[b] = 1
} catch(e) {
caught = true
}
if (!caught) throw "array should not be able to use object as key"
},
test_array_boolean_key_throws: function() {
var a = []
var caught = false
try {
a[true] = 1
} catch(e) {
caught = true
}
if (!caught) throw "array should not be able to use boolean as key"
},
test_array_null_key_throws: function() {
var a = []
var caught = false
try {
a[null] = 1
} catch(e) {
caught = true
}
if (!caught) throw "array should not be able to use null as key"
},
test_array_array_key_throws: function() {
var a = []
var c = []
var caught = false
try {
a[c] = 1
} catch(e) {
caught = true
}
if (!caught) throw "array should not be able to use array as key"
},
test_obj_number_key_throws: function() {
var a = {}
var caught = false
try {
a[1] = 1
} catch(e) {
caught = true
}
if (!caught) throw "object should not be able to use number as key"
},
test_obj_array_key_throws: function() {
var a = {}
var c = []
var caught = false
try {
a[c] = 1
} catch(e) {
caught = true
}
if (!caught) throw "object should not be able to use array as key"
},
test_obj_boolean_key_throws: function() {
var a = {}
var caught = false
try {
a[true] = 1
} catch(e) {
caught = true
}
if (!caught) throw "object should not be able to use boolean as key"
},
test_obj_null_key_throws: function() {
var a = {}
var caught = false
try {
a[null] = 1
} catch(e) {
caught = true
}
if (!caught) throw "object should not be able to use null as key"
},
}