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,159 +27,55 @@ 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 );
22
-
23
- EM_JS (CountArgsFunc , _PyEM_GetCountArgsPtr , ( ), {
24
- return Module ._PyEM_CountArgsPtr ; // initialized below
25
- }
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
-
73
- function getPyEMCountArgsPtr () {
74
- // Starting with iOS 18.3.1, WebKit on iOS has an issue with the garbage
75
- // collector that breaks the call trampoline. See #130418 and
76
- // https://bugs.webkit.org/show_bug.cgi?id=293113 for details.
77
- let isIOS = globalThis .navigator && (
78
- /iPad |iPhone |iPod /.test (navigator .userAgent ) ||
79
- // Starting with iPadOS 13, iPads might send a platform string that looks like a desktop Mac.
80
- // To differentiate, we check if the platform is 'MacIntel' (common for Macs and newer iPads)
81
- // AND if the device has multi-touch capabilities (navigator.maxTouchPoints > 1)
82
- (navigator .platform == = 'MacIntel' && typeof navigator .maxTouchPoints != = 'undefined' && navigator .maxTouchPoints > 1 )
83
- );
84
- if (isIOS ) {
30
+ EMSCRIPTEN_KEEPALIVE const int _PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET =
31
+ offsetof(_PyRuntimeState , emscripten_trampoline );
32
+
33
+ EM_JS (
34
+ TrampolineFunc , _PyEM_GetTrampolinePtr , (void ) ,
35
+ {
36
+ return Module ._PyEM_CountArgsPtr ; // initialized below
37
+ } function getPyEMTrampolinePtr () {
38
+ // Starting with iOS 18.3.1, WebKit on iOS has an issue with the garbage
39
+ // collector that breaks the call trampoline. See #130418 and
40
+ // https://bugs.webkit.org/show_bug.cgi?id=293113 for details.
41
+ let isIOS = globalThis .navigator &&
42
+ (/ iPad | iPhone | iPod /.test (navigator .userAgent ) ||
43
+ // Starting with iPadOS 13, iPads might send a platform
44
+ // string that looks like a desktop Mac. To differentiate, we
45
+ // check if the platform is 'MacIntel' (common for Macs and
46
+ // newer iPads) AND if the device has multi-touch
47
+ // capabilities (navigator.maxTouchPoints > 1)
48
+ (navigator .platform ==
49
+ = 'MacIntel' && typeof navigator .maxTouchPoints !=
50
+ = 'undefined' && navigator .maxTouchPoints > 1 ));
51
+ if (isIOS ) {
85
52
return 0 ;
86
- }
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
- ]);
151
- try {
53
+ }
54
+ const code = HEAP8 .subarray (
55
+ _trampoline_inner_wasm ,
56
+ _trampoline_inner_wasm + HEAP32 [_trampoline_inner_wasm_length / 4 ]);
57
+ try {
152
58
const mod = new WebAssembly .Module (code );
153
59
const inst = new WebAssembly .Instance (mod , { e : { t : wasmTable } });
154
- return addFunction (inst .exports .f );
155
- } catch (e ) {
60
+ return addFunction (inst .exports .trampoline_call );
61
+ } catch (e ) {
156
62
// If something goes wrong, we'll null out _PyEM_CountFuncParams and fall
157
63
// back to the JS trampoline.
158
64
return 0 ;
65
+ }
159
66
}
160
- }
161
67
162
- addOnPreRun (() = > {
163
- const ptr = getPyEMCountArgsPtr ();
164
- Module ._PyEM_CountArgsPtr = ptr ;
165
- const offset = HEAP32 [__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET / 4 ];
166
- HEAP32 [(__PyRuntime + offset ) / 4 ] = ptr ;
167
- });
168
- );
68
+ addOnPreRun (() = > {
69
+ const ptr = getPyEMTrampolinePtr ();
70
+ Module ._PyEM_CountArgsPtr = ptr ;
71
+ const offset = HEAP32 [__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET / 4 ];
72
+ HEAP32 [(__PyRuntime + offset ) / 4 ] = ptr ;
73
+ }););
169
74
170
75
void
171
76
_Py_EmscriptenTrampoline_Init (_PyRuntimeState * runtime )
172
77
{
173
- runtime -> emscripten_count_args_function = _PyEM_GetCountArgsPtr ();
78
+ runtime -> emscripten_trampoline = _PyEM_GetTrampolinePtr ();
174
79
}
175
80
176
81
// We have to be careful to work correctly with memory snapshots. Even if we are
@@ -196,23 +101,16 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func,
196
101
PyObject * args ,
197
102
PyObject * kw )
198
103
{
199
- CountArgsFunc count_args = _PyRuntime .emscripten_count_args_function ;
200
- if (count_args == 0 ) {
201
- return _PyEM_TrampolineCall_JS (func , self , args , kw );
202
- }
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 ;
215
- }
104
+ TrampolineFunc trampoline = _PyRuntime .emscripten_trampoline ;
105
+ if (trampoline == 0 ) {
106
+ return _PyEM_TrampolineCall_JS (func , self , args , kw );
107
+ }
108
+ int success = 0 ;
109
+ PyObject * result = trampoline (& success , func , self , args , kw );
110
+ if (!success ) {
111
+ PyErr_SetString (PyExc_SystemError , "Handler takes too many arguments" );
112
+ }
113
+ return result ;
216
114
}
217
115
218
116
#endif
0 commit comments