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

Commit

Permalink
Update implementation of reference types (#213)
Browse files Browse the repository at this point in the history
* Update implementation of reference types

Update the implementation given WebAssembly/reference-types#87 which has
a number of changes to the reference types implementation:

* Subtyping is removed
* `nullref` as a type was removed
* `anyref` was renamed to `externref`
* `ref.null` and `ref.is_null` take immediate type arguments as to what
  kind of `null` they're producing.

The spec testsuite repo has not yet updated with this change yet, so the
tests are temporarily vendored here in the `tests/wast` directory.

* Update TypedSelect validation
  • Loading branch information
alexcrichton authored May 12, 2020
1 parent 75c40a0 commit afc8ffa
Show file tree
Hide file tree
Showing 39 changed files with 28,135 additions and 88 deletions.
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

0 comments on commit afc8ffa

Please sign in to comment.