
/*
 * 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,

    NJS_OBJECT                = 0x10,
    NJS_ARRAY,
#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_TEXT_ENCODER,
    NJS_DATA_TAG_TEXT_DECODER,
    NJS_DATA_TAG_ARRAY_ITERATOR,
    NJS_DATA_TAG_FOREACH_NEXT,
    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;


union njs_value_s {
    struct {
        uint32_t                      magic32;
        njs_value_type_t              type:8;  /* 5 bits */

        /*
         * The truth field is set during value assignment and then can be
         * quickly tested by logical and conditional operations regardless
         * of value type.
         */
        uint8_t                       truth;

        uint16_t                      magic16;

        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;
            void                      *data;
        } u;
    } data;

    struct {
        uint32_t                      atom_id;
        njs_value_type_t              type:8;  /* 5 bits */
        uint8_t                       truth;
        uint8_t                       token_type;
        uint8_t                       token_id;
        njs_string_t                  *data;
    } string;

    struct {
        uint32_t                      atom_id;
        njs_value_type_t              type:8;  /* 5 bits */
        uint8_t                       truth;
    };
};


typedef struct {
    /* Get, also Set if writable, also Delete if configurable. */
    njs_prop_handler_t  prop_handler;
    uint32_t            magic32;
    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_flathsh_t       external_shared_hash;
} njs_exotic_slots_t;


struct njs_object_s {
    /* A private hash of njs_object_prop_t. */
    njs_flathsh_t                     hash;

    /* A shared hash of njs_object_prop_t. */
    njs_flathsh_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                           stack_attached: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;
};


struct njs_function_s {
    njs_object_t                      object;

    /* Number of bound args excluding 'this'. */
    uint8_t                           bound_args;

    uint8_t                           args_count:4;

    uint8_t                           closure_copied:1;
    uint8_t                           native:1;
    uint8_t                           ctor:1;
    uint8_t                           global_this:1;
    uint8_t                           global:1;

    uint8_t                           magic8;

    union {
        njs_function_lambda_t         *lambda;
        njs_function_native_t         native;
    } u;

    void                              *context;

    /* Bound args including 'this'. */
    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;


struct njs_object_type_init_s {
    njs_function_t            constructor;
    const njs_object_init_t   *constructor_props;
    const njs_object_init_t   *prototype_props;
    njs_object_prototype_t    prototype_value;
};


typedef enum {
    NJS_PROPERTY = 0,
    NJS_ACCESSOR,
    NJS_PROPERTY_HANDLER,

    NJS_PROPERTY_REF,
    NJS_PROPERTY_PLACE_REF,
    NJS_PROPERTY_TYPED_ARRAY_REF,
    NJS_WHITEOUT,
} njs_object_prop_type_t;


typedef enum {
    NJS_PROPERTY_QUERY_GET = 0,
    NJS_PROPERTY_QUERY_SET,
    NJS_PROPERTY_QUERY_DELETE,
} njs_prop_query_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 {
    union {
        njs_value_t             value;
        struct {
            njs_function_t      *getter;
            njs_function_t      *setter;
        } accessor;
    } u;

#define njs_prop_value(_p)      (&(_p)->u.value)
#define njs_prop_handler(_p)    (_p)->u.value.data.u.prop_handler
#define njs_prop_ref(_p)        (_p)->u.value.data.u.value
#define njs_prop_typed_ref(_p)  (_p)->u.value.data.u.typed_array
#define njs_prop_magic16(_p)    (_p)->u.value.data.magic16
#define njs_prop_magic32(_p)    (_p)->u.value.data.magic32
#define NJS_PROP_PTR_UNSET      ((void *) (uintptr_t) -1)
#define njs_prop_getter(_p)     (_p)->u.accessor.getter
#define njs_prop_setter(_p)     (_p)->u.accessor.setter

    njs_object_prop_type_t      type:8;          /* 3 bits */
    njs_object_prop_type_t      enum_in_object_hash: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 */
};


struct njs_object_prop_init_s {
    struct njs_object_prop_s    desc;
    uint32_t                    atom_id;
};


typedef struct {
    njs_flathsh_query_t         lhq;

    uint8_t                     query;

    /* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */
    njs_object_prop_t           scratch;

    njs_flathsh_t              *own_whiteout;

    uint8_t                     temp;
    uint8_t                     own;
} njs_property_query_t;


#define njs_value(_type, _truth, _number) (njs_value_t) {                     \
    .data = {                                                                 \
        .type = _type,                                                        \
        .truth = _truth,                                                      \
        .u.number = _number,                                                  \
    }                                                                         \
}


#define njs_symval(_sym_id, _s) {                                             \
    .data = {                                                                 \
        .type = NJS_SYMBOL,                                                   \
        .truth = 1,                                                           \
        .magic32 = NJS_ATOM_SYMBOL_ ## _sym_id,                               \
        .u = { .value = (njs_value_t *) &njs_ascii_strval(_s) }               \
    }                                                                         \
}


#define njs_ascii_strval(_s) (njs_value_t) {                                  \
    .string = {                                                               \
        .atom_id = NJS_ATOM_STRING_unknown,                                   \
        .type = NJS_STRING,                                                   \
        .truth = njs_length(_s) ? 1 : 0,                                      \
        .data = & (njs_string_t) {                                            \
            .start = (u_char *) _s,                                           \
            .length = njs_length(_s),                                         \
            .size = njs_length(_s),                                           \
        },                                                                    \
    }                                                                         \
}


#define _njs_function(_function, _args_count, _ctor, _magic) {                \
    .native = 1,                                                              \
    .magic8 = _magic,                                                         \
    .args_count = _args_count,                                                \
    .ctor = _ctor,                                                            \
    .u.native = _function,                                                    \
    .object = { .type = NJS_FUNCTION,                                         \
                .shared = 1,                                                  \
                .extensible = 1 },                                            \
}


#define _njs_native_function(_func, _args, _ctor, _magic) (njs_value_t) {     \
    .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_getter(_function, _magic) {                                       \
    .getter = & (njs_function_t) _njs_function(_function, 0, 0, _magic),      \
    .setter = NULL,                                                           \
}


#define njs_accessor(_getter, _m1, _setter, _m2) {                            \
    .getter = & (njs_function_t) _njs_function(_getter, 0, 0, _m1),           \
    .setter = & (njs_function_t) _njs_function(_setter, 0, 0, _m2),           \
}


#define njs_native_ctor(_function, _args_count, _magic)                       \
    _njs_function(_function, _args_count, 1, _magic)


#define njs_prop_handler2(_handler, _magic16) (njs_value_t) {                 \
    .data = {                                                                 \
        .type = NJS_INVALID,                                                  \
        .truth = 1,                                                           \
        .magic16 = _magic16,                                                  \
        .magic32 = 2,                                                         \
        .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))


#define njs_is_index_or_key(value)                                            \
    (njs_is_number(value) || njs_is_key(value))


#define njs_string_get_unsafe(value, str)                                     \
    do {                                                                      \
        njs_assert((value)->string.data != NULL);                             \
        (str)->length = (value)->string.data->size;                           \
        (str)->start = (u_char *) (value)->string.data->start;                \
    } while (0)


#define njs_string_get(vm, value, str)                                        \
    do {                                                                      \
        njs_value_t  _dst;                                                    \
                                                                              \
        njs_assert(njs_is_string(value));                                     \
                                                                              \
        if (njs_slow_path((value)->string.data == NULL)) {                    \
            njs_assert((value)->atom_id != 0 /* NJS_ATOM_STRING_unknown */);  \
            njs_atom_to_value(vm, &_dst, (value)->atom_id);                   \
            njs_assert(njs_is_string(&_dst));                                 \
            njs_string_get_unsafe(&_dst, str);                                \
                                                                              \
        } else {                                                              \
            njs_string_get_unsafe(value, str);                                \
        }                                                                     \
    } while (0)


#define njs_is_primitive(value)                                               \
    ((value)->type <= NJS_STRING)


#define njs_make_tag(proto_id)                                                \
    (((njs_uint_t) proto_id << 8) | NJS_DATA_TAG_EXTERNAL)


#define njs_is_data(value, tag)                                               \
    ((value)->type == NJS_DATA                                                \
     && ((tag) == njs_make_tag(NJS_PROTO_ID_ANY)                              \
         || value->data.magic32 == (tag)))


#define njs_is_object(value)                                                  \
    ((value)->type >= NJS_OBJECT)


#define njs_has_prototype(vm, value, proto_id)                                \
    (njs_object(value)->__proto__ == njs_vm_proto(vm, proto_id))


#define njs_is_object_value(value)                                            \
    ((value)->type == NJS_OBJECT_VALUE)


#define njs_is_object_boolean(_value)                                         \
    (((_value)->type == NJS_OBJECT_VALUE)                                     \
     && njs_is_boolean(njs_object_value(_value)))


#define njs_is_object_number(_value)                                          \
    (((_value)->type == NJS_OBJECT_VALUE)                                     \
     && njs_is_number(njs_object_value(_value)))


#define njs_is_object_symbol(_value)                                          \
    (((_value)->type == NJS_OBJECT_VALUE)                                     \
     && njs_is_symbol(njs_object_value(_value)))


