1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
//! Safe(r) Rust bindings for writing a native `.so` module for the [Code
//! programming language](https://github.com/codelovesme/code).
//!
//! `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
//!
//! ```rust,ignore
//! 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.
use ;
// ===========================================================================
// Wire layout — bit-for-bit `code_abi.h`. Only the pointer/int/float shapes
// matter for ABI compatibility (not what they're named), but names are kept
// identical to the header so the two are trivially diffable.
// ===========================================================================
/// Current ABI version. A module's `code_module_abi_version` must return
/// this.
pub const CODE_ABI_VERSION: u32 = 1;
/// 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.
pub const CODE_VALUE_SLOT_SIZE: usize = 80;
// Both types carry raw pointers, so Rust doesn't derive Send/Sync for them
// automatically — but `code_module_vars` (see README) is exactly the case
// that needs a `static`/`OnceLock<CodeVarList>`, and the host only ever
// reads this data (once, at `link` time), never mutates it concurrently.
// Matches the old language's own `code-abi` crate, which needed the same
// impls for the same reason.
unsafe
unsafe
unsafe
unsafe
// ===========================================================================
// Raw bindings to `runtime.c`'s exported (non-`static`) functions — the same
// symbols `code_abi.h` declares for a C module. Calling into the actual
// compiled `runtime.c`, not a port of it, is what keeps this crate free of
// the layout-drift risk the *old* language's `code-native`/`code-abi` pair
// needed a dedicated test to guard against.
// ===========================================================================
extern "C"
/// 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.
///
/// # Safety
/// `v` must point to a valid, initialized `CodeValue` — the same
/// requirement `runtime.c`'s own `code_release` has. The host only ever
/// calls this on values it deep-copied out of your `code_module_dispatch`
/// result, so you should never need to call it yourself except via
/// [`release`].
pub unsafe extern "C"
/// 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`).
// ===========================================================================
// Safe scalar constructors — thin wrappers: `code_release`s `out` first
// (matching every `runtime.c` constructor's own contract), then delegates.
// ===========================================================================
/// Write a Number into `out`.
/// 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.
/// 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).
/// Write a Bool into `out`.
/// Write Null into `out`.
/// 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).
/// 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`].
/// `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`.
/// `arr[index]` element access, exactly like `.code` source's own
/// semantics: writes Null on a non-Array or out-of-bounds index.
/// Structural equality, matching `.code` source's `=` operator.
/// 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. `"&&"`).
/// `assert v` semantics: fatal error (never returns) if `v` isn't `true`.
/// 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 buffers — for Array/Object construction, which `runtime.c` expects
// as a `CODE_VALUE_SLOT_SIZE`-strided scratch buffer of already-built
// elements (see `code_array`/`code_object`'s doc comments in `runtime.c`;
// `tests/native_modules/test_math.c`'s `factors`/`meta` exported vars are
// the C-side version of the same pattern).
// ===========================================================================
/// 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 [`release`]d
/// (the copy took its own reference; yours is still live until you drop it).
/// 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`]).
/// 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.
// ===========================================================================
// Reading helpers — for use inside `code_module_dispatch`.
// ===========================================================================
/// 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.
/// Read `v` as a `&str`, if it's a Str with a valid UTF-8 payload.
/// Read `v` as an `f64`, if it's a Number.
/// Read `v` as a `bool`, if it's a Bool.
/// Convenience: [`find_field`] + [`read_str`].
/// Convenience: [`find_field`] + [`read_number`].
/// Convenience: [`find_field`] + [`read_bool`].
/// Iterate an Array's elements.
/// 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.