blob: 9787be32a890bd7b504b7fd759f3b51f71e06f61 [file] [log] [blame]
/*
* Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
#include "njs_externals_test.h"
typedef struct {
njs_lvlhsh_t hash;
njs_external_proto_t proto;
uint32_t a;
uint32_t d;
njs_str_t uri;
njs_opaque_value_t value;
} njs_unit_test_req_t;
typedef struct {
njs_value_t name;
njs_value_t value;
} njs_unit_test_prop_t;
static njs_int_t
lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data)
{
njs_str_t name;
njs_unit_test_prop_t *prop;
prop = data;
njs_string_get(&prop->name, &name);
if (name.length != lhq->key.length) {
return NJS_DECLINED;
}
if (memcmp(name.start, lhq->key.start, lhq->key.length) == 0) {
return NJS_OK;
}
return NJS_DECLINED;
}
static void *
lvlhsh_unit_test_pool_alloc(void *pool, size_t size)
{
return njs_mp_align(pool, size, size);
}
static void
lvlhsh_unit_test_pool_free(void *pool, void *p, size_t size)
{
njs_mp_free(pool, p);
}
static const njs_lvlhsh_proto_t lvlhsh_proto njs_aligned(64) = {
NJS_LVLHSH_LARGE_SLAB,
lvlhsh_unit_test_key_test,
lvlhsh_unit_test_pool_alloc,
lvlhsh_unit_test_pool_free,
};
static njs_unit_test_prop_t *
lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_value_t *name,
const njs_value_t *value)
{
njs_unit_test_prop_t *prop;
prop = njs_mp_alloc(pool, sizeof(njs_unit_test_prop_t));
if (prop == NULL) {
return NULL;
}
prop->name = *name;
prop->value = *value;
return prop;
}
static njs_int_t
lvlhsh_unit_test_add(njs_mp_t *pool, njs_unit_test_req_t *r,
njs_unit_test_prop_t *prop)
{
njs_lvlhsh_query_t lhq;
njs_string_get(&prop->name, &lhq.key);
lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
lhq.replace = 1;
lhq.value = (void *) prop;
lhq.proto = &lvlhsh_proto;
lhq.pool = pool;
switch (njs_lvlhsh_insert(&r->hash, &lhq)) {
case NJS_OK:
return NJS_OK;
case NJS_DECLINED:
default:
return NJS_ERROR;
}
}
static njs_int_t
njs_unit_test_r_uri(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
char *p;
njs_str_t *field;
p = njs_vm_external(vm, value);
if (p == NULL) {
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
field = (njs_str_t *) (p + njs_vm_prop_magic32(prop));
if (setval != NULL) {
return njs_vm_value_to_string(vm, field, setval);
}
return njs_vm_value_string_set(vm, retval, field->start, field->length);
}
static njs_int_t
njs_unit_test_r_a(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
{
u_char *p;
njs_unit_test_req_t *r;
u_char buf[16];
r = njs_vm_external(vm, value);
if (r == NULL) {
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
p = njs_sprintf(buf, buf + njs_length(buf), "%uD", r->a);
return njs_vm_value_string_set(vm, retval, buf, p - buf);
}
static njs_int_t
njs_unit_test_r_b(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
{
njs_value_number_set(retval, njs_vm_prop_magic32(prop));
return NJS_OK;
}
static njs_int_t
njs_unit_test_r_d(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
{
njs_unit_test_req_t *r;
r = njs_vm_external(vm, value);
if (r == NULL) {
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
njs_value_number_set(retval, r->d);
return NJS_OK;
}
static njs_int_t
njs_unit_test_r_host(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
return njs_vm_value_string_set(vm, retval, (u_char *) "АБВГДЕЁЖЗИЙ", 22);
}
static njs_int_t
njs_unit_test_r_buffer(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
return njs_vm_value_buffer_set(vm, retval, (u_char *) "АБВГДЕЁЖЗИЙ", 22);
}
static njs_int_t
njs_unit_test_r_vars(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_value_t name;
njs_lvlhsh_query_t lhq;
njs_unit_test_req_t *r;
njs_unit_test_prop_t *prop;
r = njs_vm_external(vm, value);
if (r == NULL) {
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
ret = njs_vm_prop_name(vm, self, &lhq.key);
if (ret != NJS_OK) {
if (setval == NULL && retval != NULL) {
/* Get. */
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
return NJS_ERROR;
}
if (setval != NULL || retval == NULL) {
/* Set or Delete. */
if (lhq.key.length == 5 && memcmp(lhq.key.start, "error", 5) == 0) {
njs_vm_error(vm, "cannot %s \"error\" prop",
retval != NULL ? "set" : "delete");
return NJS_ERROR;
}
}
if (setval != NULL) {
/* Set. */
njs_vm_value_string_set(vm, &name, lhq.key.start, lhq.key.length);
prop = lvlhsh_unit_test_alloc(vm->mem_pool, &name, setval);
if (prop == NULL) {
njs_memory_error(vm);
return NJS_ERROR;
}
ret = lvlhsh_unit_test_add(vm->mem_pool, r, prop);
if (ret != NJS_OK) {
njs_vm_error(vm, "lvlhsh_unit_test_add() failed");
return NJS_ERROR;
}
return NJS_OK;
}
/* Get or Delete. */
lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
lhq.proto = &lvlhsh_proto;
ret = njs_lvlhsh_find(&r->hash, &lhq);
prop = lhq.value;
if (ret == NJS_OK) {
if (retval == NULL) {
njs_set_invalid(&prop->value);
return NJS_OK;
}
if (njs_is_valid(&prop->value)) {
*retval = prop->value;
return NJS_OK;
}
}
if (retval != NULL) {
njs_value_undefined_set(retval);
}
return NJS_DECLINED;
}
static njs_int_t
njs_unit_test_r_header(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *unused, njs_value_t *retval)
{
u_char *p;
uint32_t size;
njs_int_t ret;
njs_str_t h;
ret = njs_vm_prop_name(vm, prop, &h);
if (ret == NJS_OK) {
size = 7 + h.length;
p = njs_vm_value_string_alloc(vm, retval, size);
if (p == NULL) {
return NJS_ERROR;
}
p = njs_cpymem(p, h.start, h.length);
*p++ = '|';
memcpy(p, "АБВ", njs_length("АБВ"));
return NJS_OK;
}
njs_value_undefined_set(retval);
return NJS_DECLINED;
}
static njs_int_t
njs_unit_test_r_header_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys)
{
njs_int_t ret, i;
njs_value_t *push;
u_char k[2];
ret = njs_vm_array_alloc(vm, keys, 4);
if (ret != NJS_OK) {
return NJS_ERROR;
}
k[0] = '0';
k[1] = '1';
for (i = 0; i < 3; i++) {
push = njs_vm_array_push(vm, keys);
if (push == NULL) {
return NJS_ERROR;
}
(void) njs_vm_value_string_set(vm, push, k, 2);
k[1]++;
}
return NJS_OK;
}
static njs_int_t
njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_str_t s;
njs_unit_test_req_t *r;
r = njs_vm_external(vm, njs_arg(args, nargs, 0));
if (r == NULL) {
njs_type_error(vm, "\"this\" is not an external");
return NJS_ERROR;
}
ret = njs_vm_value_to_string(vm, &s, njs_arg(args, nargs, 1));
if (ret == NJS_OK && s.length == 3 && memcmp(s.start, "YES", 3) == 0) {
return njs_vm_value_string_set(vm, njs_vm_retval(vm), r->uri.start,
r->uri.length);
}
njs_set_undefined(&vm->retval);
return NJS_OK;
}
static njs_int_t
njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_unit_test_req_t *r, *sr;
r = njs_vm_external(vm, njs_arg(args, nargs, 0));
if (r == NULL) {
njs_type_error(vm, "\"this\" is not an external");
return NJS_ERROR;
}
sr = njs_mp_zalloc(vm->mem_pool, sizeof(njs_unit_test_req_t));
if (sr == NULL) {
goto memory_error;
}
if (njs_vm_value_to_string(vm, &sr->uri, njs_arg(args, nargs, 1))
!= NJS_OK)
{
return NJS_ERROR;
}
sr->proto = r->proto;
ret = njs_vm_external_create(vm, &vm->retval, sr->proto, sr, 0);
if (ret != NJS_OK) {
return NJS_ERROR;
}
return NJS_OK;
memory_error:
njs_memory_error(vm);
return NJS_ERROR;
}
static njs_int_t
njs_unit_test_r_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_str_t name;
njs_unit_test_req_t *r;
r = njs_vm_external(vm, njs_arg(args, nargs, 0));
if (r == NULL) {
njs_type_error(vm, "\"this\" is not an external");
return NJS_ERROR;
}
if (njs_vm_value_to_string(vm, &name, njs_arg(args, nargs, 1)) != NJS_OK) {
return NJS_ERROR;
}
return njs_vm_bind(vm, &name, njs_arg(args, nargs, 2), 0);
}
static njs_external_t njs_unit_test_r_c[] = {
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("d"),
.enumerable = 1,
.u.property = {
.handler = njs_unit_test_r_d,
}
},
};
static njs_external_t njs_unit_test_r_props[] = {
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("a"),
.enumerable = 1,
.u.property = {
.handler = njs_unit_test_r_a,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("b"),
.enumerable = 1,
.u.property = {
.handler = njs_unit_test_r_b,
.magic32 = 42,
}
},
{
.flags = NJS_EXTERN_OBJECT,
.name.string = njs_str("c"),
.enumerable = 1,
.u.object = {
.properties = njs_unit_test_r_c,
.nproperties = njs_nitems(njs_unit_test_r_c),
}
},
};
static njs_external_t njs_unit_test_r_header_props[] = {
{
.flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
.name.symbol = NJS_SYMBOL_TO_STRING_TAG,
.u.property = {
.value = "Header",
}
},
};
static njs_external_t njs_unit_test_r_external[] = {
{
.flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
.name.symbol = NJS_SYMBOL_TO_STRING_TAG,
.u.property = {
.value = "External",
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("bind"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.method = {
.native = njs_unit_test_r_bind,
}
},
{
.flags = NJS_EXTERN_OBJECT,
.name.string = njs_str("consts"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.object = {
.enumerable = 1,
.prop_handler = njs_unit_test_r_vars,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("create"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.method = {
.native = njs_unit_test_r_create,
}
},
{
.flags = NJS_EXTERN_OBJECT,
.name.string = njs_str("header"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.object = {
.properties = njs_unit_test_r_header_props,
.nproperties = njs_nitems(njs_unit_test_r_header_props),
.enumerable = 1,
.prop_handler = njs_unit_test_r_header,
.keys = njs_unit_test_r_header_keys,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("host"),
.enumerable = 1,
.u.property = {
.handler = njs_unit_test_r_host,
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("buffer"),
.enumerable = 1,
.u.property = {
.handler = njs_unit_test_r_buffer,
}
},
{
.flags = NJS_EXTERN_METHOD,
.name.string = njs_str("method"),
.writable = 1,
.configurable = 1,
.enumerable = 1,
.u.method = {
.native = njs_unit_test_r_method,
}
},
{
.flags = NJS_EXTERN_OBJECT,
.name.string = njs_str("props"),
.enumerable = 1,
.writable = 1,
.u.object = {
.enumerable = 1,
.properties = njs_unit_test_r_props,
.nproperties = njs_nitems(njs_unit_test_r_props),
}
},
{
.flags = NJS_EXTERN_PROPERTY,
.name.string = njs_str("uri"),
.writable = 1,
.enumerable = 1,
.u.property = {
.handler = njs_unit_test_r_uri,
.magic32 = offsetof(njs_unit_test_req_t, uri),
}
},
{
.flags = NJS_EXTERN_OBJECT,
.name.string = njs_str("vars"),
.enumerable = 1,
.u.object = {
.writable = 1,
.configurable = 1,
.enumerable = 1,
.prop_handler = njs_unit_test_r_vars,
}
},
};
typedef struct {
njs_str_t name;
njs_unit_test_req_t request;
njs_unit_test_prop_t props[2];
} njs_unit_test_req_init_t;
static njs_unit_test_req_init_t njs_test_requests[] = {
{ njs_str("$shared"),
{
.uri = njs_str("shared"),
.a = 11,
.d = 13,
},
{
{ njs_string("r"), njs_string("rval") },
{ njs_string("r2"), njs_string("r2val") },
}
},
{ njs_str("$r"),
{
.uri = njs_str("АБВ"),
.a = 1,
.d = 1024,
},
{
{ njs_string("p"), njs_string("pval") },
{ njs_string("p2"), njs_string("p2val") },
}
},
{ njs_str("$r2"),
{
.uri = njs_str("αβγ"),
.a = 2,
.d = 1025,
},
{
{ njs_string("q"), njs_string("qval") },
{ njs_string("q2"), njs_string("q2val") },
}
},
{ njs_str("$r3"),
{
.uri = njs_str("abc"),
.a = 3,
.d = 1026,
},
{
{ njs_string("k"), njs_string("kval") },
{ njs_string("k2"), njs_string("k2val") },
}
},
};
static njs_external_proto_t
njs_externals_init_internal(njs_vm_t *vm, njs_external_proto_t proto,
njs_unit_test_req_init_t *init, njs_uint_t n, njs_bool_t shared)
{
njs_int_t ret;
njs_uint_t i, j;
njs_unit_test_req_t *requests;
njs_unit_test_prop_t *prop;
if (proto == NULL) {
proto = njs_vm_external_prototype(vm, njs_unit_test_r_external,
njs_nitems(njs_unit_test_r_external));
if (njs_slow_path(proto == NULL)) {
njs_printf("njs_vm_external_prototype() failed\n");
return NULL;
}
}
requests = njs_mp_zalloc(vm->mem_pool, n * sizeof(njs_unit_test_req_t));
if (njs_slow_path(requests == NULL)) {
return NULL;
}
for (i = 0; i < n; i++) {
requests[i] = init[i].request;
requests[i].proto = proto;
ret = njs_vm_external_create(vm, njs_value_arg(&requests[i].value),
proto, &requests[i], shared);
if (njs_slow_path(ret != NJS_OK)) {
njs_printf("njs_vm_external_create() failed\n");
return NULL;
}
ret = njs_vm_bind(vm, &init[i].name, njs_value_arg(&requests[i].value),
shared);
if (njs_slow_path(ret != NJS_OK)) {
njs_printf("njs_vm_bind() failed\n");
return NULL;
}
for (j = 0; j < njs_nitems(init[i].props); j++) {
prop = lvlhsh_unit_test_alloc(vm->mem_pool, &init[i].props[j].name,
&init[i].props[j].value);
if (njs_slow_path(prop == NULL)) {
njs_printf("lvlhsh_unit_test_alloc() failed\n");
return NULL;
}
ret = lvlhsh_unit_test_add(vm->mem_pool, &requests[i], prop);
if (njs_slow_path(ret != NJS_OK)) {
njs_printf("lvlhsh_unit_test_add() failed\n");
return NULL;
}
}
}
return proto;
}
njs_external_proto_t
njs_externals_shared_init(njs_vm_t *vm)
{
return njs_externals_init_internal(vm, NULL, njs_test_requests, 1, 1);
}
njs_int_t
njs_externals_init(njs_vm_t *vm, njs_external_proto_t proto)
{
proto = njs_externals_init_internal(vm, proto, &njs_test_requests[1], 3, 0);
if (proto == NULL) {
return NJS_ERROR;
}
return NJS_OK;
}