@@ -4,20 +4,17 @@ use anyhow::Context;
44use clap:: parser:: ValueSource ;
55use clap:: Arg ;
66use clap:: ArgAction :: Set ;
7- use core:: mem;
87use fs_err as fs;
98use spacetimedb_lib:: de:: serde:: DeserializeWrapper ;
10- use spacetimedb_lib:: { bsatn, RawModuleDefV8 } ;
11- use spacetimedb_lib:: { RawModuleDef , MODULE_ABI_MAJOR_VERSION } ;
12- use spacetimedb_primitives:: errno;
9+ use spacetimedb_lib:: { sats, RawModuleDef } ;
1310use spacetimedb_schema;
1411use spacetimedb_schema:: def:: { ModuleDef , ReducerDef , ScopedTypeName , TableDef , TypeDef } ;
1512use spacetimedb_schema:: identifier:: Identifier ;
1613use std:: path:: { Path , PathBuf } ;
17- use wasmtime :: { Caller , StoreContextMut } ;
14+ use std :: process :: { Command , Stdio } ;
1815
1916use crate :: generate:: util:: iter_reducers;
20- use crate :: util:: y_or_n;
17+ use crate :: util:: { resolve_sibling_binary , y_or_n} ;
2118use crate :: Config ;
2219use crate :: { build, common_args} ;
2320use clap:: builder:: PossibleValue ;
@@ -127,10 +124,8 @@ pub async fn exec(config: Config, args: &clap::ArgMatches) -> anyhow::Result<()>
127124 } ;
128125 let spinner = indicatif:: ProgressBar :: new_spinner ( ) ;
129126 spinner. enable_steady_tick ( std:: time:: Duration :: from_millis ( 60 ) ) ;
130- spinner. set_message ( "Compiling wasm..." ) ;
131- let module = compile_wasm ( & wasm_path) ?;
132127 spinner. set_message ( "Extracting schema from wasm..." ) ;
133- extract_descriptions_from_module ( module ) ?
128+ extract_descriptions ( & wasm_path ) . context ( "could not extract schema" ) ?
134129 } ;
135130
136131 fs:: create_dir_all ( out_dir) ?;
@@ -147,7 +142,8 @@ pub async fn exec(config: Config, args: &clap::ArgMatches) -> anyhow::Result<()>
147142 Language :: TypeScript => & typescript:: TypeScript ,
148143 } ;
149144
150- for ( fname, code) in generate ( module, lang) ? {
145+ let module = module. try_into ( ) ?;
146+ for ( fname, code) in generate ( & module, lang) ? {
151147 let fname = Path :: new ( & fname) ;
152148 // If a generator asks for a file in a subdirectory, create the subdirectory first.
153149 if let Some ( parent) = fname. parent ( ) . filter ( |p| !p. as_os_str ( ) . is_empty ( ) ) {
@@ -230,8 +226,7 @@ impl clap::ValueEnum for Language {
230226 }
231227}
232228
233- pub fn generate ( module : RawModuleDef , lang : & dyn Lang ) -> anyhow:: Result < Vec < ( String , String ) > > {
234- let module = & ModuleDef :: try_from ( module) ?;
229+ pub fn generate ( module : & ModuleDef , lang : & dyn Lang ) -> anyhow:: Result < Vec < ( String , String ) > > {
235230 Ok ( itertools:: chain!(
236231 module
237232 . tables( )
@@ -266,113 +261,14 @@ pub trait Lang {
266261 Self : Sized ;
267262}
268263
269- pub fn extract_descriptions ( wasm_file : & Path ) -> anyhow:: Result < RawModuleDef > {
270- let module = compile_wasm ( wasm_file) ?;
271- extract_descriptions_from_module ( module)
272- }
273-
274- fn compile_wasm ( wasm_file : & Path ) -> anyhow:: Result < wasmtime:: Module > {
275- wasmtime:: Module :: from_file ( & wasmtime:: Engine :: default ( ) , wasm_file)
276- }
277-
278- fn extract_descriptions_from_module ( module : wasmtime:: Module ) -> anyhow:: Result < RawModuleDef > {
279- let engine = module. engine ( ) ;
280- let ctx = WasmCtx {
281- mem : None ,
282- sink : Vec :: new ( ) ,
283- } ;
284- let mut store = wasmtime:: Store :: new ( engine, ctx) ;
285- let mut linker = wasmtime:: Linker :: new ( engine) ;
286- linker. allow_shadowing ( true ) . define_unknown_imports_as_traps ( & module) ?;
287- let module_name = & * format ! ( "spacetime_{MODULE_ABI_MAJOR_VERSION}.0" ) ;
288- linker. func_wrap (
289- module_name,
290- "console_log" ,
291- |mut caller : Caller < ' _ , WasmCtx > ,
292- _level : u32 ,
293- _target_ptr : u32 ,
294- _target_len : u32 ,
295- _filename_ptr : u32 ,
296- _filename_len : u32 ,
297- _line_number : u32 ,
298- message_ptr : u32 ,
299- message_len : u32 | {
300- let ( mem, _) = WasmCtx :: mem_env ( & mut caller) ;
301- let slice = deref_slice ( mem, message_ptr, message_len) . unwrap ( ) ;
302- println ! ( "from wasm: {}" , String :: from_utf8_lossy( slice) ) ;
303- } ,
304- ) ?;
305- linker. func_wrap ( module_name, "bytes_sink_write" , WasmCtx :: bytes_sink_write) ?;
306- let instance = linker. instantiate ( & mut store, & module) ?;
307- let memory = instance. get_memory ( & mut store, "memory" ) . context ( "no memory export" ) ?;
308- store. data_mut ( ) . mem = Some ( memory) ;
309-
310- let mut preinits = instance
311- . exports ( & mut store)
312- . filter_map ( |exp| Some ( ( exp. name ( ) . strip_prefix ( "__preinit__" ) ?. to_owned ( ) , exp. into_func ( ) ?) ) )
313- . collect :: < Vec < _ > > ( ) ;
314- preinits. sort_by ( |( a, _) , ( b, _) | a. cmp ( b) ) ;
315- for ( _, func) in preinits {
316- func. typed ( & store) ?. call ( & mut store, ( ) ) ?
317- }
318- let module: RawModuleDef = match instance. get_func ( & mut store, "__describe_module__" ) {
319- Some ( f) => {
320- store. data_mut ( ) . sink = Vec :: new ( ) ;
321- f. typed :: < u32 , ( ) > ( & store) ?. call ( & mut store, 1 ) ?;
322- let buf = mem:: take ( & mut store. data_mut ( ) . sink ) ;
323- bsatn:: from_slice ( & buf) ?
324- }
325- // TODO: shouldn't we return an error here?
326- None => RawModuleDef :: V8BackCompat ( RawModuleDefV8 :: default ( ) ) ,
327- } ;
328- Ok ( module)
329- }
330-
331- struct WasmCtx {
332- mem : Option < wasmtime:: Memory > ,
333- sink : Vec < u8 > ,
334- }
335-
336- fn deref_slice ( mem : & [ u8 ] , offset : u32 , len : u32 ) -> anyhow:: Result < & [ u8 ] > {
337- anyhow:: ensure!( offset != 0 , "ptr is null" ) ;
338- mem. get ( offset as usize ..)
339- . and_then ( |s| s. get ( ..len as usize ) )
340- . context ( "pointer out of bounds" )
341- }
342-
343- fn read_u32 ( mem : & [ u8 ] , offset : u32 ) -> anyhow:: Result < u32 > {
344- Ok ( u32:: from_le_bytes ( deref_slice ( mem, offset, 4 ) ?. try_into ( ) . unwrap ( ) ) )
345- }
346-
347- impl WasmCtx {
348- pub fn get_mem ( & self ) -> wasmtime:: Memory {
349- self . mem . expect ( "Initialized memory" )
350- }
351-
352- fn mem_env < ' a > ( ctx : impl Into < StoreContextMut < ' a , Self > > ) -> ( & ' a mut [ u8 ] , & ' a mut Self ) {
353- let ctx = ctx. into ( ) ;
354- let mem = ctx. data ( ) . get_mem ( ) ;
355- mem. data_and_store_mut ( ctx)
356- }
357-
358- pub fn bytes_sink_write (
359- mut caller : Caller < ' _ , Self > ,
360- sink_handle : u32 ,
361- buffer_ptr : u32 ,
362- buffer_len_ptr : u32 ,
363- ) -> anyhow:: Result < u32 > {
364- if sink_handle != 1 {
365- return Ok ( errno:: NO_SUCH_BYTES . get ( ) . into ( ) ) ;
366- }
367-
368- let ( mem, env) = Self :: mem_env ( & mut caller) ;
369-
370- // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`.
371- let buffer_len = read_u32 ( mem, buffer_len_ptr) ?;
372- // Write `buffer` to `sink`.
373- let buffer = deref_slice ( mem, buffer_ptr, buffer_len) ?;
374- env. sink . extend ( buffer) ;
375-
376- Ok ( 0 )
377- }
264+ fn extract_descriptions ( wasm_file : & Path ) -> anyhow:: Result < RawModuleDef > {
265+ let bin_path = resolve_sibling_binary ( "spacetimedb-standalone" ) ?;
266+ let child = Command :: new ( & bin_path)
267+ . arg ( "extract-schema" )
268+ . arg ( wasm_file)
269+ . stdout ( Stdio :: piped ( ) )
270+ . spawn ( )
271+ . with_context ( || format ! ( "failed to spawn {}" , bin_path. display( ) ) ) ?;
272+ let sats:: serde:: SerdeWrapper ( def) = serde_json:: from_reader ( child. stdout . unwrap ( ) ) ?;
273+ Ok ( def)
378274}
0 commit comments