]> git.kaiwu.me - njs.git/commitdiff
Added njs_vm_external_constructor().
authorDmitry Volyntsev <xeioex@nginx.com>
Mon, 3 Jul 2023 19:49:00 +0000 (12:49 -0700)
committerDmitry Volyntsev <xeioex@nginx.com>
Mon, 3 Jul 2023 19:49:00 +0000 (12:49 -0700)
The new API allows to add new constructor/prototype pairs.

34 files changed:
external/njs_crypto_module.c
external/njs_fs_module.c
external/njs_query_string_module.c
external/njs_shell.c
external/njs_webcrypto_module.c
external/njs_xml_module.c
external/njs_zlib_module.c
nginx/ngx_http_js_module.c
nginx/ngx_js.c
nginx/ngx_js_fetch.c
nginx/ngx_stream_js_module.c
src/njs.h
src/njs_array.c
src/njs_array_buffer.c
src/njs_async.c
src/njs_buffer.c
src/njs_builtin.c
src/njs_date.c
src/njs_error.c
src/njs_extern.c
src/njs_function.c
src/njs_json.c
src/njs_object.c
src/njs_parser.c
src/njs_promise.c
src/njs_regexp.c
src/njs_typed_array.c
src/njs_value.c
src/njs_value.h
src/njs_vm.c
src/njs_vm.h
src/njs_vmcode.c
src/test/njs_externals_test.c
src/test/njs_unit_test.c

index 58f1787e9a72918bf54aa21ea9c38caa6ffa4585..af5ddf2fb37a8357554348a7122f863a2992e954 100644 (file)
@@ -287,6 +287,7 @@ static njs_int_t    njs_crypto_hmac_proto_id;
 
 njs_module_t  njs_crypto_module = {
     .name = njs_str("crypto"),
+    .preinit = NULL,
     .init = njs_crypto_init,
 };
 
index e5213c0a22eb64e08cb3913f54285a4cf3a0ce53..b92e7b894b030b3a05447af2af947333f1fe827c 100644 (file)
@@ -1407,6 +1407,7 @@ static njs_int_t    njs_fs_bytes_written_proto_id;
 
 njs_module_t  njs_fs_module = {
     .name = njs_str("fs"),
+    .preinit = NULL,
     .init = njs_fs_init,
 };
 
index 7eb066d1d3653458f994bc85bc332fed0fb146a6..b30ffe84393505739778c80916d08b71f8d63a9b 100644 (file)
@@ -105,6 +105,7 @@ static njs_external_t  njs_ext_query_string[] = {
 
 njs_module_t  njs_query_string_module = {
     .name = njs_str("querystring"),
+    .preinit = NULL,
     .init = njs_query_string_init,
 };
 
index e8b221d230e380264e5ecc9d6ae812d02dfe971c..3a92092f5b18fbe228aff77347aab53bdff58c73 100644 (file)
@@ -254,6 +254,7 @@ static njs_vm_ops_t njs_console_ops = {
 
 njs_module_t  njs_console_module = {
     .name = njs_str("console"),
+    .preinit = NULL,
     .init = njs_externals_init,
 };
 
index 1d35bc0830129e8eea48bcb10d9a2b6b62f3e3ca..cf7151866a33dd4f1bd37f783dc2dec17d24f220 100644 (file)
@@ -659,6 +659,7 @@ static njs_external_t  njs_ext_webcrypto[] = {
 
 njs_module_t  njs_webcrypto_module = {
     .name = njs_str("webcrypto"),
+    .preinit = NULL,
     .init = njs_webcrypto_init,
 };
 
index b84bebc82df9e8e0a19b0524b83e76a8290f193c..2b3745013cf2009c5c32efe7a720452e34dc9620 100644 (file)
@@ -402,6 +402,7 @@ static njs_external_t  njs_ext_xml_attr[] = {
 
 njs_module_t  njs_xml_module = {
     .name = njs_str("xml"),
+    .preinit = NULL,
     .init = njs_xml_init,
 };
 
index e00e23008fd7537993ee824bdf7c023755221506..3f2e23feb3808ede8d8cee1c3e4f475c826a245b 100644 (file)
@@ -176,6 +176,7 @@ static njs_external_t  njs_ext_zlib[] = {
 
 njs_module_t  njs_zlib_module = {
     .name = njs_str("zlib"),
+    .preinit = NULL,
     .init = njs_zlib_init,
 };
 
index 135afa5b2003e420099476651d838c472ef8062a..df3a0e5a7974f9a7b6fadf88e0dcab649ea1d37b 100644 (file)
@@ -786,6 +786,7 @@ static njs_vm_meta_t ngx_http_js_metas = {
 
 njs_module_t  ngx_js_http_module = {
     .name = njs_str("http"),
+    .preinit = NULL,
     .init = ngx_js_http_init,
 };
 
index 8973f9f6e61dd5defcc9ada6bd50e8aee1d40bb7..a3b91ca471832ffc1ca51157d53f3fdb245af35b 100644 (file)
@@ -165,6 +165,7 @@ static njs_external_t  ngx_js_ext_core[] = {
 
 njs_module_t  ngx_js_ngx_module = {
     .name = njs_str("ngx"),
+    .preinit = NULL,
     .init = ngx_js_core_init,
 };
 
index 9d81c292d510d0a068eee9a486e2e70c04c61b06..46d751b28ec9f8f1bd0f3da861c535146616046f 100644 (file)
@@ -656,6 +656,7 @@ static njs_int_t    ngx_http_js_fetch_headers_proto_id;
 
 njs_module_t  ngx_js_fetch_module = {
     .name = njs_str("fetch"),
+    .preinit = NULL,
     .init = ngx_js_fetch_init,
 };
 
index b5c8681bf8558e74aad4a6bcd6ce0abca7d82e1a..3e2a2dab36d27dd805d2eaa37a7c1e0d8520c640 100644 (file)
@@ -568,6 +568,7 @@ static njs_int_t    ngx_stream_js_session_flags_proto_id;
 
 njs_module_t  ngx_js_stream_module = {
     .name = njs_str("stream"),
+    .preinit = NULL,
     .init = ngx_js_stream_init,
 };
 
index de9375859a89d18743b6874993fb373e13d087bb..38357f0f43822f46c34dc39f977fe00294e8b840 100644 (file)
--- a/src/njs.h
+++ b/src/njs.h
@@ -257,6 +257,7 @@ typedef njs_int_t (*njs_addon_init_pt)(njs_vm_t *vm);
 
 typedef struct {
     njs_str_t                       name;
+    njs_addon_init_pt               preinit;
     njs_addon_init_pt               init;
 } njs_module_t;
 
@@ -403,6 +404,10 @@ NJS_EXPORT njs_int_t njs_vm_add_path(njs_vm_t *vm, const njs_str_t *path);
 
 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_constructor(njs_vm_t *vm,
+    const njs_str_t *name, njs_function_native_t native,
+    const njs_external_t *ctor_props, njs_uint_t ctor_nprops,
+    const njs_external_t *proto_props, njs_uint_t proto_nprops);
 NJS_EXPORT njs_int_t 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_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm,
@@ -417,6 +422,15 @@ NJS_EXPORT njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value,
 NJS_EXPORT uintptr_t njs_vm_meta(njs_vm_t *vm, njs_uint_t index);
 NJS_EXPORT njs_vm_opt_t *njs_vm_options(njs_vm_t *vm);
 
+NJS_EXPORT njs_int_t njs_error_constructor(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t type, njs_value_t *retval);
+NJS_EXPORT njs_int_t njs_object_prototype_create_constructor(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
+NJS_EXPORT njs_int_t njs_object_prototype_create(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
+
 NJS_EXPORT njs_function_t *njs_vm_function_alloc(njs_vm_t *vm,
     njs_function_native_t native, njs_bool_t shared, njs_bool_t ctor);
 
@@ -433,7 +447,9 @@ NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t *name);
 NJS_EXPORT njs_bool_t njs_vm_constructor(njs_vm_t *vm);
 
 NJS_EXPORT void njs_vm_throw(njs_vm_t *vm, const njs_value_t *value);
-NJS_EXPORT void njs_vm_error2(njs_vm_t *vm, unsigned type, const char *fmt,
+NJS_EXPORT void njs_vm_error2(njs_vm_t *vm, unsigned error_type,
+    const char *fmt, ...);
+NJS_EXPORT void njs_vm_error3(njs_vm_t *vm, unsigned type, const char *fmt,
     ...);
 NJS_EXPORT void njs_vm_exception_get(njs_vm_t *vm, njs_value_t *retval);
 NJS_EXPORT njs_mp_t *njs_vm_memory_pool(njs_vm_t *vm);
index f87abd874ea9d730d573d500ae285b7e77cf5668..09a45339d9f49dd887f3397ebde74c16e4fe1a84 100644 (file)
@@ -83,7 +83,7 @@ njs_array_alloc(njs_vm_t *vm, njs_bool_t flat, uint64_t length, uint32_t spare)
     array->start = array->data;
     njs_lvlhsh_init(&array->object.hash);
     array->object.shared_hash = vm->shared->array_instance_hash;
-    array->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_ARRAY].object;
+    array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY);
     array->object.slots = NULL;
     array->object.type = NJS_ARRAY;
     array->object.shared = 0;
index 43f5982a849f0e96e73816d221a09365b84addb4..68eb8db0835d4cedead091e3174186c287a95a73 100644 (file)
@@ -33,7 +33,7 @@ njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size, njs_bool_t zeroing)
         goto memory_error;
     }
 
-    proto = &vm->prototypes[NJS_OBJ_TYPE_ARRAY_BUFFER].object;
+    proto = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY_BUFFER);
 
     njs_lvlhsh_init(&array->object.hash);
     njs_lvlhsh_init(&array->object.shared_hash);
index 7b3b933184dc87110a64d7f6ce902e9108458be0..3d47f4af8d6fbc5555d117ce777c308bae8c51be 100644 (file)
@@ -18,7 +18,7 @@ njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
     njs_value_t               ctor;
     njs_promise_capability_t  *capability;
 
-    njs_set_function(&ctor, &vm->constructors[NJS_OBJ_TYPE_PROMISE]);
+    njs_set_function(&ctor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_PROMISE));
 
     capability = njs_promise_new_capability(vm, &ctor);
     if (njs_slow_path(capability == NULL)) {
index 5ab506caffa7a2a1e15fdd3016982d4d82fe4a45..7f2067fa95ab82a2350600d5da3f34be6cd5b21d 100644 (file)
@@ -126,6 +126,7 @@ static njs_external_t  njs_ext_buffer[] = {
 
 njs_module_t  njs_buffer_module = {
     .name = njs_str("buffer"),
+    .preinit = NULL,
     .init = njs_buffer_init,
 };
 
@@ -147,7 +148,7 @@ njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
 
     buffer = (njs_array_buffer_t *) &array[1];
 
-    proto = &vm->prototypes[NJS_OBJ_TYPE_ARRAY_BUFFER].object;
+    proto = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY_BUFFER);
 
     njs_lvlhsh_init(&buffer->object.hash);
     njs_lvlhsh_init(&buffer->object.shared_hash);
@@ -161,7 +162,7 @@ njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
     buffer->u.data = (void *) start;
     buffer->size = size;
 
-    proto = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object;
+    proto = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER);
 
     array->type = NJS_OBJ_TYPE_UINT8_ARRAY;
     njs_lvlhsh_init(&array->object.hash);
@@ -197,7 +198,7 @@ njs_buffer_alloc(njs_vm_t *vm, size_t size, njs_bool_t zeroing)
         return NULL;
     }
 
-    array->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object;
+    array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER);
 
     return array;
 }
@@ -462,7 +463,7 @@ njs_buffer_from_array_buffer(njs_vm_t *vm, njs_value_t *value,
         return NJS_ERROR;
     }
 
-    buffer->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object;
+    buffer->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER);
 
     buffer->offset = off;
     buffer->byte_length = len;
@@ -943,7 +944,7 @@ njs_buffer_is_buffer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     array = njs_buffer_slot_internal(vm, njs_arg(args, nargs, 1));
 
     if (njs_fast_path(array != NULL && array->object.__proto__
-                      == &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object))
+                      == njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER)))
     {
         is = 1;
     }
@@ -2184,7 +2185,7 @@ njs_buffer_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     array = njs_typed_array(retval);
-    array->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object;
+    array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER);
 
     return NJS_OK;
 }
index 317458e0a732b70a8171bfa47e6b5127ef403d01..355e82cfdc8fd4b4d410520a1702e1b04b05136e 100644 (file)
@@ -115,7 +115,7 @@ njs_object_hash_init(njs_vm_t *vm, njs_lvlhsh_t *hash,
 njs_int_t
 njs_builtin_objects_create(njs_vm_t *vm)
 {
-    njs_int_t                  ret;
+    njs_int_t                  ret, index;
     njs_uint_t                 i;
     njs_object_t               *object, *string_object;
     njs_function_t             *constructor;
@@ -129,6 +129,8 @@ njs_builtin_objects_create(njs_vm_t *vm)
         return NJS_ERROR;
     }
 
+    vm->shared = shared;
+
     njs_lvlhsh_init(&shared->keywords_hash);
     njs_lvlhsh_init(&shared->values_hash);
 
@@ -204,34 +206,42 @@ njs_builtin_objects_create(njs_vm_t *vm)
         return NJS_ERROR;
     }
 
-    prototype = shared->prototypes;
-
     for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
-        prototype[i] = njs_object_type_init[i]->prototype_value;
+        index = njs_vm_ctor_push(vm);
+        if (njs_slow_path(index < 0)) {
+            return NJS_ERROR;
+        }
+
+        njs_assert_msg((njs_uint_t) index == i,
+                       "ctor index should match object type");
 
-        ret = njs_object_hash_init(vm, &prototype[i].object.shared_hash,
+        prototype = njs_shared_prototype(shared, i);
+        *prototype = njs_object_type_init[i]->prototype_value;
+
+        ret = njs_object_hash_init(vm, &prototype->object.shared_hash,
                                    njs_object_type_init[i]->prototype_props);
         if (njs_slow_path(ret != NJS_OK)) {
             return NJS_ERROR;
         }
 
-        prototype[i].object.extensible = 1;
+        prototype->object.extensible = 1;
     }
 
