No functional changes.
njs/njs.c \
njs/njs_value.c \
njs/njs_vm.c \
+ njs/njs_vmcode.c \
njs/njs_boolean.c \
njs/njs_number.c \
njs/njs_string.c \
#include <njs.h>
#include <njs_value.h>
#include <njs_vm.h>
+#include <njs_vmcode.h>
#include <njs_variable.h>
#include <njs_lexer.h>
#include <njs_parser.h>
#include <string.h>
-struct njs_property_next_s {
- uint32_t index;
- njs_array_t *array;
-};
-
-
-/*
- * These functions are forbidden to inline to minimize JavaScript VM
- * interpreter memory footprint. The size is less than 8K on AMD64
- * and should fit in CPU L1 instruction cache.
- */
-
-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, 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);
-
-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);
-
-
-const nxt_str_t njs_entry_main = nxt_string("main");
-const nxt_str_t njs_entry_module = nxt_string("module");
-const nxt_str_t njs_entry_native = nxt_string("native");
-const nxt_str_t njs_entry_unknown = nxt_string("unknown");
-const nxt_str_t njs_entry_anonymous = nxt_string("anonymous");
-
-
-/*
- * The nJSVM is optimized for an ABIs where the first several arguments
- * are passed in registers (AMD64, ARM32/64): two pointers to the operand
- * values is passed as arguments although they are not always used.
- */
-
-nxt_int_t
-njs_vmcode_interpreter(njs_vm_t *vm)
-{
- u_char *catch, call;
- njs_ret_t ret;
- njs_value_t *retval, *value1, *value2;
- njs_frame_t *frame;
- njs_native_frame_t *previous;
- njs_vmcode_generic_t *vmcode;
-
-start:
-
- for ( ;; ) {
-
- vmcode = (njs_vmcode_generic_t *) vm->current;
-
- /*
- * The first operand is passed as is in value2 to
- * njs_vmcode_jump(),
- * njs_vmcode_if_true_jump(),
- * njs_vmcode_if_false_jump(),
- * njs_vmcode_validate(),
- * njs_vmcode_function_frame(),
- * njs_vmcode_function_call(),
- * njs_vmcode_return(),
- * njs_vmcode_try_start(),
- * njs_vmcode_try_continue(),
- * njs_vmcode_try_break(),
- * njs_vmcode_try_end(),
- * njs_vmcode_catch().
- * njs_vmcode_throw().
- * njs_vmcode_stop().
- */
- value2 = (njs_value_t *) vmcode->operand1;
- value1 = NULL;
-
- switch (vmcode->code.operands) {
-
- case NJS_VMCODE_3OPERANDS:
- value2 = njs_vmcode_operand(vm, vmcode->operand3);
-
- /* Fall through. */
-
- case NJS_VMCODE_2OPERANDS:
- value1 = njs_vmcode_operand(vm, vmcode->operand2);
- }
-
- ret = vmcode->code.operation(vm, value1, value2);
-
- /*
- * On success an operation returns size of the bytecode,
- * a jump offset or zero after the call or return operations.
- * Jumps can return a negative offset. Compilers can generate
- * (ret < 0 && ret >= NJS_PREEMPT)
- * as a single unsigned comparision.
- */
-
- if (nxt_slow_path(ret < 0 && ret >= NJS_PREEMPT)) {
- break;
- }
-
- vm->current += ret;
-
- if (vmcode->code.retval) {
- retval = njs_vmcode_operand(vm, vmcode->operand1);
- njs_release(vm, retval);
- *retval = vm->retval;
- }
- }
-
- if (ret == NXT_ERROR) {
-
- for ( ;; ) {
- frame = (njs_frame_t *) vm->top_frame;
-
- call = frame->native.call;
- catch = frame->native.exception.catch;
-
- if (catch != NULL) {
- vm->current = catch;
-
- if (vm->debug != NULL) {
- nxt_array_reset(vm->backtrace);
- }
-
- goto start;
- }
-
- if (vm->debug != NULL
- && njs_vm_add_backtrace_entry(vm, frame) != NXT_OK)
- {
- return NXT_ERROR;
- }
-
- previous = frame->native.previous;
- if (previous == NULL) {
- return NXT_ERROR;
- }
-
- njs_vm_scopes_restore(vm, frame, previous);
-
- if (frame->native.size != 0) {
- vm->stack_size -= frame->native.size;
- nxt_mp_free(vm->mem_pool, frame);
- }
-
- if (call) {
- return NXT_ERROR;
- }
- }
- }
-
- /* NXT_ERROR, NJS_STOP. */
-
- return ret;
-}
-
-
-nxt_int_t
-njs_vmcode_run(njs_vm_t *vm)
-{
- njs_ret_t ret;
-
- vm->top_frame->call = 1;
-
- 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;
-}
-
-
-njs_ret_t
-njs_vmcode_object(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- njs_object_t *object;
-
- object = njs_object_alloc(vm);
-
- if (nxt_fast_path(object != NULL)) {
- njs_set_object(&vm->retval, object);
-
- return sizeof(njs_vmcode_object_t);
- }
-
- return NXT_ERROR;
-}
-
-
-njs_ret_t
-njs_vmcode_array(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- uint32_t length;
- njs_array_t *array;
- njs_value_t *value;
- njs_vmcode_array_t *code;
-
- code = (njs_vmcode_array_t *) vm->current;
-
- array = njs_array_alloc(vm, code->length, NJS_ARRAY_SPARE);
-
- if (nxt_fast_path(array != NULL)) {
-
- if (code->code.ctor) {
- /* Array of the form [,,,], [1,,]. */
- value = array->start;
- length = array->length;
-
- do {
- njs_set_invalid(value);
- value++;
- length--;
- } while (length != 0);
-
- } else {
- /* Array of the form [], [,,1], [1,2,3]. */
- array->length = 0;
- }
-
- njs_set_array(&vm->retval, array);
-
- return sizeof(njs_vmcode_array_t);
- }
-
- return NXT_ERROR;
-}
-
-
-njs_ret_t
-njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- njs_function_t *function;
- njs_function_lambda_t *lambda;
- njs_vmcode_function_t *code;
-
- code = (njs_vmcode_function_t *) vm->current;
- lambda = code->lambda;
-
- function = njs_function_alloc(vm, lambda, vm->active_frame->closures, 0);
- if (nxt_slow_path(function == NULL)) {
- return NXT_ERROR;
- }
-
- njs_set_function(&vm->retval, function);
-
- return sizeof(njs_vmcode_function_t);
-}
-
-
-njs_ret_t
-njs_vmcode_this(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- njs_frame_t *frame;
- njs_value_t *value;
- njs_vmcode_this_t *code;
-
- frame = (njs_frame_t *) vm->active_frame;
- code = (njs_vmcode_this_t *) vm->current;
-
- value = njs_vmcode_operand(vm, code->dst);
- *value = frame->native.arguments[0];
-
- return sizeof(njs_vmcode_this_t);
-}
-
-
-njs_ret_t
-njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- nxt_int_t ret;
- njs_frame_t *frame;
- njs_value_t *value;
- njs_vmcode_arguments_t *code;
-
- frame = (njs_frame_t *) vm->active_frame;
-
- if (frame->native.arguments_object == NULL) {
- ret = njs_function_arguments_object_init(vm, &frame->native);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
- }
-
- code = (njs_vmcode_arguments_t *) vm->current;
-
- value = njs_vmcode_operand(vm, code->dst);
- njs_set_object(value, frame->native.arguments_object);
-
- return sizeof(njs_vmcode_arguments_t);
-}
-
-
-njs_ret_t
-njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- njs_regexp_t *regexp;
- njs_vmcode_regexp_t *code;
-
- code = (njs_vmcode_regexp_t *) vm->current;
-
- regexp = njs_regexp_alloc(vm, code->pattern);
-
- if (nxt_fast_path(regexp != NULL)) {
- njs_set_regexp(&vm->retval, regexp);
-
- return sizeof(njs_vmcode_regexp_t);
- }
-
- return NXT_ERROR;
-}
-
-
-njs_ret_t
-njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *invld1,
- njs_value_t *retval)
-{
- nxt_int_t ret;
- njs_array_t *array;
- njs_value_t *value;
-
- static const njs_function_t concat = {
- .native = 1,
- .args_offset = 1,
- .u.native = njs_string_prototype_concat
- };
-
- value = njs_vmcode_operand(vm, retval);
-
- if (!njs_is_primitive(value)) {
- array = njs_array(value);
-
- ret = njs_function_frame(vm, (njs_function_t *) &concat,
- (njs_value_t *) &njs_string_empty,
- array->start, array->length, 0);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
- }
-
- return sizeof(njs_vmcode_template_literal_t);
-}
-
-
-njs_ret_t
-njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- njs_object_t *object;
- njs_function_t *function;
-
- switch (value->type) {
-
- case NJS_OBJECT:
- object = njs_object_value_copy(vm, value);
- if (nxt_slow_path(object == NULL)) {
- return NXT_ERROR;
- }
-
- break;
-
- case NJS_FUNCTION:
- function = njs_function_value_copy(vm, value);
- if (nxt_slow_path(function == NULL)) {
- return NXT_ERROR;
- }
-
- break;
-
- default:
- break;
- }
-
- vm->retval = *value;
-
- njs_retain(value);
-
- return sizeof(njs_vmcode_object_copy_t);
-}
-
-
-njs_ret_t
-njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property)
-{
- njs_ret_t ret;
- njs_value_t *retval;
- njs_vmcode_prop_get_t *code;
-
- code = (njs_vmcode_prop_get_t *) vm->current;
- retval = njs_vmcode_operand(vm, code->value);
-
- ret = njs_value_property(vm, object, property, retval);
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
- }
-
- vm->retval = *retval;
-
- return sizeof(njs_vmcode_prop_get_t);
-}
-
-
-njs_ret_t
-njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property)
-{
- uint32_t index, size;
- njs_ret_t ret;
- njs_array_t *array;
- njs_value_t *init, *value, name;
- njs_object_t *obj;
- njs_object_prop_t *prop;
- nxt_lvlhsh_query_t lhq;
- njs_vmcode_prop_set_t *code;
-
- code = (njs_vmcode_prop_set_t *) vm->current;
- init = njs_vmcode_operand(vm, code->value);
-
- switch (object->type) {
- case NJS_ARRAY:
- index = njs_value_to_index(property);
- if (nxt_slow_path(index == NJS_ARRAY_INVALID_INDEX)) {
- njs_internal_error(vm,
- "invalid index while property initialization");
- return NXT_ERROR;
- }
-
- array = object->data.u.array;
-
- if (index >= array->length) {
- size = index - array->length;
-
- ret = njs_array_expand(vm, array, 0, size + 1);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- value = &array->start[array->length];
-
- while (size != 0) {
- njs_set_invalid(value);
- value++;
- size--;
- }
-
- array->length = index + 1;
- }
-
- /* GC: retain. */
- array->start[index] = *init;
-
- break;
-
- case NJS_OBJECT:
- ret = njs_value_to_string(vm, &name, property);
- if (nxt_slow_path(ret != NXT_OK)) {
- return NXT_ERROR;
- }
-
- njs_string_get(&name, &lhq.key);
- lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
- lhq.proto = &njs_object_hash_proto;
- lhq.pool = vm->mem_pool;
-
- obj = njs_object(object);
-
- ret = nxt_lvlhsh_find(&obj->__proto__->shared_hash, &lhq);
- if (ret == NXT_OK) {
- prop = lhq.value;
-
- if (prop->type == NJS_PROPERTY_HANDLER) {
- ret = prop->value.data.u.prop_handler(vm, object, init,
- &vm->retval);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- break;
- }
- }
-
- prop = njs_object_prop_alloc(vm, &name, init, 1);
- if (nxt_slow_path(prop == NULL)) {
- return NXT_ERROR;
- }
-
- lhq.value = prop;
- lhq.replace = 1;
-
- ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
- if (nxt_slow_path(ret != NXT_OK)) {
- njs_internal_error(vm, "lvlhsh insert/replace failed");
- return NXT_ERROR;
- }
-
- break;
-
- default:
- njs_internal_error(vm, "unexpected object type \"%s\" "
- "while property initialization",
- njs_type_string(object->type));
-
- return NXT_ERROR;
- }
-
- return sizeof(njs_vmcode_prop_set_t);
-}
-
-
-njs_ret_t
-njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property)
-{
- njs_ret_t ret;
- njs_value_t *value;
- njs_vmcode_prop_set_t *code;
-
- code = (njs_vmcode_prop_set_t *) vm->current;
- value = njs_vmcode_operand(vm, code->value);
-
- ret = njs_value_property_set(vm, object, property, value);
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
- }
-
- return sizeof(njs_vmcode_prop_set_t);
-}
-
-
-njs_ret_t
-njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property)
-{
- njs_ret_t ret;
- njs_object_prop_t *prop;
- const njs_value_t *retval;
- njs_property_query_t pq;
-
- retval = &njs_value_false;
-
- njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
-
- ret = njs_property_query(vm, &pq, object, property);
-
- switch (ret) {
-
- case NXT_OK:
- prop = pq.lhq.value;
-
- if (!njs_is_valid(&prop->value)) {
- break;
- }
-
- retval = &njs_value_true;
- break;
-
- case NXT_DECLINED:
- if (!njs_is_object(object) && !njs_is_external(object)) {
- njs_type_error(vm, "property in on a primitive value");
-
- return NXT_ERROR;
- }
-
- break;
-
- case NXT_ERROR:
- default:
-
- return ret;
- }
-
- vm->retval = *retval;
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property)
-{
- njs_ret_t ret;
- njs_object_prop_t *prop, *whipeout;
- njs_property_query_t pq;
-
- njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 1);
-
- ret = njs_property_query(vm, &pq, object, property);
-
- switch (ret) {
-
- case NXT_OK:
- prop = pq.lhq.value;
-
- if (nxt_slow_path(!prop->configurable)) {
- njs_type_error(vm, "Cannot delete property \"%V\" of %s",
- &pq.lhq.key, njs_type_string(object->type));
- return NXT_ERROR;
- }
-
- if (nxt_slow_path(pq.shared)) {
- whipeout = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t),
- sizeof(njs_object_prop_t));
- if (nxt_slow_path(whipeout == NULL)) {
- njs_memory_error(vm);
- return NXT_ERROR;
- }
-
- njs_set_invalid(&whipeout->value);
- whipeout->name = prop->name;
- whipeout->type = NJS_WHITEOUT;
-
- pq.lhq.replace = 0;
- pq.lhq.value = whipeout;
- pq.lhq.pool = vm->mem_pool;
-
- ret = nxt_lvlhsh_insert(&pq.prototype->hash, &pq.lhq);
- if (nxt_slow_path(ret != NXT_OK)) {
- njs_internal_error(vm, "lvlhsh insert failed");
- return NXT_ERROR;
- }
-
- break;
- }
-
- switch (prop->type) {
- case NJS_PROPERTY:
- case NJS_METHOD:
- break;
-
- case NJS_PROPERTY_REF:
- njs_set_invalid(prop->value.data.u.value);
- goto done;
-
- case NJS_PROPERTY_HANDLER:
- ret = prop->value.data.u.prop_handler(vm, object, NULL, NULL);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- goto done;
-
- default:
- njs_internal_error(vm, "unexpected property type \"%s\" "
- "while deleting",
- njs_prop_type_string(prop->type));
-
- return NXT_ERROR;
- }
-
- /* GC: release value. */
- prop->type = NJS_WHITEOUT;
- njs_set_invalid(&prop->value);
-
- break;
-
- case NXT_DECLINED:
- break;
-
- case NXT_ERROR:
- default:
-
- return ret;
- }
-
-done:
-
- vm->retval = njs_value_true;
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *invld)
-{
- void *obj;
- njs_ret_t ret;
- njs_property_next_t *next;
- const njs_extern_t *ext_proto;
- njs_vmcode_prop_foreach_t *code;
-
- if (njs_is_external(object)) {
- ext_proto = object->external.proto;
-
- if (ext_proto->foreach != NULL) {
- obj = njs_extern_object(vm, object);
-
- ret = ext_proto->foreach(vm, obj, &vm->retval);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
- }
-
- goto done;
- }
-
- next = nxt_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
- if (nxt_slow_path(next == NULL)) {
- njs_memory_error(vm);
- return NXT_ERROR;
- }
-
- next->index = 0;
- next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, 0);
- if (nxt_slow_path(next->array == NULL)) {
- njs_memory_error(vm);
- return NXT_ERROR;
- }
-
- vm->retval.data.u.next = next;
-
-done:
-
- code = (njs_vmcode_prop_foreach_t *) vm->current;
-
- return code->offset;
-}
-
-
-njs_ret_t
-njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object, njs_value_t *value)
-{
- void *obj;
- njs_ret_t ret;
- njs_value_t *retval;
- njs_property_next_t *next;
- const njs_extern_t *ext_proto;
- njs_vmcode_prop_next_t *code;
-
- code = (njs_vmcode_prop_next_t *) vm->current;
- retval = njs_vmcode_operand(vm, code->retval);
-
- if (njs_is_external(object)) {
- ext_proto = object->external.proto;
-
- if (ext_proto->next != NULL) {
- obj = njs_extern_object(vm, object);
-
- ret = ext_proto->next(vm, retval, obj, value);
-
- if (ret == NXT_OK) {
- return code->offset;
- }
-
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
- }
-
- /* ret == NJS_DONE. */
- }
-
- return sizeof(njs_vmcode_prop_next_t);
- }
-
- next = value->data.u.next;
-
- if (next->index < next->array->length) {
- *retval = next->array->data[next->index++];
-
- return code->offset;
- }
-
- nxt_mp_free(vm->mem_pool, next);
-
- return sizeof(njs_vmcode_prop_next_t);
-}
-
-
-njs_ret_t
-njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *constructor)
-{
- nxt_int_t ret;
- njs_value_t value;
- njs_object_t *prototype, *proto;
- const njs_value_t *retval;
-
- static njs_value_t prototype_string = njs_string("prototype");
-
- if (!njs_is_function(constructor)) {
- njs_type_error(vm, "right argument is not a function");
- return NXT_ERROR;
- }
-
- retval = &njs_value_false;
-
- if (njs_is_object(object)) {
- value = njs_value_undefined;
- ret = njs_value_property(vm, constructor, &prototype_string, &value);
-
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
- }
-
- if (nxt_fast_path(ret == NXT_OK)) {
-
- if (nxt_slow_path(!njs_is_object(&value))) {
- njs_internal_error(vm, "prototype is not an object");
- return NXT_ERROR;
- }
-
- prototype = njs_object(&value);
- proto = njs_object(object);
-
- do {
- proto = proto->__proto__;
-
- if (proto == prototype) {
- retval = &njs_value_true;
- break;
- }
-
- } while (proto != NULL);
- }
- }
-
- vm->retval = *retval;
-
- return sizeof(njs_vmcode_instance_of_t);
-}
-
-
-njs_ret_t
-njs_vmcode_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_slow_path(!njs_is_numeric(value))) {
- ret = njs_value_to_numeric(vm, &numeric, value);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- num = njs_number(&numeric);
-
- } else {
- num = njs_number(value);
- }
-
- 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;
- 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 (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- num = njs_number(&numeric);
-
- } else {
- num = njs_number(value);
- }
-
- 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_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_slow_path(!njs_is_numeric(value))) {
- ret = njs_value_to_numeric(vm, &numeric, value);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- num = njs_number(&numeric);
-
- } else {
- num = njs_number(value);
- }
-
- njs_release(vm, reference);
-
- njs_set_number(reference, num + 1.0);
- njs_set_number(&vm->retval, num);
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *reference,
- njs_value_t *value)
-{
- double num;
- 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 (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- num = njs_number(&numeric);
-
- } else {
- num = njs_number(value);
- }
-
- njs_release(vm, reference);
-
- njs_set_number(reference, num - 1.0);
- njs_set_number(&vm->retval, num);
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- /* ECMAScript 5.1: null, array and regexp are objects. */
-
- static const njs_value_t *types[NJS_TYPE_MAX] = {
- &njs_string_object,
- &njs_string_undefined,
- &njs_string_boolean,
- &njs_string_number,
- &njs_string_string,
- &njs_string_data,
- &njs_string_external,
- &njs_string_invalid,
- &njs_string_undefined,
- &njs_string_undefined,
- &njs_string_undefined,
- &njs_string_undefined,
- &njs_string_undefined,
- &njs_string_undefined,
- &njs_string_undefined,
- &njs_string_undefined,
-
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_function,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- &njs_string_object,
- };
-
- vm->retval = *types[value->type];
-
- return sizeof(njs_vmcode_2addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_void(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
-{
- vm->retval = njs_value_undefined;
-
- return sizeof(njs_vmcode_2addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- njs_release(vm, value);
-
- vm->retval = njs_value_true;
-
- return sizeof(njs_vmcode_2addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_unary_plus(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- 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;
- }
-
- 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)
-{
- 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;
- }
-
- 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)
-{
- njs_ret_t ret;
-
- njs_value_t primitive1, primitive2, dst, *s1, *s2, *src;
-
- if (nxt_slow_path(!njs_is_primitive(val1))) {
-
- /*
- * 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;
- }
-
- val1 = &primitive1;
- }
-
- if (nxt_slow_path(!njs_is_primitive(val2))) {
-
- /*
- * ECMAScript 5.1:
- * Date should return String, other types sould return Number.
- */
-
- ret = njs_value_to_primitive(vm, &primitive2, val2, njs_is_date(val2));
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &primitive2;
- }
-
- 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);
- }
-
- 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 ret;
-}
-
-
-static njs_ret_t
-njs_string_concat(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
-{
- u_char *start;
- size_t size, length;
- njs_string_prop_t string1, string2;
-
- (void) njs_string_prop(&string1, val1);
- (void) njs_string_prop(&string2, val2);
-
- /*
- * A result of concatenation of Byte and ASCII or UTF-8 strings
- * is a Byte string.
- */
- if ((string1.length != 0 || string1.size == 0)
- && (string2.length != 0 || string2.size == 0))
- {
- length = string1.length + string2.length;
-
- } else {
- length = 0;
- }
-
- size = string1.size + string2.size;
-
- start = njs_string_alloc(vm, &vm->retval, size, length);
-
- if (nxt_slow_path(start == NULL)) {
- return NXT_ERROR;
- }
-
- (void) memcpy(start, string1.start, string1.size);
- (void) memcpy(start + string1.size, string2.start, string2.size);
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_substraction(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
-{
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- 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)
-{
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- 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;
- njs_ret_t ret;
- nxt_bool_t valid;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- base = njs_number(val1);
- exponent = njs_number(val2);
-
- /*
- * 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;
- }
-
- 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)
-{
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- 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;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- 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;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- num1 = njs_number_to_int32(njs_number(val1));
- num2 = njs_number_to_uint32(njs_number(val2));
- njs_set_int32(&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;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- num1 = njs_number_to_int32(njs_number(val1));
- num2 = njs_number_to_uint32(njs_number(val2));
- njs_set_int32(&vm->retval, num1 >> (num2 & 0x1f));
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2)
-{
- uint32_t num1, num2;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- num1 = njs_number_to_uint32(njs_number(val1));
- num2 = njs_number_to_uint32(njs_number(val2));
- njs_set_uint32(&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)
-{
- njs_set_boolean(&vm->retval, !njs_is_true(value));
-
- return sizeof(njs_vmcode_2addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_test_if_true(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- njs_vmcode_test_jump_t *test_jump;
-
- vm->retval = *value;
-
- if (njs_is_true(value)) {
- test_jump = (njs_vmcode_test_jump_t *) vm->current;
- return test_jump->offset;
- }
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_test_if_false(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- njs_vmcode_test_jump_t *test_jump;
-
- vm->retval = *value;
-
- if (!njs_is_true(value)) {
- test_jump = (njs_vmcode_test_jump_t *) vm->current;
- return test_jump->offset;
- }
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_bitwise_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- 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;
- }
-
- njs_set_int32(&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;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- num1 = njs_number_to_integer(njs_number(val1));
- num2 = njs_number_to_integer(njs_number(val2));
- njs_set_int32(&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;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- num1 = njs_number_to_integer(njs_number(val1));
- num2 = njs_number_to_integer(njs_number(val2));
- njs_set_int32(&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;
- njs_ret_t ret;
- njs_value_t numeric1, numeric2;
-
- if (nxt_slow_path(!njs_is_numeric(val1))) {
- ret = njs_value_to_numeric(vm, &numeric1, val1);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &numeric1;
- }
-
- if (nxt_slow_path(!njs_is_numeric(val2))) {
- ret = njs_value_to_numeric(vm, &numeric2, val2);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val2 = &numeric2;
- }
-
- num1 = njs_number_to_integer(njs_number(val1));
- num2 = njs_number_to_integer(njs_number(val2));
- njs_set_int32(&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;
-
- ret = njs_values_equal(vm, val1, val2);
-
- if (nxt_fast_path(ret >= 0)) {
-
- njs_set_boolean(&vm->retval, ret != 0);
-
- return sizeof(njs_vmcode_3addr_t);
- }
-
- return ret;
-}
-
-
-njs_ret_t
-njs_vmcode_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
-{
- njs_ret_t ret;
-
- ret = njs_values_equal(vm, val1, val2);
-
- if (nxt_fast_path(ret >= 0)) {
-
- njs_set_boolean(&vm->retval, ret == 0);
-
- return sizeof(njs_vmcode_3addr_t);
- }
-
- return ret;
-}
-
-
-static njs_ret_t
-njs_values_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
-{
- 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);
-
- /* Void and null are equal and not comparable with anything else. */
- if (nv1 || nv2) {
- return (nv1 && nv2);
- }
-
- if (njs_is_numeric(val1) && njs_is_numeric(val2)) {
- /* NaNs and Infinities are handled correctly by comparision. */
- return (njs_number(val1) == njs_number(val2));
- }
-
- if (val1->type == val2->type) {
-
- if (njs_is_string(val1)) {
- return njs_string_eq(val1, val2);
- }
-
- return (njs_object(val1) == njs_object(val2));
- }
-
- /* Sort values as: numeric < string < objects. */
-
- if (val1->type > val2->type) {
- hv = val1;
- lv = val2;
-
- } else {
- hv = val2;
- lv = val1;
- }
-
- /* If "lv" is an object then "hv" can only be another object. */
- if (njs_is_object(lv)) {
- return 0;
- }
-
- /* If "hv" is a string then "lv" can only be a numeric. */
- if (njs_is_string(hv)) {
- return (njs_number(lv) == njs_string_to_number(hv, 0));
- }
-
- /* "hv" is an object and "lv" is either a string or a numeric. */
-
- ret = njs_value_to_primitive(vm, &primitive, hv, 0);
- if (ret != NXT_OK) {
- return ret;
- }
-
- val1 = &primitive;
- val2 = lv;
-
- goto again;
-}
-
-
-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;
-
- if (nxt_slow_path(!njs_is_primitive(*val1))) {
- ret = njs_value_to_primitive(vm, primitive1, *val1, 0);
- if (ret != NXT_OK) {
- return ret;
- }
-
- *val1 = primitive1;
- }
-
- if (nxt_slow_path(!njs_is_primitive(*val2))) {
- ret = njs_value_to_primitive(vm, primitive2, *val2, 0);
- if (ret != NXT_OK) {
- return ret;
- }
-
- *val2 = primitive2;
- }
-
- 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;
- }
-
- 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)
-{
- 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)
-{
- 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;
- 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, val1, val2);
-
- njs_set_boolean(&vm->retval, ret == 0);
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-/*
- * ECMAScript 5.1: 11.8.5
- * 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.
- */
-
-static njs_ret_t
-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_numeric(val1))) {
- num1 = njs_number(val1);
-
- if (nxt_fast_path(njs_is_numeric(val2))) {
- num2 = njs_number(val2);
-
- } else {
- num2 = njs_string_to_number(val2, 0);
- }
-
- } 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;
- }
-
- /* NaN and void values are not comparable with anything. */
- if (isnan(num1) || isnan(num2)) {
- return -1;
- }
-
- /* 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)
-{
- njs_set_boolean(&vm->retval, njs_values_strict_equal(val1, val2));
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
-{
- njs_set_boolean(&vm->retval, !njs_values_strict_equal(val1, val2));
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- vm->retval = *value;
-
- njs_retain(value);
-
- return sizeof(njs_vmcode_move_t);
-}
-
-
-njs_ret_t
-njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
-{
- return (njs_ret_t) offset;
-}
-
-
-njs_ret_t
-njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
-{
- if (njs_is_true(cond)) {
- return (njs_ret_t) offset;
- }
-
- return sizeof(njs_vmcode_cond_jump_t);
-}
-
-
-njs_ret_t
-njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
-{
- if (njs_is_true(cond)) {
- return sizeof(njs_vmcode_cond_jump_t);
- }
-
- return (njs_ret_t) offset;
-}
-
-
-njs_ret_t
-njs_vmcode_if_equal_jump(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
-{
- njs_vmcode_equal_jump_t *jump;
-
- if (njs_values_strict_equal(val1, val2)) {
- jump = (njs_vmcode_equal_jump_t *) vm->current;
- return jump->offset;
- }
-
- return sizeof(njs_vmcode_3addr_t);
-}
-
-
-njs_ret_t
-njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *value, njs_value_t *nargs)
-{
- njs_ret_t ret;
- njs_vmcode_function_frame_t *function;
-
- function = (njs_vmcode_function_frame_t *) vm->current;
-
- /* TODO: external object instead of void this. */
-
- ret = njs_function_frame_create(vm, value, &njs_value_undefined,
- (uintptr_t) nargs, function->code.ctor);
-
- if (nxt_fast_path(ret == NXT_OK)) {
- return sizeof(njs_vmcode_function_frame_t);
- }
-
- return ret;
-}
-
-
-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)
-{
- njs_value_t val;
- njs_object_t *object;
- njs_function_t *function;
-
- if (nxt_fast_path(njs_is_function(value))) {
-
- function = njs_function(value);
-
- if (ctor) {
- if (!function->ctor) {
- njs_type_error(vm, "%s is not a constructor",
- njs_type_string(value->type));
- return NXT_ERROR;
- }
-
- if (!function->native) {
- object = njs_function_new_object(vm, value);
- if (nxt_slow_path(object == NULL)) {
- return NXT_ERROR;
- }
-
- njs_set_object(&val, object);
- this = &val;
- }
- }
-
- return njs_function_frame(vm, function, this, NULL, nargs, ctor);
- }
-
- njs_type_error(vm, "%s is not a function", njs_type_string(value->type));
-
- return NXT_ERROR;
-}
-
-
-static njs_object_t *
-njs_function_new_object(njs_vm_t *vm, njs_value_t *value)
-{
- nxt_int_t ret;
- njs_value_t *proto;
- njs_object_t *object;
- njs_function_t *function;
- njs_object_prop_t *prop;
- nxt_lvlhsh_query_t lhq;
-
- object = njs_object_alloc(vm);
-
- if (nxt_fast_path(object != NULL)) {
-
- lhq.key_hash = NJS_PROTOTYPE_HASH;
- lhq.key = nxt_string_value("prototype");
- lhq.proto = &njs_object_hash_proto;
- function = njs_function(value);
-
- ret = nxt_lvlhsh_find(&function->object.hash, &lhq);
-
- if (ret == NXT_OK) {
- prop = lhq.value;
- proto = &prop->value;
-
- } else {
- proto = njs_function_property_prototype_create(vm, value);
- }
-
- if (nxt_fast_path(proto != NULL)) {
- object->__proto__ = njs_object(proto);
- return object;
- }
- }
-
- return NULL;
-}
-
-
-njs_ret_t
-njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
-{
- njs_ret_t ret;
- nxt_str_t string;
- njs_value_t *value;
- njs_object_prop_t *prop;
- njs_property_query_t pq;
- njs_vmcode_method_frame_t *method;
-
- value = NULL;
- method = (njs_vmcode_method_frame_t *) vm->current;
-
- njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
-
- ret = njs_property_query(vm, &pq, object, name);
-
- switch (ret) {
-
- case NXT_OK:
- prop = pq.lhq.value;
-
- switch (prop->type) {
- case NJS_PROPERTY:
- case NJS_METHOD:
- break;
-
- case NJS_PROPERTY_HANDLER:
- pq.scratch = *prop;
- prop = &pq.scratch;
- ret = prop->value.data.u.prop_handler(vm, object, NULL,
- &prop->value);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- break;
-
- default:
- njs_internal_error(vm, "unexpected property type \"%s\" "
- "while getting method",
- njs_prop_type_string(prop->type));
-
- return NXT_ERROR;
- }
-
- value = &prop->value;
-
- break;
-
- case NXT_DECLINED:
- break;
-
- case NXT_ERROR:
- default:
-
- return ret;
- }
-
- 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);
- return NXT_ERROR;
- }
-
- ret = njs_function_frame_create(vm, value, object, method->nargs,
- method->code.ctor);
-
- if (nxt_fast_path(ret == NXT_OK)) {
- return sizeof(njs_vmcode_method_frame_t);
- }
-
- return ret;
-}
-
-
-njs_ret_t
-njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
-{
- njs_ret_t ret;
-
- ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
-
- return sizeof(njs_vmcode_function_call_t);
-}
-
-
-njs_ret_t
-njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
-{
- njs_value_t *value;
- njs_frame_t *frame;
- njs_native_frame_t *previous;
-
- value = njs_vmcode_operand(vm, retval);
-
- frame = (njs_frame_t *) vm->top_frame;
-
- if (frame->native.ctor) {
- if (njs_is_object(value)) {
- njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
-
- } else {
- value = vm->scopes[NJS_SCOPE_ARGUMENTS];
- }
- }
-
- vm->current = frame->return_address;
-
- previous = njs_function_previous_frame(&frame->native);
-
- njs_vm_scopes_restore(vm, frame, previous);
-
- /*
- * If a retval is in a callee arguments scope it
- * must be in the previous callee arguments scope.
- */
- retval = njs_vmcode_operand(vm, frame->retval);
-
- /* GC: value external/internal++ depending on value and retval type */
- *retval = *value;
-
- njs_function_frame_free(vm, &frame->native);
-
- return NJS_STOP;
-}
+const nxt_str_t njs_entry_main = nxt_string("main");
+const nxt_str_t njs_entry_module = nxt_string("module");
+const nxt_str_t njs_entry_native = nxt_string("native");
+const nxt_str_t njs_entry_unknown = nxt_string("unknown");
+const nxt_str_t njs_entry_anonymous = nxt_string("anonymous");
void
}
-njs_ret_t
-njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
-{
- njs_value_t *value;
-
- value = njs_vmcode_operand(vm, retval);
-
- vm->retval = *value;
-
- return NJS_STOP;
-}
-
-
-/*
- * njs_vmcode_try_start() is set on the start of a "try" block to create
- * a "try" block, to set a catch address to the start of a "catch" or
- * "finally" blocks and to initialize a value to track uncaught exception.
- */
-
-njs_ret_t
-njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *exception_value,
- njs_value_t *offset)
-{
- njs_value_t *exit_value;
- njs_exception_t *e;
- njs_vmcode_try_start_t *try_start;
-
- if (vm->top_frame->exception.catch != NULL) {
- e = nxt_mp_alloc(vm->mem_pool, sizeof(njs_exception_t));
- if (nxt_slow_path(e == NULL)) {
- njs_memory_error(vm);
- return NXT_ERROR;
- }
-
- *e = vm->top_frame->exception;
- vm->top_frame->exception.next = e;
- }
-
- vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
-
- njs_set_invalid(exception_value);
-
- try_start = (njs_vmcode_try_start_t *) vm->current;
- exit_value = njs_vmcode_operand(vm, try_start->exit_value);
-
- njs_set_invalid(exit_value);
- njs_number(exit_value) = 0;
-
- return sizeof(njs_vmcode_try_start_t);
-}
-
-
-/*
- * njs_vmcode_try_break() sets exit_value to INVALID 1, and jumps to
- * the nearest try_end block. The exit_value is checked by njs_vmcode_finally().
- */
-
-njs_ret_t
-njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *exit_value,
- njs_value_t *offset)
-{
- /* exit_value can contain valid value set by vmcode_try_return. */
- if (!njs_is_valid(exit_value)) {
- njs_number(exit_value) = 1;
- }
-
- return (njs_ret_t) offset;
-}
-
-
-/*
- * njs_vmcode_try_continue() sets exit_value to INVALID -1, and jumps to
- * the nearest try_end block. The exit_value is checked by njs_vmcode_finally().
- */
-
-njs_ret_t
-njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *exit_value,
- njs_value_t *offset)
-{
- njs_number(exit_value) = -1;
-
- return (njs_ret_t) offset;
-}
-
-/*
- * njs_vmcode_try_return() saves a return value to use it later by
- * njs_vmcode_finally(), and jumps to the nearest try_break block.
- */
-
-njs_ret_t
-njs_vmcode_try_return(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
-{
- njs_vmcode_try_return_t *try_return;
-
- vm->retval = *value;
-
- njs_retain(value);
-
- try_return = (njs_vmcode_try_return_t *) vm->current;
-
- return try_return->offset;
-}
-
-
-/*
- * njs_vmcode_try_end() is set on the end of a "try" block to remove the block.
- * It is also set on the end of a "catch" block followed by a "finally" block.
- */
-
-njs_ret_t
-njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
-{
- njs_exception_t *e;
-
- e = vm->top_frame->exception.next;
-
- if (e == NULL) {
- vm->top_frame->exception.catch = NULL;
-
- } else {
- vm->top_frame->exception = *e;
- nxt_mp_free(vm->mem_pool, e);
- }
-
- return (njs_ret_t) offset;
-}
-
-
-njs_ret_t
-njs_vmcode_throw(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
-{
- njs_value_t *value;
-
- value = njs_vmcode_operand(vm, retval);
-
- vm->retval = *value;
-
- return NXT_ERROR;
-}
-
-
-/*
- * njs_vmcode_catch() is set on the start of a "catch" block to store
- * exception and to remove a "try" block if there is no "finally" block
- * or to update a catch address to the start of a "finally" block.
- * njs_vmcode_catch() is set on the start of a "finally" block to store
- * uncaught exception and to remove a "try" block.
- */
-
-njs_ret_t
-njs_vmcode_catch(njs_vm_t *vm, njs_value_t *exception, njs_value_t *offset)
-{
- *exception = vm->retval;
-
- if ((njs_ret_t) offset == sizeof(njs_vmcode_catch_t)) {
- return njs_vmcode_try_end(vm, exception, offset);
- }
-
- vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
-
- return sizeof(njs_vmcode_catch_t);
-}
-
-
-/*
- * njs_vmcode_finally() is set on the end of a "finally" or a "catch" block.
- * 1) to throw uncaught exception.
- * 2) to make a jump to an enslosing loop exit if "continue" or "break"
- * statement was used inside try block.
- * 3) to finalize "return" instruction from "try" block.
- */
-
-njs_ret_t
-njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
-{
- njs_value_t *exception_value, *exit_value;
- njs_vmcode_finally_t *finally;
-
- exception_value = njs_vmcode_operand(vm, retval);
-
- if (njs_is_valid(exception_value)) {
- vm->retval = *exception_value;
-
- return NXT_ERROR;
- }
-
- finally = (njs_vmcode_finally_t *) vm->current;
- exit_value = njs_vmcode_operand(vm, finally->exit_value);
-
- /*
- * exit_value is set by:
- * vmcode_try_start to INVALID 0
- * vmcode_try_break to INVALID 1
- * vmcode_try_continue to INVALID -1
- * vmcode_try_return to a valid return value
- */
-
- if (njs_is_valid(exit_value)) {
- return njs_vmcode_return(vm, NULL, exit_value);
-
- } else if (njs_number(exit_value) != 0) {
- return (njs_ret_t) (njs_number(exit_value) > 0)
- ? finally->break_offset
- : finally->continue_offset;
- }
-
- return sizeof(njs_vmcode_finally_t);
-}
-
-
-njs_ret_t
-njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1,
- njs_value_t *invld2)
-{
- nxt_str_t *file;
- njs_vmcode_reference_error_t *ref_err;
-
- ref_err = (njs_vmcode_reference_error_t *) vm->current;
-
- file = &ref_err->file;
-
- if (file->length != 0 && !vm->options.quiet) {
- njs_reference_error(vm, "\"%V\" is not defined in %V:%uD",
- &ref_err->name, file, ref_err->token_line);
-
- } else {
- njs_reference_error(vm, "\"%V\" is not defined in %uD", &ref_err->name,
- ref_err->token_line);
- }
-
- return NJS_ERROR;
-}
-
-
njs_ret_t
njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
{
}
-static njs_ret_t
+njs_ret_t
njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame)
{
nxt_int_t ret;
}
-void
-njs_debug(njs_index_t index, njs_value_t *value)
-{
-#if (NXT_DEBUG)
- u_char *p;
- uint32_t length;
-
- switch (value->type) {
-
- case NJS_NULL:
- nxt_thread_log_debug("%p [null]", index);
- return;
-
- case NJS_UNDEFINED:
- nxt_thread_log_debug("%p [void]", index);
- return;
-
- case NJS_BOOLEAN:
- nxt_thread_log_debug("%p [%s]", index,
- (njs_number(value) == 0.0) ? "false" : "true");
- return;
-
- case NJS_NUMBER:
- nxt_thread_log_debug("%p [%f]", index, njs_number(value));
- return;
-
- case NJS_STRING:
- length = value->short_string.size;
- if (length != NJS_STRING_LONG) {
- p = value->short_string.start;
-
- } else {
- length = value->long_string.size;
- p = value->long_string.data->start;
- }
-
- nxt_thread_log_debug("%p [\"%*s\"]", index, length, p);
- return;
-
- case NJS_ARRAY:
- nxt_thread_log_debug("%p [array]", index);
- return;
-
- default:
- nxt_thread_log_debug("%p [invalid]", index);
- return;
- }
-#endif
-}
-
-
void *
njs_lvlhsh_alloc(void *data, size_t size)
{
{
nxt_mp_free(data, p);
}
-
} njs_backtrace_entry_t;
-typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
- njs_value_t *value2);
-
-
-#define NJS_VMCODE_3OPERANDS 0
-#define NJS_VMCODE_2OPERANDS 1
-#define NJS_VMCODE_1OPERAND 2
-#define NJS_VMCODE_NO_OPERAND 3
-
-#define NJS_VMCODE_NO_RETVAL 0
-#define NJS_VMCODE_RETVAL 1
-
-
-typedef struct {
- njs_vmcode_operation_t operation;
- uint8_t operands; /* 2 bits */
- uint8_t retval; /* 1 bit */
- uint8_t ctor; /* 1 bit */
-} njs_vmcode_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t operand1;
- njs_index_t operand2;
- njs_index_t operand3;
-} njs_vmcode_generic_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t index;
-} njs_vmcode_1addr_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t dst;
- njs_index_t src;
-} njs_vmcode_2addr_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t dst;
- njs_index_t src1;
- njs_index_t src2;
-} njs_vmcode_3addr_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t dst;
- njs_index_t src;
-} njs_vmcode_move_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
-} njs_vmcode_object_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t dst;
-} njs_vmcode_this_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t dst;
-} njs_vmcode_arguments_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- uintptr_t length;
-} njs_vmcode_array_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
-} njs_vmcode_template_literal_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- njs_function_lambda_t *lambda;
-} njs_vmcode_function_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- njs_regexp_pattern_t *pattern;
-} njs_vmcode_regexp_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- njs_index_t object;
-} njs_vmcode_object_copy_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
-} njs_vmcode_jump_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
- njs_index_t cond;
-} njs_vmcode_cond_jump_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
- njs_index_t value1;
- njs_index_t value2;
-} njs_vmcode_equal_jump_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- njs_index_t value;
- njs_ret_t offset;
-} njs_vmcode_test_jump_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t value;
- njs_index_t object;
- njs_index_t property;
-} njs_vmcode_prop_get_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t value;
- njs_index_t object;
- njs_index_t property;
-} njs_vmcode_prop_set_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t next;
- njs_index_t object;
- njs_ret_t offset;
-} njs_vmcode_prop_foreach_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- njs_index_t object;
- njs_index_t next;
- njs_ret_t offset;
-} njs_vmcode_prop_next_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t value;
- njs_index_t constructor;
- njs_index_t object;
-} njs_vmcode_instance_of_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t nargs;
- njs_index_t name;
-} njs_vmcode_function_frame_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t nargs;
- njs_index_t object;
- njs_index_t method;
-} njs_vmcode_method_frame_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
-} njs_vmcode_function_call_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
-} njs_vmcode_return_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
-} njs_vmcode_stop_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
- njs_index_t exception_value;
- njs_index_t exit_value;
-} njs_vmcode_try_start_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
- njs_index_t exit_value;
-} njs_vmcode_try_trampoline_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
- njs_index_t exception;
-} njs_vmcode_catch_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
-} njs_vmcode_throw_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_ret_t offset;
-} njs_vmcode_try_end_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t save;
- njs_index_t retval;
- njs_ret_t offset;
-} njs_vmcode_try_return_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- njs_index_t retval;
- njs_index_t exit_value;
- njs_ret_t continue_offset;
- njs_ret_t break_offset;
-} njs_vmcode_finally_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- nxt_str_t name;
- nxt_str_t file;
- uint32_t token_line;
-} njs_vmcode_reference_error_t;
-
-
typedef enum {
NJS_SCOPE_ABSOLUTE = 0,
NJS_SCOPE_GLOBAL = 1,
};
-nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm);
-nxt_int_t njs_vmcode_run(njs_vm_t *vm);
void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
njs_native_frame_t *previous);
-
-njs_ret_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *inlvd2);
-njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *inlvd2);
-njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *invld2);
-njs_ret_t njs_vmcode_this(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *invld2);
-njs_ret_t njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *invld2);
-njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *invld2);
-njs_ret_t njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *inlvd1,
- njs_value_t *inlvd2);
-njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *invld);
-
-njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property);
-njs_ret_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property);
-njs_ret_t njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property);
-njs_ret_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property,
- njs_value_t *object);
-njs_ret_t njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *property);
-njs_ret_t njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *invld);
-njs_ret_t njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *value);
-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 *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,
- njs_value_t *invld2);
-njs_ret_t 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);
-njs_ret_t njs_vmcode_unary_negation(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *invld);
-njs_ret_t njs_vmcode_addition(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);
-njs_ret_t njs_vmcode_multiplication(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_exponentiation(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *inlvd);
-njs_ret_t njs_vmcode_test_if_true(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *invld);
-njs_ret_t 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 *inlvd);
-njs_ret_t njs_vmcode_bitwise_and(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_bitwise_xor(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_bitwise_or(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_left_shift(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_right_shift(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t 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 njs_vmcode_less(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2);
-njs_ret_t njs_vmcode_greater(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_less_or_equal(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t njs_vmcode_greater_or_equal(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-njs_ret_t 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);
-
-njs_ret_t njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld);
-
-njs_ret_t njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_if_equal_jump(njs_vm_t *vm, njs_value_t *val1,
- njs_value_t *val2);
-
-njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *nargs);
-njs_ret_t njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object,
- njs_value_t *method);
-njs_ret_t 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);
-njs_ret_t njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld,
- njs_value_t *retval);
-
-njs_ret_t njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_try_return(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld,
- njs_value_t *offset);
-njs_ret_t njs_vmcode_throw(njs_vm_t *vm, njs_value_t *invld,
- njs_value_t *retval);
-njs_ret_t njs_vmcode_catch(njs_vm_t *vm, njs_value_t *invld,
- njs_value_t *exception);
-njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
- njs_value_t *retval);
-njs_ret_t njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1,
- njs_value_t *invld2);
+njs_ret_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame);
nxt_int_t njs_builtin_objects_create(njs_vm_t *vm);
nxt_int_t njs_builtin_objects_clone(njs_vm_t *vm);
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <njs_core.h>
+#include <njs_regexp.h>
+#include <string.h>
+
+
+struct njs_property_next_s {
+ uint32_t index;
+ njs_array_t *array;
+};
+
+
+/*
+ * These functions are forbidden to inline to minimize JavaScript VM
+ * interpreter memory footprint. The size is less than 8K on AMD64
+ * and should fit in CPU L1 instruction cache.
+ */
+
+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, 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);
+
+/*
+ * The nJSVM is optimized for an ABIs where the first several arguments
+ * are passed in registers (AMD64, ARM32/64): two pointers to the operand
+ * values is passed as arguments although they are not always used.
+ */
+
+nxt_int_t
+njs_vmcode_interpreter(njs_vm_t *vm)
+{
+ u_char *catch, call;
+ njs_ret_t ret;
+ njs_value_t *retval, *value1, *value2;
+ njs_frame_t *frame;
+ njs_native_frame_t *previous;
+ njs_vmcode_generic_t *vmcode;
+
+start:
+
+ for ( ;; ) {
+
+ vmcode = (njs_vmcode_generic_t *) vm->current;
+
+ /*
+ * The first operand is passed as is in value2 to
+ * njs_vmcode_jump(),
+ * njs_vmcode_if_true_jump(),
+ * njs_vmcode_if_false_jump(),
+ * njs_vmcode_validate(),
+ * njs_vmcode_function_frame(),
+ * njs_vmcode_function_call(),
+ * njs_vmcode_return(),
+ * njs_vmcode_try_start(),
+ * njs_vmcode_try_continue(),
+ * njs_vmcode_try_break(),
+ * njs_vmcode_try_end(),
+ * njs_vmcode_catch().
+ * njs_vmcode_throw().
+ * njs_vmcode_stop().
+ */
+ value2 = (njs_value_t *) vmcode->operand1;
+ value1 = NULL;
+
+ switch (vmcode->code.operands) {
+
+ case NJS_VMCODE_3OPERANDS:
+ value2 = njs_vmcode_operand(vm, vmcode->operand3);
+
+ /* Fall through. */
+
+ case NJS_VMCODE_2OPERANDS:
+ value1 = njs_vmcode_operand(vm, vmcode->operand2);
+ }
+
+ ret = vmcode->code.operation(vm, value1, value2);
+
+ /*
+ * On success an operation returns size of the bytecode,
+ * a jump offset or zero after the call or return operations.
+ * Jumps can return a negative offset. Compilers can generate
+ * (ret < 0 && ret >= NJS_PREEMPT)
+ * as a single unsigned comparision.
+ */
+
+ if (nxt_slow_path(ret < 0 && ret >= NJS_PREEMPT)) {
+ break;
+ }
+
+ vm->current += ret;
+
+ if (vmcode->code.retval) {
+ retval = njs_vmcode_operand(vm, vmcode->operand1);
+ njs_release(vm, retval);
+ *retval = vm->retval;
+ }
+ }
+
+ if (ret == NXT_ERROR) {
+
+ for ( ;; ) {
+ frame = (njs_frame_t *) vm->top_frame;
+
+ call = frame->native.call;
+ catch = frame->native.exception.catch;
+
+ if (catch != NULL) {
+ vm->current = catch;
+
+ if (vm->debug != NULL) {
+ nxt_array_reset(vm->backtrace);
+ }
+
+ goto start;
+ }
+
+ if (vm->debug != NULL
+ && njs_vm_add_backtrace_entry(vm, frame) != NXT_OK)
+ {
+ return NXT_ERROR;
+ }
+
+ previous = frame->native.previous;
+ if (previous == NULL) {
+ return NXT_ERROR;
+ }
+
+ njs_vm_scopes_restore(vm, frame, previous);
+
+ if (frame->native.size != 0) {
+ vm->stack_size -= frame->native.size;
+ nxt_mp_free(vm->mem_pool, frame);
+ }
+
+ if (call) {
+ return NXT_ERROR;
+ }
+ }
+ }
+
+ /* NXT_ERROR, NJS_STOP. */
+
+ return ret;
+}
+
+
+nxt_int_t
+njs_vmcode_run(njs_vm_t *vm)
+{
+ njs_ret_t ret;
+
+ vm->top_frame->call = 1;
+
+ 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;
+}
+
+
+njs_ret_t
+njs_vmcode_object(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_object_t *object;
+
+ object = njs_object_alloc(vm);
+
+ if (nxt_fast_path(object != NULL)) {
+ njs_set_object(&vm->retval, object);
+
+ return sizeof(njs_vmcode_object_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_array(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ uint32_t length;
+ njs_array_t *array;
+ njs_value_t *value;
+ njs_vmcode_array_t *code;
+
+ code = (njs_vmcode_array_t *) vm->current;
+
+ array = njs_array_alloc(vm, code->length, NJS_ARRAY_SPARE);
+
+ if (nxt_fast_path(array != NULL)) {
+
+ if (code->code.ctor) {
+ /* Array of the form [,,,], [1,,]. */
+ value = array->start;
+ length = array->length;
+
+ do {
+ njs_set_invalid(value);
+ value++;
+ length--;
+ } while (length != 0);
+
+ } else {
+ /* Array of the form [], [,,1], [1,2,3]. */
+ array->length = 0;
+ }
+
+ njs_set_array(&vm->retval, array);
+
+ return sizeof(njs_vmcode_array_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_function_t *function;
+ njs_function_lambda_t *lambda;
+ njs_vmcode_function_t *code;
+
+ code = (njs_vmcode_function_t *) vm->current;
+ lambda = code->lambda;
+
+ function = njs_function_alloc(vm, lambda, vm->active_frame->closures, 0);
+ if (nxt_slow_path(function == NULL)) {
+ return NXT_ERROR;
+ }
+
+ njs_set_function(&vm->retval, function);
+
+ return sizeof(njs_vmcode_function_t);
+}
+
+
+njs_ret_t
+njs_vmcode_this(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_frame_t *frame;
+ njs_value_t *value;
+ njs_vmcode_this_t *code;
+
+ frame = (njs_frame_t *) vm->active_frame;
+ code = (njs_vmcode_this_t *) vm->current;
+
+ value = njs_vmcode_operand(vm, code->dst);
+ *value = frame->native.arguments[0];
+
+ return sizeof(njs_vmcode_this_t);
+}
+
+
+njs_ret_t
+njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ nxt_int_t ret;
+ njs_frame_t *frame;
+ njs_value_t *value;
+ njs_vmcode_arguments_t *code;
+
+ frame = (njs_frame_t *) vm->active_frame;
+
+ if (frame->native.arguments_object == NULL) {
+ ret = njs_function_arguments_object_init(vm, &frame->native);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ code = (njs_vmcode_arguments_t *) vm->current;
+
+ value = njs_vmcode_operand(vm, code->dst);
+ njs_set_object(value, frame->native.arguments_object);
+
+ return sizeof(njs_vmcode_arguments_t);
+}
+
+
+njs_ret_t
+njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_regexp_t *regexp;
+ njs_vmcode_regexp_t *code;
+
+ code = (njs_vmcode_regexp_t *) vm->current;
+
+ regexp = njs_regexp_alloc(vm, code->pattern);
+
+ if (nxt_fast_path(regexp != NULL)) {
+ njs_set_regexp(&vm->retval, regexp);
+
+ return sizeof(njs_vmcode_regexp_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *invld1,
+ njs_value_t *retval)
+{
+ nxt_int_t ret;
+ njs_array_t *array;
+ njs_value_t *value;
+
+ static const njs_function_t concat = {
+ .native = 1,
+ .args_offset = 1,
+ .u.native = njs_string_prototype_concat
+ };
+
+ value = njs_vmcode_operand(vm, retval);
+
+ if (!njs_is_primitive(value)) {
+ array = njs_array(value);
+
+ ret = njs_function_frame(vm, (njs_function_t *) &concat,
+ (njs_value_t *) &njs_string_empty,
+ array->start, array->length, 0);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ return sizeof(njs_vmcode_template_literal_t);
+}
+
+
+njs_ret_t
+njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_object_t *object;
+ njs_function_t *function;
+
+ switch (value->type) {
+
+ case NJS_OBJECT:
+ object = njs_object_value_copy(vm, value);
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ break;
+
+ case NJS_FUNCTION:
+ function = njs_function_value_copy(vm, value);
+ if (nxt_slow_path(function == NULL)) {
+ return NXT_ERROR;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ vm->retval = *value;
+
+ njs_retain(value);
+
+ return sizeof(njs_vmcode_object_copy_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ njs_ret_t ret;
+ njs_value_t *retval;
+ njs_vmcode_prop_get_t *code;
+
+ code = (njs_vmcode_prop_get_t *) vm->current;
+ retval = njs_vmcode_operand(vm, code->value);
+
+ ret = njs_value_property(vm, object, property, retval);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_prop_get_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ uint32_t index, size;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_value_t *init, *value, name;
+ njs_object_t *obj;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+ njs_vmcode_prop_set_t *code;
+
+ code = (njs_vmcode_prop_set_t *) vm->current;
+ init = njs_vmcode_operand(vm, code->value);
+
+ switch (object->type) {
+ case NJS_ARRAY:
+ index = njs_value_to_index(property);
+ if (nxt_slow_path(index == NJS_ARRAY_INVALID_INDEX)) {
+ njs_internal_error(vm,
+ "invalid index while property initialization");
+ return NXT_ERROR;
+ }
+
+ array = object->data.u.array;
+
+ if (index >= array->length) {
+ size = index - array->length;
+
+ ret = njs_array_expand(vm, array, 0, size + 1);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ value = &array->start[array->length];
+
+ while (size != 0) {
+ njs_set_invalid(value);
+ value++;
+ size--;
+ }
+
+ array->length = index + 1;
+ }
+
+ /* GC: retain. */
+ array->start[index] = *init;
+
+ break;
+
+ case NJS_OBJECT:
+ ret = njs_value_to_string(vm, &name, property);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ njs_string_get(&name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.proto = &njs_object_hash_proto;
+ lhq.pool = vm->mem_pool;
+
+ obj = njs_object(object);
+
+ ret = nxt_lvlhsh_find(&obj->__proto__->shared_hash, &lhq);
+ if (ret == NXT_OK) {
+ prop = lhq.value;
+
+ if (prop->type == NJS_PROPERTY_HANDLER) {
+ ret = prop->value.data.u.prop_handler(vm, object, init,
+ &vm->retval);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ break;
+ }
+ }
+
+ prop = njs_object_prop_alloc(vm, &name, init, 1);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ lhq.value = prop;
+ lhq.replace = 1;
+
+ ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "lvlhsh insert/replace failed");
+ return NXT_ERROR;
+ }
+
+ break;
+
+ default:
+ njs_internal_error(vm, "unexpected object type \"%s\" "
+ "while property initialization",
+ njs_type_string(object->type));
+
+ return NXT_ERROR;
+ }
+
+ return sizeof(njs_vmcode_prop_set_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ njs_ret_t ret;
+ njs_value_t *value;
+ njs_vmcode_prop_set_t *code;
+
+ code = (njs_vmcode_prop_set_t *) vm->current;
+ value = njs_vmcode_operand(vm, code->value);
+
+ ret = njs_value_property_set(vm, object, property, value);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ return sizeof(njs_vmcode_prop_set_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property)
+{
+ njs_ret_t ret;
+ njs_object_prop_t *prop;
+ const njs_value_t *retval;
+ njs_property_query_t pq;
+
+ retval = &njs_value_false;
+
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
+
+ ret = njs_property_query(vm, &pq, object, property);
+
+ switch (ret) {
+
+ case NXT_OK:
+ prop = pq.lhq.value;
+
+ if (!njs_is_valid(&prop->value)) {
+ break;
+ }
+
+ retval = &njs_value_true;
+ break;
+
+ case NXT_DECLINED:
+ if (!njs_is_object(object) && !njs_is_external(object)) {
+ njs_type_error(vm, "property in on a primitive value");
+
+ return NXT_ERROR;
+ }
+
+ break;
+
+ case NXT_ERROR:
+ default:
+
+ return ret;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ njs_ret_t ret;
+ njs_object_prop_t *prop, *whipeout;
+ njs_property_query_t pq;
+
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 1);
+
+ ret = njs_property_query(vm, &pq, object, property);
+
+ switch (ret) {
+
+ case NXT_OK:
+ prop = pq.lhq.value;
+
+ if (nxt_slow_path(!prop->configurable)) {
+ njs_type_error(vm, "Cannot delete property \"%V\" of %s",
+ &pq.lhq.key, njs_type_string(object->type));
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(pq.shared)) {
+ whipeout = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t),
+ sizeof(njs_object_prop_t));
+ if (nxt_slow_path(whipeout == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ njs_set_invalid(&whipeout->value);
+ whipeout->name = prop->name;
+ whipeout->type = NJS_WHITEOUT;
+
+ pq.lhq.replace = 0;
+ pq.lhq.value = whipeout;
+ pq.lhq.pool = vm->mem_pool;
+
+ ret = nxt_lvlhsh_insert(&pq.prototype->hash, &pq.lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "lvlhsh insert failed");
+ return NXT_ERROR;
+ }
+
+ break;
+ }
+
+ switch (prop->type) {
+ case NJS_PROPERTY:
+ case NJS_METHOD:
+ break;
+
+ case NJS_PROPERTY_REF:
+ njs_set_invalid(prop->value.data.u.value);
+ goto done;
+
+ case NJS_PROPERTY_HANDLER:
+ ret = prop->value.data.u.prop_handler(vm, object, NULL, NULL);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ goto done;
+
+ default:
+ njs_internal_error(vm, "unexpected property type \"%s\" "
+ "while deleting",
+ njs_prop_type_string(prop->type));
+
+ return NXT_ERROR;
+ }
+
+ /* GC: release value. */
+ prop->type = NJS_WHITEOUT;
+ njs_set_invalid(&prop->value);
+
+ break;
+
+ case NXT_DECLINED:
+ break;
+
+ case NXT_ERROR:
+ default:
+
+ return ret;
+ }
+
+done:
+
+ vm->retval = njs_value_true;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *invld)
+{
+ void *obj;
+ njs_ret_t ret;
+ njs_property_next_t *next;
+ const njs_extern_t *ext_proto;
+ njs_vmcode_prop_foreach_t *code;
+
+ if (njs_is_external(object)) {
+ ext_proto = object->external.proto;
+
+ if (ext_proto->foreach != NULL) {
+ obj = njs_extern_object(vm, object);
+
+ ret = ext_proto->foreach(vm, obj, &vm->retval);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ goto done;
+ }
+
+ next = nxt_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t));
+ if (nxt_slow_path(next == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ next->index = 0;
+ next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, 0);
+ if (nxt_slow_path(next->array == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.next = next;
+
+done:
+
+ code = (njs_vmcode_prop_foreach_t *) vm->current;
+
+ return code->offset;
+}
+
+
+njs_ret_t
+njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object, njs_value_t *value)
+{
+ void *obj;
+ njs_ret_t ret;
+ njs_value_t *retval;
+ njs_property_next_t *next;
+ const njs_extern_t *ext_proto;
+ njs_vmcode_prop_next_t *code;
+
+ code = (njs_vmcode_prop_next_t *) vm->current;
+ retval = njs_vmcode_operand(vm, code->retval);
+
+ if (njs_is_external(object)) {
+ ext_proto = object->external.proto;
+
+ if (ext_proto->next != NULL) {
+ obj = njs_extern_object(vm, object);
+
+ ret = ext_proto->next(vm, retval, obj, value);
+
+ if (ret == NXT_OK) {
+ return code->offset;
+ }
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ /* ret == NJS_DONE. */
+ }
+
+ return sizeof(njs_vmcode_prop_next_t);
+ }
+
+ next = value->data.u.next;
+
+ if (next->index < next->array->length) {
+ *retval = next->array->data[next->index++];
+
+ return code->offset;
+ }
+
+ nxt_mp_free(vm->mem_pool, next);
+
+ return sizeof(njs_vmcode_prop_next_t);
+}
+
+
+njs_ret_t
+njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *constructor)
+{
+ nxt_int_t ret;
+ njs_value_t value;
+ njs_object_t *prototype, *proto;
+ const njs_value_t *retval;
+
+ static njs_value_t prototype_string = njs_string("prototype");
+
+ if (!njs_is_function(constructor)) {
+ njs_type_error(vm, "right argument is not a function");
+ return NXT_ERROR;
+ }
+
+ retval = &njs_value_false;
+
+ if (njs_is_object(object)) {
+ value = njs_value_undefined;
+ ret = njs_value_property(vm, constructor, &prototype_string, &value);
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+
+ if (nxt_slow_path(!njs_is_object(&value))) {
+ njs_internal_error(vm, "prototype is not an object");
+ return NXT_ERROR;
+ }
+
+ prototype = njs_object(&value);
+ proto = njs_object(object);
+
+ do {
+ proto = proto->__proto__;
+
+ if (proto == prototype) {
+ retval = &njs_value_true;
+ break;
+ }
+
+ } while (proto != NULL);
+ }
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_instance_of_t);
+}
+
+
+njs_ret_t
+njs_vmcode_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_slow_path(!njs_is_numeric(value))) {
+ ret = njs_value_to_numeric(vm, &numeric, value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ num = njs_number(&numeric);
+
+ } else {
+ num = njs_number(value);
+ }
+
+ 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;
+ 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 (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ num = njs_number(&numeric);
+
+ } else {
+ num = njs_number(value);
+ }
+
+ 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_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_slow_path(!njs_is_numeric(value))) {
+ ret = njs_value_to_numeric(vm, &numeric, value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ num = njs_number(&numeric);
+
+ } else {
+ num = njs_number(value);
+ }
+
+ njs_release(vm, reference);
+
+ njs_set_number(reference, num + 1.0);
+ njs_set_number(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value)
+{
+ double num;
+ 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 (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ num = njs_number(&numeric);
+
+ } else {
+ num = njs_number(value);
+ }
+
+ njs_release(vm, reference);
+
+ njs_set_number(reference, num - 1.0);
+ njs_set_number(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ /* ECMAScript 5.1: null, array and regexp are objects. */
+
+ static const njs_value_t *types[NJS_TYPE_MAX] = {
+ &njs_string_object,
+ &njs_string_undefined,
+ &njs_string_boolean,
+ &njs_string_number,
+ &njs_string_string,
+ &njs_string_data,
+ &njs_string_external,
+ &njs_string_invalid,
+ &njs_string_undefined,
+ &njs_string_undefined,
+ &njs_string_undefined,
+ &njs_string_undefined,
+ &njs_string_undefined,
+ &njs_string_undefined,
+ &njs_string_undefined,
+ &njs_string_undefined,
+
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_function,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ };
+
+ vm->retval = *types[value->type];
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_void(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ vm->retval = njs_value_undefined;
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_release(vm, value);
+
+ vm->retval = njs_value_true;
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_unary_plus(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ 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;
+ }
+
+ 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)
+{
+ 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;
+ }
+
+ 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)
+{
+ njs_ret_t ret;
+
+ njs_value_t primitive1, primitive2, dst, *s1, *s2, *src;
+
+ if (nxt_slow_path(!njs_is_primitive(val1))) {
+
+ /*
+ * 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;
+ }
+
+ val1 = &primitive1;
+ }
+
+ if (nxt_slow_path(!njs_is_primitive(val2))) {
+
+ /*
+ * ECMAScript 5.1:
+ * Date should return String, other types sould return Number.
+ */
+
+ ret = njs_value_to_primitive(vm, &primitive2, val2, njs_is_date(val2));
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &primitive2;
+ }
+
+ 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);
+ }
+
+ 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 ret;
+}
+
+
+static njs_ret_t
+njs_string_concat(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ u_char *start;
+ size_t size, length;
+ njs_string_prop_t string1, string2;
+
+ (void) njs_string_prop(&string1, val1);
+ (void) njs_string_prop(&string2, val2);
+
+ /*
+ * A result of concatenation of Byte and ASCII or UTF-8 strings
+ * is a Byte string.
+ */
+ if ((string1.length != 0 || string1.size == 0)
+ && (string2.length != 0 || string2.size == 0))
+ {
+ length = string1.length + string2.length;
+
+ } else {
+ length = 0;
+ }
+
+ size = string1.size + string2.size;
+
+ start = njs_string_alloc(vm, &vm->retval, size, length);
+
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ (void) memcpy(start, string1.start, string1.size);
+ (void) memcpy(start + string1.size, string2.start, string2.size);
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_substraction(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ 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)
+{
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ 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;
+ njs_ret_t ret;
+ nxt_bool_t valid;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ base = njs_number(val1);
+ exponent = njs_number(val2);
+
+ /*
+ * 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;
+ }
+
+ 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)
+{
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ 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;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ 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;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ num1 = njs_number_to_int32(njs_number(val1));
+ num2 = njs_number_to_uint32(njs_number(val2));
+ njs_set_int32(&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;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ num1 = njs_number_to_int32(njs_number(val1));
+ num2 = njs_number_to_uint32(njs_number(val2));
+ njs_set_int32(&vm->retval, num1 >> (num2 & 0x1f));
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2)
+{
+ uint32_t num1, num2;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ num1 = njs_number_to_uint32(njs_number(val1));
+ num2 = njs_number_to_uint32(njs_number(val2));
+ njs_set_uint32(&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)
+{
+ njs_set_boolean(&vm->retval, !njs_is_true(value));
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_test_if_true(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_vmcode_test_jump_t *test_jump;
+
+ vm->retval = *value;
+
+ if (njs_is_true(value)) {
+ test_jump = (njs_vmcode_test_jump_t *) vm->current;
+ return test_jump->offset;
+ }
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_test_if_false(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_vmcode_test_jump_t *test_jump;
+
+ vm->retval = *value;
+
+ if (!njs_is_true(value)) {
+ test_jump = (njs_vmcode_test_jump_t *) vm->current;
+ return test_jump->offset;
+ }
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_bitwise_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ 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;
+ }
+
+ njs_set_int32(&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;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ num1 = njs_number_to_integer(njs_number(val1));
+ num2 = njs_number_to_integer(njs_number(val2));
+ njs_set_int32(&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;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ num1 = njs_number_to_integer(njs_number(val1));
+ num2 = njs_number_to_integer(njs_number(val2));
+ njs_set_int32(&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;
+ njs_ret_t ret;
+ njs_value_t numeric1, numeric2;
+
+ if (nxt_slow_path(!njs_is_numeric(val1))) {
+ ret = njs_value_to_numeric(vm, &numeric1, val1);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &numeric1;
+ }
+
+ if (nxt_slow_path(!njs_is_numeric(val2))) {
+ ret = njs_value_to_numeric(vm, &numeric2, val2);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val2 = &numeric2;
+ }
+
+ num1 = njs_number_to_integer(njs_number(val1));
+ num2 = njs_number_to_integer(njs_number(val2));
+ njs_set_int32(&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;
+
+ ret = njs_values_equal(vm, val1, val2);
+
+ if (nxt_fast_path(ret >= 0)) {
+
+ njs_set_boolean(&vm->retval, ret != 0);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+}
+
+
+njs_ret_t
+njs_vmcode_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_ret_t ret;
+
+ ret = njs_values_equal(vm, val1, val2);
+
+ if (nxt_fast_path(ret >= 0)) {
+
+ njs_set_boolean(&vm->retval, ret == 0);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+}
+
+
+static njs_ret_t
+njs_values_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ 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);
+
+ /* Void and null are equal and not comparable with anything else. */
+ if (nv1 || nv2) {
+ return (nv1 && nv2);
+ }
+
+ if (njs_is_numeric(val1) && njs_is_numeric(val2)) {
+ /* NaNs and Infinities are handled correctly by comparision. */
+ return (njs_number(val1) == njs_number(val2));
+ }
+
+ if (val1->type == val2->type) {
+
+ if (njs_is_string(val1)) {
+ return njs_string_eq(val1, val2);
+ }
+
+ return (njs_object(val1) == njs_object(val2));
+ }
+
+ /* Sort values as: numeric < string < objects. */
+
+ if (val1->type > val2->type) {
+ hv = val1;
+ lv = val2;
+
+ } else {
+ hv = val2;
+ lv = val1;
+ }
+
+ /* If "lv" is an object then "hv" can only be another object. */
+ if (njs_is_object(lv)) {
+ return 0;
+ }
+
+ /* If "hv" is a string then "lv" can only be a numeric. */
+ if (njs_is_string(hv)) {
+ return (njs_number(lv) == njs_string_to_number(hv, 0));
+ }
+
+ /* "hv" is an object and "lv" is either a string or a numeric. */
+
+ ret = njs_value_to_primitive(vm, &primitive, hv, 0);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ val1 = &primitive;
+ val2 = lv;
+
+ goto again;
+}
+
+
+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;
+
+ if (nxt_slow_path(!njs_is_primitive(*val1))) {
+ ret = njs_value_to_primitive(vm, primitive1, *val1, 0);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ *val1 = primitive1;
+ }
+
+ if (nxt_slow_path(!njs_is_primitive(*val2))) {
+ ret = njs_value_to_primitive(vm, primitive2, *val2, 0);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ *val2 = primitive2;
+ }
+
+ 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;
+ }
+
+ 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)
+{
+ 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)
+{
+ 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;
+ 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, val1, val2);
+
+ njs_set_boolean(&vm->retval, ret == 0);
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+/*
+ * ECMAScript 5.1: 11.8.5
+ * 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.
+ */
+
+static njs_ret_t
+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_numeric(val1))) {
+ num1 = njs_number(val1);
+
+ if (nxt_fast_path(njs_is_numeric(val2))) {
+ num2 = njs_number(val2);
+
+ } else {
+ num2 = njs_string_to_number(val2, 0);
+ }
+
+ } 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;
+ }
+
+ /* NaN and void values are not comparable with anything. */
+ if (isnan(num1) || isnan(num2)) {
+ return -1;
+ }
+
+ /* 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)
+{
+ njs_set_boolean(&vm->retval, njs_values_strict_equal(val1, val2));
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_set_boolean(&vm->retval, !njs_values_strict_equal(val1, val2));
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ vm->retval = *value;
+
+ njs_retain(value);
+
+ return sizeof(njs_vmcode_move_t);
+}
+
+
+njs_ret_t
+njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
+{
+ return (njs_ret_t) offset;
+}
+
+
+njs_ret_t
+njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
+{
+ if (njs_is_true(cond)) {
+ return (njs_ret_t) offset;
+ }
+
+ return sizeof(njs_vmcode_cond_jump_t);
+}
+
+
+njs_ret_t
+njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
+{
+ if (njs_is_true(cond)) {
+ return sizeof(njs_vmcode_cond_jump_t);
+ }
+
+ return (njs_ret_t) offset;
+}
+
+
+njs_ret_t
+njs_vmcode_if_equal_jump(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_vmcode_equal_jump_t *jump;
+
+ if (njs_values_strict_equal(val1, val2)) {
+ jump = (njs_vmcode_equal_jump_t *) vm->current;
+ return jump->offset;
+ }
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *value, njs_value_t *nargs)
+{
+ njs_ret_t ret;
+ njs_vmcode_function_frame_t *function;
+
+ function = (njs_vmcode_function_frame_t *) vm->current;
+
+ /* TODO: external object instead of void this. */
+
+ ret = njs_function_frame_create(vm, value, &njs_value_undefined,
+ (uintptr_t) nargs, function->code.ctor);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return sizeof(njs_vmcode_function_frame_t);
+ }
+
+ return ret;
+}
+
+
+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)
+{
+ njs_value_t val;
+ njs_object_t *object;
+ njs_function_t *function;
+
+ if (nxt_fast_path(njs_is_function(value))) {
+
+ function = njs_function(value);
+
+ if (ctor) {
+ if (!function->ctor) {
+ njs_type_error(vm, "%s is not a constructor",
+ njs_type_string(value->type));
+ return NXT_ERROR;
+ }
+
+ if (!function->native) {
+ object = njs_function_new_object(vm, value);
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ njs_set_object(&val, object);
+ this = &val;
+ }
+ }
+
+ return njs_function_frame(vm, function, this, NULL, nargs, ctor);
+ }
+
+ njs_type_error(vm, "%s is not a function", njs_type_string(value->type));
+
+ return NXT_ERROR;
+}
+
+
+static njs_object_t *
+njs_function_new_object(njs_vm_t *vm, njs_value_t *value)
+{
+ nxt_int_t ret;
+ njs_value_t *proto;
+ njs_object_t *object;
+ njs_function_t *function;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ object = njs_object_alloc(vm);
+
+ if (nxt_fast_path(object != NULL)) {
+
+ lhq.key_hash = NJS_PROTOTYPE_HASH;
+ lhq.key = nxt_string_value("prototype");
+ lhq.proto = &njs_object_hash_proto;
+ function = njs_function(value);
+
+ ret = nxt_lvlhsh_find(&function->object.hash, &lhq);
+
+ if (ret == NXT_OK) {
+ prop = lhq.value;
+ proto = &prop->value;
+
+ } else {
+ proto = njs_function_property_prototype_create(vm, value);
+ }
+
+ if (nxt_fast_path(proto != NULL)) {
+ object->__proto__ = njs_object(proto);
+ return object;
+ }
+ }
+
+ return NULL;
+}
+
+
+njs_ret_t
+njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
+{
+ njs_ret_t ret;
+ nxt_str_t string;
+ njs_value_t *value;
+ njs_object_prop_t *prop;
+ njs_property_query_t pq;
+ njs_vmcode_method_frame_t *method;
+
+ value = NULL;
+ method = (njs_vmcode_method_frame_t *) vm->current;
+
+ njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
+
+ ret = njs_property_query(vm, &pq, object, name);
+
+ switch (ret) {
+
+ case NXT_OK:
+ prop = pq.lhq.value;
+
+ switch (prop->type) {
+ case NJS_PROPERTY:
+ case NJS_METHOD:
+ break;
+
+ case NJS_PROPERTY_HANDLER:
+ pq.scratch = *prop;
+ prop = &pq.scratch;
+ ret = prop->value.data.u.prop_handler(vm, object, NULL,
+ &prop->value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ break;
+
+ default:
+ njs_internal_error(vm, "unexpected property type \"%s\" "
+ "while getting method",
+ njs_prop_type_string(prop->type));
+
+ return NXT_ERROR;
+ }
+
+ value = &prop->value;
+
+ break;
+
+ case NXT_DECLINED:
+ break;
+
+ case NXT_ERROR:
+ default:
+
+ return ret;
+ }
+
+ 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);
+ return NXT_ERROR;
+ }
+
+ ret = njs_function_frame_create(vm, value, object, method->nargs,
+ method->code.ctor);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return sizeof(njs_vmcode_method_frame_t);
+ }
+
+ return ret;
+}
+
+
+njs_ret_t
+njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_ret_t ret;
+
+ ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ return sizeof(njs_vmcode_function_call_t);
+}
+
+
+njs_ret_t
+njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+ njs_frame_t *frame;
+ njs_native_frame_t *previous;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ frame = (njs_frame_t *) vm->top_frame;
+
+ if (frame->native.ctor) {
+ if (njs_is_object(value)) {
+ njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
+
+ } else {
+ value = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ }
+ }
+
+ vm->current = frame->return_address;
+
+ previous = njs_function_previous_frame(&frame->native);
+
+ njs_vm_scopes_restore(vm, frame, previous);
+
+ /*
+ * If a retval is in a callee arguments scope it
+ * must be in the previous callee arguments scope.
+ */
+ retval = njs_vmcode_operand(vm, frame->retval);
+
+ /* GC: value external/internal++ depending on value and retval type */
+ *retval = *value;
+
+ njs_function_frame_free(vm, &frame->native);
+
+ return NJS_STOP;
+}
+
+
+njs_ret_t
+njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ vm->retval = *value;
+
+ return NJS_STOP;
+}
+
+
+/*
+ * njs_vmcode_try_start() is set on the start of a "try" block to create
+ * a "try" block, to set a catch address to the start of a "catch" or
+ * "finally" blocks and to initialize a value to track uncaught exception.
+ */
+
+njs_ret_t
+njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *exception_value,
+ njs_value_t *offset)
+{
+ njs_value_t *exit_value;
+ njs_exception_t *e;
+ njs_vmcode_try_start_t *try_start;
+
+ if (vm->top_frame->exception.catch != NULL) {
+ e = nxt_mp_alloc(vm->mem_pool, sizeof(njs_exception_t));
+ if (nxt_slow_path(e == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ *e = vm->top_frame->exception;
+ vm->top_frame->exception.next = e;
+ }
+
+ vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
+
+ njs_set_invalid(exception_value);
+
+ try_start = (njs_vmcode_try_start_t *) vm->current;
+ exit_value = njs_vmcode_operand(vm, try_start->exit_value);
+
+ njs_set_invalid(exit_value);
+ njs_number(exit_value) = 0;
+
+ return sizeof(njs_vmcode_try_start_t);
+}
+
+
+/*
+ * njs_vmcode_try_break() sets exit_value to INVALID 1, and jumps to
+ * the nearest try_end block. The exit_value is checked by njs_vmcode_finally().
+ */
+
+njs_ret_t
+njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *exit_value,
+ njs_value_t *offset)
+{
+ /* exit_value can contain valid value set by vmcode_try_return. */
+ if (!njs_is_valid(exit_value)) {
+ njs_number(exit_value) = 1;
+ }
+
+ return (njs_ret_t) offset;
+}
+
+
+/*
+ * njs_vmcode_try_continue() sets exit_value to INVALID -1, and jumps to
+ * the nearest try_end block. The exit_value is checked by njs_vmcode_finally().
+ */
+
+njs_ret_t
+njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *exit_value,
+ njs_value_t *offset)
+{
+ njs_number(exit_value) = -1;
+
+ return (njs_ret_t) offset;
+}
+
+/*
+ * njs_vmcode_try_return() saves a return value to use it later by
+ * njs_vmcode_finally(), and jumps to the nearest try_break block.
+ */
+
+njs_ret_t
+njs_vmcode_try_return(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_vmcode_try_return_t *try_return;
+
+ vm->retval = *value;
+
+ njs_retain(value);
+
+ try_return = (njs_vmcode_try_return_t *) vm->current;
+
+ return try_return->offset;
+}
+
+
+/*
+ * njs_vmcode_try_end() is set on the end of a "try" block to remove the block.
+ * It is also set on the end of a "catch" block followed by a "finally" block.
+ */
+
+njs_ret_t
+njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
+{
+ njs_exception_t *e;
+
+ e = vm->top_frame->exception.next;
+
+ if (e == NULL) {
+ vm->top_frame->exception.catch = NULL;
+
+ } else {
+ vm->top_frame->exception = *e;
+ nxt_mp_free(vm->mem_pool, e);
+ }
+
+ return (njs_ret_t) offset;
+}
+
+
+njs_ret_t
+njs_vmcode_throw(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ vm->retval = *value;
+
+ return NXT_ERROR;
+}
+
+
+/*
+ * njs_vmcode_catch() is set on the start of a "catch" block to store
+ * exception and to remove a "try" block if there is no "finally" block
+ * or to update a catch address to the start of a "finally" block.
+ * njs_vmcode_catch() is set on the start of a "finally" block to store
+ * uncaught exception and to remove a "try" block.
+ */
+
+njs_ret_t
+njs_vmcode_catch(njs_vm_t *vm, njs_value_t *exception, njs_value_t *offset)
+{
+ *exception = vm->retval;
+
+ if ((njs_ret_t) offset == sizeof(njs_vmcode_catch_t)) {
+ return njs_vmcode_try_end(vm, exception, offset);
+ }
+
+ vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
+
+ return sizeof(njs_vmcode_catch_t);
+}
+
+
+/*
+ * njs_vmcode_finally() is set on the end of a "finally" or a "catch" block.
+ * 1) to throw uncaught exception.
+ * 2) to make a jump to an enslosing loop exit if "continue" or "break"
+ * statement was used inside try block.
+ * 3) to finalize "return" instruction from "try" block.
+ */
+
+njs_ret_t
+njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *exception_value, *exit_value;
+ njs_vmcode_finally_t *finally;
+
+ exception_value = njs_vmcode_operand(vm, retval);
+
+ if (njs_is_valid(exception_value)) {
+ vm->retval = *exception_value;
+
+ return NXT_ERROR;
+ }
+
+ finally = (njs_vmcode_finally_t *) vm->current;
+ exit_value = njs_vmcode_operand(vm, finally->exit_value);
+
+ /*
+ * exit_value is set by:
+ * vmcode_try_start to INVALID 0
+ * vmcode_try_break to INVALID 1
+ * vmcode_try_continue to INVALID -1
+ * vmcode_try_return to a valid return value
+ */
+
+ if (njs_is_valid(exit_value)) {
+ return njs_vmcode_return(vm, NULL, exit_value);
+
+ } else if (njs_number(exit_value) != 0) {
+ return (njs_ret_t) (njs_number(exit_value) > 0)
+ ? finally->break_offset
+ : finally->continue_offset;
+ }
+
+ return sizeof(njs_vmcode_finally_t);
+}
+
+
+njs_ret_t
+njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1,
+ njs_value_t *invld2)
+{
+ nxt_str_t *file;
+ njs_vmcode_reference_error_t *ref_err;
+
+ ref_err = (njs_vmcode_reference_error_t *) vm->current;
+
+ file = &ref_err->file;
+
+ if (file->length != 0 && !vm->options.quiet) {
+ njs_reference_error(vm, "\"%V\" is not defined in %V:%uD",
+ &ref_err->name, file, ref_err->token_line);
+
+ } else {
+ njs_reference_error(vm, "\"%V\" is not defined in %uD", &ref_err->name,
+ ref_err->token_line);
+ }
+
+ return NJS_ERROR;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_VMCODE_H_INCLUDED_
+#define _NJS_VMCODE_H_INCLUDED_
+
+
+/*
+ * Negative return values handled by nJSVM interpreter as special events.
+ * The values must be in range from -1 to -11, because -12 is minimal jump
+ * offset on 32-bit platforms.
+ * -1 (NJS_ERROR/NXT_ERROR): error or exception;
+ * -2 (NJS_AGAIN/NXT_AGAIN): postpone nJSVM execution;
+ * -3: not used;
+ * -4 (NJS_STOP/NXT_DONE): njs_vmcode_stop() has stopped execution,
+ * execution has completed successfully;
+ * -5 .. -11: not used.
+ */
+
+#define NJS_STOP NXT_DONE
+
+/* The last return value which preempts execution. */
+#define NJS_PREEMPT (-11)
+
+
+typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
+ njs_value_t *value2);
+
+
+#define NJS_VMCODE_3OPERANDS 0
+#define NJS_VMCODE_2OPERANDS 1
+#define NJS_VMCODE_1OPERAND 2
+#define NJS_VMCODE_NO_OPERAND 3
+
+#define NJS_VMCODE_NO_RETVAL 0
+#define NJS_VMCODE_RETVAL 1
+
+
+typedef struct {
+ njs_vmcode_operation_t operation;
+ uint8_t operands; /* 2 bits */
+ uint8_t retval; /* 1 bit */
+ uint8_t ctor; /* 1 bit */
+} njs_vmcode_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t operand1;
+ njs_index_t operand2;
+ njs_index_t operand3;
+} njs_vmcode_generic_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t index;
+} njs_vmcode_1addr_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+ njs_index_t src;
+} njs_vmcode_2addr_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+ njs_index_t src1;
+ njs_index_t src2;
+} njs_vmcode_3addr_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+ njs_index_t src;
+} njs_vmcode_move_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_object_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+} njs_vmcode_this_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+} njs_vmcode_arguments_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ uintptr_t length;
+} njs_vmcode_array_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_template_literal_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_function_lambda_t *lambda;
+} njs_vmcode_function_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_regexp_pattern_t *pattern;
+} njs_vmcode_regexp_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t object;
+} njs_vmcode_object_copy_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+} njs_vmcode_jump_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t cond;
+} njs_vmcode_cond_jump_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t value1;
+ njs_index_t value2;
+} njs_vmcode_equal_jump_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t value;
+ njs_ret_t offset;
+} njs_vmcode_test_jump_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t value;
+ njs_index_t object;
+ njs_index_t property;
+} njs_vmcode_prop_get_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t value;
+ njs_index_t object;
+ njs_index_t property;
+} njs_vmcode_prop_set_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t next;
+ njs_index_t object;
+ njs_ret_t offset;
+} njs_vmcode_prop_foreach_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t object;
+ njs_index_t next;
+ njs_ret_t offset;
+} njs_vmcode_prop_next_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t value;
+ njs_index_t constructor;
+ njs_index_t object;
+} njs_vmcode_instance_of_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t nargs;
+ njs_index_t name;
+} njs_vmcode_function_frame_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t nargs;
+ njs_index_t object;
+ njs_index_t method;
+} njs_vmcode_method_frame_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_function_call_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_return_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_stop_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t exception_value;
+ njs_index_t exit_value;
+} njs_vmcode_try_start_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t exit_value;
+} njs_vmcode_try_trampoline_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t exception;
+} njs_vmcode_catch_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_throw_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+} njs_vmcode_try_end_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t save;
+ njs_index_t retval;
+ njs_ret_t offset;
+} njs_vmcode_try_return_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t exit_value;
+ njs_ret_t continue_offset;
+ njs_ret_t break_offset;
+} njs_vmcode_finally_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ nxt_str_t name;
+ nxt_str_t file;
+ uint32_t token_line;
+} njs_vmcode_reference_error_t;
+
+
+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);
+njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *inlvd2);
+njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
+njs_ret_t njs_vmcode_this(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
+njs_ret_t njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
+njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
+njs_ret_t njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *inlvd2);
+njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+
+njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property,
+ njs_value_t *object);
+njs_ret_t njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_property_next(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *value);
+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 *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,
+ njs_value_t *invld2);
+njs_ret_t 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);
+njs_ret_t njs_vmcode_unary_negation(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_addition(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);
+njs_ret_t njs_vmcode_multiplication(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_exponentiation(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *inlvd);
+njs_ret_t njs_vmcode_test_if_true(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+njs_ret_t 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 *inlvd);
+njs_ret_t njs_vmcode_bitwise_and(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_bitwise_xor(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_bitwise_or(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_left_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_right_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t 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 njs_vmcode_less(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2);
+njs_ret_t njs_vmcode_greater(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_less_or_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_greater_or_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t 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);
+
+njs_ret_t njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld);
+
+njs_ret_t njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_if_equal_jump(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+
+njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *nargs);
+njs_ret_t njs_vmcode_method_frame(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *method);
+njs_ret_t 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);
+njs_ret_t njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+
+njs_ret_t njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_try_return(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_throw(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+njs_ret_t njs_vmcode_catch(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *exception);
+njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+njs_ret_t njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1,
+ njs_value_t *invld2);
+
+#endif /* _NJS_VMCODE_H_INCLUDED_ */