}
-static njs_ret_t
-njs_define_property(njs_vm_t *vm, njs_object_t *object, const njs_value_t *name,
- const njs_object_t *descriptor)
+static uint8_t
+njs_descriptor_attribute(njs_vm_t *vm, const njs_object_t *descriptor,
+ nxt_lvlhsh_query_t *pq, nxt_bool_t unset)
{
- nxt_int_t ret;
- njs_object_prop_t *prop, *pr;
- nxt_lvlhsh_query_t lhq, pq;
-
- njs_string_get(name, &lhq.key);
- lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
- lhq.proto = &njs_object_hash_proto;
+ njs_object_prop_t *prop;
- ret = nxt_lvlhsh_find(&object->hash, &lhq);
+ prop = njs_object_property(vm, descriptor, pq);
+ if (prop != NULL) {
+ return prop->value.data.truth;
+ }
- if (ret != NXT_OK) {
- prop = njs_object_prop_alloc(vm, name, &njs_value_void, 0);
+ return unset ? NJS_ATTRIBUTE_UNSET : 0;
+}
- if (nxt_slow_path(prop == NULL)) {
- return NXT_ERROR;
- }
- lhq.value = prop;
+static njs_object_prop_t *
+njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name,
+ const njs_object_t *descriptor, nxt_bool_t unset)
+{
+ const njs_value_t *value;
+ njs_object_prop_t *prop, *pr;
+ nxt_lvlhsh_query_t pq;
- } else {
- prop = lhq.value;
+ value = unset ? &njs_value_invalid : &njs_value_void;
+ prop = njs_object_prop_alloc(vm, name, value, 0);
+ if (nxt_slow_path(prop == NULL)) {
+ return NULL;
}
+ pq.key = nxt_string_value("configurable");
+ pq.key_hash = NJS_CONFIGURABLE_HASH;
+ prop->configurable = njs_descriptor_attribute(vm, descriptor, &pq, unset);
+
+ pq.key = nxt_string_value("enumerable");
+ pq.key_hash = NJS_ENUMERABLE_HASH;
+ prop->enumerable = njs_descriptor_attribute(vm, descriptor, &pq, unset);
+
+ pq.key = nxt_string_value("writable");
+ pq.key_hash = NJS_WRITABABLE_HASH;
+ prop->writable = njs_descriptor_attribute(vm, descriptor, &pq, unset);
+
pq.key = nxt_string_value("value");
pq.key_hash = NJS_VALUE_HASH;
pq.proto = &njs_object_hash_proto;
pr = njs_object_property(vm, descriptor, &pq);
-
if (pr != NULL) {
prop->value = pr->value;
}
- pq.key = nxt_string_value("configurable");
- pq.key_hash = NJS_CONFIGURABLE_HASH;
+ return prop;
+}
- pr = njs_object_property(vm, descriptor, &pq);
- if (pr != NULL) {
- prop->configurable = pr->value.data.truth;
+/*
+ * ES5.1, 8.12.9: [[DefineOwnProperty]]
+ * Only data descriptors are suppored.
+ */
+static njs_ret_t
+njs_define_property(njs_vm_t *vm, njs_object_t *object, const njs_value_t *name,
+ const njs_object_t *descriptor)
+{
+ nxt_int_t ret;
+ nxt_bool_t unset;
+ njs_object_prop_t *desc, *current;
+ nxt_lvlhsh_query_t lhq;
+
+ njs_string_get(name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_find(&object->hash, &lhq);
+
+ unset = (ret == NXT_OK);
+ desc = njs_descriptor_prop(vm, name, descriptor, unset);
+ if (nxt_slow_path(desc == NULL)) {
+ return NXT_ERROR;
}
- pq.key = nxt_string_value("enumerable");
- pq.key_hash = NJS_ENUMERABLE_HASH;
+ if (nxt_fast_path(ret == NXT_DECLINED)) {
+ lhq.value = desc;
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
- pr = njs_object_property(vm, descriptor, &pq);
+ ret = nxt_lvlhsh_insert(&object->hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, NULL);
+ return NXT_ERROR;
+ }
- if (pr != NULL) {
- prop->enumerable = pr->value.data.truth;
+ return NXT_OK;
}
- pq.key = nxt_string_value("writable");
- pq.key_hash = NJS_WRITABABLE_HASH;
+ /* Updating existing prop. */
- pr = njs_object_property(vm, descriptor, &pq);
+ current = lhq.value;
- if (pr != NULL) {
- prop->writable = pr->value.data.truth;
+ if (!current->configurable) {
+ if (desc->configurable == NJS_ATTRIBUTE_TRUE) {
+ goto exception;
+ }
+
+ if (desc->enumerable != NJS_ATTRIBUTE_UNSET
+ && current->enumerable != desc->enumerable)
+ {
+ goto exception;
+ }
+
+ if (desc->writable == NJS_ATTRIBUTE_TRUE
+ && current->writable == NJS_ATTRIBUTE_FALSE)
+ {
+ goto exception;
+ }
+
+ if (njs_is_valid(&desc->value)
+ && current->writable == NJS_ATTRIBUTE_FALSE
+ && !njs_values_strict_equal(&desc->value, ¤t->value))
+ {
+ goto exception;
+ }
}
- lhq.replace = 0;
- lhq.pool = vm->mem_cache_pool;
+ if (desc->configurable != NJS_ATTRIBUTE_UNSET) {
+ current->configurable = desc->configurable;
+ }
- ret = nxt_lvlhsh_insert(&object->hash, &lhq);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
+ if (desc->enumerable != NJS_ATTRIBUTE_UNSET) {
+ current->enumerable = desc->enumerable;
+ }
+
+ if (desc->writable != NJS_ATTRIBUTE_UNSET) {
+ current->writable = desc->writable;
+ }
+
+ if (njs_is_valid(&desc->value)) {
+ current->value = desc->value;
}
return NXT_OK;
+
+exception:
+
+ njs_type_error(vm, "Cannot redefine property: '%.*s'",
+ (int) lhq.key.length, lhq.key.start);
+
+ return NXT_ERROR;
}
"Object.defineProperty(o, 'a', Object.create({value:2})); o.a"),
nxt_string("2") },
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:false});"
+ "Object.defineProperty(o, 'a', {configurable:true})"),
+ nxt_string("TypeError: Cannot redefine property: 'a'") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:false});"
+ "Object.defineProperty(o, 'a', {enumerable:true})"),
+ nxt_string("TypeError: Cannot redefine property: 'a'") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:false});"
+ "Object.defineProperty(o, 'a', {writable:true})"),
+ nxt_string("TypeError: Cannot redefine property: 'a'") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:false});"
+ "Object.defineProperty(o, 'a', {enumerable:false}).a"),
+ nxt_string("undefined") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:false});"
+ "Object.defineProperty(o, 'a', {}).a"),
+ nxt_string("undefined") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:false, writable:true});"
+ "Object.defineProperty(o, 'a', {writable:false}).a"),
+ nxt_string("undefined") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:true, writable:false});"
+ "Object.defineProperty(o, 'a', {writable:true}).a"),
+ nxt_string("undefined") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {});"
+ "Object.defineProperty(o, 'a', {}).a"),
+ nxt_string("undefined") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {value:1});"
+ "Object.defineProperty(o, 'a', {value:1}).a"),
+ nxt_string("1") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {value:1});"
+ "Object.defineProperty(o, 'a', {value:2}).a"),
+ nxt_string("TypeError: Cannot redefine property: 'a'") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', {configurable:true});"
+ "Object.defineProperty(o, 'a', {value:1}).a"),
+ nxt_string("1") },
+
+ { nxt_string("var o = {};"
+ "Object.defineProperty(o, 'a', { configurable: true, value: 0 });"
+ "Object.defineProperty(o, 'a', { value: 1 });"
+ "Object.defineProperty(o, 'a', { configurable: false, value: 2 }).a"),
+ nxt_string("2") },
+
{ nxt_string("var o = {}; Object.defineProperty()"),
nxt_string("TypeError: cannot convert void argument to object") },