-    shared->prototypes[NJS_OBJ_TYPE_REGEXP].regexp.pattern =
-                                              shared->empty_regexp_pattern;
-
-    constructor = shared->constructors;
+    prototype = njs_shared_prototype(shared, NJS_OBJ_TYPE_REGEXP);
+    prototype->regexp.pattern = shared->empty_regexp_pattern;
 
     for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
+        constructor = njs_shared_ctor(shared, i);
+
         if (njs_object_type_init[i]->constructor_props == NULL) {
+            njs_memzero(constructor, sizeof(njs_function_t));
             continue;
         }
 
-        constructor[i] = njs_object_type_init[i]->constructor;
-        constructor[i].object.shared = 0;
+        *constructor = njs_object_type_init[i]->constructor;
+        constructor->object.shared = 0;
 
-        ret = njs_object_hash_init(vm, &constructor[i].object.shared_hash,
+        ret = njs_object_hash_init(vm, &constructor->object.shared_hash,
                                    njs_object_type_init[i]->constructor_props);
         if (njs_slow_path(ret != NJS_OK)) {
             return NJS_ERROR;
@@ -259,91 +269,6 @@ njs_builtin_objects_create(njs_vm_t *vm)
 
     njs_lvlhsh_init(&shared->modules_hash);
 
-    vm->shared = shared;
-
-    return NJS_OK;
-}
-
-
-njs_int_t
-njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global)
-{
-    size_t        size;
-    njs_uint_t    i;
-    njs_object_t  *object_prototype, *function_prototype,
-                  *typed_array_prototype, *error_prototype, *async_prototype,
-                  *typed_array_ctor, *error_ctor;
-
-    /*
-     * Copy both prototypes and constructors arrays by one memcpy()
-     * because they are stored together.
-     */
-    size = (sizeof(njs_object_prototype_t) + sizeof(njs_function_t))
-           * NJS_OBJ_TYPE_MAX;
-
-    memcpy(vm->prototypes, vm->shared->prototypes, size);
-
-    object_prototype = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
-
-    for (i = NJS_OBJ_TYPE_ARRAY; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
-        vm->prototypes[i].object.__proto__ = object_prototype;
-    }
-
-    typed_array_prototype = &vm->prototypes[NJS_OBJ_TYPE_TYPED_ARRAY].object;
-
-    for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN;
-         i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX;
-         i++)
-    {
-        vm->prototypes[i].object.__proto__ = typed_array_prototype;
-    }
-
-    vm->prototypes[NJS_OBJ_TYPE_ARRAY_ITERATOR].object.__proto__ =
-                              &vm->prototypes[NJS_OBJ_TYPE_ITERATOR].object;
-
-    vm->prototypes[NJS_OBJ_TYPE_BUFFER].object.__proto__ =
-                              &vm->prototypes[NJS_OBJ_TYPE_UINT8_ARRAY].object;
-
-    error_prototype = &vm->prototypes[NJS_OBJ_TYPE_ERROR].object;
-    error_prototype->__proto__ = object_prototype;
-
-    for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < NJS_OBJ_TYPE_MAX; i++) {
-        vm->prototypes[i].object.__proto__ = error_prototype;
-    }
-
-    function_prototype = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
-
-    async_prototype = &vm->prototypes[NJS_OBJ_TYPE_ASYNC_FUNCTION].object;
-    async_prototype->__proto__ = function_prototype;
-
-    for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
-        vm->constructors[i].object.__proto__ = function_prototype;
-    }
-
-    typed_array_ctor = &vm->constructors[NJS_OBJ_TYPE_TYPED_ARRAY].object;
-
-    for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN;
-         i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX;
-         i++)
-    {
-        vm->constructors[i].object.__proto__ = typed_array_ctor;
-    }
-
-    error_ctor = &vm->constructors[NJS_OBJ_TYPE_ERROR].object;
-    error_ctor->__proto__ = function_prototype;
-
-    for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < NJS_OBJ_TYPE_MAX; i++) {
-        vm->constructors[i].object.__proto__ = error_ctor;
-    }
-
-    vm->global_object.__proto__ = object_prototype;
-
-    njs_set_undefined(global);
-    njs_set_object(global, &vm->global_object);
-
-    vm->string_object = vm->shared->string_object;
-    vm->string_object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_STRING].object;
-
     return NJS_OK;
 }
 
@@ -838,7 +763,7 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
     /* Constructor from built-in modules (not-mapped to global object). */
 
     for (i = NJS_OBJ_TYPE_HIDDEN_MIN; i < NJS_OBJ_TYPE_HIDDEN_MAX; i++) {
-        njs_set_object(&value, &vm->constructors[i].object);
+        njs_set_object(&value, &njs_vm_ctor(vm, i).object);
 
         ret = njs_value_property(vm, &value, njs_value_arg(&njs_string_name),
                                  &tag);
@@ -847,7 +772,7 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
             njs_string_get(&tag, &ctx.match);
         }
 
-        ret = njs_object_traverse(vm, &vm->constructors[i].object, &ctx,
+        ret = njs_object_traverse(vm, njs_object(&value), &ctx,
                                   njs_builtin_traverse);
 
         if (ret == NJS_DONE) {
@@ -1253,7 +1178,7 @@ njs_top_level_constructor(njs_vm_t *vm, njs_object_prop_t *self,
             return NJS_DECLINED;
         }
 
-        ctor = &vm->constructors[njs_prop_magic16(self)];
+        ctor = &njs_vm_ctor(vm, njs_prop_magic16(self));
 
         njs_set_function(retval, ctor);
     }
index 9337436c93130848f2991897ba8f96330513ab47..8aa6ea6ead665ba4a0497d99a0f4fdf6ff965149 100644 (file)
@@ -364,7 +364,7 @@ njs_date_alloc(njs_vm_t *vm, double time)
     date->object.extensible = 1;
     date->object.error_data = 0;
     date->object.fast_array = 0;
-    date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object;
+    date->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_DATE);
     date->object.slots = NULL;
 
     date->time = time;
index fc069a1c64dcddeff75363f8c2ce36a8316d16bf..4989b8975462a93ab8b967ef51c195de62d5ca37 100644 (file)
@@ -76,7 +76,7 @@ njs_throw_error(njs_vm_t *vm, njs_object_type_t type, const char *fmt, ...)
     va_list  args;
 
     va_start(args, fmt);
-    njs_throw_error_va(vm, &vm->prototypes[type].object, fmt, args);
+    njs_throw_error_va(vm, njs_vm_proto(vm, type), fmt, args);
     va_end(args);
 }
 
@@ -96,7 +96,7 @@ njs_error_fmt_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type,
         va_end(args);
     }
 
-    njs_error_new(vm, dst, &vm->prototypes[type].object, buf, p - buf);
+    njs_error_new(vm, dst, njs_vm_proto(vm, type), buf, p - buf);
 }
 
 
@@ -298,7 +298,7 @@ memory_error:
 }
 
 
