[njs] User defined function prototype.
Igor Sysoev
igor at sysoev.ru
Thu Mar 24 15:17:01 UTC 2016
details: http://hg.nginx.org/njs/rev/6e70fe1e0a47
branches:
changeset: 88:6e70fe1e0a47
user: Igor Sysoev <igor at 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") },
More information about the nginx-devel
mailing list