njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_function_call_arguments(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_function_call_frame(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_function_call_end(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_method_call(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_method_call_arguments(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_method_call_prepare(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_method_call_frame(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_call_argument_expressions(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_call_argument_expressions_after(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
+static njs_uint_t njs_generate_call_nargs(njs_parser_node_t *node);
+static njs_int_t njs_generate_capture_stable_value(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_method_call_end(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
-static njs_int_t njs_generate_move_arguments(njs_vm_t *vm,
- njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_try_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_try_left(njs_vm_t *vm, njs_generator_t *generator,
njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_variable_t *var;
- njs_parser_node_t *this_object;
+ njs_parser_node_t *callee, *this_object;
- var = NULL;
+ callee = node->left;
this_object = njs_generate_function_call_this(node);
- if (node->left != NULL) {
- if (njs_generate_is_property_call_source(node->left)) {
- njs_internal_error(vm, "unexpected function call source");
- return NJS_ERROR;
- }
+ njs_assert(callee != NULL);
- if (this_object != NULL
- && node->left->token_type != NJS_TOKEN_OPTIONAL_CHAIN)
- {
- njs_internal_error(vm, "unexpected function call this");
- return NJS_ERROR;
- }
+ if (njs_generate_is_property_call_source(callee)) {
+ njs_internal_error(vm, "unexpected function call source");
+ return NJS_ERROR;
+ }
- /* Generate function code in function expression. */
+ if (this_object != NULL
+ && callee->token_type != NJS_TOKEN_OPTIONAL_CHAIN)
+ {
+ njs_internal_error(vm, "unexpected function call this");
+ return NJS_ERROR;
+ }
- njs_generator_next(generator, njs_generate, node->left);
+ njs_generator_next(generator, njs_generate, callee);
- return njs_generator_after(vm, generator,
- njs_queue_first(&generator->stack), node,
- njs_generate_function_call_arguments,
- NULL, 0);
+ return njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_function_call_arguments,
+ NULL, 0);
+}
+
+
+static njs_int_t
+njs_generate_function_call_arguments(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+
+ ret = njs_generate_capture_stable_value(vm, generator, node->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
- ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE, &var);
+ if (node->right == NULL) {
+ return njs_generate_function_call_frame(vm, generator, node);
+ }
+
+ ret = njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_function_call_frame, NULL, 0);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- return njs_generate_function_call_arguments(vm, generator, node);
+ return njs_generate_call_argument_expressions(vm, generator, node->right);
}
static njs_int_t
-njs_generate_function_call_arguments(njs_vm_t *vm, njs_generator_t *generator,
+njs_generate_function_call_frame(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_jump_off_t func_offset;
- njs_parser_node_t *name;
+ njs_uint_t nargs;
+ njs_parser_node_t *callee, *this_object;
+ njs_parser_node_t *arg;
+ njs_vmcode_1addr_t *put_arg;
+ njs_vmcode_method_frame_t *method_frame;
njs_vmcode_function_frame_t *func;
- name = node;
+ callee = node->left;
+ njs_assert(callee != NULL);
- if (node->left != NULL) {
- name = node->left;
- }
-
- njs_generate_code(generator, njs_vmcode_function_frame_t, func,
- NJS_VMCODE_FUNCTION_FRAME, node);
- func_offset = njs_code_offset(generator, func);
- func->ctor = node->ctor;
- func->name = name->index;
- func->nargs = 0;
+ this_object = njs_generate_function_call_this(node);
+ nargs = njs_generate_call_nargs(node->right);
- njs_generator_next(generator, njs_generate,
- (node->right != NULL ? node->right->left : NULL));
+ if (this_object != NULL) {
+ njs_generate_code(generator, njs_vmcode_method_frame_t, method_frame,
+ NJS_VMCODE_METHOD_FRAME, node);
+ method_frame->ctor = node->ctor;
+ method_frame->function = callee->index;
+ method_frame->this_object = this_object->index;
+ method_frame->nargs = nargs;
- ret = njs_generator_after(vm, generator,
- njs_queue_first(&generator->stack), node,
- njs_generate_function_call_end, NULL, 0);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ } else {
+ njs_generate_code(generator, njs_vmcode_function_frame_t, func,
+ NJS_VMCODE_FUNCTION_FRAME, node);
+ func->ctor = node->ctor;
+ func->name = callee->index;
+ func->nargs = nargs;
}
- if (node->right == NULL) {
- return NJS_OK;
+ for (arg = node->right; arg != NULL; arg = arg->right) {
+ njs_generate_code(generator, njs_vmcode_1addr_t, put_arg,
+ NJS_VMCODE_PUT_ARG, arg);
+ put_arg->index = arg->left->index;
}
- return njs_generator_after(vm, generator,
- njs_queue_first(&generator->stack), node->right,
- njs_generate_move_arguments,
- &func_offset, sizeof(njs_jump_off_t));
+ return njs_generate_function_call_end(vm, generator, node);
}
njs_generate_function_call_end(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_parser_node_t *this_object;
+ njs_int_t ret;
+ njs_parser_node_t *this_object;
ret = njs_generate_call(vm, generator, node);
if (njs_fast_path(ret != NJS_OK)) {
}
}
- return njs_generator_stack_pop(vm, generator, generator->context);
+ return njs_generator_stack_pop(vm, generator, NULL);
}
njs_generate_method_call_arguments(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_jump_off_t method_offset;
- njs_parser_node_t *prop;
- njs_vmcode_method_frame_t *method;
+ njs_int_t ret;
+ njs_parser_node_t *prop, *property;
prop = node->left;
+ property = prop->right;
if (!njs_generate_is_property_call_source(prop)) {
njs_internal_error(vm, "unexpected method call source");
return NJS_ERROR;
}
- njs_generate_code(generator, njs_vmcode_method_frame_t, method,
- NJS_VMCODE_METHOD_FRAME, prop);
- method_offset = njs_code_offset(generator, method);
- method->ctor = node->ctor;
- method->object = prop->left->index;
- method->method = prop->right->index;
- method->nargs = 0;
+ if (node->right == NULL) {
+ prop->index = njs_generate_node_temp_index_get(vm, generator, prop);
+ if (njs_slow_path(prop->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_generate_property_get(vm, generator, property, prop->index,
+ prop->left->index, property->index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ return njs_generate_method_call_frame(vm, generator, node);
+ }
- njs_generator_next(generator, njs_generate,
- (node->right != NULL ? node->right->left : node->right));
+ ret = njs_generate_method_call_prepare(vm, generator, node);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
ret = njs_generator_after(vm, generator,
njs_queue_first(&generator->stack), node,
- njs_generate_method_call_end, NULL, 0);
+ njs_generate_method_call_frame, NULL, 0);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- if (node->right == NULL) {
- return NJS_OK;
+ return njs_generate_call_argument_expressions(vm, generator, node->right);
+}
+
+
+static njs_int_t
+njs_generate_method_call_prepare(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_parser_node_t *prop, *property;
+
+ prop = node->left;
+ property = prop->right;
+
+ ret = njs_generate_capture_stable_value(vm, generator, prop->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
}
- return njs_generator_after(vm, generator,
- njs_queue_first(&generator->stack), node->right,
- njs_generate_move_arguments,
- &method_offset, sizeof(njs_jump_off_t));
+ prop->index = njs_generate_node_temp_index_get(vm, generator, prop);
+ if (njs_slow_path(prop->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ return njs_generate_property_get(vm, generator, property, prop->index,
+ prop->left->index, property->index);
+}
+
+
+static njs_int_t
+njs_generate_method_call_frame(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_uint_t nargs;
+ njs_parser_node_t *arg;
+ njs_parser_node_t *prop;
+ njs_vmcode_1addr_t *put_arg;
+ njs_vmcode_method_frame_t *method_frame;
+
+ prop = node->left;
+ nargs = njs_generate_call_nargs(node->right);
+
+ if (!njs_generate_is_property_call_source(prop)) {
+ njs_internal_error(vm, "unexpected method call source");
+ return NJS_ERROR;
+ }
+
+ njs_generate_code(generator, njs_vmcode_method_frame_t, method_frame,
+ NJS_VMCODE_METHOD_FRAME, node);
+ method_frame->ctor = node->ctor;
+ method_frame->function = prop->index;
+ method_frame->this_object = prop->left->index;
+ method_frame->nargs = nargs;
+
+ for (arg = node->right; arg != NULL; arg = arg->right) {
+ njs_generate_code(generator, njs_vmcode_1addr_t, put_arg,
+ NJS_VMCODE_PUT_ARG, arg);
+ put_arg->index = arg->left->index;
+ }
+
+ return njs_generate_method_call_end(vm, generator, node);
}
return ret;
}
- return njs_generator_stack_pop(vm, generator, generator->context);
+ return njs_generator_stack_pop(vm, generator, NULL);
}
static njs_int_t
-njs_generate_move_arguments(njs_vm_t *vm, njs_generator_t *generator,
+njs_generate_call_argument_expressions(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_jump_off_t func_offset;
- njs_vmcode_1addr_t *put_arg;
- njs_vmcode_function_frame_t *func;
-
if (node == NULL) {
- return njs_generator_stack_pop(vm, generator, generator->context);
+ return njs_generator_stack_pop(vm, generator, NULL);
}
- njs_generate_code(generator, njs_vmcode_1addr_t, put_arg,
- NJS_VMCODE_PUT_ARG, node);
- put_arg->index = node->left->index;
+ njs_generator_next(generator, njs_generate, node->left);
- func_offset = *((njs_jump_off_t *) generator->context);
- func = njs_code_ptr(generator, njs_vmcode_function_frame_t, func_offset);
+ return njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_call_argument_expressions_after,
+ NULL, 0);
+}
- func->nargs++;
+static njs_int_t
+njs_generate_call_argument_expressions_after(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node)
+{
if (node->right == NULL) {
- return njs_generator_stack_pop(vm, generator, generator->context);
+ return njs_generator_stack_pop(vm, generator, NULL);
}
njs_generator_next(generator, njs_generate, node->right->left);
return njs_generator_after(vm, generator,
njs_queue_first(&generator->stack), node->right,
- njs_generate_move_arguments,
- generator->context, 0);
+ njs_generate_call_argument_expressions_after,
+ NULL, 0);
+}
+
+
+static njs_uint_t
+njs_generate_call_nargs(njs_parser_node_t *node)
+{
+ njs_uint_t nargs;
+
+ nargs = 0;
+
+ while (node != NULL) {
+ nargs++;
+ node = node->right;
+ }
+
+ return nargs;
+}
+
+
+static njs_int_t
+njs_generate_capture_stable_value(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_index_t src;
+ njs_vmcode_move_t *move;
+
+ if (node->temporary) {
+ return NJS_OK;
+ }
+
+ src = node->index;
+
+ node->index = njs_generate_node_temp_index_get(vm, generator, node);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ njs_generate_code_move(generator, move, node->index, src, node);
+
+ return NJS_OK;
}
{ njs_str("function f(a) {return function (x) {return a(x)}} f(1)(0)"),
njs_str("TypeError: number is not a function") },
+ { njs_str("var fooCalled = false;"
+ "function foo(){ fooCalled = true; }"
+ "var o = {}, r;"
+ "try { o.bar(foo()) } catch (e) { r = e.name + ':' + fooCalled }"
+ "r"),
+ njs_str("TypeError:true") },
+
+ { njs_str("var o = {}, r;"
+ "Object.defineProperty(o, 'bar', {"
+ " get: function() { this.barGetter = true; return 42; }"
+ "});"
+ "try { o.foo(o.bar) } catch (e) { r = e.name + ':' + o.barGetter }"
+ "r"),
+ njs_str("TypeError:true") },
+
+ { njs_str("var o = {}, r;"
+ "Object.defineProperty(o, 'bar', {"
+ " get: function() { this.barGetter = true; return 42; }"
+ "});"
+ "try { o.foo(o['bar']) } catch (e) {"
+ " r = e.name + ':' + o.barGetter"
+ "}"
+ "r"),
+ njs_str("TypeError:true") },
+
+ { njs_str("var fooCalled = false;"
+ "function foo(){ fooCalled = true; }"
+ "var o = {}, r;"
+ "try { o.bar.gar(foo()) } catch (e) {"
+ " r = e.name + ':' + fooCalled"
+ "}"
+ "r"),
+ njs_str("TypeError:false") },
+
+ { njs_str("var x = function() { this.foo = 42; };"
+ "var result = new x(x = 1);"
+ "[x, result.foo]"),
+ njs_str("1,42") },
+
+ { njs_str("function fn() {"
+ " var x = function() { this.foo = 42; };"
+ " var result = new x(x = 1);"
+ " return [x, result.foo];"
+ "}"
+ "fn()"),
+ njs_str("1,42") },
+
+ { njs_str("var C = {}; var ran = false, r;"
+ "try { new C((ran = true, 1)) } catch (e) {"
+ " r = e.name + ':' + ran"
+ "}"
+ "r"),
+ njs_str("TypeError:true") },
+
+ { njs_str("var o = {"
+ " get f() { this.hit = (this.hit || 0) + 1; return 1; }"
+ "};"
+ "var ran = false, r;"
+ "try { o.f((ran = true, 1)) } catch (e) {"
+ " r = ran + ':' + o.hit"
+ "}"
+ "r"),
+ njs_str("true:1") },
+
+ { njs_str("var o = {"
+ " get f() { return function(v) { return this.x + v } },"
+ " x: 7"
+ "};"
+ "o.f(5)"),
+ njs_str("12") },
+
{ njs_str("var x = 0;"
""
"function f1() {"
{ njs_str("var o = {m: function() {return 42}}; (o?.m)()"),
njs_str("42") },
+ { njs_str("var o = {x: 5, m: function() {return this.x}}; (o?.m)()"),
+ njs_str("5") },
+
+ { njs_str("var o = {x: 3, m: function() {return {c: this.x}}};"
+ "o.m?.().c"),
+ njs_str("3") },
+
+ { njs_str("var o = {x: 4, m: function() {return {c: this.x}}};"
+ "(o.m)?.().c"),
+ njs_str("4") },
+
+ { njs_str("var o = {x: 6, m: function() {return {c: this.x}}};"
+ "o?.m?.().c"),
+ njs_str("6") },
+
+ { njs_str("var o = {x: 8, m: function() {return {c: this.x}}};"
+ "(o?.m)?.().c"),
+ njs_str("8") },
+
+ { njs_str("var k = 'm';"
+ "var o = {x: 9, m: function() {return this.x}};"
+ "(o?.[k])()"),
+ njs_str("9") },
+
{ njs_str("var o = null; (o?.m)()"),
njs_str("TypeError: undefined is not a function") },
njs_str("false") },
{ njs_str("new 0[isNaN]"),
- njs_str("TypeError: (intermediate value)[\"[object Function]\"] is not a function") },
+ njs_str("TypeError: undefined is not a function") },
{ njs_str("new 0[undefined]"),
- njs_str("TypeError: (intermediate value)[\"undefined\"] is not a function") },
+ njs_str("TypeError: undefined is not a function") },
/**/
{ njs_str("function f() { Object.prototype.toString = 1; };"
"Object.prototype.toString = f;"
"(function () { try { 's'[{}](); } catch (e) { throw e; } })()"),
- njs_str("TypeError: (intermediate value)[\"undefined\"] is not a function") },
+ njs_str("TypeError: undefined is not a function") },
{ njs_str("var i; for (i = 0; i < 10; i++) { i += 1 } i"),
njs_str("10") },
njs_str("TypeError: number is not a function") },
{ njs_str("var o = {a:1}; o.a()"),
- njs_str("TypeError: (intermediate value)[\"a\"] is not a function") },
+ njs_str("TypeError: number is not a function") },
{ njs_str("(function(){})()"),
njs_str("undefined") },
njs_str("SyntaxError: Unexpected token \"null\"") },
{ njs_str("'a'.f()"),
- njs_str("TypeError: (intermediate value)[\"f\"] is not a function") },
+ njs_str("TypeError: undefined is not a function") },
{ njs_str("1..f()"),
- njs_str("TypeError: (intermediate value)[\"f\"] is not a function") },
+ njs_str("TypeError: undefined is not a function") },
{ njs_str("try {}"),
njs_str("SyntaxError: Missing catch or finally after try") },
" at main (:1)\n") },
{ njs_str("preload.a.push(2)"),
- njs_str("TypeError: (intermediate value)[\"push\"] is not a function\n"
+ njs_str("TypeError: undefined is not a function\n"
" at main (:1)\n") },
{ njs_str("Array.prototype.push.call(preload.a, 'waka')"),
{ njs_str("[].concat({}.a.a)"),
njs_str("TypeError: cannot get property \"a\" of undefined\n"
- " at concat (native)\n"
" at main (:1)\n") },
{ njs_str("''.repeat(-1)"),
{ njs_str("Math.log({}.a.a)"),
njs_str("TypeError: cannot get property \"a\" of undefined\n"
- " at log (native)\n"
" at main (:1)\n") },
{ njs_str("var bound = Math.max.bind(null, {toString(){return {}}}); bound(1)"),
" at main (:1)\n") },
{ njs_str("Object.prototype()"),
- njs_str("TypeError: (intermediate value)[\"prototype\"] is not a function\n"
+ njs_str("TypeError: object is not a function\n"
" at main (:1)\n") },
{ njs_str("eval()"),
{ njs_str("$shared.method({}.a.a)"),
njs_str("TypeError: cannot get property \"a\" of undefined\n"
- " at method (native)\n"
" at main (:1)\n") },
{ njs_str("new Function(\n\n@)"),
{ njs_str("function log(v) {}\nlog({}\n.a\n.a)"),
njs_str("TypeError: cannot get property \"a\" of undefined\n"
- " at main (:4)\n") },
+ " at main (:2)\n") },
{ njs_str("\nfor (var i = 0;\n i < a;\n i++) { }\n"),
njs_str("ReferenceError: \"a\" is not defined\n"
{ njs_str("Math\n.min(1,\na)"),
njs_str("ReferenceError: \"a\" is not defined\n"
- " at min (native)\n"
" at main (:3)\n") },
};