use crate::{CustomSection, Encode, Ieee32, Ieee64, Section};
use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
#[derive(Clone, Debug, Default)]
pub struct CoreDumpSection {
name: String,
}
impl CoreDumpSection {
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
CoreDumpSection { name }
}
fn as_custom<'a>(&'a self) -> CustomSection<'a> {
let mut data = vec![0];
self.name.encode(&mut data);
CustomSection {
name: "core".into(),
data: Cow::Owned(data),
}
}
}
impl Encode for CoreDumpSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
#[derive(Debug)]
pub struct CoreDumpModulesSection {
num_added: u32,
bytes: Vec<u8>,
}
impl CoreDumpModulesSection {
pub fn new() -> Self {
CoreDumpModulesSection {
bytes: vec![],
num_added: 0,
}
}
pub fn as_custom(&self) -> CustomSection<'_> {
let mut data = vec![];
self.num_added.encode(&mut data);
data.extend(self.bytes.iter().copied());
CustomSection {
name: "coremodules".into(),
data: Cow::Owned(data),
}
}
pub fn module(&mut self, module_name: impl AsRef<str>) -> &mut Self {
self.bytes.push(0x0);
module_name.as_ref().encode(&mut self.bytes);
self.num_added += 1;
self
}
pub fn len(&self) -> u32 {
self.num_added
}
}
impl Encode for CoreDumpModulesSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpModulesSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
#[derive(Debug)]
pub struct CoreDumpInstancesSection {
num_added: u32,
bytes: Vec<u8>,
}
impl CoreDumpInstancesSection {
pub fn new() -> Self {
CoreDumpInstancesSection {
bytes: vec![],
num_added: 0,
}
}
pub fn as_custom(&self) -> CustomSection<'_> {
let mut data = vec![];
self.num_added.encode(&mut data);
data.extend(self.bytes.iter().copied());
CustomSection {
name: "coreinstances".into(),
data: Cow::Owned(data),
}
}
pub fn instance<M, G>(&mut self, module_index: u32, memories: M, globals: G) -> &mut Self
where
M: IntoIterator<Item = u32>,
<M as IntoIterator>::IntoIter: ExactSizeIterator,
G: IntoIterator<Item = u32>,
<G as IntoIterator>::IntoIter: ExactSizeIterator,
{
self.bytes.push(0x0);
module_index.encode(&mut self.bytes);
crate::encode_vec(memories, &mut self.bytes);
crate::encode_vec(globals, &mut self.bytes);
self.num_added += 1;
self
}
pub fn len(&self) -> u32 {
self.num_added
}
}
impl Encode for CoreDumpInstancesSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpInstancesSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
#[derive(Clone, Debug, Default)]
pub struct CoreDumpStackSection {
frame_bytes: Vec<u8>,
count: u32,
name: String,
}
impl CoreDumpStackSection {
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
CoreDumpStackSection {
frame_bytes: Vec::new(),
count: 0,
name,
}
}
pub fn frame<L, S>(
&mut self,
instanceidx: u32,
funcidx: u32,
codeoffset: u32,
locals: L,
stack: S,
) -> &mut Self
where
L: IntoIterator<Item = CoreDumpValue>,
<L as IntoIterator>::IntoIter: ExactSizeIterator,
S: IntoIterator<Item = CoreDumpValue>,
<S as IntoIterator>::IntoIter: ExactSizeIterator,
{
self.count += 1;
self.frame_bytes.push(0);
instanceidx.encode(&mut self.frame_bytes);
funcidx.encode(&mut self.frame_bytes);
codeoffset.encode(&mut self.frame_bytes);
crate::encode_vec(locals, &mut self.frame_bytes);
crate::encode_vec(stack, &mut self.frame_bytes);
self
}
pub fn as_custom<'a>(&'a self) -> CustomSection<'a> {
let mut data = vec![0];
self.name.encode(&mut data);
self.count.encode(&mut data);
data.extend(&self.frame_bytes);
CustomSection {
name: "corestack".into(),
data: Cow::Owned(data),
}
}
}
impl Encode for CoreDumpStackSection {
fn encode(&self, sink: &mut Vec<u8>) {
self.as_custom().encode(sink);
}
}
impl Section for CoreDumpStackSection {
fn id(&self) -> u8 {
crate::core::SectionId::Custom as u8
}
}
#[derive(Clone, Debug)]
pub enum CoreDumpValue {
Missing,
I32(i32),
I64(i64),
F32(Ieee32),
F64(Ieee64),
}
impl Encode for CoreDumpValue {
fn encode(&self, sink: &mut Vec<u8>) {
match self {
CoreDumpValue::Missing => sink.push(0x01),
CoreDumpValue::I32(x) => {
sink.push(0x7F);
x.encode(sink);
}
CoreDumpValue::I64(x) => {
sink.push(0x7E);
x.encode(sink);
}
CoreDumpValue::F32(x) => {
sink.push(0x7D);
x.encode(sink);
}
CoreDumpValue::F64(x) => {
sink.push(0x7C);
x.encode(sink);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Module;
use wasmparser::{KnownCustom, Parser, Payload};
#[test]
fn test_roundtrip_core() {
let core = CoreDumpSection::new("test.wasm");
let mut module = Module::new();
module.section(&core);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "core");
let core = match section.as_known() {
KnownCustom::CoreDump(s) => s,
_ => panic!("not coredump"),
};
assert_eq!(core.name, "test.wasm");
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_roundtrip_coremodules() {
let mut coremodules = CoreDumpModulesSection::new();
coremodules.module("test_module");
let mut module = crate::Module::new();
module.section(&coremodules);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "coremodules");
let modules = match section.as_known() {
KnownCustom::CoreDumpModules(s) => s,
_ => panic!("not coremodules"),
};
assert_eq!(modules.modules[0], "test_module");
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_roundtrip_coreinstances() {
let mut coreinstances = CoreDumpInstancesSection::new();
let module_index = 0;
let memories = vec![42];
let globals = vec![17];
coreinstances.instance(module_index, memories, globals);
let mut module = Module::new();
module.section(&coreinstances);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "coreinstances");
let coreinstances = match section.as_known() {
KnownCustom::CoreDumpInstances(s) => s,
_ => panic!("not coreinstances"),
};
assert_eq!(coreinstances.instances.len(), 1);
let instance = coreinstances
.instances
.first()
.expect("instance is encoded");
assert_eq!(instance.module_index, 0);
assert_eq!(instance.memories.len(), 1);
assert_eq!(instance.globals.len(), 1);
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_roundtrip_corestack() {
let mut corestack = CoreDumpStackSection::new("main");
corestack.frame(
0,
12,
0,
vec![CoreDumpValue::I32(10)],
vec![CoreDumpValue::I32(42)],
);
let mut module = Module::new();
module.section(&corestack);
let wasm_bytes = module.finish();
let mut parser = Parser::new(0).parse_all(&wasm_bytes);
match parser.next() {
Some(Ok(Payload::Version { .. })) => {}
_ => panic!(""),
}
let payload = parser
.next()
.expect("parser is not empty")
.expect("element is a payload");
match payload {
Payload::CustomSection(section) => {
assert_eq!(section.name(), "corestack");
let corestack = match section.as_known() {
KnownCustom::CoreDumpStack(s) => s,
_ => panic!("not a corestack section"),
};
assert_eq!(corestack.name, "main");
assert_eq!(corestack.frames.len(), 1);
let frame = corestack
.frames
.first()
.expect("frame is encoded in corestack");
assert_eq!(frame.instanceidx, 0);
assert_eq!(frame.funcidx, 12);
assert_eq!(frame.codeoffset, 0);
assert_eq!(frame.locals.len(), 1);
match frame.locals.first().expect("frame contains a local") {
&wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 10),
_ => panic!("unexpected local value"),
}
assert_eq!(frame.stack.len(), 1);
match frame.stack.first().expect("stack contains a value") {
&wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 42),
_ => panic!("unexpected stack value"),
}
}
_ => panic!("unexpected payload"),
}
}
#[test]
fn test_encode_coredump_section() {
let core = CoreDumpSection::new("test");
let mut encoded = vec![];
core.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(encoded, vec![
11,
4,
b'c',b'o',b'r',b'e',
0, 4, b't', b'e', b's', b't',
]);
}
#[test]
fn test_encode_coremodules_section() {
let mut modules = CoreDumpModulesSection::new();
modules.module("mod1");
modules.module("mod2");
let mut encoded = vec![];
modules.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(encoded, vec![
25,
11,
b'c',b'o',b'r',b'e',b'm',b'o',b'd',b'u',b'l',b'e',b's',
2,
0x0, 4, b'm',b'o',b'd',b'1',
0x0, 4, b'm',b'o',b'd',b'2'
]);
}
#[test]
fn test_encode_coreinstances_section() {
let mut instances = CoreDumpInstancesSection::new();
instances.instance(0, vec![42], vec![17]);
let mut encoded = vec![];
instances.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(encoded, vec![
21,
13,
b'c',b'o',b'r',b'e',b'i',b'n',b's',b't',b'a',b'n',b'c',b'e',b's',
1,
0x0, 0,
1, 42,
1, 17
]);
}
#[test]
fn test_encode_corestack_section() {
let mut thread = CoreDumpStackSection::new("main");
thread.frame(
0,
42,
51,
vec![CoreDumpValue::I32(1)],
vec![CoreDumpValue::I32(2)],
);
let mut encoded = vec![];
thread.encode(&mut encoded);
#[rustfmt::skip]
assert_eq!(
encoded,
vec![
27,
9,
b'c',b'o',b'r',b'e',b's',b't',b'a',b'c',b'k',
0, 4,
b'm',b'a',b'i',b'n',
1,
0, 0, 42, 51,
1,
0x7F,
1,
1,
0x7F,
2
]
);
}
}