Simplified serialization

This commit is contained in:
Syrus
2020-05-20 21:06:12 -07:00
parent 2c66e2ac3c
commit 3d6ff89da0
10 changed files with 80 additions and 106 deletions

7
Cargo.lock generated
View File

@@ -444,12 +444,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240" checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240"
[[package]]
name = "downcast-rs"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6"
[[package]] [[package]]
name = "dynasm" name = "dynasm"
version = "0.5.2" version = "0.5.2"
@@ -1925,7 +1919,6 @@ version = "0.16.2"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bincode", "bincode",
"downcast-rs",
"lazy_static", "lazy_static",
"more-asserts", "more-asserts",
"region", "region",

View File

@@ -159,7 +159,7 @@ impl Module {
/// let serialized = module.serialize()?; /// let serialized = module.serialize()?;
/// ``` /// ```
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> { pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
self.store.engine().serialize(self.artifact.borrow()) self.artifact.serialize()
} }
/// Deserializes a a serialized Module binary into a `Module`. /// Deserializes a a serialized Module binary into a `Module`.

View File

@@ -33,6 +33,13 @@ pub struct JITArtifact {
} }
impl JITArtifact { impl JITArtifact {
const MAGIC_HEADER: &'static [u8] = b"\0wasmer-jit";
/// Check if the provided bytes look like a serialized `JITArtifact`.
pub fn is_deserializable(bytes: &[u8]) -> bool {
bytes.starts_with(Self::MAGIC_HEADER)
}
/// Compile a data buffer into a `JITArtifact`, which may then be instantiated. /// Compile a data buffer into a `JITArtifact`, which may then be instantiated.
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> { pub fn new(jit: &JITEngine, data: &[u8]) -> Result<Self, CompileError> {
@@ -123,21 +130,20 @@ impl JITArtifact {
)) ))
} }
/// Serialize a JITArtifact
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
// let mut s = flexbuffers::FlexbufferSerializer::new();
// self.serializable.serialize(&mut s).map_err(|e| SerializeError::Generic(format!("{:?}", e)));
// Ok(s.take_buffer())
bincode::serialize(&self.serializable)
.map_err(|e| SerializeError::Generic(format!("{:?}", e)))
}
/// Deserialize a JITArtifact /// Deserialize a JITArtifact
pub fn deserialize(jit: &JITEngine, bytes: &[u8]) -> Result<Self, DeserializeError> { pub fn deserialize(jit: &JITEngine, bytes: &[u8]) -> Result<Self, DeserializeError> {
if !Self::is_deserializable(bytes) {
return Err(DeserializeError::Incompatible(
"The provided bytes are not wasmer-jit".to_string(),
));
}
let inner_bytes = &bytes[Self::MAGIC_HEADER.len()..];
// let r = flexbuffers::Reader::get_root(bytes).map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?; // let r = flexbuffers::Reader::get_root(bytes).map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
// let serializable = SerializableModule::deserialize(r).map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?; // let serializable = SerializableModule::deserialize(r).map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
let serializable: SerializableModule = bincode::deserialize(bytes) let serializable: SerializableModule = bincode::deserialize(inner_bytes)
.map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?; .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
Self::from_parts(&mut jit.inner_mut(), serializable).map_err(DeserializeError::Compiler) Self::from_parts(&mut jit.inner_mut(), serializable).map_err(DeserializeError::Compiler)
@@ -242,4 +248,17 @@ impl Artifact for JITArtifact {
fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> { fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
&self.signatures &self.signatures
} }
fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
// let mut s = flexbuffers::FlexbufferSerializer::new();
// self.serializable.serialize(&mut s).map_err(|e| SerializeError::Generic(format!("{:?}", e)));
// Ok(s.take_buffer())
let bytes = bincode::serialize(&self.serializable)
.map_err(|e| SerializeError::Generic(format!("{:?}", e)))?;
// We append the header
let mut serialized = Self::MAGIC_HEADER.to_vec();
serialized.extend(bytes);
Ok(serialized)
}
} }

View File

