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 crate::executor::builtins::remote_builtins::set_current_program(&(*vm_ptr).program);
119 let result = module_fn(args, &ctx).map_err(VMError::RuntimeError);
120 crate::executor::builtins::remote_builtins::clear_current_program();
121
122 if (*vm_ptr).pending_resume.is_some() {
125 return Err(VMError::ResumeRequested);
126 }
127
128 result
129 }
130 }
131
132 pub fn populate_module_objects(&mut self) {
136 let module_data: Vec<(
138 String,
139 Vec<(String, shape_runtime::module_exports::ModuleFn)>,
140 Vec<(String, shape_runtime::module_exports::AsyncModuleFn)>,
141 Vec<String>,
142 )> = self
143 .module_registry
144 .module_names()
145 .iter()
146 .filter_map(|name| {
147 let module = self.module_registry.get(name)?;
148 let sync_exports: Vec<_> = module
149 .exports
150 .iter()
151 .map(|(k, v)| (k.clone(), v.clone()))
152 .collect();
153 let async_exports: Vec<_> = module
154 .async_exports
155 .iter()
156 .map(|(k, v)| (k.clone(), v.clone()))
157 .collect();
158 let mut source_exports = Vec::new();
159 for artifact in &module.module_artifacts {
160 if artifact.module_path != *name {
161 continue;
162 }
163 let Some(source) = artifact.source.as_deref() else {
164 continue;
165 };
166 if let Ok(exports) =
167 shape_runtime::module_loader::collect_exported_function_names_from_source(
168 &artifact.module_path,
169 source,
170 )
171 {
172 source_exports.extend(exports);
173 }
174 }
175 source_exports.sort();
176 source_exports.dedup();
177 Some((
178 name.to_string(),
179 sync_exports,
180 async_exports,
181 source_exports,
182 ))
183 })
184 .collect();
185
186 for (module_name, sync_exports, async_exports, source_exports) in module_data {
187 let binding_idx = self
189 .program
190 .module_binding_names
191 .iter()
192 .position(|n| n == &module_name);
193
194 if let Some(idx) = binding_idx {
195 let mut obj = HashMap::new();
196
197 for (export_name, module_fn) in sync_exports {
199 let fn_id = self.register_module_fn(module_fn);
200 obj.insert(export_name, ValueWord::from_module_function(fn_id as u32));
201 }
202
203 for (export_name, async_fn) in async_exports {
205 let wrapped: shape_runtime::module_exports::ModuleFn =
206 Arc::new(move |args: &[ValueWord], _ctx: &shape_runtime::module_exports::ModuleContext| {
207 let future = async_fn(args);
208 tokio::task::block_in_place(|| {
209 tokio::runtime::Handle::current().block_on(future)
210 })
211 });
212 let fn_id = self.register_module_fn(wrapped);
213 obj.insert(export_name, ValueWord::from_module_function(fn_id as u32));
214 }
215
216 for export_name in source_exports {
219 if obj.contains_key(&export_name) {
220 continue;
221 }
222 if let Some(&func_id) = self.function_name_index.get(&export_name) {
223 obj.insert(export_name, ValueWord::from_function(func_id));
224 }
225 }
226
227 let cache_name = format!("__mod_{}", module_name);
229 let schema_id = if let Some(schema) = self.lookup_schema_by_name(&cache_name) {
230 schema.id
231 } else {
232 continue;
235 };
236
237 let Some(schema) = self.lookup_schema(schema_id) else {
239 continue;
240 };
241 let field_order: Vec<String> =
242 schema.fields.iter().map(|f| f.name.clone()).collect();
243
244 let mut slots = Vec::with_capacity(field_order.len());
245 let mut heap_mask: u64 = 0;
246 for (i, field_name) in field_order.iter().enumerate() {
247 let nb_val = obj.get(field_name).cloned().unwrap_or_else(ValueWord::none);
248 let (slot, is_heap) =
249 crate::executor::objects::object_creation::nb_to_slot_with_field_type(
250 &nb_val, None,
251 );
252 slots.push(slot);
253 if is_heap {
254 heap_mask |= 1u64 << i;
255 }
256 }
257
258 let typed_nb = ValueWord::from_heap_value(HeapValue::TypedObject {
259 schema_id: schema_id as u64,
260 slots: slots.into_boxed_slice(),
261 heap_mask,
262 });
263 if idx >= self.module_bindings.len() {
264 self.module_bindings.resize_with(idx + 1, ValueWord::none);
265 }
266 self.module_bindings[idx] = typed_nb;
268 }
269 }
270 }
271}