From 5a84fa01f8d751b37bd0015278684a5f996ccfcb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 8 Jun 2017 15:40:08 +0300 Subject: [PATCH] validate code after unreachable --- spec/src/fixtures.rs | 5 + spec/src/run.rs | 27 +- src/interpreter/runner.rs | 2 +- src/interpreter/stack.rs | 6 + src/interpreter/tests/wabt.rs | 2 +- src/interpreter/validator.rs | 544 +++++++++++++++++++++------------- 6 files changed, 355 insertions(+), 231 deletions(-) diff --git a/spec/src/fixtures.rs b/spec/src/fixtures.rs index c7539c3..b6f48e1 100644 --- a/spec/src/fixtures.rs +++ b/spec/src/fixtures.rs @@ -26,5 +26,10 @@ run_test!("i32", wasm_i32); run_test!("i64", wasm_i64); run_test!("tee_local", wasm_tee_local); run_test!("traps", wasm_traps); +// compilation: run_test!("typecheck", wasm_typecheck); run_test!("unreachable", wasm_unreachable); +run_test!("unreached-invalid", wasm_unreached_invalid); run_test!("unwind", wasm_unwind); +run_test!("utf8-custom-selection-id", wasm_utf8_custom_selection_id); +run_test!("utf8-import-field", wasm_utf8_import_field); +run_test!("utf8-import-module", wasm_utf8_import_module); diff --git a/spec/src/run.rs b/spec/src/run.rs index 863d5a0..975bf23 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -140,25 +140,18 @@ pub fn spec(name: &str) { .expect(&format!("Failed to load json file {}", &fixture.json)); let spec: test::Spec = serde_json::from_reader(&mut f).expect("Failed to deserialize JSON file"); - let first_command = &spec.commands[0]; - let (mut _program, mut module) = match first_command { - &test::Command::Module { ref filename, .. } => { - setup_program(&outdir, filename) - }, - _ => { - panic!("First command supposed to specify module"); - } - }; - - for command in spec.commands.iter().skip(1) { + let mut _program = None; + let mut module = None; + for command in &spec.commands { println!("command {:?}", command); match command { &test::Command::Module { ref filename, .. } => { - let (_new_program, new_module) = setup_program(&outdir, &filename); - module = new_module; + let (new_program, new_module) = setup_program(&outdir, &filename); + _program = Some(new_program); + module = Some(new_module); }, &test::Command::AssertReturn { line, ref action, ref expected } => { - let result = run_action(&*module, action); + let result = run_action(&*module.as_ref().unwrap(), action); match result { Ok(result) => { let spec_expected = runtime_values(expected); @@ -186,7 +179,7 @@ pub fn spec(name: &str) { } }, &test::Command::AssertReturnCanonicalNan { line, ref action } | &test::Command::AssertReturnArithmeticNan { line, ref action } => { - let result = run_action(&*module, action); + let result = run_action(&*module.as_ref().unwrap(), action); match result { Ok(result) => { for actual_result in result.into_iter().collect::>() { @@ -204,7 +197,7 @@ pub fn spec(name: &str) { } }, &test::Command::AssertTrap { line, ref action, .. } => { - let result = run_action(&*module, action); + let result = run_action(&*module.as_ref().unwrap(), action); match result { Ok(result) => { panic!("Expected action to result in a trap, got result: {:?}", result); @@ -228,7 +221,7 @@ pub fn spec(name: &str) { } }, &test::Command::Action { line, ref action } => { - match run_action(&*module, action) { + match run_action(&*module.as_ref().unwrap(), action) { Ok(_) => { }, Err(e) => { panic!("Failed to invoke action at line {}: {:?}", line, e) diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 2a2b1de..b49bab9 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -432,7 +432,7 @@ impl Interpreter { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_store(context: &mut FunctionContext, _align: u32, offset: u32, ) -> Result + fn run_store(context: &mut FunctionContext, _align: u32, offset: u32) -> Result where RuntimeValue: TryInto, T: LittleEndianConvert { let stack_value = context .value_stack_mut() diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs index b075cae..d21267e 100644 --- a/src/interpreter/stack.rs +++ b/src/interpreter/stack.rs @@ -44,6 +44,12 @@ impl StackWithLimit where T: Clone { .ok_or(Error::Stack("non-empty stack expected".into())) } + pub fn top_mut(&mut self) -> Result<&mut T, Error> { + self.values + .back_mut() + .ok_or(Error::Stack("non-empty stack expected".into())) + } + pub fn get(&self, index: usize) -> Result<&T, Error> { if index >= self.values.len() { return Err(Error::Stack(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs index efe1258..28fcb93 100644 --- a/src/interpreter/tests/wabt.rs +++ b/src/interpreter/tests/wabt.rs @@ -598,7 +598,7 @@ fn call_2() { Opcode::GetLocal(0), Opcode::I32Const(0), Opcode::I32GtS, - Opcode::If(BlockType::NoResult, + Opcode::If(BlockType::Value(ValueType::I32), Opcodes::new(vec![ Opcode::GetLocal(0), Opcode::GetLocal(0), diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs index 4a0b96f..19745fc 100644 --- a/src/interpreter/validator.rs +++ b/src/interpreter/validator.rs @@ -15,13 +15,33 @@ pub struct FunctionValidationContext<'a> { /// Local variables. locals: &'a [ValueType], /// Value stack. - value_stack: StackWithLimit, + value_stack: StackWithLimit, /// Frame stack. - frame_stack: StackWithLimit<(BlockType, usize)>, + frame_stack: StackWithLimit, /// Function return type. None if validating expression. return_type: Option, } +/// Value type on the stack. +#[derive(Debug, Clone, Copy)] +pub enum StackValueType { + /// Any value type. + Any, + /// Any number of any values of any type. + AnyUnlimited, + /// Concrete value type. + Specific(ValueType), +} + +/// Function validation frame. +#[derive(Debug, Clone)] +struct ValidationFrame { + /// Return type. + pub block_type: BlockType, + /// Value stack len. + pub value_stack_len: usize, +} + /// Function validator. pub struct Validator; @@ -44,17 +64,10 @@ impl Validator { for opcode in body { match Validator::validate_instruction(context, opcode)? { InstructionOutcome::RunNextInstruction => (), - InstructionOutcome::Unreachable => return Ok(InstructionOutcome::Unreachable), + InstructionOutcome::Unreachable => context.unreachable()?, } } - let return_type = context.pop_label()?; - - match block_type { - BlockType::NoResult => Ok(InstructionOutcome::RunNextInstruction), - BlockType::Value(value_type) if Some(value_type) != return_type => - Err(Error::Validation(format!("Expected block to return {:?} while it has returned {:?}", value_type, return_type))), - BlockType::Value(value_type) => context.push_value(value_type).map(|_| InstructionOutcome::RunNextInstruction), - } + context.pop_label() } pub fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result { @@ -83,206 +96,206 @@ impl Validator { &Opcode::GetGlobal(index) => Validator::validate_get_global(context, index), &Opcode::SetGlobal(index) => Validator::validate_set_global(context, index), - &Opcode::I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32), - &Opcode::I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64), - &Opcode::F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32), - &Opcode::F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64), - &Opcode::I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), - &Opcode::I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), - &Opcode::I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), - &Opcode::I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), - &Opcode::I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), - &Opcode::I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), - &Opcode::I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), - &Opcode::I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), - &Opcode::I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), - &Opcode::I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), + &Opcode::I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32.into()), + &Opcode::I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64.into()), + &Opcode::F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32.into()), + &Opcode::F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64.into()), + &Opcode::I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), + &Opcode::I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), + &Opcode::I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), + &Opcode::I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), + &Opcode::I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), + &Opcode::I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), + &Opcode::I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), + &Opcode::I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), + &Opcode::I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), + &Opcode::I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), - &Opcode::I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32), - &Opcode::I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64), - &Opcode::F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32), - &Opcode::F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64), - &Opcode::I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32), - &Opcode::I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32), - &Opcode::I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64), - &Opcode::I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64), - &Opcode::I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64), + &Opcode::I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32.into()), + &Opcode::I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64.into()), + &Opcode::F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32.into()), + &Opcode::F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64.into()), + &Opcode::I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32.into()), + &Opcode::I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32.into()), + &Opcode::I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64.into()), + &Opcode::I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64.into()), + &Opcode::I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64.into()), &Opcode::CurrentMemory(_) => Validator::validate_current_memory(context), &Opcode::GrowMemory(_) => Validator::validate_grow_memory(context), - &Opcode::I32Const(_) => Validator::validate_const(context, ValueType::I32), - &Opcode::I64Const(_) => Validator::validate_const(context, ValueType::I64), - &Opcode::F32Const(_) => Validator::validate_const(context, ValueType::F32), - &Opcode::F64Const(_) => Validator::validate_const(context, ValueType::F64), + &Opcode::I32Const(_) => Validator::validate_const(context, ValueType::I32.into()), + &Opcode::I64Const(_) => Validator::validate_const(context, ValueType::I64.into()), + &Opcode::F32Const(_) => Validator::validate_const(context, ValueType::F32.into()), + &Opcode::F64Const(_) => Validator::validate_const(context, ValueType::F64.into()), - &Opcode::I32Eqz => Validator::validate_testop(context, ValueType::I32), - &Opcode::I32Eq => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32Ne => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32LtS => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32LtU => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32GtS => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32GtU => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32LeS => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32LeU => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32GeS => Validator::validate_relop(context, ValueType::I32), - &Opcode::I32GeU => Validator::validate_relop(context, ValueType::I32), + &Opcode::I32Eqz => Validator::validate_testop(context, ValueType::I32.into()), + &Opcode::I32Eq => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32Ne => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32LtS => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32LtU => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32GtS => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32GtU => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32LeS => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32LeU => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32GeS => Validator::validate_relop(context, ValueType::I32.into()), + &Opcode::I32GeU => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I64Eqz => Validator::validate_testop(context, ValueType::I64), - &Opcode::I64Eq => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64Ne => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64LtS => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64LtU => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64GtS => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64GtU => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64LeS => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64LeU => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64GeS => Validator::validate_relop(context, ValueType::I64), - &Opcode::I64GeU => Validator::validate_relop(context, ValueType::I64), + &Opcode::I64Eqz => Validator::validate_testop(context, ValueType::I64.into()), + &Opcode::I64Eq => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64Ne => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64LtS => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64LtU => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64GtS => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64GtU => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64LeS => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64LeU => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64GeS => Validator::validate_relop(context, ValueType::I64.into()), + &Opcode::I64GeU => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::F32Eq => Validator::validate_relop(context, ValueType::F32), - &Opcode::F32Ne => Validator::validate_relop(context, ValueType::F32), - &Opcode::F32Lt => Validator::validate_relop(context, ValueType::F32), - &Opcode::F32Gt => Validator::validate_relop(context, ValueType::F32), - &Opcode::F32Le => Validator::validate_relop(context, ValueType::F32), - &Opcode::F32Ge => Validator::validate_relop(context, ValueType::F32), + &Opcode::F32Eq => Validator::validate_relop(context, ValueType::F32.into()), + &Opcode::F32Ne => Validator::validate_relop(context, ValueType::F32.into()), + &Opcode::F32Lt => Validator::validate_relop(context, ValueType::F32.into()), + &Opcode::F32Gt => Validator::validate_relop(context, ValueType::F32.into()), + &Opcode::F32Le => Validator::validate_relop(context, ValueType::F32.into()), + &Opcode::F32Ge => Validator::validate_relop(context, ValueType::F32.into()), - &Opcode::F64Eq => Validator::validate_relop(context, ValueType::F64), - &Opcode::F64Ne => Validator::validate_relop(context, ValueType::F64), - &Opcode::F64Lt => Validator::validate_relop(context, ValueType::F64), - &Opcode::F64Gt => Validator::validate_relop(context, ValueType::F64), - &Opcode::F64Le => Validator::validate_relop(context, ValueType::F64), - &Opcode::F64Ge => Validator::validate_relop(context, ValueType::F64), + &Opcode::F64Eq => Validator::validate_relop(context, ValueType::F64.into()), + &Opcode::F64Ne => Validator::validate_relop(context, ValueType::F64.into()), + &Opcode::F64Lt => Validator::validate_relop(context, ValueType::F64.into()), + &Opcode::F64Gt => Validator::validate_relop(context, ValueType::F64.into()), + &Opcode::F64Le => Validator::validate_relop(context, ValueType::F64.into()), + &Opcode::F64Ge => Validator::validate_relop(context, ValueType::F64.into()), - &Opcode::I32Clz => Validator::validate_unop(context, ValueType::I32), - &Opcode::I32Ctz => Validator::validate_unop(context, ValueType::I32), - &Opcode::I32Popcnt => Validator::validate_unop(context, ValueType::I32), - &Opcode::I32Add => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Sub => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Mul => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32DivS => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32DivU => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32RemS => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32RemU => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32And => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Or => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Xor => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Shl => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32ShrS => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32ShrU => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Rotl => Validator::validate_binop(context, ValueType::I32), - &Opcode::I32Rotr => Validator::validate_binop(context, ValueType::I32), + &Opcode::I32Clz => Validator::validate_unop(context, ValueType::I32.into()), + &Opcode::I32Ctz => Validator::validate_unop(context, ValueType::I32.into()), + &Opcode::I32Popcnt => Validator::validate_unop(context, ValueType::I32.into()), + &Opcode::I32Add => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Sub => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Mul => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32DivS => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32DivU => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32RemS => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32RemU => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32And => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Or => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Xor => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Shl => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32ShrS => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32ShrU => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Rotl => Validator::validate_binop(context, ValueType::I32.into()), + &Opcode::I32Rotr => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I64Clz => Validator::validate_unop(context, ValueType::I64), - &Opcode::I64Ctz => Validator::validate_unop(context, ValueType::I64), - &Opcode::I64Popcnt => Validator::validate_unop(context, ValueType::I64), - &Opcode::I64Add => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Sub => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Mul => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64DivS => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64DivU => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64RemS => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64RemU => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64And => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Or => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Xor => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Shl => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64ShrS => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64ShrU => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Rotl => Validator::validate_binop(context, ValueType::I64), - &Opcode::I64Rotr => Validator::validate_binop(context, ValueType::I64), + &Opcode::I64Clz => Validator::validate_unop(context, ValueType::I64.into()), + &Opcode::I64Ctz => Validator::validate_unop(context, ValueType::I64.into()), + &Opcode::I64Popcnt => Validator::validate_unop(context, ValueType::I64.into()), + &Opcode::I64Add => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Sub => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Mul => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64DivS => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64DivU => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64RemS => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64RemU => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64And => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Or => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Xor => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Shl => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64ShrS => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64ShrU => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Rotl => Validator::validate_binop(context, ValueType::I64.into()), + &Opcode::I64Rotr => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::F32Abs => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Neg => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Ceil => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Floor => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Trunc => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Nearest => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Sqrt => Validator::validate_unop(context, ValueType::F32), - &Opcode::F32Add => Validator::validate_binop(context, ValueType::F32), - &Opcode::F32Sub => Validator::validate_binop(context, ValueType::F32), - &Opcode::F32Mul => Validator::validate_binop(context, ValueType::F32), - &Opcode::F32Div => Validator::validate_binop(context, ValueType::F32), - &Opcode::F32Min => Validator::validate_binop(context, ValueType::F32), - &Opcode::F32Max => Validator::validate_binop(context, ValueType::F32), - &Opcode::F32Copysign => Validator::validate_binop(context, ValueType::F32), + &Opcode::F32Abs => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Neg => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Ceil => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Floor => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Trunc => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Nearest => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Sqrt => Validator::validate_unop(context, ValueType::F32.into()), + &Opcode::F32Add => Validator::validate_binop(context, ValueType::F32.into()), + &Opcode::F32Sub => Validator::validate_binop(context, ValueType::F32.into()), + &Opcode::F32Mul => Validator::validate_binop(context, ValueType::F32.into()), + &Opcode::F32Div => Validator::validate_binop(context, ValueType::F32.into()), + &Opcode::F32Min => Validator::validate_binop(context, ValueType::F32.into()), + &Opcode::F32Max => Validator::validate_binop(context, ValueType::F32.into()), + &Opcode::F32Copysign => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F64Abs => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Neg => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Ceil => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Floor => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Trunc => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Nearest => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Sqrt => Validator::validate_unop(context, ValueType::F64), - &Opcode::F64Add => Validator::validate_binop(context, ValueType::F64), - &Opcode::F64Sub => Validator::validate_binop(context, ValueType::F64), - &Opcode::F64Mul => Validator::validate_binop(context, ValueType::F64), - &Opcode::F64Div => Validator::validate_binop(context, ValueType::F64), - &Opcode::F64Min => Validator::validate_binop(context, ValueType::F64), - &Opcode::F64Max => Validator::validate_binop(context, ValueType::F64), - &Opcode::F64Copysign => Validator::validate_binop(context, ValueType::F64), + &Opcode::F64Abs => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Neg => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Ceil => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Floor => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Trunc => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Nearest => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Sqrt => Validator::validate_unop(context, ValueType::F64.into()), + &Opcode::F64Add => Validator::validate_binop(context, ValueType::F64.into()), + &Opcode::F64Sub => Validator::validate_binop(context, ValueType::F64.into()), + &Opcode::F64Mul => Validator::validate_binop(context, ValueType::F64.into()), + &Opcode::F64Div => Validator::validate_binop(context, ValueType::F64.into()), + &Opcode::F64Min => Validator::validate_binop(context, ValueType::F64.into()), + &Opcode::F64Max => Validator::validate_binop(context, ValueType::F64.into()), + &Opcode::F64Copysign => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::I32WarpI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::I32), - &Opcode::I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - &Opcode::I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - &Opcode::I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), - &Opcode::I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), - &Opcode::I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), - &Opcode::I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), - &Opcode::I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), - &Opcode::I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), - &Opcode::I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - &Opcode::I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - &Opcode::F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - &Opcode::F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - &Opcode::F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), - &Opcode::F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), - &Opcode::F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::F32), - &Opcode::F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), - &Opcode::F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), - &Opcode::F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - &Opcode::F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - &Opcode::F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::F64), + &Opcode::I32WarpI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::I32.into()), + &Opcode::I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), + &Opcode::I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), + &Opcode::I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), + &Opcode::I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), + &Opcode::I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), + &Opcode::I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), + &Opcode::I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), + &Opcode::I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), + &Opcode::I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), + &Opcode::I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), + &Opcode::F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), + &Opcode::F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), + &Opcode::F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), + &Opcode::F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), + &Opcode::F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::F32.into()), + &Opcode::F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), + &Opcode::F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), + &Opcode::F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), + &Opcode::F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), + &Opcode::F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::F64.into()), - &Opcode::I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - &Opcode::I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - &Opcode::F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - &Opcode::F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), + &Opcode::I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), + &Opcode::I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), + &Opcode::F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), + &Opcode::F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), } } - fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + fn validate_const(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { context.push_value(value_type)?; Ok(InstructionOutcome::RunNextInstruction) } - fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + fn validate_unop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { context.pop_value(value_type)?; context.push_value(value_type)?; Ok(InstructionOutcome::RunNextInstruction) } - fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + fn validate_binop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { context.pop_value(value_type)?; context.pop_value(value_type)?; context.push_value(value_type)?; Ok(InstructionOutcome::RunNextInstruction) } - fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + fn validate_testop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { context.pop_value(value_type)?; - context.push_value(ValueType::I32)?; + context.push_value(ValueType::I32.into())?; Ok(InstructionOutcome::RunNextInstruction) } - fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + fn validate_relop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { context.pop_value(value_type)?; context.pop_value(value_type)?; - context.push_value(ValueType::I32)?; + context.push_value(ValueType::I32.into())?; Ok(InstructionOutcome::RunNextInstruction) } - fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result { + fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result { context.pop_value(value_type1)?; context.push_value(value_type2)?; Ok(InstructionOutcome::RunNextInstruction) @@ -294,7 +307,7 @@ impl Validator { } fn validate_select(context: &mut FunctionValidationContext) -> Result { - context.pop_value(ValueType::I32)?; + context.pop_value(ValueType::I32.into())?; let select_type = context.pop_any_value()?; context.pop_value(select_type)?; context.push_value(select_type)?; @@ -340,23 +353,25 @@ impl Validator { Ok(InstructionOutcome::RunNextInstruction) } - fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result { + fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result { if align > max_align { return Err(Error::Validation(format!("Too large memory alignment {} (expected at most {})", align, max_align))); } + context.pop_value(ValueType::I32.into())?; context.require_memory(DEFAULT_MEMORY_INDEX)?; context.push_value(value_type)?; Ok(InstructionOutcome::RunNextInstruction) } - fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result { + fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result { if align > max_align { return Err(Error::Validation(format!("Too large memory alignment {} (expected at most {})", align, max_align))); } context.require_memory(DEFAULT_MEMORY_INDEX)?; context.pop_value(value_type)?; + context.pop_value(ValueType::I32.into())?; Ok(InstructionOutcome::RunNextInstruction) } @@ -365,6 +380,8 @@ impl Validator { } fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result { + context.pop_value(ValueType::I32.into())?; + let body_len = body.len(); let separator_index = body.iter() .position(|op| *op == Opcode::Else) @@ -378,13 +395,17 @@ impl Validator { } fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result { - context.require_label(idx)?; + if let BlockType::Value(value_type) = context.require_label(idx)? { + context.tee_value(value_type.into())?; + } Ok(InstructionOutcome::Unreachable) } fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result { - context.pop_value(ValueType::I32)?; - context.require_label(idx)?; + context.pop_value(ValueType::I32.into())?; + if let BlockType::Value(value_type) = context.require_label(idx)? { + context.tee_value(value_type.into())?; + } Ok(InstructionOutcome::RunNextInstruction) } @@ -397,12 +418,17 @@ impl Validator { } } + context.pop_value(ValueType::I32.into())?; + if let BlockType::Value(value_type) = default_block_type { + context.tee_value(value_type.into())?; + } + Ok(InstructionOutcome::Unreachable) } fn validate_return(context: &mut FunctionValidationContext) -> Result { if let BlockType::Value(value_type) = context.return_type()? { - context.tee_value(value_type)?; + context.tee_value(value_type.into())?; } Ok(InstructionOutcome::Unreachable) } @@ -410,10 +436,10 @@ impl Validator { fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result { let (argument_types, return_type) = context.require_function(idx)?; for argument_type in argument_types.iter().rev() { - context.pop_value(*argument_type)?; + context.pop_value((*argument_type).into())?; } if let BlockType::Value(value_type) = return_type { - context.push_value(value_type)?; + context.push_value(value_type.into())?; } Ok(InstructionOutcome::RunNextInstruction) } @@ -422,24 +448,24 @@ impl Validator { context.require_table(DEFAULT_TABLE_INDEX, VariableType::AnyFunc)?; let (argument_types, return_type) = context.require_function_type(idx)?; for argument_type in argument_types.iter().rev() { - context.pop_value(*argument_type)?; + context.pop_value((*argument_type).into())?; } if let BlockType::Value(value_type) = return_type { - context.push_value(value_type)?; + context.push_value(value_type.into())?; } Ok(InstructionOutcome::RunNextInstruction) } fn validate_current_memory(context: &mut FunctionValidationContext) -> Result { context.require_memory(DEFAULT_MEMORY_INDEX)?; - context.push_value(ValueType::I32)?; + context.push_value(ValueType::I32.into())?; Ok(InstructionOutcome::RunNextInstruction) } fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result { context.require_memory(DEFAULT_MEMORY_INDEX)?; - context.pop_value(ValueType::I32)?; - context.push_value(ValueType::I32)?; + context.pop_value(ValueType::I32.into())?; + context.push_value(ValueType::I32.into())?; Ok(InstructionOutcome::RunNextInstruction) } } @@ -456,67 +482,97 @@ impl<'a> FunctionValidationContext<'a> { } } - pub fn push_value(&mut self, value_type: ValueType) -> Result<(), Error> { - self.value_stack.push(value_type) + pub fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> { + self.value_stack.push(value_type.into()) } - pub fn pop_value(&mut self, value_type: ValueType) -> Result<(), Error> { - let stack_value_type = self.value_stack.pop()?; - if stack_value_type == value_type { - return Ok(()); + pub fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> { + self.check_stack_access()?; + match self.value_stack.pop()? { + StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), + StackValueType::Any => Ok(()), + StackValueType::AnyUnlimited => { + self.value_stack.push(StackValueType::AnyUnlimited)?; + Ok(()) + }, + stack_value_type @ _ => Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), } - - Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))) } - pub fn tee_value(&mut self, value_type: ValueType) -> Result<(), Error> { - let stack_value_type = self.value_stack.top()?; - if *stack_value_type == value_type { - return Ok(()); + pub fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> { + self.check_stack_access()?; + match *self.value_stack.top()? { + StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), + StackValueType::Any | StackValueType::AnyUnlimited => Ok(()), + stack_value_type @ _ => Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), } - - Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))) } - pub fn pop_any_value(&mut self) -> Result { - self.value_stack.pop() + pub fn pop_any_value(&mut self) -> Result { + self.check_stack_access()?; + match self.value_stack.pop()? { + StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)), + StackValueType::Any => Ok(StackValueType::Any), + StackValueType::AnyUnlimited => { + self.value_stack.push(StackValueType::AnyUnlimited)?; + Ok(StackValueType::Any) + }, + } } - pub fn tee_any_value(&mut self) -> Result { + pub fn tee_any_value(&mut self) -> Result { + self.check_stack_access()?; self.value_stack.top().map(Clone::clone) } - pub fn push_label(&mut self, block_type: BlockType) -> Result<(), Error> { - self.frame_stack.push((block_type, self.value_stack.len())) + pub fn unreachable(&mut self) -> Result<(), Error> { + self.value_stack.push(StackValueType::AnyUnlimited) } - pub fn pop_label(&mut self) -> Result, Error> { - let (_, value_stack_len) = self.frame_stack.pop()?; - let return_type = if self.value_stack.len() > value_stack_len { + pub fn push_label(&mut self, block_type: BlockType) -> Result<(), Error> { + self.frame_stack.push(ValidationFrame { + block_type: block_type, + value_stack_len: self.value_stack.len() + }) + } + + pub fn pop_label(&mut self) -> Result { + let frame = self.frame_stack.pop()?; + let actual_value_type = if self.value_stack.len() > frame.value_stack_len { Some(self.value_stack.pop()?) } else { None }; + self.value_stack.resize(frame.value_stack_len, StackValueType::Any); - self.value_stack.resize(value_stack_len, ValueType::I32); - Ok(return_type) + match frame.block_type { + BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (), + BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) =>(), + _ => return Err(Error::Validation(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))), + } + if let BlockType::Value(value_type) = frame.block_type { + self.push_value(value_type.into())?; + } + + Ok(InstructionOutcome::RunNextInstruction) } pub fn require_label(&self, idx: u32) -> Result { - self.frame_stack.get(idx as usize).map(|&(block_type, _)| block_type) + self.frame_stack.get(idx as usize).map(|ref frame| frame.block_type) } pub fn return_type(&self) -> Result { self.return_type.ok_or(Error::Validation("Trying to return from expression".into())) } - pub fn require_local(&self, idx: u32) -> Result { + pub fn require_local(&self, idx: u32) -> Result { self.locals.get(idx as usize) .cloned() + .map(Into::into) .ok_or(Error::Validation(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) } - pub fn require_global(&self, idx: u32, mutability: Option) -> Result { + pub fn require_global(&self, idx: u32, mutability: Option) -> Result { match self.imports.parse_global_index(ItemIndex::IndexSpace(idx)) { ItemIndex::IndexSpace(_) => unreachable!("parse_global_index is intended to resolve this"), ItemIndex::Internal(internal_idx) => self.module @@ -527,7 +583,7 @@ impl<'a> FunctionValidationContext<'a> { Some(false) if g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be immutable", internal_idx))), _ => Ok(g), }) - .map(|g| g.global_type().content_type()), + .map(|g| g.global_type().content_type().into()), ItemIndex::External(external_idx) => self.module .import_section().ok_or(Error::Validation(format!("Trying to access external global {} in module without import section", external_idx))) .and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external global with index {} in module with {}-entries import section", external_idx, s.entries().len())))) @@ -536,7 +592,7 @@ impl<'a> FunctionValidationContext<'a> { match mutability { Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be mutable", external_idx))), Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be immutable", external_idx))), - _ => Ok(g.content_type()), + _ => Ok(g.content_type().into()), } }, _ => Err(Error::Validation(format!("Import entry {} expected to import global", external_idx))) @@ -604,4 +660,68 @@ impl<'a> FunctionValidationContext<'a> { _ => Err(Error::Validation(format!("Trying to access internal function {} with wrong type", idx))), }) } + + fn check_stack_access(&self) -> Result<(), Error> { + let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len; + if self.value_stack.len() > value_stack_min { + Ok(()) + } else { + Err(Error::Validation("Trying to access parent frame stack values.".into())) + } + } +} + +impl StackValueType { + pub fn is_any(&self) -> bool { + match self { + &StackValueType::Any => true, + _ => false, + } + } + + pub fn is_any_unlimited(&self) -> bool { + match self { + &StackValueType::AnyUnlimited => true, + _ => false, + } + } + + pub fn value_type(&self) -> ValueType { + match self { + &StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"), + &StackValueType::Specific(value_type) => value_type, + } + } +} + +impl From for StackValueType { + fn from(value_type: ValueType) -> Self { + StackValueType::Specific(value_type) + } +} + +impl PartialEq for StackValueType { + fn eq(&self, other: &StackValueType) -> bool { + if self.is_any() || other.is_any() || self.is_any_unlimited() || other.is_any_unlimited() { + true + } else { + self.value_type() == other.value_type() + } + } +} + +impl PartialEq for StackValueType { + fn eq(&self, other: &ValueType) -> bool { + if self.is_any() || self.is_any_unlimited() { + true + } else { + self.value_type() == *other + } + } +} + +impl PartialEq for ValueType { + fn eq(&self, other: &StackValueType) -> bool { + other == self + } }