@@ -23,8 +23,6 @@ pub struct JITEngine {
} }
impl JITEngine { impl JITEngine {
const MAGIC_HEADER: &'static [u8] = b"\0wasmer-jit";
/// Create a new `JITEngine` with the given config /// Create a new `JITEngine` with the given config
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub fn new( pub fn new(
@@ -76,12 +74,6 @@ impl JITEngine {
pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, JITEngineInner> { pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, JITEngineInner> {
self.inner.lock().unwrap() self.inner.lock().unwrap()
} }
/// Check if the provided bytes look like a serialized
/// module by the `JITEngine` implementation.
pub fn is_deserializable(bytes: &[u8]) -> bool {
bytes.starts_with(Self::MAGIC_HEADER)
}
} }
impl Engine for JITEngine { impl Engine for JITEngine {
@@ -117,28 +109,9 @@ impl Engine for JITEngine {
Ok(Arc::new(JITArtifact::new(&self, binary)?)) Ok(Arc::new(JITArtifact::new(&self, binary)?))
} }
/// Serializes a WebAssembly module
fn serialize(&self, compiled_module: &dyn Artifact) -> Result<Vec<u8>, SerializeError> {
let compiled_module = compiled_module
.downcast_ref::<JITArtifact>()
.expect("The provided module is not a JIT compiled module");
// We append the header
let mut serialized = Self::MAGIC_HEADER.to_vec();
serialized.extend(compiled_module.serialize()?);
Ok(serialized)
}
/// Deserializes a WebAssembly module /// Deserializes a WebAssembly module
fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> { fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
if !Self::is_deserializable(bytes) { Ok(Arc::new(JITArtifact::deserialize(&self, &bytes)?))
return Err(DeserializeError::Incompatible(
"The provided bytes are not wasmer-jit".to_string(),
));
}
Ok(Arc::new(JITArtifact::deserialize(
&self,
&bytes[Self::MAGIC_HEADER.len()..],
)?))
} }
} }

View File

@@ -38,6 +38,39 @@ fn to_compile_error(err: impl Error) -> CompileError {
} }
impl NativeArtifact { impl NativeArtifact {
// Mach-O header in Mac
#[allow(dead_code)]
const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
// ELF Magic header for Linux (32 bit)
#[allow(dead_code)]
const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
// ELF Magic header for Linux (64 bit)
#[allow(dead_code)]
const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
/// Check if the provided bytes look like `NativeArtifact`.
///
/// This means, if the bytes look like a shared object file in the target
/// system.
pub fn is_deserializable(bytes: &[u8]) -> bool {
cfg_if::cfg_if! {
if #[cfg(all(target_pointer_width = "64", target_os="macos"))] {
return bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64);
}
else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
return bytes.starts_with(Self::MAGIC_HEADER_ELF_64);
}
else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
return bytes.starts_with(Self::MAGIC_HEADER_ELF_32);
}
else {
false
}
}
}
/// Compile a data buffer into a `NativeArtifact`, which may then be instantiated. /// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub fn new(engine: &NativeEngine, data: &[u8]) -> Result<Self, CompileError> { pub fn new(engine: &NativeEngine, data: &[u8]) -> Result<Self, CompileError> {
@@ -384,11 +417,6 @@ impl NativeArtifact {
)) ))
} }
/// Serialize a NativeArtifact
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
Ok(std::fs::read(&self.sharedobject_path)?)
}
/// Deserialize a NativeArtifact /// Deserialize a NativeArtifact
pub unsafe fn deserialize_from_file( pub unsafe fn deserialize_from_file(
engine: &NativeEngine, engine: &NativeEngine,
@@ -466,4 +494,9 @@ impl Artifact for NativeArtifact {
fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> { fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
&self.signatures &self.signatures
} }
/// Serialize a NativeArtifact
fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
Ok(std::fs::read(&self.sharedobject_path)?)
}
} }

View File

