|
x-engine-c v0.2.13
The C engine for x-lang
|
Foreign Function Interface primitives for x-lang. More...
#include "x-prim.h"#include "x-stdlib.h"#include "x-sys.h"#include "x-eval.h"#include "x-type/int.h"#include "x-type/prim.h"#include "x-type/ptr.h"#include "x-type/str.h"#include "x-type/symbol.h"#include <string.h>#include <stdio.h>#include <dlfcn.h>Functions | |
| static x_obj_t * | x_prim_dlopen (x_obj_t *p_base, x_obj_t *p_args) |
| Open a dynamic shared library. | |
| static x_obj_t * | x_prim_dlsym (x_obj_t *p_base, x_obj_t *p_args) |
| Look up a symbol in a dynamic library. | |
| static void * | x_ffi_fptr (x_obj_t *p_base, x_obj_t *p_fptr, const char *who) |
| Unwrap a function pointer, raising on nil. | |
| static x_obj_t * | x_prim_ffi_call (x_obj_t *p_base, x_obj_t *p_args) |
| Call a foreign function using a convention string. | |
| static x_obj_t * | x_prim_ptr_call (x_obj_t *p_base, x_obj_t *p_args) |
| Call a raw function pointer with up to 7 long-typed arguments. | |
| static x_obj_t * | x_prim_int_to_ptr (x_obj_t *p_base, x_obj_t *p_args) |
| Cast an integer to a pointer object. | |
| static x_obj_t * | x_prim_ptr_to_int (x_obj_t *p_base, x_obj_t *p_args) |
| Cast a pointer object to an integer. | |
| static x_obj_t * | x_prim_mem_alloc (x_obj_t *p_base, x_obj_t *p_args) |
| Allocate a raw, UNMANAGED byte region. | |
| static x_obj_t * | x_prim_mem_copy (x_obj_t *p_base, x_obj_t *p_args) |
| Block copy: the machine's rep-movs instruction. | |
| static x_obj_t * | x_prim_mem_cmp (x_obj_t *p_base, x_obj_t *p_args) |
| Block compare: the machine's rep-cmps instruction. | |
| static x_obj_t * | x_prim_mem_set (x_obj_t *p_base, x_obj_t *p_args) |
| Block fill: the machine's rep-stos instruction. | |
| static x_obj_t * | x_prim_mem_free (x_obj_t *p_base, x_obj_t *p_args) |
| Release a region obtained from (mem alloc). | |
| static x_obj_t * | x_prim_ptr_set (x_obj_t *p_base, x_obj_t *p_args) |
| Write nbytes of an integer value into raw memory at ptr+offset. | |
| static x_obj_t * | x_prim_ptr_ref (x_obj_t *p_base, x_obj_t *p_args) |
| Read nbytes from raw memory at ptr+offset as an integer. | |
| static x_obj_t * | x_prim_ptr_alloc (x_obj_t *p_base, x_obj_t *p_args) |
Allocate n bytes of raw memory. x-lang: (ptr alloc n) | |
| static x_obj_t * | x_prim_ptr_free (x_obj_t *p_base, x_obj_t *p_args) |
| Release memory from (ptr alloc). x-lang: (ptr free! p) | |
| static x_obj_t * | x_prim_ptr_copy (x_obj_t *p_base, x_obj_t *p_args) |
Copy n bytes between raw regions. x-lang: (ptr copy! dst src n) | |
| static x_obj_t * | x_prim_ptr_fill (x_obj_t *p_base, x_obj_t *p_args) |
Set n bytes to byte. x-lang: (ptr fill! p byte n) | |
| static x_obj_t * | x_prim_ptr_set_word (x_obj_t *p_base, x_obj_t *p_args) |
| Write a machine-word-sized value into raw memory at ptr+offset. | |
| static x_obj_t * | x_prim_string_to_ptr (x_obj_t *p_base, x_obj_t *p_args) |
| Get the raw C string pointer from a string object. | |
| static x_obj_t * | x_prim_ptr_to_string (x_obj_t *p_base, x_obj_t *p_args) |
| Copy a C string from a pointer into a new string object. | |
| static x_obj_t * | x_prim_obj_to_ptr (x_obj_t *p_base, x_obj_t *p_args) |
| Get the raw C pointer to an x-lang object. | |
| static x_obj_t * | x_prim_ptr_to_obj (x_obj_t *p_base, x_obj_t *p_args) |
| Materialize an object reference from a raw pointer. | |
| static x_obj_t * | x_prim_ptr_ref_word (x_obj_t *p_base, x_obj_t *p_args) |
| Read a machine-word-sized value from raw memory at ptr+offset. | |
| static x_obj_t * | x_prim_make_callable (x_obj_t *p_base, x_obj_t *p_args) |
| Create a callable primitive from a raw function pointer. | |
| x_obj_t * | x_prim_ffi_register (x_obj_t *p_base, x_obj_t *p_args) |
| Register all FFI primitives and platform constants. | |
Double Bit-Pattern Helpers | |
IEEE 754 doubles are passed through the FFI as raw bit patterns stored in integers. On 64-bit platforms a single x_int_t holds the full 8-byte pattern; on 32-bit platforms two integers (a pair) carry the low and high halves. | |
| static void | x_ffi_to_double (x_obj_t *p_base, x_obj_t *p_bits, double *out) |
| Convert a pair of integers to a C double (32-bit path). | |
| static x_obj_t * | x_ffi_from_double (x_obj_t *p_base, double *in) |
| Convert a C double to a pair of integers (32-bit path). | |
The engine's own C library, reachable from X | |
x-stdlib.h and x-sys.h in full. The engine carries its own memcpy, memset, strlen, strcmp and allocator, and its own read/write/open/close, precisely so it need not assume a C library on the machine that runs it. None of it was reachable from X, so X code that wanted any of it reached dlopen/dlsym for libc's – borrowing exactly the dependency the engine went to the trouble of avoiding. These operate on RAW MEMORY, not on x-lang strings: a char* here is a pointer object, which is why they are filed under | |
| static x_obj_t * | x_prim_ptr_strlen (x_obj_t *p_base, x_obj_t *p_args) |
Length of the NUL-terminated string at p. x-lang: (ptr strlen p) | |
| static x_obj_t * | x_prim_ptr_strcmp (x_obj_t *p_base, x_obj_t *p_args) |
| Compare two NUL-terminated strings. x-lang: (ptr strcmp a b) | |
| static x_obj_t * | x_prim_ptr_strncmp (x_obj_t *p_base, x_obj_t *p_args) |
Compare at most n bytes. x-lang: (ptr strncmp a b n) | |
| static x_obj_t * | x_prim_ptr_strchr (x_obj_t *p_base, x_obj_t *p_args) |
First occurrence of byte c, or nil. x-lang: (ptr strchr p c) | |
| static x_obj_t * | x_prim_ptr_strndup (x_obj_t *p_base, x_obj_t *p_args) |
Duplicate at most n bytes; the caller owns it. x-lang: (ptr strndup p n) | |
| static x_obj_t * | x_prim_int_abs (x_obj_t *p_base, x_obj_t *p_args) |
| Absolute value. x-lang: (int abs n) | |
| static x_obj_t * | x_prim_sys_read (x_obj_t *p_base, x_obj_t *p_args) |
Read up to n bytes into raw memory. x-lang: (sys read fd p n) | |
| static x_obj_t * | x_prim_sys_write (x_obj_t *p_base, x_obj_t *p_args) |
Write n bytes from raw memory. x-lang: (sys write fd p n) | |
| static x_obj_t * | x_prim_sys_open (x_obj_t *p_base, x_obj_t *p_args) |
Open path with raw flags. x-lang: (sys open path flags) | |
| static x_obj_t * | x_prim_sys_close (x_obj_t *p_base, x_obj_t *p_args) |
| Close a descriptor. x-lang: (sys close fd) | |
| static x_obj_t * | x_prim_sys_read_char (x_obj_t *p_base, x_obj_t *p_args) |
| One byte from a descriptor, or -1. x-lang: (sys read-char fd) | |
| static x_obj_t * | x_prim_sys_exit (x_obj_t *p_base, x_obj_t *p_args) |
| Terminate the process. x-lang: (sys exit status) | |
Foreign Function Interface primitives for x-lang.
Provides dynamic library loading (dlopen, dlsym), typed foreign calls (ffi-call with convention strings), raw pointer calls (ptr-call), pointer/integer/string/object conversions (int->ptr, ptr->int, str->ptr, ptr->str, obj->ptr, ptr->obj), raw memory access (ptr-ref, ptr-set!, ptr-ref-word, ptr-set-word!), and callable construction (make-callable). (The obj-meta-* accessors are pure x-lang now: boot/reflect.x files reflective implementations into the catalog over the layout contracts.)
Unwrap a function pointer, raising on nil.
| p_base | Base (execution context). |
| p_fptr | Evaluated fptr argument (a POINTER object, or nil). |
| who | Prim name for the error message. |
sqrt is not reachable from a self-handle (the engine links no libm; macOS bundles the math functions, which is why the crash never showed there). Calling through NULL is not catchable; this raise is. Convert a C double to a pair of integers (32-bit path).
| p_base | Base (execution context) for allocation. | |
| [in] | in | Pointer to the source double. |
Convert a pair of integers to a C double (32-bit path).
| p_base | Unused. | |
| p_bits | Pair whose first/rest are the low/high 32-bit halves. | |
| [out] | out | Destination double. |
Open a dynamic shared library.
x-lang form:
Wraps POSIX dlopen(3). If path is nil, opens the main program handle.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self path-string flags-int). |
Look up a symbol in a dynamic library.
x-lang form:
Wraps POSIX dlsym(3).
| p_base | Base (execution context). |
| p_args | Unevaluated: (self handle-ptr name-string). |
Call a foreign function using a convention string.
x-lang form:
The convention string selects the calling convention and type coercions:
Doubles are represented as raw IEEE 754 bit patterns in integers.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self convention-string fptr args...). |
Register all FFI primitives and platform constants.
Binds: dlopen, dlsym, ffi-call, ptr-call, int->ptr, ptr->int, ptr->obj, ptr-set!, ptr-ref, ptr-ref-word, ptr-set-word!, obj->ptr, str->ptr, ptr->str, make-callable. (The obj-meta-* accessors are pure x-lang now: boot/reflect.x files reflective implementations into the catalog over tools/contract/obj-layout.x and tools/contract/base-paths.x.)
Platform constants live in X, not here: word size is probed by boot/data.x, and the O_* open flags come from the per-OS tables in x/platform/syscall.x.
| p_base | Base (execution context) to bind primitives into. |
| p_args | Unused. |
p_base. Absolute value. x-lang: (int abs n)
Cast an integer to a pointer object.
x-lang form:
| p_base | Base (execution context). |
| p_args | Unevaluated: (self n). |
Create a callable primitive from a raw function pointer.
x-lang form:
Wraps a PTR object's address as an x_fn_t in a new prim object, making it callable from x-lang as an operative.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self fn-ptr). |
Allocate a raw, UNMANAGED byte region.
x-lang form:
The region is zeroed (calloc semantics – deterministic contents) and returned as a POINTER object that does NOT own it: the caller must release it with (mem free), or it leaks. Prefer (str make) – a GC-owned region – unless a header-less block is required (FFI structs, alignment-sensitive out-params). UNCHECKED like first/rest: n is trusted.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self n). |
Block compare: the machine's rep-cmps instruction.
x-lang form:
TRUE memcmp semantics (NUL bytes do not terminate – strncmp would be wrong for raw regions), implemented as a local byte loop so x-lib needs no new entry. Returns 0 on equality, else the sign of the first differing byte pair (-1/1). UNCHECKED.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self a b n). |
Block copy: the machine's rep-movs instruction.
x-lang form:
Copies n bytes from src to dst (x_lib_memcpy; regions must not overlap). UNCHECKED like first/rest: pointers and n are trusted. Side-effect prim: returns NULL.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self dst src n). |
Release a region obtained from (mem alloc).
x-lang form:
UNCHECKED: freeing anything not returned by (mem alloc), or freeing twice, is UB exactly as in C. Side-effect prim: returns NULL.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr). |
Block fill: the machine's rep-stos instruction.
x-lang form:
Fills n bytes at p with byte (x_lib_memset). UNCHECKED. Side-effect prim: returns NULL.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self p byte n). |
Get the raw C pointer to an x-lang object.
x-lang form:
Returns the object's address as a pointer. The pointer is invalidated if GC relocates the object.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self obj). |
Allocate n bytes of raw memory. x-lang: (ptr alloc n)
x_sys_malloc is the engine's own allocator – x-cli.c, callcc.c, buffer.c and string.c all use it – and it was not reachable from X, so X code that wanted a scratch buffer reached dlopen/dlsym for libc's malloc instead. A runtime cannot assume a C library is on the machine that runs it.
The alternative already in X is (obj make), which allocates through the collector – right for a value, wrong for a scratch buffer, because such a buffer is then an object in the heap: a state-image writer's own workspace would land inside the image it is writing.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self n). |
Call a raw function pointer with up to 7 long-typed arguments.
x-lang form:
Arguments are evaluated and coerced: integers become long, strings and pointers pass their raw C pointer. The function is called with the C calling convention (long, long, ...) -> long.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self fptr arg0 ... arg6). |
Copy n bytes between raw regions. x-lang: (ptr copy! dst src n)
The engine has its own memcpy (x_lib_memcpy); reaching dlopen/dlsym for libc's is borrowing a system library a runtime cannot assume is present. The alternative in X is a per-byte loop, which costs hundreds of evals a byte – so without this coordinate the only options were a hack or a pathology.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self dst src n). |
Set n bytes to byte. x-lang: (ptr fill! p byte n)
The zeroing half of the same argument: a buffer wanted zeroed had to come from libc's calloc for want of this.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr byte n). |
Release memory from (ptr alloc). x-lang: (ptr free! p)
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr). |
Read nbytes from raw memory at ptr+offset as an integer.
x-lang form:
Copies nbytes from the memory at the pointer plus byte offset into a zero-initialized integer value via memcpy.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr offset nbytes). |
Read a machine-word-sized value from raw memory at ptr+offset.
x-lang form:
Reads sizeof(long) bytes from memory at ptr+offset via memcpy.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr offset). |
Write nbytes of an integer value into raw memory at ptr+offset.
x-lang form:
Copies the low nbytes of value via memcpy into the memory at the given pointer plus byte offset.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr offset value nbytes). |
Write a machine-word-sized value into raw memory at ptr+offset.
x-lang form:
Writes sizeof(long) bytes of value into memory at ptr+offset.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr offset value). |
First occurrence of byte c, or nil. x-lang: (ptr strchr p c)
Compare two NUL-terminated strings. x-lang: (ptr strcmp a b)
Length of the NUL-terminated string at p. x-lang: (ptr strlen p)
Compare at most n bytes. x-lang: (ptr strncmp a b n)
Duplicate at most n bytes; the caller owns it. x-lang: (ptr strndup p n)
Cast a pointer object to an integer.
x-lang form:
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr). |
Materialize an object reference from a raw pointer.
x-lang form:
The inverse of obj->ptr and the reflective ISA's load-word-as-reference instruction: it lets X read a word (e.g. an object's data or type slot via ptr-ref-word) and treat it as the object it addresses, which is what makes the reflective accessors in lib/x/boot/reflect.x able to RETURN objects, not just integers.
UNCHECKED, like first/rest: an address that is not a live object is undefined behaviour. Safe under the explicit-only GC (allocation never relocates), same argument as holding obj->ptr across an expression.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr). |
Copy a C string from a pointer into a new string object.
x-lang form:
Reads a NUL-terminated string from the pointer address and duplicates it into a new owned string object.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self ptr). |
Get the raw C string pointer from a string object.
x-lang form:
Returns a pointer to the string's internal character buffer. The pointer is only valid while the string is not garbage collected.
| p_base | Base (execution context). |
| p_args | Unevaluated: (self str). |
Close a descriptor. x-lang: (sys close fd)
Terminate the process. x-lang: (sys exit status)
Open path with raw flags. x-lang: (sys open path flags)
Read up to n bytes into raw memory. x-lang: (sys read fd p n)
One byte from a descriptor, or -1. x-lang: (sys read-char fd)