Skip to content

Commit

Permalink
Rely on wasmparser for validation
Browse files Browse the repository at this point in the history
This commit removes the type-level validation performed within each
function that walrus previously did. Long-ago in the AST-based IR this
sort of made more sense but nowadays walrus does very little other than
simply validate the input. Recent refactorings in wasmparser are
targeted at reuse of the internal validator, so this commit does exactly
that, reuses wasmparser's validator and removes the type-level
validation in walrus.
  • Loading branch information
alexcrichton committed Jul 14, 2020
1 parent 56f78ff commit fd94d9f
Show file tree
Hide file tree
Showing 3 changed files with 562 additions and 975 deletions.
138 changes: 3 additions & 135 deletions src/module/functions/local_function/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ pub(crate) struct ControlFrame {
/// The result type of the block (used to check its result).
pub end_types: Box<[ValType]>,

/// The height of the operand stack at the start of the block (used to check
/// that operands do not underflow the current block).
pub height: usize,

/// If `true`, then this frame is unreachable. This is used to handle
/// stack-polymorphic typing after unconditional branches.
pub unreachable: bool,
Expand All @@ -32,25 +28,6 @@ pub(crate) struct ControlFrame {
pub kind: BlockKind,
}

impl ControlFrame {
/// Get the expected types on the stack for branches to this block.
pub fn label_types(&self) -> &[ValType] {
if let BlockKind::Loop = self.kind {
&self.start_types
} else {
&self.end_types
}
}
}

/// The operand stack.
///
/// `None` is used for `Unknown` stack-polymophic values.
///
/// We also keep track of the instruction that created the value at each stack
/// slot.
pub(crate) type OperandStack = Vec<Option<ValType>>;

/// The control frame stack.
pub(crate) type ControlStack = Vec<ControlFrame>;

Expand All @@ -68,9 +45,6 @@ pub(crate) struct ValidationContext<'a> {
/// The function being validated/constructed.
pub func: &'a mut LocalFunction,

/// The operands stack.
pub operands: &'a mut OperandStack,

/// The control frames stack.
pub controls: &'a mut ControlStack,

Expand All @@ -91,40 +65,18 @@ impl<'a> ValidationContext<'a> {
indices: &'a IndicesToIds,
func_id: FunctionId,
func: &'a mut LocalFunction,
operands: &'a mut OperandStack,
controls: &'a mut ControlStack,
) -> ValidationContext<'a> {
ValidationContext {
module,
indices,
func_id,
func,
operands,
controls,
if_else: Vec::new(),
}
}

pub fn push_operand(&mut self, op: Option<ValType>) {
impl_push_operand(&mut self.operands, op);
}

pub fn pop_operand(&mut self) -> Result<Option<ValType>> {
impl_pop_operand(&mut self.operands, &mut self.controls)
}

pub fn pop_operand_expected(&mut self, expected: Option<ValType>) -> Result<Option<ValType>> {
impl_pop_operand_expected(&mut self.operands, &mut self.controls, expected)
}

pub fn push_operands(&mut self, types: &[ValType]) {
impl_push_operands(&mut self.operands, types);
}

pub fn pop_operands(&mut self, expected: &[ValType]) -> Result<()> {
impl_pop_operands(&mut self.operands, &self.controls, expected)
}

pub fn push_control(
&mut self,
kind: BlockKind,
Expand All @@ -136,7 +88,6 @@ impl<'a> ValidationContext<'a> {
kind,
self.func,
self.controls,
self.operands,
start_types,
end_types,
)
Expand All @@ -151,24 +102,21 @@ impl<'a> ValidationContext<'a> {
kind,
self.func,
self.controls,
self.operands,
ty.into(),
start_types,
end_types,
)
}

pub fn pop_control(&mut self) -> Result<(ControlFrame, InstrSeqId)> {
let frame = impl_pop_control(&mut self.controls, &mut self.operands)?;
let frame = impl_pop_control(&mut self.controls)?;
let block = frame.block;
Ok((frame, block))
}

