blob: 8536996e305be0eb28a778f5afd6158fdd7e3c2d [file] [log] [blame]
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
static njs_int_t njs_external_prop_handler(njs_vm_t *vm,
njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval,
njs_value_t *retval);
static njs_int_t
njs_external_add(njs_vm_t *vm, njs_arr_t *protos,
const njs_external_t *external, njs_uint_t n)
{
size_t size;
ssize_t length;
njs_int_t ret;
njs_lvlhsh_t *hash;
const u_char *start;
njs_function_t *function;
njs_object_prop_t *prop;
njs_lvlhsh_query_t lhq;
njs_exotic_slots_t *slot, *next;
slot = njs_arr_add(protos);
njs_memzero(slot, sizeof(njs_exotic_slots_t));
hash = &slot->external_shared_hash;
njs_lvlhsh_init(hash);
lhq.replace = 0;
lhq.proto = &njs_object_hash_proto;
lhq.pool = vm->mem_pool;
while (n != 0) {
prop = njs_object_prop_alloc(vm, &njs_string_empty,
&njs_value_invalid, 1);
if (njs_slow_path(prop == NULL)) {
goto memory_error;
}
prop->writable = external->writable;
prop->configurable = external->configurable;
prop->enumerable = external->enumerable;
if (external->flags & 4) {
njs_set_symbol(&prop->name, external->name.symbol);
lhq.key_hash = external->name.symbol;
} else {
ret = njs_string_set(vm, &prop->name, external->name.string.start,
external->name.string.length);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
lhq.key = external->name.string;
lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
}
lhq.value = prop;
switch (external->flags & 3) {
case NJS_EXTERN_METHOD:
function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t));
if (njs_slow_path(function == NULL)) {
goto memory_error;
}
function->object.shared_hash = vm->shared->arrow_instance_hash;
function->object.type = NJS_FUNCTION;
function->object.shared = 1;
function->object.extensible = 1;
function->args_offset = 1;
function->native = 1;
function->magic8 = external->u.method.magic8;
function->u.native = external->u.method.native;
njs_set_function(&prop->value, function);
break;
case NJS_EXTERN_PROPERTY:
if (external->u.property.handler != NULL) {
prop->type = NJS_PROPERTY_HANDLER;
prop->value.type = NJS_INVALID;
prop->value.data.truth = 1;
prop->value.data.magic16 = 0;
prop->value.data.magic32 = external->u.property.magic32;
prop->value.data.u.prop_handler = external->u.property.handler;
} else {
start = (u_char *) external->u.property.value;
size = njs_strlen(start);
length = njs_utf8_length(start, size);
if (njs_slow_path(length < 0)) {
length = 0;
}
ret = njs_string_new(vm, &prop->value, start, size, length);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
break;
case NJS_EXTERN_OBJECT:
default:
next = njs_arr_item(protos, protos->items);
ret = njs_external_add(vm, protos, external->u.object.properties,
external->u.object.nproperties);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
prop->type = NJS_PROPERTY_HANDLER;
prop->value.type = NJS_INVALID;
prop->value.data.truth = 1;
prop->value.data.magic16 = next - slot;
prop->value.data.magic32 = lhq.key_hash;
prop->value.data.u.prop_handler = njs_external_prop_handler;
next->writable = external->u.object.writable;
next->configurable = external->u.object.configurable;
next->enumerable = external->u.object.enumerable;
next->prop_handler = external->u.object.prop_handler;
next->magic32 = external->u.object.magic32;
next->keys = external->u.object.keys;
break;
}
ret = njs_lvlhsh_insert(hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "lvlhsh insert failed");
return NJS_ERROR;
}
n--;
external++;
}
return NJS_OK;
memory_error:
njs_memory_error(vm);
return NJS_ERROR;
}
static njs_int_t
njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
njs_int_t ret;
njs_object_prop_t *prop;
njs_external_ptr_t external;
njs_object_value_t *ov;
njs_lvlhsh_query_t lhq;
njs_exotic_slots_t *slots;
if (njs_slow_path(retval == NULL)) {
return NJS_DECLINED;
}
slots = NULL;
if (njs_slow_path(setval != NULL)) {
*retval = *setval;
} else {
ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
if (njs_slow_path(ov == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
slots = njs_object(value)->slots + self->value.data.magic16;
njs_lvlhsh_init(&ov->object.hash);
ov->object.shared_hash = slots->external_shared_hash;
ov->object.type = NJS_OBJECT;
ov->object.shared = 0;
ov->object.extensible = 1;
ov->object.error_data = 0;
ov->object.fast_array = 0;
ov->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
ov->object.slots = slots;
external = njs_vm_external(vm, NJS_PROTO_ID_ANY, value);
njs_set_data(&ov->value, external, njs_value_external_tag(value));
njs_set_object_value(retval, ov);
}
prop = njs_object_prop_alloc(vm, &self->name, retval, 1);
if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
if (slots != NULL) {
prop->writable = slots->writable;
prop->configurable = slots->configurable;
prop->enumerable = slots->enumerable;
}
lhq.value = prop;
njs_string_get(&self->name, &lhq.key);
lhq.key_hash = self->value.data.magic32;
lhq.replace = 1;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_object_hash_proto;
ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "lvlhsh insert/replace failed");
return NJS_ERROR;
}
return NJS_OK;
}
static njs_uint_t
njs_external_protos(const njs_external_t *external, njs_uint_t size)
{
njs_uint_t n;
n = 1;
while (size != 0) {
if ((external->flags & 3) == NJS_EXTERN_OBJECT) {
n += njs_external_protos(external->u.object.properties,
external->u.object.nproperties);
}
size--;
external++;
}
return n;
}
njs_int_t
njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition,
njs_uint_t n)
{
njs_arr_t *protos;
njs_int_t ret;
uintptr_t *pr;
njs_uint_t size;
size = njs_external_protos(definition, n) + 1;
protos = njs_arr_create(vm->mem_pool, size, sizeof(njs_exotic_slots_t));
if (njs_slow_path(protos == NULL)) {
njs_memory_error(vm);
return -1;
}
ret = njs_external_add(vm, protos, definition, n);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "njs_vm_external_add() failed");
return -1;
}
if (vm->protos == NULL) {
vm->protos = njs_arr_create(vm->mem_pool, 4, sizeof(uintptr_t));
if (njs_slow_path(vm->protos == NULL)) {
return -1;
}
}
pr = njs_arr_add(vm->protos);
if (njs_slow_path(pr == NULL)) {
return -1;
}
*pr = (uintptr_t) protos;
return vm->protos->items - 1;
}
njs_int_t
njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, njs_int_t proto_id,
njs_external_ptr_t external, njs_bool_t shared)
{
njs_arr_t *protos;
uintptr_t proto;
njs_object_value_t *ov;
njs_exotic_slots_t *slots;
if (vm->protos == NULL || (njs_int_t) vm->protos->items <= proto_id) {
return NJS_ERROR;
}
proto = ((uintptr_t *) vm->protos->start)[proto_id];
ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
if (njs_slow_path(ov == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
protos = (njs_arr_t *) proto;
slots = protos->start;
njs_lvlhsh_init(&ov->object.hash);
ov->object.shared_hash = slots->external_shared_hash;
ov->object.type = NJS_OBJECT;
ov->object.shared = shared;
ov->object.extensible = 1;
ov->object.error_data = 0;
ov->object.fast_array = 0;
ov->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
ov->object.slots = slots;
njs_set_object_value(value, ov);
njs_set_data(&ov->value, external, njs_make_tag(proto_id));
return NJS_OK;
}
njs_external_ptr_t
njs_vm_external(njs_vm_t *vm, njs_int_t proto_id, const njs_value_t *value)
{
njs_external_ptr_t external;
if (njs_fast_path(njs_is_object_data(value, njs_make_tag(proto_id)))) {
external = njs_object_data(value);
if (external == NULL) {
external = vm->external;
}
return external;
}
return NULL;
}
njs_int_t
njs_value_external_tag(const njs_value_t *value)
{
if (njs_is_object_data(value, njs_make_tag(NJS_PROTO_ID_ANY))) {
return njs_object_value(value)->data.magic32;
}
return -1;
}