• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #if defined(PY_CALL_TRAMPOLINE)
2 
3 #include <emscripten.h>             // EM_JS
4 #include <Python.h>
5 #include "pycore_runtime.h"         // _PyRuntime
6 
7 
8 /**
9  * This is the GoogleChromeLabs approved way to feature detect type-reflection:
10  * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js
11  */
12 EM_JS(int, _PyEM_detect_type_reflection, (), {
13     if (!("Function" in WebAssembly)) {
14         return false;
15     }
16     if (WebAssembly.Function.type) {
17         // Node v20
18         Module.PyEM_CountArgs = (func) => WebAssembly.Function.type(wasmTable.get(func)).parameters.length;
19     } else {
20         // Node >= 22, v8-based browsers
21         Module.PyEM_CountArgs = (func) => wasmTable.get(func).type().parameters.length;
22     }
23     return true;
24 });
25 
26 void
_Py_EmscriptenTrampoline_Init(_PyRuntimeState * runtime)27 _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime)
28 {
29     runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection();
30 }
31 
32 /**
33  * Backwards compatible trampoline works with all JS runtimes
34  */
35 EM_JS(PyObject*,
36 _PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func,
37                                   PyObject *arg1,
38                                   PyObject *arg2,
39                                   PyObject *arg3),
40 {
41     return wasmTable.get(func)(arg1, arg2, arg3);
42 }
43 );
44 
45 /**
46  * In runtimes with WebAssembly type reflection, count the number of parameters
47  * and cast to the appropriate signature
48  */
49 EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func),
50 {
51     let n = _PyEM_CountFuncParams.cache.get(func);
52 
53     if (n !== undefined) {
54         return n;
55     }
56     n = Module.PyEM_CountArgs(func);
57     _PyEM_CountFuncParams.cache.set(func, n);
58     return n;
59 }
60 _PyEM_CountFuncParams.cache = new Map();
61 )
62 
63 
64 typedef PyObject* (*zero_arg)(void);
65 typedef PyObject* (*one_arg)(PyObject*);
66 typedef PyObject* (*two_arg)(PyObject*, PyObject*);
67 typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*);
68 
69 
70 PyObject*
_PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func,PyObject * self,PyObject * args,PyObject * kw)71 _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func,
72                                 PyObject* self,
73                                 PyObject* args,
74                                 PyObject* kw)
75 {
76     switch (_PyEM_CountFuncParams(func)) {
77         case 0:
78             return ((zero_arg)func)();
79         case 1:
80             return ((one_arg)func)(self);
81         case 2:
82             return ((two_arg)func)(self, args);
83         case 3:
84             return ((three_arg)func)(self, args, kw);
85         default:
86             PyErr_SetString(PyExc_SystemError,
87                             "Handler takes too many arguments");
88             return NULL;
89     }
90 }
91 
92 #endif
93