use crate::mappings::map_operator;
pub const REMAINING_POINTS_EXPORT_NAME: &str = "wasmarin_metering_remaining_points";
pub struct Metering {
enabled: bool,
remaining_points_global_index: u32,
bulk_memory_operation_unit: i64,
}
impl Default for Metering {
fn default() -> Self {
Self::new(false)
}
}
impl Metering {
pub fn new(enabled: bool) -> Self {
Self {
enabled,
remaining_points_global_index: 0,
bulk_memory_operation_unit: 32,
}
}
pub fn update_global_section(&mut self, global_section: &mut wasm_encoder::GlobalSection) {
if self.enabled {
self.remaining_points_global_index = global_section.len();
global_section.global(
wasm_encoder::GlobalType {
val_type: wasm_encoder::ValType::I64,
mutable: true,
shared: false,
},
&wasm_encoder::ConstExpr::i64_const(0),
);
}
}
pub fn update_export_section(&mut self, export_section: &mut wasm_encoder::ExportSection) {
if self.enabled {
export_section.export(REMAINING_POINTS_EXPORT_NAME, wasm_encoder::ExportKind::Global, self.remaining_points_global_index);
}
}
pub fn update_function(&mut self, function: &mut wasm_encoder::Function, operators: Vec<wasmparser::Operator>) {
if self.enabled {
let mut accumulated_cost = 0;
for operator in operators {
accumulated_cost += self.cost(&operator);
for op in self.feed(operator, &mut accumulated_cost) {
function.instruction(&map_operator(op));
}
}
} else {
for operator in operators {
function.instruction(&map_operator(operator));
}
}
}
fn feed<'a>(&self, operator: wasmparser::Operator<'a>, accumulated_cost: &mut i64) -> Vec<wasmparser::Operator<'a>> {
if self.is_branching_operator(&operator) && *accumulated_cost > 0 {
return vec![
wasmparser::Operator::GlobalGet {
global_index: self.remaining_points_global_index,
},
wasmparser::Operator::I64Const { value: *accumulated_cost },
wasmparser::Operator::I64Sub,
wasmparser::Operator::GlobalSet {
global_index: self.remaining_points_global_index,
},
wasmparser::Operator::GlobalGet {
global_index: self.remaining_points_global_index,
},
wasmparser::Operator::I64Const { value: 0 },
wasmparser::Operator::I64LtS,
wasmparser::Operator::If {
blockty: wasmparser::BlockType::Empty,
},
wasmparser::Operator::Unreachable,
wasmparser::Operator::End,
operator,
];
}
if self.is_bulk_memory_operator(&operator) {
_ = self.bulk_memory_operation_unit;
return vec![operator];
}
vec![operator]
}
fn cost(&self, operator: &wasmparser::Operator) -> i64 {
match operator {
wasmparser::Operator::End => 0,
_ => 1,
}
}
fn is_branching_operator(&self, operator: &wasmparser::Operator) -> bool {
matches!(
operator,
wasmparser::Operator::Loop { .. } | wasmparser::Operator::End | wasmparser::Operator::If { .. } | wasmparser::Operator::Else | wasmparser::Operator::Br { .. } | wasmparser::Operator::BrTable { .. } | wasmparser::Operator::BrIf { .. } | wasmparser::Operator::Call { .. } | wasmparser::Operator::CallIndirect { .. } | wasmparser::Operator::Return | wasmparser::Operator::Throw { .. } | wasmparser::Operator::ThrowRef | wasmparser::Operator::Rethrow { .. } | wasmparser::Operator::Delegate { .. } | wasmparser::Operator::Catch { .. } | wasmparser::Operator::ReturnCall { .. } | wasmparser::Operator::ReturnCallIndirect { .. } | wasmparser::Operator::BrOnCast { .. } | wasmparser::Operator::BrOnCastFail { .. } | wasmparser::Operator::CallRef { .. } | wasmparser::Operator::ReturnCallRef { .. } | wasmparser::Operator::BrOnNull { .. } | wasmparser::Operator::BrOnNonNull { .. } )
}
fn is_bulk_memory_operator(&self, operator: &wasmparser::Operator) -> bool {
matches!(
operator,
wasmparser::Operator::MemoryInit { .. }
| wasmparser::Operator::MemoryFill { .. }
| wasmparser::Operator::MemoryCopy { .. }
| wasmparser::Operator::TableInit { .. }
| wasmparser::Operator::TableCopy { .. }
| wasmparser::Operator::DataDrop { .. }
| wasmparser::Operator::ElemDrop { .. }
)
}
}