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:
copilot-swe-agent[bot]
2025-08-14 08:56:39 +00:00
parent 9b25330d94
commit 2091462441
9 changed files with 318 additions and 3 deletions

View File

@ -371,6 +371,38 @@ impl WasmCodegen {
])
},
// Phase 9.7: External Function Calls
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => {
// Generate call to external function import
let call_target = match (iface_name.as_str(), method_name.as_str()) {
("env.console", "log") => "console_log",
("env.canvas", "fillRect") => "canvas_fillRect",
("env.canvas", "fillText") => "canvas_fillText",
_ => return Err(WasmError::UnsupportedInstruction(
format!("Unsupported extern call: {}.{}", iface_name, method_name)
)),
};
let mut instructions = Vec::new();
// Load all arguments onto stack in order
for arg in args {
instructions.push(format!("local.get ${}", self.get_local_index(*arg)?));
}
// Call the external function
instructions.push(format!("call ${}", call_target));
// Store result if destination is provided
if let Some(dst) = dst {
// For void functions, we still need to provide a dummy value
instructions.push("i32.const 0".to_string()); // Void result
instructions.push(format!("local.set ${}", self.get_local_index(*dst)?));
}
Ok(instructions)
},
// Unsupported instructions
_ => Err(WasmError::UnsupportedInstruction(
format!("Instruction not yet supported: {:?}", instruction)

View File

@ -51,6 +51,44 @@ impl RuntimeImports {
result: None,
});
// Phase 9.7: Box FFI/ABI imports per BID specifications
// env.console_log for console.log(message) - (string_ptr, string_len)
self.imports.push(ImportFunction {
module: "env".to_string(),
name: "console_log".to_string(),
params: vec!["i32".to_string(), "i32".to_string()],
result: None,
});
// env.canvas_fillRect for canvas.fillRect(canvas_id, x, y, w, h, color)
// Parameters: (canvas_id_ptr, canvas_id_len, x, y, w, h, color_ptr, color_len)
self.imports.push(ImportFunction {
module: "env".to_string(),
name: "canvas_fillRect".to_string(),
params: vec![
"i32".to_string(), "i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(), "i32".to_string(), "i32".to_string(), "i32".to_string(), // x, y, w, h
"i32".to_string(), "i32".to_string(), // color (ptr, len)
],
result: None,
});
// env.canvas_fillText for canvas.fillText(canvas_id, text, x, y, font, color)
// Parameters: (canvas_id_ptr, canvas_id_len, text_ptr, text_len, x, y, font_ptr, font_len, color_ptr, color_len)
self.imports.push(ImportFunction {
module: "env".to_string(),
name: "canvas_fillText".to_string(),
params: vec![
"i32".to_string(), "i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(), "i32".to_string(), // text (ptr, len)
"i32".to_string(), "i32".to_string(), // x, y
"i32".to_string(), "i32".to_string(), // font (ptr, len)
"i32".to_string(), "i32".to_string(), // color (ptr, len)
],
result: None,
});
// Future: env.file_read, env.file_write for file I/O
// Future: env.http_request for network access
}
@ -120,6 +158,49 @@ impl RuntimeImports {
"print" => {
js.push_str(" print: (value) => console.log(value),\n");
},
"print_str" => {
js.push_str(" print_str: (ptr, len) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n");
js.push_str(" console.log(str);\n");
js.push_str(" },\n");
},
"console_log" => {
js.push_str(" console_log: (ptr, len) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n");
js.push_str(" console.log(str);\n");
js.push_str(" },\n");
},
"canvas_fillRect" => {
js.push_str(" canvas_fillRect: (canvasIdPtr, canvasIdLen, x, y, w, h, colorPtr, colorLen) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const canvasId = new TextDecoder().decode(new Uint8Array(memory.buffer, canvasIdPtr, canvasIdLen));\n");
js.push_str(" const color = new TextDecoder().decode(new Uint8Array(memory.buffer, colorPtr, colorLen));\n");
js.push_str(" const canvas = document.getElementById(canvasId);\n");
js.push_str(" if (canvas) {\n");
js.push_str(" const ctx = canvas.getContext('2d');\n");
js.push_str(" ctx.fillStyle = color;\n");
js.push_str(" ctx.fillRect(x, y, w, h);\n");
js.push_str(" }\n");
js.push_str(" },\n");
},
"canvas_fillText" => {
js.push_str(" canvas_fillText: (canvasIdPtr, canvasIdLen, textPtr, textLen, x, y, fontPtr, fontLen, colorPtr, colorLen) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const canvasId = new TextDecoder().decode(new Uint8Array(memory.buffer, canvasIdPtr, canvasIdLen));\n");
js.push_str(" const text = new TextDecoder().decode(new Uint8Array(memory.buffer, textPtr, textLen));\n");
js.push_str(" const font = new TextDecoder().decode(new Uint8Array(memory.buffer, fontPtr, fontLen));\n");
js.push_str(" const color = new TextDecoder().decode(new Uint8Array(memory.buffer, colorPtr, colorLen));\n");
js.push_str(" const canvas = document.getElementById(canvasId);\n");
js.push_str(" if (canvas) {\n");
js.push_str(" const ctx = canvas.getContext('2d');\n");
js.push_str(" ctx.font = font;\n");
js.push_str(" ctx.fillStyle = color;\n");
js.push_str(" ctx.fillText(text, x, y);\n");
js.push_str(" }\n");
js.push_str(" },\n");
},
_ => {
js.push_str(&format!(" {}: () => {{ throw new Error('Not implemented: {}'); }},\n",
function.name, function.name));