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


#include <njs_main.h>
#include <signal.h>


typedef struct {
    enum {
       NJS_BUILTIN_TRAVERSE_KEYS,
       NJS_BUILTIN_TRAVERSE_MATCH,
    }                          type;

    njs_function_t             *func;

    njs_flathsh_t              keys;
    njs_str_t                  match;
} njs_builtin_traverse_t;


typedef struct {
    njs_str_t       name;
    int             value;
} njs_signal_entry_t;


static njs_int_t njs_global_this_prop_handler(njs_vm_t *vm,
    njs_object_prop_t *self, uint32_t atom_id, njs_value_t *global,
    njs_value_t *setval, njs_value_t *retval);

static njs_int_t njs_env_hash_init(njs_vm_t *vm, njs_flathsh_t *hash,
    char **environment);


static const njs_object_init_t  njs_global_this_init;
static const njs_object_init_t  njs_njs_object_init;
static const njs_object_init_t  njs_process_object_init;


static const njs_object_init_t  *njs_object_init[] = {
    &njs_global_this_init,
    &njs_njs_object_init,
    &njs_process_object_init,
    &njs_math_object_init,
    &njs_json_object_init,
    NULL
};


static const njs_object_type_init_t *const
    njs_object_type_init[NJS_OBJ_TYPE_MAX] =
{
    /* Global types. */

    &njs_obj_type_init,
    &njs_array_type_init,
    &njs_boolean_type_init,
    &njs_number_type_init,
    &njs_symbol_type_init,
    &njs_string_type_init,
    &njs_function_type_init,
    &njs_async_function_type_init,
    &njs_regexp_type_init,
    &njs_date_type_init,
    &njs_promise_type_init,
    &njs_array_buffer_type_init,
    &njs_data_view_type_init,
    &njs_text_decoder_type_init,
    &njs_text_encoder_type_init,
    &njs_buffer_type_init,

    /* Hidden types. */

    &njs_iterator_type_init,
    &njs_array_iterator_type_init,
    &njs_typed_array_type_init,

    /* TypedArray types. */

    &njs_typed_array_u8_type_init,
    &njs_typed_array_u8clamped_type_init,
    &njs_typed_array_i8_type_init,
    &njs_typed_array_u16_type_init,
    &njs_typed_array_i16_type_init,
    &njs_typed_array_u32_type_init,
    &njs_typed_array_i32_type_init,
    &njs_typed_array_f32_type_init,
    &njs_typed_array_f64_type_init,

    /* Error types. */
    &njs_error_type_init,
    &njs_eval_error_type_init,
    &njs_internal_error_type_init,
    &njs_range_error_type_init,
    &njs_reference_error_type_init,
    &njs_syntax_error_type_init,
    &njs_type_error_type_init,
    &njs_uri_error_type_init,
    &njs_memory_error_type_init,
    &njs_aggregate_error_type_init,
};


/* P1990 signals from `man 7 signal` are supported */
static njs_signal_entry_t njs_signals_table[] = {
    { njs_str("ABRT"), SIGABRT },
    { njs_str("ALRM"), SIGALRM },
    { njs_str("CHLD"), SIGCHLD },
    { njs_str("CONT"), SIGCONT },
    { njs_str("FPE"),  SIGFPE  },
    { njs_str("HUP"),  SIGHUP  },
    { njs_str("ILL"),  SIGILL  },
    { njs_str("INT"),  SIGINT  },
    { njs_str("KILL"), SIGKILL },
    { njs_str("PIPE"), SIGPIPE },
    { njs_str("QUIT"), SIGQUIT },
    { njs_str("SEGV"), SIGSEGV },
    { njs_str("STOP"), SIGSTOP },
    { njs_str("TSTP"), SIGTSTP },
    { njs_str("TERM"), SIGTERM },
    { njs_str("TTIN"), SIGTTIN },
    { njs_str("TTOU"), SIGTTOU },
    { njs_str("USR1"), SIGUSR1 },
    { njs_str("USR2"), SIGUSR2 },
    { njs_null_str, 0 }
};


njs_inline njs_int_t
njs_object_hash_init(njs_vm_t *vm, njs_flathsh_t *hash,
    const njs_object_init_t *init)
{
    return njs_object_hash_create(vm, hash, init->properties, init->items);
}