-static njs_int_t
+njs_int_t
 njs_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t type, njs_value_t *retval)
 {
@@ -339,7 +339,7 @@ njs_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    error = njs_error_alloc(vm, &vm->prototypes[type].object, NULL,
+    error = njs_error_alloc(vm, njs_vm_proto(vm, type), NULL,
                             njs_is_defined(value) ? value : NULL,
                             njs_is_defined(&list) ? &list : NULL);
     if (njs_slow_path(error == NULL)) {
@@ -499,15 +499,13 @@ const njs_object_init_t  njs_aggregate_error_constructor_init = {
 void
 njs_memory_error_set(njs_vm_t *vm, njs_value_t *value)
 {
-    njs_object_t            *object;
-    njs_object_prototype_t  *prototypes;
+    njs_object_t  *object;
 
-    prototypes = vm->prototypes;
     object = &vm->memory_error_object;
 
     njs_lvlhsh_init(&object->hash);
     njs_lvlhsh_init(&object->shared_hash);
-    object->__proto__ = &prototypes[NJS_OBJ_TYPE_INTERNAL_ERROR].object;
+    object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_INTERNAL_ERROR);
     object->slots = NULL;
     object->type = NJS_OBJECT;
     object->shared = 1;
@@ -555,7 +553,7 @@ njs_memory_error_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
 
     function = njs_function(value);
     proto = njs_property_prototype_create(vm, &function->object.hash,
-                                          &vm->prototypes[index].object);
+                                          njs_vm_proto(vm, index));
     if (proto == NULL) {
         proto = &njs_value_undefined;
     }
index 6cc9b00a2fd21d880b939272da5a99f80ef66376..50ac76c4189f7ec3388323ac7927490e3e67b80d 100644 (file)
@@ -315,6 +315,77 @@ njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition,
 }
 
 
+static njs_int_t
+njs_vm_external_constructor_handler(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    njs_set_function(retval, &njs_vm_ctor(vm, njs_prop_magic32(prop)));
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_vm_external_constructor(njs_vm_t *vm, const njs_str_t *name,
+    njs_function_native_t native, const njs_external_t *ctor_props,
+    njs_uint_t ctor_nprops, const njs_external_t *proto_props,
+    njs_uint_t proto_nprops)
+{
+    njs_int_t               ret, index, proto_id;
+    njs_arr_t               **pprotos;
+    njs_function_t          *constructor;
+    njs_exotic_slots_t      *slots;
+    njs_object_prototype_t  *prototype;
+
+    index = njs_vm_ctor_push(vm);
+    if (njs_slow_path(index < 0)) {
+        njs_internal_error(vm, "njs_vm_ctor_push() failed");
+        return -1;
+    }
+
+    proto_id = njs_vm_external_prototype(vm, proto_props, proto_nprops);
+    if (njs_slow_path(proto_id < 0)) {
+        njs_internal_error(vm, "njs_vm_external_prototype(proto_props) failed");
+        return -1;
+    }
+
+    prototype = njs_shared_prototype(vm->shared, index);
+    njs_memzero(prototype, sizeof(njs_object_prototype_t));
+    prototype->object.type = NJS_OBJECT;
+    prototype->object.extensible = 1;
+
+    pprotos = njs_arr_item(vm->protos, proto_id);
+    slots = (*pprotos)->start;
+    prototype->object.shared_hash = slots->external_shared_hash;
+
+    proto_id = njs_vm_external_prototype(vm, ctor_props, ctor_nprops);
+    if (njs_slow_path(proto_id < 0)) {
+        njs_internal_error(vm, "njs_vm_external_prototype(ctor_props) failed");
+        return -1;
+    }
+
+    constructor = njs_shared_ctor(vm->shared, index);
+    njs_memzero(constructor, sizeof(njs_function_t));
+    constructor->object.type = NJS_FUNCTION;
+    constructor->u.native = native;
+    constructor->magic8 = index;
+    constructor->native = 1;
+    constructor->ctor = 1;
+
+    pprotos = njs_arr_item(vm->protos, proto_id);
+    slots = (*pprotos)->start;
+    constructor->object.shared_hash = slots->external_shared_hash;
+
+    ret = njs_vm_bind_handler(vm, name, njs_vm_external_constructor_handler, 0,
+                              index, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    return index;
+}
+
+
 njs_int_t
 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)
index 67f74db8d649bd84ba8e989ae5f1045448762070..1d91fde32e938616891ccf00b8aa710cb9905853 100644 (file)
@@ -46,10 +46,10 @@ njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
     }
 
     if (async) {
-        proto = &vm->prototypes[NJS_OBJ_TYPE_ASYNC_FUNCTION].object;
+        proto = njs_vm_proto(vm, NJS_OBJ_TYPE_ASYNC_FUNCTION);
 
     } else {
-        proto = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+        proto = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION);
     }
 
     function->object.__proto__ = proto;
@@ -84,7 +84,7 @@ njs_vm_function_alloc(njs_vm_t *vm, njs_function_native_t native,
     function->object.shared = shared;
     function->u.native = native;
     function->object.shared_hash = vm->shared->function_instance_hash;
-    function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+    function->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION);
     function->object.type = NJS_FUNCTION;
 
     return function;
@@ -212,7 +212,7 @@ njs_function_copy(njs_vm_t *vm, njs_function_t *function)
 
     type = njs_function_object_type(vm, function);
 
-    copy->object.__proto__ = &vm->prototypes[type].object;
+    copy->object.__proto__ = njs_vm_proto(vm, type);
     copy->object.shared = 0;
 
     if (copy->ctor) {
@@ -1370,7 +1370,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     /* Bound functions have no "prototype" property. */
     function->object.shared_hash = vm->shared->arrow_instance_hash;
 
-    function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+    function->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION);
     function->object.shared = 0;
 
     function->context = njs_function(&args[0]);
index ffe68ff283fec63b2e9e6f5fa4c57f0edd7b1f0c..01bf3214e204239c859a58a0eaff025637f22861 100644 (file)
@@ -1979,7 +1979,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value,
 
     if (njs_slow_path(vm->top_frame == NULL)) {
         /* An exception was thrown during compilation. */
-        njs_vm_init(vm);
+        njs_vm_runtime_init(vm);
     }
 
     if (njs_is_valid(&vm->exception)) {
index 287b354ab66e0edaf954fcf053904b3d5ad3b3a8..91ae922af191e4defae3673700dd71ddff38a9fd 100644 (file)
@@ -45,7 +45,7 @@ njs_object_alloc(njs_vm_t *vm)
     if (njs_fast_path(object != NULL)) {
         njs_lvlhsh_init(&object->hash);
         njs_lvlhsh_init(&object->shared_hash);
-        object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
+        object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
         object->slots = NULL;
         object->type = NJS_OBJECT;
         object->shared = 0;
@@ -80,7 +80,7 @@ njs_object_value_copy(njs_vm_t *vm, njs_value_t *value)
 
     if (njs_fast_path(object != NULL)) {
         memcpy(object, njs_object(value), size);
-        object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
+        object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
         object->shared = 0;
         value->data.u.object = object;
         return object;
@@ -119,7 +119,7 @@ njs_object_value_alloc(njs_vm_t *vm, njs_uint_t prototype_index, size_t extra,
     ov->object.error_data = 0;
     ov->object.fast_array = 0;
 
-    ov->object.__proto__ = &vm->prototypes[prototype_index].object;
+    ov->object.__proto__ = njs_vm_proto(vm, prototype_index);
     ov->object.slots = NULL;
 
     if (value != NULL) {
@@ -1501,7 +1501,7 @@ njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         index = njs_primitive_prototype_index(value->type);
 
         if (njs_is_symbol(value)) {
-            njs_set_object(retval, &vm->prototypes[index].object);
+            njs_set_object(retval, njs_vm_proto(vm, index));
 
         } else {
             njs_set_object_value(retval,
@@ -1843,7 +1843,7 @@ njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_object_prop_t *prop,
 
     } else {
         index = njs_primitive_prototype_index(value->type);
-        proto = &vm->prototypes[index].object;
+        proto = njs_vm_proto(vm, index);
     }
 
     if (proto != NULL) {
@@ -1875,7 +1875,7 @@ njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
     function = njs_function(value);
     index = function - vm->constructors;
 
-    if (index >= 0 && index < NJS_OBJ_TYPE_MAX) {
+    if (index >= 0 && (size_t) index < vm->constructors_size) {
         proto = njs_property_prototype_create(vm, &function->object.hash,
                                               &vm->prototypes[index].object);
     }
@@ -2111,7 +2111,7 @@ njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop,
             prototype = (njs_object_prototype_t *) object;
             index = prototype - vm->prototypes;
 
-            if (index >= 0 && index < NJS_OBJ_TYPE_MAX) {
+            if (index >= 0 && (size_t) index < vm->constructors_size) {
                 goto found;
             }
 
@@ -2128,7 +2128,7 @@ njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop,
 
 found:
 
-    njs_set_function(&constructor, &vm->constructors[index]);
+    njs_set_function(&constructor, &njs_vm_ctor(vm, index));
     setval = &constructor;
 
     cons = njs_property_constructor_set(vm, &prototype->object.hash, setval);
index 0d689adb4574d53b1bcf68b0943459583d5c3913..5b1b2aa7866f250da44a33b4a41ea8c227a2fe71 100644 (file)
@@ -9199,6 +9199,10 @@ njs_parser_error(njs_vm_t *vm, njs_object_type_t type, njs_str_t *file,
     static const njs_value_t  file_name = njs_string("fileName");
     static const njs_value_t  line_number = njs_string("lineNumber");
 
+    if (njs_slow_path(vm->top_frame == NULL)) {
+        njs_vm_runtime_init(vm);
+    }
+
     p = msg;
     end = msg + NJS_MAX_ERROR_STR;
 
@@ -9217,7 +9221,7 @@ njs_parser_error(njs_vm_t *vm, njs_object_type_t type, njs_str_t *file,
         p = njs_sprintf(p, end, " in %uD", line);
     }
 
-    njs_error_new(vm, &error, &vm->prototypes[type].object, msg, p - msg);
+    njs_error_new(vm, &error, njs_vm_proto(vm, type), msg, p - msg);
 
     njs_set_number(&value, line);
     njs_value_property_set(vm, &error, njs_value_arg(&line_number), &value);
index 6dc3de18d70dda8ff71a39895baacdf416c28a45..d9cebe0451da5bbf6e43352a1789fdc05cd5e182 100644 (file)
@@ -131,7 +131,7 @@ njs_promise_alloc(njs_vm_t *vm)
     promise->object.extensible = 1;
     promise->object.error_data = 0;
     promise->object.fast_array = 0;
-    promise->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_PROMISE].object;
+    promise->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_PROMISE);
     promise->object.slots = NULL;
 
     data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t));
@@ -265,7 +265,7 @@ njs_promise_create_function(njs_vm_t *vm, size_t context_size)
         context = NULL;
     }
 
-    function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+    function->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION);
     function->object.shared_hash = vm->shared->arrow_instance_hash;
     function->object.type = NJS_FUNCTION;
     function->object.extensible = 1;
@@ -1352,8 +1352,8 @@ njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator,
 
         if (handler == njs_promise_perform_any_handler) {
             error = njs_error_alloc(vm,
-                           &vm->prototypes[NJS_OBJ_TYPE_AGGREGATE_ERROR].object,
-                           NULL, &string_any_rejected, &argument);
+                                njs_vm_proto(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR),
+                                NULL, &string_any_rejected, &argument);
             if (njs_slow_path(error == NULL)) {
                 return NJS_ERROR;
             }
@@ -1730,8 +1730,8 @@ njs_promise_any_reject_element_functions(njs_vm_t *vm, njs_value_t *args,
         njs_mp_free(vm->mem_pool, context->remaining_elements);
 
         error = njs_error_alloc(vm,
-                          &vm->prototypes[NJS_OBJ_TYPE_AGGREGATE_ERROR].object,
-                          NULL, &string_any_rejected, &arr_value);
+                                njs_vm_proto(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR),
+                                NULL, &string_any_rejected, &arr_value);
         if (njs_slow_path(error == NULL)) {
             return NJS_ERROR;
         }
index de25a2a7a69d745aede453033628c00dcea13eba..e432651c797400dc77375b8f4bc701af48eff649 100644 (file)
@@ -503,7 +503,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern)
     if (njs_fast_path(regexp != NULL)) {
         njs_lvlhsh_init(&regexp->object.hash);
         regexp->object.shared_hash = vm->shared->regexp_instance_hash;
-        regexp->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object;
+        regexp->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_REGEXP);
         regexp->object.slots = NULL;
         regexp->object.type = NJS_REGEXP;
         regexp->object.shared = 0;
@@ -628,7 +628,7 @@ njs_regexp_prototype_flag(njs_vm_t *vm, njs_value_t *args,
     }
 
     if (njs_slow_path(!njs_is_regexp(this))) {
-        if (njs_object(this) == &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object) {
+        if (njs_object(this) == njs_vm_proto(vm, NJS_OBJ_TYPE_REGEXP)) {
             njs_set_undefined(retval);
             return NJS_OK;
         }
@@ -679,7 +679,7 @@ njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *args,
     }
 
     if (njs_slow_path(!njs_is_regexp(this))) {
-        if (njs_object(this) == &vm->prototypes[NJS_OBJ_TYPE_REGEXP].object) {
+        if (njs_object(this) == njs_vm_proto(vm, NJS_OBJ_TYPE_REGEXP)) {
             njs_value_assign(retval, &njs_string_empty_regexp);
             return NJS_OK;
         }
@@ -1553,7 +1553,7 @@ njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args,
         return ret;
     }
 
-    njs_set_function(&constructor, &vm->constructors[NJS_OBJ_TYPE_REGEXP]);
+    njs_set_function(&constructor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_REGEXP));
 
     ret = njs_value_species_constructor(vm, rx, &constructor, &constructor);
     if (njs_slow_path(ret != NJS_OK)) {
index 4a4e5ffcde2a1d259eeebb0d2e5a49ad6c566e25..3bc37b223951563798e890339d2130cb53ed5ef2 100644 (file)
@@ -180,7 +180,7 @@ njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     njs_lvlhsh_init(&array->object.hash);
     njs_lvlhsh_init(&array->object.shared_hash);
-    array->object.__proto__ = &vm->prototypes[type].object;
+    array->object.__proto__ = njs_vm_proto(vm, type);
     array->object.type = NJS_TYPED_ARRAY;
     array->object.extensible = 1;
     array->object.fast_array = 1;
@@ -264,7 +264,7 @@ njs_typed_array_species_create(njs_vm_t *vm, njs_value_t *exemplar,
 
     array = njs_typed_array(exemplar);
 
-    njs_set_function(&constructor, &vm->constructors[array->type]);
+    njs_set_function(&constructor, &njs_vm_ctor(vm, array->type));
 
     ret = njs_value_species_constructor(vm, exemplar, &constructor,
                                         &constructor);
@@ -2413,7 +2413,7 @@ njs_data_view_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     njs_lvlhsh_init(&view->object.hash);
     njs_lvlhsh_init(&view->object.shared_hash);
-    view->object.__proto__ = &vm->prototypes[view->type].object;
+    view->object.__proto__ = njs_vm_proto(vm, view->type);
     view->object.type = NJS_DATA_VIEW;
     view->object.extensible = 1;
 
index 05dcf9e8a2ba2d1a9c5c42403e6e4336137648e0..361e11f6577ef04d946e94f0758a698251e65c72 100644 (file)
@@ -638,7 +638,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
     case NJS_NUMBER:
     case NJS_SYMBOL:
         index = njs_primitive_prototype_index(value->type);
-        obj = &vm->prototypes[index].object;
+        obj = njs_vm_proto(vm, index);
         break;
 
     case NJS_STRING:
index 570c84ff21caf230240fc91964b010e51ac0cc3a..9491702aadfbcaa17757fb4fa4ace82f93808366 100644 (file)
@@ -625,9 +625,8 @@ typedef struct {
     ((value)->type >= NJS_OBJECT)
 
 
-#define njs_has_prototype(vm, value, proto)                                   \
-    (((njs_object_prototype_t *)                                              \
-        njs_object(value)->__proto__ - (vm)->prototypes) == proto)
+#define njs_has_prototype(vm, value, proto_id)                                \
+    (njs_object(value)->__proto__ == njs_vm_proto(vm, proto_id))
 
 
 #define njs_is_object_value(value)                                            \
index fb334294386ca28f79d3cf234d29c9523dbfab78..920f836fd11a74c30d9c02724aa31d353079b62c 100644 (file)
@@ -9,6 +9,7 @@
 
 
 static njs_int_t njs_vm_handle_events(njs_vm_t *vm);
+static njs_int_t njs_vm_protos_init(njs_vm_t *vm, njs_value_t *global);
 
 
 const njs_str_t  njs_entry_empty =          njs_str("");
@@ -78,13 +79,47 @@ njs_vm_create(njs_vm_opt_t *options)
     vm->trace.data = vm;
 
     if (options->init) {
-        ret = njs_vm_init(vm);
+        ret = njs_vm_runtime_init(vm);
         if (njs_slow_path(ret != NJS_OK)) {
             return NULL;
         }
     }
 
     for (i = 0; njs_modules[i] != NULL; i++) {
+        if (njs_modules[i]->preinit == NULL) {
+            continue;
+        }
+
+        ret = njs_modules[i]->preinit(vm);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NULL;
+        }
+    }
+
+    if (options->addons != NULL) {
+        addons = options->addons;
+        for (i = 0; addons[i] != NULL; i++) {
+            if (addons[i]->preinit == NULL) {
+                continue;
+            }
+
+            ret = addons[i]->preinit(vm);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NULL;
+            }
+        }
+    }
+
+    ret = njs_vm_protos_init(vm, &vm->global_value);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NULL;
+    }
+
+    for (i = 0; njs_modules[i] != NULL; i++) {
+        if (njs_modules[i]->init == NULL) {
+            continue;
+        }
+
         ret = njs_modules[i]->init(vm);
         if (njs_slow_path(ret != NJS_OK)) {
             return NULL;
@@ -94,6 +129,10 @@ njs_vm_create(njs_vm_opt_t *options)
     if (options->addons != NULL) {
         addons = options->addons;
         for (i = 0; addons[i] != NULL; i++) {
+            if (addons[i]->init == NULL) {
+                continue;
+            }
+
             ret = addons[i]->init(vm);
             if (njs_slow_path(ret != NJS_OK)) {
                 return NULL;
@@ -111,6 +150,51 @@ njs_vm_create(njs_vm_opt_t *options)
 }
 
 
+njs_int_t
+njs_vm_ctor_push(njs_vm_t *vm)
+{
+    njs_function_t          *ctor;
+    njs_vm_shared_t         *shared;
+    njs_object_prototype_t  *prototype;
+
+    shared = vm->shared;
+
+    if (shared->constructors == NULL) {
+        shared->constructors = njs_arr_create(vm->mem_pool,
+                                              NJS_OBJ_TYPE_MAX + 8,
+                                              sizeof(njs_function_t));
+        if (njs_slow_path(shared->constructors == NULL)) {
+            njs_memory_error(vm);
+            return -1;
+        }
+
+        shared->prototypes = njs_arr_create(vm->mem_pool,
+                                              NJS_OBJ_TYPE_MAX + 8,
+                                              sizeof(njs_object_prototype_t));
+        if (njs_slow_path(shared->prototypes == NULL)) {
+            njs_memory_error(vm);
+            return -1;
+        }
+    }
+
+    ctor = njs_arr_add(shared->constructors);
+    if (njs_slow_path(ctor == NULL)) {
+        njs_memory_error(vm);
+        return -1;
+    }
+
+    prototype = njs_arr_add(shared->prototypes);
+    if (njs_slow_path(prototype == NULL)) {
+        njs_memory_error(vm);
+        return -1;
+    }
+
+    njs_assert(shared->constructors->items == shared->prototypes->items);
+
+    return shared->constructors->items - 1;
+}
+
+
 void
 njs_vm_destroy(njs_vm_t *vm)
 {
@@ -343,7 +427,12 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
     nvm->trace.data = nvm;
     nvm->external = external;
 
-    ret = njs_vm_init(nvm);
+    ret = njs_vm_runtime_init(nvm);
+    if (njs_slow_path(ret != NJS_OK)) {
+        goto fail;
+    }
+
+    ret = njs_vm_protos_init(nvm, &nvm->global_value);
     if (njs_slow_path(ret != NJS_OK)) {
         goto fail;
     }
@@ -373,7 +462,7 @@ fail:
 
 
 njs_int_t
-njs_vm_init(njs_vm_t *vm)
+njs_vm_runtime_init(njs_vm_t *vm)
 {
     njs_int_t    ret;
     njs_frame_t  *frame;
@@ -395,11 +484,6 @@ njs_vm_init(njs_vm_t *vm)
         return NJS_ERROR;
     }
 
-    ret = njs_builtin_objects_clone(vm, &vm->global_value);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-
     njs_lvlhsh_init(&vm->values_hash);
     njs_lvlhsh_init(&vm->keywords_hash);
     njs_lvlhsh_init(&vm->modules_hash);
@@ -414,6 +498,105 @@ njs_vm_init(njs_vm_t *vm)
 }
 
 
+void
+njs_vm_constructors_init(njs_vm_t *vm)
+{
+    njs_uint_t    i;
+    njs_object_t  *object_prototype, *function_prototype,
+                  *typed_array_prototype, *error_prototype, *async_prototype,
+                  *typed_array_ctor, *error_ctor;
+
+    object_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
+
+    for (i = NJS_OBJ_TYPE_ARRAY; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
+        vm->prototypes[i].object.__proto__ = object_prototype;
+    }
+
+    typed_array_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_TYPED_ARRAY);
+
+    for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN;
+         i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX;
+         i++)
+    {
+        vm->prototypes[i].object.__proto__ = typed_array_prototype;
+    }
+
+    vm->prototypes[NJS_OBJ_TYPE_ARRAY_ITERATOR].object.__proto__ =
+                                       njs_vm_proto(vm, NJS_OBJ_TYPE_ITERATOR);
+
+    vm->prototypes[NJS_OBJ_TYPE_BUFFER].object.__proto__ =
+                                    njs_vm_proto(vm, NJS_OBJ_TYPE_UINT8_ARRAY);
+
+    error_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_ERROR);
+    error_prototype->__proto__ = object_prototype;
+
+    for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < vm->constructors_size; i++) {
+        vm->prototypes[i].object.__proto__ = error_prototype;
+    }
+
+    function_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION);
+
+    async_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_ASYNC_FUNCTION);
+    async_prototype->__proto__ = function_prototype;
+
+    for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
+        vm->constructors[i].object.__proto__ = function_prototype;
+    }
+
+    typed_array_ctor = &njs_vm_ctor(vm, NJS_OBJ_TYPE_TYPED_ARRAY).object;
+
+    for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN;
+         i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX;
+         i++)
+    {
+        vm->constructors[i].object.__proto__ = typed_array_ctor;
+    }
+
+    error_ctor = &njs_vm_ctor(vm, NJS_OBJ_TYPE_ERROR).object;
+    error_ctor->__proto__ = function_prototype;
+
+    for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < vm->constructors_size; i++) {
+        vm->constructors[i].object.__proto__ = error_ctor;
+    }
+}
+
+
+static njs_int_t
+njs_vm_protos_init(njs_vm_t *vm, njs_value_t *global)
+{
+    size_t  ctor_size, proto_size;
+
+    vm->constructors_size = vm->shared->constructors->items;
+
+    ctor_size = vm->constructors_size * sizeof(njs_function_t);
+    proto_size = vm->constructors_size * sizeof(njs_object_prototype_t);
+
+    vm->constructors = njs_mp_alloc(vm->mem_pool, ctor_size + proto_size);
+    if (njs_slow_path(vm->constructors == NULL)) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    vm->prototypes = (njs_object_prototype_t *)
+                                     ((u_char *) vm->constructors + ctor_size);
+
+    memcpy(vm->constructors, vm->shared->constructors->start, ctor_size);
+    memcpy(vm->prototypes, vm->shared->prototypes->start, proto_size);
+
+    njs_vm_constructors_init(vm);
+
+    vm->global_object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
+
+    njs_set_undefined(global);
+    njs_set_object(global, &vm->global_object);
+
+    vm->string_object = vm->shared->string_object;
+    vm->string_object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_STRING);
+
+    return NJS_OK;
+}
+
+
 njs_int_t
 njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
     njs_uint_t nargs)
