blob: 78206b3babe3b0da6df7cc5a9279ed738478bdee [file] [log] [blame]
/*
* Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
typedef struct {
int fd;
njs_str_t name;
njs_str_t file;
char path[NJS_MAX_PATH + 1];
} njs_module_info_t;
static njs_int_t njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd,
njs_module_info_t *info);
static njs_int_t njs_module_path(njs_vm_t *vm, const njs_str_t *dir,
njs_module_info_t *info);
static njs_int_t njs_module_read(njs_vm_t *vm, int fd, njs_str_t *body);
static njs_mod_t *njs_default_module_loader(njs_vm_t *vm,
njs_external_ptr_t external, njs_str_t *name);
njs_mod_t *
njs_parser_module(njs_parser_t *parser, njs_str_t *name)
{
njs_mod_t *module;
njs_vm_t *vm;
njs_external_ptr_t external;
njs_module_loader_t loader;
vm = parser->vm;
if (name->length == 0) {
njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name);
return NULL;
}
module = njs_module_find(vm, name, 1);
if (module != NULL) {
goto done;
}
external = parser;
loader = njs_default_module_loader;
if (vm->options.ops != NULL && vm->options.ops->module_loader != NULL) {
loader = vm->options.ops->module_loader;
external = vm->external;
}
module = loader(vm, external, name);
if (module == NULL) {
njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name);
return NULL;
}
done:
if (module->index == 0) {
module->index = vm->shared->module_items++;
}
return module;
}
static njs_int_t
njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd, njs_module_info_t *info)
{
njs_int_t ret;
njs_str_t *path;
njs_uint_t i;
if (info->name.start[0] == '/') {
return njs_module_path(vm, NULL, info);
}
ret = njs_module_path(vm, cwd, info);
if (ret != NJS_DECLINED) {
return ret;
}
if (vm->paths == NULL) {
return NJS_DECLINED;
}
path = vm->paths->start;
for (i = 0; i < vm->paths->items; i++) {
ret = njs_module_path(vm, path, info);
if (ret != NJS_DECLINED) {
return ret;
}
path++;
}
return NJS_DECLINED;
}
static njs_int_t
njs_module_path(njs_vm_t *vm, const njs_str_t *dir, njs_module_info_t *info)
{
char *p;
size_t length;
njs_bool_t trail;
char src[NJS_MAX_PATH + 1];
trail = 0;
length = info->name.length;
if (dir != NULL) {
length += dir->length;
if (length == 0) {
return NJS_DECLINED;
}
trail = (dir->start[dir->length - 1] != '/');
if (trail) {
length++;
}
}
if (njs_slow_path(length > NJS_MAX_PATH)) {
return NJS_ERROR;
}
p = &src[0];
if (dir != NULL) {
p = (char *) njs_cpymem(p, dir->start, dir->length);
if (trail) {
*p++ = '/';
}
}
p = (char *) njs_cpymem(p, info->name.start, info->name.length);
*p = '\0';
p = realpath(&src[0], &info->path[0]);
if (p == NULL) {
return NJS_DECLINED;
}
info->fd = open(&info->path[0], O_RDONLY);
if (info->fd < 0) {
return NJS_DECLINED;
}
info->file.start = (u_char *) &info->path[0];
info->file.length = njs_strlen(info->file.start);
return NJS_OK;
}
static njs_int_t
njs_module_read(njs_vm_t *vm, int fd, njs_str_t *text)
{
ssize_t n;
struct stat sb;
text->start = NULL;
if (fstat(fd, &sb) == -1) {
goto fail;
}
if (!S_ISREG(sb.st_mode)) {
goto fail;
}
text->length = sb.st_size;
text->start = njs_mp_alloc(vm->mem_pool, text->length);
if (text->start == NULL) {
goto fail;
}
n = read(fd, text->start, sb.st_size);
if (n < 0 || n != sb.st_size) {
goto fail;
}
return NJS_OK;
fail:
if (text->start != NULL) {
njs_mp_free(vm->mem_pool, text->start);
}
return NJS_ERROR;
}
static njs_int_t
njs_module_hash_test(njs_lvlhsh_query_t *lhq, void *data)
{
njs_mod_t *module;
module = data;
if (njs_strstr_eq(&lhq->key, &module->name)) {
return NJS_OK;
}
return NJS_DECLINED;
}
const njs_lvlhsh_proto_t njs_modules_hash_proto
njs_aligned(64) =
{
NJS_LVLHSH_DEFAULT,
njs_module_hash_test,
njs_lvlhsh_alloc,
njs_lvlhsh_free,
};
njs_mod_t *
njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared)
{
njs_int_t ret;
njs_mod_t *shrd, *module;
njs_object_t *object;
njs_lvlhsh_query_t lhq;
lhq.key = *name;
lhq.key_hash = njs_djb_hash(name->start, name->length);
lhq.proto = &njs_modules_hash_proto;
if (njs_lvlhsh_find(&vm->modules_hash, &lhq) == NJS_OK) {
return lhq.value;
}
if (njs_lvlhsh_find(&vm->shared->modules_hash, &lhq) == NJS_OK) {
shrd = lhq.value;
if (shared) {
return shrd;
}
module = njs_mp_alloc(vm->mem_pool, sizeof(njs_mod_t));
if (njs_slow_path(module == NULL)) {
njs_memory_error(vm);
return NULL;
}
memcpy(module, shrd, sizeof(njs_mod_t));
object = njs_object_value_copy(vm, &module->value);
if (njs_slow_path(object == NULL)) {
return NULL;
}
lhq.replace = 0;
lhq.value = module;
lhq.pool = vm->mem_pool;
ret = njs_lvlhsh_insert(&vm->modules_hash, &lhq);
if (njs_fast_path(ret == NJS_OK)) {
return module;
}
}
return NULL;
}
njs_mod_t *
njs_module_add(njs_vm_t *vm, njs_str_t *name)
{
njs_int_t ret;
njs_mod_t *module;
njs_lvlhsh_query_t lhq;
module = njs_mp_zalloc(vm->mem_pool, sizeof(njs_mod_t));
if (njs_slow_path(module == NULL)) {
njs_memory_error(vm);
return NULL;
}
ret = njs_name_copy(vm, &module->name, name);
if (njs_slow_path(ret != NJS_OK)) {
njs_memory_error(vm);
return NULL;
}
lhq.replace = 0;
lhq.key = *name;
lhq.key_hash = njs_djb_hash(name->start, name->length);
lhq.value = module;
lhq.pool = vm->mem_pool;
lhq.proto = &njs_modules_hash_proto;
ret = njs_lvlhsh_insert(&vm->shared->modules_hash, &lhq);
if (njs_fast_path(ret == NJS_OK)) {
return module;
}
njs_mp_free(vm->mem_pool, module->name.start);
njs_mp_free(vm->mem_pool, module);
njs_internal_error(vm, "lvlhsh insert failed");
return NULL;
}
njs_int_t
njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_str_t name;
njs_mod_t *module;
njs_value_t *path;
if (nargs < 2) {
njs_type_error(vm, "missing path");
return NJS_ERROR;
}
path = njs_argument(args, 1);
ret = njs_value_to_string(vm, path, path);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_string_get(path, &name);
module = njs_module_find(vm, &name, 0);
if (njs_slow_path(module == NULL)) {
njs_error(vm, "Cannot find module \"%V\"", &name);
return NJS_ERROR;
}
njs_value_assign(&vm->retval, &module->value);
return NJS_OK;
}
static njs_mod_t *
njs_default_module_loader(njs_vm_t *vm, njs_external_ptr_t external,
njs_str_t *name)
{
njs_int_t ret;
njs_str_t cwd, text;
njs_parser_t *prev;
njs_mod_t *module;
njs_module_info_t info;
prev = external;
njs_memzero(&info, sizeof(njs_module_info_t));
info.name = *name;
njs_file_dirname(&prev->lexer->file, &cwd);
ret = njs_module_lookup(vm, &cwd, &info);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
ret = njs_module_read(vm, info.fd, &text);
(void) close(info.fd);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "while reading \"%V\" module", &info.file);
return NULL;
}
module = njs_vm_compile_module(vm, &info.file, &text.start,
&text.start[text.length]);
njs_mp_free(vm->mem_pool, text.start);
return module;
}