njs_int_t
njs_builtin_objects_create(njs_vm_t *vm)
{
    njs_int_t                  ret, index;
    njs_uint_t                 i;
    njs_object_t               *object, *string_object;
    njs_function_t             *constructor;
    njs_vm_shared_t            *shared;
    njs_regexp_pattern_t       *pattern;
    njs_object_prototype_t     *prototype;
    const njs_object_init_t    *obj, **p;

    shared = njs_mp_zalloc(vm->mem_pool, sizeof(njs_vm_shared_t));
    if (njs_slow_path(shared == NULL)) {
        return NJS_ERROR;
    }

    vm->shared = shared;

    njs_lvlhsh_init(&shared->values_hash);

    vm->atom_id_generator = njs_atom_hash_init(vm);
    if (njs_slow_path(vm->atom_id_generator == 0xffffffff)) {
        return NJS_ERROR;
    }

    pattern = njs_regexp_pattern_create(vm, (u_char *) "(?:)",
                                        njs_length("(?:)"), 0);
    if (njs_slow_path(pattern == NULL)) {
        return NJS_ERROR;
    }

    shared->empty_regexp_pattern = pattern;

    ret = njs_object_hash_init(vm, &shared->array_instance_hash,
                               &njs_array_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    ret = njs_object_hash_init(vm, &shared->string_instance_hash,
                               &njs_string_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    ret = njs_object_hash_init(vm, &shared->function_instance_hash,
                               &njs_function_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    ret = njs_object_hash_init(vm, &shared->async_function_instance_hash,
                               &njs_async_function_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    ret = njs_object_hash_init(vm, &shared->arrow_instance_hash,
                               &njs_arrow_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    ret = njs_object_hash_init(vm, &shared->arguments_object_instance_hash,
                               &njs_arguments_object_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    ret = njs_object_hash_init(vm, &shared->regexp_instance_hash,
                               &njs_regexp_instance_init);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    object = shared->objects;

    for (p = njs_object_init; *p != NULL; p++) {
        obj = *p;

        ret = njs_object_hash_init(vm, &object->shared_hash, obj);
        if (njs_slow_path(ret != NJS_OK)) {
            return NJS_ERROR;
        }

        object->type = NJS_OBJECT;
        object->shared = 1;
        object->extensible = 1;

        object++;
    }

    ret = njs_env_hash_init(vm, &shared->env_hash, environ);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
        index = njs_vm_ctor_push(vm);
        if (njs_slow_path(index < 0)) {
            return NJS_ERROR;
        }

        njs_assert_msg((njs_uint_t) index == i,
                       "ctor index should match object type");

        prototype = njs_shared_prototype(shared, i);
        *prototype = njs_object_type_init[i]->prototype_value;

        if (njs_object_type_init[i] == &njs_boolean_type_init) {
            prototype->object_value.value = njs_value(NJS_BOOLEAN, 0, 0.0);

        } else if (njs_object_type_init[i] == &njs_number_type_init) {
            prototype->object_value.value = njs_value(NJS_NUMBER, 0, 0.0);

        } else if (njs_object_type_init[i] == &njs_string_type_init) {
            njs_set_empty_string(vm, &prototype->object_value.value);
        }

        ret = njs_object_hash_init(vm, &prototype->object.shared_hash,
                                   njs_object_type_init[i]->prototype_props);
        if (njs_slow_path(ret != NJS_OK)) {
            return NJS_ERROR;
        }

        prototype->object.extensible = 1;
    }

    prototype = njs_shared_prototype(shared, NJS_OBJ_TYPE_REGEXP);
    prototype->regexp.pattern = shared->empty_regexp_pattern;

    for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
        constructor = njs_shared_ctor(shared, i);

        if (njs_object_type_init[i]->constructor_props == NULL) {
            njs_memzero(constructor, sizeof(njs_function_t));
            continue;
        }

        *constructor = njs_object_type_init[i]->constructor;
        constructor->object.shared = 0;

        ret = njs_object_hash_init(vm, &constructor->object.shared_hash,
                                   njs_object_type_init[i]->constructor_props);
        if (njs_slow_path(ret != NJS_OK)) {
            return NJS_ERROR;
        }
    }

    shared->global_slots.prop_handler = njs_global_this_prop_handler;
    shared->global_slots.writable = 1;
    shared->global_slots.configurable = 1;
    shared->global_slots.enumerable = 1;

    shared->objects[0].slots = &shared->global_slots;

    vm->global_object = shared->objects[0];
    vm->global_object.shared = 0;

    string_object = &shared->string_object;
    njs_lvlhsh_init(&string_object->hash);
    string_object->shared_hash = shared->string_instance_hash;
    string_object->type = NJS_OBJECT_VALUE;
    string_object->shared = 1;
    string_object->extensible = 0;

    njs_lvlhsh_init(&shared->modules_hash);

    return NJS_OK;
}


static njs_int_t
njs_builtin_traverse(njs_vm_t *vm, njs_traverse_t *traverse, void *data)
{
    size_t                  len;
    u_char                  *p, *start, *end;
    njs_int_t               ret, n;
    njs_str_t               name;
    njs_bool_t              symbol;
    njs_value_t             key, *value, prop_name;
    njs_function_t          *func, *target;
    njs_object_prop_t       *prop;
    njs_flathsh_query_t     lhq;
    njs_builtin_traverse_t  *ctx;
    njs_traverse_t          *path[NJS_TRAVERSE_MAX_DEPTH];
    u_char                  buf[256];

    ctx = data;

    if (ctx->type == NJS_BUILTIN_TRAVERSE_MATCH) {
        prop = traverse->prop;
        func = ctx->func;

        if (njs_is_accessor_descriptor(prop)) {
            target = njs_prop_getter(prop);

        } else {
            value = njs_prop_value(prop);
            target = (njs_is_function(value) && njs_function(value)->native)
                            ? njs_function(value)
                            : NULL;
        }

        if (target == NULL
            || !njs_native_function_same(target, func))
        {
            return NJS_OK;
        }
    }

    if (traverse == NULL) {
        njs_type_error(vm, "njs_builtin_traverse() traverse arg is NULL");
        return NJS_ERROR;
    }

    n = 0;

    while (traverse != NULL) {
        path[n++] = traverse;
        traverse = traverse->parent;
    }

    n--;

    p = buf;
    end = buf + sizeof(buf);

    do {
        symbol = 0;

        ret = njs_atom_to_value(vm, &key, path[n]->atom_id);
        if (ret != NJS_OK) {
            return NJS_ERROR;
        }

        if (njs_slow_path(njs_is_symbol(&key))) {
            symbol = 1;
            key = *njs_symbol_description(&key);
            if (njs_is_undefined(&key)) {
                njs_set_empty_string(vm, &key);
            }
        }

        if (njs_slow_path(!njs_is_string(&key))) {
            /* Skipping special properties (e.g. array index properties). */
            return NJS_OK;
        }

        njs_string_get(vm, &key, &name);

        if (njs_slow_path((p + name.length + 3) > end)) {
            njs_type_error(vm, "njs_builtin_traverse() key is too long");
            return NJS_ERROR;
        }

        if (symbol) {
            *p++ = '[';

        } else if (p != buf) {
            *p++ = '.';
        }

        p = njs_cpymem(p, name.start, name.length);

        if (symbol) {
            *p++ = ']';
        }

    } while (n-- > 0);

    if (ctx->type == NJS_BUILTIN_TRAVERSE_MATCH) {
        len = ctx->match.length;
        start = njs_mp_alloc(vm->mem_pool, len + (p - buf) + (len != 0));
        if (njs_slow_path(start == NULL)) {
            njs_memory_error(vm);
            return NJS_ERROR;
        }

        if (len != 0) {
            memcpy(start, ctx->match.start, len);
            start[len++] = '.';
        }

        memcpy(start + len, buf, p - buf);
        ctx->match.length = len + p - buf;
        ctx->match.start = start;

        return NJS_DONE;
    }

    /* NJS_BUILTIN_TRAVERSE_KEYS. */

    ret = njs_atom_string_create(vm, &prop_name, buf, p - buf);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    lhq.key_hash = prop_name.atom_id;
    lhq.replace = 1;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;

    ret = njs_flathsh_unique_insert(&ctx->keys, &lhq);
    if (njs_slow_path(ret != NJS_OK)) {
        njs_internal_error(vm, "lvlhsh insert/replace failed");
        return NJS_ERROR;
    }

    prop = lhq.value;

    prop->type = NJS_PROPERTY;
    prop->enumerable = 0;
    prop->configurable = 0;
    prop->writable = 0;
    prop->u.value = njs_value_null;

    return NJS_OK;
}


typedef struct {
    njs_str_t               name;
    njs_function_native_t   native;
    uint8_t                 magic8;
} njs_function_name_t;


njs_int_t
njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
    njs_str_t *name)
{
    uint8_t                 magic8;
    njs_int_t               ret;
    njs_arr_t               **pprotos;
    njs_mod_t               *module;
    njs_uint_t              i, n;
    njs_value_t             value, tag;
    njs_object_t            object;
    njs_object_prop_t       *prop;
    njs_lvlhsh_each_t       lhe;
    njs_exotic_slots_t      *slots;
    njs_function_name_t     *fn;
    njs_function_native_t   native;
    njs_builtin_traverse_t  ctx;

    if (vm->functions_name_cache != NULL) {
        n = vm->functions_name_cache->items;
        fn = vm->functions_name_cache->start;

        magic8 = function->magic8;
        native = function->u.native;

        while (n != 0) {
            if (fn->native == native && fn->magic8 == magic8) {
                *name = fn->name;
                return NJS_OK;
            }

            fn++;
            n--;
        }
    }

    ctx.type = NJS_BUILTIN_TRAVERSE_MATCH;
    ctx.func = function;

    /* Global object. */

    ctx.match = njs_str_value("");

    ret = njs_object_traverse(vm, njs_object(&vm->global_value), &ctx,
                              njs_builtin_traverse);

    if (ret == NJS_DONE) {
        goto found;
    }

    /* Constructor from built-in modules (not-mapped to global object). */

    for (i = NJS_OBJ_TYPE_HIDDEN_MIN; i < NJS_OBJ_TYPE_HIDDEN_MAX; i++) {
        njs_set_object(&value, &njs_vm_ctor(vm, i).object);

        ret = njs_value_property(vm, &value, NJS_ATOM_STRING_name, &tag);
        if (ret == NJS_OK && njs_is_string(&tag)) {
            njs_string_get(vm, &tag, &ctx.match);
        }

        ret = njs_object_traverse(vm, njs_object(&value), &ctx,
                                  njs_builtin_traverse);

        if (ret == NJS_DONE) {
            goto found;
        }
    }

    /* Modules. */

    njs_lvlhsh_each_init(&lhe, &njs_modules_hash_proto);

    for ( ;; ) {
        prop = (njs_object_prop_t *) njs_flathsh_each(&vm->modules_hash, &lhe);
        if (prop == NULL) {
            break;
        }

        module = prop->u.mod;

        if (njs_is_object(&module->value)
            && !njs_object(&module->value)->shared)
        {
            ctx.match = module->name;

            ret = njs_object_traverse(vm, njs_object(&module->value), &ctx,
                                      njs_builtin_traverse);

            if (ret == NJS_DONE) {
                goto found;
            }
        }
    }

    /* External prototypes (not mapped to global object). */

    ctx.match = njs_str_value("");

    for (i = 0; i< vm->protos->items; i++) {
        njs_memzero(&object, sizeof(njs_object_t));

        pprotos = njs_arr_item(vm->protos, i);
        slots = (*pprotos)->start;

        object.shared_hash = slots->external_shared_hash;
        object.slots = slots;

        njs_set_object(&value, &object);

        ret = njs_object_string_tag(vm, &value, &tag);
        if (ret == NJS_OK && njs_is_string(&tag)) {
            njs_string_get(vm, &tag, &ctx.match);
        }

        ret = njs_object_traverse(vm, njs_object(&value), &ctx,
                                  njs_builtin_traverse);

        if (ret == NJS_DONE) {
            goto found;
        }
    }

    return NJS_DECLINED;

found:

    if (vm->functions_name_cache == NULL) {
        vm->functions_name_cache = njs_arr_create(vm->mem_pool, 4,
                                                  sizeof(njs_function_name_t));
        if (njs_slow_path(vm->functions_name_cache == NULL)) {
            return NJS_ERROR;
        }
    }

    fn = njs_arr_add(vm->functions_name_cache);
    if (njs_slow_path(fn == NULL)) {
        njs_memory_error(vm);
        return NJS_ERROR;
    }

    fn->name = ctx.match;
    fn->native = function->u.native;
    fn->magic8 = function->magic8;

    *name = fn->name;

    return NJS_OK;
}


static njs_int_t
njs_ext_dump(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
    njs_index_t unused, njs_value_t *retval)
{
    uint32_t     n;
    njs_int_t    ret;
    njs_str_t    str;
    njs_value_t  *value, *indent;

    value = njs_arg(args, nargs, 1);
    indent = njs_arg(args, nargs, 2);

    ret = njs_value_to_uint32(vm, indent, &n);
    if (njs_slow_path(ret != NJS_OK)) {
        return ret;
    }

    n = njs_min(n, 5);

    if (njs_vm_value_dump(vm, &str, value, 1, n) != NJS_OK) {
        return NJS_ERROR;
    }

    return njs_string_create(vm, retval, str.start, str.length);
}


static njs_int_t
njs_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
    njs_index_t unused, njs_value_t *retval)
{
    njs_str_t    type;
    njs_uint_t   i, n;
    njs_value_t  *value;

    static const struct {
        njs_str_t   name;
        njs_uint_t  id;
    } hooks[] = {
        {
            njs_str("exit"),
            NJS_HOOK_EXIT
        },
    };

    value = njs_arg(args, nargs, 1);

    if (njs_slow_path(!njs_is_string(value))) {
        njs_type_error(vm, "hook type is not a string");
        return NJS_ERROR;
    }

    njs_string_get(vm, value, &type);

    i = 0;
    n = sizeof(hooks) / sizeof(hooks[0]);

    while (i < n) {
        if (njs_strstr_eq(&type, &hooks[i].name)) {
            break;
        }

        i++;
    }

    if (i == n) {
        njs_type_error(vm, "unknown hook type \"%V\"", &type);
        return NJS_ERROR;
    }

    value = njs_arg(args, nargs, 2);

    if (njs_slow_path(!njs_is_function(value) && !njs_is_null(value))) {
        njs_type_error(vm, "callback is not a function or null");
        return NJS_ERROR;
    }

    vm->hooks[i] = njs_is_function(value) ? njs_function(value) : NULL;

    return NJS_OK;
}


static njs_int_t
njs_ext_memory_stats(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
    njs_value_t *unused2, njs_value_t *unused3, njs_value_t *retval)
{
    njs_int_t      ret;
    njs_value_t    object, value;
    njs_object_t   *stat;
    njs_mp_stat_t  mp_stat;

    stat = njs_object_alloc(vm);
    if (njs_slow_path(stat == NULL)) {
        return NJS_ERROR;
    }

    njs_set_object(&object, stat);

    njs_mp_stat(vm->mem_pool, &mp_stat);

    njs_set_number(&value, mp_stat.size);

    ret = njs_value_property_set(vm, &object, NJS_ATOM_STRING_size, &value);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    njs_set_number(&value, mp_stat.nblocks);

    ret = njs_value_property_set(vm, &object, NJS_ATOM_STRING_nblocks, &value);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    njs_set_number(&value, mp_stat.cluster_size);

    ret = njs_value_property_set(vm, &object, NJS_ATOM_STRING_cluster_size,
                                 &value);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    njs_set_number(&value, mp_stat.page_size);

    ret = njs_value_property_set(vm, &object, NJS_ATOM_STRING_page_size, &value);
    if (njs_slow_path(ret != NJS_OK)) {
        return NJS_ERROR;
    }

    njs_set_object(retval, stat);

    return NJS_OK;
}




static njs_int_t
njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop,
    uint32_t atom_id, njs_value_t *global, njs_value_t *setval,
    njs_value_t *retval)
{
    njs_value_t          *value;
    njs_variable_t       *var;
    njs_rbtree_node_t    *rb_node;
    njs_variable_node_t  *node, var_node;

    if (retval == NULL) {
        return NJS_DECLINED;
    }

    var_node.key = atom_id;

    if (njs_slow_path(vm->global_scope == NULL)) {
        return NJS_DECLINED;
    }

    rb_node = njs_rbtree_find(&vm->global_scope->variables, &var_node.node);
    if (rb_node == NULL) {
        return NJS_DECLINED;
    }

    node = (njs_variable_node_t *) rb_node;

    var = node->variable;

    if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) {
        return NJS_DECLINED;
    }

    value = njs_scope_valid_value(vm, var->index);

    if (setval != NULL) {
        njs_value_assign(value, setval);
    }

    njs_value_assign(retval, value);

    return NJS_OK;
}


static njs_int_t
njs_global_this_object(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
    njs_value_t *global, njs_value_t *setval, njs_value_t *retval)
{
    njs_int_t            ret;
    njs_object_prop_t    *prop;
    njs_flathsh_query_t  lhq;

    if (retval == NULL) {
        return NJS_DECLINED;
    }

    njs_value_assign(retval, global);

    if (njs_slow_path(setval != NULL)) {
        njs_value_assign(retval, setval);
    }

    lhq.key_hash = atom_id;
    lhq.replace = 1;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;

    ret = njs_flathsh_unique_insert(njs_object_hash(global), &lhq);
    if (njs_slow_path(ret != NJS_OK)) {
        njs_internal_error(vm, "lvlhsh insert/replace failed");
        return NJS_ERROR;
    }

    prop = lhq.value;

    prop->type = NJS_PROPERTY;
    prop->enumerable = self->enumerable;
    prop->configurable = 1;
    prop->writable = 1;
    prop->u.value = *retval;

    return NJS_OK;
}


static njs_int_t
njs_top_level_object(njs_vm_t *vm, njs_object_prop_t *self, uint32_t atom_id,
    njs_value_t *global, njs_value_t *setval, njs_value_t *retval)
{
    njs_int_t            ret;
    njs_object_t         *object;
    njs_object_prop_t    *prop;
    njs_flathsh_query_t  lhq;

    if (njs_slow_path(setval != NULL)) {
        njs_value_assign(retval, setval);

    } else {
        if (njs_slow_path(retval == NULL)) {
            return NJS_DECLINED;
        }

        njs_set_object(retval, &vm->shared->objects[njs_prop_magic16(self)]);

        object = njs_object_value_copy(vm, retval);
        if (njs_slow_path(object == NULL)) {
            return NJS_ERROR;
        }

        object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT);
    }

    lhq.key_hash = atom_id;
    lhq.replace = 1;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;

    ret = njs_flathsh_unique_insert(njs_object_hash(global), &lhq);
    if (njs_slow_path(ret != NJS_OK)) {
        njs_internal_error(vm, "lvlhsh insert/replace failed");
        return NJS_ERROR;
    }

    prop = lhq.value;

    prop->type = NJS_PROPERTY;
    prop->enumerable = self->enumerable;
    prop->configurable = 1;
    prop->writable = 1;
    prop->u.value = *retval;

    return NJS_OK;
}


static njs_int_t
njs_top_level_constructor(njs_vm_t *vm, njs_object_prop_t *self,
    uint32_t atom_id, njs_value_t *global, njs_value_t *setval,
    njs_value_t *retval)
{
    njs_int_t            ret;
    njs_function_t       *ctor;
    njs_object_prop_t    *prop;
    njs_flathsh_query_t  lhq;

    if (njs_slow_path(setval != NULL)) {
        njs_value_assign(retval, setval);

    } else {
        if (njs_slow_path(retval == NULL)) {
            return NJS_DECLINED;
        }

        ctor = &njs_vm_ctor(vm, njs_prop_magic16(self));

        njs_set_function(retval, ctor);

        return NJS_OK;
    }

    lhq.key_hash = atom_id;
    lhq.replace = 1;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;

    ret = njs_flathsh_unique_insert(njs_object_hash(global), &lhq);
    if (njs_slow_path(ret != NJS_OK)) {
        njs_internal_error(vm, "lvlhsh insert/replace failed");
        return NJS_ERROR;
    }

    prop = lhq.value;

    prop->type = NJS_PROPERTY;
    prop->enumerable = 0;
    prop->configurable = 1;
    prop->writable = 1;
    prop->u.value = *retval;

    return NJS_OK;
}


static const njs_object_prop_init_t  njs_global_this_object_properties[] =
{
    NJS_DECLARE_PROP_VALUE(SYMBOL_toStringTag, njs_ascii_strval("global"),
                           NJS_OBJECT_PROP_VALUE_C),

    /* Global aliases. */

    NJS_DECLARE_PROP_HANDLER(STRING_global, njs_global_this_object, 0,
                             NJS_OBJECT_PROP_VALUE_ECW),

    NJS_DECLARE_PROP_HANDLER(STRING_globalThis, njs_global_this_object, 0,
                             NJS_OBJECT_PROP_VALUE_CW),

    /* Global constants. */

    NJS_DECLARE_PROP_VALUE(STRING_NaN,  njs_value(NJS_NUMBER, 0, NAN), 0),

    NJS_DECLARE_PROP_VALUE(STRING_Infinity,
                           njs_value(NJS_NUMBER, 1, INFINITY), 0),

    NJS_DECLARE_PROP_VALUE(STRING_undefined,
                           njs_value(NJS_UNDEFINED, 0, NAN), 0),

    /* Global functions. */

    NJS_DECLARE_PROP_NATIVE(STRING_isFinite, njs_number_global_is_finite, 1,
                            0),

    NJS_DECLARE_PROP_NATIVE(STRING_isNaN, njs_number_global_is_nan, 1, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_parseFloat, njs_number_parse_float, 1,
                            0),

    NJS_DECLARE_PROP_NATIVE(STRING_parseInt, njs_number_parse_int, 2, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_toString, njs_object_prototype_to_string,
                            0, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_encodeURI, njs_string_encode_uri, 1, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_encodeURIComponent,
                            njs_string_encode_uri, 1, 1),

    NJS_DECLARE_PROP_NATIVE(STRING_decodeURI, njs_string_decode_uri, 1, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_decodeURIComponent,
                            njs_string_decode_uri, 1, 1),

    NJS_DECLARE_PROP_NATIVE(STRING_atob, njs_string_atob, 1, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_btoa, njs_string_btoa, 1, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_eval, njs_eval_function, 1, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_require, njs_module_require, 1, 0),

    /* Global objects. */

    NJS_DECLARE_PROP_HANDLER(STRING_njs, njs_top_level_object,
                             NJS_OBJECT_NJS, NJS_OBJECT_PROP_VALUE_ECW),

    NJS_DECLARE_PROP_HANDLER(STRING_process, njs_top_level_object,
                             NJS_OBJECT_PROCESS, NJS_OBJECT_PROP_VALUE_ECW),

    NJS_DECLARE_PROP_HANDLER(STRING_Math, njs_top_level_object,
                             NJS_OBJECT_MATH, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_JSON, njs_top_level_object,
                             NJS_OBJECT_JSON, NJS_OBJECT_PROP_VALUE_CW),


#ifdef NJS_TEST262
    NJS_DECLARE_PROP_HANDLER(STRING__262, njs_top_level_object,
                             NJS_OBJECT_262, NJS_OBJECT_PROP_VALUE_ECW),
#endif

    /* Global constructors. */

    NJS_DECLARE_PROP_HANDLER(STRING_Object, njs_top_level_constructor,
                             NJS_OBJ_TYPE_OBJECT, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_ARRAY, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_ArrayBuffer, njs_top_level_constructor,
                             NJS_OBJ_TYPE_ARRAY_BUFFER,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_DataView, njs_top_level_constructor,
                             NJS_OBJ_TYPE_DATA_VIEW,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_TextDecoder, njs_top_level_constructor,
                             NJS_OBJ_TYPE_TEXT_DECODER,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_TextEncoder, njs_top_level_constructor,
                             NJS_OBJ_TYPE_TEXT_ENCODER,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Buffer, njs_top_level_constructor,
                             NJS_OBJ_TYPE_BUFFER, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Uint8Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_UINT8_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Uint16Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_UINT16_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Uint32Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_UINT32_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Int8Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_INT8_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Int16Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_INT16_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Int32Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_INT32_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Float32Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_FLOAT32_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Float64Array, njs_top_level_constructor,
                             NJS_OBJ_TYPE_FLOAT64_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Uint8ClampedArray,
                             njs_top_level_constructor,
                             NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Boolean, njs_top_level_constructor,
                             NJS_OBJ_TYPE_BOOLEAN, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Number, njs_top_level_constructor,
                             NJS_OBJ_TYPE_NUMBER, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Symbol, njs_top_level_constructor,
                             NJS_OBJ_TYPE_SYMBOL, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_String, njs_top_level_constructor,
                             NJS_OBJ_TYPE_STRING, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Function, njs_top_level_constructor,
                             NJS_OBJ_TYPE_FUNCTION, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_RegExp, njs_top_level_constructor,
                             NJS_OBJ_TYPE_REGEXP, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Date, njs_top_level_constructor,
                             NJS_OBJ_TYPE_DATE, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Promise, njs_top_level_constructor,
                             NJS_OBJ_TYPE_PROMISE, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_Error, njs_top_level_constructor,
                             NJS_OBJ_TYPE_ERROR, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_EvalError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_EVAL_ERROR, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_InternalError,
                             njs_top_level_constructor,
                             NJS_OBJ_TYPE_INTERNAL_ERROR,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_RangeError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_RANGE_ERROR,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_ReferenceError,
                             njs_top_level_constructor,
                             NJS_OBJ_TYPE_REF_ERROR, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_SyntaxError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_SYNTAX_ERROR,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_TypeError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_TYPE_ERROR, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_URIError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_URI_ERROR, NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_MemoryError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_MEMORY_ERROR,
                             NJS_OBJECT_PROP_VALUE_CW),

    NJS_DECLARE_PROP_HANDLER(STRING_AggregateError, njs_top_level_constructor,
                             NJS_OBJ_TYPE_AGGREGATE_ERROR,
                             NJS_OBJECT_PROP_VALUE_CW),
};


