@@ -4,20 +4,17 @@ use anyhow::Context;
4
4
use clap:: parser:: ValueSource ;
5
5
use clap:: Arg ;
6
6
use clap:: ArgAction :: Set ;
7
- use core:: mem;
8
7
use fs_err as fs;
9
8
use 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 } ;
13
10
use spacetimedb_schema;
14
11
use spacetimedb_schema:: def:: { ModuleDef , ReducerDef , ScopedTypeName , TableDef , TypeDef } ;
15
12
use spacetimedb_schema:: identifier:: Identifier ;
16
13
use std:: path:: { Path , PathBuf } ;
17
- use wasmtime :: { Caller , StoreContextMut } ;
14
+ use std :: process :: { Command , Stdio } ;
18
15
19
16
use crate :: generate:: util:: iter_reducers;
20
- use crate :: util:: y_or_n;
17
+ use crate :: util:: { resolve_sibling_binary , y_or_n} ;
21
18
use crate :: Config ;
22
19
use crate :: { build, common_args} ;
23
20
use clap:: builder:: PossibleValue ;
@@ -127,10 +124,8 @@ pub async fn exec(config: Config, args: &clap::ArgMatches) -> anyhow::Result<()>
127
124
} ;
128
125
let spinner = indicatif:: ProgressBar :: new_spinner ( ) ;
129
126
spinner. enable_steady_tick ( std:: time:: Duration :: from_millis ( 60 ) ) ;
130
- spinner. set_message ( "Compiling wasm..." ) ;
131
- let module = compile_wasm ( & wasm_path) ?;
132
127
spinner. set_message ( "Extracting schema from wasm..." ) ;
133
- extract_descriptions_from_module ( module ) ?
128
+ extract_descriptions ( & wasm_path ) . context ( "could not extract schema" ) ?
134
129
} ;
135
130
136
131
fs:: create_dir_all ( out_dir) ?;
@@ -147,7 +142,8 @@ pub async fn exec(config: Config, args: &clap::ArgMatches) -> anyhow::Result<()>
147
142
Language :: TypeScript => & typescript:: TypeScript ,
148
143
} ;
149
144
150
- for ( fname, code) in generate ( module, lang) ? {
145
+ let module = module. try_into ( ) ?;
146
+ for ( fname, code) in generate ( & module, lang) ? {
151
147
let fname = Path :: new ( & fname) ;
152
148
// If a generator asks for a file in a subdirectory, create the subdirectory first.
153
149
if let Some ( parent) = fname. parent ( ) . filter ( |p| !p. as_os_str ( ) . is_empty ( ) ) {
@@ -230,8 +226,7 @@ impl clap::ValueEnum for Language {
230
226
}
231
227
}
232
228
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 ) > > {
235
230
Ok ( itertools:: chain!(
236
231
module
237
232
. tables( )
@@ -266,113 +261,14 @@ pub trait Lang {
266
261
Self : Sized ;
267
262
}
268
263
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)
378
274
}
0 commit comments