@@ -733,17 +916,32 @@ njs_vm_throw(njs_vm_t *vm, const njs_value_t *value)
 
 
 void
-njs_vm_error2(njs_vm_t *vm, unsigned type, const char *fmt, ...)
+njs_vm_error2(njs_vm_t *vm, unsigned error_type, const char *fmt, ...)
+{
+    va_list  args;
+
+    if (error_type > (NJS_OBJ_TYPE_ERROR_MAX - NJS_OBJ_TYPE_ERROR)) {
+        return;
+    }
+
+    va_start(args, fmt);
+    error_type += NJS_OBJ_TYPE_ERROR;
+    njs_throw_error_va(vm, njs_vm_proto(vm, error_type), fmt, args);
+    va_end(args);
+}
+
+
+void
+njs_vm_error3(njs_vm_t *vm, unsigned type, const char *fmt, ...)
 {
     va_list  args;
 
-    if (type > (NJS_OBJ_TYPE_ERROR_MAX - NJS_OBJ_TYPE_ERROR)) {
+    if (type > vm->constructors_size) {
         return;
     }
 
     va_start(args, fmt);
-    type += NJS_OBJ_TYPE_ERROR;
-    njs_throw_error_va(vm, &vm->prototypes[type].object, fmt, args);
+    njs_throw_error_va(vm, njs_vm_proto(vm, type), fmt, args);
     va_end(args);
 }
 
@@ -1013,7 +1211,7 @@ njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src)
 
     if (njs_slow_path(vm->top_frame == NULL)) {
         /* An exception was thrown during compilation. */
-        njs_vm_init(vm);
+        njs_vm_runtime_init(vm);
     }
 
     if (njs_is_valid(&vm->exception)) {
index b1ecb7e20be659f4ca94fcb4fbffc938a776d5fd..2389113ffd047c0a54a8ceb162c16183d5727142 100644 (file)
@@ -142,13 +142,12 @@ struct njs_vm_s {
 
     njs_vm_opt_t             options;
 
-    /*
-     * The prototypes and constructors arrays must be together because
-     * they are copied from njs_vm_shared_t by single memcpy()
-     * in njs_builtin_objects_clone().
-     */
-    njs_object_prototype_t   prototypes[NJS_OBJ_TYPE_MAX];
-    njs_function_t           constructors[NJS_OBJ_TYPE_MAX];
+#define njs_vm_proto(vm, index) (&(vm)->prototypes[index].object)
+#define njs_vm_ctor(vm, index) ((vm)->constructors[index])
+
+    njs_object_prototype_t   *prototypes;
+    njs_function_t           *constructors;
+    size_t                   constructors_size;
 
     njs_function_t           *hooks[NJS_HOOK_MAX];
 
@@ -223,25 +222,28 @@ struct njs_vm_shared_s {
     njs_object_t             string_object;
     njs_object_t             objects[NJS_OBJECT_MAX];
 
-    njs_exotic_slots_t       global_slots;
+#define njs_shared_ctor(shared, index)                                       \
+    ((njs_function_t *) njs_arr_item((shared)->constructors, index))
 
-    /*
-     * The prototypes and constructors arrays must be togther because they are
-     * copied to njs_vm_t by single memcpy() in njs_builtin_objects_clone().
-     */
-    njs_object_prototype_t   prototypes[NJS_OBJ_TYPE_MAX];
-    njs_function_t           constructors[NJS_OBJ_TYPE_MAX];
+#define njs_shared_prototype(shared, index)                                  \
+    ((njs_object_prototype_t *) njs_arr_item((shared)->prototypes, index))
+
+    njs_arr_t                *constructors; /* of njs_function_t */
+    njs_arr_t                *prototypes; /* of njs_object_prototype_t */
+
+    njs_exotic_slots_t       global_slots;
 
     njs_regexp_pattern_t     *empty_regexp_pattern;
 };
 
 
-njs_int_t njs_vm_init(njs_vm_t *vm);
+njs_int_t njs_vm_runtime_init(njs_vm_t *vm);
+njs_int_t njs_vm_ctor_push(njs_vm_t *vm);
+void njs_vm_constructors_init(njs_vm_t *vm);
 njs_value_t njs_vm_exception(njs_vm_t *vm);
 void njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *frame);
 
 njs_int_t njs_builtin_objects_create(njs_vm_t *vm);
-njs_int_t njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global);
 njs_int_t njs_builtin_match_native_function(njs_vm_t *vm,
     njs_function_t *function, njs_str_t *name);
 
