[njs] Walking over prototypes chain during iteration over an object.

Alexander Borisov alexander.borisov at nginx.com
Thu Apr 18 16:10:17 UTC 2019


details:   https://hg.nginx.org/njs/rev/7d2d28095c42
branches:  
changeset: 907:7d2d28095c42
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Wed Apr 17 18:00:56 2019 +0300
description:
Walking over prototypes chain during iteration over an object.

This closes #33 issue on Github.

diffstat:

 njs/njs_vm.c             |  93 ++++++++++++++++++-----------------------------
 njs/test/njs_unit_test.c |  56 ++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 58 deletions(-)

diffs (209 lines):

diff -r 519785f57b81 -r 7d2d28095c42 njs/njs_vm.c
--- a/njs/njs_vm.c	Wed Apr 17 17:27:14 2019 +0300
+++ b/njs/njs_vm.c	Wed Apr 17 18:00:56 2019 +0300
@@ -10,8 +10,8 @@
 
 
 struct njs_property_next_s {
-    int32_t                        index;
-    nxt_lvlhsh_each_t              lhe;
+    uint32_t     index;
+    njs_array_t  *array;
 };
 
 
@@ -764,23 +764,7 @@ njs_vmcode_property_foreach(njs_vm_t *vm
     const njs_extern_t         *ext_proto;
     njs_vmcode_prop_foreach_t  *code;
 
-    if (njs_is_object(object)) {
-        next = nxt_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
-        if (nxt_slow_path(next == NULL)) {
-            njs_memory_error(vm);
-            return NXT_ERROR;
-        }
-
-        vm->retval.data.u.next = next;
-
-        nxt_lvlhsh_each_init(&next->lhe, &njs_object_hash_proto);
-        next->index = -1;
-
-        if (njs_is_array(object) && object->data.u.array->length != 0) {
-            next->index = 0;
-        }
-
-    } else if (njs_is_external(object)) {
+    if (njs_is_external(object)) {
         ext_proto = object->external.proto;
 
         if (ext_proto->foreach != NULL) {
@@ -791,8 +775,27 @@ njs_vmcode_property_foreach(njs_vm_t *vm
                 return ret;
             }
         }
+
+        goto done;
     }
 
+    next = nxt_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
+    if (nxt_slow_path(next == NULL)) {
+        njs_memory_error(vm);
+        return NXT_ERROR;
+    }
+
+    next->index = 0;
+    next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, 0);
+    if (nxt_slow_path(next->array == NULL)) {
+        njs_memory_error(vm);
+        return NXT_ERROR;
+    }
+
+    vm->retval.data.u.next = next;
+
+done:
+
     code = (njs_vmcode_prop_foreach_t *) vm->current;
 
     return code->offset;
@@ -804,10 +807,7 @@ njs_vmcode_property_next(njs_vm_t *vm, n
 {
     void                    *obj;
     njs_ret_t               ret;
-    nxt_uint_t              n;
     njs_value_t             *retval;
-    njs_array_t             *array;
-    njs_object_prop_t       *prop;
     njs_property_next_t     *next;
     const njs_extern_t      *ext_proto;
     njs_vmcode_prop_next_t  *code;
@@ -815,42 +815,7 @@ njs_vmcode_property_next(njs_vm_t *vm, n
     code = (njs_vmcode_prop_next_t *) vm->current;
     retval = njs_vmcode_operand(vm, code->retval);
 
-    if (njs_is_object(object)) {
-        next = value->data.u.next;
-
-        if (next->index >= 0) {
-            array = object->data.u.array;
-
-            while ((uint32_t) next->index < array->length) {
-                n = next->index++;
-
-                if (njs_is_valid(&array->start[n])) {
-                    njs_uint32_to_string(retval, n);
-
-                    return code->offset;
-                }
-            }
-
-            next->index = -1;
-        }
-
-        for ( ;; ) {
-            prop = nxt_lvlhsh_each(&object->data.u.object->hash, &next->lhe);
-
-            if (prop == NULL) {
-                break;
-            }
-
-            if (prop->type != NJS_WHITEOUT && prop->enumerable) {
-                *retval = prop->name;
-
-                return code->offset;
-            }
-        }
-
-        nxt_mp_free(vm->mem_pool, next);
-
-    } else if (njs_is_external(object)) {
+    if (njs_is_external(object)) {
         ext_proto = object->external.proto;
 
         if (ext_proto->next != NULL) {
@@ -868,8 +833,20 @@ njs_vmcode_property_next(njs_vm_t *vm, n
 
             /* ret == NJS_DONE. */
         }
+
+        return sizeof(njs_vmcode_prop_next_t);
     }
 
+    next = value->data.u.next;
+
+    if (next->index < next->array->length) {
+        *retval = next->array->data[ next->index++ ];
+
+        return code->offset;
+    }
+
+    nxt_mp_free(vm->mem_pool, next);
+
     return sizeof(njs_vmcode_prop_next_t);
 }
 
diff -r 519785f57b81 -r 7d2d28095c42 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Wed Apr 17 17:27:14 2019 +0300
+++ b/njs/test/njs_unit_test.c	Wed Apr 17 18:00:56 2019 +0300
@@ -7819,6 +7819,62 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.prototype.__proto__.f()"),
       nxt_string("TypeError: cannot get property \"f\" of undefined") },
 
+    { nxt_string("var obj = Object.create(null); obj.one = 1;"
+                 "var res = [];"
+                 "for (var val in obj) res.push(val); res"),
+      nxt_string("one") },
+
+    { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+                 "var o2 = Object.create(o1); o2.two = 2;"
+                 "var o3 = Object.create(o2); o3.three = 3;"
+                 "var res = [];"
+                 "for (var val in o3) res.push(val); res"),
+      nxt_string("three,two,one") },
+
+    { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+                 "var o2 = Object.create(o1);"
+                 "var o3 = Object.create(o2); o3.three = 3;"
+                 "var res = [];"
+                 "for (var val in o3) res.push(val); res"),
+      nxt_string("three,one") },
+
+    { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+                 "var o2 = Object.create(o1);"
+                 "var o3 = Object.create(o2);"
+                 "var res = [];"
+                 "for (var val in o3) res.push(val); res"),
+      nxt_string("one") },
+
+    { nxt_string("var o1 = Object.create(null); o1.one = 1;"
+                 "var o2 = Object.create(o1); o2.two = 2;"
+                 "var o3 = Object.create(o2); o3.three = 3;"
+                 "o3.two = -2; o3.one = -1;"
+                 "var res = [];"
+                 "for (var val in o3) res.push(val); res"),
+      nxt_string("three,two,one") },
+
+    { nxt_string("var a = []; for(var p in 'abc') a.push(p); a"),
+      nxt_string("0,1,2") },
+
+    { nxt_string("var a = []; for(var p in Object('abc')) a.push(p); a"),
+      nxt_string("0,1,2") },
+
+    { nxt_string("var o = Object('abc'); var x = Object.create(o);"
+                 "x.a = 1; x.b = 2;"
+                 "var a = []; for(var p in x) a.push(p); a"),
+      nxt_string("a,b,0,1,2") },
+
+#if 0
+    /* TODO: No properties implementation for array type
+     * (enumerable, writable, configurable).
+     */
+
+    { nxt_string("var o = Object("abc"); var x = Object.create(o);"
+                 "x['sd'] = 44; x[1] = 8; x[55] = 8;"
+                 "Object.keys(x)"),
+      nxt_string("55,sd") },
+#endif
+
     { nxt_string("Object.prototype.toString.call(Object.prototype)"),
       nxt_string("[object Object]") },
 


More information about the nginx-devel mailing list