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