static const njs_object_init_t  njs_global_this_init = {
    njs_global_this_object_properties,
    njs_nitems(njs_global_this_object_properties)
};


static const njs_object_prop_init_t  njs_njs_object_properties[] =
{
    NJS_DECLARE_PROP_VALUE(SYMBOL_toStringTag, njs_ascii_strval("njs"),
                           NJS_OBJECT_PROP_VALUE_C),

    NJS_DECLARE_PROP_VALUE(STRING_engine, njs_ascii_strval("njs"),
                           NJS_OBJECT_PROP_VALUE_EC),

    NJS_DECLARE_PROP_VALUE(STRING_version, njs_ascii_strval(NJS_VERSION),
                           NJS_OBJECT_PROP_VALUE_EC),

    NJS_DECLARE_PROP_VALUE(STRING_version_number,
                           njs_value(NJS_NUMBER, 1, NJS_VERSION_NUMBER),
                           NJS_OBJECT_PROP_VALUE_EC),

    NJS_DECLARE_PROP_NATIVE(STRING_dump, njs_ext_dump, 0, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_on, njs_ext_on, 0, 0),

    NJS_DECLARE_PROP_HANDLER(STRING_memoryStats, njs_ext_memory_stats, 0,
                             NJS_OBJECT_PROP_VALUE_EC),

};


