Refactored working with external prototypes.

Previously, njs_vm_external_prototype() returned the pointer to a
created prototype structure. Which were expected to be passed to
njs_vm_external_create() as is.  The returned pointer is needed to be
stored somewhere by user code which complicates user code in cases when
many prototypes are created.

Instead, an index in the VM internal table is returned.
njs_vm_external_create() is changed accordingly.  This simplifies
user code because the index is known at static time for most cases.
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
index 9a6c5cd..5bf28d3 100644
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -19,7 +19,6 @@
     ngx_uint_t             line;
     ngx_array_t           *imports;
     ngx_array_t           *paths;
-    njs_external_proto_t   req_proto;
 } ngx_http_js_main_conf_t;
 
 
@@ -836,7 +835,7 @@
     }
 
     rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request),
-                                jmcf->req_proto, r, 0);
+                                NGX_JS_PROTO_MAIN, r, 0);
     if (rc != NJS_OK) {
         return NGX_ERROR;
     }
@@ -2708,10 +2707,9 @@
 {
     njs_vm_event_t  vm_event = data;
 
-    njs_int_t                 ret;
-    ngx_http_js_ctx_t        *ctx;
-    njs_opaque_value_t        reply;
-    ngx_http_js_main_conf_t  *jmcf;
+    njs_int_t            ret;
+    ngx_http_js_ctx_t   *ctx;
+    njs_opaque_value_t   reply;
 
     if (rc != NGX_OK || r->connection->error || r->buffered) {
         return rc;
@@ -2734,8 +2732,6 @@
 
     ctx->done = 1;
 
-    jmcf = ngx_http_get_module_main_conf(r, ngx_http_js_module);
-
     ctx = ngx_http_get_module_ctx(r->parent, ngx_http_js_module);
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -2750,7 +2746,7 @@
     }
 
     ret = njs_vm_external_create(ctx->vm, njs_value_arg(&reply),
-                                 jmcf->req_proto, r, 0);
+                                 NGX_JS_PROTO_MAIN, r, 0);
     if (ret != NJS_OK) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "js subrequest reply creation failed");
@@ -2954,7 +2950,7 @@
     ssize_t                n;
     ngx_fd_t               fd;
     ngx_str_t             *m, file;
-    njs_int_t              rc;
+    njs_int_t              rc, proto_id;
     njs_str_t              text, path;
     ngx_uint_t             i;
     njs_value_t           *value;
@@ -2962,7 +2958,6 @@
     ngx_file_info_t        fi;
     ngx_pool_cleanup_t    *cln;
     njs_opaque_value_t     lvalue, exception;
-    njs_external_proto_t   proto;
     ngx_http_js_import_t  *import;
 
     static const njs_str_t line_number_key = njs_str("lineNumber");
@@ -3114,16 +3109,14 @@
         }
     }
 
-    proto = njs_vm_external_prototype(jmcf->vm, ngx_http_js_ext_request,
-                                      njs_nitems(ngx_http_js_ext_request));
-    if (proto == NULL) {
+    proto_id = njs_vm_external_prototype(jmcf->vm, ngx_http_js_ext_request,
+                                         njs_nitems(ngx_http_js_ext_request));
+    if (proto_id < 0) {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "failed to add js request proto");
         return NGX_CONF_ERROR;
     }
 
