Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various issues with empty array items and array length property #1

Closed
drsm opened this issue Apr 16, 2018 · 5 comments
Closed

various issues with empty array items and array length property #1

drsm opened this issue Apr 16, 2018 · 5 comments
Assignees
Labels
Milestone

Comments

@drsm
Copy link
Contributor

drsm commented Apr 16, 2018

tested with njs -V 0.2.0

>> var test = [1,2,3];
undefined
>> Object.keys(test);
0,1,2
>> test.length = 4;
4
>> Object.keys(test);
0,1,2,length
>> test[4] = 'some';
some
>> Object.keys(test);
0,1,2,4,length
>> test.length
4
>> JSON.stringify(test)
MemoryError
    at JSON.stringify (native)
    at main (native)
>> var test = [1,2,3];
undefined
>> test[4] = 'some';
some
>> test.length
5
@xeioex xeioex added the bug label Apr 17, 2018
@xeioex xeioex self-assigned this Apr 17, 2018
@xeioex xeioex added this to the 0.2.1 milestone Apr 18, 2018
@xeioex
Copy link
Contributor

xeioex commented Apr 19, 2018

Please, try the patch below

# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524143773 -10800
#      Thu Apr 19 16:16:13 2018 +0300
# Node ID 9f85ae2d277c5b25019fd8ddeb9c779cf2876a60
# Parent  35486fa8c8b55dadc0a770c232a47e79c985318e
Fixed JSON.stringify() for arrays with empty cells.

diff --git a/njs/njs_json.c b/njs/njs_json.c
--- a/njs/njs_json.c
+++ b/njs/njs_json.c
@@ -1719,6 +1719,7 @@ njs_json_append_value(njs_json_stringify
 
     case NJS_VOID:
     case NJS_NULL:
+    case NJS_INVALID:
     case NJS_FUNCTION:
         return njs_json_buf_append(stringify, "null", 4);
 
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -8673,6 +8673,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("JSON.stringify([])"),
       nxt_string("[]") },
 
+    { nxt_string("var a = [1]; a[2] = 'x'; JSON.stringify(a)"),
+      nxt_string("[1,null,\"x\"]") },
+
     { nxt_string("JSON.stringify({a:\"b\",c:19,e:null,t:true,f:false})"),
       nxt_string("{\"a\":\"b\",\"c\":19,\"e\":null,\"t\":true,\"f\":false}") },
 
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524143774 -10800
#      Thu Apr 19 16:16:14 2018 +0300
# Node ID 1f30a312125597ac7be9dd787cccde8545d39fa8
# Parent  9f85ae2d277c5b25019fd8ddeb9c779cf2876a60
Fixed exception type for unsupported types in JSON.stringify().

diff --git a/njs/njs_json.c b/njs/njs_json.c
--- a/njs/njs_json.c
+++ b/njs/njs_json.c
@@ -1184,6 +1184,10 @@ njs_json_parse_exception(njs_json_parse_
     state->written = 1;                                                       \
     ret = njs_json_append_value(stringify, value);                            \
     if (nxt_slow_path(ret != NXT_OK)) {                                       \
+        if (ret == NXT_DECLINED) {                                            \
+            return NXT_ERROR;                                                 \
+        }                                                                     \
+                                                                              \
         goto memory_error;                                                    \
     }
 
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -8708,6 +8708,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var e = URIError('e'); e.foo = 'E'; JSON.stringify(e)"),
       nxt_string("{\"foo\":\"E\"}") },
 
