mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-04-25 14:57:56 +00:00
Add rustfmt.toml from substrate repo (#161)
* Add rustfmt.toml from substrate repo * Apply rustfmt to code base * Fix formatting * Move rustfmt job to the top
This commit is contained in:
committed by
GitHub
parent
77ad07e347
commit
a0b548b37d
+98
-103
@@ -1,8 +1,8 @@
|
||||
use crate::std::vec::Vec;
|
||||
|
||||
use super::{resolve_func_type, Error};
|
||||
use log::trace;
|
||||
use parity_wasm::elements::{self, BlockType, Type};
|
||||
use super::{resolve_func_type, Error};
|
||||
|
||||
/// Control stack frame.
|
||||
#[derive(Debug)]
|
||||
@@ -35,10 +35,7 @@ struct Stack {
|
||||
|
||||
impl Stack {
|
||||
fn new() -> Stack {
|
||||
Stack {
|
||||
height: 0,
|
||||
control_stack: Vec::new(),
|
||||
}
|
||||
Stack { height: 0, control_stack: Vec::new() }
|
||||
}
|
||||
|
||||
/// Returns current height of the value stack.
|
||||
@@ -64,7 +61,8 @@ impl Stack {
|
||||
/// This effectively makes stack polymorphic.
|
||||
fn mark_unreachable(&mut self) -> Result<(), Error> {
|
||||
trace!(target: "max_height", "unreachable");
|
||||
let top_frame = self.control_stack
|
||||
let top_frame = self
|
||||
.control_stack
|
||||
.last_mut()
|
||||
.ok_or_else(|| Error("stack must be non-empty".into()))?;
|
||||
top_frame.is_polymorphic = true;
|
||||
@@ -82,7 +80,8 @@ impl Stack {
|
||||
/// Returns `Err` if the control stack is empty.
|
||||
fn pop_frame(&mut self) -> Result<Frame, Error> {
|
||||
trace!(target: "max_height", "pop_frame: {:?}", self.control_stack.last());
|
||||
Ok(self.control_stack
|
||||
Ok(self
|
||||
.control_stack
|
||||
.pop()
|
||||
.ok_or_else(|| Error("stack must be non-empty".into()))?)
|
||||
}
|
||||
@@ -98,7 +97,8 @@ impl Stack {
|
||||
/// Returns `Err` if the height overflow usize value.
|
||||
fn push_values(&mut self, value_count: u32) -> Result<(), Error> {
|
||||
trace!(target: "max_height", "push: {}", value_count);
|
||||
self.height = self.height
|
||||
self.height = self
|
||||
.height
|
||||
.checked_add(value_count)
|
||||
.ok_or_else(|| Error("stack overflow".into()))?;
|
||||
Ok(())
|
||||
@@ -111,7 +111,7 @@ impl Stack {
|
||||
fn pop_values(&mut self, value_count: u32) -> Result<(), Error> {
|
||||
trace!(target: "max_height", "pop: {}", value_count);
|
||||
if value_count == 0 {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
{
|
||||
let top_frame = self.frame(0)?;
|
||||
@@ -127,7 +127,8 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
self.height = self.height
|
||||
self.height = self
|
||||
.height
|
||||
.checked_sub(value_count)
|
||||
.ok_or_else(|| Error("stack underflow".into()))?;
|
||||
|
||||
@@ -139,16 +140,10 @@ impl Stack {
|
||||
pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, Error> {
|
||||
use parity_wasm::elements::Instruction::*;
|
||||
|
||||
let func_section = module
|
||||
.function_section()
|
||||
.ok_or_else(|| Error("No function section".into()))?;
|
||||
let code_section = module
|
||||
.code_section()
|
||||
.ok_or_else(|| Error("No code section".into()))?;
|
||||
let type_section = module
|
||||
.type_section()
|
||||
.ok_or_else(|| Error("No type section".into()))?;
|
||||
|
||||
let func_section =
|
||||
module.function_section().ok_or_else(|| Error("No function section".into()))?;
|
||||
let code_section = module.code_section().ok_or_else(|| Error("No code section".into()))?;
|
||||
let type_section = module.type_section().ok_or_else(|| Error("No type section".into()))?;
|
||||
|
||||
trace!(target: "max_height", "func_idx: {}", func_idx);
|
||||
|
||||
@@ -184,7 +179,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
|
||||
loop {
|
||||
if pc >= instructions.elements().len() {
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
// If current value stack is higher than maximal height observed so far,
|
||||
@@ -198,7 +193,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
trace!(target: "max_height", "{:?}", opcode);
|
||||
|
||||
match opcode {
|
||||
Nop => {}
|
||||
Nop => {},
|
||||
Block(ty) | Loop(ty) | If(ty) => {
|
||||
let end_arity = if *ty == BlockType::NoResult { 0 } else { 1 };
|
||||
let branch_arity = if let Loop(_) = *opcode { 0 } else { end_arity };
|
||||
@@ -212,19 +207,19 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
branch_arity,
|
||||
start_height: height,
|
||||
});
|
||||
}
|
||||
},
|
||||
Else => {
|
||||
// The frame at the top should be pushed by `If`. So we leave
|
||||
// it as is.
|
||||
}
|
||||
},
|
||||
End => {
|
||||
let frame = stack.pop_frame()?;
|
||||
stack.trunc(frame.start_height);
|
||||
stack.push_values(frame.end_arity)?;
|
||||
}
|
||||
},
|
||||
Unreachable => {
|
||||
stack.mark_unreachable()?;
|
||||
}
|
||||
},
|
||||
Br(target) => {
|
||||
// Pop values for the destination block result.
|
||||
let target_arity = stack.frame(*target)?.branch_arity;
|
||||
@@ -233,7 +228,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
// This instruction unconditionally transfers control to the specified block,
|
||||
// thus all instruction until the end of the current block is deemed unreachable
|
||||
stack.mark_unreachable()?;
|
||||
}
|
||||
},
|
||||
BrIf(target) => {
|
||||
// Pop values for the destination block result.
|
||||
let target_arity = stack.frame(*target)?.branch_arity;
|
||||
@@ -244,7 +239,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
|
||||
// Push values back.
|
||||
stack.push_values(target_arity)?;
|
||||
}
|
||||
},
|
||||
BrTable(br_table_data) => {
|
||||
let arity_of_default = stack.frame(br_table_data.default)?.branch_arity;
|
||||
|
||||
@@ -252,9 +247,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
for target in &*br_table_data.table {
|
||||
let arity = stack.frame(*target)?.branch_arity;
|
||||
if arity != arity_of_default {
|
||||
return Err(Error(
|
||||
"Arity of all jump-targets must be equal".into()
|
||||
))
|
||||
return Err(Error("Arity of all jump-targets must be equal".into()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,13 +258,13 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
// This instruction doesn't let control flow to go further, since the control flow
|
||||
// should take either one of branches depending on the value or the default branch.
|
||||
stack.mark_unreachable()?;
|
||||
}
|
||||
},
|
||||
Return => {
|
||||
// Pop return values of the function. Mark successive instructions as unreachable
|
||||
// since this instruction doesn't let control flow to go further.
|
||||
stack.pop_values(func_arity)?;
|
||||
stack.mark_unreachable()?;
|
||||
}
|
||||
},
|
||||
Call(idx) => {
|
||||
let ty = resolve_func_type(*idx, module)?;
|
||||
|
||||
@@ -281,7 +274,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
// Push result of the function execution to the stack.
|
||||
let callee_arity = ty.results().len() as u32;
|
||||
stack.push_values(callee_arity)?;
|
||||
}
|
||||
},
|
||||
CallIndirect(x, _) => {
|
||||
let Type::Function(ty) = type_section
|
||||
.types()
|
||||
@@ -297,10 +290,10 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
// Push result of the function execution to the stack.
|
||||
let callee_arity = ty.results().len() as u32;
|
||||
stack.push_values(callee_arity)?;
|
||||
}
|
||||
},
|
||||
Drop => {
|
||||
stack.pop_values(1)?;
|
||||
}
|
||||
},
|
||||
Select => {
|
||||
// Pop two values and one condition.
|
||||
stack.pop_values(2)?;
|
||||
@@ -308,118 +301,118 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
|
||||
// Push the selected value.
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
GetLocal(_) => {
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
SetLocal(_) => {
|
||||
stack.pop_values(1)?;
|
||||
}
|
||||
},
|
||||
TeeLocal(_) => {
|
||||
// This instruction pops and pushes the value, so
|
||||
// effectively it doesn't modify the stack height.
|
||||
stack.pop_values(1)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
GetGlobal(_) => {
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
SetGlobal(_) => {
|
||||
stack.pop_values(1)?;
|
||||
}
|
||||
I32Load(_, _)
|
||||
| I64Load(_, _)
|
||||
| F32Load(_, _)
|
||||
| F64Load(_, _)
|
||||
| I32Load8S(_, _)
|
||||
| I32Load8U(_, _)
|
||||
| I32Load16S(_, _)
|
||||
| I32Load16U(_, _)
|
||||
| I64Load8S(_, _)
|
||||
| I64Load8U(_, _)
|
||||
| I64Load16S(_, _)
|
||||
| I64Load16U(_, _)
|
||||
| I64Load32S(_, _)
|
||||
| I64Load32U(_, _) => {
|
||||
},
|
||||
I32Load(_, _) |
|
||||
I64Load(_, _) |
|
||||
F32Load(_, _) |
|
||||
F64Load(_, _) |
|
||||
I32Load8S(_, _) |
|
||||
I32Load8U(_, _) |
|
||||
I32Load16S(_, _) |
|
||||
I32Load16U(_, _) |
|
||||
I64Load8S(_, _) |
|
||||
I64Load8U(_, _) |
|
||||
I64Load16S(_, _) |
|
||||
I64Load16U(_, _) |
|
||||
I64Load32S(_, _) |
|
||||
I64Load32U(_, _) => {
|
||||
// These instructions pop the address and pushes the result,
|
||||
// which effictively don't modify the stack height.
|
||||
stack.pop_values(1)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32Store(_, _)
|
||||
| I64Store(_, _)
|
||||
| F32Store(_, _)
|
||||
| F64Store(_, _)
|
||||
| I32Store8(_, _)
|
||||
| I32Store16(_, _)
|
||||
| I64Store8(_, _)
|
||||
| I64Store16(_, _)
|
||||
| I64Store32(_, _) => {
|
||||
I32Store(_, _) |
|
||||
I64Store(_, _) |
|
||||
F32Store(_, _) |
|
||||
F64Store(_, _) |
|
||||
I32Store8(_, _) |
|
||||
I32Store16(_, _) |
|
||||
I64Store8(_, _) |
|
||||
I64Store16(_, _) |
|
||||
I64Store32(_, _) => {
|
||||
// These instructions pop the address and the value.
|
||||
stack.pop_values(2)?;
|
||||
}
|
||||
},
|
||||
|
||||
CurrentMemory(_) => {
|
||||
// Pushes current memory size
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
GrowMemory(_) => {
|
||||
// Grow memory takes the value of pages to grow and pushes
|
||||
stack.pop_values(1)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32Const(_) | I64Const(_) | F32Const(_) | F64Const(_) => {
|
||||
// These instructions just push the single literal value onto the stack.
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32Eqz | I64Eqz => {
|
||||
// These instructions pop the value and compare it against zero, and pushes
|
||||
// the result of the comparison.
|
||||
stack.pop_values(1)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32Eq | I32Ne | I32LtS | I32LtU | I32GtS | I32GtU | I32LeS | I32LeU | I32GeS
|
||||
| I32GeU | I64Eq | I64Ne | I64LtS | I64LtU | I64GtS | I64GtU | I64LeS | I64LeU
|
||||
| I64GeS | I64GeU | F32Eq | F32Ne | F32Lt | F32Gt | F32Le | F32Ge | F64Eq | F64Ne
|
||||
| F64Lt | F64Gt | F64Le | F64Ge => {
|
||||
I32Eq | I32Ne | I32LtS | I32LtU | I32GtS | I32GtU | I32LeS | I32LeU | I32GeS |
|
||||
I32GeU | I64Eq | I64Ne | I64LtS | I64LtU | I64GtS | I64GtU | I64LeS | I64LeU |
|
||||
I64GeS | I64GeU | F32Eq | F32Ne | F32Lt | F32Gt | F32Le | F32Ge | F64Eq | F64Ne |
|
||||
F64Lt | F64Gt | F64Le | F64Ge => {
|
||||
// Comparison operations take two operands and produce one result.
|
||||
stack.pop_values(2)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32Clz | I32Ctz | I32Popcnt | I64Clz | I64Ctz | I64Popcnt | F32Abs | F32Neg
|
||||
| F32Ceil | F32Floor | F32Trunc | F32Nearest | F32Sqrt | F64Abs | F64Neg | F64Ceil
|
||||
| F64Floor | F64Trunc | F64Nearest | F64Sqrt => {
|
||||
I32Clz | I32Ctz | I32Popcnt | I64Clz | I64Ctz | I64Popcnt | F32Abs | F32Neg |
|
||||
F32Ceil | F32Floor | F32Trunc | F32Nearest | F32Sqrt | F64Abs | F64Neg | F64Ceil |
|
||||
F64Floor | F64Trunc | F64Nearest | F64Sqrt => {
|
||||
// Unary operators take one operand and produce one result.
|
||||
stack.pop_values(1)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32Add | I32Sub | I32Mul | I32DivS | I32DivU | I32RemS | I32RemU | I32And | I32Or
|
||||
| I32Xor | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr | I64Add | I64Sub
|
||||
| I64Mul | I64DivS | I64DivU | I64RemS | I64RemU | I64And | I64Or | I64Xor | I64Shl
|
||||
| I64ShrS | I64ShrU | I64Rotl | I64Rotr | F32Add | F32Sub | F32Mul | F32Div
|
||||
| F32Min | F32Max | F32Copysign | F64Add | F64Sub | F64Mul | F64Div | F64Min
|
||||
| F64Max | F64Copysign => {
|
||||
I32Add | I32Sub | I32Mul | I32DivS | I32DivU | I32RemS | I32RemU | I32And | I32Or |
|
||||
I32Xor | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr | I64Add | I64Sub |
|
||||
I64Mul | I64DivS | I64DivU | I64RemS | I64RemU | I64And | I64Or | I64Xor | I64Shl |
|
||||
I64ShrS | I64ShrU | I64Rotl | I64Rotr | F32Add | F32Sub | F32Mul | F32Div |
|
||||
F32Min | F32Max | F32Copysign | F64Add | F64Sub | F64Mul | F64Div | F64Min |
|
||||
F64Max | F64Copysign => {
|
||||
// Binary operators take two operands and produce one result.
|
||||
stack.pop_values(2)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
|
||||
I32WrapI64 | I32TruncSF32 | I32TruncUF32 | I32TruncSF64 | I32TruncUF64
|
||||
| I64ExtendSI32 | I64ExtendUI32 | I64TruncSF32 | I64TruncUF32 | I64TruncSF64
|
||||
| I64TruncUF64 | F32ConvertSI32 | F32ConvertUI32 | F32ConvertSI64 | F32ConvertUI64
|
||||
| F32DemoteF64 | F64ConvertSI32 | F64ConvertUI32 | F64ConvertSI64 | F64ConvertUI64
|
||||
| F64PromoteF32 | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32
|
||||
| F64ReinterpretI64 => {
|
||||
I32WrapI64 | I32TruncSF32 | I32TruncUF32 | I32TruncSF64 | I32TruncUF64 |
|
||||
I64ExtendSI32 | I64ExtendUI32 | I64TruncSF32 | I64TruncUF32 | I64TruncSF64 |
|
||||
I64TruncUF64 | F32ConvertSI32 | F32ConvertUI32 | F32ConvertSI64 | F32ConvertUI64 |
|
||||
F32DemoteF64 | F64ConvertSI32 | F64ConvertUI32 | F64ConvertSI64 | F64ConvertUI64 |
|
||||
F64PromoteF32 | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 |
|
||||
F64ReinterpretI64 => {
|
||||
// Conversion operators take one value and produce one result.
|
||||
stack.pop_values(1)?;
|
||||
stack.push_values(1)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
pc += 1;
|
||||
}
|
||||
@@ -429,8 +422,8 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use parity_wasm::elements;
|
||||
use super::*;
|
||||
use parity_wasm::elements;
|
||||
|
||||
fn parse_wat(source: &str) -> elements::Module {
|
||||
elements::deserialize_buffer(&wabt::wat2wasm(source).expect("Failed to wat2wasm"))
|
||||
@@ -515,12 +508,14 @@ mod tests {
|
||||
)
|
||||
)
|
||||
"#;
|
||||
let module = elements::deserialize_buffer(&wabt::Wat2Wasm::new()
|
||||
.validate(false)
|
||||
.convert(SOURCE)
|
||||
.expect("Failed to wat2wasm")
|
||||
.as_ref())
|
||||
.expect("Failed to deserialize the module");
|
||||
let module = elements::deserialize_buffer(
|
||||
&wabt::Wat2Wasm::new()
|
||||
.validate(false)
|
||||
.convert(SOURCE)
|
||||
.expect("Failed to wat2wasm")
|
||||
.as_ref(),
|
||||
)
|
||||
.expect("Failed to deserialize the module");
|
||||
|
||||
let height = compute(0, &module).unwrap();
|
||||
assert_eq!(height, 2);
|
||||
@@ -550,7 +545,7 @@ mod tests {
|
||||
assert_eq!(height, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[test]
|
||||
fn breaks() {
|
||||
let module = parse_wat(
|
||||
r#"
|
||||
@@ -572,7 +567,7 @@ mod tests {
|
||||
assert_eq!(height, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[test]
|
||||
fn if_else_works() {
|
||||
let module = parse_wat(
|
||||
r#"
|
||||
|
||||
+30
-49
@@ -49,11 +49,12 @@
|
||||
//! between the frames.
|
||||
//! - upon entry into the function entire stack frame is allocated.
|
||||
|
||||
use crate::std::string::String;
|
||||
use crate::std::vec::Vec;
|
||||
use crate::std::{string::String, vec::Vec};
|
||||
|
||||
use parity_wasm::elements::{self, Type};
|
||||
use parity_wasm::builder;
|
||||
use parity_wasm::{
|
||||
builder,
|
||||
elements::{self, Type},
|
||||
};
|
||||
|
||||
/// Macro to generate preamble and postamble.
|
||||
macro_rules! instrument_call {
|
||||
@@ -151,14 +152,14 @@ fn generate_stack_height_global(module: &mut elements::Module) -> u32 {
|
||||
for section in module.sections_mut() {
|
||||
if let elements::Section::Global(gs) = section {
|
||||
gs.entries_mut().push(global_entry);
|
||||
return (gs.entries().len() as u32) - 1;
|
||||
return (gs.entries().len() as u32) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// Existing section not found, create one!
|
||||
module.sections_mut().push(elements::Section::Global(
|
||||
elements::GlobalSection::with_entries(vec![global_entry]),
|
||||
));
|
||||
module
|
||||
.sections_mut()
|
||||
.push(elements::Section::Global(elements::GlobalSection::with_entries(vec![global_entry])));
|
||||
0
|
||||
}
|
||||
|
||||
@@ -188,13 +189,13 @@ fn compute_stack_cost(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
// To calculate the cost of a function we need to convert index from
|
||||
// function index space to defined function spaces.
|
||||
let func_imports = module.import_count(elements::ImportCountType::Function) as u32;
|
||||
let defined_func_idx = func_idx.checked_sub(func_imports).ok_or_else(|| {
|
||||
Error("This should be a index of a defined function".into())
|
||||
})?;
|
||||
let defined_func_idx = func_idx
|
||||
.checked_sub(func_imports)
|
||||
.ok_or_else(|| Error("This should be a index of a defined function".into()))?;
|
||||
|
||||
let code_section = module.code_section().ok_or_else(|| {
|
||||
Error("Due to validation code section should exists".into())
|
||||
})?;
|
||||
let code_section = module
|
||||
.code_section()
|
||||
.ok_or_else(|| Error("Due to validation code section should exists".into()))?;
|
||||
let body = &code_section
|
||||
.bodies()
|
||||
.get(defined_func_idx as usize)
|
||||
@@ -207,13 +208,10 @@ fn compute_stack_cost(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
||||
.ok_or_else(|| Error("Overflow in local count".into()))?;
|
||||
}
|
||||
|
||||
let max_stack_height =
|
||||
max_height::compute(
|
||||
defined_func_idx,
|
||||
module
|
||||
)?;
|
||||
let max_stack_height = max_height::compute(defined_func_idx, module)?;
|
||||
|
||||
locals_count.checked_add(max_stack_height)
|
||||
locals_count
|
||||
.checked_add(max_stack_height)
|
||||
.ok_or_else(|| Error("Overflow in adding locals_count and max_stack_height".into()))
|
||||
}
|
||||
|
||||
@@ -264,14 +262,11 @@ fn instrument_function(
|
||||
let mut cursor = 0;
|
||||
loop {
|
||||
if cursor >= instructions.elements().len() {
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
enum Action {
|
||||
InstrumentCall {
|
||||
callee_idx: u32,
|
||||
callee_stack_cost: u32,
|
||||
},
|
||||
InstrumentCall { callee_idx: u32, callee_stack_cost: u32 },
|
||||
Nop,
|
||||
}
|
||||
|
||||
@@ -279,21 +274,14 @@ fn instrument_function(
|
||||
let instruction = &instructions.elements()[cursor];
|
||||
match instruction {
|
||||
Call(callee_idx) => {
|
||||
let callee_stack_cost = ctx
|
||||
.stack_cost(*callee_idx)
|
||||
.ok_or_else(||
|
||||
Error(
|
||||
format!("Call to function that out-of-bounds: {}", callee_idx)
|
||||
)
|
||||
)?;
|
||||
let callee_stack_cost = ctx.stack_cost(*callee_idx).ok_or_else(|| {
|
||||
Error(format!("Call to function that out-of-bounds: {}", callee_idx))
|
||||
})?;
|
||||
|
||||
// Instrument only calls to a functions which stack_cost is
|
||||
// non-zero.
|
||||
if callee_stack_cost > 0 {
|
||||
Action::InstrumentCall {
|
||||
callee_idx: *callee_idx,
|
||||
callee_stack_cost,
|
||||
}
|
||||
Action::InstrumentCall { callee_idx: *callee_idx, callee_stack_cost }
|
||||
} else {
|
||||
Action::Nop
|
||||
}
|
||||
@@ -326,11 +314,11 @@ fn instrument_function(
|
||||
|
||||
// Advance cursor to be after the inserted sequence.
|
||||
cursor += new_seq.len();
|
||||
}
|
||||
},
|
||||
// Do nothing for other instructions.
|
||||
_ => {
|
||||
cursor += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,10 +330,7 @@ fn resolve_func_type(
|
||||
module: &elements::Module,
|
||||
) -> Result<&elements::FunctionType, Error> {
|
||||
let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]);
|
||||
let functions = module
|
||||
.function_section()
|
||||
.map(|fs| fs.entries())
|
||||
.unwrap_or(&[]);
|
||||
let functions = module.function_section().map(|fs| fs.entries()).unwrap_or(&[]);
|
||||
|
||||
let func_imports = module.import_count(elements::ImportCountType::Function);
|
||||
let sig_idx = if func_idx < func_imports as u32 {
|
||||
@@ -371,18 +356,15 @@ fn resolve_func_type(
|
||||
.type_ref()
|
||||
};
|
||||
let Type::Function(ty) = types.get(sig_idx as usize).ok_or_else(|| {
|
||||
Error(format!(
|
||||
"Signature {} (specified by func {}) isn't defined",
|
||||
sig_idx, func_idx
|
||||
))
|
||||
Error(format!("Signature {} (specified by func {}) isn't defined", sig_idx, func_idx))
|
||||
})?;
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use parity_wasm::elements;
|
||||
use super::*;
|
||||
use parity_wasm::elements;
|
||||
|
||||
fn parse_wat(source: &str) -> elements::Module {
|
||||
elements::deserialize_buffer(&wabt::wat2wasm(source).expect("Failed to wat2wasm"))
|
||||
@@ -411,8 +393,7 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
let module = inject_limiter(module, 1024)
|
||||
.expect("Failed to inject stack counter");
|
||||
let module = inject_limiter(module, 1024).expect("Failed to inject stack counter");
|
||||
validate_module(module);
|
||||
}
|
||||
}
|
||||
|
||||
+48
-58
@@ -1,11 +1,13 @@
|
||||
#[cfg(features = "std")]
|
||||
use crate::std::collections::{HashMap as Map};
|
||||
#[cfg(not(features = "std"))]
|
||||
use crate::std::collections::{BTreeMap as Map};
|
||||
use crate::std::collections::BTreeMap as Map;
|
||||
#[cfg(features = "std")]
|
||||
use crate::std::collections::HashMap as Map;
|
||||
use crate::std::vec::Vec;
|
||||
|
||||
use parity_wasm::elements::{self, FunctionType, Internal};
|
||||
use parity_wasm::builder;
|
||||
use parity_wasm::{
|
||||
builder,
|
||||
elements::{self, FunctionType, Internal},
|
||||
};
|
||||
|
||||
use super::{resolve_func_type, Context, Error};
|
||||
|
||||
@@ -23,41 +25,38 @@ pub(crate) fn generate_thunks(
|
||||
// First, we need to collect all function indices that should be replaced by thunks
|
||||
|
||||
let mut replacement_map: Map<u32, Thunk> = {
|
||||
let exports = module
|
||||
.export_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[]);
|
||||
let elem_segments = module
|
||||
.elements_section()
|
||||
.map(|es| es.entries())
|
||||
.unwrap_or(&[]);
|
||||
let start_func_idx = module
|
||||
.start_section();
|
||||
let exports = module.export_section().map(|es| es.entries()).unwrap_or(&[]);
|
||||
let elem_segments = module.elements_section().map(|es| es.entries()).unwrap_or(&[]);
|
||||
let start_func_idx = module.start_section();
|
||||
|
||||
let exported_func_indices = exports.iter().filter_map(|entry| match entry.internal() {
|
||||
Internal::Function(function_idx) => Some(*function_idx),
|
||||
_ => None,
|
||||
});
|
||||
let table_func_indices = elem_segments
|
||||
.iter()
|
||||
.flat_map(|segment| segment.members())
|
||||
.cloned();
|
||||
let table_func_indices =
|
||||
elem_segments.iter().flat_map(|segment| segment.members()).cloned();
|
||||
|
||||
// Replacement map is at least export section size.
|
||||
let mut replacement_map: Map<u32, Thunk> = Map::new();
|
||||
|
||||
for func_idx in exported_func_indices.chain(table_func_indices).chain(start_func_idx.into_iter()) {
|
||||
let callee_stack_cost = ctx.stack_cost(func_idx).ok_or_else(|| {
|
||||
Error(format!("function with idx {} isn't found", func_idx))
|
||||
})?;
|
||||
for func_idx in exported_func_indices
|
||||
.chain(table_func_indices)
|
||||
.chain(start_func_idx.into_iter())
|
||||
{
|
||||
let callee_stack_cost = ctx
|
||||
.stack_cost(func_idx)
|
||||
.ok_or_else(|| Error(format!("function with idx {} isn't found", func_idx)))?;
|
||||
|
||||
// Don't generate a thunk if stack_cost of a callee is zero.
|
||||
if callee_stack_cost != 0 {
|
||||
replacement_map.insert(func_idx, Thunk {
|
||||
signature: resolve_func_type(func_idx, &module)?.clone(),
|
||||
idx: None,
|
||||
callee_stack_cost,
|
||||
});
|
||||
replacement_map.insert(
|
||||
func_idx,
|
||||
Thunk {
|
||||
signature: resolve_func_type(func_idx, &module)?.clone(),
|
||||
idx: None,
|
||||
callee_stack_cost,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +80,8 @@ pub(crate) fn generate_thunks(
|
||||
// - argument pushing
|
||||
// - instrumented call
|
||||
// - end
|
||||
let mut thunk_body: Vec<elements::Instruction> = Vec::with_capacity(
|
||||
thunk.signature.params().len() +
|
||||
instrumented_call.len() +
|
||||
1
|
||||
);
|
||||
let mut thunk_body: Vec<elements::Instruction> =
|
||||
Vec::with_capacity(thunk.signature.params().len() + instrumented_call.len() + 1);
|
||||
|
||||
for (arg_idx, _) in thunk.signature.params().iter().enumerate() {
|
||||
thunk_body.push(elements::Instruction::GetLocal(arg_idx as u32));
|
||||
@@ -95,18 +91,17 @@ pub(crate) fn generate_thunks(
|
||||
|
||||
// TODO: Don't generate a signature, but find an existing one.
|
||||
|
||||
mbuilder = mbuilder.function()
|
||||
// Signature of the thunk should match the original function signature.
|
||||
.signature()
|
||||
.with_params(thunk.signature.params().to_vec())
|
||||
.with_results(thunk.signature.results().to_vec())
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(elements::Instructions::new(
|
||||
thunk_body
|
||||
))
|
||||
.build()
|
||||
.build();
|
||||
mbuilder = mbuilder
|
||||
.function()
|
||||
// Signature of the thunk should match the original function signature.
|
||||
.signature()
|
||||
.with_params(thunk.signature.params().to_vec())
|
||||
.with_results(thunk.signature.results().to_vec())
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(elements::Instructions::new(thunk_body))
|
||||
.build()
|
||||
.build();
|
||||
|
||||
thunk.idx = Some(next_func_idx);
|
||||
next_func_idx += 1;
|
||||
@@ -120,32 +115,27 @@ pub(crate) fn generate_thunks(
|
||||
// Check whether this function is in replacement_map, since
|
||||
// we can skip thunk generation (e.g. if stack_cost of function is 0).
|
||||
if let Some(thunk) = replacement_map.get(function_idx) {
|
||||
*function_idx = thunk
|
||||
.idx
|
||||
.expect("At this point an index must be assigned to each thunk");
|
||||
*function_idx =
|
||||
thunk.idx.expect("At this point an index must be assigned to each thunk");
|
||||
}
|
||||
};
|
||||
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
elements::Section::Export(export_section) => {
|
||||
elements::Section::Export(export_section) =>
|
||||
for entry in export_section.entries_mut() {
|
||||
if let Internal::Function(function_idx) = entry.internal_mut() {
|
||||
fixup(function_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
elements::Section::Element(elem_section) => {
|
||||
},
|
||||
elements::Section::Element(elem_section) =>
|
||||
for segment in elem_section.entries_mut() {
|
||||
for function_idx in segment.members_mut() {
|
||||
fixup(function_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
elements::Section::Start(start_idx) => {
|
||||
fixup(start_idx)
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
elements::Section::Start(start_idx) => fixup(start_idx),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user