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,
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;
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++) {
}
}
- 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;
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;
}
return NULL;
}
- frame->u.function = function;
+ frame->function = function;
frame->ctor = code->ctor;
this = (njs_value_t *) ((u_char *) njs_native_data(frame)
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;
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);
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];
{
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)) {
(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 {
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;
/* 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 */
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;
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,
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;
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,
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;
/* 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. */
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
* 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;
}
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];
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;
}
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];
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);
+ }
+ }
}
}
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,
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;
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);
*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;
}
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);
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;
{ 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';"