Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created September 13, 2018 17:36
Show Gist options
  • Save xeioex/b44c9d876e18a1e59ecbf7f432eca974 to your computer and use it in GitHub Desktop.
Save xeioex/b44c9d876e18a1e59ecbf7f432eca974 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536859188 -10800
# Thu Sep 13 20:19:48 2018 +0300
# Node ID ebc9a6f65579c68976a54f88bdeff1248e1d0be8
# Parent c4f9a49486979285b6b508109d4988b4bc30d35d
Fixed Array.prototype.length setter.
diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -377,7 +377,7 @@ njs_array_prototype_length(njs_vm_t *vm,
njs_value_t *setval, njs_value_t *retval)
{
double num;
- int32_t size;
+ int64_t size;
uint32_t length;
njs_ret_t ret;
njs_value_t *val;
@@ -399,7 +399,7 @@ njs_array_prototype_length(njs_vm_t *vm,
return NJS_ERROR;
}
- size = (int32_t) (length - array->length);
+ size = (int64_t) length - array->length;
if (size > 0) {
ret = njs_array_expand(vm, array, 0, size);
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
@@ -2790,9 +2790,15 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("[].length = {}"),
nxt_string("RangeError: Invalid array length") },
+ { nxt_string("[].length = 2**32 - 1"),
+ nxt_string("MemoryError") },
+
{ nxt_string("[].length = 2**32"),
nxt_string("RangeError: Invalid array length") },
+ { nxt_string("[].length = 2**32 + 1"),
+ nxt_string("RangeError: Invalid array length") },
+
{ nxt_string("[].length = -1"),
nxt_string("RangeError: Invalid array length") },
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536859188 -10800
# Thu Sep 13 20:19:48 2018 +0300
# Node ID 84d4d83acd928ca222143dbba3ad48c985027262
# Parent ebc9a6f65579c68976a54f88bdeff1248e1d0be8
Fixed njs_array_alloc() for length > 2**31.
diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -117,7 +117,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t l
goto memory_error;
}
- size = length + spare;
+ size = (size_t) length + spare;
if (nxt_slow_path(size * sizeof(njs_value_t) < size)) {
goto memory_error;
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536859189 -10800
# Thu Sep 13 20:19:49 2018 +0300
# Node ID 7baa55f6e48df106e5226bea86949a64804b93d0
# Parent 84d4d83acd928ca222143dbba3ad48c985027262
Fixed njs_string_slice().
dst retval argument was ignored.
diff --git a/njs/njs_string.c b/njs/njs_string.c
--- a/njs/njs_string.c
+++ b/njs/njs_string.c
@@ -1288,7 +1288,7 @@ njs_string_slice_args(njs_slice_prop_t *
nxt_noinline njs_ret_t
njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
- const njs_string_prop_t *string, njs_slice_prop_t *slice)
+ const njs_string_prop_t *string, const njs_slice_prop_t *slice)
{
size_t size, n, length;
const u_char *p, *start, *end;
@@ -1325,10 +1325,10 @@ njs_string_slice(njs_vm_t *vm, njs_value
}
if (nxt_fast_path(size != 0)) {
- return njs_string_new(vm, &vm->retval, start, size, length);
+ return njs_string_new(vm, dst, start, size, length);
}
- vm->retval = njs_string_empty;
+ *dst = njs_string_empty;
return NXT_OK;
}
diff --git a/njs/njs_string.h b/njs/njs_string.h
--- a/njs/njs_string.h
+++ b/njs/njs_string.h
@@ -146,7 +146,7 @@ njs_ret_t njs_string_constructor(njs_vm_
nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2);
nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2);
njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
- const njs_string_prop_t *string, njs_slice_prop_t *slice);
+ const njs_string_prop_t *string, const njs_slice_prop_t *slice);
const u_char *njs_string_offset(const u_char *start, const u_char *end,
size_t index);
nxt_noinline uint32_t njs_string_index(njs_string_prop_t *string,
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860013 -10800
# Thu Sep 13 20:33:33 2018 +0300
# Node ID ffe3b161805980547f16f3ff1142fc55f3f628ca
# Parent 7baa55f6e48df106e5226bea86949a64804b93d0
Object property quering is refactored.
njs_property_query() only returns property descriptors and
does not try to invoke PROPERTY_HANDLER for
AccessorDescriptors.
njs_property_query_t.own can be used to query for an object's
OwnProperty.
njs_value_property() is introduced which corresponds to [[Get]]
method from specification.
This fixes #32 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
@@ -246,13 +246,15 @@ njs_object_property(njs_vm_t *vm, const
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_array_t *array;
njs_object_t *obj;
njs_function_t *function;
+ njs_object_prop_t *prop;
const njs_extern_t *ext_proto;
hash = nxt_djb_hash;
@@ -285,6 +287,34 @@ njs_property_query(njs_vm_t *vm, njs_pro
if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
return njs_array_property_query(vm, pq, object, index);
+
+ } else if (pq->query == NJS_PROPERTY_QUERY_GET) {
+
+ /* FIXME: length should be a DataProperty */
+
+ njs_primitive_value_to_string(vm, &pq->value, property);
+ njs_string_get(&pq->value, &pq->lhq.key);
+
+ if (nxt_slow_path(pq->lhq.key.length == 6
+ && memcmp(pq->lhq.key.start, "length", 6)
+ == 0))
+ {
+ prop = &pq->scratch;
+
+ prop->name = (njs_value_t) njs_string("length");
+
+ array = object->data.u.array;
+ njs_value_number_set(&prop->value, array->length);
+
+ prop->type = NJS_PROPERTY;
+ prop->configurable = 0;
+ prop->enumerable = 0;
+ prop->writable = 1;
+
+ pq->lhq.value = prop;
+
+ return NXT_OK;
+ }
}
} else {
@@ -381,6 +411,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 +423,49 @@ njs_object_property_query(njs_vm_t *vm,
}
}
+ proto = object;
+
do {
- pq->prototype = object;
+ pq->prototype = proto;
- ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
+ /* length and other shared properties should be Own property */
- if (ret == NXT_OK) {
- prop = pq->lhq.value;
+ if (nxt_fast_path(!pq->own || proto == object)) {
+ ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
- if (prop->type != NJS_WHITEOUT) {
+ if (ret == NXT_OK) {
+ prop = pq->lhq.value;
pq->shared = 0;
- return ret;
+ return (prop->type != NJS_WHITEOUT) ? ret : NXT_DECLINED;
}
-
- goto next;
}
if (pq->query > NJS_PROPERTY_QUERY_IN) {
- /* NXT_DECLINED */
- return ret;
+ return NXT_DECLINED;
}
- ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
+ ret = nxt_lvlhsh_find(&proto->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;
+ return NXT_DECLINED;
}
- next:
+ proto = proto->__proto__;
- object = object->__proto__;
-
- } while (object != NULL);
+ } while (proto != NULL);
if (njs_is_string(value)) {
return NJS_STRING_VALUE;
}
- /* NXT_DECLINED */
-
- return ret;
+ return NXT_DECLINED;
}
@@ -1037,12 +1047,9 @@ njs_object_get_own_property_descriptor(n
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;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
- ret = njs_property_query(vm, &pq, (njs_value_t *) value,
- (njs_value_t *) property);
+ ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
if (ret != NXT_OK) {
vm->retval = njs_value_void;
@@ -1050,6 +1057,16 @@ njs_object_get_own_property_descriptor(n
}
prop = pq.lhq.value;
+
+ if (prop->type == 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;
+ }
+ }
}
descriptor = njs_object_alloc(vm);
diff --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -51,9 +51,17 @@ typedef struct {
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 +75,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,
diff --git a/njs/njs_vm.c b/njs/njs_vm.c
--- a/njs/njs_vm.c
+++ b/njs/njs_vm.c
@@ -476,147 +476,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);
@@ -646,8 +512,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);
@@ -767,7 +632,7 @@ njs_vmcode_property_in(njs_vm_t *vm, njs
retval = &njs_value_false;
- pq.query = NJS_PROPERTY_QUERY_IN;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_IN, 0);
ret = njs_property_query(vm, &pq, object, property);
@@ -851,8 +716,7 @@ njs_vmcode_property_delete(njs_vm_t *vm,
retval = &njs_value_false;
- pq.lhq.key.length = 0;
- pq.query = NJS_PROPERTY_QUERY_DELETE;
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 0);
ret = njs_property_query(vm, &pq, object, property);
@@ -1097,12 +961,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 +976,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. */
@@ -2123,9 +1982,7 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj
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,6 +1991,16 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj
case NXT_OK:
prop = pq.lhq.value;
+ if (prop->type == 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;
+ }
+ }
+
ret = njs_function_frame_create(vm, &prop->value, object, method->nargs,
method->code.ctor);
break;
@@ -3159,6 +3026,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 +3163,165 @@ 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;
+ void *obj;
+ int32_t index;
+ uintptr_t data;
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_value_t *val, ext_val;
+ njs_slice_prop_t slice;
+ njs_string_prop_t string;
+ njs_object_prop_t *prop;
+ const njs_extern_t *ext_proto;
+ 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, "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, value);
+
+ if (slice.start < slice.string_length) {
+ /*
+ * A single codepoint string fits in retval
+ * so the function cannot fail.
+ */
+ (void) njs_string_slice(vm, retval, &string, &slice);
+ }
+ }
+
+ break;
+
+ case NJS_ARRAY_VALUE:
+ val = pq.lhq.value;
+
+ if (njs_is_valid(val)) {
+ *retval = *val;
+ }
+
+ break;
+
+ case NJS_EXTERNAL_VALUE:
+ ext_proto = value->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 = value->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;
+ }
+
+ if (ext_proto->get != NULL) {
+ obj = njs_extern_object(vm, value);
+
+ ret = ext_proto->get(vm, retval, obj, data);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ if (ext_proto->type == NJS_EXTERN_METHOD) {
+ retval->data.u.function = ext_proto->function;
+ retval->type = NJS_FUNCTION;
+ retval->data.truth = 1;
+ }
+
+ 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/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
@@ -7001,9 +7001,18 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Object.getOwnPropertyDescriptor([3,4], 1).value"),
nxt_string("4") },
+ { nxt_string("Object.getOwnPropertyDescriptor(Object.create({a:1}), 'a')"),
+ nxt_string("undefined") },
+
{ nxt_string("Object.getOwnPropertyDescriptor([], 'length').value"),
nxt_string("0") },
+ { nxt_string("Object.getOwnPropertyDescriptor(Array.of, 'length').value"),
+ nxt_string("0") },
+
+ { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor(new String('abc'), 'length'))"),
+ nxt_string("{\"value\":3,\"configurable\":false,\"enumerable\":false,\"writable\":false}") },
+
{ nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor([3,4], 'length'))"),
nxt_string("{\"value\":2,\"configurable\":false,\"enumerable\":false,\"writable\":true}") },
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860015 -10800
# Thu Sep 13 20:33:35 2018 +0300
# Node ID b9a1326c940d11f303c3d6328dcd1557d20d507f
# Parent ffe3b161805980547f16f3ff1142fc55f3f628ca
Fixed delete operator.
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860015 -10800
# Thu Sep 13 20:33:35 2018 +0300
# Node ID b85d3a6cf1eafd0f6d8b43d61f8e6421282a55ef
# Parent b9a1326c940d11f303c3d6328dcd1557d20d507f
Added njs_primitive_value_to_number().
diff --git a/njs/njs_number.c b/njs/njs_number.c
--- a/njs/njs_number.c
+++ b/njs/njs_number.c
@@ -64,6 +64,24 @@ njs_value_to_index(const njs_value_t *va
double
+njs_primitive_value_to_number(const njs_value_t *value)
+{
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ return value->data.u.number;
+ }
+
+ return njs_string_to_number(value, 1);
+}
+
+
+uint32_t
+njs_primitive_value_to_uint32(const njs_value_t *value)
+{
+ return njs_number_to_integer(njs_primitive_value_to_number(value));
+}
+
+
+double
njs_number_dec_parse(const u_char **start, const u_char *end)
{
return nxt_strtod(start, end);
diff --git a/njs/njs_number.h b/njs/njs_number.h
--- a/njs/njs_number.h
+++ b/njs/njs_number.h
@@ -13,6 +13,8 @@
uint32_t njs_value_to_index(const njs_value_t *value);
+double njs_primitive_value_to_number(const njs_value_t *value);
+uint32_t njs_primitive_value_to_uint32(const njs_value_t *value);
double njs_number_dec_parse(const u_char **start, const u_char *end);
uint64_t njs_number_oct_parse(const u_char **start, const u_char *end);
uint64_t njs_number_bin_parse(const u_char **start, const u_char *end);
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860016 -10800
# Thu Sep 13 20:33:36 2018 +0300
# Node ID b87565896b6efc8ac59da011665ed1acbc23a1e6
# Parent b85d3a6cf1eafd0f6d8b43d61f8e6421282a55ef
Fixed String.slice() for undefined arguments.
diff --git a/njs/njs_string.c b/njs/njs_string.c
--- a/njs/njs_string.c
+++ b/njs/njs_string.c
@@ -1239,45 +1239,43 @@ njs_string_slice_args(njs_slice_prop_t *
ssize_t start, end, length;
length = slice->string_length;
- start = 0;
-
- if (nargs > 1) {
- start = args[1].data.u.number;
+
+ start = njs_arg(args, nargs, 1)->data.u.number;
+
+ if (start < 0) {
+ start += length;
if (start < 0) {
- start += length;
-
- if (start < 0) {
- start = 0;
- }
+ start = 0;
}
-
- if (start >= length) {
- start = 0;
- length = 0;
+ }
+
+ if (start >= length) {
+ start = 0;
+ length = 0;
+
+ } else {
+ if (!njs_is_void(njs_arg(args, nargs, 2))) {
+ end = njs_arg(args, nargs, 2)->data.u.number;
} else {
end = length;
-
- if (nargs > 2) {
- end = args[2].data.u.number;
-
- if (end < 0) {
- end += length;
- }
+ }
+
+ if (end < 0) {
+ end += length;
+ }
+
+ if (length >= end) {
+ length = end - start;
+
+ if (length < 0) {
+ start = 0;
+ length = 0;
}
- if (length >= end) {
- length = end - start;
-
- if (length < 0) {
- start = 0;
- length = 0;
- }
-
- } else {
- length -= start;
- }
+ } else {
+ length -= start;
}
}
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
@@ -3800,6 +3800,18 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("'abcdefgh'.slice(3)"),
nxt_string("defgh") },
+ { nxt_string("'abcdefgh'.slice(undefined, undefined)"),
+ nxt_string("abcdefgh") },
+
+ { nxt_string("'abcdefgh'.slice(undefined)"),
+ nxt_string("abcdefgh") },
+
+ { nxt_string("'abcdefgh'.slice(undefined, 1)"),
+ nxt_string("a") },
+
+ { nxt_string("'abcdefgh'.slice(3, undefined)"),
+ nxt_string("defgh") },
+
{ nxt_string("'abcde'.slice(50)"),
nxt_string("") },
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860016 -10800
# Thu Sep 13 20:33:36 2018 +0300
# Node ID 96e1d261f0b2c36a797c139b4fb689fe535d0ea8
# Parent b87565896b6efc8ac59da011665ed1acbc23a1e6
Added njs_string_slice_string_prop().
diff --git a/njs/njs_string.c b/njs/njs_string.c
--- a/njs/njs_string.c
+++ b/njs/njs_string.c
@@ -1284,46 +1284,60 @@ njs_string_slice_args(njs_slice_prop_t *
}
+nxt_noinline void
+njs_string_slice_string_prop(njs_string_prop_t *dst,
+ const njs_string_prop_t *string, const njs_slice_prop_t *slice)
+{
+ size_t size, n, length;
+ const u_char *p, *start, *end;
+
+ length = slice->length;
+ start = string->start;
+
+ if (string->size == slice->string_length) {
+ /* Byte or ASCII string. */
+ start += slice->start;
+ size = slice->length;
+
+ if (string->length == 0) {
+ /* Byte string. */
+ length = 0;
+ }
+
+ } else {
+ /* UTF-8 string. */
+ end = start + string->size;
+ start = njs_string_offset(start, end, slice->start);
+
+ /* Evaluate size of the slice in bytes and ajdust length. */
+ p = start;
+ n = length;
+
+ do {
+ p = nxt_utf8_next(p, end);
+ n--;
+ } while (n != 0 && p < end);
+
+ size = p - start;
+ length -= n;
+ }
+
+ dst->start = (u_char *) start;
+ dst->length = length;
+ dst->size = size;
+}
+
+
nxt_noinline njs_ret_t
njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
const njs_string_prop_t *string, const njs_slice_prop_t *slice)
{
- size_t size, n, length;
- const u_char *p, *start, *end;
-
- length = slice->length;
- start = string->start;
-
- if (string->size == slice->string_length) {
- /* Byte or ASCII string. */
- start += slice->start;
- size = slice->length;
-
- if (string->length == 0) {
- /* Byte string. */
- length = 0;
- }
-
- } else {
- /* UTF-8 string. */
- end = start + string->size;
- start = njs_string_offset(start, end, slice->start);
-
- /* Evaluate size of the slice in bytes and ajdust length. */
- p = start;
- n = length;
-
- do {
- p = nxt_utf8_next(p, end);
- n--;
- } while (n != 0 && p < end);
-
- size = p - start;
- length -= n;
- }
-
- if (nxt_fast_path(size != 0)) {
- return njs_string_new(vm, dst, start, size, length);
+ njs_string_prop_t prop;
+
+ njs_string_slice_string_prop(&prop, string, slice);
+
+ if (nxt_fast_path(prop.size != 0)) {
+ return njs_string_new(vm, dst, prop.start, prop.size, prop.length);
}
*dst = njs_string_empty;
diff --git a/njs/njs_string.h b/njs/njs_string.h
--- a/njs/njs_string.h
+++ b/njs/njs_string.h
@@ -145,6 +145,8 @@ njs_ret_t njs_string_constructor(njs_vm_
nxt_uint_t nargs, njs_index_t unused);
nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2);
nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2);
+nxt_noinline void njs_string_slice_string_prop(njs_string_prop_t *dst,
+ const njs_string_prop_t *string, const njs_slice_prop_t *slice);
njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
const njs_string_prop_t *string, const njs_slice_prop_t *slice);
const u_char *njs_string_offset(const u_char *start, const u_char *end,
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860017 -10800
# Thu Sep 13 20:33:37 2018 +0300
# Node ID 0b601d903078774410f9c44ee3554c7e9b7db6ee
# Parent 96e1d261f0b2c36a797c139b4fb689fe535d0ea8
Hanling non-array values properly in Array.prototype.slice.
diff --git a/njs/njs_array.c b/njs/njs_array.c
--- a/njs/njs_array.c
+++ b/njs/njs_array.c
@@ -9,6 +9,19 @@
typedef struct {
+ union {
+ njs_continuation_t cont;
+ u_char padding[NJS_CONTINUATION_SIZE];
+ } u;
+ /*
+ * This retval value must be aligned so the continuation is padded
+ * to aligned size.
+ */
+ njs_value_t length;
+} njs_array_slice_t;
+
+
+typedef struct {
njs_continuation_t cont;
njs_value_t *values;
uint32_t max;
@@ -67,6 +80,8 @@ typedef struct {
} njs_array_sort_t;
+static njs_ret_t njs_array_prototype_slice_continuation(njs_vm_t *vm,
+ njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
static njs_ret_t njs_array_prototype_to_string_continuation(njs_vm_t *vm,
njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm,
@@ -434,55 +449,87 @@ static njs_ret_t
njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
{
- int32_t start, end, length;
- uint32_t n;
- njs_array_t *array;
- njs_value_t *value;
+ njs_ret_t ret;
+ njs_array_slice_t *slice;
+
+ static const njs_value_t njs_string_length = njs_string("length");
+
+ slice = njs_vm_continuation(vm);
+ slice->u.cont.function = njs_array_prototype_slice_continuation;
+
+ ret = njs_value_property(vm, &args[0], &njs_string_length, &slice->length);
+ if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) {
+ return ret;
+ }
+
+ return njs_array_prototype_slice_continuation(vm, args, nargs, unused);
+}
+
+
+static njs_ret_t
+njs_array_prototype_slice_continuation(njs_vm_t *vm, njs_value_t *args,
+ nxt_uint_t nargs, njs_index_t unused)
+{
+ size_t size;
+ u_char *dst;
+ int32_t start, end;
+ int64_t length;
+ uint32_t n;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_value_t *value, name, retval, *this;
+ const u_char *src, *last;
+ njs_slice_prop_t string_slice;
+ njs_array_slice_t *slice;
+ njs_string_prop_t string;
start = 0;
- length = 0;
-
- if (njs_is_array(&args[0])) {
- length = args[0].data.u.array->length;
-
- if (nargs > 1) {
- start = args[1].data.u.number;
-
- if (start < 0) {
- start += length;
-
- if (start < 0) {
- start = 0;
- }
- }
-
- if (start >= length) {
+
+ slice = njs_vm_continuation(vm);
+
+ if (nxt_slow_path(!njs_is_primitive(&slice->length))) {
+ njs_vm_trap_value(vm, &slice->length);
+ return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+ }
+
+ length = njs_primitive_value_to_uint32(&slice->length);
+
+ start = njs_arg(args, nargs, 1)->data.u.number;
+
+ if (start < 0) {
+ start += length;
+
+ if (start < 0) {
+ start = 0;
+ }
+ }
+
+ if (start >= length) {
+ start = 0;
+ length = 0;
+
+ } else {
+ if (!njs_is_void(njs_arg(args, nargs, 2))) {
+ end = njs_arg(args, nargs, 2)->data.u.number;
+
+ } else {
+ end = length;
+ }
+
+ if (end < 0) {
+ end += length;
+ }
+
+ if (length >= end) {
+ length = end - start;
+
+ if (length < 0) {
start = 0;
length = 0;
-
- } else {
- end = length;
-
- if (nargs > 2) {
- end = args[2].data.u.number;
-
- if (end < 0) {
- end += length;
- }
- }
-
- if (length >= end) {
- length = end - start;
-
- if (length < 0) {
- start = 0;
- length = 0;
- }
-
- } else {
- length -= start;
- }
}
+
+ } else {
+ length -= start;
}
}
@@ -496,14 +543,56 @@ njs_array_prototype_slice(njs_vm_t *vm,
vm->retval.data.truth = 1;
if (length != 0) {
- value = args[0].data.u.array->start;
n = 0;
-
- do {
- /* GC: retain long string and object in values[start]. */
- array->start[n++] = value[start++];
- length--;
- } while (length != 0);
+ this = &args[0];
+
+ if (nxt_fast_path(njs_is_array(this))) {
+ value = this->data.u.array->start;
+
+ do {
+ /* GC: retain long string and object in values[start]. */
+ array->start[n++] = value[start++];
+ length--;
+ } while (length != 0);
+
+ } else if (njs_is_string(this) || this->type == NJS_OBJECT_STRING) {
+
+ if (this->type == NJS_OBJECT_STRING) {
+ this = &this->data.u.object_value->value;
+ }
+
+ string_slice.start = start;
+ string_slice.length = length;
+ string_slice.string_length = njs_string_prop(&string, this);
+
+ njs_string_slice_string_prop(&string, &string, &string_slice);
+
+ src = string.start;
+ last = src + string.size;
+
+ do {
+ value = &array->start[n++];
+ dst = njs_string_short_start(value);
+ dst = nxt_utf8_copy(dst, &src, last);
+ size = dst - njs_string_short_start(value);
+ njs_string_short_set(value, size, (size == 1) ? 0 : 1);
+
+ length--;
+ } while (length != 0);
+
+
+ } else if (njs_is_object(this)) {
+
+ do {
+ njs_uint32_to_string(&name, start++);
+
+ ret = njs_value_property(vm, this, &name, &retval);
+
+ array->start[n++] = (ret == NXT_OK) ? retval
+ : njs_value_invalid;
+ length--;
+ } while (length != 0);
+ }
}
return NXT_OK;
@@ -2101,7 +2190,8 @@ static const njs_object_prop_t njs_arra
{
.type = NJS_METHOD,
.name = njs_string("slice"),
- .value = njs_native_function(njs_array_prototype_slice, 0,
+ .value = njs_native_function(njs_array_prototype_slice,
+ njs_continuation_size(njs_array_slice_t),
NJS_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
},
diff --git a/njs/njs_object_hash.h b/njs/njs_object_hash.h
--- a/njs/njs_object_hash.h
+++ b/njs/njs_object_hash.h
@@ -108,6 +108,16 @@
'j'), 'o'), 'i'), 'n')
+#define NJS_LENGTH_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'l'), 'e'), 'n'), 'g'), 't'), 'h')
+
+
#define NJS_NAME_HASH \
nxt_djb_hash_add( \
nxt_djb_hash_add( \
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
@@ -2981,12 +2981,93 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Array.prototype.slice(1,2)"),
nxt_string("") },
+ { nxt_string("Array.prototype.slice.call(undefined)"),
+ nxt_string("TypeError: cannot convert void to object") },
+
+ { nxt_string("Array.prototype.slice.call(1)"),
+ nxt_string("") },
+
+ { nxt_string("Array.prototype.slice.call(false)"),
+ nxt_string("") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:1})"),
+ nxt_string("a") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2})"),
+ nxt_string("a,b") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:4})"),
+ nxt_string("a,b,,") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1)"),
+ nxt_string("b") },
+
+ { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1, 2)"),
+ nxt_string("b") },
+
+ { nxt_string("Array.prototype.slice.call({length:'2'})"),
+ nxt_string(",") },
+
+ { nxt_string("njs.dump(Array.prototype.slice.call({length: 3, 1: undefined }))"),
+ nxt_string("[<empty>,undefined,<empty>]") },
+
+ { nxt_string("Array.prototype.slice.call({length:new Number(3)})"),
+ nxt_string(",,") },
+
+ { nxt_string("Array.prototype.slice.call({length: { valueOf: function() { return 2; } }})"),
+ nxt_string(",") },
+
+ { nxt_string("Array.prototype.slice.call({ length: Object.create(null) })"),
+ nxt_string("TypeError: Cannot convert object to primitive value") },
+
+ { nxt_string("Array.prototype.slice.call({length:-1})"),
+ nxt_string("MemoryError") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ')"),
+ nxt_string("α,β,Z,γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 1)"),
+ nxt_string("β,Z,γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 2)"),
+ nxt_string("Z,γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 3)"),
+ nxt_string("γ") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 4)"),
+ nxt_string("") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 0, 1)"),
+ nxt_string("α") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ', 1, 2)"),
+ nxt_string("β") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ').length"),
+ nxt_string("4") },
+
+ { nxt_string("Array.prototype.slice.call('αβZγ')[1].length"),
+ nxt_string("1") },
+
+ { nxt_string("Array.prototype.slice.call(new String('αβZγ'))"),
+ nxt_string("α,β,Z,γ") },
+
{ nxt_string("Array.prototype.pop()"),
nxt_string("undefined") },
{ nxt_string("Array.prototype.shift()"),
nxt_string("undefined") },
+ { nxt_string("[0,1].slice()"),
+ nxt_string("0,1") },
+
+ { nxt_string("[0,1].slice(undefined)"),
+ nxt_string("0,1") },
+
+ { nxt_string("[0,1].slice(undefined, undefined)"),
+ nxt_string("0,1") },
+
{ nxt_string("[0,1,2,3,4].slice(1,4)"),
nxt_string("1,2,3") },
@@ -9442,6 +9523,9 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("njs.dump({a:1, b:[1,,2,{c:new Boolean(1)}]})"),
nxt_string("{a:1,b:[1,<empty>,2,{c:[Boolean: true]}]}") },
+ { nxt_string("njs.dump(Array.prototype.slice.call({'1':'b', length:2}))"),
+ nxt_string("[<empty>,'b']") },
+
{ nxt_string("njs.dump($r.props)"),
nxt_string("{a:{type:\"property\",props:[\"getter\"]},b:{type:\"property\",props:[\"getter\"]}}") },
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536860117 -10800
# Thu Sep 13 20:35:17 2018 +0300
# Node ID fb2b4c6ac8b022a4530428ad744e9297a342906a
# Parent 0b601d903078774410f9c44ee3554c7e9b7db6ee
Added arguments object.
This closes #19 issue on Github.
diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c
--- a/njs/njs_disassembler.c
+++ b/njs/njs_disassembler.c
@@ -24,6 +24,8 @@ static njs_code_name_t code_names[] = {
nxt_string("OBJECT ") },
{ njs_vmcode_function, sizeof(njs_vmcode_function_t),
nxt_string("FUNCTION ") },
+ { njs_vmcode_arguments, sizeof(njs_vmcode_arguments_t),
+ nxt_string("ARGUMENTS ") },
{ njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t),
nxt_string("REGEXP ") },
{ njs_vmcode_object_copy, sizeof(njs_vmcode_object_copy_t),
diff --git a/njs/njs_function.c b/njs/njs_function.c
--- a/njs/njs_function.c
+++ b/njs/njs_function.c
@@ -8,6 +8,8 @@
#include <string.h>
+static njs_ret_t njs_function_arguments_thrower(njs_vm_t *vm,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
static njs_ret_t njs_function_activate(njs_vm_t *vm, njs_function_t *function,
njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
@@ -95,6 +97,137 @@ njs_function_value_copy(njs_vm_t *vm, nj
}
+/*
+ * ES5.1, 10.6: CreateArgumentsObject.
+ */
+njs_ret_t
+njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame)
+{
+ nxt_int_t ret;
+ nxt_uint_t nargs, n;
+ njs_value_t val, *value;
+ njs_object_t *obj;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ static const njs_value_t njs_string_length = njs_string("length");
+ static const njs_value_t njs_string_callee = njs_string("callee");
+ static const njs_value_t njs_string_caller = njs_string("caller");
+
+ obj = njs_object_alloc(vm);
+ if (nxt_slow_path(obj == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nargs = frame->nargs;
+
+ value = &val;
+ njs_value_number_set(value, nargs);
+
+ prop = njs_object_prop_alloc(vm, &njs_string_length, value, 1);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ prop->enumerable = 0;
+
+ lhq.value = prop;
+ lhq.key_hash = NJS_LENGTH_HASH;
+ njs_string_get(&prop->name, &lhq.key);
+
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NXT_ERROR;
+ }
+
+ prop = njs_object_prop_handler_alloc(vm, &njs_string_callee,
+ njs_function_arguments_thrower, 0);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ lhq.value = prop;
+ njs_string_get(&prop->name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NXT_ERROR;
+ }
+
+ prop = njs_object_prop_handler_alloc(vm, &njs_string_caller,
+ njs_function_arguments_thrower, 0);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ lhq.value = prop;
+ njs_string_get(&prop->name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NXT_ERROR;
+ }
+
+ for (n = 0; n < nargs; n++) {
+ njs_uint32_to_string(&val, n);
+
+ prop = njs_object_prop_alloc(vm, &val, &frame->arguments[n + 1], 1);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ lhq.value = prop;
+ njs_string_get(&prop->name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NXT_ERROR;
+ }
+
+ }
+
+ frame->arguments_object = obj;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_function_arguments_thrower(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *setval, njs_value_t *retval)
+{
+ njs_type_error(vm, "'caller', 'callee' properties may not be accessed");
+ return NXT_ERROR;
+}
+
+
njs_ret_t
njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
@@ -315,6 +448,7 @@ nxt_noinline njs_ret_t
njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance)
{
size_t size;
+ njs_ret_t ret;
nxt_uint_t n, nesting;
njs_frame_t *frame;
njs_value_t *value;
@@ -393,6 +527,13 @@ njs_function_call(njs_vm_t *vm, njs_inde
vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
}
+ if (lambda->arguments_object) {
+ ret = njs_function_arguments_object_init(vm, &frame->native);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
vm->active_frame = frame;
return NJS_APPLIED;
diff --git a/njs/njs_function.h b/njs/njs_function.h
--- a/njs/njs_function.h
+++ b/njs/njs_function.h
@@ -30,6 +30,8 @@ struct njs_function_lambda_s {
/* Function internal block closures levels. */
uint8_t block_closures; /* 4 bits */
+ uint8_t arguments_object;/* 1 bit */
+
/* Initial values of local scope. */
njs_value_t *local_scope;
@@ -102,7 +104,9 @@ struct njs_native_frame_s {
njs_function_t *function;
njs_native_frame_t *previous;
+
njs_value_t *arguments;
+ njs_object_t *arguments_object;
njs_exception_t exception;
@@ -147,6 +151,8 @@ struct njs_frame_s {
njs_function_t *njs_function_alloc(njs_vm_t *vm);
njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
+njs_ret_t njs_function_arguments_object_init(njs_vm_t *vm,
+ njs_native_frame_t *frame);
njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval);
njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm,
diff --git a/njs/njs_generator.c b/njs/njs_generator.c
--- a/njs/njs_generator.c
+++ b/njs/njs_generator.c
@@ -14,6 +14,8 @@ static nxt_int_t njs_generate_name(njs_v
njs_parser_node_t *node);
static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
+static nxt_int_t njs_generate_arguments_object(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
static nxt_int_t njs_generate_var_statement(njs_vm_t *vm, njs_parser_t *parser,
@@ -308,6 +310,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t
case NJS_TOKEN_CLEAR_TIMEOUT:
return njs_generate_builtin_object(vm, parser, node);
+ case NJS_TOKEN_ARGUMENTS:
+ return njs_generate_arguments_object(vm, parser, node);
+
case NJS_TOKEN_FUNCTION:
return njs_generate_function_declaration(vm, parser, node);
@@ -396,6 +401,29 @@ njs_generate_builtin_object(njs_vm_t *vm
static nxt_int_t
+njs_generate_arguments_object(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ njs_vmcode_arguments_t *gen;
+
+ parser->arguments_object = 1;
+
+ node->index = njs_generator_object_dest_index(vm, parser, node);
+ if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NXT_ERROR;
+ }
+
+ njs_generate_code(parser, njs_vmcode_arguments_t, gen);
+ gen->code.operation = njs_vmcode_arguments;
+ gen->code.operands = NJS_VMCODE_1OPERAND;
+ gen->code.retval = NJS_VMCODE_RETVAL;
+ gen->retval = node->index;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
{
@@ -2010,6 +2038,7 @@ njs_generate_function_scope(njs_vm_t *vm
parser->code_size += node->scope->argument_closures
* sizeof(njs_vmcode_move_t);
+ parser->arguments_object = 0;
ret = njs_generate_scope(vm, parser, node);
if (nxt_fast_path(ret == NXT_OK)) {
@@ -2023,6 +2052,7 @@ njs_generate_function_scope(njs_vm_t *vm
lambda->nesting = node->scope->nesting;
lambda->closure_size = size;
+ lambda->arguments_object = parser->arguments_object;
lambda->local_size = parser->scope_size;
lambda->local_scope = parser->local_scope;
diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c
--- a/njs/njs_lexer_keyword.c
+++ b/njs/njs_lexer_keyword.c
@@ -53,6 +53,7 @@ static const njs_keyword_t njs_keywords
/* Builtin objects. */
{ nxt_string("this"), NJS_TOKEN_THIS, 0 },
+ { nxt_string("arguments"), NJS_TOKEN_ARGUMENTS, 0 },
{ nxt_string("njs"), NJS_TOKEN_NJS, 0 },
{ nxt_string("Math"), NJS_TOKEN_MATH, 0 },
{ nxt_string("JSON"), NJS_TOKEN_JSON, 0 },
diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -203,6 +203,36 @@ njs_object_prop_alloc(njs_vm_t *vm, cons
nxt_noinline njs_object_prop_t *
+njs_object_prop_handler_alloc(njs_vm_t *vm, const njs_value_t *name,
+ njs_prop_handler_t handler, uint8_t attributes)
+{
+ njs_object_prop_t *prop;
+
+ prop = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_object_prop_t));
+
+ if (nxt_fast_path(prop != NULL)) {
+ /* GC: retain. */
+ njs_set_invalid(&prop->value);
+ prop->value.data.u.prop_handler = handler;
+
+ /* GC: retain. */
+ prop->name = *name;
+
+ prop->type = NJS_PROPERTY_HANDLER;
+ prop->enumerable = attributes;
+ prop->writable = attributes;
+ prop->configurable = attributes;
+ return prop;
+ }
+
+ njs_memory_error(vm);
+
+ return NULL;
+}
+
+
+nxt_noinline njs_object_prop_t *
njs_object_property(njs_vm_t *vm, const njs_object_t *object,
nxt_lvlhsh_query_t *lhq)
{
diff --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -87,6 +87,8 @@ njs_ret_t njs_object_constructor(njs_vm_
nxt_uint_t nargs, njs_index_t unused);
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);
+nxt_noinline njs_object_prop_t *njs_object_prop_handler_alloc(njs_vm_t *vm,
+ const njs_value_t *name, njs_prop_handler_t handler, uint8_t attributes);
njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
njs_value_t *setval, njs_value_t *retval);
njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value,
diff --git a/njs/njs_parser.c b/njs/njs_parser.c
--- a/njs/njs_parser.c
+++ b/njs/njs_parser.c
@@ -455,6 +455,13 @@ njs_parser_function_declaration(njs_vm_t
}
if (token != NJS_TOKEN_NAME) {
+ if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
+ njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
+ "is forbidden in function declaration",
+ (int) parser->lexer->text.length,
+ parser->lexer->text.start);
+ }
+
return NJS_TOKEN_ILLEGAL;
}
@@ -821,6 +828,13 @@ njs_parser_var_statement(njs_vm_t *vm, n
}
if (token != NJS_TOKEN_NAME) {
+ if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
+ njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
+ "is forbidden in var declaration",
+ (int) parser->lexer->text.length,
+ parser->lexer->text.start);
+ }
+
return NJS_TOKEN_ILLEGAL;
}
@@ -1306,6 +1320,13 @@ njs_parser_for_var_statement(njs_vm_t *v
}
if (token != NJS_TOKEN_NAME) {
+ if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
+ njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
+ "is forbidden in for-in var declaration",
+ (int) parser->lexer->text.length,
+ parser->lexer->text.start);
+ }
+
return NJS_TOKEN_ILLEGAL;
}
@@ -1973,6 +1994,24 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
case NJS_TOKEN_JSON:
return njs_parser_builtin_object(vm, parser, node);
+ case NJS_TOKEN_ARGUMENTS:
+ nxt_thread_log_debug("JS: arguments");
+
+ if (parser->scope->type <= NJS_SCOPE_GLOBAL) {
+ njs_parser_syntax_error(vm, parser, "\"%.*s\" object "
+ "in global scope",
+ (int) parser->lexer->text.length,
+ parser->lexer->text.start);
+
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ node->token = NJS_TOKEN_ARGUMENTS;
+
+ parser->code_size += sizeof(njs_vmcode_arguments_t);
+
+ break;
+
case NJS_TOKEN_OBJECT_CONSTRUCTOR:
node->index = NJS_INDEX_OBJECT;
break;
diff --git a/njs/njs_parser.h b/njs/njs_parser.h
--- a/njs/njs_parser.h
+++ b/njs/njs_parser.h
@@ -161,6 +161,7 @@ typedef enum {
NJS_TOKEN_THROW,
NJS_TOKEN_THIS,
+ NJS_TOKEN_ARGUMENTS,
#define NJS_TOKEN_FIRST_OBJECT NJS_TOKEN_GLOBAL_THIS
@@ -346,6 +347,8 @@ struct njs_parser_s {
u_char *code_end;
njs_parser_t *parent;
+
+ nxt_uint_t arguments_object;
};
diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c
--- a/njs/njs_parser_expression.c
+++ b/njs/njs_parser_expression.c
@@ -414,8 +414,19 @@ njs_parser_assignment_expression(njs_vm_
}
if (!njs_parser_is_lvalue(parser->node)) {
- njs_parser_ref_error(vm, parser,
- "Invalid left-hand side in assignment");
+ token = parser->node->token;
+
+ if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
+ njs_parser_syntax_error(vm, parser, "Identifier \"%s\" "
+ "is forbidden as left-hand in assignment",
+ (token == NJS_TOKEN_EVAL) ? "eval"
+ : "arguments");
+
+ } else {
+ njs_parser_ref_error(vm, parser,
+ "Invalid left-hand side in assignment");
+ }
+
return NJS_TOKEN_ILLEGAL;
}
diff --git a/njs/njs_vm.c b/njs/njs_vm.c
--- a/njs/njs_vm.c
+++ b/njs/njs_vm.c
@@ -415,6 +415,29 @@ njs_vmcode_function(njs_vm_t *vm, njs_va
njs_ret_t
+njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_ret_t ret;
+ njs_frame_t *frame;
+
+ frame = (njs_frame_t *) vm->active_frame;
+
+ if (frame->native.arguments_object == NULL) {
+ ret = njs_function_arguments_object_init(vm, &frame->native);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ vm->retval.data.u.object = frame->native.arguments_object;
+ vm->retval.type = NJS_OBJECT;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_arguments_t);
+}
+
+
+njs_ret_t
njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
{
njs_regexp_t *regexp;
diff --git a/njs/njs_vm.h b/njs/njs_vm.h
--- a/njs/njs_vm.h
+++ b/njs/njs_vm.h
@@ -644,6 +644,12 @@ typedef struct {
typedef struct {
njs_vmcode_t code;
njs_index_t retval;
+} njs_vmcode_arguments_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
uintptr_t length;
} njs_vmcode_array_t;
@@ -1112,6 +1118,8 @@ njs_ret_t njs_vmcode_array(njs_vm_t *vm,
njs_value_t *inlvd2);
njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1,
njs_value_t *invld2);
+njs_ret_t njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1,
njs_value_t *invld2);
njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
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
@@ -5574,6 +5574,77 @@ static njs_unit_test_t njs_test[] =
"var b = a(); b(2)"),
nxt_string("3") },
+ /* arguments object. */
+
+ { nxt_string("var arguments"),
+ nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") },
+
+ { nxt_string("for (var arguments in []) {}"),
+ nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in for-in var declaration in 1") },
+
+ { nxt_string("function arguments(){}"),
+ nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in function declaration in 1") },
+
+ { nxt_string("(function () {arguments = [];})"),
+ nxt_string("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") },
+
+ { nxt_string("(function(){return arguments[0];})(1,2,3)"),
+ nxt_string("1") },
+
+ { nxt_string("(function(){return arguments[2];})(1,2,3)"),
+ nxt_string("3") },
+
+ { nxt_string("(function(){return arguments[3];})(1,2,3)"),
+ nxt_string("undefined") },
+
+ { nxt_string("(function(a,b,c){return a;})(1,2,3)"),
+ nxt_string("1") },
+
+ { nxt_string("(function(a,b,c){arguments[0] = 4; return a;})(1,2,3)"),
+ nxt_string("1") },
+
+ { nxt_string("(function(a,b,c){a = 4; return arguments[0];})(1,2,3)"),
+ nxt_string("1") },
+
+ { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; "
+ "function f() {check(arguments.length > 1); return 1}; f()"),
+ nxt_string("TypeError: Too few arguments") },
+
+ { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; "
+ "function f() {check(arguments.length > 1); return 1}; f(1,2)"),
+ nxt_string("1") },
+
+ { nxt_string("(function(a,b){delete arguments[0]; return arguments[0]})(1,1)"),
+ nxt_string("undefined") },
+
+ { nxt_string("(function(){return arguments.length;})()"),
+ nxt_string("0") },
+
+ { nxt_string("(function(){return arguments.length;})(1,2,3)"),
+ nxt_string("3") },
+
+ { nxt_string("(function(){arguments.length = 1; return arguments.length;})(1,2,3)"),
+ nxt_string("1") },
+
+ { nxt_string("(function(){return arguments.callee;})()"),
+ nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") },
+
+ { nxt_string("(function(){return arguments.caller;})()"),
+ nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") },
+
+ { nxt_string("(function(){return arguments.callee;})()"),
+ nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") },
+
+ { nxt_string("function sum() { var args = Array.prototype.slice.call(arguments); "
+ "return args.reduce(function(prev, curr) {return prev + curr})};"
+ "[sum(1), sum(1,2), sum(1,2,3), sum(1,2,3,4)]"),
+ nxt_string("1,3,6,10") },
+
+ { nxt_string("function concat(sep) { var args = Array.prototype.slice.call(arguments, 1); "
+ "return args.join(sep)};"
+ "[concat('.',1,2,3), concat('+',1,2,3,4)]"),
+ nxt_string("1.2.3,1+2+3+4") },
+
/* Scopes. */
{ nxt_string("function f(x) { a = x } var a; f(5); a"),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment