]> git.kaiwu.me - njs.git/commitdiff
Trap changes.
authorIgor Sysoev <igor@sysoev.ru>
Wed, 18 Nov 2015 12:45:58 +0000 (15:45 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Wed, 18 Nov 2015 12:45:58 +0000 (15:45 +0300)
njs/njs_function.c
njs/njs_function.h
njs/njs_generator.c
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.c
njs/test/njs_unit_test.c

index 5cb1b00d3146078b3c2a8876ffca41e50ad456c6..7f6c645c8974685323cd8328b4f19321a561455e 100644 (file)
 #include <string.h>
 
 
+static const njs_vmcode_1addr_t  njs_trap_strings[] = {
+    { .code = { .operation = njs_vmcode_string_primitive,
+                .operands =  NJS_VMCODE_1OPERAND,
+                .retval = NJS_VMCODE_NO_RETVAL },
+      .index = 0 },
+    { .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_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_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_vm_trap_t  njs_vm_traps[] = {
+    /* NJS_TRAP_PROPERTY */  { &njs_trap_strings[1], 0 },
+    /* NJS_TRAP_STRINGS */   { &njs_trap_strings[0], 0 },
+    /* NJS_TRAP_INCDEC */    { &njs_trap_numbers[1], 1 },
+    /* NJS_TRAP_NUMBERS */   { &njs_trap_numbers[0], 0 },
+    /* NJS_TRAP_NUMBER */    { &njs_trap_number[0],  0 },
+};
+
+
 njs_function_t *
 njs_function_alloc(njs_vm_t *vm)
 {
@@ -74,7 +124,7 @@ njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method, uintptr_t nargs,
 
     frame->ctor = ctor;
     frame->reentrant = 0;
-    frame->lvalue = 0;
+    frame->trap_reference = 0;
 
     frame->u.exception.next = NULL;
     frame->u.exception.catch = NULL;
@@ -93,11 +143,11 @@ njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method, uintptr_t nargs,
 
 
 njs_ret_t
-njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
-    njs_value_t *value2, nxt_bool_t lvalue)
+njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
+    njs_value_t *value2)
 {
     size_t              size, spare_size;
-    njs_value_t         *data;
+    njs_value_t         *values;
     njs_native_frame_t  *frame;
 
     size = NJS_NATIVE_FRAME_SIZE + 3 * sizeof(njs_value_t);
@@ -123,22 +173,23 @@ njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
 
     frame->ctor = 0;
     frame->reentrant = 0;
-    frame->lvalue = lvalue;
 
-    data = njs_native_data(frame);
-    njs_set_invalid(&data[0]);
-    data[1] = *value1;
+    values = njs_native_data(frame);
+    njs_set_invalid(&values[0]);
+    values[2] = *value2;
+
+    frame->trap_reference = njs_vm_traps[trap].reference_value;
 
-    if (lvalue) {
-        data[2].data.u.value = value2;
+    if (njs_vm_traps[trap].reference_value) {
+        values[1].data.u.value = value1;
 
     } else {
-        data[2] = *value2;
+        values[1] = *value1;
     }
 
     frame->u.exception.catch = NULL;
     frame->u.restart = vm->current;
-    vm->current = trap;
+    vm->current = (u_char *) njs_vm_traps[trap].code;
 
     frame->last = (u_char *) frame + size;
     frame->previous = vm->frame;
@@ -216,7 +267,7 @@ njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name, njs_param_t *param,
 
     frame->native.ctor = ctor;
     frame->native.reentrant = 0;
-    frame->native.lvalue = 0;
+    frame->native.trap_reference = 0;
 
     frame->native.u.exception.next = NULL;
     frame->native.u.exception.catch = NULL;
index be856285c8726e5d567b24553f5128c3d21aef7c..9c045bec0cac2b7fd636ff02d7c61661800256ed 100644 (file)
@@ -96,10 +96,10 @@ struct njs_native_frame_s {
 
     uint32_t                       size;
 
-    uint8_t                        start;      /* 1 bit */
-    uint8_t                        ctor;       /* 1 bit */
-    uint8_t                        reentrant;  /* 1 bit */
-    uint8_t                        lvalue;     /* 1 bit */
+    uint8_t                        start;           /* 1 bit */
+    uint8_t                        ctor;            /* 1 bit */
+    uint8_t                        reentrant;       /* 1 bit */
+    uint8_t                        trap_reference;  /* 1 bit */
 };
 
 
@@ -121,8 +121,8 @@ njs_ret_t njs_function_apply(njs_vm_t *vm, njs_value_t *name,
     njs_param_t *param);
 njs_value_t *njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method,
     uintptr_t nargs, nxt_bool_t ctor);
-njs_ret_t njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
-    njs_value_t *value2, nxt_bool_t lvalue);
+njs_ret_t njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
+    njs_value_t *value2);
 njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name,
     njs_param_t *param, nxt_bool_t ctor);
 njs_ret_t njs_function_call(njs_vm_t *vm, njs_function_t *func,
index 652116aa1c142d8ada77b1010365def178a7421c..b3ad32d9a2094a9a3ef9c1705d6ccf2a606be88a 100644 (file)
@@ -80,7 +80,8 @@ static nxt_int_t njs_generator_index_release(njs_vm_t *vm, njs_parser_t *parser,
 static nxt_int_t
 njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
-    nxt_int_t  ret;
+    nxt_int_t          ret;
+    njs_parser_node_t  *left;
 
     if (node == NULL) {
         return NXT_OK;
@@ -148,6 +149,19 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_REMAINDER_ASSIGNMENT:
         return njs_generate_operation_assignment(vm, parser, node);
 
+    case NJS_TOKEN_IN:
+        /*
+         * An "in" operation is parsed as standard binary expression by
+         * njs_parser_binary_expression().  However, its operands should
+         * be swapped to be uniform with other property operations (get/set
+         * and delete) to use the array and object property traps.
+         */
+        left = node->left;
+        node->left = node->right;
+        node->right = left;
+
+        /* Fall through. */
+
     case NJS_TOKEN_LOGICAL_OR:
     case NJS_TOKEN_LOGICAL_AND:
     case NJS_TOKEN_BITWISE_OR:
@@ -157,7 +171,6 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_NOT_EQUAL:
     case NJS_TOKEN_STRICT_EQUAL:
     case NJS_TOKEN_STRICT_NOT_EQUAL:
-    case NJS_TOKEN_IN:
     case NJS_TOKEN_INSTANCEOF:
     case NJS_TOKEN_LESS:
     case NJS_TOKEN_LESS_OR_EQUAL:
@@ -2019,83 +2032,3 @@ njs_generator_index_release(njs_vm_t *vm, njs_parser_t *parser,
 
     return NXT_ERROR;
 }
-
-
-u_char *
-njs_number_trap_create(njs_vm_t *vm)
-{
-    u_char                  *p, *code;
-    size_t                  size;
-    njs_vmcode_restart_t    *restart;
-    njs_vmcode_to_number_t  *to_number;
-
-    size = 2 * sizeof(njs_vmcode_to_number_t) + sizeof(njs_vmcode_restart_t);
-
-    code = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
-
-    if (nxt_fast_path(code != NULL)) {
-        p = code;
-
-        to_number = (njs_vmcode_to_number_t *) p;
-        p += sizeof(njs_vmcode_to_number_t);
-        to_number->code.operation = njs_vmcode_to_number;
-        to_number->code.operands = NJS_VMCODE_1OPERAND;
-        to_number->code.retval = NJS_VMCODE_NO_RETVAL;
-        to_number->narg = 1;
-
-        to_number = (njs_vmcode_to_number_t *) p;
-        p += sizeof(njs_vmcode_to_number_t);
-        to_number->code.operation = njs_vmcode_to_number;
-        to_number->code.operands = NJS_VMCODE_1OPERAND;
-        to_number->code.retval = NJS_VMCODE_NO_RETVAL;
-        to_number->narg = 0;
-
-        restart = (njs_vmcode_restart_t *) p;
-        p += sizeof(njs_vmcode_restart_t);
-        restart->code.operation = njs_vmcode_restart;
-        restart->code.operands = NJS_VMCODE_NO_OPERAND;
-        restart->code.retval = NJS_VMCODE_NO_RETVAL;
-    }
-
-    return code;
-}
-
-
-u_char *
-njs_string_trap_create(njs_vm_t *vm)
-{
-    u_char                  *p, *code;
-    size_t                  size;
-    njs_vmcode_restart_t    *restart;
-    njs_vmcode_to_string_t  *to_string;
-
-    size = 2 * sizeof(njs_vmcode_to_string_t) + sizeof(njs_vmcode_restart_t);
-
-    code = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
-
-    if (nxt_fast_path(code != NULL)) {
-        p = code;
-
-        to_string = (njs_vmcode_to_string_t *) p;
-        p += sizeof(njs_vmcode_to_string_t);
-        to_string->code.operation = njs_vmcode_to_string;
-        to_string->code.operands = NJS_VMCODE_1OPERAND;
-        to_string->code.retval = NJS_VMCODE_NO_RETVAL;
-        to_string->narg = 0;
-
-        to_string = (njs_vmcode_to_string_t *) p;
-        p += sizeof(njs_vmcode_to_string_t);
-        to_string->code.operation = njs_vmcode_to_string;
-        to_string->code.operands = NJS_VMCODE_1OPERAND;
-        to_string->code.retval = NJS_VMCODE_NO_RETVAL;
-        to_string->narg = 1;
-
-        restart = (njs_vmcode_restart_t *) p;
-        p += sizeof(njs_vmcode_restart_t);
-        restart->code.operation = njs_vmcode_restart;
-        restart->code.operands = NJS_VMCODE_NO_OPERAND;
-        restart->code.retval = NJS_VMCODE_NO_RETVAL;
-    }
-
-    return code;
-}
index 74628a9c5951fbbe9f4b490920190359045ea242..f1b23ae2ed60d5abcae9a68e5ee74ebb080a3d47 100644 (file)
 #include <string.h>
 
 
-/* The values must not coincide with NXT_OK, NXT_ERROR, and NXT_DECLINED. */
+/* The values must be greater than NXT_OK. */
 #define NJS_PRIMITIVE_VALUE        1
-#define NJS_ARRAY_VALUE            2
-#define NJS_EXTERNAL_VALUE         3
+#define NJS_STRING_VALUE           2
+#define NJS_ARRAY_VALUE            3
+#define NJS_EXTERNAL_VALUE         4
 
+
+/*
+ * NJS_PROPERTY_QUERY_GET must be less or equal to NJS_PROPERTY_QUERY_IN,
+ * NJS_PROPERTY_QUERY_SET and NJS_PROPERTY_QUERY_DELETE must be greater
+ * than NJS_PROPERTY_QUERY_IN.
+ */
 #define NJS_PROPERTY_QUERY_GET     0
-#define NJS_PROPERTY_QUERY_IN      0
-#define NJS_PROPERTY_QUERY_DELETE  1
+#define NJS_PROPERTY_QUERY_IN      1
 #define NJS_PROPERTY_QUERY_SET     2
+#define NJS_PROPERTY_QUERY_DELETE  3
 
 
 typedef struct {
@@ -74,6 +81,8 @@ static nxt_noinline njs_ret_t njs_values_compare(njs_value_t *val1,
     njs_value_t *val2);
 static nxt_noinline nxt_bool_t njs_values_strict_equal(njs_value_t *val1,
     njs_value_t *val2);
+static njs_ret_t njs_primitive_value(njs_vm_t *vm, njs_value_t *value,
+    nxt_uint_t hint);
 void njs_debug(njs_index_t index, njs_value_t *value);
 
 
@@ -204,31 +213,16 @@ njs_vmcode_interpreter(njs_vm_t *vm)
         switch (ret) {
 
         case NJS_TRAP_NUMBER:
-            ret = njs_vmcode_trap(vm,
-                             vm->number_trap + sizeof(njs_vmcode_to_number_t),
-                             value1, value1, 0);
-
-            if (nxt_fast_path(ret == NXT_OK)) {
-                goto again;
-            }
-
-            ret = NXT_ERROR;
-            break;
-
-        case NJS_TRAP_LVALUE_NUMBER:
-            ret = njs_vmcode_trap(vm,
-                             vm->number_trap + sizeof(njs_vmcode_to_number_t),
-                             value1, value2, 1);
+            value2 = value1;
 
-            if (nxt_fast_path(ret == NXT_OK)) {
-                goto again;
-            }
-
-            ret = NXT_ERROR;
-            break;
+            /* Fall through. */
 
         case NJS_TRAP_NUMBERS:
-            ret = njs_vmcode_trap(vm, vm->number_trap, value1, value2, 0);
+        case NJS_TRAP_STRINGS:
+        case NJS_TRAP_INCDEC:
+        case NJS_TRAP_PROPERTY:
+
+            ret = njs_vmcode_trap(vm, ret - NJS_TRAP_LAST, value1, value2);
 
             if (nxt_fast_path(ret == NXT_OK)) {
                 goto again;
@@ -237,14 +231,7 @@ njs_vmcode_interpreter(njs_vm_t *vm)
             ret = NXT_ERROR;
             break;
 
-        case NJS_TRAP_STRINGS:
-            ret = njs_vmcode_trap(vm, vm->string_trap, value1, value2, 0);
-
-            if (nxt_fast_path(ret == NXT_OK)) {
-                goto again;
-            }
-
-            ret = NXT_ERROR;
+        default:
             break;
         }
 
@@ -463,7 +450,9 @@ njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
 
     retval = &njs_value_void;
 
-    if (nxt_fast_path(ret == NXT_OK)) {
+    switch (ret) {
+
+    case NXT_OK:
         prop = pq.lhq.value;
 
         switch (prop->type) {
@@ -498,16 +487,47 @@ njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
             return NXT_ERROR;
         }
 
-    } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+        break;
+
+    case NXT_DECLINED:
+    case NJS_PRIMITIVE_VALUE:
+        break;
 
+    case NJS_STRING_VALUE:
+
+        /* string[n]. */
+
+        num = njs_value_to_number(property);
+        index = (int32_t) num;
+
+        if (index >= 0 && index == num) {
+            slice.start = index;
+            slice.length = 1;
+            slice.string_length = njs_string_prop(&string, object);
+            /*
+             * A single codepoint string fits in vm->retval
+             * so the function cannot fail.
+             */
+            (void) njs_string_slice(vm, &vm->retval, &string, &slice);
+
+            if (nxt_fast_path(vm->retval.data.truth != 0)) {
+                /* Non-empty string. */
+                return sizeof(njs_vmcode_prop_get_t);
+            }
+        }
+
+        break;
+
+    case NJS_ARRAY_VALUE:
         val = pq.lhq.value;
 
         if (njs_is_valid(val)) {
             retval = val;
         }
 
-    } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+        break;
 
+    case NJS_EXTERNAL_VALUE:
         ext = object->data.u.external;
 
         ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
@@ -517,7 +537,7 @@ njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
 
             if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
                 retval = &ext->value;
-                goto done;
+                break;
             }
 
             data = ext->data;
@@ -537,36 +557,13 @@ njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
 
         return sizeof(njs_vmcode_prop_get_t);
 
-    } else if (nxt_fast_path(ret == NJS_PRIMITIVE_VALUE)) {
-
-        if (njs_is_string(object)) {
-
-            /* string[n]. */
-
-            num = njs_value_to_number(property);
-            index = (int32_t) num;
-
-            if (index >= 0 && index == num) {
-                slice.start = index;
-                slice.length = 1;
-                slice.string_length = njs_string_prop(&string, object);
-
-                /* A single codepoint string fits in vm->retval cannot fail. */
-                (void) njs_string_slice(vm, &vm->retval, &string, &slice);
-
-                if (nxt_fast_path(vm->retval.data.truth != 0)) {
-                    /* Non-empty string. */
-                    return sizeof(njs_vmcode_prop_get_t);
-                }
-            }
-        }
+    default:
+        /* NJS_TRAP_PROPERTY */
+        /* NXT_ERROR */
 
-    } else if (ret == NXT_ERROR) {
         return ret;
     }
 
-done:
-
     vm->retval = *retval;
 
     /* GC: njs_retain(retval) */
@@ -595,19 +592,13 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
 
     ret = njs_property_query(vm, &pq, object, property);
 
-    if (nxt_fast_path(ret == NXT_OK)) {
+    switch (ret) {
 
+    case NXT_OK:
         prop = pq.lhq.value;
+        break;
 
-    } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
-
-        p = pq.lhq.value;
-        *p = *value;
-
-        return sizeof(njs_vmcode_prop_set_t);
-
-    } else if (nxt_fast_path(ret == NXT_DECLINED)) {
-
+    case NXT_DECLINED:
         prop = njs_object_prop_alloc(vm, &pq.value);
         if (nxt_slow_path(prop == NULL)) {
             return NXT_ERROR;
@@ -623,8 +614,19 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
             return ret;
         }
 
-    } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+        break;
+
+    case NJS_PRIMITIVE_VALUE:
+    case NJS_STRING_VALUE:
+        return sizeof(njs_vmcode_prop_set_t);
+
+    case NJS_ARRAY_VALUE:
+        p = pq.lhq.value;
+        *p = *value;
 
+        return sizeof(njs_vmcode_prop_set_t);
+
+    case NJS_EXTERNAL_VALUE:
         ext = object->data.u.external;
 
         ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
@@ -651,10 +653,10 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
 
         return sizeof(njs_vmcode_prop_set_t);
 
-    } else if (ret == NJS_PRIMITIVE_VALUE) {
-        return sizeof(njs_vmcode_prop_set_t);
+    default:
+        /* NJS_TRAP_PROPERTY */
+        /* NXT_ERROR */
 
-    } else {
         return ret;
     }
 
@@ -665,7 +667,7 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
 
 
 njs_ret_t
-njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property, njs_value_t *object)
+njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property)
 {
     uintptr_t             data;
     njs_ret_t             ret;
@@ -680,17 +682,31 @@ njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property, njs_value_t *object)
 
     ret = njs_property_query(vm, &pq, object, property);
 
-    if (nxt_fast_path(ret == NXT_OK)) {
+    switch (ret) {
+
+    case NXT_OK:
         retval = &njs_value_true;
-        goto done;
+        break;
 
-    } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+    case NXT_DECLINED:
+        break;
+
+    case NJS_PRIMITIVE_VALUE:
+    case NJS_STRING_VALUE:
+        vm->exception = &njs_exception_type_error;
+
+        return NXT_ERROR;
+
+    case NJS_ARRAY_VALUE:
         value = pq.lhq.value;
-        retval = njs_is_valid(value) ? &njs_value_true : &njs_value_false;
-        goto done;
 
-    } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+        if (njs_is_valid(value)) {
+            retval = &njs_value_true;
+        }
+
+        break;
 
+    case NJS_EXTERNAL_VALUE:
         ext = object->data.u.external;
 
         ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
@@ -700,7 +716,7 @@ njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property, njs_value_t *object)
 
             if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
                 retval = &njs_value_true;
-                goto done;
+                break;
             }
 
             data = ext->data;
@@ -719,20 +735,15 @@ njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property, njs_value_t *object)
             retval = &njs_value_true;
         }
 
-        goto done;
+        break;
 
-    } else if (nxt_fast_path(ret == NXT_DECLINED)) {
-        retval = &njs_value_false;
-        goto done;
+    default:
+        /* NJS_TRAP_PROPERTY */
+        /* NXT_ERROR */
 
-    } else if (nxt_fast_path(ret != NXT_ERROR)) {
-        vm->exception = &njs_exception_type_error;
+        return ret;
     }
 
-    return NXT_ERROR;
-
-done:
-
     vm->retval = *retval;
 
     return sizeof(njs_vmcode_3addr_t);
@@ -757,7 +768,9 @@ njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
 
     ret = njs_property_query(vm, &pq, object, property);
 
-    if (nxt_fast_path(ret == NXT_OK)) {
+    switch (ret) {
+
+    case NXT_OK:
         prop = pq.lhq.value;
 
         if (prop->configurable) {
@@ -770,13 +783,20 @@ njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
             retval = &njs_value_true;
         }
 
-    } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+        break;
+
+    case NXT_DECLINED:
+    case NJS_PRIMITIVE_VALUE:
+    case NJS_STRING_VALUE:
+        break;
+
+    case NJS_ARRAY_VALUE:
         value = pq.lhq.value;
         njs_set_invalid(value);
-
         retval = &njs_value_true;
+        break;
 
-    } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+    case NJS_EXTERNAL_VALUE:
 
         ext = object->data.u.external;
 
@@ -786,7 +806,7 @@ njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
             ext = pq.lhq.value;
 
             if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
-                goto done;
+                break;
             }
 
             data = ext->data;
@@ -805,18 +825,34 @@ njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
             retval = &njs_value_true;
         }
 
-    } else if (nxt_slow_path(ret == NXT_ERROR)) {
+        break;
+
+    default:
+        /* NJS_TRAP_PROPERTY */
+        /* NXT_ERROR */
+
         return ret;
     }
 
-done:
-
     vm->retval = *retval;
 
     return sizeof(njs_vmcode_3addr_t);
 }
 
 
+/*
+ * The njs_property_query() returns values
+ *   NXT_OK               property has been found in object,
+ *   NXT_DECLINED         property was not found in object,
+ *   NJS_PRIMITIVE_VALUE  property operation was applied to a numeric
+ *                        or undefined value,
+ *   NJS_STRING_VALUE     property operation was applied to a string,
+ *   NJS_ARRAY_VALUE      object is array,
+ *   NJS_EXTERNAL_VALUE   object is external entity,
+ *   NJS_TRAP_PROPERTY    the property trap must be called,
+ *   NXT_ERROR            exception has been thrown.
+ */
+
 static nxt_noinline njs_ret_t
 njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
     njs_value_t *property)
@@ -848,11 +884,11 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
     case NJS_ARRAY:
         if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
 
-            if (njs_is_number(property)) {
-                num = property->data.u.number;
+            if (nxt_fast_path(njs_is_primitive(property))) {
+                num = njs_value_to_number(property);
 
             } else {
-                num = njs_value_to_number(property);
+                return NJS_TRAP_PROPERTY;
             }
 
             index = (int) num;
@@ -888,32 +924,37 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
         return NJS_PRIMITIVE_VALUE;
     }
 
-    ret = njs_value_to_string(vm, &pq->value, property);
+    if (nxt_fast_path(njs_is_primitive(property))) {
 
-    if (nxt_fast_path(ret == NXT_OK)) {
+        ret = njs_value_to_string(vm, &pq->value, property);
 
-        pq->lhq.key.len = pq->value.short_string.size;
+        if (nxt_fast_path(ret == NXT_OK)) {
 
-        if (pq->lhq.key.len != NJS_STRING_LONG) {
-            pq->lhq.key.data = pq->value.short_string.start;
+            pq->lhq.key.len = pq->value.short_string.size;
 
-        } else {
-            pq->lhq.key.len = pq->value.data.string_size;
-            pq->lhq.key.data = pq->value.data.u.string->start;
-        }
+            if (pq->lhq.key.len != NJS_STRING_LONG) {
+                pq->lhq.key.data = pq->value.short_string.start;
 
-        pq->lhq.key_hash = hash(pq->lhq.key.data, pq->lhq.key.len);
+            } else {
+                pq->lhq.key.len = pq->value.data.string_size;
+                pq->lhq.key.data = pq->value.data.u.string->start;
+            }
+
+            pq->lhq.key_hash = hash(pq->lhq.key.data, pq->lhq.key.len);
 
-        if (obj == NULL) {
-            pq->lhq.proto = &njs_extern_hash_proto;
+            if (obj == NULL) {
+                pq->lhq.proto = &njs_extern_hash_proto;
+
+                return NJS_EXTERNAL_VALUE;
+            }
 
-            return NJS_EXTERNAL_VALUE;
+            return njs_object_property_query(vm, pq, object, obj);
         }
 
-        return njs_object_property_query(vm, pq, object, obj);
+        return ret;
     }
 
-    return ret;
+    return NJS_TRAP_PROPERTY;
 }
 
 
@@ -1005,10 +1046,12 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
 
     } while (object != NULL);
 
-    if (nxt_fast_path(njs_is_primitive(value))) {
-        return NJS_PRIMITIVE_VALUE;
+    if (njs_is_string(value)) {
+        return NJS_STRING_VALUE;
     }
 
+    /* NXT_DECLINED */
+
     return ret;
 }
 
@@ -1211,83 +1254,92 @@ 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 *value, njs_value_t *lvalue)
+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 = value->data.u.number + 1.0;
 
-        njs_release(vm, lvalue);
+        njs_release(vm, reference);
 
-        njs_number_set(lvalue, num);
-        vm->retval = *lvalue;
+        njs_number_set(reference, num);
+        vm->retval = *reference;
 
         return sizeof(njs_vmcode_3addr_t);
     }
 
-    return NJS_TRAP_LVALUE_NUMBER;
+    return NJS_TRAP_INCDEC;
 }
 
 
 njs_ret_t
-njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+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 = value->data.u.number - 1.0;
 
-        njs_release(vm, lvalue);
+        njs_release(vm, reference);
 
-        njs_number_set(lvalue, num);
-        vm->retval = *lvalue;
+        njs_number_set(reference, num);
+        vm->retval = *reference;
 
         return sizeof(njs_vmcode_3addr_t);
     }
 
-    return NJS_TRAP_LVALUE_NUMBER;
+    return NJS_TRAP_INCDEC;
 }
 
 
 njs_ret_t
-njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *reference,
+    njs_value_t *value)
 {
     double  num;
 
     if (nxt_fast_path(njs_is_numeric(value))) {
         num = value->data.u.number;
 
-        njs_release(vm, lvalue);
+        njs_release(vm, reference);
 
-        njs_number_set(lvalue, num + 1.0);
+        njs_number_set(reference, num + 1.0);
         njs_number_set(&vm->retval, num);
 
         return sizeof(njs_vmcode_3addr_t);
     }
 
-    return NJS_TRAP_LVALUE_NUMBER;
+    return NJS_TRAP_INCDEC;
 }
 
 
 njs_ret_t
-njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+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 = value->data.u.number;
 
-        njs_release(vm, lvalue);
+        njs_release(vm, reference);
 
-        njs_number_set(lvalue, num - 1.0);
+        njs_number_set(reference, num - 1.0);
         njs_number_set(&vm->retval, num);
 
         return sizeof(njs_vmcode_3addr_t);
     }
 
-    return NJS_TRAP_LVALUE_NUMBER;
+    return NJS_TRAP_INCDEC;
 }
 
 
@@ -2393,7 +2445,7 @@ njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 
 
 njs_ret_t
-njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
+njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 {
     double       num;
     njs_ret_t    ret;
@@ -2402,7 +2454,7 @@ njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
     value = njs_native_data(vm->frame);
     value = &value[(uintptr_t) narg + 1];
 
-    ret = njs_value_to_primitive(vm, value, 0);
+    ret = njs_primitive_value(vm, value, 0);
 
     if (nxt_fast_path(ret > 0)) {
 
@@ -2416,7 +2468,7 @@ njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
             njs_number_set(value, num);
         }
 
-        ret = sizeof(njs_vmcode_to_number_t);
+        ret = sizeof(njs_vmcode_1addr_t);
     }
 
     return ret;
@@ -2424,7 +2476,7 @@ njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 
 
 njs_ret_t
-njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
+njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 {
     njs_ret_t          ret;
     njs_value_t        *value;
@@ -2433,7 +2485,7 @@ njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
     value = njs_native_data(vm->frame);
     value = &value[(uintptr_t) narg + 1];
 
-    ret = njs_value_to_primitive(vm, value, 1);
+    ret = njs_primitive_value(vm, value, 1);
 
     if (nxt_fast_path(ret > 0)) {
 
@@ -2469,7 +2521,7 @@ njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
 
     done:
 
-        ret = sizeof(njs_vmcode_to_string_t);
+        ret = sizeof(njs_vmcode_1addr_t);
     }
 
     return ret;
@@ -2482,8 +2534,8 @@ njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
  * for numbers and "toString", "valueOf" for strings.
  */
 
-nxt_noinline njs_ret_t
-njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
+static nxt_noinline njs_ret_t
+njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
 {
     njs_ret_t           ret;
     njs_param_t         param;
@@ -2566,7 +2618,7 @@ 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, *value2;
+    njs_value_t           *retval, *values, *value1;
     njs_native_frame_t    *frame;
     njs_vmcode_generic_t  *vmcode;
 
@@ -2578,17 +2630,14 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
     vm->current = restart;
     vmcode = (njs_vmcode_generic_t *) restart;
 
-    value1 = njs_native_data(frame);
-    value1 = &value1[1];
+    values = njs_native_data(frame);
+    value1 = &values[1];
 
-    if (frame->lvalue) {
-        value2 = value1[1].data.u.value;
-
-    } else {
-        value2 = &value1[1];
+    if (frame->trap_reference) {
+        value1 = value1->data.u.value;
     }
 
-    ret = vmcode->code.operation(vm, value1, value2);
+    ret = vmcode->code.operation(vm, value1, &values[2]);
 
     retval = njs_vmcode_operand(vm, vmcode->operand1);
 
index 28d3aed9b60a69f55a623bac4789bc690721b5d3..1a5c8e38bf6a181c87dd54c4a634ed3eb26fd0b0 100644 (file)
@@ -8,12 +8,14 @@
 #define _NJS_VM_H_INCLUDED_
 
 
-#define NJS_TRAP_STRINGS        -10
-#define NJS_TRAP_NUMBERS        -11
-#define NJS_TRAP_LVALUE_NUMBER  -12
-#define NJS_TRAP_NUMBER         -13
+#define NJS_TRAP_NUMBER    -10
+#define NJS_TRAP_NUMBERS   -11
+#define NJS_TRAP_INCDEC    -12
+#define NJS_TRAP_STRINGS   -13
+#define NJS_TRAP_PROPERTY  -14
+#define NJS_TRAP_LAST      NJS_TRAP_PROPERTY
 
-#define NJS_PASS                -20
+#define NJS_PASS           -20
 
 
 /* The order of the enum is used in njs_vmcode_typeof() */
@@ -556,24 +558,6 @@ typedef struct {
 } njs_vmcode_finally_t;
 
 
-typedef struct {
-    njs_vmcode_t               code;
-    uintptr_t                  narg;
-} njs_vmcode_to_string_t;
-
-
-typedef struct {
-    njs_vmcode_t               code;
-    uintptr_t                  narg;
-} njs_vmcode_to_number_t;
-
-
-typedef struct {
-    njs_vmcode_t               code;
-    uintptr_t                  unused;
-} njs_vmcode_restart_t;
-
-
 typedef enum {
     NJS_SCOPE_ABSOLUTE = 0,
     NJS_SCOPE_LOCAL,
@@ -663,6 +647,12 @@ njs_index_size(index)                                                         \
     njs_offset(index)
 
 
+typedef struct {
+    const njs_vmcode_1addr_t  *code;
+    nxt_bool_t                reference_value;
+} njs_vm_trap_t;
+
+
 struct njs_vm_s {
     /* njs_vm_t must be aligned to njs_value_t due to scratch value. */
     njs_value_t              retval;
@@ -691,9 +681,6 @@ struct njs_vm_s {
     njs_object_t             *prototypes;
     njs_function_t           *functions;
 
-    u_char                   *number_trap;
-    u_char                   *string_trap;
-
     nxt_mem_cache_pool_t     *mem_cache_pool;
 
     njs_value_t              *global_scope;
@@ -710,9 +697,6 @@ struct njs_vm_shared_s {
 
     njs_object_t             *prototypes;
     njs_function_t           *functions;
-
-    u_char                   *number_trap;
-    u_char                   *string_trap;
 };
 
 
@@ -747,14 +731,14 @@ njs_ret_t njs_vmcode_property_each(njs_vm_t *vm, njs_value_t *object,
 njs_ret_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *constructor);
 
-njs_ret_t njs_vmcode_increment(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *lvalue);
-njs_ret_t njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *lvalue);
-njs_ret_t njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *lvalue);
-njs_ret_t njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *lvalue);
+njs_ret_t njs_vmcode_increment(njs_vm_t *vm, njs_value_t *reference,
+    njs_value_t *value);
+njs_ret_t njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *reference,
+    njs_value_t *value);
+njs_ret_t njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *reference,
+    njs_value_t *value);
+njs_ret_t njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *reference,
+    njs_value_t *value);
 njs_ret_t njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *invld);
 njs_ret_t njs_vmcode_void(njs_vm_t *vm, njs_value_t *invld1,
@@ -842,12 +826,10 @@ njs_ret_t njs_vmcode_catch(njs_vm_t *vm, njs_value_t *invld,
 njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
     njs_value_t *retval);
 
-njs_ret_t njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld,
+njs_ret_t njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld,
     njs_value_t *narg);
-njs_ret_t njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld,
+njs_ret_t njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld,
     njs_value_t *narg);
-njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value,
-    nxt_uint_t hint);
 njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *invld2);
 
@@ -857,11 +839,6 @@ nxt_int_t njs_shared_objects_create(njs_vm_t *vm);
 nxt_int_t njs_shared_objects_clone(njs_vm_t *vm);
 
 
-/* STUB */
-u_char *njs_number_trap_create(njs_vm_t *vm);
-u_char *njs_string_trap_create(njs_vm_t *vm);
-
-
 void *njs_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc);
 void njs_lvlhsh_free(void *data, void *p, size_t size);
 
index efaa5cb3fc92e1758b6462b2bafcfc02a32198f1..0f3c62365f890be44b113865df5c37650c011ef0 100644 (file)
@@ -173,9 +173,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
         keywords_hash = vm->shared->keywords_hash;
         parser->values_hash = vm->shared->values_hash;
 
-        vm->number_trap = vm->shared->number_trap;
-        vm->string_trap = vm->shared->string_trap;
-
         /* STUB */
         if (vm->shared->prototypes == NULL) {
             ret = njs_shared_objects_create(vm);
@@ -200,24 +197,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
         }
     }
 
-    if (vm->number_trap == NULL) {
-
-        vm->number_trap = njs_number_trap_create(vm);
-        if (nxt_slow_path(vm->number_trap == NULL)) {
-            return NJS_ERROR;
-        }
-
-        vm->string_trap = njs_string_trap_create(vm);
-        if (nxt_slow_path(vm->string_trap == NULL)) {
-            return NJS_ERROR;
-        }
-
-        if (vm->shared != NULL) {
-            vm->shared->number_trap = vm->number_trap;
-            vm->shared->string_trap = vm->string_trap;
-        }
-    }
-
     parser->lexer->keywords_hash = keywords_hash;
 
     parser->lexer->start = *start;
@@ -305,9 +284,6 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external)
         nvm->variables_hash = vm->variables_hash;
         nvm->values_hash = vm->values_hash;
 
-        nvm->number_trap = vm->number_trap;
-        nvm->string_trap = vm->string_trap;
-
         nvm->retval = njs_value_void;
         nvm->current = vm->current;
         nvm->external = external;
index 43723f7984f31a65b99e571696f6889d041af004..9a3fb2e6fc9b825c136590ea402e9abd44e6f127 100644 (file)
@@ -250,6 +250,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("!2"),
       nxt_string("false") },
 
+    /**/
+
+    { nxt_string("var a = { valueOf: function() { return 1 } };   ~a"),
+      nxt_string("-2") },
+
+    { nxt_string("var a = { valueOf: function() { return '1' } }; ~a"),
+      nxt_string("-2") },
+
+    /**/
+
     { nxt_string("1 || 2"),
       nxt_string("1") },
 
@@ -815,6 +825,20 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("a = 1 ? b = 2 + 4 : b = 3"),
       nxt_string("6") },
 
+    /**/
+
+    { nxt_string("var a = { valueOf: function() { return 1 } };   +a"),
+      nxt_string("1") },
+
+    { nxt_string("var a = { valueOf: function() { return '1' } }; +a"),
+      nxt_string("1") },
+
+    { nxt_string("var a = { valueOf: function() { return 1 } };   -a"),
+      nxt_string("-1") },
+
+    { nxt_string("var a = { valueOf: function() { return '1' } }; -a"),
+      nxt_string("-1") },
+
     /* Increment. */
 
     { nxt_string("var a = 1;   ++a"),
@@ -829,17 +853,21 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = {};  ++a"),
       nxt_string("NaN") },
 
-    { nxt_string("var a = { valueOf: function() { return 1 } };   ++a"),
-      nxt_string("2") },
+    { nxt_string("var a = { valueOf: function() { return 1 } };"
+                 "++a +' '+ a +' '+ typeof a"),
+      nxt_string("2 2 number") },
 
-    { nxt_string("var a = { valueOf: function() { return '1' } }; ++a"),
-      nxt_string("2") },
+    { nxt_string("var a = { valueOf: function() { return '1' } };"
+                 "++a +' '+ a +' '+ typeof a"),
+      nxt_string("2 2 number") },
 
-    { nxt_string("var a = { valueOf: function() { return [1] } }; ++a"),
-      nxt_string("NaN") },
+    { nxt_string("var a = { valueOf: function() { return [1] } };"
+                 "++a +' '+ a +' '+ typeof a"),
+      nxt_string("NaN NaN number") },
 
-    { nxt_string("var a = { valueOf: function() { return {} } };  ++a"),
-      nxt_string("NaN") },
+    { nxt_string("var a = { valueOf: function() { return {} } };"
+                 "++a +' '+ a +' '+ typeof a"),
+      nxt_string("NaN NaN number") },
 
     /**/
 
@@ -1506,6 +1534,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("a = 1; 1 in a"),
       nxt_string("TypeError") },
 
+    { nxt_string("var n = { toString: function() { return 'a' } };"
+                 "var o = { a: 5 }; o[n]"),
+      nxt_string("5") },
+
+    { nxt_string("var n = { valueOf: function() { return 'a' } };"
+                 "var o = { a: 5, '[object Object]': 7 }; o[n]"),
+      nxt_string("7") },
+
+    /* Arrays */
+
     { nxt_string("a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"),
       nxt_string("6") },
 
@@ -1604,6 +1642,44 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("a = []; a.concat([]) +''"),
       nxt_string("") },
 
+    /**/
+
+    { nxt_string("var n = { toString: function() { return 1 } };   [1,2][n]"),
+      nxt_string("2") },
+
+    { nxt_string("var n = { toString: function() { return '1' } }; [1,2][n]"),
+      nxt_string("2") },
+
+    { nxt_string("var n = { toString: function() { return 1 },"
+                          " valueOf:  function() { return 0 } };   [1,2][n]"),
+      nxt_string("2") },
+
+    { nxt_string("var n = { toString: function() { return 1.5 } };"
+                 "var a = [1,2]; a[1.5] = 5; a[n]"),
+      nxt_string("5") },
+
+    { nxt_string("var n = { toString: function() { return 1.5 } };"
+                 "var a = [1,2]; a[n] = 5; a[1.5]"),
+      nxt_string("5") },
+
+    { nxt_string("var n = { toString: function() { return '1.5' } };"
+                 "var a = [1,2]; a[1.5] = 5; a[n]"),
+      nxt_string("5") },
+
+    { nxt_string("var n = { toString: function() { return '1.5' } };"
+                 "var a = [1,2]; a[n] = 5; a[1.5]"),
+      nxt_string("5") },
+
+    { nxt_string("var n = { toString: function() { return 1.5 } };"
+                 "var a = [1,2]; a[1.5] = 5; n in a"),
+      nxt_string("true") },
+
+    { nxt_string("var n = { toString: function() { return '1.5' } };"
+                 "var a = [1,2]; a[1.5] = 5; '' + (n in a) + (delete a[n])"),
+      nxt_string("truetrue") },
+
+    /**/
+
     { nxt_string("a = [1,2,3]; a.concat(4, [5, 6, 7], 8) +''"),
       nxt_string("1,2,3,4,5,6,7,8") },
 
@@ -1689,6 +1765,8 @@ static njs_unit_test_t  njs_test[] =
                  "a.every(function(v, i, a) { return v > 0 })"),
       nxt_string("true") },
 
+    /* Strings. */
+
     { nxt_string("var a = '0123456789' + '012345'"
                  "var b = 'abcdefghij' + 'klmnop'"
                  "    a = b"),
@@ -1960,6 +2038,20 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("a = 'abcdef'; b = 1 + 2; a[b]"),
       nxt_string("d") },
 
+    /**/
+
+    { nxt_string("var n = { toString: function() { return 1 } };   '12'[n]"),
+      nxt_string("2") },
+
+    { nxt_string("var n = { toString: function() { return '1' } }; '12'[n]"),
+      nxt_string("2") },
+
+    { nxt_string("var n = { toString: function() { return 1 },"
+                          " valueOf:  function() { return 0 } };   '12'[n]"),
+      nxt_string("2") },
+
+    /**/
+
     { nxt_string("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"),
       nxt_string("1077") },