aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Volyntsev <xeioex@nginx.com>2023-07-03 12:49:00 -0700
committerDmitry Volyntsev <xeioex@nginx.com>2023-07-03 12:49:00 -0700
commit40b01ac098d7835263b1ab80bb040edc3d98b29b (patch)
treefcbc6ef97d9fa64842930c2a9e0b1fe2289f51c8
parent7d5e38cc7df93767cd44904f892acdec384c6638 (diff)
downloadnjs-40b01ac098d7835263b1ab80bb040edc3d98b29b.tar.gz
njs-40b01ac098d7835263b1ab80bb040edc3d98b29b.zip
Added njs_vm_external_constructor().
The new API allows to add new constructor/prototype pairs.
-rw-r--r--external/njs_crypto_module.c1
-rw-r--r--external/njs_fs_module.c1
-rw-r--r--external/njs_query_string_module.c1
-rw-r--r--external/njs_shell.c1
-rw-r--r--external/njs_webcrypto_module.c1
-rw-r--r--external/njs_xml_module.c1
-rw-r--r--external/njs_zlib_module.c1
-rw-r--r--nginx/ngx_http_js_module.c1
-rw-r--r--nginx/ngx_js.c1
-rw-r--r--nginx/ngx_js_fetch.c1
-rw-r--r--nginx/ngx_stream_js_module.c1
-rw-r--r--src/njs.h18
-rw-r--r--src/njs_array.c2
-rw-r--r--src/njs_array_buffer.c2
-rw-r--r--src/njs_async.c2
-rw-r--r--src/njs_buffer.c13
-rw-r--r--src/njs_builtin.c127
-rw-r--r--src/njs_date.c2
-rw-r--r--src/njs_error.c16
-rw-r--r--src/njs_extern.c71
-rw-r--r--src/njs_function.c10
-rw-r--r--src/njs_json.c2
-rw-r--r--src/njs_object.c16
-rw-r--r--src/njs_parser.c6
-rw-r--r--src/njs_promise.c12
-rw-r--r--src/njs_regexp.c8
-rw-r--r--src/njs_typed_array.c6
-rw-r--r--src/njs_value.c2
-rw-r--r--src/njs_value.h5
-rw-r--r--src/njs_vm.c224
-rw-r--r--src/njs_vm.h34
-rw-r--r--src/njs_vmcode.c2
-rw-r--r--src/test/njs_externals_test.c113
-rw-r--r--src/test/njs_unit_test.c44
34 files changed, 561 insertions, 187 deletions
diff --git a/external/njs_crypto_module.c b/external/njs_crypto_module.c
index 58f1787e..af5ddf2f 100644
--- a/external/njs_crypto_module.c
+++ b/external/njs_crypto_module.c
@@ -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,
};
diff --git a/external/njs_fs_module.c b/external/njs_fs_module.c
index e5213c0a..b92e7b89 100644
--- a/external/njs_fs_module.c
+++ b/external/njs_fs_module.c
@@ -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,
};
diff --git a/external/njs_query_string_module.c b/external/njs_query_string_module.c
index 7eb066d1..b30ffe84 100644
--- a/external/njs_query_string_module.c
+++ b/external/njs_query_string_module.c
@@ -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,
};
diff --git a/external/njs_shell.c b/external/njs_shell.c
index e8b221d2..3a92092f 100644
--- a/external/njs_shell.c
+++ b/external/njs_shell.c
@@ -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,
};
diff --git a/external/njs_webcrypto_module.c b/external/njs_webcrypto_module.c
index 1d35bc08..cf715186 100644
--- a/external/njs_webcrypto_module.c
+++ b/external/njs_webcrypto_module.c
@@ -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,
};
diff --git a/external/njs_xml_module.c b/external/njs_xml_module.c
index b84bebc8..2b374501 100644
--- a/external/njs_xml_module.c
+++ b/external/njs_xml_module.c
@@ -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,
};
diff --git a/external/njs_zlib_module.c b/external/njs_zlib_module.c
index e00e2300..3f2e23fe 100644
--- a/external/njs_zlib_module.c
+++ b/external/njs_zlib_module.c
@@ -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,
};
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
index 135afa5b..df3a0e5a 100644
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -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,
};
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index 8973f9f6..a3b91ca4 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -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,
};
diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c
index 9d81c292..46d751b2 100644
--- a/nginx/ngx_js_fetch.c
+++ b/nginx/ngx_js_fetch.c
@@ -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,
};
diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c
index b5c8681b..3e2a2dab 100644
--- a/nginx/ngx_stream_js_module.c
+++ b/nginx/ngx_stream_js_module.c
@@ -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,
};
diff --git a/src/njs.h b/src/njs.h
index de937585..38357f0f 100644
--- 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);
diff --git a/src/njs_array.c b/src/njs_array.c
index f87abd87..09a45339 100644
--- a/src/njs_array.c
+++ b/src/njs_array.c
@@ -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;
diff --git a/src/njs_array_buffer.c b/src/njs_array_buffer.c
index 43f5982a..68eb8db0 100644
--- a/src/njs_array_buffer.c
+++ b/src/njs_array_buffer.c
@@ -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);
diff --git a/src/njs_async.c b/src/njs_async.c
index 7b3b9331..3d47f4af 100644
--- a/src/njs_async.c
+++ b/src/njs_async.c
@@ -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)) {
diff --git a/src/njs_buffer.c b/src/njs_buffer.c
index 5ab506ca..7f2067fa 100644
--- a/src/njs_buffer.c
+++ b/src/njs_buffer.c
@@ -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;
}
diff --git a/src/njs_builtin.c b/src/njs_builtin.c
index 317458e0..355e82cf 100644
--- a/src/njs_builtin.c
+++ b/src/njs_builtin.c
@@ -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);
}
diff --git a/src/njs_date.c b/src/njs_date.c
index 9337436c..8aa6ea6e 100644
--- a/src/njs_date.c
+++ b/src/njs_date.c
@@ -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;
diff --git a/src/njs_error.c b/src/njs_error.c
index fc069a1c..4989b897 100644
--- a/src/njs_error.c
+++ b/src/njs_error.c
@@ -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;
}
diff --git a/src/njs_extern.c b/src/njs_extern.c
index 6cc9b00a..50ac76c4 100644
--- a/src/njs_extern.c
+++ b/src/njs_extern.c
@@ -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)
diff --git a/src/njs_function.c b/src/njs_function.c
index 67f74db8..1d91fde3 100644
--- a/src/njs_function.c
+++ b/src/njs_function.c
@@ -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]);
diff --git a/src/njs_json.c b/src/njs_json.c
index ffe68ff2..01bf3214 100644
--- a/src/njs_json.c
+++ b/src/njs_json.c
@@ -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)) {
diff --git a/src/njs_object.c b/src/njs_object.c
index 287b354a..91ae922a 100644
--- a/src/njs_object.c
+++ b/src/njs_object.c
@@ -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);
diff --git a/src/njs_parser.c b/src/njs_parser.c
index 0d689adb..5b1b2aa7 100644
--- a/src/njs_parser.c
+++ b/src/njs_parser.c
@@ -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);
diff --git a/src/njs_promise.c b/src/njs_promise.c
index 6dc3de18..d9cebe04 100644
--- a/src/njs_promise.c
+++ b/src/njs_promise.c
@@ -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;
}
diff --git a/src/njs_regexp.c b/src/njs_regexp.c
index de25a2a7..e432651c 100644
--- a/src/njs_regexp.c
+++ b/src/njs_regexp.c
@@ -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)) {
diff --git a/src/njs_typed_array.c b/src/njs_typed_array.c
index 4a4e5ffc..3bc37b22 100644
--- a/src/njs_typed_array.c
+++ b/src/njs_typed_array.c
@@ -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;
diff --git a/src/njs_value.c b/src/njs_value.c
index 05dcf9e8..361e11f6 100644
--- a/src/njs_value.c
+++ b/src/njs_value.c
@@ -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:
diff --git a/src/njs_value.h b/src/njs_value.h
index 570c84ff..9491702a 100644
--- a/src/njs_value.h
+++ b/src/njs_value.h
@@ -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) \
diff --git a/src/njs_vm.c b/src/njs_vm.c
index fb334294..920f836f 100644
--- a/src/njs_vm.c
+++ b/src/njs_vm.c
@@ -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)) {
diff --git a/src/njs_vm.h b/src/njs_vm.h
index b1ecb7e2..2389113f 100644
--- a/src/njs_vm.h
+++ b/src/njs_vm.h
@@ -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);
diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c
index 80a45f0f..b67c2fd9 100644
--- a/src/njs_vmcode.c
+++ b/src/njs_vmcode.c
@@ -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)) {
diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c
index 19d076a3..2fa75109 100644
--- a/src/test/njs_externals_test.c
+++ b/src/test/njs_externals_test.c
@@ -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,
};
@@ -482,6 +485,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)
{
@@ -566,6 +579,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)
{
@@ -967,6 +996,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"),
.enumerable = 1,
@@ -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);
}
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index d3128ba0..f58e968e 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -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") },