
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#ifndef _NJS_VM_H_INCLUDED_
#define _NJS_VM_H_INCLUDED_


#define NJS_MAX_STACK_SIZE       (256 * 1024)


/*
 * NJS_PROPERTY_QUERY_GET must be less to NJS_PROPERTY_QUERY_SET
 * and NJS_PROPERTY_QUERY_DELETE.
 */
#define NJS_PROPERTY_QUERY_GET     0
#define NJS_PROPERTY_QUERY_SET     1
#define NJS_PROPERTY_QUERY_DELETE  2


typedef struct njs_frame_s            njs_frame_t;
typedef struct njs_native_frame_s     njs_native_frame_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;
typedef struct njs_generator_s        njs_generator_t;


typedef enum {
    NJS_SCOPE_GLOBAL = 0,
    NJS_SCOPE_FUNCTION,
    NJS_SCOPE_BLOCK
} njs_scope_t;


typedef enum {
    NJS_OBJ_TYPE_OBJECT = 0,
    NJS_OBJ_TYPE_ARRAY,
    NJS_OBJ_TYPE_BOOLEAN,
    NJS_OBJ_TYPE_NUMBER,
    NJS_OBJ_TYPE_SYMBOL,
    NJS_OBJ_TYPE_STRING,
    NJS_OBJ_TYPE_FUNCTION,
    NJS_OBJ_TYPE_ASYNC_FUNCTION,
    NJS_OBJ_TYPE_REGEXP,
    NJS_OBJ_TYPE_DATE,
    NJS_OBJ_TYPE_PROMISE,
    NJS_OBJ_TYPE_ARRAY_BUFFER,
    NJS_OBJ_TYPE_DATA_VIEW,
    NJS_OBJ_TYPE_TEXT_DECODER,
    NJS_OBJ_TYPE_TEXT_ENCODER,
    NJS_OBJ_TYPE_BUFFER,

#define NJS_OBJ_TYPE_HIDDEN_MIN    (NJS_OBJ_TYPE_ITERATOR)
    NJS_OBJ_TYPE_ITERATOR,
    NJS_OBJ_TYPE_ARRAY_ITERATOR,
    NJS_OBJ_TYPE_FS_DIRENT,
    NJS_OBJ_TYPE_CRYPTO_HASH,
    NJS_OBJ_TYPE_CRYPTO_HMAC,
    NJS_OBJ_TYPE_TYPED_ARRAY,
#define NJS_OBJ_TYPE_HIDDEN_MAX    (NJS_OBJ_TYPE_TYPED_ARRAY + 1)
#define NJS_OBJ_TYPE_NORMAL_MAX    (NJS_OBJ_TYPE_HIDDEN_MAX)

#define NJS_OBJ_TYPE_TYPED_ARRAY_MIN    (NJS_OBJ_TYPE_UINT8_ARRAY)
#define njs_typed_array_index(type)     (type - NJS_OBJ_TYPE_TYPED_ARRAY_MIN)
    NJS_OBJ_TYPE_UINT8_ARRAY,
    NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY,
    NJS_OBJ_TYPE_INT8_ARRAY,
    NJS_OBJ_TYPE_UINT16_ARRAY,
    NJS_OBJ_TYPE_INT16_ARRAY,
    NJS_OBJ_TYPE_UINT32_ARRAY,
    NJS_OBJ_TYPE_INT32_ARRAY,
    NJS_OBJ_TYPE_FLOAT32_ARRAY,
    NJS_OBJ_TYPE_FLOAT64_ARRAY,
#define NJS_OBJ_TYPE_TYPED_ARRAY_MAX    (NJS_OBJ_TYPE_FLOAT64_ARRAY + 1)
#define NJS_OBJ_TYPE_TYPED_ARRAY_SIZE   (NJS_OBJ_TYPE_TYPED_ARRAY_MAX         \
                                         - NJS_OBJ_TYPE_TYPED_ARRAY_MIN)

    NJS_OBJ_TYPE_ERROR,
    NJS_OBJ_TYPE_EVAL_ERROR,
    NJS_OBJ_TYPE_INTERNAL_ERROR,
    NJS_OBJ_TYPE_RANGE_ERROR,
    NJS_OBJ_TYPE_REF_ERROR,
    NJS_OBJ_TYPE_SYNTAX_ERROR,
    NJS_OBJ_TYPE_TYPE_ERROR,
    NJS_OBJ_TYPE_URI_ERROR,
    NJS_OBJ_TYPE_MEMORY_ERROR,
    NJS_OBJ_TYPE_AGGREGATE_ERROR,

    NJS_OBJ_TYPE_MAX,
} njs_object_type_t;


#define njs_primitive_prototype_index(type)                                   \
    (NJS_OBJ_TYPE_BOOLEAN + ((type) - NJS_BOOLEAN))


#define njs_prototype_type(index)                                             \
    (index + NJS_OBJECT)


enum njs_object_e {
    NJS_OBJECT_THIS = 0,
    NJS_OBJECT_NJS,
    NJS_OBJECT_PROCESS,
    NJS_OBJECT_MATH,
    NJS_OBJECT_JSON,
#ifdef NJS_TEST262
    NJS_OBJECT_262,
#endif
    NJS_OBJECT_MAX
};