+    { nxt_string("JSON.stringify($r)"),
+      nxt_string("TypeError: Non-serializable object") },
+
     /* Ignoring named properties of an array. */
 
     { nxt_string("var a = [1,2]; a.a = 1;"
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524143775 -10800
#      Thu Apr 19 16:16:15 2018 +0300
# Node ID f318876c581c2b0f636b82b40b1f6713f6662062
# Parent  1f30a312125597ac7be9dd787cccde8545d39fa8
Making native getters into universal property handlers.

diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -351,9 +351,9 @@ static const njs_object_prop_t  njs_arra
 
     /* Array.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 
     /* Array.isArray(). */
@@ -382,7 +382,7 @@ const njs_object_init_t  njs_array_const
 
 static njs_ret_t
 njs_array_prototype_length(njs_vm_t *vm, njs_value_t *array,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     njs_value_number_set(retval, array->data.u.array->length);
 
@@ -2057,9 +2057,9 @@ njs_array_prototype_sort_continuation(nj
 static const njs_object_prop_t  njs_array_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("length"),
-        .value = njs_native_getter(njs_array_prototype_length),
+        .value = njs_prop_handler(njs_array_prototype_length),
     },
 
     {
diff --git a/njs/njs_boolean.c b/njs/njs_boolean.c
--- a/njs/njs_boolean.c
+++ b/njs/njs_boolean.c
@@ -71,9 +71,9 @@ static const njs_object_prop_t  njs_bool
 
     /* Boolean.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -140,9 +140,9 @@ njs_boolean_prototype_to_string(njs_vm_t
 static const njs_object_prop_t  njs_boolean_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("__proto__"),
-        .value = njs_native_getter(njs_primitive_prototype_get_proto),
+        .value = njs_prop_handler(njs_primitive_prototype_get_proto),
     },
 
     {
diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c
--- a/njs/njs_builtin.c
+++ b/njs/njs_builtin.c
@@ -246,9 +246,9 @@ njs_builtin_objects_create(njs_vm_t *vm)
     };
 
     static const njs_object_prop_t    function_prototype_property = {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_function_prototype_create),
+        .value = njs_prop_handler(njs_function_prototype_create),
     };
 
     ret = njs_object_hash_create(vm, &vm->shared->function_prototype_hash,
diff --git a/njs/njs_date.c b/njs/njs_date.c
--- a/njs/njs_date.c
+++ b/njs/njs_date.c
@@ -905,9 +905,9 @@ static const njs_object_prop_t  njs_date
 
     /* Date.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 
     {
@@ -1931,9 +1931,9 @@ njs_date_prototype_to_json_continuation(
 static const njs_object_prop_t  njs_date_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("__proto__"),
-        .value = njs_native_getter(njs_primitive_prototype_get_proto),
+        .value = njs_prop_handler(njs_primitive_prototype_get_proto),
     },
 
     {
diff --git a/njs/njs_error.c b/njs/njs_error.c
--- a/njs/njs_error.c
+++ b/njs/njs_error.c
@@ -192,9 +192,9 @@ static const njs_object_prop_t  njs_erro
 
     /* Error.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -232,9 +232,9 @@ static const njs_object_prop_t  njs_eval
 
     /* EvalError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -272,9 +272,9 @@ static const njs_object_prop_t  njs_inte
 
     /* InternalError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -312,9 +312,9 @@ static const njs_object_prop_t  njs_rang
 
     /* RangeError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -352,9 +352,9 @@ static const njs_object_prop_t  njs_refe
 
     /* ReferenceError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -392,9 +392,9 @@ static const njs_object_prop_t  njs_synt
 
     /* SyntaxError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -432,9 +432,9 @@ static const njs_object_prop_t  njs_type
 
     /* TypeError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -472,9 +472,9 @@ static const njs_object_prop_t  njs_uri_
 
     /* URIError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -533,7 +533,7 @@ njs_memory_error_constructor(njs_vm_t *v
 
 static njs_ret_t
 njs_memory_error_prototype_create(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     int32_t         index;
     njs_value_t     *proto;
@@ -574,9 +574,9 @@ static const njs_object_prop_t  njs_memo
 
     /* MemoryError.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_memory_error_prototype_create),
+        .value = njs_prop_handler(njs_memory_error_prototype_create),
     },
 };
 
diff --git a/njs/njs_function.c b/njs/njs_function.c
--- a/njs/njs_function.c
+++ b/njs/njs_function.c
@@ -419,7 +419,7 @@ njs_function_call(njs_vm_t *vm, njs_inde
 
 njs_ret_t
 njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     njs_value_t  *proto;
 
@@ -495,9 +495,9 @@ static const njs_object_prop_t  njs_func
 
     /* Function.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
diff --git a/njs/njs_function.h b/njs/njs_function.h
--- a/njs/njs_function.h
+++ b/njs/njs_function.h
@@ -147,7 +147,7 @@ njs_function_t *njs_function_alloc(njs_v
 njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
 njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
 njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval);
+    njs_value_t *arg, njs_value_t *retval);
 njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm,
     njs_value_t *value);
 njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args,
diff --git a/njs/njs_math.c b/njs/njs_math.c
--- a/njs/njs_math.c
+++ b/njs/njs_math.c
@@ -822,9 +822,9 @@ static const njs_object_prop_t  njs_math
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("__proto__"),
-        .value = njs_native_getter(njs_object_prototype_get_proto),
+        .value = njs_prop_handler(njs_object_prototype_get_proto),
     },
 
     {
diff --git a/njs/njs_number.c b/njs/njs_number.c
--- a/njs/njs_number.c
+++ b/njs/njs_number.c
@@ -463,9 +463,9 @@ static const njs_object_prop_t  njs_numb
 
     /* Number.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 
     /* ES6. */
@@ -707,9 +707,9 @@ njs_number_to_string_radix(njs_vm_t *vm,
 static const njs_object_prop_t  njs_number_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("__proto__"),
-        .value = njs_native_getter(njs_primitive_prototype_get_proto),
+        .value = njs_prop_handler(njs_primitive_prototype_get_proto),
     },
 
     {
diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -740,7 +740,7 @@ njs_object_get_prototype_of(njs_vm_t *vm
     njs_index_t unused)
 {
     if (nargs > 1 && njs_is_object(&args[1])) {
-        njs_object_prototype_get_proto(vm, &args[1], &vm->retval);
+        njs_object_prototype_get_proto(vm, &args[1], NULL, &vm->retval);
         return NXT_OK;
     }
 
@@ -971,7 +971,7 @@ njs_object_is_extensible(njs_vm_t *vm, n
 
 njs_ret_t
 njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     nxt_uint_t    index;
     njs_object_t  *proto;
@@ -1004,7 +1004,7 @@ njs_primitive_prototype_get_proto(njs_vm
 
 njs_ret_t
 njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     int32_t         index;
     njs_value_t     *proto;
@@ -1088,9 +1088,9 @@ static const njs_object_prop_t  njs_obje
 
     /* Object.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 
     /* Object.create(). */
@@ -1202,7 +1202,7 @@ const njs_object_init_t  njs_object_cons
 
 njs_ret_t
 njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     njs_object_t  *proto;
 
@@ -1229,7 +1229,7 @@ njs_object_prototype_get_proto(njs_vm_t 
 
 static njs_ret_t
 njs_object_prototype_create_constructor(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     int32_t                 index;
     njs_value_t             *cons;
@@ -1521,15 +1521,15 @@ njs_object_prototype_is_prototype_of(njs
 static const njs_object_prop_t  njs_object_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("__proto__"),
-        .value = njs_native_getter(njs_object_prototype_get_proto),
+        .value = njs_prop_handler(njs_object_prototype_get_proto),
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("constructor"),
-        .value = njs_native_getter(njs_object_prototype_create_constructor),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
     },
 
     {
diff --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -13,8 +13,7 @@ typedef enum {
     NJS_GETTER,
     NJS_SETTER,
     NJS_METHOD,
-    NJS_NATIVE_GETTER,
-    NJS_NATIVE_SETTER,
+    NJS_PROPERTY_HANDLER,
     NJS_WHITEOUT,
 } njs_object_property_type_t;
 
@@ -52,13 +51,13 @@ njs_ret_t njs_object_constructor(njs_vm_
 njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
         const njs_value_t *value, uint8_t attributes);
 njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval);
+    njs_value_t *arg, njs_value_t *retval);
 njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval);
+    njs_value_t *arg, njs_value_t *retval);
 njs_value_t *njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
     njs_object_t *prototype);
 njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval);
+    njs_value_t *arg, njs_value_t *retval);
 njs_value_t *njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
     njs_value_t *constructor);
 njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
diff --git a/njs/njs_regexp.c b/njs/njs_regexp.c
--- a/njs/njs_regexp.c
+++ b/njs/njs_regexp.c
@@ -464,7 +464,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regex
 
 static njs_ret_t
 njs_regexp_prototype_last_index(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     uint32_t           index;
     njs_regexp_t       *regexp;
@@ -485,7 +485,7 @@ njs_regexp_prototype_last_index(njs_vm_t
 
 static njs_ret_t
 njs_regexp_prototype_global(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     njs_regexp_pattern_t  *pattern;
 
@@ -499,7 +499,7 @@ njs_regexp_prototype_global(njs_vm_t *vm
 
 static njs_ret_t
 njs_regexp_prototype_ignore_case(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     njs_regexp_pattern_t  *pattern;
 
@@ -513,7 +513,7 @@ njs_regexp_prototype_ignore_case(njs_vm_
 
 static njs_ret_t
 njs_regexp_prototype_multiline(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     njs_regexp_pattern_t  *pattern;
 
@@ -527,7 +527,7 @@ njs_regexp_prototype_multiline(njs_vm_t 
 
 static njs_ret_t
 njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     u_char                *source;
     int32_t               length;
@@ -824,9 +824,9 @@ static const njs_object_prop_t  njs_rege
 
     /* RegExp.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
@@ -841,33 +841,33 @@ const njs_object_init_t  njs_regexp_cons
 static const njs_object_prop_t  njs_regexp_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("lastIndex"),
-        .value = njs_native_getter(njs_regexp_prototype_last_index),
+        .value = njs_prop_handler(njs_regexp_prototype_last_index),
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("global"),
-        .value = njs_native_getter(njs_regexp_prototype_global),
+        .value = njs_prop_handler(njs_regexp_prototype_global),
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("ignoreCase"),
-        .value = njs_native_getter(njs_regexp_prototype_ignore_case),
+        .value = njs_prop_handler(njs_regexp_prototype_ignore_case),
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("multiline"),
-        .value = njs_native_getter(njs_regexp_prototype_multiline),
+        .value = njs_prop_handler(njs_regexp_prototype_multiline),
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("source"),
-        .value = njs_native_getter(njs_regexp_prototype_source),
+        .value = njs_prop_handler(njs_regexp_prototype_source),
     },
 
     {
diff --git a/njs/njs_string.c b/njs/njs_string.c
--- a/njs/njs_string.c
+++ b/njs/njs_string.c
@@ -570,9 +570,9 @@ static const njs_object_prop_t  njs_stri
 
     /* String.prototype. */
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
-        .value = njs_native_getter(njs_object_prototype_create),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 
     /* String.fromCharCode(). */
@@ -600,7 +600,7 @@ const njs_object_init_t  njs_string_cons
 
 static njs_ret_t
 njs_string_prototype_length(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *retval)
+    njs_value_t *arg, njs_value_t *retval)
 {
     size_t     size;
     uintptr_t  length;
@@ -3348,15 +3348,15 @@ njs_string_to_c_string(njs_vm_t *vm, njs
 static const njs_object_prop_t  njs_string_prototype_properties[] =
 {
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("__proto__"),
-        .value = njs_native_getter(njs_primitive_prototype_get_proto),
+        .value = njs_prop_handler(njs_primitive_prototype_get_proto),
     },
 
     {
-        .type = NJS_NATIVE_GETTER,
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("length"),
-        .value = njs_native_getter(njs_string_prototype_length),
+        .value = njs_prop_handler(njs_string_prototype_length),
     },
 
     {
diff --git a/njs/njs_vm.c b/njs/njs_vm.c
--- a/njs/njs_vm.c
+++ b/njs/njs_vm.c
@@ -54,7 +54,7 @@
 typedef struct {
     nxt_lvlhsh_query_t             lhq;
 
-    /* scratch is used to get the value of an NJS_NATIVE_GETTER property. */
+    /* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
     njs_object_prop_t              scratch;
 
     njs_value_t                    value;
@@ -82,6 +82,8 @@ static njs_ret_t njs_array_property_quer
     njs_property_query_t *pq, njs_value_t *object, uint32_t index);
 static njs_ret_t njs_object_property_query(njs_vm_t *vm,
     njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
+static njs_ret_t njs_object_query_prop_handler(njs_property_query_t *pq,
+    njs_object_t *object);
 static njs_ret_t njs_method_private_copy(njs_vm_t *vm,
     njs_property_query_t *pq);
 static nxt_noinline njs_ret_t njs_values_equal(const njs_value_t *val1,
@@ -695,6 +697,17 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
 
     case NXT_OK:
         prop = pq.lhq.value;
+
+        if (prop->type == NJS_PROPERTY_HANDLER) {
+            ret = prop->value.data.u.prop_handler(vm, object, value,
+                                                  &vm->retval);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
+            return sizeof(njs_vmcode_prop_set_t);
+        }
+
         break;
 
     case NXT_DECLINED:
@@ -1153,6 +1166,13 @@ njs_object_property_query(njs_vm_t *vm, 
 
     pq->lhq.proto = &njs_object_hash_proto;
 
+    if (pq->query == NJS_PROPERTY_QUERY_SET) {
+        ret = njs_object_query_prop_handler(pq, object);
+        if (ret == NXT_OK) {
+            return ret;
+        }
+    }
+
     do {
         pq->prototype = object;
 
@@ -1183,10 +1203,11 @@ njs_object_property_query(njs_vm_t *vm, 
             if (pq->query == NJS_PROPERTY_QUERY_GET) {
                 prop = pq->lhq.value;
 
-                if (prop->type == NJS_NATIVE_GETTER) {
+                if (prop->type == NJS_PROPERTY_HANDLER) {
                     pq->scratch = *prop;
                     prop = &pq->scratch;
-                    ret = prop->value.data.u.getter(vm, value, &prop->value);
+                    ret = prop->value.data.u.prop_handler(vm, value, NULL,
+                                                          &prop->value);
 
                     if (nxt_fast_path(ret == NXT_OK)) {
                         prop->type = NJS_PROPERTY;
@@ -1220,6 +1241,35 @@ njs_object_property_query(njs_vm_t *vm, 
 
 
 static njs_ret_t
+njs_object_query_prop_handler(njs_property_query_t *pq, njs_object_t *object)
+{
+    njs_ret_t          ret;
+    njs_object_prop_t  *prop;
+
+    do {
+        pq->prototype = object;
+
+        ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
+
+        if (ret == NXT_OK) {
+            pq->shared = 1;
+
+            prop = pq->lhq.value;
+
+            if (prop->type == NJS_PROPERTY_HANDLER) {
+                return NXT_OK;
+            }
+        }
+
+        object = object->__proto__;
+
+    } while (object != NULL);
+
+    return NXT_DECLINED;
+}
+
+
+static njs_ret_t
 njs_method_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
 {
     njs_function_t     *function;
diff --git a/njs/njs_vm.h b/njs/njs_vm.h
--- a/njs/njs_vm.h
+++ b/njs/njs_vm.h
@@ -118,8 +118,13 @@ typedef enum {
 
 typedef struct njs_parser_s           njs_parser_t;
 
-typedef njs_ret_t (*njs_getter_t) (njs_vm_t *vm, njs_value_t *obj,
-    njs_value_t *retval);
+/*
+ * njs_prop_handler_t operates as a property getter and/or setter.
+ * The handler receives NULL arg if it is invoked in GET context and
+ * non-null otherwise.
+ */
+typedef njs_ret_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *arg, njs_value_t *retval);
 typedef njs_ret_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t retval);
 
@@ -177,7 +182,7 @@ union njs_value_s {
             njs_function_lambda_t     *lambda;
             njs_regexp_t              *regexp;
             njs_date_t                *date;
-            njs_getter_t              getter;
+            njs_prop_handler_t        prop_handler;
             njs_value_t               *value;
             njs_property_next_t       *next;
             void                      *data;
@@ -374,11 +379,11 @@ typedef union {
 }
 
 
-#define njs_native_getter(_getter) {                                          \
+#define njs_prop_handler(_handler) {                                          \
     .data = {                                                                 \
         .type = NJS_INVALID,                                                  \
         .truth = 1,                                                           \
-        .u = { .getter = _getter }                                            \
+        .u = { .prop_handler = _handler }                                     \
     }                                                                         \
 }
 
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1524143796 -10800
#      Thu Apr 19 16:16:36 2018 +0300
# Node ID a03bdd0434c4501fa20064214a8e66c6c12cb7cf
# Parent  f318876c581c2b0f636b82b40b1f6713f6662062
Added array length setter.

This fixes #1 issue on GitHub.

diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -381,14 +381,56 @@ const njs_object_init_t  njs_array_const
 
 
 static njs_ret_t
-njs_array_prototype_length(njs_vm_t *vm, njs_value_t *array,
+njs_array_prototype_length(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *arg, njs_value_t *retval)
 {
-    njs_value_number_set(retval, array->data.u.array->length);
-
-    njs_release(vm, array);
-
-    return NXT_OK;
+    uint32_t     length;
+    njs_ret_t    ret;
+    nxt_uint_t   i;
+    njs_value_t  *start;
+    njs_array_t  *array;
+
+    if (arg == NULL) {
+        /* getter */
+        njs_value_number_set(retval, value->data.u.array->length);
+        return NJS_OK;
+    }
+
+    /* setter */
+
+    if (!njs_value_is_valid_number(arg)) {
+        njs_range_error(vm, "Invalid array length", NULL);
+        return NJS_ERROR;
+    }
+
+    length = (uint32_t) arg->data.u.number;
+
+    if ((double) length != arg->data.u.number) {
+        njs_range_error(vm, "Invalid array length", NULL);
+        return NJS_ERROR;
+    }
+
+    array = value->data.u.array;
+
+    if (length > array->length) {
+        ret = njs_array_expand(vm, array, 0, length - array->length);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        i = array->length;
+        start = array->start;
+
+        while (i < length) {
+            njs_set_invalid(&start[i++]);
+        }
+    }
+
+    array->length = length;
+
+    njs_value_number_set(retval, array->length);
+    return NJS_OK;
 }
 
 
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -2700,6 +2700,38 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = [1,2]; a.length"),
       nxt_string("2") },
 
+    /* Array.length setter */
+
+    { nxt_string("[].length = {}"),
+      nxt_string("RangeError: Invalid array length") },
+
+    { nxt_string("[].length = 2**32"),
+      nxt_string("RangeError: Invalid array length") },
+
+    { nxt_string("var a = []; a.length = 0; JSON.stringify(a)"),
+      nxt_string("[]") },
+
+    { nxt_string("var a = []; a.length = 1; JSON.stringify(a)"),
+      nxt_string("[null]") },
+
+    { nxt_string("var a = [1]; a.length = 1; JSON.stringify(a)"),
+      nxt_string("[1]") },
+
+    { nxt_string("var a = [1]; a.length = 2; JSON.stringify(a)"),
+      nxt_string("[1,null]") },
+
+    { nxt_string("var a = [1]; a.length = 4; a.length = 0; JSON.stringify(a)"),
+      nxt_string("[]") },
+
+    { nxt_string("var a = [1,2,3]; a.length = 2; JSON.stringify(a)"),
+      nxt_string("[1,2]") },
+
+    { nxt_string("var a = [1,2,3]; a.length = 3; a"),
+      nxt_string("1,2,3") },
+
+    { nxt_string("var a = [1,2,3]; a.length = 16; a"),
+      nxt_string("1,2,3,,,,,,,,,,,,,") },
+
     { nxt_string("var a = [1,2,3]; a.join()"),
       nxt_string("1,2,3") },

@drsm
Copy link
Contributor Author

drsm commented Apr 19, 2018

@xeioex the patch works fine. thanks!

@drsm
Copy link
Contributor Author

drsm commented Apr 19, 2018

...

@xeioex
Copy link
Contributor

xeioex commented Apr 19, 2018

@drsm please, create separate issues for each new bug.

@drsm
Copy link
Contributor Author

drsm commented Apr 19, 2018

@xeioex sorry for messing things up. will do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants