Skip to content
This repository has been archived by the owner on Jun 16, 2020. It is now read-only.

Update implementation of reference types #213

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ edition = "2018"
anyhow = "1.0"
criterion = "0.3"
getopts = "0.2"
wast = "15.0.0"
wat = "1.0.16"
wast = "17.0.0"
wat = "1.0.18"
rayon = "1.3"

[badges]
Expand Down
15 changes: 9 additions & 6 deletions src/binary_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,8 @@ impl<'a> BinaryReader<'a> {
-0x03 => Ok(Type::F32),
-0x04 => Ok(Type::F64),
-0x05 => Ok(Type::V128),
-0x10 => Ok(Type::AnyFunc),
-0x11 => Ok(Type::AnyRef),
-0x12 => Ok(Type::NullRef),
-0x10 => Ok(Type::FuncRef),
-0x11 => Ok(Type::ExternRef),
-0x20 => Ok(Type::Func),
-0x40 => Ok(Type::EmptyBlockType),
_ => Err(BinaryReaderError::new(
Expand Down Expand Up @@ -984,7 +983,7 @@ impl<'a> BinaryReader<'a> {
let results = self.read_var_u32()?;
if results != 1 {
return Err(BinaryReaderError::new(
"bad number of results",
"invalid result arity",
self.position,
));
}
Expand Down Expand Up @@ -1230,8 +1229,12 @@ impl<'a> BinaryReader<'a> {
0xc3 => Operator::I64Extend16S,
0xc4 => Operator::I64Extend32S,

0xd0 => Operator::RefNull,
0xd1 => Operator::RefIsNull,
0xd0 => Operator::RefNull {
ty: self.read_type()?,
},
0xd1 => Operator::RefIsNull {
ty: self.read_type()?,
},
0xd2 => Operator::RefFunc {
function_index: self.read_var_u32()?,
},
Expand Down
80 changes: 41 additions & 39 deletions src/operators_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ use crate::{
WasmMemoryType, WasmModuleResources, WasmTableType, WasmType,
};

/// Test if `subtype` is a subtype of `supertype`.
pub(crate) fn is_subtype_supertype(subtype: Type, supertype: Type) -> bool {
match supertype {
Type::AnyRef => {
subtype == Type::AnyRef || subtype == Type::AnyFunc || subtype == Type::NullRef
}
Type::AnyFunc => subtype == Type::AnyFunc || subtype == Type::NullRef,
_ => subtype == supertype,
}
}

#[derive(Debug)]
struct BlockState {
start_types: Vec<Type>,
Expand Down Expand Up @@ -79,10 +68,7 @@ impl FuncState {
return true;
}
assert!(stack_starts_at + index < self.stack_types.len());
is_subtype_supertype(
self.stack_types[self.stack_types.len() - 1 - index],
expected,
)
self.stack_types[self.stack_types.len() - 1 - index] == expected
}
fn assert_block_stack_len(&self, depth: usize, minimal_len: usize) -> bool {
assert!(depth < self.blocks.len());
Expand Down Expand Up @@ -364,7 +350,7 @@ pub(crate) fn check_value_type(
) -> OperatorValidatorResult<()> {
match ty {
Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()),
Type::NullRef | Type::AnyFunc | Type::AnyRef => {
Type::FuncRef | Type::ExternRef => {
if !operator_config.enable_reference_types {
return Err(OperatorValidatorError::new(
"reference types support is not enabled",
Expand Down Expand Up @@ -784,7 +770,7 @@ impl OperatorValidator {
| TypeOrFuncType::Type(Type::I64)
| TypeOrFuncType::Type(Type::F32)
| TypeOrFuncType::Type(Type::F64) => Ok(()),
TypeOrFuncType::Type(Type::AnyRef) | TypeOrFuncType::Type(Type::AnyFunc) => {
TypeOrFuncType::Type(Type::ExternRef) | TypeOrFuncType::Type(Type::FuncRef) => {
self.check_reference_types_enabled()
}
TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(),
Expand Down Expand Up @@ -851,7 +837,7 @@ impl OperatorValidator {
Ok(())
}

fn check_select(&self) -> OperatorValidatorResult<Option<Type>> {
fn check_select(&self, expected_ty: Option<Type>) -> OperatorValidatorResult<Option<Type>> {
self.check_frame_size(3)?;
let func_state = &self.func_state;
let last_block = func_state.last_block();
Expand All @@ -868,21 +854,19 @@ impl OperatorValidator {
func_state.stack_types[func_state.stack_types.len() - 2]
}
_ => {
let ty = func_state.stack_types[func_state.stack_types.len() - 3];
let ty = expected_ty
.unwrap_or(func_state.stack_types[func_state.stack_types.len() - 3]);
self.check_operands_2(ty, Type::I32)?;
ty
}
}
} else {
let ty = func_state.stack_types[func_state.stack_types.len() - 3];
let ty =
expected_ty.unwrap_or(func_state.stack_types[func_state.stack_types.len() - 3]);
self.check_operands_2(ty, Type::I32)?;
ty
};

if !ty.is_valid_for_old_select() {
return Err(OperatorValidatorError::new("invalid type for select"));
}

Ok(Some(ty))
}

Expand Down Expand Up @@ -1032,11 +1016,18 @@ impl OperatorValidator {
self.func_state.change_frame(1)?;
}
Operator::Select => {
let ty = self.check_select()?;
let ty = self.check_select(None)?;
match ty {
Some(Type::I32) | Some(Type::I64) | Some(Type::F32) | Some(Type::F64) => {}
Some(_) => {
bail_op_err!("type mismatch: only integer types allowed with bare `select`")
}
None => {}
}
self.func_state.change_frame_after_select(ty)?;
}
Operator::TypedSelect { ty } => {
self.check_operands_3(Type::I32, ty, ty)?;
self.check_select(Some(ty))?;
self.func_state.change_frame_after_select(Some(ty))?;
}
Operator::LocalGet { local_index } => {
Expand Down Expand Up @@ -1608,13 +1599,29 @@ impl OperatorValidator {
));
}
}
Operator::RefNull => {
Operator::RefNull { ty } => {
self.check_reference_types_enabled()?;
self.func_state.change_frame_with_type(0, Type::NullRef)?;
match ty {
Type::FuncRef | Type::ExternRef => {}
_ => {
return Err(OperatorValidatorError::new(
"invalid reference type in ref.null",
))
}
}
self.func_state.change_frame_with_type(0, ty)?;
}
Operator::RefIsNull => {
Operator::RefIsNull { ty } => {
self.check_reference_types_enabled()?;
self.check_operands_1(Type::AnyRef)?;
match ty {
Type::FuncRef | Type::ExternRef => {}
_ => {
return Err(OperatorValidatorError::new(
"invalid reference type in ref.is_null",
))
}
}
self.check_operands_1(ty)?;
self.func_state.change_frame_with_type(1, Type::I32)?;
}
Operator::RefFunc { function_index } => {
Expand All @@ -1624,7 +1631,7 @@ impl OperatorValidator {
"unknown function: function index out of bounds",
));
}
self.func_state.change_frame_with_type(0, Type::AnyFunc)?;
self.func_state.change_frame_with_type(0, Type::FuncRef)?;
}
Operator::V128Load { memarg } => {
self.check_simd_enabled()?;
Expand Down Expand Up @@ -1966,9 +1973,7 @@ impl OperatorValidator {
Operator::DataDrop { segment } => {
self.check_bulk_memory_enabled()?;
if segment >= resources.data_count() {
return Err(OperatorValidatorError::new(
"unknown data segment: segment index out of bounds",
));
bail_op_err!("unknown data segment {}", segment);
}
}
Operator::MemoryCopy | Operator::MemoryFill => {
Expand All @@ -1993,7 +1998,7 @@ impl OperatorValidator {
segment
),
};
if !is_subtype_supertype(segment_ty, table.element_type().to_parser_type()) {
if segment_ty != table.element_type().to_parser_type() {
return Err(OperatorValidatorError::new("type mismatch"));
}
self.check_operands_3(Type::I32, Type::I32, Type::I32)?;
Expand Down Expand Up @@ -2021,10 +2026,7 @@ impl OperatorValidator {
(Some(a), Some(b)) => (a, b),
_ => return Err(OperatorValidatorError::new("table index out of bounds")),
};
if !is_subtype_supertype(
src.element_type().to_parser_type(),
dst.element_type().to_parser_type(),
) {
if src.element_type().to_parser_type() != dst.element_type().to_parser_type() {
return Err(OperatorValidatorError::new("type mismatch"));
}
self.check_operands_3(Type::I32, Type::I32, Type::I32)?;
Expand Down
18 changes: 4 additions & 14 deletions src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,12 @@ pub enum Type {
F32,
F64,
V128,
AnyFunc,
AnyRef,
NullRef,
FuncRef,
ExternRef,
Func,
EmptyBlockType,
}

impl Type {
pub(crate) fn is_valid_for_old_select(self) -> bool {
match self {
Type::I32 | Type::I64 | Type::F32 | Type::F64 => true,
_ => false,
}
}
}

/// Either a value type or a function type.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TypeOrFuncType {
Expand Down Expand Up @@ -321,8 +311,8 @@ pub enum Operator<'a> {
I64Const { value: i64 },
F32Const { value: Ieee32 },
F64Const { value: Ieee64 },
RefNull,
RefIsNull,
RefNull { ty: Type },
RefIsNull { ty: Type },
RefFunc { function_index: u32 },
I32Eqz,
I32Eq,
Expand Down
8 changes: 4 additions & 4 deletions src/readers/element_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct ElementItems<'a> {

#[derive(Debug)]
pub enum ElementItem {
Null,
Null(Type),
Func(u32),
}

Expand Down Expand Up @@ -91,7 +91,7 @@ impl<'a> ElementItemsReader<'a> {
if self.exprs {
let offset = self.reader.original_position();
let ret = match self.reader.read_operator()? {
Operator::RefNull => ElementItem::Null,
Operator::RefNull { ty } => ElementItem::Null(ty),
Operator::RefFunc { function_index } => ElementItem::Func(function_index),
_ => return Err(BinaryReaderError::new("invalid passive segment", offset)),
};
Expand Down Expand Up @@ -229,7 +229,7 @@ impl<'a> ElementSectionReader<'a> {
self.reader.read_type()?
} else {
match self.reader.read_external_kind()? {
ExternalKind::Function => Type::AnyFunc,
ExternalKind::Function => Type::FuncRef,
_ => {
return Err(BinaryReaderError::new(
"only the function external type is supported in elem segment",
Expand All @@ -239,7 +239,7 @@ impl<'a> ElementSectionReader<'a> {
}
}
} else {
Type::AnyFunc
Type::FuncRef
};
let data_start = self.reader.position;
let items_count = self.reader.read_var_u32()?;
Expand Down
32 changes: 21 additions & 11 deletions src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ use crate::primitives::{
};

use crate::operators_validator::{
check_value_type, is_subtype_supertype, FunctionEnd, OperatorValidator,
OperatorValidatorConfig, OperatorValidatorError, DEFAULT_OPERATOR_VALIDATOR_CONFIG,
check_value_type, FunctionEnd, OperatorValidator, OperatorValidatorConfig,
OperatorValidatorError, DEFAULT_OPERATOR_VALIDATOR_CONFIG,
};
use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder};
use crate::{ElemSectionEntryTable, ElementItem};
Expand Down Expand Up @@ -251,7 +251,7 @@ impl<'a> ValidatingParser<'a> {

fn check_table_type(&self, table_type: &TableType) -> ValidatorResult<'a, ()> {
match table_type.element_type {
Type::AnyFunc => {}
Type::FuncRef => {}
_ => {
if !self.config.operator_config.enable_reference_types {
return self.create_error("element is not anyfunc");
Expand Down Expand Up @@ -332,11 +332,11 @@ impl<'a> ValidatingParser<'a> {
Operator::I64Const { .. } => Type::I64,
Operator::F32Const { .. } => Type::F32,
Operator::F64Const { .. } => Type::F64,
Operator::RefNull => {
Operator::RefNull { ty } => {
if !self.config.operator_config.enable_reference_types {
return self.create_error("reference types support is not enabled");
}
Type::NullRef
ty
}
Operator::V128Const { .. } => {
if !self.config.operator_config.enable_simd {
Expand All @@ -358,14 +358,14 @@ impl<'a> ValidatingParser<'a> {
function_index
));
}
Type::AnyFunc
Type::FuncRef
}
_ => {
return self
.create_error("constant expression required: invalid init_expr operator")
}
};
if !is_subtype_supertype(ty, state.ty) {
if ty != state.ty {
return self.create_error("type mismatch: invalid init_expr type");
}
Ok(())
Expand Down Expand Up @@ -566,7 +566,7 @@ impl<'a> ValidatingParser<'a> {
return;
}
};
if !is_subtype_supertype(ty, table.element_type) {
if ty != table.element_type {
self.set_validation_error("element_type != table type");
return;
}
Expand All @@ -584,9 +584,19 @@ impl<'a> ValidatingParser<'a> {
}
}
}
if !self.config.operator_config.enable_reference_types && ty != Type::AnyFunc {
self.set_validation_error("element_type != anyfunc is not supported yet");
return;
match ty {
Type::FuncRef => {}
Type::ExternRef if self.config.operator_config.enable_reference_types => {}
Type::ExternRef => {
self.set_validation_error(
"reference types must be enabled for anyref elem segment",
);
return;
}
_ => {
self.set_validation_error("invalid reference type");
return;
}
}
}
ParserState::ElementSectionEntryBody(ref indices) => {
Expand Down
Loading