|
1 | 1 | #include "emscripten.h" |
2 | 2 | #include "stdio.h" |
| 3 | +// for Py_EMSCRIPTEN_DYNAMIC_LINKING |
| 4 | +#include "pyconfig.h" |
3 | 5 |
|
4 | 6 | // All system calls: return nonnegative number on success, return -errno on |
5 | 7 | // failure. Negative results get stored back into errno here: |
@@ -54,7 +56,7 @@ __attribute__((constructor)) void __syscall_init_umask(void) { |
54 | 56 | #define EM_JS_MACROS(ret, func_name, args, body...) \ |
55 | 57 | EM_JS(ret, func_name, args, body) |
56 | 58 |
|
57 | | -EM_JS_MACROS(void, _emscripten_promising_main_js, (void), { |
| 59 | +EM_JS_MACROS(void, _emscripten_setup_async_input_device_js, (void), { |
58 | 60 | // Define FS.createAsyncInputDevice(), This is quite similar to |
59 | 61 | // FS.createDevice() defined here: |
60 | 62 | // https://github.com/emscripten-core/emscripten/blob/4.0.11/src/lib/libfs.js?plain=1#L1642 |
@@ -103,34 +105,83 @@ EM_JS_MACROS(void, _emscripten_promising_main_js, (void), { |
103 | 105 | FS.registerDevice(dev, ops); |
104 | 106 | return FS.mkdev(path, mode, dev); |
105 | 107 | }; |
| 108 | +}) |
| 109 | + |
| 110 | +EM_JS_DEPS(_emscripten_setup_async_input_device, "$FS,$PATH,$FS_getMode"); |
| 111 | + |
| 112 | +__attribute__((constructor)) void _emscripten_setup_async_input_device(void) { |
| 113 | + _emscripten_setup_async_input_device_js(); |
| 114 | +} |
| 115 | + |
| 116 | +// Wrapping the program entry point with WebAssembly.promising() is what lets |
| 117 | +// main() suspend, so that a blocking read() can await the input device |
| 118 | +// installed above. How Emscripten hands us that entry point depends on how we |
| 119 | +// were linked; see getEntryFunction() in Emscripten's src/parseTools.mjs. |
| 120 | +// |
| 121 | +// * With -sMAIN_MODULE, Emscripten looks main up through resolveGlobalSymbol(), |
| 122 | +// so we hook the lookup. resolveGlobalSymbol() is defined in libdylink.js, |
| 123 | +// which is only linked in for MAIN_MODULE builds. |
| 124 | +// |
| 125 | +// * Statically linked, Emscripten calls the `_main` binding directly. We rebind |
| 126 | +// it to a wrapper around the raw export, because WebAssembly.promising() |
| 127 | +// rejects the createExportWrapper() shim that `_main` normally holds. A |
| 128 | +// static libpython is usually embedded in a host application whose main() is |
| 129 | +// not Python's, so an embedder has to opt in by setting |
| 130 | +// Module.Py_EmscriptenPromisingMain before the runtime starts. |
| 131 | +#ifdef Py_EMSCRIPTEN_DYNAMIC_LINKING |
| 132 | +#define _Py_EM_INSTALL_PROMISING_MAIN \ |
| 133 | + const origResolveGlobalSymbol = resolveGlobalSymbol; \ |
| 134 | + resolveGlobalSymbol = function (name, direct = false) { \ |
| 135 | + const orig = origResolveGlobalSymbol(name, direct); \ |
| 136 | + if (name === "main") { \ |
| 137 | + orig.sym = _PyEM_promisingEntryPoint(orig.sym); \ |
| 138 | + } \ |
| 139 | + return orig; \ |
| 140 | + }; |
| 141 | +#else |
| 142 | +#define _Py_EM_INSTALL_PROMISING_MAIN \ |
| 143 | + if (!Module.Py_EmscriptenPromisingMain) { \ |
| 144 | + return; \ |
| 145 | + } \ |
| 146 | + _main = _PyEM_promisingEntryPoint(wasmExports["main"]); |
| 147 | +#endif |
| 148 | + |
| 149 | +EM_JS_MACROS(void, _emscripten_promising_main_js, (void), { |
106 | 150 | if (!WebAssembly.promising) { |
107 | 151 | // No stack switching support =( |
108 | 152 | return; |
109 | 153 | } |
110 | | - const origResolveGlobalSymbol = resolveGlobalSymbol; |
| 154 | + _Py_EM_INSTALL_PROMISING_MAIN |
| 155 | +} |
| 156 | +// * wrap the entry point with WebAssembly.promising, |
| 157 | +// * call exit_with_live_runtime() to prevent emscripten from shutting down |
| 158 | +// the runtime before the promise resolves, |
| 159 | +// * call onExit / process.exit ourselves, since exit_with_live_runtime() |
| 160 | +// prevented Emscripten from calling it normally. |
| 161 | +function _PyEM_promisingEntryPoint(orig) { |
111 | 162 | if (ENVIRONMENT_IS_NODE && !Module.onExit) { |
112 | 163 | Module.onExit = (code) => process.exit(code); |
113 | 164 | } |
114 | | - // * wrap the main symbol with WebAssembly.promising, |
115 | | - // * call exit_with_live_runtime() to prevent emscripten from shutting down |
116 | | - // the runtime before the promise resolves, |
117 | | - // * call onExit / process.exit ourselves, since exit_with_live_runtime() |
118 | | - // prevented Emscripten from calling it normally. |
119 | | - resolveGlobalSymbol = function (name, direct = false) { |
120 | | - const orig = origResolveGlobalSymbol(name, direct); |
121 | | - if (name === "main") { |
122 | | - const main = WebAssembly.promising(orig.sym); |
123 | | - orig.sym = (...args) => { |
124 | | - (async () => { |
125 | | - const ret = await main(...args); |
126 | | - Module.onExit?.(ret); |
127 | | - })(); |
128 | | - _emscripten_exit_with_live_runtime(); |
129 | | - }; |
130 | | - } |
131 | | - return orig; |
| 165 | + const main = WebAssembly.promising(orig); |
| 166 | + return (...args) => { |
| 167 | + (async () => { |
| 168 | + const ret = await main(...args); |
| 169 | + Module.onExit?.(ret); |
| 170 | + })(); |
| 171 | + _emscripten_exit_with_live_runtime(); |
132 | 172 | }; |
133 | | -}) |
| 173 | +} |
| 174 | +) |
| 175 | + |
| 176 | +// EM_JS bodies are emitted verbatim, so Emscripten's dependency tracker cannot |
| 177 | +// see what they reference. Undeclared, these are dropped from the JS glue |
| 178 | +// without a diagnostic and the body throws a ReferenceError at initRuntime. |
| 179 | +#ifdef Py_EMSCRIPTEN_DYNAMIC_LINKING |
| 180 | +EM_JS_DEPS(_emscripten_promising_main, |
| 181 | + "$resolveGlobalSymbol,emscripten_exit_with_live_runtime"); |
| 182 | +#else |
| 183 | +EM_JS_DEPS(_emscripten_promising_main, "emscripten_exit_with_live_runtime"); |
| 184 | +#endif |
134 | 185 |
|
135 | 186 | __attribute__((constructor)) void _emscripten_promising_main(void) { |
136 | 187 | _emscripten_promising_main_js(); |
|
0 commit comments