]> git.kaiwu.me - njs.git/commitdiff
Refactoring iteration over external objects.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 23 Oct 2019 11:42:38 +0000 (14:42 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Wed, 23 Oct 2019 11:42:38 +0000 (14:42 +0300)
Previously, two callbacks were required to support
array-like iteration for external objects (foreach, next).

Instead using only one callback (keys) to simplify.

nginx/ngx_http_js_module.c
nginx/ngx_stream_js_module.c
src/njs.h
src/njs_extern.c
src/njs_extern.h
src/njs_json.c
src/njs_shell.c
src/njs_value.c
src/njs_vmcode.c
src/test/njs_unit_test.c

index d6502509a41559903b11d12774fd8aaf1fceb3a4..68c49b72817fd5e8121919fd5529544c0744dff5 100644 (file)
@@ -35,12 +35,6 @@ typedef struct {
 } ngx_http_js_ctx_t;
 
 
-typedef struct {
-    ngx_list_part_t     *part;
-    ngx_uint_t           item;
-} ngx_http_js_table_entry_t;
-
-
 typedef struct {
     ngx_http_request_t  *request;
     njs_vm_event_t       vm_event;
@@ -62,10 +56,8 @@ static void ngx_http_js_cleanup_vm(void *data);
 
 static njs_int_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value,
     void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_foreach_header(njs_vm_t *vm, void *obj,
-    void *next, uintptr_t data);
-static njs_int_t ngx_http_js_ext_next_header(njs_vm_t *vm, njs_value_t *value,
-    void *obj, void *next);
+static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, void *obj,
+    njs_value_t *keys, uintptr_t data);
 static ngx_table_elt_t *ngx_http_js_get_header(ngx_list_part_t *part,
     u_char *data, size_t len);
 static njs_int_t ngx_http_js_ext_get_header_out(njs_vm_t *vm,
@@ -74,8 +66,8 @@ static njs_int_t ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj,
     uintptr_t data, njs_str_t *value);
 static njs_int_t ngx_http_js_ext_delete_header_out(njs_vm_t *vm, void *obj,
     uintptr_t data, njs_bool_t delete);
-static njs_int_t ngx_http_js_ext_foreach_header_out(njs_vm_t *vm, void *obj,
-    void *next); /*FIXME*/
+static njs_int_t ngx_http_js_ext_keys_header_out(njs_vm_t *vm, void *obj,
+    njs_value_t *keys); /*FIXME*/
 static njs_int_t ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value,
     void *obj, uintptr_t data);
 static njs_int_t ngx_http_js_ext_set_status(njs_vm_t *vm, void *obj,
@@ -108,14 +100,12 @@ static njs_int_t ngx_http_js_ext_get_request_body(njs_vm_t *vm,
     njs_value_t *value, void *obj, uintptr_t data);
 static njs_int_t ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_value_t *value,
     void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_foreach_header_in(njs_vm_t *vm, void *obj,
-    void *next); /*FIXME*/
+static njs_int_t ngx_http_js_ext_keys_header_in(njs_vm_t *vm, void *obj,
+    njs_value_t *keys); /*FIXME*/
 static njs_int_t ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_value_t *value,
     void *obj, uintptr_t data);
-static njs_int_t ngx_http_js_ext_foreach_arg(njs_vm_t *vm, void *obj,
-    void *next);
-static njs_int_t ngx_http_js_ext_next_arg(njs_vm_t *vm, njs_value_t *value,
-    void *obj, void *next);
+static njs_int_t ngx_http_js_ext_keys_arg(njs_vm_t *vm, void *obj,
+    njs_value_t *keys);
 static njs_int_t ngx_http_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value,
     void *obj, uintptr_t data);
 static njs_int_t ngx_http_js_ext_set_variable(njs_vm_t *vm, void *obj,
@@ -229,7 +219,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       offsetof(ngx_http_request_t, uri) },
 
     { njs_str("method"),
@@ -241,7 +230,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       offsetof(ngx_http_request_t, method_name) },
 
     { njs_str("httpVersion"),
@@ -253,7 +241,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("remoteAddress"),
@@ -265,7 +252,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("parent"),
@@ -277,7 +263,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("requestBody"),
@@ -289,7 +274,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("responseBody"),
@@ -301,7 +285,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("headersIn"),
@@ -311,8 +294,7 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       ngx_http_js_ext_get_header_in,
       NULL,
       NULL,
-      ngx_http_js_ext_foreach_header_in,
-      ngx_http_js_ext_next_header,
+      ngx_http_js_ext_keys_header_in,
       NULL,
       0 },
 
