Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created September 20, 2018 14:06
Show Gist options
  • Save xeioex/f691a64d48ddb7db0fda16ceceab5670 to your computer and use it in GitHub Desktop.
Save xeioex/f691a64d48ddb7db0fda16ceceab5670 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1537376128 -10800
# Wed Sep 19 19:55:28 2018 +0300
# Node ID 4a160b7b58bd2badc597c92c261ceae74b76ecff
# Parent 68a3580688ab3a5dcd1d02a52f13c2e6532a58ff
Fixed http response and parent getters.
Getters are expected to set resulting value to the provided
argument, not to vm->retval.
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -1844,7 +1844,7 @@ ngx_http_js_ext_get_response(njs_vm_t *v
ctx = ngx_http_get_module_ctx(r, ngx_http_js_module);
- njs_vm_retval_set(vm, njs_value_arg(&ctx->args[1]));
+ njs_value_assign(value, njs_value_arg(&ctx->args[1]));
return NJS_OK;
}
@@ -2189,7 +2189,7 @@ ngx_http_js_ext_get_parent(njs_vm_t *vm,
return NJS_ERROR;
}
- njs_vm_retval_set(vm, njs_value_arg(&ctx->args[0]));
+ njs_value_assign(value, njs_value_arg(&ctx->args[0]));
return NJS_OK;
}
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1537449234 -10800
# Thu Sep 20 16:13:54 2018 +0300
# Node ID d80d5490da4d3bed45001b90e8d73bf7085ae4e8
# Parent 4a160b7b58bd2badc597c92c261ceae74b76ecff
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.
diff --git a/njs/njs_extern.h b/njs/njs_extern.h
--- a/njs/njs_extern.h
+++ b/njs/njs_extern.h
@@ -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 --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -13,6 +13,14 @@ static njs_ret_t njs_object_property_que
njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
static njs_ret_t njs_array_property_query(njs_vm_t *vm,
njs_property_query_t *pq, njs_value_t *object, 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 +240,29 @@ 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,
* NJS_TRAP the property trap must be called,
* NXT_ERROR exception has been thrown.
*/
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;
hash = nxt_djb_hash;
@@ -261,30 +270,23 @@ 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;
- }
-
- obj = &vm->prototypes[NJS_PROTOTYPE_STRING].object;
- break;
-
- case NJS_ARRAY:
+ case NJS_OBJECT_STRING:
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);
+ if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+ if (object->type == NJS_OBJECT_STRING) {
+ object = &object->data.u.object_value->value;
+ }
+
+ return njs_string_property_query(vm, pq, object, index);
}
} else {
@@ -292,12 +294,35 @@ njs_property_query(njs_vm_t *vm, njs_pro
}
}
+ if (object->type == NJS_STRING) {
+ obj = &vm->prototypes[NJS_PROTOTYPE_STRING].object;
+
+ } else {
+ 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);
+ }
+ }
+
/* Fall through. */
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,12 +347,6 @@ 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;
@@ -361,9 +380,7 @@ njs_property_query(njs_vm_t *vm, njs_pro
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_external_property_query(vm, pq, object);
}
return njs_object_property_query(vm, pq, object, obj);
@@ -381,6 +398,7 @@ njs_object_property_query(njs_vm_t *vm,
njs_value_t *value, njs_object_t *object)
{
njs_ret_t ret;
+ njs_object_t *proto;
njs_object_prop_t *prop;
pq->lhq.proto = &njs_object_hash_proto;
@@ -392,70 +410,41 @@ njs_object_property_query(njs_vm_t *vm,
}
}
+ proto = object;
+
do {
- pq->prototype = object;
-
- ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
+ 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;
+ pq->shared = 0;
+
+ return (prop->type != NJS_WHITEOUT) ? ret : NXT_DECLINED;
+ }
+ }
+
+ if (pq->query > NJS_PROPERTY_QUERY_GET) {
+ return NXT_DECLINED;
+ }
+
+ ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
if (ret == NXT_OK) {
- prop = pq->lhq.value;
-
- if (prop->type != NJS_WHITEOUT) {
- pq->shared = 0;
-
- return ret;
- }
-
- goto next;
- }
-
- if (pq->query > NJS_PROPERTY_QUERY_IN) {
- /* NXT_DECLINED */
+ pq->shared = 1;
+
return ret;
}
- ret = nxt_lvlhsh_find(&object->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;
- }
-
- next:
-
- object = object->__proto__;
-
- } while (object != NULL);
-
- if (njs_is_string(value)) {
- return NJS_STRING_VALUE;
- }
-
- /* NXT_DECLINED */
-
- return ret;
+ proto = proto->__proto__;
+
+ } while (proto != NULL);
+
+ return NXT_DECLINED;
}
@@ -463,10 +452,11 @@ static njs_ret_t
njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
njs_value_t *object, uint32_t index)
{
- uint32_t size;
- njs_ret_t ret;
- njs_value_t *value;
- njs_array_t *array;
+ uint32_t size;
+ njs_ret_t ret;
+ njs_value_t *value;
+ njs_array_t *array;
+ njs_object_prop_t *prop;
array = object->data.u.array;
@@ -493,9 +483,204 @@ 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;
+
+ 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;
+ }
+
+ obj = njs_extern_index(vm, pq->ext_index);
+
+ ret = pq->ext_proto->set(vm, obj, pq->ext_data, &s);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ *retval = *setval;
+
+ return NXT_OK;
+}
+
+
+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_ret_t ret;
+ njs_property_query_t *pq;
+
+ pq = (njs_property_query_t *) vm->stash;
+
+ obj = njs_extern_index(vm, pq->ext_index);
+
+ ret = pq->ext_proto->find(vm, obj, pq->ext_data, 1);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ return NXT_OK;
}
@@ -527,6 +712,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 +1201,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 +1277,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 +2174,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 --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -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,26 @@ typedef struct {
/* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
njs_object_prop_t scratch;
+ /* scratch data 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)->query = _query; \
+ (pq)->own = _own; \
+ } while (0)
+
struct njs_object_init_s {
nxt_str_t name;
@@ -67,10 +79,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 +104,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 --git a/njs/njs_vm.c b/njs/njs_vm.c
--- a/njs/njs_vm.c
+++ b/njs/njs_vm.c
@@ -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,147 +474,13 @@ 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;
- vm->retval.data.truth = 1;
- }
-
- return sizeof(njs_vmcode_prop_get_t);
-
- case NJS_TRAP:
- case NXT_ERROR:
- default:
-
+ njs_ret_t ret;
+
+ ret = njs_value_property(vm, object, property, &vm->retval);
+ if (ret == NXT_ERROR || ret == NJS_TRAP) {
return ret;
}
- vm->retval = *retval;
-
/* GC: njs_retain(retval) */
return sizeof(njs_vmcode_prop_get_t);
@@ -627,13 +491,9 @@ njs_ret_t
njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
njs_value_t *property)
{
- void *obj;
- uintptr_t data;
- nxt_str_t s;
njs_ret_t ret;
- njs_value_t *p, *value;
+ njs_value_t *value;
njs_object_prop_t *prop;
- const njs_extern_t *ext_proto;
njs_property_query_t pq;
njs_vmcode_prop_set_t *code;
@@ -646,8 +506,7 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
code = (njs_vmcode_prop_set_t *) vm->current;
value = njs_vmcode_operand(vm, code->value);
- pq.lhq.key.length = 0;
- pq.query = NJS_PROPERTY_QUERY_SET;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
ret = njs_property_query(vm, &pq, object, property);
@@ -656,14 +515,33 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
case NXT_OK:
prop = pq.lhq.value;
- if (prop->type == NJS_PROPERTY_HANDLER && prop->writable) {
- ret = prop->value.data.u.prop_handler(vm, object, value,
- &vm->retval);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
+ switch (prop->type) {
+ case NJS_PROPERTY:
+ break;
+
+ case NJS_PROPERTY_REF:
+ *prop->value.data.u.value = *value;
+ return sizeof(njs_vmcode_prop_set_t);
+
+ case NJS_PROPERTY_HANDLER:
+ if (prop->writable) {
+ ret = prop->value.data.u.prop_handler(vm, object, value,
+ &vm->retval);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ return sizeof(njs_vmcode_prop_set_t);
}
- return sizeof(njs_vmcode_prop_set_t);
+ break;
+
+ default:
+ njs_internal_error(vm, "unexpected property type '%s' "
+ "while setting",
+ njs_prop_type_string(prop->type));
+
+ return NXT_ERROR;
}
break;
@@ -693,47 +571,6 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
break;
- case NJS_PRIMITIVE_VALUE:
- case NJS_STRING_VALUE:
- return sizeof(njs_vmcode_prop_set_t);
-
- case NJS_ARRAY_VALUE:
- p = pq.lhq.value;
- *p = *value;
-
- return sizeof(njs_vmcode_prop_set_t);
-
- 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;
- data = ext_proto->data;
-
- } else {
- data = (uintptr_t) &pq.lhq.key;
- }
-
- if (ext_proto->set != NULL) {
- ret = njs_vm_value_to_ext_string(vm, &s, value, 0);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- /* TODO retain value if it is string. */
-
- obj = njs_extern_object(vm, object);
-
- ret = ext_proto->set(vm, obj, data, &s);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
- }
-
- return sizeof(njs_vmcode_prop_set_t);
-
case NJS_TRAP:
case NXT_ERROR:
default:
@@ -757,68 +594,34 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
njs_ret_t
njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property)
{
- void *obj;
- uintptr_t data;
njs_ret_t ret;
- njs_value_t *value;
+ njs_object_prop_t *prop;
const njs_value_t *retval;
- const njs_extern_t *ext_proto;
njs_property_query_t pq;
retval = &njs_value_false;
- pq.query = NJS_PROPERTY_QUERY_IN;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
ret = njs_property_query(vm, &pq, object, property);
switch (ret) {
case NXT_OK:
+ prop = pq.lhq.value;
+
+ if (!njs_is_valid(&prop->value)) {
+ break;
+ }
+
retval = &njs_value_true;
break;
case NXT_DECLINED:
- break;
-
- case NJS_PRIMITIVE_VALUE:
- case NJS_STRING_VALUE:
- njs_type_error(vm, "property in on a primitive value");
-
- return NXT_ERROR;
-
- case NJS_ARRAY_VALUE:
- value = pq.lhq.value;
-
- if (njs_is_valid(value)) {
- retval = &njs_value_true;
- }
-
- break;
-
- case NJS_EXTERNAL_VALUE:
- ext_proto = object->external.proto;
-
- ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq);
-
- if (ret == NXT_OK) {
- retval = &njs_value_true;
-
- } else {
- data = (uintptr_t) &pq.lhq.key;
-
- if (ext_proto->find != NULL) {
- obj = njs_extern_object(vm, object);
-
- ret = ext_proto->find(vm, obj, data, 0);
-
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
- }
-
- if (ret == NXT_OK) {
- retval = &njs_value_true;
- }
- }
+ if (!njs_is_object(object) && !njs_is_external(object)) {
+ njs_type_error(vm, "property in on a primitive value");
+
+ return NXT_ERROR;
}
break;
@@ -840,19 +643,14 @@ njs_ret_t
njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
njs_value_t *property)
{
- void *obj;
- uintptr_t data;
njs_ret_t ret;
- njs_value_t *value, ext_val;
const njs_value_t *retval;
njs_object_prop_t *prop;
- const njs_extern_t *ext_proto;
njs_property_query_t pq;
retval = &njs_value_false;
- pq.lhq.key.length = 0;
- pq.query = NJS_PROPERTY_QUERY_DELETE;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 1);
ret = njs_property_query(vm, &pq, object, property);
@@ -861,6 +659,36 @@ njs_vmcode_property_delete(njs_vm_t *vm,
case NXT_OK:
prop = pq.lhq.value;
+ switch (prop->type) {
+ case NJS_PROPERTY:
+ break;
+
+ case NJS_PROPERTY_REF:
+ njs_set_invalid(prop->value.data.u.value);
+ retval = &njs_value_true;
+ goto done;
+
+ case NJS_PROPERTY_HANDLER:
+ if (prop->configurable) {
+ ret = prop->value.data.u.prop_handler(vm, object, NULL, NULL);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ retval = &njs_value_true;
+ goto done;
+ }
+
+ break;
+
+ default:
+ njs_internal_error(vm, "unexpected property type '%s' "
+ "while deleting",
+ njs_prop_type_string(prop->type));
+
+ return NXT_ERROR;
+ }
+
if (nxt_slow_path(!prop->configurable)) {
njs_type_error(vm, "Cannot delete property '%.*s' of %s",
pq.lhq.key.length, pq.lhq.key.start,
@@ -879,56 +707,6 @@ njs_vmcode_property_delete(njs_vm_t *vm,
break;
case NXT_DECLINED:
- case NJS_PRIMITIVE_VALUE:
- case NJS_STRING_VALUE:
- break;
-
- case NJS_ARRAY_VALUE:
- value = pq.lhq.value;
- njs_set_invalid(value);
- retval = &njs_value_true;
- 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;
-
- if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
-
- ext_val.type = NJS_EXTERNAL;
- ext_val.data.truth = 1;
- ext_val.external.proto = ext_proto;
- ext_val.external.index = object->external.index;
-
- data = (uintptr_t) &ext_val;
-
- } else {
- data = ext_proto->data;
- }
-
- } else {
- data = (uintptr_t) &pq.lhq.key;
- }
-
- if (ext_proto->find != NULL) {
- obj = njs_extern_object(vm, object);
-
- ret = ext_proto->find(vm, obj, data, 1);
-
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
- }
-
- if (ret == NXT_OK) {
- retval = &njs_value_true;
- }
- }
-
break;
case NJS_TRAP:
@@ -938,40 +716,14 @@ njs_vmcode_property_delete(njs_vm_t *vm,
return ret;
}
+done:
+
vm->retval = *retval;
return sizeof(njs_vmcode_3addr_t);
}
-static 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_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
njs_value_t *invld)
@@ -1097,12 +849,10 @@ njs_ret_t
njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
njs_value_t *constructor)
{
- nxt_int_t ret;
- njs_value_t *value;
- njs_object_t *prototype, *proto;
- njs_object_prop_t *prop;
- const njs_value_t *retval;
- njs_property_query_t pq;
+ nxt_int_t ret;
+ njs_value_t *value, val;
+ njs_object_t *prototype, *proto;
+ const njs_value_t *retval;
static njs_value_t prototype_string = njs_string("prototype");
@@ -1114,13 +864,10 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs
retval = &njs_value_false;
if (njs_is_object(object)) {
- pq.query = NJS_PROPERTY_QUERY_GET;
-
- ret = njs_property_query(vm, &pq, constructor, &prototype_string);
+ ret = njs_value_property(vm, constructor, &prototype_string, &val);
if (nxt_fast_path(ret == NXT_OK)) {
- prop = pq.lhq.value;
- value = &prop->value;
+ value = &val;
/* TODO: test prop->value is object. */
@@ -2115,17 +1862,16 @@ njs_ret_t
njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
{
njs_ret_t ret;
+ nxt_str_t string;
njs_value_t *value;
njs_object_prop_t *prop;
njs_property_query_t pq;
- const njs_extern_t *ext_proto;
njs_vmcode_method_frame_t *method;
+ value = NULL;
method = (njs_vmcode_method_frame_t *) vm->current;
- pq.lhq.key.length = 0;
- pq.lhq.key.start = NULL;
- pq.query = NJS_PROPERTY_QUERY_GET;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
ret = njs_property_query(vm, &pq, object, name);
@@ -2134,53 +1880,54 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj
case NXT_OK:
prop = pq.lhq.value;
- ret = njs_function_frame_create(vm, &prop->value, object, method->nargs,
- method->code.ctor);
- break;
-
- case NJS_ARRAY_VALUE:
- value = pq.lhq.value;
-
- ret = njs_function_frame_create(vm, value, object, method->nargs,
- method->code.ctor);
- break;
-
- case NJS_EXTERNAL_VALUE:
- ext_proto = object->external.proto;
-
- ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq);
-
- if (nxt_slow_path(ret != NXT_OK)) {
- njs_type_error(vm,
- "cannot find property '%.*s' of an external object",
- (int) pq.lhq.key.length, pq.lhq.key.start);
- return NXT_ERROR;
-
- }
-
- ext_proto = pq.lhq.value;
-
- if (nxt_slow_path(ext_proto->type != NJS_EXTERN_METHOD)) {
- njs_type_error(vm,
- "method '%.*s' of an external object is not callable",
- (int) pq.lhq.key.length, pq.lhq.key.start);
+ switch (prop->type) {
+ case NJS_PROPERTY:
+ case NJS_METHOD:
+ break;
+
+ case NJS_PROPERTY_HANDLER:
+ pq.scratch = *prop;
+ prop = &pq.scratch;
+ ret = prop->value.data.u.prop_handler(vm, object, NULL,
+ &prop->value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ break;
+
+ default:
+ njs_internal_error(vm, "unexpected property type '%s' "
+ "while getting method",
+ njs_prop_type_string(prop->type));
+
return NXT_ERROR;
}
- ret = njs_function_native_frame(vm, ext_proto->function, object, NULL,
- method->nargs, 0, method->code.ctor);
+ value = &prop->value;
+
break;
+ case NXT_DECLINED:
+ break;
+
+ case NJS_TRAP:
case NXT_ERROR:
- /* An exception was set in njs_property_query(). */
- return NXT_ERROR;
-
default:
- njs_internal_error(vm, "method '%.*s' query failed:%d",
- (int) pq.lhq.key.length, pq.lhq.key.start, ret);
+
+ return ret;
+ }
+
+ if (value == NULL || !njs_is_function(value)) {
+ njs_string_get(name, &string);
+ njs_type_error(vm, "'%.*s' is not a function", (int) string.length,
+ string.start);
return NXT_ERROR;
}
+ ret = njs_function_frame_create(vm, value, object, method->nargs,
+ method->code.ctor);
+
if (nxt_fast_path(ret == NXT_OK)) {
return sizeof(njs_vmcode_method_frame_t);
}
@@ -3159,6 +2906,45 @@ njs_vmcode_string_argument(njs_vm_t *vm,
}
+static njs_ret_t
+njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ u_char *restart;
+ njs_ret_t ret;
+ njs_value_t *retval, *value1;
+ njs_native_frame_t *frame;
+ njs_vmcode_generic_t *vmcode;
+
+ frame = vm->top_frame;
+ restart = frame->trap_restart;
+ frame->trap_restart = NULL;
+ vm->current = restart;
+ vmcode = (njs_vmcode_generic_t *) restart;
+
+ value1 = &frame->trap_values[0];
+
+ if (frame->trap_reference) {
+ value1 = value1->data.u.value;
+ }
+
+ ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]);
+
+ if (nxt_slow_path(ret == NJS_TRAP)) {
+ /* Trap handlers are not reentrant. */
+ njs_internal_error(vm, "trap inside restart instruction");
+ return NXT_ERROR;
+ }
+
+ retval = njs_vmcode_operand(vm, vmcode->operand1);
+
+ //njs_release(vm, retval);
+
+ *retval = vm->retval;
+
+ return ret;
+}
+
+
/*
* A hint value is 0 for numbers and 1 for strings. The value chooses
* method calls order specified by ECMAScript 5.1: "valueOf", "toString"
@@ -3257,42 +3043,87 @@ njs_primitive_value(njs_vm_t *vm, njs_va
}
-static njs_ret_t
-njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+/*
+ * ES5.1, 8.12.3: [[Get]].
+ * NXT_OK property has been found in object,
+ * retval will contain the property's value
+ *
+ * NXT_DECLINED property was not found in object,
+ * NJS_TRAP the property trap must be called,
+ * NXT_ERROR exception has been thrown.
+ * retval will contain undefined
+ */
+njs_ret_t
+njs_value_property(njs_vm_t *vm, njs_value_t *value,
+ const njs_value_t *property, njs_value_t *retval)
{
- u_char *restart;
njs_ret_t ret;
- njs_value_t *retval, *value1;
- njs_native_frame_t *frame;
- njs_vmcode_generic_t *vmcode;
-
- frame = vm->top_frame;
- restart = frame->trap_restart;
- frame->trap_restart = NULL;
- vm->current = restart;
- vmcode = (njs_vmcode_generic_t *) restart;
-
- value1 = &frame->trap_values[0];
-
- if (frame->trap_reference) {
- value1 = value1->data.u.value;
+ njs_object_prop_t *prop;
+ njs_property_query_t pq;
+
+ *retval = njs_value_void;
+
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
+
+ ret = njs_property_query(vm, &pq, value, property);
+
+ 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;
+
+ case 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)) {
+ *retval = prop->value;
+ }
+
+ break;
+
+ default:
+ njs_internal_error(vm, "unexpected property type '%s' "
+ "while getting",
+ njs_prop_type_string(prop->type));
+
+ return NXT_ERROR;
+ }
+
+ break;
+
+ case NXT_DECLINED:
+ break;
+
+ case NJS_TRAP:
+ case NXT_ERROR:
+ default:
+
+ return ret;
}
- ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]);
-
- if (nxt_slow_path(ret == NJS_TRAP)) {
- /* Trap handlers are not reentrant. */
- njs_internal_error(vm, "trap inside restart instruction");
- return NXT_ERROR;
- }
-
- retval = njs_vmcode_operand(vm, vmcode->operand1);
-
- //njs_release(vm, retval);
-
- *retval = vm->retval;
-
- return ret;
+ return (ret == NXT_OK) ? NXT_OK : NXT_DECLINED;
}
diff --git a/njs/njs_vm.h b/njs/njs_vm.h
--- a/njs/njs_vm.h
+++ b/njs/njs_vm.h
@@ -61,22 +61,13 @@ typedef enum {
#define NJS_APPLIED NXT_DONE
-/* The values must be greater than NXT_OK. */
-#define NJS_PRIMITIVE_VALUE 1
-#define NJS_STRING_VALUE 2
-#define NJS_ARRAY_VALUE 3
-#define NJS_EXTERNAL_VALUE 4
-
-
/*
- * NJS_PROPERTY_QUERY_GET must be less or equal to NJS_PROPERTY_QUERY_IN,
- * NJS_PROPERTY_QUERY_SET and NJS_PROPERTY_QUERY_DELETE must be greater
- * than NJS_PROPERTY_QUERY_IN.
+ * NJS_PROPERTY_QUERY_GET must be less to NJS_PROPERTY_QUERY_SET
+ * and NJS_PROPERTY_QUERY_DELETE.
*/
#define NJS_PROPERTY_QUERY_GET 0
-#define NJS_PROPERTY_QUERY_IN 1
-#define NJS_PROPERTY_QUERY_SET 2
-#define NJS_PROPERTY_QUERY_DELETE 3
+#define NJS_PROPERTY_QUERY_SET 1
+#define NJS_PROPERTY_QUERY_DELETE 2
/*
@@ -1073,6 +1064,13 @@ struct njs_vm_s {
nxt_array_t *backtrace;
njs_trap_t trap:8;
+
+ /*
+ * njs_property_query() uses it to store reference to a temporary
+ * PROPERTY_HANDLERs for NJS_EXTERNAL values in NJS_PROPERTY_QUERY_SET
+ * and NJS_PROPERTY_QUERY_DELETE modes.
+ */
+ uintptr_t stash; /* njs_property_query_t * */
};
diff --git a/njs/test/njs_expect_test.exp b/njs/test/njs_expect_test.exp
--- a/njs/test/njs_expect_test.exp
+++ b/njs/test/njs_expect_test.exp
@@ -192,7 +192,7 @@ njs_test {
njs_test {
{"console.ll()\r\n"
- "console.ll()\r\nTypeError: cannot find property 'll' of an external object"}
+ "console.ll()\r\nTypeError: 'll' is not a function"}
}
njs_test {
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -5,6 +5,8 @@
*/
#include <njs_core.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_djb_hash.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@@ -2619,9 +2621,18 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("delete --[][1]"),
nxt_string("true") },
+ { nxt_string("var a = [1,2,3]; a.x = 10; delete a[1]"),
+ nxt_string("true") },
+
{ nxt_string("var a = {}; 1 in a"),
nxt_string("false") },
+ { nxt_string("'a' in {a:1}"),
+ nxt_string("true") },
+
+ { nxt_string("'a' in Object.create({a:1})"),
+ nxt_string("true") },
+
{ nxt_string("var a = 1; 1 in a"),
nxt_string("TypeError: property in on a primitive value") },
@@ -3998,6 +4009,62 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("var p1 = $r.props, p2 = $r2.props; '' + p1.a + p2.a"),
nxt_string("12") },
+ { nxt_string("var p = $r3.props; p.a = 1"),
+ nxt_string("TypeError: Cannot assign to read-only property 'a' of external") },
+ { nxt_string("var p = $r3.props; delete p.a"),
+ nxt_string("TypeError: Cannot delete property 'a' of external") },
+
+ { nxt_string("$r.vars.p + $r2.vars.q + $r3.vars.k"),
+ nxt_string("pvalqvalkval") },
+
+ { nxt_string("$r.vars.unset"),
+ nxt_string("undefined") },
+
+ { nxt_string("var v = $r3.vars; v.k"),
+ nxt_string("kval") },
+
+ { nxt_string("var v = $r3.vars; v.unset = 1; v.unset"),
+ nxt_string("1") },
+
+ { nxt_string("$r.vars.unset = 'a'; $r2.vars.unset = 'b';"
+ "$r.vars.unset + $r2.vars.unset"),
+ nxt_string("ab") },
+
+ { nxt_string("$r.vars.unset = 1; $r2.vars.unset = 2;"
+ "$r.vars.unset + $r2.vars.unset"),
+ nxt_string("12") },
+
+ { nxt_string("$r3.vars.p = 'a'; $r3.vars.p2 = 'b';"
+ "$r3.vars.p + $r3.vars.p2"),
+ nxt_string("ab") },
+
+ { nxt_string("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p"),
+ nxt_string("undefined") },
+
+ { nxt_string("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p = 'b'; $r3.vars.p"),
+ nxt_string("b") },
+
+ { nxt_string("$r3.vars.error = 1"),
+ nxt_string("Error: cannot set 'error' prop") },
+
+ { nxt_string("delete $r3.vars.error"),
+ nxt_string("Error: cannot delete 'error' prop") },
+
+ { nxt_string("delete $r3.vars.e"),
+ nxt_string("true") },
+
+ { nxt_string("$r3.consts.k"),
+ nxt_string("kval") },
+
+ { nxt_string("$r3.consts.k = 1"),
+ nxt_string("TypeError: Cannot assign to read-only property 'k' of external") },
+
+ { nxt_string("delete $r3.consts.k"),
+ nxt_string("TypeError: Cannot delete property 'k' of external") },
+
+ { nxt_string("delete $r3.vars.p; $r3.vars.p"),
+ nxt_string("undefined") },
+
{ nxt_string("var a = $r.host; a +' '+ a.length +' '+ a"),
nxt_string("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") },
@@ -4026,6 +4093,9 @@ static njs_unit_test_t njs_test[] =
"sr.uri + sr2.uri"),
nxt_string("ZZZYYY") },
+ { nxt_string("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"),
+ nxt_string("a") },
+
{ nxt_string("var p; for (p in $r.some_method);"),
nxt_string("undefined") },
@@ -4039,10 +4109,10 @@ static njs_unit_test_t njs_test[] =
nxt_string("true") },
{ nxt_string("delete $r.uri"),
- nxt_string("false") },
+ nxt_string("TypeError: Cannot delete property 'uri' of external") },
{ nxt_string("delete $r.one"),
- nxt_string("false") },
+ nxt_string("TypeError: Cannot delete property 'one' of external") },
{ nxt_string("$r.some_method.call($r, 'YES')"),
nxt_string("АБВ") },
@@ -4063,7 +4133,7 @@ static njs_unit_test_t njs_test[] =
nxt_string("undefined") },
{ nxt_string("$r.error = 'OK'"),
- nxt_string("OK") },
+ nxt_string("TypeError: Cannot assign to read-only property 'error' of external") },
{ nxt_string("var a = { toString: function() { return 1 } }; a"),
nxt_string("1") },
@@ -4949,7 +5019,7 @@ static njs_unit_test_t njs_test[] =
nxt_string("TypeError: object is not callable") },
{ nxt_string("var o = {a:1}; o.a()"),
- nxt_string("TypeError: object is not callable") },
+ nxt_string("TypeError: 'a' is not a function") },
{ nxt_string("(function(){})()"),
nxt_string("undefined") },
@@ -5945,10 +6015,10 @@ static njs_unit_test_t njs_test[] =
nxt_string("SyntaxError: Unexpected token \"null\" in 1") },
{ nxt_string("'a'.f()"),
- nxt_string("InternalError: method 'f' query failed:2") },
+ nxt_string("TypeError: 'f' is not a function") },
{ nxt_string("1..f()"),
- nxt_string("InternalError: method 'f' query failed:-3") },
+ nxt_string("TypeError: 'f' is not a function") },
{ nxt_string("try {}"),
nxt_string("SyntaxError: Missing catch or finally after try in 1") },
@@ -6530,6 +6600,27 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("new String([1,2,3])"),
nxt_string("1,2,3") },
+ { nxt_string("var s = new String('αβ'); s.one = 1; 'one' in s"),
+ nxt_string("true") },
+
+ { nxt_string("var s = new String('αβ'); 'one' in s"),
+ nxt_string("false") },
+
+ { nxt_string("var s = new String('αβ'); s.one = 1; '1' in s"),
+ nxt_string("true") },
+
+ { nxt_string("var s = new String('αβ'); s.one = 1; 1 in s"),
+ nxt_string("true") },
+
+ { nxt_string("var s = new String('αβ'); s.one = 1; 2 in s"),
+ nxt_string("false") },
+
+ { nxt_string("var s = new String('αβ'); s[1]"),
+ nxt_string("β") },
+
+ { nxt_string("var s = new String('αβ'); s.valueOf()[1]"),
+ nxt_string("β") },
+
{ nxt_string("var o = { toString: function() { return 'OK' } };"
"String(o)"),
nxt_string("OK") },
@@ -7007,6 +7098,9 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Object.getOwnPropertyDescriptor({}, 'a')"),
nxt_string("undefined") },
+ { nxt_string("Object.getOwnPropertyDescriptor(Object.create({a:1}), 'a')"),
+ nxt_string("undefined") },
+
{ nxt_string("Object.getOwnPropertyDescriptor([3,4], '1').value"),
nxt_string("4") },
@@ -7016,18 +7110,56 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Object.getOwnPropertyDescriptor([], 'length').value"),
nxt_string("0") },
+ { nxt_string("Object.getOwnPropertyDescriptor([], '0')"),
+ nxt_string("undefined") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor([1,2], '1').value"),
+ nxt_string("2") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor([1,2], new String('1')).value"),
+ nxt_string("2") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor({undefined:1}, void 0).value"),
+ nxt_string("1") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor([1,2], 1).value"),
+ nxt_string("2") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor([1,,,3], '1')"),
+ nxt_string("undefined") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor([1,2], '3')"),
+ nxt_string("undefined") },
+
{ nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor([3,4], 'length'))"),
nxt_string("{\"value\":2,\"configurable\":false,\"enumerable\":false,\"writable\":true}") },
- { nxt_string("Object.getOwnPropertyDescriptor([3,4], '3')"),
- nxt_string("undefined") },
-
- { nxt_string("Object.getOwnPropertyDescriptor([], '0')"),
- nxt_string("undefined") },
+ { nxt_string("Object.getOwnPropertyDescriptor(Array.of, 'length').value"),
+ nxt_string("0") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor('αβγδ', '1').value"),
+ nxt_string("β") },
+
+ { nxt_string("Object.getOwnPropertyDescriptor(new String('αβγδ'), '1').value"),
+ nxt_string("β") },
+
+ { nxt_string("var s = new String('αβγδ'); s.a = 1;"
+ "Object.getOwnPropertyDescriptor(s, 'a').value"),
+ nxt_string("1") },
+
+ { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor('αβγδ', '2'))"),
+ nxt_string("{\"value\":\"γ\",\"configurable\":false,\"enumerable\":true,\"writable\":false}") },
+
+ { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor(new String('abc'), 'length'))"),
+ nxt_string("{\"value\":3,\"configurable\":false,\"enumerable\":false,\"writable\":false}") },
{ nxt_string("Object.getOwnPropertyDescriptor(1, '0')"),
nxt_string("undefined") },
+ { nxt_string("var min = Object.getOwnPropertyDescriptor(Math, 'min').value;"
+ "[min(1,2), min(2,1), min(-1,1)]"),
+ nxt_string("1,1,-1") },
+
{ nxt_string("Object.getOwnPropertyDescriptor()"),
nxt_string("TypeError: cannot convert void argument to object") },
@@ -9820,24 +9952,121 @@ static njs_unit_test_t njs_test[] =
typedef struct {
+ nxt_lvlhsh_t hash;
+ const njs_extern_t *proto;
+ nxt_mem_cache_pool_t *mem_cache_pool;
+
+ uint32_t a;
nxt_str_t uri;
- uint32_t a;
- nxt_mem_cache_pool_t *mem_cache_pool;
- const njs_extern_t *proto;
njs_opaque_value_t value;
} njs_unit_test_req_t;
+typedef struct {
+ njs_value_t name;
+ njs_value_t value;
+} njs_unit_test_prop_t;
+
+
+static nxt_int_t
+lvlhsh_unit_test_key_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ nxt_str_t name;
+ njs_unit_test_prop_t *prop;
+
+ prop = data;
+ njs_string_get(&prop->name, &name);
+
+ if (name.length != lhq->key.length) {
+ return NXT_DECLINED;
+ }
+
+ if (memcmp(name.start, lhq->key.start, lhq->key.length) == 0) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static void *
+lvlhsh_unit_test_pool_alloc(void *pool, size_t size, nxt_uint_t nalloc)
+{
+ return nxt_mem_cache_align(pool, size, size);
+}
+
+
+static void
+lvlhsh_unit_test_pool_free(void *pool, void *p, size_t size)
+{
+ nxt_mem_cache_free(pool, p);
+}
+
+
+static const nxt_lvlhsh_proto_t lvlhsh_proto nxt_aligned(64) = {
+ NXT_LVLHSH_LARGE_SLAB,
+ 0,
+ lvlhsh_unit_test_key_test,
+ lvlhsh_unit_test_pool_alloc,
+ lvlhsh_unit_test_pool_free,
+};
+
+
+static njs_unit_test_prop_t *
+lvlhsh_unit_test_alloc(nxt_mem_cache_pool_t *pool, const njs_value_t *name,
+ const njs_value_t *value)
+{
+ njs_unit_test_prop_t *prop;
+
+ prop = nxt_mem_cache_alloc(pool, sizeof(njs_unit_test_prop_t));
+ if (prop == NULL) {
+ return NULL;
+ }
+
+ prop->name = *name;
+ prop->value = *value;
+
+ return prop;
+}
+
+
+static nxt_int_t
+lvlhsh_unit_test_add(njs_unit_test_req_t *r, njs_unit_test_prop_t *prop)
+{
+ nxt_lvlhsh_query_t lhq;
+
+ njs_string_get(&prop->name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.replace = 1;
+ lhq.value = (void *) prop;
+ lhq.proto = &lvlhsh_proto;
+ lhq.pool = r->mem_cache_pool;
+
+ switch (nxt_lvlhsh_insert(&r->hash, &lhq)) {
+
+ case NXT_OK:
+ return NXT_OK;
+
+ case NXT_DECLINED:
+ default:
+ return NXT_ERROR;
+ }
+}
+
+
static njs_ret_t
njs_unit_test_r_get_uri_external(njs_vm_t *vm, njs_value_t *value, void *obj,
uintptr_t data)
{
- njs_unit_test_req_t *r;
-
- r = (njs_unit_test_req_t *) obj;
-
- return njs_string_create(vm, value, r->uri.start, r->uri.length, 0);
+ char *p = obj;
+
+ nxt_str_t *field;
+
+ field = (nxt_str_t *) (p + data);
+
+ return njs_string_create(vm, value, field->start, field->length, 0);
}
@@ -9845,11 +10074,13 @@ static njs_ret_t
njs_unit_test_r_set_uri_external(njs_vm_t *vm, void *obj, uintptr_t data,
nxt_str_t *value)
{
- njs_unit_test_req_t *r;
-
- r = (njs_unit_test_req_t *) obj;
-
- r->uri = *value;
+ char *p = obj;
+
+ nxt_str_t *field;
+
+ field = (nxt_str_t *) (p + data);
+
+ *field = *value;
return NXT_OK;
}
@@ -9890,6 +10121,109 @@ njs_unit_test_host_external(njs_vm_t *vm
static njs_ret_t
+njs_unit_test_r_get_vars(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ nxt_int_t ret;
+ nxt_str_t *key;
+ nxt_lvlhsh_query_t lhq;
+ njs_unit_test_req_t *r;
+ njs_unit_test_prop_t *prop;
+
+ r = (njs_unit_test_req_t *) obj;
+ key = (nxt_str_t *) data;
+
+ lhq.key = *key;
+ lhq.key_hash = nxt_djb_hash(key->start, key->length);
+ lhq.proto = &lvlhsh_proto;
+
+ ret = nxt_lvlhsh_find(&r->hash, &lhq);
+
+ prop = lhq.value;
+
+ if (ret == NXT_OK && njs_is_valid(&prop->value)) {
+ *value = prop->value;
+ return NXT_OK;
+ }
+
+ njs_value_void_set(value);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_unit_test_r_set_vars(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_str_t *value)
+{
+ nxt_int_t ret;
+ nxt_str_t *key;
+ njs_value_t name, val;
+ njs_unit_test_req_t *r;
+ njs_unit_test_prop_t *prop;
+
+ r = (njs_unit_test_req_t *) obj;
+ key = (nxt_str_t *) data;
+
+ if (key->length == 5 && memcmp(key->start, "error", 5) == 0) {
+ njs_vm_error(vm, "cannot set 'error' prop");
+ return NXT_ERROR;
+ }
+
+ njs_string_create(vm, &name, key->start, key->length, 0);
+ njs_string_create(vm, &val, value->start, value->length, 0);
+
+ prop = lvlhsh_unit_test_alloc(vm->mem_cache_pool, &name, &val);
+ if (prop == NULL) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ ret = lvlhsh_unit_test_add(r, prop);
+ if (ret != NXT_OK) {
+ njs_vm_error(vm, "lvlhsh_unit_test_add() failed");
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_unit_test_r_del_vars(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_bool_t delete)
+{
+ nxt_int_t ret;
+ nxt_str_t *key;
+ nxt_lvlhsh_query_t lhq;
+ njs_unit_test_req_t *r;
+ njs_unit_test_prop_t *prop;
+
+ r = (njs_unit_test_req_t *) obj;
+ key = (nxt_str_t *) data;
+
+ if (key->length == 5 && memcmp(key->start, "error", 5) == 0) {
+ njs_vm_error(vm, "cannot delete 'error' prop");
+ return NXT_ERROR;
+ }
+
+ lhq.key = *key;
+ lhq.key_hash = nxt_djb_hash(key->start, key->length);
+ lhq.proto = &lvlhsh_proto;
+
+ ret = nxt_lvlhsh_find(&r->hash, &lhq);
+
+ prop = lhq.value;
+
+ if (ret == NXT_OK) {
+ njs_set_invalid(&prop->value);
+ }
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
njs_unit_test_header_external(njs_vm_t *vm, njs_value_t *value, void *obj,
uintptr_t data)
{
@@ -10060,7 +10394,7 @@ static njs_external_t njs_unit_test_r_e
NULL,
NULL,
NULL,
- 0 },
+ offsetof(njs_unit_test_req_t, uri) },
{ nxt_string("host"),
NJS_EXTERN_PROPERTY,
@@ -10086,6 +10420,30 @@ static njs_external_t njs_unit_test_r_e
NULL,
0 },
+ { nxt_string("vars"),
+ NJS_EXTERN_OBJECT,
+ NULL,
+ 0,
+ njs_unit_test_r_get_vars,
+ njs_unit_test_r_set_vars,
+ njs_unit_test_r_del_vars,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("consts"),
+ NJS_EXTERN_OBJECT,
+ NULL,
+ 0,
+ njs_unit_test_r_get_vars,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
{ nxt_string("header"),
NJS_EXTERN_OBJECT,
NULL,
@@ -10145,23 +10503,55 @@ static njs_external_t nxt_test_external
typedef struct {
nxt_str_t name;
njs_unit_test_req_t request;
+ njs_unit_test_prop_t props[2];
} njs_unit_test_req_t_init_t;
static const njs_unit_test_req_t_init_t nxt_test_requests[] = {
- { nxt_string("$r"), {.uri = nxt_string("АБВ"), .a = 1}},
- { nxt_string("$r2"), {.uri = nxt_string("αβγ"), .a = 2}},
- { nxt_string("$r3"), {.uri = nxt_string("abc"), .a = 3}},
+
+ { nxt_string("$r"),
+ {
+ .uri = nxt_string("АБВ"),
+ .a = 1
+ },
+ {
+ { njs_string("p"), njs_string("pval") },
+ { njs_string("p2"), njs_string("p2val") },
+ }
+ },
+
+ { nxt_string("$r2"),
+ {
+ .uri = nxt_string("αβγ"),
+ .a = 2
+ },
+ {
+ { njs_string("q"), njs_string("qval") },
+ { njs_string("q2"), njs_string("q2val") },
+ }
+ },
+
+ { nxt_string("$r3"),
+ {
+ .uri = nxt_string("abc"),
+ .a = 3
+ },
+ {
+ { njs_string("k"), njs_string("kval") },
+ { njs_string("k2"), njs_string("k2val") },
+ }
+ },
};
static nxt_int_t
njs_externals_init(njs_vm_t *vm)
{
- nxt_int_t ret;
- nxt_uint_t i;
- const njs_extern_t *proto;
- njs_unit_test_req_t *requests;
+ nxt_int_t ret;
+ nxt_uint_t i, j;
+ const njs_extern_t *proto;
+ njs_unit_test_req_t *requests;
+ njs_unit_test_prop_t *prop;
proto = njs_vm_external_prototype(vm, &nxt_test_external[0]);
if (proto == NULL) {
@@ -10195,6 +10585,23 @@ njs_externals_init(njs_vm_t *vm)
printf("njs_vm_external_bind() failed\n");
return NXT_ERROR;
}
+
+ for (j = 0; j < nxt_nitems(nxt_test_requests[i].props); j++) {
+ prop = lvlhsh_unit_test_alloc(vm->mem_cache_pool,
+ &nxt_test_requests[i].props[j].name,
+ &nxt_test_requests[i].props[j].value);
+
+ if (prop == NULL) {
+ printf("lvlhsh_unit_test_alloc() failed\n");
+ return NXT_ERROR;
+ }
+
+ ret = lvlhsh_unit_test_add(&requests[i], prop);
+ if (ret != NXT_OK) {
+ printf("lvlhsh_unit_test_add() failed\n");
+ return NXT_ERROR;
+ }
+ }
}
return NXT_OK;
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1537449688 -10800
# Thu Sep 20 16:21:28 2018 +0300
# Node ID b85a69fb2ee5419594cd84e47bdb5ac505444e30
# Parent d80d5490da4d3bed45001b90e8d73bf7085ae4e8
Fixed Object.prototype.hasOwnProperty() for non-object properties.
This fixes #9 issue on Github.
diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -2053,45 +2053,38 @@ static njs_ret_t
njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused)
{
- uint32_t index;
- nxt_int_t ret;
- njs_array_t *array;
- const njs_value_t *value, *prop, *retval;
- nxt_lvlhsh_query_t lhq;
-
- retval = &njs_value_false;
- value = &args[0];
-
- if (njs_is_object(value)) {
-
- prop = njs_arg(args, nargs, 1);
-
- if (njs_is_array(value)) {
- array = value->data.u.array;
- index = njs_string_to_index(prop);
-
- if (index < array->length && njs_is_valid(&array->start[index])) {
- retval = &njs_value_true;
- goto done;
- }
- }
-
- njs_string_get(prop, &lhq.key);
- lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
- lhq.proto = &njs_object_hash_proto;
-
- ret = nxt_lvlhsh_find(&value->data.u.object->hash, &lhq);
-
- if (ret == NXT_OK) {
- retval = &njs_value_true;
- }
+ nxt_int_t ret;
+ const njs_value_t *value, *property;
+ njs_property_query_t pq;
+
+ value = njs_arg(args, nargs, 0);
+
+ 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;
}
-done:
-
- vm->retval = *retval;
-
- return NXT_OK;
+ property = njs_arg(args, nargs, 1);
+
+ 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:
+ vm->retval = njs_value_true;
+ return NXT_OK;
+
+ case NXT_DECLINED:
+ vm->retval = njs_value_false;
+ return NXT_OK;
+
+ case NJS_TRAP:
+ case NXT_ERROR:
+ default:
+ return ret;
+ }
}
diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c
+++ b/njs/test/njs_unit_test.c
@@ -3611,6 +3611,15 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("'abc'.length"),
nxt_string("3") },
+ { nxt_string("''.hasOwnProperty('length')"),
+ nxt_string("true") },
+
+ { nxt_string("'abc'.hasOwnProperty('length')"),
+ nxt_string("true") },
+
+ { nxt_string("(new String('abc')).hasOwnProperty('length')"),
+ nxt_string("true") },
+
{ nxt_string("'abc'.toUTF8().length"),
nxt_string("3") },
@@ -7025,6 +7034,21 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("[,].hasOwnProperty()"),
nxt_string("false") },
+ { nxt_string("[1,2].hasOwnProperty('len')"),
+ nxt_string("false") },
+
+ { nxt_string("[].hasOwnProperty('length')"),
+ nxt_string("true") },
+
+ { nxt_string("[1,2].hasOwnProperty('length')"),
+ nxt_string("true") },
+
+ { nxt_string("(new Array()).hasOwnProperty('length')"),
+ nxt_string("true") },
+
+ { nxt_string("(new Array(10)).hasOwnProperty('length')"),
+ nxt_string("true") },
+
{ nxt_string("Object.valueOf.hasOwnProperty()"),
nxt_string("false") },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment