[njs] Object property quering is refactored.
Dmitry Volyntsev
xeioex at nginx.com
Fri Oct 19 18:30:53 UTC 2018
details: http://hg.nginx.org/njs/rev/9d7b9cc03569
branches:
changeset: 623:9d7b9cc03569
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Fri Oct 19 20:52:57 2018 +0300
description:
Object property quering is refactored.
njs_property_query() is rectified and unified
1) returns only property descriptors. Special return codes
NJS_PRIMITIVE_VALUE, NJS_STRING_VALUE, NJS_ARRAY_VALUE and
NJS_EXTERNAL_VALUE are replaced with a temporary property
descriptor of type NJS_PROPERTY_REF or NJS_PROPERTY_HANDLER.
If NJS_PROPERTY_REF is set reference to a value is contained
in prop->value.data.u.value.
2) NJS_PROPERTY_HANDLER properties returned as is.
3) njs_property_query_t.own can be used to query for an object's
OwnProperty.
4) NJS_PROPERTY_QUERY_IN is removed.
The aim is to implement with it [[GetOwnProperty]] and
[[GetProperty]] methods from specification. Which are used
extensively in many places of the ECMAScript spec.
njs_value_property() is introduced which corresponds to [[Get]]
method from specification.
This fixes #32 and #34 issues on Github.
diffstat:
njs/njs_extern.h | 3 +
njs/njs_object.c | 620 ++++++++++++++++++++++++++++-----------
njs/njs_object.h | 24 +-
njs/njs_vm.c | 668 ++++++++++++++++--------------------------
njs/njs_vm.h | 24 +-
njs/test/njs_expect_test.exp | 2 +-
njs/test/njs_unit_test.c | 527 +++++++++++++++++++++++++++++++--
7 files changed, 1228 insertions(+), 640 deletions(-)
diffs (truncated from 2473 to 1000 lines):
diff -r 6b06b00139a1 -r 9d7b9cc03569 njs/njs_extern.h
--- a/njs/njs_extern.h Sat Oct 06 17:52:40 2018 +0300
+++ b/njs/njs_extern.h Fri Oct 19 20:52:57 2018 +0300
@@ -11,6 +11,9 @@
#define njs_extern_object(vm, ext) \
(*(void **) nxt_array_item((vm)->external_objects, (ext)->external.index))
+#define njs_extern_index(vm, idx) \
+ (*(void **) nxt_array_item((vm)->external_objects, idx))
+
struct njs_extern_s {
/* A hash of inclusive njs_extern_t. */
diff -r 6b06b00139a1 -r 9d7b9cc03569 njs/njs_object.c
--- a/njs/njs_object.c Sat Oct 06 17:52:40 2018 +0300
+++ b/njs/njs_object.c Fri Oct 19 20:52:57 2018 +0300
@@ -10,9 +10,18 @@
static nxt_int_t njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
static njs_ret_t njs_object_property_query(njs_vm_t *vm,
- njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
+ njs_property_query_t *pq, njs_object_t *object,
+ const njs_value_t *property);
static njs_ret_t njs_array_property_query(njs_vm_t *vm,
+ njs_property_query_t *pq, njs_array_t *array, uint32_t index);
+static njs_ret_t njs_string_property_query(njs_vm_t *vm,
njs_property_query_t *pq, njs_value_t *object, uint32_t index);
+static njs_ret_t njs_external_property_query(njs_vm_t *vm,
+ njs_property_query_t *pq, njs_value_t *object);
+static njs_ret_t njs_external_property_set(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *setval, njs_value_t *retval);
+static njs_ret_t njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *setval, njs_value_t *retval);
static njs_ret_t njs_object_query_prop_handler(njs_property_query_t *pq,
njs_object_t *object);
static njs_ret_t njs_define_property(njs_vm_t *vm, njs_object_t *object,
@@ -232,28 +241,39 @@ njs_object_property(njs_vm_t *vm, const
/*
+ * ES5.1, 8.12.1: [[GetOwnProperty]], [[GetProperty]].
* The njs_property_query() returns values
* NXT_OK property has been found in object,
+ * retval of type njs_object_prop_t * is in pq->lhq.value.
+ * in NJS_PROPERTY_QUERY_GET
+ * prop->type is NJS_PROPERTY, NJS_METHOD or NJS_PROPERTY_HANDLER.
+ * in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE
+ * prop->type is NJS_PROPERTY, NJS_PROPERTY_REF, NJS_METHOD or
+ * NJS_PROPERTY_HANDLER.
* NXT_DECLINED property was not found in object,
- * NJS_PRIMITIVE_VALUE property operation was applied to a numeric
- * or boolean value,
- * NJS_STRING_VALUE property operation was applied to a string,
- * NJS_ARRAY_VALUE object is array,
- * NJS_EXTERNAL_VALUE object is external entity,
+ * if pq->lhq.value != NULL it contains retval of type
+ * njs_object_prop_t * where prop->type is NJS_WHITEOUT
* NJS_TRAP the property trap must be called,
* NXT_ERROR exception has been thrown.
+ *
+ * TODO:
+ * Object.create([1,2]).length
+ * Object.defineProperty([1,2], '1', {configurable:false})
*/
njs_ret_t
njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
- njs_value_t *property)
+ const njs_value_t *property)
{
- uint32_t index;
- uint32_t (*hash)(const void *, size_t);
- njs_ret_t ret;
- njs_object_t *obj;
- njs_function_t *function;
- const njs_extern_t *ext_proto;
+ uint32_t index;
+ uint32_t (*hash)(const void *, size_t);
+ njs_ret_t ret;
+ njs_object_t *obj;
+ njs_function_t *function;
+
+ if (nxt_slow_path(!njs_is_primitive(property))) {
+ return njs_trap(vm, NJS_TRAP_PROPERTY);
+ }
hash = nxt_djb_hash;
@@ -261,34 +281,47 @@ njs_property_query(njs_vm_t *vm, njs_pro
case NJS_BOOLEAN:
case NJS_NUMBER:
- if (pq->query != NJS_PROPERTY_QUERY_GET) {
- return NJS_PRIMITIVE_VALUE;
- }
-
index = njs_primitive_prototype_index(object->type);
obj = &vm->prototypes[index].object;
break;
case NJS_STRING:
- if (pq->query == NJS_PROPERTY_QUERY_DELETE) {
- return NXT_DECLINED;
+ if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
+ index = njs_value_to_index(property);
+
+ if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+ return njs_string_property_query(vm, pq, object, index);
+ }
}
obj = &vm->prototypes[NJS_PROTOTYPE_STRING].object;
break;
+ case NJS_OBJECT_STRING:
+ if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
+ index = njs_value_to_index(property);
+
+ if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+ ret = njs_string_property_query(vm, pq,
+ &object->data.u.object_value->value,
+ index);
+
+ if (nxt_fast_path(ret != NXT_DECLINED)) {
+ return ret;
+ }
+ }
+ }
+
+ obj = object->data.u.object;
+ break;
+
case NJS_ARRAY:
if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
-
- if (nxt_fast_path(njs_is_primitive(property))) {
- index = njs_value_to_index(property);
-
- if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
- return njs_array_property_query(vm, pq, object, index);
- }
-
- } else {
- return njs_trap(vm, NJS_TRAP_PROPERTY);
+ index = njs_value_to_index(property);
+
+ if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
+ return njs_array_property_query(vm, pq, object->data.u.array,
+ index);
}
}
@@ -297,7 +330,6 @@ njs_property_query(njs_vm_t *vm, njs_pro
case NJS_OBJECT:
case NJS_OBJECT_BOOLEAN:
case NJS_OBJECT_NUMBER:
- case NJS_OBJECT_STRING:
case NJS_REGEXP:
case NJS_DATE:
case NJS_OBJECT_ERROR:
@@ -322,28 +354,19 @@ njs_property_query(njs_vm_t *vm, njs_pro
break;
case NJS_EXTERNAL:
- ext_proto = object->external.proto;
-
- if (ext_proto->type == NJS_EXTERN_CASELESS_OBJECT) {
- hash = nxt_djb_hash_lowcase;
- }
-
obj = NULL;
break;
case NJS_VOID:
case NJS_NULL:
default:
- if (nxt_fast_path(njs_is_primitive(property))) {
-
- ret = njs_primitive_value_to_string(vm, &pq->value, property);
-
- if (nxt_fast_path(ret == NXT_OK)) {
- njs_string_get(&pq->value, &pq->lhq.key);
- njs_type_error(vm, "cannot get property '%.*s' of undefined",
- (int) pq->lhq.key.length, pq->lhq.key.start);
- return NXT_ERROR;
- }
+ ret = njs_primitive_value_to_string(vm, &pq->value, property);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ njs_string_get(&pq->value, &pq->lhq.key);
+ njs_type_error(vm, "cannot get property '%.*s' of undefined",
+ (int) pq->lhq.key.length, pq->lhq.key.start);
+ return NXT_ERROR;
}
njs_type_error(vm, "cannot get property 'unknown' of undefined");
@@ -351,37 +374,34 @@ njs_property_query(njs_vm_t *vm, njs_pro
return NXT_ERROR;
}
- if (nxt_fast_path(njs_is_primitive(property))) {
-
- ret = njs_primitive_value_to_string(vm, &pq->value, property);
-
- if (nxt_fast_path(ret == NXT_OK)) {
-
- njs_string_get(&pq->value, &pq->lhq.key);
- pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
-
- if (obj == NULL) {
- pq->lhq.proto = &njs_extern_hash_proto;
-
- return NJS_EXTERNAL_VALUE;
- }
-
- return njs_object_property_query(vm, pq, object, obj);
+ ret = njs_primitive_value_to_string(vm, &pq->value, property);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+
+ njs_string_get(&pq->value, &pq->lhq.key);
+ pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
+
+ if (obj == NULL) {
+ return njs_external_property_query(vm, pq, object);
}
- return ret;
+ return njs_object_property_query(vm, pq, obj, property);
}
- return njs_trap(vm, NJS_TRAP_PROPERTY);
+ return ret;
}
njs_ret_t
njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
- njs_value_t *value, njs_object_t *object)
+ njs_object_t *object, const njs_value_t *property)
{
- njs_ret_t ret;
- njs_object_prop_t *prop;
+ uint32_t index;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_object_t *proto;
+ njs_object_prop_t *prop;
+ njs_object_value_t *ov;
pq->lhq.proto = &njs_object_hash_proto;
@@ -392,83 +412,83 @@ njs_object_property_query(njs_vm_t *vm,
}
}
+ proto = object;
+
do {
- pq->prototype = object;
-
- ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
-
- if (ret == NXT_OK) {
- prop = pq->lhq.value;
-
- if (prop->type != NJS_WHITEOUT) {
- pq->shared = 0;
-
- return ret;
+ pq->prototype = proto;
+
+ /* length and other shared properties should be Own property */
+
+ if (nxt_fast_path(!pq->own || proto == object)) {
+ ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
+
+ if (ret == NXT_OK) {
+ prop = pq->lhq.value;
+
+ if (prop->type != NJS_WHITEOUT) {
+ pq->shared = 0;
+
+ return ret;
+ }
+
+ goto next;
}
- goto next;
+ if (proto != object && !njs_is_null_or_void_or_boolean(property)) {
+ switch (proto->type) {
+ case NJS_ARRAY:
+ index = njs_value_to_index(property);
+ if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
+ array = (njs_array_t *) proto;
+ return njs_array_property_query(vm, pq, array, index);
+ }
+
+ break;
+
+ case NJS_OBJECT_STRING:
+ index = njs_value_to_index(property);
+ if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+ ov = (njs_object_value_t *) proto;
+ return njs_string_property_query(vm, pq, &ov->value,
+ index);
+ }
+
+ default:
+ break;
+ }
+ }
}
- if (pq->query > NJS_PROPERTY_QUERY_IN) {
- /* NXT_DECLINED */
- return ret;
- }
-
- ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
+ ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
if (ret == NXT_OK) {
pq->shared = 1;
- if (pq->query == NJS_PROPERTY_QUERY_GET) {
- prop = pq->lhq.value;
-
- if (prop->type == NJS_PROPERTY_HANDLER) {
- pq->scratch = *prop;
- prop = &pq->scratch;
- ret = prop->value.data.u.prop_handler(vm, value, NULL,
- &prop->value);
-
- if (nxt_fast_path(ret == NXT_OK)) {
- prop->type = NJS_PROPERTY;
- pq->lhq.value = prop;
- }
- }
- }
-
return ret;
}
- if (pq->query > NJS_PROPERTY_QUERY_IN) {
- /* NXT_DECLINED */
- return ret;
+ if (pq->query > NJS_PROPERTY_QUERY_GET) {
+ return NXT_DECLINED;
}
- next:
-
- object = object->__proto__;
-
- } while (object != NULL);
-
- if (njs_is_string(value)) {
- return NJS_STRING_VALUE;
- }
-
- /* NXT_DECLINED */
-
- return ret;
+next:
+
+ proto = proto->__proto__;
+
+ } while (proto != NULL);
+
+ return NXT_DECLINED;
}
static njs_ret_t
njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
- njs_value_t *object, uint32_t index)
+ njs_array_t *array, uint32_t index)
{
- uint32_t size;
- njs_ret_t ret;
- njs_value_t *value;
- njs_array_t *array;
-
- array = object->data.u.array;
+ uint32_t size;
+ njs_ret_t ret;
+ njs_value_t *value;
+ njs_object_prop_t *prop;
if (index >= array->length) {
if (pq->query != NJS_PROPERTY_QUERY_SET) {
@@ -493,9 +513,200 @@ njs_array_property_query(njs_vm_t *vm, n
array->length = index + 1;
}
- pq->lhq.value = &array->start[index];
-
- return NJS_ARRAY_VALUE;
+ prop = &pq->scratch;
+
+ if (pq->query == NJS_PROPERTY_QUERY_GET) {
+ if (!njs_is_valid(&array->start[index])) {
+ return NXT_DECLINED;
+ }
+
+ prop->value = array->start[index];
+ prop->type = NJS_PROPERTY;
+
+ } else {
+ prop->value.data.u.value = &array->start[index];
+ prop->type = NJS_PROPERTY_REF;
+ }
+
+ prop->configurable = 1;
+ prop->enumerable = 1;
+ prop->writable = 1;
+
+ pq->lhq.value = prop;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+ njs_value_t *object, uint32_t index)
+{
+ njs_slice_prop_t slice;
+ njs_object_prop_t *prop;
+ njs_string_prop_t string;
+
+ prop = &pq->scratch;
+
+ slice.start = index;
+ slice.length = 1;
+ slice.string_length = njs_string_prop(&string, object);
+
+ if (slice.start < slice.string_length) {
+ /*
+ * A single codepoint string fits in retval
+ * so the function cannot fail.
+ */
+ (void) njs_string_slice(vm, &prop->value, &string, &slice);
+ prop->type = NJS_PROPERTY;
+ prop->configurable = 0;
+ prop->enumerable = 1;
+ prop->writable = 0;
+
+ pq->lhq.value = prop;
+
+ if (pq->query != NJS_PROPERTY_QUERY_GET) {
+ /* pq->lhq.key is used by njs_vmcode_property_set for TypeError */
+ njs_uint32_to_string(&pq->value, index);
+ njs_string_get(&pq->value, &pq->lhq.key);
+ }
+
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static njs_ret_t
+njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+ njs_value_t *object)
+{
+ void *obj;
+ njs_ret_t ret;
+ uintptr_t data;
+ njs_object_prop_t *prop;
+ const njs_extern_t *ext_proto;
+
+ prop = &pq->scratch;
+
+ prop->type = NJS_PROPERTY;
+ prop->configurable = 0;
+ prop->enumerable = 1;
+ prop->writable = 0;
+
+ ext_proto = object->external.proto;
+
+ pq->lhq.proto = &njs_extern_hash_proto;
+ ret = nxt_lvlhsh_find(&ext_proto->hash, &pq->lhq);
+
+ if (ret == NXT_OK) {
+ ext_proto = pq->lhq.value;
+
+ prop->value.type = NJS_EXTERNAL;
+ prop->value.data.truth = 1;
+ prop->value.external.proto = ext_proto;
+ prop->value.external.index = object->external.index;
+
+ if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
+ goto done;
+ }
+
+ data = ext_proto->data;
+
+ } else {
+ data = (uintptr_t) &pq->lhq.key;
+ }
+
+ switch (pq->query) {
+
+ case NJS_PROPERTY_QUERY_GET:
+ if (ext_proto->get != NULL) {
+ obj = njs_extern_object(vm, object);
+ ret = ext_proto->get(vm, &prop->value, obj, data);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ break;
+
+ case NJS_PROPERTY_QUERY_SET:
+ case NJS_PROPERTY_QUERY_DELETE:
+
+ prop->type = NJS_PROPERTY_HANDLER;
+ prop->name = *object;
+
+ if (pq->query == NJS_PROPERTY_QUERY_SET) {
+ prop->writable = (ext_proto->set != NULL);
+ prop->value.data.u.prop_handler = njs_external_property_set;
+
+ } else {
+ prop->configurable = (ext_proto->find != NULL);
+ prop->value.data.u.prop_handler = njs_external_property_delete;
+ }
+
+ pq->ext_data = data;
+ pq->ext_proto = ext_proto;
+ pq->ext_index = object->external.index;
+
+ pq->lhq.value = prop;
+
+ vm->stash = (uintptr_t) pq;
+
+ return NXT_OK;
+ }
+
+done:
+
+ if (ext_proto->type == NJS_EXTERN_METHOD) {
+ prop->value.type = NJS_FUNCTION;
+ prop->value.data.u.function = ext_proto->function;
+ prop->value.data.truth = 1;
+ }
+
+ pq->lhq.value = prop;
+
+ return ret;
+}
+
+
+static njs_ret_t
+njs_external_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval,
+ njs_value_t *retval)
+{
+ void *obj;
+ njs_ret_t ret;
+ nxt_str_t s;
+ njs_property_query_t *pq;
+
+ pq = (njs_property_query_t *) vm->stash;
+
+ ret = njs_vm_value_to_ext_string(vm, &s, setval, 0);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ *retval = *setval;
+
+ obj = njs_extern_index(vm, pq->ext_index);
+
+ return pq->ext_proto->set(vm, obj, pq->ext_data, &s);
+}
+
+
+static njs_ret_t
+njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *unused, njs_value_t *unused2)
+{
+ void *obj;
+ njs_property_query_t *pq;
+
+ pq = (njs_property_query_t *) vm->stash;
+
+ obj = njs_extern_index(vm, pq->ext_index);
+
+ return pq->ext_proto->find(vm, obj, pq->ext_data, 1);
}
@@ -527,6 +738,34 @@ njs_object_query_prop_handler(njs_proper
njs_ret_t
+njs_method_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
+{
+ njs_function_t *function;
+ njs_object_prop_t *prop, *shared;
+
+ prop = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_prop_t));
+ if (nxt_slow_path(prop == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ shared = pq->lhq.value;
+ *prop = *shared;
+
+ function = njs_function_value_copy(vm, &prop->value);
+ if (nxt_slow_path(function == NULL)) {
+ return NXT_ERROR;
+ }
+
+ pq->lhq.replace = 0;
+ pq->lhq.value = prop;
+ pq->lhq.pool = vm->mem_cache_pool;
+
+ return nxt_lvlhsh_insert(&pq->prototype->hash, &pq->lhq);
+}
+
+
+njs_ret_t
njs_object_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
{
@@ -988,68 +1227,75 @@ static njs_ret_t
njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused)
{
- double num;
- uint32_t index;
nxt_int_t ret;
- njs_array_t *array;
njs_object_t *descriptor;
- njs_object_prop_t *pr, *prop, array_prop;
+ njs_object_prop_t *pr, *prop;
const njs_value_t *value, *property, *setval;
nxt_lvlhsh_query_t lhq;
njs_property_query_t pq;
value = njs_arg(args, nargs, 1);
- if (!njs_is_object(value)) {
- if (njs_is_null_or_void(value)) {
- njs_type_error(vm, "cannot convert %s argument to object",
- njs_type_string(value->type));
- return NXT_ERROR;
- }
-
+ if (njs_is_null_or_void(value)) {
+ njs_type_error(vm, "cannot convert %s argument to object",
+ njs_type_string(value->type));
+ return NXT_ERROR;
+ }
+
+ property = njs_arg(args, nargs, 2);
+
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
+
+ ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
+
+ switch (ret) {
+ case NXT_OK:
+ break;
+
+ case NXT_DECLINED:
vm->retval = njs_value_void;
return NXT_OK;
+
+ case NJS_TRAP:
+ case NXT_ERROR:
+ default:
+ return ret;
}
- prop = NULL;
- property = njs_arg(args, nargs, 2);
-
- if (njs_is_array(value)) {
- array = value->data.u.array;
- num = njs_string_to_index(property);
- index = num;
-
- if ((double) index == num
- && index < array->length
- && njs_is_valid(&array->start[index]))
- {
- prop = &array_prop;
-
- array_prop.name = *property;
- array_prop.value = array->start[index];
-
- array_prop.configurable = 1;
- array_prop.enumerable = 1;
- array_prop.writable = 1;
+ 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, (njs_value_t *) value,
+ NULL, &prop->value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
}
- }
-
- lhq.proto = &njs_object_hash_proto;
-
- if (prop == NULL) {
- pq.query = NJS_PROPERTY_QUERY_GET;
- pq.lhq.key.length = 0;
- pq.lhq.key.start = NULL;
-
- ret = njs_property_query(vm, &pq, (njs_value_t *) value,
- (njs_value_t *) property);
-
- if (ret != NXT_OK) {
- vm->retval = njs_value_void;
- return NXT_OK;
+
+ break;
+
+ case NJS_METHOD:
+ if (pq.shared) {
+ ret = njs_method_private_copy(vm, &pq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ prop = pq.lhq.value;
}
- prop = pq.lhq.value;
+ break;
+
+ default:
+ njs_type_error(vm, "unexpected property type: %s",
+ njs_prop_type_string(prop->type));
+ return NXT_ERROR;
}
descriptor = njs_object_alloc(vm);
@@ -1057,6 +1303,7 @@ njs_object_get_own_property_descriptor(n
return NXT_ERROR;
}
+ lhq.proto = &njs_object_hash_proto;
lhq.replace = 0;
lhq.pool = vm->mem_cache_pool;
lhq.proto = &njs_object_hash_proto;
@@ -1953,3 +2200,28 @@ const njs_object_init_t njs_object_prot
njs_object_prototype_properties,
nxt_nitems(njs_object_prototype_properties),
};
+
+
+const char *
+njs_prop_type_string(njs_object_property_type_t type)
+{
+ switch (type) {
+ case NJS_PROPERTY_REF:
+ return "property_ref";
+
+ case NJS_METHOD:
+ return "method";
+
+ case NJS_PROPERTY_HANDLER:
+ return "property handler";
+
+ case NJS_WHITEOUT:
+ return "whiteout";
+
+ case NJS_PROPERTY:
+ return "property";
+
+ default:
+ return "unknown";
+ }
+}
diff -r 6b06b00139a1 -r 9d7b9cc03569 njs/njs_object.h
--- a/njs/njs_object.h Sat Oct 06 17:52:40 2018 +0300
+++ b/njs/njs_object.h Fri Oct 19 20:52:57 2018 +0300
@@ -10,8 +10,7 @@
typedef enum {
NJS_PROPERTY = 0,
- NJS_GETTER,
- NJS_SETTER,
+ NJS_PROPERTY_REF,
NJS_METHOD,
NJS_PROPERTY_HANDLER,
NJS_WHITEOUT,
@@ -47,13 +46,27 @@ typedef struct {
/* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
njs_object_prop_t scratch;
+ /* These three fields are used for NJS_EXTERNAL setters. */
+ uintptr_t ext_data;
+ const njs_extern_t *ext_proto;
+ uint32_t ext_index;
+
njs_value_t value;
njs_object_t *prototype;
uint8_t query;
uint8_t shared;
+ uint8_t own;
} njs_property_query_t;
+#define njs_property_query_init(pq, _query, _own) \
+ do { \
+ (pq)->lhq.key.length = 0; \
+ (pq)->lhq.value = NULL; \
+ (pq)->query = _query; \
+ (pq)->own = _own; \
+ } while (0)
+
struct njs_object_init_s {
nxt_str_t name;
@@ -67,10 +80,12 @@ njs_object_t *njs_object_value_copy(njs_
njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
nxt_uint_t type);
njs_array_t *njs_object_keys_array(njs_vm_t *vm, const njs_value_t *object);
+njs_ret_t njs_value_property(njs_vm_t *vm, njs_value_t *value,
+ const njs_value_t *property, njs_value_t *retval);
njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
nxt_lvlhsh_query_t *lhq);
njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq,
- njs_value_t *object, njs_value_t *property);
+ njs_value_t *object, const njs_value_t *property);
nxt_int_t njs_object_hash_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
const njs_object_prop_t *prop, nxt_uint_t n);
njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args,
@@ -90,6 +105,9 @@ njs_value_t *njs_property_constructor_cr
njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
+njs_ret_t njs_method_private_copy(njs_vm_t *vm, njs_property_query_t *pq);
+const char * njs_prop_type_string(njs_object_property_type_t type);
+
extern const njs_object_init_t njs_object_constructor_init;
extern const njs_object_init_t njs_object_prototype_init;
diff -r 6b06b00139a1 -r 9d7b9cc03569 njs/njs_vm.c
--- a/njs/njs_vm.c Sat Oct 06 17:52:40 2018 +0300
+++ b/njs/njs_vm.c Fri Oct 19 20:52:57 2018 +0300
@@ -25,8 +25,6 @@ struct njs_property_next_s {
static nxt_noinline njs_ret_t njs_string_concat(njs_vm_t *vm,
njs_value_t *val1, njs_value_t *val2);
-static njs_ret_t njs_method_private_copy(njs_vm_t *vm,
- njs_property_query_t *pq);
static nxt_noinline njs_ret_t njs_values_equal(njs_vm_t *vm,
const njs_value_t *val1, const njs_value_t *val2);
static nxt_noinline njs_ret_t njs_values_compare(njs_vm_t *vm,
@@ -476,150 +474,14 @@ njs_ret_t
njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
njs_value_t *property)
{
- void *obj;
- int32_t index;
- uintptr_t data;
- njs_ret_t ret;
- njs_value_t *val, ext_val;
- njs_slice_prop_t slice;
- njs_string_prop_t string;
- njs_object_prop_t *prop;
- const njs_value_t *retval;
- const njs_extern_t *ext_proto;
- njs_property_query_t pq;
-
- pq.query = NJS_PROPERTY_QUERY_GET;
-
- ret = njs_property_query(vm, &pq, object, property);
-
- retval = &njs_value_void;
-
- switch (ret) {
-
- case NXT_OK:
- prop = pq.lhq.value;
-
- switch (prop->type) {
-
- case NJS_METHOD:
- if (pq.shared) {
- ret = njs_method_private_copy(vm, &pq);
-
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- prop = pq.lhq.value;
- }
-
- /* Fall through. */
-
- case NJS_PROPERTY:
- retval = &prop->value;
- break;
-
- default:
- nxt_thread_log_alert("invalid property get type:%d", prop->type);
-
- return NXT_ERROR;
- }
-
- break;
-
- case NXT_DECLINED:
- case NJS_PRIMITIVE_VALUE:
- break;
-
- case NJS_STRING_VALUE:
-
- /* string[n]. */
-
- index = (int32_t) njs_value_to_index(property);
-
- if (nxt_fast_path(index >= 0)) {
- slice.start = index;
- slice.length = 1;
- slice.string_length = njs_string_prop(&string, object);
-
- if (slice.start < slice.string_length) {
- /*
- * A single codepoint string fits in vm->retval
- * so the function cannot fail.
- */
- (void) njs_string_slice(vm, &vm->retval, &string, &slice);
-
- return sizeof(njs_vmcode_prop_get_t);
- }
- }
-
- break;
-
- case NJS_ARRAY_VALUE:
- val = pq.lhq.value;
-
- if (njs_is_valid(val)) {
- retval = val;
- }
-
- break;
-
- case NJS_EXTERNAL_VALUE:
- ext_proto = object->external.proto;
-
- ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq);
-
- if (ret == NXT_OK) {
- ext_proto = pq.lhq.value;
-
- ext_val.type = NJS_EXTERNAL;
- ext_val.data.truth = 1;
- ext_val.external.proto = ext_proto;
- ext_val.external.index = object->external.index;
-
- if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
- retval = &ext_val;
- break;
- }
-
- data = ext_proto->data;
-
- } else {
- data = (uintptr_t) &pq.lhq.key;
- }
-
- vm->retval = njs_value_void;
-
- if (ext_proto->get != NULL) {
- obj = njs_extern_object(vm, object);
-
- ret = ext_proto->get(vm, &vm->retval, obj, data);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- /* The vm->retval is already retained by ext_proto->get(). */
- }
-
- if (ext_proto->type == NJS_EXTERN_METHOD) {
- vm->retval.data.u.function = ext_proto->function;
- vm->retval.type = NJS_FUNCTION;
More information about the nginx-devel
mailing list