enum njs_hook_e {
    NJS_HOOK_EXIT = 0,
    NJS_HOOK_MAX
};


typedef enum {
    NJS_LEVEL_LOCAL = 0,
    NJS_LEVEL_CLOSURE,
    NJS_LEVEL_GLOBAL,
    NJS_LEVEL_STATIC,
    NJS_LEVEL_TEMP,
    NJS_LEVEL_MAX
} njs_level_type_t;


struct njs_vm_s {
    /* njs_vm_t must be aligned to njs_value_t due to scratch value. */
    njs_value_t              retval;

    njs_arr_t                *paths;
    njs_arr_t                *protos;

    njs_arr_t                *scope_absolute;
    njs_value_t              **levels[NJS_LEVEL_MAX];
    size_t                   global_items;

    njs_external_ptr_t       external;

    njs_native_frame_t       *top_frame;
    njs_frame_t              *active_frame;

    njs_rbtree_t             *variables_hash;
    njs_lvlhsh_t             keywords_hash;
    njs_lvlhsh_t             values_hash;

    njs_arr_t                *modules;
    njs_lvlhsh_t             modules_hash;

    uint32_t                 event_id;
    njs_lvlhsh_t             events_hash;
    njs_queue_t              posted_events;
    njs_queue_t              promise_events;

    njs_vm_opt_t             options;

    /*
     * The prototypes and constructors arrays must be together because
     * they are copied from njs_vm_shared_t by single memcpy()
     * in njs_builtin_objects_clone().
     */
    njs_object_prototype_t   prototypes[NJS_OBJ_TYPE_MAX];
    njs_function_t           constructors[NJS_OBJ_TYPE_MAX];

    njs_function_t           *hooks[NJS_HOOK_MAX];

    njs_mp_t                 *mem_pool;

    u_char                   *start;
    size_t                   stack_size;

    njs_vm_shared_t          *shared;

    njs_regex_context_t      *regex_context;
    njs_regex_match_data_t   *single_match_data;

    njs_array_t              *promise_reason;

    njs_parser_scope_t       *global_scope;

    /*
     * MemoryError is statically allocated immutable Error object
     * with the InternalError prototype.
     */
    njs_object_t             memory_error_object;

    njs_object_t             string_object;
    njs_object_t             global_object;
    njs_value_t              global_value;

    njs_arr_t                *codes;  /* of njs_vm_code_t */
    njs_arr_t                *functions_name_cache;

    njs_trace_t              trace;
    njs_random_t             random;

    uint64_t                 symbol_generator;
};


typedef struct {
    uint32_t                 offset;
    uint32_t                 line;
} njs_vm_line_num_t;


typedef struct {
    u_char                   *start;
    u_char                   *end;
    njs_str_t                file;
    njs_str_t                name;
    njs_arr_t                *lines;  /* of njs_vm_line_num_t */
} njs_vm_code_t;


struct njs_vm_shared_s {
    njs_lvlhsh_t             keywords_hash;
    njs_lvlhsh_t             values_hash;

    njs_lvlhsh_t             array_instance_hash;
    njs_lvlhsh_t             string_instance_hash;
    njs_lvlhsh_t             function_instance_hash;
    njs_lvlhsh_t             async_function_instance_hash;
    njs_lvlhsh_t             arrow_instance_hash;
    njs_lvlhsh_t             arguments_object_instance_hash;
    njs_lvlhsh_t             regexp_instance_hash;

    njs_lvlhsh_t             modules_hash;

    njs_lvlhsh_t             env_hash;

    njs_object_t             string_object;
    njs_object_t             objects[NJS_OBJECT_MAX];

    njs_exotic_slots_t       global_slots;

    /*
     * The prototypes and constructors arrays must be togther because they are
     * copied to njs_vm_t by single memcpy() in njs_builtin_objects_clone().
     */
    njs_object_prototype_t   prototypes[NJS_OBJ_TYPE_MAX];
    njs_function_t           constructors[NJS_OBJ_TYPE_MAX];

    njs_regexp_pattern_t     *empty_regexp_pattern;
};


void njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *frame,
    njs_native_frame_t *previous);

njs_int_t njs_builtin_objects_create(njs_vm_t *vm);
njs_int_t njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global);
njs_int_t njs_builtin_match_native_function(njs_vm_t *vm,
    njs_function_t *function, njs_str_t *name);

njs_arr_t *njs_vm_completions(njs_vm_t *vm, njs_str_t *expression);

void *njs_lvlhsh_alloc(void *data, size_t size);
void njs_lvlhsh_free(void *data, void *p, size_t size);


extern const njs_str_t    njs_entry_main;
extern const njs_str_t    njs_entry_module;
extern const njs_str_t    njs_entry_native;
extern const njs_str_t    njs_entry_unknown;
extern const njs_str_t    njs_entry_anonymous;

extern const njs_lvlhsh_proto_t  njs_object_hash_proto;


#endif /* _NJS_VM_H_INCLUDED_ */
