initial interpreter commit

This commit is contained in:
Svyatoslav Nikolsky
2017-04-21 14:35:12 +03:00
parent afb5b8d523
commit 0eb881f487
14 changed files with 2195 additions and 4 deletions

View File

@ -13,3 +13,4 @@ exclude = [ "res/*" ]
[dependencies] [dependencies]
byteorder = "1.0" byteorder = "1.0"
parking_lot = "0.4"

View File

@ -2,6 +2,7 @@ use std::io;
use super::{Deserialize, Serialize, Error, VarUint7, VarUint32}; use super::{Deserialize, Serialize, Error, VarUint7, VarUint32};
/// Internal reference of the exported entry. /// Internal reference of the exported entry.
#[derive(Clone, Copy)]
pub enum Internal { pub enum Internal {
/// Function reference. /// Function reference.
Function(u32), Function(u32),

View File

@ -1,7 +1,7 @@
use std::io; use std::io;
use super::{Deserialize, Serialize, Error, Uint32}; use super::{Deserialize, Serialize, Error, Uint32};
use super::section::{ use super::section::{
Section, CodeSection, TypeSection, ImportSection, FunctionsSection, Section, CodeSection, TypeSection, ImportSection, ExportSection, FunctionsSection,
GlobalSection, TableSection, ElementSection, DataSection, MemorySection, GlobalSection, TableSection, ElementSection, DataSection, MemorySection,
}; };
@ -82,6 +82,14 @@ impl Module {
None None
} }
/// Exports section, if any.
pub fn export_section(&self) -> Option<&ExportSection> {
for section in self.sections() {
if let &Section::Export(ref export_section) = section { return Some(export_section); }
}
None
}
/// Table section, if any. /// Table section, if any.
pub fn table_section(&self) -> Option<&TableSection> { pub fn table_section(&self) -> Option<&TableSection> {
for section in self.sections() { for section in self.sections() {

View File

@ -199,7 +199,7 @@ pub enum Opcode {
I32Or, I32Or,
I32Xor, I32Xor,
I32Shl, I32Shl,
I32ShlS, I32ShrS,
I32ShrU, I32ShrU,
I32Rotl, I32Rotl,
I32Rotr, I32Rotr,
@ -485,7 +485,7 @@ impl Deserialize for Opcode {
0x72 => I32Or, 0x72 => I32Or,
0x73 => I32Xor, 0x73 => I32Xor,
0x74 => I32Shl, 0x74 => I32Shl,
0x75 => I32ShlS, 0x75 => I32ShrS,
0x76 => I32ShrU, 0x76 => I32ShrU,
0x77 => I32Rotl, 0x77 => I32Rotl,
0x78 => I32Rotr, 0x78 => I32Rotr,
@ -805,7 +805,7 @@ impl Serialize for Opcode {
I32Or => op!(writer, 0x72), I32Or => op!(writer, 0x72),
I32Xor => op!(writer, 0x73), I32Xor => op!(writer, 0x73),
I32Shl => op!(writer, 0x74), I32Shl => op!(writer, 0x74),
I32ShlS => op!(writer, 0x75), I32ShrS => op!(writer, 0x75),
I32ShrU => op!(writer, 0x76), I32ShrU => op!(writer, 0x76),
I32Rotl => op!(writer, 0x77), I32Rotl => op!(writer, 0x77),
I32Rotr => op!(writer, 0x78), I32Rotr => op!(writer, 0x78),

144
src/interpreter/imports.rs Normal file
View File

@ -0,0 +1,144 @@
use std::sync::{Arc, Weak};
use elements::{ImportSection, ImportEntry, External, Internal};
use interpreter::Error;
use interpreter::memory::MemoryInstance;
use interpreter::module::{ModuleInstance, ItemIndex};
use interpreter::program::ProgramInstanceEssence;
use interpreter::table::TableInstance;
use interpreter::variable::VariableInstance;
// TODO: cache Internal-s to fasten access
/// Module imports.
pub struct ModuleImports {
/// Program instance.
program: Weak<ProgramInstanceEssence>,
/// External functions.
functions: Vec<usize>,
/// External tables.
tables: Vec<usize>,
/// External memory regions.
memory: Vec<usize>,
/// External globals.
globals: Vec<usize>,
}
impl ModuleImports {
/// Create new imports for given import section.
pub fn new(program: Weak<ProgramInstanceEssence>, import_section: Option<&ImportSection>) -> Self {
let mut functions = Vec::new();
let mut tables = Vec::new();
let mut memory = Vec::new();
let mut globals = Vec::new();
if let Some(import_section) = import_section {
for (import_index, import_entry) in import_section.entries().iter().enumerate() {
match import_entry.external() {
&External::Function(_) => functions.push(import_index),
&External::Table(_) => tables.push(import_index),
&External::Memory(_) => memory.push(import_index),
&External::Global(_) => globals.push(import_index),
}
}
}
ModuleImports {
program: program,
functions: functions,
tables: tables,
memory: memory,
globals: globals,
}
}
/// Parse function index.
pub fn parse_function_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.functions.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(index),
},
index @ _ => index,
}
}
/// Parse table index.
pub fn parse_table_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.tables.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(index),
},
index @ _ => index,
}
}
/// Parse memory index.
pub fn parse_memory_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.memory.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(index),
},
index @ _ => index,
}
}
/// Parse global index.
pub fn parse_global_index(&self, index: ItemIndex) -> ItemIndex {
match index {
ItemIndex::IndexSpace(index) => match index.checked_sub(self.globals.len() as u32) {
Some(index) => ItemIndex::Internal(index),
None => ItemIndex::External(index),
},
index @ _ => index,
}
}
/// Get module reference.
pub fn module(&self, name: &str) -> Result<Arc<ModuleInstance>, Error> {
self.program
.upgrade()
.ok_or(Error::Program("program unloaded".into()))
.and_then(|p| p.module(name).ok_or(Error::Program(format!("module {} is not loaded", name))))
}
/// Get table reference.
pub fn table(&self, import: &ImportEntry) -> Result<Arc<TableInstance>, Error> {
let (module, export) = self.external_export(import)?;
if let Internal::Table(external_index) = export {
return module.table(ItemIndex::Internal(external_index));
}
Err(Error::Program(format!("wrong import {} from module {} (expecting table)", import.field(), import.module())))
}
/// Get memory reference.
pub fn memory(&self, import: &ImportEntry) -> Result<Arc<MemoryInstance>, Error> {
let (module, export) = self.external_export(import)?;
if let Internal::Memory(external_index) = export {
return module.memory(ItemIndex::Internal(external_index));
}
Err(Error::Program(format!("wrong import {} from module {} (expecting memory)", import.field(), import.module())))
}
/// Get global reference.
pub fn global(&self, import: &ImportEntry) -> Result<Arc<VariableInstance>, Error> {
let (module, export) = self.external_export(import)?;
if let Internal::Global(external_index) = export {
return module.global(ItemIndex::Internal(external_index));
}
Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module())))
}
fn external_export(&self, import: &ImportEntry) -> Result<(Arc<ModuleInstance>, Internal), Error> {
self.module(import.module())
.and_then(|m| m.module().export_section()
.ok_or(Error::Program(format!("trying to import from module {} without export section", import.module())))
.and_then(|s| s.entries().iter()
.find(|e| e.field() == import.field())
.map(|e| e.internal())
.ok_or(Error::Program(format!("unresolved import {} from module {}", import.field(), import.module())))
.map(|export| (m.clone(), *export))))
}
}

