/*
module.c - Module loading and runtime implementation
Copyright (C) 2015 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs 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 3 of the License, or
(at your option) any later version.
GNU Emacs 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 GNU Emacs. If not, see .
*/
#include
#include "lisp.h"
#include "character.h"
#include "buffer.h"
/* see comment in emacs_module.h at emacs_value for this define */
#define EMACS_VALUE_TYPE EMACS_INT
#include "emacs_module.h"
#include
/* internal functions */
void syms_of_module (void);
static struct emacs_runtime* module_get_runtime (void);
static emacs_env* module_get_environment (struct emacs_runtime *ert);
/* emacs_module.h emacs_env_* functions; same order as there */
/* FIXME: make_global_reference */
/* FIXME: free_global_reference */
/* FIXME: error_check */
/* FIXME: clear_error */
/* FIXME: get_error */
static void module_signal_error (emacs_env *env,
const char* msg,
emacs_value error_data);
static emacs_value module_make_function (emacs_env *env,
int min_arity,
int max_arity,
emacs_subr subr);
static emacs_value module_funcall (emacs_env *env,
emacs_value fun,
int nargs,
emacs_value args[]);
static emacs_value module_intern (emacs_env *env,
const char *name);
static emacs_value module_intern_soft (emacs_env *env,
const char *name);
static void module_bind_function (emacs_env *env,
const char *name,
emacs_value definition);
/* FIXME: type_of */
static int64_t module_fixnum_to_int (emacs_env *env,
emacs_value n);
static emacs_value module_make_fixnum (emacs_env *env,
int64_t n);
/* FIXME: float_to_c_double */
/* FIXME: make_float */
/* FIXME: copy_string_contents */
static size_t module_buffer_byte_length (emacs_env *env,
emacs_value start,
emacs_value end);
static void module_copy_buffer_substring (emacs_env *env,
emacs_value start,
emacs_value end,
char *buffer,
size_t *length_inout);
static emacs_value module_make_string (emacs_env *env,
const char *contents);
static void module_message (emacs_env *env,
emacs_value msg);
static emacs_value module_symbol_value (emacs_env *env,
emacs_value symbol);
static struct emacs_runtime* module_get_runtime (void)
{
/* FIXME: why do we need module_get_runtime, as opposed to just module_get_environment? */
struct emacs_runtime *ert = xzalloc (sizeof *ert);
ert->size = sizeof *ert;
ert->get_environment = module_get_environment;
return ert;
}
static emacs_env* module_get_environment (struct emacs_runtime *ert)
{
/* FIXME: error if not on main emacs thread? */
emacs_env *env = xzalloc (sizeof *env);
env->size = sizeof *env;
env->Qt_value = (emacs_value) Qt;
env->Qnil_value = (emacs_value) Qnil;
/* FIXME: make_global_reference */
/* FIXME: free_global_reference */
/* FIXME: error_check */
/* FIXME: clear_error */
/* FIXME: get_error */
env->signal_error = module_signal_error;
env->make_function = module_make_function;
env->funcall = module_funcall;
env->intern = module_intern;
env->intern_soft = module_intern_soft;
env->bind_function = module_bind_function;
env->fixnum_to_int = module_fixnum_to_int;
env->make_fixnum = module_make_fixnum;
/* FIXME: copy_string_contents */
env->buffer_byte_length = module_buffer_byte_length;
env->copy_buffer_substring = module_copy_buffer_substring;
env->make_string = module_make_string;
env->message = module_message;
env->symbol_value = module_symbol_value;
return env;
}
static emacs_value module_make_fixnum (emacs_env *env, int64_t n)
{
return (emacs_value) make_number (n);
}
static int64_t module_fixnum_to_int (emacs_env *env, emacs_value n)
{
return (int64_t) XINT ((Lisp_Object) n);
}
static emacs_value module_intern (emacs_env *env, const char *name)
{
return (emacs_value) intern (name);
}
static emacs_value module_intern_soft (emacs_env *env, const char *name)
{
register ptrdiff_t len = strlen (name);
register Lisp_Object tem = oblookup (Vobarray, name, len, len);
if (INTEGERP (tem))
return (emacs_value) Qnil;
else
return (emacs_value) tem;
}
static void module_bind_function (emacs_env *env,
const char *name,
emacs_value definition)
{
Lisp_Object symbol = intern (name);
set_symbol_function (symbol, (Lisp_Object) definition);
}
static void module_signal_error (emacs_env *env,
const char* msg,
emacs_value error_data)
{
signal_error (msg, (Lisp_Object) (error_data));
}
static emacs_value module_make_function (emacs_env *env,
int min_arity,
int max_arity,
emacs_subr subr)
{
/*
(function
(lambda
(&rest arglist)
(module-call
envptr
subrptr
arglist)))
*/
/* FIXME: allow for doc string and interactive */
Lisp_Object Qrest = intern ("&rest");
Lisp_Object Qarglist = intern ("arglist");
Lisp_Object Qmodule_call = intern ("module-call");
Lisp_Object envptr = make_save_ptr ((void*) env);
Lisp_Object subrptr = make_save_ptr ((void*) subr);
Lisp_Object form = list2 (Qfunction,
list3 (Qlambda,
list2 (Qrest, Qarglist),
list4 (Qmodule_call,
envptr,
subrptr,
Qarglist)));
struct gcpro gcpro1;
GCPRO1 (Qform);
Lisp_Object ret = Feval (form, Qnil);
UNGCPRO;
return (emacs_value) ret;
}
static emacs_value module_funcall (emacs_env *env,
emacs_value fun,
int nargs,
emacs_value args[])
{
/*
* Make a new Lisp_Object array starting with the function as the
* first arg, because that's what Ffuncall takes
*/
int i;
Lisp_Object *newargs = xmalloc ((nargs+1) * sizeof (*newargs));
newargs[0] = (Lisp_Object) fun;
for (i = 0; i < nargs; i++)
newargs[1 + i] = (Lisp_Object) args[i];
struct gcpro gcpro1;
GCPRO1 (newargs[0]);
Lisp_Object ret = Ffuncall (nargs+1, newargs);
UNGCPRO;
xfree (newargs);
return (emacs_value) ret;
}
static size_t module_buffer_byte_length (emacs_env *env,
emacs_value start,
emacs_value end)
{
Lisp_Object start_1 = (Lisp_Object)start;
Lisp_Object end_1 = (Lisp_Object)end;
validate_region (&start_1, &end_1);
{
ptrdiff_t start_byte = CHAR_TO_BYTE (XINT (start_1));
ptrdiff_t end_byte = CHAR_TO_BYTE (XINT (end_1));
return (size_t) end_byte - start_byte;
}
}
static void module_copy_buffer_substring (emacs_env *env,
emacs_value start,
emacs_value end,
char *buffer,
size_t *length_inout)
{
/* Copied from editfns.c "buffer-substring-no-properties" and make_buffer_string_both */
Lisp_Object start_1 = (Lisp_Object)start;
Lisp_Object end_1 = (Lisp_Object)end;
validate_region (&start_1, &end_1);
{
ptrdiff_t start = XINT (start_1);
ptrdiff_t start_byte = CHAR_TO_BYTE (start);
ptrdiff_t end = XINT (end_1);
ptrdiff_t end_byte = CHAR_TO_BYTE (end);
ptrdiff_t beg0, end0, beg1, end1;
size_t size;
if (end_byte - start_byte > *length_inout)
{
/* buffer too small */
/* FIXME: could copy less than requested, but that's
complicated for multi-byte characters */
signal_error ("module_copy_buffer_substring: buffer too small", Qnil);
}
if (start_byte < GPT_BYTE && GPT_BYTE < end_byte)
{
/* Two regions, before and after the gap. */
beg0 = start_byte;
end0 = GPT_BYTE;
beg1 = GPT_BYTE + GAP_SIZE - BEG_BYTE;
end1 = end_byte + GAP_SIZE - BEG_BYTE;
}
else
{
/* One region, before the gap. */
beg0 = start_byte;
end0 = end_byte;
beg1 = -1;
end1 = -1;
}
size = end0 - beg0;
/* FIXME: need to decode? See external process stuff. */
/* BYTE_POS_ADDR handles one region after the gap */
memcpy (buffer, BYTE_POS_ADDR (beg0), size);
if (beg1 != -1)
memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
}
}
static emacs_value module_make_string (emacs_env *env, const char *contents)
{
return (emacs_value) make_string (contents, strlen (contents));
}
static void module_message (emacs_env *env,
emacs_value msg)
{
message3 ((Lisp_Object) msg);
}
static emacs_value module_symbol_value (emacs_env *env,
emacs_value symbol)
{
Lisp_Object val= find_symbol_value ((Lisp_Object) symbol);
if (!EQ (val, Qunbound))
return (emacs_value) val;
xsignal1 (Qvoid_variable, (Lisp_Object) symbol);
}
DEFUN ("module-call", Fmodule_call, Smodule_call, 3, 3, 0,
doc: "Call a module function")
(Lisp_Object envptr, Lisp_Object subrptr, Lisp_Object arglist)
{
int len = XINT (Flength (arglist));
emacs_value *args = xzalloc (len * sizeof (*args));
int i;
for (i = 0; i < len; i++)
{
args[i] = (emacs_value) XCAR (arglist);
arglist = XCDR (arglist);
}
emacs_env *env = (emacs_env*) XSAVE_POINTER (envptr, 0);
emacs_subr subr = (emacs_subr) XSAVE_POINTER (subrptr, 0);
emacs_value ret = subr (env, len, args);
return (Lisp_Object) ret;
}
static int lt_init_done = 0;
EXFUN (Fmodule_load, 1);
DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0,
doc: /* Load module FILE. */)
(Lisp_Object file)
{
lt_dlhandle handle;
emacs_init_function module_init;
void *gpl_sym;
Lisp_Object doc_name, args[2];
/* init libtool once per emacs process */
if (!lt_init_done)
{
int ret = lt_dlinit ();
if (ret)
{
const char* s = lt_dlerror ();
error ("ltdl init fail: %s", s);
}
lt_init_done = 1;
}
/* FIXME: check for libltdl, load it if available; don't require
--with-ltdl at configure time. See image.c for example. */
CHECK_STRING (file);
handle = lt_dlopen (SDATA (file));
if (!handle)
error ("Cannot load file %s : %s", SDATA (file), lt_dlerror());
gpl_sym = lt_dlsym (handle, "plugin_is_GPL_compatible");
if (!gpl_sym)
error ("Module %s is not GPL compatible", SDATA (file));
module_init = (emacs_init_function) lt_dlsym (handle, "emacs_module_init");
if (!module_init)
error ("Module %s does not have an init function.", SDATA (file));
int r = module_init (module_get_runtime ());
/* Errors are reported by calling env->signal_error. FIXME: so why does module_init return anything? */
return Qt;
}
EXFUN (Fmodule_unsafe_unload, 1);
DEFUN ("module-unsafe-unload", Fmodule_unsafe_unload, Smodule_unsafe_unload, 1, 1, 0,
doc: /* Unload module FILE; does not undefine any functions defined by the module.
This permits re-compiling and re-loading while developing the module,
but is otherwise not recommended. */)
(Lisp_Object file)
{
lt_dlhandle handle;
if (!lt_init_done)
{
error ("no module loaded");
}
CHECK_STRING (file);
handle = lt_dlopen (SDATA (file));
if (!handle)
error ("file not loaded %s : %s", SDATA (file), lt_dlerror());
if (lt_dlclose (handle))
error ("Module %s not unloaded: %s", SDATA (file), lt_dlerror());
return Qt;
}
EXFUN (Fmodule_emacs_value_type, 0);
DEFUN ("module-emacs_value-type", Fmodule_emacs_value_type, Smodule_emacs_value_type, 0, 0, 0,
doc: /* Return a string specifying the type for emacs_value in emacs_modules.h. */)
()
{
if (sizeof (EMACS_INT) == 4) /* 4 bytes == 32 bits */
return make_string ("uint32_t", 8);
else
return make_string ("uint64_t", 8);
}
void syms_of_module (void)
{
defsubr (&Smodule_call);
defsubr (&Smodule_load);
defsubr (&Smodule_unsafe_unload);
defsubr (&Smodule_emacs_value_type);
}