Skip to content

Commit a0e6b75

Browse files
committed
Switch to using imported module idents for marking version requirements
1 parent 2daf06c commit a0e6b75

File tree

10 files changed

+102
-124
lines changed

10 files changed

+102
-124
lines changed

crates/bindings-csharp/Runtime/bindings.c

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ static MonoArray* stdb_buffer_consume(Buffer buf);
100100
// return out;
101101
// }
102102

103-
__attribute__((import_module("spacetime"),
103+
#define STDB_ABI_MAJOR_VERSION 5
104+
#define STDB_IMPORT_MODULE_MINOR(minor) ("spacetime_" #STDB_ABI_MAJOR_VERSION "." #minor)
105+
#define STDB_IMPORT_MODULE STDB_IMPORT_MODULE_MINOR(0)
106+
107+
__attribute__((import_module(STDB_IMPORT_MODULE),
104108
import_name("_get_table_id"))) extern uint16_t
105109
_get_table_id(const char* name, size_t name_len, uint32_t* out);
106110

@@ -117,7 +121,7 @@ static uint32_t stdb_get_table_id(MonoString* name_) {
117121
return out;
118122
}
119123

120-
__attribute__((import_module("spacetime"),
124+
__attribute__((import_module(STDB_IMPORT_MODULE),
121125
import_name("_create_index"))) extern uint16_t
122126
_create_index(const char* index_name,
123127
size_t index_name_len,
@@ -141,7 +145,7 @@ static void stdb_create_index(MonoString* index_name_,
141145
check_result(result);
142146
}
143147

144-
__attribute__((import_module("spacetime"),
148+
__attribute__((import_module(STDB_IMPORT_MODULE),
145149
import_name("_iter_by_col_eq"))) extern uint16_t
146150
_iter_by_col_eq(uint32_t table_id,
147151
uint32_t col_id,
@@ -163,7 +167,7 @@ static MonoArray* stdb_iter_by_col_eq(uint32_t table_id,
163167
return stdb_buffer_consume(out);
164168
}
165169

166-
__attribute__((import_module("spacetime"),
170+
__attribute__((import_module(STDB_IMPORT_MODULE),
167171
import_name("_insert"))) extern uint16_t
168172
_insert(uint32_t table_id, uint8_t* row, size_t row_len);
169173

@@ -175,7 +179,7 @@ static void stdb_insert(uint32_t table_id, MonoArray* row_) {
175179
check_result(result);
176180
}
177181

178-
// __attribute__((import_module("spacetime"),
182+
// __attribute__((import_module(STDB_IMPORT_MODULE),
179183
// import_name("_delete_pk"))) extern uint16_t
180184
// _delete_pk(uint32_t table_id, const uint8_t* pk, size_t pk_len);
181185

@@ -187,7 +191,7 @@ static void stdb_insert(uint32_t table_id, MonoArray* row_) {
187191
// check_result(result);
188192
// }
189193

190-
// __attribute__((import_module("spacetime"),
194+
// __attribute__((import_module(STDB_IMPORT_MODULE),
191195
// import_name("_delete_value"))) extern uint16_t
192196
// _delete_value(uint32_t table_id, const uint8_t* row, size_t row_len);
193197

@@ -199,7 +203,7 @@ static void stdb_insert(uint32_t table_id, MonoArray* row_) {
199203
// check_result(result);
200204
// }
201205

202-
__attribute__((import_module("spacetime"),
206+
__attribute__((import_module(STDB_IMPORT_MODULE),
203207
import_name("_delete_by_col_eq"))) extern uint16_t
204208
_delete_by_col_eq(uint32_t table_id,
205209
uint32_t col_id,
@@ -221,7 +225,7 @@ static uint32_t stdb_delete_by_col_eq(uint32_t table_id,
221225
return out;
222226
}
223227

224-
// __attribute__((import_module("spacetime"),
228+
// __attribute__((import_module(STDB_IMPORT_MODULE),
225229
// import_name("_delete_range"))) extern uint16_t
226230
// _delete_range(uint32_t table_id,
227231
// uint32_t col_id,
@@ -248,7 +252,7 @@ static uint32_t stdb_delete_by_col_eq(uint32_t table_id,
248252
// return out;
249253
// }
250254

251-
__attribute__((import_module("spacetime"),
255+
__attribute__((import_module(STDB_IMPORT_MODULE),
252256
import_name("_iter_start"))) extern uint16_t
253257
_iter_start(uint32_t table_id, BufferIter* out);
254258

@@ -258,7 +262,7 @@ static void stdb_iter_start(uint32_t table_id, BufferIter* iter) {
258262
check_result(result);
259263
}
260264

261-
__attribute__((import_module("spacetime"),
265+
__attribute__((import_module(STDB_IMPORT_MODULE),
262266
import_name("_iter_start_filtered"))) extern uint16_t
263267
_iter_start_filtered(uint32_t table_id,
264268
const uint8_t* filter,
@@ -276,7 +280,7 @@ static void stdb_iter_start_filtered(uint32_t table_id,
276280
check_result(result);
277281
}
278282

279-
__attribute__((import_module("spacetime"),
283+
__attribute__((import_module(STDB_IMPORT_MODULE),
280284
import_name("_iter_next"))) extern uint16_t
281285
_iter_next(BufferIter iter, Buffer* out);
282286

@@ -289,7 +293,7 @@ static MonoArray* stdb_iter_next(BufferIter iter) {
289293
return stdb_buffer_consume(out);
290294
}
291295

292-
__attribute__((import_module("spacetime"),
296+
__attribute__((import_module(STDB_IMPORT_MODULE),
293297
import_name("_iter_drop"))) extern uint16_t
294298
_iter_drop(BufferIter iter);
295299

@@ -307,7 +311,7 @@ static void stdb_iter_drop(BufferIter* iter) {
307311
check_result(result);
308312
}
309313

310-
__attribute__((import_module("spacetime"),
314+
__attribute__((import_module(STDB_IMPORT_MODULE),
311315
import_name("_console_log"))) extern void
312316
_console_log(uint8_t level,
313317
const char* target,
@@ -335,7 +339,7 @@ static void stdb_console_log(MonoString* text_,
335339
free_string(filename);
336340
}
337341

338-
__attribute__((import_module("spacetime"),
342+
__attribute__((import_module(STDB_IMPORT_MODULE),
339343
import_name("_schedule_reducer"))) extern void
340344
_schedule_reducer(const char* name,
341345
size_t name_len,
@@ -360,19 +364,19 @@ static void stdb_schedule_reducer(
360364
free_string(name);
361365
}
362366

363-
__attribute__((import_module("spacetime"),
367+
__attribute__((import_module(STDB_IMPORT_MODULE),
364368
import_name("_cancel_reducer"))) extern void
365369
_cancel_reducer(ScheduleToken token);
366370

367371
static void stdb_cancel_reducer(ScheduleToken* token) {
368372
_cancel_reducer(*token);
369373
}
370374

371-
__attribute__((import_module("spacetime"),
375+
__attribute__((import_module(STDB_IMPORT_MODULE),
372376
import_name("_buffer_len"))) extern size_t
373377
_buffer_len(Buffer buf);
374378

375-
__attribute__((import_module("spacetime"),
379+
__attribute__((import_module(STDB_IMPORT_MODULE),
376380
import_name("_buffer_consume"))) extern void
377381
_buffer_consume(Buffer buf, uint8_t* into, size_t len);
378382

@@ -387,7 +391,7 @@ static MonoArray* stdb_buffer_consume(Buffer buf) {
387391
return result;
388392
}
389393

390-
__attribute__((import_module("spacetime"),
394+
__attribute__((import_module(STDB_IMPORT_MODULE),
391395
import_name("_buffer_alloc"))) extern Buffer
392396
_buffer_alloc(const uint8_t* data, size_t data_len);
393397

crates/bindings-sys/src/lib.rs

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,15 @@ use std::ptr;
1313

1414
use alloc::boxed::Box;
1515

16-
/// The current version of the ABI.
17-
///
18-
/// Exported as `SPACETIME_ABI_VERSION`, a `u32` WASM global.
19-
/// If this global contains an address into linear memory at which the version is stored,
20-
/// then a WASM global named `SPACETIME_ABI_VERSION_IS_ADDR` is also be exported.
21-
///
22-
/// In rust this looks like:
23-
/// ```rust,ignore
24-
/// #[no_mangle]
25-
/// static SPACETIME_ABI_VERSION: u32 = _; // right now, rust `static`s always export as an address.
26-
/// #[no_mangle]
27-
/// static SPACETIME_ABI_VERSION_IS_ADDR: () = ();
28-
/// ```
29-
///
30-
/// The (big-endian) first 2 bytes constitute the major version (`A`) of the ABI,
31-
/// and the last 2 bytes constitute the minor version (`B`).
32-
///
33-
/// The semantics of a version number `A.B` is that a host implementing version `A.B`
34-
/// can run a module declaring `X.Y` if and only if `X == A && Y <= B`.
35-
/// So, the minor version is intended for backwards-compatible changes, e.g. adding a new function,
36-
/// and the major version is for fully breaking changes.
37-
pub const ABI_VERSION: u32 = 0x0004_0000;
38-
3916
/// Provides a raw set of sys calls which abstractions can be built atop of.
4017
pub mod raw {
4118
use core::mem::ManuallyDrop;
4219

43-
#[link(wasm_import_module = "spacetime")]
20+
// this module identifier determines the abi version that modules built with this crate depend
21+
// on. Any non-breaking additions to the abi surface should be put in a new `extern {}` block
22+
// with a module identifier with a minor version 1 above the previous highest minor version.
23+
// For breaking changes, all functions should be moved into one new `spacetime_X.0` block.
24+
#[link(wasm_import_module = "spacetime_5.0")]
4425
extern "C" {
4526
/*
4627
/// Create a table with `name`, a UTF-8 slice in WASM memory lasting `name_len` bytes,

crates/bindings/src/lib.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,6 @@ pub use log;
3737

3838
pub type Result<T = (), E = Errno> = core::result::Result<T, E>;
3939

40-
#[no_mangle]
41-
static SPACETIME_ABI_VERSION: u32 = {
42-
assert!(spacetimedb_lib::MODULE_ABI_VERSION.to_u32() == sys::ABI_VERSION);
43-
sys::ABI_VERSION
44-
};
45-
#[no_mangle]
46-
static SPACETIME_ABI_VERSION_IS_ADDR: () = ();
47-
4840
/// A context that any reducer is provided with.
4941
#[non_exhaustive]
5042
#[derive(Copy, Clone)]

crates/cli/src/subcommands/generate/mod.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use convert_case::{Case, Casing};
77
use duct::cmd;
88
use spacetimedb_lib::sats::{AlgebraicType, Typespace};
99
use spacetimedb_lib::{bsatn, MiscModuleExport, ModuleDef, ReducerDef, TableDef, TypeAlias};
10-
use wasmtime::{AsContext, Caller, ExternType};
10+
use wasmtime::{AsContext, Caller};
1111

1212
mod code_indenter;
1313
pub mod csharp;
@@ -347,18 +347,9 @@ fn extract_descriptions(wasm_file: &Path) -> anyhow::Result<ModuleDef> {
347347
};
348348
let mut store = wasmtime::Store::new(&engine, ctx);
349349
let mut linker = wasmtime::Linker::new(&engine);
350-
linker.allow_shadowing(true);
351-
for imp in module.imports() {
352-
if let ExternType::Func(func_type) = imp.ty() {
353-
linker
354-
.func_new(imp.module(), imp.name(), func_type, |_, _, _| {
355-
anyhow::bail!("don't call me!!")
356-
})
357-
.unwrap();
358-
}
359-
}
350+
linker.allow_shadowing(true).define_unknown_imports_as_traps(&module)?;
360351
linker.func_wrap(
361-
"spacetime",
352+
"spacetime_5.0",
362353
"_console_log",
363354
|caller: Caller<'_, WasmCtx>,
364355
_level: u32,
@@ -378,7 +369,7 @@ fn extract_descriptions(wasm_file: &Path) -> anyhow::Result<ModuleDef> {
378369
}
379370
},
380371
)?;
381-
linker.func_wrap("spacetime", "_buffer_alloc", WasmCtx::buffer_alloc)?;
372+
linker.func_wrap("spacetime_5.0", "_buffer_alloc", WasmCtx::buffer_alloc)?;
382373
let instance = linker.instantiate(&mut store, &module)?;
383374
let memory = Memory {
384375
mem: instance.get_memory(&mut store, "memory").unwrap(),

crates/core/src/host/wasm_common.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod abi;
12
pub mod module_host_actor;
23

34
use std::time::Instant;
@@ -19,9 +20,6 @@ pub const UPDATE_DUNDER: &str = "__update__";
1920
pub const IDENTITY_CONNECTED_DUNDER: &str = "__identity_connected__";
2021
pub const IDENTITY_DISCONNECTED_DUNDER: &str = "__identity_disconnected__";
2122

22-
pub const STDB_ABI_SYM: &str = "SPACETIME_ABI_VERSION";
23-
pub const STDB_ABI_IS_ADDR_SYM: &str = "SPACETIME_ABI_VERSION_IS_ADDR";
24-
2523
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
2624
#[allow(unused)]
2725
pub enum WasmType {
@@ -228,6 +226,7 @@ impl FuncNames {
228226
pub enum ModuleCreationError {
229227
WasmCompileError(anyhow::Error),
230228
Init(#[from] module_host_actor::InitializationError),
229+
Abi(#[from] abi::AbiVersionError),
231230
}
232231

233232
pub trait ResourceIndex {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
pub use spacetimedb_lib::VersionTuple;
2+
3+
const MODULE_PREFIX: &str = "spacetime_";
4+
5+
pub fn determine_spacetime_abi<I>(
6+
imports: impl IntoIterator<Item = I>,
7+
get_module: impl Fn(&I) -> &str,
8+
) -> Result<Option<VersionTuple>, AbiVersionError> {
9+
let it = imports.into_iter().filter_map(|imp| {
10+
let s = get_module(&imp);
11+
let err = || AbiVersionError::Parse { module: s.to_owned() };
12+
s.strip_prefix(MODULE_PREFIX).map(|ver| {
13+
let (major, minor) = ver.split_once('.').ok_or_else(err)?;
14+
let (major, minor) = Option::zip(major.parse().ok(), minor.parse().ok()).ok_or_else(err)?;
15+
Ok(VersionTuple { major, minor })
16+
})
17+
});
18+
itertools::process_results(it, |mut it| try_reduce(&mut it, refine_ver_req))?
19+
}
20+
21+
// TODO: replace with Iterator::try_reduce once stabilized
22+
fn try_reduce<I: Iterator, E>(
23+
it: &mut I,
24+
f: impl FnMut(I::Item, I::Item) -> Result<I::Item, E>,
25+
) -> Result<Option<I::Item>, E> {
26+
let Some(first) = it.next() else { return Ok(None) };
27+
it.try_fold(first, f).map(Some)
28+
}
29+
30+
fn refine_ver_req(ver: VersionTuple, new: VersionTuple) -> Result<VersionTuple, AbiVersionError> {
31+
if ver.major != new.major {
32+
Err(AbiVersionError::MultipleMajor(ver.major, new.major))
33+
} else {
34+
Ok(Ord::max(ver, new))
35+
}
36+
}
37+
38+
pub fn verify_supported(implements: VersionTuple, got: VersionTuple) -> Result<(), AbiVersionError> {
39+
if implements.supports(got) {
40+
Ok(())
41+
} else {
42+
Err(AbiVersionError::UnsupportedVersion { implements, got })
43+
}
44+
}
45+
46+
#[derive(thiserror::Error, Debug)]
47+
pub enum AbiVersionError {
48+
#[error("import module {module:?} has malformed version string")]
49+
Parse { module: String },
50+
#[error("module cannot depend on both major version {0} and major version {1}")]
51+
MultipleMajor(u16, u16),
52+
#[error("abi version {got} is not supported (host implements {implements})")]
53+
UnsupportedVersion { got: VersionTuple, implements: VersionTuple },
54+
}

crates/core/src/host/wasm_common/module_host_actor.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bytes::Bytes;
1111
use nonempty::NonEmpty;
1212
use spacetimedb_lib::buffer::DecodeError;
1313
use spacetimedb_lib::identity::AuthCtx;
14-
use spacetimedb_lib::{bsatn, Address, IndexType, ModuleDef, VersionTuple};
14+
use spacetimedb_lib::{bsatn, Address, IndexType, ModuleDef};
1515
use spacetimedb_vm::expr::CrudExpr;
1616

1717
use crate::client::ClientConnectionSender;
@@ -100,20 +100,8 @@ pub(crate) struct WasmModuleHostActor<T: WasmModule> {
100100
energy_monitor: Arc<dyn EnergyMonitor>,
101101
}
102102

103-
#[derive(thiserror::Error, Debug)]
104-
pub enum AbiVersionError {
105-
#[error("module doesn't indicate spacetime ABI version")]
106-
NoVersion,
107-
#[error("abi version is malformed somehow (out-of-bounds, etc)")]
108-
Malformed,
109-
#[error("abi version {got} is not supported (host implements {implement})")]
110-
UnsupportedVersion { got: VersionTuple, implement: VersionTuple },
111-
}
112-
113103
#[derive(thiserror::Error, Debug)]
114104
pub enum InitializationError {
115-
#[error(transparent)]
116-
Abi(#[from] AbiVersionError),
117105
#[error(transparent)]
118106
Validation(#[from] ValidationError),
119107
#[error("setup function returned an error: {0}")]

crates/core/src/host/wasmer/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod wasmer_module;
1515
use wasmer_module::WasmerModule;
1616

1717
use super::scheduler::Scheduler;
18-
use super::wasm_common::{module_host_actor::WasmModuleHostActor, ModuleCreationError};
18+
use super::wasm_common::{abi, module_host_actor::WasmModuleHostActor, ModuleCreationError};
1919
use super::{EnergyMonitor, EnergyQuanta};
2020

2121
pub fn make_actor(
@@ -47,6 +47,12 @@ pub fn make_actor(
4747

4848
let module = Module::new(&engine, program_bytes).map_err(|e| ModuleCreationError::WasmCompileError(e.into()))?;
4949

50+
let abi = abi::determine_spacetime_abi(module.imports().functions(), wasmer::ImportType::module)?;
51+
52+
if let Some(abi) = abi {
53+
abi::verify_supported(WasmerModule::IMPLEMENTED_ABI, abi)?;
54+
}
55+
5056
let module = WasmerModule::new(module, engine);
5157

5258
WasmModuleHostActor::new(dbic, module_hash, module, scheduler, energy_monitor).map_err(Into::into)

0 commit comments

Comments
 (0)