87
src/interpreter/memory.rs Normal file
View File

@ -0,0 +1,87 @@
use std::u32;
use std::sync::Arc;
use parking_lot::RwLock;
use elements::MemoryType;
use interpreter::Error;
/// Linear memory page size.
const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
/// Linear memory instance.
pub struct MemoryInstance {
/// Linear memory buffer.
buffer: RwLock<Vec<u8>>,
/// Maximum buffer size.
maximum_size: u32,
}
impl MemoryInstance {
/// Create new linear memory instance.
pub fn new(memory_type: &MemoryType) -> Result<Arc<Self>, Error> {
let memory = MemoryInstance {
buffer: RwLock::new(Vec::new()), // TODO: with_capacity
maximum_size: memory_type.limits().maximum()
.map(|s| s.saturating_mul(LINEAR_MEMORY_PAGE_SIZE))
.unwrap_or(u32::MAX),
};
if memory.grow(memory_type.limits().initial())? == u32::MAX {
return Err(Error::Memory(format!("error initializing {}-pages linear memory region", memory_type.limits().initial())));
}
Ok(Arc::new(memory))
}
/// Return linear memory size (in pages).
pub fn size(&self) -> u32 {
self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
}
/// Get data at given offset.
pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
let begin = offset as usize;
let end = match begin.checked_add(size) {
Some(end) => end,
None => return Err(Error::Memory(format!("trying to read memory block of size {} from offset {}", size, offset))),
};
let buffer = self.buffer.read();
if buffer.len() <= end {
return Err(Error::Memory(format!("trying to read region [{}..{}] in memory [0..{}]", begin, end, buffer.len())));
}
Ok(buffer[begin..end].to_vec())
}
/// Set data at given offset.
pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> {
let size = value.len();
let begin = offset as usize;
let end = match begin.checked_add(size) {
Some(end) => end,
None => return Err(Error::Memory(format!("trying to update memory block of size {} from offset {}", size, offset))),
};
let mut buffer = self.buffer.write();
if buffer.len() <= end {
return Err(Error::Memory(format!("trying to update region [{}..{}] in memory [0..{}]", begin, end, buffer.len())));
}
let mut mut_buffer = buffer.as_mut_slice();
mut_buffer[begin..end].copy_from_slice(value);
Ok(())
}
/// Increases the size of the linear memory by given number of pages.
/// Returns -1 if allocation fails or previous memory size, if succeeds.
pub fn grow(&self, pages: u32) -> Result<u32, Error> {
let mut buffer = self.buffer.write();
let old_size = buffer.len() as u32;
match pages.checked_mul(LINEAR_MEMORY_PAGE_SIZE).and_then(|bytes| old_size.checked_add(bytes)) {
None => Ok(u32::MAX),
Some(new_size) if new_size > self.maximum_size => Ok(u32::MAX),
Some(new_size) => {
buffer.extend(vec![0; (new_size - old_size) as usize]);
Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
},
}
}
}