@@ -323,8 +305,7 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       ngx_http_js_ext_get_arg,
       NULL,
       NULL,
-      ngx_http_js_ext_foreach_arg,
-      ngx_http_js_ext_next_arg,
+      ngx_http_js_ext_keys_arg,
       NULL,
       0 },
 
@@ -337,7 +318,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("status"),
@@ -349,7 +329,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       offsetof(ngx_http_request_t, headers_out.status) },
 
     { njs_str("headersOut"),
@@ -359,8 +338,7 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       ngx_http_js_ext_get_header_out,
       ngx_http_js_ext_set_header_out,
       ngx_http_js_ext_delete_header_out,
-      ngx_http_js_ext_foreach_header_out,
-      ngx_http_js_ext_next_header,
+      ngx_http_js_ext_keys_header_out,
       NULL,
       0 },
 
@@ -372,7 +350,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_subrequest,
       0 },
 
@@ -384,7 +361,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_log,
       0 },
 
@@ -396,7 +372,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_warn,
       0 },
 
@@ -408,7 +383,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_error,
       0 },
 
@@ -420,7 +394,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_send_header,
       0 },
 
@@ -432,7 +405,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_send,
       0 },
 
@@ -444,7 +416,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_finish,
       0 },
 
@@ -456,7 +427,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_return,
       0 },
 
@@ -468,7 +438,6 @@ static njs_external_t  ngx_http_js_ext_request[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_http_js_ext_internal_redirect,
       0 },
 };
@@ -485,7 +454,6 @@ static njs_external_t  ngx_http_js_externals[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 };
 
@@ -822,64 +790,79 @@ ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value, void *obj,
 
 
 static njs_int_t
