Memory units (#42)
* Initial implementation * Not use grow as it is makes debug builds very slow * Use Pages::BYTE_SIZE for LINEAR_MEMORY_PAGE_SIZE * Tidy docs. * Use memory_units from git.
This commit is contained in:
parent
a3ad4b0e49
commit
483736b1bd
|
@ -11,6 +11,7 @@ exclude = [ "res/*", "spec/*" ]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-wasm = "0.23"
|
parity-wasm = "0.23"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
|
memory_units = { git = "https://github.com/pepyakin/memory_units.git", rev = "e09093e" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wabt = "0.1.2"
|
wabt = "0.1.2"
|
||||||
|
|
|
@ -16,6 +16,7 @@ use wasmi::{
|
||||||
Module, Signature, MemoryDescriptor, Trap,
|
Module, Signature, MemoryDescriptor, Trap,
|
||||||
TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs,
|
TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs,
|
||||||
};
|
};
|
||||||
|
use wasmi::memory_units::Pages;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
|
@ -43,7 +44,7 @@ impl SpecModule {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
SpecModule {
|
SpecModule {
|
||||||
table: TableInstance::alloc(10, Some(20)).unwrap(),
|
table: TableInstance::alloc(10, Some(20)).unwrap(),
|
||||||
memory: MemoryInstance::alloc(1, Some(2)).unwrap(),
|
memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(),
|
||||||
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
|
global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false),
|
||||||
global_i64: GlobalInstance::alloc(RuntimeValue::I64(666), false),
|
global_i64: GlobalInstance::alloc(RuntimeValue::I64(666), false),
|
||||||
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false),
|
global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0), false),
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
extern crate wabt;
|
extern crate wabt;
|
||||||
extern crate parity_wasm;
|
extern crate parity_wasm;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
extern crate memory_units as memory_units_crate;
|
||||||
|
|
||||||
#[cfg(all(not(feature = "32bit_opt_in"), target_pointer_width = "32"))]
|
#[cfg(all(not(feature = "32bit_opt_in"), target_pointer_width = "32"))]
|
||||||
compile_error! {"32-bit targets are not supported at the moment.
|
compile_error! {"32-bit targets are not supported at the moment.
|
||||||
|
@ -360,6 +361,12 @@ pub use self::global::{GlobalInstance, GlobalRef};
|
||||||
pub use self::func::{FuncInstance, FuncRef};
|
pub use self::func::{FuncInstance, FuncRef};
|
||||||
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||||
|
|
||||||
|
/// WebAssembly-specific sizes and units.
|
||||||
|
pub mod memory_units {
|
||||||
|
pub use memory_units_crate::wasm32::*;
|
||||||
|
pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of};
|
||||||
|
}
|
||||||
|
|
||||||
/// Deserialized module prepared for instantiation.
|
/// Deserialized module prepared for instantiation.
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
labels: HashMap<usize, HashMap<usize, usize>>,
|
labels: HashMap<usize, HashMap<usize, usize>>,
|
||||||
|
|
199
src/memory.rs
199
src/memory.rs
|
@ -6,17 +6,17 @@ use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use parity_wasm::elements::ResizableLimits;
|
use parity_wasm::elements::ResizableLimits;
|
||||||
use Error;
|
use Error;
|
||||||
use module::check_limits;
|
use memory_units::{RoundUpTo, Pages, Bytes};
|
||||||
|
|
||||||
/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
|
/// Size of a page of [linear memory][`MemoryInstance`] - 64KiB.
|
||||||
///
|
///
|
||||||
/// The size of a memory is always a integer multiple of a page size.
|
/// The size of a memory is always a integer multiple of a page size.
|
||||||
///
|
///
|
||||||
/// [`MemoryInstance`]: struct.MemoryInstance.html
|
/// [`MemoryInstance`]: struct.MemoryInstance.html
|
||||||
pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536;
|
pub const LINEAR_MEMORY_PAGE_SIZE: Bytes = Bytes(65536);
|
||||||
|
|
||||||
/// Maximal number of pages.
|
/// Maximal number of pages.
|
||||||
const LINEAR_MEMORY_MAX_PAGES: u32 = 65536;
|
const LINEAR_MEMORY_MAX_PAGES: Pages = Pages(65536);
|
||||||
|
|
||||||
/// Reference to a memory (See [`MemoryInstance`] for details).
|
/// Reference to a memory (See [`MemoryInstance`] for details).
|
||||||
///
|
///
|
||||||
|
@ -52,8 +52,8 @@ pub struct MemoryInstance {
|
||||||
limits: ResizableLimits,
|
limits: ResizableLimits,
|
||||||
/// Linear memory buffer.
|
/// Linear memory buffer.
|
||||||
buffer: RefCell<Vec<u8>>,
|
buffer: RefCell<Vec<u8>>,
|
||||||
/// Maximum buffer size.
|
initial: Pages,
|
||||||
maximum_size: u32,
|
maximum: Option<Pages>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for MemoryInstance {
|
impl fmt::Debug for MemoryInstance {
|
||||||
|
@ -61,7 +61,8 @@ impl fmt::Debug for MemoryInstance {
|
||||||
f.debug_struct("MemoryInstance")
|
f.debug_struct("MemoryInstance")
|
||||||
.field("limits", &self.limits)
|
.field("limits", &self.limits)
|
||||||
.field("buffer.len", &self.buffer.borrow().len())
|
.field("buffer.len", &self.buffer.borrow().len())
|
||||||
.field("maximum_size", &self.maximum_size)
|
.field("maximum", &self.maximum)
|
||||||
|
.field("initial", &self.initial)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,12 +93,12 @@ impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref<Target=Vec<u8>>
|
||||||
impl MemoryInstance {
|
impl MemoryInstance {
|
||||||
/// Allocate a memory instance.
|
/// Allocate a memory instance.
|
||||||
///
|
///
|
||||||
/// The memory allocated with initial number of pages specified by `initial_pages`.
|
/// The memory allocated with initial number of pages specified by `initial`.
|
||||||
/// Minimal possible value for `initial_pages` is 0 and maximum possible is `65536`.
|
/// Minimal possible value for `initial` is 0 and maximum possible is `65536`.
|
||||||
/// (Since maximum addressible memory is 2<sup>32</sup> = 4GiB = 65536 * [64KiB][`LINEAR_MEMORY_PAGE_SIZE`]).
|
/// (Since maximum addressible memory is 2<sup>32</sup> = 4GiB = 65536 * [64KiB][`LINEAR_MEMORY_PAGE_SIZE`]).
|
||||||
///
|
///
|
||||||
/// It is possible to limit maximum number of pages this memory instance can have by specifying
|
/// It is possible to limit maximum number of pages this memory instance can have by specifying
|
||||||
/// `maximum_page`. If not specified, this memory instance would be able to allocate up to 4GiB.
|
/// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB.
|
||||||
///
|
///
|
||||||
/// Allocated memory is always zeroed.
|
/// Allocated memory is always zeroed.
|
||||||
///
|
///
|
||||||
|
@ -105,39 +106,28 @@ impl MemoryInstance {
|
||||||
///
|
///
|
||||||
/// Returns `Err` if:
|
/// Returns `Err` if:
|
||||||
///
|
///
|
||||||
/// - `initial_pages` is greater than `maximum_pages`
|
/// - `initial` is greater than `maximum`
|
||||||
/// - either `initial_pages` or `maximum_pages` is greater than `65536`.
|
/// - either `initial` or `maximum` is greater than `65536`.
|
||||||
///
|
///
|
||||||
/// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html
|
/// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html
|
||||||
pub fn alloc(initial_pages: u32, maximum_pages: Option<u32>) -> Result<MemoryRef, Error> {
|
pub fn alloc(initial: Pages, maximum: Option<Pages>) -> Result<MemoryRef, Error> {
|
||||||
let memory = MemoryInstance::new(ResizableLimits::new(initial_pages, maximum_pages))?;
|
validate_memory(initial, maximum).map_err(Error::Memory)?;
|
||||||
|
|
||||||
|
let memory = MemoryInstance::new(initial, maximum);
|
||||||
Ok(MemoryRef(Rc::new(memory)))
|
Ok(MemoryRef(Rc::new(memory)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new linear memory instance.
|
/// Create new linear memory instance.
|
||||||
fn new(limits: ResizableLimits) -> Result<Self, Error> {
|
fn new(initial: Pages, maximum: Option<Pages>) -> Self {
|
||||||
check_limits(&limits)?;
|
let limits = ResizableLimits::new(initial.0 as u32, maximum.map(|p| p.0 as u32));
|
||||||
|
|
||||||
let initial_pages = limits.initial();
|
let initial_size: Bytes = initial.into();
|
||||||
let maximum_pages = limits.maximum().unwrap_or(LINEAR_MEMORY_MAX_PAGES);
|
MemoryInstance {
|
||||||
|
|
||||||
if initial_pages > LINEAR_MEMORY_MAX_PAGES {
|
|
||||||
return Err(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)));
|
|
||||||
}
|
|
||||||
if maximum_pages > LINEAR_MEMORY_MAX_PAGES {
|
|
||||||
return Err(Error::Memory(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let maximum_size = maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE);
|
|
||||||
let initial_size = initial_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE);
|
|
||||||
|
|
||||||
let memory = MemoryInstance {
|
|
||||||
limits: limits,
|
limits: limits,
|
||||||
buffer: RefCell::new(vec![0; initial_size as usize]),
|
buffer: RefCell::new(vec![0; initial_size.0]),
|
||||||
maximum_size: maximum_size,
|
initial: initial,
|
||||||
};
|
maximum: maximum,
|
||||||
|
}
|
||||||
Ok(memory)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return linear memory limits.
|
/// Return linear memory limits.
|
||||||
|
@ -146,24 +136,47 @@ impl MemoryInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of pages this `MemoryInstance` was created with.
|
/// Returns number of pages this `MemoryInstance` was created with.
|
||||||
pub fn initial_pages(&self) -> u32 {
|
pub fn initial(&self) -> Pages {
|
||||||
self.limits.initial()
|
self.initial
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns maximum amount of pages this `MemoryInstance` can grow to.
|
/// Returns maximum amount of pages this `MemoryInstance` can grow to.
|
||||||
///
|
///
|
||||||
/// Returns `None` if there is no limit set.
|
/// Returns `None` if there is no limit set.
|
||||||
/// This means that memory can grow up to 4GiB.
|
/// Maximum memory size cannot exceed `65536` pages or 4GiB.
|
||||||
pub fn maximum_pages(&self) -> Option<u32> {
|
pub fn maximum(&self) -> Option<Pages> {
|
||||||
self.limits.maximum()
|
self.maximum
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return linear memory size (in pages).
|
/// Returns current linear memory size.
|
||||||
pub fn size(&self) -> u32 {
|
///
|
||||||
self.buffer.borrow().len() as u32 / LINEAR_MEMORY_PAGE_SIZE
|
/// Maximum memory size cannot exceed `65536` pages or 4GiB.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// To convert number of pages to number of bytes you can use the following code:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use wasmi::MemoryInstance;
|
||||||
|
/// use wasmi::memory_units::*;
|
||||||
|
///
|
||||||
|
/// let memory = MemoryInstance::alloc(Pages(1), None).unwrap();
|
||||||
|
/// let byte_size: Bytes = memory.current_size().into();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// byte_size,
|
||||||
|
/// Bytes(65536),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn current_size(&self) -> Pages {
|
||||||
|
Bytes(self.buffer.borrow().len()).round_up_to()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy data from memory at given offset.
|
/// Copy data from memory at given offset.
|
||||||
|
///
|
||||||
|
/// This will allocate vector for you.
|
||||||
|
/// If you can provide a mutable slice you can use [`get_into`].
|
||||||
|
///
|
||||||
|
/// [`get_into`]: #method.get_into
|
||||||
pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
|
pub fn get(&self, offset: u32, size: usize) -> Result<Vec<u8>, Error> {
|
||||||
let buffer = self.buffer.borrow();
|
let buffer = self.buffer.borrow();
|
||||||
let region = self.checked_region(&buffer, offset as usize, size)?;
|
let region = self.checked_region(&buffer, offset as usize, size)?;
|
||||||
|
@ -196,27 +209,42 @@ impl MemoryInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increases the size of the linear memory by given number of pages.
|
/// Increases the size of the linear memory by given number of pages.
|
||||||
/// Returns previous memory size (in pages) if succeeds.
|
/// Returns previous memory size if succeeds.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns `Err` if attempted to allocate more memory than permited by the limit.
|
/// Returns `Err` if attempted to allocate more memory than permited by the limit.
|
||||||
pub fn grow(&self, pages: u32) -> Result<u32, Error> {
|
pub fn grow(&self, additional: Pages) -> Result<Pages, Error> {
|
||||||
let mut buffer = self.buffer.borrow_mut();
|
let size_before_grow: Pages = self.current_size();
|
||||||
let old_size = buffer.len() as u32;
|
println!("grow({:?}) = {:?}", additional, size_before_grow);
|
||||||
match calculate_memory_size(old_size, pages, self.maximum_size) {
|
|
||||||
None => Err(Error::Memory(
|
if additional == Pages(0) {
|
||||||
format!(
|
return Ok(size_before_grow);
|
||||||
"Trying to grow memory by {} pages when already have {}",
|
|
||||||
pages,
|
|
||||||
old_size / LINEAR_MEMORY_PAGE_SIZE,
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
Some(new_size) => {
|
|
||||||
buffer.resize(new_size as usize, 0);
|
|
||||||
Ok(old_size / LINEAR_MEMORY_PAGE_SIZE)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
if additional > Pages(65536) {
|
||||||
|
return Err(Error::Memory(format!(
|
||||||
|
"Trying to grow memory by more than 65536 pages"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_size: Pages = size_before_grow + additional;
|
||||||
|
let maximum = self.maximum.unwrap_or(LINEAR_MEMORY_MAX_PAGES);
|
||||||
|
if new_size > maximum {
|
||||||
|
return Err(Error::Memory(format!(
|
||||||
|
"Trying to grow memory by {} pages when already have {}",
|
||||||
|
additional.0, size_before_grow.0,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize underlying buffer up to a new size filling newly allocated space with zeroes.
|
||||||
|
// This size is guaranteed to be larger than current size.
|
||||||
|
let new_buffer_length: Bytes = new_size.into();
|
||||||
|
{
|
||||||
|
let mut buffer = self.buffer.borrow_mut();
|
||||||
|
debug_assert!(new_buffer_length.0 > buffer.len());
|
||||||
|
buffer.resize(new_buffer_length.0, 0);
|
||||||
|
}
|
||||||
|
Ok(size_before_grow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error>
|
fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result<CheckedRegion<'a, B>, Error>
|
||||||
|
@ -313,21 +341,32 @@ impl MemoryInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option<u32> {
|
pub fn validate_memory(initial: Pages, maximum: Option<Pages>) -> Result<(), String> {
|
||||||
let size = additional_pages
|
if initial > LINEAR_MEMORY_MAX_PAGES {
|
||||||
.saturating_mul(LINEAR_MEMORY_PAGE_SIZE);
|
return Err(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
|
||||||
match size.checked_add(old_size) {
|
|
||||||
Some(size) if size <= maximum_size => Some(size),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
|
if let Some(maximum) = maximum {
|
||||||
|
if initial > maximum {
|
||||||
|
return Err(format!(
|
||||||
|
"maximum limit {} is less than minimum {}",
|
||||||
|
maximum.0,
|
||||||
|
initial.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if maximum > LINEAR_MEMORY_MAX_PAGES {
|
||||||
|
return Err(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::{MemoryInstance, LINEAR_MEMORY_MAX_PAGES};
|
use super::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
|
||||||
use Error;
|
use Error;
|
||||||
use parity_wasm::elements::ResizableLimits;
|
use memory_units::Pages;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alloc() {
|
fn alloc() {
|
||||||
|
@ -338,16 +377,19 @@ mod tests {
|
||||||
(1, Some(1), true),
|
(1, Some(1), true),
|
||||||
(0, Some(1), true),
|
(0, Some(1), true),
|
||||||
(1, Some(0), false),
|
(1, Some(0), false),
|
||||||
(0, Some(LINEAR_MEMORY_MAX_PAGES), true),
|
(0, Some(65536), true),
|
||||||
(LINEAR_MEMORY_MAX_PAGES, Some(LINEAR_MEMORY_MAX_PAGES), true),
|
(65536, Some(65536), true),
|
||||||
(LINEAR_MEMORY_MAX_PAGES, Some(0), false),
|
(65536, Some(0), false),
|
||||||
(LINEAR_MEMORY_MAX_PAGES, None, true),
|
(65536, None, true),
|
||||||
];
|
];
|
||||||
for &(initial, maybe_max, expected_ok) in fixtures {
|
for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() {
|
||||||
let result = MemoryInstance::alloc(initial, maybe_max);
|
let initial: Pages = Pages(initial);
|
||||||
|
let maximum: Option<Pages> = maybe_max.map(|m| Pages(m));
|
||||||
|
let result = MemoryInstance::alloc(initial, maximum);
|
||||||
if result.is_ok() != expected_ok {
|
if result.is_ok() != expected_ok {
|
||||||
panic!(
|
panic!(
|
||||||
"unexpected error, initial={}, max={:?}, expected={}, result={:?}",
|
"unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}",
|
||||||
|
index,
|
||||||
initial,
|
initial,
|
||||||
maybe_max,
|
maybe_max,
|
||||||
expected_ok,
|
expected_ok,
|
||||||
|
@ -357,9 +399,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ensure_page_size() {
|
||||||
|
use memory_units::ByteSize;
|
||||||
|
assert_eq!(LINEAR_MEMORY_PAGE_SIZE, Pages::byte_size());
|
||||||
|
}
|
||||||
|
|
||||||
fn create_memory(initial_content: &[u8]) -> MemoryInstance {
|
fn create_memory(initial_content: &[u8]) -> MemoryInstance {
|
||||||
let mem = MemoryInstance::new(ResizableLimits::new(1, Some(1)))
|
let mem = MemoryInstance::new(Pages(1), Some(Pages(1)));
|
||||||
.expect("MemoryInstance created successfuly");
|
|
||||||
mem.set(0, initial_content).expect("Successful initialize the memory");
|
mem.set(0, initial_content).expect("Successful initialize the memory");
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
|
@ -418,7 +465,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_into() {
|
fn get_into() {
|
||||||
let mem = MemoryInstance::new(ResizableLimits::new(1, None)).expect("memory instance creation should not fail");
|
let mem = MemoryInstance::new(Pages(1), None);
|
||||||
mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
|
mem.set(6, &[13, 17, 129]).expect("memory set should not fail");
|
||||||
|
|
||||||
let mut data = [0u8; 2];
|
let mut data = [0u8; 2];
|
||||||
|
|
|
@ -14,6 +14,7 @@ use memory::MemoryRef;
|
||||||
use host::Externals;
|
use host::Externals;
|
||||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||||
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor};
|
||||||
|
use memory_units::Pages;
|
||||||
|
|
||||||
/// Reference to a [`ModuleInstance`].
|
/// Reference to a [`ModuleInstance`].
|
||||||
///
|
///
|
||||||
|
@ -325,10 +326,11 @@ impl ModuleInstance {
|
||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let memory = MemoryInstance::alloc(
|
let initial: Pages = Pages(memory_type.limits().initial() as usize);
|
||||||
memory_type.limits().initial(),
|
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
|
||||||
memory_type.limits().maximum()
|
|
||||||
)?;
|
let memory = MemoryInstance::alloc(initial, maximum)
|
||||||
|
.expect("Due to validation `initial` and `maximum` should be valid");
|
||||||
instance.push_memory(memory);
|
instance.push_memory(memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ use host::Externals;
|
||||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
|
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType};
|
||||||
use common::stack::StackWithLimit;
|
use common::stack::StackWithLimit;
|
||||||
use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
|
use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
|
||||||
|
use memory_units::Pages;
|
||||||
|
|
||||||
/// Function interpreter.
|
/// Function interpreter.
|
||||||
pub struct Interpreter<'a, E: Externals + 'a> {
|
pub struct Interpreter<'a, E: Externals + 'a> {
|
||||||
|
@ -643,7 +644,7 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
||||||
let m = context.module()
|
let m = context.module()
|
||||||
.memory_by_index(DEFAULT_MEMORY_INDEX)
|
.memory_by_index(DEFAULT_MEMORY_INDEX)
|
||||||
.expect("Due to validation memory should exists");
|
.expect("Due to validation memory should exists");
|
||||||
let s = m.size();
|
let s = m.current_size().0;
|
||||||
context
|
context
|
||||||
.value_stack_mut()
|
.value_stack_mut()
|
||||||
.push(RuntimeValue::I32(s as i32))?;
|
.push(RuntimeValue::I32(s as i32))?;
|
||||||
|
@ -657,9 +658,10 @@ impl<'a, E: Externals> Interpreter<'a, E> {
|
||||||
let m = context.module()
|
let m = context.module()
|
||||||
.memory_by_index(DEFAULT_MEMORY_INDEX)
|
.memory_by_index(DEFAULT_MEMORY_INDEX)
|
||||||
.expect("Due to validation memory should exists");
|
.expect("Due to validation memory should exists");
|
||||||
// Pushes -1 if allocation fails or previous memory size, if succeeds.
|
let m = match m.grow(Pages(pages as usize)) {
|
||||||
let m = m.grow(pages)
|
Ok(Pages(new_size)) => new_size as u32,
|
||||||
.unwrap_or(u32::MAX);
|
Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error.
|
||||||
|
};
|
||||||
context
|
context
|
||||||
.value_stack_mut()
|
.value_stack_mut()
|
||||||
.push(RuntimeValue::I32(m as i32))?;
|
.push(RuntimeValue::I32(m as i32))?;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use {
|
||||||
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind,
|
RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind,
|
||||||
};
|
};
|
||||||
use types::ValueType;
|
use types::ValueType;
|
||||||
|
use memory_units::Pages;
|
||||||
use super::parse_wat;
|
use super::parse_wat;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -36,7 +37,7 @@ struct TestHost {
|
||||||
impl TestHost {
|
impl TestHost {
|
||||||
fn new() -> TestHost {
|
fn new() -> TestHost {
|
||||||
TestHost {
|
TestHost {
|
||||||
memory: Some(MemoryInstance::alloc(1, Some(1)).unwrap()),
|
memory: Some(MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap()),
|
||||||
instance: None,
|
instance: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,7 +484,7 @@ fn defer_providing_externals() {
|
||||||
// Create HostImportResolver with some initialized memory instance.
|
// Create HostImportResolver with some initialized memory instance.
|
||||||
// This memory instance will be provided as 'mem' export.
|
// This memory instance will be provided as 'mem' export.
|
||||||
let host_import_resolver =
|
let host_import_resolver =
|
||||||
HostImportResolver { mem: MemoryInstance::alloc(1, Some(1)).unwrap() };
|
HostImportResolver { mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap() };
|
||||||
|
|
||||||
// Instantiate module with `host_import_resolver` as import resolver for "host" module.
|
// Instantiate module with `host_import_resolver` as import resolver for "host" module.
|
||||||
let instance = ModuleInstance::new(
|
let instance = ModuleInstance::new(
|
||||||
|
|
|
@ -3,6 +3,7 @@ use {
|
||||||
MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue,
|
MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue,
|
||||||
TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor,
|
TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor,
|
||||||
};
|
};
|
||||||
|
use memory_units::Pages;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
struct Env {
|
struct Env {
|
||||||
|
@ -17,7 +18,7 @@ impl Env {
|
||||||
Env {
|
Env {
|
||||||
table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
||||||
memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false),
|
||||||
memory: MemoryInstance::alloc(256, None).unwrap(),
|
memory: MemoryInstance::alloc(Pages(256), None).unwrap(),
|
||||||
table: TableInstance::alloc(64, None).unwrap(),
|
table: TableInstance::alloc(64, None).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use parity_wasm::elements::{
|
||||||
use common::stack;
|
use common::stack;
|
||||||
use self::context::ModuleContextBuilder;
|
use self::context::ModuleContextBuilder;
|
||||||
use self::func::Validator;
|
use self::func::Validator;
|
||||||
|
use memory_units::Pages;
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
mod func;
|
mod func;
|
||||||
|
@ -267,7 +268,7 @@ fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||||
if let Some(maximum) = limits.maximum() {
|
if let Some(maximum) = limits.maximum() {
|
||||||
if limits.initial() > maximum {
|
if limits.initial() > maximum {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
"maximum limit {} is lesser than minimum {}",
|
"maximum limit {} is less than minimum {}",
|
||||||
maximum,
|
maximum,
|
||||||
limits.initial()
|
limits.initial()
|
||||||
)));
|
)));
|
||||||
|
@ -277,7 +278,9 @@ fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> {
|
fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> {
|
||||||
validate_limits(memory_type.limits())
|
let initial: Pages = Pages(memory_type.limits().initial() as usize);
|
||||||
|
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
|
||||||
|
::memory::validate_memory(initial, maximum).map_err(Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_table_type(table_type: &TableType) -> Result<(), Error> {
|
fn validate_table_type(table_type: &TableType) -> Result<(), Error> {
|
||||||
|
|
Loading…
Reference in New Issue