From: Igor Sysoev Date: Sun, 21 Feb 2016 21:06:53 +0000 (+0300) Subject: Continuations have been refactored. X-Git-Tag: 0.1.0~58 X-Git-Url: http://git.kaiwu.me/sitemap.xml?a=commitdiff_plain;h=d9576bfa892d147a76aea72c52c16d25b81613bf;p=njs.git Continuations have been refactored. --- diff --git a/njs/njs_array.c b/njs/njs_array.c index 9cf8d150..0eda9d79 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -24,25 +24,42 @@ typedef struct { - njs_continuation_t cont; - njs_value_t *values; - uint32_t max; + njs_continuation_t cont; + njs_value_t *values; + uint32_t max; } njs_array_join_t; typedef struct { - njs_value_t retval; - int32_t index; - uint32_t length; + union { + njs_continuation_t cont; + u_char padding[NJS_CONTINUATION_SIZE]; + }; + njs_value_t retval; + int32_t index; + uint32_t length; } njs_array_next_t; +static njs_ret_t +njs_array_prototype_to_string_continuation(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t retval); static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); 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, - nxt_uint_t length); +static nxt_noinline njs_ret_t njs_array_prototype_for_each_cont(njs_vm_t *vm, + njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); +static nxt_noinline njs_ret_t njs_array_prototype_some_cont(njs_vm_t *vm, + njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); +static nxt_noinline njs_ret_t njs_array_prototype_every_cont(njs_vm_t *vm, + njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); +static nxt_noinline njs_ret_t njs_array_iterator_args(njs_vm_t *vm, + njs_value_t * args, nxt_uint_t nargs); +static nxt_noinline nxt_int_t njs_array_iterator_next(njs_value_t *value, + nxt_uint_t n, nxt_uint_t length); +static nxt_noinline njs_ret_t njs_array_iterator_apply(njs_vm_t *vm, + njs_array_next_t *next, njs_value_t *args, nxt_uint_t nargs); njs_value_t * @@ -476,6 +493,8 @@ njs_array_prototype_shift(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, /* * ECMAScript 5.1: try first to use object method "join", then * use the standard built-in method Object.prototype.toString(). + * Array.toString() must be a continuation otherwise it may + * endlessly call Array.join(). */ static njs_ret_t @@ -483,8 +502,12 @@ njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval) { njs_object_prop_t *prop; + njs_continuation_t *cont; nxt_lvlhsh_query_t lhq; + cont = (njs_continuation_t *) njs_continuation(vm->frame); + cont->function = njs_array_prototype_to_string_continuation; + if (njs_is_object(&args[0])) { lhq.key_hash = NJS_JOIN_HASH; lhq.key.len = sizeof("join") - 1; @@ -502,6 +525,17 @@ njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } +static njs_ret_t +njs_array_prototype_to_string_continuation(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t retval) +{ + /* Skip retval update. */ + vm->frame->skip = 1; + + return NXT_OK; +} + + static njs_ret_t njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) @@ -522,6 +556,9 @@ njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, goto empty; } + join = (njs_array_join_t *) njs_continuation(vm->frame); + join->values = NULL; + join->max = 0; max = 0; for (i = 0; i < array->length; i++) { @@ -532,24 +569,16 @@ njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } 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 = (njs_array_join_t *) njs_continuation(vm->frame); join->cont.function = njs_array_prototype_join_continuation; - join->cont.args = args; - join->cont.nargs = nargs; join->values = values; join->max = max; - vm->frame->continuation = &join->cont; n = 0; @@ -588,16 +617,9 @@ njs_array_prototype_join_continuation(njs_vm_t *vm, njs_value_t *args, njs_array_join_t *join; njs_string_prop_t separator, string; - join = (njs_array_join_t *) vm->frame->continuation; - - if (join != NULL) { - values = join->values; - max = join->max; - - } else { - values = NULL; - max = 0; - } + join = (njs_array_join_t *) njs_continuation(vm->frame); + values = join->values; + max = join->max; size = 0; length = 0; @@ -679,8 +701,6 @@ njs_array_prototype_join_continuation(njs_vm_t *vm, njs_value_t *args, nxt_mem_cache_free(vm->mem_cache_pool, values); - vm->frame->continuation = NULL; - return NXT_OK; } @@ -746,179 +766,156 @@ njs_array_copy(njs_value_t *dst, njs_value_t *src) } -nxt_inline nxt_int_t -njs_array_iterator_args(njs_vm_t *vm, njs_value_t * args, nxt_uint_t nargs) +static njs_ret_t +njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) { - if (nargs > 1 && njs_is_array(&args[0]) && njs_is_function(&args[1])) { - return NXT_OK; + nxt_int_t ret; + njs_array_next_t *next; + + ret = njs_array_iterator_args(vm, args, nargs); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; } - vm->exception = &njs_exception_type_error; + next = njs_continuation(vm->frame); + next->cont.function = njs_array_prototype_for_each_cont; - return NXT_ERROR; + return njs_array_prototype_for_each_cont(vm, args, nargs, unused); } static njs_ret_t -njs_array_prototype_for_each(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, - njs_index_t unused) +njs_array_prototype_for_each_cont(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) { - nxt_int_t n, ret; - njs_array_t *array; njs_array_next_t *next; - njs_value_t arguments[4]; - if (!vm->frame->reentrant) { - vm->frame->reentrant = 1; + next = njs_continuation(vm->frame); - ret = njs_array_iterator_args(vm, args, nargs); - if (nxt_slow_path(ret != NXT_OK)) { - return ret; - } + if (next->index < 0) { + vm->retval = njs_value_void; + return NXT_OK; + } - array = args[0].data.u.array; - n = njs_array_next(array->start, 0, array->length); + return njs_array_iterator_apply(vm, next, args, nargs); +} - if (n < 0) { - vm->retval = njs_value_void; - return NXT_OK; - } - next = njs_native_data(vm->frame); - next->index = n; - next->length = array->length; +static njs_ret_t +njs_array_prototype_some(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_int_t ret; + njs_array_next_t *next; + + ret = njs_array_iterator_args(vm, args, nargs); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; } - next = njs_native_data(vm->frame); - n = next->index; + next = njs_continuation(vm->frame); + next->cont.function = njs_array_prototype_some_cont; + next->retval.data.truth = 0; + + return njs_array_prototype_some_cont(vm, args, nargs, unused); +} - arguments[0] = (nargs > 2) ? args[2] : njs_value_void; - /* GC: array elt, array */ - array = args[0].data.u.array; - arguments[1] = array->start[n]; - njs_number_set(&arguments[2], n); - arguments[3] = args[0]; - n = njs_array_next(array->start, ++n, next->length); - next->index = n; +static njs_ret_t +njs_array_prototype_some_cont(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + njs_array_next_t *next; + const njs_value_t *retval; - if (n < 0) { - vm->current += sizeof(njs_vmcode_function_call_t); + next = njs_continuation(vm->frame); + + if (njs_is_true(&next->retval)) { + retval = &njs_value_true; + + } else if (next->index < 0) { + retval = &njs_value_false; + + } else { + return njs_array_iterator_apply(vm, next, args, nargs); } - return njs_function_apply(vm, args[1].data.u.function, arguments, 4, - (njs_index_t) &next->retval); + vm->retval = *retval; + + return NXT_OK; } static njs_ret_t -njs_array_prototype_some(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, +njs_array_prototype_every(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { - nxt_int_t n, ret; - njs_array_t *array; + nxt_int_t ret; njs_array_next_t *next; - njs_value_t arguments[4]; - if (!vm->frame->reentrant) { - vm->frame->reentrant = 1; + ret = njs_array_iterator_args(vm, args, nargs); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } - ret = njs_array_iterator_args(vm, args, nargs); - if (nxt_slow_path(ret != NXT_OK)) { - return ret; - } + next = njs_continuation(vm->frame); + next->cont.function = njs_array_prototype_every_cont; + next->retval.data.truth = 1; - array = args[0].data.u.array; - n = njs_array_next(array->start, 0, array->length); - next = njs_native_data(vm->frame); - next->index = n; - next->length = array->length; + return njs_array_prototype_every_cont(vm, args, nargs, unused); +} - } else { - next = njs_native_data(vm->frame); - if (njs_is_true(&next->retval)) { - vm->retval = njs_value_true; - return NXT_OK; - } - } +static njs_ret_t +njs_array_prototype_every_cont(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + njs_array_next_t *next; + const njs_value_t *retval; - n = next->index; + next = njs_continuation(vm->frame); - if (n < 0) { - vm->retval = njs_value_false; - return NXT_OK; - } + if (!njs_is_true(&next->retval)) { + retval = &njs_value_false; - arguments[0] = (nargs > 2) ? args[2] : njs_value_void; - /* GC: array elt, array */ - array = args[0].data.u.array; - arguments[1] = array->start[n]; - njs_number_set(&arguments[2], n); - arguments[3] = args[0]; + } else if (next->index < 0) { + retval = &njs_value_true; - next->index = njs_array_next(array->start, ++n, next->length); + } else { + return njs_array_iterator_apply(vm, next, args, nargs); + } - return njs_function_apply(vm, args[1].data.u.function, arguments, 4, - (njs_index_t) &next->retval); + vm->retval = *retval; + + return NXT_OK; } -static njs_ret_t -njs_array_prototype_every(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, - njs_index_t unused) +static nxt_noinline njs_ret_t +njs_array_iterator_args(njs_vm_t *vm, njs_value_t * args, nxt_uint_t nargs) { - nxt_int_t n, ret; njs_array_t *array; njs_array_next_t *next; - njs_value_t arguments[4]; - if (!vm->frame->reentrant) { - vm->frame->reentrant = 1; - - ret = njs_array_iterator_args(vm, args, nargs); - if (nxt_slow_path(ret != NXT_OK)) { - return ret; - } + if (nargs > 1 && njs_is_array(&args[0]) && njs_is_function(&args[1])) { array = args[0].data.u.array; - n = njs_array_next(array->start, 0, array->length); - next = njs_native_data(vm->frame); - next->index = n; + next = njs_continuation(vm->frame); next->length = array->length; + next->index = njs_array_iterator_next(array->start, 0, array->length); - } else { - next = njs_native_data(vm->frame); - - if (!njs_is_true(&next->retval)) { - vm->retval = njs_value_false; - return NXT_OK; - } - } - - n = next->index; - - if (n < 0) { - vm->retval = njs_value_true; return NXT_OK; } - arguments[0] = (nargs > 2) ? args[2] : njs_value_void; - /* GC: array elt, array */ - array = args[0].data.u.array; - arguments[1] = array->start[n]; - njs_number_set(&arguments[2], n); - arguments[3] = args[0]; - - next->index = njs_array_next(array->start, ++n, next->length); + vm->exception = &njs_exception_type_error; - return njs_function_apply(vm, args[1].data.u.function, arguments, 4, - (njs_index_t) &next->retval); + return NXT_ERROR; } static nxt_noinline nxt_int_t -njs_array_next(njs_value_t *value, nxt_uint_t n, nxt_uint_t length) +njs_array_iterator_next(njs_value_t *value, nxt_uint_t n, nxt_uint_t length) { while (n < length) { if (njs_is_valid(&value[n])) { @@ -932,6 +929,30 @@ njs_array_next(njs_value_t *value, nxt_uint_t n, nxt_uint_t length) } +static nxt_noinline njs_ret_t +njs_array_iterator_apply(njs_vm_t *vm, njs_array_next_t *next, + njs_value_t *args, nxt_uint_t nargs) +{ + nxt_int_t n; + njs_array_t *array; + njs_value_t arguments[4]; + + n = next->index; + + arguments[0] = (nargs > 2) ? args[2] : njs_value_void; + /* GC: array elt, array */ + array = args[0].data.u.array; + arguments[1] = array->start[n]; + njs_number_set(&arguments[2], n); + arguments[3] = args[0]; + + next->index = njs_array_iterator_next(array->start, ++n, next->length); + + return njs_function_apply(vm, args[1].data.u.function, arguments, 4, + (njs_index_t) &next->retval); +} + + static const njs_object_prop_t njs_array_prototype_properties[] = { { @@ -974,7 +995,8 @@ static const njs_object_prop_t njs_array_prototype_properties[] = { .type = NJS_METHOD, .name = njs_string("toString"), - .value = njs_native_function(njs_array_prototype_to_string, 0, 0), + .value = njs_native_function(njs_array_prototype_to_string, + NJS_CONTINUATION_SIZE, 0), }, { diff --git a/njs/njs_function.c b/njs/njs_function.c index c7bdb5c1..820bca90 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -19,16 +19,6 @@ #include -typedef struct { - njs_continuation_t cont; - njs_function_t *function; -} njs_function_apply_t; - - -static njs_ret_t njs_function_prototype_apply_continuation(njs_vm_t *vm, - njs_value_t *args, nxt_uint_t nargs, njs_index_t retval); - - njs_function_t * njs_function_alloc(njs_vm_t *vm) { @@ -54,16 +44,19 @@ njs_function_alloc(njs_vm_t *vm) njs_ret_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, - nxt_bool_t ctor) + size_t reserve, nxt_bool_t ctor) { size_t size; nxt_uint_t n; njs_value_t *value, *bound; njs_native_frame_t *frame; - size = NJS_NATIVE_FRAME_SIZE - + function->continuation_size - + (function->args_offset + nargs - 1) * sizeof(njs_value_t); + nargs--; + + reserve = nxt_max(reserve, function->continuation_size); + + size = NJS_NATIVE_FRAME_SIZE + reserve + + (function->args_offset + nargs) * sizeof(njs_value_t); frame = njs_function_frame_alloc(vm, size); if (nxt_slow_path(frame == NULL)) { @@ -71,10 +64,10 @@ njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, } frame->function = function; + frame->nargs = function->args_offset + nargs; frame->ctor = ctor; - value = (njs_value_t *) ((u_char *) njs_native_data(frame) - + function->continuation_size); + value = (njs_value_t *) ((u_char *) njs_continuation(frame) + reserve); bound = function->bound; @@ -94,7 +87,7 @@ njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value; if (args != NULL) { - memcpy(value, args, (nargs - 1) * sizeof(njs_value_t)); + memcpy(value, args, nargs * sizeof(njs_value_t)); } return NXT_OK; @@ -152,10 +145,34 @@ nxt_noinline njs_ret_t njs_function_apply(njs_vm_t *vm, njs_function_t *function, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval) { - njs_ret_t ret; + size_t reserve; + njs_ret_t ret; + njs_continuation_t *cont; if (function->native) { - return function->u.native(vm, args, nargs, retval); + + if (function->continuation_size == 0 && function->bound == NULL) { + return function->u.native(vm, args, nargs, retval); + } + + reserve = nxt_align_size(sizeof(njs_continuation_t), + sizeof(njs_value_t)), + + ret = njs_function_native_frame(vm, function, &args[0], &args[1], + nargs, reserve, 0); + if (ret != NJS_OK) { + return ret; + } + + cont = njs_continuation(vm->frame); + + cont->function = function->u.native; + cont->retval = retval; + + cont->return_address = vm->current; + vm->current = (u_char *) njs_continuation_nexus; + + return NJS_APPLIED; } ret = njs_function_frame(vm, function, &args[0], &args[1], nargs - 1, 0); @@ -190,6 +207,7 @@ njs_function_frame(njs_vm_t *vm, njs_function_t *function, njs_value_t *this, } native_frame->function = function; + native_frame->nargs = function->args_offset + nargs - 1; native_frame->ctor = ctor; value = (njs_value_t *) ((u_char *) native_frame + NJS_FRAME_SIZE); @@ -305,48 +323,30 @@ njs_function_prototype_call(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, return NXT_ERROR; } - nargs = nargs - 1; - function = args[0].data.u.function; - - if (function->native) { - - if (nargs == 0) { - nargs++; - args[1] = njs_value_void; - } - - ret = njs_normalize_args(vm, &args[1], function->args_types, nargs); + this = &args[1]; + nargs--; - if (ret != NJS_OK) { - return ret; - } + if (nargs == 0) { + this = (njs_value_t *) &njs_value_void; - if (function->continuation_size == 0) { - args = &args[1]; + } else { + nargs--; + } - } else { - ret = njs_function_native_frame(vm, function, &args[1], &args[2], - nargs, 0); - if (ret != NJS_OK) { - return ret; - } + function = args[0].data.u.function; - /* Skip the "call" method frame. */ - vm->frame->previous->skip = 1; + if (function->native) { - args = vm->frame->arguments - function->args_offset; + ret = njs_function_native_frame(vm, function, this, &args[2], + nargs + 1, 0, 0); + if (nxt_slow_path(ret != NJS_OK)) { + return ret; } - return function->u.native(vm, args, nargs, retval); - } - - this = &args[1]; - - if (nargs == 0) { - this = (njs_value_t *) &njs_value_void; + /* Skip the "call" method frame. */ + vm->frame->previous->skip = 1; - } else { - nargs--; + return NJS_APPLIED; } ret = njs_function_frame(vm, function, this, &args[2], nargs, 0); @@ -366,11 +366,10 @@ static njs_ret_t njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval) { - njs_ret_t ret; - njs_array_t *array; - njs_value_t *this; - njs_function_t *function; - njs_function_apply_t *apply; + njs_ret_t ret; + njs_array_t *array; + njs_value_t *this; + njs_function_t *function; if (!njs_is_function(&args[0])) { goto type_error; @@ -397,46 +396,28 @@ njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } if (function->native) { - nargs++; - - ret = njs_function_native_frame(vm, function, this, args, nargs, 0); + ret = njs_function_native_frame(vm, function, this, args, + nargs + 1, 0, 0); if (nxt_slow_path(ret != NXT_OK)) { return ret; } -// apply = njs_continuation(vm->frame); - apply = nxt_mem_cache_alloc(vm->mem_cache_pool, - sizeof(njs_function_apply_t)); - if (nxt_slow_path(apply == NULL)) { - return NXT_ERROR; - } - - args = vm->frame->arguments - function->args_offset; - /* Skip the "apply" method frame. */ vm->frame->previous->skip = 1; - apply->cont.function = njs_function_prototype_apply_continuation; - apply->cont.args = args; - apply->cont.nargs = nargs; - apply->function = function; - vm->frame->continuation = &apply->cont; - - return njs_function_prototype_apply_continuation(vm, args, nargs, - retval); + return NJS_APPLIED; } ret = njs_function_frame(vm, function, this, args, nargs, 0); - if (nxt_fast_path(ret == NXT_OK)) { - /* Skip the "apply" method frame. */ - vm->frame->previous->skip = 1; - - return njs_function_call(vm, retval, - sizeof(njs_vmcode_function_call_t)); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; } - return ret; + /* Skip the "apply" method frame. */ + vm->frame->previous->skip = 1; + + return njs_function_call(vm, retval, sizeof(njs_vmcode_function_call_t)); type_error: @@ -446,27 +427,6 @@ type_error: } -static njs_ret_t -njs_function_prototype_apply_continuation(njs_vm_t *vm, njs_value_t *args, - nxt_uint_t nargs, njs_index_t retval) -{ - njs_ret_t ret; - njs_function_t *function; - njs_function_apply_t *apply; - - apply = (njs_function_apply_t *) vm->frame->continuation; - function = apply->function; - - ret = njs_normalize_args(vm, args, function->args_types, nargs); - - if (ret == NJS_OK) { - return function->u.native(vm, args, nargs, retval); - } - - return ret; -} - - static njs_ret_t njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) @@ -531,8 +491,7 @@ static const njs_object_prop_t njs_function_prototype_properties[] = { .type = NJS_METHOD, .name = njs_string("apply"), - .value = njs_native_function(njs_function_prototype_apply, - njs_continuation_size(njs_function_apply_t), 0), + .value = njs_native_function(njs_function_prototype_apply, 0, 0), }, { diff --git a/njs/njs_function.h b/njs/njs_function.h index 3cc20c56..288c26f2 100644 --- a/njs/njs_function.h +++ b/njs/njs_function.h @@ -39,27 +39,19 @@ struct njs_function_lambda_s { #define NJS_FRAME_SIZE \ nxt_align_size(sizeof(njs_frame_t), sizeof(njs_value_t)) -/* The retval field are not used in the global frame. */ +/* The retval field is not used in the global frame. */ #define NJS_GLOBAL_FRAME_SIZE \ nxt_align_size(offsetof(njs_frame_t, retval), sizeof(njs_value_t)) #define NJS_FRAME_SPARE_SIZE 512 -#define njs_native_data(frame) \ - (void *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE) - #define njs_continuation(frame) \ (void *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE) #define njs_continuation_size(size) \ nxt_align_size(sizeof(size), sizeof(njs_value_t)) - -typedef struct { - njs_function_native_t function; - njs_value_t *args; - nxt_uint_t nargs; -} njs_continuation_t; +#define NJS_CONTINUATION_SIZE njs_continuation_size(njs_continuation_t) typedef struct njs_exception_s njs_exception_t; @@ -82,7 +74,6 @@ struct njs_native_frame_s { u_char *free; njs_function_t *function; - njs_continuation_t *continuation; njs_native_frame_t *previous; njs_value_t *arguments; @@ -90,6 +81,8 @@ struct njs_native_frame_s { uint32_t free_size; + uint16_t nargs; + /* Function is called as constructor with "new" keyword. */ uint8_t ctor; /* 1 bit */ @@ -103,12 +96,6 @@ struct njs_native_frame_s { /* Skip the Function.call() and Function.apply() methods frames. */ uint8_t skip:1; /* 1 bit */ - /* 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 */ @@ -141,7 +128,7 @@ njs_ret_t njs_function_apply(njs_vm_t *vm, njs_function_t *function, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval); njs_ret_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, - nxt_bool_t ctor); + size_t reserve, nxt_bool_t ctor); njs_ret_t njs_function_frame(njs_vm_t *vm, njs_function_t *function, njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, nxt_bool_t ctor); njs_ret_t njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance); diff --git a/njs/njs_generator.c b/njs/njs_generator.c index ba51a755..d67fe3f3 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -1874,6 +1874,7 @@ njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) parser->code_size, code_size); if (nxt_slow_path(parser->code_size < code_size)) { + vm->exception = &njs_exception_internal_error; return NXT_ERROR; } diff --git a/njs/njs_vm.c b/njs/njs_vm.c index c711439a..852cd242 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -81,6 +81,8 @@ 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 njs_ret_t njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1, + njs_value_t *invld2); static njs_native_frame_t * njs_function_previous_frame(njs_native_frame_t *frame); static njs_ret_t njs_function_frame_free(njs_vm_t *vm, @@ -2099,8 +2101,9 @@ njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *invld, njs_value_t *name) function = value->data.u.function; if (function->native) { - ret = njs_function_native_frame(vm, function, &njs_value_void, NULL, - func->code.nargs, func->code.ctor); + ret = njs_function_native_frame(vm, function, &njs_value_void, + NULL, func->code.nargs, 0, + func->code.ctor); if (nxt_fast_path(ret == NXT_OK)) { return sizeof(njs_vmcode_function_frame_t); @@ -2178,7 +2181,7 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *name, njs_value_t *object) } ret = njs_function_native_frame(vm, function, object, NULL, - method->code.nargs, + method->code.nargs, 0, method->code.ctor); if (nxt_fast_path(ret == NXT_OK)) { @@ -2204,7 +2207,7 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *name, njs_value_t *object) this.data.u.data = vm->external[ext->object]; ret = njs_function_native_frame(vm, ext->function, &this, NULL, - method->code.nargs, + method->code.nargs, 0, method->code.ctor); if (nxt_fast_path(ret == NXT_OK)) { @@ -2230,14 +2233,12 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *name, njs_value_t *object) njs_ret_t njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) { - njs_ret_t ret; - nxt_uint_t nargs; - njs_value_t *args; - njs_function_t *function; - njs_native_frame_t *frame; - njs_continuation_t *continuation; - njs_function_native_t native; - njs_vmcode_function_call_t *call; + njs_ret_t ret; + nxt_uint_t nargs; + njs_value_t *args; + njs_function_t *function; + njs_continuation_t *cont; + njs_native_frame_t *frame; function = vm->frame->function; @@ -2247,28 +2248,27 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) return 0; } - call = (njs_vmcode_function_call_t *) vm->current; - args = vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] - function->args_offset; + args = vm->frame->arguments - function->args_offset; + nargs = vm->frame->nargs; - continuation = vm->frame->continuation; + ret = njs_normalize_args(vm, args, function->args_types, nargs); + if (ret != NJS_OK) { + return ret; + } - if (continuation == NULL) { - nargs = function->args_offset + call->code.nargs - 1; + if (function->continuation_size != 0) { + cont = njs_continuation(vm->frame); - ret = njs_normalize_args(vm, args, function->args_types, nargs); - if (ret != NJS_OK) { - return ret; - } + cont->function = function->u.native; + cont->retval = (njs_index_t) retval; - native = function->u.native; + cont->return_address = vm->current + sizeof(njs_vmcode_function_call_t); + vm->current = (u_char *) njs_continuation_nexus; - } else { - args = continuation->args; - nargs = continuation->nargs; - native = continuation->function; + return 0; } - ret = native(vm, args, nargs, (njs_index_t) retval); + ret = function->u.native(vm, args, nargs, (njs_index_t) retval); /* * A native method can return: @@ -2284,7 +2284,6 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) frame = vm->frame; vm->frame = njs_function_previous_frame(frame); - (void) njs_function_frame_free(vm, frame); /* @@ -2305,14 +2304,6 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) } else if (ret == NJS_APPLIED) { /* A user-defined method has been prepared to run. */ ret = 0; - - } else if (ret == NXT_AGAIN) { - /* - * Revert nJSVM current address, execution will - * continue on the same function after resumption. - */ - vm->frame->reentrant = 1; - vm->current -= sizeof(njs_vmcode_function_call_t); } return ret; @@ -2477,6 +2468,66 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) } +const njs_vmcode_1addr_t njs_continuation_nexus[] = { + { .code = { .operation = njs_vmcode_continuation, + .operands = NJS_VMCODE_NO_OPERAND, + .retval = NJS_VMCODE_NO_RETVAL } }, +}; + + +static njs_ret_t +njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) +{ + njs_ret_t ret; + nxt_bool_t skip; + njs_value_t *args, *retval; + njs_native_frame_t *frame; + njs_continuation_t *cont; + + frame = vm->frame; + cont = njs_continuation(frame); + args = frame->arguments - frame->function->args_offset; + + ret = cont->function(vm, args, frame->nargs, cont->retval); + + switch (ret) { + + case NXT_OK: + + frame = vm->frame; + skip = frame->skip; + + vm->frame = njs_function_previous_frame(frame); + (void) njs_function_frame_free(vm, frame); + + /* + * If a retval is in a callee arguments scope it + * must be in the previous callee arguments scope. + */ + vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments; + + if (!skip) { + retval = njs_vmcode_operand(vm, cont->retval); + /* + * GC: value external/internal++ depending + * on vm->retval and retval type + */ + *retval = vm->retval; + } + + vm->current = cont->return_address; + + return 0; + + case NJS_APPLIED: + return 0; + + default: + return ret; + } +} + + static njs_native_frame_t * njs_function_previous_frame(njs_native_frame_t *frame) { @@ -2746,16 +2797,6 @@ njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap) value = frame->trap_scratch.data.u.value; njs_set_invalid(&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; - } - - frame->trap_frame = 1; - } - frame->trap_values[1].data.u.value = value; frame->trap_values[0] = *value; @@ -2824,10 +2865,9 @@ 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; - njs_native_frame_t *frame; + double num; + njs_ret_t ret; + njs_value_t *value; value = &vm->frame->trap_values[0]; @@ -2845,19 +2885,10 @@ njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1, njs_number_set(value, num); } - frame = vm->frame; - *frame->trap_values[1].data.u.value = *value; + *vm->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); - } - } + vm->frame->trap_restart = NULL; return 0; } @@ -2870,9 +2901,8 @@ 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_native_frame_t *frame; + njs_ret_t ret; + njs_value_t *value; value = &vm->frame->trap_values[0]; @@ -2882,19 +2912,10 @@ 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)) { - 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; + *vm->frame->trap_values[1].data.u.value = *value; - if (frame->first) { - nxt_mem_cache_free(vm->mem_cache_pool, frame); - } - } + vm->current = vm->frame->trap_restart; + vm->frame->trap_restart = NULL; } } @@ -2916,7 +2937,6 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) njs_function_t *function; njs_object_prop_t *prop; nxt_lvlhsh_query_t lhq; - njs_continuation_t *continuation; static const uint32_t hashes[] = { NJS_VALUE_OF_HASH, @@ -2928,25 +2948,6 @@ 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) { - ret = continuation->function(vm, continuation->args, - continuation->nargs, - (njs_index_t) &vm->frame->trap_scratch); - 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; @@ -2992,9 +2993,9 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) if (ret == NJS_APPLIED) { /* - * A user-defined method has been prepared to - * run. The method will return to the current - * instruction and will restart it. + * A user-defined method or continuation have + * been prepared to run. The method will return + * to the current instruction and will restart it. */ ret = 0; } @@ -3045,14 +3046,6 @@ 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; } @@ -3193,13 +3186,6 @@ njs_value_data(njs_value_t *value) } -nxt_uint_t -njs_vm_is_reentrant(njs_vm_t *vm) -{ - return vm->frame->reentrant; -} - - nxt_int_t njs_value_string_copy(njs_vm_t *vm, nxt_str_t *retval, njs_value_t *value, uintptr_t *next) diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 04778250..f7c45a29 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -155,6 +155,15 @@ typedef struct { } njs_function_t; +typedef struct njs_continuation_s njs_continuation_t; + +struct njs_continuation_s { + njs_function_native_t function; + u_char *return_address; + njs_index_t retval; +}; + + union njs_value_s { /* * The njs_value_t size is 16 bytes and must be aligned to 16 bytes @@ -393,8 +402,8 @@ typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1, typedef struct { njs_vmcode_operation_t operation; uint8_t operands; /* 2 bits */ - uint8_t retval; /* 1 bit */ - uint8_t ctor; /* 1 bit */ + uint8_t retval; /* 1 bit */ + uint8_t ctor; /* 1 bit */ #if (NXT_64BIT) uint32_t nargs; #else @@ -940,5 +949,7 @@ extern const njs_value_t njs_exception_internal_error; extern const nxt_mem_proto_t njs_array_mem_proto; extern const nxt_lvlhsh_proto_t njs_object_hash_proto; +extern const njs_vmcode_1addr_t njs_continuation_nexus[]; + #endif /* _NJS_VM_H_INCLUDED_ */ diff --git a/njs/njscript.h b/njs/njscript.h index 6486a4c7..80688837 100644 --- a/njs/njscript.h +++ b/njs/njscript.h @@ -93,7 +93,6 @@ NXT_EXPORT njs_ret_t njs_string_create(njs_vm_t *vm, njs_value_t *value, NXT_EXPORT njs_ret_t njs_void_set(njs_value_t *value); NXT_EXPORT void *njs_value_data(njs_value_t *value); -NXT_EXPORT nxt_uint_t njs_vm_is_reentrant(njs_vm_t *vm); NXT_EXPORT nxt_int_t njs_value_string_copy(njs_vm_t *vm, nxt_str_t *retval, njs_value_t *value, uintptr_t *next); diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index bc251e13..5283e1db 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -2332,21 +2332,36 @@ static njs_unit_test_t njs_test[] = "f(2)"), nxt_string("012") }, -#if 0 { nxt_string("var f = String.prototype.concat.bind(0, 1);" "var o = { toString: f }; o"), nxt_string("01") }, -#endif -#if 0 - { nxt_string("''.concat.bind(1,2,3,4).call(5,6,7,8)"), + { nxt_string("''.concat.bind(0, 1, 2, 3, 4).call(5, 6, 7, 8, 9)"), nxt_string("012346789") }, -#endif -#if 0 - { nxt_string("''.concat.bind(1,2,3,4).apply(5,[6,7,8])"), + { nxt_string("''.concat.bind(0, 1, 2, 3, 4).apply(5,[6, 7, 8, 9])"), nxt_string("012346789") }, -#endif + + { nxt_string("var f = Array.prototype.join.bind([0, 1, 2]); f()"), + nxt_string("0,1,2") }, + + { nxt_string("var f = Array.prototype.join.bind([0, 1, 2]);" + "var o = { toString: f }; o"), + nxt_string("0,1,2") }, + + { nxt_string("var f = Array.prototype.join.bind([0, 1, 2]); f('.')"), + nxt_string("0.1.2") }, + + { nxt_string("var f = Array.prototype.join.bind([0, 1, 2], '.');" + "var o = { toString: f }; o"), + nxt_string("0.1.2") }, + + { nxt_string("var f = Array.prototype.toString.bind([0, 1, 2]); f()"), + nxt_string("0,1,2") }, + + { nxt_string("var f = Array.prototype.toString.bind([0, 1, 2]);" + "var o = { toString: f }; o"), + nxt_string("0,1,2") }, { nxt_string("var s = { toString: function() { return '123' } };" "var a = 'abc'; a.concat('абв', s)"),