#include <njs_main.h>
-static njs_int_t njs_descriptor_prop(njs_vm_t *vm,
- njs_object_prop_t *prop, const njs_value_t *desc);
+static njs_object_prop_t *njs_object_prop_alloc2(njs_vm_t *vm,
+ const njs_value_t *name, njs_object_prop_type_t type, unsigned flags);
+static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
+ const njs_value_t *name, const njs_value_t *desc);
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)
{
+ unsigned flags;
+ njs_object_prop_t *prop;
+
+ switch (attributes) {
+ case NJS_ATTRIBUTE_FALSE:
+ case NJS_ATTRIBUTE_TRUE:
+ flags = attributes ? NJS_OBJECT_PROP_VALUE_ECW : 0;
+ break;
+
+ case NJS_ATTRIBUTE_UNSET:
+ default:
+ flags = NJS_OBJECT_PROP_UNSET;
+ break;
+ }
+
+ prop = njs_object_prop_alloc2(vm, name, NJS_PROPERTY, flags);
+ if (njs_slow_path(prop == NULL)) {
+ return NULL;
+ }
+
+ njs_value_assign(njs_prop_value(prop), value);
+
+ return prop;
+}
+
+
+static njs_object_prop_t *
+njs_object_prop_alloc2(njs_vm_t *vm, const njs_value_t *name,
+ njs_object_prop_type_t type, unsigned flags)
+{
+ njs_int_t ret;
njs_object_prop_t *prop;
prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
sizeof(njs_object_prop_t));
+ if (njs_slow_path(prop == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
- if (njs_fast_path(prop != NULL)) {
- njs_value_assign(&prop->name, name);
- njs_value_assign(njs_prop_value(prop), value);
-
- prop->type = NJS_PROPERTY;
- prop->writable = attributes;
- prop->enumerable = attributes;
- prop->configurable = attributes;
+ njs_value_assign(&prop->name, name);
- return prop;
+ if (njs_slow_path(!njs_is_key(&prop->name))) {
+ ret = njs_value_to_key(vm, &prop->name, &prop->name);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
+ }
}
- njs_memory_error(vm);
+ prop->type = type;
+
+ if (flags != NJS_OBJECT_PROP_UNSET) {
+ prop->enumerable = !!(flags & NJS_OBJECT_PROP_ENUMERABLE);
+ prop->configurable = !!(flags & NJS_OBJECT_PROP_CONFIGURABLE);
+
+ if (type == NJS_PROPERTY) {
+ prop->writable = !!(flags & NJS_OBJECT_PROP_WRITABLE);
+
+ } else {
+ prop->writable = NJS_ATTRIBUTE_UNSET;
+ }
+
+ } else {
+ prop->enumerable = NJS_ATTRIBUTE_UNSET;
+ prop->configurable = NJS_ATTRIBUTE_UNSET;
+ prop->writable = NJS_ATTRIBUTE_UNSET;
+ }
- return NULL;
+ return prop;
}
njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
njs_value_t *name, njs_value_t *value, unsigned flags, uint32_t hash)
{
- uint32_t length;
+ uint32_t length, index;
njs_int_t ret;
njs_array_t *array;
njs_object_prop_t *prop, *prev;
static const njs_str_t length_key = njs_str("length");
- if (njs_slow_path(!njs_is_key(name))) {
+ if (njs_slow_path(!njs_is_index_or_key(name))) {
ret = njs_value_to_key(vm, name, name);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}
- if (njs_slow_path(njs_is_fast_array(object))) {
- array = njs_array(object);
- length = array->length;
-
- ret = njs_array_convert_to_slow_array(vm, array);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
-
- ret = njs_array_length_redefine(vm, object, length, 1);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
- }
-
again:
njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, hash, 1);
return ret;
}
- prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
- NJS_ATTRIBUTE_UNSET);
- if (njs_slow_path(prop == NULL)) {
- return NJS_ERROR;
- }
-
switch (njs_prop_type(flags)) {
case NJS_OBJECT_PROP_DESCRIPTOR:
- if (njs_descriptor_prop(vm, prop, value) != NJS_OK) {
+ prop = njs_descriptor_prop(vm, name, value);
+ if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
break;
case NJS_OBJECT_PROP_VALUE:
- njs_value_assign(njs_prop_value(prop), value);
- prop->enumerable = !!(flags & NJS_OBJECT_PROP_ENUMERABLE);
- prop->configurable = !!(flags & NJS_OBJECT_PROP_CONFIGURABLE);
- prop->writable = !!(flags & NJS_OBJECT_PROP_WRITABLE);
+ if (((flags & NJS_OBJECT_PROP_VALUE_ECW) == NJS_OBJECT_PROP_VALUE_ECW)
+ && njs_is_fast_array(object)
+ && njs_is_number(name))
+ {
+ if (njs_number_is_integer_index(njs_number(name))) {
+ array = njs_array(object);
+ index = (uint32_t) njs_number(name);
- break;
+ if (index < array->length) {
+ njs_value_assign(&array->start[index], value);
+ return NJS_OK;
+ }
+ }
+ }
- case NJS_OBJECT_PROP_GETTER:
- if (!njs_is_function(value)) {
- njs_type_error(vm, "Getter must be a function");
+ prop = njs_object_prop_alloc2(vm, name, NJS_PROPERTY,
+ flags & NJS_OBJECT_PROP_VALUE_ECW);
+ if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
- prop->type = NJS_ACCESSOR;
- njs_prop_getter(prop) = njs_function(value);
- njs_prop_setter(prop) = NJS_PROP_PTR_UNSET;
- prop->enumerable = NJS_ATTRIBUTE_TRUE;
- prop->configurable = NJS_ATTRIBUTE_TRUE;
-
+ njs_value_assign(njs_prop_value(prop), value);
break;
+ case NJS_OBJECT_PROP_GETTER:
case NJS_OBJECT_PROP_SETTER:
- if (!njs_is_function(value)) {
- njs_type_error(vm, "Setter must be a function");
+ default:
+ njs_assert(njs_is_function(value));
+
+ prop = njs_object_prop_alloc2(vm, name, NJS_ACCESSOR,
+ NJS_OBJECT_PROP_VALUE_EC);
+ if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
- prop->type = NJS_ACCESSOR;
- njs_prop_getter(prop) = NJS_PROP_PTR_UNSET;
- njs_prop_setter(prop) = njs_function(value);
- prop->enumerable = NJS_ATTRIBUTE_TRUE;
- prop->configurable = NJS_ATTRIBUTE_TRUE;
+ if (njs_prop_type(flags) == NJS_OBJECT_PROP_GETTER) {
+ njs_prop_getter(prop) = njs_function(value);
+ njs_prop_setter(prop) = NJS_PROP_PTR_UNSET;
+
+ } else {
+ njs_prop_getter(prop) = NJS_PROP_PTR_UNSET;
+ njs_prop_setter(prop) = njs_function(value);
+ }
break;
}
break;
case NJS_PROPERTY_REF:
- if (njs_is_accessor_descriptor(prop)
- || prop->configurable == NJS_ATTRIBUTE_FALSE
- || prop->enumerable == NJS_ATTRIBUTE_FALSE
- || prop->writable == NJS_ATTRIBUTE_FALSE)
+ case NJS_PROPERTY_PLACE_REF:
+ if (prev->type == NJS_PROPERTY_REF
+ && !njs_is_accessor_descriptor(prop)
+ && prop->configurable != NJS_ATTRIBUTE_FALSE
+ && prop->enumerable != NJS_ATTRIBUTE_FALSE
+ && prop->writable != NJS_ATTRIBUTE_FALSE)
{
- array = njs_array(object);
- length = array->length;
-
- ret = njs_array_convert_to_slow_array(vm, array);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ if (njs_is_valid(njs_prop_value(prop))) {
+ njs_value_assign(njs_prop_ref(prev), njs_prop_value(prop));
}
- ret = njs_array_length_redefine(vm, object, length, 1);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- goto again;
+ return NJS_OK;
}
- if (njs_is_valid(njs_prop_value(prop))) {
- njs_value_assign(njs_prop_ref(prop), njs_prop_value(prop));
+ array = njs_array(object);
+ length = array->length;
- } else {
- njs_set_undefined(njs_prop_ref(prop));
+ ret = njs_array_convert_to_slow_array(vm, array);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
- return NJS_OK;
+ ret = njs_array_length_redefine(vm, object, length, 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ flags &= ~NJS_OBJECT_PROP_CREATE;
+
+ goto again;
case NJS_PROPERTY_TYPED_ARRAY_REF:
if (njs_is_accessor_descriptor(prop)) {
done:
+ if (njs_slow_path(njs_is_fast_array(object)
+ && pq.lhq.key_hash == NJS_LENGTH_HASH)
+ && njs_strstr_eq(&pq.lhq.key, &length_key)
+ && prop->writable == NJS_ATTRIBUTE_FALSE)
+ {
+ array = njs_array(object);
+ length = array->length;
+
+ ret = njs_array_convert_to_slow_array(vm, array);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_array_length_redefine(vm, object, length, 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ goto again;
+ }
+
if (njs_is_accessor_descriptor(prop)) {
prev->type = prop->type;
}
} else {
- if (njs_is_array(object)) {
- if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
-
- if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
-
- if (prev->configurable != 1 &&
- prev->writable != 1 &&
- !njs_values_strict_equal(njs_prop_value(prev),
- njs_prop_value(prop)))
- {
- njs_type_error(vm, "Cannot redefine "
- "property: \"length\"");
- return NJS_ERROR;
- }
-
- if (prop->writable != NJS_ATTRIBUTE_UNSET) {
- prev->writable = prop->writable;
- }
-
- return njs_array_length_set(vm, object, prev,
- njs_prop_value(prop));
- }
+
+ if (njs_slow_path(njs_is_array(object)
+ && pq.lhq.key_hash == NJS_LENGTH_HASH)
+ && njs_strstr_eq(&pq.lhq.key, &length_key))
+ {
+ if (prev->configurable != NJS_ATTRIBUTE_TRUE
+ && prev->writable != NJS_ATTRIBUTE_TRUE
+ && !njs_values_strict_equal(njs_prop_value(prev),
+ njs_prop_value(prop)))
+ {
+ njs_type_error(vm, "Cannot redefine property: \"length\"");
+ return NJS_ERROR;
}
+
+ if (prop->writable != NJS_ATTRIBUTE_UNSET) {
+ prev->writable = prop->writable;
+ }
+
+ return njs_array_length_set(vm, object, prev,
+ njs_prop_value(prop));
}
njs_value_assign(njs_prop_value(prev), njs_prop_value(prop));
}
-static njs_int_t
-njs_descriptor_prop(njs_vm_t *vm, njs_object_prop_t *prop,
+static njs_object_prop_t *
+njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name,
const njs_value_t *desc)
{
njs_int_t ret;
njs_bool_t data, accessor;
njs_value_t value;
njs_function_t *getter, *setter;
+ njs_object_prop_t *prop;
njs_lvlhsh_query_t lhq;
static const njs_value_t get_string = njs_string("get");
if (!njs_is_object(desc)) {
njs_type_error(vm, "property descriptor must be an object");
- return NJS_ERROR;
+ return NULL;
+ }
+
+ prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
+ NJS_ATTRIBUTE_UNSET);
+ if (njs_slow_path(prop == NULL)) {
+ return NULL;
}
data = 0;
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return NJS_ERROR;
+ return NULL;
}
if (ret == NJS_OK) {
if (njs_is_defined(&value) && !njs_is_function(&value)) {
njs_type_error(vm, "Getter must be a function");
- return NJS_ERROR;
+ return NULL;
}
accessor = 1;
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
if (njs_is_defined(&value) && !njs_is_function(&value)) {
njs_type_error(vm, "Setter must be a function");
- return NJS_ERROR;
+ return NULL;
}
accessor = 1;
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
if (accessor && data) {
njs_type_error(vm, "Cannot both specify accessors "
"and a value or writable attribute");
- return NJS_ERROR;
+ return NULL;
}
lhq.key = njs_str_value("enumerable");
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
njs_prop_setter(prop) = setter;
}
- return NJS_OK;
+ return prop;
}
{
switch (type) {
case NJS_PROPERTY_REF:
+ case NJS_PROPERTY_PLACE_REF:
return "property_ref";
case NJS_PROPERTY_HANDLER:
"Object.defineProperty(a, 'length', {writable:true})"),
njs_str("TypeError: Cannot redefine property: \"length\"") },
+ { njs_str("var a = [0,1]; Object.defineProperty(a, 'length', {writable: false}); "
+ "Object.defineProperty(a, 'length', {value:12})"),
+ njs_str("TypeError: Cannot redefine property: \"length\"") },
+
+ { njs_str("var a = [0,1]; Object.defineProperty(a, 'length', {writable: false}); "
+ "Object.defineProperty(a, 'length', {value:2}); a.length"),
+ njs_str("2") },
+
{ njs_str ("var a =[0,1,2]; Object.defineProperty(a, 100, {value:100});"
"njs.dump(a);"),
njs_str("[0,1,2,<97 empty items>,100]") },
"Array.prototype.pop.call(a); [a.length, a[a.length - 1]]"),
njs_str("15,y") },
+ { njs_str("var a = new Array(1), arrayPrototypeGet0Calls = 0;"
+ "Object.defineProperty(Array.prototype, '0', {"
+ " get() { Object.defineProperty(a, 'length', {writable: false});"
+ " arrayPrototypeGet0Calls++;"
+ " },"
+ "});"
+ "var e = null;"
+ "try { a.pop(); } catch (ee) { e = ee.name };"
+ "[e, a.length, arrayPrototypeGet0Calls]"),
+ njs_str("TypeError,1,1") },
+
{ njs_str("[0,1].slice()"),
njs_str("0,1") },
njs_str("a,b") },
{ njs_str("Object.getOwnPropertyNames(Object.defineProperty([], 'b', {}))"),
- njs_str("length,b") },
+ njs_str("b,length") },
{ njs_str("Object.getOwnPropertyNames(Object.defineProperty(new String(), 'b', {}))"),
njs_str("b,length") },
{ njs_str("Object.defineProperty([1,2], 'a', {value:1}).a"),
njs_str("1") },
+ { njs_str("var a = []; a[0] = 101; Object.defineProperty(a, 0, {});"
+ "a[0]"),
+ njs_str("101") },
+
{ njs_str("var a = Object.freeze([1,2]);"
"Object.defineProperty(a, 'a', {value:1}).a"),
njs_str("TypeError: Cannot add property \"a\", object is not extensible") },