shape_vm/executor/vm_impl/
modules.rs1use super::super::*;
2
3impl VirtualMachine {
4 pub fn register_stdlib_module(&mut self, module: shape_runtime::module_exports::ModuleExports) {
8 self.register_extension(module);
9 }
10
11 pub fn register_extension(&mut self, module: shape_runtime::module_exports::ModuleExports) {
15 for (type_name, methods) in &module.method_intrinsics {
17 let entry = self.extension_methods.entry(type_name.clone()).or_default();
18 for (method_name, func) in methods {
19 entry.insert(method_name.clone(), func.clone());
20 }
21 }
22 let module_type_name = format!("__mod_{}", module.name);
25 let module_entry = self.extension_methods.entry(module_type_name).or_default();
26 for (export_name, func) in &module.exports {
27 module_entry.insert(export_name.clone(), func.clone());
28 }
29 for (export_name, async_fn) in &module.async_exports {
30 let async_fn = async_fn.clone();
31 let wrapped: shape_runtime::module_exports::ModuleFn = Arc::new(
32 move |args: &[ValueWord], _ctx: &shape_runtime::module_exports::ModuleContext| {
33 let future = async_fn(args);
34 tokio::task::block_in_place(|| {
35 tokio::runtime::Handle::current().block_on(future)
36 })
37 },
38 );
39 module_entry.insert(export_name.clone(), wrapped);
40 }
41 self.module_registry.register(module);
42 }
43
44 pub fn register_module_fn(&mut self, f: shape_runtime::module_exports::ModuleFn) -> usize {
46 let id = self.module_fn_table.len();
47 self.module_fn_table.push(f);
48 id
49 }
50
51 pub(crate) fn invoke_module_fn(
57 &mut self,
58 module_fn: &shape_runtime::module_exports::ModuleFn,
59 args: &[ValueWord],
60 ) -> Result<ValueWord, VMError> {
61 unsafe {
66 let vm_ptr = self as *mut VirtualMachine;
67
68 let invoker =
69 |callable: &ValueWord, call_args: &[ValueWord]| -> Result<ValueWord, String> {
70 (*vm_ptr)
71 .call_value_immediate_nb(callable, call_args, None)
72 .map_err(|e| e.to_string())
73 };
74
75 unsafe fn vm_callable_invoker(
76 ctx: *mut std::ffi::c_void,
77 callable: &ValueWord,
78 args: &[ValueWord],
79 ) -> Result<ValueWord, String> {
80 let vm = unsafe { &mut *(ctx as *mut VirtualMachine) };
81 vm.call_value_immediate_nb(callable, args, None)
82 .map_err(|err| err.to_string())
83 }
84
85 let vm_snapshot = (*vm_ptr).capture_vm_state();
89
90 let ctx = shape_runtime::module_exports::ModuleContext {
91 schemas: &(*vm_ptr).program.type_schema_registry,
92 invoke_callable: Some(&invoker),
93 raw_invoker: Some(shape_runtime::module_exports::RawCallableInvoker {
94 ctx: vm_ptr as *mut std::ffi::c_void,
95 invoke: vm_callable_invoker,
96 }),
97 function_hashes: if (*vm_ptr).function_hash_raw.is_empty() {
98 None
99 } else {
100 Some(&(*vm_ptr).function_hash_raw)
101 },
102 vm_state: Some(&vm_snapshot),
103 granted_permissions: None,
104 scope_constraints: None,
105 set_pending_resume: Some(&|snapshot| {
106 (*vm_ptr).pending_resume = Some(snapshot);
109 }),
110 set_pending_frame_resume: Some(&|ip_offset, locals| {
111 (*vm_ptr).pending_frame_resume = Some(FrameResumeData { ip_offset, locals });
114 }),
115 };
116
117 let result = module_fn(args, &ctx).map_err(VMError::RuntimeError);
118
119 if (*vm_ptr).pending_resume.is_some() {
122 return Err(VMError::ResumeRequested);
123 }
124
125 result
126 }
127 }
128
129 pub fn populate_module_objects(&mut self) {
133 let module_data: Vec<(
135 String,
136 Vec<(String, shape_runtime::module_exports::ModuleFn)>,
137 Vec<(String, shape_runtime::module_exports::AsyncModuleFn)>,
138 Vec<String>,
139 )> = self
140 .module_registry
141 .module_names()
142 .iter()
143 .filter_map(|name| {
144 let module = self.module_registry.get(name)?;
145 let sync_exports: Vec<_> = module
146 .exports
147 .iter()
148 .map(|(k, v)| (k.clone(), v.clone()))
149 .collect();
150 let async_exports: Vec<_> = module
151 .async_exports
152 .iter()
153 .map(|(k, v)| (k.clone(), v.clone()))
154 .collect();
155 let mut source_exports = Vec::new();
156 for artifact in &module.module_artifacts {
157 if artifact.module_path != *name {
158 continue;
159 }
160 let Some(source) = artifact.source.as_deref() else {
161 continue;
162 };
163 if let Ok(exports) =
164 shape_runtime::module_loader::collect_exported_function_names_from_source(
165 &artifact.module_path,
166 source,
167 )
168 {
169 source_exports.extend(exports);
170 }
171 }
172 source_exports.sort();
173 source_exports.dedup();
174 Some((
175 name.to_string(),
176 sync_exports,
177 async_exports,
178 source_exports,
179 ))
180 })
181 .collect();
182
183 for (module_name, sync_exports, async_exports, source_exports) in module_data {
184 let binding_idx = self
186 .program
187 .module_binding_names
188 .iter()
189 .position(|n| n == &module_name);
190
191 if let Some(idx) = binding_idx {
192 let mut obj = HashMap::new();
193
194 for (export_name, module_fn) in sync_exports {
196 let fn_id = self.register_module_fn(module_fn);
197 obj.insert(export_name, ValueWord::from_module_function(fn_id as u32));
198 }
199
200 for (export_name, async_fn) in async_exports {
202 let wrapped: shape_runtime::module_exports::ModuleFn =
203 Arc::new(move |args: &[ValueWord], _ctx: &shape_runtime::module_exports::ModuleContext| {
204 let future = async_fn(args);
205 tokio::task::block_in_place(|| {
206 tokio::runtime::Handle::current().block_on(future)
207 })
208 });
209 let fn_id = self.register_module_fn(wrapped);
210 obj.insert(export_name, ValueWord::from_module_function(fn_id as u32));
211 }
212
213 for export_name in source_exports {
216 if obj.contains_key(&export_name) {
217 continue;
218 }
219 if let Some(&func_id) = self.function_name_index.get(&export_name) {
220 obj.insert(export_name, ValueWord::from_function(func_id));
221 }
222 }
223
224 let cache_name = format!("__mod_{}", module_name);
226 let schema_id = if let Some(schema) = self.lookup_schema_by_name(&cache_name) {
227 schema.id
228 } else {
229 continue;
232 };
233
234 let Some(schema) = self.lookup_schema(schema_id) else {
236 continue;
237 };
238 let field_order: Vec<String> =
239 schema.fields.iter().map(|f| f.name.clone()).collect();
240
241 let mut slots = Vec::with_capacity(field_order.len());
242 let mut heap_mask: u64 = 0;
243 for (i, field_name) in field_order.iter().enumerate() {
244 let nb_val = obj.get(field_name).cloned().unwrap_or_else(ValueWord::none);
245 let (slot, is_heap) =
246 crate::executor::objects::object_creation::nb_to_slot_with_field_type(
247 &nb_val, None,
248 );
249 slots.push(slot);
250 if is_heap {
251 heap_mask |= 1u64 << i;
252 }
253 }
254
255 let typed_nb = ValueWord::from_heap_value(HeapValue::TypedObject {
256 schema_id: schema_id as u64,
257 slots: slots.into_boxed_slice(),
258 heap_mask,
259 });
260 if idx >= self.module_bindings.len() {
261 self.module_bindings.resize_with(idx + 1, ValueWord::none);
262 }
263 self.module_bindings[idx] = typed_nb;
265 }
266 }
267 }
268}