static const njs_object_init_t  njs_njs_object_init = {
    njs_njs_object_properties,
    njs_nitems(njs_njs_object_properties),
};


static njs_int_t
njs_process_object_argv(njs_vm_t *vm, njs_object_prop_t *pr, uint32_t unused,
    njs_value_t *process, njs_value_t *unused2, njs_value_t *retval)
{
    char                 **arg;
    njs_int_t            ret;
    njs_uint_t           i;
    njs_array_t          *argv;
    njs_object_prop_t    *prop;
    njs_flathsh_query_t  lhq;

    argv = njs_array_alloc(vm, 1, vm->options.argc, 0);
    if (njs_slow_path(argv == NULL)) {
        return NJS_ERROR;
    }

    i = 0;

    for (arg = vm->options.argv; i < vm->options.argc; arg++) {
        ret = njs_string_create(vm, &argv->start[i++], (u_char *) *arg,
                                njs_strlen(*arg));
        if (njs_slow_path(ret != NJS_OK)) {
            return NJS_ERROR;
        }
    }

    lhq.key_hash = NJS_ATOM_STRING_argv;
    lhq.replace = 1;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;

    ret = njs_flathsh_unique_insert(njs_object_hash(process), &lhq);
    if (njs_slow_path(ret != NJS_OK)) {
        njs_internal_error(vm, "lvlhsh insert failed");
        return NJS_ERROR;
    }

    prop = lhq.value;

    prop->type = NJS_PROPERTY;
    prop->enumerable = 1;
    prop->configurable = 1;
    prop->writable = 1;

    njs_set_array(njs_prop_value(prop), argv);

    njs_value_assign(retval, njs_prop_value(prop));
    return NJS_OK;

}


