From: hongzhidao Date: Sat, 6 Jul 2019 14:27:14 +0000 (-0400) Subject: Moving value methods to njs_value.c. X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=7d79ab1f4d8516f9031d1c0824506c28d60590cd;p=njs.git Moving value methods to njs_value.c. --- diff --git a/auto/sources b/auto/sources index b24d1a41..d8c078e6 100644 --- a/auto/sources +++ b/auto/sources @@ -30,6 +30,7 @@ NXT_TEST_SRCS=" \ NJS_LIB_SRCS=" \ njs/njs.c \ + njs/njs_value.c \ njs/njs_vm.c \ njs/njs_boolean.c \ njs/njs_number.c \ diff --git a/njs/njs_core.h b/njs/njs_core.h index f8ec3d23..cb04ad18 100644 --- a/njs/njs_core.h +++ b/njs/njs_core.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include diff --git a/njs/njs_value.c b/njs/njs_value.c new file mode 100644 index 00000000..bda00a09 --- /dev/null +++ b/njs/njs_value.c @@ -0,0 +1,418 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include +#include + + +const njs_value_t njs_value_null = njs_value(NJS_NULL, 0, 0.0); +const njs_value_t njs_value_undefined = njs_value(NJS_UNDEFINED, 0, NAN); +const njs_value_t njs_value_false = njs_value(NJS_BOOLEAN, 0, 0.0); +const njs_value_t njs_value_true = njs_value(NJS_BOOLEAN, 1, 1.0); +const njs_value_t njs_value_zero = njs_value(NJS_NUMBER, 0, 0.0); +const njs_value_t njs_value_nan = njs_value(NJS_NUMBER, 0, NAN); +const njs_value_t njs_value_invalid = njs_value(NJS_INVALID, 0, 0.0); + +const njs_value_t njs_string_empty = njs_string(""); +const njs_value_t njs_string_comma = njs_string(","); +const njs_value_t njs_string_null = njs_string("null"); +const njs_value_t njs_string_undefined = njs_string("undefined"); +const njs_value_t njs_string_boolean = njs_string("boolean"); +const njs_value_t njs_string_false = njs_string("false"); +const njs_value_t njs_string_true = njs_string("true"); +const njs_value_t njs_string_number = njs_string("number"); +const njs_value_t njs_string_minus_zero = njs_string("-0"); +const njs_value_t njs_string_minus_infinity = + njs_string("-Infinity"); +const njs_value_t njs_string_plus_infinity = + njs_string("Infinity"); +const njs_value_t njs_string_nan = njs_string("NaN"); +const njs_value_t njs_string_string = njs_string("string"); +const njs_value_t njs_string_object = njs_string("object"); +const njs_value_t njs_string_function = njs_string("function"); +const njs_value_t njs_string_memory_error = njs_string("MemoryError"); + + +void +njs_value_retain(njs_value_t *value) +{ + njs_string_t *string; + + if (njs_is_string(value)) { + + if (value->long_string.external != 0xff) { + string = value->long_string.data; + + nxt_thread_log_debug("retain:%uxD \"%*s\"", string->retain, + value->long_string.size, string->start); + + if (string->retain != 0xffff) { + string->retain++; + } + } + } +} + + +void +njs_value_release(njs_vm_t *vm, njs_value_t *value) +{ + njs_string_t *string; + + if (njs_is_string(value)) { + + if (value->long_string.external != 0xff) { + string = value->long_string.data; + + nxt_thread_log_debug("release:%uxD \"%*s\"", string->retain, + value->long_string.size, string->start); + + if (string->retain != 0xffff) { + string->retain--; + +#if 0 + if (string->retain == 0) { + if ((u_char *) string + sizeof(njs_string_t) + != string->start) + { + nxt_memcache_pool_free(vm->mem_pool, + string->start); + } + + nxt_memcache_pool_free(vm->mem_pool, string); + } +#endif + } + } + } +} + + +nxt_bool_t +njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2) +{ + size_t size, length1, length2; + const u_char *start1, *start2; + + if (val1->type != val2->type) { + return 0; + } + + if (njs_is_numeric(val1)) { + + if (njs_is_undefined(val1)) { + return 1; + } + + /* Infinities are handled correctly by comparision. */ + return (val1->data.u.number == val2->data.u.number); + } + + if (njs_is_string(val1)) { + size = val1->short_string.size; + + if (size != val2->short_string.size) { + return 0; + } + + if (size != NJS_STRING_LONG) { + length1 = val1->short_string.length; + length2 = val2->short_string.length; + + /* + * Using full memcmp() comparison if at least one string + * is a Byte string. + */ + if (length1 != 0 && length2 != 0 && length1 != length2) { + return 0; + } + + start1 = val1->short_string.start; + start2 = val2->short_string.start; + + } else { + size = val1->long_string.size; + + if (size != val2->long_string.size) { + return 0; + } + + length1 = val1->long_string.data->length; + length2 = val2->long_string.data->length; + + /* + * Using full memcmp() comparison if at least one string + * is a Byte string. + */ + if (length1 != 0 && length2 != 0 && length1 != length2) { + return 0; + } + + start1 = val1->long_string.data->start; + start2 = val2->long_string.data->start; + } + + return (memcmp(start1, start2, size) == 0); + } + + return (val1->data.u.object == val2->data.u.object); +} + + +/* + * A hint value is 0 for numbers and 1 for strings. The value chooses + * method calls order specified by ECMAScript 5.1: "valueOf", "toString" + * for numbers and "toString", "valueOf" for strings. + */ + +njs_ret_t +njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) +{ + njs_ret_t ret; + njs_value_t *retval; + njs_function_t *function; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + static const uint32_t hashes[] = { + NJS_VALUE_OF_HASH, + NJS_TO_STRING_HASH, + }; + + static const nxt_str_t names[] = { + nxt_string("valueOf"), + nxt_string("toString"), + }; + + if (!njs_is_primitive(value)) { + retval = &vm->top_frame->trap_scratch; + + if (!njs_is_primitive(retval)) { + + for ( ;; ) { + ret = NXT_ERROR; + + if (njs_is_object(value) && vm->top_frame->trap_tries < 2) { + hint ^= vm->top_frame->trap_tries++; + + lhq.key_hash = hashes[hint]; + lhq.key = names[hint]; + + prop = njs_object_property(vm, value->data.u.object, &lhq); + + if (nxt_fast_path(prop != NULL)) { + + if (!njs_is_function(&prop->value)) { + /* Try the second method. */ + continue; + } + + function = prop->value.data.u.function; + + ret = njs_function_apply(vm, function, value, 1, + (njs_index_t) retval); + /* + * njs_function_apply() can return + * NXT_OK, NJS_APPLIED, NXT_ERROR, NXT_AGAIN. + */ + if (nxt_fast_path(ret == NXT_OK)) { + + if (njs_is_primitive(&vm->retval)) { + retval = &vm->retval; + break; + } + + /* Try the second method. */ + continue; + } + + if (ret == NJS_APPLIED) { + /* + * A user-defined method or continuation have + * been prepared to run. The method will return + * to the current instruction and will restart it. + */ + ret = 0; + } + } + } + + if (ret == NXT_ERROR) { + njs_type_error(vm, + "Cannot convert object to primitive value"); + } + + return ret; + } + } + + *value = *retval; + + njs_set_invalid(retval); + } + + vm->top_frame->trap_tries = 0; + + return 1; +} + + +njs_array_t * +njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, + njs_object_enum_t kind, nxt_bool_t all) +{ + njs_object_value_t obj_val; + + if (njs_is_object(value)) { + return njs_object_enumerate(vm, value->data.u.object, kind, all); + } + + if (value->type != NJS_STRING) { + return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); + } + + obj_val.object = vm->string_object; + obj_val.value = *value; + + return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, all); +} + + +njs_array_t * +njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, + njs_object_enum_t kind, nxt_bool_t all) +{ + njs_object_value_t obj_val; + + if (njs_is_object(value)) { + return njs_object_own_enumerate(vm, value->data.u.object, kind, all); + } + + if (value->type != NJS_STRING) { + return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); + } + + obj_val.object = vm->string_object; + obj_val.value = *value; + + return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, all); +} + + +const char * +njs_type_string(njs_value_type_t type) +{ + switch (type) { + case NJS_NULL: + return "null"; + + case NJS_UNDEFINED: + return "undefined"; + + case NJS_BOOLEAN: + return "boolean"; + + case NJS_NUMBER: + return "number"; + + case NJS_STRING: + return "string"; + + case NJS_EXTERNAL: + return "external"; + + case NJS_INVALID: + return "invalid"; + + case NJS_OBJECT: + return "object"; + + case NJS_ARRAY: + return "array"; + + case NJS_OBJECT_BOOLEAN: + return "object boolean"; + + case NJS_OBJECT_NUMBER: + return "object number"; + + case NJS_OBJECT_STRING: + return "object string"; + + case NJS_FUNCTION: + return "function"; + + case NJS_REGEXP: + return "regexp"; + + case NJS_DATE: + return "date"; + + case NJS_OBJECT_ERROR: + return "error"; + + case NJS_OBJECT_EVAL_ERROR: + return "eval error"; + + case NJS_OBJECT_INTERNAL_ERROR: + return "internal error"; + + case NJS_OBJECT_RANGE_ERROR: + return "range error"; + + case NJS_OBJECT_REF_ERROR: + return "reference error"; + + case NJS_OBJECT_SYNTAX_ERROR: + return "syntax error"; + + case NJS_OBJECT_TYPE_ERROR: + return "type error"; + + case NJS_OBJECT_URI_ERROR: + return "uri error"; + + default: + return NULL; + } +} + + +const char * +njs_arg_type_string(uint8_t arg) +{ + switch (arg) { + case NJS_SKIP_ARG: + return "skip"; + + case NJS_NUMBER_ARG: + return "number"; + + case NJS_INTEGER_ARG: + return "integer"; + + case NJS_STRING_ARG: + return "string"; + + case NJS_OBJECT_ARG: + return "object"; + + case NJS_STRING_OBJECT_ARG: + return "string object"; + + case NJS_FUNCTION_ARG: + return "function"; + + case NJS_REGEXP_ARG: + return "regexp"; + + case NJS_DATE_ARG: + return "date"; + + default: + return "unknown"; + } +} diff --git a/njs/njs_value.h b/njs/njs_value.h new file mode 100644 index 00000000..b2c98846 --- /dev/null +++ b/njs/njs_value.h @@ -0,0 +1,660 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_VALUE_H_INCLUDED_ +#define _NJS_VALUE_H_INCLUDED_ + + +#include +#include +#include +#include +#include +#include + +#include + + +/* + * The order of the enum is used in njs_vmcode_typeof() + * and njs_object_prototype_to_string(). + */ + +typedef enum { + NJS_NULL = 0x00, + NJS_UNDEFINED = 0x01, + + /* The order of the above type is used in njs_is_null_or_undefined(). */ + + NJS_BOOLEAN = 0x02, + /* + * The order of the above type is used in + * njs_is_null_or_undefined_or_boolean(). + */ + NJS_NUMBER = 0x03, + /* + * The order of the above type is used in njs_is_numeric(). + * Booleans, null and void values can be used in mathematical operations: + * a numeric value of the true value is one, + * a numeric value of the null and false values is zero, + * a numeric value of the void value is NaN. + */ + NJS_STRING = 0x04, + + /* The order of the above type is used in njs_is_primitive(). */ + + NJS_DATA = 0x05, + + /* The type is external code. */ + NJS_EXTERNAL = 0x06, + + /* + * The invalid value type is used: + * for uninitialized array members, + * to detect non-declared explicitly or implicitly variables, + * for native property getters. + */ + NJS_INVALID = 0x07, + + /* + * The object types are >= NJS_OBJECT, this is used in njs_is_object(). + * NJS_OBJECT_BOOLEAN, NJS_OBJECT_NUMBER, and NJS_OBJECT_STRING must be + * in the same order as NJS_BOOLEAN, NJS_NUMBER, and NJS_STRING. It is + * used in njs_primitive_prototype_index(). The order of object types + * is used in vm->prototypes and vm->constructors arrays. + */ + NJS_OBJECT = 0x10, + NJS_ARRAY = 0x11, + NJS_OBJECT_BOOLEAN = 0x12, + NJS_OBJECT_NUMBER = 0x13, + NJS_OBJECT_STRING = 0x14, + NJS_FUNCTION = 0x15, + NJS_REGEXP = 0x16, + NJS_DATE = 0x17, + NJS_OBJECT_ERROR = 0x18, + NJS_OBJECT_EVAL_ERROR = 0x19, + NJS_OBJECT_INTERNAL_ERROR = 0x1a, + NJS_OBJECT_RANGE_ERROR = 0x1b, + NJS_OBJECT_REF_ERROR = 0x1c, + NJS_OBJECT_SYNTAX_ERROR = 0x1d, + NJS_OBJECT_TYPE_ERROR = 0x1e, + NJS_OBJECT_URI_ERROR = 0x1f, + NJS_OBJECT_VALUE = 0x20, +#define NJS_TYPE_MAX (NJS_OBJECT_VALUE + 1) +} njs_value_type_t; + + +/* + * njs_prop_handler_t operates as a property getter and/or setter. + * The handler receives NULL setval if it is invoked in GET context and + * non-null otherwise. + * + * njs_prop_handler_t is expected to return: + * NXT_OK - handler executed successfully; + * NXT_ERROR - some error, vm->retval contains appropriate exception; + * NXT_DECLINED - handler was applied to inappropriate object. + */ +typedef njs_ret_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_value_t *value, + njs_value_t *setval, njs_value_t *retval); +typedef njs_ret_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t retval); + + +typedef struct njs_string_s njs_string_t; +typedef struct njs_object_s njs_object_t; +typedef struct njs_object_value_s njs_object_value_t; +typedef struct njs_function_lambda_s njs_function_lambda_t; +typedef struct njs_regexp_pattern_s njs_regexp_pattern_t; +typedef struct njs_array_s njs_array_t; +typedef struct njs_regexp_s njs_regexp_t; +typedef struct njs_date_s njs_date_t; +typedef struct njs_property_next_s njs_property_next_t; +typedef struct njs_object_init_s njs_object_init_t; + + +union njs_value_s { + /* + * The njs_value_t size is 16 bytes and must be aligned to 16 bytes + * to provide 4 bits to encode scope in njs_index_t. This space is + * used to store short strings. The maximum size of a short string + * is 14 (NJS_STRING_SHORT). If the short_string.size field is 15 + * (NJS_STRING_LONG) then the size is in the long_string.size field + * and the long_string.data field points to a long string. + * + * The number of the string types is limited to 2 types to minimize + * overhead of processing string fields. It is also possible to add + * strings with size from 14 to 254 which size and length are stored in + * the string_size and string_length byte wide fields. This will lessen + * the maximum size of short string to 13. + */ + struct { + njs_value_type_t type:8; /* 6 bits */ + /* + * The truth field is set during value assignment and then can be + * quickly tested by logical and conditional operations regardless + * of value type. The truth field coincides with short_string.size + * and short_string.length so when string size and length are zero + * the string's value is false. + */ + uint8_t truth; + + uint16_t _spare1; + uint32_t _spare2; + + union { + double number; + njs_object_t *object; + njs_array_t *array; + njs_object_value_t *object_value; + njs_function_t *function; + njs_function_lambda_t *lambda; + njs_regexp_t *regexp; + njs_date_t *date; + njs_prop_handler_t prop_handler; + njs_value_t *value; + njs_property_next_t *next; + void *data; + } u; + } data; + + struct { + njs_value_type_t type:8; /* 6 bits */ + +#define NJS_STRING_SHORT 14 +#define NJS_STRING_LONG 15 + + uint8_t size:4; + uint8_t length:4; + + u_char start[NJS_STRING_SHORT]; + } short_string; + + struct { + njs_value_type_t type:8; /* 6 bits */ + uint8_t truth; + + /* 0xff if data is external string. */ + uint8_t external; + uint8_t _spare; + + uint32_t size; + njs_string_t *data; + } long_string; + + struct { + njs_value_type_t type:8; /* 6 bits */ + uint8_t truth; + + uint16_t _spare; + + uint32_t index; + const njs_extern_t *proto; + } external; + + njs_value_type_t type:8; /* 6 bits */ +}; + + +struct njs_object_s { + /* A private hash of njs_object_prop_t. */ + nxt_lvlhsh_t hash; + + /* A shared hash of njs_object_prop_t. */ + nxt_lvlhsh_t shared_hash; + + /* An object __proto__. */ + njs_object_t *__proto__; + + /* The type is used in constructor prototypes. */ + njs_value_type_t type:8; + uint8_t shared; /* 1 bit */ + uint8_t extensible; /* 1 bit */ +}; + + +struct njs_object_value_s { + njs_object_t object; + /* The value can be unaligned since it never used in nJSVM operations. */ + njs_value_t value; +}; + + +struct njs_array_s { + njs_object_t object; + uint32_t size; + uint32_t length; + njs_value_t *start; + njs_value_t *data; +}; + + +typedef struct { + union { + uint32_t count; + njs_value_t values; + } u; + + njs_value_t values[1]; +} njs_closure_t; + + +#define NJS_ARGS_TYPES_MAX 5 + +struct njs_function_s { + njs_object_t object; + + uint8_t args_types[NJS_ARGS_TYPES_MAX]; + uint8_t args_offset; + uint8_t continuation_size; + + /* Function is a closure. */ + uint8_t closure:1; + + uint8_t native:1; + uint8_t ctor:1; + + union { + njs_function_lambda_t *lambda; + njs_function_native_t native; + } u; + + njs_value_t *bound; +#if (NXT_SUNC) + njs_closure_t *closures[1]; +#else + njs_closure_t *closures[]; +#endif +}; + + +struct njs_regexp_s { + njs_object_t object; + uint32_t last_index; + njs_regexp_pattern_t *pattern; + /* + * This string value can be unaligned since + * it never used in nJSVM operations. + */ + njs_value_t string; +}; + + +struct njs_date_s { + njs_object_t object; + double time; +}; + + +typedef union { + njs_object_t object; + njs_object_value_t object_value; + njs_array_t array; + njs_function_t function; + njs_regexp_t regexp; + njs_date_t date; +} njs_object_prototype_t; + + +typedef enum { + NJS_ENUM_KEYS, + NJS_ENUM_VALUES, + NJS_ENUM_BOTH, +} njs_object_enum_t; + + +#define njs_value(_type, _truth, _number) { \ + .data = { \ + .type = _type, \ + .truth = _truth, \ + .u.number = _number, \ + } \ +} + + +#define njs_string(s) { \ + .short_string = { \ + .type = NJS_STRING, \ + .size = nxt_length(s), \ + .length = nxt_length(s), \ + .start = s, \ + } \ +} + + +/* NJS_STRING_LONG is set for both big and little endian platforms. */ + +#define njs_long_string(s) { \ + .long_string = { \ + .type = NJS_STRING, \ + .truth = (NJS_STRING_LONG << 4) | NJS_STRING_LONG, \ + .size = nxt_length(s), \ + .data = & (njs_string_t) { \ + .start = (u_char *) s, \ + .length = nxt_length(s), \ + } \ + } \ +} + + +#define njs_native_function(_function, _size, ...) { \ + .data = { \ + .type = NJS_FUNCTION, \ + .truth = 1, \ + .u.function = & (njs_function_t) { \ + .native = 1, \ + .continuation_size = _size, \ + .args_types = { __VA_ARGS__ }, \ + .args_offset = 1, \ + .u.native = _function, \ + .object = { .type = NJS_FUNCTION, \ + .shared = 1, \ + .extensible = 1 }, \ + } \ + } \ +} + + +#define njs_prop_handler(_handler) { \ + .data = { \ + .type = NJS_INVALID, \ + .truth = 1, \ + .u = { .prop_handler = _handler } \ + } \ +} + + +#define njs_is_null(value) \ + ((value)->type == NJS_NULL) + + +#define njs_is_undefined(value) \ + ((value)->type == NJS_UNDEFINED) + + +#define njs_is_null_or_undefined(value) \ + ((value)->type <= NJS_UNDEFINED) + + +#define njs_is_boolean(value) \ + ((value)->type == NJS_BOOLEAN) + + +#define njs_is_null_or_undefined_or_boolean(value) \ + ((value)->type <= NJS_BOOLEAN) + + +#define njs_is_true(value) \ + ((value)->data.truth != 0) + + +#define njs_is_number(value) \ + ((value)->type == NJS_NUMBER) + + +/* Testing for NaN first generates a better code at least on i386/amd64. */ + +#define njs_is_number_true(num) \ + (!isnan(num) && num != 0) + + +#define njs_is_numeric(value) \ + ((value)->type <= NJS_NUMBER) + + +#define njs_is_string(value) \ + ((value)->type == NJS_STRING) + +#define njs_is_error(value) \ + ((value)->type >= NJS_OBJECT_ERROR \ + && (value)->type <= NJS_OBJECT_URI_ERROR) + + +/* + * The truth field coincides with short_string.size and short_string.length + * so when string size and length are zero the string's value is false and + * otherwise is true. + */ +#define njs_string_truth(value, size) + + +#define njs_string_get(value, str) \ + do { \ + if ((value)->short_string.size != NJS_STRING_LONG) { \ + (str)->length = (value)->short_string.size; \ + (str)->start = (u_char *) (value)->short_string.start; \ + \ + } else { \ + (str)->length = (value)->long_string.size; \ + (str)->start = (u_char *) (value)->long_string.data->start; \ + } \ + } while (0) + + +#define njs_string_short_start(value) \ + (value)->short_string.start + + +#define njs_string_short_set(value, _size, _length) \ + do { \ + (value)->type = NJS_STRING; \ + njs_string_truth(value, _size); \ + (value)->short_string.size = _size; \ + (value)->short_string.length = _length; \ + } while (0) + + +#define njs_string_length_set(value, _length) \ + do { \ + if ((value)->short_string.size != NJS_STRING_LONG) { \ + (value)->short_string.length = length; \ + \ + } else { \ + (value)->long_string.data->length = length; \ + } \ + } while (0) + +#define njs_is_primitive(value) \ + ((value)->type <= NJS_STRING) + + +#define njs_is_data(value) \ + ((value)->type == NJS_DATA) + + +#define njs_is_object(value) \ + ((value)->type >= NJS_OBJECT) + + +#define njs_is_object_value(value) \ + ((value)->type == NJS_OBJECT_VALUE) + + +#define njs_object_value_type(type) \ + (type + NJS_OBJECT) + + +#define njs_is_array(value) \ + ((value)->type == NJS_ARRAY) + + +#define njs_is_function(value) \ + ((value)->type == NJS_FUNCTION) + + +#define njs_is_regexp(value) \ + ((value)->type == NJS_REGEXP) + + +#define njs_is_date(value) \ + ((value)->type == NJS_DATE) + + +#define njs_is_external(value) \ + ((value)->type == NJS_EXTERNAL) + + +#define njs_is_valid(value) \ + ((value)->type != NJS_INVALID) + + +#define njs_bool(value) \ + ((value)->data.truth) + + +#define njs_number(value) \ + ((value)->data.u.number) + + +#define njs_data(value) \ + ((value)->data.u.data) + + +#define njs_function(value) \ + ((value)->data.u.function) + + +#define njs_object(value) \ + ((value)->data.u.object) + + +#define njs_object_hash(value) \ + (&(value)->data.u.object->hash) + + +#define njs_array(value) \ + ((value)->data.u.array) + + +#define njs_array_len(value) \ + ((value)->data.u.array->length) + + +#define njs_array_start(value) \ + ((value)->data.u.array->start) + + +#define njs_set_undefined(value) \ + *(value) = njs_value_undefined + + +#define njs_set_boolean(value, yn) \ + *(value) = yn ? njs_value_true : njs_value_false + + +#define njs_set_true(value) \ + *(value) = njs_value_true + + +#define njs_set_false(value) \ + *(value) = njs_value_false + + +nxt_inline void +njs_set_number(njs_value_t *value, double num) +{ + value->data.u.number = num; + value->type = NJS_NUMBER; + value->data.truth = njs_is_number_true(num); +} + + +nxt_inline void +njs_set_data(njs_value_t *value, void *data) +{ + value->data.u.data = data; + value->type = NJS_DATA; + value->data.truth = 1; +} + + +nxt_inline void +njs_set_object(njs_value_t *value, njs_object_t *object) +{ + value->data.u.object = object; + value->type = NJS_OBJECT; + value->data.truth = 1; +} + + +nxt_inline void +njs_set_array(njs_value_t *value, njs_array_t *array) +{ + value->data.u.array = array; + value->type = NJS_ARRAY; + value->data.truth = 1; +} + + +#define njs_set_invalid(value) \ + (value)->type = NJS_INVALID + + +#if 0 /* GC: todo */ + +#define njs_retain(value) \ + do { \ + if ((value)->data.truth == NJS_STRING_LONG) { \ + njs_value_retain(value); \ + } \ + } while (0) + + +#define njs_release(vm, value) \ + do { \ + if ((value)->data.truth == NJS_STRING_LONG) { \ + njs_value_release((vm), (value)); \ + } \ + } while (0) + +#else + +#define njs_retain(value) +#define njs_release(vm, value) + +#endif + + +void njs_value_retain(njs_value_t *value); +void njs_value_release(njs_vm_t *vm, njs_value_t *value); +nxt_bool_t njs_values_strict_equal(const njs_value_t *val1, + const njs_value_t *val2); +njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, + nxt_uint_t hint); +njs_array_t *njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, + njs_object_enum_t kind, nxt_bool_t all); +njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, + njs_object_enum_t kind, nxt_bool_t all); +const char *njs_type_string(njs_value_type_t type); +const char *njs_arg_type_string(uint8_t arg); + + +extern const njs_value_t njs_value_null; +extern const njs_value_t njs_value_undefined; +extern const njs_value_t njs_value_false; +extern const njs_value_t njs_value_true; +extern const njs_value_t njs_value_zero; +extern const njs_value_t njs_value_nan; +extern const njs_value_t njs_value_invalid; + +extern const njs_value_t njs_string_empty; +extern const njs_value_t njs_string_comma; +extern const njs_value_t njs_string_null; +extern const njs_value_t njs_string_undefined; +extern const njs_value_t njs_string_boolean; +extern const njs_value_t njs_string_false; +extern const njs_value_t njs_string_true; +extern const njs_value_t njs_string_number; +extern const njs_value_t njs_string_minus_zero; +extern const njs_value_t njs_string_minus_infinity; +extern const njs_value_t njs_string_plus_infinity; +extern const njs_value_t njs_string_nan; +extern const njs_value_t njs_string_string; +extern const njs_value_t njs_string_object; +extern const njs_value_t njs_string_function; +extern const njs_value_t njs_string_memory_error; + + +#endif /* _NJS_VALUE_H_INCLUDED_ */ diff --git a/njs/njs_vm.c b/njs/njs_vm.c index e64d8676..a5bbabca 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -52,8 +52,6 @@ static njs_ret_t njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *inlvd2); static njs_ret_t njs_vmcode_primitive_argument(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *inlvd2); -static njs_ret_t njs_primitive_value(njs_vm_t *vm, njs_value_t *value, - nxt_uint_t hint); static njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2); static njs_ret_t njs_object_value_to_string(njs_vm_t *vm, njs_value_t *value); @@ -65,36 +63,6 @@ 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 njs_value_t njs_value_null = njs_value(NJS_NULL, 0, 0.0); -const njs_value_t njs_value_undefined = njs_value(NJS_UNDEFINED, 0, NAN); -const njs_value_t njs_value_false = njs_value(NJS_BOOLEAN, 0, 0.0); -const njs_value_t njs_value_true = njs_value(NJS_BOOLEAN, 1, 1.0); -const njs_value_t njs_value_zero = njs_value(NJS_NUMBER, 0, 0.0); -const njs_value_t njs_value_nan = njs_value(NJS_NUMBER, 0, NAN); -const njs_value_t njs_value_invalid = njs_value(NJS_INVALID, 0, 0.0); - - -const njs_value_t njs_string_empty = njs_string(""); -const njs_value_t njs_string_comma = njs_string(","); -const njs_value_t njs_string_null = njs_string("null"); -const njs_value_t njs_string_undefined = njs_string("undefined"); -const njs_value_t njs_string_boolean = njs_string("boolean"); -const njs_value_t njs_string_false = njs_string("false"); -const njs_value_t njs_string_true = njs_string("true"); -const njs_value_t njs_string_number = njs_string("number"); -const njs_value_t njs_string_minus_zero = njs_string("-0"); -const njs_value_t njs_string_minus_infinity = - njs_string("-Infinity"); -const njs_value_t njs_string_plus_infinity = - njs_string("Infinity"); -const njs_value_t njs_string_nan = njs_string("NaN"); -const njs_value_t njs_string_string = njs_string("string"); -const njs_value_t njs_string_object = njs_string("object"); -const njs_value_t njs_string_function = njs_string("function"); - -const njs_value_t njs_string_memory_error = njs_string("MemoryError"); - - 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"); @@ -255,61 +223,6 @@ start: } -void -njs_value_retain(njs_value_t *value) -{ - njs_string_t *string; - - if (njs_is_string(value)) { - - if (value->long_string.external != 0xff) { - string = value->long_string.data; - - nxt_thread_log_debug("retain:%uxD \"%*s\"", string->retain, - value->long_string.size, string->start); - - if (string->retain != 0xffff) { - string->retain++; - } - } - } -} - - -void -njs_value_release(njs_vm_t *vm, njs_value_t *value) -{ - njs_string_t *string; - - if (njs_is_string(value)) { - - if (value->long_string.external != 0xff) { - string = value->long_string.data; - - nxt_thread_log_debug("release:%uxD \"%*s\"", string->retain, - value->long_string.size, string->start); - - if (string->retain != 0xffff) { - string->retain--; - -#if 0 - if (string->retain == 0) { - if ((u_char *) string + sizeof(njs_string_t) - != string->start) - { - nxt_memcache_pool_free(vm->mem_pool, - string->start); - } - - nxt_memcache_pool_free(vm->mem_pool, string); - } -#endif - } - } - } -} - - njs_ret_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { @@ -1748,77 +1661,6 @@ njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2) } -nxt_bool_t -njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2) -{ - size_t size, length1, length2; - const u_char *start1, *start2; - - if (val1->type != val2->type) { - return 0; - } - - if (njs_is_numeric(val1)) { - - if (njs_is_undefined(val1)) { - return 1; - } - - /* Infinities are handled correctly by comparision. */ - return (val1->data.u.number == val2->data.u.number); - } - - if (njs_is_string(val1)) { - size = val1->short_string.size; - - if (size != val2->short_string.size) { - return 0; - } - - if (size != NJS_STRING_LONG) { - length1 = val1->short_string.length; - length2 = val2->short_string.length; - - /* - * Using full memcmp() comparison if at least one string - * is a Byte string. - */ - if (length1 != 0 && length2 != 0 && length1 != length2) { - return 0; - } - - start1 = val1->short_string.start; - start2 = val2->short_string.start; - - } else { - size = val1->long_string.size; - - if (size != val2->long_string.size) { - return 0; - } - - length1 = val1->long_string.data->length; - length2 = val2->long_string.data->length; - - /* - * Using full memcmp() comparison if at least one string - * is a Byte string. - */ - if (length1 != 0 && length2 != 0 && length1 != length2) { - return 0; - } - - start1 = val1->long_string.data->start; - start2 = val2->long_string.data->start; - } - - return (memcmp(start1, start2, size) == 0); - } - - return (val1->data.u.object == val2->data.u.object); -} - - njs_ret_t njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld) { @@ -2094,122 +1936,6 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) } -const char * -njs_type_string(njs_value_type_t type) -{ - switch (type) { - case NJS_NULL: - return "null"; - - case NJS_UNDEFINED: - return "undefined"; - - case NJS_BOOLEAN: - return "boolean"; - - case NJS_NUMBER: - return "number"; - - case NJS_STRING: - return "string"; - - case NJS_EXTERNAL: - return "external"; - - case NJS_INVALID: - return "invalid"; - - case NJS_OBJECT: - return "object"; - - case NJS_ARRAY: - return "array"; - - case NJS_OBJECT_BOOLEAN: - return "object boolean"; - - case NJS_OBJECT_NUMBER: - return "object number"; - - case NJS_OBJECT_STRING: - return "object string"; - - case NJS_FUNCTION: - return "function"; - - case NJS_REGEXP: - return "regexp"; - - case NJS_DATE: - return "date"; - - case NJS_OBJECT_ERROR: - return "error"; - - case NJS_OBJECT_EVAL_ERROR: - return "eval error"; - - case NJS_OBJECT_INTERNAL_ERROR: - return "internal error"; - - case NJS_OBJECT_RANGE_ERROR: - return "range error"; - - case NJS_OBJECT_REF_ERROR: - return "reference error"; - - case NJS_OBJECT_SYNTAX_ERROR: - return "syntax error"; - - case NJS_OBJECT_TYPE_ERROR: - return "type error"; - - case NJS_OBJECT_URI_ERROR: - return "uri error"; - - default: - return NULL; - } -} - - -const char * -njs_arg_type_string(uint8_t arg) -{ - switch (arg) { - case NJS_SKIP_ARG: - return "skip"; - - case NJS_NUMBER_ARG: - return "number"; - - case NJS_INTEGER_ARG: - return "integer"; - - case NJS_STRING_ARG: - return "string"; - - case NJS_OBJECT_ARG: - return "object"; - - case NJS_STRING_OBJECT_ARG: - return "string object"; - - case NJS_FUNCTION_ARG: - return "function"; - - case NJS_REGEXP_ARG: - return "regexp"; - - case NJS_DATE_ARG: - return "date"; - - default: - return "unknown"; - } -} - - njs_ret_t njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) { @@ -2728,7 +2454,7 @@ njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg) value = &vm->top_frame->trap_values[(uintptr_t) narg]; - ret = njs_primitive_value(vm, value, 0); + ret = njs_value_to_primitive(vm, value, 0); if (nxt_fast_path(ret > 0)) { @@ -2757,7 +2483,7 @@ njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg) value = &vm->top_frame->trap_values[(uintptr_t) narg]; - ret = njs_primitive_value(vm, value, 1); + ret = njs_value_to_primitive(vm, value, 1); if (nxt_fast_path(ret > 0)) { ret = njs_primitive_value_to_string(vm, value, value); @@ -2787,7 +2513,7 @@ njs_vmcode_addition_primitive(njs_vm_t *vm, njs_value_t *invld, */ hint = njs_is_date(value); - ret = njs_primitive_value(vm, value, hint); + ret = njs_value_to_primitive(vm, value, hint); if (nxt_fast_path(ret > 0)) { return sizeof(njs_vmcode_1addr_t); @@ -2806,7 +2532,7 @@ njs_vmcode_comparison_primitive(njs_vm_t *vm, njs_value_t *invld, value = &vm->top_frame->trap_values[(uintptr_t) narg]; - ret = njs_primitive_value(vm, value, 0); + ret = njs_value_to_primitive(vm, value, 0); if (nxt_fast_path(ret > 0)) { return sizeof(njs_vmcode_1addr_t); @@ -2826,7 +2552,7 @@ njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1, value = &vm->top_frame->trap_values[0]; - ret = njs_primitive_value(vm, value, 0); + ret = njs_value_to_primitive(vm, value, 0); if (nxt_fast_path(ret > 0)) { @@ -2861,7 +2587,7 @@ njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1, value = &vm->top_frame->trap_values[0]; - ret = njs_primitive_value(vm, value, 1); + ret = njs_value_to_primitive(vm, value, 1); if (nxt_fast_path(ret > 0)) { ret = njs_primitive_value_to_string(vm, value, value); @@ -2887,7 +2613,7 @@ njs_vmcode_primitive_argument(njs_vm_t *vm, njs_value_t *invld1, value = &vm->top_frame->trap_values[0]; - ret = njs_primitive_value(vm, value, 0); + ret = njs_value_to_primitive(vm, value, 0); if (nxt_fast_path(ret > 0)) { *vm->top_frame->trap_values[1].data.u.value = *value; @@ -2945,146 +2671,6 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) } -/* - * A hint value is 0 for numbers and 1 for strings. The value chooses - * method calls order specified by ECMAScript 5.1: "valueOf", "toString" - * for numbers and "toString", "valueOf" for strings. - */ - -static njs_ret_t -njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint) -{ - njs_ret_t ret; - njs_value_t *retval; - njs_function_t *function; - njs_object_prop_t *prop; - nxt_lvlhsh_query_t lhq; - - static const uint32_t hashes[] = { - NJS_VALUE_OF_HASH, - NJS_TO_STRING_HASH, - }; - - static const nxt_str_t names[] = { - nxt_string("valueOf"), - nxt_string("toString"), - }; - - if (!njs_is_primitive(value)) { - retval = &vm->top_frame->trap_scratch; - - if (!njs_is_primitive(retval)) { - - for ( ;; ) { - ret = NXT_ERROR; - - if (njs_is_object(value) && vm->top_frame->trap_tries < 2) { - hint ^= vm->top_frame->trap_tries++; - - lhq.key_hash = hashes[hint]; - lhq.key = names[hint]; - - prop = njs_object_property(vm, value->data.u.object, &lhq); - - if (nxt_fast_path(prop != NULL)) { - - if (!njs_is_function(&prop->value)) { - /* Try the second method. */ - continue; - } - - function = prop->value.data.u.function; - - ret = njs_function_apply(vm, function, value, 1, - (njs_index_t) retval); - /* - * njs_function_apply() can return - * NXT_OK, NJS_APPLIED, NXT_ERROR, NXT_AGAIN. - */ - if (nxt_fast_path(ret == NXT_OK)) { - - if (njs_is_primitive(&vm->retval)) { - retval = &vm->retval; - break; - } - - /* Try the second method. */ - continue; - } - - if (ret == NJS_APPLIED) { - /* - * A user-defined method or continuation have - * been prepared to run. The method will return - * to the current instruction and will restart it. - */ - ret = 0; - } - } - } - - if (ret == NXT_ERROR) { - njs_type_error(vm, - "Cannot convert object to primitive value"); - } - - return ret; - } - } - - *value = *retval; - - njs_set_invalid(retval); - } - - vm->top_frame->trap_tries = 0; - - return 1; -} - - -njs_array_t * -njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, nxt_bool_t all) -{ - njs_object_value_t obj_val; - - if (njs_is_object(value)) { - return njs_object_enumerate(vm, value->data.u.object, kind, all); - } - - if (value->type != NJS_STRING) { - return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); - } - - obj_val.object = vm->string_object; - obj_val.value = *value; - - return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, all); -} - - -njs_array_t * -njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, nxt_bool_t all) -{ - njs_object_value_t obj_val; - - if (njs_is_object(value)) { - return njs_object_own_enumerate(vm, value->data.u.object, kind, all); - } - - if (value->type != NJS_STRING) { - return njs_array_alloc(vm, 0, NJS_ARRAY_SPARE); - } - - obj_val.object = vm->string_object; - obj_val.value = *value; - - return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, all); -} - - njs_ret_t njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src) { @@ -3198,7 +2784,7 @@ njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1, { njs_ret_t ret; - ret = njs_primitive_value(vm, &vm->top_frame->trap_values[0], 1); + ret = njs_value_to_primitive(vm, &vm->top_frame->trap_values[0], 1); if (nxt_fast_path(ret > 0)) { return NJS_STOP; @@ -3380,3 +2966,4 @@ njs_lvlhsh_free(void *data, void *p, size_t size) { nxt_mp_free(data, p); } + diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 6fc9c4b5..e6029c11 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -8,16 +8,6 @@ #define _NJS_VM_H_INCLUDED_ -#include -#include -#include -#include -#include -#include - -#include - - #define NJS_MAX_STACK_SIZE (16 * 1024 * 1024) /* @@ -73,291 +63,12 @@ typedef enum { #define NJS_PROPERTY_QUERY_DELETE 2 -/* - * The order of the enum is used in njs_vmcode_typeof() - * and njs_object_prototype_to_string(). - */ - -typedef enum { - NJS_NULL = 0x00, - NJS_UNDEFINED = 0x01, - - /* The order of the above type is used in njs_is_null_or_undefined(). */ - - NJS_BOOLEAN = 0x02, - /* - * The order of the above type is used in - * njs_is_null_or_undefined_or_boolean(). - */ - NJS_NUMBER = 0x03, - /* - * The order of the above type is used in njs_is_numeric(). - * Booleans, null and void values can be used in mathematical operations: - * a numeric value of the true value is one, - * a numeric value of the null and false values is zero, - * a numeric value of the void value is NaN. - */ - NJS_STRING = 0x04, - - /* The order of the above type is used in njs_is_primitive(). */ - - NJS_DATA = 0x05, - - /* The type is external code. */ - NJS_EXTERNAL = 0x06, - - /* - * The invalid value type is used: - * for uninitialized array members, - * to detect non-declared explicitly or implicitly variables, - * for native property getters. - */ - NJS_INVALID = 0x07, - - /* - * The object types are >= NJS_OBJECT, this is used in njs_is_object(). - * NJS_OBJECT_BOOLEAN, NJS_OBJECT_NUMBER, and NJS_OBJECT_STRING must be - * in the same order as NJS_BOOLEAN, NJS_NUMBER, and NJS_STRING. It is - * used in njs_primitive_prototype_index(). The order of object types - * is used in vm->prototypes and vm->constructors arrays. - */ - NJS_OBJECT = 0x10, - NJS_ARRAY = 0x11, - NJS_OBJECT_BOOLEAN = 0x12, - NJS_OBJECT_NUMBER = 0x13, - NJS_OBJECT_STRING = 0x14, - NJS_FUNCTION = 0x15, - NJS_REGEXP = 0x16, - NJS_DATE = 0x17, - NJS_OBJECT_ERROR = 0x18, - NJS_OBJECT_EVAL_ERROR = 0x19, - NJS_OBJECT_INTERNAL_ERROR = 0x1a, - NJS_OBJECT_RANGE_ERROR = 0x1b, - NJS_OBJECT_REF_ERROR = 0x1c, - NJS_OBJECT_SYNTAX_ERROR = 0x1d, - NJS_OBJECT_TYPE_ERROR = 0x1e, - NJS_OBJECT_URI_ERROR = 0x1f, - NJS_OBJECT_VALUE = 0x20, -#define NJS_TYPE_MAX (NJS_OBJECT_VALUE + 1) -} njs_value_type_t; - - -typedef struct njs_parser_s njs_parser_t; -typedef struct njs_generator_s njs_generator_t; - -/* - * njs_prop_handler_t operates as a property getter and/or setter. - * The handler receives NULL setval if it is invoked in GET context and - * non-null otherwise. - * - * njs_prop_handler_t is expected to return: - * NXT_OK - handler executed successfully; - * NXT_ERROR - some error, vm->retval contains appropriate exception; - * NXT_DECLINED - handler was applied to inappropriate object. - */ -typedef njs_ret_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_value_t *value, - njs_value_t *setval, njs_value_t *retval); -typedef njs_ret_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args, - nxt_uint_t nargs, njs_index_t retval); - - -typedef struct njs_string_s njs_string_t; -typedef struct njs_object_s njs_object_t; -typedef struct njs_object_init_s njs_object_init_t; -typedef struct njs_object_value_s njs_object_value_t; -typedef struct njs_array_s njs_array_t; -typedef struct njs_function_lambda_s njs_function_lambda_t; -typedef struct njs_regexp_s njs_regexp_t; -typedef struct njs_regexp_pattern_s njs_regexp_pattern_t; -typedef struct njs_date_s njs_date_t; typedef struct njs_frame_s njs_frame_t; typedef struct njs_native_frame_s njs_native_frame_t; -typedef struct njs_property_next_s njs_property_next_t; +typedef struct njs_parser_s njs_parser_t; typedef struct njs_parser_scope_s njs_parser_scope_t; typedef struct njs_parser_node_s njs_parser_node_t; - - -union njs_value_s { - /* - * The njs_value_t size is 16 bytes and must be aligned to 16 bytes - * to provide 4 bits to encode scope in njs_index_t. This space is - * used to store short strings. The maximum size of a short string - * is 14 (NJS_STRING_SHORT). If the short_string.size field is 15 - * (NJS_STRING_LONG) then the size is in the long_string.size field - * and the long_string.data field points to a long string. - * - * The number of the string types is limited to 2 types to minimize - * overhead of processing string fields. It is also possible to add - * strings with size from 14 to 254 which size and length are stored in - * the string_size and string_length byte wide fields. This will lessen - * the maximum size of short string to 13. - */ - struct { - njs_value_type_t type:8; /* 6 bits */ - /* - * The truth field is set during value assignment and then can be - * quickly tested by logical and conditional operations regardless - * of value type. The truth field coincides with short_string.size - * and short_string.length so when string size and length are zero - * the string's value is false. - */ - uint8_t truth; - - uint16_t _spare1; - uint32_t _spare2; - - union { - double number; - njs_object_t *object; - njs_array_t *array; - njs_object_value_t *object_value; - njs_function_t *function; - njs_function_lambda_t *lambda; - njs_regexp_t *regexp; - njs_date_t *date; - njs_prop_handler_t prop_handler; - njs_value_t *value; - njs_property_next_t *next; - void *data; - } u; - } data; - - struct { - njs_value_type_t type:8; /* 6 bits */ - -#define NJS_STRING_SHORT 14 -#define NJS_STRING_LONG 15 - - uint8_t size:4; - uint8_t length:4; - - u_char start[NJS_STRING_SHORT]; - } short_string; - - struct { - njs_value_type_t type:8; /* 6 bits */ - uint8_t truth; - - /* 0xff if data is external string. */ - uint8_t external; - uint8_t _spare; - - uint32_t size; - njs_string_t *data; - } long_string; - - struct { - njs_value_type_t type:8; /* 6 bits */ - uint8_t truth; - - uint16_t _spare; - - uint32_t index; - const njs_extern_t *proto; - } external; - - njs_value_type_t type:8; /* 6 bits */ -}; - - -struct njs_object_s { - /* A private hash of njs_object_prop_t. */ - nxt_lvlhsh_t hash; - - /* A shared hash of njs_object_prop_t. */ - nxt_lvlhsh_t shared_hash; - - /* An object __proto__. */ - njs_object_t *__proto__; - - /* The type is used in constructor prototypes. */ - njs_value_type_t type:8; - uint8_t shared; /* 1 bit */ - uint8_t extensible; /* 1 bit */ -}; - - -struct njs_object_value_s { - njs_object_t object; - /* The value can be unaligned since it never used in nJSVM operations. */ - njs_value_t value; -}; - - -struct njs_array_s { - njs_object_t object; - uint32_t size; - uint32_t length; - njs_value_t *start; - njs_value_t *data; -}; - - -typedef struct { - union { - uint32_t count; - njs_value_t values; - } u; - - njs_value_t values[1]; -} njs_closure_t; - - -#define NJS_ARGS_TYPES_MAX 5 - -struct njs_function_s { - njs_object_t object; - - uint8_t args_types[NJS_ARGS_TYPES_MAX]; - uint8_t args_offset; - uint8_t continuation_size; - - /* Function is a closure. */ - uint8_t closure:1; - - uint8_t native:1; - uint8_t ctor:1; - - union { - njs_function_lambda_t *lambda; - njs_function_native_t native; - } u; - - njs_value_t *bound; -#if (NXT_SUNC) - njs_closure_t *closures[1]; -#else - njs_closure_t *closures[]; -#endif -}; - - -struct njs_regexp_s { - njs_object_t object; - uint32_t last_index; - njs_regexp_pattern_t *pattern; - /* - * This string value can be unaligned since - * it never used in nJSVM operations. - */ - njs_value_t string; -}; - - -struct njs_date_s { - njs_object_t object; - double time; -}; - - -typedef union { - njs_object_t object; - njs_object_value_t object_value; - njs_array_t array; - njs_function_t function; - njs_regexp_t regexp; - njs_date_t date; -} njs_object_prototype_t; +typedef struct njs_generator_s njs_generator_t; typedef struct { @@ -367,324 +78,10 @@ typedef struct { } njs_backtrace_entry_t; -#define njs_value(_type, _truth, _number) { \ - .data = { \ - .type = _type, \ - .truth = _truth, \ - .u.number = _number, \ - } \ -} - - -#define njs_string(s) { \ - .short_string = { \ - .type = NJS_STRING, \ - .size = nxt_length(s), \ - .length = nxt_length(s), \ - .start = s, \ - } \ -} - - -/* NJS_STRING_LONG is set for both big and little endian platforms. */ - -#define njs_long_string(s) { \ - .long_string = { \ - .type = NJS_STRING, \ - .truth = (NJS_STRING_LONG << 4) | NJS_STRING_LONG, \ - .size = nxt_length(s), \ - .data = & (njs_string_t) { \ - .start = (u_char *) s, \ - .length = nxt_length(s), \ - } \ - } \ -} - - -#define njs_native_function(_function, _size, ...) { \ - .data = { \ - .type = NJS_FUNCTION, \ - .truth = 1, \ - .u.function = & (njs_function_t) { \ - .native = 1, \ - .continuation_size = _size, \ - .args_types = { __VA_ARGS__ }, \ - .args_offset = 1, \ - .u.native = _function, \ - .object = { .type = NJS_FUNCTION, \ - .shared = 1, \ - .extensible = 1 }, \ - } \ - } \ -} - - -#define njs_prop_handler(_handler) { \ - .data = { \ - .type = NJS_INVALID, \ - .truth = 1, \ - .u = { .prop_handler = _handler } \ - } \ -} - - typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1, njs_value_t *value2); -#define njs_is_null(value) \ - ((value)->type == NJS_NULL) - - -#define njs_is_undefined(value) \ - ((value)->type == NJS_UNDEFINED) - - -#define njs_is_null_or_undefined(value) \ - ((value)->type <= NJS_UNDEFINED) - - -#define njs_is_boolean(value) \ - ((value)->type == NJS_BOOLEAN) - - -#define njs_is_null_or_undefined_or_boolean(value) \ - ((value)->type <= NJS_BOOLEAN) - - -#define njs_is_true(value) \ - ((value)->data.truth != 0) - - -#define njs_is_number(value) \ - ((value)->type == NJS_NUMBER) - - -/* Testing for NaN first generates a better code at least on i386/amd64. */ - -#define njs_is_number_true(num) \ - (!isnan(num) && num != 0) - - -#define njs_is_numeric(value) \ - ((value)->type <= NJS_NUMBER) - - -#define njs_is_string(value) \ - ((value)->type == NJS_STRING) - -#define njs_is_error(value) \ - ((value)->type >= NJS_OBJECT_ERROR \ - && (value)->type <= NJS_OBJECT_URI_ERROR) - - -/* - * The truth field coincides with short_string.size and short_string.length - * so when string size and length are zero the string's value is false and - * otherwise is true. - */ -#define njs_string_truth(value, size) - - -#define njs_string_get(value, str) \ - do { \ - if ((value)->short_string.size != NJS_STRING_LONG) { \ - (str)->length = (value)->short_string.size; \ - (str)->start = (u_char *) (value)->short_string.start; \ - \ - } else { \ - (str)->length = (value)->long_string.size; \ - (str)->start = (u_char *) (value)->long_string.data->start; \ - } \ - } while (0) - - -#define njs_string_short_start(value) \ - (value)->short_string.start - - -#define njs_string_short_set(value, _size, _length) \ - do { \ - (value)->type = NJS_STRING; \ - njs_string_truth(value, _size); \ - (value)->short_string.size = _size; \ - (value)->short_string.length = _length; \ - } while (0) - - -#define njs_string_length_set(value, _length) \ - do { \ - if ((value)->short_string.size != NJS_STRING_LONG) { \ - (value)->short_string.length = length; \ - \ - } else { \ - (value)->long_string.data->length = length; \ - } \ - } while (0) - -#define njs_is_primitive(value) \ - ((value)->type <= NJS_STRING) - - -#define njs_is_data(value) \ - ((value)->type == NJS_DATA) - - -#define njs_is_object(value) \ - ((value)->type >= NJS_OBJECT) - - -#define njs_is_object_value(value) \ - ((value)->type == NJS_OBJECT_VALUE) - - -#define njs_object_value_type(type) \ - (type + NJS_OBJECT) - - -#define njs_is_array(value) \ - ((value)->type == NJS_ARRAY) - - -#define njs_is_function(value) \ - ((value)->type == NJS_FUNCTION) - - -#define njs_is_regexp(value) \ - ((value)->type == NJS_REGEXP) - - -#define njs_is_date(value) \ - ((value)->type == NJS_DATE) - - -#define njs_is_external(value) \ - ((value)->type == NJS_EXTERNAL) - - -#define njs_is_valid(value) \ - ((value)->type != NJS_INVALID) - - -#define njs_bool(value) \ - ((value)->data.truth) - - -#define njs_number(value) \ - ((value)->data.u.number) - - -#define njs_data(value) \ - ((value)->data.u.data) - - -#define njs_function(value) \ - ((value)->data.u.function) - - -#define njs_object(value) \ - ((value)->data.u.object) - - -#define njs_object_hash(value) \ - (&(value)->data.u.object->hash) - - -#define njs_array(value) \ - ((value)->data.u.array) - - -#define njs_array_len(value) \ - ((value)->data.u.array->length) - - -#define njs_array_start(value) \ - ((value)->data.u.array->start) - - -#define njs_set_undefined(value) \ - *(value) = njs_value_undefined - - -#define njs_set_boolean(value, yn) \ - *(value) = yn ? njs_value_true : njs_value_false - - -#define njs_set_true(value) \ - *(value) = njs_value_true - - -#define njs_set_false(value) \ - *(value) = njs_value_false - - -nxt_inline void -njs_set_number(njs_value_t *value, double num) -{ - value->data.u.number = num; - value->type = NJS_NUMBER; - value->data.truth = njs_is_number_true(num); -} - - -nxt_inline void -njs_set_data(njs_value_t *value, void *data) -{ - value->data.u.data = data; - value->type = NJS_DATA; - value->data.truth = 1; -} - - -nxt_inline void -njs_set_object(njs_value_t *value, njs_object_t *object) -{ - value->data.u.object = object; - value->type = NJS_OBJECT; - value->data.truth = 1; -} - - -nxt_inline void -njs_set_array(njs_value_t *value, njs_array_t *array) -{ - value->data.u.array = array; - value->type = NJS_ARRAY; - value->data.truth = 1; -} - - -#define njs_set_invalid(value) \ - (value)->type = NJS_INVALID - - -#if 0 /* GC: todo */ - -#define njs_retain(value) \ - do { \ - if ((value)->data.truth == NJS_STRING_LONG) { \ - njs_value_retain(value); \ - } \ - } while (0) - - -#define njs_release(vm, value) \ - do { \ - if ((value)->data.truth == NJS_STRING_LONG) { \ - njs_value_release((vm), (value)); \ - } \ - } while (0) - -#else - -#define njs_retain(value) - - -#define njs_release(vm, value) - -#endif - - #define NJS_VMCODE_3OPERANDS 0 #define NJS_VMCODE_2OPERANDS 1 #define NJS_VMCODE_1OPERAND 2 @@ -1261,18 +658,8 @@ struct njs_vm_shared_s { }; -typedef enum { - NJS_ENUM_KEYS, - NJS_ENUM_VALUES, - NJS_ENUM_BOTH, -} njs_object_enum_t; - - nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm); -void njs_value_retain(njs_value_t *value); -void njs_value_release(njs_vm_t *vm, njs_value_t *value); - 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, @@ -1413,15 +800,6 @@ njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_ret_t njs_vmcode_reference_error(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2); -nxt_bool_t njs_values_strict_equal(const njs_value_t *val1, - const njs_value_t *val2); - -const char *njs_type_string(njs_value_type_t type); -const char *njs_arg_type_string(uint8_t arg); - -njs_ret_t njs_native_function_arguments(njs_vm_t *vm, njs_value_t *args, - uint8_t *args_types, nxt_uint_t nargs); - nxt_int_t njs_builtin_objects_create(njs_vm_t *vm); nxt_int_t njs_builtin_objects_clone(njs_vm_t *vm); nxt_int_t njs_builtin_match_native_function(njs_vm_t *vm, @@ -1432,32 +810,6 @@ nxt_array_t *njs_vm_backtrace(njs_vm_t *vm); void *njs_lvlhsh_alloc(void *data, size_t size); void njs_lvlhsh_free(void *data, void *p, size_t size); -njs_array_t * njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, nxt_bool_t all); -njs_array_t * njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, nxt_bool_t all); - -extern const njs_value_t njs_value_undefined; -extern const njs_value_t njs_value_null; -extern const njs_value_t njs_value_false; -extern const njs_value_t njs_value_true; -extern const njs_value_t njs_value_zero; -extern const njs_value_t njs_value_nan; -extern const njs_value_t njs_value_invalid; - -extern const njs_value_t njs_string_empty; -extern const njs_value_t njs_string_comma; -extern const njs_value_t njs_string_undefined; -extern const njs_value_t njs_string_null; -extern const njs_value_t njs_string_false; -extern const njs_value_t njs_string_true; -extern const njs_value_t njs_string_native; -extern const njs_value_t njs_string_minus_zero; -extern const njs_value_t njs_string_minus_infinity; -extern const njs_value_t njs_string_plus_infinity; -extern const njs_value_t njs_string_nan; -extern const njs_value_t njs_string_internal_error; -extern const njs_value_t njs_string_memory_error; extern const nxt_str_t njs_entry_main; extern const nxt_str_t njs_entry_module;