#define njs_is_object_string(_value)                                          \
    (((_value)->type == NJS_OBJECT_VALUE)                                     \
     && njs_is_string(njs_object_value(_value)))


#define njs_is_object_primitive(_value)                                       \
    (((_value)->type == NJS_OBJECT_VALUE)                                     \
     && njs_is_primitive(njs_object_value(_value)))


#define njs_is_object_data(_value, tag)                                       \
    (((_value)->type == NJS_OBJECT_VALUE)                                     \
     && njs_is_data(njs_object_value(_value), tag))


#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;

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);
    value->atom_id = 0 /* NJS_ATOM_STRING_unknown */;
}


#define njs_set_empty_string(vm, value)                                       \
    njs_atom_to_value(vm, value, NJS_ATOM_STRING_empty)


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);
    value->atom_id = 0 /* NJS_ATOM_STRING_unknown */;
}


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);
    value->atom_id = 0 /* NJS_ATOM_STRING_unknown */;
}


njs_inline void
njs_set_symbol(njs_value_t *value, uint32_t symbol, njs_value_t *name)
{
    value->data.magic32 = symbol;
    value->type = NJS_SYMBOL;
    value->data.truth = 1;
    value->data.u.value = name;
}


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

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,
    uint32_t flags);
njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value,
    uint32_t flags);
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(njs_vm_t *vm, const njs_value_t *value);
njs_int_t njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64);

njs_bool_t njs_string_eq(njs_vm_t *vm, 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, uint32_t atom_id);
njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value,
    uint32_t atom_id, njs_value_t *setval);
njs_int_t njs_value_property_delete(njs_vm_t *vm, njs_value_t *value,
    uint32_t atom_id, njs_value_t *removed, njs_bool_t thrw);
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_construct(njs_vm_t *vm, njs_value_t *constructor,
    njs_value_t *args, njs_uint_t nargs, njs_value_t *retval);
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, uint32_t atom_id,
    njs_value_t *retval);


njs_inline void
njs_property_query_init(njs_property_query_t *pq, njs_prop_query_t query,
    uint8_t own)
{
        pq->query = query;
        pq->own = own;

        if (query == NJS_PROPERTY_QUERY_SET) {
            pq->lhq.value = NULL;
            pq->own_whiteout = NULL;
            pq->temp = 0;
        }
}


njs_inline njs_int_t
njs_property_query_val(njs_vm_t *vm, njs_property_query_t *pq,
    njs_value_t *value, njs_value_t *key)
{
    njs_int_t  ret;

    if (njs_value_atom(key) == 0 /* NJS_ATOM_STRING_unknown */) {
        ret = njs_atom_atomize_key(vm, key);
        if (ret != NJS_OK) {
            return ret;
        }
    }

    return njs_property_query(vm, pq, value, key->atom_id);
}


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_value_t  key;

    if (index < 0x80000000) {
        return njs_value_property(vm, value, njs_number_atom(index), retval);
    }

    njs_set_number(&key, index);

    return njs_value_property_val(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_value_t  key;

    if (index < 0x80000000) {
        return njs_value_property_set(vm, value, njs_number_atom(index),
                                      setval);
    }

    njs_set_number(&key, index);

    return njs_value_property_val_set(vm, value, &key, setval);
}


njs_inline njs_int_t
njs_value_property_val_delete(njs_vm_t *vm, njs_value_t *value,
    njs_value_t *key, njs_value_t *removed, njs_bool_t thrw)
{
    njs_int_t  ret;

    if (njs_value_atom(key) == 0 /* NJS_ATOM_STRING_unknown */) {
        ret = njs_atom_atomize_key(vm, key);
        if (ret != NJS_OK) {
            return ret;
        }
    }

    return njs_value_property_delete(vm, value, key->atom_id, removed, thrw);
}


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_value_t  key;

    if (index < 0x80000000) {
        return njs_value_property_delete(vm, value, njs_number_atom(index),
                                         removed, 1);
    }

    njs_set_number(&key, index);

    return njs_value_property_val_delete(vm, value, &key, removed, 1);
}


njs_inline njs_bool_t
njs_values_same_non_numeric(njs_vm_t *vm, const njs_value_t *val1,
    const njs_value_t *val2)
{
    if (njs_is_string(val1)) {
        return njs_string_eq(vm, 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(njs_vm_t *vm, 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(vm, val1, val2);
}


njs_inline njs_bool_t
njs_values_same(njs_vm_t *vm, 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(vm, val1, val2);
}


njs_inline njs_bool_t
njs_values_same_zero(njs_vm_t *vm, 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(vm, val1, val2);
}


#endif /* _NJS_VALUE_H_INCLUDED_ */
