]> git.kaiwu.me - njs.git/commitdiff
Refactored working with non-primitive types.
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 5 Jul 2019 18:45:28 +0000 (21:45 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Fri, 5 Jul 2019 18:45:28 +0000 (21:45 +0300)
Traps mechanism is remove.

17 files changed:
njs/njs.c
njs/njs_array.c
njs/njs_date.c
njs/njs_function.c
njs/njs_function.h
njs/njs_math.c
njs/njs_object.c
njs/njs_object_property.c
njs/njs_regexp.c
njs/njs_string.c
njs/njs_string.h
njs/njs_value.c
njs/njs_value.h
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_interactive_test.c
njs/test/njs_unit_test.c

index 790d8c2da926f57b69cfc5f01f9faf670be25a50..0cd35733cfbb3e368c248bb962edd54278eff3da 100644 (file)
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -984,8 +984,6 @@ again:
 
         /* value evaluation threw an exception. */
 
-        vm->top_frame->trap_tries = 0;
-
         src = &vm->retval;
         goto again;
     }
index b86bd78a97d65d78b585d924069f63d6e529ef9d..70da49541fedbb627cf6fafc5b5f6f79fd69b02f 100644 (file)
@@ -528,10 +528,7 @@ njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     ret = njs_value_property(vm, &args[0], &njs_string_length, &slice->length,
                              0);
 
-    if (nxt_slow_path(ret == NXT_ERROR
-                      || ret == NJS_TRAP
-                      || ret == NJS_APPLIED))
-    {
+    if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_APPLIED)) {
         return ret;
     }
 
@@ -544,13 +541,16 @@ njs_array_prototype_slice_continuation(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused)
 {
     int64_t            start, end, length;
+    njs_ret_t          ret;
     njs_array_slice_t  *slice;
 
     slice = njs_vm_continuation(vm);
 
     if (nxt_slow_path(!njs_is_primitive(&slice->length))) {
-        njs_vm_trap_value(vm, &slice->length);
-        return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+        ret = njs_value_to_numeric(vm, &slice->length, &slice->length);
+        if (ret != NXT_OK) {
+            return ret;
+        }
     }
 
     start = njs_primitive_value_to_integer(njs_arg(args, nargs, 1));
@@ -958,7 +958,7 @@ njs_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 
 static njs_ret_t
 njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
-    njs_index_t retval)
+    njs_index_t unused)
 {
     njs_object_prop_t   *prop;
     nxt_lvlhsh_query_t  lhq;
@@ -970,12 +970,12 @@ njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         prop = njs_object_property(vm, njs_object(&args[0]), &lhq);
 
         if (nxt_fast_path(prop != NULL && njs_is_function(&prop->value))) {
-            return njs_function_replace(vm, njs_function(&prop->value),
-                                        args, nargs, retval);
+            return njs_function_call(vm, njs_function(&prop->value), args,
+                                     nargs, (njs_index_t) &vm->retval);
         }
     }
 
-    return njs_object_prototype_to_string(vm, args, nargs, retval);
+    return njs_object_prototype_to_string(vm, args, nargs, unused);
 }
 
 
@@ -1063,6 +1063,7 @@ njs_array_prototype_join_continuation(njs_vm_t *vm, njs_value_t *args,
     u_char             *p;
     size_t             size, length, mask;
     uint32_t           max;
+    njs_ret_t          ret;
     nxt_uint_t         i, n;
     njs_array_t        *array;
     njs_value_t        *value, *values;
@@ -1089,9 +1090,10 @@ njs_array_prototype_join_continuation(njs_vm_t *vm, njs_value_t *args,
                 value = &values[n++];
 
                 if (!njs_is_string(value)) {
-                    njs_vm_trap_value(vm, value);
-
-                    return njs_trap(vm, NJS_TRAP_STRING_ARG);
+                    ret = njs_value_to_string(vm, value, value);
+                    if (ret != NXT_OK) {
+                        return ret;
+                    }
                 }
             }
 
@@ -1442,10 +1444,7 @@ njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         ret = njs_value_property(vm, this, &njs_string_length, &fill->length,
                                  0);
 
-        if (nxt_slow_path(ret == NXT_ERROR
-                          || ret == NJS_TRAP
-                          || ret == NJS_APPLIED))
-        {
+        if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_APPLIED)) {
             return ret;
         }
     }
