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);
}
}
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);
}
}
{
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) {
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;
}
(!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;
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,
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 *
}
-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);
}
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;
}
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;
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) {
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);
{
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[] = {
}
tries = 0;
+ lhq.proto = &njs_object_hash_proto;
for ( ;; ) {
ret = NJS_ERROR;
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");
{ 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") },