static njs_int_t
njs_env_hash_init(njs_vm_t *vm, njs_flathsh_t *hash, char **environment)
{
    char                 **ep;
    u_char               *dst;
    ssize_t              length;
    uint32_t             cp;
    njs_int_t            ret;
    njs_value_t          prop_name;
    const u_char         *val, *entry, *s, *end;
    njs_object_prop_t    *prop;
    njs_string_prop_t    string;
    njs_flathsh_query_t  lhq;

    lhq.replace = 0;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;

    ep = environment;

    while (*ep != NULL) {

        entry = (u_char *) *ep++;

        val = njs_strchr(entry, '=');
        if (njs_slow_path(val == NULL)) {
            continue;
        }

        ret = njs_string_create(vm, &prop_name, entry, val - entry);
        if (njs_slow_path(ret != NJS_OK)) {
            return NJS_ERROR;
        }

        (void) njs_string_prop(vm, &string, &prop_name);

        length = string.length;
        s = string.start;
        end = s + string.size;
        dst = (u_char *) s;

        while (length != 0) {
            cp = njs_utf8_upper_case(&s, end);
            dst = njs_utf8_encode(dst, cp);
            length--;
        }

        val++;

        ret = njs_atom_atomize_key(vm, &prop_name);
        if (ret != NJS_OK) {
            return ret;
        }

        lhq.key_hash = prop_name.atom_id;

        ret = njs_flathsh_unique_insert(hash, &lhq);
        if (njs_slow_path(ret != NJS_OK)) {
            if (ret == NJS_ERROR) {
                njs_internal_error(vm, "lvlhsh insert failed");
                return NJS_ERROR;
            }

            /* ret == NJS_DECLINED: entry already exists */

            /*
             * Always using the first element among the duplicates
             * and ignoring the rest.
             */
            continue;
        }

        prop = lhq.value;

        prop->type = NJS_PROPERTY;
        prop->enumerable = 1;
        prop->configurable = 1;
        prop->writable = 1;

        ret = njs_string_create(vm, njs_prop_value(prop), val, njs_strlen(val));
        if (njs_slow_path(ret != NJS_OK)) {
            return NJS_ERROR;
        }
    }

    return NJS_OK;
}


