mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-10 22:58:18 +00:00
feat: Implement safe, checked module artifact deserialization
Enable rkyv validation of serialized module artifacts. Required additions: * derive the required CheckBytes trait for all types * Add `_checked` variants of all the deserialization functions Also enables the `strict` feature of rkyv by default. This will ensure consistent archive binary layout across architectures and Rust compiler versions.
This commit is contained in:
committed by
Christoph Herzog
parent
9cb4bf32c9
commit
47cc5bbf99
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -5709,6 +5709,7 @@ dependencies = [
|
||||
name = "wasmer-types"
|
||||
version = "3.2.0-beta.1"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
"enum-iterator",
|
||||
"enumset",
|
||||
"indexmap",
|
||||
|
||||
@@ -238,6 +238,18 @@ impl Module {
|
||||
return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string()));
|
||||
}
|
||||
|
||||
pub fn deserialize_checked(
|
||||
_engine: &impl AsEngineRef,
|
||||
_bytes: impl IntoBytes,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
#[cfg(feature = "js-serializable-module")]
|
||||
return Self::from_binary(_engine, &_bytes.into_bytes())
|
||||
.map_err(|e| DeserializeError::Compiler(e));
|
||||
|
||||
#[cfg(not(feature = "js-serializable-module"))]
|
||||
return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string()));
|
||||
}
|
||||
|
||||
pub unsafe fn deserialize_from_file(
|
||||
engine: &impl AsEngineRef,
|
||||
path: impl AsRef<Path>,
|
||||
@@ -246,6 +258,14 @@ impl Module {
|
||||
Self::deserialize(engine, bytes)
|
||||
}
|
||||
|
||||
pub fn deserialize_from_file_checked(
|
||||
engine: &impl AsEngineRef,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let bytes = std::fs::read(path.as_ref())?;
|
||||
Self::deserialize_checked(engine, bytes)
|
||||
}
|
||||
|
||||
pub fn set_name(&mut self, name: &str) -> bool {
|
||||
self.name = Some(name.to_string());
|
||||
true
|
||||
|
||||
@@ -219,6 +219,8 @@ impl Module {
|
||||
|
||||
/// Deserializes a serialized Module binary into a `Module`.
|
||||
///
|
||||
/// Note: You should usually prefer the safe [`Module::deserialize_checked`].
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This function only accepts a custom binary format, which will be different
|
||||
@@ -253,6 +255,56 @@ impl Module {
|
||||
Ok(Self(module_imp::Module::deserialize(engine, bytes)?))
|
||||
}
|
||||
|
||||
/// Deserializes a serialized Module binary into a `Module`.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This function only accepts a custom binary format, which will be different
|
||||
/// than the `wasm` binary format and may change among Wasmer versions.
|
||||
/// (it should be the result of the serialization of a Module via the
|
||||
/// `Module::serialize` method.).
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```ignore
|
||||
/// # use wasmer::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let mut store = Store::default();
|
||||
/// let module = Module::deserialize_checked(&store, serialized_data)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn deserialize_checked(
|
||||
engine: &impl AsEngineRef,
|
||||
bytes: impl IntoBytes,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
Ok(Self(module_imp::Module::deserialize_checked(
|
||||
engine, bytes,
|
||||
)?))
|
||||
}
|
||||
|
||||
/// Deserializes a a serialized Module located in a `Path` into a `Module`.
|
||||
/// > Note: the module has to be serialized before with the `serialize` method.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```ignore
|
||||
/// # use wasmer::*;
|
||||
/// # let mut store = Store::default();
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// let module = Module::deserialize_from_file(&store, path)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn deserialize_from_file_checked(
|
||||
engine: &impl AsEngineRef,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
Ok(Self(module_imp::Module::deserialize_from_file_checked(
|
||||
engine, path,
|
||||
)?))
|
||||
}
|
||||
|
||||
/// Deserializes a a serialized Module located in a `Path` into a `Module`.
|
||||
/// > Note: the module has to be serialized before with the `serialize` method.
|
||||
///
|
||||
|
||||
@@ -78,6 +78,19 @@ impl Module {
|
||||
Ok(Self::from_artifact(artifact))
|
||||
}
|
||||
|
||||
pub fn deserialize_checked(
|
||||
engine: &impl AsEngineRef,
|
||||
bytes: impl IntoBytes,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let bytes = bytes.into_bytes();
|
||||
let artifact = engine
|
||||
.as_engine_ref()
|
||||
.engine()
|
||||
.0
|
||||
.deserialize_checked(&bytes)?;
|
||||
Ok(Self::from_artifact(artifact))
|
||||
}
|
||||
|
||||
pub unsafe fn deserialize_from_file(
|
||||
engine: &impl AsEngineRef,
|
||||
path: impl AsRef<Path>,
|
||||
@@ -90,6 +103,18 @@ impl Module {
|
||||
Ok(Self::from_artifact(artifact))
|
||||
}
|
||||
|
||||
pub fn deserialize_from_file_checked(
|
||||
engine: &impl AsEngineRef,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let artifact = engine
|
||||
.as_engine_ref()
|
||||
.engine()
|
||||
.0
|
||||
.deserialize_from_file_checked(path.as_ref())?;
|
||||
Ok(Self::from_artifact(artifact))
|
||||
}
|
||||
|
||||
fn from_artifact(artifact: Arc<Artifact>) -> Self {
|
||||
Self { artifact }
|
||||
}
|
||||
|
||||
@@ -148,6 +148,30 @@ impl Artifact {
|
||||
/// # Safety
|
||||
/// This function is unsafe because rkyv reads directly without validating
|
||||
/// the data.
|
||||
pub fn deserialize_checked(engine: &Engine, bytes: &[u8]) -> Result<Self, DeserializeError> {
|
||||
if !ArtifactBuild::is_deserializable(bytes) {
|
||||
return Err(DeserializeError::Incompatible(
|
||||
"Magic header not found".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let bytes = Self::get_byte_slice(bytes, ArtifactBuild::MAGIC_HEADER.len(), bytes.len())?;
|
||||
|
||||
let metadata_len = MetadataHeader::parse(bytes)?;
|
||||
let metadata_slice = Self::get_byte_slice(bytes, MetadataHeader::LEN, bytes.len())?;
|
||||
let metadata_slice = Self::get_byte_slice(metadata_slice, 0, metadata_len)?;
|
||||
|
||||
let serializable = SerializableModule::deserialize_checked(metadata_slice)?;
|
||||
let artifact = ArtifactBuild::from_serializable(serializable);
|
||||
let mut inner_engine = engine.inner_mut();
|
||||
Self::from_parts(&mut inner_engine, artifact, engine.target())
|
||||
.map_err(DeserializeError::Compiler)
|
||||
}
|
||||
|
||||
/// Deserialize a ArtifactBuild
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe because rkyv reads directly without validating the data.
|
||||
pub unsafe fn deserialize(engine: &Engine, bytes: &[u8]) -> Result<Self, DeserializeError> {
|
||||
if !ArtifactBuild::is_deserializable(bytes) {
|
||||
let static_artifact = Self::deserialize_object(engine, bytes);
|
||||
|
||||
@@ -200,12 +200,28 @@ impl Engine {
|
||||
Ok(Arc::new(Artifact::deserialize(self, bytes)?))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn deserialize_checked(&self, bytes: &[u8]) -> Result<Arc<Artifact>, DeserializeError> {
|
||||
Ok(Arc::new(Artifact::deserialize_checked(self, bytes)?))
|
||||
}
|
||||
|
||||
/// Deserializes a WebAssembly module from a path
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn deserialize_from_file_checked(
|
||||
&self,
|
||||
file_ref: &Path,
|
||||
) -> Result<Arc<Artifact>, DeserializeError> {
|
||||
let contents = std::fs::read(file_ref)?;
|
||||
self.deserialize_checked(&contents)
|
||||
}
|
||||
|
||||
/// Deserialize from a file path.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The file's content must represent a serialized WebAssembly module.
|
||||
/// See [`crate::Module::deserialize_from_file`].
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub unsafe fn deserialize_from_file(
|
||||
&self,
|
||||
file_ref: &Path,
|
||||
|
||||
@@ -16,10 +16,11 @@ serde_bytes = { version = "0.11", optional = true }
|
||||
thiserror = "1.0"
|
||||
more-asserts = "0.2"
|
||||
indexmap = { version = "1.6" }
|
||||
rkyv = { version = "0.7.40", features = ["indexmap"] }
|
||||
rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] }
|
||||
enum-iterator = "0.7.0"
|
||||
target-lexicon = { version = "0.12.2", default-features = false }
|
||||
enumset = "1.0"
|
||||
bytecheck = "0.6.8"
|
||||
|
||||
[dev-dependencies]
|
||||
memoffset = "0.6"
|
||||
|
||||
@@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// Single source location to generated address mapping.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct InstructionAddressMap {
|
||||
/// Original source location.
|
||||
pub srcloc: SourceLoc,
|
||||
@@ -24,6 +25,7 @@ pub struct InstructionAddressMap {
|
||||
/// Function and its instructions addresses mappings.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct FunctionAddressMap {
|
||||
/// Instructions maps.
|
||||
/// The array is sorted by the InstructionAddressMap::code_offset field.
|
||||
|
||||
@@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// the frame information after a `Trap`.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct CompiledFunctionFrameInfo {
|
||||
/// The traps (in the function body).
|
||||
///
|
||||
@@ -34,6 +35,7 @@ pub struct CompiledFunctionFrameInfo {
|
||||
/// The function body.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct FunctionBody {
|
||||
/// The function body bytes.
|
||||
#[cfg_attr(feature = "enable-serde", serde(with = "serde_bytes"))]
|
||||
@@ -50,6 +52,7 @@ pub struct FunctionBody {
|
||||
/// and unwind information).
|
||||
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct CompiledFunction {
|
||||
/// The function body.
|
||||
pub body: FunctionBody,
|
||||
@@ -74,7 +77,9 @@ pub type CustomSections = PrimaryMap<SectionIndex, CustomSection>;
|
||||
/// In the future this structure may also hold other information useful
|
||||
/// for debugging.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(
|
||||
RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, PartialEq, Eq, Clone,
|
||||
)]
|
||||
#[archive(as = "Self")]
|
||||
pub struct Dwarf {
|
||||
/// The section index in the [`Compilation`] that corresponds to the exception frames.
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::sync::Arc;
|
||||
/// or the `MemoryStyle` and `TableStyle`).
|
||||
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct CompileModuleInfo {
|
||||
/// The features used for compiling the module
|
||||
pub features: Features,
|
||||
|
||||
@@ -21,8 +21,11 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Relocation kinds for every ISA.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(
|
||||
RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Copy, Clone, Debug, PartialEq, Eq,
|
||||
)]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum RelocationKind {
|
||||
/// absolute 4-byte
|
||||
Abs4,
|
||||
@@ -90,6 +93,7 @@ impl fmt::Display for RelocationKind {
|
||||
/// A record of a relocation to perform.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct Relocation {
|
||||
/// The relocation kind.
|
||||
pub kind: RelocationKind,
|
||||
@@ -103,8 +107,11 @@ pub struct Relocation {
|
||||
|
||||
/// Destination function. Can be either user function or some special one, like `memory.grow`.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(
|
||||
RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, Copy, Clone, PartialEq, Eq,
|
||||
)]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum RelocationTarget {
|
||||
/// A relocation to a function defined locally in the wasm (not an imported one).
|
||||
LocalFunc(LocalFunctionIndex),
|
||||
|
||||
@@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize};
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
@@ -37,8 +38,11 @@ entity_impl!(SectionIndex);
|
||||
///
|
||||
/// Determines how a custom section may be used.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(
|
||||
RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, Clone, PartialEq, Eq,
|
||||
)]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum CustomSectionProtection {
|
||||
/// A custom section with read permission.
|
||||
Read,
|
||||
@@ -53,6 +57,7 @@ pub enum CustomSectionProtection {
|
||||
/// in the emitted module.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct CustomSection {
|
||||
/// Memory protection that applies to this section.
|
||||
pub protection: CustomSectionProtection,
|
||||
@@ -72,6 +77,7 @@ pub struct CustomSection {
|
||||
/// The bytes in the section.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct SectionBody(#[cfg_attr(feature = "enable-serde", serde(with = "serde_bytes"))] Vec<u8>);
|
||||
|
||||
impl SectionBody {
|
||||
|
||||
@@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize};
|
||||
)]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, rkyv::CheckBytes)]
|
||||
#[archive(as = "Self")]
|
||||
pub struct SourceLoc(u32);
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ use crate::{
|
||||
SectionIndex, SerializeError, SignatureIndex,
|
||||
};
|
||||
use rkyv::{
|
||||
archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer,
|
||||
ser::Serializer as RkyvSerializer, Archive, Deserialize as RkyvDeserialize,
|
||||
Serialize as RkyvSerialize,
|
||||
archived_value, check_archived_value, de::deserializers::SharedDeserializeMap,
|
||||
ser::serializers::AllocSerializer, ser::Serializer as RkyvSerializer, Archive,
|
||||
Deserialize as RkyvDeserialize, Serialize as RkyvSerialize,
|
||||
};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -50,6 +50,7 @@ pub trait SymbolRegistry: Send + Sync {
|
||||
/// Serializable struct that represents the compiled metadata.
|
||||
#[derive(Debug, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct ModuleMetadata {
|
||||
/// Compile info
|
||||
pub compile_info: CompileModuleInfo,
|
||||
@@ -114,6 +115,14 @@ impl ModuleMetadata {
|
||||
Self::deserialize_from_archive(archived)
|
||||
}
|
||||
|
||||
/// Deserialize a Module from a slice.
|
||||
/// The slice must have the following format:
|
||||
/// RKYV serialization (any length) + POS (8 bytes)
|
||||
pub fn deserialize_checked(metadata_slice: &[u8]) -> Result<Self, DeserializeError> {
|
||||
let archived = Self::archive_from_slice_checked(metadata_slice)?;
|
||||
Self::deserialize_from_archive(archived)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This method is unsafe.
|
||||
@@ -135,6 +144,25 @@ impl ModuleMetadata {
|
||||
))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This method is unsafe.
|
||||
/// Please check `ModuleMetadata::deserialize` for more details.
|
||||
fn archive_from_slice_checked(
|
||||
metadata_slice: &[u8],
|
||||
) -> Result<&ArchivedModuleMetadata, DeserializeError> {
|
||||
if metadata_slice.len() < 8 {
|
||||
return Err(DeserializeError::Incompatible(
|
||||
"invalid serialized ModuleMetadata".into(),
|
||||
));
|
||||
}
|
||||
let mut pos: [u8; 8] = Default::default();
|
||||
pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]);
|
||||
let pos: u64 = u64::from_le_bytes(pos);
|
||||
check_archived_value::<Self>(&metadata_slice[..metadata_slice.len() - 8], pos as usize)
|
||||
.map_err(|e| DeserializeError::CorruptedBinary(e.to_string()))
|
||||
}
|
||||
|
||||
/// Deserialize a compilation module from an archive
|
||||
pub fn deserialize_from_archive(
|
||||
archived: &ArchivedModuleMetadata,
|
||||
|
||||
@@ -7,7 +7,9 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Information about trap.
|
||||
#[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(
|
||||
RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Clone, Debug, PartialEq, Eq,
|
||||
)]
|
||||
#[archive(as = "Self")]
|
||||
pub struct TrapInformation {
|
||||
/// The offset of the trapping instruction in native code. It is relative to the beginning of the function.
|
||||
|
||||
@@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// [unwind info]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2019
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub enum CompiledFunctionUnwindInfo {
|
||||
/// Windows UNWIND_INFO.
|
||||
WindowsX64(Vec<u8>),
|
||||
|
||||
@@ -34,6 +34,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct PrimaryMap<K, V>
|
||||
where
|
||||
K: EntityRef,
|
||||
|
||||
@@ -28,6 +28,7 @@ use serde::{
|
||||
/// The map does not track if an entry for a key has been inserted or not. Instead it behaves as if
|
||||
/// all keys have a default entry from the beginning.
|
||||
#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(rkyv::CheckBytes))]
|
||||
pub struct SecondaryMap<K, V>
|
||||
where
|
||||
K: EntityRef,
|
||||
|
||||
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// Features usually have a corresponding [WebAssembly proposal].
|
||||
///
|
||||
/// [WebAssembly proposal]: https://github.com/WebAssembly/proposals
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, rkyv::CheckBytes)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive(as = "Self")]
|
||||
|
||||
@@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize};
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -49,6 +50,7 @@ entity_impl!(LocalMemoryIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -68,6 +70,7 @@ entity_impl!(LocalGlobalIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -87,6 +90,7 @@ entity_impl!(FunctionIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -106,6 +110,7 @@ entity_impl!(TableIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -125,6 +130,7 @@ entity_impl!(GlobalIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -144,6 +150,7 @@ entity_impl!(MemoryIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -163,6 +170,7 @@ entity_impl!(SignatureIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -182,6 +190,7 @@ entity_impl!(DataIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -201,6 +210,7 @@ entity_impl!(ElemIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
@@ -220,9 +230,11 @@ entity_impl!(CustomSectionIndex);
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum ExportIndex {
|
||||
/// Function export.
|
||||
Function(FunctionIndex),
|
||||
@@ -236,10 +248,21 @@ pub enum ExportIndex {
|
||||
|
||||
/// An entity to import.
|
||||
#[derive(
|
||||
Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, RkyvSerialize, RkyvDeserialize, Archive,
|
||||
Clone,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum ImportIndex {
|
||||
/// Function import.
|
||||
Function(FunctionIndex),
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use crate::indexes::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex};
|
||||
use crate::lib::std::boxed::Box;
|
||||
|
||||
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||
use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A WebAssembly table initializer.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct TableInitializer {
|
||||
/// The index of a table to initialize.
|
||||
pub table_index: TableIndex,
|
||||
@@ -23,6 +24,7 @@ pub struct TableInitializer {
|
||||
/// should be performed.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct DataInitializerLocation {
|
||||
/// The index of the memory to initialize.
|
||||
pub memory_index: MemoryIndex,
|
||||
@@ -49,6 +51,7 @@ pub struct DataInitializer<'data> {
|
||||
/// holding a reference to it
|
||||
#[derive(Debug, Clone, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct OwnedDataInitializer {
|
||||
/// The location where the initialization is to be performed.
|
||||
pub location: DataInitializerLocation,
|
||||
|
||||
@@ -18,9 +18,11 @@ use std::fmt;
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u16)]
|
||||
pub enum LibCall {
|
||||
/// ceil.f32
|
||||
CeilF32,
|
||||
|
||||
@@ -7,9 +7,21 @@ use std::iter::Sum;
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
/// Implementation styles for WebAssembly linear memory.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum MemoryStyle {
|
||||
/// The actual memory can be resized and moved.
|
||||
Dynamic {
|
||||
|
||||
@@ -14,8 +14,8 @@ use crate::{
|
||||
use indexmap::IndexMap;
|
||||
use rkyv::{
|
||||
de::SharedDeserializeRegistry, ser::ScratchSpace, ser::Serializer,
|
||||
ser::SharedSerializeRegistry, Archive, Archived, Deserialize as RkyvDeserialize, Fallible,
|
||||
Serialize as RkyvSerialize,
|
||||
ser::SharedSerializeRegistry, Archive, Archived, CheckBytes, Deserialize as RkyvDeserialize,
|
||||
Fallible, Serialize as RkyvSerialize,
|
||||
};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -26,6 +26,7 @@ use std::iter::ExactSizeIterator;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
|
||||
#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct ModuleId {
|
||||
id: usize,
|
||||
}
|
||||
@@ -48,6 +49,7 @@ impl Default for ModuleId {
|
||||
/// Hash key of an import
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Default, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive_attr(derive(CheckBytes, PartialEq, Eq, Hash))]
|
||||
pub struct ImportKey {
|
||||
/// Module name
|
||||
pub module: String,
|
||||
@@ -177,6 +179,7 @@ pub struct ModuleInfo {
|
||||
|
||||
/// Mirror version of ModuleInfo that can derive rkyv traits
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct ArchivableModuleInfo {
|
||||
name: Option<String>,
|
||||
imports: IndexMap<ImportKey, ImportIndex>,
|
||||
|
||||
@@ -6,9 +6,10 @@ use crate::{
|
||||
SerializeError, SignatureIndex, TableIndex, TableStyle,
|
||||
};
|
||||
use enumset::EnumSet;
|
||||
use rkyv::check_archived_value;
|
||||
use rkyv::{
|
||||
archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer,
|
||||
ser::Serializer as RkyvSerializer, Archive, Deserialize as RkyvDeserialize,
|
||||
ser::Serializer as RkyvSerializer, Archive, CheckBytes, Deserialize as RkyvDeserialize,
|
||||
Serialize as RkyvSerialize,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
@@ -17,6 +18,7 @@ use std::mem;
|
||||
/// The compilation related data for a serialized modules
|
||||
#[derive(Archive, Default, RkyvDeserialize, RkyvSerialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct SerializableCompilation {
|
||||
pub function_bodies: PrimaryMap<LocalFunctionIndex, FunctionBody>,
|
||||
pub function_relocations: PrimaryMap<LocalFunctionIndex, Vec<Relocation>>,
|
||||
@@ -51,6 +53,7 @@ impl SerializableCompilation {
|
||||
/// Serializable struct that is able to serialize from and to a `ArtifactInfo`.
|
||||
#[derive(Archive, RkyvDeserialize, RkyvSerialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct SerializableModule {
|
||||
/// The main serializable compilation object
|
||||
pub compilation: SerializableCompilation,
|
||||
@@ -96,6 +99,16 @@ impl SerializableModule {
|
||||
Self::deserialize_from_archive(archived)
|
||||
}
|
||||
|
||||
/// Deserialize a Module from a slice.
|
||||
/// The slice must have the following format:
|
||||
/// RKYV serialization (any length) + POS (8 bytes)
|
||||
///
|
||||
/// Unlike [`Self::deserialize`], this function will validate the data.
|
||||
pub fn deserialize_checked(metadata_slice: &[u8]) -> Result<Self, DeserializeError> {
|
||||
let archived = Self::archive_from_slice_checked(metadata_slice)?;
|
||||
Self::deserialize_from_archive(archived)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This method is unsafe.
|
||||
@@ -117,6 +130,25 @@ impl SerializableModule {
|
||||
))
|
||||
}
|
||||
|
||||
/// Deserialize an archived module.
|
||||
///
|
||||
/// In contrast to [`Self::deserialize`], this method performs validation
|
||||
/// and is not unsafe.
|
||||
fn archive_from_slice_checked(
|
||||
metadata_slice: &[u8],
|
||||
) -> Result<&ArchivedSerializableModule, DeserializeError> {
|
||||
if metadata_slice.len() < 8 {
|
||||
return Err(DeserializeError::Incompatible(
|
||||
"invalid serialized data".into(),
|
||||
));
|
||||
}
|
||||
let mut pos: [u8; 8] = Default::default();
|
||||
pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]);
|
||||
let pos: u64 = u64::from_le_bytes(pos);
|
||||
check_archived_value::<Self>(&metadata_slice[..metadata_slice.len() - 8], pos as usize)
|
||||
.map_err(|err| DeserializeError::CorruptedBinary(err.to_string()))
|
||||
}
|
||||
|
||||
/// Deserialize a compilation module from an archive
|
||||
pub fn deserialize_from_archive(
|
||||
archived: &ArchivedSerializableModule,
|
||||
@@ -175,7 +207,7 @@ pub struct MetadataHeader {
|
||||
impl MetadataHeader {
|
||||
/// Current ABI version. Increment this any time breaking changes are made
|
||||
/// to the format of the serialized data.
|
||||
const CURRENT_VERSION: u32 = 3;
|
||||
const CURRENT_VERSION: u32 = 4;
|
||||
|
||||
/// Magic number to identify wasmer metadata.
|
||||
const MAGIC: [u8; 8] = *b"WASMER\0\0";
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use bytecheck::CheckBytes;
|
||||
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Implementation styles for WebAssembly tables.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[derive(
|
||||
Debug, Clone, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive, CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum TableStyle {
|
||||
/// Signatures are stored in the table and checked in the caller.
|
||||
CallerChecksSignature,
|
||||
|
||||
@@ -14,7 +14,17 @@ use thiserror::Error;
|
||||
///
|
||||
/// All trap instructions have an explicit trap code.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Debug, Hash, Error, RkyvSerialize, RkyvDeserialize, Archive,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Debug,
|
||||
Hash,
|
||||
Error,
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[repr(u32)]
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::lib::std::string::{String, ToString};
|
||||
use crate::lib::std::vec::Vec;
|
||||
use crate::units::Pages;
|
||||
|
||||
use bytecheck::CheckBytes;
|
||||
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -17,8 +18,9 @@ use serde::{Deserialize, Serialize};
|
||||
/// A list of all possible value types in WebAssembly.
|
||||
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes)]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum Type {
|
||||
/// Signed 32 bit integer.
|
||||
I32,
|
||||
@@ -58,10 +60,10 @@ impl fmt::Display for Type {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
/// The WebAssembly V128 type
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, CheckBytes)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
/// The WebAssembly V128 type
|
||||
#[archive(as = "Self")]
|
||||
pub struct V128(pub(crate) [u8; 16]);
|
||||
|
||||
@@ -240,6 +242,7 @@ impl ExternType {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct FunctionType {
|
||||
/// The parameters of the function
|
||||
params: Box<[Type]>,
|
||||
@@ -323,10 +326,11 @@ impl From<&Self> for FunctionType {
|
||||
}
|
||||
|
||||
/// Indicator of whether a global is mutable or not
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, CheckBytes)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum Mutability {
|
||||
/// The global is constant and its value does not change
|
||||
Const,
|
||||
@@ -361,7 +365,7 @@ impl From<Mutability> for bool {
|
||||
}
|
||||
|
||||
/// WebAssembly global.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, CheckBytes)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive(as = "Self")]
|
||||
@@ -408,8 +412,9 @@ impl fmt::Display for GlobalType {
|
||||
/// Globals are initialized via the `const` operators or by referring to another import.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes)]
|
||||
#[archive(as = "Self")]
|
||||
#[repr(u8)]
|
||||
pub enum GlobalInit {
|
||||
/// An `i32.const`.
|
||||
I32Const(i32),
|
||||
@@ -442,6 +447,7 @@ pub enum GlobalInit {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct TableType {
|
||||
/// The type of data stored in elements of the table.
|
||||
pub ty: Type,
|
||||
@@ -482,6 +488,7 @@ impl fmt::Display for TableType {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
|
||||
#[archive_attr(derive(CheckBytes))]
|
||||
pub struct MemoryType {
|
||||
/// The minimum number of pages in the memory.
|
||||
pub minimum: Pages,
|
||||
|
||||
@@ -21,7 +21,17 @@ pub const WASM_MIN_PAGES: u32 = 0x100;
|
||||
|
||||
/// Units of WebAssembly pages (as specified to be 65,536 bytes).
|
||||
#[derive(
|
||||
Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RkyvSerialize, RkyvDeserialize, Archive,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
Archive,
|
||||
rkyv::CheckBytes,
|
||||
)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[archive(as = "Self")]
|
||||
|
||||
Reference in New Issue
Block a user