1#![doc(html_root_url = "https://docs.rs/gluon_c-api/0.18.2")] use std::{slice, str};
5
6use futures::{executor::block_on, future};
7
8use gluon::{
9 vm::{
10 api::{CPrimitive, Getable, Hole, OpaqueValue, Pushable},
11 stack,
12 thread::{RootedThread, Status, Thread, ThreadInternal},
13 types::{VmIndex, VmInt},
14 },
15 ThreadExt,
16};
17
18pub type Function = extern "C" fn(&Thread) -> Status;
19
20#[repr(C)]
23#[derive(Debug, PartialEq)]
24pub enum Error {
25 Ok,
26 Unknown,
27}
28
29#[no_mangle]
30pub extern "C" fn glu_new_vm() -> *const Thread {
31 let vm = RootedThread::new();
32 vm.into_raw()
33}
34
35#[no_mangle]
36pub unsafe extern "C" fn glu_free_vm(vm: &Thread) {
37 RootedThread::from_raw(vm);
38}
39
40#[no_mangle]
41pub unsafe extern "C" fn glu_run_expr(
42 vm: &Thread,
43 module: &u8,
44 module_len: usize,
45 expr: &u8,
46 expr_len: usize,
47) -> Error {
48 let module = match str::from_utf8(slice::from_raw_parts(module, module_len)) {
49 Ok(s) => s,
50 Err(_) => return Error::Unknown,
51 };
52 let expr = match str::from_utf8(slice::from_raw_parts(expr, expr_len)) {
53 Ok(s) => s,
54 Err(_) => return Error::Unknown,
55 };
56 let result = vm.run_expr::<OpaqueValue<&Thread, Hole>>(module, expr);
57 match result {
58 Ok(_) => Error::Ok,
59 Err(_) => Error::Unknown,
60 }
61}
62
63#[no_mangle]
64pub unsafe extern "C" fn glu_load_script(
65 vm: &Thread,
66 module: &u8,
67 module_len: usize,
68 expr: &u8,
69 expr_len: usize,
70) -> Error {
71 let module = match str::from_utf8(slice::from_raw_parts(module, module_len)) {
72 Ok(s) => s,
73 Err(_) => return Error::Unknown,
74 };
75 let expr = match str::from_utf8(slice::from_raw_parts(expr, expr_len)) {
76 Ok(s) => s,
77 Err(_) => return Error::Unknown,
78 };
79 let result = vm.load_script(module, expr);
80 match result {
81 Ok(_) => Error::Ok,
82 Err(_) => Error::Unknown,
83 }
84}
85
86#[no_mangle]
87pub extern "C" fn glu_call_function(thread: &Thread, args: VmIndex) -> Error {
88 match block_on(future::poll_fn(|cx| {
89 let context = thread.context();
90 thread.call_function(cx, context, args)
91 })) {
92 Ok(_) => Error::Ok,
93 Err(_) => Error::Unknown,
94 }
95}
96
97#[no_mangle]
98pub extern "C" fn glu_len(vm: &Thread) -> usize {
99 let mut context = vm.context();
100 let stack = context.stack_frame::<stack::State>();
101 stack.len() as usize
102}
103
104#[no_mangle]
105pub extern "C" fn glu_pop(vm: &Thread, n: usize) {
106 let mut context = vm.context();
107 let mut stack = context.stack_frame::<stack::State>();
108 for _ in 0..n {
109 stack.pop();
110 }
111}
112
113#[no_mangle]
114pub extern "C" fn glu_push_int(vm: &Thread, int: VmInt) {
115 Thread::push(vm, int).unwrap();
116}
117
118#[no_mangle]
119pub extern "C" fn glu_push_byte(vm: &Thread, b: u8) {
120 Thread::push(vm, b).unwrap();
121}
122
123#[no_mangle]
124pub extern "C" fn glu_push_float(vm: &Thread, float: f64) {
125 Thread::push(vm, float).unwrap();
126}
127
128#[no_mangle]
129pub extern "C" fn glu_push_bool(vm: &Thread, b: i8) {
130 Thread::push(vm, b != 0).unwrap();
131}
132
133#[no_mangle]
134pub unsafe extern "C" fn glu_push_function(
135 vm: &Thread,
136 name: &u8,
137 len: usize,
138 function: Function,
139 args: VmIndex,
140) -> Error {
141 let s = match str::from_utf8(slice::from_raw_parts(name, len)) {
142 Ok(s) => s,
143 Err(_) => return Error::Unknown,
144 };
145 match Thread::push(vm, CPrimitive::new(function, args, s)) {
146 Ok(()) => Error::Ok,
147 Err(_) => Error::Unknown,
148 }
149}
150
151#[no_mangle]
153pub unsafe extern "C" fn glu_push_string(vm: &Thread, s: &u8, len: usize) -> Error {
154 let s = match str::from_utf8(slice::from_raw_parts(s, len)) {
155 Ok(s) => s,
156 Err(_) => return Error::Unknown,
157 };
158 match s.vm_push(&mut vm.current_context()) {
159 Ok(()) => Error::Ok,
160 Err(_) => Error::Unknown,
161 }
162}
163
164#[no_mangle]
167pub unsafe extern "C" fn glu_push_string_unchecked(vm: &Thread, s: &u8, len: usize) -> Error {
168 let s = str::from_utf8_unchecked(slice::from_raw_parts(s, len));
169 match s.vm_push(&mut vm.current_context()) {
170 Ok(()) => Error::Ok,
171 Err(_) => Error::Unknown,
172 }
173}
174
175#[cfg(not(target_arch = "wasm32"))]
176#[no_mangle]
177pub extern "C" fn glu_push_light_userdata(vm: &Thread, data: *mut libc::c_void) {
178 Thread::push(vm, data as usize).unwrap()
179}
180
181#[no_mangle]
182pub extern "C" fn glu_get_byte(vm: &Thread, index: VmIndex, out: &mut u8) -> Error {
183 get_value(vm, index, out)
184}
185
186#[no_mangle]
187pub extern "C" fn glu_get_int(vm: &Thread, index: VmIndex, out: &mut VmInt) -> Error {
188 get_value(vm, index, out)
189}
190
191#[no_mangle]
192pub extern "C" fn glu_get_float(vm: &Thread, index: VmIndex, out: &mut f64) -> Error {
193 get_value(vm, index, out)
194}
195
196#[no_mangle]
197pub extern "C" fn glu_get_bool(vm: &Thread, index: VmIndex, out: &mut i8) -> Error {
198 let mut b = false;
199 let err = get_value(vm, index, &mut b);
200 if err == Error::Ok {
201 *out = b as i8;
202 }
203 err
204}
205
206#[no_mangle]
209pub unsafe extern "C" fn glu_get_string(
210 vm: &Thread,
211 index: VmIndex,
212 out: &mut *const u8,
213 out_len: &mut usize,
214) -> Error {
215 let mut context = vm.context();
216 let stack = context.stack_frame::<stack::State>();
217 match stack
218 .get_variant(index)
219 .map(|value| <&str>::from_value(vm, value))
220 {
221 Some(value) => {
222 *out = &*value.as_ptr();
223 *out_len = value.len();
224 Error::Ok
225 }
226 None => Error::Unknown,
227 }
228}
229
230#[cfg(not(target_arch = "wasm32"))]
231#[no_mangle]
232pub extern "C" fn glu_get_light_userdata(
233 vm: &Thread,
234 index: VmIndex,
235 out: &mut *mut libc::c_void,
236) -> Error {
237 let mut userdata = 0usize;
238 let err = get_value(vm, index, &mut userdata);
239 if err == Error::Ok {
240 *out = userdata as *mut libc::c_void;
241 }
242 err
243}
244
245fn get_value<T>(vm: &Thread, index: VmIndex, out: &mut T) -> Error
246where
247 T: for<'vm, 'value> Getable<'vm, 'value>,
248{
249 let mut context = vm.context();
250 let stack = context.stack_frame::<stack::State>();
251 match stack
252 .get_variant(index)
253 .map(|value| T::from_value(vm, value))
254 {
255 Some(value) => {
256 *out = value;
257 Error::Ok
258 }
259 None => Error::Unknown,
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 use gluon::vm::thread::{Status, Thread};
268
269 use std::{ptr, slice, str};
270
271 #[test]
272 fn push_pop() {
273 unsafe {
274 let vm = &*glu_new_vm();
275
276 glu_push_int(vm, 123);
277 glu_push_float(vm, 3.14);
278 let s = "test";
279 glu_push_string(vm, &s.as_bytes()[0], s.len());
280 glu_push_bool(vm, 1);
281 glu_push_byte(vm, 128);
282
283 let mut int = 0;
284 assert_eq!(glu_get_int(vm, 0, &mut int), Error::Ok);
285 assert_eq!(int, 123);
286
287 let mut float = 0.0;
288 assert_eq!(glu_get_float(vm, 1, &mut float), Error::Ok);
289 assert_eq!(float, 3.14);
290
291 let mut string_ptr = ptr::null();
292 let mut string_len = 0;
293 assert_eq!(
294 glu_get_string(vm, 2, &mut string_ptr, &mut string_len),
295 Error::Ok
296 );
297 assert_eq!(
298 str::from_utf8(slice::from_raw_parts(string_ptr, string_len)),
299 Ok("test")
300 );
301
302 let mut b = 0;
303 assert_eq!(glu_get_bool(vm, 3, &mut b), Error::Ok);
304 assert_eq!(b, 1);
305
306 let mut b = 0;
307 assert_eq!(glu_get_byte(vm, 4, &mut b), Error::Ok);
308 assert_eq!(b, 128);
309
310 assert_eq!(glu_len(vm), 5);
311
312 glu_pop(vm, 4);
313
314 glu_free_vm(vm);
315 }
316 }
317
318 #[cfg(not(target_arch = "wasm32"))]
319 #[test]
320 fn push_userdata() {
321 unsafe {
322 let vm = &*glu_new_vm();
323
324 let x = 123i32;
325 glu_push_light_userdata(vm, &x as *const i32 as *mut ::libc::c_void);
326 let mut p = ptr::null_mut();
327 assert_eq!(glu_get_light_userdata(vm, 0, &mut p), Error::Ok);
328 assert_eq!(*(p as *const i32), 123);
329 }
330 }
331
332 #[test]
333 fn call_function() {
334 extern "C" fn mult(vm: &Thread) -> Status {
335 let mut l = 0.0;
336 assert_eq!(glu_get_float(vm, 0, &mut l), Error::Ok);
337 let mut r = 0.0;
338 assert_eq!(glu_get_float(vm, 1, &mut r), Error::Ok);
339 glu_push_float(vm, l * r);
340 Status::Ok
341 }
342
343 unsafe {
344 let vm = &*glu_new_vm();
345 let name = "mult";
346 glu_push_function(vm, &name.as_bytes()[0], name.len(), mult, 2);
347 glu_push_float(vm, 12.0);
348 glu_push_float(vm, 3.0);
349
350 assert_eq!(glu_call_function(vm, 2), Error::Ok);
351 let mut result = 0.0;
352 assert_eq!(glu_get_float(vm, 0, &mut result), Error::Ok);
353 assert_eq!(result, 36.0);
354
355 glu_free_vm(vm);
356 }
357 }
358}