Repository: pyodide/pyodide

Issue:
TypeError: getWasmTableEntry(...) is not a function
## 🐛 Bug

```
pyodide.asm.js:10 Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers.
pyodide.asm.js:10 The cause of the fatal error was:
pyodide.asm.js:10 TypeError: getWasmTableEntry(...) is not a function
    at invoke_ii (pyodide.asm.js:10:1544237)
    at 00bcba5a:0x54516
    at invoke_viiiiiiiiiiiiiii (pyodide.asm.js:10:1548668)
    at 00bcba5a:0x414a2
    at invoke_viii (pyodide.asm.js:10:1545014)
    at 00bcba5a:0x56286
    at method_call_trampoline (pyodide.asm.js:10:216572)
    at pyodide.asm.wasm:0x162162
    at method_call_trampoline (pyodide.asm.js:10:216572)
    at pyodide.asm.wasm:0x126317
API.fatal_error @ pyodide.asm.js:10
Module.callPyObjectKwargs @ pyodide.asm.js:10
Module.callPyObject @ pyodide.asm.js:10
apply @ pyodide.asm.js:10
apply @ pyodide.asm.js:10
runPython @ pyodide.asm.js:10
main @ (index):15
await in main (async)
(anonymous) @ (index):22
pyodide.asm.js:10 Stack (most recent call first):
pyodide.asm.js:10   File "<exec>", line 5 in <module>
pyodide.asm.js:10   File "/lib/python3.10/site-packages/_pyodide/_base.py", line 304 in run
pyodide.asm.js:10   File "/lib/python3.10/site-packages/_pyodide/_base.py", line 435 in eval_code
(index):21 Uncaught (in promise) TypeError: getWasmTableEntry(...) is not a function
    at invoke_ii (pyodide.asm.js:10:1544237)
    at 00bcba5a:0x54516
    at invoke_viiiiiiiiiiiiiii (pyodide.asm.js:10:1548668)
    at 00bcba5a:0x414a2
    at invoke_viii (pyodide.asm.js:10:1545014)
    at 00bcba5a:0x56286
    at method_call_trampoline (pyodide.asm.js:10:216572)
    at pyodide.asm.wasm:0x162162
    at method_call_trampoline (pyodide.asm.js:10:216572)
    at pyodide.asm.wasm:0x126317
```

### To Reproduce

https://github.com/kitao/pyxel/tree/maturin

* First install the maturin beta version: `pip install -U --pre maturin`
* Build with maturin: `RUSTUP_TOOLCHAIN=nightly maturin build -o dist --release --target wasm32-unknown-emscripten`
* Run `./scripts/start_server` and open `http://localhost:8000` in browser, see the error in console.

### Expected behavior

No `TypeError: getWasmTableEntry(...) is not a function` in console.

### Environment

- Pyodide Version<!-- (e.g. 1.8.1) -->: 0.21.0
- Browser version<!-- (e.g. Chrome 95.0.4638.54) -->: 104.0.5112.79
- Any other relevant information:

<!-- If you are building Pyodide by yourself, please also include these information: -->

<!--
- Commit hash of Pyodide git repository:
- Build environment<!--(e.g. Ubuntu 18.04, pyodide/pyodide-env:19 docker)- ->:
-->

### Additional context

<!-- Add any other context about the problem here. -->



Discussion:
<img width="540" alt="image" src="https://user-images.githubusercontent.com/1556054/184469209-50bedef2-60e5-4aff-b9cf-3b4473f234fb.png">

I do see `getWasmTableEntry` function in minified `pyodide.asm.js`, kinda odd.

Further debugging shows that `getWasmTableEntry(0)` returned `null` so `getWasmTableEntry(0)(a1)` throws `TypeError: getWasmTableEntry(...) is not a function`

<img width="618" alt="image" src="https://user-images.githubusercontent.com/1556054/184469476-2e96c3d4-c64e-4763-9dc1-321fe136ca03.png">

Use a debug build of the wheel shows a different error

```
pyodide.asm.js:formatted:3231 TypeError: Cannot read properties of undefined (reading 'apply')
    at stubs.<computed> (pyodide.asm.js:formatted:8037:49)
    at sdl2::video::_$LT$impl$u20$sdl2..sdl..VideoSubsystem$GT$::desktop_display_mode::h4260488f51b53104 (138d923e:0x3a8287)
    at invoke_viii (pyodide.asm.js:formatted:49510:41)
    at _$LT$pyxel..platform_sdl2..PlatformSdl2$u20$as$u20$pyxel..platform..Platform$GT$::new::h2b4331a8480d882d (138d923e:0x230577)
    at pyxel::Pyxel::new::hf0a38d431462844d (138d923e:0x28edcf)
    at pyxel_wrapper::system_wrapper::init::h3708483ad3f6cd52 (138d923e:0x10792e)
    at pyxel_wrapper::system_wrapper::__pyfunction_init::_$u7b$$u7b$closure$u7d$$u7d$::hdacd4ac63436b841 (138d923e:0x1aa522)
    at std::panicking::try::do_call::h0deb1ab2490d9ce2 (138d923e:0xcb16b)
    at invoke_vi (pyodide.asm.js:formatted:49543:41)
    at __rust_try (138d923e:0x8e92e)
```

Patch https://github.com/kitao/pyxel/blob/30bb7b5e087408ed3737ab1e7e224bf9bb3f789d/crates/pyxel-engine/src/platform_sdl2.rs#L59-L60 to `let scale = get_scale(width, height);` reverts back to the original `TypeError: getWasmTableEntry(...) is not a function` error

<img width="904" alt="image" src="https://user-images.githubusercontent.com/1556054/184470795-39bac57b-1dd7-4c63-88af-aeb4aea085e1.png">


Thanks for the report, @messense. I can reproduce the error.
For those who want to quickly reproduce the error, here is the wheel of pyxel (compressed due to Github limitation).

[pyxel-1.8.0-cp37-abi3-emscripten_3_1_14_wasm32.zip](https://github.com/pyodide/pyodide/files/9330928/pyxel-1.8.0-cp37-abi3-emscripten_3_1_14_wasm32.zip)

What happens if you build with `panic=abort`?
@hoodmane With `panic=abort`

```
TypeError: Cannot read properties of undefined (reading 'apply')
    at stubs.<computed> (pyodide.asm.js:10:253214)
    at 00898aaa:0x2393f
    at method_call_trampoline (pyodide.asm.js:10:216572)
    at pyodide.asm.wasm:0x162162
    at method_call_trampoline (pyodide.asm.js:10:216572)
    at pyodide.asm.wasm:0x126317
    at pyodide.asm.wasm:0x1e246f
    at pyodide.asm.wasm:0x1e00b3
    at pyodide.asm.wasm:0x1da6a5
    at pyodide.asm.wasm:0x1d9a6a
```
Looks like progress. We should patch library_dylink to report the name of the missing symbols so that we can get better error messages in this case. We can probably contribute such a change to emscripten.
Add a conditional breakpoint shows that the missing symbols is `SDL_GetDesktopDisplayMode`, seems like SDL2 isn't properly linked? I don't know much about linking in Emscripten, not sure what went wrong. 
I thought the linking error occurred because LLVM version mismatch.
Emscripten 3.1.14 uses LLVM 15, but rustc 1.65.0-nightly (latest) uses LLVM 14.

However, when I downgraded pyodide to 0.20.0 and emscripten 2.0.27 (which uses LLVM 14) , it threw the following error:
![image](https://user-images.githubusercontent.com/20869932/184562851-736b3f96-1003-4894-b712-ac5e344b5f49.png)

So I was not able to find out whether the LLVM version matters.

To reproduce:

I built it using `RUSTUP_TOOLCHAIN=nightly maturin build -o dist --release --target wasm32-unknown-emscripten -- -C target-feature=+mutable-globals`, with rustc 1.65.0-nightly. 

And I used `pyodide.loadPackage` to load the emscripten wheel. (Due to the limitation of pyodide 0.20.0)
```js

        async function main() {
            let pyodide = await loadPyodide();
            await pyodide.loadPackage(location.href.replace(/\/[^\/]+?\.[^\/]+?$/, '/')+"pyxel-1.8.0-cp37-abi3-emscripten_2_0_27_wasm32.whl");
            await pyodide.runPython(`
                import pyxel
                print("PYXEL_VERSION = ", pyxel.PYXEL_VERSION)
                print("KEY_BACKSPACE = ", pyxel.KEY_BACKSPACE)
                pyxel.init(100, 100)
            `);
        }
        main();
    
```
Probably the issue is that we aren't linking SDL into Pyodide?
> Probably the issue is that we aren't linking SDL into Pyodide?

OK, I've just read the [Dynamic Linking](https://emscripten.org/docs/compiling/Dynamic-Linking.html) documentation of Emscripten now. In this case I think Pyodide is the **main module** and pyxel is the **side module**, because only main module has system libraries linked in, in order to make it work we need to link SDL into Pyodide, right?

@hoodmane Do we have an easy way to do a customized Pyodide build with SDL2 linked in?
There is a closed issue that seems related to this.
https://github.com/pyodide/pyodide/issues/1657#issuecomment-870091116

Should I rebuild pyodide with modified `MAIN_MODULE_LDFLAGS` in [Makefile.envs](https://github.com/pyodide/pyodide/blob/main/Makefile.envs#L98)?

I guess another option is to statically link SDL2 in pyxel? See https://github.com/Rust-SDL2/rust-sdl2#bundled-feature and https://github.com/Rust-SDL2/rust-sdl2#static-linking-in-linux, I haven't tried so not sure whether it will build or not for `wasm32-unknown-emscripten` target.
> Should I rebuild pyodide with modified `MAIN_MODULE_LDFLAGS` in [Makefile.envs](https://github.com/pyodide/pyodide/blob/main/Makefile.envs?rgh-link-date=2022-08-15T07%3A29%3A36Z#L98)?

Yes. You need to add `-sUSE_SDL=2` to the `MAIN_MODULE_LDFLAGS`.

> another option is to statically link SDL2 in pyxel?

This is not possible because SDL2 is implemented in Javascript and Emscripten does not currently allow dynamically loading Javascript.
I rebuilt pyodide with `-sUSE_SDL=2`, then the message changed:
![image](https://user-images.githubusercontent.com/20869932/184601948-6083f0f9-2219-43da-adf1-7901d21e18cd.png)

I assumed this is related to a canvas issue(https://github.com/emscripten-ports/SDL2/issues/130),
so I added a blank canvas with id "canvas":
```html
<!-- Bootstrap HTML for running the unit tests. -->
<!DOCTYPE html>
<html>
  <head>
    <title>pyodide</title>
    <script src="./pyodide.js"></script>
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <script type="text/javascript">
      async function main(){
        const pyodide = await loadPyodide();
        await pyodide.loadPackage("micropip")
        const micropip = pyodide.pyimport("micropip")
        await micropip.install("pyxel-1.8.0-cp37-abi3-emscripten_3_1_14_wasm32.whl")
        await pyodide.runPython(`
                import pyxel
                pyxel.init(600,480)
        `)
      }
      main()
    </script>
  </body>
</html>
```

Then the error changed:
![image](https://user-images.githubusercontent.com/20869932/184601384-e2c89c66-0f03-4ba1-b39f-f0d2fba1d7d4.png)


Although it crashes, the initial linking issue seems to be resolved.

@km19809 Can you upload the built `pyodide.js` somewhere?
@messense 
[Dropbox](https://www.dropbox.com/s/4tba71egs6jhza1/pyodide_sdl.tar?dl=0)

I think this issue is resolved, but I post the link here as a (potential) conclusion.
I think setting [`-sGL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0`](https://github.com/emscripten-core/emscripten/blob/main/src/library_webgl.js#L632) would fix the most recent error.

You may also want [`-sMAX_WEBGL_VERSION=2`.](https://emscripten.org/docs/optimizing/Optimizing-WebGL.html?highlight=webgl#which-gl-mode-to-target)

Thanks for the help!
Please don't hesitate to open a follow up issue if you can't figure out how to get it working.
> I think setting [`-sGL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0`](https://github.com/emscripten-core/emscripten/blob/main/src/library_webgl.js#L632) would fix the most recent error.
> 
> You may also want [`-sMAX_WEBGL_VERSION=2`.](https://emscripten.org/docs/optimizing/Optimizing-WebGL.html?highlight=webgl#which-gl-mode-to-target)

This resolved SafariWebGL2 issue. (It still crashes though 😂)
Thank you a lot!
Using the new cibuildwheel support, both boost-histogram and iminuit report this error.
I can reproduce it, I'll take a look soon =)

Resolving patch:
diff --git a/packages/cpp-exceptions-test2/src/cpp_exceptions_test2/__init__.py b/packages/cpp-exceptions-test2/src/cpp_exceptions_test2/__init__.py
new file mode 100644
diff --git a/packages/cpp-exceptions-test2/src/setup.py b/packages/cpp-exceptions-test2/src/setup.py
new file mode 100644
--- /dev/null
+++ b/packages/cpp-exceptions-test2/src/setup.py
@@ -0,0 +1,16 @@
+from setuptools import Extension, setup
+
+setup(
+    name="cpp-exceptions-test2",
+    version="1.0",
+    ext_modules=[
+        Extension(
+            name="cpp_exceptions_test2",  # as it would be imported
+            # may include packages/namespaces separated by `.`
+            language="c++",
+            sources=[
+                "cpp_exceptions_test2.cpp"
+            ],  # all sources are compiled into a single binary file
+        ),
+    ],
+)


Test patch:
diff --git a/src/tests/test_cmdline_runner.py b/src/tests/test_cmdline_runner.py
--- a/src/tests/test_cmdline_runner.py
+++ b/src/tests/test_cmdline_runner.py
@@ -502,3 +502,19 @@ def test_sys_exit(selenium, venv):
     assert result.returncode == 12
     assert result.stdout == ""
     assert result.stderr == ""
+
+
+def test_cpp_exceptions(selenium, venv):
+    result = install_pkg(venv, "cpp-exceptions-test2")
+    print(result.stdout)
+    print(result.stderr)
+    assert result.returncode == 0
+    result = subprocess.run(
+        [venv / "bin/python", "-c", "import cpp_exceptions_test2"],
+        capture_output=True,
+        encoding="utf-8",
+    )
+    print(result.stdout)
+    print(result.stderr)
+    assert result.returncode == 1
+    assert "ImportError: oops" in result.stderr