solverforge_core/wasm/
host_functions.rs

1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3
4/// WASM value types
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6pub enum WasmType {
7    /// 32-bit integer
8    I32,
9    /// 64-bit integer
10    I64,
11    /// 32-bit float
12    F32,
13    /// 64-bit float
14    F64,
15    /// Pointer (i32 in WASM)
16    Ptr,
17    /// No return value
18    Void,
19}
20
21/// Host function definition
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct HostFunctionDef {
24    /// Function name (as it appears in imports)
25    pub name: String,
26    /// Parameter types
27    pub params: Vec<WasmType>,
28    /// Return type
29    pub return_type: WasmType,
30}
31
32impl HostFunctionDef {
33    pub fn new(name: impl Into<String>, params: Vec<WasmType>, return_type: WasmType) -> Self {
34        Self {
35            name: name.into(),
36            params,
37            return_type,
38        }
39    }
40}
41
42/// Registry of host functions available for import into WASM modules
43/// Uses IndexMap to preserve insertion order for deterministic function indices
44#[derive(Debug, Clone, Default)]
45pub struct HostFunctionRegistry {
46    functions: IndexMap<String, HostFunctionDef>,
47}
48
49impl HostFunctionRegistry {
50    /// Create a new registry with standard SolverForge host functions
51    pub fn with_standard_functions() -> Self {
52        let mut registry = Self::default();
53
54        // String operations
55        registry.register(HostFunctionDef::new(
56            "hstringEquals",
57            vec![WasmType::Ptr, WasmType::Ptr],
58            WasmType::I32,
59        ));
60
61        // List operations
62        registry.register(HostFunctionDef::new(
63            "hlistContainsString",
64            vec![WasmType::Ptr, WasmType::Ptr],
65            WasmType::I32,
66        ));
67        registry.register(HostFunctionDef::new("hnewList", vec![], WasmType::Ptr));
68
69        registry.register(HostFunctionDef::new(
70            "hgetItem",
71            vec![WasmType::Ptr, WasmType::I32],
72            WasmType::Ptr,
73        ));
74
75        registry.register(HostFunctionDef::new(
76            "hsize",
77            vec![WasmType::Ptr],
78            WasmType::I32,
79        ));
80
81        registry.register(HostFunctionDef::new(
82            "happend",
83            vec![WasmType::Ptr, WasmType::Ptr],
84            WasmType::Void,
85        ));
86
87        registry.register(HostFunctionDef::new(
88            "hsetItem",
89            vec![WasmType::Ptr, WasmType::I32, WasmType::Ptr],
90            WasmType::Void,
91        ));
92
93        registry.register(HostFunctionDef::new(
94            "hinsert",
95            vec![WasmType::Ptr, WasmType::I32, WasmType::Ptr],
96            WasmType::Void,
97        ));
98
99        registry.register(HostFunctionDef::new(
100            "hremove",
101            vec![WasmType::Ptr, WasmType::I32],
102            WasmType::Void,
103        ));
104
105        // Schedule operations
106        registry.register(HostFunctionDef::new(
107            "hparseSchedule",
108            vec![WasmType::I32, WasmType::Ptr], // (length: i32, json: ptr) -> ptr
109            WasmType::Ptr,
110        ));
111
112        registry.register(HostFunctionDef::new(
113            "hscheduleString",
114            vec![WasmType::Ptr],
115            WasmType::Ptr,
116        ));
117
118        // Math operations
119        registry.register(HostFunctionDef::new(
120            "hround",
121            vec![WasmType::F32],
122            WasmType::I32,
123        ));
124
125        // Trigonometric functions (f64 -> f64)
126        registry.register(HostFunctionDef::new(
127            "hsin",
128            vec![WasmType::F64],
129            WasmType::F64,
130        ));
131        registry.register(HostFunctionDef::new(
132            "hcos",
133            vec![WasmType::F64],
134            WasmType::F64,
135        ));
136        registry.register(HostFunctionDef::new(
137            "hasin",
138            vec![WasmType::F64],
139            WasmType::F64,
140        ));
141        registry.register(HostFunctionDef::new(
142            "hacos",
143            vec![WasmType::F64],
144            WasmType::F64,
145        ));
146        registry.register(HostFunctionDef::new(
147            "hatan",
148            vec![WasmType::F64],
149            WasmType::F64,
150        ));
151        registry.register(HostFunctionDef::new(
152            "hatan2",
153            vec![WasmType::F64, WasmType::F64],
154            WasmType::F64,
155        ));
156
157        // Pre-computed method lookup
158        // These functions look up pre-computed values for methods that couldn't be inlined.
159        // The method_id is a hash of "ClassName.method_name".
160        // The object_ptr is the pointer to the receiver object.
161        // Additional args are pointers to argument objects.
162
163        // hprecomputed0: method with no arguments (besides self)
164        // (method_id: i32, object_ptr: ptr) -> i32
165        registry.register(HostFunctionDef::new(
166            "hprecomputed0",
167            vec![WasmType::I32, WasmType::Ptr],
168            WasmType::I32,
169        ));
170
171        // hprecomputed1: method with 1 argument (besides self)
172        // (method_id: i32, object_ptr: ptr, arg1_ptr: ptr) -> i32
173        registry.register(HostFunctionDef::new(
174            "hprecomputed1",
175            vec![WasmType::I32, WasmType::Ptr, WasmType::Ptr],
176            WasmType::I32,
177        ));
178
179        // hprecomputed2: method with 2 arguments (besides self)
180        // (method_id: i32, object_ptr: ptr, arg1_ptr: ptr, arg2_ptr: ptr) -> i32
181        registry.register(HostFunctionDef::new(
182            "hprecomputed2",
183            vec![WasmType::I32, WasmType::Ptr, WasmType::Ptr, WasmType::Ptr],
184            WasmType::I32,
185        ));
186
187        registry
188    }
189
190    /// Create an empty registry
191    pub fn new() -> Self {
192        Self::default()
193    }
194
195    /// Register a host function
196    pub fn register(&mut self, def: HostFunctionDef) {
197        self.functions.insert(def.name.clone(), def);
198    }
199
200    /// Look up a host function by name
201    pub fn lookup(&self, name: &str) -> Option<&HostFunctionDef> {
202        self.functions.get(name)
203    }
204
205    /// Get all registered function names
206    pub fn function_names(&self) -> Vec<&str> {
207        self.functions.keys().map(|s| s.as_str()).collect()
208    }
209
210    /// Get the number of registered functions
211    pub fn len(&self) -> usize {
212        self.functions.len()
213    }
214
215    /// Check if the registry is empty
216    pub fn is_empty(&self) -> bool {
217        self.functions.is_empty()
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    #[test]
226    fn test_empty_registry() {
227        let registry = HostFunctionRegistry::new();
228        assert_eq!(registry.len(), 0);
229        assert!(registry.is_empty());
230    }
231
232    #[test]
233    fn test_register_function() {
234        let mut registry = HostFunctionRegistry::new();
235        let func = HostFunctionDef::new(
236            "test_func",
237            vec![WasmType::I32, WasmType::Ptr],
238            WasmType::I32,
239        );
240
241        registry.register(func.clone());
242        assert_eq!(registry.len(), 1);
243
244        let looked_up = registry.lookup("test_func");
245        assert!(looked_up.is_some());
246        assert_eq!(looked_up.unwrap(), &func);
247    }
248
249    #[test]
250    fn test_lookup_nonexistent() {
251        let registry = HostFunctionRegistry::new();
252        assert!(registry.lookup("nonexistent").is_none());
253    }
254
255    #[test]
256    fn test_standard_functions() {
257        let registry = HostFunctionRegistry::with_standard_functions();
258
259        // Should have the standard set of functions
260        assert!(registry.len() > 0);
261
262        // Check for key functions
263        assert!(registry.lookup("hstringEquals").is_some());
264        assert!(registry.lookup("hnewList").is_some());
265        assert!(registry.lookup("hgetItem").is_some());
266        assert!(registry.lookup("hsize").is_some());
267        assert!(registry.lookup("happend").is_some());
268        assert!(registry.lookup("hparseSchedule").is_some());
269        assert!(registry.lookup("hscheduleString").is_some());
270        assert!(registry.lookup("hround").is_some());
271    }
272
273    #[test]
274    fn test_hstring_equals_signature() {
275        let registry = HostFunctionRegistry::with_standard_functions();
276        let func = registry.lookup("hstringEquals").unwrap();
277
278        assert_eq!(func.name, "hstringEquals");
279        assert_eq!(func.params, vec![WasmType::Ptr, WasmType::Ptr]);
280        assert_eq!(func.return_type, WasmType::I32);
281    }
282
283    #[test]
284    fn test_function_names() {
285        let mut registry = HostFunctionRegistry::new();
286        registry.register(HostFunctionDef::new("func1", vec![], WasmType::Void));
287        registry.register(HostFunctionDef::new("func2", vec![], WasmType::Void));
288
289        let names = registry.function_names();
290        assert_eq!(names.len(), 2);
291        assert!(names.contains(&"func1"));
292        assert!(names.contains(&"func2"));
293    }
294
295    #[test]
296    fn test_wasm_type_serialization() {
297        let wasm_type = WasmType::I32;
298        let json = serde_json::to_string(&wasm_type).unwrap();
299        let deserialized: WasmType = serde_json::from_str(&json).unwrap();
300        assert_eq!(wasm_type, deserialized);
301    }
302}