[njs] Added property getter support for njs_object_property().

Dmitry Volyntsev xeioex at nginx.com
Wed Aug 7 18:28:57 UTC 2019


details:   https://hg.nginx.org/njs/rev/b9d9f2813d9a
branches:  
changeset: 1114:b9d9f2813d9a
user:      hongzhidao <hongzhidao at gmail.com>
date:      Tue Aug 06 22:54:13 2019 -0400
description:
Added property getter support for njs_object_property().

diffstat:

 src/njs_array.c          |   22 +++--
 src/njs_date.c           |   22 +++--
 src/njs_error.c          |   29 +++---
 src/njs_json.c           |   18 ++--
 src/njs_object.h         |   12 ++-
 src/njs_object_prop.c    |  202 +++++++++++++++++++++++++++++-----------------
 src/njs_value.c          |   33 +++---
 src/test/njs_unit_test.c |   37 ++++++++
 8 files changed, 239 insertions(+), 136 deletions(-)

diffs (600 lines):

diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_array.c
--- a/src/njs_array.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_array.c	Tue Aug 06 22:54:13 2019 -0400
@@ -825,18 +825,22 @@ static njs_int_t
 njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    njs_object_prop_t   *prop;
+    njs_int_t           ret;
+    njs_value_t         value;
     njs_lvlhsh_query_t  lhq;
 
     if (njs_is_object(&args[0])) {
-        lhq.key_hash = NJS_JOIN_HASH;
-        lhq.key = njs_str_value("join");
-
-        prop = njs_object_property(vm, njs_object(&args[0]), &lhq);
-
-        if (njs_fast_path(prop != NULL && njs_is_function(&prop->value))) {
-            return njs_function_apply(vm, njs_function(&prop->value), args,
-                                      nargs, &vm->retval);
+        njs_object_property_init(&lhq, "join", NJS_JOIN_HASH);
+
+        ret = njs_object_property(vm, &args[0], &lhq, &value);
+
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (njs_is_function(&value)) {
+            return njs_function_apply(vm, njs_function(&value), args, nargs,
+                                      &vm->retval);
         }
     }
 
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_date.c
--- a/src/njs_date.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_date.c	Tue Aug 06 22:54:13 2019 -0400
@@ -1896,18 +1896,22 @@ static njs_int_t
 njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t retval)
 {
-    njs_object_prop_t   *prop;
+    njs_int_t           ret;
+    njs_value_t         value;
     njs_lvlhsh_query_t  lhq;
 
     if (njs_is_object(&args[0])) {
-        lhq.key_hash = NJS_TO_ISO_STRING_HASH;
-        lhq.key = njs_str_value("toISOString");
-
-        prop = njs_object_property(vm, njs_object(&args[0]), &lhq);
-
-        if (njs_fast_path(prop != NULL && njs_is_function(&prop->value))) {
-            return njs_function_apply(vm, njs_function(&prop->value), args,
-                                      nargs, &vm->retval);
+        njs_object_property_init(&lhq, "toISOString", NJS_TO_ISO_STRING_HASH);
+
+        ret = njs_object_property(vm, &args[0], &lhq, &value);
+
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (njs_is_function(&value)) {
+            return njs_function_apply(vm, njs_function(&value), args, nargs,
+                                      &vm->retval);
         }
     }
 
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_error.c
--- a/src/njs_error.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_error.c	Tue Aug 06 22:54:13 2019 -0400
@@ -619,39 +619,36 @@ njs_error_to_string(njs_vm_t *vm, njs_va
 {
     size_t              size;
     u_char              *p;
+    njs_int_t           ret;
     njs_str_t           name, message;
+    njs_value_t         value1, value2;
     const njs_value_t   *name_value, *message_value;
-    njs_object_prop_t   *prop;
     njs_lvlhsh_query_t  lhq;
 
     static const njs_value_t  default_name = njs_string("Error");
 
-    lhq.key_hash = NJS_NAME_HASH;
-    lhq.key = njs_str_value("name");
-    lhq.proto = &njs_object_hash_proto;
+    njs_object_property_init(&lhq, "name", NJS_NAME_HASH);
 
-    prop = njs_object_property(vm, njs_object(error), &lhq);
+    ret = njs_object_property(vm, error, &lhq, &value1);
 
-    if (prop != NULL) {
-        name_value = &prop->value;
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
 
-    } else {
-        name_value = &default_name;
-    }
+    name_value = (ret == NJS_OK) ? &value1 : &default_name;
 
     njs_string_get(name_value, &name);
 
     lhq.key_hash = NJS_MESSAGE_HASH;
     lhq.key = njs_str_value("message");
 
-    prop = njs_object_property(vm, njs_object(error), &lhq);
+    ret = njs_object_property(vm, error, &lhq, &value2);
 
-    if (prop != NULL) {
-        message_value = &prop->value;
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
 
-    } else {
-        message_value = &njs_string_empty;
-    }
+    message_value = (ret == NJS_OK) ? &value2 : &njs_string_empty;
 
     njs_string_get(message_value, &message);
 
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_json.c
--- a/src/njs_json.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_json.c	Tue Aug 06 22:54:13 2019 -0400
@@ -1497,19 +1497,19 @@ memory_error:
 static njs_function_t *
 njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value)
 {
-    njs_object_prop_t   *prop;
+    njs_int_t           ret;
+    njs_value_t         retval;
     njs_lvlhsh_query_t  lhq;
 
-    lhq.key_hash = NJS_TO_JSON_HASH;
-    lhq.key = njs_str_value("toJSON");
-
-    prop = njs_object_property(vm, njs_object(value), &lhq);
-
-    if (prop != NULL && njs_is_function(&prop->value)) {
-        return njs_function(&prop->value);
+    njs_object_property_init(&lhq, "toJSON", NJS_TO_JSON_HASH);
+
+    ret = njs_object_property(vm, value, &lhq, &retval);
+
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
     }
 
-    return NULL;
+    return njs_is_function(&retval) ? njs_function(&retval) : NULL;
 }
 
 
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_object.h
--- a/src/njs_object.h	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_object.h	Tue Aug 06 22:54:13 2019 -0400
@@ -20,6 +20,14 @@
     (!njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop))
 
 
+#define njs_object_property_init(lhq, _key, hash)                             \
+    do {                                                                      \
+        (lhq)->proto = &njs_object_hash_proto;                                \
+        (lhq)->key_hash = hash;                                               \
+        (lhq)->key = njs_str_value(_key);                                     \
+    } while (0)
+
+
 struct njs_object_init_s {
     njs_str_t                   name;
     const njs_object_prop_t     *properties;
@@ -56,8 +64,8 @@ njs_int_t njs_object_prototype_to_string
 
 njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
     const njs_value_t *value, uint8_t attributes);
-njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
-    njs_lvlhsh_query_t *lhq);
+njs_int_t njs_object_property(njs_vm_t *vm, const njs_value_t *value,
+    njs_lvlhsh_query_t *lhq, njs_value_t *retval);
 njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *name, njs_value_t *value);
 njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_object_prop.c
