Implement Phase 9.7: ExternCall instruction and WASM runtime imports
Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
@ -855,7 +855,7 @@ impl MirBuilder {
|
||||
/// Build method call: object.method(arguments)
|
||||
fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Build the object expression
|
||||
let object_value = self.build_expression(object)?;
|
||||
let object_value = self.build_expression(object.clone())?;
|
||||
|
||||
// Build argument expressions
|
||||
let mut arg_values = Vec::new();
|
||||
@ -866,7 +866,70 @@ impl MirBuilder {
|
||||
// Create result value
|
||||
let result_id = self.value_gen.next();
|
||||
|
||||
// Emit a BoxCall instruction
|
||||
// Check if this is an external call (console.log, canvas.fillRect, etc.)
|
||||
if let ASTNode::Variable { name: object_name, .. } = object {
|
||||
match (object_name.as_str(), method.as_str()) {
|
||||
("console", "log") => {
|
||||
// Generate ExternCall for console.log
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None, // console.log is void
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO, // Console output is I/O
|
||||
})?;
|
||||
|
||||
// Return void value
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_id,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
("canvas", "fillRect") => {
|
||||
// Generate ExternCall for canvas.fillRect
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None, // canvas.fillRect is void
|
||||
iface_name: "env.canvas".to_string(),
|
||||
method_name: "fillRect".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO, // Canvas operations are I/O
|
||||
})?;
|
||||
|
||||
// Return void value
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_id,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
("canvas", "fillText") => {
|
||||
// Generate ExternCall for canvas.fillText
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None, // canvas.fillText is void
|
||||
iface_name: "env.canvas".to_string(),
|
||||
method_name: "fillText".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO, // Canvas operations are I/O
|
||||
})?;
|
||||
|
||||
// Return void value
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_id,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
_ => {
|
||||
// Regular method call - continue with BoxCall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit a BoxCall instruction for regular method calls
|
||||
self.emit_instruction(MirInstruction::BoxCall {
|
||||
dst: Some(result_id),
|
||||
box_val: object_value,
|
||||
|
||||
@ -273,6 +273,18 @@ pub enum MirInstruction {
|
||||
dst: ValueId,
|
||||
future: ValueId,
|
||||
},
|
||||
|
||||
// === Phase 9.7: External Function Calls (Box FFI/ABI) ===
|
||||
|
||||
/// External function call through Box FFI/ABI
|
||||
/// `%dst = extern_call interface.method(%args...)`
|
||||
ExternCall {
|
||||
dst: Option<ValueId>,
|
||||
iface_name: String, // e.g., "env.console"
|
||||
method_name: String, // e.g., "log"
|
||||
args: Vec<ValueId>,
|
||||
effects: EffectMask,
|
||||
},
|
||||
}
|
||||
|
||||
/// Constant values in MIR
|
||||
@ -389,6 +401,9 @@ impl MirInstruction {
|
||||
MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate
|
||||
MirInstruction::FutureSet { .. } => EffectMask::WRITE, // Setting future has write effects
|
||||
MirInstruction::Await { .. } => EffectMask::READ.add(Effect::Async), // Await blocks and reads
|
||||
|
||||
// Phase 9.7: External Function Calls
|
||||
MirInstruction::ExternCall { effects, .. } => *effects, // Use provided effect mask
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,7 +429,8 @@ impl MirInstruction {
|
||||
MirInstruction::Await { dst, .. } => Some(*dst),
|
||||
|
||||
MirInstruction::Call { dst, .. } |
|
||||
MirInstruction::BoxCall { dst, .. } => *dst,
|
||||
MirInstruction::BoxCall { dst, .. } |
|
||||
MirInstruction::ExternCall { dst, .. } => *dst,
|
||||
|
||||
MirInstruction::Store { .. } |
|
||||
MirInstruction::Branch { .. } |
|
||||
@ -500,6 +516,9 @@ impl MirInstruction {
|
||||
MirInstruction::FutureNew { value, .. } => vec![*value],
|
||||
MirInstruction::FutureSet { future, value } => vec![*future, *value],
|
||||
MirInstruction::Await { future, .. } => vec![*future],
|
||||
|
||||
// Phase 9.7: External Function Calls
|
||||
MirInstruction::ExternCall { args, .. } => args.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -572,6 +591,17 @@ impl fmt::Display for MirInstruction {
|
||||
write!(f, "ret void")
|
||||
}
|
||||
},
|
||||
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => {
|
||||
if let Some(dst) = dst {
|
||||
write!(f, "{} = extern_call {}.{}({}); effects: {}", dst, iface_name, method_name,
|
||||
args.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "),
|
||||
effects)
|
||||
} else {
|
||||
write!(f, "extern_call {}.{}({}); effects: {}", iface_name, method_name,
|
||||
args.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "),
|
||||
effects)
|
||||
}
|
||||
},
|
||||
_ => write!(f, "{:?}", self), // Fallback for other instructions
|
||||
}
|
||||
}
|
||||
@ -730,4 +760,34 @@ mod tests {
|
||||
assert!(write_barrier.effects().contains(super::super::effect::Effect::Barrier));
|
||||
assert!(write_barrier.effects().contains(super::super::effect::Effect::WriteHeap));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extern_call_instruction() {
|
||||
let dst = ValueId::new(0);
|
||||
let arg1 = ValueId::new(1);
|
||||
let arg2 = ValueId::new(2);
|
||||
let inst = MirInstruction::ExternCall {
|
||||
dst: Some(dst),
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: vec![arg1, arg2],
|
||||
effects: super::super::effect::EffectMask::IO,
|
||||
};
|
||||
|
||||
assert_eq!(inst.dst_value(), Some(dst));
|
||||
assert_eq!(inst.used_values(), vec![arg1, arg2]);
|
||||
assert_eq!(inst.effects(), super::super::effect::EffectMask::IO);
|
||||
|
||||
// Test void extern call
|
||||
let void_inst = MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.canvas".to_string(),
|
||||
method_name: "fillRect".to_string(),
|
||||
args: vec![arg1],
|
||||
effects: super::super::effect::EffectMask::IO,
|
||||
};
|
||||
|
||||
assert_eq!(void_inst.dst_value(), None);
|
||||
assert_eq!(void_inst.used_values(), vec![arg1]);
|
||||
}
|
||||
}
|
||||
@ -360,6 +360,16 @@ impl MirPrinter {
|
||||
MirInstruction::Await { dst, future } => {
|
||||
format!("{} = await {}", dst, future)
|
||||
},
|
||||
|
||||
// Phase 9.7: External Function Calls
|
||||
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => {
|
||||
let args_str = args.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", ");
|
||||
if let Some(dst) = dst {
|
||||
format!("{} = extern_call {}.{}({}) [effects: {}]", dst, iface_name, method_name, args_str, effects)
|
||||
} else {
|
||||
format!("extern_call {}.{}({}) [effects: {}]", iface_name, method_name, args_str, effects)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user