static njs_int_t
njs_process_object_env(njs_vm_t *vm, njs_object_prop_t *pr, uint32_t unused,
    njs_value_t *process, njs_value_t *unused2, njs_value_t *retval)
{
    njs_int_t            ret;
    njs_object_t         *env;
    njs_object_prop_t    *prop;
    njs_flathsh_query_t  lhq;

    env = njs_object_alloc(vm);
    if (njs_slow_path(env == NULL)) {
        return NJS_ERROR;
    }

    env->shared_hash = vm->shared->env_hash;

    lhq.replace = 1;
    lhq.pool = vm->mem_pool;
    lhq.proto = &njs_object_hash_proto;
    lhq.key_hash = NJS_ATOM_STRING_env;

    ret = njs_flathsh_unique_insert(njs_object_hash(process), &lhq);
    if (njs_slow_path(ret != NJS_OK)) {
        njs_internal_error(vm, "lvlhsh insert failed");
        return NJS_ERROR;
    }

    prop = lhq.value;

    prop->type = NJS_PROPERTY;
    prop->enumerable = 1;
    prop->configurable = 1;
    prop->writable = 1;

    njs_set_object(njs_prop_value(prop), env);

    njs_value_assign(retval, njs_prop_value(prop));

    return NJS_OK;
}


static njs_int_t
njs_process_object_pid(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
    njs_value_t *unused2, njs_value_t *unused3, njs_value_t *retval)
{
    njs_set_number(retval, getpid());

    return NJS_OK;
}


static njs_int_t
njs_process_object_ppid(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
    njs_value_t *unused2, njs_value_t *unused3, njs_value_t *retval)
{
    njs_set_number(retval, getppid());

    return NJS_OK;
}


static njs_int_t
njs_ext_process_kill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
    njs_index_t magic, njs_value_t *retval)
{
    int                       signal;
    njs_str_t                 str;
    njs_uint_t                pid;
    njs_value_t               *arg;
    const njs_signal_entry_t  *s;

    arg = njs_arg(args, nargs, 1);
    if (!njs_value_is_number(arg)) {
        njs_vm_type_error(vm, "\"pid\" is not a number");
        return NJS_ERROR;
    }

    pid = njs_value_number(arg);
    signal = SIGTERM;

    arg = njs_arg(args, nargs, 2);
    if (njs_value_is_number(arg)) {
        signal = njs_value_number(arg);

    } else if (njs_value_is_string(arg)) {
        njs_string_get(vm, arg, &str);

        if (str.length < 3 || memcmp(str.start, "SIG", 3) != 0) {
            njs_vm_type_error(vm, "\"signal\" unknown value: \"%V\"", &str);
            return NJS_ERROR;
        }

        str.start += 3;
        str.length -= 3;

        for (s = &njs_signals_table[0]; s->name.length != 0; s++) {
            if (njs_strstr_eq(&str, &s->name)) {
                signal = s->value;
                break;
            }
        }

        if (s->name.length == 0) {
            njs_vm_type_error(vm, "\"signal\" unknown value");
            return NJS_ERROR;
        }

    } else if (!njs_value_is_undefined(arg)) {
        njs_vm_type_error(vm, "\"signal\" invalid type");
        return NJS_ERROR;
    }

    if (kill(pid, signal) < 0) {
        njs_vm_error(vm, "kill failed with (%d:%s)", errno, strerror(errno));
        return NJS_ERROR;
    }

    njs_set_boolean(retval, 1);
    return NJS_OK;
}


static const njs_object_prop_init_t  njs_process_object_properties[] =
{
    NJS_DECLARE_PROP_VALUE(SYMBOL_toStringTag, njs_ascii_strval("process"),
                           NJS_OBJECT_PROP_VALUE_C),

    NJS_DECLARE_PROP_HANDLER(STRING_argv, njs_process_object_argv, 0, 0),

    NJS_DECLARE_PROP_HANDLER(STRING_env, njs_process_object_env, 0, 0),

    NJS_DECLARE_PROP_HANDLER(STRING_pid, njs_process_object_pid, 0, 0),

    NJS_DECLARE_PROP_HANDLER(STRING_ppid, njs_process_object_ppid, 0, 0),

    NJS_DECLARE_PROP_NATIVE(STRING_kill, njs_ext_process_kill, 2, 0),
};


static const njs_object_init_t  njs_process_object_init = {
    njs_process_object_properties,
    njs_nitems(njs_process_object_properties),
};
