mirror of
https://github.com/python/cpython.git
synced 2024-12-14 12:26:47 +08:00
6b179adb8c
There is a WIP proposal to enable webassembly stack switching which have been implemented in v8: https://github.com/WebAssembly/js-promise-integration It is not possible to switch stacks that contain JS frames so the Emscripten JS trampolines that allow calling functions with the wrong number of arguments don't work in this case. However, the js-promise-integration proposal requires the [type reflection for Wasm/JS API](https://github.com/WebAssembly/js-types) proposal, which allows us to actually count the number of arguments a function expects. For better compatibility with stack switching, this PR checks if type reflection is available, and if so we use a switch block to decide the appropriate signature. If type reflection is unavailable, we should use the current EMJS trampoline. We cache the function argument counts since when I didn't cache them performance was negatively affected. Co-authored-by: T. Wouters <thomas@python.org> Co-authored-by: Brett Cannon <brett@python.org>
83 lines
2.2 KiB
C
83 lines
2.2 KiB
C
#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, (), {
|
|
return "Function" in WebAssembly;
|
|
});
|
|
|
|
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 = WebAssembly.Function.type(wasmTable.get(func)).parameters.length;
|
|
_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
|