[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