go_vm/
ffi.rs

1// Copyright 2022 The Goscript Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5use crate::bytecode::*;
6use crate::dispatcher::ArrCaller;
7use crate::gc::GcContainer;
8use crate::stack::Stack;
9use crate::value::*;
10use crate::value::{GosValue, RuntimeResult};
11#[cfg(feature = "async")]
12use futures_lite::future::Future;
13use go_parser::Map;
14use std::cell::Ref;
15#[cfg(feature = "async")]
16use std::pin::Pin;
17use std::rc::Rc;
18
19pub struct FfiCtx<'a> {
20    pub func_name: &'a str,
21    pub vm_objs: &'a VMObjects,
22    pub user_data: Option<usize>,
23    pub stack: &'a mut Stack,
24    pub gcc: &'a GcContainer,
25    pub(crate) array_slice_caller: &'a ArrCaller,
26}
27
28impl<'a> FfiCtx<'a> {
29    #[inline]
30    pub fn new_nil(t: ValueType) -> GosValue {
31        GosValue::new_nil(t)
32    }
33
34    #[inline]
35    pub fn new_nil_slice(t_elem: ValueType) -> GosValue {
36        GosValue::new_nil_slice(t_elem)
37    }
38
39    #[inline]
40    pub fn new_uint_ptr(u: usize) -> GosValue {
41        GosValue::new_uint_ptr(u)
42    }
43
44    #[inline]
45    pub fn new_complex64(r: f32, i: f32) -> GosValue {
46        GosValue::new_complex64(r.into(), i.into())
47    }
48
49    #[inline]
50    pub fn new_function(f: FunctionKey) -> GosValue {
51        GosValue::new_function(f)
52    }
53
54    #[inline]
55    pub fn new_package(p: PackageKey) -> GosValue {
56        GosValue::new_package(p)
57    }
58
59    #[inline]
60    pub fn new_metadata(m: Meta) -> GosValue {
61        GosValue::new_metadata(m)
62    }
63
64    #[inline]
65    pub fn new_complex128(r: f64, i: f64) -> GosValue {
66        GosValue::new_complex128(r.into(), i.into())
67    }
68
69    #[inline]
70    pub fn new_string(s: &str) -> GosValue {
71        GosValue::with_str(s)
72    }
73
74    #[inline]
75    pub fn new_unsafe_ptr(p: Rc<dyn UnsafePtr>) -> GosValue {
76        GosValue::new_unsafe_ptr(p)
77    }
78
79    #[inline]
80    pub fn new_struct(&self, fields: Vec<GosValue>) -> GosValue {
81        GosValue::new_struct(StructObj::new(fields), self.gcc)
82    }
83
84    #[inline]
85    pub fn new_array(&self, member: Vec<GosValue>, t_elem: ValueType) -> GosValue {
86        GosValue::array_with_data(member, self.array_slice_caller.get(t_elem), self.gcc)
87    }
88
89    #[inline]
90    pub fn new_primitive_array<T>(&self, member: Vec<T>, t_elem: ValueType) -> GosValue
91    where
92        T: CellData,
93    {
94        let buf: Vec<CellElem<T>> = unsafe { std::mem::transmute(member) };
95        GosValue::new_non_gc_array(ArrayObj::with_raw_data(buf), t_elem)
96    }
97
98    #[inline]
99    pub fn new_map(&self, m: Map<GosValue, GosValue>) -> GosValue {
100        GosValue::map_with_data(m, self.gcc)
101    }
102
103    #[inline]
104    pub fn new_pointer(pointee: GosValue) -> GosValue {
105        let pobj = PointerObj::UpVal(UpValue::new_closed(pointee));
106        GosValue::new_pointer(pobj)
107    }
108
109    /// Create a new interface value with the underlying value,
110    /// and optionally the meta of the interface and the value.
111    ///
112    /// -- Note --
113    /// if you pass `None` to metas, the returned interface won't behave like
114    /// a normal interface. you cannot cast it or call it's methods.
115    ///
116    /// # Arguments
117    ///
118    /// * `underlying` - The underlying value
119    /// * `metas` - (Interface meta, value meta)
120    #[inline]
121    pub fn new_interface(&self, underlying: GosValue, metas: Option<(&Meta, Meta)>) -> GosValue {
122        GosValue::new_interface(InterfaceObj::with_value(
123            underlying,
124            metas.map(|(iface, value_meta)| {
125                let (m, b) = iface.bind_with_iface(value_meta, &self.vm_objs.metas);
126                (m, b.into_iter().map(|x| x.into()).collect())
127            }),
128        ))
129    }
130
131    #[inline]
132    pub fn new_empty_interface(&self, underlying: GosValue, meta: Meta) -> GosValue {
133        GosValue::new_interface(InterfaceObj::with_value(underlying, Some((meta, vec![]))))
134    }
135
136    #[inline]
137    pub fn deref_pointer(&self, ptr: &GosValue) -> RuntimeResult<GosValue> {
138        ptr.as_non_nil_pointer()?
139            .deref(&self.stack, &self.vm_objs.packages)
140    }
141
142    #[inline]
143    pub fn zero_val(&self, m: &Meta) -> GosValue {
144        m.zero(&self.vm_objs.metas, self.gcc)
145    }
146
147    #[inline]
148    pub fn slice_as_rust_slice<T>(val: &GosValue) -> RuntimeResult<Ref<[T]>>
149    where
150        T: Element,
151    {
152        Ok(val.as_non_nil_slice::<T>()?.0.as_rust_slice())
153    }
154
155    #[inline]
156    pub fn slice_as_primitive_slice<'b, C, D>(val: &'b GosValue) -> RuntimeResult<Ref<[D]>>
157    where
158        C: CellData + 'b,
159        D: Copy,
160    {
161        Ok(val.as_non_nil_slice::<CellElem<C>>()?.0.as_raw_slice::<D>())
162    }
163
164    #[inline]
165    pub fn array_as_rust_slice<T>(val: &GosValue) -> Ref<[T]>
166    where
167        T: Element,
168    {
169        val.as_array::<T>().0.as_rust_slice()
170    }
171
172    #[inline]
173    pub fn array_as_primitive_slice<'b, C, D>(val: &'b GosValue) -> Ref<[D]>
174    where
175        C: CellData + 'b,
176        D: Copy,
177    {
178        val.as_array::<CellElem<C>>().0.as_raw_slice::<D>()
179    }
180}
181
182/// A FFI Object implemented in Rust for Goscript to call
183pub trait Ffi {
184    fn call(&self, ctx: &mut FfiCtx, params: Vec<GosValue>) -> RuntimeResult<Vec<GosValue>>;
185
186    #[cfg(feature = "async")]
187    fn async_call(
188        &self,
189        ctx: &mut FfiCtx,
190        params: Vec<GosValue>,
191    ) -> Pin<Box<dyn Future<Output = RuntimeResult<Vec<GosValue>>> + '_>>;
192}
193
194impl std::fmt::Debug for dyn Ffi {
195    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
196        write!(f, "{}", "ffi")
197    }
198}
199
200pub struct FfiFactory {
201    registry: Map<&'static str, Rc<dyn Ffi>>,
202    /// Down-casting only works for 'static types,
203    /// so we just use the good old pointers
204    user_data: Option<usize>,
205}
206
207impl FfiFactory {
208    pub fn new() -> FfiFactory {
209        FfiFactory {
210            registry: Map::new(),
211            user_data: None,
212        }
213    }
214
215    pub fn with_user_data(ptr: usize) -> FfiFactory {
216        FfiFactory {
217            registry: Map::new(),
218            user_data: Some(ptr),
219        }
220    }
221
222    pub fn register(&mut self, name: &'static str, proto: Rc<dyn Ffi>) {
223        assert!(self.registry.insert(name, proto).is_none());
224    }
225
226    pub(crate) fn user_data(&self) -> Option<usize> {
227        self.user_data
228    }
229
230    pub(crate) fn create(&self, name: &str) -> RuntimeResult<Rc<dyn Ffi>> {
231        match self.registry.get(name) {
232            Some(proto) => Ok(proto.clone()),
233            None => Err(format!("FFI named {} not found", name).into()),
234        }
235    }
236}
237
238impl std::fmt::Debug for FfiFactory {
239    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
240        write!(f, "FfiFactory")
241    }
242}
243
244/// Used by CodeGen, so that CodeGen can share the API provided by FFI
245pub struct CodeGenVMCtx {
246    vm_objs: VMObjects,
247    dummy_func_name: &'static str,
248    dummy_stack: Stack,
249    dummy_gcc: GcContainer,
250    caller: ArrCaller,
251}
252
253impl CodeGenVMCtx {
254    pub fn new(vm_objs: VMObjects) -> CodeGenVMCtx {
255        CodeGenVMCtx {
256            vm_objs,
257            dummy_func_name: "dummy_name",
258            dummy_stack: Stack::new(),
259            dummy_gcc: GcContainer::new(),
260            caller: ArrCaller::new(),
261        }
262    }
263
264    pub fn ffi_ctx(&mut self) -> FfiCtx {
265        FfiCtx {
266            func_name: self.dummy_func_name,
267            vm_objs: &self.vm_objs,
268            user_data: None,
269            stack: &mut self.dummy_stack,
270            gcc: &&self.dummy_gcc,
271            array_slice_caller: &self.caller,
272        }
273    }
274
275    pub fn objects(&self) -> &VMObjects {
276        &self.vm_objs
277    }
278
279    pub fn objects_mut(&mut self) -> &mut VMObjects {
280        &mut self.vm_objs
281    }
282
283    pub fn metas(&self) -> &MetadataObjs {
284        &self.vm_objs.metas
285    }
286
287    pub fn metas_mut(&mut self) -> &mut MetadataObjs {
288        &mut self.vm_objs.metas
289    }
290
291    pub fn functions(&self) -> &FunctionObjs {
292        &self.vm_objs.functions
293    }
294
295    pub fn functions_mut(&mut self) -> &mut FunctionObjs {
296        &mut self.vm_objs.functions
297    }
298
299    pub fn packages(&self) -> &PackageObjs {
300        &self.vm_objs.packages
301    }
302
303    pub fn packages_mut(&mut self) -> &mut PackageObjs {
304        &mut self.vm_objs.packages
305    }
306
307    pub fn prim_meta(&self) -> &PrimitiveMeta {
308        &self.vm_objs.prim_meta
309    }
310
311    pub fn gc_container(&self) -> &GcContainer {
312        &self.dummy_gcc
313    }
314
315    pub fn into_vmo(self) -> VMObjects {
316        self.vm_objs
317    }
318
319    pub fn function_with_meta(
320        &mut self,
321        package: Option<PackageKey>,
322        meta: Meta,
323        flag: FuncFlag,
324    ) -> GosValue {
325        let package = package.unwrap_or_else(|| PackageKey::null());
326        let val = FunctionObj::new(package, meta, &self.vm_objs.metas, &self.dummy_gcc, flag);
327        GosValue::new_function(self.vm_objs.functions.insert(val))
328    }
329
330    pub fn new_struct_meta(&mut self, fields: Fields) -> Meta {
331        Meta::new_struct(fields, &mut self.vm_objs)
332    }
333
334    #[inline]
335    pub fn new_closure_static(
336        func: FunctionKey,
337        up_ptrs: Option<&Vec<ValueDesc>>,
338        meta: Meta,
339    ) -> GosValue {
340        GosValue::new_closure_static(func, up_ptrs, meta)
341    }
342}