From: Igor Sysoev Date: Wed, 3 Feb 2016 14:47:41 +0000 (+0300) Subject: Traps for native method arguments. X-Git-Tag: 0.1.0~75 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=7a870cf502942146413df210dc4f17aced9bdf48;p=njs.git Traps for native method arguments. --- diff --git a/njs/njs_array.c b/njs/njs_array.c index a13cd9c0..969b3776 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -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), }, }; diff --git a/njs/njs_boolean.c b/njs/njs_boolean.c index 3affde65..5e4213d1 100644 --- a/njs/njs_boolean.c +++ b/njs/njs_boolean.c @@ -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), }, }; diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index b37c7170..9ee2d281 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -22,15 +22,21 @@ #include +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, diff --git a/njs/njs_function.c b/njs/njs_function.c index f9153876..2c2af491 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -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 = ¶m->args[0]; p.args = ¶m->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, ¶m->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), }, }; diff --git a/njs/njs_function.h b/njs/njs_function.h index 796ae79c..74abdd02 100644 --- a/njs/njs_function.h +++ b/njs/njs_function.h @@ -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; diff --git a/njs/njs_number.c b/njs/njs_number.c index 2468ce5e..3f479e32 100644 --- a/njs/njs_number.c +++ b/njs/njs_number.c @@ -222,7 +222,6 @@ njs_number_constructor(njs_vm_t *vm, njs_param_t *param) value = &njs_value_zero; } else { - /* TODO: to_number. */ value = ¶m->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), }, }; diff --git a/njs/njs_object.c b/njs/njs_object.c index 289567d3..6d4f1f67 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -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), }, }; diff --git a/njs/njs_regexp.c b/njs/njs_regexp.c index 7d238343..54edc123 100644 --- a/njs/njs_regexp.c +++ b/njs/njs_regexp.c @@ -62,7 +62,12 @@ njs_regexp_constructor(njs_vm_t *vm, njs_param_t *param) case 1: string.length = njs_string_prop(&string, ¶m->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, ¶m->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 = ¶m->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, ®exp->string, ¶m->args[0]); + if (param->nargs != 0) { + value = ¶m->args[0]; - if (nxt_fast_path(ret == NXT_OK)) { - (void) njs_string_prop(&string, ®exp->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), }, }; diff --git a/njs/njs_string.c b/njs/njs_string.c index 21447442..95915d97 100644 --- a/njs/njs_string.c +++ b/njs/njs_string.c @@ -260,7 +260,6 @@ njs_string_constructor(njs_vm_t *vm, njs_param_t *param) value = &njs_string_empty; } else { - /* TODO: to_string. */ value = ¶m->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(¶m->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(¶m->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, ¶m->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, ¶m->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, ¶m->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), }, }; diff --git a/njs/njs_vm.c b/njs/njs_vm.c index 488439ea..61ffbde5 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -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" diff --git a/njs/njs_vm.h b/njs/njs_vm.h index bfe83926..552eaa21 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -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; diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index b55c11e8..4243b951 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -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]") },