diff --git a/test/test_freetype_exc.cpp b/test/test_freetype_exc.cpp new file mode 100644 index 0000000000000..dfe45d4ed2075 --- /dev/null +++ b/test/test_freetype_exc.cpp @@ -0,0 +1,31 @@ +/* + * Minimal test that links FreeType and exercises C++ exception handling. + * When the freetype port is built with legacy EH but the application uses + * standardized Wasm EH (-sWASM_LEGACY_EXCEPTIONS=0), the resulting .wasm + * contains a mix of legacy and new EH instructions and fails at + * instantiation time. + */ +#include +#include +#include +#include FT_FREETYPE_H + +int main() { + // Exercise C++ exception handling so that new-EH instructions are emitted. + try { + throw std::runtime_error("test"); + } catch (const std::exception& e) { + printf("caught: %s\n", e.what()); + } + + // Also call into FreeType so it is actually linked. + FT_Library library; + FT_Error error = FT_Init_FreeType(&library); + if (error) { + printf("FT_Init_FreeType failed: %d\n", error); + return 1; + } + printf("FreeType initialized successfully\n"); + FT_Done_FreeType(library); + return 0; +} diff --git a/test/test_other.py b/test/test_other.py index 360c8077d2187..3944f53cbb08b 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -2630,6 +2630,39 @@ def test_freetype_pthreads(self): copy_asset('freetype/LiberationSansBold.ttf') self.do_run_in_out_file_test('test_freetype.c', cflags=['--embed-file=LiberationSansBold.ttf', '-pthread', '-sUSE_FREETYPE']) + @requires_network + def test_freetype_wasm_eh(self): + # Verify that the freetype port selects the correct new-EH variant + # (libfreetype-wasmsjlj.a) when using standardized Wasm EH combined with + # SUPPORT_LONGJMP=wasm. Without the new-EH variant the port falls back to + # libfreetype-legacysjlj.a which is built with legacy EH instructions, + # causing "module uses a mix of legacy and new exception handling + # instructions" at instantiation time in browsers. + # Node.js with --experimental-wasm-exnref tolerates mixed EH, so we + # verify correctness by checking the cached library name and also + # confirming the program runs. + self.require_wasm_eh() + + # Build with -v and capture stderr to verify the correct variant is used. + proc = self.run_process([ + EMCC, '-v', + test_file('test_freetype_exc.cpp'), + '-fwasm-exceptions', + '-sUSE_FREETYPE', + '-sSUPPORT_LONGJMP=wasm', + '-sWASM_LEGACY_EXCEPTIONS=0', + '-o', 'test_out.js', + ], stderr=PIPE) + self.assertContained('libfreetype-wasmsjlj', proc.stderr) + self.assertNotContained('libfreetype-legacysjlj', proc.stderr) + + # Also verify the program actually runs. + self.do_runf('test_freetype_exc.cpp', 'caught: test\nFreeType initialized successfully\n', cflags=[ + '-fwasm-exceptions', + '-sUSE_FREETYPE', + '-sSUPPORT_LONGJMP=wasm', + ]) + @requires_network def test_icu(self): self.set_setting('USE_ICU') diff --git a/tools/ports/freetype.py b/tools/ports/freetype.py index d99a6c0059975..9dae17b25690e 100644 --- a/tools/ports/freetype.py +++ b/tools/ports/freetype.py @@ -10,7 +10,10 @@ PKG_VERSION = '26.2.20' HASH = 'ce413487c24e689631d705f53b64725256f89fffe9aade7cf07bbd785a9cd49eb6b8d2297a55554f3fee0a50b17e8af78f505cdab565768afab833794f968c2f' -variants = {'freetype-legacysjlj': {'SUPPORT_LONGJMP': 'wasm', 'WASM_LEGACY_EXCEPTIONS': 1}} +variants = { + 'freetype-legacysjlj': {'SUPPORT_LONGJMP': 'wasm', 'WASM_LEGACY_EXCEPTIONS': 1}, + 'freetype-wasmsjlj': {'SUPPORT_LONGJMP': 'wasm', 'WASM_LEGACY_EXCEPTIONS': 0}, +} deps = ['zlib'] @@ -20,7 +23,10 @@ def needed(settings): def get_lib_name(settings): if settings.SUPPORT_LONGJMP == 'wasm': - return 'libfreetype-legacysjlj.a' + if settings.WASM_LEGACY_EXCEPTIONS: + return 'libfreetype-legacysjlj.a' + else: + return 'libfreetype-wasmsjlj.a' else: return 'libfreetype.a' @@ -97,6 +103,8 @@ def create(final): if settings.SUPPORT_LONGJMP == 'wasm': flags.append('-sSUPPORT_LONGJMP=wasm') + if not settings.WASM_LEGACY_EXCEPTIONS: + flags.append('-sWASM_LEGACY_EXCEPTIONS=0') ports.make_pkg_config('freetype2', PKG_VERSION, '-sUSE_FREETYPE') ports.build_port(source_path, final, 'freetype', flags=flags, srcs=srcs)