Repository: bytecodealliance/wasm-tools
Pull request #1796 (2024-09-18 00:28:17)

Remove custom translation framework from wasm-mutate
This commit removes the final users of the `Translator` trait and migrates them to `wasm_encoder::reencode`. Additionally when fuzzing this some minor issues have additionally been fixed in wasm-mutate.


Patch:
diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs
index f954c5bd..dfb204ba 100644
--- a/crates/wasm-encoder/src/core/code.rs
+++ b/crates/wasm-encoder/src/core/code.rs
@@ -3736,7 +3736,7 @@ impl Encode for Catch {
 /// A constant expression.
 ///
 /// Usable in contexts such as offsets or initializers.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub struct ConstExpr {
     bytes: Vec<u8>,
 }
diff --git a/crates/wasm-encoder/src/core/elements.rs b/crates/wasm-encoder/src/core/elements.rs
index 6ed6e03e..e44022e7 100644
--- a/crates/wasm-encoder/src/core/elements.rs
+++ b/crates/wasm-encoder/src/core/elements.rs
@@ -1,4 +1,5 @@
 use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
+use std::borrow::Cow;
 
 /// An encoder for the element section.
 ///
@@ -7,6 +8,7 @@ use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
 /// # Example
 ///
 /// ```
+/// use std::borrow::Cow;
 /// use wasm_encoder::{
 ///     Elements, ElementSection, Module, TableSection, TableType,
 ///     RefType, ConstExpr
@@ -24,9 +26,9 @@ use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
 /// let mut elements = ElementSection::new();
 /// let table_index = 0;
 /// let offset = ConstExpr::i32_const(42);
-/// let functions = Elements::Functions(&[
+/// let functions = Elements::Functions(Cow::Borrowed(&[
 ///     // Function indices...
-/// ]);
+/// ]));
 /// elements.active(Some(table_index), &offset, functions);
 ///
 /// let mut module = Module::new();
@@ -43,12 +45,12 @@ pub struct ElementSection {
 }
 
 /// A sequence of elements in a segment in the element section.
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Debug)]
 pub enum Elements<'a> {
     /// A sequences of references to functions by their indices.
-    Functions(&'a [u32]),
+    Functions(Cow<'a, [u32]>),
     /// A sequence of reference expressions.
-    Expressions(RefType, &'a [ConstExpr]),
+    Expressions(RefType, Cow<'a, [ConstExpr]>),
 }
 
 /// An element segment's mode.
@@ -151,7 +153,7 @@ impl ElementSection {
                     ty.encode(&mut self.bytes);
                 }
                 e.len().encode(&mut self.bytes);
-                for expr in e {
+                for expr in e.iter() {
                     expr.encode(&mut self.bytes);
                 }
             }
diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs
index dd054624..e7cc95a7 100644
--- a/crates/wasm-encoder/src/reencode.rs
+++ b/crates/wasm-encoder/src/reencode.rs
@@ -304,6 +304,13 @@ pub trait Reencode {
         utils::parse_element(self, elements, element)
     }
 
+    fn element_items<'a>(
+        &mut self,
+        items: wasmparser::ElementItems<'a>,
+    ) -> Result<crate::Elements<'a>, Error<Self::Error>> {
+        utils::element_items(self, items)
+    }
+
     /// Parses the input `section` given from the `wasmparser` crate and adds
     /// all the exports to the `exports` section.
     fn parse_export_section(
@@ -1390,24 +1397,7 @@ pub mod utils {
         elements: &mut crate::ElementSection,
         element: wasmparser::Element<'_>,
     ) -> Result<(), Error<T::Error>> {
-        let mut funcs;
-        let mut exprs;
-        let elems = match element.items {
-            wasmparser::ElementItems::Functions(f) => {
-                funcs = Vec::new();
-                for func in f {
-                    funcs.push(reencoder.function_index(func?));
-                }
-                crate::Elements::Functions(&funcs)
-            }
-            wasmparser::ElementItems::Expressions(ty, e) => {
-                exprs = Vec::new();
-                for expr in e {
-                    exprs.push(reencoder.const_expr(expr?)?);
-                }
-                crate::Elements::Expressions(reencoder.ref_type(ty)?, &exprs)
-            }
-        };
+        let elems = reencoder.element_items(element.items)?;
         match element.kind {
             wasmparser::ElementKind::Active {
                 table_index,
@@ -1436,6 +1426,28 @@ pub mod utils {
         Ok(())
     }
 
+    pub fn element_items<'a, T: ?Sized + Reencode>(
+        reencoder: &mut T,
+        items: wasmparser::ElementItems<'a>,
+    ) -> Result<crate::Elements<'a>, Error<T::Error>> {
+        Ok(match items {
+            wasmparser::ElementItems::Functions(f) => {
+                let mut funcs = Vec::new();
+                for func in f {
+                    funcs.push(reencoder.function_index(func?));
+                }
+                crate::Elements::Functions(funcs.into())
+            }
+            wasmparser::ElementItems::Expressions(ty, e) => {
+                let mut exprs = Vec::new();
+                for expr in e {
+                    exprs.push(reencoder.const_expr(expr?)?);
+                }
+                crate::Elements::Expressions(reencoder.ref_type(ty)?, exprs.into())
+            }
+        })
+    }
+
     pub fn table_index<T: ?Sized + Reencode>(_reencoder: &mut T, table: u32) -> u32 {
         table
     }
diff --git a/crates/wasm-mutate/src/error.rs b/crates/wasm-mutate/src/error.rs
index 0f0f1791..fabc3b2f 100644
--- a/crates/wasm-mutate/src/error.rs
+++ b/crates/wasm-mutate/src/error.rs
@@ -101,3 +101,6 @@ pub enum ErrorKind {
 
 /// A `Result` type that is either `Ok(T)` or `Err(wasm_mutate::Error)`.
 pub type Result<T, E = Error> = std::result::Result<T, E>;
+
+/// A `Result` type for use with `wasm_encoder::reencode`
+pub type ReencodeResult<T, E = Error> = Result<T, wasm_encoder::reencode::Error<E>>;
diff --git a/crates/wasm-mutate/src/mutators.rs b/crates/wasm-mutate/src/mutators.rs
index 9a64961a..577bdc01 100644
--- a/crates/wasm-mutate/src/mutators.rs
+++ b/crates/wasm-mutate/src/mutators.rs
@@ -39,7 +39,6 @@ pub mod start;
 
 mod translate;
 pub use self::translate::Item;
-use self::translate::{DefaultTranslator, Translator};
 
 use std::borrow::Cow;
 
diff --git a/crates/wasm-mutate/src/mutators/codemotion/ir.rs b/crates/wasm-mutate/src/mutators/codemotion/ir.rs
index 0ca3e6ea..cea95999 100644
--- a/crates/wasm-mutate/src/mutators/codemotion/ir.rs
+++ b/crates/wasm-mutate/src/mutators/codemotion/ir.rs
@@ -2,6 +2,7 @@
 use crate::{
     module::map_block_type,
     mutators::{codemotion::ir::parse_context::ParseContext, OperatorAndByteOffset},
+    Error,
 };
 use std::ops::Range;
 use wasm_encoder::{Function, Instruction};
@@ -287,6 +288,7 @@ impl AstBuilder {
                     parse_context.push_state();
                     parse_context.push_frame(State::Loop, Some(*blockty), idx);
                 }
+                Operator::TryTable { .. } => return Err(Error::no_mutations_applicable()),
                 Operator::End => {
                     if !parse_context.current_code_is_empty() {
                         parse_context.push_current_code_as_node();
diff --git a/crates/wasm-mutate/src/mutators/codemotion/loop_unrolling.rs b/crates/wasm-mutate/src/mutators/codemotion/loop_unrolling.rs
index 9eb1b86e..9f762949 100644
--- a/crates/wasm-mutate/src/mutators/codemotion/loop_unrolling.rs
+++ b/crates/wasm-mutate/src/mutators/codemotion/loop_unrolling.rs
@@ -70,13 +70,10 @@ impl LoopUnrollWriter {
                 newfunc.instruction(&Instruction::Block(map_block_type(*ty)?));
                 for (idx, (op, _)) in chunk.iter().enumerate() {
                     match op {
-                        Operator::Block { .. } => {
-                            current_depth += 1;
-                        }
-                        Operator::Loop { .. } => {
-                            current_depth += 1;
-                        }
-                        Operator::If { .. } => {
+                        Operator::Block { .. }
+                        | Operator::Loop { .. }
+                        | Operator::If { .. }
+                        | Operator::TryTable { .. } => {
                             current_depth += 1;
                         }
                         Operator::End { .. } => {
diff --git a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs
index 173f53cf..8051e73a 100644
--- a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs
+++ b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs
@@ -1,13 +1,13 @@
 //! This mutator modifies the constant initializer expressions between various valid forms in
 //! entities which require constant initializers.
 
-use crate::mutators::translate::{self, ConstExprKind, DefaultTranslator, Item, Translator};
-use crate::{Error, Mutator, Result};
+use crate::{Error, Mutator, ReencodeResult};
 use rand::Rng;
+use wasm_encoder::reencode::{self, Reencode, RoundtripReencoder};
 use wasm_encoder::{ElementSection, GlobalSection};
-use wasmparser::{ConstExpr, ElementSectionReader, GlobalSectionReader, ValType};
+use wasmparser::{ConstExpr, ElementSectionReader, GlobalSectionReader};
 
-#[derive(Copy, Clone)]
+#[derive(PartialEq, Copy, Clone)]
 pub enum ConstExpressionMutator {
     Global,
     ElementOffset,
@@ -17,7 +17,7 @@ pub enum ConstExpressionMutator {
 struct InitTranslator<'cfg, 'wasm> {
     config: &'cfg mut crate::WasmMutate<'wasm>,
     skip_inits: u32,
-    kind: ConstExprKind,
+    kind: ConstExpressionMutator,
 }
 
 impl<'cfg, 'wasm> InitTranslator<'cfg, 'wasm> {
@@ -35,41 +35,42 @@ impl<'cfg, 'wasm> InitTranslator<'cfg, 'wasm> {
     }
 }
 
-impl<'cfg, 'wasm> Translator for InitTranslator<'cfg, 'wasm> {
-    fn as_obj(&mut self) -> &mut dyn Translator {
-        self
-    }
+impl<'cfg, 'wasm> Reencode for InitTranslator<'cfg, 'wasm> {
+    type Error = Error;
 
     /// Handle `elem`s with values of the `ElementItem::Func` kind. This function will not be
     /// called for values of the `ElementItem::Expr` kind.
-    fn remap(&mut self, item: Item, idx: u32) -> Result<u32> {
-        Ok(match (self.kind, item) {
-            (ConstExprKind::ElementFunction, Item::Function) if self.should_process() => {
-                log::trace!("... replacing referenced function index with 0");
-                // FIXME: generate random function indices when `!config.reduce`.
-                0
-            }
-            _ => idx,
-        })
+    fn function_index(&mut self, idx: u32) -> u32 {
+        if self.kind != ConstExpressionMutator::ElementFunc || !self.should_process() {
+            return idx;
+        }
+
+        log::trace!("... replacing referenced function index with 0");
+        // FIXME: generate random function indices when `!config.reduce`.
+        0
     }
 
     /// Handle `global` initalizers and `elem`s with values of the `ElementItem::Expr` kind.
     ///
     /// This function will not be called for `elem` values of the `ElementItem::Func` kind.
-    fn translate_const_expr(
-        &mut self,
-        e: &ConstExpr<'_>,
-        ty: &ValType,
-        kind: ConstExprKind,
-    ) -> Result<wasm_encoder::ConstExpr> {
+    fn const_expr(&mut self, e: ConstExpr<'_>) -> ReencodeResult<wasm_encoder::ConstExpr> {
+        use crate::module::PrimitiveTypeInfo as T;
         use wasm_encoder::ConstExpr as CE;
-        use wasmparser::{Operator as O, ValType as T};
+        use wasmparser::Operator as O;
 
-        if kind != self.kind || !self.should_process() {
-            return translate::const_expr(self.as_obj(), e, kind);
+        if !self.should_process() {
+            return reencode::utils::const_expr(self, e);
         }
+
         let mut reader = e.get_operators_reader();
+
+        if !self.config.reduce {
+            // FIXME: implement non-reducing mutations for constant expressions.
+            return Err(reencode::Error::UserError(Error::no_mutations_applicable()));
+        }
+
         let op = reader.read()?;
+
         // Don't mutate further if the expressions are already their most reduced form.
         let is_simplest = match op {
             O::RefNull { .. } | O::I32Const { value: 0 | 1 } | O::I64Const { value: 0 | 1 } => true,
@@ -78,67 +79,77 @@ impl<'cfg, 'wasm> Translator for InitTranslator<'cfg, 'wasm> {
             O::V128Const { value } => value.i128() == 0,
             _ => false,
         };
-        if self.config.reduce && is_simplest {
-            return Err(Error::no_mutations_applicable());
+        if is_simplest {
+            return Err(reencode::Error::UserError(Error::no_mutations_applicable()));
         }
 
-        let new_op = if self.config.reduce {
-            // For globals give a 25% chance to produce a const with 0 value (arguably the simplest
-            // representation) to give a chance to quickly discover this final reduction if it is
-            // in fact applicable.
-            //
-            // For element offsets always generate `i32.const 0` (effectively removing the offset)
-            // as other values may not necessarily be valid (e.g. maximum table size is limited)
-            let is_element_offset = matches!(kind, ConstExprKind::ElementOffset);
-            let should_zero = is_element_offset || self.config.rng().gen::<u8>() & 0b11 == 0;
-            match *ty {
-                T::I32 if should_zero => CE::i32_const(0),
-                T::I64 if should_zero => CE::i64_const(0),
-                T::V128 if should_zero => CE::v128_const(0),
-                T::F32 if should_zero => CE::f32_const(0.0),
-                T::F64 if should_zero => CE::f64_const(0.0),
-                T::I32 => CE::i32_const(if let O::I32Const { value } = op {
-                    let range = if value < 0 { value..0 } else { 0..value };
-                    self.config.rng().gen_range(range)
-                } else {
-                    self.config.rng().gen()
-                }),
-                T::I64 => CE::i64_const(if let O::I64Const { value } = op {
-                    let range = if value < 0 { value..0 } else { 0..value };
-                    self.config.rng().gen_range(range)
-                } else {
-                    self.config.rng().gen()
-                }),
-                T::V128 => CE::v128_const(if let O::V128Const { value } = op {
-                    self.config.rng().gen_range(0..value.i128() as u128) as i128
-                } else {
-                    self.config.rng().gen()
-                }),
-                T::F32 => CE::f32_const(if let O::F32Const { value } = op {
-                    f32::from_bits(value.bits()) / 2.0
-                } else {
-                    f32::from_bits(self.config.rng().gen())
-                }),
-                T::F64 => CE::f64_const(if let O::F64Const { value } = op {
-                    f64::from_bits(value.bits()) / 2.0
-                } else {
-                    f64::from_bits(self.config.rng().gen())
-                }),
-                T::FUNCREF => CE::ref_null(wasm_encoder::HeapType::Abstract {
-                    shared: false,
-                    ty: wasm_encoder::AbstractHeapType::Func,
-                }),
-                T::EXTERNREF => CE::ref_null(wasm_encoder::HeapType::Abstract {
-                    shared: false,
-                    ty: wasm_encoder::AbstractHeapType::Func,
-                }),
-                T::Ref(_) => unimplemented!(),
+        let ty = match op {
+            O::I32Const { .. } => T::I32,
+            O::I64Const { .. } => T::I64,
+            O::V128Const { .. } => T::V128,
+            O::F32Const { .. } => T::F32,
+            O::F64Const { .. } => T::F64,
+            O::RefFunc { .. }
+            | O::RefNull {
+                hty: wasmparser::HeapType::FUNC,
+            } => T::FuncRef,
+            O::RefNull {
+                hty: wasmparser::HeapType::EXTERN,
+            } => T::ExternRef,
+            O::GlobalGet { global_index } => self.config.info().global_types[global_index as usize],
+            other => {
+                log::info!("unsupported opcode in init expr {other:?}");
+                return Err(reencode::Error::UserError(Error::no_mutations_applicable()));
             }
-        } else {
-            // FIXME: implement non-reducing mutations for constant expressions.
-            return Err(Error::no_mutations_applicable());
         };
 
+        // For globals give a 25% chance to produce a const with 0 value
+        // (arguably the simplest representation) to give a chance to
+        // quickly discover this final reduction if it is in fact
+        // applicable.
+        //
+        // For element offsets always generate `i32.const 0` (effectively
+        // removing the offset) as other values may not necessarily be valid
+        // (e.g. maximum table size is limited)
+        let is_element_offset = matches!(self.kind, ConstExpressionMutator::ElementOffset);
+        let should_zero = is_element_offset || self.config.rng().gen::<u8>() & 0b11 == 0;
+        let new_op = match ty {
+            T::I32 if should_zero => CE::i32_const(0),
+            T::I64 if should_zero => CE::i64_const(0),
+            T::V128 if should_zero => CE::v128_const(0),
+            T::F32 if should_zero => CE::f32_const(0.0),
+            T::F64 if should_zero => CE::f64_const(0.0),
+            T::I32 => CE::i32_const(if let O::I32Const { value } = op {
+                let range = if value < 0 { value..0 } else { 0..value };
+                self.config.rng().gen_range(range)
+            } else {
+                self.config.rng().gen()
+            }),
+            T::I64 => CE::i64_const(if let O::I64Const { value } = op {
+                let range = if value < 0 { value..0 } else { 0..value };
+                self.config.rng().gen_range(range)
+            } else {
+                self.config.rng().gen()
+            }),
+            T::V128 => CE::v128_const(if let O::V128Const { value } = op {
+                self.config.rng().gen_range(0..value.i128() as u128) as i128
+            } else {
+                self.config.rng().gen()
+            }),
+            T::F32 => CE::f32_const(if let O::F32Const { value } = op {
+                f32::from_bits(value.bits()) / 2.0
+            } else {
+                f32::from_bits(self.config.rng().gen())
+            }),
+            T::F64 => CE::f64_const(if let O::F64Const { value } = op {
+                f64::from_bits(value.bits()) / 2.0
+            } else {
+                f64::from_bits(self.config.rng().gen())
+            }),
+            T::FuncRef => CE::ref_null(wasm_encoder::HeapType::FUNC),
+            T::ExternRef => CE::ref_null(wasm_encoder::HeapType::EXTERN),
+            T::Empty => unreachable!(),
+        };
         log::trace!("... replacing original expression with {:?}", new_op);
         Ok(new_op)
     }
@@ -149,11 +160,6 @@ impl Mutator for ConstExpressionMutator {
         &self,
         config: &'a mut crate::WasmMutate,
     ) -> crate::Result<Box<dyn Iterator<Item = crate::Result<wasm_encoder::Module>> + 'a>> {
-        let translator_kind = match self {
-            Self::Global => ConstExprKind::Global,
-            Self::ElementOffset => ConstExprKind::ElementOffset,
-            Self::ElementFunc => ConstExprKind::ElementFunction,
-        };
         let skip_err = Error::no_mutations_applicable();
         match self {
             Self::Global => {
@@ -166,16 +172,16 @@ impl Mutator for ConstExpressionMutator {
                 let mut translator = InitTranslator {
                     config,
                     skip_inits: 0,
-                    kind: translator_kind,
+                    kind: *self,
                 };
                 for (idx, global) in reader.into_iter().enumerate() {
                     translator.config.consume_fuel(1)?;
                     let global = global?;
                     if idx as u32 == mutate_idx {
                         log::trace!("Modifying global at index {}...", idx);
-                        translator.translate_global(global, &mut new_section)?;
+                        translator.parse_global(&mut new_section, global)?;
                     } else {
-                        DefaultTranslator.translate_global(global, &mut new_section)?;
+                        RoundtripReencoder.parse_global(&mut new_section, global)?;
                     }
                 }
                 let new_module = config.info().replace_section(section, &new_section);
@@ -191,7 +197,7 @@ impl Mutator for ConstExpressionMutator {
                 let mut translator = InitTranslator {
                     config,
                     skip_inits: 0,
-                    kind: translator_kind,
+                    kind: *self,
                 };
                 for (idx, element) in reader.into_iter().enumerate() {
                     translator.config.consume_fuel(1)?;
@@ -211,15 +217,10 @@ impl Mutator for ConstExpressionMutator {
                                 return Err(Error::no_mutations_applicable());
                             }
                         }
-                        log::trace!(
-                            "Modifying {} element's {:?}({})...",
-                            idx,
-                            translator_kind,
-                            translator.skip_inits
-                        );
-                        translator.translate_element(element, &mut new_section)?;
+                        log::trace!("Modifying {} element's ({})...", idx, translator.skip_inits);
+                        translator.parse_element(&mut new_section, element)?;
                     } else {
-                        DefaultTranslator.translate_element(element, &mut new_section)?;
+                        RoundtripReencoder.parse_element(&mut new_section, element)?;
                     }
                 }
                 let new_module = config.info().replace_section(section, &new_section);
diff --git a/crates/wasm-mutate/src/mutators/peephole.rs b/crates/wasm-mutate/src/mutators/peephole.rs
index fbdf49d0..efa618a8 100644
--- a/crates/wasm-mutate/src/mutators/peephole.rs
+++ b/crates/wasm-mutate/src/mutators/peephole.rs
@@ -33,13 +33,14 @@ use self::{
         lang::*,
     },
 };
-use super::{DefaultTranslator, Mutator, OperatorAndByteOffset, Translator};
+use super::{Mutator, OperatorAndByteOffset};
 use crate::{
     module::{map_type, PrimitiveTypeInfo},
     Error, ErrorKind, ModuleInfo, Result, WasmMutate,
 };
 use egg::{Rewrite, Runner};
 use rand::Rng;
+use wasm_encoder::reencode::{Reencode, RoundtripReencoder};
 use wasm_encoder::{CodeSection, ConstExpr, Function, GlobalSection, Module, ValType};
 use wasmparser::{CodeSectionReader, FunctionBody, GlobalSectionReader, LocalsReader};
 
@@ -292,10 +293,8 @@ impl PeepholeMutator {
                             // new raw section
                             let reader = config.info().get_binary_reader(global_section);
                             let globalreader = GlobalSectionReader::new(reader)?;
-
-                            for g in globalreader {
-                                DefaultTranslator.translate_global(g?, &mut new_global_section)?;
-                            }
+                            RoundtripReencoder
+                                .parse_global_section(&mut new_global_section, globalreader)?;
                         }
 
                         if needed_resources.len() > 0 {
diff --git a/crates/wasm-mutate/src/mutators/peephole/dfg.rs b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
index 42765268..70a19fc6 100644
--- a/crates/wasm-mutate/src/mutators/peephole/dfg.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
@@ -767,9 +767,6 @@ impl<'a> DFGBuilder {
                 } => {
                     self.push_node(Lang::RefNull(RefType::Func), idx);
                 }
-                Operator::RefNull { .. } => {
-                    unimplemented!()
-                }
                 Operator::RefFunc { function_index } => {
                     self.push_node(Lang::RefFunc(*function_index), idx);
                 }
diff --git a/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs b/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
index ddfb67cb..c2ee6f6b 100644
--- a/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
@@ -577,7 +577,7 @@ impl Analysis<Lang> for PeepholeMutationAnalysis {
             tpe: egraph
                 .analysis
                 .get_returning_tpe(l, egraph)
-                .expect("Missing type"),
+                .unwrap_or(PrimitiveTypeInfo::Empty),
         })
     }
 
diff --git a/crates/wasm-mutate/src/mutators/remove_item.rs b/crates/wasm-mutate/src/mutators/remove_item.rs
index 81419944..9a2246c1 100644
--- a/crates/wasm-mutate/src/mutators/remove_item.rs
+++ b/crates/wasm-mutate/src/mutators/remove_item.rs
@@ -8,7 +8,7 @@
 //! `wasm_encoder` structures.
 
 use crate::mutators::{Item, Mutator};
-use crate::Error;
+use crate::{Error, ReencodeResult};
 use crate::{ModuleInfo, Result, WasmMutate};
 use rand::Rng;
 use std::collections::HashSet;
@@ -114,8 +114,6 @@ enum Funcref {
     RequireReferenced,
 }
 
-type ReencodeResult<T, E = Error> = Result<T, wasm_encoder::reencode::Error<E>>;
-
 impl RemoveItem<'_> {
     fn remove(&mut self) -> Result<Module> {
         // This is the main workhorse loop of the module translation. This will
diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs
index 8b07f105..6ea58680 100644
--- a/crates/wasm-mutate/src/mutators/translate.rs
+++ b/crates/wasm-mutate/src/mutators/translate.rs
@@ -1,7 +1,3 @@
-use crate::{Error, Result};
-use wasm_encoder::*;
-use wasmparser::{ElementKind, Global, Operator};
-
 #[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)]
 pub enum Item {
     Function,
@@ -13,332 +9,3 @@ pub enum Item {
     Data,
     Element,
 }
-
-#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)]
-pub enum ConstExprKind {
-    Global,
-    ElementOffset,
-    ElementFunction,
-}
-
-pub trait Translator {
-    fn as_obj(&mut self) -> &mut dyn Translator;
-
-    fn translate_global_type(
-        &mut self,
-        ty: &wasmparser::GlobalType,
-    ) -> Result<wasm_encoder::GlobalType> {
-        global_type(self.as_obj(), ty)
-    }
-
-    fn translate_ty(&mut self, t: &wasmparser::ValType) -> Result<ValType> {
-        ty(self.as_obj(), t)
-    }
-
-    fn translate_refty(&mut self, t: &wasmparser::RefType) -> Result<RefType> {
-        refty(self.as_obj(), t)
-    }
-
-    fn translate_heapty(&mut self, t: &wasmparser::HeapType) -> Result<HeapType> {
-        heapty(self.as_obj(), t)
-    }
-
-    fn translate_global(&mut self, g: Global, s: &mut GlobalSection) -> Result<()> {
-        global(self.as_obj(), g, s)
-    }
-
-    fn translate_const_expr(
-        &mut self,
-        e: &wasmparser::ConstExpr<'_>,
-        _ty: &wasmparser::ValType,
-        ctx: ConstExprKind,
-    ) -> Result<wasm_encoder::ConstExpr> {
-        const_expr(self.as_obj(), e, ctx)
-    }
-
-    fn translate_element(
-        &mut self,
-        e: wasmparser::Element<'_>,
-        s: &mut ElementSection,
-    ) -> Result<()> {
-        element(self.as_obj(), e, s)
-    }
-
-    fn translate_op(&mut self, e: &Operator<'_>) -> Result<Instruction<'static>> {
-        op(self.as_obj(), e)
-    }
-
-    fn translate_block_type(&mut self, ty: &wasmparser::BlockType) -> Result<BlockType> {
-        block_type(self.as_obj(), ty)
-    }
-
-    fn translate_memarg(&mut self, arg: &wasmparser::MemArg) -> Result<MemArg> {
-        memarg(self.as_obj(), arg)
-    }
-
-    fn translate_ordering(&mut self, arg: &wasmparser::Ordering) -> Result<Ordering> {
-        Ok(match arg {
-            wasmparser::Ordering::SeqCst => Ordering::SeqCst,
-            wasmparser::Ordering::AcqRel => Ordering::AcqRel,
-        })
-    }
-
-    fn remap(&mut self, item: Item, idx: u32) -> Result<u32> {
-        let _ = item;
-        Ok(idx)
-    }
-}
-
-pub struct DefaultTranslator;
-
-impl Translator for DefaultTranslator {
-    fn as_obj(&mut self) -> &mut dyn Translator {
-        self
-    }
-}
-
-pub fn global_type(
-    t: &mut dyn Translator,
-    ty: &wasmparser::GlobalType,
-) -> Result<wasm_encoder::GlobalType> {
-    Ok(wasm_encoder::GlobalType {
-        val_type: t.translate_ty(&ty.content_type)?,
-        mutable: ty.mutable,
-        shared: ty.shared,
-    })
-}
-
-pub fn ty(t: &mut dyn Translator, ty: &wasmparser::ValType) -> Result<ValType> {
-    match ty {
-        wasmparser::ValType::I32 => Ok(ValType::I32),
-        wasmparser::ValType::I64 => Ok(ValType::I64),
-        wasmparser::ValType::F32 => Ok(ValType::F32),
-        wasmparser::ValType::F64 => Ok(ValType::F64),
-        wasmparser::ValType::V128 => Ok(ValType::V128),
-        wasmparser::ValType::Ref(ty) => Ok(ValType::Ref(t.translate_refty(ty)?)),
-    }
-}
-
-pub fn refty(t: &mut dyn Translator, ty: &wasmparser::RefType) -> Result<RefType> {
-    Ok(RefType {
-        nullable: ty.is_nullable(),
-        heap_type: t.translate_heapty(&ty.heap_type())?,
-    })
-}
-
-pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result<HeapType> {
-    match ty {
-        wasmparser::HeapType::Concrete(i) => Ok(HeapType::Concrete(
-            t.remap(Item::Type, i.as_module_index().unwrap())?,
-        )),
-        wasmparser::HeapType::Abstract { shared, ty } => {
-            let ty = (*ty).into();
-            Ok(HeapType::Abstract {
-                shared: *shared,
-                ty,
-            })
-        }
-    }
-}
-
-pub fn global(t: &mut dyn Translator, global: Global, s: &mut GlobalSection) -> Result<()> {
-    let ty = t.translate_global_type(&global.ty)?;
-    let insn = t.translate_const_expr(
-        &global.init_expr,
-        &global.ty.content_type,
-        ConstExprKind::Global,
-    )?;
-    s.global(ty, &insn);
-    Ok(())
-}
-
-pub fn const_expr(
-    t: &mut dyn Translator,
-    e: &wasmparser::ConstExpr<'_>,
-    ctx: ConstExprKind,
-) -> Result<wasm_encoder::ConstExpr> {
-    let mut e = e.get_operators_reader();
-    let mut offset_bytes = Vec::new();
-    let op = e.read()?;
-    if let ConstExprKind::ElementFunction = ctx {
-        match op {
-            Operator::RefFunc { .. }
-            | Operator::RefNull {
-                hty:
-                    wasmparser::HeapType::Abstract {
-                        ty: wasmparser::AbstractHeapType::Func,
-                        ..
-                    },
-                ..
-            }
-            | Operator::GlobalGet { .. } => {}
-            _ => return Err(Error::no_mutations_applicable()),
-        }
-    }
-    t.translate_op(&op)?.encode(&mut offset_bytes);
-    match e.read()? {
-        Operator::End if e.eof() => {}
-        _ => return Err(Error::no_mutations_applicable()),
-    }
-    Ok(wasm_encoder::ConstExpr::raw(offset_bytes))
-}
-
-pub fn element(
-    t: &mut dyn Translator,
-    element: wasmparser::Element<'_>,
-    s: &mut ElementSection,
-) -> Result<()> {
-    let offset;
-    let mode = match &element.kind {
-        ElementKind::Active {
-            table_index,
-            offset_expr,
-        } => {
-            offset = t.translate_const_expr(
-                offset_expr,
-                &wasmparser::ValType::I32,
-                ConstExprKind::ElementOffset,
-            )?;
-            let table_index = table_index.unwrap_or(0);
-            let table = t.remap(Item::Table, table_index)?;
-            ElementMode::Active {
-                table: if table == 0 { None } else { Some(table) },
-                offset: &offset,
-            }
-        }
-        ElementKind::Passive => ElementMode::Passive,
-        ElementKind::Declared => ElementMode::Declared,
-    };
-    let functions;
-    let exprs;
-    let elements = match element.items {
-        wasmparser::ElementItems::Functions(reader) => {
-            functions = reader
-                .into_iter()
-                .map(|f| t.remap(Item::Function, f?))
-                .collect::<Result<Vec<_>, _>>()?;
-            Elements::Functions(&functions)
-        }
-        wasmparser::ElementItems::Expressions(ty, reader) => {
-            exprs = reader
-                .into_iter()
-                .map(|f| {
-                    t.translate_const_expr(
-                        &f?,
-                        &wasmparser::ValType::Ref(ty),
-                        ConstExprKind::ElementFunction,
-                    )
-                })
-                .collect::<Result<Vec<_>, _>>()?;
-            Elements::Expressions(t.translate_refty(&ty)?, &exprs)
-        }
-    };
-    s.segment(ElementSegment { mode, elements });
-    Ok(())
-}
-
-#[allow(unused_variables)]
-pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'static>> {
-    use wasm_encoder::Instruction as I;
-
-    macro_rules! translate {
-        ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
-            Ok(match op {
-                $(
-                    wasmparser::Operator::$op $({ $($arg),* })? => {
-                        $(
-                            $(let $arg = translate!(map $arg $arg);)*
-                        )?
-                        translate!(build $op $($($arg)*)?)
-                    }
-                )*
-            })
-        };
-
-        // This case is used to map, based on the name of the field, from the
-        // wasmparser payload type to the wasm-encoder payload type through
-        // `Translator` as applicable.
-        (map $arg:ident tag_index) => (t.remap(Item::Tag, *$arg)?);
-        (map $arg:ident function_index) => (t.remap(Item::Function, *$arg)?);
-        (map $arg:ident table) => (t.remap(Item::Table, *$arg)?);
-        (map $arg:ident table_index) => (t.remap(Item::Table, *$arg)?);
-        (map $arg:ident table) => (t.remap(Item::Table, *$arg)?);
-        (map $arg:ident dst_table) => (t.remap(Item::Table, *$arg)?);
-        (map $arg:ident src_table) => (t.remap(Item::Table, *$arg)?);
-        (map $arg:ident type_index) => (t.remap(Item::Type, *$arg)?);
-        (map $arg:ident array_type_index) => (t.remap(Item::Type, *$arg)?);
-        (map $arg:ident array_type_index_dst) => (t.remap(Item::Type, *$arg)?);
-        (map $arg:ident array_type_index_src) => (t.remap(Item::Type, *$arg)?);
-        (map $arg:ident struct_type_index) => (t.remap(Item::Type, *$arg)?);
-        (map $arg:ident global_index) => (t.remap(Item::Global, *$arg)?);
-        (map $arg:ident mem) => (t.remap(Item::Memory, *$arg)?);
-        (map $arg:ident src_mem) => (t.remap(Item::Memory, *$arg)?);
-        (map $arg:ident dst_mem) => (t.remap(Item::Memory, *$arg)?);
-        (map $arg:ident data_index) => (t.remap(Item::Data, *$arg)?);
-        (map $arg:ident elem_index) => (t.remap(Item::Element, *$arg)?);
-        (map $arg:ident array_data_index) => (t.remap(Item::Data, *$arg)?);
-        (map $arg:ident array_elem_index) => (t.remap(Item::Element, *$arg)?);
-        (map $arg:ident blockty) => (t.translate_block_type($arg)?);
-        (map $arg:ident relative_depth) => (*$arg);
-        (map $arg:ident targets) => ((
-            $arg
-                .targets()
-                .collect::<Result<Vec<_>, wasmparser::BinaryReaderError>>()?
-                .into(),
-            $arg.default(),
-        ));
-        (map $arg:ident flags) => (());
-        (map $arg:ident ty) => (t.translate_ty($arg)?);
-        (map $arg:ident hty) => (t.translate_heapty($arg)?);
-        (map $arg:ident from_ref_type) => (t.translate_refty($arg)?);
-        (map $arg:ident to_ref_type) => (t.translate_refty($arg)?);
-        (map $arg:ident memarg) => (t.translate_memarg($arg)?);
-        (map $arg:ident ordering) => (t.translate_ordering($arg)?);
-        (map $arg:ident local_index) => (*$arg);
-        (map $arg:ident value) => ($arg);
-        (map $arg:ident lane) => (*$arg);
-        (map $arg:ident lanes) => (*$arg);
-        (map $arg:ident array_size) => (*$arg);
-        (map $arg:ident field_index) => (*$arg);
-        (map $arg:ident from_type_nullable) => (*$arg);
-        (map $arg:ident to_type_nullable) => (*$arg);
-        (map $arg:ident try_table) => ($arg);
-
-        // This case takes the arguments of a wasmparser instruction and creates
-        // a wasm-encoder instruction. There are a few special cases for where
-        // the structure of a wasmparser instruction differs from that of
-        // wasm-encoder.
-        (build $op:ident) => (I::$op);
-        (build BrTable $arg:ident) => (I::BrTable($arg.0, $arg.1));
-        (build I32Const $arg:ident) => (I::I32Const(*$arg));
-        (build I64Const $arg:ident) => (I::I64Const(*$arg));
-        (build F32Const $arg:ident) => (I::F32Const(f32::from_bits($arg.bits())));
-        (build F64Const $arg:ident) => (I::F64Const(f64::from_bits($arg.bits())));
-        (build V128Const $arg:ident) => (I::V128Const($arg.i128()));
-        (build TryTable $table:ident) => (unimplemented_try_table());
-        (build $op:ident $arg:ident) => (I::$op($arg));
-        (build $op:ident $($arg:ident)*) => (I::$op { $($arg),* });
-    }
-
-    wasmparser::for_each_operator!(translate)
-}
-
-fn unimplemented_try_table() -> wasm_encoder::Instruction<'static> {
-    unimplemented!()
-}
-
-pub fn block_type(t: &mut dyn Translator, ty: &wasmparser::BlockType) -> Result<BlockType> {
-    match ty {
-        wasmparser::BlockType::Empty => Ok(BlockType::Empty),
-        wasmparser::BlockType::Type(ty) => Ok(BlockType::Result(t.translate_ty(ty)?)),
-        wasmparser::BlockType::FuncType(f) => Ok(BlockType::FunctionType(t.remap(Item::Type, *f)?)),
-    }
-}
-
-pub fn memarg(t: &mut dyn Translator, memarg: &wasmparser::MemArg) -> Result<MemArg> {
-    Ok(MemArg {
-        offset: memarg.offset,
-        align: memarg.align.into(),
-        memory_index: t.remap(Item::Memory, memarg.memory)?,
-    })
-}
diff --git a/crates/wasm-smith/src/core/encode.rs b/crates/wasm-smith/src/core/encode.rs
index b7727996..dd913728 100644
--- a/crates/wasm-smith/src/core/encode.rs
+++ b/crates/wasm-smith/src/core/encode.rs
@@ -164,10 +164,10 @@ impl Module {
         let mut elems = wasm_encoder::ElementSection::new();
         for el in &self.elems {
             let elements = match &el.items {
-                Elements::Expressions(es) => wasm_encoder::Elements::Expressions(el.ty, es),
+                Elements::Expressions(es) => wasm_encoder::Elements::Expressions(el.ty, es.into()),
                 Elements::Functions(fs) => {
                     assert_eq!(el.ty, RefType::FUNCREF);
-                    wasm_encoder::Elements::Functions(fs)
+                    wasm_encoder::Elements::Functions(fs.into())
                 }
             };
             match &el.kind {
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 17f68972..d609390a 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -1282,7 +1282,7 @@ impl<'a> EncodingState<'a> {
         elements.active(
             None,
             &ConstExpr::i32_const(0),
-            Elements::Functions(&func_indexes),
+            Elements::Functions(func_indexes.into()),
         );
 
         let mut shim = Module::new();
diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs
index 1bae4105..69428228 100644
--- a/crates/wit-component/src/linking.rs
+++ b/crates/wit-component/src/linking.rs
@@ -742,12 +742,12 @@ fn make_init_module(
         elements.active(
             None,
             &const_u32(dl_openables.table_base),
-            Elements::Functions(&dl_openable_functions),
+            Elements::Functions(dl_openable_functions.into()),
         );
         elements.active(
             None,
             &const_u32(indirection_table_base),
-            Elements::Functions(&indirections),
+            Elements::Functions(indirections.into()),
         );
         module.section(&elements);
     }
diff --git a/fuzz/src/mutate.rs b/fuzz/src/mutate.rs
index 88b51c89..d1492f1d 100644
--- a/fuzz/src/mutate.rs
+++ b/fuzz/src/mutate.rs
@@ -42,11 +42,7 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
     let mut wasm_mutate = wasm_mutate::WasmMutate::default();
     wasm_mutate.seed(seed);
     wasm_mutate.fuel(300);
-    wasm_mutate.preserve_semantics(
-        // If we are going to check that we get the same evaluated results
-        // before and after mutation, then we need to preserve semantics.
-        cfg!(feature = "wasmtime") && preserve_semantics,
-    );
+    wasm_mutate.preserve_semantics(preserve_semantics);
 
     let iterator = match wasm_mutate.run(&wasm) {
         Ok(iterator) => iterator,
@@ -88,6 +84,8 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
 
         validation_result.expect("`wasm-mutate` should always produce a valid Wasm file");
 
+        // If semantics are preserved then assert so between executions of
+        // Wasmtime if that's also enabled.
         #[cfg(feature = "wasmtime")]
         if preserve_semantics {
             eval::assert_same_evaluation(&wasm, &mutated_wasm);


Test patch:
diff --git a/crates/wasmparser/tests/big-module.rs b/crates/wasmparser/tests/big-module.rs
index 68d4d7f0..d6b0aece 100644
--- a/crates/wasmparser/tests/big-module.rs
+++ b/crates/wasmparser/tests/big-module.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
 use wasm_encoder::*;
 
 #[test]
@@ -14,7 +15,7 @@ fn big_type_indices() {
     module.section(&funcs);
 
     let mut elems = ElementSection::new();
-    elems.declared(Elements::Functions(&[0]));
+    elems.declared(Elements::Functions(Cow::Borrowed(&[0])));
     module.section(&elems);
 
     let mut code = CodeSection::new();