Skip to content

Commit

Permalink
wasmprinter: prints field names declared in the names section (#1512)
Browse files Browse the repository at this point in the history
* wasmprinter: prints field names declared in the names section

* fixes for testsuite

* bit of type juggling

* missed this one

* names field names in the types section for --name-unnamed

* emits names for fields when encoding with wast

* adds an extra test case for field names and updates snapshots

---------

Co-authored-by: Christoph Hegemann <christoph@deepchannel.com>
  • Loading branch information
kritzcreek and Christoph Hegemann authored Apr 24, 2024
1 parent cbd87b7 commit 97cad53
Show file tree
Hide file tree
Showing 16 changed files with 197 additions and 54 deletions.
1 change: 1 addition & 0 deletions crates/wasm-metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ impl<'a> ModuleNames<'a> {
wasmparser::Name::Global(m) => section.globals(&name_map(&m)?),
wasmparser::Name::Element(m) => section.elements(&name_map(&m)?),
wasmparser::Name::Data(m) => section.data(&name_map(&m)?),
wasmparser::Name::Field(m) => section.fields(&indirect_name_map(&m)?),
wasmparser::Name::Tag(m) => section.tags(&name_map(&m)?),
wasmparser::Name::Unknown { .. } => {} // wasm-encoder doesn't support it
}
Expand Down
3 changes: 3 additions & 0 deletions crates/wasmparser/src/readers/core/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub enum Name<'a> {
Element(NameMap<'a>),
/// The name is for the data segments.
Data(NameMap<'a>),
/// The name is for fields.
Field(IndirectNameMap<'a>),
/// The name is for tags.
Tag(NameMap<'a>),
/// An unknown [name subsection](https://webassembly.github.io/spec/core/appendix/custom.html#subsections).
Expand Down Expand Up @@ -145,6 +147,7 @@ impl<'a> Subsection<'a> for Name<'a> {
7 => Name::Global(NameMap::new(data, offset)?),
8 => Name::Element(NameMap::new(data, offset)?),
9 => Name::Data(NameMap::new(data, offset)?),
10 => Name::Field(IndirectNameMap::new(data, offset)?),
11 => Name::Tag(NameMap::new(data, offset)?),
ty => Name::Unknown {
ty,
Expand Down
58 changes: 39 additions & 19 deletions crates/wasmprinter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct CoreState {
local_names: NamingMap<(u32, u32), NameLocal>,
label_names: NamingMap<(u32, u32), NameLabel>,
type_names: NamingMap<u32, NameType>,
field_names: NamingMap<(u32, u32), NameField>,
tag_names: NamingMap<u32, NameTag>,
table_names: NamingMap<u32, NameTable>,
memory_names: NamingMap<u32, NameMemory>,
Expand Down Expand Up @@ -639,7 +640,7 @@ impl Printer {
let mut used = match name {
// labels can be shadowed, so maintaining the used names is not useful.
"label" => None,
"local" => Some(HashSet::new()),
"local" | "field" => Some(HashSet::new()),
_ => unimplemented!("{name} is an unknown type of indirect names"),
};
for naming in indirect.names {
Expand Down Expand Up @@ -668,6 +669,7 @@ impl Printer {
Name::Global(n) => name_map(&mut state.core.global_names, n, "global")?,
Name::Element(n) => name_map(&mut state.core.element_names, n, "elem")?,
Name::Data(n) => name_map(&mut state.core.data_names, n, "data")?,
Name::Field(n) => indirect_name_map(&mut state.core.field_names, n, "field")?,
Name::Tag(n) => name_map(&mut state.core.tag_names, n, "tag")?,
Name::Unknown { .. } => (),
}
Expand Down Expand Up @@ -774,37 +776,33 @@ impl Printer {

fn print_type(&mut self, state: &mut State, ty: SubType) -> Result<()> {
self.start_group("type ");
self.print_name(&state.core.type_names, state.core.types.len() as u32)?;
let ty_idx = state.core.types.len() as u32;
self.print_name(&state.core.type_names, ty_idx)?;
self.result.push(' ');
self.print_sub(state, &ty, None)?;
self.print_sub(state, &ty, ty_idx)?;
self.end_group(); // `type`
state.core.types.push(Some(ty));
Ok(())
}

fn print_sub(&mut self, state: &State, ty: &SubType, names_for: Option<u32>) -> Result<u32> {
fn print_sub(&mut self, state: &State, ty: &SubType, ty_idx: u32) -> Result<u32> {
let r = if !ty.is_final || !ty.supertype_idx.is_none() {
self.start_group("sub");
self.print_sub_type(state, ty)?;
let r = self.print_composite(state, &ty.composite_type, names_for)?;
let r = self.print_composite(state, &ty.composite_type, ty_idx)?;
self.end_group(); // `sub`
r
} else {
self.print_composite(state, &ty.composite_type, names_for)?
self.print_composite(state, &ty.composite_type, ty_idx)?
};
Ok(r)
}

fn print_composite(
&mut self,
state: &State,
ty: &CompositeType,
names_for: Option<u32>,
) -> Result<u32> {
fn print_composite(&mut self, state: &State, ty: &CompositeType, ty_idx: u32) -> Result<u32> {
let r = match &ty {
CompositeType::Func(ty) => {
self.start_group("func");
let r = self.print_func_type(state, ty, names_for)?;
let r = self.print_func_type(state, ty, None)?;
self.end_group(); // `func`
r
}
Expand All @@ -816,7 +814,7 @@ impl Printer {
}
CompositeType::Struct(ty) => {
self.start_group("struct");
let r = self.print_struct_type(state, ty)?;
let r = self.print_struct_type(state, ty, ty_idx)?;
self.end_group(); // `struct`
r
}
Expand Down Expand Up @@ -904,8 +902,20 @@ impl Printer {
Ok(ty.params().len() as u32)
}

fn print_field_type(&mut self, state: &State, ty: &FieldType) -> Result<u32> {
fn print_field_type(
&mut self,
state: &State,
ty: &FieldType,
ty_field_idx: Option<(u32, u32)>,
) -> Result<u32> {
self.result.push(' ');
if let Some(idxs @ (_, field_idx)) = ty_field_idx {
match state.core.field_names.index_to_name.get(&idxs) {
Some(name) => write!(self.result, "${} ", name.identifier())?,
None if self.name_unnamed => write!(self.result, "$#field{field_idx} ")?,
None => {}
}
}
if ty.mutable {
self.result.push_str("(mut ");
}
Expand All @@ -917,13 +927,13 @@ impl Printer {
}

fn print_array_type(&mut self, state: &State, ty: &ArrayType) -> Result<u32> {
self.print_field_type(state, &ty.0)
self.print_field_type(state, &ty.0, None)
}

fn print_struct_type(&mut self, state: &State, ty: &StructType) -> Result<u32> {
for field in ty.fields.iter() {
fn print_struct_type(&mut self, state: &State, ty: &StructType, ty_idx: u32) -> Result<u32> {
for (field_index, field) in ty.fields.iter().enumerate() {
self.result.push_str(" (field");
self.print_field_type(state, field)?;
self.print_field_type(state, field, Some((ty_idx, field_index as u32)))?;
self.result.push(')');
}
Ok(0)
Expand Down Expand Up @@ -1459,6 +1469,15 @@ impl Printer {
Ok(())
}

fn print_field_idx(&mut self, state: &State, ty: u32, idx: u32) -> Result<()> {
match state.core.field_names.index_to_name.get(&(ty, idx)) {
Some(name) => write!(self.result, "${}", name.identifier())?,
None if self.name_unnamed => write!(self.result, "$#field{idx}")?,
None => write!(self.result, "{}", idx)?,
}
Ok(())
}

fn print_name<K>(&mut self, names: &NamingMap<u32, K>, cur_idx: u32) -> Result<()>
where
K: NamingNamespace,
Expand Down Expand Up @@ -3184,6 +3203,7 @@ naming_namespaces! {
struct NameTable => "table"
struct NameValue => "value"
struct NameType => "type"
struct NameField => "field"
struct NameData => "data"
struct NameElem => "elem"
struct NameComponent => "component"
Expand Down
29 changes: 24 additions & 5 deletions crates/wasmprinter/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,6 @@ impl<'a, 'b> PrintOperator<'a, 'b> {
self.printer.print_idx(&self.state.core.type_names, idx)
}

fn field_index(&mut self, idx: u32) -> Result<()> {
write!(&mut self.printer.result, "{idx}")?;
Ok(())
}

fn from_ref_type(&mut self, ref_ty: RefType) -> Result<()> {
self.printer.print_reftype(self.state, ref_ty)
}
Expand Down Expand Up @@ -555,6 +550,30 @@ macro_rules! define_visit {
.ok_or_else(|| anyhow!("implementation limit: type index too large"))?;
$self.printer.print_reftype($self.state, rty)?;
);
(payload $self:ident StructGet $ty:ident $field:ident) => (
$self.push_str(" ");
$self.struct_type_index($ty)?;
$self.push_str(" ");
$self.printer.print_field_idx($self.state, $ty, $field)?;
);
(payload $self:ident StructGetS $ty:ident $field:ident) => (
$self.push_str(" ");
$self.struct_type_index($ty)?;
$self.push_str(" ");
$self.printer.print_field_idx($self.state, $ty, $field)?;
);
(payload $self:ident StructGetU $ty:ident $field:ident) => (
$self.push_str(" ");
$self.struct_type_index($ty)?;
$self.push_str(" ");
$self.printer.print_field_idx($self.state, $ty, $field)?;
);
(payload $self:ident StructSet $ty:ident $field:ident) => (
$self.push_str(" ");
$self.struct_type_index($ty)?;
$self.push_str(" ");
$self.printer.print_field_idx($self.state, $ty, $field)?;
);
(payload $self:ident $op:ident $($arg:ident)*) => (
$(
$self.push_str(" ");
Expand Down
26 changes: 25 additions & 1 deletion crates/wast/src/core/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ struct Names<'a> {
data_idx: u32,
elems: Vec<(u32, &'a str)>,
elem_idx: u32,
fields: Vec<(u32, Vec<(u32, &'a str)>)>,
}

fn find_names<'a>(
Expand Down Expand Up @@ -1045,6 +1046,24 @@ fn find_names<'a>(
}
}

// Handle struct fields separately from above
if let ModuleField::Type(ty) = field {
let mut field_names = vec![];
match &ty.def {
TypeDef::Func(_) | TypeDef::Array(_) => {}
TypeDef::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;
}

Expand All @@ -1061,8 +1080,9 @@ impl Names<'_> {
&& self.memories.is_empty()
&& self.tables.is_empty()
&& self.types.is_empty()
&& self.data.is_empty()
&& self.elems.is_empty()
&& self.data.is_empty()
&& self.fields.is_empty()
&& self.tags.is_empty()
// NB: specifically don't check modules/instances since they're
// not encoded for now.
Expand Down Expand Up @@ -1119,6 +1139,10 @@ impl Encode for Names<'_> {
self.data.encode(&mut tmp);
subsec(9, &mut tmp);
}
if self.fields.len() > 0 {
self.fields.encode(&mut tmp);
subsec(10, &mut tmp);
}
if self.tags.len() > 0 {
self.tags.encode(&mut tmp);
subsec(11, &mut tmp);
Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn validate_name_section(wasm: &[u8]) -> wasmparser::Result<()> {
name?;
}
}
Name::Local(n) | Name::Label(n) => {
Name::Local(n) | Name::Label(n) | Name::Field(n) => {
for name in n {
for name in name?.names {
name?;
Expand Down
1 change: 1 addition & 0 deletions src/bin/wasm-tools/demangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl Opts {
Name::Data(names) => new_section.data(&self.name_map(names)?),
Name::Local(names) => new_section.locals(&self.indirect_name_map(names)?),
Name::Label(names) => new_section.labels(&self.indirect_name_map(names)?),
Name::Field(names) => new_section.fields(&self.indirect_name_map(names)?),
Name::Tag(names) => new_section.tags(&self.name_map(names)?),
Name::Unknown { .. } => bail!("unknown name section"),
}
Expand Down
1 change: 1 addition & 0 deletions src/bin/wasm-tools/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ impl<'a> Dump<'a> {
Name::Global(n) => self.print_name_map("global", n)?,
Name::Element(n) => self.print_name_map("element", n)?,
Name::Data(n) => self.print_name_map("data", n)?,
Name::Field(n) => self.print_indirect_name_map("type", "field", n)?,
Name::Tag(n) => self.print_name_map("tag", n)?,
Name::Unknown { ty, range, .. } => {
write!(self.state, "unknown names: {}", ty)?;
Expand Down
39 changes: 39 additions & 0 deletions tests/local/gc/gc-struct-names.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
;; --enable-gc

(module
(type $named (struct (field $x i32) (field $y i32)))
(type $partially_named (struct (field $x i32) (field i32)))
(type $unnamed (struct (field i32) (field i32)))

(func (param (ref $named) (ref $partially_named) (ref $unnamed))
i32.const 1
i32.const 2
struct.new $named
local.tee 0
struct.get $named 0
local.get 0
struct.get $named 1
drop
drop

i32.const 1
i32.const 2
struct.new $partially_named
local.tee 1
struct.get $partially_named 0
local.get 1
struct.get $partially_named 1
drop
drop

i32.const 1
i32.const 2
struct.new $unnamed
local.tee 2
struct.get $unnamed 0
local.get 2
struct.get $unnamed 1
drop
drop
)
)
35 changes: 35 additions & 0 deletions tests/snapshots/local/gc/gc-struct-names.wat.print
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
(module
(type $named (;0;) (struct (field $x i32) (field $y i32)))
(type $partially_named (;1;) (struct (field $x i32) (field i32)))
(type $unnamed (;2;) (struct (field i32) (field i32)))
(type (;3;) (func (param (ref $named) (ref $partially_named) (ref $unnamed))))
(func (;0;) (type 3) (param (ref $named) (ref $partially_named) (ref $unnamed))
i32.const 1
i32.const 2
struct.new $named
local.tee 0
struct.get $named $x
local.get 0
struct.get $named $y
drop
drop
i32.const 1
i32.const 2
struct.new $partially_named
local.tee 1
struct.get $partially_named $x
local.get 1
struct.get $partially_named 1
drop
drop
i32.const 1
i32.const 2
struct.new $unnamed
local.tee 2
struct.get $unnamed 0
local.get 2
struct.get $unnamed 1
drop
drop
)
)
6 changes: 3 additions & 3 deletions tests/snapshots/local/gc/gc-struct-types.wat.print
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
(type (;5;) (struct (field (mut i32)) (field (mut i32))))
(type $a (;6;) (struct (field f32)))
(type $b (;7;) (struct (field f32)))
(type (;8;) (struct (field externref)))
(type (;9;) (struct (field externref) (field funcref)))
(type (;8;) (struct (field $field_a externref)))
(type (;9;) (struct (field $field_b externref) (field $field_c funcref)))
(type (;10;) (struct))
(type (;11;) (struct (field i32) (field i32)))
(type (;12;) (struct (field i32) (field (mut i32))))
(type (;13;) (struct (field (mut i32)) (field i32)))
(type (;14;) (struct (field (mut i32)) (field (mut i32))))
(type (;15;) (struct (field i32) (field f32) (field f32) (field f32)))
(type (;15;) (struct (field $a i32) (field f32) (field f32) (field f32)))
(type (;16;) (struct (field i32) (field (ref null $a)) (field (mut (ref null $b)))))
(type (;17;) (struct (field i32) (field (ref null $a)) (field (mut (ref null $b)))))
(type (;18;) (struct (field i32) (field i64) (field i8) (field i31ref) (field anyref)))
Expand Down
Loading

0 comments on commit 97cad53

Please sign in to comment.