pub fn unreachable(&mut self) {
let frame = self.controls.last_mut().unwrap();
frame.unreachable = true;
let height = frame.height;
self.operands.truncate(height);
}

pub fn control(&self, n: usize) -> Result<&ControlFrame> {
Expand Down Expand Up @@ -208,73 +156,11 @@ impl<'a> ValidationContext<'a> {
}
}

fn impl_push_operand(operands: &mut OperandStack, op: Option<ValType>) {
log::trace!("push operand: {:?}", op);
operands.push(op);
}

fn impl_pop_operand(
operands: &mut OperandStack,
controls: &ControlStack,
) -> Result<Option<ValType>> {
if let Some(f) = controls.last() {
if operands.len() == f.height {
if f.unreachable {
log::trace!("pop operand: None");
return Ok(None);
}
return Err(ErrorKind::InvalidWasm)
.context("popped operand past control frame height in non-unreachable code");
}
}
let op = operands.pop().unwrap();
log::trace!("pop operand: {:?}", op);
Ok(op)
}

fn impl_pop_operand_expected(
operands: &mut OperandStack,
controls: &ControlStack,
expected: Option<ValType>,
) -> Result<Option<ValType>> {
match (impl_pop_operand(operands, controls)?, expected) {
(None, expected) => Ok(expected),
(actual, None) => Ok(actual),
(Some(actual), Some(expected)) => {
if actual != expected {
Err(ErrorKind::InvalidWasm)
.context(format!("expected type {}", expected))
.context(format!("found type {}", actual))
} else {
Ok(Some(expected))
}
}
}
}

fn impl_push_operands(operands: &mut OperandStack, types: &[ValType]) {
for ty in types {
impl_push_operand(operands, Some(*ty));
}
}

fn impl_pop_operands(
operands: &mut OperandStack,
controls: &ControlStack,
expected: &[ValType],
) -> Result<()> {
for ty in expected.iter().cloned().rev() {
impl_pop_operand_expected(operands, controls, Some(ty))?;
}
Ok(())
}

fn impl_push_control(
types: &ModuleTypes,
kind: BlockKind,
func: &mut LocalFunction,
controls: &mut ControlStack,
operands: &mut OperandStack,
start_types: Box<[ValType]>,
end_types: Box<[ValType]>,
) -> Result<InstrSeqId> {
Expand All @@ -291,7 +177,6 @@ fn impl_push_control(
kind,
func,
controls,
operands,
ty,
start_types,
end_types,
Expand All @@ -303,7 +188,6 @@ fn impl_push_control_with_ty(
kind: BlockKind,
func: &mut LocalFunction,
controls: &mut ControlStack,
operands: &mut OperandStack,
ty: InstrSeqType,
start_types: Box<[ValType]>,
end_types: Box<[ValType]>,
Expand All @@ -313,15 +197,11 @@ fn impl_push_control_with_ty(
debug_assert_eq!(types.results(ty), &end_types[..]);
}

let height = operands.len();
impl_push_operands(operands, &start_types);

let block = func.add_block(|id| InstrSeq::new(id, ty));

controls.push(ControlFrame {
start_types,
end_types,
height,
unreachable: false,
block,
kind,
Expand All @@ -330,23 +210,11 @@ fn impl_push_control_with_ty(
block
}

fn impl_pop_control(
controls: &mut ControlStack,
operands: &mut OperandStack,
) -> Result<ControlFrame> {
let frame = controls
fn impl_pop_control(controls: &mut ControlStack) -> Result<ControlFrame> {
controls
.last()
.ok_or_else(|| ErrorKind::InvalidWasm)
.context("attempted to pop a frame from an empty control stack")?;
impl_pop_operands(operands, controls, &frame.end_types)?;
if operands.len() != frame.height {
return Err(ErrorKind::InvalidWasm).context(format!(
"incorrect number of operands on the stack at the end of a control frame; \
found {}, expected {}",
operands.len(),
frame.height
));
}
let frame = controls.pop().unwrap();
Ok(frame)
}

0 comments on commit fd94d9f

Please sign in to comment.