Revision control
Copy as Markdown
Other Tools
#[cfg(feature = "component-model")]
use crate::component::Component;
use crate::core::*;
use crate::encode::Encode;
use crate::token::*;
use crate::Wat;
use std::borrow::Cow;
use std::marker;
#[cfg(feature = "dwarf")]
use std::path::Path;
/// Options that can be specified when encoding a component or a module to
/// customize what the final binary looks like.
///
/// Methods such as [`Module::encode`], [`Wat::encode`], and
/// [`Component::encode`] will use the default options.
#[derive(Default)]
pub struct EncodeOptions<'a> {
#[cfg(feature = "dwarf")]
dwarf_info: Option<(&'a Path, &'a str, GenerateDwarf)>,
_marker: marker::PhantomData<&'a str>,
}
#[cfg(feature = "dwarf")]
mod dwarf;
#[cfg(not(feature = "dwarf"))]
mod dwarf_disabled;
#[cfg(not(feature = "dwarf"))]
use self::dwarf_disabled as dwarf;
/// Configuration of how DWARF debugging information may be generated.
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum GenerateDwarf {
/// Only generate line tables to map binary offsets back to source
/// locations.
Lines,
/// Generate full debugging information for both line numbers and
/// variables/locals/operands.
Full,
}
impl<'a> EncodeOptions<'a> {
/// Creates a new set of default encoding options.
pub fn new() -> EncodeOptions<'a> {
EncodeOptions::default()
}
/// Enables emission of DWARF debugging information in the final binary.
///
/// This method will use the `file` specified as the source file for the
/// `*.wat` file whose `contents` must also be supplied here. These are
/// used to calculate filenames/line numbers and are referenced from the
/// generated DWARF.
#[cfg(feature = "dwarf")]
pub fn dwarf(&mut self, file: &'a Path, contents: &'a str, style: GenerateDwarf) -> &mut Self {
self.dwarf_info = Some((file, contents, style));
self
}
/// Encodes the given [`Module`] with these options.
///
/// For more information see [`Module::encode`].
pub fn encode_module(
&self,
module: &mut Module<'_>,
) -> std::result::Result<Vec<u8>, crate::Error> {
module.resolve()?;
Ok(match &module.kind {
ModuleKind::Text(fields) => encode(&module.id, &module.name, fields, self),
ModuleKind::Binary(blobs) => blobs.iter().flat_map(|b| b.iter().cloned()).collect(),
})
}
/// Encodes the given [`Component`] with these options.
///
/// For more information see [`Component::encode`].
#[cfg(feature = "component-model")]
pub fn encode_component(
&self,
component: &mut Component<'_>,
) -> std::result::Result<Vec<u8>, crate::Error> {
component.resolve()?;
Ok(crate::component::binary::encode(component, self))
}
/// Encodes the given [`Wat`] with these options.
///
/// For more information see [`Wat::encode`].
pub fn encode_wat(&self, wat: &mut Wat<'_>) -> std::result::Result<Vec<u8>, crate::Error> {
match wat {
Wat::Module(m) => self.encode_module(m),
#[cfg(feature = "component-model")]
Wat::Component(c) => self.encode_component(c),
#[cfg(not(feature = "component-model"))]
Wat::Component(_) => unreachable!(),
}
}
}
pub(crate) fn encode(
module_id: &Option<Id<'_>>,
module_name: &Option<NameAnnotation<'_>>,
fields: &[ModuleField<'_>],
opts: &EncodeOptions,
) -> Vec<u8> {
use CustomPlace::*;
use CustomPlaceAnchor::*;
let mut types = Vec::new();
let mut imports = Vec::new();
let mut funcs = Vec::new();
let mut tables = Vec::new();
let mut memories = Vec::new();
let mut globals = Vec::new();
let mut exports = Vec::new();
let mut start = Vec::new();
let mut elem = Vec::new();
let mut data = Vec::new();
let mut tags = Vec::new();
let mut customs = Vec::new();
for field in fields {
match field {
ModuleField::Type(i) => types.push(RecOrType::Type(i)),
ModuleField::Rec(i) => types.push(RecOrType::Rec(i)),
ModuleField::Import(i) => imports.push(i),
ModuleField::Func(i) => funcs.push(i),
ModuleField::Table(i) => tables.push(i),
ModuleField::Memory(i) => memories.push(i),
ModuleField::Global(i) => globals.push(i),
ModuleField::Export(i) => exports.push(i),
ModuleField::Start(i) => start.push(i),
ModuleField::Elem(i) => elem.push(i),
ModuleField::Data(i) => data.push(i),
ModuleField::Tag(i) => tags.push(i),
ModuleField::Custom(i) => customs.push(i),
}
}
let mut e = Encoder {
wasm: wasm_encoder::Module::new(),
customs: &customs,
};
e.custom_sections(BeforeFirst);
e.typed_section(&types);
e.typed_section(&imports);
let functys = funcs
.iter()
.map(|f| FuncSectionTy(&f.ty))
.collect::<Vec<_>>();
e.typed_section(&functys);
e.typed_section(&tables);
e.typed_section(&memories);
e.typed_section(&tags);
e.typed_section(&globals);
e.typed_section(&exports);
e.custom_sections(Before(Start));
if let Some(start) = start.get(0) {
e.wasm.section(&wasm_encoder::StartSection {
function_index: start.unwrap_u32(),
});
}
e.custom_sections(After(Start));
e.typed_section(&elem);
if needs_data_count(&funcs) {
e.wasm.section(&wasm_encoder::DataCountSection {
count: data.len().try_into().unwrap(),
});
}
// Prepare to and emit the code section. This is where DWARF may optionally
// be emitted depending on configuration settings. Note that `code_section`
// will internally emit the branch hints section if necessary.
let names = find_names(module_id, module_name, fields);
let num_import_funcs = imports
.iter()
.filter(|i| matches!(i.item.kind, ItemKind::Func(..)))
.count() as u32;
let mut dwarf = dwarf::Dwarf::new(num_import_funcs, opts, &names, &types);
e.code_section(&funcs, num_import_funcs, dwarf.as_mut());
e.typed_section(&data);
if !names.is_empty() {
e.wasm.section(&names.to_name_section());
}
e.custom_sections(AfterLast);
if let Some(dwarf) = &mut dwarf {
dwarf.emit(&mut e);
}
return e.wasm.finish();
fn needs_data_count(funcs: &[&crate::core::Func<'_>]) -> bool {
funcs
.iter()
.filter_map(|f| match &f.kind {
FuncKind::Inline { expression, .. } => Some(expression),
_ => None,
})
.flat_map(|e| e.instrs.iter())
.any(|i| i.needs_data_count())
}
}
struct Encoder<'a> {
wasm: wasm_encoder::Module,
customs: &'a [&'a Custom<'a>],
}
impl Encoder<'_> {
fn custom_sections(&mut self, place: CustomPlace) {
for entry in self.customs.iter() {
if entry.place() == place {
entry.encode(&mut self.wasm);
}
}
}
fn typed_section<T>(&mut self, list: &[T])
where
T: SectionItem,
{
self.custom_sections(CustomPlace::Before(T::ANCHOR));
if !list.is_empty() {
let mut section = T::Section::default();
for item in list {
item.encode(&mut section);
}
self.wasm.section(§ion);
}
self.custom_sections(CustomPlace::After(T::ANCHOR));
}
/// Encodes the code section of a wasm module module while additionally
/// handling the branch hinting proposal.
///
/// The branch hinting proposal requires to encode the offsets of the
/// instructions relative from the beginning of the function. Here we encode
/// each instruction and we save its offset. If needed, we use this
/// information to build the branch hint section and insert it before the
/// code section.
///
/// The `list` provided is the list of functions that are emitted into the
/// code section. The `func_index` provided is the initial index of defined
/// functions, so it's the count of imported functions. The `dwarf` field is
/// optionally used to track debugging information.
fn code_section<'a>(
&'a mut self,
list: &[&'a Func<'_>],
mut func_index: u32,
mut dwarf: Option<&mut dwarf::Dwarf>,
) {
self.custom_sections(CustomPlace::Before(CustomPlaceAnchor::Code));
if !list.is_empty() {
let mut branch_hints = wasm_encoder::BranchHints::new();
let mut code_section = wasm_encoder::CodeSection::new();
for func in list.iter() {
let hints = func.encode(&mut code_section, dwarf.as_deref_mut());
if !hints.is_empty() {
branch_hints.function_hints(func_index, hints.into_iter());
}
func_index += 1;
}
// Branch hints section has to be inserted before the Code section
// Insert the section only if we have some hints
if !branch_hints.is_empty() {
self.wasm.section(&branch_hints);
}
// Finally, insert the Code section from the tmp buffer
self.wasm.section(&code_section);
if let Some(dwarf) = &mut dwarf {
dwarf.set_code_section_size(code_section.byte_len());
}
}
self.custom_sections(CustomPlace::After(CustomPlaceAnchor::Code));
}
}
trait SectionItem {
type Section: wasm_encoder::Section + Default;
const ANCHOR: CustomPlaceAnchor;
fn encode(&self, section: &mut Self::Section);
}
impl<T> SectionItem for &T
where
T: SectionItem,
{
type Section = T::Section;
const ANCHOR: CustomPlaceAnchor = T::ANCHOR;
fn encode(&self, section: &mut Self::Section) {
T::encode(self, section)
}
}
impl From<&FunctionType<'_>> for wasm_encoder::FuncType {
fn from(ft: &FunctionType) -> Self {
wasm_encoder::FuncType::new(
ft.params.iter().map(|(_, _, ty)| (*ty).into()),
ft.results.iter().map(|ty| (*ty).into()),
)
}
}
impl From<&StructType<'_>> for wasm_encoder::StructType {
fn from(st: &StructType) -> wasm_encoder::StructType {
wasm_encoder::StructType {
fields: st.fields.iter().map(|f| f.into()).collect(),
}
}
}
impl From<&StructField<'_>> for wasm_encoder::FieldType {
fn from(f: &StructField) -> wasm_encoder::FieldType {
wasm_encoder::FieldType {
element_type: f.ty.into(),
mutable: f.mutable,
}
}
}
impl From<&ArrayType<'_>> for wasm_encoder::ArrayType {
fn from(at: &ArrayType) -> Self {
let field = wasm_encoder::FieldType {
element_type: at.ty.into(),
mutable: at.mutable,
};
wasm_encoder::ArrayType(field)
}
}
impl From<&ContType<'_>> for wasm_encoder::ContType {
fn from(at: &ContType) -> Self {
wasm_encoder::ContType(at.0.into())
}
}
enum RecOrType<'a> {
Type(&'a Type<'a>),
Rec(&'a Rec<'a>),
}
impl SectionItem for RecOrType<'_> {
type Section = wasm_encoder::TypeSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Type;
fn encode(&self, types: &mut wasm_encoder::TypeSection) {
match self {
RecOrType::Type(ty) => types.ty().subtype(&ty.to_subtype()),
RecOrType::Rec(rec) => types.ty().rec(rec.types.iter().map(|t| t.to_subtype())),
}
}
}
impl Type<'_> {
pub(crate) fn to_subtype(&self) -> wasm_encoder::SubType {
self.def.to_subtype()
}
}
impl TypeDef<'_> {
pub(crate) fn to_subtype(&self) -> wasm_encoder::SubType {
use wasm_encoder::CompositeInnerType::*;
let composite_type = wasm_encoder::CompositeType {
inner: match &self.kind {
InnerTypeKind::Func(ft) => Func(ft.into()),
InnerTypeKind::Struct(st) => Struct(st.into()),
InnerTypeKind::Array(at) => Array(at.into()),
InnerTypeKind::Cont(ct) => Cont(ct.into()),
},
shared: self.shared,
};
wasm_encoder::SubType {
composite_type,
is_final: self.final_type.unwrap_or(true),
supertype_idx: self.parent.map(|i| i.unwrap_u32()),
}
}
}
impl From<ValType<'_>> for wasm_encoder::ValType {
fn from(ty: ValType) -> Self {
match ty {
ValType::I32 => Self::I32,
ValType::I64 => Self::I64,
ValType::F32 => Self::F32,
ValType::F64 => Self::F64,
ValType::V128 => Self::V128,
ValType::Ref(r) => Self::Ref(r.into()),
}
}
}
impl From<RefType<'_>> for wasm_encoder::RefType {
fn from(r: RefType<'_>) -> Self {
wasm_encoder::RefType {
nullable: r.nullable,
heap_type: r.heap.into(),
}
}
}
impl From<HeapType<'_>> for wasm_encoder::HeapType {
fn from(r: HeapType<'_>) -> Self {
use wasm_encoder::AbstractHeapType::*;
match r {
HeapType::Abstract { shared, ty } => {
let ty = match ty {
AbstractHeapType::Func => Func,
AbstractHeapType::Extern => Extern,
AbstractHeapType::Exn => Exn,
AbstractHeapType::NoExn => NoExn,
AbstractHeapType::Any => Any,
AbstractHeapType::Eq => Eq,
AbstractHeapType::Struct => Struct,
AbstractHeapType::Array => Array,
AbstractHeapType::NoFunc => NoFunc,
AbstractHeapType::NoExtern => NoExtern,
AbstractHeapType::None => None,
AbstractHeapType::I31 => I31,
AbstractHeapType::Cont => Cont,
AbstractHeapType::NoCont => NoCont,
};
Self::Abstract { shared, ty }
}
HeapType::Concrete(i) => Self::Concrete(i.unwrap_u32()),
}
}
}
impl Encode for Option<Id<'_>> {
fn encode(&self, _e: &mut Vec<u8>) {
// used for parameters in the tuple impl as well as instruction labels
}
}
impl<'a> Encode for ValType<'a> {
fn encode(&self, e: &mut Vec<u8>) {
wasm_encoder::Encode::encode(&wasm_encoder::ValType::from(*self), e)
}
}
impl<'a> Encode for HeapType<'a> {
fn encode(&self, e: &mut Vec<u8>) {
wasm_encoder::Encode::encode(&wasm_encoder::HeapType::from(*self), e)
}
}
impl From<StorageType<'_>> for wasm_encoder::StorageType {
fn from(st: StorageType) -> Self {
use wasm_encoder::StorageType::*;
match st {
StorageType::I8 => I8,
StorageType::I16 => I16,
StorageType::Val(vt) => Val(vt.into()),
}
}
}
impl SectionItem for Import<'_> {
type Section = wasm_encoder::ImportSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Import;
fn encode(&self, section: &mut wasm_encoder::ImportSection) {
section.import(self.module, self.field, self.item.to_entity_type());
}
}
impl ItemSig<'_> {
pub(crate) fn to_entity_type(&self) -> wasm_encoder::EntityType {
self.kind.to_entity_type()
}
}
impl ItemKind<'_> {
fn to_entity_type(&self) -> wasm_encoder::EntityType {
use wasm_encoder::EntityType as ET;
match self {
ItemKind::Func(t) => ET::Function(t.unwrap_u32()),
ItemKind::Table(t) => ET::Table(t.to_table_type()),
ItemKind::Memory(t) => ET::Memory(t.to_memory_type()),
ItemKind::Global(t) => ET::Global(t.to_global_type()),
ItemKind::Tag(t) => ET::Tag(t.to_tag_type()),
}
}
}
impl TableType<'_> {
fn to_table_type(&self) -> wasm_encoder::TableType {
wasm_encoder::TableType {
element_type: self.elem.into(),
minimum: self.limits.min,
maximum: self.limits.max,
table64: self.limits.is64,
shared: self.shared,
}
}
}
impl MemoryType {
fn to_memory_type(&self) -> wasm_encoder::MemoryType {
wasm_encoder::MemoryType {
minimum: self.limits.min,
maximum: self.limits.max,
memory64: self.limits.is64,
shared: self.shared,
page_size_log2: self.page_size_log2,
}
}
}
impl GlobalType<'_> {
fn to_global_type(&self) -> wasm_encoder::GlobalType {
wasm_encoder::GlobalType {
val_type: self.ty.into(),
mutable: self.mutable,
shared: self.shared,
}
}
}
impl TagType<'_> {
fn to_tag_type(&self) -> wasm_encoder::TagType {
match self {
TagType::Exception(r) => wasm_encoder::TagType {
kind: wasm_encoder::TagKind::Exception,
func_type_idx: r.unwrap_u32(),
},
}
}
}
impl<T> TypeUse<'_, T> {
fn unwrap_u32(&self) -> u32 {
self.index
.as_ref()
.expect("TypeUse should be filled in by this point")
.unwrap_u32()
}
}
struct FuncSectionTy<'a>(&'a TypeUse<'a, FunctionType<'a>>);
impl SectionItem for FuncSectionTy<'_> {
type Section = wasm_encoder::FunctionSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Func;
fn encode(&self, section: &mut wasm_encoder::FunctionSection) {
section.function(self.0.unwrap_u32());
}
}
impl Encode for Index<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.unwrap_u32().encode(e)
}
}
impl Index<'_> {
fn unwrap_u32(&self) -> u32 {
match self {
Index::Num(n, _) => *n,
Index::Id(n) => panic!("unresolved index in emission: {:?}", n),
}
}
}
impl From<Index<'_>> for u32 {
fn from(i: Index<'_>) -> Self {
match i {
Index::Num(i, _) => i,
Index::Id(_) => unreachable!("unresolved index in encoding: {:?}", i),
}
}
}
impl SectionItem for Table<'_> {
type Section = wasm_encoder::TableSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Table;
fn encode(&self, section: &mut wasm_encoder::TableSection) {
assert!(self.exports.names.is_empty());
match &self.kind {
TableKind::Normal {
ty,
init_expr: None,
} => {
section.table(ty.to_table_type());
}
TableKind::Normal {
ty,
init_expr: Some(init_expr),
} => {
section.table_with_init(ty.to_table_type(), &init_expr.to_const_expr());
}
_ => panic!("TableKind should be normal during encoding"),
}
}
}
impl SectionItem for Memory<'_> {
type Section = wasm_encoder::MemorySection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Memory;
fn encode(&self, section: &mut wasm_encoder::MemorySection) {
assert!(self.exports.names.is_empty());
match &self.kind {
MemoryKind::Normal(t) => {
section.memory(t.to_memory_type());
}
_ => panic!("MemoryKind should be normal during encoding"),
}
}
}
impl SectionItem for Global<'_> {
type Section = wasm_encoder::GlobalSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Global;
fn encode(&self, section: &mut wasm_encoder::GlobalSection) {
assert!(self.exports.names.is_empty());
let init = match &self.kind {
GlobalKind::Inline(expr) => expr.to_const_expr(),
_ => panic!("GlobalKind should be inline during encoding"),
};
section.global(self.ty.to_global_type(), &init);
}
}
impl SectionItem for Export<'_> {
type Section = wasm_encoder::ExportSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Export;
fn encode(&self, section: &mut wasm_encoder::ExportSection) {
section.export(self.name, self.kind.into(), self.item.unwrap_u32());
}
}
impl From<ExportKind> for wasm_encoder::ExportKind {
fn from(kind: ExportKind) -> Self {
match kind {
ExportKind::Func => Self::Func,
ExportKind::Table => Self::Table,
ExportKind::Memory => Self::Memory,
ExportKind::Global => Self::Global,
ExportKind::Tag => Self::Tag,
}
}
}
impl SectionItem for Elem<'_> {
type Section = wasm_encoder::ElementSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Elem;
fn encode(&self, section: &mut wasm_encoder::ElementSection) {
use wasm_encoder::Elements;
let elements = match &self.payload {
ElemPayload::Indices(v) => {
Elements::Functions(Cow::Owned(v.iter().map(|i| i.unwrap_u32()).collect()))
}
ElemPayload::Exprs { exprs, ty } => Elements::Expressions(
(*ty).into(),
Cow::Owned(exprs.iter().map(|e| e.to_const_expr()).collect()),
),
};
match &self.kind {
ElemKind::Active { table, offset } => {
section.active(
table.map(|t| t.unwrap_u32()),
&offset.to_const_expr(),
elements,
);
}
ElemKind::Passive => {
section.passive(elements);
}
ElemKind::Declared => {
section.declared(elements);
}
}
}
}
impl SectionItem for Data<'_> {
type Section = wasm_encoder::DataSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Data;
fn encode(&self, section: &mut wasm_encoder::DataSection) {
let mut data = Vec::new();
for val in self.data.iter() {
val.push_onto(&mut data);
}
match &self.kind {
DataKind::Passive => {
section.passive(data);
}
DataKind::Active { memory, offset } => {
section.active(memory.unwrap_u32(), &offset.to_const_expr(), data);
}
}
}
}
impl Func<'_> {
/// Encodes the function into `e` while returning all branch hints with
/// known relative offsets after encoding.
///
/// The `dwarf` field is optional and used to track debugging information
/// for each instruction.
fn encode(
&self,
section: &mut wasm_encoder::CodeSection,
mut dwarf: Option<&mut dwarf::Dwarf>,
) -> Vec<wasm_encoder::BranchHint> {
assert!(self.exports.names.is_empty());
let (expr, locals) = match &self.kind {
FuncKind::Inline { expression, locals } => (expression, locals),
_ => panic!("should only have inline functions in emission"),
};
if let Some(dwarf) = &mut dwarf {
let index = match self.ty.index.as_ref().unwrap() {
Index::Num(n, _) => *n,
_ => unreachable!(),
};
dwarf.start_func(self.span, index, locals);
}
// Encode the function into a temporary vector because functions are
// prefixed with their length. The temporary vector, when encoded,
// encodes its length first then the body.
let mut func =
wasm_encoder::Function::new_with_locals_types(locals.iter().map(|t| t.ty.into()));
let branch_hints = expr.encode(&mut func, dwarf.as_deref_mut());
let func_size = func.byte_len();
section.function(&func);
if let Some(dwarf) = &mut dwarf {
dwarf.end_func(func_size, section.byte_len());
}
branch_hints
}
}
impl Expression<'_> {
/// Encodes this expression into `e` and optionally tracks debugging
/// information for each instruction in `dwarf`.
///
/// Returns all branch hints, if any, found while parsing this function.
fn encode(
&self,
func: &mut wasm_encoder::Function,
mut dwarf: Option<&mut dwarf::Dwarf>,
) -> Vec<wasm_encoder::BranchHint> {
let mut hints = Vec::with_capacity(self.branch_hints.len());
let mut next_hint = self.branch_hints.iter().peekable();
let mut tmp = Vec::new();
for (i, instr) in self.instrs.iter().enumerate() {
// Branch hints are stored in order of increasing `instr_index` so
// check to see if the next branch hint matches this instruction's
// index.
if let Some(hint) = next_hint.next_if(|h| h.instr_index == i) {
hints.push(wasm_encoder::BranchHint {
branch_func_offset: u32::try_from(func.byte_len() + tmp.len()).unwrap(),
branch_hint_value: hint.value,
});
}
// If DWARF is enabled then track this instruction's binary offset
// and source location.
if let Some(dwarf) = &mut dwarf {
if let Some(span) = self.instr_spans.as_ref().map(|s| s[i]) {
dwarf.instr(func.byte_len() + tmp.len(), span);
}
}
// Finally emit the instruction and move to the next.
instr.encode(&mut tmp);
}
func.raw(tmp.iter().copied());
func.instruction(&wasm_encoder::Instruction::End);
hints
}
fn to_const_expr(&self) -> wasm_encoder::ConstExpr {
let mut tmp = Vec::new();
for instr in self.instrs.iter() {
instr.encode(&mut tmp);
}
wasm_encoder::ConstExpr::raw(tmp)
}
}
impl Encode for BlockType<'_> {
fn encode(&self, e: &mut Vec<u8>) {
// block types using an index are encoded as an sleb, not a uleb
if let Some(Index::Num(n, _)) = &self.ty.index {
return i64::from(*n).encode(e);
}
let ty = self
.ty
.inline
.as_ref()
.expect("function type not filled in");
if ty.params.is_empty() && ty.results.is_empty() {
return e.push(0x40);
}
if ty.params.is_empty() && ty.results.len() == 1 {
return ty.results[0].encode(e);
}
panic!("multi-value block types should have an index");
}
}
impl Encode for LaneArg {
fn encode(&self, e: &mut Vec<u8>) {
self.lane.encode(e);
}
}
impl Encode for MemArg<'_> {
fn encode(&self, e: &mut Vec<u8>) {
match &self.memory {
Index::Num(0, _) => {
self.align.trailing_zeros().encode(e);
self.offset.encode(e);
}
_ => {
(self.align.trailing_zeros() | (1 << 6)).encode(e);
self.memory.encode(e);
self.offset.encode(e);
}
}
}
}
impl Encode for Ordering {
fn encode(&self, buf: &mut Vec<u8>) {
let flag: u8 = match self {
Ordering::SeqCst => 0,
Ordering::AcqRel => 1,
};
flag.encode(buf);
}
}
impl<T> Encode for Ordered<T>
where
T: Encode,
{
fn encode(&self, buf: &mut Vec<u8>) {
self.ordering.encode(buf);
self.inner.encode(buf);
}
}
impl Encode for LoadOrStoreLane<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.memarg.encode(e);
self.lane.encode(e);
}
}
impl Encode for CallIndirect<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.ty.unwrap_u32().encode(e);
self.table.encode(e);
}
}
impl Encode for TableInit<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.elem.encode(e);
self.table.encode(e);
}
}
impl Encode for TableCopy<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.dst.encode(e);
self.src.encode(e);
}
}
impl Encode for TableArg<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.dst.encode(e);
}
}
impl Encode for MemoryArg<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.mem.encode(e);
}
}
impl Encode for MemoryInit<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.data.encode(e);
self.mem.encode(e);
}
}
impl Encode for MemoryCopy<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.dst.encode(e);
self.src.encode(e);
}
}
impl Encode for BrTableIndices<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.labels.encode(e);
self.default.encode(e);
}
}
impl Encode for F32 {
fn encode(&self, e: &mut Vec<u8>) {
e.extend_from_slice(&self.bits.to_le_bytes());
}
}
impl Encode for F64 {
fn encode(&self, e: &mut Vec<u8>) {
e.extend_from_slice(&self.bits.to_le_bytes());
}
}
#[derive(Default)]
struct Names<'a> {
module: Option<&'a str>,
funcs: Vec<(u32, &'a str)>,
func_idx: u32,
locals: Vec<(u32, Vec<(u32, &'a str)>)>,
labels: Vec<(u32, Vec<(u32, &'a str)>)>,
globals: Vec<(u32, &'a str)>,
global_idx: u32,
memories: Vec<(u32, &'a str)>,
memory_idx: u32,
tables: Vec<(u32, &'a str)>,
table_idx: u32,
tags: Vec<(u32, &'a str)>,
tag_idx: u32,
types: Vec<(u32, &'a str)>,
type_idx: u32,
data: Vec<(u32, &'a str)>,
data_idx: u32,
elems: Vec<(u32, &'a str)>,
elem_idx: u32,
fields: Vec<(u32, Vec<(u32, &'a str)>)>,
}
fn find_names<'a>(
module_id: &Option<Id<'a>>,
module_name: &Option<NameAnnotation<'a>>,
fields: &[ModuleField<'a>],
) -> Names<'a> {
fn get_name<'a>(id: &Option<Id<'a>>, name: &Option<NameAnnotation<'a>>) -> Option<&'a str> {
name.as_ref().map(|n| n.name).or(id.and_then(|id| {
if id.is_gensym() {
None
} else {
Some(id.name())
}
}))
}
enum Name {
Type,
Global,
Func,
Memory,
Table,
Tag,
Elem,
Data,
}
let mut ret = Names::default();
ret.module = get_name(module_id, module_name);
let mut names = Vec::new();
for field in fields {
// Extract the kind/id/name from whatever kind of field this is...
let (kind, id, name) = match field {
ModuleField::Import(i) => (
match i.item.kind {
ItemKind::Func(_) => Name::Func,
ItemKind::Table(_) => Name::Table,
ItemKind::Memory(_) => Name::Memory,
ItemKind::Global(_) => Name::Global,
ItemKind::Tag(_) => Name::Tag,
},
&i.item.id,
&i.item.name,
),
ModuleField::Global(g) => (Name::Global, &g.id, &g.name),
ModuleField::Table(t) => (Name::Table, &t.id, &t.name),
ModuleField::Memory(m) => (Name::Memory, &m.id, &m.name),
ModuleField::Tag(t) => (Name::Tag, &t.id, &t.name),
ModuleField::Type(t) => (Name::Type, &t.id, &t.name),
ModuleField::Rec(r) => {
for ty in &r.types {
names.push((Name::Type, &ty.id, &ty.name, field));
}
continue;
}
ModuleField::Elem(e) => (Name::Elem, &e.id, &e.name),
ModuleField::Data(d) => (Name::Data, &d.id, &d.name),
ModuleField::Func(f) => (Name::Func, &f.id, &f.name),
ModuleField::Export(_) | ModuleField::Start(_) | ModuleField::Custom(_) => continue,
};
names.push((kind, id, name, field));
}
for (kind, id, name, field) in names {
// .. and using the kind we can figure out where to place this name
let (list, idx) = match kind {
Name::Func => (&mut ret.funcs, &mut ret.func_idx),
Name::Table => (&mut ret.tables, &mut ret.table_idx),
Name::Memory => (&mut ret.memories, &mut ret.memory_idx),
Name::Global => (&mut ret.globals, &mut ret.global_idx),
Name::Tag => (&mut ret.tags, &mut ret.tag_idx),
Name::Type => (&mut ret.types, &mut ret.type_idx),
Name::Elem => (&mut ret.elems, &mut ret.elem_idx),
Name::Data => (&mut ret.data, &mut ret.data_idx),
};
if let Some(name) = get_name(id, name) {
list.push((*idx, name));
}
// Handle module locals separately from above
if let ModuleField::Func(f) = field {
let mut local_names = Vec::new();
let mut label_names = Vec::new();
let mut local_idx = 0;
let mut label_idx = 0;
// Consult the inline type listed for local names of parameters.
// This is specifically preserved during the name resolution
// pass, but only for functions, so here we can look at the
// original source's names.
if let Some(ty) = &f.ty.inline {
for (id, name, _) in ty.params.iter() {
if let Some(name) = get_name(id, name) {
local_names.push((local_idx, name));
}
local_idx += 1;
}
}
if let FuncKind::Inline {
locals, expression, ..
} = &f.kind
{
for local in locals.iter() {
if let Some(name) = get_name(&local.id, &local.name) {
local_names.push((local_idx, name));
}
local_idx += 1;
}
for i in expression.instrs.iter() {
match i {
Instruction::If(block)
| Instruction::Block(block)
| Instruction::Loop(block)
| Instruction::Try(block)
| Instruction::TryTable(TryTable { block, .. }) => {
if let Some(name) = get_name(&block.label, &block.label_name) {
label_names.push((label_idx, name));
}
label_idx += 1;
}
_ => {}
}
}
}
if local_names.len() > 0 {
ret.locals.push((*idx, local_names));
}
if label_names.len() > 0 {
ret.labels.push((*idx, label_names));
}
}
// Handle struct fields separately from above
if let ModuleField::Type(ty) = field {
let mut field_names = vec![];
match &ty.def.kind {
InnerTypeKind::Func(_) | InnerTypeKind::Array(_) | InnerTypeKind::Cont(_) => {}
InnerTypeKind::Struct(ty_struct) => {
for (idx, field) in ty_struct.fields.iter().enumerate() {
if let Some(name) = get_name(&field.id, &None) {
field_names.push((idx as u32, name))
}
}
}
}
if field_names.len() > 0 {
ret.fields.push((*idx, field_names))
}
}
*idx += 1;
}
return ret;
}
impl Names<'_> {
fn is_empty(&self) -> bool {
self.module.is_none()
&& self.funcs.is_empty()
&& self.locals.is_empty()
&& self.labels.is_empty()
&& self.globals.is_empty()
&& self.memories.is_empty()
&& self.tables.is_empty()
&& self.types.is_empty()
&& self.elems.is_empty()
&& self.data.is_empty()
&& self.fields.is_empty()
&& self.tags.is_empty()
}
}
impl Names<'_> {
fn to_name_section(&self) -> wasm_encoder::NameSection {
let mut names = wasm_encoder::NameSection::default();
if let Some(id) = self.module {
names.module(id);
}
let name_map = |indices: &[(u32, &str)]| {
if indices.is_empty() {
return None;
}
let mut map = wasm_encoder::NameMap::default();
for (idx, name) in indices {
map.append(*idx, *name);
}
Some(map)
};
let indirect_name_map = |indices: &[(u32, Vec<(u32, &str)>)]| {
if indices.is_empty() {
return None;
}
let mut map = wasm_encoder::IndirectNameMap::default();
for (idx, names) in indices {
if let Some(names) = name_map(names) {
map.append(*idx, &names);
}
}
Some(map)
};
if let Some(map) = name_map(&self.funcs) {
names.functions(&map);
}
if let Some(map) = indirect_name_map(&self.locals) {
names.locals(&map);
}
if let Some(map) = indirect_name_map(&self.labels) {
names.labels(&map);
}
if let Some(map) = name_map(&self.types) {
names.types(&map);
}
if let Some(map) = name_map(&self.tables) {
names.tables(&map);
}
if let Some(map) = name_map(&self.memories) {
names.memories(&map);
}
if let Some(map) = name_map(&self.globals) {
names.globals(&map);
}
if let Some(map) = name_map(&self.elems) {
names.elements(&map);
}
if let Some(map) = name_map(&self.data) {
names.data(&map);
}
if let Some(map) = indirect_name_map(&self.fields) {
names.fields(&map);
}
if let Some(map) = name_map(&self.tags) {
names.tags(&map);
}
names
}
}
impl Encode for Id<'_> {
fn encode(&self, dst: &mut Vec<u8>) {
assert!(!self.is_gensym());
self.name().encode(dst);
}
}
impl<'a> Encode for TryTable<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.block.encode(dst);
self.catches.encode(dst);
}
}
impl<'a> Encode for TryTableCatch<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
let flag_byte: u8 = match self.kind {
TryTableCatchKind::Catch(..) => 0,
TryTableCatchKind::CatchRef(..) => 1,
TryTableCatchKind::CatchAll => 2,
TryTableCatchKind::CatchAllRef => 3,
};
flag_byte.encode(dst);
match self.kind {
TryTableCatchKind::Catch(tag) | TryTableCatchKind::CatchRef(tag) => {
tag.encode(dst);
}
TryTableCatchKind::CatchAll | TryTableCatchKind::CatchAllRef => {}
}
self.label.encode(dst);
}
}
impl<'a> Encode for ContBind<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.argument_index.encode(dst);
self.result_index.encode(dst);
}
}
impl<'a> Encode for Resume<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.type_index.encode(dst);
self.table.encode(dst);
}
}
impl<'a> Encode for ResumeThrow<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.type_index.encode(dst);
self.tag_index.encode(dst);
self.table.encode(dst);
}
}
impl<'a> Encode for ResumeTable<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.handlers.encode(dst);
}
}
impl<'a> Encode for Handle<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
match self {
Handle::OnLabel { tag, label } => {
dst.push(0x00);
tag.encode(dst);
label.encode(dst);
}
Handle::OnSwitch { tag } => {
dst.push(0x01);
tag.encode(dst);
}
}
}
}
impl<'a> Encode for Switch<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
self.type_index.encode(dst);
self.tag_index.encode(dst);
}
}
impl Encode for V128Const {
fn encode(&self, dst: &mut Vec<u8>) {
dst.extend_from_slice(&self.to_le_bytes());
}
}
impl Encode for I8x16Shuffle {
fn encode(&self, dst: &mut Vec<u8>) {
dst.extend_from_slice(&self.lanes);
}
}
impl<'a> Encode for SelectTypes<'a> {
fn encode(&self, dst: &mut Vec<u8>) {
match &self.tys {
Some(list) => {
dst.push(0x1c);
list.encode(dst);
}
None => dst.push(0x1b),
}
}
}
impl Custom<'_> {
fn encode(&self, module: &mut wasm_encoder::Module) {
match self {
Custom::Raw(r) => {
module.section(&r.to_section());
}
Custom::Producers(p) => {
module.section(&p.to_section());
}
Custom::Dylink0(p) => {
module.section(&p.to_section());
}
}
}
}
impl RawCustomSection<'_> {
fn to_section(&self) -> wasm_encoder::CustomSection<'_> {
let mut ret = Vec::new();
for list in self.data.iter() {
ret.extend_from_slice(list);
}
wasm_encoder::CustomSection {
name: self.name.into(),
data: ret.into(),
}
}
}
impl Producers<'_> {
pub(crate) fn to_section(&self) -> wasm_encoder::ProducersSection {
let mut ret = wasm_encoder::ProducersSection::default();
for (name, fields) in self.fields.iter() {
let mut field = wasm_encoder::ProducersField::new();
for (key, value) in fields {
field.value(key, value);
}
ret.field(name, &field);
}
ret
}
}
impl Dylink0<'_> {
fn to_section(&self) -> wasm_encoder::CustomSection<'_> {
let mut e = Vec::new();
for section in self.subsections.iter() {
e.push(section.id());
let mut tmp = Vec::new();
section.encode(&mut tmp);
tmp.encode(&mut e);
}
wasm_encoder::CustomSection {
name: "dylink.0".into(),
data: e.into(),
}
}
}
impl Encode for Dylink0Subsection<'_> {
fn encode(&self, e: &mut Vec<u8>) {
match self {
Dylink0Subsection::MemInfo {
memory_size,
memory_align,
table_size,
table_align,
} => {
memory_size.encode(e);
memory_align.encode(e);
table_size.encode(e);
table_align.encode(e);
}
Dylink0Subsection::Needed(libs) => libs.encode(e),
Dylink0Subsection::ExportInfo(list) => list.encode(e),
Dylink0Subsection::ImportInfo(list) => list.encode(e),
}
}
}
impl SectionItem for Tag<'_> {
type Section = wasm_encoder::TagSection;
const ANCHOR: CustomPlaceAnchor = CustomPlaceAnchor::Tag;
fn encode(&self, section: &mut wasm_encoder::TagSection) {
section.tag(self.ty.to_tag_type());
match &self.kind {
TagKind::Inline() => {}
_ => panic!("TagKind should be inline during encoding"),
}
}
}
impl Encode for StructAccess<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.r#struct.encode(e);
self.field.encode(e);
}
}
impl Encode for ArrayFill<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.array.encode(e);
}
}
impl Encode for ArrayCopy<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.dest_array.encode(e);
self.src_array.encode(e);
}
}
impl Encode for ArrayInit<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.array.encode(e);
self.segment.encode(e);
}
}
impl Encode for ArrayNewFixed<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.array.encode(e);
self.length.encode(e);
}
}
impl Encode for ArrayNewData<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.array.encode(e);
self.data_idx.encode(e);
}
}
impl Encode for ArrayNewElem<'_> {
fn encode(&self, e: &mut Vec<u8>) {
self.array.encode(e);
self.elem_idx.encode(e);
}
}
impl Encode for RefTest<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0xfb);
if self.r#type.nullable {
e.push(0x15);
} else {
e.push(0x14);
}
self.r#type.heap.encode(e);
}
}
impl Encode for RefCast<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0xfb);
if self.r#type.nullable {
e.push(0x17);
} else {
e.push(0x16);
}
self.r#type.heap.encode(e);
}
}
fn br_on_cast_flags(from_nullable: bool, to_nullable: bool) -> u8 {
let mut flag = 0;
if from_nullable {
flag |= 1 << 0;
}
if to_nullable {
flag |= 1 << 1;
}
flag
}
impl Encode for BrOnCast<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0xfb);
e.push(0x18);
e.push(br_on_cast_flags(
self.from_type.nullable,
self.to_type.nullable,
));
self.label.encode(e);
self.from_type.heap.encode(e);
self.to_type.heap.encode(e);
}
}
impl Encode for BrOnCastFail<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0xfb);
e.push(0x19);
e.push(br_on_cast_flags(
self.from_type.nullable,
self.to_type.nullable,
));
self.label.encode(e);
self.from_type.heap.encode(e);
self.to_type.heap.encode(e);
}
}