pr 63
This commit is contained in:
parent
7819dd2d0a
commit
3af2aa1328
@ -20,7 +20,5 @@ pub mod asm;
|
||||
pub mod disasm;
|
||||
|
||||
mod model;
|
||||
mod module_linker;
|
||||
|
||||
pub use model::*;
|
||||
pub use module_linker::*;
|
||||
|
||||
@ -61,12 +61,12 @@ pub struct Export {
|
||||
pub func_idx: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Import {
|
||||
pub symbol: String,
|
||||
pub relocation_pcs: Vec<u32>,
|
||||
}
|
||||
|
||||
/// Represents the final serialized format of a PBS v0 module.
|
||||
///
|
||||
/// This structure is a pure data container for the PBS format. It does NOT
|
||||
/// contain any linker-like logic (symbol resolution, patching, etc.).
|
||||
/// All multi-module programs must be flattened and linked by the compiler
|
||||
/// before being serialized into this format.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BytecodeModule {
|
||||
pub version: u16,
|
||||
@ -75,7 +75,6 @@ pub struct BytecodeModule {
|
||||
pub code: Vec<u8>,
|
||||
pub debug_info: Option<DebugInfo>,
|
||||
pub exports: Vec<Export>,
|
||||
pub imports: Vec<Import>,
|
||||
}
|
||||
|
||||
impl BytecodeModule {
|
||||
@ -85,7 +84,6 @@ impl BytecodeModule {
|
||||
let code_data = self.code.clone();
|
||||
let debug_data = self.debug_info.as_ref().map(|di| self.serialize_debug(di)).unwrap_or_default();
|
||||
let export_data = self.serialize_exports();
|
||||
let import_data = self.serialize_imports();
|
||||
|
||||
let mut final_sections = Vec::new();
|
||||
if !cp_data.is_empty() { final_sections.push((0, cp_data)); }
|
||||
@ -93,7 +91,6 @@ impl BytecodeModule {
|
||||
if !code_data.is_empty() { final_sections.push((2, code_data)); }
|
||||
if !debug_data.is_empty() { final_sections.push((3, debug_data)); }
|
||||
if !export_data.is_empty() { final_sections.push((4, export_data)); }
|
||||
if !import_data.is_empty() { final_sections.push((5, import_data)); }
|
||||
|
||||
let mut out = Vec::new();
|
||||
// Magic "PBS\0"
|
||||
@ -207,21 +204,6 @@ impl BytecodeModule {
|
||||
data
|
||||
}
|
||||
|
||||
fn serialize_imports(&self) -> Vec<u8> {
|
||||
if self.imports.is_empty() { return Vec::new(); }
|
||||
let mut data = Vec::new();
|
||||
data.extend_from_slice(&(self.imports.len() as u32).to_le_bytes());
|
||||
for imp in &self.imports {
|
||||
let s_bytes = imp.symbol.as_bytes();
|
||||
data.extend_from_slice(&(s_bytes.len() as u32).to_le_bytes());
|
||||
data.extend_from_slice(s_bytes);
|
||||
data.extend_from_slice(&(imp.relocation_pcs.len() as u32).to_le_bytes());
|
||||
for pc in &imp.relocation_pcs {
|
||||
data.extend_from_slice(&pc.to_le_bytes());
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BytecodeLoader;
|
||||
@ -287,7 +269,6 @@ impl BytecodeLoader {
|
||||
code: Vec::new(),
|
||||
debug_info: None,
|
||||
exports: Vec::new(),
|
||||
imports: Vec::new(),
|
||||
};
|
||||
|
||||
for (kind, offset, length) in sections {
|
||||
@ -308,9 +289,6 @@ impl BytecodeLoader {
|
||||
4 => { // Exports
|
||||
module.exports = parse_exports(section_data)?;
|
||||
}
|
||||
5 => { // Imports
|
||||
module.imports = parse_imports(section_data)?;
|
||||
}
|
||||
_ => {} // Skip unknown or optional sections
|
||||
}
|
||||
}
|
||||
@ -493,45 +471,6 @@ fn parse_exports(data: &[u8]) -> Result<Vec<Export>, LoadError> {
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
fn parse_imports(data: &[u8]) -> Result<Vec<Import>, LoadError> {
|
||||
if data.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if data.len() < 4 {
|
||||
return Err(LoadError::MalformedSection);
|
||||
}
|
||||
let count = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
|
||||
let mut imports = Vec::with_capacity(count);
|
||||
let mut pos = 4;
|
||||
|
||||
for _ in 0..count {
|
||||
if pos + 8 > data.len() {
|
||||
return Err(LoadError::UnexpectedEof);
|
||||
}
|
||||
let relocation_count = u32::from_le_bytes(data[pos..pos+4].try_into().unwrap()) as usize;
|
||||
let name_len = u32::from_le_bytes(data[pos+4..pos+8].try_into().unwrap()) as usize;
|
||||
pos += 8;
|
||||
|
||||
if pos + name_len > data.len() {
|
||||
return Err(LoadError::UnexpectedEof);
|
||||
}
|
||||
let symbol = String::from_utf8_lossy(&data[pos..pos+name_len]).into_owned();
|
||||
pos += name_len;
|
||||
|
||||
if pos + relocation_count * 4 > data.len() {
|
||||
return Err(LoadError::UnexpectedEof);
|
||||
}
|
||||
let mut relocation_pcs = Vec::with_capacity(relocation_count);
|
||||
for _ in 0..relocation_count {
|
||||
let pc = u32::from_le_bytes(data[pos..pos+4].try_into().unwrap());
|
||||
relocation_pcs.push(pc);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
imports.push(Import { symbol, relocation_pcs });
|
||||
}
|
||||
Ok(imports)
|
||||
}
|
||||
|
||||
fn validate_module(module: &BytecodeModule) -> Result<(), LoadError> {
|
||||
for func in &module.functions {
|
||||
|
||||
@ -1,296 +0,0 @@
|
||||
use crate::opcode::OpCode;
|
||||
use crate::{BytecodeModule, ConstantPoolEntry, DebugInfo, FunctionMeta};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum LinkError {
|
||||
UnresolvedSymbol(String),
|
||||
DuplicateExport(String),
|
||||
}
|
||||
|
||||
pub struct ModuleLinker;
|
||||
|
||||
/// Internal representation for linking process
|
||||
#[derive(Debug)]
|
||||
pub struct LinkedProgram {
|
||||
pub rom: Vec<u8>,
|
||||
pub constant_pool: Vec<ConstantPoolEntry>,
|
||||
pub functions: Vec<FunctionMeta>,
|
||||
pub debug_info: Option<DebugInfo>,
|
||||
pub exports: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl ModuleLinker {
|
||||
pub fn link(modules: &[BytecodeModule]) -> Result<LinkedProgram, LinkError> {
|
||||
let mut combined_code = Vec::new();
|
||||
let mut combined_functions = Vec::new();
|
||||
let mut combined_constants = Vec::new();
|
||||
let mut combined_debug_pc_to_span = Vec::new();
|
||||
let mut combined_debug_function_names = Vec::new();
|
||||
|
||||
let mut exports = HashMap::new();
|
||||
|
||||
// Offset mapping for each module
|
||||
let mut module_code_offsets = Vec::with_capacity(modules.len());
|
||||
let mut module_function_offsets = Vec::with_capacity(modules.len());
|
||||
|
||||
// First pass: collect exports and calculate offsets
|
||||
for module in modules {
|
||||
let code_offset = combined_code.len() as u32;
|
||||
let function_offset = combined_functions.len() as u32;
|
||||
|
||||
module_code_offsets.push(code_offset);
|
||||
module_function_offsets.push(function_offset);
|
||||
|
||||
for export in &module.exports {
|
||||
if exports.contains_key(&export.symbol) {
|
||||
return Err(LinkError::DuplicateExport(export.symbol.clone()));
|
||||
}
|
||||
exports.insert(export.symbol.clone(), (function_offset + export.func_idx) as u32);
|
||||
}
|
||||
|
||||
combined_code.extend_from_slice(&module.code);
|
||||
|
||||
for func in &module.functions {
|
||||
let mut linked_func = func.clone();
|
||||
linked_func.code_offset += code_offset;
|
||||
combined_functions.push(linked_func);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: resolve imports and relocate constants/code
|
||||
for (i, module) in modules.iter().enumerate() {
|
||||
let code_offset = module_code_offsets[i] as usize;
|
||||
let const_base = combined_constants.len() as u32;
|
||||
|
||||
// Relocate constant pool entries for this module
|
||||
for entry in &module.const_pool {
|
||||
combined_constants.push(entry.clone());
|
||||
}
|
||||
|
||||
// Patch relocations for imports
|
||||
for import in &module.imports {
|
||||
let target_func_idx = exports.get(&import.symbol)
|
||||
.ok_or_else(|| LinkError::UnresolvedSymbol(import.symbol.clone()))?;
|
||||
|
||||
for &reloc_pc in &import.relocation_pcs {
|
||||
let absolute_pc = code_offset + reloc_pc as usize;
|
||||
// CALL opcode is 2 bytes, immediate is next 4 bytes
|
||||
let imm_offset = absolute_pc + 2;
|
||||
if imm_offset + 4 <= combined_code.len() {
|
||||
let bytes = target_func_idx.to_le_bytes();
|
||||
combined_code[imm_offset..imm_offset+4].copy_from_slice(&bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Relocate PUSH_CONST instructions
|
||||
if const_base > 0 {
|
||||
let mut pos = code_offset;
|
||||
let end = code_offset + module.code.len();
|
||||
while pos < end {
|
||||
if pos + 2 > end { break; }
|
||||
let op_val = u16::from_le_bytes([combined_code[pos], combined_code[pos+1]]);
|
||||
let opcode = match OpCode::try_from(op_val) {
|
||||
Ok(op) => op,
|
||||
Err(_) => {
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
pos += 2;
|
||||
|
||||
match opcode {
|
||||
OpCode::PushConst => {
|
||||
if pos + 4 <= end {
|
||||
let old_idx = u32::from_le_bytes(combined_code[pos..pos+4].try_into().unwrap());
|
||||
let new_idx = old_idx + const_base;
|
||||
combined_code[pos..pos+4].copy_from_slice(&new_idx.to_le_bytes());
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
OpCode::PushI32 | OpCode::PushBounded | OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue
|
||||
| OpCode::GetGlobal | OpCode::SetGlobal | OpCode::GetLocal | OpCode::SetLocal
|
||||
| OpCode::PopN | OpCode::Syscall | OpCode::GateLoad | OpCode::GateStore | OpCode::Call => {
|
||||
pos += 4;
|
||||
}
|
||||
OpCode::PushI64 | OpCode::PushF64 | OpCode::Alloc => {
|
||||
pos += 8;
|
||||
}
|
||||
OpCode::PushBool => {
|
||||
pos += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle debug info
|
||||
if let Some(debug_info) = &module.debug_info {
|
||||
for (pc, span) in &debug_info.pc_to_span {
|
||||
combined_debug_pc_to_span.push((pc + module_code_offsets[i], span.clone()));
|
||||
}
|
||||
for (func_idx, name) in &debug_info.function_names {
|
||||
combined_debug_function_names.push((func_idx + module_function_offsets[i], name.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let debug_info = if !combined_debug_pc_to_span.is_empty() {
|
||||
Some(DebugInfo {
|
||||
pc_to_span: combined_debug_pc_to_span,
|
||||
function_names: combined_debug_function_names,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(LinkedProgram {
|
||||
rom: combined_code,
|
||||
constant_pool: combined_constants,
|
||||
functions: combined_functions,
|
||||
debug_info,
|
||||
exports,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::opcode::OpCode;
|
||||
use crate::{BytecodeModule, Export, FunctionMeta, Import};
|
||||
|
||||
#[test]
|
||||
fn test_linker_basic() {
|
||||
// Module 1: defines 'foo', calls 'bar'
|
||||
let mut code1 = Vec::new();
|
||||
// Function 'foo' at offset 0
|
||||
code1.extend_from_slice(&(OpCode::Call as u16).to_le_bytes());
|
||||
code1.extend_from_slice(&0u32.to_le_bytes()); // placeholder for 'bar'
|
||||
code1.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
|
||||
let m1 = BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![],
|
||||
functions: vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: code1.len() as u32,
|
||||
..Default::default()
|
||||
}],
|
||||
code: code1,
|
||||
debug_info: None,
|
||||
exports: vec![Export { symbol: "foo".to_string(), func_idx: 0 }],
|
||||
imports: vec![Import { symbol: "bar".to_string(), relocation_pcs: vec![0] }],
|
||||
};
|
||||
|
||||
// Module 2: defines 'bar'
|
||||
let mut code2 = Vec::new();
|
||||
code2.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
|
||||
let m2 = BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![],
|
||||
functions: vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: code2.len() as u32,
|
||||
..Default::default()
|
||||
}],
|
||||
code: code2,
|
||||
debug_info: None,
|
||||
exports: vec![Export { symbol: "bar".to_string(), func_idx: 0 }],
|
||||
imports: vec![],
|
||||
};
|
||||
|
||||
let result = ModuleLinker::link(&[m1, m2]).unwrap();
|
||||
|
||||
assert_eq!(result.functions.len(), 2);
|
||||
// 'foo' is func 0, 'bar' is func 1
|
||||
assert_eq!(result.functions[0].code_offset, 0);
|
||||
assert_eq!(result.functions[1].code_offset, 8);
|
||||
|
||||
// Let's check patched code
|
||||
let patched_func_id = u32::from_le_bytes(result.rom[2..6].try_into().unwrap());
|
||||
assert_eq!(patched_func_id, 1); // Points to 'bar'
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_linker_unresolved() {
|
||||
let m1 = BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![],
|
||||
functions: vec![],
|
||||
code: vec![],
|
||||
debug_info: None,
|
||||
exports: vec![],
|
||||
imports: vec![Import { symbol: "missing".to_string(), relocation_pcs: vec![] }],
|
||||
};
|
||||
let result = ModuleLinker::link(&[m1]);
|
||||
assert_eq!(result.unwrap_err(), LinkError::UnresolvedSymbol("missing".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_linker_duplicate_export() {
|
||||
let m1 = BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![],
|
||||
functions: vec![],
|
||||
code: vec![],
|
||||
debug_info: None,
|
||||
exports: vec![Export { symbol: "dup".to_string(), func_idx: 0 }],
|
||||
imports: vec![],
|
||||
};
|
||||
let m2 = m1.clone();
|
||||
let result = ModuleLinker::link(&[m1, m2]);
|
||||
assert_eq!(result.unwrap_err(), LinkError::DuplicateExport("dup".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_linker_const_relocation() {
|
||||
// Module 1: uses constants
|
||||
let mut code1 = Vec::new();
|
||||
code1.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
|
||||
code1.extend_from_slice(&0u32.to_le_bytes()); // Index 0
|
||||
code1.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
|
||||
let m1 = BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![ConstantPoolEntry::Int32(42)],
|
||||
functions: vec![FunctionMeta { code_offset: 0, code_len: code1.len() as u32, ..Default::default() }],
|
||||
code: code1,
|
||||
debug_info: None,
|
||||
exports: vec![],
|
||||
imports: vec![],
|
||||
};
|
||||
|
||||
// Module 2: also uses constants
|
||||
let mut code2 = Vec::new();
|
||||
code2.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
|
||||
code2.extend_from_slice(&0u32.to_le_bytes()); // Index 0 (local to module 2)
|
||||
code2.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
|
||||
let m2 = BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![ConstantPoolEntry::Int32(99)],
|
||||
functions: vec![FunctionMeta { code_offset: 0, code_len: code2.len() as u32, ..Default::default() }],
|
||||
code: code2,
|
||||
debug_info: None,
|
||||
exports: vec![],
|
||||
imports: vec![],
|
||||
};
|
||||
|
||||
let result = ModuleLinker::link(&[m1, m2]).unwrap();
|
||||
|
||||
assert_eq!(result.constant_pool.len(), 2);
|
||||
assert_eq!(result.constant_pool[0], ConstantPoolEntry::Int32(42));
|
||||
assert_eq!(result.constant_pool[1], ConstantPoolEntry::Int32(99));
|
||||
|
||||
// Code for module 1 (starts at 0)
|
||||
let idx1 = u32::from_le_bytes(result.rom[2..6].try_into().unwrap());
|
||||
assert_eq!(idx1, 0);
|
||||
|
||||
// Code for module 2 (starts at 8)
|
||||
let idx2 = u32::from_le_bytes(result.rom[10..14].try_into().unwrap());
|
||||
assert_eq!(idx2, 1);
|
||||
}
|
||||
}
|
||||
@ -50,8 +50,7 @@ pub fn emit_module(module: &ir_vm::Module) -> Result<EmitResult> {
|
||||
functions: fragments.functions,
|
||||
code: fragments.code,
|
||||
debug_info: fragments.debug_info,
|
||||
exports,
|
||||
imports: vec![],
|
||||
exports,
|
||||
};
|
||||
|
||||
Ok(EmitResult {
|
||||
|
||||
@ -448,7 +448,6 @@ mod tests {
|
||||
code: vec![0x02, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
debug_info: None,
|
||||
exports: vec![prometeu_bytecode::Export { symbol: "main".into(), func_idx: 0 }],
|
||||
imports: vec![],
|
||||
}.serialize();
|
||||
let cartridge = Cartridge {
|
||||
app_id: 1234,
|
||||
@ -505,7 +504,6 @@ mod tests {
|
||||
],
|
||||
debug_info: None,
|
||||
exports: vec![prometeu_bytecode::Export { symbol: "main".into(), func_idx: 0 }],
|
||||
imports: vec![],
|
||||
}.serialize();
|
||||
let cartridge = Cartridge {
|
||||
app_id: 1234,
|
||||
@ -721,7 +719,6 @@ mod tests {
|
||||
],
|
||||
debug_info: None,
|
||||
exports: vec![prometeu_bytecode::Export { symbol: "main".into(), func_idx: 0 }],
|
||||
imports: vec![],
|
||||
}.serialize();
|
||||
let cartridge = Cartridge {
|
||||
app_id: 1234,
|
||||
|
||||
@ -4,6 +4,14 @@ use prometeu_bytecode::{BytecodeModule, ConstantPoolEntry, DebugInfo, Export, Fu
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Represents a fully linked, executable PBS program image.
|
||||
///
|
||||
/// Under the Prometeu architecture, the ProgramImage is a "closed-world" artifact
|
||||
/// produced by the compiler. All linking, relocation, and symbol resolution
|
||||
/// MUST be performed by the compiler before this image is created.
|
||||
///
|
||||
/// The runtime (VM) assumes this image is authoritative and performs no
|
||||
/// additional linking or fixups.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ProgramImage {
|
||||
pub rom: Arc<[u8]>,
|
||||
@ -14,14 +22,7 @@ pub struct ProgramImage {
|
||||
}
|
||||
|
||||
impl ProgramImage {
|
||||
pub fn new(rom: Vec<u8>, constant_pool: Vec<Value>, mut functions: Vec<FunctionMeta>, debug_info: Option<DebugInfo>, exports: HashMap<String, u32>) -> Self {
|
||||
if functions.is_empty() && !rom.is_empty() {
|
||||
functions.push(FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
pub fn new(rom: Vec<u8>, constant_pool: Vec<Value>, functions: Vec<FunctionMeta>, debug_info: Option<DebugInfo>, exports: HashMap<String, u32>) -> Self {
|
||||
Self {
|
||||
rom: Arc::from(rom),
|
||||
constant_pool: Arc::from(constant_pool),
|
||||
@ -117,7 +118,6 @@ impl From<ProgramImage> for BytecodeModule {
|
||||
code: program.rom.as_ref().to_vec(),
|
||||
debug_info: program.debug_info.clone(),
|
||||
exports,
|
||||
imports: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,14 +148,13 @@ impl VirtualMachine {
|
||||
return Err(VmInitError::InvalidFormat);
|
||||
};
|
||||
|
||||
// Resolve the entrypoint: empty (defaults to 0), numeric PC, or symbol name.
|
||||
// Resolve the entrypoint: empty (defaults to func 0), numeric func_idx, or symbol name.
|
||||
let pc = if entrypoint.is_empty() {
|
||||
0
|
||||
} else if let Ok(addr) = entrypoint.parse::<usize>() {
|
||||
if addr >= program.rom.len() && (addr > 0 || !program.rom.is_empty()) {
|
||||
return Err(VmInitError::EntrypointNotFound);
|
||||
}
|
||||
addr
|
||||
program.functions.get(0).map(|f| f.code_offset as usize).unwrap_or(0)
|
||||
} else if let Ok(func_idx) = entrypoint.parse::<usize>() {
|
||||
program.functions.get(func_idx)
|
||||
.map(|f| f.code_offset as usize)
|
||||
.ok_or(VmInitError::EntrypointNotFound)?
|
||||
} else {
|
||||
// Try to resolve as a symbol name from the exports map
|
||||
if let Some(&func_idx) = program.exports.get(entrypoint) {
|
||||
@ -163,18 +162,7 @@ impl VirtualMachine {
|
||||
.map(|f| f.code_offset as usize)
|
||||
.ok_or(VmInitError::EntrypointNotFound)?
|
||||
} else {
|
||||
// Suffix match fallback (e.g. "frame" matches "src/main/modules/main:frame")
|
||||
let suffix = format!(":{}", entrypoint);
|
||||
let found = program.exports.iter()
|
||||
.find(|(name, _)| name == &entrypoint || name.ends_with(&suffix));
|
||||
|
||||
if let Some((_, &func_idx)) = found {
|
||||
program.functions.get(func_idx as usize)
|
||||
.map(|f| f.code_offset as usize)
|
||||
.ok_or(VmInitError::EntrypointNotFound)?
|
||||
} else {
|
||||
return Err(VmInitError::EntrypointNotFound);
|
||||
}
|
||||
return Err(VmInitError::EntrypointNotFound);
|
||||
}
|
||||
};
|
||||
|
||||
@ -189,33 +177,18 @@ impl VirtualMachine {
|
||||
/// Prepares the VM to execute a specific entrypoint by setting the PC and
|
||||
/// pushing an initial call frame.
|
||||
pub fn prepare_call(&mut self, entrypoint: &str) {
|
||||
let (addr, func_idx) = if let Ok(addr) = entrypoint.parse::<usize>() {
|
||||
let idx = self.program.functions.iter().position(|f| {
|
||||
addr >= f.code_offset as usize && addr < (f.code_offset + f.code_len) as usize
|
||||
}).unwrap_or(0);
|
||||
(addr, idx)
|
||||
let func_idx = if let Ok(idx) = entrypoint.parse::<usize>() {
|
||||
idx
|
||||
} else {
|
||||
// Try to resolve as a symbol name
|
||||
let found = self.program.exports.get(entrypoint)
|
||||
.map(|&idx| (idx, entrypoint.to_string()))
|
||||
.or_else(|| {
|
||||
let suffix = format!(":{}", entrypoint);
|
||||
self.program.exports.iter()
|
||||
.find(|(name, _)| name == &entrypoint || name.ends_with(&suffix))
|
||||
.map(|(name, &idx)| (idx, name.clone()))
|
||||
});
|
||||
|
||||
if let Some((func_idx, _)) = found {
|
||||
let addr = self.program.functions.get(func_idx as usize)
|
||||
.map(|f| f.code_offset as usize)
|
||||
.unwrap_or(0);
|
||||
(addr, func_idx as usize)
|
||||
} else {
|
||||
// Default to 0 for non-numeric entrypoints that aren't found.
|
||||
(0, 0)
|
||||
}
|
||||
self.program.exports.get(entrypoint)
|
||||
.map(|&idx| idx as usize)
|
||||
.ok_or(()).unwrap_or(0) // Default to 0 if not found
|
||||
};
|
||||
|
||||
let callee = self.program.functions.get(func_idx).cloned().unwrap_or_default();
|
||||
let addr = callee.code_offset as usize;
|
||||
|
||||
self.pc = addr;
|
||||
self.halted = false;
|
||||
|
||||
@ -943,6 +916,17 @@ impl VirtualMachine {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn new_test_vm(rom: Vec<u8>, constant_pool: Vec<Value>) -> VirtualMachine {
|
||||
let rom_len = rom.len() as u32;
|
||||
let mut vm = VirtualMachine::new(rom, constant_pool);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom_len,
|
||||
..Default::default()
|
||||
}]);
|
||||
vm
|
||||
}
|
||||
use crate::hardware::HardwareBridge;
|
||||
use crate::virtual_machine::{expect_int, HostReturn, Value, VmFault};
|
||||
use prometeu_bytecode::abi::SourceSpan;
|
||||
@ -993,7 +977,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Mod as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(0));
|
||||
@ -1012,7 +996,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Div as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
match report.reason {
|
||||
@ -1035,7 +1019,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::IntToBoundChecked as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
match report.reason {
|
||||
@ -1060,7 +1044,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Add as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
match report.reason {
|
||||
@ -1086,7 +1070,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Lt as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Boolean(true));
|
||||
}
|
||||
@ -1098,7 +1082,7 @@ mod tests {
|
||||
rom.extend_from_slice(&42i64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1113,7 +1097,7 @@ mod tests {
|
||||
rom.extend_from_slice(&3.14f64.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1132,7 +1116,7 @@ mod tests {
|
||||
rom.push(0);
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1328,7 +1312,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::PopScope as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1427,7 +1411,7 @@ mod tests {
|
||||
rom.extend_from_slice(&42i32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1449,7 +1433,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::BitAnd as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
@ -1464,7 +1448,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::BitOr as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
@ -1485,7 +1469,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Lte as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
@ -1500,7 +1484,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Gte as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
@ -1518,7 +1502,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Neg as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
vm.step(&mut native, &mut hw).unwrap();
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(-42));
|
||||
@ -1549,7 +1533,7 @@ mod tests {
|
||||
rom.extend_from_slice(&100i32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.step(&mut native, &mut hw).unwrap(); // PushBool
|
||||
vm.step(&mut native, &mut hw).unwrap(); // JmpIfTrue
|
||||
assert_eq!(vm.pc, 11);
|
||||
@ -1568,7 +1552,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Trap as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
assert_eq!(report.reason, LogicalFrameEndingReason::Breakpoint);
|
||||
@ -1592,7 +1576,7 @@ mod tests {
|
||||
rom.extend_from_slice(&2u32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
assert_eq!(vm.pop().unwrap(), Value::Int32(1));
|
||||
@ -1614,7 +1598,7 @@ mod tests {
|
||||
rom.extend_from_slice(&1u32.to_le_bytes()); // offset 1
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
match report.reason {
|
||||
@ -1641,7 +1625,7 @@ mod tests {
|
||||
rom.extend_from_slice(&0u32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
match report.reason {
|
||||
@ -1662,7 +1646,12 @@ mod tests {
|
||||
0x11, 0x00, // Pop
|
||||
0x51, 0x00 // Ret
|
||||
];
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = VirtualMachine::new(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut hw = crate::Hardware::new();
|
||||
struct TestNative;
|
||||
impl NativeInterface for TestNative {
|
||||
@ -1692,7 +1681,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = VirtualMachine::new(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut native = MultiReturnNative;
|
||||
let mut hw = crate::Hardware::new();
|
||||
|
||||
@ -1718,7 +1712,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = VirtualMachine::new(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut native = VoidReturnNative;
|
||||
let mut hw = crate::Hardware::new();
|
||||
|
||||
@ -1748,7 +1747,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = VirtualMachine::new(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![prometeu_bytecode::FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}]);
|
||||
let mut native = ArgCheckNative;
|
||||
let mut hw = crate::Hardware::new();
|
||||
|
||||
@ -1770,7 +1774,7 @@ mod tests {
|
||||
0x70, 0x00, // Syscall + Reserved
|
||||
0xEF, 0xBE, 0xAD, 0xDE, // 0xDEADBEEF
|
||||
];
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1795,7 +1799,7 @@ mod tests {
|
||||
0x70, 0x00, // Syscall + Reserved
|
||||
0x01, 0x10, 0x00, 0x00, // Syscall ID 0x1001
|
||||
];
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = MockNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1832,7 +1836,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let mut native = BadNative;
|
||||
let mut hw = MockHardware;
|
||||
|
||||
@ -1962,7 +1966,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
let f1_len = rom.len() as u32 - f1_start;
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![
|
||||
FunctionMeta {
|
||||
code_offset: f0_start as u32,
|
||||
@ -2014,7 +2018,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
let f1_len = rom.len() as u32 - f1_start;
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![
|
||||
FunctionMeta {
|
||||
code_offset: f0_start as u32,
|
||||
@ -2065,7 +2069,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
let f1_len = rom.len() as u32 - f1_start;
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![
|
||||
FunctionMeta {
|
||||
code_offset: f0_start as u32,
|
||||
@ -2097,7 +2101,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Call as u16).to_le_bytes());
|
||||
rom.extend_from_slice(&99u32.to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
let report = vm.run_budget(100, &mut native, &mut hw).unwrap();
|
||||
|
||||
match report.reason {
|
||||
@ -2130,7 +2134,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
let f1_len = rom.len() as u32 - f1_start;
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![
|
||||
FunctionMeta {
|
||||
code_offset: f0_start as u32,
|
||||
@ -2180,7 +2184,7 @@ mod tests {
|
||||
rom.extend_from_slice(&0u32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: 20,
|
||||
@ -2234,7 +2238,7 @@ mod tests {
|
||||
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||
let f1_len = rom.len() as u32 - f1_start;
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![
|
||||
FunctionMeta {
|
||||
code_offset: f0_start as u32,
|
||||
@ -2271,7 +2275,7 @@ mod tests {
|
||||
rom.extend_from_slice(&1u32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: 8,
|
||||
@ -2346,7 +2350,7 @@ mod tests {
|
||||
// 48: HALT
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
// We need to set up the function meta for absolute jumps to work correctly
|
||||
vm.program.functions = std::sync::Arc::from(vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
@ -2385,7 +2389,7 @@ mod tests {
|
||||
// 15-16: HALT
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: 17,
|
||||
@ -2413,7 +2417,7 @@ mod tests {
|
||||
rom.extend_from_slice(&9u32.to_le_bytes());
|
||||
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||
|
||||
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||
vm.program.functions = std::sync::Arc::from(vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: 14,
|
||||
@ -2454,7 +2458,11 @@ mod tests {
|
||||
function_names: vec![(0, "main".to_string())],
|
||||
};
|
||||
|
||||
let program = ProgramImage::new(rom, vec![], vec![], Some(debug_info), std::collections::HashMap::new());
|
||||
let program = ProgramImage::new(rom.clone(), vec![], vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
..Default::default()
|
||||
}], Some(debug_info), std::collections::HashMap::new());
|
||||
let mut vm = VirtualMachine {
|
||||
program,
|
||||
..Default::default()
|
||||
|
||||
@ -39,20 +39,13 @@ fn test_canonical_cartridge_heartbeat() {
|
||||
|
||||
let pbc_bytes = fs::read(pbc_path).expect("Failed to read canonical PBC. Did you run the generation test?");
|
||||
|
||||
// Determine numeric entrypoint PC from the compiled module exports
|
||||
// Determine entrypoint from the compiled module exports
|
||||
let module = BytecodeLoader::load(&pbc_bytes).expect("Failed to parse PBC");
|
||||
let func_idx = module
|
||||
.exports
|
||||
.iter()
|
||||
.find(|e| e.symbol == "src/main/modules:frame")
|
||||
.map(|e| e.func_idx as usize)
|
||||
.expect("Entrypoint symbol not found in exports");
|
||||
let entry_pc = module.functions[func_idx].code_offset as usize;
|
||||
let entry_pc_str = entry_pc.to_string();
|
||||
let entry_symbol = "src/main/modules:frame";
|
||||
|
||||
let mut vm = VirtualMachine::new(vec![], vec![]);
|
||||
vm.initialize(pbc_bytes, &entry_pc_str).expect("Failed to initialize VM with canonical cartridge");
|
||||
vm.prepare_call(&entry_pc_str);
|
||||
vm.initialize(pbc_bytes, entry_symbol).expect("Failed to initialize VM with canonical cartridge");
|
||||
vm.prepare_call(entry_symbol);
|
||||
|
||||
let mut native = MockNative;
|
||||
let mut hw = Hardware::new();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user