Next: 4.9 API Up: 4. The libonyx library Previous: 4.7.3 Signals Contents Index

# 4.8 Guidelines for writing extensions

When embedding libonyx in an application, it is usually desireable to add some operators so that the interpreter can interact with the rest of the application. The libonyx source code contains hundreds of operators that can be used as examples when writing new operators. However, there are some very important rules that operators must follow, some of which may not be obvious when reading the code.

- Manually managed ( malloc()/ free()) memory should not be allocated unless the code is very careful. If a function recurses into the interpreter (this includes calls to functions such as nxo_thread_nerror()), there is the very real possibility that control will never return to the operator due to an exception. Code must either catch all exceptions and clean up allocations, or not recurse into the interpreter.

- Composite objects should never be allocated on the C stack. The garbage collector has no knowledge of such objects, so if the only reference to an object is on the C stack, the object may be collected, which will lead to unpredictable program behavior. Instead of allocating objects on the C stack, use tstack, available via nxo_thread_tstack_get(), which is a per-thread stack that the garbage collector scans.

- For an object to be safe from garbage collection, there must always be at least one reference to it inside the interpreter. So, if C code obtains a pointer to a composite object, then destroys the last known internal Onyx reference (pops it off a stack, redefines it in a dict, replaces an element of an array, etc.), the pointer is no longer safe to use. The libonyx API is structured such that it is invalid to do such a thing, for this reason.

- tstack must be cleaned up before returning from a function. This constraint is placed on the code in order to avoid leaking space on tstack. In debug versions of libonyx, this is enforced by assertions. The one exception to this rule has to do with xep exceptions, in which case the catchers of the exceptions are responsible for cleaning up tstack. Therefore, it is not necessary to catch exceptions merely to avoid tstack leakage.

Since Onyx type checking is dynamic, it is the responsibility of the operators to assure objects are the correct type before calling any of the type-specific nxo_*() functions. Failure to do so will result in unpredictable behavior and likely crashes.

Next: 4.9 API Up: 4. The libonyx library Previous: 4.7.3 Signals Contents Index