[njs] Improved njs_object_prop_define() for fast-arrays.
Dmitry Volyntsev
xeioex at nginx.com
Fri Nov 11 01:55:05 UTC 2022
details: https://hg.nginx.org/njs/rev/ecf6c3e56857
branches:
changeset: 1994:ecf6c3e56857
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Thu Nov 10 17:51:32 2022 -0800
description:
Improved njs_object_prop_define() for fast-arrays.
Previously, any Object.defineProperty() for fast-arrays converted them
to slow ones. Now, it is only done when it is necessary.
diffstat:
src/njs_object.h | 7 +-
src/njs_object_prop.c | 293 +++++++++++++++++++++++++++++-----------------
src/njs_value.c | 10 +-
src/njs_value.h | 1 +
src/test/njs_unit_test.c | 25 +++-
5 files changed, 216 insertions(+), 120 deletions(-)
diffs (639 lines):
diff -r 581c321d4015 -r ecf6c3e56857 src/njs_object.h
--- a/src/njs_object.h Thu Nov 10 09:33:36 2022 -0800
+++ b/src/njs_object.h Thu Nov 10 17:51:32 2022 -0800
@@ -18,6 +18,7 @@ typedef enum {
NJS_OBJECT_PROP_ENUMERABLE = 8,
NJS_OBJECT_PROP_CONFIGURABLE = 16,
NJS_OBJECT_PROP_WRITABLE = 32,
+ NJS_OBJECT_PROP_UNSET = 64,
#define NJS_OBJECT_PROP_VALUE_ECW (NJS_OBJECT_PROP_VALUE \
| NJS_OBJECT_PROP_ENUMERABLE \
| NJS_OBJECT_PROP_CONFIGURABLE \
@@ -280,13 +281,9 @@ njs_inline njs_int_t
njs_value_create_data_prop_i64(njs_vm_t *vm, njs_value_t *value, int64_t index,
njs_value_t *setval, uint32_t hash)
{
- njs_int_t ret;
njs_value_t key;
- ret = njs_int64_to_string(vm, &key, index);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
+ njs_set_number(&key, index);
return njs_value_create_data_prop(vm, value, &key, setval, hash);
}
diff -r 581c321d4015 -r ecf6c3e56857 src/njs_object_prop.c
--- a/src/njs_object_prop.c Thu Nov 10 09:33:36 2022 -0800
+++ b/src/njs_object_prop.c Thu Nov 10 17:51:32 2022 -0800
@@ -8,34 +8,85 @@
#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_fast_path(prop != NULL)) {
- njs_value_assign(&prop->name, name);
- njs_value_assign(njs_prop_value(prop), value);
+ if (njs_slow_path(prop == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
- 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);
- return NULL;
+ } else {
+ prop->writable = NJS_ATTRIBUTE_UNSET;
+ }
+
+ } else {
+ prop->enumerable = NJS_ATTRIBUTE_UNSET;
+ prop->configurable = NJS_ATTRIBUTE_UNSET;
+ prop->writable = NJS_ATTRIBUTE_UNSET;
+ }
+
+ return prop;
}
@@ -131,7 +182,7 @@ njs_int_t
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;
@@ -139,28 +190,13 @@ njs_object_prop_define(njs_vm_t *vm, njs
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);
@@ -173,53 +209,59 @@ again:
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;
}
@@ -330,35 +372,36 @@ set_prop:
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)) {
@@ -473,6 +516,27 @@ set_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;
@@ -506,29 +570,26 @@ done:
}
} 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 (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;
- }
+ if (prop->writable != NJS_ATTRIBUTE_UNSET) {
+ prev->writable = prop->writable;
+ }
- return njs_array_length_set(vm, object, prev,
- njs_prop_value(prop));
- }
- }
+ return njs_array_length_set(vm, object, prev,
+ njs_prop_value(prop));
}
njs_value_assign(njs_prop_value(prev), njs_prop_value(prop));
@@ -652,21 +713,28 @@ njs_prop_private_copy(njs_vm_t *vm, njs_
}
-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;
@@ -678,13 +746,13 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
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;
@@ -696,13 +764,13 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
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;
@@ -714,7 +782,7 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
@@ -727,7 +795,7 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
@@ -738,7 +806,7 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
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");
@@ -746,7 +814,7 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
@@ -758,7 +826,7 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
ret = njs_object_property(vm, desc, &lhq, &value);
if (njs_slow_path(ret == NJS_ERROR)) {
- return ret;
+ return NULL;
}
if (ret == NJS_OK) {
@@ -771,7 +839,7 @@ njs_descriptor_prop(njs_vm_t *vm, njs_ob
njs_prop_setter(prop) = setter;
}
- return NJS_OK;
+ return prop;
}
@@ -983,6 +1051,7 @@ njs_prop_type_string(njs_object_prop_typ
{
switch (type) {
case NJS_PROPERTY_REF:
+ case NJS_PROPERTY_PLACE_REF:
return "property_ref";
case NJS_PROPERTY_HANDLER:
diff -r 581c321d4015 -r ecf6c3e56857 src/njs_value.c
--- a/src/njs_value.c Thu Nov 10 09:33:36 2022 -0800
+++ b/src/njs_value.c Thu Nov 10 17:51:32 2022 -0800
@@ -519,7 +519,7 @@ njs_value_is_buffer(const njs_value_t *v
* in NJS_PROPERTY_QUERY_GET
* prop->type is NJS_PROPERTY or NJS_PROPERTY_HANDLER.
* in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE
- * prop->type is NJS_PROPERTY, NJS_PROPERTY_REF,
+ * prop->type is NJS_PROPERTY, NJS_PROPERTY_REF, NJS_PROPERTY_PLACE_REF,
* NJS_PROPERTY_TYPED_ARRAY_REF or
* NJS_PROPERTY_HANDLER.
* NJS_DECLINED property was not found in object,
@@ -737,9 +737,12 @@ njs_array_property_query(njs_vm_t *vm, n
int64_t length;
uint64_t size;
njs_int_t ret;
+ njs_bool_t resized;
njs_value_t *setval, value;
njs_object_prop_t *prop;
+ resized = 0;
+
if (pq->query == NJS_PROPERTY_QUERY_SET) {
if (!array->object.extensible) {
return NJS_DECLINED;
@@ -764,6 +767,7 @@ njs_array_property_query(njs_vm_t *vm, n
}
array->length = index + 1;
+ resized = 1;
}
goto prop;
@@ -829,7 +833,7 @@ prop:
} else {
njs_prop_ref(prop) = &array->start[index];
- prop->type = NJS_PROPERTY_REF;
+ prop->type = resized ? NJS_PROPERTY_PLACE_REF : NJS_PROPERTY_REF;
}
njs_set_number(&prop->name, index);
@@ -1229,6 +1233,7 @@ slow_path:
goto found;
case NJS_PROPERTY_REF:
+ case NJS_PROPERTY_PLACE_REF:
njs_value_assign(njs_prop_ref(prop), setval);
return NJS_OK;
@@ -1385,6 +1390,7 @@ njs_value_property_delete(njs_vm_t *vm,
return njs_function_apply(vm, njs_prop_getter(prop), value, 1, removed);
case NJS_PROPERTY_REF:
+ case NJS_PROPERTY_PLACE_REF:
if (removed != NULL) {
njs_value_assign(removed, njs_prop_ref(prop));
}
diff -r 581c321d4015 -r ecf6c3e56857 src/njs_value.h
--- a/src/njs_value.h Thu Nov 10 09:33:36 2022 -0800
+++ b/src/njs_value.h Thu Nov 10 17:51:32 2022 -0800
@@ -330,6 +330,7 @@ typedef enum {
NJS_PROPERTY = 0,
NJS_ACCESSOR,
NJS_PROPERTY_REF,
+ NJS_PROPERTY_PLACE_REF,
NJS_PROPERTY_TYPED_ARRAY_REF,
NJS_PROPERTY_HANDLER,
NJS_WHITEOUT,
diff -r 581c321d4015 -r ecf6c3e56857 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Thu Nov 10 09:33:36 2022 -0800
+++ b/src/test/njs_unit_test.c Thu Nov 10 17:51:32 2022 -0800
@@ -4521,6 +4521,14 @@ static njs_unit_test_t njs_test[] =
"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]") },
@@ -4718,6 +4726,17 @@ static njs_unit_test_t njs_test[] =
"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") },
@@ -14919,7 +14938,7 @@ static njs_unit_test_t njs_test[] =
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") },
@@ -15002,6 +15021,10 @@ static njs_unit_test_t njs_test[] =
{ 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") },
More information about the nginx-devel
mailing list