Express the compiler using validation trait
This commit is contained in:
parent
9723dfbfb6
commit
fc3d21a17a
|
@ -24,8 +24,6 @@ struct BlockFrame {
|
||||||
/// A signature, which is a block signature type indicating the number and types of result
|
/// A signature, which is a block signature type indicating the number and types of result
|
||||||
/// values of the region.
|
/// values of the region.
|
||||||
block_type: BlockType,
|
block_type: BlockType,
|
||||||
/// A label for reference to block instruction.
|
|
||||||
begin_position: usize,
|
|
||||||
/// A limit integer value, which is an index into the value stack indicating where to reset it
|
/// A limit integer value, which is an index into the value stack indicating where to reset it
|
||||||
/// to on a branch to that label.
|
/// to on a branch to that label.
|
||||||
value_stack_len: usize,
|
value_stack_len: usize,
|
||||||
|
@ -156,11 +154,15 @@ pub fn drive<T: FunctionValidator>(
|
||||||
module: &ModuleContext,
|
module: &ModuleContext,
|
||||||
func: &Func,
|
func: &Func,
|
||||||
body: &FuncBody,
|
body: &FuncBody,
|
||||||
validator: T,
|
|
||||||
) -> Result<T::Output, Error> {
|
) -> Result<T::Output, Error> {
|
||||||
let (params, result_ty) = module.require_function_type(func.type_ref())?;
|
let (params, result_ty) = module.require_function_type(func.type_ref())?;
|
||||||
|
|
||||||
let ins_size_estimate = body.code().elements().len();
|
let code = body.code().elements();
|
||||||
|
let code_len = code.len();
|
||||||
|
if code_len == 0 {
|
||||||
|
return Err(Error("Non-empty function body expected".into()));
|
||||||
|
}
|
||||||
|
|
||||||
let mut context = FunctionValidationContext::new(
|
let mut context = FunctionValidationContext::new(
|
||||||
&module,
|
&module,
|
||||||
Locals::new(params, body.locals())?,
|
Locals::new(params, body.locals())?,
|
||||||
|
@ -169,110 +171,62 @@ pub fn drive<T: FunctionValidator>(
|
||||||
result_ty,
|
result_ty,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut compiler = Compiler {
|
let mut validator = T::new(&context);
|
||||||
sink: Sink::with_instruction_capacity(ins_size_estimate),
|
|
||||||
label_stack: Vec::new(),
|
|
||||||
};
|
|
||||||
let end_label = compiler.sink.new_label();
|
|
||||||
compiler
|
|
||||||
.label_stack
|
|
||||||
.push(BlockFrameType::Block { end_label });
|
|
||||||
|
|
||||||
assert!(context.frame_stack.is_empty());
|
for (position, instruction) in code.iter().enumerate() {
|
||||||
|
validator
|
||||||
let body = body.code().elements();
|
.next_instruction(&mut context, instruction)
|
||||||
let body_len = body.len();
|
|
||||||
if body_len == 0 {
|
|
||||||
return Err(Error("Non-empty function body expected".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let instruction = &body[context.position];
|
|
||||||
|
|
||||||
compiler
|
|
||||||
.compile_instruction(&mut context, instruction)
|
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
Error(format!(
|
Error(format!(
|
||||||
"At instruction {:?}(@{}): {}",
|
"At instruction {:?}(@{}): {}",
|
||||||
instruction, context.position, err
|
instruction, position, err
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
context.position += 1;
|
|
||||||
if context.position == body_len {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The last `end` opcode should pop last instruction.
|
||||||
|
// TODO: This looks like it should be returned as an error?
|
||||||
|
assert!(context.frame_stack.is_empty());
|
||||||
|
|
||||||
Ok(validator.finish())
|
Ok(validator.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move under prepare
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
/// A sink used to emit optimized code.
|
/// A sink used to emit optimized code.
|
||||||
sink: Sink,
|
sink: Sink,
|
||||||
label_stack: Vec<BlockFrameType>,
|
label_stack: Vec<BlockFrameType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compiler {
|
impl FunctionValidator for Compiler {
|
||||||
pub fn compile(
|
type Output = isa::Instructions;
|
||||||
module: &ModuleContext,
|
fn new(_module: &FunctionValidationContext) -> Self {
|
||||||
func: &Func,
|
|
||||||
body: &FuncBody,
|
|
||||||
) -> Result<isa::Instructions, Error> {
|
|
||||||
let (params, result_ty) = module.require_function_type(func.type_ref())?;
|
|
||||||
|
|
||||||
let ins_size_estimate = body.code().elements().len();
|
|
||||||
let mut context = FunctionValidationContext::new(
|
|
||||||
&module,
|
|
||||||
Locals::new(params, body.locals())?,
|
|
||||||
DEFAULT_VALUE_STACK_LIMIT,
|
|
||||||
DEFAULT_FRAME_STACK_LIMIT,
|
|
||||||
result_ty,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut compiler = Compiler {
|
let mut compiler = Compiler {
|
||||||
sink: Sink::with_instruction_capacity(ins_size_estimate),
|
sink: Sink::with_instruction_capacity(0), // TODO: Estimate instruction number.
|
||||||
label_stack: Vec::new(),
|
label_stack: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Push implicit frame for the outer function block.
|
||||||
let end_label = compiler.sink.new_label();
|
let end_label = compiler.sink.new_label();
|
||||||
compiler
|
compiler
|
||||||
.label_stack
|
.label_stack
|
||||||
.push(BlockFrameType::Block { end_label });
|
.push(BlockFrameType::Block { end_label });
|
||||||
compiler.compile_function_body(&mut context, body.code().elements())?;
|
|
||||||
|
|
||||||
assert!(context.frame_stack.is_empty());
|
compiler
|
||||||
|
|
||||||
Ok(compiler.sink.into_inner())
|
|
||||||
}
|
}
|
||||||
|
fn next_instruction(
|
||||||
fn compile_function_body(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut FunctionValidationContext,
|
ctx: &mut FunctionValidationContext,
|
||||||
body: &[Instruction],
|
instruction: &Instruction,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let body_len = body.len();
|
self.compile_instruction(ctx, instruction)
|
||||||
if body_len == 0 {
|
|
||||||
return Err(Error("Non-empty function body expected".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let instruction = &body[context.position];
|
|
||||||
|
|
||||||
self.compile_instruction(context, instruction)
|
|
||||||
.map_err(|err| {
|
|
||||||
Error(format!(
|
|
||||||
"At instruction {:?}(@{}): {}",
|
|
||||||
instruction, context.position, err
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
context.position += 1;
|
|
||||||
if context.position == body_len {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fn finish(self) -> Self::Output {
|
||||||
|
self.sink.into_inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compiler {
|
||||||
fn compile_instruction(
|
fn compile_instruction(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut FunctionValidationContext,
|
context: &mut FunctionValidationContext,
|
||||||
|
@ -1139,11 +1093,9 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function validation context.
|
/// Function validation context.
|
||||||
struct FunctionValidationContext<'a> {
|
pub struct FunctionValidationContext<'a> {
|
||||||
/// Wasm module
|
/// Wasm module
|
||||||
module: &'a ModuleContext,
|
module: &'a ModuleContext,
|
||||||
/// Current instruction position.
|
|
||||||
position: usize,
|
|
||||||
/// Local variables.
|
/// Local variables.
|
||||||
locals: Locals<'a>,
|
locals: Locals<'a>,
|
||||||
/// Value stack.
|
/// Value stack.
|
||||||
|
@ -1164,7 +1116,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut ctx = FunctionValidationContext {
|
let mut ctx = FunctionValidationContext {
|
||||||
module: module,
|
module: module,
|
||||||
position: 0,
|
|
||||||
locals: locals,
|
locals: locals,
|
||||||
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
||||||
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
|
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
|
||||||
|
@ -1176,7 +1127,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
let _ = push_label(
|
let _ = push_label(
|
||||||
StartedWith::Block,
|
StartedWith::Block,
|
||||||
return_type,
|
return_type,
|
||||||
ctx.position,
|
|
||||||
&ctx.value_stack,
|
&ctx.value_stack,
|
||||||
&mut ctx.frame_stack,
|
&mut ctx.frame_stack,
|
||||||
);
|
);
|
||||||
|
@ -1187,7 +1137,7 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
self.return_type
|
self.return_type
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&mut self, instruction: &Instruction) -> Result<(), Error> {
|
pub fn step(&mut self, instruction: &Instruction) -> Result<(), Error> {
|
||||||
use self::Instruction::*;
|
use self::Instruction::*;
|
||||||
|
|
||||||
match *instruction {
|
match *instruction {
|
||||||
|
@ -1202,7 +1152,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
push_label(
|
push_label(
|
||||||
StartedWith::Block,
|
StartedWith::Block,
|
||||||
block_type,
|
block_type,
|
||||||
self.position,
|
|
||||||
&self.value_stack,
|
&self.value_stack,
|
||||||
&mut self.frame_stack,
|
&mut self.frame_stack,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1211,7 +1160,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
push_label(
|
push_label(
|
||||||
StartedWith::Loop,
|
StartedWith::Loop,
|
||||||
block_type,
|
block_type,
|
||||||
self.position,
|
|
||||||
&self.value_stack,
|
&self.value_stack,
|
||||||
&mut self.frame_stack,
|
&mut self.frame_stack,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1225,7 +1173,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
push_label(
|
push_label(
|
||||||
StartedWith::If,
|
StartedWith::If,
|
||||||
block_type,
|
block_type,
|
||||||
self.position,
|
|
||||||
&self.value_stack,
|
&self.value_stack,
|
||||||
&mut self.frame_stack,
|
&mut self.frame_stack,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1245,7 +1192,6 @@ impl<'a> FunctionValidationContext<'a> {
|
||||||
push_label(
|
push_label(
|
||||||
StartedWith::Else,
|
StartedWith::Else,
|
||||||
block_type,
|
block_type,
|
||||||
self.position,
|
|
||||||
&self.value_stack,
|
&self.value_stack,
|
||||||
&mut self.frame_stack,
|
&mut self.frame_stack,
|
||||||
)?;
|
)?;
|
||||||
|
@ -2173,14 +2119,12 @@ fn tee_value(
|
||||||
fn push_label(
|
fn push_label(
|
||||||
started_with: StartedWith,
|
started_with: StartedWith,
|
||||||
block_type: BlockType,
|
block_type: BlockType,
|
||||||
position: usize,
|
|
||||||
value_stack: &StackWithLimit<StackValueType>,
|
value_stack: &StackWithLimit<StackValueType>,
|
||||||
frame_stack: &mut StackWithLimit<BlockFrame>,
|
frame_stack: &mut StackWithLimit<BlockFrame>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Ok(frame_stack.push(BlockFrame {
|
Ok(frame_stack.push(BlockFrame {
|
||||||
started_with,
|
started_with,
|
||||||
block_type: block_type,
|
block_type: block_type,
|
||||||
begin_position: position,
|
|
||||||
value_stack_len: value_stack.len(),
|
value_stack_len: value_stack.len(),
|
||||||
polymorphic_stack: false,
|
polymorphic_stack: false,
|
||||||
})?)
|
})?)
|
||||||
|
|
|
@ -10,7 +10,6 @@ use hashbrown::HashSet;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use self::context::ModuleContextBuilder;
|
use self::context::ModuleContextBuilder;
|
||||||
use self::func::Compiler;
|
|
||||||
use common::stack;
|
use common::stack;
|
||||||
use isa;
|
use isa;
|
||||||
use memory_units::Pages;
|
use memory_units::Pages;
|
||||||
|
@ -179,22 +178,52 @@ pub trait Validation {
|
||||||
type Output;
|
type Output;
|
||||||
type FunctionValidator: FunctionValidator;
|
type FunctionValidator: FunctionValidator;
|
||||||
fn new(module: &Module) -> Self;
|
fn new(module: &Module) -> Self;
|
||||||
fn create_function_validator(&mut self) -> Self::FunctionValidator;
|
fn on_function_validated(
|
||||||
fn on_function_validated(&mut self, index: u32,
|
&mut self,
|
||||||
output: <<Self as Validation>::FunctionValidator as FunctionValidator>::Output
|
index: u32,
|
||||||
);
|
output: <<Self as Validation>::FunctionValidator as FunctionValidator>::Output,
|
||||||
|
);
|
||||||
fn finish(self) -> Self::Output;
|
fn finish(self) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FunctionValidator {
|
pub trait FunctionValidator {
|
||||||
type Output;
|
type Output;
|
||||||
fn new() -> Self;
|
fn new(ctx: &func::FunctionValidationContext) -> Self;
|
||||||
fn next_instruction(&mut self, instruction: &Instruction) -> Result<(), ()>;
|
fn next_instruction(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut func::FunctionValidationContext,
|
||||||
|
instruction: &Instruction,
|
||||||
|
) -> Result<(), Error>;
|
||||||
fn finish(self) -> Self::Output;
|
fn finish(self) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WasmiValidation {
|
||||||
|
code_map: Vec<isa::Instructions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Validation for WasmiValidation {
|
||||||
|
type Output = Vec<isa::Instructions>;
|
||||||
|
type FunctionValidator = func::Compiler;
|
||||||
|
fn new(_module: &Module) -> Self {
|
||||||
|
WasmiValidation {
|
||||||
|
// TODO: with capacity?
|
||||||
|
code_map: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn on_function_validated(
|
||||||
|
&mut self,
|
||||||
|
_index: u32,
|
||||||
|
output: isa::Instructions,
|
||||||
|
) {
|
||||||
|
self.code_map.push(output);
|
||||||
|
}
|
||||||
|
fn finish(self) -> Vec<isa::Instructions> {
|
||||||
|
self.code_map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Rename to validate_module
|
// TODO: Rename to validate_module
|
||||||
pub fn validate_module2<V: Validation>(module: &mut Module) -> Result<V::Output, Error> {
|
pub fn validate_module2<V: Validation>(module: &Module) -> Result<V::Output, Error> {
|
||||||
let mut context_builder = ModuleContextBuilder::new();
|
let mut context_builder = ModuleContextBuilder::new();
|
||||||
let mut imported_globals = Vec::new();
|
let mut imported_globals = Vec::new();
|
||||||
let mut validation = V::new(&module);
|
let mut validation = V::new(&module);
|
||||||
|
@ -285,8 +314,7 @@ pub fn validate_module2<V: Validation>(module: &mut Module) -> Result<V::Output,
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
||||||
|
|
||||||
let func_validator = validation.create_function_validator();
|
let output = func::drive::<V::FunctionValidator>(&context, function, function_body).map_err(
|
||||||
let output = func::drive(&context, function, function_body, func_validator).map_err(
|
|
||||||
|Error(ref msg)| {
|
|Error(ref msg)| {
|
||||||
Error(format!(
|
Error(format!(
|
||||||
"Function #{} reading/validation error: {}",
|
"Function #{} reading/validation error: {}",
|
||||||
|
@ -406,210 +434,7 @@ pub fn validate_module2<V: Validation>(module: &mut Module) -> Result<V::Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
pub fn validate_module(module: Module) -> Result<ValidatedModule, Error> {
|
||||||
let mut context_builder = ModuleContextBuilder::new();
|
let code_map = validate_module2::<WasmiValidation>(&module)?;
|
||||||
let mut imported_globals = Vec::new();
|
|
||||||
let mut code_map = Vec::new();
|
|
||||||
|
|
||||||
// Copy types from module as is.
|
|
||||||
context_builder.set_types(
|
|
||||||
module
|
|
||||||
.type_section()
|
|
||||||
.map(|ts| {
|
|
||||||
ts.types()
|
|
||||||
.into_iter()
|
|
||||||
.map(|&Type::Function(ref ty)| ty)
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fill elements with imported values.
|
|
||||||
for import_entry in module
|
|
||||||
.import_section()
|
|
||||||
.map(|i| i.entries())
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
match *import_entry.external() {
|
|
||||||
External::Function(idx) => context_builder.push_func_type_index(idx),
|
|
||||||
External::Table(ref table) => context_builder.push_table(table.clone()),
|
|
||||||
External::Memory(ref memory) => context_builder.push_memory(memory.clone()),
|
|
||||||
External::Global(ref global) => {
|
|
||||||
context_builder.push_global(global.clone());
|
|
||||||
imported_globals.push(global.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concatenate elements with defined in the module.
|
|
||||||
if let Some(function_section) = module.function_section() {
|
|
||||||
for func_entry in function_section.entries() {
|
|
||||||
context_builder.push_func_type_index(func_entry.type_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(table_section) = module.table_section() {
|
|
||||||
for table_entry in table_section.entries() {
|
|
||||||
validate_table_type(table_entry)?;
|
|
||||||
context_builder.push_table(table_entry.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(mem_section) = module.memory_section() {
|
|
||||||
for mem_entry in mem_section.entries() {
|
|
||||||
validate_memory_type(mem_entry)?;
|
|
||||||
context_builder.push_memory(mem_entry.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(global_section) = module.global_section() {
|
|
||||||
for global_entry in global_section.entries() {
|
|
||||||
validate_global_entry(global_entry, &imported_globals)?;
|
|
||||||
context_builder.push_global(global_entry.global_type().clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let context = context_builder.build();
|
|
||||||
|
|
||||||
let function_section_len = module
|
|
||||||
.function_section()
|
|
||||||
.map(|s| s.entries().len())
|
|
||||||
.unwrap_or(0);
|
|
||||||
let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
|
|
||||||
if function_section_len != code_section_len {
|
|
||||||
return Err(Error(format!(
|
|
||||||
"length of function section is {}, while len of code section is {}",
|
|
||||||
function_section_len, code_section_len
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate every function body in user modules
|
|
||||||
if function_section_len != 0 {
|
|
||||||
// tests use invalid code
|
|
||||||
let function_section = module
|
|
||||||
.function_section()
|
|
||||||
.expect("function_section_len != 0; qed");
|
|
||||||
let code_section = module
|
|
||||||
.code_section()
|
|
||||||
.expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
|
||||||
// check every function body
|
|
||||||
for (index, function) in function_section.entries().iter().enumerate() {
|
|
||||||
let function_body = code_section
|
|
||||||
.bodies()
|
|
||||||
.get(index as usize)
|
|
||||||
.ok_or(Error(format!("Missing body for function {}", index)))?;
|
|
||||||
let code = Compiler::compile(&context, function, function_body).map_err(|e| {
|
|
||||||
let Error(ref msg) = e;
|
|
||||||
Error(format!(
|
|
||||||
"Function #{} reading/validation error: {}",
|
|
||||||
index, msg
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
code_map.push(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate start section
|
|
||||||
if let Some(start_fn_idx) = module.start_section() {
|
|
||||||
let (params, return_ty) = context.require_function(start_fn_idx)?;
|
|
||||||
if return_ty != BlockType::NoResult || params.len() != 0 {
|
|
||||||
return Err(Error(
|
|
||||||
"start function expected to have type [] -> []".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate export section
|
|
||||||
if let Some(export_section) = module.export_section() {
|
|
||||||
let mut export_names = HashSet::with_capacity(export_section.entries().len());
|
|
||||||
for export in export_section.entries() {
|
|
||||||
// HashSet::insert returns false if item already in set.
|
|
||||||
let duplicate = export_names.insert(export.field()) == false;
|
|
||||||
if duplicate {
|
|
||||||
return Err(Error(format!("duplicate export {}", export.field())));
|
|
||||||
}
|
|
||||||
match *export.internal() {
|
|
||||||
Internal::Function(function_index) => {
|
|
||||||
context.require_function(function_index)?;
|
|
||||||
}
|
|
||||||
Internal::Global(global_index) => {
|
|
||||||
context.require_global(global_index, Some(false))?;
|
|
||||||
}
|
|
||||||
Internal::Memory(memory_index) => {
|
|
||||||
context.require_memory(memory_index)?;
|
|
||||||
}
|
|
||||||
Internal::Table(table_index) => {
|
|
||||||
context.require_table(table_index)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate import section
|
|
||||||
if let Some(import_section) = module.import_section() {
|
|
||||||
for import in import_section.entries() {
|
|
||||||
match *import.external() {
|
|
||||||
External::Function(function_type_index) => {
|
|
||||||
context.require_function_type(function_type_index)?;
|
|
||||||
}
|
|
||||||
External::Global(ref global_type) => {
|
|
||||||
if global_type.is_mutable() {
|
|
||||||
return Err(Error(format!(
|
|
||||||
"trying to import mutable global {}",
|
|
||||||
import.field()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
External::Memory(ref memory_type) => {
|
|
||||||
validate_memory_type(memory_type)?;
|
|
||||||
}
|
|
||||||
External::Table(ref table_type) => {
|
|
||||||
validate_table_type(table_type)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// there must be no greater than 1 table in tables index space
|
|
||||||
if context.tables().len() > 1 {
|
|
||||||
return Err(Error(format!(
|
|
||||||
"too many tables in index space: {}",
|
|
||||||
context.tables().len()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// there must be no greater than 1 linear memory in memory index space
|
|
||||||
if context.memories().len() > 1 {
|
|
||||||
return Err(Error(format!(
|
|
||||||
"too many memory regions in index space: {}",
|
|
||||||
context.memories().len()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// use data section to initialize linear memory regions
|
|
||||||
if let Some(data_section) = module.data_section() {
|
|
||||||
for data_segment in data_section.entries() {
|
|
||||||
context.require_memory(data_segment.index())?;
|
|
||||||
let init_ty = expr_const_type(data_segment.offset(), context.globals())?;
|
|
||||||
if init_ty != ValueType::I32 {
|
|
||||||
return Err(Error("segment offset should return I32".into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use element section to fill tables
|
|
||||||
if let Some(element_section) = module.elements_section() {
|
|
||||||
for element_segment in element_section.entries() {
|
|
||||||
context.require_table(element_segment.index())?;
|
|
||||||
|
|
||||||
let init_ty = expr_const_type(element_segment.offset(), context.globals())?;
|
|
||||||
if init_ty != ValueType::I32 {
|
|
||||||
return Err(Error("segment offset should return I32".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for function_index in element_segment.members() {
|
|
||||||
context.require_function(*function_index)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ValidatedModule { module, code_map })
|
Ok(ValidatedModule { module, code_map })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue