diff --git a/crates/core/src/host/module_common.rs b/crates/core/src/host/module_common.rs index b488de987e0..56b49227910 100644 --- a/crates/core/src/host/module_common.rs +++ b/crates/core/src/host/module_common.rs @@ -3,11 +3,7 @@ use crate::{ energy::EnergyMonitor, - host::{ - module_host::ModuleInfo, - wasm_common::{module_host_actor::DescribeError, DESCRIBE_MODULE_DUNDER}, - Scheduler, - }, + host::{module_host::ModuleInfo, wasm_common::module_host_actor::DescribeError, Scheduler}, module_host_context::ModuleCreationContextLimited, replica_context::ReplicaContext, }; @@ -90,11 +86,10 @@ impl ModuleCommon { /// Runs the describer of modules in `run` and does some logging around it. pub(crate) fn run_describer( + describer_func_name: &str, log_traceback: impl Copy + FnOnce(&str, &str, &anyhow::Error), run: impl FnOnce() -> anyhow::Result, ) -> Result { - let describer_func_name = DESCRIBE_MODULE_DUNDER; - let start = std::time::Instant::now(); log::trace!("Start describer \"{describer_func_name}\"..."); diff --git a/crates/core/src/host/v8/mod.rs b/crates/core/src/host/v8/mod.rs index 133def696ce..8b8b2dd8f9d 100644 --- a/crates/core/src/host/v8/mod.rs +++ b/crates/core/src/host/v8/mod.rs @@ -25,7 +25,7 @@ use crate::host::wasm_common::module_host_actor::{ InstanceOp, ProcedureExecuteResult, ProcedureOp, ReducerExecuteResult, ReducerOp, ViewExecuteResult, ViewOp, WasmInstance, }; -use crate::host::wasm_common::{RowIters, TimingSpanSet}; +use crate::host::wasm_common::{RowIters, TimingSpanSet, DESCRIBE_MODULE_DUNDER}; use crate::host::{ModuleHost, ReducerCallError, ReducerCallResult, Scheduler}; use crate::module_host_context::{ModuleCreationContext, ModuleCreationContextLimited}; use crate::replica_context::ReplicaContext; @@ -862,6 +862,8 @@ fn extract_description<'scope>( replica_ctx: &ReplicaContext, ) -> Result { run_describer( + //TODO(shub): make it work with `DESCRIBE_MODULE_DUNDER_V10` + DESCRIBE_MODULE_DUNDER, |a, b, c| log_traceback(replica_ctx, a, b, c), || { catch_exception(scope, |scope| { diff --git a/crates/core/src/host/wasm_common.rs b/crates/core/src/host/wasm_common.rs index ad4aa989707..dd77ca0427d 100644 --- a/crates/core/src/host/wasm_common.rs +++ b/crates/core/src/host/wasm_common.rs @@ -10,6 +10,7 @@ use super::{scheduler::ScheduleError, AbiCall}; use crate::error::{DBError, DatastoreError, IndexError, NodesError}; use spacetimedb_primitives::errno; use spacetimedb_sats::typespace::TypeRefError; +use spacetimedb_schema::def::RawModuleDefVersion; use spacetimedb_table::table::UniqueConstraintViolation; pub const CALL_REDUCER_DUNDER: &str = "__call_reducer__"; @@ -20,13 +21,52 @@ pub const CALL_VIEW_DUNDER: &str = "__call_view__"; pub const CALL_VIEW_ANON_DUNDER: &str = "__call_view_anon__"; +/// Name of the function that modules export to the host to describe themselves. +/// +/// Used by module definitions with versions up to +/// [`RawModuleDefVersion::V9OrEarlier`]. pub const DESCRIBE_MODULE_DUNDER: &str = "__describe_module__"; +/// Versioned variant of [`DESCRIBE_MODULE_DUNDER`] for +/// [`RawModuleDefVersion::V10`]. +pub const DESCRIBE_MODULE_DUNDER_V10: &str = "__describe_module_v10__"; + /// functions with this prefix run prior to __setup__, initializing global variables and the like pub const PREINIT_DUNDER: &str = "__preinit__"; /// initializes the user code in the module. fallible pub const SETUP_DUNDER: &str = "__setup__"; +/// Detects the [`RawModuleDefVersion`] of a module by checking for the presence +/// of a known describe function export. +pub fn detect_raw_def_version(module: &M) -> Result +where + M: module_host_actor::WasmModule, +{ + if module.get_export(DESCRIBE_MODULE_DUNDER).is_some() { + Ok(RawModuleDefVersion::V9OrEarlier) + } else if module.get_export(DESCRIBE_MODULE_DUNDER_V10).is_some() { + Ok(RawModuleDefVersion::V10) + } else { + Err(module_host_actor::DescribeError::Signature(anyhow::anyhow!( + "module does not export a {} or {} function", + DESCRIBE_MODULE_DUNDER, + DESCRIBE_MODULE_DUNDER_V10 + ))) + } +} +/// Returns the describe dunder symbol for a given module version. +pub const fn describe_dunder(version: RawModuleDefVersion) -> &'static str { + match version { + RawModuleDefVersion::V9OrEarlier => DESCRIBE_MODULE_DUNDER, + RawModuleDefVersion::V10 => DESCRIBE_MODULE_DUNDER_V10, + } +} + +/// Returns all known describe dunder symbols. +pub const fn describe_dunders() -> &'static [&'static str] { + &[DESCRIBE_MODULE_DUNDER, DESCRIBE_MODULE_DUNDER_V10] +} + #[derive(Debug, Clone)] #[allow(unused)] pub enum WasmType { @@ -229,7 +269,7 @@ impl FuncNames { } Ok(()) } - pub fn check_required(get_export: F) -> Result<(), ValidationError> + pub fn check_required(raw_def_ver: RawModuleDefVersion, get_export: F) -> Result<(), ValidationError> where F: Fn(&str) -> Option, T: FuncSigLike, @@ -243,8 +283,9 @@ impl FuncNames { let sig = get_func(CALL_REDUCER_DUNDER)?; Self::validate_signature("call_reducer", &sig, CALL_REDUCER_DUNDER, CALL_REDUCER_SIG)?; - let sig = get_func(DESCRIBE_MODULE_DUNDER)?; - Self::validate_signature("describe_module", &sig, DESCRIBE_MODULE_DUNDER, DESCRIBE_MODULE_SIG)?; + let describe_dunder = describe_dunder(raw_def_ver); + let sig = get_func(describe_dunder)?; + Self::validate_signature("describe_module", &sig, describe_dunder, DESCRIBE_MODULE_SIG)?; Ok(()) } diff --git a/crates/core/src/host/wasm_common/module_host_actor.rs b/crates/core/src/host/wasm_common/module_host_actor.rs index e1534c539c4..27377fb0fa5 100644 --- a/crates/core/src/host/wasm_common/module_host_actor.rs +++ b/crates/core/src/host/wasm_common/module_host_actor.rs @@ -280,7 +280,7 @@ impl From for InitializationError { #[derive(thiserror::Error, Debug)] pub enum DescribeError { - #[error("bad signature for descriptor function: {0}")] + #[error("failed to call descriptor function: invalid signature or function does not exist: {0}")] Signature(anyhow::Error), #[error("error when preparing descriptor function: {0}")] Setup(anyhow::Error), @@ -301,8 +301,9 @@ impl WasmModuleHostActor { mcc.program_hash, ); + let raw_def_version = detect_raw_def_version(&module)?; let func_names = { - FuncNames::check_required(|name| module.get_export(name))?; + FuncNames::check_required(raw_def_version, |name| module.get_export(name))?; let mut func_names = FuncNames::default(); module.for_each_export(|sym, ty| func_names.update_from_general(sym, ty))?; func_names.preinits.sort_unstable(); diff --git a/crates/core/src/host/wasmtime/wasmtime_module.rs b/crates/core/src/host/wasmtime/wasmtime_module.rs index 7253067c062..f7bbe15db90 100644 --- a/crates/core/src/host/wasmtime/wasmtime_module.rs +++ b/crates/core/src/host/wasmtime/wasmtime_module.rs @@ -352,16 +352,27 @@ pub struct WasmtimeInstance { impl module_host_actor::WasmInstance for WasmtimeInstance { fn extract_descriptions(&mut self) -> Result { - let describer_func_name = DESCRIBE_MODULE_DUNDER; + let mut describer_res = None; + for &describe_func_name in describe_dunders() { + match self + .instance + .get_typed_func::(&mut self.store, describe_func_name) + { + Ok(describer) => { + describer_res = Some(Ok((describe_func_name, describer))); + break; + } + Err(e) => describer_res = Some(Err(DescribeError::Signature(e))), + } + } - let describer = self - .instance - .get_typed_func::(&mut self.store, describer_func_name) - .map_err(DescribeError::Signature)?; + let (describer_func_name, describer) = describer_res + .transpose()? + .ok_or_else(|| DescribeError::Signature(anyhow::anyhow!("no describer function found")))?; let sink = self.store.data_mut().setup_standard_bytes_sink(); - run_describer(log_traceback, || { + run_describer(describer_func_name, log_traceback, || { call_sync_typed_func(&describer, &mut self.store, sink) })?; diff --git a/crates/schema/src/def.rs b/crates/schema/src/def.rs index 84a84d66307..55ddd24ac3c 100644 --- a/crates/schema/src/def.rs +++ b/crates/schema/src/def.rs @@ -146,7 +146,7 @@ pub struct ModuleDef { raw_module_def_version: RawModuleDefVersion, } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] pub enum RawModuleDefVersion { /// Represents [`RawModuleDefV9`] and earlier. V9OrEarlier,