index 80a45f0f51188676025322b6456802e8dd6c0caf..b67c2fd98325ed2e2d09809e1a34d4cefac5e7e2 100644 (file)
@@ -2660,7 +2660,7 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await,
         return NJS_ERROR;
     }
 
-    njs_set_function(&ctor, &vm->constructors[NJS_OBJ_TYPE_PROMISE]);
+    njs_set_function(&ctor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_PROMISE));
 
     promise = njs_promise_resolve(vm, &ctor, value);
     if (njs_slow_path(promise == NULL)) {
index 19d076a34b4c99ef012032349fc2a5fe0931876d..2fa7510992e4013cb9bd2b09998d1646be1d9eb9 100644 (file)
@@ -27,23 +27,26 @@ typedef struct {
 
 
 static njs_int_t njs_externals_262_init(njs_vm_t *vm);
-static njs_int_t njs_externals_shared_init(njs_vm_t *vm);
+static njs_int_t njs_externals_shared_preinit(njs_vm_t *vm);
 njs_int_t njs_array_buffer_detach(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 
 
 static njs_int_t    njs_external_r_proto_id;
+static njs_int_t    njs_external_error_ctor_id;
 
 
 njs_module_t  njs_unit_test_262_module = {
     .name = njs_str("$262"),
+    .preinit = NULL,
     .init = njs_externals_262_init,
 };
 
 
 njs_module_t  njs_unit_test_external_module = {
     .name = njs_str("external"),
-    .init = njs_externals_shared_init,
+    .preinit = njs_externals_shared_preinit,
+    .init = NULL,
 };
 
 
@@ -481,6 +484,16 @@ njs_unit_test_r_retval(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
+static njs_int_t
+njs_unit_test_r_custom_exception(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
+{
+    njs_vm_error3(vm, njs_external_error_ctor_id, "Oops", NULL);
+
+    return NJS_ERROR;
+}
+
+
 static njs_int_t
 njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
@@ -565,6 +578,22 @@ njs_unit_test_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
+static njs_int_t
+njs_unit_test_error_name(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    return njs_vm_value_string_set(vm, retval, (u_char *) "ExternalError", 13);
+}
+
+
+static njs_int_t
+njs_unit_test_error_message(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    return njs_vm_value_string_set(vm, retval, (u_char *) "", 0);
+}
+
+
 static njs_int_t
 njs_262_bytes_from_array_like(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *retval)
@@ -966,6 +995,17 @@ static njs_external_t  njs_unit_test_r_external[] = {
         }
     },
 
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("customException"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_unit_test_r_custom_exception,
+        }
+    },
+
     {
         .flags = NJS_EXTERN_OBJECT,
         .name.string = njs_str("props"),
@@ -1004,6 +1044,61 @@ static njs_external_t  njs_unit_test_r_external[] = {
 };
 
 
+static njs_external_t  njs_unit_test_ctor_props[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("name"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_error_name,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("prototype"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_object_prototype_create,
+        }
+    },
+
+};
+
+
+static njs_external_t  njs_unit_test_proto_props[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("name"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_error_name,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("message"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_unit_test_error_message,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("constructor"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_object_prototype_create_constructor,
+        }
+    },
+
+};
+
+
 typedef struct {
     njs_str_t             name;
     njs_unit_test_req_t   request;
@@ -1075,6 +1170,7 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init,
     njs_unit_test_prop_t  *prop;
 
     static const njs_str_t  external_ctor = njs_str("ExternalConstructor");
+    static const njs_str_t  external_error = njs_str("ExternalError");
 
     if (shared) {
         njs_external_r_proto_id = njs_vm_external_prototype(vm,
@@ -1098,6 +1194,17 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init,
             njs_printf("njs_vm_bind() failed\n");
             return NJS_ERROR;
         }
+
+        njs_external_error_ctor_id =
+            njs_vm_external_constructor(vm, &external_error,
+                              njs_error_constructor, njs_unit_test_ctor_props,
+                              njs_nitems(njs_unit_test_ctor_props),
+                              njs_unit_test_proto_props,
+                              njs_nitems(njs_unit_test_proto_props));
+        if (njs_slow_path(njs_external_error_ctor_id < 0)) {
+            njs_printf("njs_vm_external_constructor() failed\n");
+            return NJS_ERROR;
+        }
     }
 
     requests = njs_mp_zalloc(vm->mem_pool, n * sizeof(njs_unit_test_req_t));
@@ -1177,7 +1284,7 @@ njs_externals_262_init(njs_vm_t *vm)
 
 
 static njs_int_t
-njs_externals_shared_init(njs_vm_t *vm)
+njs_externals_shared_preinit(njs_vm_t *vm)
 {
     return njs_externals_init_internal(vm, njs_test_requests, 1, 1);
 }
index d3128ba0b8ad40b5cc36beebacd8beb72fa4f076..f58e968ebe5069bbf68a86846c3890d8b4b6723b 100644 (file)
@@ -22358,6 +22358,48 @@ static njs_unit_test_t  njs_module_test[] =
 
 static njs_unit_test_t  njs_externals_test[] =
 {
+    { njs_str("(new ExternalError('XXX')) instanceof ExternalError"),
+      njs_str("true") },
+
+    { njs_str("(new ExternalError('XXX')) instanceof Error"),
+      njs_str("true") },
+
+    { njs_str("(new ExternalError()).message"),
+      njs_str("") },
+
+    { njs_str("(new ExternalError('XXX')).message"),
+      njs_str("XXX") },
+
+    { njs_str("(new ExternalError('XXX')).constructor == ExternalError"),
+      njs_str("true") },
+
+    { njs_str("(new ExternalError('XXX')).name"),
+      njs_str("ExternalError") },
+
+    { njs_str("(new ExternalError('XXX')).__proto__.name"),
+      njs_str("ExternalError") },
+
+    { njs_str("(new ExternalError('XXX')).__proto__.__proto__.name"),
+      njs_str("Error") },
+
+    { njs_str("(new ExternalError('XXX'))"),
+      njs_str("ExternalError: XXX") },
+
+    { njs_str("(new ExternalError('XXX')).toString()"),
+      njs_str("ExternalError: XXX") },
+
+    { njs_str("njs.dump(new ExternalError('XXX'))"),
+      njs_str("ExternalError: XXX") },
+
+    { njs_str("JSON.stringify(new ExternalError('XXX'))"),
+      njs_str("{}") },
+
+    { njs_str("Object.getOwnPropertyNames(new ExternalError('XXX'))"),
+      njs_str("message") },
+
+    { njs_str("var ee; try{ $r.customException() } catch (e) { if (!(e instanceof ExternalError)) { throw 'Oops'} ee = e;}; ee.toString()"),
+      njs_str("ExternalError: Oops") },
+
     { njs_str("typeof $r"),
       njs_str("object") },
 
@@ -22583,7 +22625,7 @@ static njs_unit_test_t  njs_externals_test[] =
 #endif
 
     { njs_str("Object.keys(this).sort()"),
-      njs_str("$262,$r,$r2,$r3,$shared,ExternalConstructor," NCRYPTO "global,njs,process") },
+      njs_str("$262,$r,$r2,$r3,$shared,ExternalConstructor,ExternalError," NCRYPTO "global,njs,process") },
 
     { njs_str("Object.getOwnPropertySymbols($r2)[0] == Symbol.toStringTag"),
       njs_str("true") },