From: Dmitry Volyntsev Date: Tue, 21 Jan 2020 13:01:45 +0000 (+0300) Subject: Fixed Function constructor according to the spec. X-Git-Tag: 0.3.8~3 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=008d70f1b938ac6b52e2956bf57ad846b33010ed;p=njs.git Fixed Function constructor according to the spec. "this" value should point to the global object. --- diff --git a/src/njs.h b/src/njs.h index 087cfbda..c1660238 100644 --- a/src/njs.h +++ b/src/njs.h @@ -41,7 +41,7 @@ typedef struct { /* sizeof(njs_value_t) is 16 bytes. */ #define njs_argument(args, n) \ - (njs_value_t *) ((u_char *) args + n * 16) + (njs_value_t *) ((u_char *) args + (n) * 16) extern const njs_value_t njs_value_undefined; diff --git a/src/njs_function.c b/src/njs_function.c index ed594902..72c14297 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -463,7 +463,14 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function, native_frame->arguments = value; if (bound == NULL) { - *value++ = *this; + *value = *this; + + if (njs_slow_path(function->global_this + && njs_is_null_or_undefined(this))) { + njs_set_object(value, &vm->global_object); + } + + value++; } else { n = function->args_offset; @@ -838,64 +845,69 @@ static njs_int_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - 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; + njs_chb_t chain; + njs_int_t ret; + njs_str_t str, file; + njs_uint_t i; + njs_value_t *body; + njs_lexer_t lexer; + njs_parser_t *parser; + njs_function_t *function; + njs_generator_t generator; + njs_parser_scope_t *scope; + njs_function_lambda_t *lambda; + njs_vmcode_function_t *code; if (!vm->options.unsafe) { - njs_type_error(vm, "function constructor is disabled in \"safe\" mode"); - return NJS_ERROR; - } - - if (nargs < 2) { - start = (u_char *) "(function(){})"; - end = start + njs_length("(function(){})"); + body = njs_argument(args, nargs - 1); + ret = njs_value_to_string(vm, body, body); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - } 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(body, &str); - njs_string_get(&args[i], &str); - size += str.length; - } + /* + * Safe mode exception: + * "(new Function('return this'))" is often used to get + * the global object in a portable way. + */ - start = njs_mp_alloc(vm->mem_pool, size); - if (njs_slow_path(start == NULL)) { + if (str.length != njs_length("return this") + || memcmp(str.start, "return this", 11) != 0) + { + njs_type_error(vm, "function constructor is disabled" + " in \"safe\" mode"); return NJS_ERROR; } + } - end = njs_cpymem(start, "(function(", njs_length("(function(")); + njs_chb_init(&chain, vm->mem_pool); - for (i = 1; i < nargs - 1; i++) { - if (i != 1) { - *end++ = ','; - } + njs_chb_append_literal(&chain, "(function("); - njs_string_get(&args[i], &str); - end = njs_cpymem(end, str.start, str.length); + for (i = 1; i < nargs - 1; i++) { + ret = njs_value_to_chain(vm, &chain, njs_argument(args, i)); + if (njs_slow_path(ret < NJS_OK)) { + return ret; } - *end++ = ')'; - *end++ = '{'; + njs_chb_append_literal(&chain, ","); + } + + njs_chb_append_literal(&chain, "){"); - njs_string_get(&args[nargs - 1], &str); - end = njs_cpymem(end, str.start, str.length); + ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1)); + if (njs_slow_path(ret < NJS_OK)) { + return ret; + } + + njs_chb_append_literal(&chain, "})"); - *end++ = '}'; - *end++ = ')'; + ret = njs_chb_join(&chain, &str); + if (njs_slow_path(ret != NJS_OK)) { + njs_memory_error(vm); + return NJS_ERROR; } vm->options.accumulative = 1; @@ -909,7 +921,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, file = njs_str_value("runtime"); - ret = njs_lexer_init(vm, &lexer, &file, start, end); + ret = njs_lexer_init(vm, &lexer, &file, str.start, str.start + str.length); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -940,16 +952,21 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } - if (vm->options.disassemble) { - njs_printf("new Function:runtime\n"); - njs_disassemble(generator.code_start, generator.code_end); - } + njs_chb_destroy(&chain); - ret = njs_vmcode_interpreter(vm, generator.code_start); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + code = (njs_vmcode_function_t *) generator.code_start; + lambda = code->lambda; + + function = njs_function_alloc(vm, lambda, NULL, 0); + if (njs_slow_path(function == NULL)) { + return NJS_ERROR; } + function->global_this = 1; + function->args_count = lambda->nargs - lambda->rest_parameters; + + njs_set_function(&vm->retval, function); + return NJS_OK; } diff --git a/src/njs_value.h b/src/njs_value.h index b29353a0..304b6bb2 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -278,7 +278,7 @@ struct njs_function_s { uint8_t args_offset; - uint8_t args_count:5; + uint8_t args_count:4; /* * If "closure" is true njs_closure_t[] is available right after the @@ -291,6 +291,7 @@ struct njs_function_s { uint8_t closure:1; uint8_t native:1; uint8_t ctor:1; + uint8_t global_this:1; uint8_t magic; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index fcbd7aec..3482faf6 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -11296,6 +11296,18 @@ static njs_unit_test_t njs_test[] = { 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("(new Function('return this'))() === globalThis"), + njs_str("true") }, + + { njs_str("(new Function('a', 'return a')).length"), + njs_str("1") }, + + { njs_str("(new Function('a','b', 'return a + b')).length"), + njs_str("2") }, + + { njs_str("var o = {}; (new Function('return this')).call(o) === o"), + njs_str("true") }, + { njs_str("this.NN = {}; var f = Function('eval = 42;'); f()"), njs_str("SyntaxError: Identifier \"eval\" is forbidden as left-hand in assignment in runtime:1") }, diff --git a/test/njs_expect_test.exp b/test/njs_expect_test.exp index 574bc729..2ed271aa 100644 --- a/test/njs_expect_test.exp +++ b/test/njs_expect_test.exp @@ -849,6 +849,12 @@ njs_test { njs_test { {"new Function()\r\n" "TypeError: function constructor is disabled in \"safe\" mode\r\n"} + {"(new Function('return this'))() === globalThis\r\n" + "true\r\n"} + {"new Function('return this;')\r\n" + "TypeError: function constructor is disabled in \"safe\" mode\r\n"} + {"new Function('return thi')\r\n" + "TypeError: function constructor is disabled in \"safe\" mode\r\n"} } "-u"