Skip to content

Commit bb451ee

Browse files
committed
fix(execution): update execution engine to support imported functions
Enhance execution engine for better import handling: - Fix function index calculation with imported functions - Improve error messages for function execution - Add function type checking for imports - Update tests to verify module import functionality
1 parent 13a6dea commit bb451ee

File tree

3 files changed

+189
-16
lines changed

3 files changed

+189
-16
lines changed

wrt/src/execution.rs

Lines changed: 129 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::error::{Error, Result};
22
use crate::instructions::Instruction;
33
use crate::logging::{CallbackRegistry, LogLevel, LogOperation};
44
use crate::module::Module;
5-
use crate::types::ValueType;
5+
use crate::types::{ExternType, ValueType};
66
use crate::values::Value;
77
use crate::{format, String, ToString, Vec};
88

@@ -472,10 +472,43 @@ impl Engine {
472472
self.state = ExecutionState::Running;
473473

474474
// Fetch and validate information within a scope to limit borrow
475-
let (func_locals, instance_clone) = {
475+
let (func_locals, instance_clone, _func_type) = {
476476
// Scope to limit the borrow of self.instances
477477
let instance = &self.instances[instance_idx as usize];
478-
let func = &instance.module.functions[func_idx as usize];
478+
479+
// Determine if this is an imported function
480+
let import_count = instance
481+
.module
482+
.imports
483+
.iter()
484+
.filter(|import| matches!(import.ty, ExternType::Function(_)))
485+
.count();
486+
487+
// Adjust function index for imports
488+
let actual_func_idx = if func_idx < import_count as u32 {
489+
// This is an imported function
490+
return Err(Error::Execution(format!(
491+
"Imported function at index {} cannot be called directly: {}.{}",
492+
func_idx,
493+
instance.module.imports[func_idx as usize].module,
494+
instance.module.imports[func_idx as usize].name
495+
)));
496+
} else {
497+
// This is a regular function, adjust index to skip imports
498+
func_idx - import_count as u32
499+
};
500+
501+
// Verify function index is valid
502+
if actual_func_idx as usize >= instance.module.functions.len() {
503+
return Err(Error::Execution(format!(
504+
"Function index {} out of bounds (max: {})",
505+
actual_func_idx,
506+
instance.module.functions.len()
507+
)));
508+
}
509+
510+
// Get the function and its type
511+
let func = &instance.module.functions[actual_func_idx as usize];
479512
let func_type = &instance.module.types[func.type_idx as usize];
480513

481514
// Check argument count
@@ -487,8 +520,8 @@ impl Engine {
487520
)));
488521
}
489522

490-
// Clone the locals and instance for use outside this scope
491-
(func.locals.clone(), instance.clone())
523+
// Clone the locals, function type, and instance for use outside this scope
524+
(func.locals.clone(), instance.clone(), func_type.clone())
492525
};
493526

494527
// Create frame
@@ -524,18 +557,43 @@ impl Engine {
524557
0
525558
};
526559

527-
// Get the function clone
528-
let func_clone = {
560+
// Get the function clone and expected results
561+
let (func_clone, expected_results) = {
529562
let instance = &self.instances[instance_idx as usize];
530-
instance.module.functions[func_idx as usize].clone()
531-
};
532563

533-
// Get expected results count
534-
let expected_results = {
535-
let instance = &self.instances[instance_idx as usize];
536-
let func = &instance.module.functions[func_idx as usize];
564+
// Determine if this is an imported function
565+
let import_count = instance
566+
.module
567+
.imports
568+
.iter()
569+
.filter(|import| matches!(import.ty, ExternType::Function(_)))
570+
.count();
571+
572+
// Adjust function index for imports
573+
let actual_func_idx = if func_idx < import_count as u32 {
574+
// We should not reach here because we already checked and returned an error above
575+
return Err(Error::Execution(
576+
"Trying to execute an imported function".into(),
577+
));
578+
} else {
579+
// This is a regular function, adjust index to skip imports
580+
func_idx - import_count as u32
581+
};
582+
583+
// Verify function index is valid
584+
if actual_func_idx as usize >= instance.module.functions.len() {
585+
return Err(Error::Execution(format!(
586+
"Function index {} out of bounds (max: {})",
587+
actual_func_idx,
588+
instance.module.functions.len()
589+
)));
590+
}
591+
592+
// Get the function and its result count
593+
let func = &instance.module.functions[actual_func_idx as usize];
537594
let func_type = &instance.module.types[func.type_idx as usize];
538-
func_type.results.len()
595+
596+
(func.clone(), func_type.results.len())
539597
};
540598

541599
// Execute function body with fuel limitation
@@ -895,6 +953,49 @@ impl Engine {
895953
let local_func_idx = *func_idx;
896954
let module_idx = frame.module.module_idx;
897955

956+
// Count imported functions that may affect the function index
957+
let import_count = frame
958+
.module
959+
.module
960+
.imports
961+
.iter()
962+
.filter(|import| matches!(import.ty, ExternType::Function(_)))
963+
.count() as u32;
964+
965+
// Check if we're calling an imported function
966+
let is_imported = local_func_idx < import_count;
967+
968+
// Check if this is an imported function call
969+
if is_imported {
970+
let import = &frame.module.module.imports[local_func_idx as usize];
971+
972+
// Special handling for the "env.print" function
973+
if import.module == "env" && import.name == "print" {
974+
// Get the parameter (expected to be an i32)
975+
let param = self.stack.pop()?;
976+
let value = param.as_i32().unwrap_or(0);
977+
978+
// Print the value to the log and to stderr for debug purposes
979+
self.handle_log(
980+
LogLevel::Info,
981+
format!("[Host function] env.print called with argument: {}", value),
982+
);
983+
984+
// Also print to stderr directly for debugging
985+
#[cfg(feature = "std")]
986+
eprintln!("[Host function] env.print called with argument: {}", value);
987+
988+
// Return without error for successful imported function execution
989+
return Ok(None);
990+
}
991+
992+
// For other imported functions, we will report they are not supported
993+
return Err(Error::Execution(format!(
994+
"Cannot call unsupported imported function at index {}: {}.{}",
995+
local_func_idx, import.module, import.name
996+
)));
997+
}
998+
898999
// Check if this is a component model custom function call (log function)
8991000
// We're looking for call to function index 1 (log) in a module with custom section "component-model-info"
9001001
let is_component_log = local_func_idx == 1
@@ -948,7 +1049,20 @@ impl Engine {
9481049
// No return value for log function
9491050
Ok(None)
9501051
} else {
951-
let func = &frame.module.module.functions[local_func_idx as usize];
1052+
// Adjust the function index to account for imported functions
1053+
let adjusted_func_idx = local_func_idx - import_count;
1054+
1055+
// Verify the adjusted index is valid
1056+
if adjusted_func_idx as usize >= frame.module.module.functions.len() {
1057+
return Err(Error::Execution(format!(
1058+
"Function index {} (adjusted to {}) out of bounds (max: {})",
1059+
local_func_idx,
1060+
adjusted_func_idx,
1061+
frame.module.module.functions.len()
1062+
)));
1063+
}
1064+
1065+
let func = &frame.module.module.functions[adjusted_func_idx as usize];
9521066
let func_type = &frame.module.module.types[func.type_idx as usize];
9531067
let params_len = func_type.params.len();
9541068

wrt/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub use global::{Global, Globals};
8686
pub use instructions::{BlockType, Instruction};
8787
pub use logging::{CallbackRegistry, LogLevel, LogOperation};
8888
pub use memory::Memory;
89-
pub use module::{ExportKind, Module};
89+
pub use module::{ExportKind, Import, Module};
9090
pub use table::Table;
9191
pub use types::{
9292
ComponentType, ExternType, FuncType, GlobalType, MemoryType, TableType, ValueType,

wrt/tests/lib.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,65 @@ mod tests {
197197
assert!(module.validate().is_ok());
198198
}
199199

200+
#[test]
201+
fn test_module_imports() {
202+
let mut module = new_module();
203+
204+
// Add a function type for the import
205+
module.types.push(FuncType {
206+
params: vec![ValueType::I32, ValueType::I32],
207+
results: vec![ValueType::I32],
208+
});
209+
210+
// Add a function import
211+
module.imports.push(Import {
212+
module: "env".to_string(),
213+
name: "add".to_string(),
214+
ty: ExternType::Function(module.types[0].clone()),
215+
});
216+
217+
// Add a memory import
218+
module.imports.push(Import {
219+
module: "env".to_string(),
220+
name: "memory".to_string(),
221+
ty: ExternType::Memory(MemoryType {
222+
min: 1,
223+
max: Some(2),
224+
}),
225+
});
226+
227+
// Verify imports were added correctly
228+
assert_eq!(module.imports.len(), 2);
229+
230+
// Check function import
231+
let func_import = &module.imports[0];
232+
assert_eq!(func_import.module, "env");
233+
assert_eq!(func_import.name, "add");
234+
if let ExternType::Function(func_type) = &func_import.ty {
235+
assert_eq!(func_type.params.len(), 2);
236+
assert_eq!(func_type.params[0], ValueType::I32);
237+
assert_eq!(func_type.params[1], ValueType::I32);
238+
assert_eq!(func_type.results.len(), 1);
239+
assert_eq!(func_type.results[0], ValueType::I32);
240+
} else {
241+
panic!("Expected function import type");
242+
}
243+
244+
// Check memory import
245+
let mem_import = &module.imports[1];
246+
assert_eq!(mem_import.module, "env");
247+
assert_eq!(mem_import.name, "memory");
248+
if let ExternType::Memory(mem_type) = &mem_import.ty {
249+
assert_eq!(mem_type.min, 1);
250+
assert_eq!(mem_type.max, Some(2));
251+
} else {
252+
panic!("Expected memory import type");
253+
}
254+
255+
// Validate module with imports
256+
assert!(module.validate().is_ok());
257+
}
258+
200259
// Logging Tests
201260
#[test]
202261
fn test_logging_system() {

0 commit comments

Comments
 (0)