object cannot use anything but string and obj as keys
This commit is contained in:
@@ -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,6 +7585,37 @@ 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);
|
||||
if (JS_IsNull(this_obj)) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
109
tests/suite.cm
109
tests/suite.cm
@@ -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"
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user