-
Notifications
You must be signed in to change notification settings - Fork 153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
No support for arguments object? #19
Comments
The argument object currently is not available. What is and is not supported described in http://nginx.org/en/docs/njs_about.html |
@xeioex oof, sorry I missed that. I'm trying to create an
However...
|
Hi @drsm. Probably you would want to take a look at the patch for arguments object. I am planning to add #21 rest syntax later. Arguments object is used by test262 test suite.
>>function sum() { var args = Array.prototype.slice.call(arguments); return args.reduce(function(prev, curr) {return prev + curr})}
>> sum(1)
1
>> sum(1,2,3)
6
>> function concat(sep) { var args = Array.prototype.slice.call(arguments, 1); return args.join(sep)};
>> concat('+',1,2,3,4)
'1+2+3+4' # HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1536061570 -10800
# Tue Sep 04 14:46:10 2018 +0300
# Node ID 9a4e2f805db60601268868d60cd1fc3e35687f54
# Parent 1a02c6a3bdd99d02a171571d970d21c51b5227bf
Added njs_u32_to_string().
diff --git a/njs/njs_event.c b/njs/njs_event.c
--- a/njs/njs_event.c
+++ b/njs/njs_event.c
@@ -6,7 +6,6 @@
#include <njs_core.h>
#include <string.h>
-#include <stdio.h>
static nxt_int_t njs_event_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
@@ -44,13 +43,10 @@ njs_event_hash_test(nxt_lvlhsh_query_t *
nxt_int_t
njs_add_event(njs_vm_t *vm, njs_event_t *event)
{
- size_t size;
nxt_int_t ret;
nxt_lvlhsh_query_t lhq;
- size = snprintf((char *) njs_string_short_start(&event->id),
- NJS_STRING_SHORT, "%u", vm->event_id++);
- njs_string_short_set(&event->id, size, size);
+ njs_u32_to_string(&event->id, vm->event_id++);
njs_string_get(&event->id, &lhq.key);
lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
diff --git a/njs/njs_json.c b/njs/njs_json.c
--- a/njs/njs_json.c
+++ b/njs/njs_json.c
@@ -8,7 +8,6 @@
#include <njs_json.h>
#include <njs_date.h>
#include <njs_regexp.h>
-#include <stdio.h>
#include <string.h>
@@ -1036,7 +1035,6 @@ memory_error:
static njs_ret_t
njs_json_parse_continuation_apply(njs_vm_t *vm, njs_json_parse_t *parse)
{
- size_t size;
njs_value_t arguments[3];
njs_json_state_t *state;
@@ -1053,9 +1051,7 @@ njs_json_parse_continuation_apply(njs_vm
break;
case NJS_JSON_ARRAY_START:
- size = snprintf((char *) njs_string_short_start(&arguments[1]),
- NJS_STRING_SHORT, "%u", state->index);
- njs_string_short_set(&arguments[1], size, size);
+ njs_u32_to_string(&arguments[1], state->index);
arguments[2] = state->value.data.u.array->start[state->index];
state->type = NJS_JSON_ARRAY_REPLACED;
@@ -1455,7 +1451,6 @@ static njs_ret_t
njs_json_stringify_to_json(njs_vm_t *vm, njs_json_stringify_t* stringify,
njs_function_t *function, njs_value_t *key, njs_value_t *value)
{
- size_t size;
njs_value_t arguments[2];
njs_json_state_t *state;
@@ -1482,9 +1477,7 @@ njs_json_stringify_to_json(njs_vm_t *vm,
case NJS_JSON_ARRAY_START:
case NJS_JSON_ARRAY_CONTINUE:
- size = snprintf((char *) njs_string_short_start(&arguments[1]),
- NJS_STRING_SHORT, "%u", state->index - 1);
- njs_string_short_set(&arguments[1], size, size);
+ njs_u32_to_string(&arguments[1], state->index - 1);
state->type = NJS_JSON_ARRAY_TO_JSON_REPLACED;
break;
@@ -1504,7 +1497,6 @@ static njs_ret_t
njs_json_stringify_replacer(njs_vm_t *vm, njs_json_stringify_t* stringify,
njs_value_t *key, njs_value_t *value)
{
- size_t size;
njs_value_t arguments[3];
njs_json_state_t *state;
@@ -1526,9 +1518,7 @@ njs_json_stringify_replacer(njs_vm_t *vm
case NJS_JSON_ARRAY_START:
case NJS_JSON_ARRAY_CONTINUE:
case NJS_JSON_ARRAY_TO_JSON_REPLACED:
- size = snprintf((char *) njs_string_short_start(&arguments[1]),
- NJS_STRING_SHORT, "%u", state->index - 1);
- njs_string_short_set(&arguments[1], size, size);
+ njs_u32_to_string(&arguments[1], state->index - 1);
arguments[2] = *value;
state->type = NJS_JSON_ARRAY_REPLACED;
diff --git a/njs/njs_number.h b/njs/njs_number.h
--- a/njs/njs_number.h
+++ b/njs/njs_number.h
@@ -9,6 +9,7 @@
#include <math.h>
+#include <stdio.h>
uint32_t njs_value_to_index(const njs_value_t *value);
@@ -56,6 +57,17 @@ njs_char_to_hex(u_char c)
}
+nxt_inline void
+njs_u32_to_string(njs_value_t *value, uint32_t u32)
+{
+ size_t size;
+
+ size = snprintf((char *) njs_string_short_start(value),
+ NJS_STRING_SHORT, "%u", u32);
+ njs_string_short_set(value, size, size);
+}
+
+
extern const njs_object_init_t njs_number_constructor_init;
extern const njs_object_init_t njs_number_prototype_init;
diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -5,7 +5,6 @@
*/
#include <njs_core.h>
-#include <stdio.h>
#include <string.h>
@@ -651,7 +650,6 @@ njs_object_keys(njs_vm_t *vm, njs_value_
njs_array_t *
njs_object_keys_array(njs_vm_t *vm, const njs_value_t *object)
{
- size_t size;
uint32_t i, n, keys_length, array_length;
njs_value_t *value;
njs_array_t *keys, *array;
@@ -704,9 +702,7 @@ njs_object_keys_array(njs_vm_t *vm, cons
* The maximum array index is 4294967294, so
* it can be stored as a short string inside value.
*/
- size = snprintf((char *) njs_string_short_start(value),
- NJS_STRING_SHORT, "%u", i);
- njs_string_short_set(value, size, size);
+ njs_u32_to_string(value, i);
}
}
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1536083411 -10800
# Tue Sep 04 20:50:11 2018 +0300
# Node ID 8f5eaeb2817ee7b9ca890f343ed82b2cf3acb3d0
# Parent 9a4e2f805db60601268868d60cd1fc3e35687f54
Hanling object 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
@@ -434,54 +434,83 @@ 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;
+ int32_t start, end, length;
+ uint32_t n;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_value_t *value, val;
+ //njs_slice_prop_t slice;
+ njs_string_prop_t string;
+ njs_object_prop_t *prop;
+ njs_property_query_t pq;
start = 0;
length = 0;
- if (njs_is_array(&args[0])) {
+ if (nxt_fast_path(njs_is_array(&args[0]))) {
length = args[0].data.u.array->length;
- if (nargs > 1) {
- start = args[1].data.u.number;
+ } else if (njs_is_string(&args[0])) {
+ length = njs_string_prop(&string, &args[0]);
+
+ } else if (njs_is_object(&args[0])) {
+
+ pq.query = NJS_PROPERTY_QUERY_GET;
+ pq.lhq.key = nxt_string_value("length");
+ pq.lhq.key_hash = NJS_LENGTH_HASH;
+
+ ret = njs_object_property_query(vm, &pq, &args[0],
+ args[0].data.u.object);
+
+ if (ret == NXT_OK) {
+ prop = pq.lhq.value;
+
+ if (!njs_is_primitive(&prop->value)) {
+ njs_internal_error(vm, "length prop value is not primitive",
+ (int) pq.lhq.key.length, pq.lhq.key.start);
+ return NXT_ERROR;
+ }
+
+ length = njs_primitive_value_to_uint32(&prop->value);
+ }
+ }
+
+ if (nargs > 1) {
+ start = args[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;
+
+ } else {
+ end = length;
+
+ if (nargs > 2 && !njs_is_void(&args[2])) {
+ end = args[2].data.u.number;
+
+ if (end < 0) {
+ end += length;
}
}
- if (start >= length) {
- start = 0;
- length = 0;
+ 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;
- }
+ length -= start;
}
}
}
@@ -499,11 +528,44 @@ njs_array_prototype_slice(njs_vm_t *vm,
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);
+ if (nxt_fast_path(njs_is_array(&args[0]))) {
+
+ do {
+ /* GC: retain long string and object in values[start]. */
+ array->start[n++] = value[start++];
+ length--;
+ } while (length != 0);
+
+ } else if (njs_is_string(&args[0])) {
+
+ // TODO
+ //slice.start = start;
+ //slice.length = length;
+ //
+ //ret = njs_string_slice(vm, &val, &string, &slice);
+ //if (nxt_slow_path(ret != NXT_OK)) {
+ //return ret;
+ //}
+
+
+ } else if (njs_is_object(&args[0])) {
+ pq.query = NJS_PROPERTY_QUERY_GET;
+
+ do {
+ njs_u32_to_string(&val, start++);
+ njs_string_get(&val, &pq.lhq.key);
+ pq.lhq.key_hash = nxt_djb_hash(pq.lhq.key.start,
+ pq.lhq.key.length);
+
+ ret = njs_object_property_query(vm, &pq, &args[0],
+ args[0].data.u.object);
+ prop = (ret == NXT_OK) ? pq.lhq.value : NULL;
+
+ array->start[n++] = (prop != NULL) ? prop->value
+ : njs_value_void;
+ length--;
+ } while (length != 0);
+ }
}
return NXT_OK;
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);
diff --git a/njs/njs_object.c b/njs/njs_object.c
--- a/njs/njs_object.c
+++ b/njs/njs_object.c
@@ -9,8 +9,6 @@
static nxt_int_t njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
-static njs_ret_t njs_object_property_query(njs_vm_t *vm,
- njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
static njs_ret_t njs_array_property_query(njs_vm_t *vm,
njs_property_query_t *pq, njs_value_t *object, uint32_t index);
static njs_ret_t njs_object_query_prop_handler(njs_property_query_t *pq,
diff --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -71,6 +71,8 @@ njs_object_prop_t *njs_object_property(n
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_ret_t njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+ njs_value_t *value, njs_object_t *object);
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_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
@@ -2975,12 +2975,36 @@ static njs_unit_test_t njs_test[] =
{ nxt_string("Array.prototype.slice(1,2)"),
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.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") },
# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1536083413 -10800
# Tue Sep 04 20:50:13 2018 +0300
# Node ID fe71f97513ade753199d27c347925a51ba966f3c
# Parent 8f5eaeb2817ee7b9ca890f343ed82b2cf3acb3d0
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_u32_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;
@@ -101,7 +103,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;
@@ -146,6 +150,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
@@ -201,6 +201,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)
{
@@ -398,6 +428,22 @@ njs_object_property_query(njs_vm_t *vm,
if (ret == NXT_OK) {
prop = pq->lhq.value;
+ 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;
+ }
+ }
+ }
+
if (prop->type != NJS_WHITEOUT) {
pq->shared = 0;
diff --git a/njs/njs_object.h b/njs/njs_object.h
--- a/njs/njs_object.h
+++ b/njs/njs_object.h
@@ -79,6 +79,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_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;
@@ -1114,6 +1120,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
@@ -5499,6 +5499,74 @@ 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(){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"), |
Hi @xeioex ! >> Array.prototype.slice.call({length: 3, 1: undefined })
[
undefined,
undefined,
undefined
]
>> Array.prototype.slice.call({length: -1})
Segmentation fault SyntaxError is expected here: >> (function () { arguments = []; })(1,2,3)
ReferenceError: Invalid left-hand side in assignment in 1 |
Also, this should work according to https://www.ecma-international.org/ecma-262/5.1/#sec-9.6 >> Array.prototype.slice.call({length: new Number(5) })
InternalError: length prop value is not primitive
at native (native)
at Function.prototype.call (native)
at main (native)
>> Array.prototype.slice.call({length: { valueOf: function() { return 2; } }})
InternalError: length prop value is not primitive
at native (native)
at Function.prototype.call (native)
at main (native) and fail with a TypeError only when no conversion is possible (https://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8) Array.prototype.slice.call({ length: Object.create(null) }) |
@drsm Here is the second try: https://gist.github.com/xeioex/d8a5c34c20853902ea0381b1097e8b82 It includes, among other things, fixes for all the issues you reported here. |
Hi @xeioex ! >> Array.prototype.slice.call({length: 3, 1: undefined })
[
<empty>,
<empty>,
<empty>
] also, nodejs and firefox returns an empty array here, but jjs tries to alloc: Array.prototype.slice.call({length: -1}) not sure who is right... |
Array.prototype.slice.call({length: -1}) The spec (https://www.ecma-international.org/ecma-262/5.1/#sec-9.6) says
For instance |
Good catch! Here is one more try: The patch set contain only uncommitted changes. Do not forget to pull the latest changes from the repo. |
the last version of the patch works fine for me, thanks!
Oh, I just found that the later version of the spec introduce an another abstract operation ToLength https://tc39.github.io/ecma262/#sec-tolength , ff & v8 follow it |
This will only produce a SyntaxError in strict mode code. |
Hi @rwaldron, we plan to focus on strict mode only. (http://nginx.org/en/docs/njs_about.html) |
That's awesome—good to know! |
Produces:
I expect the following output:
The text was updated successfully, but these errors were encountered: