]> git.kaiwu.me - njs.git/commitdiff
Continuations have been refactored.
authorIgor Sysoev <igor@sysoev.ru>
Sun, 21 Feb 2016 21:06:53 +0000 (00:06 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Sun, 21 Feb 2016 21:06:53 +0000 (00:06 +0300)
njs/njs_array.c
njs/njs_function.c
njs/njs_function.h
njs/njs_generator.c
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.h
njs/test/njs_unit_test.c

index 9cf8d150a436a4fdfd5eab2a97a8258b4a04e56a..0eda9d795de3211940dd4465d85eaa5394ea678a 100644 (file)
 
 
 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),
     },
 
     {
index c7bdb5c1cfec0d2dec42fd01a35e86bcb386f616..820bca908f09fb54fb4a02785d043ee247b16248 100644 (file)
 #include <string.h>
 
 
-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),
     },
 
     {
index 3cc20c56b09934257798a699dd4ca0121bd52422..288c26f2470589e80cf038b33a1afc08d11a89a4 100644 (file)
@@ -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);
index ba51a755694ec133a3fa9164712db11906265792..d67fe3f370f8f822dfa0d8f9d8221c601921ed4f 100644 (file)
@@ -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;
     }
 
index c711439ada37b921bbfcdbbe354debab3933cb87..852cd24274a921d546245fa7479742c331550797 100644 (file)
@@ -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)
index 047782500f060439549fc5971bd045aff6158b5b..f7c45a299a5c611e9a7390ba30296d9325780916 100644 (file)
@@ -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_ */
index 6486a4c757eabb5c0a3d071ba307cd2db17e2990..80688837936973262e38ce8eb3e669177c2b79d1 100644 (file)
@@ -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);
 
index bc251e13e458c5352eba8dcdb942836c08052c23..5283e1db0ba108fcbee2b3b7f1fcc81bb98a3306 100644 (file)
@@ -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)"),