Skip to content

Commit 2abf5bf

Browse files
authored
linera_execution::wasm::wasmer: don't serialize JS modules (#2758)
We need to do this on native, where we use two different runtimes for compilation and running, but we don't need to (and indeed can't) do it on the Web. Failure to do this causes Wasmer to issue the very surprising error: RuntimeError: call stack exhausted during Operation(0)
1 parent ed6f6ba commit 2abf5bf

File tree

4 files changed

+32
-25
lines changed

4 files changed

+32
-25
lines changed

Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

linera-execution/Cargo.toml

-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ fs = ["tokio/fs"]
1717
metrics = ["prometheus", "linera-views/metrics"]
1818
unstable-oracles = []
1919
wasmer = [
20-
"bytes",
2120
"dep:wasmer",
2221
"linera-witty/wasmer",
2322
"wasm-encoder",
@@ -37,7 +36,6 @@ anyhow.workspace = true
3736
async-graphql.workspace = true
3837
async-trait.workspace = true
3938
bcs.workspace = true
40-
bytes = { workspace = true, optional = true }
4139
cfg-if.workspace = true
4240
clap.workspace = true
4341
custom_debug_derive.workspace = true

linera-execution/src/wasm/wasmer.rs

+32-21
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
66
use std::{marker::Unpin, sync::LazyLock};
77

8-
use bytes::Bytes;
98
use linera_base::data_types::Bytecode;
109
use linera_witty::{
1110
wasmer::{EntrypointInstance, InstanceBuilder},
1211
ExportTo,
1312
};
1413
use tokio::sync::Mutex;
1514
use wasm_instrument::{gas_metering, parity_wasm};
16-
use wasmer::{Engine, Module, Store};
1715

1816
use super::{
1917
module_cache::ModuleCache,
@@ -27,7 +25,7 @@ use crate::{
2725
};
2826

2927
/// An [`Engine`] instance configured to run application services.
30-
static SERVICE_ENGINE: LazyLock<Engine> = LazyLock::new(|| {
28+
static SERVICE_ENGINE: LazyLock<wasmer::Engine> = LazyLock::new(|| {
3129
#[cfg(web)]
3230
{
3331
wasmer::Engine::default()
@@ -39,12 +37,12 @@ static SERVICE_ENGINE: LazyLock<Engine> = LazyLock::new(|| {
3937
}
4038
});
4139

42-
/// A cache of compiled contract modules, with their respective [`Engine`] instances.
40+
/// A cache of compiled contract modules, with their respective [`wasmer::Engine`] instances.
4341
static CONTRACT_CACHE: LazyLock<Mutex<ModuleCache<CachedContractModule>>> =
4442
LazyLock::new(Mutex::default);
4543

4644
/// A cache of compiled service modules.
47-
static SERVICE_CACHE: LazyLock<Mutex<ModuleCache<Module>>> = LazyLock::new(Mutex::default);
45+
static SERVICE_CACHE: LazyLock<Mutex<ModuleCache<wasmer::Module>>> = LazyLock::new(Mutex::default);
4846

4947
/// Type representing a running [Wasmer](https://wasmer.io/) contract.
5048
pub(crate) struct WasmerContractInstance<Runtime> {
@@ -77,8 +75,8 @@ where
7775
{
7876
/// Prepares a runtime instance to call into the Wasm contract.
7977
pub fn prepare(
80-
contract_engine: Engine,
81-
contract_module: &Module,
78+
contract_engine: wasmer::Engine,
79+
contract_module: &wasmer::Module,
8280
runtime: Runtime,
8381
) -> Result<Self, WasmExecutionError> {
8482
let system_api_data = SystemApiData::new(runtime);
@@ -99,7 +97,7 @@ impl WasmServiceModule {
9997
let mut service_cache = SERVICE_CACHE.lock().await;
10098
let module = service_cache
10199
.get_or_insert_with(service_bytecode, |bytecode| {
102-
Module::new(&*SERVICE_ENGINE, bytecode).map_err(anyhow::Error::from)
100+
wasmer::Module::new(&*SERVICE_ENGINE, bytecode).map_err(anyhow::Error::from)
103101
})
104102
.map_err(WasmExecutionError::LoadServiceModule)?;
105103
Ok(WasmServiceModule::Wasmer { module })
@@ -111,7 +109,10 @@ where
111109
Runtime: ServiceRuntime + WriteBatch + Clone + Unpin + 'static,
112110
{
113111
/// Prepares a runtime instance to call into the Wasm service.
114-
pub fn prepare(service_module: &Module, runtime: Runtime) -> Result<Self, WasmExecutionError> {
112+
pub fn prepare(
113+
service_module: &wasmer::Module,
114+
runtime: Runtime,
115+
) -> Result<Self, WasmExecutionError> {
115116
let system_api_data = SystemApiData::new(runtime);
116117
let mut instance_builder = InstanceBuilder::new(SERVICE_ENGINE.clone(), system_api_data);
117118

@@ -197,9 +198,9 @@ impl From<wasmer::RuntimeError> for ExecutionError {
197198
}
198199

199200
/// Serialized bytes of a compiled contract bytecode.
200-
pub struct CachedContractModule {
201-
compiled_bytecode: Bytes,
202-
}
201+
// Cloning `Module`s is cheap.
202+
#[derive(Clone)]
203+
pub struct CachedContractModule(wasmer::Module);
203204

204205
pub fn add_metering(bytecode: Bytecode) -> anyhow::Result<Bytecode> {
205206
struct WasmtimeRules;
@@ -246,16 +247,15 @@ pub fn add_metering(bytecode: Bytecode) -> anyhow::Result<Bytecode> {
246247
impl CachedContractModule {
247248
/// Creates a new [`CachedContractModule`] by compiling a `contract_bytecode`.
248249
pub fn new(contract_bytecode: Bytecode) -> Result<Self, anyhow::Error> {
249-
let module = Module::new(
250+
let module = wasmer::Module::new(
250251
&Self::create_compilation_engine(),
251252
add_metering(contract_bytecode)?,
252253
)?;
253-
let compiled_bytecode = module.serialize()?;
254-
Ok(CachedContractModule { compiled_bytecode })
254+
Ok(CachedContractModule(module))
255255
}
256256

257257
/// Creates a new [`Engine`] to compile a contract bytecode.
258-
fn create_compilation_engine() -> Engine {
258+
fn create_compilation_engine() -> wasmer::Engine {
259259
#[cfg(not(web))]
260260
{
261261
let mut compiler_config = wasmer_compiler_singlepass::Singlepass::default();
@@ -269,10 +269,21 @@ impl CachedContractModule {
269269
}
270270

271271
/// Creates a [`Module`] from a compiled contract using a headless [`Engine`].
272-
pub fn create_execution_instance(&self) -> Result<(Engine, Module), anyhow::Error> {
273-
let engine = Engine::default();
274-
let store = Store::new(engine.clone());
275-
let module = unsafe { Module::deserialize(&store, &*self.compiled_bytecode) }?;
276-
Ok((engine, module))
272+
pub fn create_execution_instance(
273+
&self,
274+
) -> Result<(wasmer::Engine, wasmer::Module), anyhow::Error> {
275+
#[cfg(web)]
276+
{
277+
Ok((wasmer::Engine::default(), self.0.clone()))
278+
}
279+
280+
#[cfg(not(web))]
281+
{
282+
let engine = wasmer::Engine::default();
283+
let store = wasmer::Store::new(engine.clone());
284+
let bytes = self.0.serialize()?;
285+
let module = unsafe { wasmer::Module::deserialize(&store, bytes) }?;
286+
Ok((engine, module))
287+
}
277288
}
278289
}

0 commit comments

Comments
 (0)