[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