From: Igor Sysoev Date: Fri, 11 Dec 2015 15:41:00 +0000 (+0300) Subject: Functions refactored. X-Git-Tag: 0.1.0~105 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=3adb731bd262d880c45b02ad9dcd0007b80f0b71;p=njs.git Functions refactored. --- diff --git a/njs/njs_array.c b/njs/njs_array.c index 87d9a323..51fd4357 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -723,7 +723,7 @@ njs_array_prototype_for_each(njs_vm_t *vm, njs_param_t *param) each->index = n; if (n > 0) { - vm->current -= sizeof(njs_vmcode_call_t); + vm->current -= sizeof(njs_vmcode_function_call_t); } nargs = param->nargs; @@ -800,7 +800,7 @@ njs_array_prototype_some(njs_vm_t *vm, njs_param_t *param) func = (nargs != 0) ? &args[0] : (njs_value_t *) &njs_value_void; - vm->current -= sizeof(njs_vmcode_call_t); + vm->current -= sizeof(njs_vmcode_function_call_t); return njs_function_apply(vm, func, &p); } @@ -866,7 +866,7 @@ njs_array_prototype_every(njs_vm_t *vm, njs_param_t *param) func = (nargs != 0) ? &args[0] : (njs_value_t *) &njs_value_void; - vm->current -= sizeof(njs_vmcode_call_t); + vm->current -= sizeof(njs_vmcode_function_call_t); return njs_function_apply(vm, func, &p); } diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 9c627572..2a5d0057 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -94,7 +94,7 @@ njs_builtin_objects_create(njs_vm_t *vm) for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) { functions[i].native = 1; functions[i].args_offset = 1; - functions[i].code.native = native_functions[i]; + functions[i].u.native = native_functions[i]; ret = njs_object_hash_create(vm, &functions[i].object.shared_hash, function_init[i]->properties, diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index 8436d0f1..2bfc9d82 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -31,12 +31,12 @@ typedef struct { static njs_code_name_t code_names[] = { - { njs_vmcode_object_create, sizeof(njs_vmcode_object_t), - nxt_string("OBJECT CREATE ") }, - { njs_vmcode_function_create, sizeof(njs_vmcode_function_create_t), - nxt_string("FUNCTION CREATE ") }, - { njs_vmcode_regexp_create, sizeof(njs_vmcode_regexp_t), - nxt_string("REGEXP CREATE ") }, + { njs_vmcode_object, sizeof(njs_vmcode_object_t), + nxt_string("OBJECT ") }, + { njs_vmcode_function, sizeof(njs_vmcode_function_t), + nxt_string("FUNCTION ") }, + { njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t), + nxt_string("REGEXP ") }, { njs_vmcode_property_get, sizeof(njs_vmcode_prop_get_t), nxt_string("PROPERTY GET ") }, @@ -49,11 +49,11 @@ static njs_code_name_t code_names[] = { { njs_vmcode_instance_of, sizeof(njs_vmcode_instance_of_t), nxt_string("INSTANCE OF ") }, - { njs_vmcode_function, sizeof(njs_vmcode_function_t), - nxt_string("FUNCTION ") }, - { njs_vmcode_call, sizeof(njs_vmcode_call_t), - nxt_string("CALL ") }, - { njs_vmcode_return, sizeof(njs_vmcode_stop_t), + { njs_vmcode_function_frame, sizeof(njs_vmcode_function_frame_t), + nxt_string("FUNCTION FRAME ") }, + { njs_vmcode_function_call, sizeof(njs_vmcode_function_call_t), + nxt_string("FUNCTION CALL ") }, + { njs_vmcode_return, sizeof(njs_vmcode_return_t), nxt_string("RETURN ") }, { njs_vmcode_stop, sizeof(njs_vmcode_stop_t), nxt_string("STOP ") }, @@ -164,35 +164,35 @@ njs_disassembler(njs_vm_t *vm) static void njs_disassemble(u_char *start, u_char *end) { - u_char *p; - nxt_str_t *name; - nxt_uint_t n; - const char *sign; - njs_code_name_t *code_name; - njs_vmcode_jump_t *jump; - njs_vmcode_1addr_t *code1; - njs_vmcode_2addr_t *code2; - njs_vmcode_3addr_t *code3; - njs_vmcode_array_t *array; - njs_vmcode_catch_t *catch; - njs_vmcode_method_t *method; - njs_vmcode_try_end_t *try_end; - njs_vmcode_try_start_t *try_start; - njs_vmcode_operation_t operation; - njs_vmcode_cond_jump_t *cond_jump; - njs_vmcode_prop_each_t *each; - njs_vmcode_prop_start_t *prop_start; + u_char *p; + nxt_str_t *name; + nxt_uint_t n; + const char *sign; + njs_code_name_t *code_name; + njs_vmcode_jump_t *jump; + njs_vmcode_1addr_t *code1; + njs_vmcode_2addr_t *code2; + njs_vmcode_3addr_t *code3; + njs_vmcode_array_t *array; + njs_vmcode_catch_t *catch; + njs_vmcode_try_end_t *try_end; + njs_vmcode_try_start_t *try_start; + njs_vmcode_operation_t operation; + njs_vmcode_cond_jump_t *cond_jump; + njs_vmcode_prop_each_t *each; + njs_vmcode_prop_start_t *prop_start; + njs_vmcode_method_frame_t *method; p = start; while (p < end) { operation = *(njs_vmcode_operation_t *) p; - if (operation == njs_vmcode_array_create) { + if (operation == njs_vmcode_array) { array = (njs_vmcode_array_t *) p; p += sizeof(njs_vmcode_array_t); - printf("ARRAY CREATE %04lX %ld\n", + printf("ARRAY %04lX %ld\n", array->retval, array->length); continue; @@ -230,11 +230,11 @@ njs_disassemble(u_char *start, u_char *end) continue; } - if (operation == njs_vmcode_method) { - method = (njs_vmcode_method_t *) p; - p += sizeof(njs_vmcode_method_t); + if (operation == njs_vmcode_method_frame) { + method = (njs_vmcode_method_frame_t *) p; + p += sizeof(njs_vmcode_method_frame_t); - printf("METHOD %04lX %04lX %04lX %d\n", method->function, + printf("METHOD FRAME %04lX %04lX %d\n", method->object, method->method, method->code.nargs); continue; diff --git a/njs/njs_function.c b/njs/njs_function.c index 2ec1856e..bca792cb 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -19,125 +19,49 @@ #include -static const njs_vmcode_1addr_t njs_trap_strings[] = { - { .code = { .operation = njs_vmcode_string_primitive, - .operands = NJS_VMCODE_1OPERAND, - .retval = NJS_VMCODE_NO_RETVAL }, - .index = 0 }, - { .code = { .operation = njs_vmcode_string_primitive, - .operands = NJS_VMCODE_1OPERAND, - .retval = NJS_VMCODE_NO_RETVAL }, - .index = 1 }, - { .code = { .operation = njs_vmcode_restart, - .operands = NJS_VMCODE_NO_OPERAND, - .retval = NJS_VMCODE_NO_RETVAL } }, -}; - - -static const njs_vmcode_1addr_t njs_trap_numbers[] = { - { .code = { .operation = njs_vmcode_number_primitive, - .operands = NJS_VMCODE_1OPERAND, - .retval = NJS_VMCODE_NO_RETVAL }, - .index = 0 }, - { .code = { .operation = njs_vmcode_number_primitive, - .operands = NJS_VMCODE_1OPERAND, - .retval = NJS_VMCODE_NO_RETVAL }, - .index = 1 }, - { .code = { .operation = njs_vmcode_restart, - .operands = NJS_VMCODE_NO_OPERAND, - .retval = NJS_VMCODE_NO_RETVAL } }, -}; - - -static const njs_vmcode_1addr_t njs_trap_number[] = { - { .code = { .operation = njs_vmcode_number_primitive, - .operands = NJS_VMCODE_1OPERAND, - .retval = NJS_VMCODE_NO_RETVAL }, - .index = 0 }, - { .code = { .operation = njs_vmcode_restart, - .operands = NJS_VMCODE_NO_OPERAND, - .retval = NJS_VMCODE_NO_RETVAL } }, -}; - - -static const njs_vm_trap_t njs_vm_traps[] = { - /* NJS_TRAP_PROPERTY */ { &njs_trap_strings[1], 0 }, - /* NJS_TRAP_STRINGS */ { &njs_trap_strings[0], 0 }, - /* NJS_TRAP_INCDEC */ { &njs_trap_numbers[1], 1 }, - /* NJS_TRAP_NUMBERS */ { &njs_trap_numbers[0], 0 }, - /* NJS_TRAP_NUMBER */ { &njs_trap_number[0], 0 }, -}; - - njs_function_t * njs_function_alloc(njs_vm_t *vm) { - njs_function_t *func; - njs_function_script_t *script; + njs_function_t *function; - func = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t)); + function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t)); - if (nxt_fast_path(func != NULL)) { - func->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION]; - func->args_offset = 1; + if (nxt_fast_path(function != NULL)) { + function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION]; + function->args_offset = 1; - script = nxt_mem_cache_zalloc(vm->mem_cache_pool, - sizeof(njs_function_script_t)); - if (nxt_slow_path(script == NULL)) { + function->u.lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool, + sizeof(njs_function_lambda_t)); + if (nxt_slow_path(function->u.lambda == NULL)) { return NULL; } - - func->code.script = script; } - return func; + return function; } -nxt_noinline njs_value_t * -njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method, uintptr_t nargs, - nxt_bool_t ctor) +njs_value_t * +njs_function_native_frame(njs_vm_t *vm, njs_native_t native, size_t local_size, + njs_vmcode_t *code) { - size_t size, spare_size; + size_t size; njs_value_t *this; njs_native_frame_t *frame; - size = NJS_NATIVE_FRAME_SIZE - + method->data.string_size - + nargs * sizeof(njs_value_t); - - if (nxt_fast_path(size <= vm->frame->size)) { - frame = (njs_native_frame_t *) vm->frame->last; - frame->size = vm->frame->size - size; - frame->start = 0; + size = NJS_NATIVE_FRAME_SIZE + local_size + + code->nargs * sizeof(njs_value_t); - } else { - spare_size = size + NJS_FRAME_SPARE_SIZE; - spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE); - - frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), - spare_size); - if (nxt_slow_path(frame == NULL)) { - return NULL; - } - - frame->size = spare_size - size; - frame->start = 1; + frame = njs_function_frame_alloc(vm, size); + if (nxt_slow_path(frame == NULL)) { + return NULL; } - frame->ctor = ctor; - frame->reentrant = 0; - frame->trap_reference = 0; - - frame->u.exception.next = NULL; - frame->u.exception.catch = NULL; - - frame->last = (u_char *) frame + size; - frame->previous = vm->frame; - vm->frame = frame; + frame->u.native = native; + frame->native = 1; + frame->ctor = code->ctor; - this = (njs_value_t *) - ((u_char *) njs_native_data(frame) + method->data.string_size); + this = (njs_value_t *) ((u_char *) njs_native_data(frame) + local_size); frame->arguments = this + 1; vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = frame->arguments; @@ -145,20 +69,18 @@ njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method, uintptr_t nargs, } -njs_ret_t -njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, - njs_value_t *value2) +nxt_noinline njs_native_frame_t * +njs_function_frame_alloc(njs_vm_t *vm, size_t size) { - size_t size, spare_size; - njs_value_t *values; + size_t spare_size; njs_native_frame_t *frame; - size = NJS_NATIVE_FRAME_SIZE + 3 * sizeof(njs_value_t); + spare_size = vm->frame->free_size; - if (nxt_fast_path(size <= vm->frame->size)) { - frame = (njs_native_frame_t *) vm->frame->last; - frame->size = vm->frame->size - size; - frame->start = 0; + if (nxt_fast_path(size <= spare_size)) { + frame = (njs_native_frame_t *) vm->frame->free; + frame->first = 0; + frame->skip = 0; } else { spare_size = size + NJS_FRAME_SPARE_SIZE; @@ -167,38 +89,26 @@ njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), spare_size); if (nxt_slow_path(frame == NULL)) { - return NXT_ERROR; + return NULL; } - frame->size = spare_size - size; - frame->start = 1; + frame->first = 1; + frame->skip = 0; } - frame->ctor = 0; - frame->reentrant = 0; - - values = njs_native_data(frame); - njs_set_invalid(&values[0]); - values[2] = *value2; - - frame->trap_reference = njs_vm_traps[trap].reference_value; + frame->free_size = spare_size - size; + frame->free = (u_char *) frame + size; - if (njs_vm_traps[trap].reference_value) { - values[1].data.u.value = value1; - - } else { - values[1] = *value1; - } + frame->reentrant = 0; + frame->trap_reference = 0; - frame->u.exception.catch = NULL; - frame->u.restart = vm->current; - vm->current = (u_char *) njs_vm_traps[trap].code; + frame->exception.next = NULL; + frame->exception.catch = NULL; - frame->last = (u_char *) frame + size; frame->previous = vm->frame; vm->frame = frame; - return NXT_OK; + return frame; } @@ -212,23 +122,26 @@ njs_function_constructor(njs_vm_t *vm, njs_param_t *param) nxt_noinline njs_ret_t njs_function_apply(njs_vm_t *vm, njs_value_t *name, njs_param_t *param) { - njs_ret_t ret; + njs_ret_t ret; + njs_function_t *function; if (njs_is_native(name)) { return name->data.u.method(vm, param); } else if (njs_is_function(name)) { - if (name->data.u.function->native) { - return name->data.u.function->code.native(vm, param); + function = name->data.u.function; + + if (function->native) { + return function->u.native(vm, param); } - ret = njs_vmcode_function_frame(vm, name, param, 0); + ret = njs_function_frame(vm, function, param, 0); if (nxt_fast_path(ret == NXT_OK)) { vm->retval = njs_value_void; - return njs_function_call(vm, name->data.u.function, param->retval); + return njs_function_call(vm, param->retval); } } @@ -237,58 +150,35 @@ njs_function_apply(njs_vm_t *vm, njs_value_t *name, njs_param_t *param) nxt_noinline njs_ret_t -njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name, njs_param_t *param, +njs_function_frame(njs_vm_t *vm, njs_function_t *function, njs_param_t *param, nxt_bool_t ctor) { - size_t size, spare_size; - uintptr_t nargs, n; - njs_value_t *args, *arguments; - njs_frame_t *frame; - njs_function_t *func; + size_t size; + uintptr_t nargs, n; + njs_value_t *args, *arguments; + njs_frame_t *frame; + njs_native_frame_t *native_frame; - func = name->data.u.function; - nargs = nxt_max(param->nargs, func->code.script->nargs); + nargs = nxt_max(param->nargs, function->u.lambda->nargs); size = NJS_FRAME_SIZE + nargs * sizeof(njs_value_t) - + func->code.script->local_size; - spare_size = size + func->code.script->spare_size; - - if (spare_size <= vm->frame->size) { - frame = (njs_frame_t *) vm->frame->last; - frame->native.size = vm->frame->size - size; - frame->native.start = 0; + + function->u.lambda->local_size; - } else { - if (func->code.script->spare_size != 0) { - spare_size = size + NJS_FRAME_SPARE_SIZE; - spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE); - } - - frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), - spare_size); - if (nxt_slow_path(frame == NULL)) { - return NXT_ERROR; - } - - frame->native.size = spare_size - size; - frame->native.start = 1; + native_frame = njs_function_frame_alloc(vm, size); + if (nxt_slow_path(native_frame == NULL)) { + return NXT_ERROR; } - frame->native.ctor = ctor; - frame->native.reentrant = 0; - frame->native.trap_reference = 0; - - frame->native.u.exception.next = NULL; - frame->native.u.exception.catch = NULL; + native_frame->u.function = function; + native_frame->native = 0; + native_frame->ctor = ctor; - frame->native.last = (u_char *) frame + size; - frame->native.previous = vm->frame; - vm->frame = &frame->native; + args = (njs_value_t *) ((u_char *) native_frame + NJS_FRAME_SIZE); + native_frame->arguments = args + function->args_offset; + vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = native_frame->arguments; - args = (njs_value_t *) ((u_char *) frame + NJS_FRAME_SIZE); - frame->native.arguments = args + func->args_offset; - vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = frame->native.arguments; + frame = (njs_frame_t *) native_frame; frame->local = &args[nargs]; @@ -312,31 +202,30 @@ njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name, njs_param_t *param, nargs--; } - memcpy(frame->local, func->code.script->local_scope, - func->code.script->local_size); - - vm->retval = *name; + memcpy(frame->local, function->u.lambda->local_scope, + function->u.lambda->local_size); return NXT_OK; } nxt_noinline njs_ret_t -njs_function_call(njs_vm_t *vm, njs_function_t *func, njs_index_t retval) +njs_function_call(njs_vm_t *vm, njs_index_t retval) { - njs_frame_t *frame; + njs_frame_t *frame; + njs_function_t *function; frame = (njs_frame_t *) vm->frame; frame->retval = retval; - frame->return_address = vm->current; - - vm->current = func->code.script->u.code; + function = frame->native.u.function; + frame->native.u.return_address = vm->current; + vm->current = function->u.lambda->u.start; frame->prev_arguments = vm->scopes[NJS_SCOPE_ARGUMENTS]; vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments - - func->args_offset; + - function->args_offset; #if (NXT_DEBUG) vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL; #endif @@ -375,11 +264,12 @@ const njs_object_init_t njs_function_constructor_init = { static njs_ret_t njs_function_prototype_call(njs_vm_t *vm, njs_param_t *param) { - uintptr_t nargs; - njs_ret_t ret; - njs_param_t p; - njs_value_t *func; - njs_vmcode_call_t *call; + uintptr_t nargs; + njs_ret_t ret; + njs_param_t p; + njs_value_t *func; + njs_function_t *function; + njs_vmcode_function_call_t *call; p.object = ¶m->args[0]; p.args = ¶m->args[1]; @@ -400,13 +290,15 @@ njs_function_prototype_call(njs_vm_t *vm, njs_param_t *param) return NXT_ERROR; } - if (func->data.u.function->native) { + function = func->data.u.function; + + if (function->native) { if (nargs != 0) { p.nargs = nargs - 1; p.retval = param->retval; - return func->data.u.function->code.native(vm, &p); + return function->u.native(vm, &p); } vm->exception = &njs_exception_type_error; @@ -422,30 +314,31 @@ njs_function_prototype_call(njs_vm_t *vm, njs_param_t *param) p.nargs = nargs; - ret = njs_vmcode_function_frame(vm, func, &p, 0); + ret = njs_function_frame(vm, function, &p, 0); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } /* Skip the "call" method frame. */ - vm->frame->previous = vm->frame->previous->previous; + vm->frame->previous->skip = 1; - call = (njs_vmcode_call_t *) vm->current; + call = (njs_vmcode_function_call_t *) vm->current; - return njs_function_call(vm, func->data.u.function, call->retval); + return njs_function_call(vm, call->retval); } static njs_ret_t njs_function_prototype_apply(njs_vm_t *vm, njs_param_t *param) { - uintptr_t nargs; - njs_ret_t ret; - njs_param_t p; - njs_array_t *array; - njs_value_t *func, *args; - njs_vmcode_call_t *code; + uintptr_t nargs; + njs_ret_t ret; + njs_param_t p; + njs_array_t *array; + njs_value_t *func, *args; + njs_function_t *function; + njs_vmcode_function_call_t *code; args = param->args; p.object = &args[0]; @@ -481,7 +374,9 @@ njs_function_prototype_apply(njs_vm_t *vm, njs_param_t *param) return func->data.u.method(vm, &p); } - if (func->data.u.function->native) { + function = func->data.u.function; + + if (function->native) { p.retval = param->retval; if (nargs < 2) { @@ -494,7 +389,7 @@ njs_function_prototype_apply(njs_vm_t *vm, njs_param_t *param) } } - return func->data.u.function->code.native(vm, &p); + return function->u.native(vm, &p); } if (nargs < 2) { @@ -506,15 +401,15 @@ njs_function_prototype_apply(njs_vm_t *vm, njs_param_t *param) } } - ret = njs_vmcode_function_frame(vm, func, &p, 0); + ret = njs_function_frame(vm, function, &p, 0); if (nxt_fast_path(ret == NXT_OK)) { /* Skip the "apply" method frame. */ - vm->frame->previous = vm->frame->previous->previous; + vm->frame->previous->skip = 1; - code = (njs_vmcode_call_t *) vm->current; + code = (njs_vmcode_function_call_t *) vm->current; - return njs_function_call(vm, func->data.u.function, code->retval); + return njs_function_call(vm, code->retval); } return NXT_ERROR; @@ -533,8 +428,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_param_t *param) njs_value_t *func; njs_function_t *bound; - bound = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), - sizeof(njs_function_t)); + bound = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t)); if (nxt_fast_path(bound != NULL)) { nxt_lvlhsh_init(&bound->object.hash); @@ -543,7 +437,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_param_t *param) bound->args_offset = 1; func = param->object; - bound->code.script = func->data.u.function->code.script; + bound->u.lambda = func->data.u.function->u.lambda; vm->retval.data.u.function = bound; vm->retval.type = NJS_FUNCTION; diff --git a/njs/njs_function.h b/njs/njs_function.h index 51db59b5..f30848bd 100644 --- a/njs/njs_function.h +++ b/njs/njs_function.h @@ -8,20 +8,15 @@ #define _NJS_FUNCTION_H_INCLUDED_ -struct njs_function_script_s { +struct njs_function_lambda_s { uint32_t nargs; uint32_t local_size; - /* - * Native methods do not allocate frame space so calling function - * reserves space in its scope for method frame and arguments. - */ - uint32_t spare_size; /* Initial values of local scope. */ njs_value_t *local_scope; union { - u_char *code; + u_char *start; njs_parser_t *parser; } u; }; @@ -61,21 +56,53 @@ struct njs_exception_s { struct njs_native_frame_s { - u_char *last; - njs_native_frame_t *previous; - njs_value_t *arguments; + u_char *free; + /* + * The return_address is required in njs_frame_t only, however, it + * can be stored here just after function adddress has been fetched. + */ union { + njs_function_t *function; + u_char *return_address; + njs_native_t native; u_char *restart; - njs_exception_t exception; } u; - uint32_t size; + njs_native_frame_t *previous; + njs_value_t *arguments; + + njs_exception_t exception; + + uint32_t free_size; + + /* Script or native function or method. */ + uint8_t native; /* 1 bit */ - uint8_t start; /* 1 bit */ - uint8_t ctor; /* 1 bit */ - uint8_t reentrant; /* 1 bit */ - uint8_t trap_reference; /* 1 bit */ + /* Function is called as constructor with "new" keyword. */ + uint8_t ctor; /* 1 bit */ + + /* + * The first frame in chunk. + * 7 bits are just to possibly initialize first and skip + * fields with one operation. + */ + uint8_t first:7; /* 1 bit */ + + /* Skip the Function.call() and Function.apply() methods frames. */ + uint8_t skip:1; /* 1 bit */ + + /* + * The function is reentrant. It is usually used as a flag, + * however, in traps it used to allow just two entrances. + */ + uint8_t reentrant:7; /* 2 bits */ + + /* + * The first operand in trap is reference to original value, + * it is used to increment or decrement this value. + */ + uint8_t trap_reference:1; }; @@ -88,22 +115,19 @@ typedef struct { njs_value_t *closure; njs_index_t retval; - u_char *return_address; } njs_frame_t; njs_function_t *njs_function_alloc(njs_vm_t *vm); +njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size); njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_param_t *param); njs_ret_t njs_function_apply(njs_vm_t *vm, njs_value_t *name, njs_param_t *param); -njs_value_t *njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method, - uintptr_t nargs, nxt_bool_t ctor); -njs_ret_t njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, - njs_value_t *value2); -njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name, +njs_value_t *njs_function_native_frame(njs_vm_t *vm, njs_native_t native, + size_t local_size, njs_vmcode_t *code); +njs_ret_t njs_function_frame(njs_vm_t *vm, njs_function_t *function, njs_param_t *param, nxt_bool_t ctor); -njs_ret_t njs_function_call(njs_vm_t *vm, njs_function_t *func, - njs_index_t retval); +njs_ret_t njs_function_call(njs_vm_t *vm, njs_index_t retval); extern const njs_object_init_t njs_function_constructor_init; extern const njs_object_init_t njs_function_prototype_init; diff --git a/njs/njs_generator.c b/njs/njs_generator.c index 27a1c56e..f66fb443 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -57,7 +57,7 @@ static nxt_int_t njs_generate_2addr_operation(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); static nxt_int_t njs_generate_inc_dec_operation(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node, nxt_bool_t post); -static nxt_int_t njs_generate_function_statement(njs_vm_t *vm, +static nxt_int_t njs_generate_function_declaration(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); static nxt_int_t njs_generate_return_statement(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); @@ -225,20 +225,20 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) return NXT_ERROR; - case NJS_TOKEN_OBJECT_LITERAL: + case NJS_TOKEN_OBJECT_VALUE: node->index = node->u.object->index; return NXT_OK; - case NJS_TOKEN_OBJECT_CREATE: + case NJS_TOKEN_OBJECT: return njs_generate_object(vm, parser, node); - case NJS_TOKEN_ARRAY_CREATE: + case NJS_TOKEN_ARRAY: return njs_generate_array(vm, parser, node); - case NJS_TOKEN_FUNCTION_CREATE: + case NJS_TOKEN_FUNCTION_EXPRESSION: return njs_generate_function(vm, parser, node); - case NJS_TOKEN_REGEXP_LITERAL: + case NJS_TOKEN_REGEXP: return njs_generate_regexp(vm, parser, node); case NJS_TOKEN_THIS: @@ -257,7 +257,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) return njs_generate_variable(parser, node); case NJS_TOKEN_FUNCTION: - return njs_generate_function_statement(vm, parser, node); + return njs_generate_function_declaration(vm, parser, node); case NJS_TOKEN_FUNCTION_CALL: return njs_generate_function_call(vm, parser, node); @@ -944,7 +944,7 @@ njs_generate_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) } njs_generate_code(parser, njs_vmcode_object_t, obj); - obj->code.operation = njs_vmcode_object_create; + obj->code.operation = njs_vmcode_object; obj->code.operands = NJS_VMCODE_1OPERAND; obj->code.retval = NJS_VMCODE_RETVAL; obj->retval = index; @@ -996,7 +996,7 @@ njs_generate_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) } njs_generate_code(parser, njs_vmcode_array_t, array); - array->code.operation = njs_vmcode_array_create; + array->code.operation = njs_vmcode_array; array->code.operands = NJS_VMCODE_1OPERAND; array->code.retval = NJS_VMCODE_RETVAL; array->retval = index; @@ -1030,34 +1030,19 @@ static nxt_int_t njs_generate_function(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { - nxt_int_t ret; - njs_index_t index; - njs_parser_node_t *body; - njs_function_script_t *func; - njs_vmcode_operation_t last; - njs_vmcode_function_create_t *function; - - body = node->right; - - if (body != NULL - && body->right != NULL - && body->right->token == NJS_TOKEN_RETURN) - { - last = NULL; - - } else { - last = njs_vmcode_return; - } + nxt_int_t ret; + njs_index_t index; + njs_function_lambda_t *lambda; + njs_vmcode_function_t *function; - func = node->u.value.data.u.data; + lambda = node->u.value.data.u.lambda; - ret = njs_generate_scope(vm, func->u.parser, body, last); + ret = njs_generate_scope(vm, lambda->u.parser, node->right); if (nxt_fast_path(ret == NXT_OK)) { - func->local_size = func->u.parser->scope_size; - func->spare_size = func->u.parser->method_arguments_size; - func->local_scope = func->u.parser->local_scope; - func->u.code = func->u.parser->code_start; + lambda->local_size = lambda->u.parser->scope_size; + lambda->local_scope = lambda->u.parser->local_scope; + lambda->u.start = lambda->u.parser->code_start; /* Try to assign directly to variable. */ @@ -1067,15 +1052,16 @@ njs_generate_function(njs_vm_t *vm, njs_parser_t *parser, } if (index == NJS_INDEX_NONE) { + node->temporary = 1; index = njs_generator_temp_index_get(parser); } - njs_generate_code(parser, njs_vmcode_function_create_t, function); - function->code.operation = njs_vmcode_function_create; + njs_generate_code(parser, njs_vmcode_function_t, function); + function->code.operation = njs_vmcode_function; function->code.operands = NJS_VMCODE_1OPERAND; function->code.retval = NJS_VMCODE_RETVAL; function->retval = index; - function->function = func; + function->lambda = lambda; node->index = index; @@ -1104,7 +1090,7 @@ njs_generate_regexp(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) } njs_generate_code(parser, njs_vmcode_regexp_t, regexp); - regexp->code.operation = njs_vmcode_regexp_create; + regexp->code.operation = njs_vmcode_regexp; regexp->code.operands = NJS_VMCODE_1OPERAND; regexp->code.retval = NJS_VMCODE_RETVAL; regexp->retval = index; @@ -1433,39 +1419,24 @@ njs_generate_inc_dec_operation(njs_vm_t *vm, njs_parser_t *parser, static nxt_int_t -njs_generate_function_statement(njs_vm_t *vm, njs_parser_t *parser, +njs_generate_function_declaration(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { - nxt_int_t ret; - njs_value_t *value; - njs_function_t *func; - njs_parser_node_t *body; - njs_vmcode_operation_t last; + nxt_int_t ret; + njs_value_t *value; + njs_function_t *func; value = njs_variable_value(parser, node->index); func = value->data.u.function; - body = node->right; - - if (body != NULL - && body->right != NULL - && body->right->token == NJS_TOKEN_RETURN) - { - last = NULL; - - } else { - last = njs_vmcode_return; - } - - ret = njs_generate_scope(vm, func->code.script->u.parser, body, last); + ret = njs_generate_scope(vm, func->u.lambda->u.parser, node->right); if (nxt_fast_path(ret == NXT_OK)) { - parser = func->code.script->u.parser; + parser = func->u.lambda->u.parser; - func->code.script->local_size = parser->scope_size; - func->code.script->spare_size = parser->method_arguments_size; - func->code.script->local_scope = parser->local_scope; - func->code.script->u.code = parser->code_start; + func->u.lambda->local_size = parser->scope_size; + func->u.lambda->local_scope = parser->local_scope; + func->u.lambda->u.start = parser->code_start; node->u.value = *value; } @@ -1474,8 +1445,7 @@ njs_generate_function_statement(njs_vm_t *vm, njs_parser_t *parser, nxt_int_t -njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node, - njs_vmcode_operation_t last) +njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { size_t code_size, size; u_char *p; @@ -1500,16 +1470,17 @@ njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node, } } - if (last != NULL) { + if (parser->scope == NJS_SCOPE_GLOBAL) { njs_generate_code(parser, njs_vmcode_stop_t, stop); - stop->code.operation = last; + stop->code.operation = njs_vmcode_stop; stop->code.operands = NJS_VMCODE_1OPERAND; stop->code.retval = NJS_VMCODE_NO_RETVAL; - index = njs_value_index(vm, parser, &njs_value_void); - - if (last == njs_vmcode_stop && node->index != 0) { + if (node->index != 0) { index = node->index; + + } else { + index = njs_value_index(vm, parser, &njs_value_void); } stop->retval = index; @@ -1569,14 +1540,14 @@ static nxt_int_t njs_generate_return_statement(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { - nxt_int_t ret; - njs_index_t index; - njs_vmcode_stop_t *code; + nxt_int_t ret; + njs_index_t index; + njs_vmcode_return_t *code; ret = njs_generator(vm, parser, node->right); if (nxt_fast_path(ret == NXT_OK)) { - njs_generate_code(parser, njs_vmcode_stop_t, code); + njs_generate_code(parser, njs_vmcode_return_t, code); code->code.operation = njs_vmcode_return; code->code.operands = NJS_VMCODE_1OPERAND; code->code.retval = NJS_VMCODE_NO_RETVAL; @@ -1600,13 +1571,13 @@ static nxt_int_t njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { - uintptr_t nargs; - nxt_int_t ret; - njs_index_t retval, index, name; - njs_parser_node_t *arg; - njs_vmcode_call_t *call; - njs_vmcode_move_t *move; - njs_vmcode_function_t *func; + uintptr_t nargs; + nxt_int_t ret; + njs_index_t retval; + njs_parser_node_t *arg, *name; + njs_vmcode_move_t *move; + njs_vmcode_function_call_t *call; + njs_vmcode_function_frame_t *func; if (node->left != NULL) { /* Generate function code in function expression. */ @@ -1615,23 +1586,25 @@ njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser, return ret; } - name = node->left->index; + name = node->left; } else { /* njs_generate_variable() always returns NXT_OK. */ (void) njs_generate_variable(parser, node); - name = node->index; + name = node; } - njs_generate_code(parser, njs_vmcode_function_t, func); - func->code.operation = njs_vmcode_function; - func->code.operands = NJS_VMCODE_2OPERANDS; - func->code.retval = NJS_VMCODE_RETVAL; + njs_generate_code(parser, njs_vmcode_function_frame_t, func); + func->code.operation = njs_vmcode_function_frame; + func->code.operands = NJS_VMCODE_1OPERAND; + func->code.retval = NJS_VMCODE_NO_RETVAL; func->code.ctor = node->ctor; - func->name = name; + func->name = name->index; - index = njs_generator_temp_index_get(parser); - func->function = index; + ret = njs_generator_node_index_release(vm, parser, name); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } nargs = 1; @@ -1663,24 +1636,19 @@ njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser, if (retval == NJS_INDEX_NONE) { node->temporary = 1; - retval = index; + retval = njs_generator_temp_index_get(parser); } node->index = retval; - njs_generate_code(parser, njs_vmcode_call_t, call); - call->code.operation = njs_vmcode_call; - call->code.operands = NJS_VMCODE_2OPERANDS; + njs_generate_code(parser, njs_vmcode_function_call_t, call); + call->code.operation = njs_vmcode_function_call; + call->code.operands = NJS_VMCODE_1OPERAND; call->code.retval = NJS_VMCODE_NO_RETVAL; call->code.nargs = nargs; - call->function = index; call->retval = retval; - if (retval == index) { - return NXT_OK; - } - - return njs_generator_index_release(vm, parser, index); + return NXT_OK; } @@ -1688,13 +1656,13 @@ static nxt_int_t njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { - uintptr_t nargs; - nxt_int_t ret; - njs_index_t retval, index; - njs_parser_node_t *arg, *prop; - njs_vmcode_call_t *call; - njs_vmcode_move_t *move; - njs_vmcode_method_t *method; + uintptr_t nargs; + nxt_int_t ret; + njs_index_t retval; + njs_parser_node_t *arg, *prop; + njs_vmcode_move_t *move; + njs_vmcode_method_frame_t *method; + njs_vmcode_function_call_t *call; prop = node->left; @@ -1712,30 +1680,19 @@ njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser, return ret; } - if (prop->left->temporary) { - index = prop->left->index; - - ret = njs_generator_node_index_release(vm, parser, prop->right); - if (nxt_slow_path(ret != NXT_OK)) { - return ret; - } - - } else if (prop->right->temporary) { - index = prop->right->index; - - } else { - index = njs_generator_temp_index_get(parser); - } - - njs_generate_code(parser, njs_vmcode_method_t, method); - method->code.operation = njs_vmcode_method; - method->code.operands = NJS_VMCODE_3OPERANDS; - method->code.retval = NJS_VMCODE_RETVAL; + njs_generate_code(parser, njs_vmcode_method_frame_t, method); + method->code.operation = njs_vmcode_method_frame; + method->code.operands = NJS_VMCODE_2OPERANDS; + method->code.retval = NJS_VMCODE_NO_RETVAL; method->code.ctor = node->ctor; - method->function = index; method->object = prop->left->index; method->method = prop->right->index; + ret = njs_generator_children_indexes_release(vm, parser, prop); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + nargs = 1; for (arg = node->right; arg != NULL; arg = arg->right) { @@ -1766,24 +1723,19 @@ njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser, if (retval == NJS_INDEX_NONE) { node->temporary = 1; - retval = index; + retval = njs_generator_temp_index_get(parser); } node->index = retval; - njs_generate_code(parser, njs_vmcode_call_t, call); - call->code.operation = njs_vmcode_call; - call->code.operands = NJS_VMCODE_2OPERANDS; + njs_generate_code(parser, njs_vmcode_function_call_t, call); + call->code.operation = njs_vmcode_function_call; + call->code.operands = NJS_VMCODE_1OPERAND; call->code.retval = NJS_VMCODE_NO_RETVAL; call->code.nargs = nargs; - call->function = index; call->retval = retval; - if (retval == index) { - return NXT_OK; - } - - return njs_generator_index_release(vm, parser, index); + return NXT_OK; } diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 3db099f2..a33b45ea 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -37,8 +37,12 @@ static njs_token_t njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token); static njs_token_t njs_parser_block(njs_vm_t *vm, njs_parser_t *parser); -static njs_token_t njs_parser_function_statement(njs_vm_t *vm, +static njs_token_t njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser); +static njs_parser_t *njs_parser_function_create(njs_vm_t *vm, + njs_parser_t *parent); +static njs_token_t njs_parser_function_lambda(njs_vm_t *vm, + njs_function_lambda_t *lambda, njs_token_t token); static njs_token_t njs_parser_return_statement(njs_vm_t *vm, njs_parser_t *parser); static njs_token_t njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser); @@ -135,7 +139,7 @@ njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser, switch (token) { case NJS_TOKEN_FUNCTION: - return njs_parser_function_statement(vm, parser); + return njs_parser_function_declaration(vm, parser); case NJS_TOKEN_RETURN: return njs_parser_return_statement(vm, parser); @@ -256,15 +260,13 @@ njs_parser_match(njs_parser_t *parser, njs_token_t token, static njs_token_t -njs_parser_function_statement(njs_vm_t *vm, njs_parser_t *parser) +njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser) { - nxt_str_t *name; nxt_uint_t level; - njs_index_t index; - njs_value_t *value; njs_token_t token; - njs_parser_t *fn_parser; - njs_variable_t *arg, *var; + njs_value_t *value; + njs_variable_t *var; + njs_function_t *function; njs_parser_node_t *node; node = njs_parser_node_alloc(vm); @@ -279,168 +281,70 @@ njs_parser_function_statement(njs_vm_t *vm, njs_parser_t *parser) return token; } - if (token == NJS_TOKEN_NAME) { - nxt_thread_log_debug("function: %V", &parser->lexer->text); - - var = njs_parser_variable(vm, parser, &level); - if (nxt_slow_path(var == NULL)) { - return NJS_TOKEN_ERROR; - } - - var->state = NJS_VARIABLE_DECLARED; - - token = njs_parser_token(parser); - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { - return token; - } - - index = var->index; - value = njs_variable_value(parser, index); - - } else { - /* Anonymous function. */ - value = nxt_array_add(parser->scope_values, &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(value == NULL)) { - return NJS_TOKEN_ERROR; - } - - index = njs_parser_index(parser, parser->scope); - } - - value->type = NJS_FUNCTION; - node->index = index; - - token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS); - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { - return token; - } - - value->data.u.function = njs_function_alloc(vm); - if (nxt_slow_path(value->data.u.function == NULL)) { - return NJS_TOKEN_ERROR; - } - - fn_parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t)); - if (nxt_slow_path(fn_parser == NULL)) { + if (token != NJS_TOKEN_NAME) { return NJS_TOKEN_ERROR; } - value->data.u.function->code.script->u.parser = fn_parser; - - fn_parser->lexer = parser->lexer; - - /* njs_vmcode_return() size. */ - fn_parser->code_size = sizeof(njs_vmcode_stop_t); - - fn_parser->arguments = nxt_array_create(4, sizeof(njs_variable_t), - &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(fn_parser->arguments == NULL)) { + var = njs_parser_variable(vm, parser, &level); + if (nxt_slow_path(var == NULL)) { return NJS_TOKEN_ERROR; } - fn_parser->parent = parser; - - vm->parser = fn_parser; - - index = NJS_SCOPE_ARGUMENTS; - - /* A "this" reservation. */ - index += sizeof(njs_value_t); - - while (token != NJS_TOKEN_CLOSE_PARENTHESIS) { - - if (nxt_slow_path(token != NJS_TOKEN_NAME)) { - return NJS_TOKEN_ERROR; - } - - name = &fn_parser->lexer->text; - - nxt_thread_log_debug("arg: %V", name); - - arg = nxt_array_add(fn_parser->arguments, &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(arg == NULL)) { - return NJS_TOKEN_ERROR; - } - - arg->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->len); - if (nxt_slow_path(arg->name_start == NULL)) { - return NJS_TOKEN_ERROR; - } - - memcpy(arg->name_start, name->data, name->len); - arg->name_len = name->len; - - arg->state = NJS_VARIABLE_DECLARED; - arg->index = index; - index += sizeof(njs_value_t); - - token = njs_parser_token(fn_parser); - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { - return token; - } - - if (token == NJS_TOKEN_COMMA) { - token = njs_parser_token(fn_parser); - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { - return token; - } - } - } + var->state = NJS_VARIABLE_DECLARED; + node->index = var->index; - value->data.u.function->code.script->nargs = - njs_index_size(index) / sizeof(njs_value_t); - - token = njs_parser_token(fn_parser); + token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } - if (nxt_slow_path(token != NJS_TOKEN_OPEN_BRACE)) { - return NJS_TOKEN_ERROR; - } + parser->node = node; - fn_parser->scope_values = nxt_array_create(4, sizeof(njs_value_t), - &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(fn_parser->scope_values == NULL)) { - return NXT_ERROR; + function = njs_function_alloc(vm); + if (nxt_slow_path(function == NULL)) { + return NJS_TOKEN_ERROR; } - fn_parser->scope = NJS_SCOPE_LOCAL; - - token = njs_parser_block(vm, fn_parser); + value = njs_variable_value(parser, node->index); + value->data.u.function = function; + value->type = NJS_FUNCTION; - vm->parser = parser; - node->right = fn_parser->node; + parser = njs_parser_function_create(vm, parser); + if (nxt_slow_path(parser == NULL)) { + return NJS_TOKEN_ERROR; + } - parser->node = node; + function->u.lambda->u.parser = parser; - return token; + return njs_parser_function_lambda(vm, function->u.lambda, token); } static njs_token_t njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser) { - nxt_str_t *name; nxt_uint_t level; + njs_token_t token; njs_index_t index; njs_value_t *value; - njs_token_t token; - njs_parser_t *fn_parser; - njs_variable_t *arg, *var; + njs_variable_t *var; + njs_function_t *function; njs_parser_node_t *node; - njs_function_script_t *func; + njs_function_lambda_t *lambda; node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } - node->token = NJS_TOKEN_FUNCTION_CREATE; + node->token = NJS_TOKEN_FUNCTION_EXPRESSION; + parser->node = node; + parser->code_size += sizeof(njs_vmcode_function_t); + + parser = njs_parser_function_create(vm, parser); + if (nxt_slow_path(parser == NULL)) { + return NJS_TOKEN_ERROR; + } token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { @@ -448,56 +352,98 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser) } if (token == NJS_TOKEN_NAME) { - nxt_thread_log_debug("function: %V", &parser->lexer->text); - var = njs_parser_variable(vm, parser, &level); if (nxt_slow_path(var == NULL)) { return NJS_TOKEN_ERROR; } + var->state = NJS_VARIABLE_DECLARED; + token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } + + index = var->index; + value = njs_variable_value(parser, index); + + function = njs_function_alloc(vm); + if (nxt_slow_path(function == NULL)) { + return NJS_TOKEN_ERROR; + } + + value->data.u.function = function; + value->type = NJS_FUNCTION; + lambda = function->u.lambda; + + } else { + /* Anonymous function. */ + lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool, + sizeof(njs_function_lambda_t)); + if (nxt_slow_path(lambda == NULL)) { + return NJS_TOKEN_ERROR; + } } - value = &node->u.value; + node->u.value.data.u.lambda = lambda; + lambda->u.parser = parser; - token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS); - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { - return token; + return njs_parser_function_lambda(vm, lambda, token); +} + + +static njs_parser_t * +njs_parser_function_create(njs_vm_t *vm, njs_parser_t *parent) +{ + nxt_array_t *values, *arguments; + njs_parser_t *parser; + + parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t)); + if (nxt_slow_path(parser == NULL)) { + return NULL; } - func = nxt_mem_cache_zalloc(vm->mem_cache_pool, - sizeof(njs_function_script_t)); - if (nxt_slow_path(func == NULL)) { - return NJS_TOKEN_ERROR; + parser->parent = parent; + parser->lexer = parent->lexer; + vm->parser = parser; + + arguments = nxt_array_create(4, sizeof(njs_variable_t), + &njs_array_mem_proto, vm->mem_cache_pool); + if (nxt_slow_path(arguments == NULL)) { + return NULL; } - value->data.u.data = func; + parser->arguments = arguments; - fn_parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t)); - if (nxt_slow_path(fn_parser == NULL)) { - return NJS_TOKEN_ERROR; + values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto, + vm->mem_cache_pool); + if (nxt_slow_path(values == NULL)) { + return NULL; } - func->u.parser = fn_parser; + parser->scope_values = values; + parser->scope = NJS_SCOPE_LOCAL; - fn_parser->lexer = parser->lexer; + return parser; +} - /* njs_vmcode_return() size. */ - fn_parser->code_size = sizeof(njs_vmcode_stop_t); - fn_parser->arguments = nxt_array_create(4, sizeof(njs_variable_t), - &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(fn_parser->arguments == NULL)) { - return NJS_TOKEN_ERROR; - } +static njs_token_t +njs_parser_function_lambda(njs_vm_t *vm, njs_function_lambda_t *lambda, + njs_token_t token) +{ + nxt_str_t *name; + njs_index_t index; + njs_parser_t *parser; + njs_variable_t *arg; + njs_parser_node_t *node, *body; - fn_parser->parent = parser; + parser = lambda->u.parser; - vm->parser = fn_parser; + token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS); + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { + return token; + } index = NJS_SCOPE_ARGUMENTS; @@ -510,16 +456,14 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser) return NJS_TOKEN_ERROR; } - name = &fn_parser->lexer->text; - - nxt_thread_log_debug("arg: %V", name); - - arg = nxt_array_add(fn_parser->arguments, &njs_array_mem_proto, + arg = nxt_array_add(parser->arguments, &njs_array_mem_proto, vm->mem_cache_pool); if (nxt_slow_path(arg == NULL)) { return NJS_TOKEN_ERROR; } + name = &parser->lexer->text; + arg->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->len); if (nxt_slow_path(arg->name_start == NULL)) { return NJS_TOKEN_ERROR; @@ -532,22 +476,22 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser) arg->index = index; index += sizeof(njs_value_t); - token = njs_parser_token(fn_parser); + token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (token == NJS_TOKEN_COMMA) { - token = njs_parser_token(fn_parser); + token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } } } - func->nargs = njs_index_size(index) / sizeof(njs_value_t); + lambda->nargs = njs_index_size(index) / sizeof(njs_value_t); - token = njs_parser_token(fn_parser); + token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } @@ -556,22 +500,40 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser) return NJS_TOKEN_ERROR; } - fn_parser->scope_values = nxt_array_create(4, sizeof(njs_value_t), - &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(fn_parser->scope_values == NULL)) { - return NXT_ERROR; + token = njs_parser_block(vm, parser); + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { + return token; } - fn_parser->scope = NJS_SCOPE_LOCAL; + /* + * There is no function body or the last function body statement is not + * "return" statement. If function has body then the body->right node is + * always present and it is a NJS_TOKEN_STATEMENT link node. + */ + body = parser->node; - token = njs_parser_block(vm, fn_parser); + if (body == NULL || body->right->token != NJS_TOKEN_RETURN) { + node = njs_parser_node_alloc(vm); + if (nxt_slow_path(node == NULL)) { + return NJS_TOKEN_ERROR; + } - vm->parser = parser; - node->right = fn_parser->node; + node->token = NJS_TOKEN_STATEMENT; + node->left = parser->node; + parser->node = node; - parser->node = node; - parser->code_size += sizeof(njs_vmcode_function_create_t); + node->right = njs_parser_node_alloc(vm); + if (nxt_slow_path(node->right == NULL)) { + return NJS_TOKEN_ERROR; + } + + node->right->token = NJS_TOKEN_RETURN; + + parser->code_size += sizeof(njs_vmcode_return_t); + } + + parser->parent->node->right = parser->node; + vm->parser = parser->parent; return token; } @@ -590,7 +552,7 @@ njs_parser_return_statement(njs_vm_t *vm, njs_parser_t *parser) node->token = NJS_TOKEN_RETURN; parser->node = node; - parser->code_size += sizeof(njs_vmcode_stop_t); + parser->code_size += sizeof(njs_vmcode_return_t); token = njs_lexer_token(parser->lexer); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { @@ -1303,7 +1265,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) break; case NJS_TOKEN_OPEN_BRACE: - node->token = NJS_TOKEN_OBJECT_CREATE; + node->token = NJS_TOKEN_OBJECT; nxt_thread_log_debug("JS: OBJECT"); @@ -1322,7 +1284,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) return token; case NJS_TOKEN_OPEN_BRACKET: - node->token = NJS_TOKEN_ARRAY_CREATE; + node->token = NJS_TOKEN_ARRAY; nxt_thread_log_debug("JS: ARRAY"); @@ -1348,7 +1310,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) nxt_thread_log_debug("REGEX: '%V'", &parser->lexer->text); - node->token = NJS_TOKEN_REGEXP_LITERAL; + node->token = NJS_TOKEN_REGEXP; parser->code_size += sizeof(njs_vmcode_regexp_t); break; @@ -1488,7 +1450,7 @@ njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj) return NJS_TOKEN_ERROR; } - object->token = NJS_TOKEN_OBJECT_LITERAL; + object->token = NJS_TOKEN_OBJECT_VALUE; object->u.object = obj; propref = njs_parser_node_alloc(vm); @@ -1591,7 +1553,7 @@ njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj) return NJS_TOKEN_ERROR; } - object->token = NJS_TOKEN_OBJECT_LITERAL; + object->token = NJS_TOKEN_OBJECT_VALUE; object->u.object = obj; propref = njs_parser_node_alloc(vm); diff --git a/njs/njs_parser.h b/njs/njs_parser.h index 69ca4556..9f83268f 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -118,21 +118,21 @@ typedef enum { NJS_TOKEN_ESCAPE_STRING, NJS_TOKEN_NAME, - NJS_TOKEN_OBJECT_CREATE, - NJS_TOKEN_OBJECT_LITERAL, + NJS_TOKEN_OBJECT, + NJS_TOKEN_OBJECT_VALUE, NJS_TOKEN_PROPERTY, NJS_TOKEN_PROPERTY_DELETE, - NJS_TOKEN_ARRAY_CREATE, + NJS_TOKEN_ARRAY, - NJS_TOKEN_FUNCTION_CREATE, NJS_TOKEN_FUNCTION, + NJS_TOKEN_FUNCTION_EXPRESSION, NJS_TOKEN_FUNCTION_CALL, NJS_TOKEN_METHOD_CALL, NJS_TOKEN_ARGUMENT, NJS_TOKEN_RETURN, - NJS_TOKEN_REGEXP_LITERAL, + NJS_TOKEN_REGEXP, NJS_TOKEN_EXTERNAL, @@ -261,10 +261,6 @@ struct njs_parser_s { size_t scope_size; size_t scope_offset; - uint32_t nesting_arguments; - uint32_t nesting_arguments_size; - uint32_t method_arguments_size; - u_char *code_start; u_char *code_end; @@ -298,7 +294,7 @@ nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value); njs_index_t njs_parser_index(njs_parser_t *parser, uint32_t scope); nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node); nxt_int_t njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *node, njs_vmcode_operation_t last); + njs_parser_node_t *node); #define njs_generate_code(parser, type, code) \ diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c index e50f55f6..da229519 100644 --- a/njs/njs_parser_expression.c +++ b/njs/njs_parser_expression.c @@ -899,11 +899,11 @@ njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser, case NJS_TOKEN_NAME: func = node; func->token = NJS_TOKEN_FUNCTION_CALL; - parser->code_size += sizeof(njs_vmcode_function_t) - + sizeof(njs_vmcode_call_t); + parser->code_size += sizeof(njs_vmcode_function_frame_t) + + sizeof(njs_vmcode_function_call_t); break; - case NJS_TOKEN_FUNCTION_CREATE: + case NJS_TOKEN_FUNCTION_EXPRESSION: func = njs_parser_node_alloc(vm); if (nxt_slow_path(func == NULL)) { return NJS_TOKEN_ERROR; @@ -912,39 +912,44 @@ njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser, func->token = NJS_TOKEN_FUNCTION_CALL; func->left = node; func->index = node->index; - parser->code_size += sizeof(njs_vmcode_function_t) - + sizeof(njs_vmcode_call_t); + parser->code_size += sizeof(njs_vmcode_function_frame_t) + + sizeof(njs_vmcode_function_call_t); break; - case NJS_TOKEN_OBJECT_CONSTRUCTOR: - case NJS_TOKEN_ARRAY_CONSTRUCTOR: - case NJS_TOKEN_BOOLEAN_CONSTRUCTOR: - case NJS_TOKEN_NUMBER_CONSTRUCTOR: - case NJS_TOKEN_STRING_CONSTRUCTOR: - case NJS_TOKEN_FUNCTION_CONSTRUCTOR: - case NJS_TOKEN_REGEXP_CONSTRUCTOR: - case NJS_TOKEN_EVAL: + case NJS_TOKEN_PROPERTY: func = njs_parser_node_alloc(vm); if (nxt_slow_path(func == NULL)) { return NJS_TOKEN_ERROR; } - func->token = NJS_TOKEN_FUNCTION_CALL; + func->token = NJS_TOKEN_METHOD_CALL; func->left = node; - parser->code_size += sizeof(njs_vmcode_method_t) - + sizeof(njs_vmcode_call_t); + parser->code_size += sizeof(njs_vmcode_method_frame_t) + + sizeof(njs_vmcode_function_call_t); break; default: + /* + * NJS_TOKEN_OPEN_PARENTHESIS, + * NJS_TOKEN_OBJECT_CONSTRUCTOR, + * NJS_TOKEN_ARRAY_CONSTRUCTOR, + * NJS_TOKEN_BOOLEAN_CONSTRUCTOR, + * NJS_TOKEN_NUMBER_CONSTRUCTOR, + * NJS_TOKEN_STRING_CONSTRUCTOR, + * NJS_TOKEN_FUNCTION_CONSTRUCTOR, + * NJS_TOKEN_REGEXP_CONSTRUCTOR, + * NJS_TOKEN_EVAL. + */ func = njs_parser_node_alloc(vm); if (nxt_slow_path(func == NULL)) { return NJS_TOKEN_ERROR; } - func->token = NJS_TOKEN_METHOD_CALL; + func->token = NJS_TOKEN_FUNCTION_CALL; func->left = node; - parser->code_size += sizeof(njs_vmcode_method_t) - + sizeof(njs_vmcode_call_t); + parser->code_size += sizeof(njs_vmcode_function_frame_t) + + sizeof(njs_vmcode_function_call_t); + break; } token = njs_parser_arguments(vm, parser, func); @@ -1064,7 +1069,6 @@ njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser, njs_index_t index; njs_parser_node_t *node; - parser->nesting_arguments++; index = NJS_SCOPE_CALLEE_ARGUMENTS; do { @@ -1104,21 +1108,5 @@ njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser, return NJS_TOKEN_ILLEGAL; } - index = njs_index_size(index); - index += NJS_NATIVE_FRAME_SIZE + sizeof(njs_value_t); - - parser->nesting_arguments_size += index; - - parser->nesting_arguments--; - - if (parser->nesting_arguments == 0) { - - if (parser->method_arguments_size < parser->nesting_arguments_size) { - parser->method_arguments_size = parser->nesting_arguments_size; - } - - parser->nesting_arguments_size = 0; - } - return token; } diff --git a/njs/njs_vm.c b/njs/njs_vm.c index ae99d454..3b30a031 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -81,8 +81,20 @@ static nxt_noinline njs_ret_t njs_values_compare(njs_value_t *val1, njs_value_t *val2); static nxt_noinline nxt_bool_t njs_values_strict_equal(njs_value_t *val1, njs_value_t *val2); +static nxt_noinline njs_ret_t njs_function_frame_free(njs_vm_t *vm, + njs_native_frame_t *frame, njs_native_frame_t *skip); + +static njs_ret_t njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, + njs_value_t *narg); +static njs_ret_t njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, + njs_value_t *narg); static njs_ret_t njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint); +static njs_ret_t njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, + njs_value_t *value2); +static njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, + njs_value_t *invld2); + void njs_debug(njs_index_t index, njs_value_t *value); @@ -147,7 +159,9 @@ njs_vmcode_interpreter(njs_vm_t *vm) * njs_vmcode_if_true_jump(), * njs_vmcode_if_false_jump(), * njs_vmcode_validate(), - * njs_vmcode_call(), + * njs_vmcode_function_frame(), + * njs_vmcode_method_frame(), + * njs_vmcode_function_call(), * njs_vmcode_return(), * njs_vmcode_try_start(), * njs_vmcode_try_next(), @@ -203,7 +217,7 @@ njs_vmcode_interpreter(njs_vm_t *vm) case NJS_TRAP_INCDEC: case NJS_TRAP_PROPERTY: - ret = njs_vmcode_trap(vm, ret - NJS_TRAP_LAST, value1, value2); + ret = njs_vm_trap(vm, ret - NJS_TRAP_LAST, value1, value2); if (nxt_fast_path(ret == NXT_OK)) { goto again; @@ -220,7 +234,7 @@ njs_vmcode_interpreter(njs_vm_t *vm) for ( ;; ) { frame = (njs_frame_t *) vm->frame; - catch = frame->native.u.exception.catch; + catch = frame->native.exception.catch; if (catch != NULL) { vm->current = catch; @@ -240,7 +254,7 @@ njs_vmcode_interpreter(njs_vm_t *vm) vm->scopes[NJS_SCOPE_LOCAL] = frame->prev_local; vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments; - if (frame->native.start) { + if (frame->native.first) { nxt_mem_cache_free(vm->mem_cache_pool, frame); } } @@ -309,7 +323,7 @@ njs_value_release(njs_vm_t *vm, njs_value_t *value) njs_ret_t -njs_vmcode_object_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) +njs_vmcode_object(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { njs_object_t *object; @@ -328,7 +342,7 @@ njs_vmcode_object_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) njs_ret_t -njs_vmcode_array_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) +njs_vmcode_array(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { uint32_t size; njs_array_t *array; @@ -361,26 +375,25 @@ njs_vmcode_array_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) njs_ret_t -njs_vmcode_function_create(njs_vm_t *vm, njs_value_t *invld1, +njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { - njs_function_t *func; - njs_vmcode_function_create_t *code; + njs_function_t *function; + njs_vmcode_function_t *code; - func = nxt_mem_cache_zalign(vm->mem_cache_pool, sizeof(njs_value_t), - sizeof(njs_function_t)); + function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t)); - if (nxt_fast_path(func != NULL)) { - func->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION]; - func->args_offset = 1; + if (nxt_fast_path(function != NULL)) { + function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION]; + function->args_offset = 1; - code = (njs_vmcode_function_create_t *) vm->current; - func->code.script = code->function; - vm->retval.data.u.function = func; + code = (njs_vmcode_function_t *) vm->current; + function->u.lambda = code->lambda; + vm->retval.data.u.function = function; vm->retval.type = NJS_FUNCTION; vm->retval.data.truth = 1; - return sizeof(njs_vmcode_function_create_t); + return sizeof(njs_vmcode_function_t); } return NXT_ERROR; @@ -388,7 +401,7 @@ njs_vmcode_function_create(njs_vm_t *vm, njs_value_t *invld1, njs_ret_t -njs_vmcode_regexp_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) +njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { njs_regexp_t *regexp; njs_vmcode_regexp_t *code; @@ -2042,29 +2055,30 @@ njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset) njs_ret_t -njs_vmcode_function(njs_vm_t *vm, njs_value_t *name, njs_value_t *invld) +njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *invld, njs_value_t *name) { - njs_ret_t ret; - njs_value_t value, *this; - njs_param_t param; - njs_object_t *object; - njs_function_t *function; - njs_vmcode_function_t *func; + njs_ret_t ret; + njs_value_t val, *this, *value; + njs_param_t param; + njs_object_t *object; + njs_function_t *function; + njs_vmcode_function_frame_t *func; + + value = njs_vmcode_operand(vm, name); - if (nxt_fast_path(njs_is_function(name))) { + if (nxt_fast_path(njs_is_function(value))) { - func = (njs_vmcode_function_t *) vm->current; + func = (njs_vmcode_function_frame_t *) vm->current; - function = name->data.u.function; + function = value->data.u.function; if (function->native) { - this = njs_vmcode_native_frame(vm, &vm->retval, func->code.nargs, - func->code.ctor); + this = njs_function_native_frame(vm, function->u.native, 0, + &func->code); if (nxt_fast_path(this != NULL)) { *this = njs_value_void; - vm->retval = *name; - return sizeof(njs_vmcode_function_t); + return sizeof(njs_vmcode_function_frame_t); } return NXT_ERROR; @@ -2077,10 +2091,10 @@ njs_vmcode_function(njs_vm_t *vm, njs_value_t *name, njs_value_t *invld) return NXT_ERROR; } - value.data.u.object = object; - value.type = NJS_OBJECT; - value.data.truth = 1; - param.object = &value; + val.data.u.object = object; + val.type = NJS_OBJECT; + val.data.truth = 1; + param.object = &val; } else { param.object = (njs_value_t *) &njs_value_void; @@ -2089,34 +2103,34 @@ njs_vmcode_function(njs_vm_t *vm, njs_value_t *name, njs_value_t *invld) param.args = NULL; param.nargs = func->code.nargs; - ret = njs_vmcode_function_frame(vm, name, ¶m, func->code.ctor); + ret = njs_function_frame(vm, function, ¶m, func->code.ctor); if (nxt_fast_path(ret == NXT_OK)) { - return sizeof(njs_vmcode_function_t); + return sizeof(njs_vmcode_function_frame_t); } - } else { - vm->exception = &njs_exception_type_error; + return ret; } + vm->exception = &njs_exception_type_error; + return NXT_ERROR; } njs_ret_t -njs_vmcode_method(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) +njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *name, njs_value_t *object) { - uintptr_t nargs; - njs_ret_t ret; - njs_value_t *this; - njs_param_t param; - njs_extern_t *ext; - njs_object_prop_t *prop; - njs_vmcode_method_t *method; - njs_property_query_t pq; + njs_ret_t ret; + njs_value_t *this; + njs_param_t param; + njs_extern_t *ext; + njs_function_t *function; + njs_object_prop_t *prop; + njs_property_query_t pq; + njs_vmcode_method_frame_t *method; - method = (njs_vmcode_method_t *) vm->current; - nargs = method->code.nargs; + object = njs_vmcode_operand(vm, object); pq.query = NJS_PROPERTY_QUERY_GET; @@ -2125,36 +2139,40 @@ njs_vmcode_method(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) case NXT_OK: prop = pq.lhq.value; - if (njs_is_function(&prop->value) - && !prop->value.data.u.function->native) - { - param.object = object; - param.args = NULL; - param.nargs = nargs; + if (njs_is_function(&prop->value)) { - ret = njs_vmcode_function_frame(vm, &prop->value, ¶m, - method->code.ctor); + method = (njs_vmcode_method_frame_t *) vm->current; + function = prop->value.data.u.function; - if (nxt_fast_path(ret == NXT_OK)) { - return sizeof(njs_vmcode_method_t); - } + if (!function->native) { + param.object = object; + param.args = NULL; + param.nargs = method->code.nargs; - return ret; - } + ret = njs_function_frame(vm, function, ¶m, + method->code.ctor); + + if (nxt_fast_path(ret == NXT_OK)) { + return sizeof(njs_vmcode_method_frame_t); + } + + return ret; + } - vm->retval = prop->value; + this = njs_function_native_frame(vm, function->u.native, + prop->value.data.string_size, + &method->code); + if (nxt_slow_path(this == NULL)) { + return NXT_ERROR; + } - njs_retain(object); + njs_retain(object); + *this = *object; - this = njs_vmcode_native_frame(vm, &vm->retval, nargs, - method->code.ctor); - if (nxt_slow_path(this == NULL)) { - return NXT_ERROR; + return sizeof(njs_vmcode_method_frame_t); } - *this = *object; - - return sizeof(njs_vmcode_method_t); + break; case NJS_EXTERNAL_VALUE: ext = object->data.u.external; @@ -2162,16 +2180,12 @@ njs_vmcode_method(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq); if (ret == NXT_OK) { + method = (njs_vmcode_method_frame_t *) vm->current; ext = pq.lhq.value; if (ext->type == NJS_EXTERN_METHOD) { - vm->retval.type = NJS_NATIVE; - vm->retval.data.truth = 1; - vm->retval.data.string_size = 0; - vm->retval.data.u.method = ext->method; - - this = njs_vmcode_native_frame(vm, &vm->retval, nargs, - method->code.ctor); + this = njs_function_native_frame(vm, ext->method, 0, + &method->code); if (nxt_slow_path(this == NULL)) { return NXT_ERROR; @@ -2179,44 +2193,37 @@ njs_vmcode_method(njs_vm_t *vm, njs_value_t *object, njs_value_t *name) this->data.u.data = vm->external[ext->object]; - return sizeof(njs_vmcode_method_t); + return sizeof(njs_vmcode_method_frame_t); } } - /* Fall through. */ + break; default: - vm->exception = &njs_exception_type_error; - return NXT_ERROR; + break; } + + vm->exception = &njs_exception_type_error; + + return NXT_ERROR; } njs_ret_t -njs_vmcode_call(njs_vm_t *vm, njs_value_t *func, njs_value_t *retval) +njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) { - njs_ret_t ret; - njs_value_t *args; - njs_param_t param; - njs_native_t native; - njs_function_t *function; - njs_vmcode_call_t *call; - - call = (njs_vmcode_call_t *) vm->current; - vm->current += sizeof(njs_vmcode_call_t); + njs_ret_t ret; + njs_value_t *args; + njs_param_t param; + njs_native_frame_t *frame, *previous, *skip; + njs_vmcode_function_call_t *call; - if (njs_is_function(func)) { - function = func->data.u.function; + call = (njs_vmcode_function_call_t *) vm->current; + vm->current += sizeof(njs_vmcode_function_call_t); - if (!function->native) { - (void) njs_function_call(vm, function, (njs_index_t) retval); - return 0; - } - - native = function->code.native; - - } else { - native = func->data.u.method; + if (!vm->frame->native) { + (void) njs_function_call(vm, (njs_index_t) retval); + return 0; } param.retval = (njs_index_t) retval; @@ -2225,7 +2232,7 @@ njs_vmcode_call(njs_vm_t *vm, njs_value_t *func, njs_value_t *retval) param.args = args; param.object = args - 1; - ret = native(vm, ¶m); + ret = vm->frame->u.native(vm, ¶m); /* * A native method can return: * NXT_OK on method success; @@ -2236,11 +2243,27 @@ njs_vmcode_call(njs_vm_t *vm, njs_value_t *func, njs_value_t *retval) * The callee arguments must be preserved for NJS_PASS and NXT_AGAIN cases. */ if (ret == NXT_OK) { + skip = NULL; + frame = vm->frame; + previous = frame->previous; + + if (previous->skip) { + + if (previous->first) { + skip = previous; + } + + previous = previous->previous; + } + + vm->frame = previous; + + (void) njs_function_frame_free(vm, frame, skip); + /* * If a retval is in a callee arguments scope it * must be in the previous callee arguments scope. */ - vm->frame = vm->frame->previous; vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments; retval = njs_vmcode_operand(vm, retval); @@ -2267,7 +2290,7 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) njs_value_t *value; njs_frame_t *frame; njs_value_t *args; - njs_native_frame_t *previous; + njs_native_frame_t *previous, *skip; value = njs_vmcode_operand(vm, retval); @@ -2282,7 +2305,18 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) } } + skip = NULL; previous = frame->native.previous; + + if (previous->skip) { + + if (previous->first) { + skip = previous; + } + + previous = previous->previous; + } + vm->frame = previous; vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments; @@ -2299,16 +2333,28 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) /* GC: value external/internal++ depending on value and retval type */ *retval = *value; - vm->current = frame->return_address; + vm->current = frame->native.u.return_address; /* GC: arguments and local. */ njs_release(vm, &args[0]); - if (frame->native.start) { + return njs_function_frame_free(vm, &frame->native, skip); +} + + +static nxt_noinline njs_ret_t +njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame, + njs_native_frame_t *skip) +{ + if (frame->first) { nxt_mem_cache_free(vm->mem_cache_pool, frame); } + if (skip != NULL) { + nxt_mem_cache_free(vm->mem_cache_pool, skip); + } + return 0; } @@ -2337,17 +2383,17 @@ njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset) { njs_exception_t *e; - if (vm->frame->u.exception.catch != NULL) { + if (vm->frame->exception.catch != NULL) { e = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_exception_t)); if (nxt_slow_path(e == NULL)) { return NXT_ERROR; } - *e = vm->frame->u.exception; - vm->frame->u.exception.next = e; + *e = vm->frame->exception; + vm->frame->exception.next = e; } - vm->frame->u.exception.catch = vm->current + (njs_ret_t) offset; + vm->frame->exception.catch = vm->current + (njs_ret_t) offset; njs_set_invalid(value); @@ -2365,13 +2411,13 @@ njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset) { njs_exception_t *e; - e = vm->frame->u.exception.next; + e = vm->frame->exception.next; if (e == NULL) { - vm->frame->u.exception.catch = NULL; + vm->frame->exception.catch = NULL; } else { - vm->frame->u.exception = *e; + vm->frame->exception = *e; nxt_mem_cache_free(vm->mem_cache_pool, e); } @@ -2409,7 +2455,7 @@ njs_vmcode_catch(njs_vm_t *vm, njs_value_t *exception, njs_value_t *offset) return njs_vmcode_try_end(vm, exception, offset); } - vm->frame->u.exception.catch = vm->current + (njs_ret_t) offset; + vm->frame->exception.catch = vm->current + (njs_ret_t) offset; return sizeof(njs_vmcode_catch_t); } @@ -2606,7 +2652,94 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) } -njs_ret_t +static const njs_vmcode_1addr_t njs_trap_strings[] = { + { .code = { .operation = njs_vmcode_string_primitive, + .operands = NJS_VMCODE_1OPERAND, + .retval = NJS_VMCODE_NO_RETVAL }, + .index = 0 }, + { .code = { .operation = njs_vmcode_string_primitive, + .operands = NJS_VMCODE_1OPERAND, + .retval = NJS_VMCODE_NO_RETVAL }, + .index = 1 }, + { .code = { .operation = njs_vmcode_restart, + .operands = NJS_VMCODE_NO_OPERAND, + .retval = NJS_VMCODE_NO_RETVAL } }, +}; + + +static const njs_vmcode_1addr_t njs_trap_numbers[] = { + { .code = { .operation = njs_vmcode_number_primitive, + .operands = NJS_VMCODE_1OPERAND, + .retval = NJS_VMCODE_NO_RETVAL }, + .index = 0 }, + { .code = { .operation = njs_vmcode_number_primitive, + .operands = NJS_VMCODE_1OPERAND, + .retval = NJS_VMCODE_NO_RETVAL }, + .index = 1 }, + { .code = { .operation = njs_vmcode_restart, + .operands = NJS_VMCODE_NO_OPERAND, + .retval = NJS_VMCODE_NO_RETVAL } }, +}; + + +static const njs_vmcode_1addr_t njs_trap_number[] = { + { .code = { .operation = njs_vmcode_number_primitive, + .operands = NJS_VMCODE_1OPERAND, + .retval = NJS_VMCODE_NO_RETVAL }, + .index = 0 }, + { .code = { .operation = njs_vmcode_restart, + .operands = NJS_VMCODE_NO_OPERAND, + .retval = NJS_VMCODE_NO_RETVAL } }, +}; + + +static const njs_vm_trap_t njs_vm_traps[] = { + /* NJS_TRAP_PROPERTY */ { &njs_trap_strings[1], 0 }, + /* NJS_TRAP_STRINGS */ { &njs_trap_strings[0], 0 }, + /* NJS_TRAP_INCDEC */ { &njs_trap_numbers[1], 1 }, + /* NJS_TRAP_NUMBERS */ { &njs_trap_numbers[0], 0 }, + /* NJS_TRAP_NUMBER */ { &njs_trap_number[0], 0 }, +}; + + +static njs_ret_t +njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, + njs_value_t *value2) +{ + size_t size; + njs_value_t *values; + njs_native_frame_t *frame; + + size = NJS_NATIVE_FRAME_SIZE + 3 * sizeof(njs_value_t); + + frame = njs_function_frame_alloc(vm, size); + if (nxt_slow_path(frame == NULL)) { + return NXT_ERROR; + } + + frame->ctor = 0; + + values = njs_native_data(frame); + njs_set_invalid(&values[0]); + values[2] = *value2; + + frame->trap_reference = njs_vm_traps[trap].reference_value; + + if (njs_vm_traps[trap].reference_value) { + values[1].data.u.value = value1; + + } else { + values[1] = *value1; + } + + frame->u.restart = vm->current; + vm->current = (u_char *) njs_vm_traps[trap].code; + + return NXT_OK; +} + + +static njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { u_char *restart; @@ -2638,7 +2771,7 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) *retval = vm->retval; - if (frame->start) { + if (frame->first) { nxt_mem_cache_free(vm->mem_cache_pool, frame); } diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 133feeaf..3ba17210 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -82,7 +82,7 @@ typedef struct njs_string_s njs_string_t; typedef struct njs_object_init_s njs_object_init_t; typedef struct njs_object_value_s njs_object_value_t; typedef struct njs_array_s njs_array_t; -typedef struct njs_function_script_s njs_function_script_t; +typedef struct njs_function_lambda_s njs_function_lambda_t; typedef struct njs_regexp_s njs_regexp_t; typedef struct njs_regexp_pattern_s njs_regexp_pattern_t; typedef struct njs_extern_s njs_extern_t; @@ -115,9 +115,9 @@ typedef struct { #endif union { - njs_function_script_t *script; + njs_function_lambda_t *lambda; njs_native_t native; - } code; + } u; njs_value_t *args; } njs_function_t; @@ -139,7 +139,7 @@ union njs_value_s { * the maximum size of short string to 13. */ struct { - njs_value_type_t type:8; /* 4 bits */ + njs_value_type_t type:8; /* 4 bits */ /* * The truth field is set during value assignment and then can be * quickly tested by logical and conditional operations regardless @@ -147,44 +147,49 @@ union njs_value_s { * and short_string.length so when string size and length are zero * the string's value is false. */ - uint8_t truth; + uint8_t truth; /* 0xff if u.data.string is external string. */ - uint8_t external0; - uint8_t _spare; + uint8_t external0; + uint8_t _spare; - /* A long string size. */ - uint32_t string_size; + /* + * A long string size. + * Besides this field is used in native reentrant methods to + * store size of local state data allocated on stack frame. + */ + uint32_t string_size; union { - double number; - njs_string_t *string; - njs_object_t *object; - njs_array_t *array; - njs_object_value_t *object_value; - njs_function_t *function; - njs_regexp_t *regexp; - njs_getter_t getter; - njs_native_t method; - njs_extern_t *external; - njs_value_t *value; - void *data; + double number; + njs_string_t *string; + njs_object_t *object; + njs_array_t *array; + njs_object_value_t *object_value; + njs_function_t *function; + njs_function_lambda_t *lambda; + njs_regexp_t *regexp; + njs_getter_t getter; + njs_native_t method; + njs_extern_t *external; + njs_value_t *value; + void *data; } u; } data; struct { - njs_value_type_t type:8; /* 4 bits */ + njs_value_type_t type:8; /* 4 bits */ -#define NJS_STRING_SHORT 14 -#define NJS_STRING_LONG 15 +#define NJS_STRING_SHORT 14 +#define NJS_STRING_LONG 15 - uint8_t size:4; - uint8_t length:4; + uint8_t size:4; + uint8_t length:4; - u_char start[NJS_STRING_SHORT]; + u_char start[NJS_STRING_SHORT]; } short_string; - njs_value_type_t type:8; /* 4 bits */ + njs_value_type_t type:8; /* 4 bits */ }; @@ -230,7 +235,7 @@ union njs_value_s { .u.function = & (njs_function_t) { \ .native = 1, \ .args_offset = 1, \ - .code.native = _function, \ + .u.native = _function, \ } \ } \ } @@ -243,14 +248,6 @@ union njs_value_s { } } -#define njs_method(_method, _size) \ - { .data = { .type = NJS_NATIVE, \ - .truth = 1, \ - .string_size = _size, \ - .u = { .method = _method } \ - } } - - typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1, njs_value_t *value2); @@ -408,12 +405,6 @@ typedef struct { } njs_vmcode_3addr_t; -typedef struct { - njs_vmcode_t code; - njs_index_t retval; -} njs_vmcode_stop_t; - - typedef struct { njs_vmcode_t code; njs_index_t index; @@ -443,8 +434,8 @@ typedef struct { typedef struct { njs_vmcode_t code; njs_index_t retval; - njs_function_script_t *function; -} njs_vmcode_function_create_t; + njs_function_lambda_t *lambda; +} njs_vmcode_function_t; typedef struct { @@ -510,24 +501,33 @@ typedef struct { typedef struct { njs_vmcode_t code; - njs_index_t function; njs_index_t name; -} njs_vmcode_function_t; +} njs_vmcode_function_frame_t; typedef struct { njs_vmcode_t code; - njs_index_t function; njs_index_t object; njs_index_t method; -} njs_vmcode_method_t; +} njs_vmcode_method_frame_t; typedef struct { njs_vmcode_t code; njs_index_t retval; - njs_index_t function; -} njs_vmcode_call_t; +} njs_vmcode_function_call_t; + + +typedef struct { + njs_vmcode_t code; + njs_index_t retval; +} njs_vmcode_return_t; + + +typedef struct { + njs_vmcode_t code; + njs_index_t retval; +} njs_vmcode_stop_t; typedef struct { @@ -717,13 +717,13 @@ nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm); void njs_value_retain(njs_value_t *value); void njs_value_release(njs_vm_t *vm, njs_value_t *value); -njs_ret_t njs_vmcode_object_create(njs_vm_t *vm, njs_value_t *inlvd1, +njs_ret_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *inlvd2); -njs_ret_t njs_vmcode_array_create(njs_vm_t *vm, njs_value_t *inlvd1, +njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *inlvd2); -njs_ret_t njs_vmcode_function_create(njs_vm_t *vm, njs_value_t *inlvd1, +njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *invld2); -njs_ret_t njs_vmcode_regexp_create(njs_vm_t *vm, njs_value_t *inlvd1, +njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *invld2); njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, @@ -815,11 +815,12 @@ njs_ret_t njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond, njs_ret_t njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset); -njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *name, - njs_value_t *invld); -njs_ret_t njs_vmcode_method(njs_vm_t *vm, njs_value_t *object, +njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *invld, + njs_value_t *name); +njs_ret_t njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *method); -njs_ret_t njs_vmcode_call(njs_vm_t *vm, njs_value_t *func, njs_value_t *retval); +njs_ret_t njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, + njs_value_t *retval); njs_ret_t njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval); njs_ret_t njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld, @@ -836,14 +837,7 @@ njs_ret_t njs_vmcode_catch(njs_vm_t *vm, njs_value_t *invld, njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval); -njs_ret_t njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, - njs_value_t *narg); -njs_ret_t njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, - njs_value_t *narg); -njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, - njs_value_t *invld2); - -nxt_noinline void njs_number_set(njs_value_t *value, double num); +void njs_number_set(njs_value_t *value, double num); nxt_int_t njs_builtin_objects_create(njs_vm_t *vm); nxt_int_t njs_builtin_objects_clone(njs_vm_t *vm); diff --git a/njs/njscript.c b/njs/njscript.c index 49eef4b0..9825dd6e 100644 --- a/njs/njscript.c +++ b/njs/njscript.c @@ -218,7 +218,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) *start = parser->lexer->start; - ret = njs_generate_scope(vm, parser, node, njs_vmcode_stop); + ret = njs_generate_scope(vm, parser, node); if (nxt_slow_path(ret != NXT_OK)) { return NJS_ERROR; } @@ -288,20 +288,20 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external) frame->native.previous = NULL; frame->native.arguments = NULL; - frame->native.start = 1; + frame->native.first = 1; - frame->native.u.exception.next = NULL; - frame->native.u.exception.catch = NULL; + frame->native.exception.next = NULL; + frame->native.exception.catch = NULL; frame->prev_arguments = NULL; frame->local = NULL; frame->closure = NULL; - frame->native.size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size); + frame->native.free_size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size); values = (u_char *) frame + NJS_GLOBAL_FRAME_SIZE; - frame->native.last = values + scope_size; + frame->native.free = values + scope_size; nvm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values; memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope, @@ -374,10 +374,10 @@ njs_vm_run(njs_vm_t *vm) } -void -njs_vm_return(njs_vm_t *vm, njs_value_t *retval) +njs_ret_t +njs_vm_return_string(njs_vm_t *vm, u_char *start, size_t size) { - vm->retval = *retval; + return njs_string_create(vm, &vm->retval, start, size, 0); } diff --git a/njs/njscript.h b/njs/njscript.h index 7e9de7db..8cd238a6 100644 --- a/njs/njscript.h +++ b/njs/njscript.h @@ -88,7 +88,8 @@ NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external); NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t *vm); -NXT_EXPORT void njs_vm_return(njs_vm_t *vm, njs_value_t *retval); +NXT_EXPORT njs_ret_t njs_vm_return_string(njs_vm_t *vm, u_char *start, + size_t size); NXT_EXPORT nxt_int_t njs_vm_retval(njs_vm_t *vm, nxt_str_t *retval); NXT_EXPORT nxt_int_t njs_vm_exception(njs_vm_t *vm, nxt_str_t *retval); diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index facd48a5..a9f50eb9 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -1381,9 +1381,6 @@ static njs_unit_test_t njs_test[] = { nxt_string("typeof /./i"), nxt_string("object") }, - { nxt_string("typeof $r"), - nxt_string("undefined") }, - { nxt_string("typeof a"), nxt_string("undefined") }, @@ -2003,44 +2000,6 @@ static njs_unit_test_t njs_test[] = { nxt_string("a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes(1, 3)"), nxt_string("§±") }, - { nxt_string("a = $r.uri; s = a.fromUTF8(); s.length +' '+ s"), - nxt_string("3 АБВ") }, - - { nxt_string("a = $r.uri; s = a.fromUTF8(2); s.length +' '+ s"), - nxt_string("2 БВ") }, - - { nxt_string("a = $r.uri; s = a.fromUTF8(2, 4); s.length +' '+ s"), - nxt_string("1 Б") }, - - { nxt_string("a = $r.uri; a +' '+ a.length +' '+ a"), - nxt_string("АБВ 6 АБВ") }, - - { nxt_string("$r.uri = 'αβγ'; a = $r.uri; a.length +' '+ a"), - nxt_string("6 αβγ") }, - - { nxt_string("$r.uri.length +' '+ $r.uri"), - nxt_string("6 АБВ") }, - - { nxt_string("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"), - nxt_string("4 БВ") }, - - { nxt_string("a = $r.host; a +' '+ a.length +' '+ a"), - nxt_string("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") }, - - { nxt_string("a = $r.host; a.substr(2, 2)"), - nxt_string("Б") }, - - { nxt_string("a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"), - nxt_string("User-Agent|АБВ 17 User-Agent|АБВ") }, - - { nxt_string("var a='';" - "for (p in $r.header) { a += p +':'+ $r.header[p] +',' }" - "a"), - nxt_string("01:01|АБВ,02:02|АБВ,03:03|АБВ,") }, - - { nxt_string("$r.nonexistent"), - nxt_string("undefined") }, - { nxt_string("a = 'abcdefgh'; a.substr(3, 15)"), nxt_string("defgh") }, @@ -2160,7 +2119,59 @@ static njs_unit_test_t njs_test[] = " valueOf: function() { return 0 } }; '12'[n]"), nxt_string("2") }, - /**/ + /* Externals. */ + + { nxt_string("typeof $r"), + nxt_string("undefined") }, + + { nxt_string("a = $r.uri; s = a.fromUTF8(); s.length +' '+ s"), + nxt_string("3 АБВ") }, + + { nxt_string("a = $r.uri; s = a.fromUTF8(2); s.length +' '+ s"), + nxt_string("2 БВ") }, + + { nxt_string("a = $r.uri; s = a.fromUTF8(2, 4); s.length +' '+ s"), + nxt_string("1 Б") }, + + { nxt_string("a = $r.uri; a +' '+ a.length +' '+ a"), + nxt_string("АБВ 6 АБВ") }, + + { nxt_string("$r.uri = 'αβγ'; a = $r.uri; a.length +' '+ a"), + nxt_string("6 αβγ") }, + + { nxt_string("$r.uri.length +' '+ $r.uri"), + nxt_string("6 АБВ") }, + + { nxt_string("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"), + nxt_string("4 БВ") }, + + { nxt_string("a = $r.host; a +' '+ a.length +' '+ a"), + nxt_string("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") }, + + { nxt_string("a = $r.host; a.substr(2, 2)"), + nxt_string("Б") }, + + { nxt_string("a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"), + nxt_string("User-Agent|АБВ 17 User-Agent|АБВ") }, + + { nxt_string("var a='';" + "for (p in $r.header) { a += p +':'+ $r.header[p] +',' }" + "a"), + nxt_string("01:01|АБВ,02:02|АБВ,03:03|АБВ,") }, + + { nxt_string("$r.external('YES')"), + nxt_string("АБВ") }, + +#if 0 + { nxt_string("$r.external.call($r, 'YES')"), + nxt_string("АБВ") }, + + { nxt_string("$r.external.apply($r, ['YES'])"), + nxt_string("АБВ") }, +#endif + + { nxt_string("$r.nonexistent"), + nxt_string("undefined") }, { nxt_string("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"), nxt_string("1077") }, @@ -2248,6 +2259,14 @@ static njs_unit_test_t njs_test[] = { nxt_string("'abc ABC aBc'.match(/abc/ig) +''"), nxt_string("abc,ABC,aBc") }, + /* Functions. */ + + { nxt_string("var a = 1; a()"), + nxt_string("TypeError") }, + + { nxt_string("var o = {a:1}; o.a()"), + nxt_string("TypeError") }, + { nxt_string("var q = 1; function x(a, b, c) { q = a } x(5); q"), nxt_string("5") }, @@ -2269,6 +2288,13 @@ static njs_unit_test_t njs_test[] = { nxt_string("function f(a) { return (a > 1) ? a * f(a - 1) : 1 } f(10)"), nxt_string("3628800") }, + { nxt_string("var g = function f(a) { return (a > 1) ? a * f(a - 1) : 1 };" + "g(10)"), + nxt_string("3628800") }, + + { nxt_string("(function f(a) { return (a > 1) ? a * f(a - 1) : 1 })(10)"), + nxt_string("3628800") }, + /* Recursive fibonacci. */ { nxt_string("function fibo(n) {" @@ -2317,6 +2343,10 @@ static njs_unit_test_t njs_test[] = { nxt_string("function f() { return 5 } f()"), nxt_string("5") }, + { nxt_string("function g(x) { return x + 1 }" + "function f(x) { return x } f(g)(2)"), + nxt_string("3") }, + { nxt_string("function f() { return 5 } f(1)"), nxt_string("5") }, @@ -2371,6 +2401,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("a = {}; function f(a) { return a + 1 } a.b = f(2); a.b"), nxt_string("3") }, + { nxt_string("(function(x) { return x + 1 })(2)"), + nxt_string("3") }, + + { nxt_string("(function(x) { return x + 1 }(2))"), + nxt_string("3") }, + { nxt_string("a = (function() { return 1 })(); a"), nxt_string("1") }, @@ -2392,6 +2428,13 @@ static njs_unit_test_t njs_test[] = { nxt_string("a = 0, function(a) { return a + 1 }(2); a"), nxt_string("0") }, + { nxt_string("a = (0, function(a) { return a + 1 }(2)); a"), + nxt_string("3") }, + + { nxt_string("var a = +function f(a) { return a + 1 }(2)" + "var b = f(5); a"), + nxt_string("ReferenceError") }, + { nxt_string("var o = { f: function(a) { return a * 2 } }; o.f(5)"), nxt_string("10") }, @@ -3196,6 +3239,33 @@ njs_unit_test_header_each_external(njs_vm_t *vm, njs_value_t *value, void *obj, } +static njs_ret_t +njs_unit_test_method_external(njs_vm_t *vm, njs_param_t *param) +{ + nxt_int_t ret; + nxt_str_t s; + uintptr_t next; + njs_unit_test_req *r; + + next = 0; + + if (param->nargs != 0) { + + ret = njs_value_string_copy(vm, &s, njs_argument(param->args, 0), + &next); + + if (ret == NXT_OK && s.len == 3 && memcmp(s.data, "YES", 3) == 0) { + r = njs_value_data(param->object); + njs_vm_return_string(vm, r->uri.data, r->uri.len); + + return NXT_OK; + } + } + + return NXT_ERROR; +} + + static njs_ret_t njs_unit_test_undefined_external(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data) @@ -3244,6 +3314,18 @@ static njs_external_t njs_unit_test_r_external[] = { NULL, 0 }, + { nxt_string("external"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_unit_test_method_external, + 0 }, + };