Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created September 13, 2018 14:37
Show Gist options
  • Save xeioex/d8a5c34c20853902ea0381b1097e8b82 to your computer and use it in GitHub Desktop.
Save xeioex/d8a5c34c20853902ea0381b1097e8b82 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536843137 -10800
# Thu Sep 13 15:52:17 2018 +0300
# Node ID a92ae4986bc04c512d180b3e7748027514ce729a
# Parent c2cddf3b97b7874bccf569ea08731120fdf82199
Introduced sandboxing mode.
Thanks to David CARLIER.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -110,6 +110,8 @@ njs_vm_create(njs_vm_opt_t *options)
return NULL;
}
+ vm->options = *options;
+
if (options->shared != NULL) {
vm->shared = options->shared;
@@ -160,15 +162,11 @@ njs_vm_create(njs_vm_opt_t *options)
nxt_lvlhsh_init(&vm->externals_hash);
nxt_lvlhsh_init(&vm->external_prototypes_hash);
- vm->ops = options->ops;
-
vm->trace.level = NXT_LEVEL_TRACE;
vm->trace.size = 2048;
vm->trace.handler = njs_parser_trace_handler;
vm->trace.data = vm;
- vm->trailer = options->trailer;
-
if (options->backtrace) {
debug = nxt_array_create(4, sizeof(njs_function_debug_t),
&njs_array_mem_proto,
@@ -180,8 +178,7 @@ njs_vm_create(njs_vm_opt_t *options)
vm->debug = debug;
}
- vm->accumulative = options->accumulative;
- if (vm->accumulative) {
+ if (options->accumulative) {
ret = njs_vm_init(vm);
if (nxt_slow_path(ret != NXT_OK)) {
return NULL;
@@ -232,7 +229,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
return NJS_ERROR;
}
- if (vm->parser != NULL && !vm->accumulative) {
+ if (vm->parser != NULL && !vm->options.accumulative) {
return NJS_ERROR;
}
@@ -307,7 +304,7 @@ njs_vm_clone(njs_vm_t *vm, njs_external_
nxt_thread_log_debug("CLONE:");
- if (vm->accumulative) {
+ if (vm->options.accumulative) {
return NULL;
}
@@ -347,7 +344,7 @@ njs_vm_clone(njs_vm_t *vm, njs_external_
nvm->external_objects = externals;
- nvm->ops = vm->ops;
+ nvm->options = vm->options;
nvm->current = vm->current;
diff --git a/njs/njs.h b/njs/njs.h
--- a/njs/njs.h
+++ b/njs/njs.h
@@ -145,6 +145,7 @@ typedef struct {
uint8_t trailer; /* 1 bit */
uint8_t accumulative; /* 1 bit */
uint8_t backtrace; /* 1 bit */
+ uint8_t sandbox; /* 1 bit */
} njs_vm_opt_t;
diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c
--- a/njs/njs_builtin.c
+++ b/njs/njs_builtin.c
@@ -256,6 +256,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
lhq.pool = vm->mem_cache_pool;
for (i = NJS_MODULE_FS; i < NJS_MODULE_MAX; i++) {
+ if (vm->options.sandbox && !njs_sandbox_module(i)) {
+ continue;
+ }
+
module = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_module_t));
if (nxt_slow_path(module == NULL)) {
return NJS_ERROR;
diff --git a/njs/njs_generator.c b/njs/njs_generator.c
--- a/njs/njs_generator.c
+++ b/njs/njs_generator.c
@@ -2558,7 +2558,7 @@ njs_generator_temp_index_get(njs_vm_t *v
scope = scope->parent;
}
- if (vm->accumulative && scope->type == NJS_SCOPE_GLOBAL) {
+ if (vm->options.accumulative && scope->type == NJS_SCOPE_GLOBAL) {
/*
* When non-clonable VM runs in accumulative mode
* all global variables are allocated in absolute scope
diff --git a/njs/njs_module.c b/njs/njs_module.c
--- a/njs/njs_module.c
+++ b/njs/njs_module.c
@@ -74,3 +74,19 @@ const njs_object_init_t njs_require_fun
NULL,
0,
};
+
+
+nxt_bool_t
+njs_sandbox_module(enum njs_module_e module)
+{
+ switch (module) {
+ case NJS_MODULE_CRYPTO:
+ return 1;
+
+ case NJS_MODULE_FS:
+ default:
+ break;
+ }
+
+ return 0;
+}
diff --git a/njs/njs_module.h b/njs/njs_module.h
--- a/njs/njs_module.h
+++ b/njs/njs_module.h
@@ -15,6 +15,7 @@ typedef struct {
njs_ret_t njs_module_require(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
+nxt_bool_t njs_sandbox_module(enum njs_module_e module);
extern const nxt_lvlhsh_proto_t njs_modules_hash_proto;
diff --git a/njs/njs_parser.c b/njs/njs_parser.c
--- a/njs/njs_parser.c
+++ b/njs/njs_parser.c
@@ -134,7 +134,7 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p
return NULL;
}
- if (token == NJS_TOKEN_CLOSE_BRACE && vm->trailer) {
+ if (token == NJS_TOKEN_CLOSE_BRACE && vm->options.trailer) {
parser->lexer->start--;
break;
}
@@ -351,7 +351,7 @@ njs_parser_statement(njs_vm_t *vm, njs_p
return njs_parser_block_statement(vm, parser);
case NJS_TOKEN_CLOSE_BRACE:
- if (vm->trailer) {
+ if (vm->options.trailer) {
parser->node = NULL;
nxt_thread_log_debug("BLOCK END");
return token;
diff --git a/njs/njs_shell.c b/njs/njs_shell.c
--- a/njs/njs_shell.c
+++ b/njs/njs_shell.c
@@ -31,6 +31,7 @@ typedef struct {
nxt_int_t version;
nxt_int_t disassemble;
nxt_int_t interactive;
+ nxt_int_t sandbox;
} njs_opts_t;
@@ -146,6 +147,7 @@ main(int argc, char **argv)
vm_options.accumulative = 1;
vm_options.backtrace = 1;
+ vm_options.sandbox = opts.sandbox;
if (opts.interactive) {
ret = njs_interactive_shell(&opts, &vm_options);
@@ -170,6 +172,7 @@ njs_get_options(njs_opts_t *opts, int ar
"Options:\n"
" -v print njs version and exit.\n"
" -d print disassembled code.\n"
+ " -s sandbox mode.\n"
" <filename> | - run code from a file or stdin.\n";
ret = NXT_DONE;
@@ -201,6 +204,10 @@ njs_get_options(njs_opts_t *opts, int ar
opts->version = 1;
break;
+ case 's':
+ opts->sandbox = 1;
+ break;
+
default:
fprintf(stderr, "Unknown argument: \"%s\" "
"try \"%s -h\" for available options\n", argv[i], argv[0]);
diff --git a/njs/njs_time.c b/njs/njs_time.c
--- a/njs/njs_time.c
+++ b/njs/njs_time.c
@@ -28,7 +28,7 @@ njs_set_timeout(njs_vm_t *vm, njs_value_
return NJS_ERROR;
}
- ops = vm->ops;
+ ops = vm->options.ops;
if (nxt_slow_path(ops == NULL)) {
njs_internal_error(vm, "not supported by host environment");
return NJS_ERROR;
diff --git a/njs/njs_variable.c b/njs/njs_variable.c
--- a/njs/njs_variable.c
+++ b/njs/njs_variable.c
@@ -123,7 +123,7 @@ njs_variable_add(njs_vm_t *vm, njs_parse
return var;
}
- lhq.replace = vm->accumulative;
+ lhq.replace = vm->options.accumulative;
lhq.value = var;
lhq.pool = vm->mem_cache_pool;
@@ -389,7 +389,7 @@ njs_variable_get(njs_vm_t *vm, njs_parse
goto not_found;
}
- if (vm->accumulative && vs.scope->type == NJS_SCOPE_GLOBAL) {
+ if (vm->options.accumulative && vs.scope->type == NJS_SCOPE_GLOBAL) {
/*
* When non-clonable VM runs in accumulative mode all
* global variables should be allocated in absolute scope
diff --git a/njs/njs_vm.h b/njs/njs_vm.h
--- a/njs/njs_vm.h
+++ b/njs/njs_vm.h
@@ -1036,7 +1036,7 @@ struct njs_vm_s {
nxt_lvlhsh_t events_hash;
nxt_queue_t posted_events;
- njs_vm_ops_t *ops;
+ njs_vm_opt_t options;
/*
* The prototypes and constructors arrays must be together because
@@ -1073,8 +1073,6 @@ struct njs_vm_s {
nxt_array_t *backtrace;
njs_trap_t trap:8;
- uint8_t trailer; /* 1 bit */
- uint8_t accumulative; /* 1 bit */
};
diff --git a/njs/test/njs_expect_test.exp b/njs/test/njs_expect_test.exp
--- a/njs/test/njs_expect_test.exp
+++ b/njs/test/njs_expect_test.exp
@@ -3,8 +3,15 @@
# Copyright (C) NGINX, Inc.
#
-proc njs_test {body} {
- spawn -nottycopy njs
+proc njs_test {body {opts ""}} {
+
+ if {$opts eq ""} {
+ spawn -nottycopy njs
+
+ } else {
+ spawn -nottycopy njs $opts
+ }
+
expect -re "interactive njs \\d+\.\\d+\.\\d+\r\n\r"
expect "v.<Tab> -> the properties and prototype methods of v.\r
type console.help() for more information\r
@@ -23,6 +30,12 @@ type console.help() for more information
expect eof
}
+proc njs_run {opts output} {
+ spawn -nottycopy njs $opts
+ expect -re $output
+ expect eof
+}
+
njs_test {
{"njs.version\r\n"
"njs.version\r\n\*\.\*\.\*"}
@@ -490,3 +503,23 @@ njs_test {
{"fs.readFileSync('njs_test_file2')\r\n"
"'ABCABC'\r\n>> "}
}
+
+# CLI OPTIONS
+
+# version
+
+njs_run "-v" "\\d+\.\\d+\.\\d+"
+
+# disassemble
+
+njs_test {
+ {"1+1\r\n"
+ "00000 ADD*\r\n00040 STOP*\r\n\r\n2"}
+} "-d"
+
+# sandboxing
+
+njs_test {
+ {"var fs = require('fs')\r\n"
+ "Error: Cannot find module 'fs'\r\n"}
+} "-s"
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1536843358 -10800
# Thu Sep 13 15:55:58 2018 +0300
# Node ID 9ed020dadadb4341471af6b65ed5dd1dfef7fc05
# Parent a92ae4986bc04c512d180b3e7748027514ce729a
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 <[email protected]>
# Date 1536843359 -10800
# Thu Sep 13 15:55:59 2018 +0300
# Node ID 45467a0d45acf3a913ff1cd2b728e89b52c4ffe6
# Parent 9ed020dadadb4341471af6b65ed5dd1dfef7fc05
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 1536843359 -10800
# Thu Sep 13 15:55:59 2018 +0300
# Node ID 8c535b80e025e6e2fba30ed17fe82040585ec363
# Parent 45467a0d45acf3a913ff1cd2b728e89b52c4ffe6
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 1536843359 -10800
# Thu Sep 13 15:55:59 2018 +0300
# Node ID 9751de05d4d40bec57e3fe4f6a08438b93ebb2bf
# Parent 8c535b80e025e6e2fba30ed17fe82040585ec363
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 1536849241 -10800
# Thu Sep 13 17:34:01 2018 +0300
# Node ID e2084d6c90ecb6f57f3433639b36e578f923be1d
# Parent 9751de05d4d40bec57e3fe4f6a08438b93ebb2bf
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,60 @@ 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);
+ /* FIXME: length and other shared properties should be Own property */
+
+ if (nxt_fast_path(!pq->own || proto == object)) {
+ ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
+
+ if (ret == NXT_OK) {
+ prop = pq->lhq.value;
+
+ if (prop->type != NJS_WHITEOUT) {
+ pq->shared = 0;
+
+ return ret;
+ }
+
+ goto next;
+ }
+ }
+
+ if (pq->query > NJS_PROPERTY_QUERY_IN) {
+ return NXT_DECLINED;
+ }
+
+ ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
if (ret == NXT_OK) {
prop = pq->lhq.value;
if (prop->type != NJS_WHITEOUT) {
- pq->shared = 0;
+ pq->shared = 1;
return ret;
}
-
- goto next;
}
if (pq->query > NJS_PROPERTY_QUERY_IN) {
- /* NXT_DECLINED */
- return ret;
- }
-
- ret = nxt_lvlhsh_find(&object->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:
- object = object->__proto__;
+ proto = proto->__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 +1058,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 +1068,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 njs_is_void(retval) ? NXT_DECLINED : NXT_OK;
}
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 1536849242 -10800
# Thu Sep 13 17:34:02 2018 +0300
# Node ID 9f35cd7f1982667a666e31aba679770369cb566a
# Parent e2084d6c90ecb6f57f3433639b36e578f923be1d
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 1536849243 -10800
# Thu Sep 13 17:34:03 2018 +0300
# Node ID fcee00ca8136045ebfaf0743e2ac5f4a36ef4fb5
# Parent 9f35cd7f1982667a666e31aba679770369cb566a
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 1536849243 -10800
# Thu Sep 13 17:34:03 2018 +0300
# Node ID 9f0373c6e4174675532d0ade7ece8cab08e24fbd
# Parent fcee00ca8136045ebfaf0743e2ac5f4a36ef4fb5
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 1536849244 -10800
# Thu Sep 13 17:34:04 2018 +0300
# Node ID 8fa1991a255eb9c8ca6147f94d78fa9594887879
# Parent 9f0373c6e4174675532d0ade7ece8cab08e24fbd
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
@@ -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,86 @@ 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_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 +542,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_u32_to_string(&name, start++);
+
+ njs_value_property(vm, this, &name, &retval);
+
+ array->start[n++] = (!njs_is_void(&retval)) ? retval
+ : njs_value_invalid;
+ length--;
+ } while (length != 0);
+ }
}
return NXT_OK;
@@ -2101,7 +2189,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,87 @@ 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({'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("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 +9517,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 1536849246 -10800
# Thu Sep 13 17:34:06 2018 +0300
# Node ID 697e8ee2fd4a05a6979c7b7530d2605168946282
# Parent 8fa1991a255eb9c8ca6147f94d78fa9594887879
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;
@@ -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)
{
@@ -455,6 +485,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 = 1;
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
@@ -5568,6 +5568,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