Daniel Colascione <
address@hidden> schrieb am So., 15. Feb. 2015 um 21:21 Uhr:
Here's a broad outline of what I have in mind.
Thanks, that looks really good. Just a few minor issues that I encountered over the last couple of weeks.
Thread-local environments
-------------------------
The `get_environment' member lets us do anything else interesting. As
in Java, environments are thread-local. We only support one thread for
the moment, so this constraint is easy to enforce. (Just abort if we
use an emacs_env off the main thread.)
Would you really abort, or rather use the error handling functions? We should be able to make the error values thread-local so that calling a function from the wrong thread would be the equivalent of raising a signal, giving the caller a chance to react. Otherwise the burden of remembering the correct thread would be on the caller's side.
typedef struct emacs_value_tag* emacs_value;
I think it's important that this is a pointer to a struct (for type safety and size correctness) rather than just an arbitrary type.
typedef emacs_value (*emacs_subr)(
emacs_env* env,
int nargs,
emacs_value args[]);
Could we give this a void* data parameter for storing arbitrary user data? This is common for callbacks in C interfaces and allows C++ users to store C++ objects in the pointer. This can be implemented using another save pointer.
emacs_value (*make_function)(
emacs_env* env,
int min_arity,
int max_arity,
emacs_subr function);
Similary, here void* data should be passed to be retrieved later.
emacs_value (*funcall)(
emacs_env* env,
emacs_value function,
int nargs,
emacs_value args[]);
Does function have to be a function object, or can it be a symbol? I.e. is the user supposed to call symbol-function first?
int64_t (*fixnum_to_int)(
emacs_env* env,
emacs_value value);
emacs_value (*make_fixnum)(
emacs_env* env,
int64_t value);
What's the behavior of these functions if the source would not fit into the result? Undefined behavior, abort(), raising a signal?
Modules can use make_global_reference to allocate a global reference
(i.e., a GC root) for any emacs_value; modules must then free these
references explicitly.
All routines (except make_global_reference) that return emacs_value
values return local references. It's up to modules to register
long-lived references explicitly.
In which cases would global references be necessary?
Like JNI, we can just declare that it's illegal to call all but a few
specially-marked functions (like global reference deregistration) with
a pending error.
What's the behavior if other functions are called? abort()?
If Lisp signals or throws, `funcall' returns NULL.
Hmm, with the current implementation emacs_value is just the same as Lisp_Object, i.e. not a real pointer, so NULL doesn't have specific semantics. Should it return Qnil instead and force the user to use check_error?
1) Do we need JNI-style local variable frames and functions that
release local references?
2) Maybe we want a separate, non-emacs_value type for global references?
No idea about these.
3) How exactly do we represent catch/throw values?
I've thought about this a bit, and I think it would be simplest to add a new function env->set_throw and have get_error and check_error return an enum { normal, signal, throw }. One could come up with something like creating a list (error-kind error-tag error-value), but it looks like the module implementation would create such lists only for the module code to convert them back, so it's simpler to represent the two kinds of non-local exits directly in the interface.
4) Do we need to use int64_t for integers?
Maybe just intmax_t and a static check that that is larger than an Elisp integer?