49
src/interpreter/mod.rs Normal file
View File

@ -0,0 +1,49 @@
#![allow(dead_code, unused_variables, missing_docs)]
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
Program(String),
Initialization(String),
Function(String),
Table(String),
Memory(String),
Variable(String),
Global(String),
Local(String),
ValueStack(String),
FrameStack(String),
Value(String),
Interpreter(String),
Trap,
NotImplemented,
}
impl Into<String> for Error {
fn into(self) -> String {
match self {
Error::Program(s) => s,
Error::Initialization(s) => s,
Error::Function(s) => s,
Error::Table(s) => s,
Error::Memory(s) => s,
Error::Variable(s) => s,
Error::Global(s) => s,
Error::Local(s) => s,
Error::ValueStack(s) => s,
Error::FrameStack(s) => s,
Error::Interpreter(s) => s,
Error::Value(s) => s,
Error::Trap => "trap".into(),
Error::NotImplemented => "not implemented".into(),
}
}
}
mod imports;
mod memory;
mod module;
mod program;
mod runner;
mod table;
mod value;
mod variable;

213
src/interpreter/module.rs Normal file
View File

@ -0,0 +1,213 @@
use std::sync::{Arc, Weak};
use elements::{Module, InitExpr, Opcode, Type};
use interpreter::Error;
use interpreter::imports::ModuleImports;
use interpreter::memory::MemoryInstance;
use interpreter::program::ProgramInstanceEssence;
use interpreter::runner::Interpreter;
use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::VariableInstance;
/// Item index in items index space.
#[derive(Debug, Clone, Copy)]
pub enum ItemIndex {
/// Index in index space.
IndexSpace(u32),
/// Internal item index (i.e. index of item in items section).
Internal(u32),
/// External item index (i.e. index of item in export section).
External(u32),
}
/// Module instance.
pub struct ModuleInstance {
/// Module.
module: Module,
/// Module imports.
imports: ModuleImports,
/// Tables.
tables: Vec<Arc<TableInstance>>,
/// Linear memory regions.
memory: Vec<Arc<MemoryInstance>>,
/// Globals.
globals: Vec<Arc<VariableInstance>>,
}
impl ModuleInstance {
/// Instantiate given module within program context.
pub fn new(program: Weak<ProgramInstanceEssence>, module: Module) -> Result<Self, Error> {
// TODO: missing validation step
// load entries from import section
let imports = ModuleImports::new(program, module.import_section());
// instantiate linear memory regions, if any
let mut memory = match module.memory_section() {
Some(memory_section) => memory_section.entries()
.iter()
.map(MemoryInstance::new)
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// instantiate tables, if any
let mut tables = match module.table_section() {
Some(table_section) => table_section.entries()
.iter()
.map(TableInstance::new)
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// instantiate globals, if any
let globals = match module.global_section() {
Some(global_section) => global_section.entries()
.iter()
.map(|g| {
get_initializer(g.init_expr())
.map_err(|e| Error::Initialization(e.into()))
.and_then(|v| VariableInstance::new(g.global_type(), v))
})
.collect::<Result<Vec<_>, _>>()?,
None => Vec::new(),
};
// use data section to initialize linear memory regions
if let Some(data_section) = module.data_section() {
for (data_segement_index, data_segment) in data_section.entries().iter().enumerate() {
let offset: u32 = get_initializer(data_segment.offset())?.try_into()?;
memory
.get_mut(data_segment.index() as usize)
.ok_or(Error::Initialization(format!("DataSegment {} initializes non-existant MemoryInstance {}", data_segement_index, data_segment.index())))
.and_then(|m| m.set(offset, data_segment.value()).map_err(|e| Error::Initialization(e.into())))?;
}
}
// use element section to fill tables
if let Some(element_section) = module.elements_section() {
for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() {
let offset: u32 = get_initializer(element_segment.offset())?.try_into()?;
tables
.get_mut(element_segment.index() as usize)
.ok_or(Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}", element_segment_index, element_segment.index())))
.and_then(|m| m.set(offset, element_segment.members()).map_err(|e| Error::Initialization(e.into())))?;
}
}
Ok(ModuleInstance {
module: module,
imports: imports,
memory: memory,
tables: tables,
globals: globals,
})
}
/// Get module description reference.
pub fn module(&self) -> &Module {
&self.module
}
/// Get table reference.
pub fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
match self.imports.parse_table_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_table_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.tables.get(index as usize).cloned()
.ok_or(Error::Table(format!("trying to access table with local index {} when there are only {} local tables", index, self.tables.len()))),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Table(format!("trying to access external table with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Table(format!("trying to access external table with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.table(e)),
}
}
/// Get memory reference.
pub fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error> {
match self.imports.parse_memory_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.memory.get(index as usize).cloned()
.ok_or(Error::Memory(format!("trying to access memory with local index {} when there are only {} memory regions", index, self.memory.len()))),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Memory(format!("trying to access external memory with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Memory(format!("trying to access external memory with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.memory(e)),
}
}
/// Get global reference.
pub fn global(&self, index: ItemIndex) -> Result<Arc<VariableInstance>, Error> {
match self.imports.parse_global_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"),
ItemIndex::Internal(index) => self.globals.get(index as usize).cloned()
.ok_or(Error::Global(format!("trying to access global with local index {} when there are only {} globals", index, self.globals.len()))),
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Global(format!("trying to access external global with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Global(format!("trying to access external global with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.global(e)),
}
}
/// Call function with given index in functions index space.
pub fn call_function(&self, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
// each functions has its own value stack
// but there's global stack limit
match self.imports.parse_function_index(index) {
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"),
ItemIndex::Internal(index) => {
// TODO: cache
// internal index = index of function in functions section && index of code in code section
// get function type index
let function_type_index = self.module
.functions_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without function section", index)))
.and_then(|s| s.entries()
.get(index as usize)
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions", index, s.entries().len()))))?
.type_ref();
// function type index = index of function type in types index
// get function type
let item_type = self.module
.type_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without types section", index)))
.and_then(|s| s.types()
.get(function_type_index as usize)
.ok_or(Error::Function(format!("trying to call function with type index {} in module with {} types", index, s.types().len()))))?;
let function_type = match item_type {
&Type::Function(ref function_type) => function_type,
};
// get function body
let function_body = self.module
.code_section()
.ok_or(Error::Function(format!("trying to call function with index {} in module without code section", index)))
.and_then(|s| s.bodies()
.get(index as usize)
.ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?;
// TODO: args, locals
Interpreter::run_function(function_type, function_body.code().elements(), &vec![])
},
ItemIndex::External(index) => self.module.import_section()
.ok_or(Error::Function(format!("trying to access external function with index {} in module without import section", index)))
.and_then(|s| s.entries().get(index as usize)
.ok_or(Error::Function(format!("trying to access external function with index {} in module with {}-entries import section", index, s.entries().len()))))
.and_then(|e| self.imports.module(e.module()))
.and_then(|m| m.call_function(ItemIndex::Internal(index))),
}
}
}
fn get_initializer(expr: &InitExpr) -> Result<RuntimeValue, Error> {
let first_opcode = expr.code().get(0).ok_or(Error::Initialization(format!("empty instantiation-time initializer")))?;
match first_opcode {
// TODO: &Opcode::GetGlobal(index) => self.get_global(index),
&Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)),
&Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)),
&Opcode::F32Const(val) => Ok(RuntimeValue::F32(val as f32)), // TODO
&Opcode::F64Const(val) => Ok(RuntimeValue::F64(val as f64)), // TODO
_ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))),
}
}

