Skip to main content

Crate code_native

Crate code_native 

Source
Expand description

Safe(r) Rust bindings for writing a native .so module for the Code programming language.

code_abi.h’s contract needs two things from a module: agreement on the CodeValue wire layout, and a code_release (plus friends) built from the real runtime.c rather than a reimplementation that merely looks compatible — getting refcounting subtly wrong is the kind of bug that corrupts memory rather than crashing where you’d notice. This crate’s build.rs compiles the vendored runtime.c and links it into your cdylib directly, so every function below calls the same code the host runtime and every C module trust.

§Quick start

use code_native::*;

#[no_mangle]
pub extern "C" fn code_module_abi_version() -> u32 {
    CODE_ABI_VERSION
}

#[no_mangle]
pub unsafe extern "C" fn code_module_dispatch(out: *mut CodeValue, particle: *const CodeValue) {
    let particle = &*particle;
    match read_field_str(particle, "_class") {
        Some("Double") => {
            let value = read_field_number(particle, "value").unwrap_or(0.0);
            make_result(&mut *out, "DoubleResult", |slot| code_number(slot, value * 2.0));
        }
        _ => code_runtime_error("unknown handler"),
    }
}

Build with crate-type = ["cdylib"], then link "libmymodule.so" as m from .code source. See this crate’s README for the full walkthrough, including .a static modules and code_module_vars.

code_module_dispatch and code_module_abi_version are the two required exports — there is no macro generating them here (unlike the old language’s code-native): the new ABI dropped the descriptor-table design for one function a module dispatches through itself, so there is no boilerplate left to generate. code_release needs no Rust code at all — it comes from the linked runtime.c object automatically.

Structs§

CodeValue
CodeVarList
SlotBuffer
A scratch buffer of count CodeValue slots, zero-initialized (so each slot starts in the same safe state CodeValue::zeroed documents). Build each element in place with SlotBuffer::slot_mut, then hand the buffer to [array] or object — matching runtime.c’s “elements are retained and copied out of this buffer, never adopted by reference” contract, after which every slot you wrote must still be released (the copy took its own reference; yours is still live until you drop it).

Enums§

CodeTag

Constants§

CODE_ABI_VERSION
Current ABI version. A module’s code_module_abi_version must return this.
CODE_VALUE_SLOT_SIZE
Byte stride of an array/object element buffer — not size_of::<CodeValue>(). This is a frozen ABI constant with headroom for CodeValue to grow without breaking already-compiled modules; always address a buffer through slot_at, never by casting to *mut CodeValue and indexing.

Functions§

array
Write an Array into out, copying (and retaining) elems’s slots. elems still owns its own references afterwards — release it once you’re done (see SlotBuffer::release_all).
array_elems
Iterate an Array’s elements.
assert_value
assert v semantics: fatal error (never returns) if v isn’t true.
bool_value
Coerce v to a bool the way a boolean operator does, raising the same fatal error a type mismatch would in .code source itself (op is the operator name, used only for that error message — e.g. "&&").
boolean
Write a Bool into out.
borrowed_str
Write a Str into out, borrowing s for 'static (a string literal or otherwise permanently-alive buffer) rather than copying it — matching code_str’s own borrowing contract. Use owned_str for a value built at runtime that needs its own heap block.
code_release
The ABI’s required code_release export. Defined here, as a real Rust function, rather than left as whatever runtime.c’s own code_release would otherwise be: cdylib targets get --exclude-libs=ALL from rustc by default, which hides every symbol pulled in from a linked static archive (exactly what build.rs’s cc::Build::compile produces from runtime.c) out of the shared library’s dynamic symbol table — even though this crate’s own code calls it just fine internally. A symbol the crate defines directly (this function) isn’t subject to that exclusion, so renaming the archive’s copy and re-exporting it from here is what makes the host’s dlsym("code_release") actually find it.
field
obj.field field access, exactly like .code source’s own semantics: writes Null into out on a non-Object or missing field rather than erroring — see code_field’s doc comment in code_abi.h.
find_field
Read a field by name off an Object value. None if v isn’t an Object or the field doesn’t exist — mirrors code_field’s own permissive-null behavior, but as an Option instead of writing Null.
index
arr[index] element access, exactly like .code source’s own semantics: writes Null on a non-Array or out-of-bounds index.
make_result
Build a { "_class": <class_name>, "value": <fill's result> } particle into out — the shape emit ... to <alias> get x expects a handler’s result to have. Mirrors runtime.c’s own code_make_result, which a C module reaches via #include "runtime.c" but isn’t exported for a separately-linked module to call directly, so this is a small reimplementation rather than an FFI binding.
null
Write Null into out.
number
Write a Number into out.
object
Write an Object into out from parallel keys and values (a SlotBuffer built the same way [array] expects). keys must outlive nothing in particular — code_object copies the pointers, and C-string field names are expected to be 'static (string literals), matching code_abi.h’s own “key pointers are read-only data” note.
owned_str
Write a Str into out from a freshly-built Rust string. Leaks the CString — acceptable here because the value crosses into the host’s own heap the moment your code_module_dispatch returns (the host deep-copies your result and then calls your module’s code_release on it, which only ever frees what runtime.c’s own allocator built, never this leaked buffer).
read_bool
Read v as a bool, if it’s a Bool.
read_field_bool
Convenience: find_field + read_bool.
read_field_number
Convenience: find_field + read_number.
read_field_str
Convenience: find_field + read_str.
read_number
Read v as an f64, if it’s a Number.
read_str
Read v as a &str, if it’s a Str with a valid UTF-8 payload.
release
Release whatever v holds — call on every temporary CodeValue you built and no longer need (matching runtime.c’s own refcounting rule: every slot that ever named a heap block owns exactly one reference to it).
retain
Increment v’s refcount — needed only if you’re holding onto a CodeValue you didn’t just build yourself (e.g. a borrowed field from find_field) somewhere that will outlive the call it came from. Every retained value must be balanced by a release.
runtime_error
Raise a fatal module error — mirrors core’s own handlers. Never returns: like core, this takes the whole host process down (code run included), the same tradeoff every native-extension mechanism makes. See code_abi.h’s doc comment.
slot_at
Addresses slot index of a CODE_VALUE_SLOT_SIZE-strided buffer — the Rust equivalent of code_abi.h’s code_slot_at. Pure pointer arithmetic, safe to reimplement independently (no allocator/refcount logic to drift from runtime.c).
values_equal
Structural equality, matching .code source’s = operator.