use crate::compiler::context::{ClassInfo, CompilationContext};
use crate::compiler::function::compile_function;
use crate::ir::{IRModule, IRType, MemoryLayout};
use std::collections::HashMap;
use wasm_encoder::{
CodeSection, ConstExpr, DataSection, ExportSection, FunctionSection, MemorySection, MemoryType,
Module, TypeSection, ValType,
};
fn ir_type_to_wasm_type(ir_type: &IRType) -> ValType {
match ir_type {
IRType::Float => ValType::F64,
IRType::Int | IRType::Bool | IRType::String => ValType::I32,
IRType::Class(_) => ValType::I32, IRType::List(_) | IRType::Dict(_, _) | IRType::Tuple(_) => ValType::I32, IRType::Optional(_) | IRType::Union(_) => ValType::I32, _ => ValType::I32,
}
}
pub fn compile_ir_module(ir_module: &IRModule) -> Vec<u8> {
let mut module = Module::new();
let mut ctx = CompilationContext::new();
let memory_layout = MemoryLayout::new();
for _func in &ir_module.functions {
}
let mut types = TypeSection::new();
let mut total_function_count = ir_module.functions.len();
for cls in &ir_module.classes {
total_function_count += cls.methods.len();
}
for func in &ir_module.functions {
let params: Vec<ValType> = func
.params
.iter()
.map(|param| ir_type_to_wasm_type(¶m.param_type))
.collect();
let results = vec![ir_type_to_wasm_type(&func.return_type)];
types.ty().function(params, results);
}
for cls in &ir_module.classes {
for method in &cls.methods {
let params: Vec<ValType> = method
.params
.iter()
.map(|param| ir_type_to_wasm_type(¶m.param_type))
.collect();
let results = vec![ir_type_to_wasm_type(&method.return_type)];
types.ty().function(params, results);
}
}
module.section(&types);
let mut functions = FunctionSection::new();
for _ in 0..total_function_count {
functions.function(functions.len());
}
module.section(&functions);
let mut memories = MemorySection::new();
memories.memory(MemoryType {
minimum: 1,
maximum: Some(2),
memory64: false,
shared: false,
page_size_log2: None,
});
module.section(&memories);
let mut exports = ExportSection::new();
let mut func_idx = 0u32;
for func in &ir_module.functions {
let param_types = func.params.iter().map(|p| p.param_type.clone()).collect();
ctx.add_function(&func.name, func_idx, param_types, func.return_type.clone());
exports.export(&func.name, wasm_encoder::ExportKind::Func, func_idx);
func_idx += 1;
}
for cls in &ir_module.classes {
let mut class_info = ClassInfo {
name: cls.name.clone(),
methods: HashMap::new(),
field_offsets: HashMap::new(),
instance_size: 0,
};
let mut current_offset = 4u64; for var in &cls.class_vars {
class_info
.field_offsets
.insert(var.name.clone(), current_offset);
current_offset += 8;
}
class_info.instance_size = current_offset as u32;
for method in &cls.methods {
let param_types = method.params.iter().map(|p| p.param_type.clone()).collect();
let qualified_name = format!("{}::{}", cls.name, method.name);
ctx.add_function(
&qualified_name,
func_idx,
param_types,
method.return_type.clone(),
);
class_info.methods.insert(method.name.clone(), func_idx);
exports.export(&qualified_name, wasm_encoder::ExportKind::Func, func_idx);
func_idx += 1;
}
ctx.add_class(class_info);
}
exports.export("memory", wasm_encoder::ExportKind::Memory, 0);
module.section(&exports);
let mut data = DataSection::new();
if !memory_layout.string_offsets.is_empty() {
let mut all_strings = Vec::new();
let mut offsets: Vec<(String, u32)> = memory_layout
.string_offsets
.iter()
.map(|(s, &offset)| (s.clone(), offset))
.collect();
offsets.sort_by_key(|(_s, offset)| *offset);
for (s, _) in offsets {
all_strings.extend_from_slice(s.as_bytes());
all_strings.push(0); }
data.active(0, &ConstExpr::i32_const(0), all_strings);
}
module.section(&data);
let mut codes = CodeSection::new();
for func_ir in &ir_module.functions {
let compiled_func = compile_function(func_ir, &mut ctx, &memory_layout);
codes.function(&compiled_func);
}
for cls in &ir_module.classes {
for method in &cls.methods {
let compiled_method = compile_function(method, &mut ctx, &memory_layout);
codes.function(&compiled_method);
}
}
module.section(&codes);
module.finish()
}