View File

@ -0,0 +1,54 @@
use std::sync::Arc;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use parking_lot::RwLock;
use elements::Module;
use interpreter::Error;
use interpreter::module::ModuleInstance;
/// Program instance. Program is a set of instantiated modules.
pub struct ProgramInstance {
/// Shared data reference.
essence: Arc<ProgramInstanceEssence>,
}
/// Program instance essence.
pub struct ProgramInstanceEssence {
/// Loaded modules.
modules: RwLock<HashMap<String, Arc<ModuleInstance>>>,
}
impl ProgramInstance {
/// Create new program instance.
pub fn new() -> Self {
ProgramInstance {
essence: Arc::new(ProgramInstanceEssence::new()),
}
}
/// Instantiate module.
pub fn add_module(&self, name: &str, module: Module) -> Result<(), Error> {
let mut modules = self.essence.modules.write();
match modules.entry(name.into()) {
Entry::Occupied(_) => Err(Error::Program(format!("module {} already instantiated", name))),
Entry::Vacant(entry) => {
entry.insert(Arc::new(ModuleInstance::new(Arc::downgrade(&self.essence), module)?));
Ok(())
},
}
}
}
impl ProgramInstanceEssence {
/// Create new program essence.
pub fn new() -> Self {
ProgramInstanceEssence {
modules: RwLock::new(HashMap::new()),
}
}
/// Get module reference.
pub fn module(&self, name: &str) -> Option<Arc<ModuleInstance>> {
self.modules.read().get(name).cloned()
}
}

1119
src/interpreter/runner.rs Normal file

File diff suppressed because it is too large Load Diff

26
src/interpreter/table.rs Normal file
View File

@ -0,0 +1,26 @@
use std::u32;
use std::sync::Arc;
use elements::TableType;
use interpreter::Error;
use interpreter::value::RuntimeValue;
/// Table instance.
pub struct TableInstance {
/// Table memory buffer.
buffer: Vec<RuntimeValue>,
/// Maximum buffer size.
maximum_size: u32,
}
impl TableInstance {
pub fn new(table_type: &TableType) -> Result<Arc<Self>, Error> {
Ok(Arc::new(TableInstance {
buffer: vec![RuntimeValue::Null; table_type.limits().initial() as usize],
maximum_size: table_type.limits().maximum().unwrap_or(u32::MAX),
}))
}
pub fn set(&self, offset: u32, value: &[u32]) -> Result<Self, Error> {
unimplemented!()
}
}

415
src/interpreter/value.rs Normal file
View File

@ -0,0 +1,415 @@
use std::{i32, i64, u32, u64, f32};
use std::mem;
use interpreter::Error;
use interpreter::variable::VariableType;
/// Runtime value.
#[derive(Debug, Clone, PartialEq)]
pub enum RuntimeValue {
/// Null value.
Null,
/// Reference to the function in the same module.
AnyFunc(u32),
/// 32b-length signed/unsigned int.
I32(i32),
/// 64b-length signed/unsigned int.
I64(i64),
/// 32b-length float.
F32(f32),
/// 64b-length float.
F64(f64),
}
/// Try to convert into trait.
pub trait TryInto<T, E> {
/// Try to convert self into other value.
fn try_into(self) -> Result<T, E>;
}
/// Convert one type to another by wrapping.
pub trait WrapInto<T> {
/// Convert one type to another by wrapping.
fn wrap_into(self) -> T;
}
/// Convert one type to another by rounding to the nearest integer towards zero.
pub trait TryTruncateInto<T, E> {
/// Convert one type to another by rounding to the nearest integer towards zero.
fn try_truncate_into(self) -> Result<T, E>;
}
/// Convert one type to another by extending with leading zeroes.
pub trait ExtendInto<T> {
/// Convert one type to another by extending with leading zeroes.
fn extend_into(self) -> T;
}
/// Reinterprets the bits of a value of one type as another type.
pub trait TransmuteInto<T> {
/// Reinterprets the bits of a value of one type as another type.
fn transmute_into(self) -> T;
}
/// Arithmetic operations.
pub trait ArithmeticOps<T> {
/// Add two values.
fn add(self, other: T) -> T;
/// Subtract two values.
fn sub(self, other: T) -> T;
/// Multiply two values.
fn mul(self, other: T) -> T;
/// Divide two values.
fn div(self, other: T) -> T;
}
/// Integer value.
pub trait Integer<T>: ArithmeticOps<T> {
/// Counts leading zeros in the bitwise representation of the value.
fn leading_zeros(self) -> T;
/// Counts trailing zeros in the bitwise representation of the value.
fn trailing_zeros(self) -> T;
/// Counts 1-bits in the bitwise representation of the value.
fn count_ones(self) -> T;
/// Get left bit rotation result.
fn rotl(self, other: T) -> T;
/// Get right bit rotation result.
fn rotr(self, other: T) -> T;
/// Get division remainder.
fn rem(self, other: T) -> T;
}
/// Float-point value.
pub trait Float<T>: ArithmeticOps<T> {
/// Get absolute value.
fn abs(self) -> T;
/// Returns the largest integer less than or equal to a number.
fn floor(self) -> T;
/// Returns the smallest integer greater than or equal to a number.
fn ceil(self) -> T;
/// Returns the integer part of a number.
fn trunc(self) -> T;
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
fn round(self) -> T;
/// Takes the square root of a number.
fn sqrt(self) -> T;
/// Returns the minimum of the two numbers.
fn min(self, other: T) -> T;
/// Returns the maximum of the two numbers.
fn max(self, other: T) -> T;
}
impl RuntimeValue {
pub fn decode_f32(val: u32) -> Self {
RuntimeValue::F32(unsafe { mem::transmute(val) })
}
pub fn decode_f64(val: u64) -> Self {
RuntimeValue::F64(unsafe { mem::transmute(val) })
}
pub fn is_null(&self) -> bool {
match *self {
RuntimeValue::Null => true,
_ => false,
}
}
pub fn as_any_func_index(&self) -> Option<u32> {
match *self {
RuntimeValue::AnyFunc(idx) => Some(idx),
_ => None,
}
}
pub fn variable_type(&self) -> Option<VariableType> {
match *self {
RuntimeValue::Null => None,
RuntimeValue::AnyFunc(_) => Some(VariableType::AnyFunc),
RuntimeValue::I32(_) => Some(VariableType::I32),
RuntimeValue::I64(_) => Some(VariableType::I64),
RuntimeValue::F32(_) => Some(VariableType::F32),
RuntimeValue::F64(_) => Some(VariableType::F64),
}
}
}
impl From<i32> for RuntimeValue {
fn from(val: i32) -> Self {
RuntimeValue::I32(val)
}
}
impl From<i64> for RuntimeValue {
fn from(val: i64) -> Self {
RuntimeValue::I64(val)
}
}
impl From<f32> for RuntimeValue {
fn from(val: f32) -> Self {
RuntimeValue::F32(val)
}
}
impl From<f64> for RuntimeValue {
fn from(val: f64) -> Self {
RuntimeValue::F64(val)
}
}
impl TryInto<bool, Error> for RuntimeValue {
fn try_into(self) -> Result<bool, Error> {
match self {
RuntimeValue::I32(val) => Ok(val != 0),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
}
impl TryInto<i32, Error> for RuntimeValue {
fn try_into(self) -> Result<i32, Error> {
match self {
RuntimeValue::I32(val) => Ok(val),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
}
impl TryInto<i64, Error> for RuntimeValue {
fn try_into(self) -> Result<i64, Error> {
match self {
RuntimeValue::I64(val) => Ok(val),
_ => Err(Error::Value(format!("64-bit int value expected"))),
}
}
}
impl TryInto<f32, Error> for RuntimeValue {
fn try_into(self) -> Result<f32, Error> {
match self {
RuntimeValue::F32(val) => Ok(val),
_ => Err(Error::Value(format!("32-bit float value expected"))),
}
}
}
impl TryInto<f64, Error> for RuntimeValue {
fn try_into(self) -> Result<f64, Error> {
match self {
//RuntimeValue::F32(val) => Some(val as f64),
RuntimeValue::F64(val) => Ok(val),
_ => Err(Error::Value(format!("64-bit float value expected"))),
}
}
}
impl TryInto<u32, Error> for RuntimeValue {
fn try_into(self) -> Result<u32, Error> {
match self {
RuntimeValue::I32(val) => Ok(unsafe {
mem::transmute(val)
}),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
}
impl TryInto<u64, Error> for RuntimeValue {
fn try_into(self) -> Result<u64, Error> {
match self {
RuntimeValue::I64(val) => Ok(unsafe {
mem::transmute(val)
}),
_ => Err(Error::Value(format!("64-bit int value expected"))),
}
}
}
macro_rules! impl_wrap_into {
($from: ident, $into: ident) => {
impl WrapInto<$into> for $from {
fn wrap_into(self) -> $into {
self as $into
}
}
}
}
impl_wrap_into!(i32, i8);
impl_wrap_into!(i32, i16);
impl_wrap_into!(i64, i8);
impl_wrap_into!(i64, i16);
impl_wrap_into!(i64, i32);
impl_wrap_into!(i64, f32);
impl_wrap_into!(u64, f32);
// Casting from an f64 to an f32 will produce the closest possible value (rounding strategy unspecified)
// NOTE: currently this will cause Undefined Behavior if the value is finite but larger or smaller than the
// largest or smallest finite value representable by f32. This is a bug and will be fixed.
impl_wrap_into!(f64, f32);
macro_rules! impl_try_truncate_into {
($from: ident, $into: ident) => {
impl TryTruncateInto<$into, Error> for $from {
fn try_truncate_into(self) -> Result<$into, Error> {
if !self.is_normal() {
return Err(Error::Value("invalid float value for this operation".into()));
}
let truncated = self.trunc();
if truncated < $into::MIN as $from || truncated > $into::MAX as $from {
return Err(Error::Value("invalid float value for this operation".into()));
}
Ok(truncated as $into)
}
}
}
}
impl_try_truncate_into!(f32, i32);
impl_try_truncate_into!(f32, i64);
impl_try_truncate_into!(f64, i32);
impl_try_truncate_into!(f64, i64);
impl_try_truncate_into!(f32, u32);
impl_try_truncate_into!(f32, u64);
impl_try_truncate_into!(f64, u32);
impl_try_truncate_into!(f64, u64);
macro_rules! impl_extend_into {
($from: ident, $into: ident) => {
impl ExtendInto<$into> for $from {
fn extend_into(self) -> $into {
self as $into
}
}
}
}
impl_extend_into!(i8, i32);
impl_extend_into!(u8, i32);
impl_extend_into!(i16, i32);
impl_extend_into!(u16, i32);
impl_extend_into!(i8, i64);
impl_extend_into!(u8, i64);
impl_extend_into!(i16, i64);
impl_extend_into!(u16, i64);
impl_extend_into!(i32, i64);
impl_extend_into!(u32, i64);
impl_extend_into!(u32, u64);
impl_extend_into!(i32, f32);
impl_extend_into!(i32, f64);
impl_extend_into!(u32, f32);
impl_extend_into!(u32, f64);
impl_extend_into!(i64, f64);
impl_extend_into!(u64, f64);
impl_extend_into!(f32, f64);
macro_rules! impl_transmute_into_self {
($type: ident) => {
impl TransmuteInto<$type> for $type {
fn transmute_into(self) -> $type {
self
}
}
}
}
impl_transmute_into_self!(i32);
impl_transmute_into_self!(i64);
impl_transmute_into_self!(f32);
impl_transmute_into_self!(f64);
macro_rules! impl_transmute_into {
($from: ident, $into: ident) => {
impl TransmuteInto<$into> for $from {
fn transmute_into(self) -> $into {
unsafe {
mem::transmute(self)
}
}
}
}
}
impl_transmute_into!(i32, u32);
impl_transmute_into!(u32, i32);
impl_transmute_into!(i32, f32);
impl_transmute_into!(f32, i32);
impl_transmute_into!(i64, u64);
impl_transmute_into!(u64, i64);
impl_transmute_into!(i64, f64);
impl_transmute_into!(f64, i64);
macro_rules! impl_integer_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
fn add(self, other: $type) -> $type { self.wrapping_add(other) }
fn sub(self, other: $type) -> $type { self.wrapping_sub(other) }
fn mul(self, other: $type) -> $type { self.wrapping_mul(other) }
fn div(self, other: $type) -> $type { self.wrapping_div(other) }
}
}
}
impl_integer_arithmetic_ops!(i32);
impl_integer_arithmetic_ops!(u32);
impl_integer_arithmetic_ops!(i64);
impl_integer_arithmetic_ops!(u64);
macro_rules! impl_float_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
fn add(self, other: $type) -> $type { self + other }
fn sub(self, other: $type) -> $type { self - other }
fn mul(self, other: $type) -> $type { self * other }
fn div(self, other: $type) -> $type { self / other }
}
}
}
impl_float_arithmetic_ops!(f32);
impl_float_arithmetic_ops!(f64);
macro_rules! impl_integer {
($type: ident) => {
impl Integer<$type> for $type {
fn leading_zeros(self) -> $type { self.leading_zeros() as $type }
fn trailing_zeros(self) -> $type { self.trailing_zeros() as $type }
fn count_ones(self) -> $type { self.count_ones() as $type }
fn rotl(self, other: $type) -> $type { self.rotate_left(other as u32) }
fn rotr(self, other: $type) -> $type { self.rotate_right(other as u32) }
fn rem(self, other: $type) -> $type { self.wrapping_rem(other) }
}
}
}
impl_integer!(i32);
impl_integer!(u32);
impl_integer!(i64);
impl_integer!(u64);
macro_rules! impl_float {
($type: ident) => {
impl Float<$type> for $type {
fn abs(self) -> $type { self.abs() }
fn floor(self) -> $type { self.floor() }
fn ceil(self) -> $type { self.ceil() }
fn trunc(self) -> $type { self.trunc() }
fn round(self) -> $type { self.round() }
fn sqrt(self) -> $type { self.sqrt() }
// TODO
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
// This differs from the IEEE 754-2008 minNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified.
// This differs from the common x<y?x:y expansion in its handling of negative zero and NaN values.
fn min(self, other: $type) -> $type { self.min(other) }
// TODO
// This instruction corresponds to what is sometimes called "maxNaN" in other languages.
// This differs from the IEEE 754-2008 maxNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified.
// This differs from the common x>y?x:y expansion in its handling of negative zero and NaN values.
fn max(self, other: $type) -> $type { self.max(other) }
}
}
}
impl_float!(f32);
impl_float!(f64);

View File

@ -0,0 +1,72 @@
use std::sync::Arc;
use parking_lot::RwLock;
use elements::{GlobalType, ValueType};
use interpreter::Error;
use interpreter::value::RuntimeValue;
/// Variable type.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VariableType {
/// Any func value.
AnyFunc,
/// i32 value.
I32,
/// i64 value.
I64,
/// f32 value.
F32,
/// f64 value.
F64,
}
/// Variable instance.
pub struct VariableInstance {
/// Is mutable?
is_mutable: bool,
/// Variable type.
variable_type: VariableType,
/// Global value.
value: RwLock<RuntimeValue>,
}
impl VariableInstance {
pub fn new(global_type: &GlobalType, value: RuntimeValue) -> Result<Arc<Self>, Error> {
let variable_type: VariableType = global_type.content_type().into();
if !value.is_null() && value.variable_type() != Some(variable_type) {
return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, value.variable_type())));
}
Ok(Arc::new(VariableInstance {
is_mutable: global_type.is_mutable(),
variable_type: variable_type,
value: RwLock::new(value),
}))
}
pub fn get(&self) -> RuntimeValue {
self.value.read().clone()
}
pub fn set(&self, value: RuntimeValue) -> Result<(), Error> {
if !self.is_mutable {
return Err(Error::Variable("trying to update immutable variable".into()));
}
if value.variable_type() != Some(self.variable_type) {
return Err(Error::Variable(format!("trying to update variable of type {:?} with value of type {:?}", self.variable_type, value.variable_type())));
}
*self.value.write() = value;
Ok(())
}
}
impl From<ValueType> for VariableType {
fn from(vt: ValueType) -> VariableType {
match vt {
ValueType::I32 => VariableType::I32,
ValueType::I64 => VariableType::I64,
ValueType::F32 => VariableType::F32,
ValueType::F64 => VariableType::F64,
}
}
}

View File

@ -3,9 +3,11 @@
#![warn(missing_docs)] #![warn(missing_docs)]
extern crate byteorder; extern crate byteorder;
extern crate parking_lot;
pub mod elements; pub mod elements;
pub mod builder; pub mod builder;
pub mod interpreter;
pub use elements::{ pub use elements::{
Error as SerializationError, Error as SerializationError,