From c931b06ab63cd454f9a81aeb4bb255cc73880252 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 8 Jul 2023 08:35:11 -0700 Subject: [PATCH] Patch CPython to use a type reflection trampoline if possible (#3964) --- .../0001-Public-pymain_run_python.patch | 4 +- ...to-allow-modifications-to-ModuleNotF.patch | 4 +- ...latform-support-to-ctypes.util.find_.patch | 6 +- ...ocessing.connection-top-level-import.patch | 6 +- ...b-ctypes-test-to-Lib-test-test_ctype.patch | 25 +- ...b-unttest-test-to-Lib-test-test_unit.patch | 711 +----------------- ...patch => 0007-Move-test-directories.patch} | 6 +- ...d_package_tests-for-testmock-GH-9405.patch | 40 - ...mscripten-trampolines-work-with-JSPI.patch | 371 +++++++++ 9 files changed, 405 insertions(+), 768 deletions(-) rename cpython/patches/{0008-Move-test-directories.patch => 0007-Move-test-directories.patch} (87%) delete mode 100644 cpython/patches/0007-gh-93839-Use-load_package_tests-for-testmock-GH-9405.patch create mode 100644 cpython/patches/0008-Make-Emscripten-trampolines-work-with-JSPI.patch diff --git a/cpython/patches/0001-Public-pymain_run_python.patch b/cpython/patches/0001-Public-pymain_run_python.patch index cbe4b1903..930435977 100644 --- a/cpython/patches/0001-Public-pymain_run_python.patch +++ b/cpython/patches/0001-Public-pymain_run_python.patch @@ -1,7 +1,7 @@ -From 8dc93243eb0cdf27a81e9baf156df621cc8e2eb9 Mon Sep 17 00:00:00 2001 +From a64272682c47c7317faebdc4f5545cb05bfedcb5 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 17 Jul 2022 14:40:39 +0100 -Subject: [PATCH 1/9] Public pymain_run_python +Subject: [PATCH 1/8] Public pymain_run_python --- Modules/main.c | 2 +- diff --git a/cpython/patches/0002-Patch-importlib-to-allow-modifications-to-ModuleNotF.patch b/cpython/patches/0002-Patch-importlib-to-allow-modifications-to-ModuleNotF.patch index 34aabeb36..aa6130c8c 100644 --- a/cpython/patches/0002-Patch-importlib-to-allow-modifications-to-ModuleNotF.patch +++ b/cpython/patches/0002-Patch-importlib-to-allow-modifications-to-ModuleNotF.patch @@ -1,7 +1,7 @@ -From 90812b6dc97860ef164a2810ebf5f3121dfc919e Mon Sep 17 00:00:00 2001 +From c5a5b530159a991c7c5936a75e6448a7916b72d1 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 16 Nov 2022 14:02:53 -0800 -Subject: [PATCH 2/9] Patch importlib to allow modifications to +Subject: [PATCH 2/8] Patch importlib to allow modifications to ModuleNotFoundError --- diff --git a/cpython/patches/0003-Add-emscripten-platform-support-to-ctypes.util.find_.patch b/cpython/patches/0003-Add-emscripten-platform-support-to-ctypes.util.find_.patch index 593876c5a..31aa1e38e 100644 --- a/cpython/patches/0003-Add-emscripten-platform-support-to-ctypes.util.find_.patch +++ b/cpython/patches/0003-Add-emscripten-platform-support-to-ctypes.util.find_.patch @@ -1,7 +1,7 @@ -From bb33672157df771613c9d6c61756c5294f13333d Mon Sep 17 00:00:00 2001 +From 08cc579925825da3388a6b4967868d86ea2c488f Mon Sep 17 00:00:00 2001 From: ryanking13 Date: Fri, 2 Dec 2022 11:36:44 +0000 -Subject: [PATCH 3/9] Add emscripten platform support to +Subject: [PATCH 3/8] Add emscripten platform support to ctypes.util.find_library --- @@ -44,5 +44,5 @@ index 0c2510e161..c8875ec009 100644 # AIX has two styles of storing shared libraries # GNU auto_tools refer to these as svr4 and aix -- -2.29.2.windows.2 +2.25.1 diff --git a/cpython/patches/0004-Allow-multiprocessing.connection-top-level-import.patch b/cpython/patches/0004-Allow-multiprocessing.connection-top-level-import.patch index 5f7a0d3b0..3992a2890 100644 --- a/cpython/patches/0004-Allow-multiprocessing.connection-top-level-import.patch +++ b/cpython/patches/0004-Allow-multiprocessing.connection-top-level-import.patch @@ -1,14 +1,14 @@ -From becbcc43e3e09a9c065ea7856a2d3069061fd570 Mon Sep 17 00:00:00 2001 +From f8970422b395b24ca58cef3dca6954bb07d4974d Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 19 Dec 2022 09:09:14 -0800 -Subject: [PATCH 4/9] Allow multiprocessing.connection top level import +Subject: [PATCH 4/8] Allow multiprocessing.connection top level import --- Lib/multiprocessing/connection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py -index 510e4b5aba..372f9ced37 100644 +index b08144f7a1..6a98829169 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -18,7 +18,10 @@ diff --git a/cpython/patches/0005-gh-93839-Move-Lib-ctypes-test-to-Lib-test-test_ctype.patch b/cpython/patches/0005-gh-93839-Move-Lib-ctypes-test-to-Lib-test-test_ctype.patch index 49353eb6f..18fd5fe96 100644 --- a/cpython/patches/0005-gh-93839-Move-Lib-ctypes-test-to-Lib-test-test_ctype.patch +++ b/cpython/patches/0005-gh-93839-Move-Lib-ctypes-test-to-Lib-test-test_ctype.patch @@ -1,7 +1,7 @@ -From d82e0bfe8b98a122ca443b356d81998c804b686e Mon Sep 17 00:00:00 2001 +From b08715ecc79e6398f5480bfb3b1b197f07d8a741 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 21 Jun 2022 10:24:33 +0200 -Subject: [PATCH 5/9] gh-93839: Move Lib/ctypes/test/ to Lib/test/test_ctypes/ +Subject: [PATCH 5/8] gh-93839: Move Lib/ctypes/test/ to Lib/test/test_ctypes/ (#94041) * Move Lib/ctypes/test/ to Lib/test/test_ctypes/ @@ -64,11 +64,10 @@ Subject: [PATCH 5/9] gh-93839: Move Lib/ctypes/test/ to Lib/test/test_ctypes/ .../test_ctypes}/test_varsize_struct.py | 0 .../test => test/test_ctypes}/test_win32.py | 0 .../test_ctypes}/test_wintypes.py | 0 - Makefile.pre.in | 4 +- ...2-06-20-23-04-52.gh-issue-93839.OE3Ybk.rst | 2 + PCbuild/lib.pyproj | 109 +++++++++--------- Tools/wasm/wasm_assets.py | 1 - - 60 files changed, 83 insertions(+), 93 deletions(-) + 59 files changed, 81 insertions(+), 91 deletions(-) delete mode 100644 Lib/ctypes/test/__main__.py delete mode 100644 Lib/test/test_ctypes.py rename Lib/{ctypes/test => test/test_ctypes}/__init__.py (100%) @@ -205,7 +204,7 @@ diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/test/test_ctypes/test_as similarity index 99% rename from Lib/ctypes/test/test_as_parameter.py rename to Lib/test/test_ctypes/test_as_parameter.py -index f9d27cb89d..b35defb158 100644 +index 9c39179d2a..e9ec9ad847 100644 --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/test/test_ctypes/test_as_parameter.py @@ -1,6 +1,6 @@ @@ -256,7 +255,7 @@ diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/test/test_ctypes/test_callb similarity index 99% rename from Lib/ctypes/test/test_callbacks.py rename to Lib/test/test_ctypes/test_callbacks.py -index 1099cf9a69..2758720d4a 100644 +index 8f95a24443..b5bef04e14 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/test/test_ctypes/test_callbacks.py @@ -3,7 +3,7 @@ @@ -286,7 +285,7 @@ diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/test/test_ctypes/test_cfuncs.p similarity index 99% rename from Lib/ctypes/test/test_cfuncs.py rename to Lib/test/test_ctypes/test_cfuncs.py -index ac2240fa19..0a9394bf31 100644 +index 09b06840bf..7cba4b0e52 100644 --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/test/test_ctypes/test_cfuncs.py @@ -3,7 +3,7 @@ @@ -338,7 +337,7 @@ diff --git a/Lib/ctypes/test/test_functions.py b/Lib/test/test_ctypes/test_funct similarity index 99% rename from Lib/ctypes/test/test_functions.py rename to Lib/test/test_ctypes/test_functions.py -index f9e92e1cc6..4a784c8d79 100644 +index fc571700ce..a3db003353 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/test/test_ctypes/test_functions.py @@ -6,7 +6,7 @@ @@ -434,7 +433,7 @@ diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/test/test_ctypes/test_para similarity index 99% rename from Lib/ctypes/test/test_parameters.py rename to Lib/test/test_ctypes/test_parameters.py -index 38af7ac13d..2f755a6d09 100644 +index 3fdc994e90..662a17e094 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/test/test_ctypes/test_parameters.py @@ -1,5 +1,5 @@ @@ -540,7 +539,7 @@ diff --git a/Lib/ctypes/test/test_structures.py b/Lib/test/test_ctypes/test_stru similarity index 99% rename from Lib/ctypes/test/test_structures.py rename to Lib/test/test_ctypes/test_structures.py -index 97ad2b8ed8..13c0470ba2 100644 +index f95d5a99a3..df39dc7f50 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -2,7 +2,7 @@ @@ -737,10 +736,10 @@ index 43c570f1da..692b083349 100644 diff --git a/Tools/wasm/wasm_assets.py b/Tools/wasm/wasm_assets.py -index b7e83517ca..d0a0570840 100755 +index 6557e3f37a..aa09d2dda8 100755 --- a/Tools/wasm/wasm_assets.py +++ b/Tools/wasm/wasm_assets.py -@@ -111,7 +111,6 @@ +@@ -112,7 +112,6 @@ # regression test sub directories OMIT_SUBDIRS = ( @@ -749,5 +748,5 @@ index b7e83517ca..d0a0570840 100755 "unittest/test/", ) -- -2.29.2.windows.2 +2.25.1 diff --git a/cpython/patches/0006-gh-93839-Move-Lib-unttest-test-to-Lib-test-test_unit.patch b/cpython/patches/0006-gh-93839-Move-Lib-unttest-test-to-Lib-test-test_unit.patch index 9e8d8c47c..e529aaa2e 100644 --- a/cpython/patches/0006-gh-93839-Move-Lib-unttest-test-to-Lib-test-test_unit.patch +++ b/cpython/patches/0006-gh-93839-Move-Lib-unttest-test-to-Lib-test-test_unit.patch @@ -1,8 +1,8 @@ -From c735d545343c3ab002c62596b2fb2cfa4488b0af Mon Sep 17 00:00:00 2001 +From bfc38df65896e34c6d7d995a044ce75974757436 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 21 Jun 2022 10:27:59 +0200 -Subject: [PATCH 6/9] gh-93839: Move Lib/unttest/test/ to Lib/test/test_unittest/ - (#94043) +Subject: [PATCH 6/8] gh-93839: Move Lib/unttest/test/ to + Lib/test/test_unittest/ (#94043) * Move Lib/unittest/test/ to Lib/test/test_unittest/ * Remove Lib/test/test_unittest.py @@ -11,714 +11,21 @@ Subject: [PATCH 6/9] gh-93839: Move Lib/unttest/test/ to Lib/test/test_unittest/ * Rewrite unittest __init__.py and __main__.py * Update build system, CODEOWNERS, and wasm_assets.py --- - .github/CODEOWNERS | 2 +- - Lib/test/test_unittest.py | 16 ----- - Lib/test/test_unittest/__init__.py | 6 ++ - Lib/test/test_unittest/__main__.py | 4 ++ - .../test_unittest}/_test_warnings.py | 0 - .../test => test/test_unittest}/dummy.py | 0 - .../test => test/test_unittest}/support.py | 0 - .../test_unittest}/test_assertions.py | 0 - .../test_unittest}/test_async_case.py | 0 - .../test => test/test_unittest}/test_break.py | 0 - .../test => test/test_unittest}/test_case.py | 2 +- - .../test_unittest}/test_discovery.py | 6 +- - .../test_unittest}/test_functiontestcase.py | 2 +- - .../test_unittest}/test_loader.py | 6 +- - .../test_unittest}/test_program.py | 16 ++--- - .../test_unittest}/test_result.py | 0 - .../test_unittest}/test_runner.py | 2 +- - .../test_unittest}/test_setups.py | 0 - .../test_unittest}/test_skipping.py | 2 +- - .../test => test/test_unittest}/test_suite.py | 2 +- - .../test_unittest}/testmock/__init__.py | 2 +- - .../test_unittest}/testmock/__main__.py | 2 +- - .../test_unittest}/testmock/support.py | 0 - .../test_unittest}/testmock/testasync.py | 0 - .../test_unittest}/testmock/testcallable.py | 2 +- - .../test_unittest}/testmock/testhelpers.py | 0 - .../testmock/testmagicmethods.py | 0 - .../test_unittest}/testmock/testmock.py | 2 +- - .../test_unittest}/testmock/testpatch.py | 22 +++---- - .../test_unittest}/testmock/testsealable.py | 0 - .../test_unittest}/testmock/testsentinel.py | 0 - .../test_unittest}/testmock/testwith.py | 2 +- - Lib/unittest/__init__.py | 10 ---- - Lib/unittest/test/__init__.py | 25 -------- - Lib/unittest/test/__main__.py | 18 ------ - Makefile.pre.in | 4 +- - PCbuild/lib.pyproj | 58 +++++++++---------- - Tools/wasm/wasm_assets.py | 1 - - 38 files changed, 77 insertions(+), 137 deletions(-) - delete mode 100644 Lib/test/test_unittest.py - create mode 100644 Lib/test/test_unittest/__init__.py - create mode 100644 Lib/test/test_unittest/__main__.py - rename Lib/{unittest/test => test/test_unittest}/_test_warnings.py (100%) - rename Lib/{unittest/test => test/test_unittest}/dummy.py (100%) - rename Lib/{unittest/test => test/test_unittest}/support.py (100%) - rename Lib/{unittest/test => test/test_unittest}/test_assertions.py (100%) - rename Lib/{unittest/test => test/test_unittest}/test_async_case.py (100%) - rename Lib/{unittest/test => test/test_unittest}/test_break.py (100%) - rename Lib/{unittest/test => test/test_unittest}/test_case.py (99%) - rename Lib/{unittest/test => test/test_unittest}/test_discovery.py (99%) - rename Lib/{unittest/test => test/test_unittest}/test_functiontestcase.py (99%) - rename Lib/{unittest/test => test/test_unittest}/test_loader.py (99%) - rename Lib/{unittest/test => test/test_unittest}/test_program.py (96%) - rename Lib/{unittest/test => test/test_unittest}/test_result.py (100%) - rename Lib/{unittest/test => test/test_unittest}/test_runner.py (99%) - rename Lib/{unittest/test => test/test_unittest}/test_setups.py (100%) - rename Lib/{unittest/test => test/test_unittest}/test_skipping.py (99%) - rename Lib/{unittest/test => test/test_unittest}/test_suite.py (99%) - rename Lib/{unittest/test => test/test_unittest}/testmock/__init__.py (86%) - rename Lib/{unittest/test => test/test_unittest}/testmock/__main__.py (86%) - rename Lib/{unittest/test => test/test_unittest}/testmock/support.py (100%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testasync.py (100%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testcallable.py (98%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testhelpers.py (100%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testmagicmethods.py (100%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testmock.py (99%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testpatch.py (98%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testsealable.py (100%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testsentinel.py (100%) - rename Lib/{unittest/test => test/test_unittest}/testmock/testwith.py (99%) - delete mode 100644 Lib/unittest/test/__init__.py - delete mode 100644 Lib/unittest/test/__main__.py + Tools/wasm/wasm_assets.py | 1 - + 1 file changed, 1 deletion(-) -diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py -deleted file mode 100644 -index 1079c7df2e..0000000000 ---- a/Lib/test/test_unittest.py -+++ /dev/null -@@ -1,16 +0,0 @@ --import unittest.test -- --from test import support -- -- --def load_tests(*_): -- # used by unittest -- return unittest.test.suite() -- -- --def tearDownModule(): -- support.reap_children() -- -- --if __name__ == "__main__": -- unittest.main() -diff --git a/Lib/test/test_unittest/__init__.py b/Lib/test/test_unittest/__init__.py -new file mode 100644 -index 0000000000..bc502ef32d ---- /dev/null -+++ b/Lib/test/test_unittest/__init__.py -@@ -0,0 +1,6 @@ -+import os.path -+from test.support import load_package_tests -+ -+ -+def load_tests(*args): -+ return load_package_tests(os.path.dirname(__file__), *args) -diff --git a/Lib/test/test_unittest/__main__.py b/Lib/test/test_unittest/__main__.py -new file mode 100644 -index 0000000000..40a23a297e ---- /dev/null -+++ b/Lib/test/test_unittest/__main__.py -@@ -0,0 +1,4 @@ -+from . import load_tests -+import unittest -+ -+unittest.main() -diff --git a/Lib/unittest/test/_test_warnings.py b/Lib/test/test_unittest/_test_warnings.py -similarity index 100% -rename from Lib/unittest/test/_test_warnings.py -rename to Lib/test/test_unittest/_test_warnings.py -diff --git a/Lib/unittest/test/dummy.py b/Lib/test/test_unittest/dummy.py -similarity index 100% -rename from Lib/unittest/test/dummy.py -rename to Lib/test/test_unittest/dummy.py -diff --git a/Lib/unittest/test/support.py b/Lib/test/test_unittest/support.py -similarity index 100% -rename from Lib/unittest/test/support.py -rename to Lib/test/test_unittest/support.py -diff --git a/Lib/unittest/test/test_assertions.py b/Lib/test/test_unittest/test_assertions.py -similarity index 100% -rename from Lib/unittest/test/test_assertions.py -rename to Lib/test/test_unittest/test_assertions.py -diff --git a/Lib/unittest/test/test_async_case.py b/Lib/test/test_unittest/test_async_case.py -similarity index 100% -rename from Lib/unittest/test/test_async_case.py -rename to Lib/test/test_unittest/test_async_case.py -diff --git a/Lib/unittest/test/test_break.py b/Lib/test/test_unittest/test_break.py -similarity index 100% -rename from Lib/unittest/test/test_break.py -rename to Lib/test/test_unittest/test_break.py -diff --git a/Lib/unittest/test/test_case.py b/Lib/test/test_unittest/test_case.py -similarity index 99% -rename from Lib/unittest/test/test_case.py -rename to Lib/test/test_unittest/test_case.py -index 374a255255..e000fe4f07 100644 ---- a/Lib/unittest/test/test_case.py -+++ b/Lib/test/test_unittest/test_case.py -@@ -15,7 +15,7 @@ - - import unittest - --from unittest.test.support import ( -+from test.test_unittest.support import ( - TestEquality, TestHashing, LoggingResult, LegacyLoggingResult, - ResultWithNoStartTestRunStopTestRun - ) -diff --git a/Lib/unittest/test/test_discovery.py b/Lib/test/test_unittest/test_discovery.py -similarity index 99% -rename from Lib/unittest/test/test_discovery.py -rename to Lib/test/test_unittest/test_discovery.py -index 3b58786ec1..946fa1258e 100644 ---- a/Lib/unittest/test/test_discovery.py -+++ b/Lib/test/test_unittest/test_discovery.py -@@ -10,7 +10,7 @@ - - import unittest - import unittest.mock --import unittest.test -+import test.test_unittest - - - class TestableTestProgram(unittest.TestProgram): -@@ -789,7 +789,7 @@ def test_discovery_from_dotted_path(self): - loader = unittest.TestLoader() - - tests = [self] -- expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__)) -+ expectedPath = os.path.abspath(os.path.dirname(test.test_unittest.__file__)) - - self.wasRun = False - def _find_tests(start_dir, pattern): -@@ -797,7 +797,7 @@ def _find_tests(start_dir, pattern): - self.assertEqual(start_dir, expectedPath) - return tests - loader._find_tests = _find_tests -- suite = loader.discover('unittest.test') -+ suite = loader.discover('test.test_unittest') - self.assertTrue(self.wasRun) - self.assertEqual(suite._tests, tests) - -diff --git a/Lib/unittest/test/test_functiontestcase.py b/Lib/test/test_unittest/test_functiontestcase.py -similarity index 99% -rename from Lib/unittest/test/test_functiontestcase.py -rename to Lib/test/test_unittest/test_functiontestcase.py -index 4971729880..2ebed9564a 100644 ---- a/Lib/unittest/test/test_functiontestcase.py -+++ b/Lib/test/test_unittest/test_functiontestcase.py -@@ -1,6 +1,6 @@ - import unittest - --from unittest.test.support import LoggingResult -+from test.test_unittest.support import LoggingResult - - - class Test_FunctionTestCase(unittest.TestCase): -diff --git a/Lib/unittest/test/test_loader.py b/Lib/test/test_unittest/test_loader.py -similarity index 99% -rename from Lib/unittest/test/test_loader.py -rename to Lib/test/test_unittest/test_loader.py -index de2268cda9..c06ebb658d 100644 ---- a/Lib/unittest/test/test_loader.py -+++ b/Lib/test/test_unittest/test_loader.py -@@ -716,7 +716,7 @@ def test_loadTestsFromName__module_not_loaded(self): - # We're going to try to load this module as a side-effect, so it - # better not be loaded before we try. - # -- module_name = 'unittest.test.dummy' -+ module_name = 'test.test_unittest.dummy' - sys.modules.pop(module_name, None) - - loader = unittest.TestLoader() -@@ -844,7 +844,7 @@ def test_loadTestsFromNames__unknown_attr_name(self): - loader = unittest.TestLoader() - - suite = loader.loadTestsFromNames( -- ['unittest.loader.sdasfasfasdf', 'unittest.test.dummy']) -+ ['unittest.loader.sdasfasfasdf', 'test.test_unittest.dummy']) - error, test = self.check_deferred_error(loader, list(suite)[0]) - expected = "module 'unittest.loader' has no attribute 'sdasfasfasdf'" - self.assertIn( -@@ -1141,7 +1141,7 @@ def test_loadTestsFromNames__module_not_loaded(self): - # We're going to try to load this module as a side-effect, so it - # better not be loaded before we try. - # -- module_name = 'unittest.test.dummy' -+ module_name = 'test.test_unittest.dummy' - sys.modules.pop(module_name, None) - - loader = unittest.TestLoader() -diff --git a/Lib/unittest/test/test_program.py b/Lib/test/test_unittest/test_program.py -similarity index 96% -rename from Lib/unittest/test/test_program.py -rename to Lib/test/test_unittest/test_program.py -index 26a8550af8..169fc4ed94 100644 ---- a/Lib/unittest/test/test_program.py -+++ b/Lib/test/test_unittest/test_program.py -@@ -5,8 +5,8 @@ - import subprocess - from test import support - import unittest --import unittest.test --from unittest.test.test_result import BufferedWriter -+import test.test_unittest -+from test.test_unittest.test_result import BufferedWriter - - - class Test_TestProgram(unittest.TestCase): -@@ -15,7 +15,7 @@ def test_discovery_from_dotted_path(self): - loader = unittest.TestLoader() - - tests = [self] -- expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__)) -+ expectedPath = os.path.abspath(os.path.dirname(test.test_unittest.__file__)) - - self.wasRun = False - def _find_tests(start_dir, pattern): -@@ -23,7 +23,7 @@ def _find_tests(start_dir, pattern): - self.assertEqual(start_dir, expectedPath) - return tests - loader._find_tests = _find_tests -- suite = loader.discover('unittest.test') -+ suite = loader.discover('test.test_unittest') - self.assertTrue(self.wasRun) - self.assertEqual(suite._tests, tests) - -@@ -93,10 +93,10 @@ def run(self, test): - sys.argv = ['faketest'] - runner = FakeRunner() - program = unittest.TestProgram(testRunner=runner, exit=False, -- defaultTest='unittest.test', -+ defaultTest='test.test_unittest', - testLoader=self.FooBarLoader()) - sys.argv = old_argv -- self.assertEqual(('unittest.test',), program.testNames) -+ self.assertEqual(('test.test_unittest',), program.testNames) - - def test_defaultTest_with_iterable(self): - class FakeRunner(object): -@@ -109,10 +109,10 @@ def run(self, test): - runner = FakeRunner() - program = unittest.TestProgram( - testRunner=runner, exit=False, -- defaultTest=['unittest.test', 'unittest.test2'], -+ defaultTest=['test.test_unittest', 'test.test_unittest2'], - testLoader=self.FooBarLoader()) - sys.argv = old_argv -- self.assertEqual(['unittest.test', 'unittest.test2'], -+ self.assertEqual(['test.test_unittest', 'test.test_unittest2'], - program.testNames) - - def test_NonExit(self): -diff --git a/Lib/unittest/test/test_result.py b/Lib/test/test_unittest/test_result.py -similarity index 100% -rename from Lib/unittest/test/test_result.py -rename to Lib/test/test_unittest/test_result.py -diff --git a/Lib/unittest/test/test_runner.py b/Lib/test/test_unittest/test_runner.py -similarity index 99% -rename from Lib/unittest/test/test_runner.py -rename to Lib/test/test_unittest/test_runner.py -index d3488b40e8..9e3a0a9ca0 100644 ---- a/Lib/unittest/test/test_runner.py -+++ b/Lib/test/test_unittest/test_runner.py -@@ -8,7 +8,7 @@ - import unittest - from unittest.case import _Outcome - --from unittest.test.support import (LoggingResult, -+from test.test_unittest.support import (LoggingResult, - ResultWithNoStartTestRunStopTestRun) - - -diff --git a/Lib/unittest/test/test_setups.py b/Lib/test/test_unittest/test_setups.py -similarity index 100% -rename from Lib/unittest/test/test_setups.py -rename to Lib/test/test_unittest/test_setups.py -diff --git a/Lib/unittest/test/test_skipping.py b/Lib/test/test_unittest/test_skipping.py -similarity index 99% -rename from Lib/unittest/test/test_skipping.py -rename to Lib/test/test_unittest/test_skipping.py -index 64ceeae37e..f146dcac18 100644 ---- a/Lib/unittest/test/test_skipping.py -+++ b/Lib/test/test_unittest/test_skipping.py -@@ -1,6 +1,6 @@ - import unittest - --from unittest.test.support import LoggingResult -+from test.test_unittest.support import LoggingResult - - - class Test_TestSkipping(unittest.TestCase): -diff --git a/Lib/unittest/test/test_suite.py b/Lib/test/test_unittest/test_suite.py -similarity index 99% -rename from Lib/unittest/test/test_suite.py -rename to Lib/test/test_unittest/test_suite.py -index 0551a16996..ca52ee9d9c 100644 ---- a/Lib/unittest/test/test_suite.py -+++ b/Lib/test/test_unittest/test_suite.py -@@ -3,7 +3,7 @@ - import gc - import sys - import weakref --from unittest.test.support import LoggingResult, TestEquality -+from test.test_unittest.support import LoggingResult, TestEquality - - - ### Support code for Test_TestSuite -diff --git a/Lib/unittest/test/testmock/__init__.py b/Lib/test/test_unittest/testmock/__init__.py -similarity index 86% -rename from Lib/unittest/test/testmock/__init__.py -rename to Lib/test/test_unittest/testmock/__init__.py -index 87d7ae994d..6ee60b2376 100644 ---- a/Lib/unittest/test/testmock/__init__.py -+++ b/Lib/test/test_unittest/testmock/__init__.py -@@ -10,7 +10,7 @@ def load_tests(*args): - suite = unittest.TestSuite() - for fn in os.listdir(here): - if fn.startswith("test") and fn.endswith(".py"): -- modname = "unittest.test.testmock." + fn[:-3] -+ modname = "test.test_unittest.testmock." + fn[:-3] - __import__(modname) - module = sys.modules[modname] - suite.addTest(loader.loadTestsFromModule(module)) -diff --git a/Lib/unittest/test/testmock/__main__.py b/Lib/test/test_unittest/testmock/__main__.py -similarity index 86% -rename from Lib/unittest/test/testmock/__main__.py -rename to Lib/test/test_unittest/testmock/__main__.py -index 45c633a4ee..1e3068b0dd 100644 ---- a/Lib/unittest/test/testmock/__main__.py -+++ b/Lib/test/test_unittest/testmock/__main__.py -@@ -6,7 +6,7 @@ def load_tests(loader, standard_tests, pattern): - # top level directory cached on loader instance - this_dir = os.path.dirname(__file__) - pattern = pattern or "test*.py" -- # We are inside unittest.test.testmock, so the top-level is three notches up -+ # We are inside test.test_unittest.testmock, so the top-level is three notches up - top_level_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir))) - package_tests = loader.discover(start_dir=this_dir, pattern=pattern, - top_level_dir=top_level_dir) -diff --git a/Lib/unittest/test/testmock/support.py b/Lib/test/test_unittest/testmock/support.py -similarity index 100% -rename from Lib/unittest/test/testmock/support.py -rename to Lib/test/test_unittest/testmock/support.py -diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/test/test_unittest/testmock/testasync.py -similarity index 100% -rename from Lib/unittest/test/testmock/testasync.py -rename to Lib/test/test_unittest/testmock/testasync.py -diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/test/test_unittest/testmock/testcallable.py -similarity index 98% -rename from Lib/unittest/test/testmock/testcallable.py -rename to Lib/test/test_unittest/testmock/testcallable.py -index 5eadc00704..ca88511f63 100644 ---- a/Lib/unittest/test/testmock/testcallable.py -+++ b/Lib/test/test_unittest/testmock/testcallable.py -@@ -3,7 +3,7 @@ - # http://www.voidspace.org.uk/python/mock/ - - import unittest --from unittest.test.testmock.support import is_instance, X, SomeClass -+from test.test_unittest.testmock.support import is_instance, X, SomeClass - - from unittest.mock import ( - Mock, MagicMock, NonCallableMagicMock, -diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py -similarity index 100% -rename from Lib/unittest/test/testmock/testhelpers.py -rename to Lib/test/test_unittest/testmock/testhelpers.py -diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/test/test_unittest/testmock/testmagicmethods.py -similarity index 100% -rename from Lib/unittest/test/testmock/testmagicmethods.py -rename to Lib/test/test_unittest/testmock/testmagicmethods.py -diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py -similarity index 99% -rename from Lib/unittest/test/testmock/testmock.py -rename to Lib/test/test_unittest/testmock/testmock.py -index c99098dc4e..8a92490137 100644 ---- a/Lib/unittest/test/testmock/testmock.py -+++ b/Lib/test/test_unittest/testmock/testmock.py -@@ -5,7 +5,7 @@ - - from test.support import ALWAYS_EQ - import unittest --from unittest.test.testmock.support import is_instance -+from test.test_unittest.testmock.support import is_instance - from unittest import mock - from unittest.mock import ( - call, DEFAULT, patch, sentinel, -diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/test/test_unittest/testmock/testpatch.py -similarity index 98% -rename from Lib/unittest/test/testmock/testpatch.py -rename to Lib/test/test_unittest/testmock/testpatch.py -index 8ab63a1317..93ec0ca4be 100644 ---- a/Lib/unittest/test/testmock/testpatch.py -+++ b/Lib/test/test_unittest/testmock/testpatch.py -@@ -7,8 +7,8 @@ - from collections import OrderedDict - - import unittest --from unittest.test.testmock import support --from unittest.test.testmock.support import SomeClass, is_instance -+from test.test_unittest.testmock import support -+from test.test_unittest.testmock.support import SomeClass, is_instance - - from test.test_importlib.util import uncache - from unittest.mock import ( -@@ -669,7 +669,7 @@ def test_patch_dict_decorator_resolution(self): - # the new dictionary during function call - original = support.target.copy() - -- @patch.dict('unittest.test.testmock.support.target', {'bar': 'BAR'}) -+ @patch.dict('test.test_unittest.testmock.support.target', {'bar': 'BAR'}) - def test(): - self.assertEqual(support.target, {'foo': 'BAZ', 'bar': 'BAR'}) - -@@ -1614,7 +1614,7 @@ def test_patch_with_spec_mock_repr(self): - - - def test_patch_nested_autospec_repr(self): -- with patch('unittest.test.testmock.support', autospec=True) as m: -+ with patch('test.test_unittest.testmock.support', autospec=True) as m: - self.assertIn(" name='support.SomeClass.wibble()'", - repr(m.SomeClass.wibble())) - self.assertIn(" name='support.SomeClass().wibble()'", -@@ -1882,7 +1882,7 @@ def foo(x=0): - - with patch.object(foo, '__module__', "testpatch2"): - self.assertEqual(foo.__module__, "testpatch2") -- self.assertEqual(foo.__module__, 'unittest.test.testmock.testpatch') -+ self.assertEqual(foo.__module__, 'test.test_unittest.testmock.testpatch') - - with patch.object(foo, '__annotations__', dict([('s', 1, )])): - self.assertEqual(foo.__annotations__, dict([('s', 1, )])) -@@ -1917,16 +1917,16 @@ def test_dotted_but_module_not_loaded(self): - # This exercises the AttributeError branch of _dot_lookup. - - # make sure it's there -- import unittest.test.testmock.support -+ import test.test_unittest.testmock.support - # now make sure it's not: - with patch.dict('sys.modules'): -- del sys.modules['unittest.test.testmock.support'] -- del sys.modules['unittest.test.testmock'] -- del sys.modules['unittest.test'] -+ del sys.modules['test.test_unittest.testmock.support'] -+ del sys.modules['test.test_unittest.testmock'] -+ del sys.modules['test.test_unittest'] - del sys.modules['unittest'] - - # now make sure we can patch based on a dotted path: -- @patch('unittest.test.testmock.support.X') -+ @patch('test.test_unittest.testmock.support.X') - def test(mock): - pass - test() -@@ -1943,7 +1943,7 @@ class Foo: - - - def test_cant_set_kwargs_when_passing_a_mock(self): -- @patch('unittest.test.testmock.support.X', new=object(), x=1) -+ @patch('test.test_unittest.testmock.support.X', new=object(), x=1) - def test(): pass - with self.assertRaises(TypeError): - test() -diff --git a/Lib/unittest/test/testmock/testsealable.py b/Lib/test/test_unittest/testmock/testsealable.py -similarity index 100% -rename from Lib/unittest/test/testmock/testsealable.py -rename to Lib/test/test_unittest/testmock/testsealable.py -diff --git a/Lib/unittest/test/testmock/testsentinel.py b/Lib/test/test_unittest/testmock/testsentinel.py -similarity index 100% -rename from Lib/unittest/test/testmock/testsentinel.py -rename to Lib/test/test_unittest/testmock/testsentinel.py -diff --git a/Lib/unittest/test/testmock/testwith.py b/Lib/test/test_unittest/testmock/testwith.py -similarity index 99% -rename from Lib/unittest/test/testmock/testwith.py -rename to Lib/test/test_unittest/testmock/testwith.py -index c74d49a63c..8dc8eb1137 100644 ---- a/Lib/unittest/test/testmock/testwith.py -+++ b/Lib/test/test_unittest/testmock/testwith.py -@@ -1,7 +1,7 @@ - import unittest - from warnings import catch_warnings - --from unittest.test.testmock.support import is_instance -+from test.test_unittest.testmock.support import is_instance - from unittest.mock import MagicMock, Mock, patch, sentinel, mock_open, call - - -diff --git a/Lib/unittest/__init__.py b/Lib/unittest/__init__.py -index 005d23f6d0..b8de8c95d6 100644 ---- a/Lib/unittest/__init__.py -+++ b/Lib/unittest/__init__.py -@@ -73,16 +73,6 @@ def testMultiply(self): - _TextTestResult = TextTestResult - - --# There are no tests here, so don't try to run anything discovered from --# introspecting the symbols (e.g. FunctionTestCase). Instead, all our --# tests come from within unittest.test. --def load_tests(loader, tests, pattern): -- import os.path -- # top level directory cached on loader instance -- this_dir = os.path.dirname(__file__) -- return loader.discover(start_dir=this_dir, pattern=pattern) -- -- - # Lazy import of IsolatedAsyncioTestCase from .async_case - # It imports asyncio, which is relatively heavy, but most tests - # do not need it. -diff --git a/Lib/unittest/test/__init__.py b/Lib/unittest/test/__init__.py -deleted file mode 100644 -index 143f4ab5a3..0000000000 ---- a/Lib/unittest/test/__init__.py -+++ /dev/null -@@ -1,25 +0,0 @@ --import os --import sys --import unittest -- -- --here = os.path.dirname(__file__) --loader = unittest.defaultTestLoader -- --def suite(): -- suite = unittest.TestSuite() -- for fn in os.listdir(here): -- if fn.startswith("test") and fn.endswith(".py"): -- modname = "unittest.test." + fn[:-3] -- try: -- __import__(modname) -- except unittest.SkipTest: -- continue -- module = sys.modules[modname] -- suite.addTest(loader.loadTestsFromModule(module)) -- suite.addTest(loader.loadTestsFromName('unittest.test.testmock')) -- return suite -- -- --if __name__ == "__main__": -- unittest.main(defaultTest="suite") -diff --git a/Lib/unittest/test/__main__.py b/Lib/unittest/test/__main__.py -deleted file mode 100644 -index 44d0591e84..0000000000 ---- a/Lib/unittest/test/__main__.py -+++ /dev/null -@@ -1,18 +0,0 @@ --import os --import unittest -- -- --def load_tests(loader, standard_tests, pattern): -- # top level directory cached on loader instance -- this_dir = os.path.dirname(__file__) -- pattern = pattern or "test_*.py" -- # We are inside unittest.test, so the top-level is two notches up -- top_level_dir = os.path.dirname(os.path.dirname(this_dir)) -- package_tests = loader.discover(start_dir=this_dir, pattern=pattern, -- top_level_dir=top_level_dir) -- standard_tests.addTests(package_tests) -- return standard_tests -- -- --if __name__ == '__main__': -- unittest.main() -diff --git a/PCbuild/lib.pyproj b/PCbuild/lib.pyproj -index 692b083349..f3f44d1d8f 100644 ---- a/PCbuild/lib.pyproj -+++ b/PCbuild/lib.pyproj -@@ -1491,33 +1491,33 @@ - - - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -@@ -1804,6 +1804,8 @@ - - - -+ -+ - - - -@@ -1813,8 +1815,6 @@ - - - -- -- - - - diff --git a/Tools/wasm/wasm_assets.py b/Tools/wasm/wasm_assets.py -index d0a0570840..67afde60f0 100755 +index aa09d2dda8..30aad35760 100755 --- a/Tools/wasm/wasm_assets.py +++ b/Tools/wasm/wasm_assets.py -@@ -112,7 +112,6 @@ +@@ -113,7 +113,6 @@ # regression test sub directories OMIT_SUBDIRS = ( "tkinter/test/", - "unittest/test/", ) - def get_builddir(args: argparse.Namespace) -> pathlib.Path: + SYSCONFIG_NAMES = ( -- -2.29.2.windows.2 +2.25.1 diff --git a/cpython/patches/0008-Move-test-directories.patch b/cpython/patches/0007-Move-test-directories.patch similarity index 87% rename from cpython/patches/0008-Move-test-directories.patch rename to cpython/patches/0007-Move-test-directories.patch index 7ca56f31b..020b2956f 100644 --- a/cpython/patches/0008-Move-test-directories.patch +++ b/cpython/patches/0007-Move-test-directories.patch @@ -1,7 +1,7 @@ -From 4c71c808cc65ed6003b1e29d583c71586ebb36e1 Mon Sep 17 00:00:00 2001 +From 19171eafd8a07b9df74014b757e51ece237a2943 Mon Sep 17 00:00:00 2001 From: ryanking13 Date: Wed, 25 Jan 2023 15:54:16 +0900 -Subject: [PATCH 8/9] Move test directories +Subject: [PATCH 7/8] Move test directories --- Makefile.pre.in | 6 +++--- @@ -32,5 +32,5 @@ index b356f6293e..68c55a356a 100644 TEST_MODULES=@TEST_MODULES@ libinstall: all $(srcdir)/Modules/xxmodule.c -- -2.29.2.windows.2 +2.25.1 diff --git a/cpython/patches/0007-gh-93839-Use-load_package_tests-for-testmock-GH-9405.patch b/cpython/patches/0007-gh-93839-Use-load_package_tests-for-testmock-GH-9405.patch deleted file mode 100644 index 33531df3b..000000000 --- a/cpython/patches/0007-gh-93839-Use-load_package_tests-for-testmock-GH-9405.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 50ebd72fb0e69c78f95cea3d4a47589beb91ac37 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 21 Jun 2022 14:51:39 +0200 -Subject: [PATCH 7/9] gh-93839: Use load_package_tests() for testmock (GH-94055) - -Fixes failing tests on WebAssembly platforms. - -Automerge-Triggered-By: GH:tiran ---- - Lib/test/test_unittest/testmock/__init__.py | 17 +++-------------- - 1 file changed, 3 insertions(+), 14 deletions(-) - -diff --git a/Lib/test/test_unittest/testmock/__init__.py b/Lib/test/test_unittest/testmock/__init__.py -index 6ee60b2376..bc502ef32d 100644 ---- a/Lib/test/test_unittest/testmock/__init__.py -+++ b/Lib/test/test_unittest/testmock/__init__.py -@@ -1,17 +1,6 @@ --import os --import sys --import unittest -+import os.path -+from test.support import load_package_tests - - --here = os.path.dirname(__file__) --loader = unittest.defaultTestLoader -- - def load_tests(*args): -- suite = unittest.TestSuite() -- for fn in os.listdir(here): -- if fn.startswith("test") and fn.endswith(".py"): -- modname = "test.test_unittest.testmock." + fn[:-3] -- __import__(modname) -- module = sys.modules[modname] -- suite.addTest(loader.loadTestsFromModule(module)) -- return suite -+ return load_package_tests(os.path.dirname(__file__), *args) --- -2.29.2.windows.2 - diff --git a/cpython/patches/0008-Make-Emscripten-trampolines-work-with-JSPI.patch b/cpython/patches/0008-Make-Emscripten-trampolines-work-with-JSPI.patch new file mode 100644 index 000000000..8367feff2 --- /dev/null +++ b/cpython/patches/0008-Make-Emscripten-trampolines-work-with-JSPI.patch @@ -0,0 +1,371 @@ +From 68a77d4dab1c27231fed2a541095929bdb2f34ab Mon Sep 17 00:00:00 2001 +From: Hood Chatham +Date: Wed, 28 Jun 2023 10:46:19 -0700 +Subject: [PATCH 8/8] Make Emscripten trampolines work with JSPI + +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. +--- + .../internal/pycore_emscripten_trampoline.h | 55 +++++++++++++ + Include/internal/pycore_object.h | 28 +------ + Include/internal/pycore_runtime.h | 5 ++ + Objects/descrobject.c | 22 ++++-- + Objects/methodobject.c | 7 -- + Python/emscripten_trampoline.c | 79 +++++++++++++++++++ + Python/pylifecycle.c | 3 + + Python/pystate.c | 2 + + configure | 4 +- + configure.ac | 4 +- + 10 files changed, 163 insertions(+), 46 deletions(-) + create mode 100644 Include/internal/pycore_emscripten_trampoline.h + create mode 100644 Python/emscripten_trampoline.c + +diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h +new file mode 100644 +index 0000000000..e5031c8362 +--- /dev/null ++++ b/Include/internal/pycore_emscripten_trampoline.h +@@ -0,0 +1,55 @@ ++#ifndef Py_EMSCRIPTEN_TRAMPOLINE_H ++#define Py_EMSCRIPTEN_TRAMPOLINE_H ++ ++#include "pycore_runtime.h" // _PyRuntimeState ++ ++/** ++ * 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(_PyRuntimeState *runtime); ++ ++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(runtime) ++ ++#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 +diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h +index f022f82469..8bf4ebac38 100644 +--- a/Include/internal/pycore_object.h ++++ b/Include/internal/pycore_object.h +@@ -10,6 +10,7 @@ extern "C" { + + #include + #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() ++#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall() + #include "pycore_interp.h" // PyInterpreterState.gc + #include "pycore_pystate.h" // _PyInterpreterState_GET() + #include "pycore_runtime.h" // _PyRuntime +@@ -277,33 +278,6 @@ extern PyObject* _PyType_GetSubclasses(PyTypeObject *); + + PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *); + +-/* C function call trampolines to mitigate bad function pointer casts. +- * +- * 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 occurences of bad fpcasts on Emscripten. +- */ +-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +-#define _PyCFunction_TrampolineCall(meth, self, args) \ +- _PyCFunctionWithKeywords_TrampolineCall( \ +- (*(PyCFunctionWithKeywords)(void(*)(void))meth), self, args, NULL) +-extern PyObject* _PyCFunctionWithKeywords_TrampolineCall( +- PyCFunctionWithKeywords meth, PyObject *, PyObject *, PyObject *); +-#else +-#define _PyCFunction_TrampolineCall(meth, self, args) \ +- (meth)((self), (args)) +-#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ +- (meth)((self), (args), (kw)) +-#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE +- + #ifdef __cplusplus + } + #endif +diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h +index ae63ae74af..521a1e87a4 100644 +--- a/Include/internal/pycore_runtime.h ++++ b/Include/internal/pycore_runtime.h +@@ -75,6 +75,11 @@ typedef struct pyruntimestate { + /* Is Python fully initialized? Set to 1 by Py_Initialize() */ + int initialized; + ++#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) ++ /* Choose between trampoline based on type reflection vs based on EM_JS */ ++ int wasm_type_reflection_available; ++#endif ++ + /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize() + is called again. + +diff --git a/Objects/descrobject.c b/Objects/descrobject.c +index 4d8b83758b..f9d4c00399 100644 +--- a/Objects/descrobject.c ++++ b/Objects/descrobject.c +@@ -15,15 +15,21 @@ class property "propertyobject *" "&PyProperty_Type" + + // see pycore_object.h + #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +-#include +-EM_JS(int, descr_set_trampoline_call, (setter set, PyObject *obj, PyObject *value, void *closure), { +- return wasmTable.get(set)(obj, value, closure); +-}); +- +-EM_JS(PyObject*, descr_get_trampoline_call, (getter get, PyObject *obj, void *closure), { +- return wasmTable.get(get)(obj, closure); +-}); ++extern PyObject* ++_PyEM_TrampolineCall(PyCFunctionWithKeywords func, ++ PyObject* self, ++ PyObject* args, ++ PyObject* kw); ++ ++#define descr_set_trampoline_call(set, obj, value, closure) \ ++ ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure))) ++ ++ ++#define descr_get_trampoline_call(get, obj, closure) \ ++ _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL) ++ + #else ++ + #define descr_set_trampoline_call(set, obj, value, closure) \ + (set)((obj), (value), (closure)) + +diff --git a/Objects/methodobject.c b/Objects/methodobject.c +index 953cf4666d..d344bfa234 100644 +--- a/Objects/methodobject.c ++++ b/Objects/methodobject.c +@@ -555,10 +555,3 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) + return _Py_CheckFunctionResult(tstate, func, result, NULL); + } + +-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +-#include +- +-EM_JS(PyObject*, _PyCFunctionWithKeywords_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *self, PyObject *args, PyObject *kw), { +- return wasmTable.get(func)(self, args, kw); +-}); +-#endif +diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c +new file mode 100644 +index 0000000000..33c9d25d2e +--- /dev/null ++++ b/Python/emscripten_trampoline.c +@@ -0,0 +1,79 @@ ++#if defined(PY_CALL_TRAMPOLINE) ++ ++#include // EM_JS, EM_JS_DEPS ++#include ++#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_DEPS(_PyEMJS_TrampolineCall, "$getWasmTableEntry") ++EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { ++ return getWasmTableEntry(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(getWasmTableEntry(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(PyCFunctionWithKeywords func, ++ PyObject* self, ++ PyObject* args, ++ PyObject* kw) ++{ ++ if (!_PyRuntime.wasm_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 +diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c +index 9248e971d9..05fd28b2df 100644 +--- a/Python/pylifecycle.c ++++ b/Python/pylifecycle.c +@@ -5,6 +5,7 @@ + #include "pycore_bytesobject.h" // _PyBytes_InitTypes() + #include "pycore_ceval.h" // _PyEval_FiniGIL() + #include "pycore_context.h" // _PyContext_Init() ++#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() + #include "pycore_exceptions.h" // _PyExc_InitTypes() + #include "pycore_dict.h" // _PyDict_Fini() + #include "pycore_fileutils.h" // _Py_ResetForceASCII() +@@ -606,7 +607,9 @@ pycore_init_runtime(_PyRuntimeState *runtime, + if (_PyStatus_EXCEPTION(status)) { + return status; + } ++ + return _PyStatus_OK(); ++ + } + + +diff --git a/Python/pystate.c b/Python/pystate.c +index dfca3f5fd7..0dd2e7733e 100644 +--- a/Python/pystate.c ++++ b/Python/pystate.c +@@ -2,6 +2,7 @@ + /* Thread and interpreter state structures and their interfaces */ + + #include "Python.h" ++#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() + #include "pycore_ceval.h" + #include "pycore_code.h" // stats + #include "pycore_frame.h" +@@ -126,6 +127,7 @@ init_runtime(_PyRuntimeState *runtime, + runtime->unicode_ids.lock = unicode_ids_mutex; + + runtime->_initialized = 1; ++ _Py_EmscriptenTrampoline_Init(runtime); + } + + PyStatus +diff --git a/configure b/configure +index 9e76287725..630ba49c0b 100755 +--- a/configure ++++ b/configure +@@ -15083,8 +15083,8 @@ PLATFORM_OBJS= + case $ac_sys_system in #( + Emscripten) : + +- as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o' +- as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h' ++ as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o Python/emscripten_trampoline.o' ++ as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h' + ;; #( + *) : + ;; +diff --git a/configure.ac b/configure.ac +index c62a565eb6..9b488da03b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -4523,8 +4523,8 @@ PLATFORM_OBJS= + + AS_CASE([$ac_sys_system], + [Emscripten], [ +- AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o']) +- AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h']) ++ AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o Python/emscripten_trampoline.o']) ++ AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h']) + ], + ) + AC_SUBST([PLATFORM_HEADERS]) +-- +2.25.1 +