use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use wasm_encoder::{
CodeSection, ConstExpr as EncConstExpr, DataSection, ElementSection, Elements,
ExportKind as EncExportKind, ExportSection, Function, FunctionSection, GlobalSection,
GlobalType, HeapType, ImportSection, Instruction, MemorySection, MemoryType, Module, RefType,
TableSection, TableType, TypeSection, ValType,
};
use super::error::ComposeError;
use super::parser::{
BlockType, ConstExpr, DataSegment, DataSegmentKind, Element, ElementItems, ElementKind,
ExportKind, FunctionCode, Global, ImportKind, MemArg, ParsedModule, RefTypeKind,
StoredOperator,
};
#[derive(Debug, Clone)]
pub struct Wiring {
pub consumer: String,
pub import_module: String,
pub import_fn: String,
pub provider: String,
pub provider_export: String,
}
#[derive(Debug, Clone)]
pub struct ExportSpec {
pub name: String,
pub source_module: String,
pub source_export: String,
}
pub struct Merger {
modules: Vec<ParsedModule>,
wirings: Vec<Wiring>,
exports: Vec<ExportSpec>,
}
#[derive(Debug, Default)]
struct IndexRemap {
types: HashMap<u32, u32>,
functions: HashMap<u32, u32>,
tables: HashMap<u32, u32>,
memories: HashMap<u32, u32>,
globals: HashMap<u32, u32>,
}
impl Merger {
pub fn new(modules: Vec<ParsedModule>, wirings: Vec<Wiring>, exports: Vec<ExportSpec>) -> Self {
Self {
modules,
wirings,
exports,
}
}
pub fn merge(self) -> Result<Vec<u8>, ComposeError> {
if self.modules.is_empty() {
return Err(ComposeError::NoModules);
}
let sorted_modules = self.topological_sort()?;
let mut merged = MergedModule::new();
let mut module_remaps: HashMap<String, IndexRemap> = HashMap::new();
let mut resolved_imports: HashMap<(String, String, String), u32> = HashMap::new();
for module in &sorted_modules {
let mut remap = IndexRemap::default();
for (old_idx, func_type) in module.types.iter().enumerate() {
let new_idx = merged.add_type(func_type);
remap.types.insert(old_idx as u32, new_idx);
}
module_remaps.insert(module.name.clone(), remap);
}
let mut import_resolutions: HashMap<(String, String, String), (String, String)> =
HashMap::new();
for wiring in &self.wirings {
import_resolutions.insert(
(
wiring.consumer.clone(),
wiring.import_module.clone(),
wiring.import_fn.clone(),
),
(wiring.provider.clone(), wiring.provider_export.clone()),
);
}
for module in &sorted_modules {
let remap = module_remaps.get_mut(&module.name).unwrap();
let mut import_func_idx = 0u32;
let mut import_table_idx = 0u32;
let mut import_memory_idx = 0u32;
let mut import_global_idx = 0u32;
for import in &module.imports {
match &import.kind {
ImportKind::Function(type_idx) => {
let old_func_idx = import_func_idx;
import_func_idx += 1;
let key = (
module.name.clone(),
import.module.clone(),
import.name.clone(),
);
if import_resolutions.contains_key(&key) {
let placeholder = u32::MAX; remap.functions.insert(old_func_idx, placeholder);
resolved_imports.insert(key, old_func_idx);
} else {
let new_type_idx = remap.types[type_idx];
let new_func_idx = merged.add_import_function(
&import.module,
&import.name,
new_type_idx,
);
remap.functions.insert(old_func_idx, new_func_idx);
}
}
ImportKind::Table(table_ty) => {
let old_idx = import_table_idx;
import_table_idx += 1;
let new_idx =
merged.add_import_table(&import.module, &import.name, table_ty);
remap.tables.insert(old_idx, new_idx);
}
ImportKind::Memory(mem_ty) => {
let old_idx = import_memory_idx;
import_memory_idx += 1;
let new_idx =
merged.add_import_memory(&import.module, &import.name, mem_ty);
remap.memories.insert(old_idx, new_idx);
}
ImportKind::Global(global_ty) => {
let old_idx = import_global_idx;
import_global_idx += 1;
let new_idx =
merged.add_import_global(&import.module, &import.name, global_ty);
remap.globals.insert(old_idx, new_idx);
}
}
}
}
for module in &sorted_modules {
let remap = module_remaps.get_mut(&module.name).unwrap();
for (i, table_ty) in module.tables.iter().enumerate() {
let old_idx = module.num_imported_tables + i as u32;
let new_idx = merged.add_table(table_ty);
remap.tables.insert(old_idx, new_idx);
}
for (i, mem_ty) in module.memories.iter().enumerate() {
let old_idx = module.num_imported_memories + i as u32;
let new_idx = if merged.memories.is_empty() {
merged.add_memory(mem_ty)
} else {
merged.expand_memory(mem_ty);
0 };
remap.memories.insert(old_idx, new_idx);
}
for (i, global) in module.globals.iter().enumerate() {
let old_idx = module.num_imported_globals + i as u32;
let (new_idx, _unified) = merged.add_global(global, remap);
remap.globals.insert(old_idx, new_idx);
}
for (i, &type_idx) in module.functions.iter().enumerate() {
let old_func_idx = module.num_imported_functions + i as u32;
let new_type_idx = remap.types[&type_idx];
let new_func_idx = merged.add_function(new_type_idx);
remap.functions.insert(old_func_idx, new_func_idx);
}
}
for wiring in &self.wirings {
let provider_module = sorted_modules
.iter()
.find(|m| m.name == wiring.provider)
.ok_or_else(|| ComposeError::ModuleNotFound(wiring.provider.clone()))?;
let export = provider_module
.get_export(&wiring.provider_export)
.ok_or_else(|| ComposeError::FunctionNotFound {
module: wiring.provider.clone(),
function: wiring.provider_export.clone(),
})?;
if export.kind != ExportKind::Function {
return Err(ComposeError::FunctionNotFound {
module: wiring.provider.clone(),
function: wiring.provider_export.clone(),
});
}
let provider_remap = &module_remaps[&wiring.provider];
let new_func_idx = provider_remap.functions[&export.index];
let consumer_remap = module_remaps.get_mut(&wiring.consumer).unwrap();
let key = (
wiring.consumer.clone(),
wiring.import_module.clone(),
wiring.import_fn.clone(),
);
if let Some(&old_import_idx) = resolved_imports.get(&key) {
consumer_remap
.functions
.insert(old_import_idx, new_func_idx);
}
}
for module in &sorted_modules {
let remap = &module_remaps[&module.name];
for code in &module.code {
let func = remap_function_body(code, remap);
merged.add_code(func);
}
}
let mut cumulative_data_offset = 0u32;
for module in &sorted_modules {
let remap = &module_remaps[&module.name];
for data in &module.data {
merged.add_data(data, remap, cumulative_data_offset);
}
cumulative_data_offset = (merged.highest_data_addr + 7) & !7;
}
for module in &sorted_modules {
let remap = &module_remaps[&module.name];
for elem in &module.elements {
merged.add_element(elem, remap);
}
}
for export_spec in &self.exports {
let source_module = sorted_modules
.iter()
.find(|m| m.name == export_spec.source_module)
.ok_or_else(|| ComposeError::ModuleNotFound(export_spec.source_module.clone()))?;
let export = source_module
.get_export(&export_spec.source_export)
.ok_or_else(|| ComposeError::FunctionNotFound {
module: export_spec.source_module.clone(),
function: export_spec.source_export.clone(),
})?;
let remap = &module_remaps[&export_spec.source_module];
let new_idx = match export.kind {
ExportKind::Function => remap.functions[&export.index],
ExportKind::Table => remap.tables[&export.index],
ExportKind::Memory => remap.memories[&export.index],
ExportKind::Global => remap.globals[&export.index],
};
merged.add_export(&export_spec.name, export.kind, new_idx);
}
merged.encode()
}
fn topological_sort(&self) -> Result<Vec<&ParsedModule>, ComposeError> {
let module_map: HashMap<&str, &ParsedModule> =
self.modules.iter().map(|m| (m.name.as_str(), m)).collect();
let mut deps: HashMap<&str, HashSet<&str>> = HashMap::new();
for module in &self.modules {
deps.insert(&module.name, HashSet::new());
}
for wiring in &self.wirings {
if let Some(dep_set) = deps.get_mut(wiring.consumer.as_str()) {
if module_map.contains_key(wiring.provider.as_str()) {
dep_set.insert(wiring.provider.as_str());
}
}
}
let mut in_degree: HashMap<&str, usize> = HashMap::new();
for module in &self.modules {
in_degree.insert(&module.name, 0);
}
for providers in deps.values() {
for provider in providers {
*in_degree.get_mut(provider).unwrap() += 1;
}
}
let mut queue: Vec<&str> = in_degree
.iter()
.filter(|(_, °)| deg == 0)
.map(|(&name, _)| name)
.collect();
let mut result = Vec::new();
while let Some(name) = queue.pop() {
result.push(module_map[name]);
if let Some(providers) = deps.get(name) {
for provider in providers {
let deg = in_degree.get_mut(provider).unwrap();
*deg -= 1;
if *deg == 0 {
queue.push(provider);
}
}
}
}
if result.len() != self.modules.len() {
return Err(ComposeError::CircularDependency {
cycle: self.modules.iter().map(|m| m.name.clone()).collect(),
});
}
result.reverse();
Ok(result)
}
}
struct MergedModule {
types: Vec<wasmparser::FuncType>,
type_dedup: HashMap<TypeKey, u32>,
imports: Vec<MergedImport>,
num_imported_functions: u32,
num_imported_tables: u32,
num_imported_memories: u32,
num_imported_globals: u32,
functions: Vec<u32>, tables: Vec<wasmparser::TableType>,
memories: Vec<wasmparser::MemoryType>,
globals: Vec<(wasmparser::GlobalType, ConstExpr)>,
exports: Vec<(String, ExportKind, u32)>,
code: Vec<Function>,
data: Vec<(DataSegmentKind, Vec<u8>)>,
elements: Vec<MergedElement>,
highest_data_addr: u32,
unified_heap_ptr: Option<u32>,
unified_enc_tuple_sp: Option<u32>,
}
struct MergedImport {
module: String,
name: String,
kind: ImportKind,
}
struct MergedElement {
kind: ElementKind,
items: Vec<u32>, }
#[derive(Hash, Eq, PartialEq)]
struct TypeKey {
params: Vec<u8>,
results: Vec<u8>,
}
impl TypeKey {
fn from_func_type(ty: &wasmparser::FuncType) -> Self {
Self {
params: ty.params().iter().map(val_type_byte).collect(),
results: ty.results().iter().map(val_type_byte).collect(),
}
}
}
fn val_type_byte(ty: &wasmparser::ValType) -> u8 {
match ty {
wasmparser::ValType::I32 => 0x7F,
wasmparser::ValType::I64 => 0x7E,
wasmparser::ValType::F32 => 0x7D,
wasmparser::ValType::F64 => 0x7C,
wasmparser::ValType::V128 => 0x7B,
wasmparser::ValType::Ref(r) => {
if r.is_func_ref() {
0x70
} else {
0x6F
}
}
}
}
impl MergedModule {
fn new() -> Self {
Self {
types: Vec::new(),
type_dedup: HashMap::new(),
imports: Vec::new(),
num_imported_functions: 0,
num_imported_tables: 0,
num_imported_memories: 0,
num_imported_globals: 0,
functions: Vec::new(),
tables: Vec::new(),
memories: Vec::new(),
globals: Vec::new(),
exports: Vec::new(),
code: Vec::new(),
data: Vec::new(),
elements: Vec::new(),
highest_data_addr: 0,
unified_heap_ptr: None,
unified_enc_tuple_sp: None,
}
}
fn add_type(&mut self, func_type: &wasmparser::FuncType) -> u32 {
let key = TypeKey::from_func_type(func_type);
if let Some(&idx) = self.type_dedup.get(&key) {
return idx;
}
let idx = self.types.len() as u32;
self.types.push(func_type.clone());
self.type_dedup.insert(key, idx);
idx
}
fn next_function_index(&self) -> u32 {
self.num_imported_functions + self.functions.len() as u32
}
fn next_table_index(&self) -> u32 {
self.num_imported_tables + self.tables.len() as u32
}
fn next_memory_index(&self) -> u32 {
self.num_imported_memories + self.memories.len() as u32
}
fn next_global_index(&self) -> u32 {
self.num_imported_globals + self.globals.len() as u32
}
fn add_import_function(&mut self, module: &str, name: &str, type_idx: u32) -> u32 {
let idx = self.num_imported_functions;
self.num_imported_functions += 1;
self.imports.push(MergedImport {
module: module.to_string(),
name: name.to_string(),
kind: ImportKind::Function(type_idx),
});
idx
}
fn add_import_table(
&mut self,
module: &str,
name: &str,
table_ty: &wasmparser::TableType,
) -> u32 {
let idx = self.num_imported_tables;
self.num_imported_tables += 1;
self.imports.push(MergedImport {
module: module.to_string(),
name: name.to_string(),
kind: ImportKind::Table(*table_ty),
});
idx
}
fn add_import_memory(
&mut self,
module: &str,
name: &str,
mem_ty: &wasmparser::MemoryType,
) -> u32 {
let idx = self.num_imported_memories;
self.num_imported_memories += 1;
self.imports.push(MergedImport {
module: module.to_string(),
name: name.to_string(),
kind: ImportKind::Memory(*mem_ty),
});
idx
}
fn add_import_global(
&mut self,
module: &str,
name: &str,
global_ty: &wasmparser::GlobalType,
) -> u32 {
let idx = self.num_imported_globals;
self.num_imported_globals += 1;
self.imports.push(MergedImport {
module: module.to_string(),
name: name.to_string(),
kind: ImportKind::Global(*global_ty),
});
idx
}
fn add_table(&mut self, table_ty: &wasmparser::TableType) -> u32 {
let idx = self.next_table_index();
self.tables.push(*table_ty);
idx
}
fn add_memory(&mut self, mem_ty: &wasmparser::MemoryType) -> u32 {
let idx = self.next_memory_index();
self.memories.push(*mem_ty);
idx
}
fn expand_memory(&mut self, mem_ty: &wasmparser::MemoryType) {
if self.memories.is_empty() {
return;
}
let shared = &mut self.memories[0];
shared.initial = shared.initial.max(mem_ty.initial);
shared.maximum = match (shared.maximum, mem_ty.maximum) {
(Some(a), Some(b)) => Some(a.max(b)),
(Some(a), None) | (None, Some(a)) => Some(a),
(None, None) => None,
};
}
fn add_global(&mut self, global: &Global, _remap: &IndexRemap) -> (u32, bool) {
if self.is_heap_ptr_global(global) {
if let Some(unified_idx) = self.unified_heap_ptr {
return (unified_idx, true);
} else {
let idx = self.next_global_index();
self.globals.push((global.ty, global.init_expr.clone()));
self.unified_heap_ptr = Some(idx);
return (idx, false);
}
}
if self.is_enc_tuple_sp_global(global) {
if let Some(unified_idx) = self.unified_enc_tuple_sp {
return (unified_idx, true);
} else {
let idx = self.next_global_index();
self.globals.push((global.ty, global.init_expr.clone()));
self.unified_enc_tuple_sp = Some(idx);
return (idx, false);
}
}
let idx = self.next_global_index();
self.globals.push((global.ty, global.init_expr.clone()));
(idx, false)
}
fn is_heap_ptr_global(&self, global: &Global) -> bool {
if !global.ty.mutable {
return false;
}
if global.ty.content_type != wasmparser::ValType::I32 {
return false;
}
matches!(global.init_expr, ConstExpr::I32Const(49152))
}
fn is_enc_tuple_sp_global(&self, global: &Global) -> bool {
if !global.ty.mutable {
return false;
}
if global.ty.content_type != wasmparser::ValType::I32 {
return false;
}
matches!(global.init_expr, ConstExpr::I32Const(45056))
}
fn add_function(&mut self, type_idx: u32) -> u32 {
let idx = self.next_function_index();
self.functions.push(type_idx);
idx
}
fn add_code(&mut self, func: Function) {
self.code.push(func);
}
fn add_data(&mut self, data: &DataSegment, remap: &IndexRemap, data_offset: u32) {
let kind = match &data.kind {
DataSegmentKind::Active {
memory_index,
offset_expr,
} => {
let new_mem_idx = remap
.memories
.get(memory_index)
.copied()
.unwrap_or(*memory_index);
let new_offset = match offset_expr {
ConstExpr::I32Const(orig_offset) => {
let relocated = (*orig_offset as u32).saturating_add(data_offset);
let data_end = relocated + data.data.len() as u32;
if data_end > self.highest_data_addr {
self.highest_data_addr = data_end;
}
ConstExpr::I32Const(relocated as i32)
}
_ => {
self.highest_data_addr = self.highest_data_addr.max(data.data.len() as u32);
offset_expr.clone()
}
};
DataSegmentKind::Active {
memory_index: new_mem_idx,
offset_expr: new_offset,
}
}
DataSegmentKind::Passive => DataSegmentKind::Passive,
};
self.data.push((kind, data.data.clone()));
}
fn add_element(&mut self, elem: &Element, remap: &IndexRemap) {
let items = match &elem.items {
ElementItems::Functions(funcs) => funcs
.iter()
.map(|&idx| remap.functions.get(&idx).copied().unwrap_or(idx))
.collect(),
ElementItems::Expressions(_) => {
return;
}
};
let kind = match &elem.kind {
ElementKind::Active {
table_index,
offset_expr,
} => {
let new_table_idx = remap
.tables
.get(table_index)
.copied()
.unwrap_or(*table_index);
ElementKind::Active {
table_index: new_table_idx,
offset_expr: offset_expr.clone(),
}
}
ElementKind::Passive => ElementKind::Passive,
ElementKind::Declared => ElementKind::Declared,
};
self.elements.push(MergedElement { kind, items });
}
fn add_export(&mut self, name: &str, kind: ExportKind, index: u32) {
self.exports.push((name.to_string(), kind, index));
}
fn encode(self) -> Result<Vec<u8>, ComposeError> {
let mut module = Module::new();
if !self.types.is_empty() {
let mut types = TypeSection::new();
for func_type in &self.types {
let params: Vec<ValType> =
func_type.params().iter().map(convert_val_type).collect();
let results: Vec<ValType> =
func_type.results().iter().map(convert_val_type).collect();
types.ty().function(params, results);
}
module.section(&types);
}
if !self.imports.is_empty() {
let mut imports = ImportSection::new();
for import in &self.imports {
match &import.kind {
ImportKind::Function(type_idx) => {
imports.import(
&import.module,
&import.name,
wasm_encoder::EntityType::Function(*type_idx),
);
}
ImportKind::Table(table_ty) => {
let enc_ty = convert_table_type(table_ty);
imports.import(&import.module, &import.name, enc_ty);
}
ImportKind::Memory(mem_ty) => {
let enc_ty = convert_memory_type(mem_ty);
imports.import(&import.module, &import.name, enc_ty);
}
ImportKind::Global(global_ty) => {
let enc_ty = convert_global_type(global_ty);
imports.import(&import.module, &import.name, enc_ty);
}
}
}
module.section(&imports);
}
if !self.functions.is_empty() {
let mut functions = FunctionSection::new();
for &type_idx in &self.functions {
functions.function(type_idx);
}
module.section(&functions);
}
if !self.tables.is_empty() {
let mut tables = TableSection::new();
for table_ty in &self.tables {
let enc_ty = convert_table_type(table_ty);
tables.table(enc_ty);
}
module.section(&tables);
}
if !self.memories.is_empty() {
let mut memories = MemorySection::new();
for mem_ty in &self.memories {
let enc_ty = convert_memory_type(mem_ty);
memories.memory(enc_ty);
}
module.section(&memories);
}
if !self.globals.is_empty() {
let mut globals = GlobalSection::new();
for (global_ty, init_expr) in &self.globals {
let enc_ty = convert_global_type(global_ty);
let enc_expr = convert_const_expr(init_expr);
globals.global(enc_ty, &enc_expr);
}
module.section(&globals);
}
if !self.exports.is_empty() {
let mut exports = ExportSection::new();
for (name, kind, index) in &self.exports {
let enc_kind = match kind {
ExportKind::Function => EncExportKind::Func,
ExportKind::Table => EncExportKind::Table,
ExportKind::Memory => EncExportKind::Memory,
ExportKind::Global => EncExportKind::Global,
};
exports.export(name, enc_kind, *index);
}
module.section(&exports);
}
if !self.elements.is_empty() {
let mut elements = ElementSection::new();
for elem in &self.elements {
match &elem.kind {
ElementKind::Active {
table_index,
offset_expr,
} => {
let offset = convert_const_expr(offset_expr);
elements.active(
Some(*table_index),
&offset,
Elements::Functions(Cow::Borrowed(&elem.items)),
);
}
ElementKind::Passive => {
elements.passive(Elements::Functions(Cow::Borrowed(&elem.items)));
}
ElementKind::Declared => {
elements.declared(Elements::Functions(Cow::Borrowed(&elem.items)));
}
}
}
module.section(&elements);
}
if !self.code.is_empty() {
let mut code = CodeSection::new();
for func in &self.code {
code.function(func);
}
module.section(&code);
}
if !self.data.is_empty() {
let mut data = DataSection::new();
for (kind, bytes) in &self.data {
match kind {
DataSegmentKind::Active {
memory_index,
offset_expr,
} => {
let offset = convert_const_expr(offset_expr);
data.active(*memory_index, &offset, bytes.iter().copied());
}
DataSegmentKind::Passive => {
data.passive(bytes.iter().copied());
}
}
}
module.section(&data);
}
Ok(module.finish())
}
}
fn remap_function_body(code: &FunctionCode, remap: &IndexRemap) -> Function {
let mut func = Function::new(
code.locals
.iter()
.map(|(count, ty)| (*count, convert_val_type(ty))),
);
for op in &code.operators {
let inst = convert_stored_operator(op, remap);
func.instruction(&inst);
}
func
}
fn convert_stored_operator(op: &StoredOperator, remap: &IndexRemap) -> Instruction<'static> {
match op {
StoredOperator::Unreachable => Instruction::Unreachable,
StoredOperator::Nop => Instruction::Nop,
StoredOperator::Block(bt) => Instruction::Block(convert_block_type(bt, remap)),
StoredOperator::Loop(bt) => Instruction::Loop(convert_block_type(bt, remap)),
StoredOperator::If(bt) => Instruction::If(convert_block_type(bt, remap)),
StoredOperator::Else => Instruction::Else,
StoredOperator::End => Instruction::End,
StoredOperator::Br(depth) => Instruction::Br(*depth),
StoredOperator::BrIf(depth) => Instruction::BrIf(*depth),
StoredOperator::BrTable { targets, default } => {
Instruction::BrTable(Cow::Owned(targets.clone()), *default)
}
StoredOperator::Return => Instruction::Return,
StoredOperator::Call(idx) => {
let new_idx = remap.functions.get(idx).copied().unwrap_or(*idx);
Instruction::Call(new_idx)
}
StoredOperator::CallIndirect {
type_index,
table_index,
} => {
let new_type = remap.types.get(type_index).copied().unwrap_or(*type_index);
let new_table = remap
.tables
.get(table_index)
.copied()
.unwrap_or(*table_index);
Instruction::CallIndirect {
type_index: new_type,
table_index: new_table,
}
}
StoredOperator::ReturnCall(idx) => {
let new_idx = remap.functions.get(idx).copied().unwrap_or(*idx);
Instruction::ReturnCall(new_idx)
}
StoredOperator::ReturnCallIndirect {
type_index,
table_index,
} => {
let new_type = remap.types.get(type_index).copied().unwrap_or(*type_index);
let new_table = remap
.tables
.get(table_index)
.copied()
.unwrap_or(*table_index);
Instruction::ReturnCallIndirect {
type_index: new_type,
table_index: new_table,
}
}
StoredOperator::Drop => Instruction::Drop,
StoredOperator::Select => Instruction::Select,
StoredOperator::TypedSelect(ty) => Instruction::TypedSelect(convert_val_type(ty)),
StoredOperator::LocalGet(idx) => Instruction::LocalGet(*idx),
StoredOperator::LocalSet(idx) => Instruction::LocalSet(*idx),
StoredOperator::LocalTee(idx) => Instruction::LocalTee(*idx),
StoredOperator::GlobalGet(idx) => {
let new_idx = remap.globals.get(idx).copied().unwrap_or(*idx);
Instruction::GlobalGet(new_idx)
}
StoredOperator::GlobalSet(idx) => {
let new_idx = remap.globals.get(idx).copied().unwrap_or(*idx);
Instruction::GlobalSet(new_idx)
}
StoredOperator::TableGet(idx) => {
let new_idx = remap.tables.get(idx).copied().unwrap_or(*idx);
Instruction::TableGet(new_idx)
}
StoredOperator::TableSet(idx) => {
let new_idx = remap.tables.get(idx).copied().unwrap_or(*idx);
Instruction::TableSet(new_idx)
}
StoredOperator::TableGrow(idx) => {
let new_idx = remap.tables.get(idx).copied().unwrap_or(*idx);
Instruction::TableGrow(new_idx)
}
StoredOperator::TableSize(idx) => {
let new_idx = remap.tables.get(idx).copied().unwrap_or(*idx);
Instruction::TableSize(new_idx)
}
StoredOperator::TableFill(idx) => {
let new_idx = remap.tables.get(idx).copied().unwrap_or(*idx);
Instruction::TableFill(new_idx)
}
StoredOperator::TableCopy { dst, src } => {
let new_dst = remap.tables.get(dst).copied().unwrap_or(*dst);
let new_src = remap.tables.get(src).copied().unwrap_or(*src);
Instruction::TableCopy {
src_table: new_src,
dst_table: new_dst,
}
}
StoredOperator::TableInit { elem, table } => {
let new_table = remap.tables.get(table).copied().unwrap_or(*table);
Instruction::TableInit {
elem_index: *elem,
table: new_table,
}
}
StoredOperator::ElemDrop(idx) => Instruction::ElemDrop(*idx),
StoredOperator::I32Load(m) => Instruction::I32Load(convert_memarg(m, remap)),
StoredOperator::I64Load(m) => Instruction::I64Load(convert_memarg(m, remap)),
StoredOperator::F32Load(m) => Instruction::F32Load(convert_memarg(m, remap)),
StoredOperator::F64Load(m) => Instruction::F64Load(convert_memarg(m, remap)),
StoredOperator::I32Load8S(m) => Instruction::I32Load8S(convert_memarg(m, remap)),
StoredOperator::I32Load8U(m) => Instruction::I32Load8U(convert_memarg(m, remap)),
StoredOperator::I32Load16S(m) => Instruction::I32Load16S(convert_memarg(m, remap)),
StoredOperator::I32Load16U(m) => Instruction::I32Load16U(convert_memarg(m, remap)),
StoredOperator::I64Load8S(m) => Instruction::I64Load8S(convert_memarg(m, remap)),
StoredOperator::I64Load8U(m) => Instruction::I64Load8U(convert_memarg(m, remap)),
StoredOperator::I64Load16S(m) => Instruction::I64Load16S(convert_memarg(m, remap)),
StoredOperator::I64Load16U(m) => Instruction::I64Load16U(convert_memarg(m, remap)),
StoredOperator::I64Load32S(m) => Instruction::I64Load32S(convert_memarg(m, remap)),
StoredOperator::I64Load32U(m) => Instruction::I64Load32U(convert_memarg(m, remap)),
StoredOperator::I32Store(m) => Instruction::I32Store(convert_memarg(m, remap)),
StoredOperator::I64Store(m) => Instruction::I64Store(convert_memarg(m, remap)),
StoredOperator::F32Store(m) => Instruction::F32Store(convert_memarg(m, remap)),
StoredOperator::F64Store(m) => Instruction::F64Store(convert_memarg(m, remap)),
StoredOperator::I32Store8(m) => Instruction::I32Store8(convert_memarg(m, remap)),
StoredOperator::I32Store16(m) => Instruction::I32Store16(convert_memarg(m, remap)),
StoredOperator::I64Store8(m) => Instruction::I64Store8(convert_memarg(m, remap)),
StoredOperator::I64Store16(m) => Instruction::I64Store16(convert_memarg(m, remap)),
StoredOperator::I64Store32(m) => Instruction::I64Store32(convert_memarg(m, remap)),
StoredOperator::MemorySize(idx) => {
let new_idx = remap.memories.get(idx).copied().unwrap_or(*idx);
Instruction::MemorySize(new_idx)
}
StoredOperator::MemoryGrow(idx) => {
let new_idx = remap.memories.get(idx).copied().unwrap_or(*idx);
Instruction::MemoryGrow(new_idx)
}
StoredOperator::MemoryInit { data, mem } => {
let new_mem = remap.memories.get(mem).copied().unwrap_or(*mem);
Instruction::MemoryInit {
data_index: *data,
mem: new_mem,
}
}
StoredOperator::DataDrop(idx) => Instruction::DataDrop(*idx),
StoredOperator::MemoryCopy { dst, src } => {
let new_dst = remap.memories.get(dst).copied().unwrap_or(*dst);
let new_src = remap.memories.get(src).copied().unwrap_or(*src);
Instruction::MemoryCopy {
src_mem: new_src,
dst_mem: new_dst,
}
}
StoredOperator::MemoryFill(idx) => {
let new_idx = remap.memories.get(idx).copied().unwrap_or(*idx);
Instruction::MemoryFill(new_idx)
}
StoredOperator::I32Const(v) => Instruction::I32Const(*v),
StoredOperator::I64Const(v) => Instruction::I64Const(*v),
StoredOperator::F32Const(v) => Instruction::F32Const(*v),
StoredOperator::F64Const(v) => Instruction::F64Const(*v),
StoredOperator::I32Eqz => Instruction::I32Eqz,
StoredOperator::I32Eq => Instruction::I32Eq,
StoredOperator::I32Ne => Instruction::I32Ne,
StoredOperator::I32LtS => Instruction::I32LtS,
StoredOperator::I32LtU => Instruction::I32LtU,
StoredOperator::I32GtS => Instruction::I32GtS,
StoredOperator::I32GtU => Instruction::I32GtU,
StoredOperator::I32LeS => Instruction::I32LeS,
StoredOperator::I32LeU => Instruction::I32LeU,
StoredOperator::I32GeS => Instruction::I32GeS,
StoredOperator::I32GeU => Instruction::I32GeU,
StoredOperator::I64Eqz => Instruction::I64Eqz,
StoredOperator::I64Eq => Instruction::I64Eq,
StoredOperator::I64Ne => Instruction::I64Ne,
StoredOperator::I64LtS => Instruction::I64LtS,
StoredOperator::I64LtU => Instruction::I64LtU,
StoredOperator::I64GtS => Instruction::I64GtS,
StoredOperator::I64GtU => Instruction::I64GtU,
StoredOperator::I64LeS => Instruction::I64LeS,
StoredOperator::I64LeU => Instruction::I64LeU,
StoredOperator::I64GeS => Instruction::I64GeS,
StoredOperator::I64GeU => Instruction::I64GeU,
StoredOperator::F32Eq => Instruction::F32Eq,
StoredOperator::F32Ne => Instruction::F32Ne,
StoredOperator::F32Lt => Instruction::F32Lt,
StoredOperator::F32Gt => Instruction::F32Gt,
StoredOperator::F32Le => Instruction::F32Le,
StoredOperator::F32Ge => Instruction::F32Ge,
StoredOperator::F64Eq => Instruction::F64Eq,
StoredOperator::F64Ne => Instruction::F64Ne,
StoredOperator::F64Lt => Instruction::F64Lt,
StoredOperator::F64Gt => Instruction::F64Gt,
StoredOperator::F64Le => Instruction::F64Le,
StoredOperator::F64Ge => Instruction::F64Ge,
StoredOperator::I32Clz => Instruction::I32Clz,
StoredOperator::I32Ctz => Instruction::I32Ctz,
StoredOperator::I32Popcnt => Instruction::I32Popcnt,
StoredOperator::I32Add => Instruction::I32Add,
StoredOperator::I32Sub => Instruction::I32Sub,
StoredOperator::I32Mul => Instruction::I32Mul,
StoredOperator::I32DivS => Instruction::I32DivS,
StoredOperator::I32DivU => Instruction::I32DivU,
StoredOperator::I32RemS => Instruction::I32RemS,
StoredOperator::I32RemU => Instruction::I32RemU,
StoredOperator::I32And => Instruction::I32And,
StoredOperator::I32Or => Instruction::I32Or,
StoredOperator::I32Xor => Instruction::I32Xor,
StoredOperator::I32Shl => Instruction::I32Shl,
StoredOperator::I32ShrS => Instruction::I32ShrS,
StoredOperator::I32ShrU => Instruction::I32ShrU,
StoredOperator::I32Rotl => Instruction::I32Rotl,
StoredOperator::I32Rotr => Instruction::I32Rotr,
StoredOperator::I64Clz => Instruction::I64Clz,
StoredOperator::I64Ctz => Instruction::I64Ctz,
StoredOperator::I64Popcnt => Instruction::I64Popcnt,
StoredOperator::I64Add => Instruction::I64Add,
StoredOperator::I64Sub => Instruction::I64Sub,
StoredOperator::I64Mul => Instruction::I64Mul,
StoredOperator::I64DivS => Instruction::I64DivS,
StoredOperator::I64DivU => Instruction::I64DivU,
StoredOperator::I64RemS => Instruction::I64RemS,
StoredOperator::I64RemU => Instruction::I64RemU,
StoredOperator::I64And => Instruction::I64And,
StoredOperator::I64Or => Instruction::I64Or,
StoredOperator::I64Xor => Instruction::I64Xor,
StoredOperator::I64Shl => Instruction::I64Shl,
StoredOperator::I64ShrS => Instruction::I64ShrS,
StoredOperator::I64ShrU => Instruction::I64ShrU,
StoredOperator::I64Rotl => Instruction::I64Rotl,
StoredOperator::I64Rotr => Instruction::I64Rotr,
StoredOperator::F32Abs => Instruction::F32Abs,
StoredOperator::F32Neg => Instruction::F32Neg,
StoredOperator::F32Ceil => Instruction::F32Ceil,
StoredOperator::F32Floor => Instruction::F32Floor,
StoredOperator::F32Trunc => Instruction::F32Trunc,
StoredOperator::F32Nearest => Instruction::F32Nearest,
StoredOperator::F32Sqrt => Instruction::F32Sqrt,
StoredOperator::F32Add => Instruction::F32Add,
StoredOperator::F32Sub => Instruction::F32Sub,
StoredOperator::F32Mul => Instruction::F32Mul,
StoredOperator::F32Div => Instruction::F32Div,
StoredOperator::F32Min => Instruction::F32Min,
StoredOperator::F32Max => Instruction::F32Max,
StoredOperator::F32Copysign => Instruction::F32Copysign,
StoredOperator::F64Abs => Instruction::F64Abs,
StoredOperator::F64Neg => Instruction::F64Neg,
StoredOperator::F64Ceil => Instruction::F64Ceil,
StoredOperator::F64Floor => Instruction::F64Floor,
StoredOperator::F64Trunc => Instruction::F64Trunc,
StoredOperator::F64Nearest => Instruction::F64Nearest,
StoredOperator::F64Sqrt => Instruction::F64Sqrt,
StoredOperator::F64Add => Instruction::F64Add,
StoredOperator::F64Sub => Instruction::F64Sub,
StoredOperator::F64Mul => Instruction::F64Mul,
StoredOperator::F64Div => Instruction::F64Div,
StoredOperator::F64Min => Instruction::F64Min,
StoredOperator::F64Max => Instruction::F64Max,
StoredOperator::F64Copysign => Instruction::F64Copysign,
StoredOperator::I32WrapI64 => Instruction::I32WrapI64,
StoredOperator::I32TruncF32S => Instruction::I32TruncF32S,
StoredOperator::I32TruncF32U => Instruction::I32TruncF32U,
StoredOperator::I32TruncF64S => Instruction::I32TruncF64S,
StoredOperator::I32TruncF64U => Instruction::I32TruncF64U,
StoredOperator::I64ExtendI32S => Instruction::I64ExtendI32S,
StoredOperator::I64ExtendI32U => Instruction::I64ExtendI32U,
StoredOperator::I64TruncF32S => Instruction::I64TruncF32S,
StoredOperator::I64TruncF32U => Instruction::I64TruncF32U,
StoredOperator::I64TruncF64S => Instruction::I64TruncF64S,
StoredOperator::I64TruncF64U => Instruction::I64TruncF64U,
StoredOperator::F32ConvertI32S => Instruction::F32ConvertI32S,
StoredOperator::F32ConvertI32U => Instruction::F32ConvertI32U,
StoredOperator::F32ConvertI64S => Instruction::F32ConvertI64S,
StoredOperator::F32ConvertI64U => Instruction::F32ConvertI64U,
StoredOperator::F32DemoteF64 => Instruction::F32DemoteF64,
StoredOperator::F64ConvertI32S => Instruction::F64ConvertI32S,
StoredOperator::F64ConvertI32U => Instruction::F64ConvertI32U,
StoredOperator::F64ConvertI64S => Instruction::F64ConvertI64S,
StoredOperator::F64ConvertI64U => Instruction::F64ConvertI64U,
StoredOperator::F64PromoteF32 => Instruction::F64PromoteF32,
StoredOperator::I32ReinterpretF32 => Instruction::I32ReinterpretF32,
StoredOperator::I64ReinterpretF64 => Instruction::I64ReinterpretF64,
StoredOperator::F32ReinterpretI32 => Instruction::F32ReinterpretI32,
StoredOperator::F64ReinterpretI64 => Instruction::F64ReinterpretI64,
StoredOperator::I32Extend8S => Instruction::I32Extend8S,
StoredOperator::I32Extend16S => Instruction::I32Extend16S,
StoredOperator::I64Extend8S => Instruction::I64Extend8S,
StoredOperator::I64Extend16S => Instruction::I64Extend16S,
StoredOperator::I64Extend32S => Instruction::I64Extend32S,
StoredOperator::I32TruncSatF32S => Instruction::I32TruncSatF32S,
StoredOperator::I32TruncSatF32U => Instruction::I32TruncSatF32U,
StoredOperator::I32TruncSatF64S => Instruction::I32TruncSatF64S,
StoredOperator::I32TruncSatF64U => Instruction::I32TruncSatF64U,
StoredOperator::I64TruncSatF32S => Instruction::I64TruncSatF32S,
StoredOperator::I64TruncSatF32U => Instruction::I64TruncSatF32U,
StoredOperator::I64TruncSatF64S => Instruction::I64TruncSatF64S,
StoredOperator::I64TruncSatF64U => Instruction::I64TruncSatF64U,
StoredOperator::RefNull(kind) => match kind {
RefTypeKind::Func => Instruction::RefNull(HeapType::Abstract {
shared: false,
ty: wasm_encoder::AbstractHeapType::Func,
}),
RefTypeKind::Extern => Instruction::RefNull(HeapType::Abstract {
shared: false,
ty: wasm_encoder::AbstractHeapType::Extern,
}),
},
StoredOperator::RefIsNull => Instruction::RefIsNull,
StoredOperator::RefFunc(idx) => {
let new_idx = remap.functions.get(idx).copied().unwrap_or(*idx);
Instruction::RefFunc(new_idx)
}
}
}
fn convert_val_type(ty: &wasmparser::ValType) -> ValType {
match ty {
wasmparser::ValType::I32 => ValType::I32,
wasmparser::ValType::I64 => ValType::I64,
wasmparser::ValType::F32 => ValType::F32,
wasmparser::ValType::F64 => ValType::F64,
wasmparser::ValType::V128 => ValType::V128,
wasmparser::ValType::Ref(r) => {
if r.is_func_ref() {
ValType::Ref(RefType::FUNCREF)
} else {
ValType::Ref(RefType::EXTERNREF)
}
}
}
}
fn convert_block_type(bt: &BlockType, remap: &IndexRemap) -> wasm_encoder::BlockType {
match bt {
BlockType::Empty => wasm_encoder::BlockType::Empty,
BlockType::Type(ty) => wasm_encoder::BlockType::Result(convert_val_type(ty)),
BlockType::FuncType(idx) => {
let new_idx = remap.types.get(idx).copied().unwrap_or(*idx);
wasm_encoder::BlockType::FunctionType(new_idx)
}
}
}
fn convert_memarg(m: &MemArg, remap: &IndexRemap) -> wasm_encoder::MemArg {
let new_mem = remap.memories.get(&m.memory).copied().unwrap_or(m.memory);
wasm_encoder::MemArg {
offset: m.offset,
align: m.align,
memory_index: new_mem,
}
}
fn convert_table_type(ty: &wasmparser::TableType) -> TableType {
let ref_type = if ty.element_type.is_func_ref() {
RefType::FUNCREF
} else {
RefType::EXTERNREF
};
TableType {
element_type: ref_type,
minimum: ty.initial,
maximum: ty.maximum,
table64: ty.table64,
shared: ty.shared,
}
}
fn convert_memory_type(ty: &wasmparser::MemoryType) -> MemoryType {
MemoryType {
minimum: ty.initial,
maximum: ty.maximum,
memory64: ty.memory64,
shared: ty.shared,
page_size_log2: ty.page_size_log2,
}
}
fn convert_global_type(ty: &wasmparser::GlobalType) -> GlobalType {
GlobalType {
val_type: convert_val_type(&ty.content_type),
mutable: ty.mutable,
shared: ty.shared,
}
}
fn convert_const_expr(expr: &ConstExpr) -> EncConstExpr {
match expr {
ConstExpr::I32Const(v) => EncConstExpr::i32_const(*v),
ConstExpr::I64Const(v) => EncConstExpr::i64_const(*v),
ConstExpr::F32Const(v) => EncConstExpr::f32_const(*v),
ConstExpr::F64Const(v) => EncConstExpr::f64_const(*v),
ConstExpr::GlobalGet(idx) => EncConstExpr::global_get(*idx),
ConstExpr::RefNull(kind) => {
let heap_type = match kind {
RefTypeKind::Func => HeapType::Abstract {
shared: false,
ty: wasm_encoder::AbstractHeapType::Func,
},
RefTypeKind::Extern => HeapType::Abstract {
shared: false,
ty: wasm_encoder::AbstractHeapType::Extern,
},
};
EncConstExpr::ref_null(heap_type)
}
ConstExpr::RefFunc(idx) => EncConstExpr::ref_func(*idx),
}
}