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