--- a/src/njs_object_prop.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_object_prop.c	Tue Aug 06 22:54:13 2019 -0400
@@ -9,7 +9,7 @@
 
 
 static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
-    const njs_value_t *name, const njs_object_t *descriptor);
+    const njs_value_t *name, const njs_value_t *desc);
 
 
 njs_object_prop_t *
@@ -45,32 +45,53 @@ njs_object_prop_alloc(njs_vm_t *vm, cons
 }
 
 
-njs_object_prop_t *
-njs_object_property(njs_vm_t *vm, const njs_object_t *object,
-    njs_lvlhsh_query_t *lhq)
+njs_int_t
+njs_object_property(njs_vm_t *vm, const njs_value_t *value,
+    njs_lvlhsh_query_t *lhq, njs_value_t *retval)
 {
-    njs_int_t  ret;
+    njs_int_t          ret;
+    njs_object_t       *object;
+    njs_object_prop_t  *prop;
 
-    lhq->proto = &njs_object_hash_proto;
+    object = njs_object(value);
 
     do {
         ret = njs_lvlhsh_find(&object->hash, lhq);
 
         if (njs_fast_path(ret == NJS_OK)) {
-            return lhq->value;
+            goto found;
         }
 
         ret = njs_lvlhsh_find(&object->shared_hash, lhq);
 
         if (njs_fast_path(ret == NJS_OK)) {
-            return lhq->value;
+            goto found;
         }
 
         object = object->__proto__;
 
     } while (object != NULL);
 
-    return NULL;
+    *retval = njs_value_undefined;
+
+    return NJS_DECLINED;
+
+found:
+
+    prop = lhq->value;
+
+    if (njs_is_data_descriptor(prop)) {
+        *retval = prop->value;
+        return NJS_OK;
+    }
+
+    if (njs_is_undefined(&prop->getter)) {
+        *retval = njs_value_undefined;
+        return NJS_OK;
+    }
+
+    return njs_function_apply(vm, njs_function(&prop->getter), value,
+                              1, retval);
 }
 
 
