#include <string.h>
+static const njs_vmcode_1addr_t njs_trap_strings[] = {
+ { .code = { .operation = njs_vmcode_string_primitive,
+ .operands = NJS_VMCODE_1OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL },
+ .index = 0 },
+ { .code = { .operation = njs_vmcode_string_primitive,
+ .operands = NJS_VMCODE_1OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL },
+ .index = 1 },
+ { .code = { .operation = njs_vmcode_restart,
+ .operands = NJS_VMCODE_NO_OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL } },
+};
+
+
+static const njs_vmcode_1addr_t njs_trap_numbers[] = {
+ { .code = { .operation = njs_vmcode_number_primitive,
+ .operands = NJS_VMCODE_1OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL },
+ .index = 0 },
+ { .code = { .operation = njs_vmcode_number_primitive,
+ .operands = NJS_VMCODE_1OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL },
+ .index = 1 },
+ { .code = { .operation = njs_vmcode_restart,
+ .operands = NJS_VMCODE_NO_OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL } },
+};
+
+
+static const njs_vmcode_1addr_t njs_trap_number[] = {
+ { .code = { .operation = njs_vmcode_number_primitive,
+ .operands = NJS_VMCODE_1OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL },
+ .index = 0 },
+ { .code = { .operation = njs_vmcode_restart,
+ .operands = NJS_VMCODE_NO_OPERAND,
+ .retval = NJS_VMCODE_NO_RETVAL } },
+};
+
+
+static const njs_vm_trap_t njs_vm_traps[] = {
+ /* NJS_TRAP_PROPERTY */ { &njs_trap_strings[1], 0 },
+ /* NJS_TRAP_STRINGS */ { &njs_trap_strings[0], 0 },
+ /* NJS_TRAP_INCDEC */ { &njs_trap_numbers[1], 1 },
+ /* NJS_TRAP_NUMBERS */ { &njs_trap_numbers[0], 0 },
+ /* NJS_TRAP_NUMBER */ { &njs_trap_number[0], 0 },
+};
+
+
njs_function_t *
njs_function_alloc(njs_vm_t *vm)
{
frame->ctor = ctor;
frame->reentrant = 0;
- frame->lvalue = 0;
+ frame->trap_reference = 0;
frame->u.exception.next = NULL;
frame->u.exception.catch = NULL;
njs_ret_t
-njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
- njs_value_t *value2, nxt_bool_t lvalue)
+njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
+ njs_value_t *value2)
{
size_t size, spare_size;
- njs_value_t *data;
+ njs_value_t *values;
njs_native_frame_t *frame;
size = NJS_NATIVE_FRAME_SIZE + 3 * sizeof(njs_value_t);
frame->ctor = 0;
frame->reentrant = 0;
- frame->lvalue = lvalue;
- data = njs_native_data(frame);
- njs_set_invalid(&data[0]);
- data[1] = *value1;
+ values = njs_native_data(frame);
+ njs_set_invalid(&values[0]);
+ values[2] = *value2;
+
+ frame->trap_reference = njs_vm_traps[trap].reference_value;
- if (lvalue) {
- data[2].data.u.value = value2;
+ if (njs_vm_traps[trap].reference_value) {
+ values[1].data.u.value = value1;
} else {
- data[2] = *value2;
+ values[1] = *value1;
}
frame->u.exception.catch = NULL;
frame->u.restart = vm->current;
- vm->current = trap;
+ vm->current = (u_char *) njs_vm_traps[trap].code;
frame->last = (u_char *) frame + size;
frame->previous = vm->frame;
frame->native.ctor = ctor;
frame->native.reentrant = 0;
- frame->native.lvalue = 0;
+ frame->native.trap_reference = 0;
frame->native.u.exception.next = NULL;
frame->native.u.exception.catch = NULL;
uint32_t size;
- uint8_t start; /* 1 bit */
- uint8_t ctor; /* 1 bit */
- uint8_t reentrant; /* 1 bit */
- uint8_t lvalue; /* 1 bit */
+ uint8_t start; /* 1 bit */
+ uint8_t ctor; /* 1 bit */
+ uint8_t reentrant; /* 1 bit */
+ uint8_t trap_reference; /* 1 bit */
};
njs_param_t *param);
njs_value_t *njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method,
uintptr_t nargs, nxt_bool_t ctor);
-njs_ret_t njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
- njs_value_t *value2, nxt_bool_t lvalue);
+njs_ret_t njs_vmcode_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
+ njs_value_t *value2);
njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name,
njs_param_t *param, nxt_bool_t ctor);
njs_ret_t njs_function_call(njs_vm_t *vm, njs_function_t *func,
static nxt_int_t
njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
{
- nxt_int_t ret;
+ nxt_int_t ret;
+ njs_parser_node_t *left;
if (node == NULL) {
return NXT_OK;
case NJS_TOKEN_REMAINDER_ASSIGNMENT:
return njs_generate_operation_assignment(vm, parser, node);
+ case NJS_TOKEN_IN:
+ /*
+ * An "in" operation is parsed as standard binary expression by
+ * njs_parser_binary_expression(). However, its operands should
+ * be swapped to be uniform with other property operations (get/set
+ * and delete) to use the array and object property traps.
+ */
+ left = node->left;
+ node->left = node->right;
+ node->right = left;
+
+ /* Fall through. */
+
case NJS_TOKEN_LOGICAL_OR:
case NJS_TOKEN_LOGICAL_AND:
case NJS_TOKEN_BITWISE_OR:
case NJS_TOKEN_NOT_EQUAL:
case NJS_TOKEN_STRICT_EQUAL:
case NJS_TOKEN_STRICT_NOT_EQUAL:
- case NJS_TOKEN_IN:
case NJS_TOKEN_INSTANCEOF:
case NJS_TOKEN_LESS:
case NJS_TOKEN_LESS_OR_EQUAL:
return NXT_ERROR;
}
-
-
-u_char *
-njs_number_trap_create(njs_vm_t *vm)
-{
- u_char *p, *code;
- size_t size;
- njs_vmcode_restart_t *restart;
- njs_vmcode_to_number_t *to_number;
-
- size = 2 * sizeof(njs_vmcode_to_number_t) + sizeof(njs_vmcode_restart_t);
-
- code = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
-
- if (nxt_fast_path(code != NULL)) {
- p = code;
-
- to_number = (njs_vmcode_to_number_t *) p;
- p += sizeof(njs_vmcode_to_number_t);
- to_number->code.operation = njs_vmcode_to_number;
- to_number->code.operands = NJS_VMCODE_1OPERAND;
- to_number->code.retval = NJS_VMCODE_NO_RETVAL;
- to_number->narg = 1;
-
- to_number = (njs_vmcode_to_number_t *) p;
- p += sizeof(njs_vmcode_to_number_t);
- to_number->code.operation = njs_vmcode_to_number;
- to_number->code.operands = NJS_VMCODE_1OPERAND;
- to_number->code.retval = NJS_VMCODE_NO_RETVAL;
- to_number->narg = 0;
-
- restart = (njs_vmcode_restart_t *) p;
- p += sizeof(njs_vmcode_restart_t);
- restart->code.operation = njs_vmcode_restart;
- restart->code.operands = NJS_VMCODE_NO_OPERAND;
- restart->code.retval = NJS_VMCODE_NO_RETVAL;
- }
-
- return code;
-}
-
-
-u_char *
-njs_string_trap_create(njs_vm_t *vm)
-{
- u_char *p, *code;
- size_t size;
- njs_vmcode_restart_t *restart;
- njs_vmcode_to_string_t *to_string;
-
- size = 2 * sizeof(njs_vmcode_to_string_t) + sizeof(njs_vmcode_restart_t);
-
- code = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
-
- if (nxt_fast_path(code != NULL)) {
- p = code;
-
- to_string = (njs_vmcode_to_string_t *) p;
- p += sizeof(njs_vmcode_to_string_t);
- to_string->code.operation = njs_vmcode_to_string;
- to_string->code.operands = NJS_VMCODE_1OPERAND;
- to_string->code.retval = NJS_VMCODE_NO_RETVAL;
- to_string->narg = 0;
-
- to_string = (njs_vmcode_to_string_t *) p;
- p += sizeof(njs_vmcode_to_string_t);
- to_string->code.operation = njs_vmcode_to_string;
- to_string->code.operands = NJS_VMCODE_1OPERAND;
- to_string->code.retval = NJS_VMCODE_NO_RETVAL;
- to_string->narg = 1;
-
- restart = (njs_vmcode_restart_t *) p;
- p += sizeof(njs_vmcode_restart_t);
- restart->code.operation = njs_vmcode_restart;
- restart->code.operands = NJS_VMCODE_NO_OPERAND;
- restart->code.retval = NJS_VMCODE_NO_RETVAL;
- }
-
- return code;
-}
#include <string.h>
-/* The values must not coincide with NXT_OK, NXT_ERROR, and NXT_DECLINED. */
+/* The values must be greater than NXT_OK. */
#define NJS_PRIMITIVE_VALUE 1
-#define NJS_ARRAY_VALUE 2
-#define NJS_EXTERNAL_VALUE 3
+#define NJS_STRING_VALUE 2
+#define NJS_ARRAY_VALUE 3
+#define NJS_EXTERNAL_VALUE 4
+
+/*
+ * NJS_PROPERTY_QUERY_GET must be less or equal to NJS_PROPERTY_QUERY_IN,
+ * NJS_PROPERTY_QUERY_SET and NJS_PROPERTY_QUERY_DELETE must be greater
+ * than NJS_PROPERTY_QUERY_IN.
+ */
#define NJS_PROPERTY_QUERY_GET 0
-#define NJS_PROPERTY_QUERY_IN 0
-#define NJS_PROPERTY_QUERY_DELETE 1
+#define NJS_PROPERTY_QUERY_IN 1
#define NJS_PROPERTY_QUERY_SET 2
+#define NJS_PROPERTY_QUERY_DELETE 3
typedef struct {
njs_value_t *val2);
static nxt_noinline nxt_bool_t njs_values_strict_equal(njs_value_t *val1,
njs_value_t *val2);
+static njs_ret_t njs_primitive_value(njs_vm_t *vm, njs_value_t *value,
+ nxt_uint_t hint);
void njs_debug(njs_index_t index, njs_value_t *value);
switch (ret) {
case NJS_TRAP_NUMBER:
- ret = njs_vmcode_trap(vm,
- vm->number_trap + sizeof(njs_vmcode_to_number_t),
- value1, value1, 0);
-
- if (nxt_fast_path(ret == NXT_OK)) {
- goto again;
- }
-
- ret = NXT_ERROR;
- break;
-
- case NJS_TRAP_LVALUE_NUMBER:
- ret = njs_vmcode_trap(vm,
- vm->number_trap + sizeof(njs_vmcode_to_number_t),
- value1, value2, 1);
+ value2 = value1;
- if (nxt_fast_path(ret == NXT_OK)) {
- goto again;
- }
-
- ret = NXT_ERROR;
- break;
+ /* Fall through. */
case NJS_TRAP_NUMBERS:
- ret = njs_vmcode_trap(vm, vm->number_trap, value1, value2, 0);
+ case NJS_TRAP_STRINGS:
+ case NJS_TRAP_INCDEC:
+ case NJS_TRAP_PROPERTY:
+
+ ret = njs_vmcode_trap(vm, ret - NJS_TRAP_LAST, value1, value2);
if (nxt_fast_path(ret == NXT_OK)) {
goto again;
ret = NXT_ERROR;
break;
- case NJS_TRAP_STRINGS:
- ret = njs_vmcode_trap(vm, vm->string_trap, value1, value2, 0);
-
- if (nxt_fast_path(ret == NXT_OK)) {
- goto again;
- }
-
- ret = NXT_ERROR;
+ default:
break;
}
retval = &njs_value_void;
- if (nxt_fast_path(ret == NXT_OK)) {
+ switch (ret) {
+
+ case NXT_OK:
prop = pq.lhq.value;
switch (prop->type) {
return NXT_ERROR;
}
- } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+ break;
+
+ case NXT_DECLINED:
+ case NJS_PRIMITIVE_VALUE:
+ break;
+ case NJS_STRING_VALUE:
+
+ /* string[n]. */
+
+ num = njs_value_to_number(property);
+ index = (int32_t) num;
+
+ if (index >= 0 && index == num) {
+ slice.start = index;
+ slice.length = 1;
+ slice.string_length = njs_string_prop(&string, object);
+ /*
+ * A single codepoint string fits in vm->retval
+ * so the function cannot fail.
+ */
+ (void) njs_string_slice(vm, &vm->retval, &string, &slice);
+
+ if (nxt_fast_path(vm->retval.data.truth != 0)) {
+ /* Non-empty string. */
+ return sizeof(njs_vmcode_prop_get_t);
+ }
+ }
+
+ break;
+
+ case NJS_ARRAY_VALUE:
val = pq.lhq.value;
if (njs_is_valid(val)) {
retval = val;
}
- } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+ break;
+ case NJS_EXTERNAL_VALUE:
ext = object->data.u.external;
ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
retval = &ext->value;
- goto done;
+ break;
}
data = ext->data;
return sizeof(njs_vmcode_prop_get_t);
- } else if (nxt_fast_path(ret == NJS_PRIMITIVE_VALUE)) {
-
- if (njs_is_string(object)) {
-
- /* string[n]. */
-
- num = njs_value_to_number(property);
- index = (int32_t) num;
-
- if (index >= 0 && index == num) {
- slice.start = index;
- slice.length = 1;
- slice.string_length = njs_string_prop(&string, object);
-
- /* A single codepoint string fits in vm->retval cannot fail. */
- (void) njs_string_slice(vm, &vm->retval, &string, &slice);
-
- if (nxt_fast_path(vm->retval.data.truth != 0)) {
- /* Non-empty string. */
- return sizeof(njs_vmcode_prop_get_t);
- }
- }
- }
+ default:
+ /* NJS_TRAP_PROPERTY */
+ /* NXT_ERROR */
- } else if (ret == NXT_ERROR) {
return ret;
}
-done:
-
vm->retval = *retval;
/* GC: njs_retain(retval) */
ret = njs_property_query(vm, &pq, object, property);
- if (nxt_fast_path(ret == NXT_OK)) {
+ switch (ret) {
+ case NXT_OK:
prop = pq.lhq.value;
+ break;
- } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
-
- p = pq.lhq.value;
- *p = *value;
-
- return sizeof(njs_vmcode_prop_set_t);
-
- } else if (nxt_fast_path(ret == NXT_DECLINED)) {
-
+ case NXT_DECLINED:
prop = njs_object_prop_alloc(vm, &pq.value);
if (nxt_slow_path(prop == NULL)) {
return NXT_ERROR;
return ret;
}
- } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+ break;
+
+ case NJS_PRIMITIVE_VALUE:
+ case NJS_STRING_VALUE:
+ return sizeof(njs_vmcode_prop_set_t);
+
+ case NJS_ARRAY_VALUE:
+ p = pq.lhq.value;
+ *p = *value;
+ return sizeof(njs_vmcode_prop_set_t);
+
+ case NJS_EXTERNAL_VALUE:
ext = object->data.u.external;
ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
return sizeof(njs_vmcode_prop_set_t);
- } else if (ret == NJS_PRIMITIVE_VALUE) {
- return sizeof(njs_vmcode_prop_set_t);
+ default:
+ /* NJS_TRAP_PROPERTY */
+ /* NXT_ERROR */
- } else {
return ret;
}
njs_ret_t
-njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property, njs_value_t *object)
+njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *object, njs_value_t *property)
{
uintptr_t data;
njs_ret_t ret;
ret = njs_property_query(vm, &pq, object, property);
- if (nxt_fast_path(ret == NXT_OK)) {
+ switch (ret) {
+
+ case NXT_OK:
retval = &njs_value_true;
- goto done;
+ break;
- } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+ case NXT_DECLINED:
+ break;
+
+ case NJS_PRIMITIVE_VALUE:
+ case NJS_STRING_VALUE:
+ vm->exception = &njs_exception_type_error;
+
+ return NXT_ERROR;
+
+ case NJS_ARRAY_VALUE:
value = pq.lhq.value;
- retval = njs_is_valid(value) ? &njs_value_true : &njs_value_false;
- goto done;
- } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+ if (njs_is_valid(value)) {
+ retval = &njs_value_true;
+ }
+
+ break;
+ case NJS_EXTERNAL_VALUE:
ext = object->data.u.external;
ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
retval = &njs_value_true;
- goto done;
+ break;
}
data = ext->data;
retval = &njs_value_true;
}
- goto done;
+ break;
- } else if (nxt_fast_path(ret == NXT_DECLINED)) {
- retval = &njs_value_false;
- goto done;
+ default:
+ /* NJS_TRAP_PROPERTY */
+ /* NXT_ERROR */
- } else if (nxt_fast_path(ret != NXT_ERROR)) {
- vm->exception = &njs_exception_type_error;
+ return ret;
}
- return NXT_ERROR;
-
-done:
-
vm->retval = *retval;
return sizeof(njs_vmcode_3addr_t);
ret = njs_property_query(vm, &pq, object, property);
- if (nxt_fast_path(ret == NXT_OK)) {
+ switch (ret) {
+
+ case NXT_OK:
prop = pq.lhq.value;
if (prop->configurable) {
retval = &njs_value_true;
}
- } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+ break;
+
+ case NXT_DECLINED:
+ case NJS_PRIMITIVE_VALUE:
+ case NJS_STRING_VALUE:
+ break;
+
+ case NJS_ARRAY_VALUE:
value = pq.lhq.value;
njs_set_invalid(value);
-
retval = &njs_value_true;
+ break;
- } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+ case NJS_EXTERNAL_VALUE:
ext = object->data.u.external;
ext = pq.lhq.value;
if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
- goto done;
+ break;
}
data = ext->data;
retval = &njs_value_true;
}
- } else if (nxt_slow_path(ret == NXT_ERROR)) {
+ break;
+
+ default:
+ /* NJS_TRAP_PROPERTY */
+ /* NXT_ERROR */
+
return ret;
}
-done:
-
vm->retval = *retval;
return sizeof(njs_vmcode_3addr_t);
}
+/*
+ * The njs_property_query() returns values
+ * NXT_OK property has been found in object,
+ * NXT_DECLINED property was not found in object,
+ * NJS_PRIMITIVE_VALUE property operation was applied to a numeric
+ * or undefined value,
+ * NJS_STRING_VALUE property operation was applied to a string,
+ * NJS_ARRAY_VALUE object is array,
+ * NJS_EXTERNAL_VALUE object is external entity,
+ * NJS_TRAP_PROPERTY the property trap must be called,
+ * NXT_ERROR exception has been thrown.
+ */
+
static nxt_noinline njs_ret_t
njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
njs_value_t *property)
case NJS_ARRAY:
if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
- if (njs_is_number(property)) {
- num = property->data.u.number;
+ if (nxt_fast_path(njs_is_primitive(property))) {
+ num = njs_value_to_number(property);
} else {
- num = njs_value_to_number(property);
+ return NJS_TRAP_PROPERTY;
}
index = (int) num;
return NJS_PRIMITIVE_VALUE;
}
- ret = njs_value_to_string(vm, &pq->value, property);
+ if (nxt_fast_path(njs_is_primitive(property))) {
- if (nxt_fast_path(ret == NXT_OK)) {
+ ret = njs_value_to_string(vm, &pq->value, property);
- pq->lhq.key.len = pq->value.short_string.size;
+ if (nxt_fast_path(ret == NXT_OK)) {
- if (pq->lhq.key.len != NJS_STRING_LONG) {
- pq->lhq.key.data = pq->value.short_string.start;
+ pq->lhq.key.len = pq->value.short_string.size;
- } else {
- pq->lhq.key.len = pq->value.data.string_size;
- pq->lhq.key.data = pq->value.data.u.string->start;
- }
+ if (pq->lhq.key.len != NJS_STRING_LONG) {
+ pq->lhq.key.data = pq->value.short_string.start;
- pq->lhq.key_hash = hash(pq->lhq.key.data, pq->lhq.key.len);
+ } else {
+ pq->lhq.key.len = pq->value.data.string_size;
+ pq->lhq.key.data = pq->value.data.u.string->start;
+ }
+
+ pq->lhq.key_hash = hash(pq->lhq.key.data, pq->lhq.key.len);
- if (obj == NULL) {
- pq->lhq.proto = &njs_extern_hash_proto;
+ if (obj == NULL) {
+ pq->lhq.proto = &njs_extern_hash_proto;
+
+ return NJS_EXTERNAL_VALUE;
+ }
- return NJS_EXTERNAL_VALUE;
+ return njs_object_property_query(vm, pq, object, obj);
}
- return njs_object_property_query(vm, pq, object, obj);
+ return ret;
}
- return ret;
+ return NJS_TRAP_PROPERTY;
}
} while (object != NULL);
- if (nxt_fast_path(njs_is_primitive(value))) {
- return NJS_PRIMITIVE_VALUE;
+ if (njs_is_string(value)) {
+ return NJS_STRING_VALUE;
}
+ /* NXT_DECLINED */
+
return ret;
}
}
+/*
+ * The increment and decrement operations require only one value parameter.
+ * However, if the value is not numeric, then the trap is generated and
+ * value parameter points to a trap frame value converted to a numeric.
+ * So the additional reference parameter points to the original value.
+ */
+
njs_ret_t
-njs_vmcode_increment(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+njs_vmcode_increment(njs_vm_t *vm, njs_value_t *reference, njs_value_t *value)
{
double num;
if (nxt_fast_path(njs_is_numeric(value))) {
num = value->data.u.number + 1.0;
- njs_release(vm, lvalue);
+ njs_release(vm, reference);
- njs_number_set(lvalue, num);
- vm->retval = *lvalue;
+ njs_number_set(reference, num);
+ vm->retval = *reference;
return sizeof(njs_vmcode_3addr_t);
}
- return NJS_TRAP_LVALUE_NUMBER;
+ return NJS_TRAP_INCDEC;
}
njs_ret_t
-njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *reference, njs_value_t *value)
{
double num;
if (nxt_fast_path(njs_is_numeric(value))) {
num = value->data.u.number - 1.0;
- njs_release(vm, lvalue);
+ njs_release(vm, reference);
- njs_number_set(lvalue, num);
- vm->retval = *lvalue;
+ njs_number_set(reference, num);
+ vm->retval = *reference;
return sizeof(njs_vmcode_3addr_t);
}
- return NJS_TRAP_LVALUE_NUMBER;
+ return NJS_TRAP_INCDEC;
}
njs_ret_t
-njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value)
{
double num;
if (nxt_fast_path(njs_is_numeric(value))) {
num = value->data.u.number;
- njs_release(vm, lvalue);
+ njs_release(vm, reference);
- njs_number_set(lvalue, num + 1.0);
+ njs_number_set(reference, num + 1.0);
njs_number_set(&vm->retval, num);
return sizeof(njs_vmcode_3addr_t);
}
- return NJS_TRAP_LVALUE_NUMBER;
+ return NJS_TRAP_INCDEC;
}
njs_ret_t
-njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value)
{
double num;
if (nxt_fast_path(njs_is_numeric(value))) {
num = value->data.u.number;
- njs_release(vm, lvalue);
+ njs_release(vm, reference);
- njs_number_set(lvalue, num - 1.0);
+ njs_number_set(reference, num - 1.0);
njs_number_set(&vm->retval, num);
return sizeof(njs_vmcode_3addr_t);
}
- return NJS_TRAP_LVALUE_NUMBER;
+ return NJS_TRAP_INCDEC;
}
njs_ret_t
-njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
+njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
{
double num;
njs_ret_t ret;
value = njs_native_data(vm->frame);
value = &value[(uintptr_t) narg + 1];
- ret = njs_value_to_primitive(vm, value, 0);
+ ret = njs_primitive_value(vm, value, 0);
if (nxt_fast_path(ret > 0)) {
njs_number_set(value, num);
}
- ret = sizeof(njs_vmcode_to_number_t);
+ ret = sizeof(njs_vmcode_1addr_t);
}
return ret;
njs_ret_t
-njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
+njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
{
njs_ret_t ret;
njs_value_t *value;
value = njs_native_data(vm->frame);
value = &value[(uintptr_t) narg + 1];
- ret = njs_value_to_primitive(vm, value, 1);
+ ret = njs_primitive_value(vm, value, 1);
if (nxt_fast_path(ret > 0)) {
done:
- ret = sizeof(njs_vmcode_to_string_t);
+ ret = sizeof(njs_vmcode_1addr_t);
}
return ret;
* for numbers and "toString", "valueOf" for strings.
*/
-nxt_noinline njs_ret_t
-njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
+static nxt_noinline njs_ret_t
+njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
{
njs_ret_t ret;
njs_param_t param;
{
u_char *restart;
njs_ret_t ret;
- njs_value_t *retval, *value1, *value2;
+ njs_value_t *retval, *values, *value1;
njs_native_frame_t *frame;
njs_vmcode_generic_t *vmcode;
vm->current = restart;
vmcode = (njs_vmcode_generic_t *) restart;
- value1 = njs_native_data(frame);
- value1 = &value1[1];
+ values = njs_native_data(frame);
+ value1 = &values[1];
- if (frame->lvalue) {
- value2 = value1[1].data.u.value;
-
- } else {
- value2 = &value1[1];
+ if (frame->trap_reference) {
+ value1 = value1->data.u.value;
}
- ret = vmcode->code.operation(vm, value1, value2);
+ ret = vmcode->code.operation(vm, value1, &values[2]);
retval = njs_vmcode_operand(vm, vmcode->operand1);
#define _NJS_VM_H_INCLUDED_
-#define NJS_TRAP_STRINGS -10
-#define NJS_TRAP_NUMBERS -11
-#define NJS_TRAP_LVALUE_NUMBER -12
-#define NJS_TRAP_NUMBER -13
+#define NJS_TRAP_NUMBER -10
+#define NJS_TRAP_NUMBERS -11
+#define NJS_TRAP_INCDEC -12
+#define NJS_TRAP_STRINGS -13
+#define NJS_TRAP_PROPERTY -14
+#define NJS_TRAP_LAST NJS_TRAP_PROPERTY
-#define NJS_PASS -20
+#define NJS_PASS -20
/* The order of the enum is used in njs_vmcode_typeof() */
} njs_vmcode_finally_t;
-typedef struct {
- njs_vmcode_t code;
- uintptr_t narg;
-} njs_vmcode_to_string_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- uintptr_t narg;
-} njs_vmcode_to_number_t;
-
-
-typedef struct {
- njs_vmcode_t code;
- uintptr_t unused;
-} njs_vmcode_restart_t;
-
-
typedef enum {
NJS_SCOPE_ABSOLUTE = 0,
NJS_SCOPE_LOCAL,
njs_offset(index)
+typedef struct {
+ const njs_vmcode_1addr_t *code;
+ nxt_bool_t reference_value;
+} njs_vm_trap_t;
+
+
struct njs_vm_s {
/* njs_vm_t must be aligned to njs_value_t due to scratch value. */
njs_value_t retval;
njs_object_t *prototypes;
njs_function_t *functions;
- u_char *number_trap;
- u_char *string_trap;
-
nxt_mem_cache_pool_t *mem_cache_pool;
njs_value_t *global_scope;
njs_object_t *prototypes;
njs_function_t *functions;
-
- u_char *number_trap;
- u_char *string_trap;
};
njs_ret_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
njs_value_t *constructor);
-njs_ret_t njs_vmcode_increment(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *lvalue);
-njs_ret_t njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *lvalue);
-njs_ret_t njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *lvalue);
-njs_ret_t njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *lvalue);
+njs_ret_t njs_vmcode_increment(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value);
+njs_ret_t njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value);
+njs_ret_t njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value);
+njs_ret_t njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *reference,
+ njs_value_t *value);
njs_ret_t njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value,
njs_value_t *invld);
njs_ret_t njs_vmcode_void(njs_vm_t *vm, njs_value_t *invld1,
njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
njs_value_t *retval);
-njs_ret_t njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld,
+njs_ret_t njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld,
njs_value_t *narg);
-njs_ret_t njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld,
+njs_ret_t njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld,
njs_value_t *narg);
-njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value,
- nxt_uint_t hint);
njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1,
njs_value_t *invld2);
nxt_int_t njs_shared_objects_clone(njs_vm_t *vm);
-/* STUB */
-u_char *njs_number_trap_create(njs_vm_t *vm);
-u_char *njs_string_trap_create(njs_vm_t *vm);
-
-
void *njs_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc);
void njs_lvlhsh_free(void *data, void *p, size_t size);
keywords_hash = vm->shared->keywords_hash;
parser->values_hash = vm->shared->values_hash;
- vm->number_trap = vm->shared->number_trap;
- vm->string_trap = vm->shared->string_trap;
-
/* STUB */
if (vm->shared->prototypes == NULL) {
ret = njs_shared_objects_create(vm);
}
}
- if (vm->number_trap == NULL) {
-
- vm->number_trap = njs_number_trap_create(vm);
- if (nxt_slow_path(vm->number_trap == NULL)) {
- return NJS_ERROR;
- }
-
- vm->string_trap = njs_string_trap_create(vm);
- if (nxt_slow_path(vm->string_trap == NULL)) {
- return NJS_ERROR;
- }
-
- if (vm->shared != NULL) {
- vm->shared->number_trap = vm->number_trap;
- vm->shared->string_trap = vm->string_trap;
- }
- }
-
parser->lexer->keywords_hash = keywords_hash;
parser->lexer->start = *start;
nvm->variables_hash = vm->variables_hash;
nvm->values_hash = vm->values_hash;
- nvm->number_trap = vm->number_trap;
- nvm->string_trap = vm->string_trap;
-
nvm->retval = njs_value_void;
nvm->current = vm->current;
nvm->external = external;
{ nxt_string("!2"),
nxt_string("false") },
+ /**/
+
+ { nxt_string("var a = { valueOf: function() { return 1 } }; ~a"),
+ nxt_string("-2") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } }; ~a"),
+ nxt_string("-2") },
+
+ /**/
+
{ nxt_string("1 || 2"),
nxt_string("1") },
{ nxt_string("a = 1 ? b = 2 + 4 : b = 3"),
nxt_string("6") },
+ /**/
+
+ { nxt_string("var a = { valueOf: function() { return 1 } }; +a"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } }; +a"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } }; -a"),
+ nxt_string("-1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } }; -a"),
+ nxt_string("-1") },
+
/* Increment. */
{ nxt_string("var a = 1; ++a"),
{ nxt_string("var a = {}; ++a"),
nxt_string("NaN") },
- { nxt_string("var a = { valueOf: function() { return 1 } }; ++a"),
- nxt_string("2") },
+ { nxt_string("var a = { valueOf: function() { return 1 } };"
+ "++a +' '+ a +' '+ typeof a"),
+ nxt_string("2 2 number") },
- { nxt_string("var a = { valueOf: function() { return '1' } }; ++a"),
- nxt_string("2") },
+ { nxt_string("var a = { valueOf: function() { return '1' } };"
+ "++a +' '+ a +' '+ typeof a"),
+ nxt_string("2 2 number") },
- { nxt_string("var a = { valueOf: function() { return [1] } }; ++a"),
- nxt_string("NaN") },
+ { nxt_string("var a = { valueOf: function() { return [1] } };"
+ "++a +' '+ a +' '+ typeof a"),
+ nxt_string("NaN NaN number") },
- { nxt_string("var a = { valueOf: function() { return {} } }; ++a"),
- nxt_string("NaN") },
+ { nxt_string("var a = { valueOf: function() { return {} } };"
+ "++a +' '+ a +' '+ typeof a"),
+ nxt_string("NaN NaN number") },
/**/
{ nxt_string("a = 1; 1 in a"),
nxt_string("TypeError") },
+ { nxt_string("var n = { toString: function() { return 'a' } };"
+ "var o = { a: 5 }; o[n]"),
+ nxt_string("5") },
+
+ { nxt_string("var n = { valueOf: function() { return 'a' } };"
+ "var o = { a: 5, '[object Object]': 7 }; o[n]"),
+ nxt_string("7") },
+
+ /* Arrays */
+
{ nxt_string("a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"),
nxt_string("6") },
{ nxt_string("a = []; a.concat([]) +''"),
nxt_string("") },
+ /**/
+
+ { nxt_string("var n = { toString: function() { return 1 } }; [1,2][n]"),
+ nxt_string("2") },
+
+ { nxt_string("var n = { toString: function() { return '1' } }; [1,2][n]"),
+ nxt_string("2") },
+
+ { nxt_string("var n = { toString: function() { return 1 },"
+ " valueOf: function() { return 0 } }; [1,2][n]"),
+ nxt_string("2") },
+
+ { nxt_string("var n = { toString: function() { return 1.5 } };"
+ "var a = [1,2]; a[1.5] = 5; a[n]"),
+ nxt_string("5") },
+
+ { nxt_string("var n = { toString: function() { return 1.5 } };"
+ "var a = [1,2]; a[n] = 5; a[1.5]"),
+ nxt_string("5") },
+
+ { nxt_string("var n = { toString: function() { return '1.5' } };"
+ "var a = [1,2]; a[1.5] = 5; a[n]"),
+ nxt_string("5") },
+
+ { nxt_string("var n = { toString: function() { return '1.5' } };"
+ "var a = [1,2]; a[n] = 5; a[1.5]"),
+ nxt_string("5") },
+
+ { nxt_string("var n = { toString: function() { return 1.5 } };"
+ "var a = [1,2]; a[1.5] = 5; n in a"),
+ nxt_string("true") },
+
+ { nxt_string("var n = { toString: function() { return '1.5' } };"
+ "var a = [1,2]; a[1.5] = 5; '' + (n in a) + (delete a[n])"),
+ nxt_string("truetrue") },
+
+ /**/
+
{ nxt_string("a = [1,2,3]; a.concat(4, [5, 6, 7], 8) +''"),
nxt_string("1,2,3,4,5,6,7,8") },
"a.every(function(v, i, a) { return v > 0 })"),
nxt_string("true") },
+ /* Strings. */
+
{ nxt_string("var a = '0123456789' + '012345'"
"var b = 'abcdefghij' + 'klmnop'"
" a = b"),
{ nxt_string("a = 'abcdef'; b = 1 + 2; a[b]"),
nxt_string("d") },
+ /**/
+
+ { nxt_string("var n = { toString: function() { return 1 } }; '12'[n]"),
+ nxt_string("2") },
+
+ { nxt_string("var n = { toString: function() { return '1' } }; '12'[n]"),
+ nxt_string("2") },
+
+ { nxt_string("var n = { toString: function() { return 1 },"
+ " valueOf: function() { return 0 } }; '12'[n]"),
+ nxt_string("2") },
+
+ /**/
+
{ nxt_string("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"),
nxt_string("1077") },