@@ -23,18 +23,6 @@ pub struct NativeEngine {
} }
impl NativeEngine { impl NativeEngine {
// Mach-O header in Mac
#[allow(dead_code)]
const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
// ELF Magic header for Linux (32 bit)
#[allow(dead_code)]
const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
// ELF Magic header for Linux (64 bit)
#[allow(dead_code)]
const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
/// Create a new `NativeEngine` with the given config /// Create a new `NativeEngine` with the given config
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub fn new( pub fn new(
@@ -105,25 +93,6 @@ impl NativeEngine {
pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, NativeEngineInner> { pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, NativeEngineInner> {
self.inner.lock().unwrap() self.inner.lock().unwrap()
} }
/// Check if the provided bytes look like a serialized
/// module by the `Native` implementation.
pub fn is_deserializable(bytes: &[u8]) -> bool {
cfg_if::cfg_if! {
if #[cfg(all(target_pointer_width = "64", target_os="macos"))] {
return &bytes[..Self::MAGIC_HEADER_MH_CIGAM_64.len()] == Self::MAGIC_HEADER_MH_CIGAM_64;
}
else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
return &bytes[..Self::MAGIC_HEADER_ELF_64.len()] == Self::MAGIC_HEADER_ELF_64;
}
else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
return &bytes[..Self::MAGIC_HEADER_ELF_32.len()] == Self::MAGIC_HEADER_ELF_32;
}
else {
false
}
}
}
} }
impl Engine for NativeEngine { impl Engine for NativeEngine {
@@ -159,18 +128,9 @@ impl Engine for NativeEngine {
Ok(Arc::new(NativeArtifact::new(&self, binary)?)) Ok(Arc::new(NativeArtifact::new(&self, binary)?))
} }
/// Serializes a WebAssembly module
fn serialize(&self, compiled_module: &dyn Artifact) -> Result<Vec<u8>, SerializeError> {
let compiled_module = compiled_module
.downcast_ref::<NativeArtifact>()
.expect("The provided module is not a Native compiled module");
let serialized = compiled_module.serialize()?;
Ok(serialized)
}
/// Deserializes a WebAssembly module (binary content of a Shared Object file) /// Deserializes a WebAssembly module (binary content of a Shared Object file)
fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> { fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
if !Self::is_deserializable(&bytes) { if !NativeArtifact::is_deserializable(&bytes) {
return Err(DeserializeError::Incompatible( return Err(DeserializeError::Incompatible(
"The provided bytes are not in any native format Wasmer can understand".to_string(), "The provided bytes are not in any native format Wasmer can understand".to_string(),
)); ));
@@ -192,7 +152,7 @@ impl Engine for NativeEngine {
let mut buffer = [0; 5]; let mut buffer = [0; 5];
// read up to 5 bytes // read up to 5 bytes
file.read_exact(&mut buffer)?; file.read_exact(&mut buffer)?;
if !Self::is_deserializable(&buffer) { if !NativeArtifact::is_deserializable(&buffer) {
return Err(DeserializeError::Incompatible( return Err(DeserializeError::Incompatible(
"The provided bytes are not in any native format Wasmer can understand".to_string(), "The provided bytes are not in any native format Wasmer can understand".to_string(),
)); ));

View File

@@ -25,7 +25,6 @@ serde = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" } serde_bytes = { version = "0.11" }
bincode = "1.2" bincode = "1.2"
lazy_static = "1.4" lazy_static = "1.4"
downcast-rs = "1.1"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winnt", "impl-default"] } winapi = { version = "0.3", features = ["winnt", "impl-default"] }

View File

@@ -1,4 +1,4 @@
use crate::{resolve_imports, Engine, InstantiationError, Resolver, RuntimeError}; use crate::{resolve_imports, Engine, InstantiationError, Resolver, RuntimeError, SerializeError};
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use wasm_common::entity::{BoxedSlice, PrimaryMap}; use wasm_common::entity::{BoxedSlice, PrimaryMap};
@@ -11,15 +11,13 @@ use wasmer_runtime::{
InstanceHandle, MemoryPlan, ModuleInfo, TablePlan, VMFunctionBody, VMSharedSignatureIndex, InstanceHandle, MemoryPlan, ModuleInfo, TablePlan, VMFunctionBody, VMSharedSignatureIndex,
}; };
use downcast_rs::{impl_downcast, Downcast};
/// An `Artifact` is the product that the `Engine` implementation /// An `Artifact` is the product that the `Engine` implementation
/// produce and use. /// produce and use.
/// ///
/// This means, the artifact that contains the compiled information /// This means, the artifact that contains the compiled information
/// for a given modue, as well as extra information needed to run the /// for a given modue, as well as extra information needed to run the
/// module at runtime. /// module at runtime.
pub trait Artifact: Downcast { pub trait Artifact {
/// Return a pointer to the Arc module /// Return a pointer to the Arc module
fn module(&self) -> &Arc<ModuleInfo>; fn module(&self) -> &Arc<ModuleInfo>;
@@ -124,6 +122,7 @@ pub trait Artifact: Downcast {
/// Returns the associated VM signatures for this `Artifact`. /// Returns the associated VM signatures for this `Artifact`.
fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex>; fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex>;
}
impl_downcast!(Artifact); /// Serializes an artifact into bytes
fn serialize(&self) -> Result<Vec<u8>, SerializeError>;
}

View File

@@ -32,9 +32,6 @@ pub trait Engine {
/// Compile a WebAssembly binary /// Compile a WebAssembly binary
fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError>; fn compile(&self, binary: &[u8]) -> Result<Arc<dyn Artifact>, CompileError>;
/// Serializes a WebAssembly module
fn serialize(&self, compiled_module: &dyn Artifact) -> Result<Vec<u8>, SerializeError>;
/// Deserializes a WebAssembly module /// Deserializes a WebAssembly module
fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError>; fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError>;

View File

@@ -135,8 +135,8 @@ impl Run {
let contents = std::fs::read(self.path.clone())?; let contents = std::fs::read(self.path.clone())?;
#[cfg(feature = "native")] #[cfg(feature = "native")]
{ {
use wasmer_engine_native::NativeEngine; use wasmer_engine_native::{NativeArtifact, NativeEngine};
if NativeEngine::is_deserializable(&contents) { if NativeArtifact::is_deserializable(&contents) {
let tunables = Tunables::default(); let tunables = Tunables::default();
let engine = NativeEngine::headless(tunables); let engine = NativeEngine::headless(tunables);
let store = Store::new(Arc::new(engine)); let store = Store::new(Arc::new(engine));
@@ -146,7 +146,8 @@ impl Run {
} }
#[cfg(feature = "jit")] #[cfg(feature = "jit")]
{ {
if wasmer_engine_jit::JITEngine::is_deserializable(&contents) { use wasmer_engine_jit::{JITArtifact, JITEngine};
if JITArtifact::is_deserializable(&contents) {
let tunables = Tunables::default(); let tunables = Tunables::default();
let engine = JITEngine::headless(tunables); let engine = JITEngine::headless(tunables);
let store = Store::new(Arc::new(engine)); let store = Store::new(Arc::new(engine));