]> git.kaiwu.me - njs.git/commitdiff
Added property getter support for njs_object_property().
authorhongzhidao <hongzhidao@gmail.com>
Wed, 7 Aug 2019 02:54:13 +0000 (22:54 -0400)
committerhongzhidao <hongzhidao@gmail.com>
Wed, 7 Aug 2019 02:54:13 +0000 (22:54 -0400)
src/njs_array.c
src/njs_date.c
src/njs_error.c
src/njs_json.c
src/njs_object.h
src/njs_object_prop.c
src/njs_value.c
src/test/njs_unit_test.c

index 9e923c390381cc662f0d26335a0dcb565c3f6cef..f0805e6823c2cf8f09c362f7c7fa8744e9aba6db 100644 (file)
@@ -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");
+        njs_object_property_init(&lhq, "join", NJS_JOIN_HASH);
 
-        prop = njs_object_property(vm, njs_object(&args[0]), &lhq);
+        ret = njs_object_property(vm, &args[0], &lhq, &value);
 
-        if (njs_fast_path(prop != NULL && njs_is_function(&prop->value))) {
-            return njs_function_apply(vm, njs_function(&prop->value), args,
-                                      nargs, &vm->retval);
+        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);
         }
     }
 
index 4da948101430b93e117152d595f248a45be1c7e2..29f2eef566a8c15132eb91071868549a0310152f 100644 (file)
@@ -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");
+        njs_object_property_init(&lhq, "toISOString", NJS_TO_ISO_STRING_HASH);
 
-        prop = njs_object_property(vm, njs_object(&args[0]), &lhq);
+        ret = njs_object_property(vm, &args[0], &lhq, &value);
 
-        if (njs_fast_path(prop != NULL && njs_is_function(&prop->value))) {
-            return njs_function_apply(vm, njs_function(&prop->value), args,
-                                      nargs, &vm->retval);
+        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);
         }
     }
 
index 5531f0a4485f18bb8666e1947f83ca1e651a2200..4d807e46f57fc912be4bdd2646c6c00744ab7661 100644 (file)
@@ -619,40 +619,37 @@ njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error)
 {
     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;
-
-    prop = njs_object_property(vm, njs_object(error), &lhq);
+    njs_object_property_init(&lhq, "name", NJS_NAME_HASH);
 
-    if (prop != NULL) {
-        name_value = &prop->value;
+    ret = njs_object_property(vm, error, &lhq, &value1);
 
-    } else {
-        name_value = &default_name;
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
     }
 
+    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);
-
-    if (prop != NULL) {
-        message_value = &prop->value;
+    ret = njs_object_property(vm, error, &lhq, &value2);
 
-    } else {
-        message_value = &njs_string_empty;
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
     }
 
+    message_value = (ret == NJS_OK) ? &value2 : &njs_string_empty;
+
     njs_string_get(message_value, &message);
 
     if (name.length == 0) {
index b7ebf1252eeba646a5dc8fc4b867d179fe8d9a63..facf82a5d84ca4094cf519b625aeebce508939be 100644 (file)
@@ -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");
+    njs_object_property_init(&lhq, "toJSON", NJS_TO_JSON_HASH);
 
-    prop = njs_object_property(vm, njs_object(value), &lhq);
+    ret = njs_object_property(vm, value, &lhq, &retval);
 
-    if (prop != NULL && njs_is_function(&prop->value)) {
-        return njs_function(&prop->value);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
     }
 
-    return NULL;
+    return njs_is_function(&retval) ? njs_function(&retval) : NULL;
 }
 
 
index 4d4fd6cc999851c133a4c3b8c4ef94cb3018524e..cac3c08e3f6c938f6338ae247c17794ce6d1f080 100644 (file)
     (!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_vm_t *vm, njs_value_t *args,
 
 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,
index 3e7711383909160ec7fdd2984f17618edb58be35..4b7942cfd4c1dfdc42364400ffe79f98e69e7c06 100644 (file)
@@ -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, const njs_value_t *name,
 }
 
 
-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_value_t *object,
         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 njs_value_t *name,
         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);
+
+    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, "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;
+
+    ret = njs_object_property(vm, desc, &lhq, &value);
 
-    pq.key = njs_str_value("value");
-    pq.key_hash = NJS_VALUE_HASH;
+    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;
     }
 
-    pq.key = njs_str_value("writable");
-    pq.key_hash = NJS_WRITABABLE_HASH;
+    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;
+    }
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
+    if (ret == NJS_OK) {
         data = 1;
-        prop->writable = njs_is_true(&pr->value);
+        prop->writable = njs_is_true(&value);
     }
 
-    pq.key = njs_str_value("enumerable");
-    pq.key_hash = NJS_ENUMERABLE_HASH;
+    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;
+    }
 
-    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;
 
-    pr = njs_object_property(vm, desc, &pq);
-    if (pr != NULL) {
-        prop->configurable = njs_is_true(&pr->value);
+    ret = njs_object_property(vm, desc, &lhq, &value);
+
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
+    }
+
+    if (ret == NJS_OK) {
+        prop->configurable = njs_is_true(&value);
     }
 
     if (accessor && data) {
@@ -435,37 +488,38 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
 
     switch (ret) {
     case NJS_OK:
-        break;
-
-    case NJS_DECLINED:
-        *dest = njs_value_undefined;
-        return NJS_OK;
+        prop = pq.lhq.value;
 
-    case NJS_ERROR:
-    default:
-        return ret;
-    }
+        switch (prop->type) {
+        case NJS_PROPERTY:
+            break;
 
-    prop = pq.lhq.value;
+        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;
+            }
 
-    switch (prop->type) {
-    case NJS_PROPERTY:
-        break;
+            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;
+        default:
+            njs_type_error(vm, "unexpected property type: %s",
+                           njs_prop_type_string(prop->type));
+            return NJS_ERROR;
         }
 
         break;
 
+    case NJS_DECLINED:
+        *dest = njs_value_undefined;
+        return NJS_OK;
+
+    case NJS_ERROR:
     default:
-        njs_type_error(vm, "unexpected property type: %s",
-                       njs_prop_type_string(prop->type));
-        return NJS_ERROR;
+        return ret;
     }
 
     desc = njs_object_alloc(vm);
index e9abc9cc8fa68ed6ebc66319750c5bfaa668da3f..17889c31cc287db7e20d11ce9b3fe7fa3198bc9a 100644 (file)
@@ -122,8 +122,7 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,
 {
     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_value_t *dst, njs_value_t *value,
     }
 
     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_value_t *dst, njs_value_t *value,
             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_slow_path(ret != NJS_OK)) {
+                    return ret;
+                }
 
-            if (njs_fast_path(ret == NJS_OK)) {
                 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");
index 3492f891184235abe7fcefa866f0a488a268ccda..5207cedb2e7f3244b7bed0bb403edef2518168a7 100644 (file)
@@ -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") },