From: hongzhidao Date: Fri, 23 Aug 2019 17:25:50 +0000 (-0400) Subject: Added new Function() support. X-Git-Tag: 0.3.6~37 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=a838ffa5f9ed380b276883525979606d881278d3;p=njs.git Added new Function() support. --- diff --git a/src/njs.h b/src/njs.h index 1c9765dd..8ed8d500 100644 --- a/src/njs.h +++ b/src/njs.h @@ -146,13 +146,28 @@ typedef struct { char **argv; njs_uint_t argc; +/* + * accumulative - enables "accumulative" mode to support incremental compiling. + * (REPL). Allows starting parent VM without cloning. + * disassemble - enables disassemble. + * backtrace - enables backtraces. + * quiet - removes filenames from backtraces. To produce comparable + test262 diffs. + * sandbox - "sandbox" mode. Disables file access. + * unsafe - enables unsafe language features: + * - Function constructors. + * module - ES6 "module" mode. Script mode is default. + */ + uint8_t trailer; /* 1 bit */ uint8_t init; /* 1 bit */ uint8_t accumulative; /* 1 bit */ + uint8_t disassemble; /* 1 bit */ uint8_t backtrace; /* 1 bit */ + uint8_t quiet; /* 1 bit */ uint8_t sandbox; /* 1 bit */ + uint8_t unsafe; /* 1 bit */ uint8_t module; /* 1 bit */ - uint8_t quiet; /* 1 bit */ } njs_vm_opt_t; @@ -225,6 +240,7 @@ NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm, const njs_value_t *value); NJS_EXPORT void njs_disassembler(njs_vm_t *vm); +NJS_EXPORT void njs_disassemble(u_char *start, u_char *end); NJS_EXPORT const njs_value_t *njs_vm_value(njs_vm_t *vm, const njs_str_t *name); NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t *name); diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c index ed02285d..184c18c5 100644 --- a/src/njs_disassembler.c +++ b/src/njs_disassembler.c @@ -8,9 +8,6 @@ #include -static void njs_disassemble(u_char *start, u_char *end); - - typedef struct { njs_vmcode_operation_t operation; size_t size; @@ -152,10 +149,12 @@ njs_disassembler(njs_vm_t *vm) code++; n--; } + + njs_printf("\n"); } -static void +void njs_disassemble(u_char *start, u_char *end) { u_char *p; diff --git a/src/njs_function.c b/src/njs_function.c index ed6cdd92..96a23ac0 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -874,9 +874,119 @@ njs_int_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_internal_error(vm, "Not implemented"); + size_t size; + u_char *start, *end; + njs_int_t ret; + njs_str_t str, file; + njs_uint_t i; + njs_lexer_t lexer; + njs_parser_t *parser; + njs_generator_t generator; + njs_parser_scope_t *scope; + + if (!vm->options.unsafe) { + njs_type_error(vm, "function constructor is disabled in \"safe\" mode"); + return NJS_ERROR; + } - return NJS_ERROR; + if (nargs < 2) { + start = (u_char *) "(function(){})"; + end = start + njs_length("(function(){})"); + + } else { + size = njs_length("(function(") + nargs + njs_length("){})"); + + for (i = 1; i < nargs; i++) { + if (!njs_is_string(&args[i])) { + ret = njs_value_to_string(vm, &args[i], &args[i]); + if (ret != NJS_OK) { + return ret; + } + } + + njs_string_get(&args[i], &str); + size += str.length; + } + + start = njs_mp_alloc(vm->mem_pool, size); + if (njs_slow_path(start == NULL)) { + return NJS_ERROR; + } + + end = njs_cpymem(start, "(function(", njs_length("(function(")); + + for (i = 1; i < nargs - 1; i++) { + if (i != 1) { + *end++ = ','; + } + + njs_string_get(&args[i], &str); + end = njs_cpymem(end, str.start, str.length); + } + + *end++ = ')'; + *end++ = '{'; + + njs_string_get(&args[nargs - 1], &str); + end = njs_cpymem(end, str.start, str.length); + + *end++ = '}'; + *end++ = ')'; + } + + vm->options.accumulative = 1; + + parser = njs_mp_zalloc(vm->mem_pool, sizeof(njs_parser_t)); + if (njs_slow_path(parser == NULL)) { + return NJS_ERROR; + } + + vm->parser = parser; + + file = njs_str_value("runtime"); + + ret = njs_lexer_init(vm, &lexer, &file, start, end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + parser->lexer = &lexer; + + ret = njs_parser(vm, parser, NULL); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + scope = parser->scope; + + ret = njs_variables_copy(vm, &scope->variables, &vm->variables_hash); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_variables_scope_reference(vm, scope); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_memzero(&generator, sizeof(njs_generator_t)); + + ret = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (vm->options.disassemble) { + njs_printf("new Function:runtime\n"); + njs_disassemble(generator.code_start, generator.code_end); + } + + ret = njs_vmcode_interpreter(vm, generator.code_start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + return NJS_OK; } diff --git a/src/njs_shell.c b/src/njs_shell.c index 7f0c6850..ec6013e3 100644 --- a/src/njs_shell.c +++ b/src/njs_shell.c @@ -25,6 +25,7 @@ typedef struct { uint8_t module; uint8_t quiet; uint8_t sandbox; + uint8_t safe; uint8_t version; char *file; @@ -74,7 +75,7 @@ static njs_int_t njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options); static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options); static njs_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options); -static njs_int_t njs_process_script(njs_console_t *console, njs_opts_t *opts, +static njs_int_t njs_process_script(njs_console_t *console, const njs_str_t *script); static njs_int_t njs_editline_init(void); static char **njs_completion_handler(const char *text, int start, int end); @@ -247,9 +248,11 @@ main(int argc, char **argv) vm_options.init = !opts.interactive; vm_options.accumulative = opts.interactive; + vm_options.disassemble = opts.disassemble; vm_options.backtrace = 1; vm_options.quiet = opts.quiet; vm_options.sandbox = opts.sandbox; + vm_options.unsafe = !opts.safe; vm_options.module = opts.module; vm_options.ops = &njs_console_ops; @@ -265,7 +268,7 @@ main(int argc, char **argv) if (vm != NULL) { command.start = (u_char *) opts.command; command.length = njs_strlen(opts.command); - ret = njs_process_script(vm_options.external, &opts, &command); + ret = njs_process_script(vm_options.external, &command); } } else { @@ -299,6 +302,7 @@ njs_get_options(njs_opts_t *opts, int argc, char** argv) " -s sandbox mode.\n" " -t script|module source code type (script is default).\n" " -v print njs version and exit.\n" + " -u disable \"unsafe\" mode.\n" " | - run code from a file or stdin.\n"; ret = NJS_DONE; @@ -382,6 +386,10 @@ njs_get_options(njs_opts_t *opts, int argc, char** argv) opts->version = 1; break; + case 'u': + opts->safe = 1; + break; + default: njs_stderror("Unknown argument: \"%s\" " "try \"%s -h\" for available options\n", argv[i], @@ -488,7 +496,7 @@ njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) add_history((char *) line.start); - njs_process_script(vm_options->external, opts, &line); + njs_process_script(vm_options->external, &line); /* editline allocs a new buffer every time. */ free(line.start); @@ -607,7 +615,7 @@ njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options) } } - ret = njs_process_script(vm_options->external, opts, &script); + ret = njs_process_script(vm_options->external, &script); if (ret != NJS_OK) { ret = NJS_ERROR; goto done; @@ -691,7 +699,7 @@ njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options) static void -njs_output(njs_vm_t *vm, njs_opts_t *opts, njs_int_t ret) +njs_output(njs_vm_t *vm, njs_int_t ret) { njs_str_t out; @@ -703,7 +711,7 @@ njs_output(njs_vm_t *vm, njs_opts_t *opts, njs_int_t ret) if (ret != NJS_OK) { njs_stderror("%V\n", &out); - } else if (opts->interactive) { + } else if (vm->options.accumulative) { njs_print(out.start, out.length); njs_printf("\n"); } @@ -711,7 +719,7 @@ njs_output(njs_vm_t *vm, njs_opts_t *opts, njs_int_t ret) static njs_int_t -njs_process_events(njs_console_t *console, njs_opts_t *opts) +njs_process_events(njs_console_t *console) { njs_ev_t *ev; njs_queue_t *events; @@ -740,8 +748,7 @@ njs_process_events(njs_console_t *console, njs_opts_t *opts) static njs_int_t -njs_process_script(njs_console_t *console, njs_opts_t *opts, - const njs_str_t *script) +njs_process_script(njs_console_t *console, const njs_str_t *script) { u_char *start; njs_vm_t *vm; @@ -753,22 +760,17 @@ njs_process_script(njs_console_t *console, njs_opts_t *opts, ret = njs_vm_compile(vm, &start, start + script->length); if (ret == NJS_OK) { - if (opts->disassemble) { - njs_disassembler(vm); - njs_printf("\n"); - } - ret = njs_vm_start(vm); } - njs_output(vm, opts, ret); + njs_output(vm, ret); for ( ;; ) { if (!njs_vm_pending(vm)) { break; } - ret = njs_process_events(console, opts); + ret = njs_process_events(console); if (njs_slow_path(ret != NJS_OK)) { njs_stderror("njs_process_events() failed\n"); ret = NJS_ERROR; @@ -786,7 +788,7 @@ njs_process_script(njs_console_t *console, njs_opts_t *opts, ret = njs_vm_run(vm); if (ret == NJS_ERROR) { - njs_output(vm, opts, ret); + njs_output(vm, ret); } } diff --git a/src/njs_vm.c b/src/njs_vm.c index 5aef0174..bd34521d 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -224,6 +224,10 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) } } + if (vm->options.disassemble) { + njs_disassembler(vm); + } + return NJS_OK; fail: diff --git a/src/test/njs_interactive_test.c b/src/test/njs_interactive_test.c index 391f0ec2..41efac86 100644 --- a/src/test/njs_interactive_test.c +++ b/src/test/njs_interactive_test.c @@ -173,6 +173,9 @@ static njs_interactive_test_t njs_test[] = " at eval (native)\n" " at main (native)\n") }, + { njs_str("new Function(\n\n@)" ENTER), + njs_str("SyntaxError: Unexpected token \"@\" in 3") }, + { njs_str("require()" ENTER), njs_str("TypeError: missing path\n" " at require (native)\n" diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index d65a53e0..e12fd3d5 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -8,6 +8,9 @@ #include +#define NJS_HAVE_LARGE_STACK (!NJS_HAVE_ADDRESS_SANITIZER && !NJS_HAVE_MEMORY_SANITIZER) + + typedef struct { njs_str_t script; njs_str_t ret; @@ -4225,7 +4228,7 @@ static njs_unit_test_t njs_test[] = "Array.prototype.fill.call(o, 2).a"), njs_str("4") }, -#if (!NJS_HAVE_ADDRESS_SANITIZER && !NJS_HAVE_MEMORY_SANITIZER) /* limited stack size */ +#if NJS_HAVE_LARGE_STACK { njs_str("var o = Object({length: 3});" "Object.defineProperty(o, '0', {set: function(v){this[0] = 2 * v}});" "Array.prototype.fill.call(o, 2)"), @@ -6558,7 +6561,7 @@ static njs_unit_test_t njs_test[] = { njs_str("{ function f() {} { var f }}"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, -#if (!NJS_HAVE_ADDRESS_SANITIZER && !NJS_HAVE_MEMORY_SANITIZER) /* limited stack size */ +#if NJS_HAVE_LARGE_STACK { njs_str("function f() { return f() } f()"), njs_str("RangeError: Maximum call stack size exceeded") }, #endif @@ -8837,9 +8840,6 @@ static njs_unit_test_t njs_test[] = { njs_str("var ex; try {({}) instanceof this} catch (e) {ex = e}; ex"), njs_str("TypeError: right argument is not callable") }, - { njs_str("Function.call(this, 'var x / = 1;')"), - njs_str("InternalError: Not implemented") }, - { njs_str("njs"), njs_str("[object Object]") }, @@ -9590,7 +9590,76 @@ static njs_unit_test_t njs_test[] = njs_str("true") }, { njs_str("Function()"), - njs_str("InternalError: Not implemented") }, + njs_str("[object Function]") }, + + { njs_str("new Function();"), + njs_str("[object Function]") }, + + { njs_str("(function(){}).constructor === Function"), + njs_str("true") }, + +#if NJS_HAVE_LARGE_STACK + { njs_str("new Function(\"(\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"{\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"[\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"`\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"{[\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"{;\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"1;\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"~\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"new \".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"typeof \".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"1\" + \"** 1\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, + + { njs_str("new Function(\"var a; a\" + \"= a\".repeat(2**13));"), + njs_str("RangeError: Maximum call stack size exceeded") }, +#endif + + { njs_str("var f = new Function('return 1;'); f();"), + njs_str("1") }, + + { njs_str("var sum = new Function('a', 'b', 'return a + b');" + "sum(2, 4);"), + njs_str("6") }, + + { njs_str("var sum = new Function('a, b', 'return a + b');" + "sum(2, 4);"), + njs_str("6") }, + + { njs_str("var sum = new Function('a, b', 'c', 'return a + b + c');" + "sum(2, 4, 4);"), + njs_str("10") }, + + { njs_str("(new Function({ toString() { return '...a'; }}, { toString() { return 'return a;' }}))(1,2,3)"), + njs_str("1,2,3") }, + + { njs_str("var x = 10; function foo() { var x = 20; return new Function('return x;'); }" + "var f = foo(); f()"), + njs_str("10") }, + + { njs_str("var fn = (function() { return new Function('return this'); }).call({}), o = {}; fn.call(o) == o && fn.bind(o).call(this) == o"), + njs_str("true") }, { njs_str("RegExp()"), njs_str("/(?:)/") }, @@ -14535,6 +14604,7 @@ njs_externals_init(njs_vm_t *vm) typedef struct { njs_bool_t disassemble; njs_bool_t verbose; + njs_bool_t unsafe; njs_bool_t module; njs_uint_t repeat; } njs_opts_t; @@ -14588,6 +14658,7 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, const char *name, njs_memzero(&options, sizeof(njs_vm_opt_t)); options.module = opts->module; + options.unsafe = opts->unsafe; vm = njs_vm_create(&options); if (vm == NULL) { @@ -15112,6 +15183,7 @@ main(int argc, char **argv) njs_memzero(&stat, sizeof(njs_stat_t)); opts.repeat = 1; + opts.unsafe = 1; ret = njs_unit_test(njs_test, njs_nitems(njs_test), "script tests", &opts, &stat); diff --git a/test/njs_expect_test.exp b/test/njs_expect_test.exp index fc116a72..093b5125 100644 --- a/test/njs_expect_test.exp +++ b/test/njs_expect_test.exp @@ -799,6 +799,14 @@ njs_test { } "-s" +# safe mode + +njs_test { + {"new Function()\r\n" + "TypeError: function constructor is disabled in \"safe\" mode\r\n"} +} "-u" + + # source type njs_test {