1use std::panic::{catch_unwind, AssertUnwindSafe};
14use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
15use std::sync::{Mutex, OnceLock};
16
17use crate::runtime_backend::{CodeHandle, PointerStore};
18use crate::runtime_core::{
19 self, CallDispatch, OutputSink, ReturnPolicy, RuntimeErrorKind, RuntimeFailure, RuntimeResult,
20 Value, NULL_VALUE,
21};
22
23static OBSERVER_FD: AtomicI64 = AtomicI64::new(-1);
25
26static GLOBALS_BASE: AtomicU64 = AtomicU64::new(0);
28static GLOBALS_COUNT: AtomicU64 = AtomicU64::new(0);
29
30fn native_store() -> &'static Mutex<PointerStore> {
31 static STORE: OnceLock<Mutex<PointerStore>> = OnceLock::new();
32 STORE.get_or_init(|| Mutex::new(PointerStore::new()))
33}
34
35struct StdoutSink;
36
37impl OutputSink for StdoutSink {
38 fn write_line(&mut self, line: &str) {
39 use std::io::Write;
40 let stdout = std::io::stdout();
41 let mut handle = stdout.lock();
42 let _ = handle.write_all(line.as_bytes());
45 let _ = handle.write_all(b"\n");
46 let _ = handle.flush();
47 }
48}
49
50#[cfg(unix)]
53fn observer_write(payload: &str) {
54 use std::io::Write;
55 use std::os::unix::io::{FromRawFd, IntoRawFd};
56
57 let fd = OBSERVER_FD.load(Ordering::SeqCst);
58 if fd < 0 {
59 return;
60 }
61 let mut file = unsafe { std::fs::File::from_raw_fd(fd as i32) };
62 let length = (payload.len() as u64).to_be_bytes();
63 let _ = file.write_all(&length);
64 let _ = file.write_all(payload.as_bytes());
65 let _ = file.flush();
66 let _ = file.into_raw_fd();
68}
69
70#[cfg(not(unix))]
71fn observer_write(_payload: &str) {}
72
73fn fatal(kind: RuntimeErrorKind, message: &str) -> ! {
76 observer_write(&format!("{{\"status\":\"error\",\"kind\":\"{}\"}}", kind.name()));
77 eprintln!("monkey: {}: {}", kind.name(), message);
78 std::process::exit(1);
79}
80
81fn ffi_shell<T>(body: impl FnOnce(&mut PointerStore) -> RuntimeResult<T>) -> T {
84 let outcome = catch_unwind(AssertUnwindSafe(|| {
85 let mut store = native_store()
86 .lock()
87 .unwrap_or_else(|poisoned| poisoned.into_inner());
88 body(&mut store)
89 }));
90 match outcome {
91 Ok(Ok(value)) => value,
92 Ok(Err(RuntimeFailure {
93 kind,
94 message,
95 })) => fatal(kind, &message),
96 Err(_) => fatal(RuntimeErrorKind::InternalError, "runtime panicked"),
97 }
98}
99
100unsafe fn value_slice<'a>(ptr: *const Value, len: u64) -> &'a [Value] {
103 if len == 0 {
104 &[]
105 } else {
106 std::slice::from_raw_parts(ptr, len as usize)
107 }
108}
109
110unsafe fn byte_slice<'a>(ptr: *const u8, len: u64) -> &'a [u8] {
111 if len == 0 {
112 &[]
113 } else {
114 std::slice::from_raw_parts(ptr, len as usize)
115 }
116}
117
118fn name_from_bytes(bytes: &[u8]) -> RuntimeResult<&str> {
119 std::str::from_utf8(bytes).map_err(|_| RuntimeFailure {
120 kind: RuntimeErrorKind::InternalError,
121 message: "identifier bytes are not valid UTF-8".to_string(),
122 })
123}
124
125unsafe fn invoke_code(code: CodeHandle, closure: Value, args: &[Value]) -> Value {
128 use std::mem::transmute;
129 let address = code as usize;
130 match args {
131 [] => transmute::<usize, extern "C" fn(Value) -> Value>(address)(closure),
132 [a1] => transmute::<usize, extern "C" fn(Value, Value) -> Value>(address)(closure, *a1),
133 [a1, a2] => transmute::<usize, extern "C" fn(Value, Value, Value) -> Value>(address)(
134 closure, *a1, *a2,
135 ),
136 [a1, a2, a3] => transmute::<usize, extern "C" fn(Value, Value, Value, Value) -> Value>(
137 address,
138 )(closure, *a1, *a2, *a3),
139 [a1, a2, a3, a4] => transmute::<
140 usize,
141 extern "C" fn(Value, Value, Value, Value, Value) -> Value,
142 >(address)(closure, *a1, *a2, *a3, *a4),
143 [a1, a2, a3, a4, a5] => transmute::<
144 usize,
145 extern "C" fn(Value, Value, Value, Value, Value, Value) -> Value,
146 >(address)(closure, *a1, *a2, *a3, *a4, *a5),
147 [a1, a2, a3, a4, a5, a6] => transmute::<
148 usize,
149 extern "C" fn(Value, Value, Value, Value, Value, Value, Value) -> Value,
150 >(address)(closure, *a1, *a2, *a3, *a4, *a5, *a6),
151 [a1, a2, a3, a4, a5, a6, a7] => {
152 transmute::<
153 usize,
154 extern "C" fn(Value, Value, Value, Value, Value, Value, Value, Value) -> Value,
155 >(address)(closure, *a1, *a2, *a3, *a4, *a5, *a6, *a7)
156 }
157 _ => fatal(
158 RuntimeErrorKind::ResourceLimit,
159 "call requires more arguments than the calling convention allows",
160 ),
161 }
162}
163
164fn complete_dispatch(dispatch: CallDispatch) -> Value {
167 match dispatch {
168 CallDispatch::Return(value) => value,
169 CallDispatch::Invoke {
170 code,
171 closure,
172 args,
173 return_policy,
174 } => {
175 let returned = unsafe { invoke_code(code, closure, &args) };
176 match return_policy {
177 ReturnPolicy::Direct => returned,
178 ReturnPolicy::ConstructorInstance(instance) => instance,
179 }
180 }
181 }
182}
183
184#[no_mangle]
185pub unsafe extern "C" fn rt_globals_init(base: *mut Value, count: u64) {
189 ffi_shell(|_store| {
190 for index in 0..count as usize {
191 unsafe {
192 *base.add(index) = NULL_VALUE;
193 }
194 }
195 GLOBALS_BASE.store(base as u64, Ordering::SeqCst);
196 GLOBALS_COUNT.store(count, Ordering::SeqCst);
197 Ok(())
198 })
199}
200
201#[no_mangle]
202pub unsafe extern "C" fn rt_string_from_bytes(ptr: *const u8, len: u64) -> Value {
205 ffi_shell(|store| {
206 let bytes = unsafe { byte_slice(ptr, len) };
207 runtime_core::string_from_utf8(store, bytes)
208 })
209}
210
211#[no_mangle]
212pub extern "C" fn rt_box_int(raw: i64) -> Value {
213 ffi_shell(|store| Ok(runtime_core::make_int(store, raw)))
214}
215
216#[no_mangle]
217pub unsafe extern "C" fn rt_array(argv: *const Value, len: u64) -> Value {
220 ffi_shell(|store| {
221 let values = unsafe { value_slice(argv, len) };
222 Ok(runtime_core::array_from_values(store, values))
223 })
224}
225
226#[no_mangle]
227pub unsafe extern "C" fn rt_hash(argv: *const Value, pairs: u64) -> Value {
231 ffi_shell(|store| {
232 let values = unsafe { value_slice(argv, pairs * 2) };
233 runtime_core::hash_from_pairs(store, values)
234 })
235}
236
237#[no_mangle]
238pub unsafe extern "C" fn rt_closure(
242 code: *const u8,
243 num_parameters: u64,
244 free: *const Value,
245 num_free: u64,
246) -> Value {
247 ffi_shell(|store| {
248 let free_values = unsafe { value_slice(free, num_free) };
249 runtime_core::closure_new(store, code as CodeHandle, num_parameters, free_values)
250 })
251}
252
253#[no_mangle]
254pub extern "C" fn rt_get_free(closure: Value, index: u64) -> Value {
255 ffi_shell(|store| runtime_core::get_free(store, closure, index))
256}
257
258#[no_mangle]
259pub unsafe extern "C" fn rt_class(name: *const u8, len: u64) -> Value {
262 ffi_shell(|store| {
263 let bytes = unsafe { byte_slice(name, len) };
264 let class_name = name_from_bytes(bytes)?;
265 Ok(runtime_core::class_new(store, class_name))
266 })
267}
268
269#[no_mangle]
270pub unsafe extern "C" fn rt_class_add_method(
273 class: Value,
274 name: *const u8,
275 len: u64,
276 method: Value,
277 is_ctor: u64,
278) {
279 ffi_shell(|store| {
280 let bytes = unsafe { byte_slice(name, len) };
281 let method_name = name_from_bytes(bytes)?;
282 runtime_core::class_add_method(store, class, method_name, method, is_ctor != 0)
283 })
284}
285
286#[no_mangle]
287pub unsafe extern "C" fn rt_get_property(obj: Value, name: *const u8, len: u64) -> Value {
290 ffi_shell(|store| {
291 let bytes = unsafe { byte_slice(name, len) };
292 let property = name_from_bytes(bytes)?;
293 runtime_core::get_property(store, obj, property)
294 })
295}
296
297#[no_mangle]
298pub unsafe extern "C" fn rt_set_property(obj: Value, name: *const u8, len: u64, v: Value) {
301 ffi_shell(|store| {
302 let bytes = unsafe { byte_slice(name, len) };
303 let property = name_from_bytes(bytes)?;
304 runtime_core::set_property(store, obj, property, v)
305 })
306}
307
308#[no_mangle]
309pub extern "C" fn rt_index(obj: Value, idx: Value) -> Value {
310 ffi_shell(|store| runtime_core::index(store, obj, idx))
311}
312
313#[no_mangle]
314pub extern "C" fn rt_add(l: Value, r: Value) -> Value {
315 ffi_shell(|store| runtime_core::add(store, l, r))
316}
317
318#[no_mangle]
319pub extern "C" fn rt_sub(l: Value, r: Value) -> Value {
320 ffi_shell(|store| runtime_core::sub(store, l, r))
321}
322
323#[no_mangle]
324pub extern "C" fn rt_mul(l: Value, r: Value) -> Value {
325 ffi_shell(|store| runtime_core::mul(store, l, r))
326}
327
328#[no_mangle]
329pub extern "C" fn rt_div(l: Value, r: Value) -> Value {
330 ffi_shell(|store| runtime_core::div(store, l, r))
331}
332
333#[no_mangle]
334pub extern "C" fn rt_eq(l: Value, r: Value) -> Value {
335 ffi_shell(|store| runtime_core::eq_values(store, l, r).map(runtime_core::bool_value))
336}
337
338#[no_mangle]
339pub extern "C" fn rt_neq(l: Value, r: Value) -> Value {
340 ffi_shell(|store| {
341 runtime_core::eq_values(store, l, r).map(|equal| runtime_core::bool_value(!equal))
342 })
343}
344
345#[no_mangle]
346pub extern "C" fn rt_gt(l: Value, r: Value) -> Value {
347 ffi_shell(|store| runtime_core::gt(store, l, r))
348}
349
350#[no_mangle]
351pub extern "C" fn rt_minus(v: Value) -> Value {
352 ffi_shell(|store| runtime_core::minus(store, v))
353}
354
355#[no_mangle]
356pub extern "C" fn rt_bang(v: Value) -> Value {
357 ffi_shell(|_store| Ok(runtime_core::bang(v)))
358}
359
360#[no_mangle]
361pub extern "C" fn rt_truthy(v: Value) -> u64 {
362 ffi_shell(|_store| Ok(if runtime_core::truthy(v) { 1 } else { 0 }))
363}
364
365#[no_mangle]
366pub unsafe extern "C" fn rt_call(callee: Value, argc: u64, argv: *const Value) -> Value {
370 let dispatch = ffi_shell(|store| {
371 let args = unsafe { value_slice(argv, argc) };
372 runtime_core::dispatch_call(store, &mut StdoutSink, callee, args)
373 });
374 complete_dispatch(dispatch)
375}
376
377#[no_mangle]
378pub unsafe extern "C" fn rt_construct(callee: Value, argc: u64, argv: *const Value) -> Value {
382 let dispatch = ffi_shell(|store| {
383 let args = unsafe { value_slice(argv, argc) };
384 runtime_core::dispatch_construct(store, callee, args)
385 });
386 complete_dispatch(dispatch)
387}
388
389#[no_mangle]
390pub unsafe extern "C" fn rt_observer_init(fd: u64) {
394 OBSERVER_FD.store(fd as i64, Ordering::SeqCst);
395}
396
397#[no_mangle]
398pub extern "C" fn rt_observe_result(v: Value) {
399 let payload = ffi_shell(|store| {
400 let value = runtime_core::canonical_value(store, v)?;
401 Ok(format!("{{\"status\":\"ok\",\"value\":{}}}", value))
402 });
403 observer_write(&payload);
404}
405
406#[no_mangle]
407pub unsafe extern "C" fn rt_fatal(kind: u64, msg: *const u8, len: u64) -> ! {
410 let outcome = catch_unwind(|| {
411 let kind = RuntimeErrorKind::from_u64(kind).unwrap_or(RuntimeErrorKind::InternalError);
412 let bytes = unsafe { byte_slice(msg, len) };
413 (kind, String::from_utf8_lossy(bytes).into_owned())
414 });
415 match outcome {
416 Ok((kind, message)) => fatal(kind, &message),
417 Err(_) => fatal(RuntimeErrorKind::InternalError, "runtime panicked"),
418 }
419}