shape_vm/executor/vm_impl/
program.rs1use super::super::*;
2
3impl VirtualMachine {
4 pub fn load_program(&mut self, program: BytecodeProgram) {
6 if let Some(ref ca_program) = program.content_addressed {
9 let linked = crate::linker::link(ca_program).unwrap_or_else(|e| {
10 panic!(
11 "content-addressed linker failed ({} function blobs): {}",
12 ca_program.function_store.len(),
13 e
14 )
15 });
16 self.load_linked_program(linked);
17 return;
18 }
19
20 self.program = program;
21 if shape_runtime::type_schema::builtin_schemas::resolve_builtin_schema_ids(
22 &self.program.type_schema_registry,
23 )
24 .is_none()
25 {
26 let (stdlib_registry, _) =
30 shape_runtime::type_schema::TypeSchemaRegistry::with_stdlib_types_and_builtin_ids();
31 self.program.type_schema_registry.merge(stdlib_registry);
32 }
33 self.builtin_schemas =
34 shape_runtime::type_schema::builtin_schemas::resolve_builtin_schema_ids(
35 &self.program.type_schema_registry,
36 )
37 .expect(
38 "compiled program is missing builtin schemas (__AnyError, __TraceFrame, ...); \
39 schema registry must include static builtin schemas",
40 );
41 let max_program_id = self
46 .program
47 .type_schema_registry
48 .max_schema_id()
49 .unwrap_or(0);
50 shape_runtime::type_schema::current_registry().ensure_next_id_above(max_program_id);
51 self.rebuild_function_name_index();
52 self.populate_content_addressed_metadata();
53 self.program_entry_ip = 0;
54 self.module_init_done = false;
55 self.feedback_vectors
56 .resize_with(self.program.functions.len(), || None);
57 self.reset();
58
59 #[cfg(debug_assertions)]
78 {
79 if let Err(errors) = crate::bytecode::verifier::verify_trusted_opcodes(&self.program) {
80 eprintln!(
81 "Bytecode verification warning: {} violation(s) found",
82 errors.len()
83 );
84 for e in &errors {
85 eprintln!(" - {}", e);
86 }
87 }
88 if let Err(errors) =
89 crate::bytecode::verifier::verify_v2_typed_opcodes(&self.program)
90 {
91 eprintln!(
92 "V2 bytecode verification warning: {} violation(s) found",
93 errors.len()
94 );
95 for e in &errors {
96 eprintln!(" - {}", e);
97 }
98 }
99 }
100
101 #[cfg(not(debug_assertions))]
102 {
103 if let Err(errors) = crate::bytecode::verifier::verify_trusted_opcodes(&self.program) {
104 eprintln!(
105 "Bytecode verification failed: {} violation(s)",
106 errors.len()
107 );
108 for e in &errors {
109 eprintln!(" - {}", e);
110 }
111 }
112 if let Err(errors) =
113 crate::bytecode::verifier::verify_v2_typed_opcodes(&self.program)
114 {
115 eprintln!(
116 "V2 bytecode verification failed: {} violation(s)",
117 errors.len()
118 );
119 for e in &errors {
120 eprintln!(" - {}", e);
121 }
122 }
123 }
124 }
125
126 pub fn load_linked_program(&mut self, linked: crate::bytecode::LinkedProgram) {
133 let entry_function_id = linked
134 .hash_to_id
135 .get(&linked.entry)
136 .copied()
137 .or_else(|| linked.functions.iter().position(|f| f.name == "__main__"))
138 .unwrap_or(0);
139 let entry_ip = linked
140 .functions
141 .get(entry_function_id)
142 .map(|f| f.entry_point)
143 .unwrap_or(0);
144
145 let hashes: Vec<Option<FunctionHash>> = linked
147 .functions
148 .iter()
149 .map(|lf| {
150 if lf.blob_hash == FunctionHash::ZERO {
151 None
152 } else {
153 Some(lf.blob_hash)
154 }
155 })
156 .collect();
157 let entry_points: Vec<usize> = linked.functions.iter().map(|lf| lf.entry_point).collect();
158
159 let functions: Vec<crate::bytecode::Function> = linked
161 .functions
162 .iter()
163 .map(|lf| crate::bytecode::Function {
164 name: lf.name.clone(),
165 arity: lf.arity,
166 param_names: lf.param_names.clone(),
167 locals_count: lf.locals_count,
168 entry_point: lf.entry_point,
169 body_length: lf.body_length,
170 is_closure: lf.is_closure,
171 captures_count: lf.captures_count,
172 is_async: lf.is_async,
173 ref_params: lf.ref_params.clone(),
174 ref_mutates: lf.ref_mutates.clone(),
175 mutable_captures: lf.mutable_captures.clone(),
176 frame_descriptor: lf.frame_descriptor.clone(),
177 osr_entry_points: Vec::new(),
178 mir_data: None,
179 })
180 .collect();
181
182 let program = BytecodeProgram {
183 instructions: linked.instructions,
184 constants: linked.constants,
185 strings: linked.strings,
186 functions,
187 debug_info: linked.debug_info,
188 data_schema: linked.data_schema,
189 module_binding_names: linked.module_binding_names,
190 top_level_locals_count: linked.top_level_locals_count,
191 top_level_local_storage_hints: linked.top_level_local_storage_hints,
192 type_schema_registry: linked.type_schema_registry,
193 module_binding_storage_hints: linked.module_binding_storage_hints,
194 function_local_storage_hints: linked.function_local_storage_hints,
195 trait_method_symbols: linked.trait_method_symbols,
196 foreign_functions: linked.foreign_functions,
197 native_struct_layouts: linked.native_struct_layouts,
198 function_blob_hashes: entry_points
199 .iter()
200 .enumerate()
201 .map(|(idx, _)| hashes.get(idx).copied().flatten())
202 .collect(),
203 closure_function_layouts: linked.closure_function_layouts.clone(),
207 top_level_frame: linked.top_level_frame,
211 trait_vtables: linked.trait_vtables.clone(),
215 ..BytecodeProgram::default()
216 };
217
218 self.load_program(program);
220
221 self.function_hashes = hashes;
225 self.function_hash_raw = self
226 .function_hashes
227 .iter()
228 .map(|opt| opt.map(|fh| fh.0))
229 .collect();
230 self.function_id_by_hash.clear();
231 for (idx, maybe_hash) in self.function_hashes.iter().enumerate() {
232 if let Some(hash) = maybe_hash {
233 self.function_id_by_hash.entry(*hash).or_insert(idx as u16);
234 }
235 }
236 self.function_entry_points = entry_points;
237 self.program_entry_ip = entry_ip;
238 self.reset();
239 }
240
241 pub fn patch_function(
253 &mut self,
254 fn_id: u16,
255 new_blob: FunctionBlob,
256 ) -> Result<Option<FunctionHash>, String> {
257 let idx = fn_id as usize;
258
259 if idx >= self.program.functions.len() {
260 return Err(format!(
261 "patch_function: fn_id {} out of range (program has {} functions)",
262 fn_id,
263 self.program.functions.len()
264 ));
265 }
266
267 let old_hash = self.function_hashes.get(idx).copied().flatten();
269
270 let func = &mut self.program.functions[idx];
271 let old_entry = func.entry_point;
272
273 let next_entry = self
276 .program
277 .functions
278 .get(idx + 1)
279 .map(|f| f.entry_point)
280 .unwrap_or(self.program.instructions.len());
281
282 let old_len = next_entry - old_entry;
283 let new_len = new_blob.instructions.len();
284
285 self.program.instructions.splice(
287 old_entry..old_entry + old_len,
288 new_blob.instructions.iter().cloned(),
289 );
290
291 if new_len != old_len {
294 let delta = new_len as isize - old_len as isize;
295 for subsequent in self.program.functions.iter_mut().skip(idx + 1) {
296 subsequent.entry_point = (subsequent.entry_point as isize + delta) as usize;
297 }
298 for ep in self.function_entry_points.iter_mut().skip(idx + 1) {
300 *ep = (*ep as isize + delta) as usize;
301 }
302 }
303
304 let const_offset = self.program.constants.len();
308 let string_offset = self.program.strings.len();
309 self.program
310 .constants
311 .extend(new_blob.constants.iter().cloned());
312 self.program
313 .strings
314 .extend(new_blob.strings.iter().cloned());
315
316 let instr_slice = &mut self.program.instructions[old_entry..old_entry + new_len];
318 for instr in instr_slice.iter_mut() {
319 remap_operand(&mut instr.operand, const_offset, string_offset);
320 }
321
322 let func = &mut self.program.functions[idx];
324 func.name = new_blob.name;
325 func.arity = new_blob.arity;
326 func.param_names = new_blob.param_names;
327 func.locals_count = new_blob.locals_count;
328 func.is_closure = new_blob.is_closure;
329 func.captures_count = new_blob.captures_count;
330 func.is_async = new_blob.is_async;
331 func.ref_params = new_blob.ref_params;
332 func.ref_mutates = new_blob.ref_mutates;
333 func.mutable_captures = new_blob.mutable_captures;
334
335 let new_hash = new_blob.content_hash;
337 if idx < self.function_hashes.len() {
338 self.function_hashes[idx] = Some(new_hash);
339 }
340 if idx < self.function_hash_raw.len() {
341 self.function_hash_raw[idx] = Some(new_hash.0);
342 }
343 self.function_id_by_hash.entry(new_hash).or_insert(fn_id);
344
345 if idx < self.function_entry_points.len() {
347 self.function_entry_points[idx] = old_entry;
348 }
349
350 self.rebuild_function_name_index();
352
353 Ok(old_hash)
354 }
355
356 pub fn load_program_with_permissions(
362 &mut self,
363 program: crate::bytecode::Program,
364 granted: &shape_abi_v1::PermissionSet,
365 ) -> Result<(), PermissionError> {
366 let linked =
367 crate::linker::link(&program).map_err(|e| PermissionError::LinkError(e.to_string()))?;
368 if !linked.total_required_permissions.is_subset(granted) {
369 let missing = linked.total_required_permissions.difference(granted);
370 return Err(PermissionError::InsufficientPermissions {
371 required: linked.total_required_permissions.clone(),
372 granted: granted.clone(),
373 missing,
374 });
375 }
376 self.load_linked_program(linked);
377 Ok(())
378 }
379
380 pub fn load_linked_program_with_permissions(
386 &mut self,
387 linked: crate::bytecode::LinkedProgram,
388 granted: &shape_abi_v1::PermissionSet,
389 ) -> Result<(), PermissionError> {
390 if !linked.total_required_permissions.is_subset(granted) {
391 let missing = linked.total_required_permissions.difference(granted);
392 return Err(PermissionError::InsufficientPermissions {
393 required: linked.total_required_permissions.clone(),
394 granted: granted.clone(),
395 missing,
396 });
397 }
398 self.load_linked_program(linked);
399 Ok(())
400 }
401
402 pub(crate) fn populate_content_addressed_metadata(&mut self) {
408 let func_count = self.program.functions.len();
409 self.function_entry_points = self
410 .program
411 .functions
412 .iter()
413 .map(|f| f.entry_point)
414 .collect();
415
416 if self.program.function_blob_hashes.len() == func_count {
417 self.function_hashes = self.program.function_blob_hashes.clone();
418 } else if let Some(ref ca_program) = self.program.content_addressed {
419 let mut name_to_hash: HashMap<String, FunctionHash> =
421 HashMap::with_capacity(ca_program.function_store.len());
422 for (hash, blob) in &ca_program.function_store {
423 name_to_hash.insert(blob.name.clone(), *hash);
424 }
425
426 self.function_hashes = Vec::with_capacity(func_count);
427 for func in &self.program.functions {
428 self.function_hashes
429 .push(name_to_hash.get(&func.name).copied());
430 }
431 } else {
432 self.function_hashes = vec![None; func_count];
433 }
434
435 self.function_hash_raw = self
437 .function_hashes
438 .iter()
439 .map(|opt| opt.map(|fh| fh.0))
440 .collect();
441 self.function_id_by_hash.clear();
442 for (idx, maybe_hash) in self.function_hashes.iter().enumerate() {
443 if let Some(hash) = maybe_hash {
444 self.function_id_by_hash.entry(*hash).or_insert(idx as u16);
445 }
446 }
447 }
448
449 pub(crate) fn rebuild_function_name_index(&mut self) {
453 self.function_name_index.clear();
454 for (i, func) in self.program.functions.iter().enumerate() {
455 self.function_name_index.insert(func.name.clone(), i as u16);
456 }
457 }
458
459 pub fn module_bindings_snapshot(&self) -> Vec<shape_value::KindedSlot> {
475 todo!(
476 "phase-2c — see ADR-006 §2.7.4: module_bindings_snapshot \
477 body deferred to the Phase-2c snapshot revival. The §2.7.8 \
478 parallel-kind track is live, so the body is one map+collect \
479 over `module_binding_read_owned_kinded` per index."
480 );
481 }
482
483 pub fn reset_for_trampoline(&mut self) {
488 for i in 0..self.sp {
494 super::stack::drop_with_kind(self.stack[i], self.kinds[i]);
495 self.stack[i] = Self::NONE_BITS;
496 self.kinds[i] = shape_value::NativeKind::Bool;
497 }
498 self.sp = 0;
499 self.ip = 0;
500 self.call_stack.clear();
501 self.loop_stack.clear();
502 self.timeframe_stack.clear();
503 self.exception_handlers.clear();
504 self.instruction_count = 0;
505 self.last_error_line = None;
506 self.last_error_file = None;
507 self.last_uncaught_exception = None;
508 self.module_init_done = true; }
510
511 pub fn reset(&mut self) {
512 self.ip = self.program_entry_ip;
513 for i in 0..self.sp {
517 super::stack::drop_with_kind(self.stack[i], self.kinds[i]);
518 self.stack[i] = Self::NONE_BITS;
519 self.kinds[i] = shape_value::NativeKind::Bool;
520 }
521 let tl = self.program.top_level_locals_count as usize;
524 self.sp = tl;
525 self.call_stack.clear();
526 self.loop_stack.clear();
527 self.timeframe_stack.clear();
528 self.exception_handlers.clear();
529 self.instruction_count = 0;
530 self.last_error_line = None;
531 self.last_error_file = None;
532 self.last_uncaught_exception = None;
533 }
534
535 pub fn reset_stack(&mut self) {
538 self.ip = self.program_entry_ip;
539 for i in 0..self.sp {
543 super::stack::drop_with_kind(self.stack[i], self.kinds[i]);
544 self.stack[i] = Self::NONE_BITS;
545 self.kinds[i] = shape_value::NativeKind::Bool;
546 }
547 let tl = self.program.top_level_locals_count as usize;
548 self.sp = tl;
549 self.call_stack.clear();
550 self.loop_stack.clear();
551 self.timeframe_stack.clear();
552 self.exception_handlers.clear();
553 self.last_error_line = None;
554 self.last_error_file = None;
555 self.last_uncaught_exception = None;
556 }
557
558 #[inline]
561 pub fn reset_minimal(&mut self) {
562 self.ip = self.program_entry_ip;
563 for i in 0..self.sp {
567 super::stack::drop_with_kind(self.stack[i], self.kinds[i]);
568 self.stack[i] = Self::NONE_BITS;
569 self.kinds[i] = shape_value::NativeKind::Bool;
570 }
571 let tl = self.program.top_level_locals_count as usize;
572 self.sp = tl;
573 self.call_stack.clear();
574 self.last_error_line = None;
575 self.last_error_file = None;
576 self.last_uncaught_exception = None;
577 }
578
579 pub fn push_value(&mut self, _value: shape_value::KindedSlot) {
592 todo!(
593 "phase-2c — see ADR-006 §2.7.4: push_value(KindedSlot) is a \
594 host-boundary helper; the in-VM surface uses \
595 push_kinded(bits, kind) sourced directly from the producer."
596 );
597 }
598}