-ngx_http_js_ext_foreach_header(njs_vm_t *vm, void *obj, void *next,
+ngx_http_js_ext_keys_header(njs_vm_t *vm, void *obj, njs_value_t *keys,
     uintptr_t data)
 {
     char *p = obj;
 
-    ngx_list_t                 *headers;
-    ngx_http_request_t         *r;
-    ngx_http_js_table_entry_t  *entry, **e;
-
-    r = (ngx_http_request_t *) obj;
+    njs_int_t         rc, cookie, x_for;
+    ngx_uint_t        item;
+    ngx_list_t       *headers;
+    njs_value_t      *value;
+    ngx_list_part_t  *part;
+    ngx_table_elt_t  *header, *h;
 
-    entry = ngx_palloc(r->pool, sizeof(ngx_http_js_table_entry_t));
-    if (entry == NULL) {
+    rc = njs_vm_array_alloc(vm, keys, 8);
+    if (rc != NJS_OK) {
         return NJS_ERROR;
     }
 
     headers = (ngx_list_t *) (p + data);
+    part = &headers->part;
+    item = 0;
 
-    entry->part = &headers->part;
-    entry->item = 0;
+    cookie = 0;
+    x_for = 0;
 
-    e = (ngx_http_js_table_entry_t **) next;
-    *e = entry;
+    while (part) {
 
-    return NJS_OK;
-}
+        if (item >= part->nelts) {
+            part = part->next;
+            item = 0;
+            continue;
+        }
 
+        header = part->elts;
+        h = &header[item++];
 
-static njs_int_t
-ngx_http_js_ext_next_header(njs_vm_t *vm, njs_value_t *value, void *obj,
-    void *next)
-{
-    ngx_http_js_table_entry_t **e = next;
+        if (h->hash == 0) {
+            continue;
+        }
 
-    ngx_table_elt_t            *header, *h;
-    ngx_http_js_table_entry_t  *entry;
+        if (h->key.len == njs_length("Cookie")
+            && ngx_strncasecmp(h->key.data, (u_char *) "Cookie",
+                               h->key.len) == 0)
+        {
+            if (cookie) {
+                continue;
+            }
 
-    entry = *e;
+            cookie = 1;
+        }
 
-    while (entry->part) {
+        if (h->key.len == njs_length("X-Forwarded-For")
+            && ngx_strncasecmp(h->key.data, (u_char *) "X-Forwarded-For",
+                               h->key.len) == 0)
+        {
+            if (x_for) {
+                continue;
+            }
 
-        if (entry->item >= entry->part->nelts) {
-            entry->part = entry->part->next;
-            entry->item = 0;
-            continue;
+            x_for = 1;
         }
 
-        header = entry->part->elts;
-        h = &header[entry->item++];
-
-        if (h->hash == 0) {
-            continue;
+        value = njs_vm_array_push(vm, keys);
+        if (value == NULL) {
+            return NJS_ERROR;
         }
 
-        return njs_vm_value_string_set(vm, value, h->key.data, h->key.len);
+        rc = njs_vm_value_string_set(vm, value, h->key.data, h->key.len);
+        if (rc != NJS_OK) {
+            return NJS_ERROR;
+        }
     }
 
-    return NJS_DONE;
+    return NJS_OK;
 }
 
 
@@ -1078,9 +1061,9 @@ ngx_http_js_ext_delete_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
 
 
 static njs_int_t
-ngx_http_js_ext_foreach_header_out(njs_vm_t *vm, void *obj, void *next)
+ngx_http_js_ext_keys_header_out(njs_vm_t *vm, void *obj, njs_value_t *keys)
 {
-    return ngx_http_js_ext_foreach_header(vm, obj, next,
+    return ngx_http_js_ext_keys_header(vm, obj, keys,
                              offsetof(ngx_http_request_t, headers_out.headers));
 }
 
@@ -1604,9 +1587,9 @@ multi:
 
 
 static njs_int_t
-ngx_http_js_ext_foreach_header_in(njs_vm_t *vm, void *obj, void *next)
+ngx_http_js_ext_keys_header_in(njs_vm_t *vm, void *obj, njs_value_t *keys)
 {
-    return ngx_http_js_ext_foreach_header(vm, obj, next,
+    return ngx_http_js_ext_keys_header(vm, obj, keys,
                               offsetof(ngx_http_request_t, headers_in.headers));
 }
 
@@ -1632,67 +1615,50 @@ ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_value_t *value, void *obj,
 
 
 static njs_int_t
-ngx_http_js_ext_foreach_arg(njs_vm_t *vm, void *obj, void *next)
+ngx_http_js_ext_keys_arg(njs_vm_t *vm, void *obj, njs_value_t *keys)
 {
-    ngx_str_t           *entry, **e;
+    u_char              *v, *p, *start, *end;
+    njs_int_t            rc;
+    njs_value_t         *value;
     ngx_http_request_t  *r;
 
     r = (ngx_http_request_t *) obj;
 
-    entry = ngx_palloc(r->pool, sizeof(ngx_str_t));
-    if (entry == NULL) {
+    rc = njs_vm_array_alloc(vm, keys, 8);
+    if (rc != NJS_OK) {
         return NJS_ERROR;
     }
 
-    *entry = r->args;
-
-    e = (ngx_str_t **) next;
-    *e = entry;
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-ngx_http_js_ext_next_arg(njs_vm_t *vm, njs_value_t *value, void *obj,
-    void *next)
-{
-    ngx_str_t **e = next;
-
-    size_t      len;
-    u_char     *v, *p, *start, *end;
-    ngx_str_t  *entry;
-
-    entry = *e;
-
-    if (entry->len == 0) {
-        return NJS_DONE;
-    }
-
-    start = entry->data;
-    end = start + entry->len;
+    start = r->args.data;
+    end = start + r->args.len;
 
-    p = ngx_strlchr(start, end, '&');
-    if (p == NULL) {
-        p = end;
-    }
+    while (start < end) {
+        p = ngx_strlchr(start, end, '&');
+        if (p == NULL) {
+            p = end;
+        }
 
-    v = ngx_strlchr(start, p, '=');
-    if (v == NULL) {
-        v = p;
-    }
+        v = ngx_strlchr(start, p, '=');
+        if (v == NULL) {
+            v = p;
+        }
 
-    len = v - start;
+        if (v != start) {
+            value = njs_vm_array_push(vm, keys);
+            if (value == NULL) {
+                return NJS_ERROR;
+            }
 
-    if (p != end) {
-        entry->data = &p[1];
-        entry->len = end - entry->data;
+            rc = njs_vm_value_string_set(vm, value, start, v - start);
+            if (rc != NJS_OK) {
+                return NJS_ERROR;
+            }
+        }
 
-    } else {
-        entry->len = 0;
+        start = p + 1;
     }
 
-    return njs_vm_value_string_set(vm, value, start, len);
+    return NJS_OK;
 }
 
 
index d6f07e5ba94c3664402b54795957ef37d132be5a..00610dfac638c5270b23314d4dbc12f1d37e990a 100644 (file)
@@ -211,7 +211,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("variables"),
@@ -223,7 +222,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("allow"),
@@ -234,7 +232,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_done,
       0 },
 
@@ -246,7 +243,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_deny,
       0 },
 
@@ -258,7 +254,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_decline,
       0 },
 
@@ -270,7 +265,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_done,
       0 },
 
@@ -282,7 +276,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_log,
       0 },
 
@@ -294,7 +287,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_warn,
       0 },
 
@@ -306,7 +298,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_error,
       0 },
 
@@ -318,7 +309,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_on,
       0 },
 
@@ -330,7 +320,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_off,
       0 },
 
@@ -342,7 +331,6 @@ static njs_external_t  ngx_stream_js_ext_session[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       ngx_stream_js_ext_send,
       0 },
 
@@ -360,7 +348,6 @@ static njs_external_t  ngx_stream_js_externals[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 };
 
index 2d37fee3a2d54451cf5bc253505fbc6d4908f0b8..90be1e571a2f48f63563fd3cb66be5f228a3f8a2 100644 (file)
--- a/src/njs.h
+++ b/src/njs.h
@@ -66,9 +66,8 @@ typedef njs_int_t (*njs_extern_set_t)(njs_vm_t *vm, void *obj, uintptr_t data,
     njs_str_t *value);
 typedef njs_int_t (*njs_extern_find_t)(njs_vm_t *vm, void *obj, uintptr_t data,
     njs_bool_t delete);
-typedef njs_int_t (*njs_extern_foreach_t)(njs_vm_t *vm, void *obj, void *next);
-typedef njs_int_t (*njs_extern_next_t)(njs_vm_t *vm, njs_value_t *value,
-    void *obj, void *next);
+typedef njs_int_t (*njs_extern_keys_t)(njs_vm_t *vm, void *obj,
+    njs_value_t *keys);
 typedef njs_int_t (*njs_extern_method_t)(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 
@@ -92,8 +91,7 @@ struct njs_external_s {
     njs_extern_set_t                set;
     njs_extern_find_t               find;
 
-    njs_extern_foreach_t            foreach;
-    njs_extern_next_t               next;
+    njs_extern_keys_t               keys;
 
     njs_extern_method_t             method;
 
index 73b952bbb70de557e46a99cb4db7557a1fd2dc26..a7d8d0b653ed3a47c5ab1b46e2138d46868a0aa2 100644 (file)
@@ -89,8 +89,7 @@ njs_vm_external_add(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_external_t *external,
         ext->get = external->get;
         ext->set = external->set;
         ext->find = external->find;
-        ext->foreach = external->foreach;
-        ext->next = external->next;
+        ext->keys = external->keys;
         ext->data = external->data;
 
         if (external->method != NULL) {
index c11557b3d0e14ceb04b6afe9730bdf87238b0f4c..6a3d9ab517aee92f512c25bcb4bb2f98c39902f1 100644 (file)
@@ -26,8 +26,7 @@ struct njs_extern_s {
     njs_extern_set_t             set;
     njs_extern_find_t            find;
 
-    njs_extern_foreach_t         foreach;
-    njs_extern_next_t            next;
+    njs_extern_keys_t            keys;
 
     njs_function_t               *function;
 
index 45e24db4374ad12408a36f11e25a556c2d6ad331..9dff714820708af8fae89901df23ba9d7665c279 100644 (file)
@@ -2295,12 +2295,8 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
             njs_dump_item("\"find\"");
         }
 
-        if (ext_proto->foreach != NULL) {
-            njs_dump_item("\"foreach\"");
-        }
-
-        if (ext_proto->next != NULL) {
-            njs_dump_item("\"next\"");
+        if (ext_proto->keys != NULL) {
+            njs_dump_item("\"keys\"");
         }
 
         return njs_json_buf_append(stringify, "]}", 2);
index 68277dfea2c5c0a2faea156b4a700edf2a66009e..07f7108fc83704a9706c9a7ad6023352a83d7742 100644 (file)
@@ -112,7 +112,6 @@ static njs_external_t  njs_ext_console[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_ext_console_log,
       0 },
 
@@ -124,7 +123,6 @@ static njs_external_t  njs_ext_console[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_ext_console_dump,
       0 },
 
@@ -136,7 +134,6 @@ static njs_external_t  njs_ext_console[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_ext_console_help,
       0 },
 
@@ -148,7 +145,6 @@ static njs_external_t  njs_ext_console[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_ext_console_time,
       0 },
 
@@ -160,7 +156,6 @@ static njs_external_t  njs_ext_console[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_ext_console_time_end,
       0 },
 };
@@ -176,7 +171,6 @@ static njs_external_t  njs_externals[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 };
 
index 7f23f15939b0976c48b1508f9db2e7e7c108edd6..4d0cf7e80921937d4216cb636fc5eaf3bb5dca93 100644 (file)
@@ -191,13 +191,34 @@ njs_array_t *
 njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
     njs_object_enum_t kind, njs_bool_t all)
 {
+    void                *obj;
+    njs_int_t           ret;
+    njs_value_t         keys;
     njs_object_value_t  obj_val;
+    const njs_extern_t  *ext_proto;
 
     if (njs_is_object(value)) {
         return njs_object_enumerate(vm, njs_object(value), kind, all);
     }
 
     if (value->type != NJS_STRING) {
+        if (kind == NJS_ENUM_KEYS && njs_is_external(value)) {
+            ext_proto = value->external.proto;
+
+            if (ext_proto->keys != NULL) {
+                obj = njs_extern_object(vm, value);
+
+                ret = ext_proto->keys(vm, obj, &keys);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return NULL;
+                }
+
+                return njs_array(&keys);
+            }
+
+            return njs_extern_keys_array(vm, ext_proto);
+        }
+
         return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
     }
 
@@ -212,13 +233,34 @@ njs_array_t *
 njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
     njs_object_enum_t kind, njs_bool_t all)
 {
+    void                *obj;
+    njs_int_t           ret;
+    njs_value_t         keys;
     njs_object_value_t  obj_val;
+    const njs_extern_t  *ext_proto;
 
     if (njs_is_object(value)) {
         return njs_object_own_enumerate(vm, njs_object(value), kind, all);
     }
 
     if (value->type != NJS_STRING) {
+        if (kind == NJS_ENUM_KEYS && njs_is_external(value)) {
+            ext_proto = value->external.proto;
+
+            if (ext_proto->keys != NULL) {
+                obj = njs_extern_object(vm, value);
+
+                ret = ext_proto->keys(vm, obj, &keys);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return NULL;
+                }
+
+                return njs_array(&keys);
+            }
+
+            return njs_extern_keys_array(vm, ext_proto);
+        }
+
         return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
     }
 
index 77993ed3d2380f2484f9e705ea438e059e0a6989..68d5a372e8ce3c8eb645bf54acd6d8ebda693b0f 100644 (file)
@@ -31,8 +31,6 @@ static njs_jump_off_t njs_vmcode_property_in(njs_vm_t *vm,
     njs_value_t *value, njs_value_t *key);
 static njs_jump_off_t njs_vmcode_property_foreach(njs_vm_t *vm,
     njs_value_t *object, njs_value_t *invld, u_char *pc);
-static njs_jump_off_t njs_vmcode_property_next(njs_vm_t *vm,
-    njs_value_t *object, njs_value_t *value, u_char *pc);
 static njs_jump_off_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *constructor);
 static njs_jump_off_t njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value,
@@ -771,25 +769,21 @@ next:
                 break;
 
             case NJS_VMCODE_PROPERTY_NEXT:
-                if (!njs_is_external(value1)) {
-                    pnext = (njs_vmcode_prop_next_t *) pc;
-                    retval = njs_vmcode_operand(vm, pnext->retval);
+                pnext = (njs_vmcode_prop_next_t *) pc;
+                retval = njs_vmcode_operand(vm, pnext->retval);
 
-                    next = value2->data.u.next;
+                next = value2->data.u.next;
 
-                    if (next->index < next->array->length) {
-                        *retval = next->array->data[next->index++];
+                if (next->index < next->array->length) {
+                    *retval = next->array->data[next->index++];
 
-                        ret = pnext->offset;
-                        break;
-                    }
+                    ret = pnext->offset;
+                    break;
                 }
 
-                ret = njs_vmcode_property_next(vm, value1, value2, pc);
-                if (njs_slow_path(ret == NJS_ERROR)) {
-                    goto error;
-                }
+                njs_mp_free(vm->mem_pool, next);
 
+                ret = sizeof(njs_vmcode_prop_next_t);
                 break;
 
             case NJS_VMCODE_THIS:
@@ -1306,27 +1300,9 @@ static njs_jump_off_t
 njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *invld, u_char *pc)
 {
-    void                       *obj;
-    njs_jump_off_t             ret;
-    const njs_extern_t         *ext_proto;
     njs_property_next_t        *next;
     njs_vmcode_prop_foreach_t  *code;
 
-    if (njs_is_external(object)) {
-        ext_proto = object->external.proto;
-
-        if (ext_proto->foreach != NULL) {
-            obj = njs_extern_object(vm, object);
-
-            ret = ext_proto->foreach(vm, obj, &vm->retval);
-            if (njs_slow_path(ret != NJS_OK)) {
-                return ret;
-            }
-        }
-
-        goto done;
-    }
-
     next = njs_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
     if (njs_slow_path(next == NULL)) {
         njs_memory_error(vm);
@@ -1342,64 +1318,12 @@ njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
 
     vm->retval.data.u.next = next;
 
-done:
-
     code = (njs_vmcode_prop_foreach_t *) pc;
 
     return code->offset;
 }
 
 
-static njs_jump_off_t
-njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object, njs_value_t *value,
-    u_char *pc)
-{
-    void                    *obj;
-    njs_value_t             *retval;
-    njs_jump_off_t          ret;
-    njs_property_next_t     *next;
-    const njs_extern_t      *ext_proto;
-    njs_vmcode_prop_next_t  *code;
-
-    code = (njs_vmcode_prop_next_t *) pc;
-    retval = njs_vmcode_operand(vm, code->retval);
-
-    if (njs_is_external(object)) {
-        ext_proto = object->external.proto;
-
-        if (ext_proto->next != NULL) {
-            obj = njs_extern_object(vm, object);
-
-            ret = ext_proto->next(vm, retval, obj, value);
-
-            if (ret == NJS_OK) {
-                return code->offset;
-            }
-
-            if (njs_slow_path(ret == NJS_ERROR)) {
-                return ret;
-            }
-
-            /* 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;
-    }
-
-    njs_mp_free(vm->mem_pool, next);
-
-    return sizeof(njs_vmcode_prop_next_t);
-}
-
-
 static njs_jump_off_t
 njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *constructor)
index c929b3e28a776c31afdecc9ddd2932c438f07512..fecd3d4c55e0ae39c5f8f7fdff8cd2bdb1605d5f 100644 (file)
@@ -14029,7 +14029,7 @@ static njs_unit_test_t  njs_test[] =
       njs_str("{a:{type:\"property\",props:[\"getter\"]},b:{type:\"property\",props:[\"getter\"]}}") },
 
     { njs_str("njs.dump($r.header)"),
-      njs_str("{type:\"object\",props:[\"getter\",\"foreach\",\"next\"]}") },
+      njs_str("{type:\"object\",props:[\"getter\",\"keys\"]}") },
 
     { njs_str("njs.dump(njs) == `{version:'${njs.version}'}`"),
       njs_str("true") },
@@ -14988,32 +14988,32 @@ njs_unit_test_header_external(njs_vm_t *vm, njs_value_t *value, void *obj,
 
 
 static njs_int_t
-njs_unit_test_header_foreach_external(njs_vm_t *vm, void *obj, void *next)
+njs_unit_test_header_keys_external(njs_vm_t *vm, void *obj, njs_value_t *keys)
 {
-    u_char  *s;
+    njs_int_t    rc, i;
+    njs_value_t  *value;
+    u_char       k[2];
 
-    s = next;
-    s[0] = '0';
-    s[1] = '0';
-
-    return NJS_OK;
-}
+    rc = njs_vm_array_alloc(vm, keys, 4);
+    if (rc != NJS_OK) {
+        return NJS_ERROR;
+    }
 
+    k[0] = '0';
+    k[1] = '1';
 
-static njs_int_t
-njs_unit_test_header_next_external(njs_vm_t *vm, njs_value_t *value, void *obj,
-    void *next)
-{
-    u_char  *s;
+    for (i = 0; i < 3; i++) {
+        value = njs_vm_array_push(vm, keys);
+        if (value == NULL) {
+            return NJS_ERROR;
+        }
 
-    s = next;
-    s[1]++;
+        (void) njs_vm_value_string_set(vm, value, k, 2);
 
-    if (s[1] == '4') {
-        return NJS_DONE;
+        k[1]++;
     }
 
-    return njs_vm_value_string_set(vm, value, s, 2);
+    return NJS_OK;
 }
 
 
@@ -15102,7 +15102,6 @@ static njs_external_t  njs_unit_test_r_props[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("b"),
@@ -15114,7 +15113,6 @@ static njs_external_t  njs_unit_test_r_props[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       42 },
 };
 
@@ -15130,7 +15128,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       offsetof(njs_unit_test_req_t, uri) },
 
     { njs_str("host"),
@@ -15142,7 +15139,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("props"),
@@ -15154,7 +15150,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("vars"),
@@ -15166,7 +15161,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       njs_unit_test_r_del_vars,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("consts"),
@@ -15178,7 +15172,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
     { njs_str("header"),
@@ -15188,8 +15181,7 @@ static njs_external_t  njs_unit_test_r_external[] = {
       njs_unit_test_header_external,
       NULL,
       NULL,
-      njs_unit_test_header_foreach_external,
-      njs_unit_test_header_next_external,
+      njs_unit_test_header_keys_external,
       NULL,
       0 },
 
@@ -15201,7 +15193,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_unit_test_method_external,
       0 },
 
@@ -15213,7 +15204,6 @@ static njs_external_t  njs_unit_test_r_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       njs_unit_test_create_external,
       0 },
 
@@ -15231,7 +15221,6 @@ static njs_external_t  njs_test_external[] = {
       NULL,
       NULL,
       NULL,
-      NULL,
       0 },
 
 };