@@ -53,10 +53,153 @@ use crate::descriptor::VectorKind;
53
53
use crate :: webidl:: { AuxExportKind , AuxImport , AuxValue , JsImport , JsImportName } ;
54
54
use crate :: webidl:: { NonstandardIncoming , NonstandardOutgoing } ;
55
55
use crate :: webidl:: { NonstandardWebidlSection , WasmBindgenAux } ;
56
- use failure:: { bail, Error , ResultExt } ;
57
- use walrus:: Module ;
56
+ use failure:: { bail, format_err, Error , ResultExt } ;
57
+ use walrus:: { GlobalId , MemoryId , Module } ;
58
+ use wasm_bindgen_multi_value_xform as multi_value_xform;
58
59
use wasm_webidl_bindings:: ast;
59
60
61
+ pub fn add_multi_value (
62
+ module : & mut Module ,
63
+ bindings : & mut NonstandardWebidlSection ,
64
+ ) -> Result < ( ) , Error > {
65
+ let mut to_xform = vec ! [ ] ;
66
+ for ( id, binding) in & bindings. exports {
67
+ if let Some ( ref results) = binding. return_via_outptr {
68
+ // LLVM currently always uses the first parameter for the return
69
+ // pointer. We hard code that here, since we have no better option.
70
+ let return_pointer_index = 0 ;
71
+ to_xform. push ( ( * id, return_pointer_index, & results[ ..] ) ) ;
72
+ }
73
+ }
74
+
75
+ if to_xform. is_empty ( ) {
76
+ // Early exit to avoid failing if we don't have a memory or shadow stack
77
+ // pointer because this is a minimal module that doesn't use linear
78
+ // memory.
79
+ return Ok ( ( ) ) ;
80
+ }
81
+
82
+ let memory = get_memory ( module) ?;
83
+ let shadow_stack_pointer = get_shadow_stack_pointer ( module) ?;
84
+ multi_value_xform:: run ( module, memory, shadow_stack_pointer, & to_xform) ?;
85
+
86
+ // Finally, unset `return_via_outptr`, fix up its incoming bindings'
87
+ // argument numberings, and update its function type.
88
+ for ( id, binding) in & mut bindings. exports {
89
+ if binding. return_via_outptr . take ( ) . is_some ( ) {
90
+ if binding. incoming . is_empty ( ) {
91
+ bail ! ( "missing incoming binding expression for return pointer parameter" ) ;
92
+ }
93
+ if !is_ret_ptr_bindings ( binding. incoming . remove ( 0 ) ) {
94
+ bail ! ( "unexpected incoming binding expression for return pointer parameter" ) ;
95
+ }
96
+
97
+ fixup_binding_argument_gets ( & mut binding. incoming ) ?;
98
+
99
+ let func = match module. exports . get ( * id) . item {
100
+ walrus:: ExportItem :: Function ( f) => f,
101
+ _ => unreachable ! ( ) ,
102
+ } ;
103
+ binding. wasm_ty = module. funcs . get ( func) . ty ( ) ;
104
+ }
105
+ }
106
+
107
+ Ok ( ( ) )
108
+ }
109
+
110
+ fn is_ret_ptr_bindings ( b : NonstandardIncoming ) -> bool {
111
+ match b {
112
+ NonstandardIncoming :: Standard ( ast:: IncomingBindingExpression :: As (
113
+ ast:: IncomingBindingExpressionAs {
114
+ ty : walrus:: ValType :: I32 ,
115
+ expr,
116
+ } ,
117
+ ) ) => match * expr {
118
+ ast:: IncomingBindingExpression :: Get ( ast:: IncomingBindingExpressionGet { idx : 0 } ) => {
119
+ true
120
+ }
121
+ _ => false ,
122
+ } ,
123
+ _ => false ,
124
+ }
125
+ }
126
+
127
+ // Since we removed the first parameter (which was the return pointer) now all
128
+ // of the `Get` binding expression's are off by one. This function fixes these
129
+ // `Get`s.
130
+ fn fixup_binding_argument_gets ( incoming : & mut [ NonstandardIncoming ] ) -> Result < ( ) , Error > {
131
+ for inc in incoming {
132
+ fixup_nonstandard_incoming ( inc) ?;
133
+ }
134
+ return Ok ( ( ) ) ;
135
+
136
+ fn fixup_nonstandard_incoming ( inc : & mut NonstandardIncoming ) -> Result < ( ) , Error > {
137
+ match inc {
138
+ NonstandardIncoming :: Standard ( s) => fixup_standard_incoming ( s) ,
139
+ _ => bail ! ( "found usage of non-standard bindings when in standard-bindings-only mode" ) ,
140
+ }
141
+ }
142
+
143
+ fn fixup_standard_incoming ( s : & mut ast:: IncomingBindingExpression ) -> Result < ( ) , Error > {
144
+ match s {
145
+ ast:: IncomingBindingExpression :: Get ( e) => {
146
+ if e. idx == 0 {
147
+ bail ! (
148
+ "found usage of removed return pointer parameter in \
149
+ non-return pointer bindings"
150
+ ) ;
151
+ } else {
152
+ e. idx -= 1 ;
153
+ Ok ( ( ) )
154
+ }
155
+ }
156
+ ast:: IncomingBindingExpression :: As ( e) => fixup_standard_incoming ( & mut e. expr ) ,
157
+ ast:: IncomingBindingExpression :: AllocUtf8Str ( e) => fixup_standard_incoming ( & mut e. expr ) ,
158
+ ast:: IncomingBindingExpression :: AllocCopy ( e) => fixup_standard_incoming ( & mut e. expr ) ,
159
+ ast:: IncomingBindingExpression :: EnumToI32 ( e) => fixup_standard_incoming ( & mut e. expr ) ,
160
+ ast:: IncomingBindingExpression :: Field ( e) => fixup_standard_incoming ( & mut e. expr ) ,
161
+ ast:: IncomingBindingExpression :: BindImport ( e) => fixup_standard_incoming ( & mut e. expr ) ,
162
+ }
163
+ }
164
+ }
165
+
166
+ fn get_memory ( module : & Module ) -> Result < MemoryId , Error > {
167
+ let mut memories = module. memories . iter ( ) . map ( |m| m. id ( ) ) ;
168
+ let memory = memories. next ( ) ;
169
+ if memories. next ( ) . is_some ( ) {
170
+ bail ! (
171
+ "expected a single memory, found multiple; multiple memories \
172
+ currently not supported"
173
+ ) ;
174
+ }
175
+ memory. ok_or_else ( || {
176
+ format_err ! (
177
+ "module does not have a memory; must have a memory \
178
+ to transform return pointers into Wasm multi-value"
179
+ )
180
+ } )
181
+ }
182
+
183
+ // Get the `__shadow_stack_pointer` global that we stashed in an export early on
184
+ // in the pipeline.
185
+ fn get_shadow_stack_pointer ( module : & mut Module ) -> Result < GlobalId , Error > {
186
+ let ( g, e) = module
187
+ . exports
188
+ . iter ( )
189
+ . find ( |e| e. name == "__shadow_stack_pointer" )
190
+ . map ( |e| {
191
+ let g = match e. item {
192
+ walrus:: ExportItem :: Global ( g) => g,
193
+ _ => unreachable ! ( ) ,
194
+ } ;
195
+ ( g, e. id ( ) )
196
+ } )
197
+ . ok_or_else ( || format_err ! ( "module does not have a shadow stack pointer" ) ) ?;
198
+
199
+ module. exports . delete ( e) ;
200
+ Ok ( g)
201
+ }
202
+
60
203
pub fn add_section (
61
204
module : & mut Module ,
62
205
aux : & WasmBindgenAux ,
0 commit comments