| #if defined(PY_CALL_TRAMPOLINE) | |
| #include <emscripten.h> // EM_JS | |
| #include <Python.h> | |
| #include "pycore_runtime.h" // _PyRuntime | |
| /** | |
| * This is the GoogleChromeLabs approved way to feature detect type-reflection: | |
| * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js | |
| */ | |
| EM_JS(int, _PyEM_detect_type_reflection, (), { | |
| if (!("Function" in WebAssembly)) { | |
| return false; | |
| } | |
| if (WebAssembly.Function.type) { | |
| // Node v20 | |
| Module.PyEM_CountArgs = (func) => WebAssembly.Function.type(wasmTable.get(func)).parameters.length; | |
| } else { | |
| // Node >= 22, v8-based browsers | |
| Module.PyEM_CountArgs = (func) => wasmTable.get(func).type().parameters.length; | |
| } | |
| return true; | |
| }); | |
| void | |
| _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) | |
| { | |
| runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection(); | |
| } | |
| /** | |
| * Backwards compatible trampoline works with all JS runtimes | |
| */ | |
| EM_JS(PyObject*, | |
| _PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func, | |
| PyObject *arg1, | |
| PyObject *arg2, | |
| PyObject *arg3), | |
| { | |
| return wasmTable.get(func)(arg1, arg2, arg3); | |
| } | |
| ); | |
| /** | |
| * In runtimes with WebAssembly type reflection, count the number of parameters | |
| * and cast to the appropriate signature | |
| */ | |
| EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), | |
| { | |
| let n = _PyEM_CountFuncParams.cache.get(func); | |
| if (n !== undefined) { | |
| return n; | |
| } | |
| n = Module.PyEM_CountArgs(func); | |
| _PyEM_CountFuncParams.cache.set(func, n); | |
| return n; | |
| } | |
| _PyEM_CountFuncParams.cache = new Map(); | |
| ) | |
| typedef PyObject* (*zero_arg)(void); | |
| typedef PyObject* (*one_arg)(PyObject*); | |
| typedef PyObject* (*two_arg)(PyObject*, PyObject*); | |
| typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); | |
| PyObject* | |
| _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, | |
| PyObject* self, | |
| PyObject* args, | |
| PyObject* kw) | |
| { | |
| switch (_PyEM_CountFuncParams(func)) { | |
| case 0: | |
| return ((zero_arg)func)(); | |
| case 1: | |
| return ((one_arg)func)(self); | |
| case 2: | |
| return ((two_arg)func)(self, args); | |
| case 3: | |
| return ((three_arg)func)(self, args, kw); | |
| default: | |
| PyErr_SetString(PyExc_SystemError, | |
| "Handler takes too many arguments"); | |
| return NULL; | |
| } | |
| } | |
| #endif |