mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-13 15:01:41 +00:00
initial interpreter commit
This commit is contained in:
@ -13,3 +13,4 @@ exclude = [ "res/*" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
|
parking_lot = "0.4"
|
||||||
|
@ -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),
|
||||||
|
@ -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() {
|
||||||
|
@ -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
144
src/interpreter/imports.rs
Normal 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
87
src/interpreter/memory.rs
Normal 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
49
src/interpreter/mod.rs
Normal 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
213
src/interpreter/module.rs
Normal 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))),
|
||||||
|
}
|
||||||
|
}
|
54
src/interpreter/program.rs
Normal file
54
src/interpreter/program.rs
Normal 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
1119
src/interpreter/runner.rs
Normal file
File diff suppressed because it is too large
Load Diff
26
src/interpreter/table.rs
Normal file
26
src/interpreter/table.rs
Normal 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
415
src/interpreter/value.rs
Normal 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);
|
72
src/interpreter/variable.rs
Normal file
72
src/interpreter/variable.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
Reference in New Issue
Block a user