@@ -1459,6 +1458,7 @@ njs_array_prototype_fill_continuation(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused)
 {
     nxt_int_t          i, start, end, length;
+    njs_ret_t          ret;
     njs_array_t        *array;
     njs_object_t       *object;
     njs_array_fill_t   *fill;
@@ -1488,8 +1488,10 @@ njs_array_prototype_fill_continuation(njs_vm_t *vm, njs_value_t *args,
     } else {
 
         if (nxt_slow_path(!njs_is_primitive(&fill->length))) {
-            njs_vm_trap_value(vm, &fill->length);
-            return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+            ret = njs_value_to_numeric(vm, &fill->length, &fill->length);
+            if (ret != NXT_OK) {
+                return ret;
+            }
         }
 
         length = njs_primitive_value_to_length(&fill->length);
@@ -2202,9 +2204,10 @@ njs_array_string_sort(njs_vm_t *vm, njs_value_t *args,
 
     for (i = 1; i < nargs; i++) {
         if (!njs_is_string(&args[i])) {
-            njs_vm_trap_value(vm, &args[i]);
-
-            return njs_trap(vm, NJS_TRAP_STRING_ARG);
+            ret = njs_value_to_string(vm, &args[i], &args[i]);
+            if (ret != NXT_OK) {
+                return ret;
+            }
         }
     }
 
index f5330839fede8168a1eff027bf8bbcdebbd494cc..d85660a051f187ea18c23e223183fa89ff1fe6c9 100644 (file)
@@ -73,6 +73,7 @@ njs_date_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 {
     double      num, time;
     int64_t     values[8];
+    njs_ret_t   ret;
     nxt_uint_t  i, n;
     njs_date_t  *date;
     struct tm   tm;
@@ -85,11 +86,14 @@ njs_date_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         } else if (nargs == 2) {
             if (njs_is_object(&args[1])) {
                 if (!njs_is_date(&args[1])) {
-                    njs_vm_trap_value(vm, &args[1]);
-
-                    return njs_trap(vm, NJS_TRAP_PRIMITIVE_ARG);
+                    ret = njs_value_to_primitive(vm, &args[1], &args[1], 0);
+                    if (ret != NXT_OK) {
+                        return ret;
+                    }
                 }
+            }
 
+            if (njs_is_date(&args[1])) {
                 time = njs_date(&args[1])->time;
 
             } else if (njs_is_string(&args[1])) {
@@ -108,9 +112,10 @@ njs_date_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 
             for (i = 1; i < n; i++) {
                 if (!njs_is_numeric(&args[i])) {
-                    njs_vm_trap_value(vm, &args[i]);
-
-                    return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+                    ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+                    if (ret != NXT_OK) {
+                        return ret;
+                    }
                 }
 
                 num = njs_number(&args[i]);
@@ -171,6 +176,7 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 {
     double      num, time;
     struct tm   tm;
+    njs_ret_t   ret;
     nxt_uint_t  i, n;
     int32_t     values[8];
 
@@ -183,9 +189,10 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 
         for (i = 1; i < n; i++) {
             if (!njs_is_numeric(&args[i])) {
-                njs_vm_trap_value(vm, &args[i]);
-
-                return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+                ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
 
             num = njs_number(&args[i]);
@@ -1902,8 +1909,8 @@ njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         prop = njs_object_property(vm, njs_object(&args[0]), &lhq);
 
         if (nxt_fast_path(prop != NULL && njs_is_function(&prop->value))) {
-            return njs_function_replace(vm, njs_function(&prop->value),
-                                        args, nargs, retval);
+            return njs_function_call(vm, njs_function(&prop->value), args,
+                                     nargs, (njs_index_t) &vm->retval);
         }
     }
 
index 545b7c9438255520f0fbbd63108d89bceb34855b..75045978e64fead524b7739c0712519db30e1a5d 100644 (file)
@@ -472,6 +472,48 @@ njs_function_frame_alloc(njs_vm_t *vm, size_t size)
 }
 
 
+njs_ret_t
+njs_function_call(njs_vm_t *vm, njs_function_t *function, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t retval)
+{
+    u_char              *return_address;
+    njs_ret_t           ret;
+    njs_native_frame_t  *frame;
+    njs_continuation_t  *cont;
+
+    ret = njs_function_frame(vm, function, &args[0], &args[1], nargs - 1, 0, 0);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    frame = vm->top_frame;
+    frame->call = 1;
+
+    return_address = vm->current;
+
+    if (function->native) {
+
+        if (function->continuation_size != 0) {
+            cont = njs_vm_continuation(vm);
+            cont->return_address = return_address;
+        }
+
+        ret = njs_function_native_call(vm, function->u.native, frame->arguments,
+                                       function->args_types, frame->nargs,
+                                       (njs_index_t) retval, return_address);
+
+    } else {
+        ret = njs_function_lambda_call(vm, retval, return_address);
+
+        if (nxt_fast_path(ret == NJS_APPLIED)) {
+            ret = njs_vmcode_run(vm);
+        }
+    }
+
+    return ret;
+}
+
+
 njs_ret_t
 njs_function_lambda_call(njs_vm_t *vm, njs_index_t retval,
     u_char *return_address)
@@ -594,9 +636,9 @@ njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
      * The callee arguments must be preserved
      * for NJS_APPLIED and NXT_AGAIN cases.
      */
-    if (ret == NXT_OK) {
-        frame = vm->top_frame;
+    frame = vm->top_frame;
 
+    if (ret == NXT_OK || (frame->call && ret == NXT_ERROR)) {
         vm->top_frame = njs_function_previous_frame(frame);
 
         /*
@@ -625,7 +667,7 @@ njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
 
         njs_function_frame_free(vm, frame);
 
-        return NXT_OK;
+        return ret;
     }
 
     return ret;
@@ -636,8 +678,8 @@ static 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   ret;
     nxt_uint_t  n;
-    njs_trap_t  trap;
 
     n = nxt_min(nargs, NJS_ARGS_TYPES_MAX);
 
@@ -655,43 +697,47 @@ njs_normalize_args(njs_vm_t *vm, njs_value_t *args, uint8_t *args_types,
 
         case NJS_STRING_ARG:
 
-            if (njs_is_string(args)) {
-                break;
+            if (!njs_is_string(args)) {
+                ret = njs_value_to_string(vm, args, args);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
 
-            trap = NJS_TRAP_STRING_ARG;
-            goto trap;
+            break;
 
         case NJS_NUMBER_ARG:
 
-            if (njs_is_numeric(args)) {
-                break;
+            if (!njs_is_numeric(args)) {
+                ret = njs_value_to_numeric(vm, args, args);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
 
-            trap = NJS_TRAP_NUMBER_ARG;
-            goto trap;
+            break;
 
         case NJS_INTEGER_ARG:
 
-            if (njs_is_numeric(args)) {
-
-                /* Numbers are truncated to fit in 32-bit integers. */
+            if (!njs_is_numeric(args)) {
+                ret = njs_value_to_numeric(vm, args, args);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
+            }
 
-                if (isnan(njs_number(args))) {
-                    njs_number(args) = 0;
+            /* Numbers are truncated to fit in 32-bit integers. */
 
-                } else if (njs_number(args) > 2147483647.0) {
+            if (!isnan(njs_number(args))) {
+                if (njs_number(args) > 2147483647.0) {
                     njs_number(args) = 2147483647.0;
 
                 } else if (njs_number(args) < -2147483648.0) {
                     njs_number(args) = -2147483648.0;
                 }
-
-                break;
             }
 
-            trap = NJS_TRAP_NUMBER_ARG;
-            goto trap;
+            break;
 
         case NJS_FUNCTION_ARG:
 
@@ -701,8 +747,10 @@ njs_normalize_args(njs_vm_t *vm, njs_value_t *args, uint8_t *args_types,
                 break;
 
             default:
-                trap = NJS_TRAP_STRING_ARG;
-                goto trap;
+                ret = njs_value_to_string(vm, args, args);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
 
             break;
@@ -716,8 +764,10 @@ njs_normalize_args(njs_vm_t *vm, njs_value_t *args, uint8_t *args_types,
                 break;
 
             default:
-                trap = NJS_TRAP_STRING_ARG;
-                goto trap;
+                ret = njs_value_to_string(vm, args, args);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
 
             break;
@@ -751,12 +801,6 @@ njs_normalize_args(njs_vm_t *vm, njs_value_t *args, uint8_t *args_types,
 
     return NJS_OK;
 
-trap:
-
-    njs_vm_trap_value(vm, args);
-
-    return njs_trap(vm, trap);
-
 type_error:
 
     njs_type_error(vm, "cannot convert %s to %s", njs_type_string(args->type),
index 5a212d1c70188ed281d019babb949dd8bcc4ad1d..0b1c939a21f68fa74783a7c7451ce99dae671215 100644 (file)
@@ -74,11 +74,6 @@ typedef struct {
 #define NJS_CONTINUATION_SIZE      njs_continuation_size(njs_continuation_t)
 
 
-#define njs_vm_trap_value(vm, val)                                            \
-    (vm)->top_frame->trap_scratch.data.u.value = val
-
-
-
 typedef struct njs_exception_s     njs_exception_t;
 
 struct njs_exception_s {
@@ -92,10 +87,6 @@ struct njs_exception_s {
 
 
 struct njs_native_frame_s {
-    njs_value_t                    trap_scratch;
-    njs_value_t                    trap_values[2];
-    u_char                         *trap_restart;
-
     u_char                         *free;
 
     njs_function_t                 *function;
@@ -118,14 +109,7 @@ struct njs_native_frame_s {
     /* Skip the Function.call() and Function.apply() methods frames. */
     uint8_t                        skip;              /* 1 bit  */
 
-    /* A number of trap tries, it can be no more than three. */
-    uint8_t                        trap_tries;        /* 2 bits */
-
-    /*
-     * The first operand in trap is reference to original value,
-     * it is used to increment or decrement this value.
-     */
-    uint8_t                        trap_reference;   /* 1 bit */
+    uint8_t                        call; /* 1 bit */
 };
 
 
@@ -168,6 +152,8 @@ njs_ret_t njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
 njs_ret_t njs_function_activate(njs_vm_t *vm, njs_function_t *function,
     const njs_value_t *this, const njs_value_t *args, nxt_uint_t nargs,
     njs_index_t retval, size_t advance);
+njs_ret_t njs_function_call(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_lambda_call(njs_vm_t *vm, njs_index_t retval,
     u_char *return_address);
 njs_ret_t njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
@@ -215,36 +201,6 @@ njs_function_apply(njs_vm_t *vm, njs_function_t *function,
 }
 
 
-/*
- * Replaces the current function with a new one.
- * Can only be used for continuation functions
- * (data.u.function.continuation_size > 0).
- */
-nxt_inline njs_ret_t
-njs_function_replace(njs_vm_t *vm, njs_function_t *function,
-    const njs_value_t *args, nxt_uint_t nargs, njs_index_t retval)
-{
-    nxt_int_t  ret;
-
-    ret = njs_function_apply(vm, function, args, nargs, retval);
-    if (nxt_slow_path(ret == NXT_ERROR)) {
-        return ret;
-    }
-
-    /*
-     * 1) njs_function_apply() allocs a new function frame,
-     *    in order to preserve the retval of a new function and ignore
-     *    retval of the current function during stack unwinding
-     *    skip flag is needed.
-     * 2) it is also needed for correct callee arguments update in
-     *    njs_function_native_call() see "Object((new Date(0)).toJSON())".
-     */
-    vm->top_frame->previous->skip = 1;
-
-    return NJS_APPLIED;
-}
-
-
 nxt_inline njs_native_frame_t *
 njs_function_previous_frame(njs_native_frame_t *frame)
 {
index a4ebf76391830327032f71e29f51af21b9a9bf02..5220067874d1ae91a98e1c7a4356324302f5ddf1 100644 (file)
@@ -359,13 +359,15 @@ njs_object_math_hypot(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
     double      num;
+    njs_ret_t   ret;
     nxt_uint_t  i;
 
     for (i = 1; i < nargs; i++) {
         if (!njs_is_numeric(&args[i])) {
-            njs_vm_trap_value(vm, &args[i]);
-
-            return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+            ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+            if (ret != NXT_OK) {
+                return ret;
+            }
         }
     }
 
@@ -498,6 +500,7 @@ njs_object_math_max(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
     double      num;
+    njs_ret_t   ret;
     nxt_uint_t  i;
 
     if (nargs > 1) {
@@ -507,9 +510,10 @@ njs_object_math_max(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
                 goto done;
 
             } else if (!njs_is_numeric(&args[i])) {
-                njs_vm_trap_value(vm, &args[i]);
-
-                return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+                ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
         }
 
@@ -536,14 +540,16 @@ njs_object_math_min(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
     double      num;
+    njs_ret_t   ret;
     nxt_uint_t  i;
 
     if (nargs > 1) {
         for (i = 1; i < nargs; i++) {
             if (!njs_is_numeric(&args[i])) {
-                njs_vm_trap_value(vm, &args[i]);
-
-                return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+                ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+                if (ret != NXT_OK) {
+                    return ret;
+                }
             }
         }
 
index 2a17e204b20ae7a21732c7fbb12d29ca5655e121..719f5186eba5804fca853601a1900850999506ce 100644 (file)
@@ -2140,7 +2140,6 @@ njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args,
         vm->retval = njs_value_false;
         return NXT_OK;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
         return ret;
@@ -2181,7 +2180,6 @@ njs_object_prototype_prop_is_enumerable(njs_vm_t *vm, njs_value_t *args,
         retval = &njs_value_false;
         break;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
         return ret;
index a1bac6fc8a458fdbdca5987075fbf97f9a5a1caf..55223b87a85980e281f1eec80426c91e862cd7c0 100644 (file)
@@ -38,7 +38,6 @@ static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
  *   NXT_DECLINED         property was not found in object,
  *     if pq->lhq.value != NULL it contains retval of type
  *     njs_object_prop_t * where prop->type is NJS_WHITEOUT
- *   NJS_TRAP             the property trap must be called,
  *   NXT_ERROR            exception has been thrown.
  *
  *   TODO:
@@ -53,10 +52,16 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
     uint32_t        (*hash)(const void *, size_t);
     njs_ret_t       ret;
     njs_object_t    *obj;
+    njs_value_t     prop;
     njs_function_t  *function;
 
     if (nxt_slow_path(!njs_is_primitive(property))) {
-        return njs_trap(vm, NJS_TRAP_PROPERTY);
+        ret = njs_value_to_string(vm, &prop, (njs_value_t *) property);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        property = &prop;
     }
 
     hash = nxt_djb_hash;
@@ -473,7 +478,6 @@ njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
  *      retval will contain the property's value
  *
  *   NXT_DECLINED         property was not found in object
- *   NJS_TRAP             the property trap must be called
  *   NJS_APPLIED          the property getter was applied
  *   NXT_ERROR            exception has been thrown.
  *      retval will contain undefined
@@ -553,7 +557,6 @@ njs_value_property(njs_vm_t *vm, const njs_value_t *value,
 
         return NXT_DECLINED;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
 
@@ -566,7 +569,6 @@ njs_value_property(njs_vm_t *vm, const njs_value_t *value,
 
 /*
  *   NXT_OK               property has been set successfully
- *   NJS_TRAP             the property trap must be called
  *   NJS_APPLIED          the property setter was applied
  *   NXT_ERROR            exception has been thrown.
  */
@@ -669,7 +671,6 @@ njs_value_property_set(njs_vm_t *vm, njs_value_t *object,
 
         break;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
 
@@ -1152,7 +1153,6 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
         *dest = njs_value_undefined;
         return NXT_OK;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
         return ret;
index 0ea4321dbce06910e942e014ea99b843134b9cbe..57c3dff8435e94e3c66a4ced36ce7037f9a4e85d 100644 (file)
@@ -112,17 +112,19 @@ njs_regexp_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     pattern = njs_arg(args, nargs, 1);
 
     if (!njs_is_regexp(pattern) && !njs_is_primitive(pattern)) {
-        njs_vm_trap_value(vm, &args[1]);
-
-        return njs_trap(vm, NJS_TRAP_STRING_ARG);
+        ret = njs_value_to_string(vm, &args[1], &args[1]);
+        if (ret != NXT_OK) {
+            return ret;
+        }
     }
 
     flags = njs_arg(args, nargs, 2);
 
     if (!njs_is_primitive(flags)) {
-        njs_vm_trap_value(vm, &args[2]);
-
-        return njs_trap(vm, NJS_TRAP_STRING_ARG);
+        ret = njs_value_to_string(vm, &args[2], &args[2]);
+        if (ret != NXT_OK) {
+            return ret;
+        }
     }
 
     re_flags = 0;
index daf2d20f9b77b0a3b54ab125404fd3c6f3bcac82..f9a2500eb5f8874e553aa78e669669f7f52828b5 100644 (file)
@@ -875,6 +875,7 @@ njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 {
     u_char             *p, *start;
     uint64_t           size, length, mask;
+    njs_ret_t          ret;
     nxt_uint_t         i;
     njs_string_prop_t  string;
 
@@ -885,9 +886,10 @@ njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 
     for (i = 0; i < nargs; i++) {
         if (!njs_is_string(&args[i])) {
-            njs_vm_trap_value(vm, &args[i]);
-
-            return njs_trap(vm, NJS_TRAP_STRING_ARG);
+            ret = njs_value_to_string(vm, &args[i], &args[i]);
+            if (ret != NXT_OK) {
+                return ret;
+            }
         }
     }
 
@@ -1244,16 +1246,12 @@ njs_string_prototype_char_at(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 
     slice.string_length = njs_string_prop(&string, &args[0]);
 
-    start = 0;
+    start = njs_primitive_value_to_integer(njs_arg(args, nargs, 1));
     length = 1;
 
-    if (nargs > 1) {
-        start = njs_number(&args[1]);
-
-        if (start < 0 || start >= (ssize_t) slice.string_length) {
-            start = 0;
-            length = 0;
-        }
+    if (start < 0 || start >= (ssize_t) slice.string_length) {
+        start = 0;
+        length = 0;
     }
 
     slice.start = start;
@@ -1409,15 +1407,11 @@ njs_string_prototype_char_code_at(njs_vm_t *vm, njs_value_t *args,
 
     length = njs_string_prop(&string, &args[0]);
 
-    index = 0;
-
-    if (nargs > 1) {
-        index = njs_number(&args[1]);
+    index = njs_primitive_value_to_integer(njs_arg(args, nargs, 1));
 
-        if (nxt_slow_path(index < 0 || index >= length)) {
-            num = NAN;
-            goto done;
-        }
+    if (nxt_slow_path(index < 0 || index >= length)) {
+        num = NAN;
+        goto done;
     }
 
     if ((uint32_t) length == string.size) {
@@ -1477,6 +1471,7 @@ njs_string_bytes_from_array(njs_vm_t *vm, const njs_value_t *value)
 {
     u_char       *p;
     uint32_t     i, length;
+    njs_ret_t    ret;
     njs_array_t  *array;
     njs_value_t  *octet;
 
@@ -1485,9 +1480,10 @@ njs_string_bytes_from_array(njs_vm_t *vm, const njs_value_t *value)
 
     for (i = 0; i < length; i++) {
         if (!njs_is_numeric(&array->start[i])) {
-            njs_vm_trap_value(vm, &array->start[i]);
-
-            return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+            ret = njs_value_to_numeric(vm, &array->start[i], &array->start[i]);
+            if (ret != NXT_OK) {
+                return ret;
+            }
         }
     }
 
@@ -1713,13 +1709,15 @@ njs_string_from_char_code(njs_vm_t *vm, njs_value_t *args,
     double      num;
     size_t      size;
     int32_t     code;
+    njs_ret_t   ret;
     nxt_uint_t  i;
 
     for (i = 1; i < nargs; i++) {
         if (!njs_is_numeric(&args[i])) {
-            njs_vm_trap_value(vm, &args[i]);
-
-            return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
+            ret = njs_value_to_numeric(vm, &args[i], &args[i]);
+            if (ret != NXT_OK) {
+                return ret;
+            }
         }
     }
 
@@ -2426,20 +2424,16 @@ njs_string_prototype_repeat(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     uint32_t           size, length;
     njs_string_prop_t  string;
 
-    n = 0;
+    n = njs_primitive_value_to_integer(njs_arg(args, nargs, 1));
 
     (void) njs_string_prop(&string, &args[0]);
 
-    if (nargs > 1) {
-        max = (string.size > 1) ? NJS_STRING_MAX_LENGTH / string.size
-                                : NJS_STRING_MAX_LENGTH;
-
-        n = njs_number(&args[1]);
+    max = (string.size > 1) ? NJS_STRING_MAX_LENGTH / string.size
+                            : NJS_STRING_MAX_LENGTH;
 
-        if (nxt_slow_path(n < 0 || n >= max)) {
-            njs_range_error(vm, NULL);
-            return NXT_ERROR;
-        }
+    if (nxt_slow_path(n < 0 || n >= max)) {
+        njs_range_error(vm, NULL);
+        return NXT_ERROR;
     }
 
     if (string.size == 0) {
@@ -3427,8 +3421,10 @@ njs_string_replace_search_continuation(njs_vm_t *vm, njs_value_t *args,
     r = njs_vm_continuation(vm);
 
     if (!njs_is_primitive(&r->retval)) {
-        njs_vm_trap_value(vm, &r->retval);
-        return njs_trap(vm, NJS_TRAP_STRING_ARG);
+        ret = njs_value_to_string(vm, &r->retval, &r->retval);
+        if (ret != NXT_OK) {
+            return ret;
+        }
     }
 
     ret = njs_primitive_value_to_string(vm, &string, &r->retval);
index 6f47db1a9a23a48ccd9f444e71e4a0f63b6a4a3c..135e8a3b22bbfb376f12c864d65b17028effdc33 100644 (file)
@@ -177,10 +177,7 @@ const u_char *njs_string_offset(const u_char *start, const u_char *end,
     size_t index);
 uint32_t njs_string_index(njs_string_prop_t *string, uint32_t offset);
 void njs_string_offset_map_init(const u_char *start, size_t size);
-njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
-    const njs_value_t *src);
 double njs_string_to_index(const njs_value_t *value);
-double njs_string_to_number(const njs_value_t *value, nxt_bool_t parse_float);
 const u_char *njs_string_to_c_string(njs_vm_t *vm, njs_value_t *value);
 njs_ret_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
index 045d2761cdaf69a10a9b80b5d6db921d860cef91..3c6469d705e6ea2c92d362d4e555095e9d267633 100644 (file)
@@ -101,14 +101,16 @@ njs_value_release(njs_vm_t *vm, njs_value_t *value)
  */
 
 njs_ret_t
-njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
+njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,
+       nxt_uint_t hint)
 {
     njs_ret_t           ret;
-    njs_value_t         *retval;
-    njs_function_t      *function;
+    nxt_uint_t          tries;
     njs_object_prop_t   *prop;
     nxt_lvlhsh_query_t  lhq;
 
+    njs_value_t         retval nxt_aligned(16);
+
     static const uint32_t  hashes[] = {
         NJS_VALUE_OF_HASH,
         NJS_TO_STRING_HASH,
@@ -119,76 +121,56 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
         nxt_string("toString"),
     };
 
-    if (!njs_is_primitive(value)) {
-        retval = &vm->top_frame->trap_scratch;
-
-        if (!njs_is_primitive(retval)) {
-
-            for ( ;; ) {
-                ret = NXT_ERROR;
 
-                if (njs_is_object(value) && vm->top_frame->trap_tries < 2) {
-                    hint ^= vm->top_frame->trap_tries++;
+    if (njs_is_primitive(value)) {
+        /* GC */
+        *dst = *value;
+        return NXT_OK;
+    }
 
-                    lhq.key_hash = hashes[hint];
-                    lhq.key = names[hint];
+    tries = 0;
 
-                    prop = njs_object_property(vm, njs_object(value), &lhq);
+    for ( ;; ) {
+        ret = NXT_ERROR;
 
-                    if (nxt_fast_path(prop != NULL)) {
+        if (njs_is_object(value) && tries < 2) {
+            hint ^= tries++;
 
-                        if (!njs_is_function(&prop->value)) {
-                            /* Try the second method. */
-                            continue;
-                        }
+            lhq.key_hash = hashes[hint];
+            lhq.key = names[hint];
 
-                        function = njs_function(&prop->value);
+            prop = njs_object_property(vm, njs_object(value), &lhq);
 
-                        ret = njs_function_apply(vm, function, value, 1,
-                                                 (njs_index_t) retval);
-                        /*
-                         * njs_function_apply() can return
-                         *   NXT_OK, NJS_APPLIED, NXT_ERROR, NXT_AGAIN.
-                         */
-                        if (nxt_fast_path(ret == NXT_OK)) {
+            if (prop == NULL || !njs_is_function(&prop->value)) {
+                /* Try the second method. */
+                continue;
+            }
 
-                            if (njs_is_primitive(&vm->retval)) {
-                                retval = &vm->retval;
-                                break;
-                            }
+            ret = njs_function_call(vm, njs_function(&prop->value), value, 1,
+                                    (njs_index_t) &retval);
 
-                            /* Try the second method. */
-                            continue;
-                        }
+            if (nxt_fast_path(ret == NXT_OK)) {
+                if (njs_is_primitive(&retval)) {
+                    break;
+                 }
 
-                        if (ret == NJS_APPLIED) {
-                            /*
-                             * 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;
-                        }
-                    }
-                }
+                /* Try the second method. */
+                continue;
+             }
 
-                if (ret == NXT_ERROR) {
-                    njs_type_error(vm,
-                                   "Cannot convert object to primitive value");
-                }
+            /* NXT_ERROR */
 
-                return ret;
-            }
-        }
+            return ret;
+         }
 
-        *value = *retval;
+        njs_type_error(vm, "Cannot convert object to primitive value");
 
-        njs_set_invalid(retval);
+        return ret;
     }
 
-    vm->top_frame->trap_tries = 0;
+    *dst = retval;
 
-    return 1;
+    return NXT_OK;
 }
 
 
index 4804e44eefc359445471f2887f0aa6cf5f13f1f3..ed73dd0be74dacd48bfcc29673f6ad3caa55ece1 100644 (file)
@@ -114,6 +114,9 @@ typedef struct njs_date_s             njs_date_t;
 typedef struct njs_property_next_s    njs_property_next_t;
 typedef struct njs_object_init_s      njs_object_init_t;
 
+#if (!NXT_HAVE_GCC_ATTRIBUTE_ALIGNED)
+#error "aligned attribute is required"
+#endif
 
 union njs_value_s {
     /*
@@ -560,10 +563,6 @@ typedef enum {
     *(value) = njs_value_undefined
 
 
-#define njs_set_boolean(value, yn)                                            \
-    *(value) = yn ? njs_value_true : njs_value_false
-
-
 #define njs_set_true(value)                                                   \
     *(value) = njs_value_true
 
@@ -572,6 +571,47 @@ typedef enum {
     *(value) = njs_value_false
 
 
+extern const njs_value_t  njs_value_null;
+extern const njs_value_t  njs_value_undefined;
+extern const njs_value_t  njs_value_false;
+extern const njs_value_t  njs_value_true;
+extern const njs_value_t  njs_value_zero;
+extern const njs_value_t  njs_value_nan;
+extern const njs_value_t  njs_value_invalid;
+
+extern const njs_value_t  njs_string_empty;
+extern const njs_value_t  njs_string_comma;
+extern const njs_value_t  njs_string_null;
+extern const njs_value_t  njs_string_undefined;
+extern const njs_value_t  njs_string_boolean;
+extern const njs_value_t  njs_string_false;
+extern const njs_value_t  njs_string_true;
+extern const njs_value_t  njs_string_number;
+extern const njs_value_t  njs_string_minus_zero;
+extern const njs_value_t  njs_string_minus_infinity;
+extern const njs_value_t  njs_string_plus_infinity;
+extern const njs_value_t  njs_string_nan;
+extern const njs_value_t  njs_string_string;
+extern const njs_value_t  njs_string_data;
+extern const njs_value_t  njs_string_external;
+extern const njs_value_t  njs_string_invalid;
+extern const njs_value_t  njs_string_object;
+extern const njs_value_t  njs_string_function;
+extern const njs_value_t  njs_string_memory_error;
+
+
+nxt_inline void
+njs_set_boolean(njs_value_t *value, unsigned yn)
+{
+    const njs_value_t  *retval;
+
+    /* Using const retval generates a better code at least on i386/amd64. */
+    retval = (yn) ? &njs_value_true : &njs_value_false;
+
+    *value = *retval;
+}
+
+
 nxt_inline void
 njs_set_number(njs_value_t *value, double num)
 {
@@ -685,8 +725,8 @@ njs_set_object_value(njs_value_t *value, njs_object_value_t *object_value)
 
 void njs_value_retain(njs_value_t *value);
 void njs_value_release(njs_vm_t *vm, njs_value_t *value);
-njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value,
-    nxt_uint_t hint);
+njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst,
+    njs_value_t *value, nxt_uint_t hint);
 njs_array_t *njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
     njs_object_enum_t kind, nxt_bool_t all);
 njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
@@ -694,9 +734,65 @@ njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value,
 const char *njs_type_string(njs_value_type_t type);
 const char *njs_arg_type_string(uint8_t arg);
 
+njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
+    const njs_value_t *src);
+double njs_string_to_number(const njs_value_t *value, nxt_bool_t parse_float);
+
 nxt_bool_t njs_string_eq(const njs_value_t *v1, const njs_value_t *v2);
 
 
+nxt_inline njs_ret_t
+njs_value_to_numeric(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value)
+{
+    double       num;
+    njs_ret_t    ret;
+    njs_value_t  primitive;
+
+    if (nxt_slow_path(!njs_is_primitive(value))) {
+        ret = njs_value_to_primitive(vm, &primitive, value, 0);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+
+        value = &primitive;
+    }
+
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        num = NAN;
+
+        if (njs_is_string(value)) {
+            num = njs_string_to_number(value, 0);
+        }
+
+    } else {
+        num = njs_number(value);
+    }
+
+    njs_set_number(dst, num);
+
+    return NXT_OK;
+}
+
+
+nxt_inline njs_ret_t
+njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value)
+{
+    njs_ret_t    ret;
+    njs_value_t  primitive;
+
+    if (nxt_slow_path(!njs_is_primitive(value))) {
+        ret = njs_value_to_primitive(vm, &primitive, value, 1);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+
+        value = &primitive;
+    }
+
+    return njs_primitive_value_to_string(vm, dst, value);
+}
+
+
 nxt_inline nxt_bool_t
 njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2)
 {
@@ -722,33 +818,4 @@ njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2)
 }
 
 
-extern const njs_value_t  njs_value_null;
-extern const njs_value_t  njs_value_undefined;
-extern const njs_value_t  njs_value_false;
-extern const njs_value_t  njs_value_true;
-extern const njs_value_t  njs_value_zero;
-extern const njs_value_t  njs_value_nan;
-extern const njs_value_t  njs_value_invalid;
-
-extern const njs_value_t  njs_string_empty;
-extern const njs_value_t  njs_string_comma;
-extern const njs_value_t  njs_string_null;
-extern const njs_value_t  njs_string_undefined;
-extern const njs_value_t  njs_string_boolean;
-extern const njs_value_t  njs_string_false;
-extern const njs_value_t  njs_string_true;
-extern const njs_value_t  njs_string_number;
-extern const njs_value_t  njs_string_minus_zero;
-extern const njs_value_t  njs_string_minus_infinity;
-extern const njs_value_t  njs_string_plus_infinity;
-extern const njs_value_t  njs_string_nan;
-extern const njs_value_t  njs_string_string;
-extern const njs_value_t  njs_string_data;
-extern const njs_value_t  njs_string_external;
-extern const njs_value_t  njs_string_invalid;
-extern const njs_value_t  njs_string_object;
-extern const njs_value_t  njs_string_function;
-extern const njs_value_t  njs_string_memory_error;
-
-
 #endif /* _NJS_VALUE_H_INCLUDED_ */
index 1217ce4a078326fe059d6ec02961c9b188e018d4..12bcbf1430292f84218013da60234e54433ce437 100644 (file)
@@ -23,10 +23,10 @@ struct njs_property_next_s {
 
 static njs_ret_t njs_string_concat(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2);
-static njs_ret_t njs_values_equal(njs_vm_t *vm, const njs_value_t *val1,
-    const njs_value_t *val2);
-static njs_ret_t njs_values_compare(njs_vm_t *vm, const njs_value_t *val1,
-    const njs_value_t *val2);
+static njs_ret_t njs_values_equal(njs_vm_t *vm, njs_value_t *val1,
+    njs_value_t *val2);
+static njs_ret_t njs_primitive_values_compare(njs_vm_t *vm, njs_value_t *val1,
+    njs_value_t *val2);
 static njs_ret_t njs_function_frame_create(njs_vm_t *vm, njs_value_t *value,
     const njs_value_t *this, uintptr_t nargs, nxt_bool_t ctor);
 static njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *value);
@@ -35,29 +35,6 @@ static void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
 static njs_ret_t njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *invld2);
 
-static void njs_vm_trap(njs_vm_t *vm, njs_trap_t trap, njs_value_t *value1,
-    njs_value_t *value2);
-static void njs_vm_trap_argument(njs_vm_t *vm, njs_trap_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,
-    njs_value_t *narg);
-static njs_ret_t njs_vmcode_addition_primitive(njs_vm_t *vm, njs_value_t *invld,
-    njs_value_t *narg);
-static njs_ret_t njs_vmcode_comparison_primitive(njs_vm_t *vm,
-    njs_value_t *invld, njs_value_t *narg);
-static njs_ret_t njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1,
-    njs_value_t *inlvd2);
-static njs_ret_t njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1,
-    njs_value_t *inlvd2);
-static njs_ret_t njs_vmcode_primitive_argument(njs_vm_t *vm,
-    njs_value_t *invld1, njs_value_t *inlvd2);
-static njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1,
-    njs_value_t *invld2);
-static njs_ret_t njs_object_value_to_string(njs_vm_t *vm, njs_value_t *value);
-static njs_ret_t njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
-    njs_value_t *invld2);
-
 static njs_ret_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame);
 
 void njs_debug(njs_index_t index, njs_value_t *value);
@@ -79,9 +56,8 @@ const nxt_str_t  njs_entry_anonymous =      nxt_string("anonymous");
 nxt_int_t
 njs_vmcode_interpreter(njs_vm_t *vm)
 {
-    u_char                *catch;
+    u_char                *catch, call;
     njs_ret_t             ret;
-    njs_trap_t            trap;
     njs_value_t           *retval, *value1, *value2;
     njs_frame_t           *frame;
     njs_native_frame_t    *previous;
@@ -147,44 +123,12 @@ start:
         }
     }
 
-    if (ret == NJS_TRAP) {
-        trap = vm->trap;
-
-        switch (trap) {
-
-        case NJS_TRAP_NUMBER:
-            value2 = value1;
-
-            /* Fall through. */
-
-        case NJS_TRAP_NUMBERS:
-        case NJS_TRAP_ADDITION:
-        case NJS_TRAP_COMPARISON:
-        case NJS_TRAP_INCDEC:
-        case NJS_TRAP_PROPERTY:
-
-            njs_vm_trap(vm, trap, value1, value2);
-
-            goto start;
-
-        case NJS_TRAP_NUMBER_ARG:
-        case NJS_TRAP_STRING_ARG:
-        case NJS_TRAP_PRIMITIVE_ARG:
-
-            njs_vm_trap_argument(vm, trap);
-
-            goto start;
-
-        default:
-            ret = NXT_ERROR;
-            break;
-        }
-    }
-
     if (ret == NXT_ERROR) {
 
         for ( ;; ) {
             frame = (njs_frame_t *) vm->top_frame;
+
+            call = frame->native.call;
             catch = frame->native.exception.catch;
 
             if (catch != NULL) {
@@ -214,10 +158,37 @@ start:
                 vm->stack_size -= frame->native.size;
                 nxt_mp_free(vm->mem_pool, frame);
             }
+
+            if (call) {
+                return NXT_ERROR;
+            }
         }
     }
 
-    /* NXT_AGAIN, NJS_STOP. */
+    /* NXT_ERROR, NJS_STOP. */
+
+    return ret;
+}
+
+
+nxt_int_t
+njs_vmcode_run(njs_vm_t *vm)
+{
+    njs_ret_t  ret;
+
+    if (nxt_slow_path(vm->count > 128)) {
+        njs_range_error(vm, "Maximum call stack size exceeded");
+        return NXT_ERROR;
+    }
+
+    vm->count++;
+
+    ret = njs_vmcode_interpreter(vm);
+    if (ret == NJS_STOP) {
+        ret = NJS_OK;
+    }
+
+    vm->count--;
 
     return ret;
 }
@@ -620,7 +591,6 @@ njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property)
 
         break;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
 
@@ -715,7 +685,6 @@ njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
     case NXT_DECLINED:
         break;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
 
@@ -882,50 +851,59 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
 }
 
 
-/*
- * The increment and decrement operations require only one value parameter.
- * However, if the value is not numeric, then the trap is generated and
- * value parameter points to a trap frame value converted to a numeric.
- * So the additional reference parameter points to the original value.
- */
-
 njs_ret_t
 njs_vmcode_increment(njs_vm_t *vm, njs_value_t *reference, njs_value_t *value)
 {
-    double  num;
-
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        num = njs_number(value) + 1.0;
+    double       num;
+    njs_ret_t    ret;
+    njs_value_t  numeric;
 
-        njs_release(vm, reference);
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
 
-        njs_set_number(reference, num);
-        vm->retval = *reference;
+        num = njs_number(&numeric);
 
-        return sizeof(njs_vmcode_3addr_t);
+    } else {
+        num = njs_number(value);
     }
 
-    return njs_trap(vm, NJS_TRAP_INCDEC);
+    njs_release(vm, reference);
+
+    njs_set_number(reference, num + 1.0);
+    vm->retval = *reference;
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *reference, njs_value_t *value)
 {
-    double  num;
-
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        num = njs_number(value) - 1.0;
+    double       num;
+    njs_ret_t    ret;
+    njs_value_t  numeric;
 
-        njs_release(vm, reference);
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
 
-        njs_set_number(reference, num);
-        vm->retval = *reference;
+        num = njs_number(&numeric);
 
-        return sizeof(njs_vmcode_3addr_t);
+    } else {
+        num = njs_number(value);
     }
 
-    return njs_trap(vm, NJS_TRAP_INCDEC);
+    njs_release(vm, reference);
+
+    njs_set_number(reference, num - 1.0);
+    vm->retval = *reference;
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
@@ -934,19 +912,27 @@ njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *reference,
     njs_value_t *value)
 {
     double  num;
+    njs_ret_t    ret;
+    njs_value_t  numeric;
 
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        num = njs_number(value);
-
-        njs_release(vm, reference);
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
 
-        njs_set_number(reference, num + 1.0);
-        njs_set_number(&vm->retval, num);
+        num = njs_number(&numeric);
 
-        return sizeof(njs_vmcode_3addr_t);
+    } else {
+        num = njs_number(value);
     }
 
-    return njs_trap(vm, NJS_TRAP_INCDEC);
+    njs_release(vm, reference);
+
+    njs_set_number(reference, num + 1.0);
+    njs_set_number(&vm->retval, num);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
@@ -954,20 +940,28 @@ njs_ret_t
 njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *reference,
     njs_value_t *value)
 {
-    double  num;
-
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        num = njs_number(value);
+    double       num;
+    njs_ret_t    ret;
+    njs_value_t  numeric;
 
-        njs_release(vm, reference);
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
 
-        njs_set_number(reference, num - 1.0);
-        njs_set_number(&vm->retval, num);
+        num = njs_number(&numeric);
 
-        return sizeof(njs_vmcode_3addr_t);
+    } else {
+        num = njs_number(value);
     }
 
-    return njs_trap(vm, NJS_TRAP_INCDEC);
+    njs_release(vm, reference);
+
+    njs_set_number(reference, num - 1.0);
+    njs_set_number(&vm->retval, num);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
@@ -1042,69 +1036,105 @@ njs_vmcode_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 njs_ret_t
 njs_vmcode_unary_plus(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 {
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        njs_set_number(&vm->retval, njs_number(value));
-        return sizeof(njs_vmcode_2addr_t);
+    njs_ret_t    ret;
+    njs_value_t  numeric;
+
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        value = &numeric;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBER);
+    njs_set_number(&vm->retval, njs_number(value));
+
+    return sizeof(njs_vmcode_2addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_unary_negation(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 {
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        njs_set_number(&vm->retval, - njs_number(value));
-        return sizeof(njs_vmcode_2addr_t);
+    njs_ret_t    ret;
+    njs_value_t  numeric;
+
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        value = &numeric;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBER);
+    njs_set_number(&vm->retval, -njs_number(value));
+
+    return sizeof(njs_vmcode_2addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_addition(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double       num;
     njs_ret_t    ret;
-    njs_value_t  *s1, *s2, *src, dst;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    njs_value_t  primitive1, primitive2, dst, *s1, *s2, *src;
 
-        num = njs_number(val1) + njs_number(val2);
-        njs_set_number(&vm->retval, num);
+    if (nxt_slow_path(!njs_is_primitive(val1))) {
 
-        return sizeof(njs_vmcode_3addr_t);
-    }
+        /*
+         * ECMAScript 5.1:
+         *   Date should return String, other types sould return Number.
+         */
+
+        ret = njs_value_to_primitive(vm, &primitive1, val1, njs_is_date(val1));
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-    if (nxt_fast_path(njs_is_string(val1) && njs_is_string(val2))) {
-        return njs_string_concat(vm, val1, val2);
+        val1 = &primitive1;
     }
 
-    if (nxt_fast_path(njs_is_primitive(val1) && njs_is_primitive(val2))) {
+    if (nxt_slow_path(!njs_is_primitive(val2))) {
 
-        if (njs_is_string(val1)) {
-            s1 = val1;
-            s2 = &dst;
-            src = val2;
+        /*
+         * ECMAScript 5.1:
+         *   Date should return String, other types sould return Number.
+         */
 
-        } else {
-            s1 = &dst;
-            s2 = val2;
-            src = val1;
+        ret = njs_value_to_primitive(vm, &primitive2, val2, njs_is_date(val2));
+        if (ret != NXT_OK) {
+            return ret;
         }
 
-        ret = njs_primitive_value_to_string(vm, &dst, src);
+        val2 = &primitive2;
+    }
 
-        if (nxt_fast_path(ret == NXT_OK)) {
-            return njs_string_concat(vm, s1, s2);
-        }
+    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+        njs_set_number(&vm->retval, njs_number(val1) + njs_number(val2));
+        return sizeof(njs_vmcode_3addr_t);
+    }
 
-        return ret;
+    if (njs_is_string(val1)) {
+        s1 = val1;
+        s2 = &dst;
+        src = val2;
+
+    } else {
+        s1 = &dst;
+        s2 = val2;
+        src = val1;
+    }
+
+    ret = njs_primitive_value_to_string(vm, &dst, src);
+
+    if (nxt_fast_path(ret == NXT_OK)) {
+        return njs_string_concat(vm, s1, s2);
     }
 
-    return njs_trap(vm, NJS_TRAP_ADDITION);
+    return ret;
 }
 
 
@@ -1149,140 +1179,240 @@ njs_string_concat(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 njs_ret_t
 njs_vmcode_substraction(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double  num;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num = njs_number(val1) - njs_number(val2);
-        njs_set_number(&vm->retval, num);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    njs_set_number(&vm->retval, njs_number(val1) - njs_number(val2));
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_multiplication(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double  num;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num = njs_number(val1) * njs_number(val2);
-        njs_set_number(&vm->retval, num);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    njs_set_number(&vm->retval, njs_number(val1) * njs_number(val2));
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_exponentiation(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double      num, base, exponent;
-    nxt_bool_t  valid;
-
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
-        base = njs_number(val1);
-        exponent = njs_number(val2);
+    double       num, base, exponent;
+    njs_ret_t    ret;
+    nxt_bool_t   valid;
+    njs_value_t  numeric1, numeric2;
 
-        /*
-         * According to ES7:
-         *  1. If exponent is NaN, the result should be NaN;
-         *  2. The result of +/-1 ** +/-Infinity should be NaN.
-         */
-        valid = nxt_expect(1, fabs(base) != 1
-                              || (!isnan(exponent) && !isinf(exponent)));
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        if (valid) {
-            num = pow(base, exponent);
+        val1 = &numeric1;
+    }
 
-        } else {
-            num = NAN;
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
         }
 
-        njs_set_number(&vm->retval, num);
+        val2 = &numeric2;
+    }
+
+    base = njs_number(val1);
+    exponent = njs_number(val2);
 
-        return sizeof(njs_vmcode_3addr_t);
+    /*
+     * According to ES7:
+     *  1. If exponent is NaN, the result should be NaN;
+     *  2. The result of +/-1 ** +/-Infinity should be NaN.
+     */
+    valid = nxt_expect(1, fabs(base) != 1
+                          || (!isnan(exponent) && !isinf(exponent)));
+
+    if (valid) {
+        num = pow(base, exponent);
+
+    } else {
+        num = NAN;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    njs_set_number(&vm->retval, num);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double  num;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num = njs_number(val1) / njs_number(val2);
-        njs_set_number(&vm->retval, num);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    njs_set_number(&vm->retval, njs_number(val1) / njs_number(val2));
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double  num;
+    double       num;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num = fmod(njs_number(val1), njs_number(val2));
-        njs_set_number(&vm->retval, num);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num = fmod(njs_number(val1), njs_number(val2));
+    njs_set_number(&vm->retval, num);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_left_shift(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    int32_t   num1;
-    uint32_t  num2;
+    int32_t      num1;
+    uint32_t     num2;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num1 = njs_number_to_int32(njs_number(val1));
-        num2 = njs_number_to_uint32(njs_number(val2));
-        njs_set_number(&vm->retval, num1 << (num2 & 0x1f));
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num1 = njs_number_to_int32(njs_number(val1));
+    num2 = njs_number_to_uint32(njs_number(val2));
+    njs_set_number(&vm->retval, num1 << (num2 & 0x1f));
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_right_shift(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    int32_t   num1;
-    uint32_t  num2;
+    int32_t      num1;
+    uint32_t     num2;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val1 = &numeric1;
+    }
 
-        num1 = njs_number_to_int32(njs_number(val1));
-        num2 = njs_number_to_uint32(njs_number(val2));
-        njs_set_number(&vm->retval, num1 >> (num2 & 0x1f));
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        return sizeof(njs_vmcode_3addr_t);
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num1 = njs_number_to_int32(njs_number(val1));
+    num2 = njs_number_to_uint32(njs_number(val2));
+    njs_set_number(&vm->retval, num1 >> (num2 & 0x1f));
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
@@ -1290,34 +1420,40 @@ njs_ret_t
 njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2)
 {
-    uint32_t  num1, num2;
+    uint32_t     num1, num2;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num1 = njs_number_to_uint32(njs_number(val1));
-        num2 = njs_number_to_uint32(njs_number(val2));
-        njs_set_number(&vm->retval, num1 >> (num2 & 0x1f));
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num1 = njs_number_to_uint32(njs_number(val1));
+    num2 = njs_number_to_uint32(njs_number(val2));
+    njs_set_number(&vm->retval, num1 >> (num2 & 0x1f));
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *inlvd)
 {
-    const njs_value_t  *retval;
-
-    if (njs_is_true(value)) {
-        retval = &njs_value_false;
-
-    } else {
-        retval = &njs_value_true;
-    }
-
-    vm->retval = *retval;
+    njs_set_boolean(&vm->retval, !njs_is_true(value));
 
     return sizeof(njs_vmcode_2addr_t);
 }
@@ -1358,85 +1494,133 @@ njs_vmcode_test_if_false(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 njs_ret_t
 njs_vmcode_bitwise_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 {
-    int32_t  num;
+    njs_ret_t    ret;
+    njs_value_t  numeric;
 
-    if (nxt_fast_path(njs_is_numeric(value))) {
-        num = njs_number_to_integer(njs_number(value));
-        njs_set_number(&vm->retval, ~num);
+    if (nxt_slow_path(!njs_is_numeric(value))) {
+        ret = njs_value_to_numeric(vm, &numeric, value);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        return sizeof(njs_vmcode_2addr_t);
+        value = &numeric;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBER);
+    njs_set_number(&vm->retval, ~njs_number_to_integer(njs_number(value)));
+
+    return sizeof(njs_vmcode_2addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_bitwise_and(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    int32_t  num1, num2;
+    int32_t      num1, num2;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num1 = njs_number_to_integer(njs_number(val1));
-        num2 = njs_number_to_integer(njs_number(val2));
-        njs_set_number(&vm->retval, num1 & num2);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num1 = njs_number_to_integer(njs_number(val1));
+    num2 = njs_number_to_integer(njs_number(val2));
+    njs_set_number(&vm->retval, num1 & num2);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_bitwise_xor(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    int32_t  num1, num2;
+    int32_t      num1, num2;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num1 = njs_number_to_integer(njs_number(val1));
-        num2 = njs_number_to_integer(njs_number(val2));
-        njs_set_number(&vm->retval, num1 ^ num2);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num1 = njs_number_to_integer(njs_number(val1));
+    num2 = njs_number_to_integer(njs_number(val2));
+    njs_set_number(&vm->retval, num1 ^ num2);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_bitwise_or(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    int32_t  num1, num2;
+    int32_t      num1, num2;
+    njs_ret_t    ret;
+    njs_value_t  numeric1, numeric2;
 
-    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+    if (nxt_slow_path(!njs_is_numeric(val1))) {
+        ret = njs_value_to_numeric(vm, &numeric1, val1);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        num1 = njs_number_to_uint32(njs_number(val1));
-        num2 = njs_number_to_uint32(njs_number(val2));
-        njs_set_number(&vm->retval, num1 | num2);
+        val1 = &numeric1;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    if (nxt_slow_path(!njs_is_numeric(val2))) {
+        ret = njs_value_to_numeric(vm, &numeric2, val2);
+        if (ret != NXT_OK) {
+            return ret;
+        }
+
+        val2 = &numeric2;
     }
 
-    return njs_trap(vm, NJS_TRAP_NUMBERS);
+    num1 = njs_number_to_integer(njs_number(val1));
+    num2 = njs_number_to_integer(njs_number(val2));
+    njs_set_number(&vm->retval, num1 | num2);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    njs_ret_t          ret;
-    const njs_value_t  *retval;
+    njs_ret_t  ret;
 
     ret = njs_values_equal(vm, val1, val2);
 
     if (nxt_fast_path(ret >= 0)) {
 
-        retval = (ret != 0) ? &njs_value_true : &njs_value_false;
-        vm->retval = *retval;
+        njs_set_boolean(&vm->retval, ret != 0);
 
         return sizeof(njs_vmcode_3addr_t);
     }
@@ -1448,15 +1632,13 @@ njs_vmcode_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 njs_ret_t
 njs_vmcode_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    njs_ret_t          ret;
-    const njs_value_t  *retval;
+    njs_ret_t  ret;
 
     ret = njs_values_equal(vm, val1, val2);
 
     if (nxt_fast_path(ret >= 0)) {
 
-        retval = (ret == 0) ? &njs_value_true : &njs_value_false;
-        vm->retval = *retval;
+        njs_set_boolean(&vm->retval, ret == 0);
 
         return sizeof(njs_vmcode_3addr_t);
     }
@@ -1466,10 +1648,14 @@ njs_vmcode_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 
 
 static njs_ret_t
-njs_values_equal(njs_vm_t *vm, const njs_value_t *val1, const njs_value_t *val2)
+njs_values_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    nxt_bool_t         nv1, nv2;
-    const njs_value_t  *hv, *lv;
+    njs_ret_t    ret;
+    nxt_bool_t   nv1, nv2;
+    njs_value_t  primitive;
+    njs_value_t  *hv, *lv;
+
+again:
 
     nv1 = njs_is_null_or_undefined(val1);
     nv2 = njs_is_null_or_undefined(val2);
@@ -1515,125 +1701,168 @@ njs_values_equal(njs_vm_t *vm, const njs_value_t *val1, const njs_value_t *val2)
     }
 
     /* "hv" is an object and "lv" is either a string or a numeric. */
-    return njs_trap(vm, NJS_TRAP_COMPARISON);
+
+    ret = njs_value_to_primitive(vm, &primitive, hv, 0);
+    if (ret != NXT_OK) {
+        return ret;
+    }
+
+    val1 = &primitive;
+    val2 = lv;
+
+    goto again;
 }
 
 
-njs_ret_t
-njs_vmcode_less(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+nxt_inline njs_ret_t
+njs_values_to_primitive(njs_vm_t *vm, njs_value_t *primitive1,
+    njs_value_t **val1, njs_value_t *primitive2, njs_value_t **val2)
 {
-    njs_ret_t          ret;
-    const njs_value_t  *retval;
+    njs_ret_t    ret;
+
+    if (nxt_slow_path(!njs_is_primitive(*val1))) {
+        ret = njs_value_to_primitive(vm, primitive1, *val1, 0);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-    ret = njs_values_compare(vm, val1, val2);
+        *val1 = primitive1;
+    }
 
-    if (nxt_fast_path(ret >= -1)) {
+    if (nxt_slow_path(!njs_is_primitive(*val2))) {
+        ret = njs_value_to_primitive(vm, primitive2, *val2, 0);
+        if (ret != NXT_OK) {
+            return ret;
+        }
 
-        retval = (ret > 0) ? &njs_value_true : &njs_value_false;
-        vm->retval = *retval;
+        *val2 = primitive2;
+    }
 
-        return sizeof(njs_vmcode_3addr_t);
+    return NXT_OK;
+}
+
+
+njs_ret_t
+njs_vmcode_less(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+    njs_ret_t    ret;
+    njs_value_t  primitive1, primitive2;
+
+    ret = njs_values_to_primitive(vm, &primitive1, &val1, &primitive2, &val2);
+    if (ret != NXT_OK) {
+        return ret;
     }
 
-    return ret;
+    ret = njs_primitive_values_compare(vm, val1, val2);
+
+    njs_set_boolean(&vm->retval, ret > 0);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_greater(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    return njs_vmcode_less(vm, val2, val1);
+    njs_ret_t    ret;
+    njs_value_t  primitive1, primitive2;
+
+    ret = njs_values_to_primitive(vm, &primitive1, &val1, &primitive2, &val2);
+    if (ret != NXT_OK) {
+        return ret;
+    }
+
+    ret = njs_primitive_values_compare(vm, val2, val1);
+
+    njs_set_boolean(&vm->retval, ret > 0);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_less_or_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    return njs_vmcode_greater_or_equal(vm, val2, val1);
+    njs_ret_t    ret;
+    njs_value_t  primitive1, primitive2;
+
+    ret = njs_values_to_primitive(vm, &primitive1, &val1, &primitive2, &val2);
+    if (ret != NXT_OK) {
+        return ret;
+    }
+
+    ret = njs_primitive_values_compare(vm, val2, val1);
+
+    njs_set_boolean(&vm->retval, ret == 0);
+
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
 njs_vmcode_greater_or_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    njs_ret_t          ret;
-    const njs_value_t  *retval;
-
-    ret = njs_values_compare(vm, val1, val2);
+    njs_ret_t    ret;
+    njs_value_t  primitive1, primitive2;
 
-    if (nxt_fast_path(ret >= -1)) {
+    ret = njs_values_to_primitive(vm, &primitive1, &val1, &primitive2, &val2);
+    if (ret != NXT_OK) {
+        return ret;
+    }
 
-        retval = (ret == 0) ? &njs_value_true : &njs_value_false;
-        vm->retval = *retval;
+    ret = njs_primitive_values_compare(vm, val1, val2);
 
-        return sizeof(njs_vmcode_3addr_t);
-    }
+    njs_set_boolean(&vm->retval, ret == 0);
 
-    return ret;
+    return sizeof(njs_vmcode_3addr_t);
 }
 
 
 /*
  * ECMAScript 5.1: 11.8.5
- * njs_values_compare() returns
+ * njs_primitive_values_compare() returns
  *   1 if val1 is less than val2,
  *   0 if val1 is greater than or equal to val2,
- *  -1 if the values are not comparable,
- *  or negative trap number if convertion to primitive is required.
+ *  -1 if the values are not comparable.
  */
 
 static njs_ret_t
-njs_values_compare(njs_vm_t *vm, const njs_value_t *val1,
-    const njs_value_t *val2)
+njs_primitive_values_compare(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    double  num1, num2;
-
-    if (nxt_fast_path(njs_is_primitive(val1) && njs_is_primitive(val2))) {
-
-        if (nxt_fast_path(njs_is_numeric(val1))) {
-            num1 = njs_number(val1);
+    double   num1, num2;
 
-            if (nxt_fast_path(njs_is_numeric(val2))) {
-                num2 = njs_number(val2);
+    if (nxt_fast_path(njs_is_numeric(val1))) {
+        num1 = njs_number(val1);
 
-            } else {
-                num2 = njs_string_to_number(val2, 0);
-            }
-
-        } else if (njs_is_numeric(val2)) {
-            num1 = njs_string_to_number(val1, 0);
+        if (nxt_fast_path(njs_is_numeric(val2))) {
             num2 = njs_number(val2);
 
         } else {
-            return (njs_string_cmp(val1, val2) < 0) ? 1 : 0;
+            num2 = njs_string_to_number(val2, 0);
         }
 
-        /* NaN and void values are not comparable with anything. */
-        if (isnan(num1) || isnan(num2)) {
-            return -1;
-        }
+    } else if (njs_is_numeric(val2)) {
+        num1 = njs_string_to_number(val1, 0);
+        num2 = njs_number(val2);
+
+    } else {
+        return (njs_string_cmp(val1, val2) < 0) ? 1 : 0;
+    }
 
-        /* Infinities are handled correctly by comparision. */
-        return (num1 < num2);
+    /* NaN and void values are not comparable with anything. */
+    if (isnan(num1) || isnan(num2)) {
+        return -1;
     }
 
-    return njs_trap(vm, NJS_TRAP_COMPARISON);
+    /* Infinities are handled correctly by comparision. */
+    return (num1 < num2);
 }
 
 
 njs_ret_t
 njs_vmcode_strict_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    const njs_value_t  *retval;
-
-    if (njs_values_strict_equal(val1, val2)) {
-        retval = &njs_value_true;
-
-    } else {
-        retval = &njs_value_false;
-    }
-
-    vm->retval = *retval;
+    njs_set_boolean(&vm->retval, njs_values_strict_equal(val1, val2));
 
     return sizeof(njs_vmcode_3addr_t);
 }
@@ -1642,16 +1871,7 @@ njs_vmcode_strict_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 njs_ret_t
 njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
-    const njs_value_t  *retval;
-
-    if (njs_values_strict_equal(val1, val2)) {
-        retval = &njs_value_false;
-
-    } else {
-        retval = &njs_value_true;
-    }
-
-    vm->retval = *retval;
+    njs_set_boolean(&vm->retval, !njs_values_strict_equal(val1, val2));
 
     return sizeof(njs_vmcode_3addr_t);
 }
@@ -1863,7 +2083,6 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
     case NXT_DECLINED:
         break;
 
-    case NJS_TRAP:
     case NXT_ERROR:
     default:
 
@@ -1871,6 +2090,11 @@ njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
     }
 
     if (value == NULL || !njs_is_function(value)) {
+        ret = njs_value_to_string(vm, name, name);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+
         njs_string_get(name, &string);
         njs_type_error(vm, "(intermediate value)[\"%V\"] is not a function",
                        &string);
@@ -1935,6 +2159,7 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 njs_ret_t
 njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 {
+    nxt_int_t           ret;
     njs_value_t         *value;
     njs_frame_t         *frame;
     njs_native_frame_t  *previous;
@@ -1967,9 +2192,11 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 
     vm->current = frame->return_address;
 
+    ret = frame->native.call ? NJS_STOP : 0;
+
     njs_function_frame_free(vm, &frame->native);
 
-    return 0;
+    return ret;
 }
 
 
@@ -2289,384 +2516,6 @@ njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1,
 }
 
 
-static const njs_vmcode_1addr_t  njs_trap_number[] = {
-    { .code = { .operation = njs_vmcode_number_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 0 },
-    { .code = { .operation = njs_vmcode_restart,
-                .operands =  NJS_VMCODE_NO_OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL } },
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_numbers[] = {
-    { .code = { .operation = njs_vmcode_number_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 0 },
-    { .code = { .operation = njs_vmcode_number_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 1 },
-    { .code = { .operation = njs_vmcode_restart,
-                .operands =  NJS_VMCODE_NO_OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL } },
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_addition[] = {
-    { .code = { .operation = njs_vmcode_addition_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 0 },
-    { .code = { .operation = njs_vmcode_addition_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 1 },
-    { .code = { .operation = njs_vmcode_restart,
-                .operands =  NJS_VMCODE_NO_OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL } },
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_comparison[] = {
-    { .code = { .operation = njs_vmcode_comparison_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 0 },
-    { .code = { .operation = njs_vmcode_comparison_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 1 },
-    { .code = { .operation = njs_vmcode_restart,
-                .operands =  NJS_VMCODE_NO_OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL } },
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_property[] = {
-    { .code = { .operation = njs_vmcode_string_primitive,
-                .operands =  NJS_VMCODE_1OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL },
-      .index = 1 },
-    { .code = { .operation = njs_vmcode_restart,
-                .operands =  NJS_VMCODE_NO_OPERAND,
-                .retval = NJS_VMCODE_NO_RETVAL } },
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_number_argument = {
-    .code = { .operation = njs_vmcode_number_argument,
-              .operands =  NJS_VMCODE_NO_OPERAND,
-              .retval = NJS_VMCODE_NO_RETVAL }
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_string_argument = {
-    .code = { .operation = njs_vmcode_string_argument,
-              .operands =  NJS_VMCODE_NO_OPERAND,
-              .retval = NJS_VMCODE_NO_RETVAL }
-};
-
-
-static const njs_vmcode_1addr_t  njs_trap_primitive_argument = {
-    .code = { .operation = njs_vmcode_primitive_argument,
-              .operands =  NJS_VMCODE_NO_OPERAND,
-              .retval = NJS_VMCODE_NO_RETVAL }
-};
-
-
-static const njs_vm_trap_t  njs_vm_traps[] = {
-    /* NJS_TRAP_NUMBER     */     { .code = &njs_trap_number[0]       },
-    /* NJS_TRAP_NUMBERS    */     { .code = &njs_trap_numbers[0]      },
-    /* NJS_TRAP_ADDITION   */     { .code = &njs_trap_addition[0]     },
-    /* NJS_TRAP_COMPARISON */     { .code = &njs_trap_comparison[0]   },
-    /* NJS_TRAP_INCDEC     */     { .code = &njs_trap_numbers[1],
-                                    .reference = 1                    },
-    /* NJS_TRAP_PROPERTY   */     { .code = &njs_trap_property[0]     },
-    /* NJS_TRAP_NUMBER_ARG */     { .code = &njs_trap_number_argument },
-    /* NJS_TRAP_STRING_ARG */     { .code = &njs_trap_string_argument },
-    /* NJS_TRAP_PRIMITIVE_ARG */  { .code = &njs_trap_primitive_argument },
-};
-
-
-static void
-njs_vm_trap(njs_vm_t *vm, njs_trap_t trap, njs_value_t *value1,
-    njs_value_t *value2)
-{
-    njs_native_frame_t  *frame;
-
-    frame = vm->top_frame;
-
-    /*
-     * The trap_scratch value is for results of "valueOf" and "toString"
-     * methods.  The trap_values[] are original operand values which will
-     * be replaced with primitive values returned by "valueOf" or "toString"
-     * methods.  The scratch value is stored separately to preserve the
-     * original operand values for the second method call if the first
-     * method call will return non-primitive value.
-     */
-    njs_set_invalid(&frame->trap_scratch);
-    frame->trap_values[1] = *value2;
-    frame->trap_reference = njs_vm_traps[trap].reference;
-
-    if (njs_vm_traps[trap].reference) {
-        frame->trap_values[0].data.u.value = value1;
-
-    } else {
-        frame->trap_values[0] = *value1;
-    }
-
-    frame->trap_restart = vm->current;
-    vm->current = (u_char *) njs_vm_traps[trap].code;
-}
-
-
-static void
-njs_vm_trap_argument(njs_vm_t *vm, njs_trap_t trap)
-{
-    njs_value_t         *value;
-    njs_native_frame_t  *frame;
-
-    frame = vm->top_frame;
-    value = frame->trap_scratch.data.u.value;
-    njs_set_invalid(&frame->trap_scratch);
-
-    frame->trap_values[1].data.u.value = value;
-    frame->trap_values[0] = *value;
-
-    frame->trap_restart = vm->current;
-    vm->current = (u_char *) njs_vm_traps[trap].code;
-}
-
-
-static njs_ret_t
-njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
-{
-    double       num;
-    njs_ret_t    ret;
-    njs_value_t  *value;
-
-    value = &vm->top_frame->trap_values[(uintptr_t) narg];
-
-    ret = njs_value_to_primitive(vm, value, 0);
-
-    if (nxt_fast_path(ret > 0)) {
-
-        if (!njs_is_numeric(value)) {
-            num = NAN;
-
-            if (njs_is_string(value)) {
-                num = njs_string_to_number(value, 0);
-            }
-
-            njs_set_number(value, num);
-        }
-
-        ret = sizeof(njs_vmcode_1addr_t);
-    }
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
-{
-    njs_ret_t    ret;
-    njs_value_t  *value;
-
-    value = &vm->top_frame->trap_values[(uintptr_t) narg];
-
-    ret = njs_value_to_primitive(vm, value, 1);
-
-    if (nxt_fast_path(ret > 0)) {
-        ret = njs_primitive_value_to_string(vm, value, value);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-            return sizeof(njs_vmcode_1addr_t);
-        }
-    }
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_vmcode_addition_primitive(njs_vm_t *vm, njs_value_t *invld,
-    njs_value_t *narg)
-{
-    njs_ret_t    ret;
-    nxt_uint_t   hint;
-    njs_value_t  *value;
-
-    value = &vm->top_frame->trap_values[(uintptr_t) narg];
-
-    /*
-     * ECMAScript 5.1:
-     *   Date should return String, other types sould return Number.
-     */
-    hint = njs_is_date(value);
-
-    ret = njs_value_to_primitive(vm, value, hint);
-
-    if (nxt_fast_path(ret > 0)) {
-        return sizeof(njs_vmcode_1addr_t);
-    }
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_vmcode_comparison_primitive(njs_vm_t *vm, njs_value_t *invld,
-    njs_value_t *narg)
-{
-    njs_ret_t    ret;
-    njs_value_t  *value;
-
-    value = &vm->top_frame->trap_values[(uintptr_t) narg];
-
-    ret = njs_value_to_primitive(vm, value, 0);
-
-    if (nxt_fast_path(ret > 0)) {
-        return sizeof(njs_vmcode_1addr_t);
-    }
-
-    return ret;
-}
-
-
-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;
-
-    value = &vm->top_frame->trap_values[0];
-
-    ret = njs_value_to_primitive(vm, value, 0);
-
-    if (nxt_fast_path(ret > 0)) {
-
-        if (!njs_is_numeric(value)) {
-            num = NAN;
-
-            if (njs_is_string(value)) {
-                num = njs_string_to_number(value, 0);
-            }
-
-            njs_set_number(value, num);
-        }
-
-        *vm->top_frame->trap_values[1].data.u.value = *value;
-
-        vm->current = vm->top_frame->trap_restart;
-        vm->top_frame->trap_restart = NULL;
-
-        return 0;
-    }
-
-    return ret;
-}
-
-
-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;
-
-    value = &vm->top_frame->trap_values[0];
-
-    ret = njs_value_to_primitive(vm, value, 1);
-
-    if (nxt_fast_path(ret > 0)) {
-        ret = njs_primitive_value_to_string(vm, value, value);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-            *vm->top_frame->trap_values[1].data.u.value = *value;
-
-            vm->current = vm->top_frame->trap_restart;
-            vm->top_frame->trap_restart = NULL;
-        }
-    }
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_vmcode_primitive_argument(njs_vm_t *vm, njs_value_t *invld1,
-    njs_value_t *inlvd2)
-{
-    njs_ret_t    ret;
-    njs_value_t  *value;
-
-    value = &vm->top_frame->trap_values[0];
-
-    ret = njs_value_to_primitive(vm, value, 0);
-
-    if (nxt_fast_path(ret > 0)) {
-        *vm->top_frame->trap_values[1].data.u.value = *value;
-
-        vm->current = vm->top_frame->trap_restart;
-        vm->top_frame->trap_restart = NULL;
-
-        return 0;
-    }
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
-    u_char                *restart;
-    njs_ret_t             ret;
-    njs_value_t           *retval, *value1;
-    njs_native_frame_t    *frame;
-    njs_vmcode_generic_t  *vmcode;
-
-    frame = vm->top_frame;
-    restart = frame->trap_restart;
-    frame->trap_restart = NULL;
-    vm->current = restart;
-    vmcode = (njs_vmcode_generic_t *) restart;
-
-    value1 = &frame->trap_values[0];
-
-    if (frame->trap_reference) {
-        value1 = value1->data.u.value;
-    }
-
-    ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]);
-
-    if (nxt_slow_path(ret == NJS_ERROR)) {
-        return ret;
-    }
-
-    if (nxt_slow_path(ret == NJS_TRAP)) {
-        /* Trap handlers are not reentrant. */
-        njs_internal_error(vm, "trap inside restart instruction");
-        return NXT_ERROR;
-    }
-
-    if (vmcode->code.retval) {
-        retval = njs_vmcode_operand(vm, vmcode->operand1);
-        njs_release(vm, retval);
-        *retval = vm->retval;
-    }
-
-    return ret;
-}
-
-
 njs_ret_t
 njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
 {
@@ -2689,14 +2538,7 @@ njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
 
     value = *src;
 
-    if (nxt_slow_path(!njs_is_primitive(&value))) {
-        ret = njs_object_value_to_string(vm, &value);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-    }
-
-    ret = njs_primitive_value_to_string(vm, &value, &value);
+    ret = njs_value_to_string(vm, &value, &value);
 
     if (nxt_fast_path(ret == NXT_OK)) {
         size = value.short_string.size;
@@ -2723,73 +2565,6 @@ njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
 }
 
 
-static njs_ret_t
-njs_object_value_to_string(njs_vm_t *vm, njs_value_t *value)
-{
-    u_char              *current;
-    njs_ret_t           ret;
-    njs_native_frame_t  *previous;
-
-    static const njs_vmcode_1addr_t  value_to_string[] = {
-        { .code = { .operation = njs_vmcode_value_to_string,
-                    .operands =  NJS_VMCODE_NO_OPERAND,
-                    .retval = NJS_VMCODE_NO_RETVAL } },
-    };
-
-    /*
-     * Execute the single njs_vmcode_value_to_string() instruction.
-     * The trap_scratch value is for results of "toString" or "valueOf"
-     * methods.  The trap_values[0] is an original object value which will
-     * be replaced with primitive value returned by "toString" or "valueOf"
-     * methods.  The scratch value is stored separately to preserve the
-     * original object value for the second "valueOf" method call if the
-     * first "toString" method call will return non-primitive value.
-     */
-
-    current = vm->current;
-    vm->current = (u_char *) value_to_string;
-
-    njs_set_invalid(&vm->top_frame->trap_scratch);
-    vm->top_frame->trap_values[0] = *value;
-
-    /*
-     * Prevent njs_vmcode_interpreter() to unwind the current frame if
-     * an exception happens.  It preserves the current frame state if
-     * njs_vm_value_string() is called from within njs_vm_run().
-     */
-    previous = vm->top_frame->previous;
-    vm->top_frame->previous = NULL;
-
-    ret = njs_vmcode_interpreter(vm);
-
-    if (ret == NJS_STOP) {
-        ret = NXT_OK;
-        *value = vm->top_frame->trap_values[0];
-    }
-
-    vm->current = current;
-    vm->top_frame->previous = previous;
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
-    njs_value_t *invld2)
-{
-    njs_ret_t  ret;
-
-    ret = njs_value_to_primitive(vm, &vm->top_frame->trap_values[0], 1);
-
-    if (nxt_fast_path(ret > 0)) {
-        return NJS_STOP;
-    }
-
-    return ret;
-}
-
-
 nxt_int_t
 njs_vm_value_string_copy(njs_vm_t *vm, nxt_str_t *retval,
     const njs_value_t *value, uintptr_t *next)
index e6029c11921f4ea4b6adc63086675c78c6fcf152..67be5d417c00a6b40381bf040b3a49e2bc25eec5 100644 (file)
  *    -3:                        not used;
  *    -4 (NJS_STOP/NXT_DONE):    njs_vmcode_stop() has stopped execution,
  *                               execution has completed successfully;
- *    -5 (NJS_TRAP)              trap to convert objects to primitive values;
- *    -6 .. -11:                 not used.
+ *    -5 .. -11:                 not used.
  */
 
 #define NJS_STOP                 NXT_DONE
-#define NJS_TRAP                 (-5)
 
 /* The last return value which preempts execution. */
 #define NJS_PREEMPT              (-11)
 
-/*  Traps events. */
-typedef enum {
-    NJS_TRAP_NUMBER = 0,
-    NJS_TRAP_NUMBERS,
-    NJS_TRAP_ADDITION,
-    NJS_TRAP_COMPARISON,
-    NJS_TRAP_INCDEC,
-    NJS_TRAP_PROPERTY,
-    NJS_TRAP_NUMBER_ARG,
-    NJS_TRAP_STRING_ARG,
-    NJS_TRAP_PRIMITIVE_ARG,
-} njs_trap_t;
-
-
-#define njs_trap(vm, code)                                                    \
-    vm->trap = code, NJS_TRAP;
-
 
 /*
  * A user-defined function is prepared to run.  This code is never
@@ -531,12 +512,6 @@ enum njs_function_e {
       + njs_scope_offset(index)))
 
 
-typedef struct {
-    const njs_vmcode_1addr_t  *code;
-    nxt_bool_t                reference;
-} njs_vm_trap_t;
-
-
 typedef struct {
     uint32_t                  line;
     nxt_str_t                 file;
@@ -549,6 +524,8 @@ struct njs_vm_s {
     /* njs_vm_t must be aligned to njs_value_t due to scratch value. */
     njs_value_t              retval;
 
+    nxt_uint_t               count;
+
     nxt_array_t              *paths;
 
     u_char                   *current;
@@ -613,8 +590,6 @@ struct njs_vm_s {
     nxt_array_t              *debug;
     nxt_array_t              *backtrace;
 
-    njs_trap_t               trap:8;
-
     /*
      * njs_property_query() uses it to store reference to a temporary
      * PROPERTY_HANDLERs for NJS_EXTERNAL values in NJS_PROPERTY_QUERY_SET
@@ -659,6 +634,7 @@ struct njs_vm_shared_s {
 
 
 nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm);
+nxt_int_t njs_vmcode_run(njs_vm_t *vm);
 
 njs_ret_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *inlvd1,
     njs_value_t *inlvd2);
index d18aa215b3970113ed1356aeedeb46b7ed7d8bbc..65653197ce9a88be94bd9f9cc95347c4266346d5 100644 (file)
@@ -230,8 +230,7 @@ static njs_interactive_test_t  njs_test[] =
 
     { nxt_string("var o = { toString: function() { return [1] } }" ENTER
                  "o" ENTER),
-      nxt_string("TypeError: Cannot convert object to primitive value\n"
-                 "    at main (native)\n") },
+      nxt_string("TypeError: Cannot convert object to primitive value") },
 
     /* line numbers */
 
index 7777d12eed389be9b6d21d622c55a03d88c11993..5d6c7ea536bddc96bbe6d3278f8d87c8fab395c9 100644 (file)
@@ -452,6 +452,56 @@ static njs_unit_test_t  njs_test[] =
                  "var b = { toString: function() { return a+'b' } }; '0'+b"),
       nxt_string("0ab") },
 
+    { nxt_string("({valueOf:()=>'4'}) / ({valueOf:()=>2})"),
+      nxt_string("2") },
+
+    { nxt_string("({valueOf:()=>{throw 'x'}}) / ({valueOf:()=>{throw 'y'}});"
+                 "var e; try { x/y } catch(ex) {e = ex}; ex"),
+      nxt_string("x") },
+
+    { nxt_string("({valueOf:()=>{ try {throw 'x'} catch (ex) {return 6} } }) / 2"),
+      nxt_string("3") },
+
+    { nxt_string("({valueOf:()=>2}) / ({valueOf:()=>{throw 'y'}});"
+                 "var e; try { x/y } catch(ex) {e = ex}; ex"),
+      nxt_string("y") },
+
+    { nxt_string("({valueOf:()=>'4'}) % ({valueOf:()=>3})"),
+      nxt_string("1") },
+
+    { nxt_string("({valueOf:()=>9}) >>> ({valueOf:()=>2})"),
+      nxt_string("2") },
+
+    { nxt_string("({valueOf:()=>0x1f}) & ({valueOf:()=>0xf})"),
+      nxt_string("15") },
+
+    { nxt_string("({valueOf:()=>0x1f}) ^ ({valueOf:()=>0xf})"),
+      nxt_string("16") },
+
+    { nxt_string("({valueOf:()=>0xf}) == ({valueOf:()=>0xf})"),
+      nxt_string("false") },
+
+    { nxt_string("var e; try {({valueOf: String.prototype.valueOf}) == 1} "
+                 "catch (ex) { e = ex}; e"),
+      nxt_string("TypeError: unexpected value type:object") },
+
+    { nxt_string("({valueOf:()=>0xf}) == 0xf"),
+      nxt_string("true") },
+
+    { nxt_string("0xf == ({valueOf:()=>0xf})"),
+      nxt_string("true") },
+
+    { nxt_string("({valueOf:()=>'0xf'}) == 0xf"),
+      nxt_string("true") },
+
+    { nxt_string("0xf == ({valueOf:()=>'0xf'})"),
+      nxt_string("true") },
+
+    { nxt_string("({valueOf:()=>0xf}) == '0xf'"),
+      nxt_string("true") },
+
+    { nxt_string("'0xf' == ({valueOf:()=>0xf})"),
+      nxt_string("true") },
     /**/
 
     { nxt_string("1 + undefined"),
@@ -1466,6 +1516,15 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = { valueOf: function() { return '1' } };   null < a"),
       nxt_string("true") },
 
+    { nxt_string("1 < {valueOf: ()=>{throw 'x'}}"),
+      nxt_string("x") },
+
+    { nxt_string("({valueOf: ()=>{throw 'x'}}) <= ({valueOf:()=>{throw 'y'}})"),
+      nxt_string("x") },
+
+    { nxt_string("({valueOf:()=>{throw 'x'}}) > ({valueOf:()=>{throw 'y'}})"),
+      nxt_string("x") },
+
     /**/
 
     { nxt_string("undefined == undefined"),
@@ -1650,6 +1709,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("new 0[isNaN]"),
       nxt_string("TypeError: (intermediate value)[\"[object Function]\"] is not a function") },
 
+    { nxt_string("new 0[undefined]"),
+      nxt_string("TypeError: (intermediate value)[\"undefined\"] is not a function") },
+
     /**/
 
     { nxt_string("var a; a = 1 ? 2 : 3"),
@@ -4876,6 +4938,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("String.prototype.substring(1, 5)"),
       nxt_string("") },
 
+    { nxt_string("String.prototype.substr.call({toString:()=>{throw new Error('Oops')}})"),
+      nxt_string("Error: Oops") },
+
     { nxt_string("String.prototype.slice(1, 5)"),
       nxt_string("") },
 
@@ -4923,6 +4988,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abc'.charCodeAt(3)"),
       nxt_string("NaN") },
 
+    { nxt_string("'abc'.charCodeAt(undefined)"),
+      nxt_string("97") },
+
     { nxt_string("var a = 'abcdef'; a.3"),
       nxt_string("SyntaxError: Unexpected token \".3\" in 1") },
 
@@ -6549,6 +6617,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("(function(){ function f() {return f}; return f()})()"),
       nxt_string("[object Function]") },
 
+    { nxt_string("function  f() {}; f.toString = ()=> 'F'; ({'F':1})[f]"),
+      nxt_string("1") },
+
     { nxt_string("var a = ''; "
                  "function f(list) {"
                  "    function add(v) {a+=v};"
@@ -10472,6 +10543,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Date.UTC(2011, 5, 24, 6, 0)"),
       nxt_string("1308895200000") },
 
+    { nxt_string("Date.UTC({valueOf:()=>2011}, 5, 24, 6, 0)"),
+      nxt_string("1308895200000") },
+
     { nxt_string("Date.parse()"),
       nxt_string("NaN") },