|
1 | | -use std::mem; |
2 | | -use std::path::Path; |
3 | | - |
4 | | -use anyhow::Context; |
5 | | -use spacetimedb_lib::{bsatn, RawModuleDefV8}; |
6 | | -use spacetimedb_lib::{RawModuleDef, MODULE_ABI_MAJOR_VERSION}; |
7 | | -use spacetimedb_primitives::errno; |
8 | 1 | use spacetimedb_schema::def::{ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; |
9 | 2 | use spacetimedb_schema::identifier::Identifier; |
10 | | -use wasmtime::{Caller, StoreContextMut}; |
11 | 3 |
|
12 | 4 | mod code_indenter; |
13 | 5 | pub mod csharp; |
@@ -49,115 +41,3 @@ pub trait Lang { |
49 | 41 | fn generate_reducer(&self, module: &ModuleDef, reducer: &ReducerDef) -> String; |
50 | 42 | fn generate_globals(&self, module: &ModuleDef) -> Vec<(String, String)>; |
51 | 43 | } |
52 | | - |
53 | | -pub fn extract_descriptions(wasm_file: &Path) -> anyhow::Result<RawModuleDef> { |
54 | | - let module = compile_wasm(wasm_file)?; |
55 | | - extract_descriptions_from_module(module) |
56 | | -} |
57 | | - |
58 | | -pub fn compile_wasm(wasm_file: &Path) -> anyhow::Result<wasmtime::Module> { |
59 | | - wasmtime::Module::from_file(&wasmtime::Engine::default(), wasm_file) |
60 | | -} |
61 | | - |
62 | | -#[allow(clippy::disallowed_macros)] |
63 | | -pub fn extract_descriptions_from_module(module: wasmtime::Module) -> anyhow::Result<RawModuleDef> { |
64 | | - let engine = module.engine(); |
65 | | - let ctx = WasmCtx { |
66 | | - mem: None, |
67 | | - sink: Vec::new(), |
68 | | - }; |
69 | | - let mut store = wasmtime::Store::new(engine, ctx); |
70 | | - let mut linker = wasmtime::Linker::new(engine); |
71 | | - linker.allow_shadowing(true).define_unknown_imports_as_traps(&module)?; |
72 | | - let module_name = &*format!("spacetime_{MODULE_ABI_MAJOR_VERSION}.0"); |
73 | | - linker.func_wrap( |
74 | | - module_name, |
75 | | - "console_log", |
76 | | - |mut caller: Caller<'_, WasmCtx>, |
77 | | - _level: u32, |
78 | | - _target_ptr: u32, |
79 | | - _target_len: u32, |
80 | | - _filename_ptr: u32, |
81 | | - _filename_len: u32, |
82 | | - _line_number: u32, |
83 | | - message_ptr: u32, |
84 | | - message_len: u32| { |
85 | | - let (mem, _) = WasmCtx::mem_env(&mut caller); |
86 | | - let slice = deref_slice(mem, message_ptr, message_len).unwrap(); |
87 | | - println!("from wasm: {}", String::from_utf8_lossy(slice)); |
88 | | - }, |
89 | | - )?; |
90 | | - linker.func_wrap(module_name, "bytes_sink_write", WasmCtx::bytes_sink_write)?; |
91 | | - let instance = linker.instantiate(&mut store, &module)?; |
92 | | - let memory = instance.get_memory(&mut store, "memory").context("no memory export")?; |
93 | | - store.data_mut().mem = Some(memory); |
94 | | - |
95 | | - let mut preinits = instance |
96 | | - .exports(&mut store) |
97 | | - .filter_map(|exp| Some((exp.name().strip_prefix("__preinit__")?.to_owned(), exp.into_func()?))) |
98 | | - .collect::<Vec<_>>(); |
99 | | - preinits.sort_by(|(a, _), (b, _)| a.cmp(b)); |
100 | | - for (_, func) in preinits { |
101 | | - func.typed(&store)?.call(&mut store, ())? |
102 | | - } |
103 | | - let module: RawModuleDef = match instance.get_func(&mut store, "__describe_module__") { |
104 | | - Some(f) => { |
105 | | - store.data_mut().sink = Vec::new(); |
106 | | - f.typed::<u32, ()>(&store)?.call(&mut store, 1)?; |
107 | | - let buf = mem::take(&mut store.data_mut().sink); |
108 | | - bsatn::from_slice(&buf)? |
109 | | - } |
110 | | - // TODO: shouldn't we return an error here? |
111 | | - None => RawModuleDef::V8BackCompat(RawModuleDefV8::default()), |
112 | | - }; |
113 | | - Ok(module) |
114 | | -} |
115 | | - |
116 | | -struct WasmCtx { |
117 | | - mem: Option<wasmtime::Memory>, |
118 | | - sink: Vec<u8>, |
119 | | -} |
120 | | - |
121 | | -fn deref_slice(mem: &[u8], offset: u32, len: u32) -> anyhow::Result<&[u8]> { |
122 | | - anyhow::ensure!(offset != 0, "ptr is null"); |
123 | | - mem.get(offset as usize..) |
124 | | - .and_then(|s| s.get(..len as usize)) |
125 | | - .context("pointer out of bounds") |
126 | | -} |
127 | | - |
128 | | -fn read_u32(mem: &[u8], offset: u32) -> anyhow::Result<u32> { |
129 | | - Ok(u32::from_le_bytes(deref_slice(mem, offset, 4)?.try_into().unwrap())) |
130 | | -} |
131 | | - |
132 | | -impl WasmCtx { |
133 | | - pub fn get_mem(&self) -> wasmtime::Memory { |
134 | | - self.mem.expect("Initialized memory") |
135 | | - } |
136 | | - |
137 | | - fn mem_env<'a>(ctx: impl Into<StoreContextMut<'a, Self>>) -> (&'a mut [u8], &'a mut Self) { |
138 | | - let ctx = ctx.into(); |
139 | | - let mem = ctx.data().get_mem(); |
140 | | - mem.data_and_store_mut(ctx) |
141 | | - } |
142 | | - |
143 | | - pub fn bytes_sink_write( |
144 | | - mut caller: Caller<'_, Self>, |
145 | | - sink_handle: u32, |
146 | | - buffer_ptr: u32, |
147 | | - buffer_len_ptr: u32, |
148 | | - ) -> anyhow::Result<u32> { |
149 | | - if sink_handle != 1 { |
150 | | - return Ok(errno::NO_SUCH_BYTES.get().into()); |
151 | | - } |
152 | | - |
153 | | - let (mem, env) = Self::mem_env(&mut caller); |
154 | | - |
155 | | - // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`. |
156 | | - let buffer_len = read_u32(mem, buffer_len_ptr)?; |
157 | | - // Write `buffer` to `sink`. |
158 | | - let buffer = deref_slice(mem, buffer_ptr, buffer_len)?; |
159 | | - env.sink.extend(buffer); |
160 | | - |
161 | | - Ok(0) |
162 | | - } |
163 | | -} |
0 commit comments