details: http://hg.nginx.org/njs/rev/6e70fe1e0a47 branches: changeset: 88:6e70fe1e0a47 user: Igor Sysoev <i...@sysoev.ru> date: Wed Mar 23 15:49:49 2016 +0300 description: User defined function prototype.
diffstat: njs/njs_builtin.c | 18 ++++++-- njs/njs_function.c | 63 ++++++++++++++++++++++++++++- njs/njs_function.h | 3 + njs/njs_object.c | 101 ++++++++++++++++++++++++++++++++-------------- njs/njs_object.h | 4 + njs/njs_vm.c | 45 ++++++++++++++++++++- njs/njs_vm.h | 1 + njs/test/njs_unit_test.c | 14 ++++++ 8 files changed, 209 insertions(+), 40 deletions(-) diffs (438 lines): diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_builtin.c --- a/njs/njs_builtin.c Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_builtin.c Wed Mar 23 15:49:49 2016 +0300 @@ -77,18 +77,30 @@ njs_builtin_objects_create(njs_vm_t *vm) &njs_math_object_init, }; - static const njs_object_prop_t null_proto_property = { + static const njs_object_prop_t null_proto_property = { .type = NJS_WHITEOUT, .name = njs_string("__proto__"), .value = njs_value(NJS_NULL, 0, 0.0), }; + static const njs_object_prop_t function_prototype_property = { + .type = NJS_NATIVE_GETTER, + .name = njs_string("prototype"), + .value = njs_native_getter(njs_function_prototype_create), + }; + ret = njs_object_hash_create(vm, &vm->shared->null_proto_hash, &null_proto_property, 1); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } + ret = njs_object_hash_create(vm, &vm->shared->function_prototype_hash, + &function_prototype_property, 1); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + objects = vm->shared->objects; for (i = NJS_OBJECT_MATH; i < NJS_OBJECT_MAX; i++) { @@ -105,8 +117,6 @@ njs_builtin_objects_create(njs_vm_t *vm) prototypes = vm->shared->prototypes; for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) { - /* TODO: shared hash: prototype & constructor getters, methods */ - ret = njs_object_hash_create(vm, &prototypes[i].shared_hash, prototype_init[i]->properties, prototype_init[i]->items); @@ -134,8 +144,6 @@ njs_builtin_objects_create(njs_vm_t *vm) } } - /* TODO: create function shared hash: prototype+contructor getter */ - return NXT_OK; } diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_function.c --- a/njs/njs_function.c Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_function.c Wed Mar 23 15:49:49 2016 +0300 @@ -35,10 +35,10 @@ njs_function_alloc(njs_vm_t *vm) /* * nxt_mem_cache_zalloc() does also: * nxt_lvlhsh_init(&function->object.hash); - * nxt_lvlhsh_init(&function->object.shared_hash); * function->object.__proto__ = NULL; */ + function->object.shared_hash = vm->shared->function_prototype_hash; function->object.shared = 1; function->args_offset = 1; @@ -305,6 +305,67 @@ njs_function_call(njs_vm_t *vm, njs_inde } +/* + * The "prototype" property of user defined functions is created on + * demand in private hash of the functions by the "prototype" getter. + * The getter creates a copy of function which is private to nJSVM, + * adds a "prototype" object property to the copy, and then adds a + * "constructor" property in the prototype object. The "constructor" + * property points to the copy of function: + * "F.prototype.constructor === F" + */ + +njs_ret_t +njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value) +{ + njs_value_t *proto; + + proto = njs_function_property_prototype_create(vm, value); + + if (nxt_fast_path(proto != NULL)) { + vm->retval = *proto; + return NXT_OK; + } + + return NXT_ERROR; +} + + +njs_value_t * +njs_function_property_prototype_create(njs_vm_t *vm, njs_value_t *value) +{ + njs_value_t *proto, *cons; + njs_object_t *prototype; + njs_function_t *function; + + prototype = njs_object_alloc(vm); + + if (nxt_slow_path(prototype == NULL)) { + return NULL; + } + + function = njs_function_value_copy(vm, value); + + if (nxt_slow_path(function == NULL)) { + return NULL; + } + + proto = njs_property_prototype_create(vm, &function->object.hash, + prototype); + if (nxt_slow_path(proto == NULL)) { + return NULL; + } + + cons = njs_property_constructor_create(vm, &prototype->hash, value); + + if (nxt_fast_path(cons != NULL)) { + return proto; + } + + return NULL; +} + + njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_function.h --- a/njs/njs_function.h Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_function.h Wed Mar 23 15:49:49 2016 +0300 @@ -122,6 +122,9 @@ typedef struct { njs_function_t *njs_function_alloc(njs_vm_t *vm); njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value); njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size); +njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value); +njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm, + njs_value_t *value); njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); njs_ret_t njs_function_apply(njs_vm_t *vm, njs_function_t *function, diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_object.c --- a/njs/njs_object.c Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_object.c Wed Mar 23 15:49:49 2016 +0300 @@ -353,28 +353,47 @@ njs_primitive_prototype_get_proto(njs_vm njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value) { - int32_t index; + int32_t index; + njs_value_t *proto; + njs_function_t *function; + + proto = NULL; + function = value->data.u.function; + index = function - vm->functions; + + if (index >= 0 && index < NJS_PROTOTYPE_MAX) { + proto = njs_property_prototype_create(vm, &function->object.hash, + &vm->prototypes[index]); + } + + if (proto == NULL) { + proto = (njs_value_t *) &njs_value_void; + } + + vm->retval = *proto; + + return NXT_OK; +} + + +njs_value_t * +njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, + njs_object_t *prototype) +{ nxt_int_t ret; - njs_function_t *function; njs_object_prop_t *prop; nxt_lvlhsh_query_t lhq; - static const njs_value_t prototype = njs_string("prototype"); + static const njs_value_t prototype_string = njs_string("prototype"); - function = value->data.u.function; - index = function - vm->functions; - - if (index < 0 && index > NJS_PROTOTYPE_MAX) { - vm->retval = njs_value_void; - return NXT_OK; + prop = njs_object_prop_alloc(vm, &prototype_string); + if (nxt_slow_path(prop == NULL)) { + return NULL; } - prop = njs_object_prop_alloc(vm, &prototype); - if (nxt_slow_path(prop == NULL)) { - return NXT_ERROR; - } + /* GC */ - prop->value.data.u.object = &vm->prototypes[index]; + prop->value.data.u.object = prototype; prop->value.type = NJS_OBJECT; prop->value.data.truth = 1; @@ -390,15 +409,16 @@ njs_object_prototype_create(njs_vm_t *vm lhq.pool = vm->mem_cache_pool; lhq.proto = &njs_object_hash_proto; - ret = nxt_lvlhsh_insert(&function->object.hash, &lhq); + ret = nxt_lvlhsh_insert(hash, &lhq); if (nxt_fast_path(ret == NXT_OK)) { - vm->retval = prop->value; + return &prop->value; } - /* TODO: exception NXT_ERROR. */ + /* Memory allocation or NXT_DECLINED error. */ + vm->exception = &njs_exception_internal_error; - return ret; + return NULL; } @@ -469,14 +489,9 @@ njs_object_prototype_get_proto(njs_vm_t static njs_ret_t njs_object_prototype_create_constructor(njs_vm_t *vm, njs_value_t *value) { - int32_t index; - nxt_int_t ret; - njs_value_t *constructor; - njs_object_t *prototype; - njs_object_prop_t *prop; - nxt_lvlhsh_query_t lhq; - - static const njs_value_t constructor_string = njs_string("constructor"); + int32_t index; + njs_value_t *cons; + njs_object_t *prototype; if (njs_is_object(value)) { prototype = value->data.u.object; @@ -503,16 +518,35 @@ njs_object_prototype_create_constructor( found: + cons = njs_property_constructor_create(vm, &prototype->hash, + &vm->scopes[NJS_SCOPE_GLOBAL][index]); + if (nxt_fast_path(cons != NULL)) { + vm->retval = *cons; + return NXT_OK; + } + + return NXT_ERROR; +} + + +njs_value_t * +njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, + njs_value_t *constructor) +{ + nxt_int_t ret; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + static const njs_value_t constructor_string = njs_string("constructor"); + prop = njs_object_prop_alloc(vm, &constructor_string); if (nxt_slow_path(prop == NULL)) { - return NXT_ERROR; + return NULL; } /* GC */ - constructor = &vm->scopes[NJS_SCOPE_GLOBAL][index]; prop->value = *constructor; - prop->enumerable = 0; lhq.value = prop; @@ -523,13 +557,16 @@ found: lhq.pool = vm->mem_cache_pool; lhq.proto = &njs_object_hash_proto; - ret = nxt_lvlhsh_insert(&prototype->hash, &lhq); + ret = nxt_lvlhsh_insert(hash, &lhq); if (nxt_fast_path(ret == NXT_OK)) { - vm->retval = *constructor; + return &prop->value; } - return ret; + /* Memory allocation or NXT_DECLINED error. */ + vm->exception = &njs_exception_internal_error; + + return NULL; } diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_object.h --- a/njs/njs_object.h Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_object.h Wed Mar 23 15:49:49 2016 +0300 @@ -56,7 +56,11 @@ njs_ret_t njs_object_constructor(njs_vm_ njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name); njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value); njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value); +njs_value_t *njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, + njs_object_t *prototype); njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value); +njs_value_t *njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, + njs_value_t *constructor); njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_vm.c --- a/njs/njs_vm.c Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_vm.c Wed Mar 23 15:49:49 2016 +0300 @@ -82,6 +82,7 @@ static nxt_noinline njs_ret_t njs_values njs_value_t *val2); static nxt_noinline nxt_bool_t njs_values_strict_equal(njs_value_t *val1, njs_value_t *val2); +static njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *value); static njs_ret_t njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2); static njs_native_frame_t * @@ -118,6 +119,7 @@ const njs_value_t njs_value_true = const njs_value_t njs_value_zero = njs_value(NJS_NUMBER, 0, 0.0); const njs_value_t njs_value_nan = njs_value(NJS_NUMBER, 0, NJS_NAN); + const njs_value_t njs_string_empty = njs_string(""); const njs_value_t njs_string_comma = njs_string(","); const njs_value_t njs_string_null = njs_string("null"); @@ -2151,8 +2153,7 @@ njs_vmcode_function_frame(njs_vm_t *vm, } if (func->code.ctor) { - object = njs_object_alloc(vm); - + object = njs_function_new_object(vm, value); if (nxt_slow_path(object == NULL)) { return NXT_ERROR; } @@ -2182,6 +2183,46 @@ njs_vmcode_function_frame(njs_vm_t *vm, } +static njs_object_t * +njs_function_new_object(njs_vm_t *vm, njs_value_t *value) +{ + nxt_int_t ret; + njs_value_t *proto; + njs_object_t *object; + njs_function_t *function; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + object = njs_object_alloc(vm); + + if (nxt_fast_path(object != NULL)) { + + lhq.key_hash = NJS_PROTOTYPE_HASH; + lhq.key.len = sizeof("prototype") - 1; + lhq.key.data = (u_char *) "prototype"; + lhq.proto = &njs_object_hash_proto; + function = value->data.u.function; + + ret = nxt_lvlhsh_find(&function->object.hash, &lhq); + + if (ret == NXT_OK) { + prop = lhq.value; + proto = &prop->value; + + } else { + proto = njs_function_property_prototype_create(vm, value); + } + + if (nxt_fast_path(proto != NULL)) { + object->__proto__ = proto->data.u.object; + return object; + } + } + + return NULL; +} + + njs_ret_t njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) { diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/njs_vm.h --- a/njs/njs_vm.h Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/njs_vm.h Wed Mar 23 15:49:49 2016 +0300 @@ -789,6 +789,7 @@ struct njs_vm_shared_s { nxt_lvlhsh_t keywords_hash; nxt_lvlhsh_t values_hash; nxt_lvlhsh_t null_proto_hash; + nxt_lvlhsh_t function_prototype_hash; njs_object_t objects[NJS_OBJECT_MAX]; diff -r 7ad8820b9c74 -r 6e70fe1e0a47 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Mar 23 15:49:46 2016 +0300 +++ b/njs/test/njs_unit_test.c Wed Mar 23 15:49:49 2016 +0300 @@ -3248,6 +3248,20 @@ static njs_unit_test_t njs_test[] = "o.a"), nxt_string("7") }, + { nxt_string("function F(a, b) { return }" + "F.prototype.constructor === F"), + nxt_string("true") }, + + { nxt_string("function F() { return }" + "F.prototype.ok = 'OK';" + "var o = new F(); o.ok"), + nxt_string("OK") }, + + { nxt_string("function F() { return }" + "var o = new F();" + "o.constructor === F"), + nxt_string("true") }, + { nxt_string("function a() { return function(x) { return x + 1 } }" "b = a(); b(2)"), nxt_string("3") }, _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel