]> git.kaiwu.me - njs.git/commitdiff
Traps for native method arguments.
authorIgor Sysoev <igor@sysoev.ru>
Wed, 3 Feb 2016 14:47:41 +0000 (17:47 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Wed, 3 Feb 2016 14:47:41 +0000 (17:47 +0300)
12 files changed:
njs/njs_array.c
njs/njs_boolean.c
njs/njs_builtin.c
njs/njs_function.c
njs/njs_function.h
njs/njs_number.c
njs/njs_object.c
njs/njs_regexp.c
njs/njs_string.c
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c

index a13cd9c0f58738ce7a4401c4ba7d9bdab09da29d..969b3776b184c010fa4c7aac8f8e7467fcb96a1c 100644 (file)
@@ -260,6 +260,11 @@ njs_array_prototype_length(njs_vm_t *vm, njs_value_t *array)
 }
 
 
+/*
+ * Array.slice(start[, end]).
+ * JavaScript 1.2, ECMAScript 3.
+ */
+
 static njs_ret_t
 njs_array_prototype_slice(njs_vm_t *vm, njs_param_t *param)
 {
@@ -267,7 +272,7 @@ njs_array_prototype_slice(njs_vm_t *vm, njs_param_t *param)
     uint32_t     n;
     uintptr_t    nargs;
     njs_array_t  *array;
-    njs_value_t  *this, *args, *value;
+    njs_value_t  *this, *value;
 
     start = 0;
     length = 0;
@@ -278,8 +283,7 @@ njs_array_prototype_slice(njs_vm_t *vm, njs_param_t *param)
         nargs = param->nargs;
 
         if (nargs != 0) {
-            args = param->args;
-            start = njs_value_to_number(&args[0]);
+            start = param->args[0].data.u.number;
 
             if (start < 0) {
                 start += length;
@@ -292,7 +296,7 @@ njs_array_prototype_slice(njs_vm_t *vm, njs_param_t *param)
             end = length;
 
             if (nargs > 1) {
-                end = njs_value_to_number(&args[1]);
+                end = param->args[1].data.u.number;
 
                 if (end < 0) {
                     end += length;
@@ -904,70 +908,72 @@ static const njs_object_prop_t  njs_array_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("slice"),
-        .value = njs_native_function(njs_array_prototype_slice, 0),
+        .value = njs_native_function(njs_array_prototype_slice, 0,
+                     NJS_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("push"),
-        .value = njs_native_function(njs_array_prototype_push, 0),
+        .value = njs_native_function(njs_array_prototype_push, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("pop"),
-        .value = njs_native_function(njs_array_prototype_pop, 0),
+        .value = njs_native_function(njs_array_prototype_pop, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("unshift"),
-        .value = njs_native_function(njs_array_prototype_unshift, 0),
+        .value = njs_native_function(njs_array_prototype_unshift, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("shift"),
-        .value = njs_native_function(njs_array_prototype_shift, 0),
+        .value = njs_native_function(njs_array_prototype_shift, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toString"),
-        .value = njs_native_function(njs_array_prototype_to_string, 0),
+        .value = njs_native_function(njs_array_prototype_to_string, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("join"),
-        .value = njs_native_function(njs_array_prototype_join, 0),
+        .value = njs_native_function(njs_array_prototype_join, 0,
+                     NJS_OBJECT_ARG, NJS_STRING_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("concat"),
-        .value = njs_native_function(njs_array_prototype_concat, 0),
+        .value = njs_native_function(njs_array_prototype_concat, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("forEach"),
         .value = njs_native_function(njs_array_prototype_for_each,
-                            njs_method_data_size(sizeof(njs_array_next_t))),
+                     njs_method_data_size(sizeof(njs_array_next_t)), 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("some"),
         .value = njs_native_function(njs_array_prototype_some,
-                            njs_method_data_size(sizeof(njs_array_next_t))),
+                     njs_method_data_size(sizeof(njs_array_next_t)), 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("every"),
         .value = njs_native_function(njs_array_prototype_every,
-                            njs_method_data_size(sizeof(njs_array_next_t))),
+                     njs_method_data_size(sizeof(njs_array_next_t)), 0),
     },
 };
 
index 3affde6573daf4181972fe2f8b64f7622d274c50..5e4213d1945178d7db75068ad7a8c88c0d953b4f 100644 (file)
@@ -139,13 +139,13 @@ static const njs_object_prop_t  njs_boolean_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("valueOf"),
-        .value = njs_native_function(njs_boolean_prototype_value_of, 0),
+        .value = njs_native_function(njs_boolean_prototype_value_of, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toString"),
-        .value = njs_native_function(njs_boolean_prototype_to_string, 0),
+        .value = njs_native_function(njs_boolean_prototype_to_string, 0, 0),
     },
 };
 
index b37c717023ae9175114c0534c83aee57c1937726..9ee2d281e8df616d884a1c61f55057e780aa3e3b 100644 (file)
 #include <string.h>
 
 
+typedef struct {
+    njs_native_t  native;
+    uint8_t       args_types[NJS_ARGS_TYPES_MAX];
+} njs_function_init_t;
+
+
 nxt_int_t
 njs_builtin_objects_create(njs_vm_t *vm)
 {
-    nxt_int_t                       ret;
-    nxt_uint_t                      i;
-    njs_object_t                    *prototypes;
-    njs_function_t                  *functions;
+    nxt_int_t                         ret;
+    nxt_uint_t                        i;
+    njs_object_t                      *prototypes;
+    njs_function_t                    *functions;
 
-    static const njs_object_init_t  *prototype_init[] = {
+    static const njs_object_init_t    *prototype_init[] = {
         &njs_object_prototype_init,
         &njs_array_prototype_init,
         &njs_boolean_prototype_init,
@@ -40,7 +46,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         &njs_regexp_prototype_init,
     };
 
-    static const njs_object_init_t  *function_init[] = {
+    static const njs_object_init_t    *function_init[] = {
         &njs_object_constructor_init,
         &njs_array_constructor_init,
         &njs_boolean_constructor_init,
@@ -52,16 +58,16 @@ njs_builtin_objects_create(njs_vm_t *vm)
         &njs_eval_function_init,
     };
 
-    static const njs_native_t       native_functions[] = {
-        njs_object_constructor,
-        njs_array_constructor,
-        njs_boolean_constructor,
-        njs_number_constructor,
-        njs_string_constructor,
-        njs_function_constructor,
-        njs_regexp_constructor,
-
-        njs_eval_function,
+    static const njs_function_init_t  native_functions[] = {
+        { njs_object_constructor,   {} },
+        { njs_array_constructor,    {} },
+        { njs_boolean_constructor,  {} },
+        { njs_number_constructor,   { NJS_SKIP_ARG, NJS_NUMBER_ARG } },
+        { njs_string_constructor,   { NJS_SKIP_ARG, NJS_STRING_ARG } },
+        { njs_function_constructor, {} },
+        njs_regexp_constructor,
+          { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_STRING_ARG } },
+        { njs_eval_function,        {} },
     };
 
     static const njs_object_prop_t  null_proto_property = {
@@ -94,7 +100,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
     for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) {
         functions[i].native = 1;
         functions[i].args_offset = 1;
-        functions[i].u.native = native_functions[i];
+        functions[i].u.native = native_functions[i].native;
+        functions[i].args_types[0] = native_functions[i].args_types[0];
+        functions[i].args_types[1] = native_functions[i].args_types[1];
+        functions[i].args_types[2] = native_functions[i].args_types[2];
 
         ret = njs_object_hash_create(vm, &functions[i].object.shared_hash,
                                      function_init[i]->properties,
index f91538761ed819dcddce9ec1fc3c3290f932c434..2c2af49137b2b9e392fa4a61b4b9b723014f54f1 100644 (file)
@@ -274,6 +274,11 @@ njs_function_prototype_call(njs_vm_t *vm, njs_param_t *param)
     njs_function_t              *function;
     njs_vmcode_function_call_t  *call;
 
+    if (!njs_is_function(param->this)) {
+        vm->exception = &njs_exception_type_error;
+        return NXT_ERROR;
+    }
+
     p.this = &param->args[0];
     p.args = &param->args[1];
 
@@ -283,14 +288,22 @@ njs_function_prototype_call(njs_vm_t *vm, njs_param_t *param)
     if (function->native) {
 
         if (nargs != 0) {
-            p.nargs = nargs - 1;
-            p.retval = param->retval;
+            nargs--;
 
-            return function->u.native(vm, &p);
+        } else {
+            param->args[0] = njs_value_void;
         }
 
-        vm->exception = &njs_exception_type_error;
-        return NXT_ERROR;
+        p.nargs = nargs;
+        p.retval = param->retval;
+
+        ret = njs_normalize_args(vm, &param->args[0], function->args_types,
+                                 nargs + 1);
+        if (ret != NJS_OK) {
+            return ret;
+        }
+
+        return function->u.native(vm, &p);
     }
 
     if (nargs != 0) {
@@ -329,6 +342,10 @@ njs_function_prototype_apply(njs_vm_t *vm, njs_param_t *param)
     njs_function_t              *function;
     njs_vmcode_function_call_t  *code;
 
+    if (!njs_is_function(param->this)) {
+        goto type_error;
+    }
+
     args = param->args;
     p.this = &args[0];
 
@@ -424,19 +441,19 @@ static const njs_object_prop_t  njs_function_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("call"),
-        .value = njs_native_function(njs_function_prototype_call, 0),
+        .value = njs_native_function(njs_function_prototype_call, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("apply"),
-        .value = njs_native_function(njs_function_prototype_apply, 0),
+        .value = njs_native_function(njs_function_prototype_apply, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("bind"),
-        .value = njs_native_function(njs_function_prototype_bind, 0),
+        .value = njs_native_function(njs_function_prototype_bind, 0, 0),
     },
 };
 
index 796ae79cd8605b3d12c771225129037713424ebc..74abdd028f5507239c152da1168d84022a542b8b 100644 (file)
@@ -8,6 +8,15 @@
 #define _NJS_FUNCTION_H_INCLUDED_
 
 
+#define NJS_SKIP_ARG               1
+#define NJS_NUMBER_ARG             2
+#define NJS_INTEGER_ARG            3
+#define NJS_STRING_OBJECT_ARG      4
+#define NJS_STRING_ARG             5
+#define NJS_OBJECT_ARG             6
+#define NJS_REGEXP_ARG             7
+
+
 struct njs_function_lambda_s {
     uint32_t                       nargs;
     uint32_t                       local_size;
index 2468ce5e294957a86df9fcf4665a39074faf8bd5..3f479e326c187c2570825bc5b0d07887039ee82d 100644 (file)
@@ -222,7 +222,6 @@ njs_number_constructor(njs_vm_t *vm, njs_param_t *param)
         value = &njs_value_zero;
 
     } else {
-        /* TODO: to_number. */
         value = &param->args[0];
     }
 
@@ -332,13 +331,13 @@ static const njs_object_prop_t  njs_number_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("valueOf"),
-        .value = njs_native_function(njs_number_prototype_value_of, 0),
+        .value = njs_native_function(njs_number_prototype_value_of, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toString"),
-        .value = njs_native_function(njs_number_prototype_to_string, 0),
+        .value = njs_native_function(njs_number_prototype_to_string, 0, 0),
     },
 };
 
index 289567d3e2bbff55e8be2d28973a1a06a435e644..6d4f1f67b38c54600cade76ab56adcf16d7dc4a1 100644 (file)
@@ -417,7 +417,7 @@ static const njs_object_prop_t  njs_object_constructor_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("create"),
-        .value = njs_native_function(njs_object_create, 0),
+        .value = njs_native_function(njs_object_create, 0, 0),
     },
 };
 
@@ -626,13 +626,13 @@ static const njs_object_prop_t  njs_object_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("valueOf"),
-        .value = njs_native_function(njs_object_prototype_value_of, 0),
+        .value = njs_native_function(njs_object_prototype_value_of, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toString"),
-        .value = njs_native_function(njs_object_prototype_to_string, 0),
+        .value = njs_native_function(njs_object_prototype_to_string, 0, 0),
     },
 };
 
index 7d2383434f43122d9630ed6835eff0fc0d9fc329..54edc1239eea73b8638dd946a8456bc81d4eb9c7 100644 (file)
@@ -62,7 +62,12 @@ njs_regexp_constructor(njs_vm_t *vm, njs_param_t *param)
 
     case 1:
         string.length = njs_string_prop(&string, &param->args[0]);
-        break;
+
+        if (string.length != 0) {
+            break;
+        }
+
+        /* Fall through. */
 
     case 0:
         string.start = (u_char *) "(?:)";
@@ -454,43 +459,47 @@ njs_regexp_prototype_test(njs_vm_t *vm, njs_param_t *param)
 {
     njs_ret_t             ret;
     nxt_uint_t            n;
-    njs_value_t           val;
+    njs_value_t           *value;
     const njs_value_t     *retval;
     njs_string_prop_t     string;
     njs_regexp_pattern_t  *pattern;
 
-    if (param->nargs != 0) {
-        ret = njs_value_to_string(vm, &val, &param->args[0]);
+    if (!njs_is_regexp(param->this)) {
+        vm->exception = &njs_exception_type_error;
+        return NXT_ERROR;
+    }
 
-        if (nxt_fast_path(ret == NXT_OK)) {
-            retval = &njs_value_false;
+    retval = &njs_value_false;
 
-            (void) njs_string_prop(&string, &val);
+    if (param->nargs != 0) {
+        value = &param->args[0];
 
-            n = (string.length != 0 && string.length != string.size);
-            pattern = param->this->data.u.regexp->pattern;
+    } else {
+        value = (njs_value_t *) &njs_string_void;
+    }
 
-            if (pattern->code[n] != NULL) {
-                ret = pcre_exec(pattern->code[n], pattern->extra[n],
-                                (char *) string.start, string.size,
-                                0, 0, NULL, 0);
+    (void) njs_string_prop(&string, value);
 
-                if (ret >= 0) {
-                    retval = &njs_value_true;
+    n = (string.length != 0 && string.length != string.size);
 
-                } else if (ret != PCRE_ERROR_NOMATCH) {
-                    /* TODO: exception */
-                    return NXT_ERROR;
-                }
-            }
+    pattern = param->this->data.u.regexp->pattern;
 
-            vm->retval = *retval;
+    if (pattern->code[n] != NULL) {
+        ret = pcre_exec(pattern->code[n], pattern->extra[n],
+                        (char *) string.start, string.size, 0, 0, NULL, 0);
 
-            return NXT_OK;
+        if (ret >= 0) {
+            retval = &njs_value_true;
+
+        } else if (ret != PCRE_ERROR_NOMATCH) {
+            vm->exception = &njs_exception_internal_error;
+            return NXT_ERROR;
         }
     }
 
-    return NXT_ERROR;
+    vm->retval = *retval;
+
+    return NXT_OK;
 }
 
 
@@ -500,64 +509,70 @@ njs_regexp_prototype_exec(njs_vm_t *vm, njs_param_t *param)
     int                   *captures, ncaptures;
     njs_ret_t             ret;
     nxt_uint_t            n, utf8;
+    njs_value_t           *value;
     njs_regexp_t          *regexp;
     njs_string_prop_t     string;
     njs_regexp_pattern_t  *pattern;
 
-    if (param->nargs != 0) {
-        regexp = param->this->data.u.regexp;
+    if (!njs_is_regexp(param->this)) {
+        vm->exception = &njs_exception_type_error;
+        return NXT_ERROR;
+    }
 
-        ret = njs_value_to_string(vm, &regexp->string, &param->args[0]);
+    if (param->nargs != 0) {
+        value = &param->args[0];
 
-        if (nxt_fast_path(ret == NXT_OK)) {
-            (void) njs_string_prop(&string, &regexp->string);
+    } else {
+        value = (njs_value_t *) &njs_string_void;
+    }
 
-            utf8 = 0;
-            n = 0;
+    regexp = param->this->data.u.regexp;
+    regexp->string = *value;
 
-            if (string.length != 0) {
-                utf8 = 1;
-                n = 1;
+    (void) njs_string_prop(&string, value);
 
-                if (string.length != string.size) {
-                    utf8 = 2;
-                }
-            }
+    utf8 = 0;
+    n = 0;
 
-            pattern = regexp->pattern;
+    if (string.length != 0) {
+        utf8 = 1;
+        n = 1;
 
-            if (pattern->code[n] != NULL) {
-                string.start += regexp->last_index;
-                string.size -= regexp->last_index;
+        if (string.length != string.size) {
+            utf8 = 2;
+        }
+    }
 
-                /* Each capture is stored in 3 vector elements. */
-                ncaptures = pattern->ncaptures * 3;
+    pattern = regexp->pattern;
 
-                captures = alloca(ncaptures * sizeof(int));
+    if (pattern->code[n] != NULL) {
+        string.start += regexp->last_index;
+        string.size -= regexp->last_index;
 
-                ret = pcre_exec(pattern->code[n], pattern->extra[n],
-                                (char *) string.start, string.size,
-                                0, 0, captures, ncaptures);
+        /* Each capture is stored in 3 vector elements. */
+        ncaptures = pattern->ncaptures * 3;
 
-                if (ret >= 0) {
-                    return njs_regexp_exec_result(vm, regexp, string.start,
-                                                  captures, utf8);
-                }
+        captures = alloca(ncaptures * sizeof(int));
 
-                if (nxt_slow_path(ret != PCRE_ERROR_NOMATCH)) {
-                    /* TODO: exception */
-                    return NXT_ERROR;
-                }
-            }
+        ret = pcre_exec(pattern->code[n], pattern->extra[n],
+                        (char *) string.start, string.size,
+                        0, 0, captures, ncaptures);
 
-            regexp->last_index = 0;
-            vm->retval = njs_value_null;
+        if (ret >= 0) {
+            return njs_regexp_exec_result(vm, regexp, string.start,
+                                          captures, utf8);
+        }
 
-            return NXT_OK;
+        if (nxt_slow_path(ret != PCRE_ERROR_NOMATCH)) {
+            vm->exception = &njs_exception_internal_error;
+            return NXT_ERROR;
         }
     }
 
-    return NXT_ERROR;
+    regexp->last_index = 0;
+    vm->retval = njs_value_null;
+
+    return NXT_OK;
 }
 
 
@@ -729,19 +744,21 @@ static const njs_object_prop_t  njs_regexp_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("toString"),
-        .value = njs_native_function(njs_regexp_prototype_to_string, 0),
+        .value = njs_native_function(njs_regexp_prototype_to_string, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("test"),
-        .value = njs_native_function(njs_regexp_prototype_test, 0),
+        .value = njs_native_function(njs_regexp_prototype_test, 0,
+                     NJS_OBJECT_ARG, NJS_STRING_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("exec"),
-        .value = njs_native_function(njs_regexp_prototype_exec, 0),
+        .value = njs_native_function(njs_regexp_prototype_exec, 0,
+                     NJS_OBJECT_ARG, NJS_STRING_ARG),
     },
 };
 
index 2144744267a5eece4d3e134bdbcf68433a3b7ba8..95915d97a0c7e78f93b05022d23ff0c0c5c9fa5d 100644 (file)
@@ -260,7 +260,6 @@ njs_string_constructor(njs_vm_t *vm, njs_param_t *param)
         value = &njs_string_empty;
 
     } else {
-        /* TODO: to_string. */
         value = &param->args[0];
     }
 
@@ -319,9 +318,15 @@ njs_string_prototype_length(njs_vm_t *vm, njs_value_t *value)
     size_t     size;
     uintptr_t  length;
 
+    /*
+     * This getter can be called for string primitive, String object,
+     * String.prototype.  The zero should be returned for the latter case.
+     */
     length = 0;
 
-    /* TODO: String object. */
+    if (value->type == NJS_OBJECT_STRING) {
+        value = &value->data.u.object_value->value;
+    }
 
     if (njs_is_string(value)) {
         size = value->short_string.size;
@@ -465,6 +470,10 @@ njs_string_prototype_value_of(njs_vm_t *vm, njs_param_t *param)
     return NXT_OK;
 }
 
+/*
+ * String.concat(string2[, ..., stringN]).
+ * JavaScript 1.2, ECMAScript 3.
+ */
 
 static njs_ret_t
 njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param)
@@ -472,27 +481,35 @@ njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param)
     u_char             *p, *start;
     size_t             size, length, mask;
     uintptr_t          nargs;
-    njs_ret_t          ret;
     nxt_uint_t         i;
-    njs_value_t        *this, *args, *values;
+    njs_value_t        *this, *args;
     njs_string_prop_t  string;
 
     this = param->this;
     nargs = param->nargs;
 
-    if (nargs == 0) {
-        njs_string_copy(&vm->retval, this);
-        return NXT_OK;
+    if (njs_is_null_or_void(this)) {
+        vm->exception = &njs_exception_type_error;
+        return NXT_ERROR;
     }
 
-    values = alloca((nargs + 1) * sizeof(njs_value_t));
+    args = param->args - 1;
 
-    ret = njs_value_to_string(vm, &values[0], this);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return NXT_ERROR;
+    for (i = 0; i <= nargs; i++) {
+
+        if (!njs_is_string(&args[i])) {
+            vm->frame->trap_scratch.data.u.value = &args[i];
+
+            return NJS_TRAP_STRING_ARG;
+        }
+    }
+
+    if (nargs == 0) {
+        njs_string_copy(&vm->retval, this);
+        return NXT_OK;
     }
 
-    (void) njs_string_prop(&string, &values[0]);
+    (void) njs_string_prop(&string, this);
 
     size = string.size;
     length = string.length;
@@ -502,12 +519,7 @@ njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param)
     args = param->args;
 
     for (i = 0; i < nargs; i++) {
-        ret = njs_value_to_string(vm, &values[i + 1], &args[i]);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NXT_ERROR;
-        }
-
-        (void) njs_string_prop(&string, &values[i + 1]);
+        (void) njs_string_prop(&string, &args[i]);
 
         size += string.size;
         length += string.length;
@@ -525,10 +537,13 @@ njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param)
         return NXT_ERROR;
     }
 
-    p = start;
+    (void) njs_string_prop(&string, this);
 
-    for (i = 0; i <= nargs; i++) {
-        (void) njs_string_prop(&string, &values[i]);
+    p = memcpy(start, string.start, string.size);
+    p += string.size;
+
+    for (i = 0; i < nargs; i++) {
+        (void) njs_string_prop(&string, &args[i]);
 
         p = memcpy(p, string.start, string.size);
         p += string.size;
@@ -542,6 +557,10 @@ njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param)
 }
 
 
+/*
+ * String.fromUTF8(start[, end]).
+ */
+
 static njs_ret_t
 njs_string_prototype_from_utf8(njs_vm_t *vm, njs_param_t *param)
 {
@@ -589,6 +608,10 @@ njs_string_prototype_from_utf8(njs_vm_t *vm, njs_param_t *param)
 }
 
 
+/*
+ * String.toUTF8(start[, end]).
+ */
+
 static njs_ret_t
 njs_string_prototype_to_utf8(njs_vm_t *vm, njs_param_t *param)
 {
@@ -606,6 +629,10 @@ njs_string_prototype_to_utf8(njs_vm_t *vm, njs_param_t *param)
 }
 
 
+/*
+ * String.fromBytes(start[, end]).
+ */
+
 static njs_ret_t
 njs_string_prototype_from_bytes(njs_vm_t *vm, njs_param_t *param)
 {
@@ -656,6 +683,10 @@ njs_string_prototype_from_bytes(njs_vm_t *vm, njs_param_t *param)
 }
 
 
+/*
+ * String.toBytes(start[, end]).
+ */
+
 static njs_ret_t
 njs_string_prototype_to_bytes(njs_vm_t *vm, njs_param_t *param)
 {
@@ -738,7 +769,6 @@ njs_string_prototype_substring(njs_vm_t *vm, njs_param_t *param)
 {
     ssize_t            start, end, length;
     uintptr_t          nargs;
-    njs_value_t        *args;
     njs_slice_prop_t   slice;
     njs_string_prop_t  string;
 
@@ -749,16 +779,14 @@ njs_string_prototype_substring(njs_vm_t *vm, njs_param_t *param)
     nargs = param->nargs;
 
     if (nargs != 0) {
-        args = param->args;
-
-        start = njs_value_to_number(&args[0]);
+        start = param->args[0].data.u.number;
 
         if (start < 0) {
             start = 0;
         }
 
         if (nargs > 1) {
-            end = njs_value_to_number(&args[1]);
+            end = param->args[1].data.u.number;
 
             if (end < 0) {
                 end = 0;
@@ -790,7 +818,6 @@ njs_string_prototype_substr(njs_vm_t *vm, njs_param_t *param)
 {
     ssize_t            start, length;
     uintptr_t          nargs;
-    njs_value_t        *args;
     njs_slice_prop_t   slice;
     njs_string_prop_t  string;
 
@@ -801,9 +828,7 @@ njs_string_prototype_substr(njs_vm_t *vm, njs_param_t *param)
     nargs = param->nargs;
 
     if (nargs != 0) {
-        args = param->args;
-
-        start = njs_value_to_number(&args[0]);
+        start = param->args[0].data.u.number;
 
         if (start < 0) {
 
@@ -814,7 +839,7 @@ njs_string_prototype_substr(njs_vm_t *vm, njs_param_t *param)
         }
 
         if (nargs > 1) {
-            length = njs_value_to_number(&args[1]);
+            length = param->args[1].data.u.number;
         }
     }
 
@@ -838,7 +863,7 @@ njs_string_prototype_char_at(njs_vm_t *vm, njs_param_t *param)
     length = 1;
 
     if (param->nargs != 0) {
-        start = njs_value_to_number(&param->args[0]);
+        start = param->args[0].data.u.number;
 
         if (start < 0) {
             length = 0;
@@ -865,18 +890,15 @@ njs_string_slice_prop(njs_param_t *param, njs_string_prop_t *string,
 static nxt_noinline void
 njs_string_slice_params(njs_param_t *param, njs_slice_prop_t *slice)
 {
-    ssize_t      start, end, length;
-    uintptr_t    nargs;
-    njs_value_t  *args;
+    ssize_t    start, end, length;
+    uintptr_t  nargs;
 
     length = slice->string_length;
     start = 0;
     nargs = param->nargs;
 
     if (nargs != 0) {
-        args = param->args;
-
-        start = njs_value_to_number(&args[0]);
+        start = param->args[0].data.u.number;
 
         if (start < 0) {
             start += length;
@@ -889,7 +911,7 @@ njs_string_slice_params(njs_param_t *param, njs_slice_prop_t *slice)
         end = length;
 
         if (nargs > 1) {
-            end = njs_value_to_number(&args[1]);
+            end = param->args[1].data.u.number;
 
             if (end < 0) {
                 end += length;
@@ -994,7 +1016,7 @@ njs_string_prototype_char_code_at(njs_vm_t *vm, njs_param_t *param)
     index = 0;
 
     if (param->nargs != 0) {
-        index = njs_value_to_number(&param->args[0]);
+        index = param->args[0].data.u.number;
 
         if (nxt_slow_path(index < 0 || index >= length)) {
             num = NJS_NAN;
@@ -1026,26 +1048,24 @@ done:
 static njs_ret_t
 njs_string_prototype_index_of(njs_vm_t *vm, njs_param_t *param)
 {
-    ssize_t      start, index;
-    uintptr_t    nargs;
-    njs_value_t  *args;
+    ssize_t    start, index;
+    uintptr_t  nargs;
 
     index = -1;
     nargs = param->nargs;
 
     if (nargs != 0) {
         start = 0;
-        args = param->args;
 
         if (nargs > 1) {
-            start = njs_value_to_number(&args[1]);
+            start = param->args[1].data.u.number;
 
             if (start < 0) {
                 start = 0;
             }
         }
 
-        index = njs_string_index_of(vm, param->this, &args[0], start);
+        index = njs_string_index_of(vm, param->this, &param->args[0], start);
     }
 
     njs_number_set(&vm->retval, index);
@@ -1057,19 +1077,17 @@ njs_string_prototype_index_of(njs_vm_t *vm, njs_param_t *param)
 static njs_ret_t
 njs_string_prototype_last_index_of(njs_vm_t *vm, njs_param_t *param)
 {
-    ssize_t      ret, index, last;
-    uintptr_t    nargs;
-    njs_value_t  *args;
+    ssize_t    ret, index, last;
+    uintptr_t  nargs;
 
     index = -1;
     nargs = param->nargs;
 
     if (nargs != 0) {
         last = NJS_STRING_MAX_LENGTH;
-        args = param->args;
 
         if (nargs > 1) {
-            last = njs_value_to_number(&args[1]);
+            last = param->args[1].data.u.number;
 
             if (last < 0) {
                 last = 0;
@@ -1079,7 +1097,7 @@ njs_string_prototype_last_index_of(njs_vm_t *vm, njs_param_t *param)
         ret = 0;
 
         for ( ;; ) {
-            ret = njs_string_index_of(vm, param->this, &args[0], ret);
+            ret = njs_string_index_of(vm, param->this, &param->args[0], ret);
 
             if (ret < 0 || ret >= last) {
                 break;
@@ -1213,41 +1231,26 @@ njs_string_prototype_search(njs_vm_t *vm, njs_param_t *param)
     int                   ret;
     nxt_int_t             index;
     nxt_uint_t            n;
-    njs_value_t           *args;
     njs_string_prop_t     string;
     njs_regexp_pattern_t  *pattern;
     int                   captures[3];
 
-    /* TODO: convert object to String. */
-
     index = 0;
 
     if (param->nargs != 0) {
 
-        args = param->args;
+        switch (param->args[0].type) {
 
-        switch (args[0].type) {
-
-        case NJS_REGEXP:
-            pattern = args[0].data.u.regexp->pattern;
-            break;
+        case NJS_VOID:
+            goto done;
 
         case NJS_STRING:
-            (void) njs_string_prop(&string, &args[0]);
+            (void) njs_string_prop(&string, &param->args[0]);
 
-            pattern = njs_regexp_pattern_create(vm, string.start,
-                                                string.length, 0);
-            if (nxt_slow_path(pattern == NULL)) {
-                return NXT_ERROR;
+            if (string.size == 0) {
+                goto done;
             }
 
-            break;
-
-        case NJS_VOID:
-            /* STUB: precompiled "/(?:)/" pattern. */
-            string.start = (u_char *) "(?:)";
-            string.length = sizeof("(?:)") - 1;
-
             pattern = njs_regexp_pattern_create(vm, string.start,
                                                 string.length, 0);
             if (nxt_slow_path(pattern == NULL)) {
@@ -1256,11 +1259,8 @@ njs_string_prototype_search(njs_vm_t *vm, njs_param_t *param)
 
             break;
 
-        default:
-            /* STUB: convert args[0] to String, then to RegExp. */
-            vm->exception = &njs_exception_type_error;
-
-            return NXT_ERROR;
+        default:  /* NJS_REGEXP */
+            pattern = param->args[0].data.u.regexp->pattern;
         }
 
         index = -1;
@@ -1278,18 +1278,24 @@ njs_string_prototype_search(njs_vm_t *vm, njs_param_t *param)
                 index = njs_string_index(&string, captures[0]);
 
             } else if (ret != PCRE_ERROR_NOMATCH) {
-                /* TODO: exception */
+                vm->exception = &njs_exception_internal_error;
                 return NXT_ERROR;
             }
         }
     }
 
+done:
+
     njs_number_set(&vm->retval, index);
 
     return NXT_OK;
 }
 
 
+/*
+ * String.match([regexp])
+ */
+
 static njs_ret_t
 njs_string_prototype_match(njs_vm_t *vm, njs_param_t *param)
 {
@@ -1299,26 +1305,49 @@ njs_string_prototype_match(njs_vm_t *vm, njs_param_t *param)
     nxt_uint_t            n, utf8;
     njs_value_t           *args;
     njs_array_t           *array;
-    njs_regexp_t          *regexp;
     njs_string_prop_t     string;
     njs_regexp_pattern_t  *pattern;
     int                   captures[3];
 
-    /* TODO: empty regexp */
+    if (param->nargs == 0) {
+        goto empty;
+    }
 
     args = param->args;
-    regexp = args[0].data.u.regexp;
 
-    if (!regexp->pattern->global) {
-        /*
-         * string.match(regexp) is the same as regexp.exec(string)
-         * if the regexp has no global flag.
-         */
-        param->args = param->this;
-        param->this = args;
-        param->nargs = 1;
+    switch (args[0].type) {
+
+    case NJS_VOID:
+        goto empty;
+
+    case NJS_STRING:
+        (void) njs_string_prop(&string, &args[0]);
+
+        if (string.size == 0) {
+            goto empty;
+        }
+
+        pattern = njs_regexp_pattern_create(vm, string.start, string.length, 0);
+        if (nxt_slow_path(pattern == NULL)) {
+            return NXT_ERROR;
+        }
+
+        break;
+
+    default:  /* NJS_REGEXP */
+        pattern = args[0].data.u.regexp->pattern;
+
+        if (!pattern->global) {
+            /*
+             * string.match(regexp) is the same as regexp.exec(string)
+             * if the regexp has no global flag.
+             */
+            param->args = param->this;
+            param->this = args;
+            param->nargs = 1;
 
-        return njs_regexp_prototype_exec(vm, param);
+            return njs_regexp_prototype_exec(vm, param);
+        }
     }
 
     vm->retval = njs_value_null;
@@ -1337,8 +1366,6 @@ njs_string_prototype_match(njs_vm_t *vm, njs_param_t *param)
         }
     }
 
-    pattern = regexp->pattern;
-
     if (pattern->code[n] != NULL) {
         array = NULL;
 
@@ -1408,14 +1435,32 @@ njs_string_prototype_match(njs_vm_t *vm, njs_param_t *param)
                 break;
 
             } else {
-                /* TODO: internal error exception */
+                vm->exception = &njs_exception_internal_error;
                 return NXT_ERROR;
             }
 
         } while (string.size > 0);
     }
 
-    regexp->last_index = 0;
+    if (njs_is_regexp(&args[0])) {
+        args[0].data.u.regexp->last_index = 0;
+    }
+
+    return NXT_OK;
+
+empty:
+
+    array = njs_array_alloc(vm, 1, 0);
+    if (nxt_slow_path(array == NULL)) {
+        return NXT_ERROR;
+    }
+
+    array->length = 1;
+    array->start[0] = njs_string_empty;
+
+    vm->retval.data.u.array = array;
+    vm->retval.type = NJS_ARRAY;
+    vm->retval.data.truth = 1;
 
     return NXT_OK;
 }
@@ -1614,104 +1659,118 @@ static const njs_object_prop_t  njs_string_prototype_properties[] =
     {
         .type = NJS_METHOD,
         .name = njs_string("valueOf"),
-        .value = njs_native_function(njs_string_prototype_value_of, 0),
+        .value = njs_native_function(njs_string_prototype_value_of, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toString"),
-        .value = njs_native_function(njs_string_prototype_value_of, 0),
+        .value = njs_native_function(njs_string_prototype_value_of, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("concat"),
-        .value = njs_native_function(njs_string_prototype_concat, 0),
+        .value = njs_native_function(njs_string_prototype_concat, 0, 0),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("fromUTF8"),
-        .value = njs_native_function(njs_string_prototype_from_utf8, 0),
+        .value = njs_native_function(njs_string_prototype_from_utf8, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toUTF8"),
-        .value = njs_native_function(njs_string_prototype_to_utf8, 0),
+        .value = njs_native_function(njs_string_prototype_to_utf8, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("fromBytes"),
-        .value = njs_native_function(njs_string_prototype_from_bytes, 0),
+        .value = njs_native_function(njs_string_prototype_from_bytes, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("toBytes"),
-        .value = njs_native_function(njs_string_prototype_to_bytes, 0),
+        .value = njs_native_function(njs_string_prototype_to_bytes, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("slice"),
-        .value = njs_native_function(njs_string_prototype_slice, 0),
+        .value = njs_native_function(njs_string_prototype_slice, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("substring"),
-        .value = njs_native_function(njs_string_prototype_substring, 0),
+        .value = njs_native_function(njs_string_prototype_substring, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("substr"),
-        .value = njs_native_function(njs_string_prototype_substr, 0),
+        .value = njs_native_function(njs_string_prototype_substr, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("charAt"),
-        .value = njs_native_function(njs_string_prototype_char_at, 0),
+        .value = njs_native_function(njs_string_prototype_char_at, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("charCodeAt"),
-        .value = njs_native_function(njs_string_prototype_char_code_at, 0),
+        .value = njs_native_function(njs_string_prototype_char_code_at, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG),
     },
 
     /* ECMAScript 6, codePointAt(). */
     {
         .type = NJS_METHOD,
         .name = njs_string("codePointAt"),
-        .value = njs_native_function(njs_string_prototype_char_code_at, 0),
+        .value = njs_native_function(njs_string_prototype_char_code_at, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("indexOf"),
-        .value = njs_native_function(njs_string_prototype_index_of, 0),
+        .value = njs_native_function(njs_string_prototype_index_of, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_STRING_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("lastIndexOf"),
-        .value = njs_native_function(njs_string_prototype_last_index_of, 0),
+        .value = njs_native_function(njs_string_prototype_last_index_of, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_STRING_ARG, NJS_INTEGER_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("search"),
-        .value = njs_native_function(njs_string_prototype_search, 0),
+        .value = njs_native_function(njs_string_prototype_search, 0,
+                     NJS_STRING_OBJECT_ARG, NJS_REGEXP_ARG),
     },
 
     {
         .type = NJS_METHOD,
         .name = njs_string("match"),
-        .value = njs_native_function(njs_string_prototype_match, 0),
+        .value = njs_native_function(njs_string_prototype_match, 0,
+                     NJS_STRING_ARG, NJS_REGEXP_ARG),
     },
 };
 
index 488439ea170b22a3b98764a1cf641330071e93a6..61ffbde5a2e9ef1ff3c39e016be5feb03b45115e 100644 (file)
@@ -86,10 +86,15 @@ static nxt_noinline njs_ret_t njs_function_frame_free(njs_vm_t *vm,
 
 static void njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
     njs_value_t *value2);
+static void njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap);
 static njs_ret_t njs_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_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_primitive_value(njs_vm_t *vm, njs_value_t *value,
     nxt_uint_t hint);
 static njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1,
@@ -131,6 +136,7 @@ const njs_value_t  njs_exception_reference_error = njs_string("ReferenceError");
 const njs_value_t  njs_exception_type_error =      njs_string("TypeError");
 const njs_value_t  njs_exception_range_error =     njs_string("RangeError");
 const njs_value_t  njs_exception_memory_error =    njs_string("MemoryError");
+const njs_value_t  njs_exception_internal_error =  njs_string("InternalError");
 
 
 /*
@@ -224,6 +230,13 @@ njs_vmcode_interpreter(njs_vm_t *vm)
 
             goto again;
 
+        case NJS_TRAP_NUMBER_ARG:
+        case NJS_TRAP_STRING_ARG:
+
+            njs_vm_trap_argument(vm, ret - NJS_TRAP_BASE);
+
+            goto again;
+
         default:
             break;
         }
@@ -2225,19 +2238,29 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
     njs_ret_t                   ret;
     njs_value_t                 *args;
     njs_param_t                 param;
+    njs_function_t              *function;
     njs_native_frame_t          *frame, *previous, *skip;
     njs_vmcode_function_call_t  *call;
 
-    if (!vm->frame->u.function->native) {
+    function = vm->frame->u.function;
+
+    if (!function->native) {
         (void) njs_function_call(vm, (njs_index_t) retval,
                                  sizeof(njs_vmcode_function_call_t));
         return 0;
     }
 
-    param.retval = (njs_index_t) retval;
     call = (njs_vmcode_function_call_t *) vm->current;
-    param.nargs = call->code.nargs - 1;
     args = vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS];
+
+    ret = njs_normalize_args(vm, args - 1, function->args_types,
+                             call->code.nargs);
+    if (ret != NJS_OK) {
+        return ret;
+    }
+
+    param.retval = (njs_index_t) retval;
+    param.nargs = call->code.nargs - 1;
     param.args = args;
     param.this = args - 1;
 
@@ -2302,6 +2325,119 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 }
 
 
+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   trap;
+    nxt_uint_t  n;
+
+    n = nxt_min(nargs, NJS_ARGS_TYPES_MAX);
+
+    while (n != 0) {
+
+        switch (*args_types) {
+
+        case NJS_STRING_OBJECT_ARG:
+
+            if (njs_is_null_or_void(args)) {
+                goto type_error;
+            }
+
+            /* Fall through. */
+
+        case NJS_STRING_ARG:
+
+            if (njs_is_string(args)) {
+                break;
+            }
+
+            trap = NJS_TRAP_STRING_ARG;
+            goto trap;
+
+        case NJS_NUMBER_ARG:
+
+            if (njs_is_numeric(args)) {
+                break;
+            }
+
+            trap = NJS_TRAP_NUMBER_ARG;
+            goto trap;
+
+        case NJS_INTEGER_ARG:
+
+            if (njs_is_numeric(args)) {
+
+                /* Convert NaN and Infinities to integer. */
+
+                if (njs_is_nan(args->data.u.number)) {
+                    args->data.u.number = 0;
+
+                } else if (njs_is_infinity(args->data.u.number)) {
+
+                    if (args->data.u.number > 0) {
+                        args->data.u.number = 0x7fffffffffffffff;
+
+                    } else {
+                        args->data.u.number = 0x8000000000000000;
+                    }
+                }
+
+                break;
+            }
+
+            trap = NJS_TRAP_NUMBER_ARG;
+            goto trap;
+
+        case NJS_REGEXP_ARG:
+
+            switch (args->type) {
+            case NJS_VOID:
+            case NJS_STRING:
+            case NJS_REGEXP:
+                break;
+
+            default:
+                trap = NJS_TRAP_STRING_ARG;
+                goto trap;
+            }
+
+            break;
+
+        case NJS_OBJECT_ARG:
+
+            if (njs_is_null_or_void(args)) {
+                goto type_error;
+            }
+
+            break;
+
+        case NJS_SKIP_ARG:
+
+            break;
+        }
+
+        args++;
+        args_types++;
+        n--;
+    }
+
+    return NJS_OK;
+
+trap:
+
+    vm->frame->trap_scratch.data.u.value = args;
+
+    return trap;
+
+type_error:
+
+    vm->exception = &njs_exception_type_error;
+
+    return NXT_ERROR;
+}
+
+
 njs_ret_t
 njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 {
@@ -2542,12 +2678,28 @@ static const njs_vmcode_1addr_t  njs_trap_number[] = {
 };
 
 
+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_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_TRAP_STRING_ARG  */  { &njs_trap_string_argument, 0 },
+    /* NJS_TRAP_INTEGER_ARG */  { &njs_trap_number_argument, 0 },
+    /* 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 },
 };
 
 
@@ -2579,6 +2731,22 @@ njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
 }
 
 
+static void
+njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap)
+{
+    njs_value_t  *value;
+
+    value = vm->frame->trap_scratch.data.u.value;
+    vm->frame->trap_values[1].data.u.value = value;
+    vm->frame->trap_values[0] = *value;
+
+    njs_set_invalid(&vm->frame->trap_scratch);
+
+    vm->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)
 {
@@ -2631,6 +2799,64 @@ njs_vmcode_string_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)
+{
+    double       num;
+    njs_ret_t    ret;
+    njs_value_t  *value;
+
+    value = &vm->frame->trap_values[0];
+
+    ret = njs_primitive_value(vm, value, 0);
+
+    if (nxt_fast_path(ret > 0)) {
+
+        if (!njs_is_numeric(value)) {
+            num = NJS_NAN;
+
+            if (njs_is_string(value)) {
+                num = njs_string_to_number(value);
+            }
+
+            njs_number_set(value, num);
+        }
+
+        *vm->frame->trap_values[1].data.u.value = *value;
+        vm->current = vm->frame->trap_restart;
+
+        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->frame->trap_values[0];
+
+    ret = njs_primitive_value(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->frame->trap_values[1].data.u.value = *value;
+            vm->current = vm->frame->trap_restart;
+        }
+    }
+
+    return ret;
+}
+
+
 /*
  * A hint value is 0 for numbers and 1 for strings.  The value chooses
  * method calls order specified by ECMAScript 5.1: "valueOf", "toString"
index bfe8392615778d15c0dd0afaaaa7c354c517b0b5..552eaa2194a26035ee1314705148e9c6c3b56c6f 100644 (file)
@@ -17,8 +17,8 @@
  *    -3:                        not used;
  *    -4 (NJS_STOP/NXT_DONE):    njs_vmcode_stop() has stopped execution,
  *                               execution has completed successfully;
- *    -5 ..  -9:                 traps to convert objects to primitive values;
- *   -10 .. -15:                 not used.
+ *    -5 .. -11:                 traps to convert objects to primitive values;
+ *   -12 .. -15:                 not used.
  */
 
 #define NJS_STOP                 NXT_DONE
@@ -29,7 +29,9 @@
 #define NJS_TRAP_INCDEC          (-7)
 #define NJS_TRAP_STRINGS         (-8)
 #define NJS_TRAP_PROPERTY        (-9)
-#define NJS_TRAP_BASE            NJS_TRAP_PROPERTY
+#define NJS_TRAP_NUMBER_ARG      (-10)
+#define NJS_TRAP_STRING_ARG      (-11)
+#define NJS_TRAP_BASE            NJS_TRAP_STRING_ARG
 
 #define NJS_PREEMPT              (-15)
 
@@ -126,9 +128,13 @@ struct njs_object_s {
 };
 
 
+#define NJS_ARGS_TYPES_MAX            3
+
 typedef struct {
     njs_object_t                      object;
 
+    uint8_t                           args_types[NJS_ARGS_TYPES_MAX];
+
 #if (NXT_64BIT)
     uint8_t                           native;
     uint8_t                           local_state_size;
@@ -248,13 +254,14 @@ union njs_value_s {
 }
 
 
-#define njs_native_function(_function, _local_size) {                         \
+#define njs_native_function(_function, _local_size, ...) {                    \
     .data = {                                                                 \
         .type = NJS_FUNCTION,                                                 \
         .truth = 1,                                                           \
         .u.function = & (njs_function_t) {                                    \
             .native = 1,                                                      \
             .local_state_size = _local_size,                                  \
+            .args_types = { __VA_ARGS__ },                                    \
             .args_offset = 1,                                                 \
             .u.native = _function,                                            \
         }                                                                     \
@@ -878,6 +885,9 @@ 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_normalize_args(njs_vm_t *vm, njs_value_t *args,
+    uint8_t *args_types, nxt_uint_t nargs);
+
 njs_ret_t njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst,
     const njs_value_t *src);
 void njs_number_set(njs_value_t *value, double num);
@@ -913,6 +923,7 @@ extern const njs_value_t  njs_exception_reference_error;
 extern const njs_value_t  njs_exception_type_error;
 extern const njs_value_t  njs_exception_range_error;
 extern const njs_value_t  njs_exception_memory_error;
+extern const njs_value_t  njs_exception_internal_error;
 
 extern const nxt_mem_proto_t     njs_array_mem_proto;
 extern const nxt_lvlhsh_proto_t  njs_object_hash_proto;
index b55c11e899b854c8a028da6e4f338b3b208236e4..4243b95194aa824e1d0a95137d87faa264bd84a8 100644 (file)
@@ -2252,6 +2252,18 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("a = 'abc'; a.concat('абв', 123)"),
       nxt_string("abcабв123") },
 
+    { nxt_string("''.concat.call(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)"),
+      nxt_string("0123456789") },
+
+#if 0
+    { nxt_string("''.concat.apply(0, [1, 2, 3, 4, 5, 6, 7, 8, 9])"),
+      nxt_string("0123456789") },
+#endif
+
+    { nxt_string("var s = { toString: function() { return '123' } };"
+                 "var a = 'abc'; a.concat('абв', s)"),
+      nxt_string("abcабв123") },
+
     { nxt_string("'\\u00CE\\u00B1'.toBytes() == 'α'"),
       nxt_string("true") },
 
@@ -2346,6 +2358,35 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abc'.charAt(3)"),
       nxt_string("") },
 
+    { nxt_string("'abc'.charAt(undefined)"),
+      nxt_string("a") },
+
+    { nxt_string("'abc'.charAt(null)"),
+      nxt_string("a") },
+
+    { nxt_string("'abc'.charAt(false)"),
+      nxt_string("a") },
+
+    { nxt_string("'abc'.charAt(true)"),
+      nxt_string("b") },
+
+    { nxt_string("'abc'.charAt(NaN)"),
+      nxt_string("a") },
+
+    { nxt_string("'abc'.charAt(Infinity)"),
+      nxt_string("") },
+
+    { nxt_string("'abc'.charAt(-Infinity)"),
+      nxt_string("") },
+
+    { nxt_string("var o = { valueOf: function() {return 2} }"
+                 "'abc'.charAt(o)"),
+      nxt_string("c") },
+
+    { nxt_string("var o = { toString: function() {return '2'} }"
+                 "'abc'.charAt(o)"),
+      nxt_string("c") },
+
     { nxt_string("'abc'.charCodeAt(1 + 1)"),
       nxt_string("99") },
 
@@ -2546,6 +2587,15 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abcdef'.indexOf('', 3)"),
       nxt_string("3") },
 
+    { nxt_string("'12345'.indexOf()"),
+      nxt_string("-1") },
+
+    { nxt_string("'12345'.indexOf(45, '0')"),
+      nxt_string("3") },
+
+    { nxt_string("''.indexOf.call(12345, 45, '0')"),
+      nxt_string("3") },
+
     { nxt_string("'abc abc abc abc'.lastIndexOf('abc')"),
       nxt_string("12") },
 
@@ -2570,6 +2620,79 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abcdefgh'.search('def')"),
       nxt_string("3") },
 
+    { nxt_string("'123456'.search('45')"),
+      nxt_string("3") },
+
+    { nxt_string("'123456'.search(45)"),
+      nxt_string("3") },
+
+    { nxt_string("'123456'.search(String(45))"),
+      nxt_string("3") },
+
+    { nxt_string("'123456'.search(Number('45'))"),
+      nxt_string("3") },
+
+    { nxt_string("var r = { toString: function() { return '45' } }"
+                 "'123456'.search(r)"),
+      nxt_string("3") },
+
+    { nxt_string("var r = { toString: function() { return 45 } }"
+                 "'123456'.search(r)"),
+      nxt_string("3") },
+
+    { nxt_string("var r = { toString: function() { return /45/ } }"
+                 "'123456'.search(r)"),
+      nxt_string("TypeError") },
+
+    { nxt_string("var r = { toString: function() { return /34/ },"
+                 "          valueOf:  function() { return 45 } }"
+                 "'123456'.search(r)"),
+      nxt_string("3") },
+
+    { nxt_string("'abcdefgh'.match()"),
+      nxt_string("") },
+
+    { nxt_string("'abcdefgh'.match('')"),
+      nxt_string("") },
+
+    { nxt_string("'abcdefgh'.match(undefined)"),
+      nxt_string("") },
+
+    { nxt_string("'abcdefgh'.match(/def/)"),
+      nxt_string("def") },
+
+    { nxt_string("'abcdefgh'.match('def')"),
+      nxt_string("def") },
+
+    { nxt_string("'123456'.match('45')"),
+      nxt_string("45") },
+
+    { nxt_string("'123456'.match(45)"),
+      nxt_string("45") },
+
+    { nxt_string("'123456'.match(String(45))"),
+      nxt_string("45") },
+
+    { nxt_string("'123456'.match(Number('45'))"),
+      nxt_string("45") },
+
+    { nxt_string("var r = { toString: function() { return '45' } }"
+                 "'123456'.match(r)"),
+      nxt_string("45") },
+
+    { nxt_string("var r = { toString: function() { return 45 } }"
+                 "'123456'.match(r)"),
+      nxt_string("45") },
+
+    { nxt_string("var r = { toString: function() { return /45/ } }"
+                 "'123456'.match(r)"),
+      nxt_string("TypeError") },
+
+    { nxt_string("var r = { toString: function() { return /34/ },"
+                 "          valueOf:  function() { return 45 } }"
+                 "'123456'.match(r)"),
+      nxt_string("45") },
+
     { nxt_string("''.match(/^$/)"),
       nxt_string("") },
 
@@ -2897,9 +3020,15 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("[].join.call([1,2,3], ':')"),
       nxt_string("1:2:3") },
 
+    { nxt_string("[].join.call([1,2,3], 55)"),
+      nxt_string("1552553") },
+
     { nxt_string("[].join.call()"),
       nxt_string("TypeError") },
 
+    { nxt_string("[].slice.call()"),
+      nxt_string("TypeError") },
+
     { nxt_string("function F(a, b) { this.a = a + b }"
                  "var o = new F(1, 2)"
                  "o.a"),
@@ -2928,6 +3057,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("/\\d/.test('123')"),
       nxt_string("true") },
 
+    { nxt_string("/\\d/.test(123)"),
+      nxt_string("true") },
+
+    { nxt_string("/undef/.test()"),
+      nxt_string("true") },
+
+    { nxt_string("var s = { toString: function() { return 123 } };"
+                 "/\\d/.test(s)"),
+      nxt_string("true") },
+
     { nxt_string("/\\d/.test('abc')"),
       nxt_string("false") },
 
@@ -2949,6 +3088,19 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("/α/.test('\\u00CE\\u00B1'.toBytes())"),
       nxt_string("true") },
 
+    { nxt_string("/\\d/.exec('123')"),
+      nxt_string("1") },
+
+    { nxt_string("/\\d/.exec(123)"),
+      nxt_string("1") },
+
+    { nxt_string("/undef/.exec()"),
+      nxt_string("undef") },
+
+    { nxt_string("var s = { toString: function() { return 123 } };"
+                 "/\\d/.exec(s)"),
+      nxt_string("1") },
+
     { nxt_string("var a = /^$/.exec(''); a.length +' '+ a"),
       nxt_string("1 ") },
 
@@ -3311,6 +3463,20 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Number()"),
       nxt_string("0") },
 
+    { nxt_string("Number(123)"),
+      nxt_string("123") },
+
+    { nxt_string("Number('123')"),
+      nxt_string("123") },
+
+    { nxt_string("var o = { valueOf: function() { return 123 } };"
+                 "Number(o)"),
+      nxt_string("123") },
+
+    { nxt_string("var o = { valueOf: function() { return 123 } };"
+                 "new Number(o)"),
+      nxt_string("123") },
+
     { nxt_string("typeof Number(1)"),
       nxt_string("number") },
 
@@ -3347,6 +3513,26 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("String()"),
       nxt_string("") },
 
+    { nxt_string("String(123)"),
+      nxt_string("123") },
+
+    { nxt_string("new String(123)"),
+      nxt_string("123") },
+
+    { nxt_string("String([1,2,3])"),
+      nxt_string("1,2,3") },
+
+    { nxt_string("new String([1,2,3])"),
+      nxt_string("1,2,3") },
+
+    { nxt_string("var o = { toString: function() { return 'OK' } }"
+                 "String(o)"),
+      nxt_string("OK") },
+
+    { nxt_string("var o = { toString: function() { return 'OK' } }"
+                 "new String(o)"),
+      nxt_string("OK") },
+
     { nxt_string("typeof String('abc')"),
       nxt_string("string") },
 
@@ -3410,6 +3596,15 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Function.constructor === Function"),
       nxt_string("true") },
 
+    { nxt_string("RegExp()"),
+      nxt_string("/(?:)/") },
+
+    { nxt_string("RegExp('')"),
+      nxt_string("/(?:)/") },
+
+    { nxt_string("RegExp(123)"),
+      nxt_string("/123/") },
+
     { nxt_string("RegExp.name"),
       nxt_string("RegExp") },
 
@@ -3428,10 +3623,8 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("RegExp.constructor === Function"),
       nxt_string("true") },
 
-#if 0
     { nxt_string("Object.prototype.toString.call()"),
       nxt_string("[object Undefined]") },
-#endif
 
     { nxt_string("Object.prototype.toString.call(undefined)"),
       nxt_string("[object Undefined]") },