@@ -95,7 +116,7 @@ njs_object_prop_define(njs_vm_t *vm, njs
         return ret;
     }
 
-    prop = njs_descriptor_prop(vm, name, njs_object(value));
+    prop = njs_descriptor_prop(vm, name, value);
     if (njs_slow_path(prop == NULL)) {
         return NJS_ERROR;
     }
@@ -313,12 +334,13 @@ exception:
 
 static njs_object_prop_t *
 njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name,
-    const njs_object_t *desc)
+    const njs_value_t *desc)
 {
+    njs_int_t           ret;
     njs_bool_t          data, accessor;
-    njs_object_prop_t   *prop, *pr;
-    const njs_value_t   *setter, *getter;
-    njs_lvlhsh_query_t  pq;
+    njs_value_t         value;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
 
     data = 0;
     accessor = 0;
@@ -329,72 +351,103 @@ njs_descriptor_prop(njs_vm_t *vm, const 
         return NULL;
     }
 
-    getter = &njs_value_invalid;
-    pq.key = njs_str_value("get");
-    pq.key_hash = NJS_GET_HASH;
+    njs_object_property_init(&lhq, "get", NJS_GET_HASH);
+
+    ret = njs_object_property(vm, desc, &lhq, &value);
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
-        if (njs_is_defined(&pr->value) && !njs_is_function(&pr->value)) {
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
+    }
+
+    if (ret == NJS_OK) {
+        if (njs_is_defined(&value) && !njs_is_function(&value)) {
             njs_type_error(vm, "Getter must be a function");
             return NULL;
         }
 
         accessor = 1;
-        getter = &pr->value;
+        prop->getter = value;
+
+    } else {
+        /* NJS_DECLINED */
+        prop->getter = njs_value_invalid;
     }
 
-    prop->getter = *getter;
+    lhq.key = njs_str_value("set");
+    lhq.key_hash = NJS_SET_HASH;
+
+    ret = njs_object_property(vm, desc, &lhq, &value);
 
-    setter = &njs_value_invalid;
-    pq.key = njs_str_value("set");
-    pq.key_hash = NJS_SET_HASH;
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
+    }
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
-        if (njs_is_defined(&pr->value) && !njs_is_function(&pr->value)) {
+    if (ret == NJS_OK) {
+        if (njs_is_defined(&value) && !njs_is_function(&value)) {
             njs_type_error(vm, "Setter must be a function");
             return NULL;
         }
 
         accessor = 1;
-        setter = &pr->value;
+        prop->setter = value;
+
+    } else {
+        /* NJS_DECLINED */
+        prop->setter = njs_value_invalid;
     }
 
-    prop->setter = *setter;
+    lhq.key = njs_str_value("value");
+    lhq.key_hash = NJS_VALUE_HASH;
 
-    pq.key = njs_str_value("value");
-    pq.key_hash = NJS_VALUE_HASH;
+    ret = njs_object_property(vm, desc, &lhq, &value);
+
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
+    }
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
+    if (ret == NJS_OK) {
         data = 1;
-        prop->value = pr->value;
+        prop->value = value;
+    }
+
+    lhq.key = njs_str_value("writable");
+    lhq.key_hash = NJS_WRITABABLE_HASH;
+
+    ret = njs_object_property(vm, desc, &lhq, &value);
+
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
     }
 
