blob: b8aa77397579132f4aa0cfb4115c743a85a488cc [file] [log] [blame]
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
*/
#ifndef _NJS_VALUE_H_INCLUDED_
#define _NJS_VALUE_H_INCLUDED_
/*
* The order of the enum is used in njs_vmcode_typeof()
* and njs_object_prototype_to_string().
*/
typedef enum {
NJS_NULL,
NJS_UNDEFINED,
/* The order of the above type is used in njs_is_null_or_undefined(). */
NJS_BOOLEAN,
/*
* The order of the above type is used in
* njs_is_null_or_undefined_or_boolean().
*/
NJS_NUMBER,
/*
* 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_SYMBOL,
NJS_STRING,
/* The order of the above type is used in njs_is_primitive(). */
NJS_DATA,
/*
* The invalid value type is used:
* for uninitialized array members,
* to detect non-declared explicitly or implicitly variables,
* for native property getters.
*/
NJS_INVALID,
/*
* 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,
#define NJS_OBJECT_WRAPPER_MIN (NJS_OBJECT_BOOLEAN)
NJS_OBJECT_BOOLEAN,
NJS_OBJECT_NUMBER,
NJS_OBJECT_SYMBOL,
NJS_OBJECT_STRING,
#define NJS_OBJECT_WRAPPER_MAX (NJS_OBJECT_STRING + 1)
#define NJS_OBJECT_SPECIAL_MIN (NJS_FUNCTION)
NJS_FUNCTION,
NJS_REGEXP,
NJS_DATE,
NJS_TYPED_ARRAY,
#define NJS_OBJECT_SPECIAL_MAX (NJS_TYPED_ARRAY + 1)
NJS_PROMISE,
NJS_OBJECT_VALUE,
NJS_ARRAY_BUFFER,
NJS_DATA_VIEW,
NJS_VALUE_TYPE_MAX
} njs_value_type_t;
typedef enum {
NJS_DATA_TAG_ANY = 0,
NJS_DATA_TAG_EXTERNAL,
NJS_DATA_TAG_CRYPTO_HASH,
NJS_DATA_TAG_CRYPTO_HMAC,
NJS_DATA_TAG_TEXT_ENCODER,
NJS_DATA_TAG_TEXT_DECODER,
NJS_DATA_TAG_MAX
} njs_data_tag_t;
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_array_buffer_s njs_array_buffer_t;
typedef struct njs_typed_array_s njs_typed_array_t;
typedef struct njs_typed_array_s njs_data_view_t;
typedef struct njs_regexp_s njs_regexp_t;
typedef struct njs_date_s njs_date_t;
typedef struct njs_object_value_s njs_promise_t;
typedef struct njs_property_next_s njs_property_next_t;
typedef struct njs_object_init_s njs_object_init_t;
#if (!NJS_HAVE_GCC_ATTRIBUTE_ALIGNED)
#error "aligned attribute is required"
#endif
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 magic16;
uint32_t magic32;
union {
double number;
njs_object_t *object;
njs_array_t *array;
njs_array_buffer_t *array_buffer;
njs_typed_array_t *typed_array;
njs_data_view_t *data_view;
njs_object_value_t *object_value;
njs_function_t *function;
njs_function_lambda_t *lambda;
njs_regexp_t *regexp;
njs_date_t *date;
njs_promise_t *promise;
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;
njs_value_type_t type:8; /* 6 bits */
};
typedef struct {
/* Get, also Set if writable, also Delete if configurable. */
njs_prop_handler_t prop_handler;
unsigned writable:1;
unsigned configurable:1;
unsigned enumerable:1;
njs_exotic_keys_t keys;
/* A shared hash of njs_object_prop_t for externals. */
njs_lvlhsh_t external_shared_hash;
} njs_exotic_slots_t;
struct njs_object_s {
/* A private hash of njs_object_prop_t. */
njs_lvlhsh_t hash;
/* A shared hash of njs_object_prop_t. */
njs_lvlhsh_t shared_hash;
njs_object_t *__proto__;
njs_exotic_slots_t *slots;
/* The type is used in constructor prototypes. */
njs_value_type_t type:8;
uint8_t shared; /* 1 bit */
uint8_t extensible:1;
uint8_t error_data:1;
uint8_t fast_array:1;
};
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;
};
struct njs_array_buffer_s {
njs_object_t object;
size_t size;
union {
uint8_t *u8;
uint16_t *u16;
uint32_t *u32;
uint64_t *u64;
int8_t *i8;
int16_t *i16;
int32_t *i32;
int64_t *i64;
float *f32;
double *f64;
void *data;
} u;
};
struct njs_typed_array_s {
njs_object_t object;
njs_array_buffer_t *buffer;
size_t offset; // byte_offset / element_size
size_t byte_length;
uint8_t type;
};
typedef struct {
union {
uint32_t count;
njs_value_t values;
} u;
njs_value_t values[1];
} njs_closure_t;
struct njs_function_s {
njs_object_t object;
uint8_t args_offset;
uint8_t args_count:4;
/*
* If "closure" is true njs_closure_t[] is available right after the
* njs_function_t and njs_function_closures() may be used to access it.
*/
#define njs_function_closures(function) \
((njs_closure_t **) ((u_char *) function + sizeof(njs_function_t)))
uint8_t closure:1;
uint8_t native:1;
uint8_t ctor:1;
uint8_t global_this:1;
uint8_t magic8;
union {
njs_function_lambda_t *lambda;
njs_function_native_t native;
njs_function_t *bound_target;
} u;
void *context;
njs_value_t *bound;
};
struct njs_regexp_s {
njs_object_t object;
njs_value_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_promise_t promise;
} njs_object_prototype_t;
typedef struct {
njs_function_t constructor;
const njs_object_init_t *constructor_props;
const njs_object_init_t *prototype_props;
njs_object_prototype_t prototype_value;
} njs_object_type_init_t;
typedef enum {
NJS_ENUM_KEYS,
NJS_ENUM_VALUES,
NJS_ENUM_BOTH,
} njs_object_enum_t;
typedef enum {
NJS_ENUM_STRING = 1,
NJS_ENUM_SYMBOL = 2,
} njs_object_enum_type_t;
typedef enum {
NJS_PROPERTY = 0,
NJS_PROPERTY_REF,
NJS_PROPERTY_TYPED_ARRAY_REF,
NJS_PROPERTY_HANDLER,
NJS_WHITEOUT,
} njs_object_prop_type_t;
/*
* Attributes are generally used as Boolean values.
* The UNSET value is can be seen:
* for newly created property descriptors in njs_define_property(),
* for writable attribute of accessor descriptors (desc->writable
* cannot be used as a boolean value).
*/
typedef enum {
NJS_ATTRIBUTE_FALSE = 0,
NJS_ATTRIBUTE_TRUE = 1,
NJS_ATTRIBUTE_UNSET,
} njs_object_attribute_t;
struct njs_object_prop_s {
/* Must be aligned to njs_value_t. */
njs_value_t value;
njs_value_t name;
njs_value_t getter;
njs_value_t setter;
/* TODO: get rid of types */
njs_object_prop_type_t type:8; /* 3 bits */
njs_object_attribute_t writable:8; /* 2 bits */
njs_object_attribute_t enumerable:8; /* 2 bits */
njs_object_attribute_t configurable:8; /* 2 bits */
};
typedef struct {
njs_lvlhsh_query_t lhq;
/* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
njs_object_prop_t scratch;
njs_value_t key;
njs_object_t *prototype;
njs_object_prop_t *own_whiteout;
uint8_t query;
uint8_t shared;
uint8_t own;
} njs_property_query_t;
#define njs_value(_type, _truth, _number) { \
.data = { \
.type = _type, \
.truth = _truth, \
.u.number = _number, \
} \
}
#define njs_wellknown_symbol(key) { \
.data = { \
.type = NJS_SYMBOL, \
.truth = 1, \
.magic32 = key, \
.u = { .value = NULL } \
} \
}
#define njs_string(s) { \
.short_string = { \
.type = NJS_STRING, \
.size = njs_length(s), \
.length = njs_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 = njs_length(s), \
.data = & (njs_string_t) { \
.start = (u_char *) s, \
.length = njs_length(s), \
} \
} \
}
#define _njs_function(_function, _args_count, _ctor, _magic) { \
.native = 1, \
.magic8 = _magic, \
.args_count = _args_count, \
.ctor = _ctor, \
.args_offset = 1, \
.u.native = _function, \
.object = { .type = NJS_FUNCTION, \
.shared = 1, \
.extensible = 1 }, \
}
#define _njs_native_function(_func, _args, _ctor, _magic) { \
.data = { \
.type = NJS_FUNCTION, \
.truth = 1, \
.u.function = & (njs_function_t) _njs_function(_func, _args, \
_ctor, _magic) \
} \
}
#define njs_native_function(_function, _args_count) \
_njs_native_function(_function, _args_count, 0, 0)
#define njs_native_function2(_function, _args_count, _magic) \
_njs_native_function(_function, _args_count, 0, _magic)
#define njs_native_ctor(_function, _args_count, _magic) \
_njs_function(_function, _args_count, 1, _magic)
#define njs_prop_handler(_handler) { \
.data = { \
.type = NJS_INVALID, \
.truth = 1, \
.u = { .prop_handler = _handler } \
} \
}
#define njs_prop_handler2(_handler, _magic16, _magic32) { \
.data = { \
.type = NJS_INVALID, \
.truth = 1, \
.magic16 = _magic16, \
.magic32 = _magic32, \
.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_defined(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_symbol(value) \
((value)->type == NJS_SYMBOL)
#define njs_is_string(value) \
((value)->type == NJS_STRING)
#define njs_is_key(value) \
(njs_is_string(value) || njs_is_symbol(value))
/*
* 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, tag) \
((value)->type == NJS_DATA && value->data.magic32 == (tag))
#define njs_is_object(value) \
((value)->type >= NJS_OBJECT)
#define njs_has_prototype(vm, value, proto) \
(((njs_object_prototype_t *) \
njs_object(value)->__proto__ - (vm)->prototypes) == proto)
#define njs_is_object_value(value) \
((value)->type == NJS_OBJECT_VALUE)
#define njs_is_object_data(_value, tag) \
(((_value)->type == NJS_OBJECT_VALUE) \
&& njs_is_data(njs_object_value(_value), tag))
#define njs_is_object_string(value) \
((value)->type == NJS_OBJECT_STRING)
#define njs_object_value_type(type) \
(type + NJS_OBJECT)
#define njs_is_array(value) \
((value)->type == NJS_ARRAY)
#define njs_is_fast_array(value) \
(njs_is_array(value) && njs_array(value)->object.fast_array)
#define njs_is_array_buffer(value) \
((value)->type == NJS_ARRAY_BUFFER)
#define njs_is_typed_array(value) \
((value)->type == NJS_TYPED_ARRAY)
#define njs_is_detached_buffer(buffer) \
((buffer)->u.data == NULL)
#define njs_is_data_view(value) \
((value)->type == NJS_DATA_VIEW)
#define njs_is_typed_array_uint8(value) \
(njs_is_typed_array(value) \
&& njs_typed_array(value)->type == NJS_OBJ_TYPE_UINT8_ARRAY)
#define njs_is_function(value) \
((value)->type == NJS_FUNCTION)
#define njs_is_function_or_undefined(value) \
((value)->type == NJS_FUNCTION || (value)->type == NJS_UNDEFINED)
#define njs_is_constructor(value) \
(njs_is_function(value) && njs_function(value)->ctor)
#define njs_is_regexp(value) \
((value)->type == NJS_REGEXP)
#define njs_is_date(value) \
((value)->type == NJS_DATE)
#define njs_is_promise(value) \
((value)->type == NJS_PROMISE)
#define njs_is_error(value) \
((value)->type == NJS_OBJECT && njs_object(value)->error_data)
#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_function_lambda(value) \
((value)->data.u.function->u.lambda)
#define njs_object(value) \
((value)->data.u.object)
#define njs_object_hash(value) \
(&(value)->data.u.object->hash)
#define njs_object_slots(value) \
((value)->data.u.object->slots)
#define njs_array(value) \
((value)->data.u.array)
#define njs_array_len(value) \
((value)->data.u.array->length)
#define njs_array_buffer(value) \
((value)->data.u.array_buffer)
#define njs_data_view(value) \
((value)->data.u.data_view)
#define njs_typed_array(value) \
((value)->data.u.typed_array)
#define njs_typed_array_buffer(value) \
((value)->buffer)
#define njs_array_start(value) \
((value)->data.u.array->start)
#define njs_date(value) \
((value)->data.u.date)
#define njs_promise(value) \
((value)->data.u.promise)
#define njs_regexp(value) \
((value)->data.u.regexp)
#define njs_regexp_pattern(value) \
((value)->data.u.regexp->pattern)
#define njs_object_value(_value) \
(&(_value)->data.u.object_value->value)
#define njs_object_data(_value) \
njs_data(njs_object_value(_value))
#define njs_set_undefined(value) \
*(value) = njs_value_undefined
#define njs_set_null(value) \
*(value) = njs_value_null
#define njs_set_true(value) \
*(value) = njs_value_true
#define njs_set_false(value) \
*(value) = njs_value_false
#define njs_symbol_key(value) \
((value)->data.magic32)
#define njs_symbol_eq(value1, value2) \
(njs_symbol_key(value1) == njs_symbol_key(value2))
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_symbol;
extern const njs_value_t njs_string_string;
extern const njs_value_t njs_string_data;
extern const njs_value_t njs_string_type;
extern const njs_value_t njs_string_name;
extern const njs_value_t njs_string_external;
extern const njs_value_t njs_string_invalid;
extern const njs_value_t njs_string_object;
extern const njs_value_t njs_string_function;
extern const njs_value_t njs_string_memory_error;
njs_inline void
njs_set_boolean(njs_value_t *value, unsigned yn)
{
const njs_value_t *retval;
/* Using const retval generates a better code at least on i386/amd64. */
retval = (yn) ? &njs_value_true : &njs_value_false;
*value = *retval;
}
njs_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);
}
njs_inline void
njs_set_int32(njs_value_t *value, int32_t num)
{
value->data.u.number = num;
value->type = NJS_NUMBER;
value->data.truth = (num != 0);
}
njs_inline void
njs_set_uint32(njs_value_t *value, uint32_t num)
{
value->data.u.number = num;
value->type = NJS_NUMBER;
value->data.truth = (num != 0);
}
njs_inline void
njs_set_symbol(njs_value_t *value, uint32_t symbol)
{
value->data.magic32 = symbol;
value->type = NJS_SYMBOL;
value->data.truth = 1;
}
njs_inline void
njs_set_data(njs_value_t *value, void *data, njs_data_tag_t tag)
{
value->data.magic32 = tag;
value->data.u.data = data;
value->type = NJS_DATA;
value->data.truth = 1;
}
njs_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;
}
njs_inline void
njs_set_type_object(njs_value_t *value, njs_object_t *object,
njs_uint_t type)
{
value->data.u.object = object;
value->type = type;
value->data.truth = 1;
}
njs_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;
}
njs_inline void
njs_set_array_buffer(njs_value_t *value, njs_array_buffer_t *array)
{
value->data.u.array_buffer = array;
value->type = NJS_ARRAY_BUFFER;
value->data.truth = 1;
}
njs_inline void
njs_set_typed_array(njs_value_t *value, njs_typed_array_t *array)
{
value->data.u.typed_array = array;
value->type = NJS_TYPED_ARRAY;
value->data.truth = 1;
}
njs_inline void
njs_set_data_view(njs_value_t *value, njs_data_view_t *array)
{
value->data.u.data_view = array;
value->type = NJS_DATA_VIEW;
value->data.truth = 1;
}
njs_inline void
njs_set_function(njs_value_t *value, njs_function_t *function)
{
value->data.u.function = function;
value->type = NJS_FUNCTION;
value->data.truth = 1;
}
njs_inline void
njs_set_date(njs_value_t *value, njs_date_t *date)
{
value->data.u.date = date;
value->type = NJS_DATE;
value->data.truth = 1;
}
njs_inline void
njs_set_promise(njs_value_t *value, njs_promise_t *promise)
{
value->data.u.promise = promise;
value->type = NJS_PROMISE;
value->data.truth = 1;
}
njs_inline void
njs_set_regexp(njs_value_t *value, njs_regexp_t *regexp)
{
value->data.u.regexp = regexp;
value->type = NJS_REGEXP;
value->data.truth = 1;
}
njs_inline void
njs_set_object_value(njs_value_t *value, njs_object_value_t *object_value)
{
value->data.u.object_value = object_value;
value->type = NJS_OBJECT_VALUE;
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_property_query_init(pq, _query, _own) \
do { \
(pq)->lhq.key.length = 0; \
(pq)->lhq.key.start = NULL; \
(pq)->lhq.value = NULL; \
/* FIXME: False-positive in MSAN?. */ \
njs_msan_unpoison(&(pq)->key, sizeof(njs_value_t)); \
(pq)->own_whiteout = NULL; \
(pq)->query = _query; \
(pq)->shared = 0; \
(pq)->own = _own; \
} while (0)
void njs_value_retain(njs_value_t *value);
void njs_value_release(njs_vm_t *vm, njs_value_t *value);
njs_int_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst,
njs_value_t *value, njs_uint_t hint);
njs_array_t *njs_value_enumerate(njs_vm_t *vm, njs_value_t *value,
njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value,
njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
njs_int_t njs_value_of(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval);
njs_int_t njs_value_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst);
const char *njs_type_string(njs_value_type_t type);
njs_int_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
const njs_value_t *src);
njs_int_t njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain,
const njs_value_t *src);
double njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float);
njs_int_t njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64);
njs_bool_t njs_string_eq(const njs_value_t *v1, const njs_value_t *v2);
njs_int_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq,
njs_value_t *value, njs_value_t *key);
njs_int_t njs_value_property(njs_vm_t *vm, njs_value_t *value,
njs_value_t *key, njs_value_t *retval);
njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value,
njs_value_t *key, njs_value_t *setval);
njs_int_t njs_value_property_delete(njs_vm_t *vm, njs_value_t *value,
njs_value_t *key, njs_value_t *removed);
njs_int_t njs_value_to_object(njs_vm_t *vm, njs_value_t *value);
void njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string);
njs_int_t njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object,
njs_value_t *default_constructor, njs_value_t *dst);
njs_int_t njs_value_method(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
njs_value_t *retval);
njs_inline njs_int_t
njs_value_property_i64(njs_vm_t *vm, njs_value_t *value, int64_t index,
njs_value_t *retval)
{
njs_int_t ret;
njs_value_t key;
/* FIXME: False-positive in MSAN?. */
njs_msan_unpoison(&key, sizeof(njs_value_t));
ret = njs_int64_to_string(vm, &key, index);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
return njs_value_property(vm, value, &key, retval);
}
njs_inline njs_int_t
njs_value_property_i64_set(njs_vm_t *vm, njs_value_t *value, int64_t index,
njs_value_t *setval)
{
njs_int_t ret;
njs_value_t key;
/* FIXME: False-positive in MSAN?. */
njs_msan_unpoison(&key, sizeof(njs_value_t));
ret = njs_int64_to_string(vm, &key, index);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
return njs_value_property_set(vm, value, &key, setval);
}
njs_inline njs_int_t
njs_value_property_i64_delete(njs_vm_t *vm, njs_value_t *value, int64_t index,
njs_value_t *removed)
{
njs_int_t ret;
njs_value_t key;
/* FIXME: False-positive in MSAN?. */
njs_msan_unpoison(&key, sizeof(njs_value_t));
ret = njs_int64_to_string(vm, &key, index);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
return njs_value_property_delete(vm, value, &key, removed);
}
njs_inline njs_bool_t
njs_values_same_non_numeric(const njs_value_t *val1, const njs_value_t *val2)
{
if (njs_is_string(val1)) {
return njs_string_eq(val1, val2);
}
if (njs_is_symbol(val1)) {
return njs_symbol_eq(val1, val2);
}
return (njs_object(val1) == njs_object(val2));
}
njs_inline njs_bool_t
njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2)
{
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 (njs_number(val1) == njs_number(val2));
}
return njs_values_same_non_numeric(val1, val2);
}
njs_inline njs_bool_t
njs_values_same(const njs_value_t *val1, const njs_value_t *val2)
{
double num1, num2;
if (val1->type != val2->type) {
return 0;
}
if (njs_is_numeric(val1)) {
if (njs_is_undefined(val1)) {
return 1;
}
num1 = njs_number(val1);
num2 = njs_number(val2);
if (njs_slow_path(isnan(num1) && isnan(num2))) {
return 1;
}
if (njs_slow_path(num1 == 0 && num2 == 0
&& (signbit(num1) ^ signbit(num2))))
{
return 0;
}
/* Infinities are handled correctly by comparision. */
return num1 == num2;
}
return njs_values_same_non_numeric(val1, val2);
}
njs_inline njs_bool_t
njs_values_same_zero(const njs_value_t *val1, const njs_value_t *val2)
{
double num1, num2;
if (val1->type != val2->type) {
return 0;
}
if (njs_is_numeric(val1)) {
if (njs_is_undefined(val1)) {
return 1;
}
num1 = njs_number(val1);
num2 = njs_number(val2);
if (njs_slow_path(isnan(num1) && isnan(num2))) {
return 1;
}
/* Infinities are handled correctly by comparision. */
return num1 == num2;
}
return njs_values_same_non_numeric(val1, val2);
}
#endif /* _NJS_VALUE_H_INCLUDED_ */