-    jmcf->req_proto = proto;
-
     rc = ngx_js_core_init(jmcf->vm, cf->log);
     if (njs_slow_path(rc != NJS_OK)) {
         return NGX_CONF_ERROR;
@@ -3379,7 +3372,6 @@
      *     conf->include = { 0, NULL };
      *     conf->file = NULL;
      *     conf->line = 0;
-     *     conf->req_proto = NULL;
      */
 
     conf->paths = NGX_CONF_UNSET_PTR;
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index 56b2bdd..f47b916 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -117,19 +117,18 @@
 ngx_int_t
 ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log)
 {
-    njs_int_t             ret;
-    njs_str_t             name;
-    njs_opaque_value_t    value;
-    njs_external_proto_t  proto;
+    njs_int_t           ret, proto_id;
+    njs_str_t           name;
+    njs_opaque_value_t  value;
 
-    proto = njs_vm_external_prototype(vm, ngx_js_ext_core,
-                                      njs_nitems(ngx_js_ext_core));
-    if (proto == NULL) {
+    proto_id = njs_vm_external_prototype(vm, ngx_js_ext_core,
+                                         njs_nitems(ngx_js_ext_core));
+    if (proto_id < 0) {
         ngx_log_error(NGX_LOG_EMERG, log, 0, "failed to add js core proto");
         return NGX_ERROR;
     }
 
-    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto, NULL, 1);
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
     if (njs_slow_path(ret != NJS_OK)) {
         ngx_log_error(NGX_LOG_EMERG, log, 0,
                       "njs_vm_external_create() failed\n");
diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h
index 0e290d5..fe31124 100644
--- a/nginx/ngx_js.h
+++ b/nginx/ngx_js.h
@@ -19,6 +19,8 @@
 #define NGX_JS_STRING  1
 #define NGX_JS_BUFFER  2
 
+#define NGX_JS_PROTO_MAIN      0
+
 
 #define ngx_external_connection(vm, ext)                                    \
     (*((ngx_connection_t **) ((u_char *) ext + njs_vm_meta(vm, 0))))
diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c
index 0830b10..91852ba 100644
--- a/nginx/ngx_stream_js_module.c
+++ b/nginx/ngx_stream_js_module.c
@@ -19,7 +19,6 @@
     ngx_uint_t             line;
     ngx_array_t           *imports;
     ngx_array_t           *paths;
-    njs_external_proto_t   proto;
 } ngx_stream_js_main_conf_t;
 
 
@@ -696,7 +695,7 @@
     }
 
     rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->args[0]),
-                                jmcf->proto, s, 0);
+                                NGX_JS_PROTO_MAIN, s, 0);
     if (rc != NJS_OK) {
         return NGX_ERROR;
     }
@@ -1306,7 +1305,7 @@
     ssize_t                  n;
     ngx_fd_t                 fd;
     ngx_str_t               *m, file;
-    njs_int_t                rc;
+    njs_int_t                rc, proto_id;
     njs_str_t                text, path;
     ngx_uint_t               i;
     njs_value_t             *value;
@@ -1314,7 +1313,6 @@
     ngx_file_info_t          fi;
     ngx_pool_cleanup_t      *cln;
     njs_opaque_value_t       lvalue, exception;
-    njs_external_proto_t     proto;
     ngx_stream_js_import_t  *import;
 
     static const njs_str_t line_number_key = njs_str("lineNumber");
@@ -1466,16 +1464,14 @@
         }
     }
 
-    proto = njs_vm_external_prototype(jmcf->vm, ngx_stream_js_ext_session,
-                                      njs_nitems(ngx_stream_js_ext_session));
-    if (proto == NULL) {
+    proto_id = njs_vm_external_prototype(jmcf->vm, ngx_stream_js_ext_session,
+                                         njs_nitems(ngx_stream_js_ext_session));
+    if (proto_id < 0) {
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                       "failed to add js request proto");
         return NGX_CONF_ERROR;
     }
 
