@@ -14,20 +14,21 @@ use crate::host::v8::{
1414 TerminationError , Throwable ,
1515} ;
1616use crate :: host:: wasm_common:: instrumentation:: span;
17- use crate :: host:: wasm_common:: module_host_actor:: { ReducerOp , ReducerResult } ;
17+ use crate :: host:: wasm_common:: module_host_actor:: { AnonymousViewOp , ReducerOp , ReducerResult , ViewOp } ;
1818use crate :: host:: wasm_common:: { err_to_errno_and_log, RowIterIdx , TimingSpan , TimingSpanIdx } ;
1919use crate :: host:: AbiCall ;
2020use anyhow:: Context ;
21+ use bytes:: Bytes ;
2122use spacetimedb_lib:: { bsatn, ConnectionId , Identity , RawModuleDef } ;
22- use spacetimedb_primitives:: { errno, ColId , IndexId , ReducerId , TableId } ;
23+ use spacetimedb_primitives:: { errno, ColId , IndexId , ReducerId , TableId , ViewId } ;
2324use spacetimedb_sats:: Serialize ;
2425use v8:: {
2526 callback_scope, ConstructorBehavior , Function , FunctionCallbackArguments , Isolate , Local , Module , Object ,
2627 PinCallbackScope , PinScope ,
2728} ;
2829
2930macro_rules! create_synthetic_module {
30- ( $scope: expr, $module_name: expr, $( ( $wrapper: ident, $abi_call: expr, $fun: ident) , ) * ) => { {
31+ ( $scope: expr, $module_name: expr $( , ( $wrapper: ident, $abi_call: expr, $fun: ident) ) * $ ( , ) ? ) => { {
3132 let export_names = & [ $( str_from_ident!( $fun) . string( $scope) ) ,* ] ;
3233 let eval_steps = |context, module| {
3334 callback_scope!( unsafe scope, context) ;
@@ -112,6 +113,11 @@ pub(super) fn sys_v1_0<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope
112113 )
113114}
114115
116+ pub ( super ) fn sys_v1_1 < ' scope > ( scope : & mut PinScope < ' scope , ' _ > ) -> Local < ' scope , Module > {
117+ use register_hooks_v1_1 as register_hooks;
118+ create_synthetic_module ! ( scope, "spacetime:sys@1.1" , ( with_nothing, ( ) , register_hooks) )
119+ }
120+
115121/// Registers a function in `module`
116122/// where the function has `name` and does `body`.
117123fn register_module_fun (
@@ -333,6 +339,51 @@ fn register_hooks_v1_0<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionC
333339 Ok ( v8:: undefined ( scope) . into ( ) )
334340}
335341
342+ /// Module ABI that registers the functions called by the host.
343+ ///
344+ /// # Signature
345+ ///
346+ /// ```ignore
347+ /// register_hooks(hooks: {
348+ /// __call_view__(view_id: u32, sender: u256, args: u8[]): u8[];
349+ /// __call_view_anon__(view_id: u32, args: u8[]): u8[];
350+ /// }): void
351+ /// ```
352+ ///
353+ /// # Types
354+ ///
355+ /// - `u8` is `number` in JS restricted to unsigned 8-bit integers.
356+ /// - `u32` is `bigint` in JS restricted to unsigned 32-bit integers.
357+ /// - `u256` is `bigint` in JS restricted to unsigned 256-bit integers.
358+ ///
359+ /// # Returns
360+ ///
361+ /// Returns nothing.
362+ ///
363+ /// # Throws
364+ ///
365+ /// Throws a `TypeError` if:
366+ /// - `hooks` is not an object that has the correct functions.
367+ fn register_hooks_v1_1 < ' scope > ( scope : & mut PinScope < ' scope , ' _ > , args : FunctionCallbackArguments < ' _ > ) -> FnRet < ' scope > {
368+ // Convert `hooks` to an object.
369+ let hooks = cast ! ( scope, args. get( 0 ) , Object , "hooks object" ) . map_err ( |e| e. throw ( scope) ) ?;
370+
371+ let call_view = get_hook_function ( scope, hooks, str_from_ident ! ( __call_view__) ) ?;
372+ let call_anonymous_view = get_hook_function ( scope, hooks, str_from_ident ! ( __call_anonymous_view__) ) ?;
373+
374+ // Set the hooks.
375+ set_hook_slots (
376+ scope,
377+ AbiVersion :: V1 ,
378+ & [
379+ ( ModuleHookKey :: CallView , call_view) ,
380+ ( ModuleHookKey :: CallAnonymousView , call_anonymous_view) ,
381+ ] ,
382+ ) ?;
383+
384+ Ok ( v8:: undefined ( scope) . into ( ) )
385+ }
386+
336387/// Calls the `__call_reducer__` function `fun`.
337388pub ( super ) fn call_call_reducer (
338389 scope : & mut PinScope < ' _ , ' _ > ,
@@ -364,6 +415,67 @@ pub(super) fn call_call_reducer(
364415 Ok ( user_res)
365416}
366417
418+ /// Calls the `__call_view__` function `fun`.
419+ pub ( super ) fn call_call_view (
420+ scope : & mut PinScope < ' _ , ' _ > ,
421+ hooks : & HookFunctions < ' _ > ,
422+ op : ViewOp < ' _ > ,
423+ ) -> Result < Bytes , ErrorOrException < ExceptionThrown > > {
424+ let fun = hooks. call_view . context ( "`__call_view__` was never defined" ) ?;
425+
426+ let ViewOp {
427+ id : ViewId ( view_id) ,
428+ name : _,
429+ caller_identity : sender,
430+ timestamp : _,
431+ args : view_args,
432+ } = op;
433+ // Serialize the arguments.
434+ let view_id = serialize_to_js ( scope, & view_id) ?;
435+ let sender = serialize_to_js ( scope, & sender. to_u256 ( ) ) ?;
436+ let view_args = serialize_to_js ( scope, view_args. get_bsatn ( ) ) ?;
437+ let args = & [ view_id, sender, view_args] ;
438+
439+ // Call the function.
440+ let ret = call_free_fun ( scope, fun, args) ?;
441+
442+ // Deserialize the user result.
443+ let ret = cast ! ( scope, ret, v8:: Uint8Array , "bytes return from `__call_view__`" ) . map_err ( |e| e. throw ( scope) ) ?;
444+ let bytes = ret. get_contents ( & mut [ ] ) ;
445+
446+ Ok ( Bytes :: copy_from_slice ( bytes) )
447+ }
448+
449+ /// Calls the `__call_view_anon__` function `fun`.
450+ pub ( super ) fn call_call_view_anon (
451+ scope : & mut PinScope < ' _ , ' _ > ,
452+ hooks : & HookFunctions < ' _ > ,
453+ op : AnonymousViewOp < ' _ > ,
454+ ) -> Result < Bytes , ErrorOrException < ExceptionThrown > > {
455+ let fun = hooks. call_view_anon . context ( "`__call_view__` was never defined" ) ?;
456+
457+ let AnonymousViewOp {
458+ id : ViewId ( view_id) ,
459+ name : _,
460+ timestamp : _,
461+ args : view_args,
462+ } = op;
463+ // Serialize the arguments.
464+ let view_id = serialize_to_js ( scope, & view_id) ?;
465+ let view_args = serialize_to_js ( scope, view_args. get_bsatn ( ) ) ?;
466+ let args = & [ view_id, view_args] ;
467+
468+ // Call the function.
469+ let ret = call_free_fun ( scope, fun, args) ?;
470+
471+ // Deserialize the user result.
472+ let ret =
473+ cast ! ( scope, ret, v8:: Uint8Array , "bytes return from `__call_view_anon__`" ) . map_err ( |e| e. throw ( scope) ) ?;
474+ let bytes = ret. get_contents ( & mut [ ] ) ;
475+
476+ Ok ( Bytes :: copy_from_slice ( bytes) )
477+ }
478+
367479/// Calls the registered `__describe_module__` function hook.
368480pub ( super ) fn call_describe_module (
369481 scope : & mut PinScope < ' _ , ' _ > ,
0 commit comments