Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.3k
gh-106213: Make Emscripten trampolines work with JSPI#106219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from8 commits
8e2ad18
6903fd5
af6850c
ffb24ff
85d88ec
9d8f23e
2dc1399
418efb4
d900dfd
62a2f7f
cd3f8bf
2c45f1b
3951848
dfd43d7
b0b50f4
1c1b9ed
44f0371
bd609e5
a00d94f
68f4dba
1d49e79
afb9ad5
aa213e0
00afb90
20ad6ab
ae7584c
1b6b6af
381bb12
58c7613
eccf06d
5fff95d
f08a02b
6b27584
22a8c75
b40bb7b
28b33b3
9ecc480
4aa1487
a57dfdc
6358371
6781a48
fd59df4
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#ifndef Py_EMSCRIPTEN_TRAMPOLINE_H | ||
#define Py_EMSCRIPTEN_TRAMPOLINE_H | ||
/** | ||
* C function call trampolines to mitigate bad function pointer casts. | ||
* | ||
* Section 6.3.2.3, paragraph 8 reads: | ||
* | ||
* A pointer to a function of one type may be converted to a pointer to a | ||
* function of another type and back again; the result shall compare equal to | ||
* the original pointer. If a converted pointer is used to call a function | ||
* whose type is not compatible with the pointed-to type, the behavior is | ||
* undefined. | ||
* | ||
* Typical native ABIs ignore additional arguments or fill in missing values | ||
* with 0/NULL in function pointer cast. Compilers do not show warnings when a | ||
* function pointer is explicitly casted to an incompatible type. | ||
* | ||
* Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict | ||
* function signature checks. Argument count, types, and return type must match. | ||
* | ||
* Third party code unintentionally rely on problematic fpcasts. The call | ||
* trampoline mitigates common occurrences of bad fpcasts on Emscripten. | ||
*/ | ||
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) | ||
void _Py_EmscriptenTrampoline_Init(void); | ||
PyObject* | ||
_PyEM_TrampolineCall(PyCFunctionWithKeywords func, | ||
PyObject* self, | ||
PyObject* args, | ||
PyObject* kw); | ||
#define _PyCFunction_TrampolineCall(meth, self, args) \ | ||
_PyEM_TrampolineCall( \ | ||
(*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL) | ||
#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ | ||
_PyEM_TrampolineCall((meth), (self), (args), (kw)) | ||
#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) | ||
#define _Py_EmscriptenTrampoline_Init() | ||
#define _PyCFunction_TrampolineCall(meth, self, args) \ | ||
(meth)((self), (args)) | ||
#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ | ||
(meth)((self), (args), (kw)) | ||
#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) | ||
#endif // ndef Py_EMSCRIPTEN_SIGNAL_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Changed the way that Emscripten call trampolines work for compatibility with | ||
Wasm/JS Promise integration. |
hoodmane marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#if defined(PY_CALL_TRAMPOLINE) | ||
#include <emscripten.h> | ||
#include <Python.h> | ||
static int type_reflection_available; | ||
erlend-aasland marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
/** | ||
* 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(){ | ||
type_reflection_available = _PyEM_detect_type_reflection(); | ||
} | ||
/** | ||
* Backwards compatible trampoline works with all JS runtimes | ||
*/ | ||
EM_JS_DEPS(_PyEMJS_TrampolineCall, "$getWasmTableEntry") | ||
EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { | ||
return getWasmTableEntry(func)(arg1, arg2, arg3); | ||
} | ||
); | ||
erlend-aasland marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
/** | ||
* 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(getWasmTableEntry(func)).parameters.length; | ||
erlend-aasland marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
_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(PyCFunctionWithKeywords func, | ||
PyObject* self, | ||
PyObject* args, | ||
PyObject* kw) | ||
{ | ||
if (!type_reflection_available) { | ||
return _PyEMJS_TrampolineCall(func, self, args, kw); | ||
} else { | ||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -7,6 +7,7 @@ | ||
#include "pycore_codecs.h" // _PyCodec_Lookup() | ||
#include "pycore_context.h" // _PyContext_Init() | ||
#include "pycore_dict.h" // _PyDict_Fini() | ||
#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() | ||
#include "pycore_exceptions.h" // _PyExc_InitTypes() | ||
#include "pycore_fileutils.h" // _Py_ResetForceASCII() | ||
#include "pycore_floatobject.h" // _PyFloat_InitTypes() | ||
@@ -542,6 +543,9 @@ pycore_init_runtime(_PyRuntimeState *runtime, | ||
if (_PyStatus_EXCEPTION(status)) { | ||
return status; | ||
} | ||
hoodmane marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
_Py_EmscriptenTrampoline_Init(); | ||
return _PyStatus_OK(); | ||
} | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.