blob: c475750aa4c96804acf4bcbe6340c0aac213c73c [file] [log] [blame]
/*
* Copyright (C) Alexander Borisov
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
static njs_value_t *njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src,
njs_uint_t runtime, njs_index_t **index);
njs_index_t
njs_scope_temp_index(njs_parser_scope_t *scope)
{
scope = njs_function_scope(scope);
if (njs_slow_path(scope == NULL)) {
return NJS_INDEX_ERROR;
}
return njs_scope_index(scope->type, scope->items++, NJS_LEVEL_LOCAL,
NJS_VARIABLE_VAR);
}
njs_value_t *
njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index)
{
njs_value_t *value;
value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
if (njs_slow_path(value == NULL)) {
return NULL;
}
njs_scope_value_set(vm, index, value);
return value;
}
njs_value_t **
njs_scope_make(njs_vm_t *vm, uint32_t count)
{
size_t size;
njs_value_t **refs, *values;
size = (count * sizeof(njs_value_t *)) + (count * sizeof(njs_value_t));
refs = njs_mp_alloc(vm->mem_pool, size);
if (njs_slow_path(refs == NULL)) {
njs_memory_error(vm);
return NULL;
}
values = (njs_value_t *) ((u_char *) refs
+ (count * sizeof(njs_value_t *)));
while (count != 0) {
count--;
refs[count] = &values[count];
njs_set_invalid(refs[count]);
}
return refs;
}
njs_index_t
njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
{
njs_index_t index, *retval;
njs_value_t **values, *value;
value = njs_scope_value_index(vm, src, runtime, &retval);
if (njs_slow_path(value == NULL)) {
return NJS_INDEX_ERROR;
}
if (*retval != NJS_INDEX_ERROR) {
return *retval;
}
if (vm->scope_absolute == NULL) {
vm->scope_absolute = njs_arr_create(vm->mem_pool, 8,
sizeof(njs_value_t *));
if (njs_slow_path(vm->scope_absolute == NULL)) {
return NJS_INDEX_ERROR;
}
}
index = vm->scope_absolute->items;
values = njs_arr_add(vm->scope_absolute);
if (njs_slow_path(values == NULL)) {
return NJS_INDEX_ERROR;
}
*values = value;
vm->levels[NJS_LEVEL_STATIC] = vm->scope_absolute->start;
*retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_LEVEL_STATIC,
NJS_VARIABLE_VAR);
return *retval;
}
njs_value_t *
njs_scope_value_get(njs_vm_t *vm, njs_index_t index)
{
return njs_scope_value(vm, index);
}
static njs_int_t
njs_scope_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
{
njs_str_t string;
njs_value_t *value;
value = data;
if (njs_is_string(value)) {
njs_string_get(value, &string);
} else {
string.start = (u_char *) value;
string.length = sizeof(njs_value_t);
}
if (lhq->key.length == string.length
&& memcmp(lhq->key.start, string.start, string.length) == 0)
{
return NJS_OK;
}
return NJS_DECLINED;
}
static const njs_lvlhsh_proto_t njs_values_hash_proto
njs_aligned(64) =
{
NJS_LVLHSH_DEFAULT,
njs_scope_values_hash_test,
njs_lvlhsh_alloc,
njs_lvlhsh_free,
};
/*
* Constant values such as njs_value_true are copied to values_hash during
* code generation when they are used as operands to guarantee aligned value.
*/
static njs_value_t *
njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
njs_index_t **index)
{
u_char *start;
uint32_t value_size, size, length;
njs_int_t ret;
njs_str_t str;
njs_bool_t long_string;
njs_value_t *value;
njs_string_t *string;
njs_lvlhsh_t *values_hash;
njs_lvlhsh_query_t lhq;
long_string = 0;
value_size = sizeof(njs_value_t);
if (njs_is_string(src)) {
njs_string_get(src, &str);
size = (uint32_t) str.length;
start = str.start;
if (src->short_string.size == NJS_STRING_LONG) {
long_string = 1;
}
} else {
size = value_size;
start = (u_char *) src;
}
lhq.key_hash = njs_djb_hash(start, size);
lhq.key.length = size;
lhq.key.start = start;
lhq.proto = &njs_values_hash_proto;
if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
value = lhq.value;
*index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
} else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
value = lhq.value;
*index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
} else {
if (long_string) {
length = src->long_string.data->length;
if (size != length && length > NJS_STRING_MAP_STRIDE) {
size = njs_string_map_offset(size)
+ njs_string_map_size(length);
}
value_size += sizeof(njs_string_t) + size;
}
value_size += sizeof(njs_index_t);
value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
if (njs_slow_path(value == NULL)) {
return NULL;
}
*value = *src;
if (long_string) {
string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t)
+ sizeof(njs_index_t));
value->long_string.data = string;
string->start = (u_char *) string + sizeof(njs_string_t);
string->length = src->long_string.data->length;
string->retain = 0xffff;
memcpy(string->start, start, size);
}
*index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
**index = NJS_INDEX_ERROR;
lhq.replace = 0;
lhq.value = value;
lhq.pool = vm->mem_pool;
values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
ret = njs_lvlhsh_insert(values_hash, &lhq);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
}
if (start != (u_char *) src) {
/*
* The source node value must be updated with the shared value
* allocated from the permanent memory pool because the node
* value can be used as a variable initial value.
*/
*(njs_value_t *) src = *value;
}
return value;
}