4
4
#include <Python.h>
5
5
#include "pycore_runtime.h" // _PyRuntime
6
6
7
- typedef int (* CountArgsFunc )(PyCFunctionWithKeywords func );
7
+ typedef PyObject * (* TrampolineFunc )(int * success , PyCFunctionWithKeywords func ,
8
+ PyObject * arg1 , PyObject * arg2 ,
9
+ PyObject * arg3 );
10
+
11
+ EMSCRIPTEN_KEEPALIVE const char trampoline_inner_wasm [] = {
12
+ #embed "Python/emscripten_trampoline_inner.wasm"
13
+ };
14
+
15
+ EMSCRIPTEN_KEEPALIVE int trampoline_inner_wasm_length =
16
+ sizeof (trampoline_inner_wasm );
8
17
9
18
// Offset of emscripten_count_args_function in _PyRuntimeState. There's a couple
10
19
// of alternatives:
@@ -18,59 +27,14 @@ typedef int (*CountArgsFunc)(PyCFunctionWithKeywords func);
18
27
//
19
28
// So putting the mutable constant in _PyRuntime and using a immutable global to
20
29
// record the offset so we can access it from JS is probably the best way.
21
- EMSCRIPTEN_KEEPALIVE const int _PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET = offsetof(_PyRuntimeState , emscripten_count_args_function );
30
+ EMSCRIPTEN_KEEPALIVE const int _PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET =
31
+ offsetof(_PyRuntimeState , emscripten_trampoline );
22
32
23
- EM_JS (CountArgsFunc , _PyEM_GetCountArgsPtr , ( ), {
33
+ EM_JS (TrampolineFunc , _PyEM_GetTrampolinePtr , (void ) , {
24
34
return Module ._PyEM_CountArgsPtr ; // initialized below
25
35
}
26
- // Binary module for the checks. It has to be done in web assembly because
27
- // clang/llvm have no support yet for the reference types yet. In fact, the wasm
28
- // binary toolkit doesn't yet support the ref.test instruction either. To
29
- // convert the following textual wasm to a binary, you can build wabt from this
30
- // branch: https://github.com/WebAssembly/wabt/pull/2529 and then use that
31
- // wat2wasm binary.
32
- //
33
- // (module
34
- // (type $type0 (func (param) (result i32)))
35
- // (type $type1 (func (param i32) (result i32)))
36
- // (type $type2 (func (param i32 i32) (result i32)))
37
- // (type $type3 (func (param i32 i32 i32) (result i32)))
38
- // (type $blocktype (func (param) (result)))
39
- // (table $funcs (import "e" "t") 0 funcref)
40
- // (export "f" (func $f))
41
- // (func $f (param $fptr i32) (result i32)
42
- // (local $fref funcref)
43
- // local.get $fptr
44
- // table.get $funcs
45
- // local.tee $fref
46
- // ref.test $type3
47
- // if $blocktype
48
- // i32.const 3
49
- // return
50
- // end
51
- // local.get $fref
52
- // ref.test $type2
53
- // if $blocktype
54
- // i32.const 2
55
- // return
56
- // end
57
- // local.get $fref
58
- // ref.test $type1
59
- // if $blocktype
60
- // i32.const 1
61
- // return
62
- // end
63
- // local.get $fref
64
- // ref.test $type0
65
- // if $blocktype
66
- // i32.const 0
67
- // return
68
- // end
69
- // i32.const -1
70
- // )
71
- // )
72
36
73
- function getPyEMCountArgsPtr () {
37
+ function getPyEMTrampolinePtr () {
74
38
// Starting with iOS 18.3.1, WebKit on iOS has an issue with the garbage
75
39
// collector that breaks the call trampoline. See #130418 and
76
40
// https://bugs.webkit.org/show_bug.cgi?id=293113 for details.
@@ -84,74 +48,13 @@ function getPyEMCountArgsPtr() {
84
48
if (isIOS ) {
85
49
return 0 ;
86
50
}
87
-
88
- // Try to initialize countArgsFunc
89
- const code = new Uint8Array ([
90
- 0x00 , 0x61 , 0x73 , 0x6d , // \0asm magic number
91
- 0x01 , 0x00 , 0x00 , 0x00 , // version 1
92
- 0x01 , 0x1a , // Type section, body is 0x1a bytes
93
- 0x05 , // 6 entries
94
- 0x60 , 0x00 , 0x01 , 0x7f , // (type $type0 (func (param) (result i32)))
95
- 0x60 , 0x01 , 0x7f , 0x01 , 0x7f , // (type $type1 (func (param i32) (result i32)))
96
- 0x60 , 0x02 , 0x7f , 0x7f , 0x01 , 0x7f , // (type $type2 (func (param i32 i32) (result i32)))
97
- 0x60 , 0x03 , 0x7f , 0x7f , 0x7f , 0x01 , 0x7f , // (type $type3 (func (param i32 i32 i32) (result i32)))
98
- 0x60 , 0x00 , 0x00 , // (type $blocktype (func (param) (result)))
99
- 0x02 , 0x09 , // Import section, 0x9 byte body
100
- 0x01 , // 1 import (table $funcs (import "e" "t") 0 funcref)
101
- 0x01 , 0x65 , // "e"
102
- 0x01 , 0x74 , // "t"
103
- 0x01 , // importing a table
104
- 0x70 , // of entry type funcref
105
- 0x00 , 0x00 , // table limits: no max, min of 0
106
- 0x03 , 0x02 , // Function section
107
- 0x01 , 0x01 , // We're going to define one function of type 1 (func (param i32) (result i32))
108
- 0x07 , 0x05 , // export section
109
- 0x01 , // 1 export
110
- 0x01 , 0x66 , // called "f"
111
- 0x00 , // a function
112
- 0x00 , // at index 0
113
-
114
- 0x0a , 56 , // Code section,
115
- 0x01 , 54 , // one entry of length 54
116
- 0x01 , 0x01 , 0x70 , // one local of type funcref
117
- // Body of the function
118
- 0x20 , 0x00 , // local.get $fptr
119
- 0x25 , 0x00 , // table.get $funcs
120
- 0x22 , 0x01 , // local.tee $fref
121
- 0xfb , 0x14 , 0x03 , // ref.test $type3
122
- 0x04 , 0x04 , // if (type $blocktype)
123
- 0x41 , 0x03 , // i32.const 3
124
- 0x0f , // return
125
- 0x0b , // end block
126
-
127
- 0x20 , 0x01 , // local.get $fref
128
- 0xfb , 0x14 , 0x02 , // ref.test $type2
129
- 0x04 , 0x04 , // if (type $blocktype)
130
- 0x41 , 0x02 , // i32.const 2
131
- 0x0f , // return
132
- 0x0b , // end block
133
-
134
- 0x20 , 0x01 , // local.get $fref
135
- 0xfb , 0x14 , 0x01 , // ref.test $type1
136
- 0x04 , 0x04 , // if (type $blocktype)
137
- 0x41 , 0x01 , // i32.const 1
138
- 0x0f , // return
139
- 0x0b , // end block
140
-
141
- 0x20 , 0x01 , // local.get $fref
142
- 0xfb , 0x14 , 0x00 , // ref.test $type0
143
- 0x04 , 0x04 , // if (type $blocktype)
144
- 0x41 , 0x00 , // i32.const 0
145
- 0x0f , // return
146
- 0x0b , // end block
147
-
148
- 0x41 , 0x7f , // i32.const -1
149
- 0x0b // end function
150
- ]);
51
+ const code = HEAP8 .subarray (
52
+ _trampoline_inner_wasm ,
53
+ _trampoline_inner_wasm + HEAP32 [_trampoline_inner_wasm_length / 4 ]);
151
54
try {
152
55
const mod = new WebAssembly .Module (code );
153
56
const inst = new WebAssembly .Instance (mod , { e : { t : wasmTable } });
154
- return addFunction (inst .exports .f );
57
+ return addFunction (inst .exports .trampoline_call );
155
58
} catch (e ) {
156
59
// If something goes wrong, we'll null out _PyEM_CountFuncParams and fall
157
60
// back to the JS trampoline.
@@ -160,17 +63,17 @@ function getPyEMCountArgsPtr() {
160
63
}
161
64
162
65
addOnPreRun (() = > {
163
- const ptr = getPyEMCountArgsPtr ();
66
+ const ptr = getPyEMTrampolinePtr ();
164
67
Module ._PyEM_CountArgsPtr = ptr ;
165
- const offset = HEAP32 [__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET / 4 ];
68
+ const offset = HEAP32 [__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET / 4 ];
166
69
HEAP32 [(__PyRuntime + offset ) / 4 ] = ptr ;
167
70
});
168
71
);
169
72
170
73
void
171
74
_Py_EmscriptenTrampoline_Init (_PyRuntimeState * runtime )
172
75
{
173
- runtime -> emscripten_count_args_function = _PyEM_GetCountArgsPtr ();
76
+ runtime -> emscripten_trampoline = _PyEM_GetTrampolinePtr ();
174
77
}
175
78
176
79
// We have to be careful to work correctly with memory snapshots. Even if we are
@@ -196,23 +99,16 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func,
196
99
PyObject * args ,
197
100
PyObject * kw )
198
101
{
199
- CountArgsFunc count_args = _PyRuntime .emscripten_count_args_function ;
200
- if (count_args == 0 ) {
102
+ TrampolineFunc trampoline = _PyRuntime .emscripten_trampoline ;
103
+ if (trampoline == 0 ) {
201
104
return _PyEM_TrampolineCall_JS (func , self , args , kw );
202
105
}
203
- switch (count_args (func )) {
204
- case 0 :
205
- return ((zero_arg )func )();
206
- case 1 :
207
- return ((one_arg )func )(self );
208
- case 2 :
209
- return ((two_arg )func )(self , args );
210
- case 3 :
211
- return ((three_arg )func )(self , args , kw );
212
- default :
213
- PyErr_SetString (PyExc_SystemError , "Handler takes too many arguments" );
214
- return NULL ;
106
+ int success = 0 ;
107
+ PyObject * result = trampoline (& success , func , self , args , kw );
108
+ if (!success ) {
109
+ PyErr_SetString (PyExc_SystemError , "Handler takes too many arguments" );
215
110
}
111
+ return result ;
216
112
}
217
113
218
114
#endif
0 commit comments