1
- use wasmi:: { AsContext , Caller , Engine , Func as Function , Linker , Module , Value } ;
1
+ use wasmi:: { AsContext , Caller , Engine , Func as Function , Linker , Memory , Module , Value } ;
2
2
3
3
type Store = wasmi:: Store < PersistentData > ;
4
4
5
+ /// Reference to a slice of memory returned after
6
+ /// [calling a wasm function](PluginInstance::call).
7
+ ///
8
+ /// # Drop
9
+ /// On [`Drop`], this will free the slice of memory inside the plugin.
10
+ ///
11
+ /// As such, this structure mutably borrows the [`PluginInstance`], which prevents
12
+ /// another function from being called.
13
+ pub struct ReturnedData < ' a > {
14
+ memory : Memory ,
15
+ ptr : u32 ,
16
+ len : u32 ,
17
+ free_function : & ' a Function ,
18
+ context_mut : & ' a mut Store ,
19
+ }
20
+
21
+ impl < ' a > ReturnedData < ' a > {
22
+ /// Get a reference to the returned slice of data.
23
+ ///
24
+ /// # Panic
25
+ /// This may panic if the function returned an invalid `(ptr, len)` pair.
26
+ pub fn get ( & self ) -> & [ u8 ] {
27
+ & self . memory . data ( & * self . context_mut ) [ self . ptr as usize ..( self . ptr + self . len ) as usize ]
28
+ }
29
+ }
30
+
31
+ impl Drop for ReturnedData < ' _ > {
32
+ fn drop ( & mut self ) {
33
+ self . free_function
34
+ . call (
35
+ & mut * self . context_mut ,
36
+ & [ Value :: I32 ( self . ptr as _ ) , Value :: I32 ( self . len as _ ) ] ,
37
+ & mut [ ] ,
38
+ )
39
+ . unwrap ( ) ;
40
+ }
41
+ }
42
+
5
43
#[ derive( Debug , Clone ) ]
6
44
struct PersistentData {
7
- result_data : Vec < u8 > ,
45
+ result_ptr : u32 ,
46
+ result_len : u32 ,
8
47
arg_buffer : Vec < u8 > ,
9
48
}
10
49
11
50
#[ derive( Debug ) ]
12
51
pub struct PluginInstance {
13
52
store : Store ,
53
+ memory : Memory ,
54
+ free_function : Function ,
14
55
functions : Vec < ( String , Function ) > ,
15
56
}
16
57
17
58
impl PluginInstance {
18
59
pub fn new_from_bytes ( bytes : impl AsRef < [ u8 ] > ) -> Result < Self , String > {
19
60
let engine = Engine :: default ( ) ;
20
61
let data = PersistentData {
21
- result_data : Vec :: new ( ) ,
22
62
arg_buffer : Vec :: new ( ) ,
63
+ result_ptr : 0 ,
64
+ result_len : 0 ,
23
65
} ;
24
66
let mut store = Store :: new ( & engine, data) ;
25
67
@@ -32,11 +74,8 @@ impl PluginInstance {
32
74
"typst_env" ,
33
75
"wasm_minimal_protocol_send_result_to_host" ,
34
76
move |mut caller : Caller < PersistentData > , ptr : u32 , len : u32 | {
35
- let memory = caller. get_export ( "memory" ) . unwrap ( ) . into_memory ( ) . unwrap ( ) ;
36
- let mut buffer = std:: mem:: take ( & mut caller. data_mut ( ) . result_data ) ;
37
- buffer. resize ( len as usize , 0 ) ;
38
- memory. read ( & caller, ptr as _ , & mut buffer) . unwrap ( ) ;
39
- caller. data_mut ( ) . result_data = buffer;
77
+ caller. data_mut ( ) . result_ptr = ptr;
78
+ caller. data_mut ( ) . result_len = len;
40
79
} ,
41
80
)
42
81
. unwrap ( )
@@ -51,54 +90,44 @@ impl PluginInstance {
51
90
} ,
52
91
)
53
92
. unwrap ( )
54
- // hack to accept wasi file
55
- // https://github.com/near/wasi-stub is preferred
56
- /*
57
- .func_wrap(
58
- "wasi_snapshot_preview1",
59
- "fd_write",
60
- |_: i32, _: i32, _: i32, _: i32| 0i32,
61
- )
62
- .unwrap()
63
- .func_wrap(
64
- "wasi_snapshot_preview1",
65
- "environ_get",
66
- |_: i32, _: i32| 0i32,
67
- )
68
- .unwrap()
69
- .func_wrap(
70
- "wasi_snapshot_preview1",
71
- "environ_sizes_get",
72
- |_: i32, _: i32| 0i32,
73
- )
74
- .unwrap()
75
- .func_wrap(
76
- "wasi_snapshot_preview1",
77
- "proc_exit",
78
- |_: i32| {},
79
- )
80
- .unwrap()
81
- */
82
93
. instantiate ( & mut store, & module)
83
94
. map_err ( |e| format ! ( "{e}" ) ) ?
84
95
. start ( & mut store)
85
96
. map_err ( |e| format ! ( "{e}" ) ) ?;
86
97
98
+ let mut free_function = None ;
87
99
let functions = instance
88
100
. exports ( & store)
89
101
. filter_map ( |e| {
90
102
let name = e. name ( ) . to_owned ( ) ;
91
- e. into_func ( ) . map ( |func| ( name, func) )
103
+
104
+ e. into_func ( ) . map ( |func| {
105
+ if name == "wasm_minimal_protocol_free_byte_buffer" {
106
+ free_function = Some ( func) ;
107
+ }
108
+ ( name, func)
109
+ } )
92
110
} )
93
111
. collect :: < Vec < _ > > ( ) ;
94
- Ok ( Self { store, functions } )
112
+ let free_function = free_function. unwrap ( ) ;
113
+ let memory = instance
114
+ . get_export ( & store, "memory" )
115
+ . unwrap ( )
116
+ . into_memory ( )
117
+ . unwrap ( ) ;
118
+ Ok ( Self {
119
+ store,
120
+ memory,
121
+ free_function,
122
+ functions,
123
+ } )
95
124
}
96
125
97
126
fn write ( & mut self , args : & [ & [ u8 ] ] ) {
98
127
self . store . data_mut ( ) . arg_buffer = args. concat ( ) ;
99
128
}
100
129
101
- pub fn call ( & mut self , function : & str , args : & [ & [ u8 ] ] ) -> Result < Vec < u8 > , String > {
130
+ pub fn call ( & mut self , function : & str , args : & [ & [ u8 ] ] ) -> Result < ReturnedData , String > {
102
131
self . write ( args) ;
103
132
104
133
let ( _, function) = self
@@ -122,11 +151,19 @@ impl PluginInstance {
122
151
code. first ( ) . cloned ( ) . unwrap_or ( Value :: I32 ( 3 ) ) // if the function returns nothing
123
152
} ;
124
153
125
- let s = std:: mem:: take ( & mut self . store . data_mut ( ) . result_data ) ;
154
+ let ( ptr, len) = ( self . store . data ( ) . result_ptr , self . store . data ( ) . result_len ) ;
155
+
156
+ let result = ReturnedData {
157
+ memory : self . memory ,
158
+ ptr,
159
+ len,
160
+ free_function : & self . free_function ,
161
+ context_mut : & mut self . store ,
162
+ } ;
126
163
127
164
match code {
128
- Value :: I32 ( 0 ) => Ok ( s ) ,
129
- Value :: I32 ( 1 ) => Err ( match String :: from_utf8 ( s ) {
165
+ Value :: I32 ( 0 ) => Ok ( result ) ,
166
+ Value :: I32 ( 1 ) => Err ( match std :: str :: from_utf8 ( result . get ( ) ) {
130
167
Ok ( err) => format ! ( "plugin errored with: '{}'" , err, ) ,
131
168
Err ( _) => String :: from ( "plugin errored and did not return valid UTF-8" ) ,
132
169
} ) ,
0 commit comments