@@ -35,7 +35,7 @@ addToLibrary({
35
35
// Asyncify code that is shared between mode 1 (original) and mode 2 (JSPI).
36
36
//
37
37
#if ASYNCIFY == 1 && MEMORY64
38
- rewindArguments : { } ,
38
+ rewindArguments : new Map ( ) ,
39
39
#endif
40
40
instrumentWasmImports ( imports ) {
41
41
#if EMBIND_GEN_MODE
@@ -99,13 +99,54 @@ addToLibrary({
99
99
}
100
100
} ,
101
101
#if ASYNCIFY == 1 && MEMORY64
102
- saveRewindArguments ( funcName , passedArguments ) {
103
- return Asyncify . rewindArguments [ funcName ] = Array . from ( passedArguments )
102
+ saveRewindArguments ( func , passedArguments ) {
103
+ return Asyncify . rewindArguments . set ( func , Array . from ( passedArguments ) ) ;
104
104
} ,
105
- restoreRewindArguments ( funcName ) {
106
- return Asyncify . rewindArguments [ funcName ] || [ ]
105
+ restoreRewindArguments ( func ) {
106
+ #if ASSERTIONS
107
+ assert ( Asyncify . rewindArguments . has ( func ) ) ;
108
+ #endif
109
+ return Asyncify . rewindArguments . get ( func ) ;
107
110
} ,
108
111
#endif
112
+
113
+ instrumentFunction ( original ) {
114
+ var wrapper = ( ...args ) => {
115
+ #if ASYNCIFY_DEBUG >= 2
116
+ dbg ( `ASYNCIFY: ${ ' ' . repeat ( Asyncify . exportCallStack . length ) } try ${ original } ` ) ;
117
+ #endif
118
+ #if ASYNCIFY == 1
119
+ Asyncify . exportCallStack . push ( original ) ;
120
+ try {
121
+ #endif
122
+ #if ASYNCIFY == 1 && MEMORY64
123
+ Asyncify . saveRewindArguments ( original , args ) ;
124
+ #endif
125
+ return original ( ...args ) ;
126
+ #if ASYNCIFY == 1
127
+ } finally {
128
+ if ( ! ABORT ) {
129
+ var top = Asyncify . exportCallStack . pop ( ) ;
130
+ #if ASSERTIONS
131
+ assert ( top === original ) ;
132
+ #endif
133
+ #if ASYNCIFY_DEBUG >= 2
134
+ dbg ( `ASYNCIFY: ${ ' ' . repeat ( Asyncify . exportCallStack . length ) } finally ${ original } ` ) ;
135
+ #endif
136
+ Asyncify . maybeStopUnwind ( ) ;
137
+ }
138
+ }
139
+ #endif
140
+ } ;
141
+ #if ASYNCIFY == 1
142
+ Asyncify . funcWrappers . set ( original , wrapper ) ;
143
+ #endif
144
+ #if MAIN_MODULE || ASYNCIFY_LAZY_LOAD_CODE
145
+ wrapper . orig = original ;
146
+ #endif
147
+ return wrapper ;
148
+ } ,
149
+
109
150
instrumentWasmExports ( exports ) {
110
151
#if EMBIND_GEN_MODE
111
152
// Instrumenting is not needed when generating code.
@@ -121,48 +162,27 @@ addToLibrary({
121
162
var ret = { } ;
122
163
for ( let [ x , original ] of Object . entries ( exports ) ) {
123
164
if ( typeof original == 'function' ) {
124
- #if ASYNCIFY == 2
165
+ #if ASYNCIFY == 2
125
166
// Wrap all exports with a promising WebAssembly function.
126
167
let isAsyncifyExport = exportPattern . test ( x ) ;
127
168
if ( isAsyncifyExport ) {
128
169
Asyncify . asyncExports . add ( original ) ;
129
170
original = Asyncify . makeAsyncFunction ( original ) ;
130
171
}
131
172
#endif
132
- ret [ x ] = ( ...args ) => {
133
- #if ASYNCIFY_DEBUG >= 2
134
- dbg ( `ASYNCIFY: ${ ' ' . repeat ( Asyncify . exportCallStack . length ) } try ${ x } ` ) ;
135
- #endif
136
- #if ASYNCIFY == 1
137
- Asyncify . exportCallStack . push ( x ) ;
138
- try {
139
- #endif
140
- #if ASYNCIFY == 1 && MEMORY64
141
- Asyncify . saveRewindArguments ( x , args ) ;
142
- #endif
143
- return original ( ...args ) ;
144
- #if ASYNCIFY == 1
145
- } finally {
146
- if ( ! ABORT ) {
147
- var y = Asyncify . exportCallStack . pop ( ) ;
148
- #if ASSERTIONS
149
- assert ( y === x ) ;
173
+ var wrapper = Asyncify . instrumentFunction ( original ) ;
174
+ #if ASYNCIFY_LAZY_LOAD_CODE
175
+ original . exportName = x ;
150
176
#endif
151
- #if ASYNCIFY_DEBUG >= 2
152
- dbg ( `ASYNCIFY: ${ ' ' . repeat ( Asyncify . exportCallStack . length ) } finally ${ x } ` ) ;
153
- #endif
154
- Asyncify . maybeStopUnwind ( ) ;
155
- }
156
- }
157
- #endif
158
- } ;
159
- #if MAIN_MODULE
160
- ret [ x ] . orig = original ;
161
- #endif
162
- } else {
177
+ ret [ x ] = wrapper ;
178
+
179
+ } else {
163
180
ret [ x ] = original ;
164
181
}
165
182
}
183
+ #if ASYNCIFY_LAZY_LOAD_CODE
184
+ Asyncify . updateFunctionMapping ( ret ) ;
185
+ #endif
166
186
return ret ;
167
187
} ,
168
188
@@ -187,24 +207,54 @@ addToLibrary({
187
207
// We must track which wasm exports are called into and
188
208
// exited, so that we know where the call stack began,
189
209
// which is where we must call to rewind it.
210
+ // This list contains the original Wasm exports.
190
211
exportCallStack : [ ] ,
191
- callStackNameToId : { } ,
192
- callStackIdToName : { } ,
212
+ callstackFuncToId : new Map ( ) ,
213
+ callStackIdToFunc : new Map ( ) ,
214
+ // Maps wasm functions to their corresponding wrapper function.
215
+ funcWrappers : new Map ( ) ,
193
216
callStackId : 0 ,
194
217
asyncPromiseHandlers : null , // { resolve, reject } pair for when *all* asynchronicity is done
195
218
sleepCallbacks : [ ] , // functions to call every time we sleep
196
219
197
- getCallStackId ( funcName ) {
220
+ #if ASYNCIFY_LAZY_LOAD_CODE
221
+ updateFunctionMapping ( newExports ) {
222
+ #if ASYNCIFY_DEBUG
223
+ dbg ( 'updateFunctionMapping' , Asyncify . callStackIdToFunc ) ;
224
+ #endif
225
+ #if ASSERTIONS
226
+ assert ( ! Asyncify . exportCallStack . length ) ;
227
+ #endif
228
+ Asyncify . callStackIdToFunc . forEach ( ( func, id ) => {
198
229
#if ASSERTIONS
199
- assert ( funcName ) ;
230
+ assert ( func . exportName ) ;
231
+ assert ( newExports [ func . exportName ] ) ;
232
+ assert ( newExports [ func . exportName ] . orig ) ;
233
+ #endif
234
+ var newFunc = newExports [ func . exportName ] . orig ;
235
+ Asyncify . callStackIdToFunc . set ( id , newFunc )
236
+ Asyncify . callstackFuncToId . set ( newFunc , id ) ;
237
+ #if MEMORY64
238
+ var args = Asyncify . rewindArguments . get ( func ) ;
239
+ if ( args ) {
240
+ Asyncify . rewindArguments . set ( newFunc , args ) ;
241
+ Asyncify . rewindArguments . delete ( func ) ;
242
+ }
200
243
#endif
201
- var id = Asyncify . callStackNameToId [ funcName ] ;
202
- if ( id === undefined ) {
203
- id = Asyncify . callStackId ++ ;
204
- Asyncify . callStackNameToId [ funcName ] = id ;
205
- Asyncify . callStackIdToName [ id ] = funcName ;
244
+ } ) ;
245
+ } ,
246
+ #endif
247
+
248
+ getCallStackId ( func ) {
249
+ #if ASSERTIONS
250
+ assert ( func ) ;
251
+ #endif
252
+ if ( ! Asyncify . callstackFuncToId . has ( func ) ) {
253
+ var id = Asyncify . callStackId ++ ;
254
+ Asyncify . callstackFuncToId . set ( func , id ) ;
255
+ Asyncify . callStackIdToFunc . set ( id , func ) ;
206
256
}
207
- return id ;
257
+ return Asyncify . callstackFuncToId . get ( func ) ;
208
258
} ,
209
259
210
260
maybeStopUnwind ( ) {
@@ -246,7 +296,7 @@ addToLibrary({
246
296
// An asyncify data structure has three fields:
247
297
// 0 current stack pos
248
298
// 4 max stack pos
249
- // 8 id of function at bottom of the call stack (callStackIdToName [id] == name of js function )
299
+ // 8 id of function at bottom of the call stack (callStackIdToFunc [id] == wasm func )
250
300
//
251
301
// The Asyncify ABI only interprets the first two fields, the rest is for the runtime.
252
302
// We also embed a stack in the same memory region here, right next to the structure.
@@ -274,38 +324,24 @@ addToLibrary({
274
324
{ { { makeSetValue ( 'ptr' , C_STRUCTS . asyncify_data_s . rewind_id , 'rewindId' , 'i32' ) } } } ;
275
325
} ,
276
326
277
- getDataRewindFuncName ( ptr ) {
327
+ getDataRewindFunc ( ptr ) {
278
328
var id = { { { makeGetValue ( 'ptr' , C_STRUCTS . asyncify_data_s . rewind_id , 'i32' ) } } } ;
279
- var name = Asyncify . callStackIdToName [ id ] ;
280
- #if ASSERTIONS
281
- assert ( name , `id ${ id } not found in callStackIdToName` ) ;
282
- #endif
283
- return name ;
284
- } ,
285
-
286
- #if RELOCATABLE
287
- getDataRewindFunc__deps : [ '$resolveGlobalSymbol ' ] ,
288
- #endif
289
- getDataRewindFunc ( name ) {
290
- var func = wasmExports [ name ] ;
291
- #if RELOCATABLE
292
- // Exported functions in side modules are not listed in `wasmExports`,
293
- // So we should use `resolveGlobalSymbol` helper function, which is defined in `library_dylink.js`.
294
- if ( ! func ) {
295
- func = resolveGlobalSymbol ( name , false ) . sym ;
296
- }
297
- #endif
329
+ var func = Asyncify . callStackIdToFunc . get ( id ) ;
298
330
#if ASSERTIONS
299
- assert ( func , `export not found: ${ name } ` ) ;
331
+ assert ( func , `id ${ id } not found in callStackIdToFunc ` ) ;
300
332
#endif
301
333
return func ;
302
334
} ,
303
335
304
336
doRewind ( ptr ) {
305
- var name = Asyncify . getDataRewindFuncName ( ptr ) ;
306
- var func = Asyncify . getDataRewindFunc ( name ) ;
337
+ var original = Asyncify . getDataRewindFunc ( ptr ) ;
307
338
#if ASYNCIFY_DEBUG
308
- dbg ( 'ASYNCIFY: doRewind:' , name ) ;
339
+ dbg ( 'ASYNCIFY: doRewind:' , original ) ;
340
+ #endif
341
+ var func = Asyncify . funcWrappers . get ( original ) ;
342
+ #if ASSERTIONS
343
+ assert ( original ) ;
344
+ assert ( func ) ;
309
345
#endif
310
346
// Once we have rewound and the stack we no longer need to artificially
311
347
// keep the runtime alive.
@@ -315,7 +351,7 @@ addToLibrary({
315
351
// can just call the function with no args at all since and the engine will produce zeros
316
352
// for all arguments. However, for i64 arguments we get `undefined cannot be converted to
317
353
// BigInt`.
318
- return func ( ...Asyncify . restoreRewindArguments ( name ) ) ;
354
+ return func ( ...Asyncify . restoreRewindArguments ( original ) ) ;
319
355
#else
320
356
return func ( ) ;
321
357
#endif
@@ -510,6 +546,7 @@ addToLibrary({
510
546
} ) ;
511
547
} ,
512
548
549
+ #if ASYNCIFY_LAZY_LOAD_CODE
513
550
emscripten_lazy_load_code__async: true ,
514
551
emscripten_lazy_load_code : ( ) => Asyncify . handleSleep ( ( wakeUp ) => {
515
552
// Update the expected wasm binary file to be the lazy one.
@@ -519,6 +556,7 @@ addToLibrary({
519
556
// Load the new wasm.
520
557
createWasm ( ) ;
521
558
} ) ,
559
+ #endif
522
560
523
561
_load_secondary_module__sig : 'v' ,
524
562
_load_secondary_module : async function ( ) {
0 commit comments