/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * clib.c: C-Library interface for extending SPL */ #include #include #include #include #ifndef USEWIN32API # include #endif #ifndef USEWIN32API # include #endif #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" #include "compat.h" static void enlarge_hash(struct spl_vm *vm) { /* Duplicate the size */ const int new_clib_hash_size = vm->clib_hash_size * 2; struct spl_clib **new_clib_hash = calloc(new_clib_hash_size, sizeof(*new_clib_hash)); /* Reorder the elements */ for (int i = 0; i < vm->clib_hash_size; i++) { struct spl_clib *clib_list = vm->clib_hash[i]; while (clib_list) { struct spl_clib *n = clib_list->next; const int new_hash = spl_subs_hash(clib_list->name, new_clib_hash_size); /* Add at the end of the list */ clib_list->next = 0; if (new_clib_hash[new_hash] != 0) { struct spl_clib *p = new_clib_hash[new_hash]; while (p->next) p = p->next; p->next = clib_list; } else new_clib_hash[new_hash] = clib_list; clib_list = n; } } /* Clear the old one */ free(vm->clib_hash); /* Set the new one */ vm->clib_hash = new_clib_hash; vm->clib_hash_size = new_clib_hash_size; } void spl_clib_reg(struct spl_vm *vm, const char *name, spl_clib_function *handler, void *data) { struct spl_clib *clib = calloc(1, sizeof(struct spl_clib)); clib->name = strdup(name); clib->handler = handler; clib->data = data; vm->clib_hash_count++; if (vm->clib_hash_count > vm->clib_hash_size && vm->clib_hash_size < 8192) { enlarge_hash(vm); } /* Really insert the new one */ const int hash = spl_subs_hash(name, vm->clib_hash_size); clib->next = vm->clib_hash[hash]; vm->clib_hash[hash] = clib; } int spl_clib_call(struct spl_task *task, const char *unparsed_name) { int name_len = strcspn(unparsed_name, " \t\r\n"); char *name = my_strndupa(unparsed_name, name_len); struct spl_clib *clib = task->vm->clib_hash[spl_subs_hash(name, task->vm->clib_hash_size)]; while (clib) { if ( !strcmp(name, clib->name) ) { struct spl_node *ret = clib->handler(task, clib->data); struct spl_node *c = spl_pop(task); while ( spl_get_int(c) > 0 ) { spl_set_int(c, spl_get_int(c) - 1); spl_put(task->vm, spl_pop(task)); } spl_put(task->vm, c); if (!ret) { ret = spl_get(0); ret->flags |= SPL_NODE_FLAG_CLNULL; } spl_push(task, ret); return 0; } clib = clib->next; } return -1; } int spl_clib_check(struct spl_vm *vm, const char *unparsed_name) { int name_len = strcspn(unparsed_name, " \t\r\n"); char *name = my_strndupa(unparsed_name, name_len); struct spl_clib *clib = vm->clib_hash[spl_subs_hash(name, vm->clib_hash_size)]; while (clib) { if ( !strcmp(name, clib->name) ) return 1; clib = clib->next; } return 0; } int spl_clib_get_argc(struct spl_task *task) { struct spl_node *c = spl_tos(task); return spl_get_int(c); } struct spl_node *spl_clib_get_hargs(struct spl_task *task) { struct spl_node *c = spl_tos(task); return spl_get(c); } struct spl_node *spl_clib_get_node(struct spl_task *task) { struct spl_node *c = spl_pop(task); struct spl_node *v = 0; if ( spl_get_int(c) > 0 ) { v = spl_pop(task); spl_set_int(c, spl_get_int(c) - 1); } spl_push(task, c); return v; } int spl_clib_get_int(struct spl_task *task) { struct spl_node *n = spl_clib_get_node(task); int ret = spl_get_int(n); spl_put(task->vm, n); return ret; } double spl_clib_get_float(struct spl_task *task) { struct spl_node *n = spl_clib_get_node(task); double ret = spl_get_float(n); spl_put(task->vm, n); return ret; } char *spl_clib_get_string(struct spl_task *task) { struct spl_node *n = spl_clib_get_node(task); char *ret = spl_get_string(n); spl_cleanup(task, n); return ret; } static struct spl_node *spl_clib_va_new(struct spl_task *task, const char *class, va_list ap) { struct spl_node *src = spl_lookup(task, task->vm->root, class, 0); if ( !src || (src->flags & SPL_NODE_FLAG_CLASS) == 0 ) return 0; struct spl_node *n = spl_get(0); n->cls = spl_get(src); if (src->ctx) n->ctx = spl_get(src->ctx); n->ctx_type = SPL_CTX_OBJECT; struct spl_string *ts = spl_string_new(SPL_STRING_STATIC, 0, spl_get_spl_string(src), "[ ", 0); spl_set_spl_string(n, spl_string_new(SPL_STRING_STATIC, ts, 0, " ]", 0)); spl_string_put(ts); while (1) { char *name = va_arg(ap, char*); if (!name) break; struct spl_node *val = va_arg(ap, struct spl_node *); spl_create(task, n, name, val, SPL_CREATE_LOCAL); } return n; } struct spl_node *spl_clib_new(struct spl_task *task, const char *class, ...) { va_list ap; va_start(ap, class); struct spl_node *n = spl_clib_va_new(task, class, ap); va_end(ap); return n; } void spl_clib_exception(struct spl_task *task, const char *class, ...) { va_list ap; va_start(ap, class); struct spl_node *ex = spl_clib_va_new(task, class, ap); va_end(ap); if (!ex) { spl_report(SPL_REPORT_RUNTIME, task, "CLIB Exception: Can't find class '%s'!", class); return; } ex->flags |= SPL_NODE_FLAG_CLEXCEPT; spl_cleanup(task, ex); } struct spl_task *spl_clib_callback_task(struct spl_vm *vm) { char task_name[64]; static int callback_task_id = 0; snprintf(task_name, 64, "__clib_callback_task_%d", callback_task_id++); struct spl_task *task = spl_task_create(vm, task_name); struct spl_node *oldctx = task->ctx; task->ctx = spl_get(0); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = oldctx; return task; } int spl_clib_callback_run(struct spl_task *task) { if (!task->vm->runloop) { spl_report(SPL_REPORT_RUNTIME, task, "CLIB Exception: Called spl_clib_callback_run() but this VM has no runloop function!\n"); return -1; } int runloop_ret = task->vm->runloop(task->vm, task); if (runloop_ret != 0) { spl_report(SPL_REPORT_RUNTIME, task, "CLIB Exception: Runloop returned %d in spl_clib_callback_run()!\n", runloop_ret); return runloop_ret; } return 0; } #ifndef USEWIN32API static int spl_module_load_so(struct spl_vm *vm, const char *name, int restore, char *filename) { void *dlhandle = dlopen(filename, RTLD_LAZY|RTLD_GLOBAL); if ( !dlhandle ) { spl_report(SPL_REPORT_HOST, vm, "Can't load module '%s': %s\n", name, dlerror()); return -1; } struct spl_module *m = calloc(1, sizeof(struct spl_module)); m->name = strdup(name); m->dlhandle = dlhandle; m->next = vm->module_list; vm->module_list = m; char func_name[strlen(name) + 40]; sprintf(func_name, "SPL_ABI_%u_spl_mod_%s_done", (unsigned int)SPL_ABICKSUM, name); m->donefunc = dlsym(dlhandle, func_name); sprintf(func_name, "SPL_ABI_%u_spl_mod_%s_init", (unsigned int)SPL_ABICKSUM, name); spl_module_init_func *initfunc = dlsym(dlhandle, func_name); if ( !initfunc ) { spl_report(SPL_REPORT_HOST, vm, "Can't load %s: seems to be no SPL module or built for other SPL ABI!\n", filename); return -1; } initfunc(vm, m, restore); return 0; } #else static int spl_module_load_dll(struct spl_vm *vm, const char *name, int restore, char *filename) { void *dlhandle = LoadLibrary(filename); if ( !dlhandle ) { spl_report(SPL_REPORT_HOST, vm, "Can't load module '%s': %s\n", name, filename); return -1; } struct spl_module *m = calloc(1, sizeof(struct spl_module)); m->name = strdup(name); m->dlhandle = dlhandle; m->next = vm->module_list; vm->module_list = m; char func_name[strlen(name) + 40]; sprintf(func_name, "SPL_ABI_%u_spl_mod_%s_done", (unsigned int)SPL_ABICKSUM, name); m->donefunc = (spl_module_done_func*)GetProcAddress(dlhandle, func_name); sprintf(func_name, "SPL_ABI_%u_spl_mod_%s_init", (unsigned int)SPL_ABICKSUM, name); spl_module_init_func *initfunc = (spl_module_init_func*)GetProcAddress(dlhandle, func_name); if ( !initfunc ) { spl_report(SPL_REPORT_HOST, vm, "Can't load %s: seems to be no SPL module or built for other SPL ABI!\n", filename); return -1; } initfunc(vm, m, restore); return 0; } #endif static int spl_module_load_splb(struct spl_vm *vm, const char *name, int restore, char *filename) { if ( restore ) return 0; struct spl_code *code = spl_code_get(0); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file(filename, &code->size); struct spl_module *m = calloc(1, sizeof(struct spl_module)); m->name = strdup(name); m->next = vm->module_list; vm->module_list = m; struct spl_task *task = spl_task_create(vm, 0); task->module = strdup(name); spl_task_setcode(task, code); while ( task->code && !spl_exec(task) ) { } spl_task_destroy(vm, task); return 0; } static int spl_module_load_builtin(struct spl_vm *vm, const char *name, int restore, struct spl_builtin_module *b) { struct spl_module *m = calloc(1, sizeof(struct spl_module)); m->name = strdup(name); m->next = vm->module_list; m->donefunc = b->donefunc; vm->module_list = m; b->initfunc(vm, m, restore); return 0; } #ifdef ENABLE_PTHREAD_SUPPORT // MacOS X has no reentrant (recursive) locks. So we need to implement our own // recursive locks when this is MacOS.. # if defined(USEBSDAPI) || defined(USEMACOSXAPI) static pthread_mutex_t load_unload_lck = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t lock_func_lck = PTHREAD_MUTEX_INITIALIZER; static volatile pthread_t lock_owner = 0; static volatile int lock_depth = 0; static inline void DO_LOCK() { pthread_mutex_lock(&lock_func_lck); if (lock_depth > 0 && lock_owner == pthread_self()) { lock_depth++; pthread_mutex_unlock(&lock_func_lck); return; } pthread_mutex_unlock(&lock_func_lck); pthread_mutex_lock(&load_unload_lck); lock_owner = pthread_self(); lock_depth = 1; } static inline void DO_UNLOCK() { pthread_mutex_lock(&lock_func_lck); if (--lock_depth == 0) pthread_mutex_unlock(&load_unload_lck); pthread_mutex_unlock(&lock_func_lck); } # else static pthread_mutex_t load_unload_lck = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; static inline void DO_LOCK() { pthread_mutex_lock(&load_unload_lck); } static inline void DO_UNLOCK() { pthread_mutex_unlock(&load_unload_lck); } # endif #else #define DO_LOCK() while(0) #define DO_UNLOCK() while(0) #endif static int try_module_load_dir(struct spl_vm *vm, const char *dir, const char *name, int restore) { int filename_len = strlen(dir) + strlen(name) + 32 + (vm->current_dir_name && *dir != '/' ? strlen(vm->current_dir_name) : 0); char filename[filename_len]; #ifndef USEWIN32API snprintf(filename, filename_len, "%s%s%s/mod_%s.so", vm->current_dir_name && *dir != '/' ? vm->current_dir_name : "", vm->current_dir_name && *dir != '/' ? "/" : "", dir, name); if ( !access(filename, F_OK) ) return spl_module_load_so(vm, name, restore, filename); #else snprintf(filename, filename_len, "%s%s%s/mod_%s.dll", vm->current_dir_name && *dir != '/' ? vm->current_dir_name : "", vm->current_dir_name && *dir != '/' ? "/" : "", dir, name); if ( !access(filename, F_OK) ) return spl_module_load_dll(vm, name, restore, filename); #endif snprintf(filename, filename_len, "%s%s%s/mod_%s.splb", vm->current_dir_name && *dir != '/' ? vm->current_dir_name : "", vm->current_dir_name && *dir != '/' ? "/" : "", dir, name); if ( !access(filename, F_OK) ) return spl_module_load_splb(vm, name, restore, filename); return 1; } int spl_module_load(struct spl_vm *vm, const char *name, int restore) { DO_LOCK(); struct spl_module *m = vm->module_list; struct spl_builtin_module *b = spl_builtin_module_list; int ret = -1; while (m) { if ( !strcmp(name, m->name) ) { ret = 0; goto leave; } m = m->next; } while (b) { if ( !strcmp(name, b->name) ) { ret = spl_module_load_builtin(vm, name, restore, b); goto leave; } b = b->next; } if ( !vm->path ) { spl_report(SPL_REPORT_HOST, vm, "Tried to load module '%s' but no module path is set!\n", name); goto leave; } { char path[strlen(vm->path)+1]; strcpy(path, vm->path); char *this_element; char *pbuffer = path; while ((this_element = my_strsep(&pbuffer, ":")) != 0) { ret = try_module_load_dir(vm, this_element, name, restore); if (ret <= 0) goto leave; if (strcmp(this_element, spl_system_modules_dir())) continue; int moddir_list_len = strlen(this_element) + 32; char moddir_list[moddir_list_len]; snprintf(moddir_list, moddir_list_len, "%s/moddir.list", this_element); FILE *f = fopen(moddir_list, "r"); if (f == 0) continue; char line[128]; while (fgets(line, 128, f)) { char *nl = strchr(line, '\n'); if (nl) *nl = 0; nl = strchr(line, '\r'); if (nl) *nl = 0; #ifndef USEWIN32API glob_t globbuf; if (glob(line, 0, NULL, &globbuf) == 0) for (int i=0; globbuf.gl_pathv[i]; i++) { ret = try_module_load_dir(vm, globbuf.gl_pathv[i], name, restore); if (ret <= 0) goto leave; } globfree(&globbuf); #else ret = try_module_load_dir(vm, line, name, restore); if (ret <= 0) goto leave; #endif } fclose(f); } spl_report(SPL_REPORT_HOST, vm, "Tried to load module '%s' but can't find it in path!\n", name); ret = -1; } leave: DO_UNLOCK(); return ret; } void spl_module_unload_all(struct spl_vm *vm) { DO_LOCK(); while (vm->module_list) { struct spl_module *n = vm->module_list->next; if (vm->module_list->donefunc) vm->module_list->donefunc(vm, vm->module_list); if (vm->module_list->dlhandle) #ifndef USEWIN32API dlclose(vm->module_list->dlhandle); #else FreeLibrary(vm->module_list->dlhandle); #endif free(vm->module_list->name); free(vm->module_list); vm->module_list = n; } DO_UNLOCK(); }