From: Igor Sysoev Date: Mon, 8 Feb 2016 14:41:09 +0000 (+0300) Subject: Native function continuation support. X-Git-Tag: 0.1.0~74 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=31e62ed06c0bab5bae4fcd60950425ce5b21c45a;p=njs.git Native function continuation support. --- diff --git a/njs/njs_array.c b/njs/njs_array.c index 969b3776..ef53cd57 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -24,12 +24,21 @@ typedef struct { - njs_value_t retval; - int32_t index; - uint32_t length; + njs_continuation_t continuation; + njs_value_t *values; + uint32_t max; +} njs_array_join_t; + + +typedef struct { + njs_value_t retval; + int32_t index; + uint32_t length; } njs_array_next_t; +static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm, + njs_param_t *param); static nxt_noinline njs_value_t *njs_array_copy(njs_value_t *dst, njs_value_t *src); static nxt_noinline nxt_int_t njs_array_next(njs_value_t *value, nxt_uint_t n, @@ -508,13 +517,11 @@ njs_array_prototype_to_string(njs_vm_t *vm, njs_param_t *param) static njs_ret_t njs_array_prototype_join(njs_vm_t *vm, njs_param_t *param) { - u_char *p; - size_t size, length; - nxt_int_t ret; - nxt_uint_t i, n, max; - njs_array_t *array; - njs_value_t *this, *value, *values; - njs_string_prop_t separator, string; + uint32_t max; + nxt_uint_t i, n; + njs_array_t *array; + njs_value_t *this, *value, *values; + njs_array_join_t *join; this = param->this; @@ -528,15 +535,6 @@ njs_array_prototype_join(njs_vm_t *vm, njs_param_t *param) goto empty; } - if (param->nargs != 0) { - value = ¶m->args[0]; - - } else { - value = (njs_value_t *) &njs_string_comma; - } - - (void) njs_string_prop(&separator, value); - max = 0; for (i = 0; i < array->length; i++) { @@ -546,37 +544,118 @@ njs_array_prototype_join(njs_vm_t *vm, njs_param_t *param) } } - values = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), - sizeof(njs_value_t) * max); - if (nxt_slow_path(values == NULL)) { - return NXT_ERROR; + if (max != 0) { + join = nxt_mem_cache_alloc(vm->mem_cache_pool, + sizeof(njs_array_join_t)); + if (nxt_slow_path(join == NULL)) { + return NXT_ERROR; + } + + values = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), + sizeof(njs_value_t) * max); + if (nxt_slow_path(values == NULL)) { + return NXT_ERROR; + } + + join->continuation.function = njs_array_prototype_join_continuation; + join->continuation.this = this; + join->continuation.args = param->args; + join->continuation.nargs = param->nargs; + join->values = values; + join->max = max; + vm->frame->continuation = &join->continuation; + + n = 0; + + for (i = 0; i < array->length; i++) { + value = &array->start[i]; + if (njs_is_valid(value) && !njs_is_string(value)) { + values[n++] = *value; + + if (n >= max) { + break; + } + } + } } + return njs_array_prototype_join_continuation(vm, param); + +empty: + + vm->retval = njs_string_empty; + + return NXT_OK; +} + + +static njs_ret_t +njs_array_prototype_join_continuation(njs_vm_t *vm, njs_param_t *param) +{ + u_char *p; + size_t size, length, mask; + uint32_t max; + nxt_uint_t i, n; + njs_array_t *array; + njs_value_t *value, *values; + njs_array_join_t *join; + njs_string_prop_t separator, string; + + if (param->nargs != 0) { + value = ¶m->args[0]; + + } else { + value = (njs_value_t *) &njs_string_comma; + } + + (void) njs_string_prop(&separator, value); + + array = param->this->data.u.array; + size = separator.size * (array->length - 1); length = separator.length * (array->length - 1); n = 0; + max = 0; + values = NULL; + + join = (njs_array_join_t *) vm->frame->continuation; + + if (join != NULL) { + values = join->values; + max = join->max; + } + + mask = -1; + for (i = 0; i < array->length; i++) { value = &array->start[i]; if (njs_is_valid(value)) { if (!njs_is_string(value)) { - ret = njs_value_to_string(vm, &values[n], value); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } - value = &values[n++]; + + if (!njs_is_string(value)) { + vm->frame->trap_scratch.data.u.value = value; + + return NJS_TRAP_STRING_ARG; + } } (void) njs_string_prop(&string, value); size += string.size; length += string.length; + + if (string.length == 0 && string.size != 0) { + mask = 0; + } } } + length &= mask; + p = njs_string_alloc(vm, &vm->retval, size, length); if (nxt_slow_path(p == NULL)) { return NXT_ERROR; @@ -610,11 +689,7 @@ njs_array_prototype_join(njs_vm_t *vm, njs_param_t *param) nxt_mem_cache_free(vm->mem_cache_pool, values); - return NXT_OK; - -empty: - - vm->retval = njs_string_empty; + vm->frame->continuation = NULL; return NXT_OK; } diff --git a/njs/njs_function.c b/njs/njs_function.c index 2c2af491..6e84a909 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -57,7 +57,7 @@ njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, return NULL; } - frame->u.function = function; + frame->function = function; frame->ctor = code->ctor; this = (njs_value_t *) ((u_char *) njs_native_data(frame) @@ -99,8 +99,11 @@ njs_function_frame_alloc(njs_vm_t *vm, size_t size) frame->free_size = spare_size - size; frame->free = (u_char *) frame + size; + frame->continuation = NULL; + frame->trap_restart = NULL; frame->ctor = 0; frame->reentrant = 0; + frame->trap_frame = 0; frame->trap_tries = 0; frame->trap_reference = 0; @@ -169,7 +172,7 @@ njs_function_frame(njs_vm_t *vm, njs_function_t *function, njs_param_t *param, return NXT_ERROR; } - native_frame->u.function = function; + native_frame->function = function; native_frame->ctor = ctor; args = (njs_value_t *) ((u_char *) native_frame + NJS_FRAME_SIZE); @@ -217,8 +220,8 @@ njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance) frame->retval = retval; - function = frame->native.u.function; - frame->native.u.return_address = vm->current + advance; + function = frame->native.function; + frame->return_address = vm->current + advance; vm->current = function->u.lambda->u.start; frame->prev_arguments = vm->scopes[NJS_SCOPE_ARGUMENTS]; @@ -416,6 +419,11 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_param_t *param) { njs_function_t *bound; + if (!njs_is_function(param->this)) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + bound = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t)); if (nxt_fast_path(bound != NULL)) { diff --git a/njs/njs_function.h b/njs/njs_function.h index 74abdd02..1ba98e25 100644 --- a/njs/njs_function.h +++ b/njs/njs_function.h @@ -52,6 +52,14 @@ struct njs_function_lambda_s { (void *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE) +typedef struct { + njs_native_t function; + njs_value_t *this; + njs_value_t *args; + nxt_uint_t nargs; +} njs_continuation_t; + + typedef struct njs_exception_s njs_exception_t; struct njs_exception_s { @@ -71,15 +79,8 @@ struct njs_native_frame_s { u_char *free; - /* - * The return_address is required in njs_frame_t only, however, it - * can be stored here just after function address has been fetched. - */ - union { - njs_function_t *function; - u_char *return_address; - } u; - + njs_function_t *function; + njs_continuation_t *continuation; njs_native_frame_t *previous; njs_value_t *arguments; @@ -103,6 +104,9 @@ struct njs_native_frame_s { /* The function is reentrant. */ uint8_t reentrant:1; /* 1 bit */ + /* A frame of trap generated from continuation. */ + uint8_t trap_frame:1; /* 1 bit */ + /* A number of trap tries, it can be no more than three. */ uint8_t trap_tries:2; /* 2 bits */ @@ -117,6 +121,7 @@ struct njs_native_frame_s { typedef struct { njs_native_frame_t native; + u_char *return_address; njs_value_t *prev_arguments; njs_value_t *prev_local; njs_value_t *local; diff --git a/njs/njs_vm.c b/njs/njs_vm.c index 61ffbde5..d0a27d6a 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -86,7 +86,7 @@ static nxt_noinline njs_ret_t njs_function_frame_free(njs_vm_t *vm, static void njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, njs_value_t *value2); -static void njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap); +static njs_ret_t njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap); 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, @@ -233,9 +233,12 @@ njs_vmcode_interpreter(njs_vm_t *vm) case NJS_TRAP_NUMBER_ARG: case NJS_TRAP_STRING_ARG: - njs_vm_trap_argument(vm, ret - NJS_TRAP_BASE); + ret = njs_vm_trap_argument(vm, ret - NJS_TRAP_BASE); + if (nxt_fast_path(ret == NXT_OK)) { + goto again; + } - goto again; + break; default: break; @@ -2238,11 +2241,13 @@ 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_native_frame_t *frame, *previous, *skip; + njs_continuation_t *continuation; njs_vmcode_function_call_t *call; - function = vm->frame->u.function; + function = vm->frame->function; if (!function->native) { (void) njs_function_call(vm, (njs_index_t) retval, @@ -2253,18 +2258,32 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) call = (njs_vmcode_function_call_t *) vm->current; args = vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS]; - ret = njs_normalize_args(vm, args - 1, function->args_types, - call->code.nargs); - if (ret != NJS_OK) { - return ret; - } + continuation = vm->frame->continuation; + + if (continuation == NULL) { + ret = njs_normalize_args(vm, args - 1, function->args_types, + call->code.nargs); + if (ret != NJS_OK) { + return ret; + } - param.retval = (njs_index_t) retval; - param.nargs = call->code.nargs - 1; - param.args = args; - param.this = args - 1; + param.args = args; + param.this = args - 1; + param.nargs = call->code.nargs - 1; + param.retval = (njs_index_t) retval; - ret = vm->frame->u.function->u.native(vm, ¶m); + native = function->u.native; + + } else { + param.this = continuation->this; + param.args = continuation->args; + param.retval = (njs_index_t) retval; + param.nargs = continuation->nargs; + + native = continuation->function; + } + + ret = native(vm, ¶m); /* * A native method can return: * NXT_OK on method success; @@ -2487,7 +2506,7 @@ 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->native.u.return_address; + vm->current = frame->return_address; /* GC: arguments and local. */ @@ -2707,6 +2726,10 @@ static void njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, njs_value_t *value2) { + njs_native_frame_t *frame; + + frame = vm->frame; + /* * The trap_scratch value is for results of "valueOf" and "toString" * methods. The trap_values[] are original operand values which will @@ -2715,35 +2738,51 @@ njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1, * original operand values for the second method call if the first * method call will return non-primitive value. */ - njs_set_invalid(&vm->frame->trap_scratch); - vm->frame->trap_values[1] = *value2; - vm->frame->trap_reference = njs_vm_traps[trap].reference_value; + njs_set_invalid(&frame->trap_scratch); + frame->trap_values[1] = *value2; + frame->trap_reference = njs_vm_traps[trap].reference_value; if (njs_vm_traps[trap].reference_value) { - vm->frame->trap_values[0].data.u.value = value1; + frame->trap_values[0].data.u.value = value1; } else { - vm->frame->trap_values[0] = *value1; + frame->trap_values[0] = *value1; } - vm->frame->trap_restart = vm->current; + frame->trap_restart = vm->current; vm->current = (u_char *) njs_vm_traps[trap].code; } -static void +static njs_ret_t njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap) { - njs_value_t *value; + njs_value_t *value; + njs_native_frame_t *frame; - value = vm->frame->trap_scratch.data.u.value; - vm->frame->trap_values[1].data.u.value = value; - vm->frame->trap_values[0] = *value; + frame = vm->frame; + value = frame->trap_scratch.data.u.value; + njs_set_invalid(&frame->trap_scratch); - njs_set_invalid(&vm->frame->trap_scratch); + if (frame->continuation != NULL) { + frame = njs_function_frame_alloc(vm, NJS_NATIVE_FRAME_SIZE); + + if (nxt_slow_path(frame == NULL)) { + return NXT_ERROR; + } - vm->frame->trap_restart = vm->current; + frame->trap_frame = 1; + } + + frame->trap_values[1].data.u.value = value; + frame->trap_values[0] = *value; + + njs_set_invalid(&frame->trap_scratch); + + frame->trap_restart = vm->current; vm->current = (u_char *) njs_vm_traps[trap].code; + + return NXT_OK; } @@ -2803,9 +2842,10 @@ static njs_ret_t njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *inlvd2) { - double num; - njs_ret_t ret; - njs_value_t *value; + double num; + njs_ret_t ret; + njs_value_t *value; + njs_native_frame_t *frame; value = &vm->frame->trap_values[0]; @@ -2823,8 +2863,19 @@ njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1, njs_number_set(value, num); } - *vm->frame->trap_values[1].data.u.value = *value; + frame = vm->frame; + *frame->trap_values[1].data.u.value = *value; + vm->current = vm->frame->trap_restart; + frame->trap_restart = NULL; + + if (frame->trap_frame) { + vm->frame = frame->previous; + + if (frame->first) { + nxt_mem_cache_free(vm->mem_cache_pool, frame); + } + } return 0; } @@ -2837,8 +2888,9 @@ static njs_ret_t njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *inlvd2) { - njs_ret_t ret; - njs_value_t *value; + njs_ret_t ret; + njs_value_t *value; + njs_native_frame_t *frame; value = &vm->frame->trap_values[0]; @@ -2848,8 +2900,19 @@ njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1, ret = njs_primitive_value_to_string(vm, value, value); if (nxt_fast_path(ret == NXT_OK)) { - *vm->frame->trap_values[1].data.u.value = *value; - vm->current = vm->frame->trap_restart; + frame = vm->frame; + *frame->trap_values[1].data.u.value = *value; + + vm->current = frame->trap_restart; + frame->trap_restart = NULL; + + if (frame->trap_frame) { + vm->frame = frame->previous; + + if (frame->first) { + nxt_mem_cache_free(vm->mem_cache_pool, frame); + } + } } } @@ -2871,6 +2934,7 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) njs_value_t *retval; njs_object_prop_t *prop; nxt_lvlhsh_query_t lhq; + njs_continuation_t *continuation; static const uint32_t hashes[] = { NJS_VALUE_OF_HASH, @@ -2882,6 +2946,29 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) nxt_string("toString"), }; + continuation = vm->frame->continuation; + + if (continuation != NULL) { + param.this = continuation->this; + param.args = continuation->args; + param.nargs = continuation->nargs; + param.retval = (njs_index_t) &vm->frame->trap_scratch; + + ret = continuation->function(vm, ¶m); + + if (ret != NXT_OK) { + return ret; + } + + if (njs_is_primitive(&vm->retval)) { + *value = vm->retval; + njs_set_invalid(&vm->frame->trap_scratch); + vm->frame->trap_tries = 0; + + return 1; + } + } + if (!njs_is_primitive(value)) { retval = &vm->frame->trap_scratch; @@ -2959,19 +3046,22 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) u_char *restart; njs_ret_t ret; njs_value_t *retval, *value1; + njs_native_frame_t *frame; njs_vmcode_generic_t *vmcode; - restart = vm->frame->trap_restart; + frame = vm->frame; + restart = frame->trap_restart; + frame->trap_restart = NULL; vm->current = restart; vmcode = (njs_vmcode_generic_t *) restart; - value1 = &vm->frame->trap_values[0]; + value1 = &frame->trap_values[0]; - if (vm->frame->trap_reference) { + if (frame->trap_reference) { value1 = value1->data.u.value; } - ret = vmcode->code.operation(vm, value1, &vm->frame->trap_values[1]); + ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]); retval = njs_vmcode_operand(vm, vmcode->operand1); @@ -2979,6 +3069,14 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) *retval = vm->retval; + if (frame->trap_frame) { + vm->frame = frame->previous; + + if (frame->first) { + nxt_mem_cache_free(vm->mem_cache_pool, frame); + } + } + return ret; } diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 552eaa21..469d6415 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -888,6 +888,9 @@ njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_ret_t njs_normalize_args(njs_vm_t *vm, njs_value_t *args, uint8_t *args_types, nxt_uint_t nargs); +njs_ret_t njs_native_function_arguments(njs_vm_t *vm, njs_value_t *args, + uint8_t *args_types, nxt_uint_t nargs); + njs_ret_t njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src); void njs_number_set(njs_value_t *value, double num); diff --git a/njs/njscript.c b/njs/njscript.c index 540caab1..5a7b4357 100644 --- a/njs/njscript.c +++ b/njs/njscript.c @@ -279,18 +279,21 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external) size = NJS_GLOBAL_FRAME_SIZE + scope_size + NJS_FRAME_SPARE_SIZE; size = nxt_align_size(size, NJS_FRAME_SPARE_SIZE); - frame = nxt_mem_cache_align(nmcp, sizeof(njs_value_t), size); + frame = nxt_mem_cache_zalign(nmcp, sizeof(njs_value_t), size); if (nxt_slow_path(frame == NULL)) { goto fail; } nvm->frame = &frame->native; + frame->native.trap_restart = NULL; + frame->native.continuation = NULL; frame->native.previous = NULL; frame->native.arguments = NULL; frame->native.first = 1; frame->native.skip = 0; frame->native.reentrant = 0; + frame->native.trap_frame = 0; frame->native.trap_tries = 0; frame->native.exception.next = NULL; diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 4243b951..176d30d3 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -1986,6 +1986,18 @@ static njs_unit_test_t njs_test[] = { nxt_string("a = []; a.concat([])"), nxt_string("") }, + { nxt_string("var s = { toString: function() { return 'S' } }" + "var v = { toString: 8, valueOf: function() { return 'V' } }" + "var o = [9]; o.join = function() { return 'O' };" + "var a = [1,2,3,[4,5,6],s,v,o]; a.join('')"), + nxt_string("1234,5,6SVO") }, + + { nxt_string("var s = { toString: function() { return 'S' } }" + "var v = { toString: 8, valueOf: function() { return 'V' } }" + "var o = [9]; o.join = function() { return 'O' };" + "var a = [1,2,3,[4,5,6],s,v,o]; a"), + nxt_string("1,2,3,4,5,6,S,V,O") }, + /* Array.toString(). */ { nxt_string("a = [1,2,3]; a.join = 'NO';"