diff options
author | Dmitry Volyntsev <xeioex@nginx.com> | 2023-07-03 12:49:00 -0700 |
---|---|---|
committer | Dmitry Volyntsev <xeioex@nginx.com> | 2023-07-03 12:49:00 -0700 |
commit | 40b01ac098d7835263b1ab80bb040edc3d98b29b (patch) | |
tree | fcbc6ef97d9fa64842930c2a9e0b1fe2289f51c8 | |
parent | 7d5e38cc7df93767cd44904f892acdec384c6638 (diff) | |
download | njs-40b01ac098d7835263b1ab80bb040edc3d98b29b.tar.gz njs-40b01ac098d7835263b1ab80bb040edc3d98b29b.zip |
Added njs_vm_external_constructor().
The new API allows to add new constructor/prototype pairs.
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, }; @@ -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(®exp->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") }, |