From e308ceeaf96d98653198307bbe3ebaf2f66c877c Mon Sep 17 00:00:00 2001 From: hongzhidao Date: Tue, 6 Aug 2019 22:54:13 -0400 Subject: [PATCH] Added property getter support for njs_object_property(). --- src/njs_array.c | 18 ++-- src/njs_date.c | 18 ++-- src/njs_error.c | 29 +++--- src/njs_json.c | 14 +-- src/njs_object.h | 12 ++- src/njs_object_prop.c | 198 +++++++++++++++++++++++++-------------- src/njs_value.c | 33 ++++--- src/test/njs_unit_test.c | 37 ++++++++ 8 files changed, 231 insertions(+), 128 deletions(-) diff --git a/src/njs_array.c b/src/njs_array.c index 9e923c39..f0805e68 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -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); } } diff --git a/src/njs_date.c b/src/njs_date.c index 4da94810..29f2eef5 100644 --- a/src/njs_date.c +++ b/src/njs_date.c @@ -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); } } diff --git a/src/njs_error.c b/src/njs_error.c index 5531f0a4..4d807e46 100644 --- a/src/njs_error.c +++ b/src/njs_error.c @@ -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) { diff --git a/src/njs_json.c b/src/njs_json.c index b7ebf125..facf82a5 100644 --- a/src/njs_json.c +++ b/src/njs_json.c @@ -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; } diff --git a/src/njs_object.h b/src/njs_object.h index 4d4fd6cc..cac3c08e 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -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_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, diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index 3e771138..4b7942cf 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -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); diff --git a/src/njs_value.c b/src/njs_value.c index e9abc9cc..17889c31 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -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"); diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 3492f891..5207cedb 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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") }, -- 2.47.3