-    jmcf->proto = proto;
-
     rc = ngx_js_core_init(jmcf->vm, cf->log);
     if (njs_slow_path(rc != NJS_OK)) {
         return NGX_CONF_ERROR;
@@ -1708,7 +1704,6 @@
      *     conf->include = { 0, NULL };
      *     conf->file = NULL;
      *     conf->line = 0;
-     *     conf->proto = NULL;
      */
 
     conf->paths = NGX_CONF_UNSET_PTR;
diff --git a/src/njs.h b/src/njs.h
index 42261a6..7af86fa 100644
--- a/src/njs.h
+++ b/src/njs.h
@@ -29,7 +29,6 @@
 typedef struct njs_vm_shared_s      njs_vm_shared_t;
 typedef struct njs_object_prop_s    njs_object_prop_t;
 typedef struct njs_external_s       njs_external_t;
-typedef void *                      njs_external_proto_t;
 
 /*
  * njs_opaque_value_t is the external storage type for native njs_value_t type.
@@ -297,10 +296,10 @@
 
 NJS_EXPORT njs_int_t njs_vm_add_path(njs_vm_t *vm, const njs_str_t *path);
 
-NJS_EXPORT njs_external_proto_t njs_vm_external_prototype(njs_vm_t *vm,
+NJS_EXPORT njs_int_t njs_vm_external_prototype(njs_vm_t *vm,
     const njs_external_t *definition, njs_uint_t n);
 NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm, njs_value_t *value,
-    njs_external_proto_t proto, njs_external_ptr_t external, njs_bool_t shared);
+    njs_int_t proto_id, njs_external_ptr_t external, njs_bool_t shared);
 NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm,
     const njs_value_t *value);
 NJS_EXPORT uintptr_t njs_vm_meta(njs_vm_t *vm, njs_uint_t index);
diff --git a/src/njs_extern.c b/src/njs_extern.c
index a2dfd01..a5de5b4 100644
--- a/src/njs_extern.c
+++ b/src/njs_extern.c
@@ -256,12 +256,13 @@
 }
 
 
-njs_external_proto_t
+njs_int_t
 njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition,
     njs_uint_t n)
 {
     njs_arr_t   *protos;
     njs_int_t   ret;
+    uintptr_t   *pr;
     njs_uint_t  size;
 
     size = njs_external_protos(definition, n) + 1;
@@ -269,38 +270,55 @@
     protos = njs_arr_create(vm->mem_pool, size, sizeof(njs_exotic_slots_t));
     if (njs_slow_path(protos == NULL)) {
         njs_memory_error(vm);
-        return NULL;
+        return -1;
     }
 
     ret = njs_external_add(vm, protos, definition, n);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_internal_error(vm, "njs_vm_external_add() failed");
-        return NULL;
+        return -1;
     }
 
-    return protos;
+    if (vm->protos == NULL) {
+        vm->protos = njs_arr_create(vm->mem_pool, 4, sizeof(uintptr_t));
+        if (njs_slow_path(vm->protos == NULL)) {
+            return -1;
+        }
+    }
+
+    pr = njs_arr_add(vm->protos);
+    if (njs_slow_path(pr == NULL)) {
+        return -1;
+    }
+
+    *pr = (uintptr_t) protos;
+
+    return vm->protos->items - 1;
 }
 
 
 njs_int_t
-njs_vm_external_create(njs_vm_t *vm, njs_value_t *value,
-    njs_external_proto_t proto, njs_external_ptr_t external, njs_bool_t shared)
+njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, njs_int_t proto_id,
+    njs_external_ptr_t external, njs_bool_t shared)
 {
     njs_arr_t           *protos;
+    uintptr_t           proto;
     njs_object_value_t  *ov;
     njs_exotic_slots_t  *slots;
 
-    if (njs_slow_path(proto == NULL)) {
+    if (vm->protos == NULL || (njs_int_t) vm->protos->items <= proto_id) {
         return NJS_ERROR;
     }
 
+    proto = ((uintptr_t *) vm->protos->start)[proto_id];
+
     ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
     if (njs_slow_path(ov == NULL)) {
         njs_memory_error(vm);
         return NJS_ERROR;
     }
 
-    protos = proto;
+    protos = (njs_arr_t *) proto;
     slots = protos->start;
 
     njs_lvlhsh_init(&ov->object.hash);
diff --git a/src/njs_shell.c b/src/njs_shell.c
index 8a87842..be7a90d 100644
--- a/src/njs_shell.c
+++ b/src/njs_shell.c
@@ -653,12 +653,11 @@
 njs_external_add(njs_vm_t *vm, njs_external_t *definition,
     njs_uint_t n, const njs_str_t *name, njs_external_ptr_t external)
 {
-    njs_int_t             ret;
-    njs_value_t           *value;
-    njs_external_proto_t  proto;
+    njs_int_t    ret, proto_id;
+    njs_value_t  *value;
 
-    proto = njs_vm_external_prototype(vm, definition, n);
-    if (njs_slow_path(proto == NULL)) {
+    proto_id = njs_vm_external_prototype(vm, definition, n);
+    if (njs_slow_path(proto_id < 0)) {
         njs_stderror("failed to add \"%V\" proto\n", name);
         return NULL;
     }
@@ -668,7 +667,7 @@
         return NULL;
     }
 
-    ret = njs_vm_external_create(vm, value, proto, external, 0);
+    ret = njs_vm_external_create(vm, value, proto_id, external, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         return NULL;
     }
diff --git a/src/njs_vm.h b/src/njs_vm.h
index 8727621..3f63b7a 100644
--- a/src/njs_vm.h
+++ b/src/njs_vm.h
@@ -180,6 +180,7 @@
     njs_value_t              retval;
 
     njs_arr_t                *paths;
+    njs_arr_t                *protos;
 
     njs_value_t              *scopes[NJS_SCOPES];
 
diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c
index 0025f35..688503d 100644
--- a/src/test/njs_benchmark.c
+++ b/src/test/njs_benchmark.c
@@ -36,13 +36,12 @@
     u_char                *start;
     njs_vm_t              *vm, *nvm;
     uint64_t              us;
-    njs_int_t             ret;
+    njs_int_t             ret, proto_id;
     njs_str_t             s, *expected;
     njs_uint_t            i, n;
     njs_bool_t            success;
     njs_value_t           *result, name, usec, times;
     njs_vm_opt_t          options;
-    njs_external_proto_t  proto;
 
     static const njs_value_t  name_key = njs_string("name");
     static const njs_value_t  usec_key = njs_string("usec");
@@ -68,8 +67,8 @@
         goto done;
     }
 
-    proto = njs_externals_shared_init(vm);
-    if (proto == NULL) {
+    proto_id = njs_externals_shared_init(vm);
+    if (proto_id < 0) {
         goto done;
     }
 
diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c
index cdb89da..f87e381 100644
--- a/src/test/njs_externals_test.c
+++ b/src/test/njs_externals_test.c
@@ -11,7 +11,7 @@
 
 typedef struct {
     njs_lvlhsh_t          hash;
-    njs_external_proto_t  proto;
+    njs_int_t             proto_id;
 
     uint32_t              a;
     uint32_t              d;
@@ -394,9 +394,9 @@
         return NJS_ERROR;
     }
 
-    sr->proto = r->proto;
+    sr->proto_id = r->proto_id;
 
-    ret = njs_vm_external_create(vm, &vm->retval, sr->proto, sr, 0);
+    ret = njs_vm_external_create(vm, &vm->retval, sr->proto_id, sr, 0);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
@@ -678,8 +678,8 @@
 };
 
 
-static njs_external_proto_t
-njs_externals_init_internal(njs_vm_t *vm, njs_external_proto_t proto,
+static njs_int_t
+njs_externals_init_internal(njs_vm_t *vm, njs_int_t proto_id,
     njs_unit_test_req_init_t *init, njs_uint_t n, njs_bool_t shared)
 {
     njs_int_t             ret;
@@ -687,37 +687,37 @@
     njs_unit_test_req_t   *requests;
     njs_unit_test_prop_t  *prop;
 
-    if (proto == NULL) {
-        proto = njs_vm_external_prototype(vm, njs_unit_test_r_external,
-                                          njs_nitems(njs_unit_test_r_external));
-        if (njs_slow_path(proto == NULL)) {
+    if (proto_id == -1) {
+        proto_id = njs_vm_external_prototype(vm, njs_unit_test_r_external,
+                                         njs_nitems(njs_unit_test_r_external));
+        if (njs_slow_path(proto_id < 0)) {
             njs_printf("njs_vm_external_prototype() failed\n");
-            return NULL;
+            return -1;
         }
     }
 
     requests = njs_mp_zalloc(vm->mem_pool, n * sizeof(njs_unit_test_req_t));
     if (njs_slow_path(requests == NULL)) {
-        return NULL;
+        return -1;
     }
 
     for (i = 0; i < n; i++) {
 
         requests[i] = init[i].request;
-        requests[i].proto = proto;
+        requests[i].proto_id = proto_id;
 
         ret = njs_vm_external_create(vm, njs_value_arg(&requests[i].value),
-                                     proto, &requests[i], shared);
+                                     proto_id, &requests[i], shared);
         if (njs_slow_path(ret != NJS_OK)) {
             njs_printf("njs_vm_external_create() failed\n");
-            return NULL;
+            return -1;
         }
 
         ret = njs_vm_bind(vm, &init[i].name, njs_value_arg(&requests[i].value),
                           shared);
         if (njs_slow_path(ret != NJS_OK)) {
             njs_printf("njs_vm_bind() failed\n");
-            return NULL;
+            return -1;
         }
 
         for (j = 0; j < njs_nitems(init[i].props); j++) {
@@ -726,33 +726,34 @@
 
             if (njs_slow_path(prop == NULL)) {
                 njs_printf("lvlhsh_unit_test_alloc() failed\n");
-                return NULL;
+                return -1;
             }
 
             ret = lvlhsh_unit_test_add(vm->mem_pool, &requests[i], prop);
             if (njs_slow_path(ret != NJS_OK)) {
                 njs_printf("lvlhsh_unit_test_add() failed\n");
-                return NULL;
+                return -1;
             }
         }
     }
 
-    return proto;
-}
-
-
-njs_external_proto_t
-njs_externals_shared_init(njs_vm_t *vm)
-{
-    return njs_externals_init_internal(vm, NULL, njs_test_requests, 1, 1);
+    return proto_id;
 }
 
 
 njs_int_t
-njs_externals_init(njs_vm_t *vm, njs_external_proto_t proto)
+njs_externals_shared_init(njs_vm_t *vm)
 {
-    proto = njs_externals_init_internal(vm, proto, &njs_test_requests[1], 3, 0);
-    if (proto == NULL) {
+    return njs_externals_init_internal(vm, -1, njs_test_requests, 1, 1);
+}
+
+
+njs_int_t
+njs_externals_init(njs_vm_t *vm, njs_int_t proto_id)
+{
+    proto_id = njs_externals_init_internal(vm, proto_id, &njs_test_requests[1],
+                                        3, 0);
+    if (proto_id < 0) {
         return NJS_ERROR;
     }
 
diff --git a/src/test/njs_externals_test.h b/src/test/njs_externals_test.h
index 97553b5..2253b4d 100644
--- a/src/test/njs_externals_test.h
+++ b/src/test/njs_externals_test.h
@@ -8,8 +8,8 @@
 #define _NJS_EXTERNALS_TEST_H_INCLUDED_
 
 
-njs_external_proto_t njs_externals_shared_init(njs_vm_t *vm);
-njs_int_t njs_externals_init(njs_vm_t *vm, njs_external_proto_t proto);
+njs_int_t njs_externals_shared_init(njs_vm_t *vm);
+njs_int_t njs_externals_init(njs_vm_t *vm, njs_int_t proto_id);
 
 
 #endif /* _NJS_EXTERNALS_TEST_H_INCLUDED_ */
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index 5109d3a..4cd6325 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -20483,19 +20483,18 @@
 njs_unit_test(njs_unit_test_t tests[], size_t num, njs_str_t *name,
     njs_opts_t *opts, njs_stat_t *stat)
 {
-    u_char                *start, *end;
-    njs_vm_t              *vm, *nvm;
-    njs_int_t             ret;
-    njs_str_t             s;
-    njs_uint_t            i, repeat;
-    njs_stat_t            prev;
-    njs_bool_t            success;
-    njs_vm_opt_t          options;
-    njs_external_proto_t  proto;
+    u_char        *start, *end;
+    njs_vm_t      *vm, *nvm;
+    njs_int_t     ret, proto_id;
+    njs_str_t     s;
+    njs_uint_t    i, repeat;
+    njs_stat_t    prev;
+    njs_bool_t    success;
+    njs_vm_opt_t  options;
 
     vm = NULL;
     nvm = NULL;
-    proto = NULL;
+    proto_id = -1;
 
     prev = *stat;
 
@@ -20519,8 +20518,8 @@
         }
 
         if (opts->externals) {
-            proto = njs_externals_shared_init(vm);
-            if (proto == NULL) {
+            proto_id = njs_externals_shared_init(vm);
+            if (proto_id < 0) {
                 goto done;
             }
         }
@@ -20549,7 +20548,7 @@
                 }
 
                 if (opts->externals) {
-                    ret = njs_externals_init(nvm, proto);
+                    ret = njs_externals_init(nvm, proto_id);
                     if (ret != NJS_OK) {
                         goto done;
                     }
@@ -20653,7 +20652,7 @@
         }
 
         if (opts->externals) {
-            ret = njs_externals_init(vm, NULL);
+            ret = njs_externals_init(vm, -1);
             if (ret != NJS_OK) {
                 goto done;
             }