Lines
97.92 %
Functions
61.54 %
Branches
100 %
use std::collections::HashMap;
use tracing::debug;
use wasm_encoder::{
ArrayType, CodeSection, CompositeInnerType, CompositeType, DataCountSection, DataSection,
EntityType as WasmEntityType, ExportKind, ExportSection, FieldType, Function, FunctionSection,
ImportSection, Instruction, MemorySection, MemoryType, Module, StorageType, SubType,
TypeSection, ValType,
};
pub struct CompileContext {
types: TypeSection,
imports: ImportSection,
functions: FunctionSection,
memories: MemorySection,
exports: ExportSection,
data: DataSection,
codes: CodeSection,
data_count: u32,
type_count: u32,
import_func_count: u32,
local_func_count: u32,
type_cache: HashMap<Vec<ValType>, HashMap<Vec<ValType>, u32>>,
type_names: HashMap<String, u32>,
func_names: HashMap<String, u32>,
}
impl CompileContext {
pub fn new() -> Self {
debug!("initializing compile context");
let mut ctx = Self {
types: TypeSection::new(),
imports: ImportSection::new(),
functions: FunctionSection::new(),
memories: MemorySection::new(),
exports: ExportSection::new(),
data: DataSection::new(),
codes: CodeSection::new(),
data_count: 0,
type_count: 0,
import_func_count: 0,
local_func_count: 0,
type_cache: HashMap::new(),
type_names: HashMap::new(),
func_names: HashMap::new(),
ctx.register_type("i8_array");
ctx.register_import("env", "get_output_offset", &[], &[ValType::I32]);
ctx.register_import("env", "symbol_resolve", &[ValType::I32, ValType::I32], &[]);
ctx.register_import(
"env",
"log",
&[ValType::I32, ValType::I32, ValType::I32],
&[],
);
ctx.register_function("should_apply", &[], &[ValType::I32]);
ctx.register_function("process", &[], &[]);
ctx.export_func("should_apply");
ctx.export_func("process");
ctx.memories.memory(MemoryType {
minimum: 1,
maximum: None,
memory64: false,
shared: false,
page_size_log2: None,
});
ctx.exports.export("memory", ExportKind::Memory, 0);
debug!("compile context initialized");
ctx
pub fn register_type(&mut self, name: &str) -> u32 {
let idx = self.type_count;
self.types.ty().subtype(&SubType {
is_final: true,
supertype_idx: None,
composite_type: CompositeType {
inner: CompositeInnerType::Array(ArrayType(FieldType {
element_type: StorageType::I8,
mutable: false,
})),
describes: None,
descriptor: None,
},
self.type_count += 1;
self.type_names.insert(name.to_string(), idx);
idx
pub fn register_import(
&mut self,
module: &str,
name: &str,
params: &[ValType],
results: &[ValType],
) -> u32 {
let type_idx = self.get_or_create_func_type(params, results);
self.imports
.import(module, name, WasmEntityType::Function(type_idx));
let func_idx = self.import_func_count;
self.import_func_count += 1;
self.func_names.insert(name.to_string(), func_idx);
func_idx
pub fn register_function(&mut self, name: &str, params: &[ValType], results: &[ValType]) {
self.functions.function(type_idx);
let func_idx = self.import_func_count + self.local_func_count;
self.local_func_count += 1;
pub fn export_func(&mut self, name: &str) {
let idx = self.func_names[name];
self.exports.export(name, ExportKind::Func, idx);
pub fn func(&self, name: &str) -> u32 {
self.func_names[name]
pub fn type_idx(&self, name: &str) -> u32 {
self.type_names[name]
fn get_or_create_func_type(&mut self, params: &[ValType], results: &[ValType]) -> u32 {
if let Some(inner) = self.type_cache.get(params)
&& let Some(&idx) = inner.get(results)
{
return idx;
self.types
.ty()
.function(params.iter().copied(), results.iter().copied());
self.type_cache
.entry(params.to_vec())
.or_default()
.insert(results.to_vec(), idx);
pub fn add_data(&mut self, bytes: &[u8]) -> u32 {
let idx = self.data_count;
debug!(idx, len = bytes.len(), "adding data segment");
self.data.passive(bytes.iter().copied());
self.data_count += 1;
pub fn add_should_apply(&mut self) {
debug!("emitting should_apply function");
let mut f = Function::new([]);
f.instruction(&Instruction::I32Const(1));
f.instruction(&Instruction::End);
self.codes.function(&f);
pub fn add_process(&mut self, f: Function) {
debug!("emitting process function");
pub fn finish(self) -> Vec<u8> {
debug!(data_segments = self.data_count, "assembling WASM module");
let mut module = Module::new();
module.section(&self.types);
module.section(&self.imports);
module.section(&self.functions);
module.section(&self.memories);
module.section(&self.exports);
module.section(&DataCountSection {
count: self.data_count,
module.section(&self.codes);
module.section(&self.data);
module.finish()
impl Default for CompileContext {
fn default() -> Self {
Self::new()