-    pq.key = njs_str_value("writable");
-    pq.key_hash = NJS_WRITABABLE_HASH;
+    if (ret == NJS_OK) {
+        data = 1;
+        prop->writable = njs_is_true(&value);
+    }
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
-        data = 1;
-        prop->writable = njs_is_true(&pr->value);
+    lhq.key = njs_str_value("enumerable");
+    lhq.key_hash = NJS_ENUMERABLE_HASH;
+
+    ret = njs_object_property(vm, desc, &lhq, &value);
+
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
     }
 
-    pq.key = njs_str_value("enumerable");
-    pq.key_hash = NJS_ENUMERABLE_HASH;
-
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
-        prop->enumerable = njs_is_true(&pr->value);
+    if (ret == NJS_OK) {
+        prop->enumerable = njs_is_true(&value);
     }
 
-    pq.key = njs_str_value("configurable");
-    pq.key_hash = NJS_CONFIGURABLE_HASH;
+    lhq.key = njs_str_value("configurable");
+    lhq.key_hash = NJS_CONFIGURABLE_HASH;
+
+    ret = njs_object_property(vm, desc, &lhq, &value);
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
-        prop->configurable = njs_is_true(&pr->value);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
+    }
+
+    if (ret == NJS_OK) {
+        prop->configurable = njs_is_true(&value);
     }
 
     if (accessor && data) {
@@ -435,6 +488,29 @@ njs_object_prop_descriptor(njs_vm_t *vm,
 
     switch (ret) {
     case NJS_OK:
+        prop = pq.lhq.value;
+
+        switch (prop->type) {
+        case NJS_PROPERTY:
+            break;
+
+        case NJS_PROPERTY_HANDLER:
+            pq.scratch = *prop;
+            prop = &pq.scratch;
+            ret = prop->value.data.u.prop_handler(vm, value, NULL,
+                                                  &prop->value);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+            break;
+
+        default:
+            njs_type_error(vm, "unexpected property type: %s",
+                           njs_prop_type_string(prop->type));
+            return NJS_ERROR;
+        }
+
         break;
 
     case NJS_DECLINED:
@@ -446,28 +522,6 @@ njs_object_prop_descriptor(njs_vm_t *vm,
         return ret;
     }
 
-    prop = pq.lhq.value;
-
-    switch (prop->type) {
-    case NJS_PROPERTY:
-        break;
-
-    case NJS_PROPERTY_HANDLER:
-        pq.scratch = *prop;
-        prop = &pq.scratch;
-        ret = prop->value.data.u.prop_handler(vm, value, NULL, &prop->value);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-
-        break;
-
-    default:
-        njs_type_error(vm, "unexpected property type: %s",
-                       njs_prop_type_string(prop->type));
-        return NJS_ERROR;
-    }
-
     desc = njs_object_alloc(vm);
     if (njs_slow_path(desc == NULL)) {
         return NJS_ERROR;
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/njs_value.c
--- a/src/njs_value.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/njs_value.c	Tue Aug 06 22:54:13 2019 -0400
@@ -122,8 +122,7 @@ njs_value_to_primitive(njs_vm_t *vm, njs
 {
     njs_int_t           ret;
     njs_uint_t          tries;
-    njs_value_t         retval;
-    njs_object_prop_t   *prop;
+    njs_value_t         method, retval;
     njs_lvlhsh_query_t  lhq;
 
     static const uint32_t  hashes[] = {
@@ -144,6 +143,7 @@ njs_value_to_primitive(njs_vm_t *vm, njs
     }
 
     tries = 0;
+    lhq.proto = &njs_object_hash_proto;
 
     for ( ;; ) {
         ret = NJS_ERROR;
@@ -154,28 +154,27 @@ njs_value_to_primitive(njs_vm_t *vm, njs
             lhq.key_hash = hashes[hint];
             lhq.key = names[hint];
 
-            prop = njs_object_property(vm, njs_object(value), &lhq);
+            ret = njs_object_property(vm, value, &lhq, &method);
 
-            if (prop == NULL || !njs_is_function(&prop->value)) {
-                /* Try the second method. */
-                continue;
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return ret;
             }
 
-            ret = njs_function_apply(vm, njs_function(&prop->value), value, 1,
-                                     &retval);
+            if (njs_is_function(&method)) {
+                ret = njs_function_apply(vm, njs_function(&method), value, 1,
+                                         &retval);
 
-            if (njs_fast_path(ret == NJS_OK)) {
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return ret;
+                }
+
                 if (njs_is_primitive(&retval)) {
                     break;
-                 }
+                }
+            }
 
-                /* Try the second method. */
-                continue;
-             }
-
-            /* NJS_ERROR */
-
-            return ret;
+            /* Try the second method. */
+            continue;
          }
 
         njs_type_error(vm, "Cannot convert object to primitive value");
diff -r 05fb6e4fdb36 -r b9d9f2813d9a src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Wed Aug 07 17:23:47 2019 +0300
+++ b/src/test/njs_unit_test.c	Tue Aug 06 22:54:13 2019 -0400
@@ -9811,6 +9811,43 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var arr = [0, 1]; Object.defineProperty(arr, 'length', {value:3}); arr.length"),
       njs_str("3") },
 
+    { njs_str("Object.defineProperty(Array.prototype, 'toString', { get: function() {return () => 1}});"
+                 "'a' + []"),
+      njs_str("a1") },
+
+    { njs_str("Object.defineProperty(Array.prototype, 'toJSON', { get: function() {return () => 1}});"
+                 "JSON.stringify([])"),
+      njs_str("1") },
+
+    { njs_str("Object.defineProperty(Array.prototype, 'join', { get: function() {return () => 1}});"
+                 "([]).toString()"),
+      njs_str("1") },
+
+    { njs_str("var o = {}, desc = {};"
+              "Object.defineProperty(desc, 'get', { get() { return () => 1 } });"
+              "Object.defineProperty(o, 'a', desc); o.a"),
+      njs_str("1") },
+
+    { njs_str("Object.defineProperty(Error.prototype, 'message', { get() {return 'm'}});"
+                 "Object.defineProperty(Error.prototype, 'name', { get() {return 'n'}});"
+                 "Error()"),
+      njs_str("n: m") },
+
+    { njs_str("var o = {}, desc = {};"
+              "Object.defineProperty(desc, 'value', { get() { return 'x'}});"
+              "Object.defineProperty(o, 'a', desc); o.a"),
+      njs_str("x") },
+
+    { njs_str("var o = {}, desc = {};"
+              "Object.defineProperty(desc, 'value', { get() { return 'x'}});"
+              "Object.defineProperty(desc, 'enumerable', { get() { return !NaN}});"
+              "Object.defineProperty(desc, 'writable', { get() { return 'x'}});"
+              "Object.defineProperty(desc, 'configurable', { get() { return 1}});"
+              "Object.defineProperty(o, 'a', desc);"
+              "var d = Object.getOwnPropertyDescriptor(o, 'a');"
+              "d.enumerable && d.writable && d.configurable"),
+      njs_str("true") },
+
     { njs_str("Object.defineProperties()"),
       njs_str("TypeError: cannot convert undefined argument to object") },
 


More information about the nginx-devel mailing list