Previously, Object.defineProperty() failed to properly set attributes for properties of fast array.
array->length = 0;
njs_set_array(&value, array);
- ret = njs_array_length_redefine(vm, &value, length);
+ ret = njs_array_length_redefine(vm, &value, length, 1);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
njs_int_t
-njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length)
+njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, int writable)
{
njs_object_prop_t *prop;
return NJS_ERROR;
}
+ prop->writable = writable;
prop->enumerable = 0;
prop->configurable = 0;
int64_t prev_length;
uint32_t i, length;
njs_int_t ret;
- njs_array_t *array, *keys;
-
- array = njs_object_proto_lookup(njs_object(value), NJS_ARRAY, njs_array_t);
- if (njs_slow_path(array == NULL)) {
- return NJS_DECLINED;
- }
+ njs_array_t *keys;
ret = njs_value_to_number(vm, setval, &num);
if (njs_slow_path(ret != NJS_OK)) {
}
}
- ret = njs_array_length_redefine(vm, value, length);
+ ret = njs_array_length_redefine(vm, value, length, prev->writable);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_array_indices_handler(const void *first, const void *second, void *ctx)
{
double num1, num2;
- int64_t diff;
+ int64_t diff, cmp_res;
njs_str_t str1, str2;
const njs_value_t *val1, *val2;
njs_string_get(val1, &str1);
njs_string_get(val2, &str2);
- return strncmp((const char *) str1.start, (const char *) str2.start,
+ cmp_res = strncmp((const char *) str1.start, (const char *) str2.start,
njs_min(str1.length, str2.length));
+ if (cmp_res == 0) {
+ if (str1.length < str2.length) {
+ return -1;
+ } else if (str1.length > str2.length) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return cmp_res;
}
njs_int_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value);
njs_int_t njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array);
njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value,
- uint32_t length);
+ uint32_t length, int writable);
njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value,
njs_object_prop_t *prev, njs_value_t *setval);
njs_array_t *njs_array_keys(njs_vm_t *vm, njs_value_t *array, njs_bool_t all);
if (njs_is_array(&stringify->replacer)) {
state->keys = njs_array(&stringify->replacer);
- } else if (state->array && !state->fast_array) {
- state->keys = njs_array_keys(vm, value, 0);
+ } else if (state->array) {
+ state->keys = njs_array_keys(vm, value, 1);
if (njs_slow_path(state->keys == NULL)) {
return NULL;
}
break;
case NJS_INVALID:
- njs_chb_append_literal(chain, "<empty>");
+ /* Fall through. */
break;
case NJS_OBJECT_VALUE:
double key, prev;
int64_t diff;
- if (!state->array || state->fast_array) {
+ if (!state->array) {
return 0;
}
} else {
key = state->length;
- prev = (state->index > 0) ? njs_key_to_index(state->key) : -1;
+ if (state->key == NULL) {
+ prev = -1;
+
+ } else {
+ prev = (state->index > 0) ? njs_key_to_index(state->key) : -1;
+ }
}
if (isnan(key)) {
if (diff > 1) {
if (sep_position == 0 && state->keys->length) {
- njs_chb_append_literal(chain, ",");
- njs_json_stringify_indent(stringify, chain, 1);
+ if (prev != -1) {
+ njs_chb_append_literal(chain, ",");
+ njs_json_stringify_indent(stringify, chain, 1);
+ }
+ }
+
+ if (diff - 1 == 1) {
+ njs_chb_sprintf(chain, 64, "<empty>");
+
+ } else {
+ njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1);
}
- njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1);
state->written = 1;
if (sep_position == 1 && state->keys->length) {
njs_json_stringify_indent(stringify, &chain, 1);
}
- if (njs_json_stringify_done(state, state->fast_array)) {
+ if (njs_json_stringify_done(state, 0)) {
njs_dump_empty(stringify, state, &chain, 0);
njs_json_stringify_indent(stringify, &chain, 0);
continue;
}
- if (state->fast_array) {
- if (state->written) {
- njs_chb_append_literal(&chain, ",");
- njs_json_stringify_indent(stringify, &chain, 1);
- }
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
- state->written = 1;
+ key = &state->keys->start[state->index++];
- val = &njs_array_start(&state->value)[state->index++];
-
- } else {
- njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
-
- key = &state->keys->start[state->index++];
- state->key = key;
-
- ret = njs_property_query(vm, &pq, &state->value, key);
- if (njs_slow_path(ret != NJS_OK)) {
- if (ret == NJS_DECLINED) {
+ if(state->array) {
+ if (key->type == NJS_STRING) {
+ njs_string_get(key, &str);
+ if (str.length == 6 && memcmp(str.start, "length", 6) == 0) {
continue;
}
-
- goto exception;
}
+ }
- prop = pq.lhq.value;
+ state->key = key;
- if (prop->type == NJS_WHITEOUT || !prop->enumerable) {
+ ret = njs_property_query(vm, &pq, &state->value, key);
+ if (njs_slow_path(ret != NJS_OK)) {
+ if (ret == NJS_DECLINED) {
continue;
}
- if (state->written) {
- njs_chb_append_literal(&chain, ",");
- njs_json_stringify_indent(stringify, &chain, 1);
+ goto exception;
+ }
+
+ prop = pq.lhq.value;
+
+ if (prop->type == NJS_WHITEOUT || !prop->enumerable) {
+ if (!state->array) {
+ continue;
}
+ }
- state->written = 1;
+ if (state->written) {
+ njs_chb_append_literal(&chain, ",");
+ njs_json_stringify_indent(stringify, &chain, 1);
+ }
- njs_dump_empty(stringify, state, &chain, 1);
+ state->written = 1;
- if (!state->array || isnan(njs_key_to_index(key))) {
- njs_key_string_get(vm, key, &pq.lhq.key);
- njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length);
- njs_chb_append_literal(&chain, ":");
- if (stringify->space.length != 0) {
- njs_chb_append_literal(&chain, " ");
- }
+ njs_dump_empty(stringify, state, &chain, 1);
+
+ if (!state->array || isnan(njs_key_to_index(key))) {
+ njs_key_string_get(vm, key, &pq.lhq.key);
+ njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length);
+ njs_chb_append_literal(&chain, ":");
+ if (stringify->space.length != 0) {
+ njs_chb_append_literal(&chain, " ");
}
+ }
- val = &prop->value;
+ val = &prop->value;
+ if (!state->fast_array) {
if (prop->type == NJS_PROPERTY_HANDLER) {
pq.scratch = *prop;
prop = &pq.scratch;
return ret;
}
- ret = njs_array_length_redefine(vm, value, length);
+ ret = njs_array_length_redefine(vm, value, length, 1);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
-
}
object = njs_object(value);
}
}
+ if (njs_slow_path(njs_is_fast_array(object))) {
+ array = njs_array(object);
+ length = array->length;
+
+ ret = njs_array_convert_to_slow_array(vm, array);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_array_length_redefine(vm, object, length, 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
again:
njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
return ret;
}
- ret = njs_array_length_redefine(vm, object, length);
+ ret = njs_array_length_redefine(vm, object, length, 1);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}
} else {
- if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
- if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
- ret = njs_array_length_set(vm, object, prev, &prop->value);
- if (ret != NJS_DECLINED) {
- return ret;
+ if (njs_is_array(object)) {
+ if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
+
+ if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
+
+ if (prev->configurable != 1 &&
+ prev->writable != 1 &&
+ !njs_values_strict_equal(&prev->value, &prop->value))
+ {
+ njs_type_error(vm, "Cannot redefine property: \"length\"");
+ return NJS_ERROR;
+ }
+
+ if (prop->writable != NJS_ATTRIBUTE_UNSET) {
+ prev->writable = prop->writable;
+ }
+
+ return njs_array_length_set(vm, object, prev, &prop->value);
}
}
}
}
if ((index + 1) > length) {
- ret = njs_array_length_redefine(vm, &value, index + 1);
+ ret = njs_array_length_redefine(vm, &value, index + 1, 1);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (pq.own) {
switch (prop->type) {
case NJS_PROPERTY:
- if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
- if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
- ret = njs_array_length_set(vm, value, prop, setval);
- if (ret != NJS_DECLINED) {
- return ret;
+ if (njs_is_array(value)) {
+ if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
+ if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
+ return njs_array_length_set(vm, value, prop, setval);
}
}
}
"Object.defineProperty(a, 'length', {writable:true})"),
njs_str("TypeError: Cannot redefine property: \"length\"") },
+ { njs_str ("var a =[0,1,2]; Object.defineProperty(a, 100, {value:100});"
+ "njs.dump(a);"),
+ njs_str("[0,1,2,<97 empty items>,100]") },
+
+ { njs_str("var a =[0,1,2]; Object.defineProperty(a, 3, {value:30});"
+ "njs.dump(Object.getOwnPropertyDescriptor(a,3));"),
+ njs_str("{value:30,writable:false,enumerable:false,configurable:false}") },
+
+ { njs_str("var a =[0,1,2]; Object.defineProperty(a, 3, {value:30});"
+ "a[3]=33;"),
+ njs_str("TypeError: Cannot assign to read-only property \"3\" of array") },
+
{ njs_str("[1, 2, 3, 4, 5].copyWithin(0, 3)"),
njs_str("4,5,3,4,5") },
{ njs_str("var a = [,,undefined,undefined,,undefined];"
"a.sort(function(x, y) { return x - y }); njs.dump(a)"),
- njs_str("[undefined,undefined,undefined,<empty>,<empty>,<empty>]") },
+ njs_str("[undefined,undefined,undefined,<3 empty items>]") },
{ njs_str("var a = [1,,undefined,8,undefined,,undefined,,2];"
"a.sort(function(x, y) { return x - y }); njs.dump(a)"),
- njs_str("[1,2,8,undefined,undefined,undefined,<empty>,<empty>,<empty>]") },
+ njs_str("[1,2,8,undefined,undefined,undefined,<3 empty items>]") },
{ njs_str("var a = [1,,];"
"a.sort(function(x, y) { return x - y })"),
njs_str("0") },
{ njs_str("var x = [0, 1, 2]; x[4294967294] = 4294967294; x.length = 2;"
- "njs.dump([x,x.length,Array.prototype,Array.prototype.length])"),
- njs_str("[[0,1],2,[],0]") },
+ "njs.dump([x,x.length,Array.prototype.length])"),
+ njs_str("[[0,1],2,0]") },
#if 0 /* TODO: length 2**53-1. */
{ njs_str("var x = Array(2**20), y = Array(2**12).fill(x);"
njs_str("a,b") },
{ njs_str("Object.getOwnPropertyNames(Object.defineProperty([], 'b', {}))"),
- njs_str("b,length") },
+ njs_str("length,b") },
{ njs_str("Object.getOwnPropertyNames(Object.defineProperty(new String(), 'b', {}))"),
njs_str("b,length") },
{ njs_str("var a = ['a',,'c']; a.length = 2**31;"
"njs.dump(a)"),
- njs_str("['a',<1 empty items>,'c',<2147483645 empty items>]") },
+ njs_str("['a',<empty>,'c',<2147483645 empty items>]") },
{ njs_str("var a = [,'b','c']; a.length = 2**31;"
"njs.dump(a)"),
- njs_str("[<1 empty items>,'b','c',<2147483645 empty items>]") },
+ njs_str("[<empty>,'b','c',<2147483645 empty items>]") },
#if (!NJS_HAVE_MEMORY_SANITIZER) /* False-positive in MSAN? */
{ njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; njs.dump(a)"),
njs_str("[1,'[Getter]',3]") },
{ njs_str("var a = [1,2,3];Object.defineProperty(a, '1', {enumerable:false});njs.dump(a)"),
- njs_str("[1,<1 empty items>,3]") },
+ njs_str("[1,2,3]") },
{ njs_str("njs.dump(-0)"),
njs_str("-0") },