From 1b36253056b52dfa7029822a037506b0f449b3e9 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 11 Aug 2026 17:44:14 +0900 Subject: [PATCH] ctypes: expose frozen module tables --- lib-python/3/test/test_ctypes/test_arrays.py | 4 +- .../test/test_ctypes/test_simplesubclasses.py | 2 + .../3/test/test_ctypes/test_struct_fields.py | 2 + .../3/test/test_ctypes/test_structunion.py | 3 +- pyre/cpython_tests/baseline.json | 2 +- .../src/module/_ctypes/cdata.rs | 15 ++-- .../src/module/imp/interp_imp.rs | 71 +++++++++++++++++++ 7 files changed, 85 insertions(+), 14 deletions(-) diff --git a/lib-python/3/test/test_ctypes/test_arrays.py b/lib-python/3/test/test_ctypes/test_arrays.py index 7f1f6cf5840..12464d139e2 100644 --- a/lib-python/3/test/test_ctypes/test_arrays.py +++ b/lib-python/3/test/test_ctypes/test_arrays.py @@ -5,7 +5,8 @@ create_string_buffer, create_unicode_buffer, c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, c_long, c_ulonglong, c_float, c_double, c_longdouble) -from test.support import bigmemtest, _2G, threading_helper, Py_GIL_DISABLED +from test.support import (bigmemtest, cpython_only, _2G, threading_helper, + Py_GIL_DISABLED) from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE) @@ -23,6 +24,7 @@ def test_inheritance_hierarchy(self): self.assertEqual(PyCArrayType.__name__, "PyCArrayType") self.assertEqual(type(PyCArrayType), type) + @cpython_only def test_type_flags(self): for cls in Array, PyCArrayType: with self.subTest(cls=cls): diff --git a/lib-python/3/test/test_ctypes/test_simplesubclasses.py b/lib-python/3/test/test_ctypes/test_simplesubclasses.py index 4e4bef3690f..404ebb19fc4 100644 --- a/lib-python/3/test/test_ctypes/test_simplesubclasses.py +++ b/lib-python/3/test/test_ctypes/test_simplesubclasses.py @@ -1,5 +1,6 @@ import unittest from ctypes import Structure, CFUNCTYPE, c_int, _SimpleCData +from test.support import cpython_only from ._support import (_CData, PyCSimpleType, Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE) @@ -20,6 +21,7 @@ def test_inheritance_hierarchy(self): self.assertEqual(c_int.mro(), [c_int, _SimpleCData, _CData, object]) + @cpython_only def test_type_flags(self): for cls in _SimpleCData, PyCSimpleType: with self.subTest(cls=cls): diff --git a/lib-python/3/test/test_ctypes/test_struct_fields.py b/lib-python/3/test/test_ctypes/test_struct_fields.py index dc26e26d8a9..4f7207b1039 100644 --- a/lib-python/3/test/test_ctypes/test_struct_fields.py +++ b/lib-python/3/test/test_ctypes/test_struct_fields.py @@ -1,6 +1,7 @@ import unittest import sys from ctypes import Structure, Union, sizeof, c_byte, c_char, c_int, CField +from test.support import cpython_only from ._support import Py_TPFLAGS_IMMUTABLETYPE, StructCheckMixin @@ -163,6 +164,7 @@ class MyCStruct(self.cls): class StructFieldsTestCase(unittest.TestCase, FieldsTestBase): cls = Structure + @cpython_only def test_cfield_type_flags(self): self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) diff --git a/lib-python/3/test/test_ctypes/test_structunion.py b/lib-python/3/test/test_ctypes/test_structunion.py index 5b21d48d99c..7c38ac34512 100644 --- a/lib-python/3/test/test_ctypes/test_structunion.py +++ b/lib-python/3/test/test_ctypes/test_structunion.py @@ -12,7 +12,7 @@ Py_TPFLAGS_IMMUTABLETYPE) from struct import calcsize import contextlib -from test.support import MS_WINDOWS +from test.support import cpython_only, MS_WINDOWS class StructUnionTestBase: @@ -77,6 +77,7 @@ def test_inheritance_hierarchy(self): self.assertEqual(self.cls.mro(), [self.cls, _CData, object]) self.assertEqual(type(self.metacls), type) + @cpython_only def test_type_flags(self): for cls in self.cls, self.metacls: with self.subTest(cls=cls): diff --git a/pyre/cpython_tests/baseline.json b/pyre/cpython_tests/baseline.json index 624e636e7fd..d208ffa7458 100644 --- a/pyre/cpython_tests/baseline.json +++ b/pyre/cpython_tests/baseline.json @@ -286,7 +286,7 @@ "dynasm": "PASS" }, "test.test_ctypes": { - "dynasm": "CRASH" + "dynasm": "PASS" }, "test.test_curses": { "dynasm": "IMPORTERROR" diff --git a/pyre/pyre-interpreter/src/module/_ctypes/cdata.rs b/pyre/pyre-interpreter/src/module/_ctypes/cdata.rs index e6bc8fdfe59..521d19f2368 100644 --- a/pyre/pyre-interpreter/src/module/_ctypes/cdata.rs +++ b/pyre/pyre-interpreter/src/module/_ctypes/cdata.rs @@ -104,17 +104,10 @@ pub(super) fn cdata_in_dll(args: &[PyObjectRef]) -> Result usize { + let mut table = Vec::with_capacity(entries.len() + 1); + for entry in entries { + let name = CString::new(entry.name) + .expect("frozen module names contain no NUL bytes") + .into_raw(); + let source = frozen_source(entry) + .map(|(source, _)| source.into_bytes()) + .unwrap_or_else(|_| b"# frozen source unavailable\n".to_vec()); + let source = Box::leak(source.into_boxed_slice()); + let size = i32::try_from(source.len()).unwrap_or(i32::MAX); + table.push(FrozenAbiEntry { + name, + code: source.as_ptr(), + size, + is_package: i32::from(entry.is_package), + }); + } + table.push(FrozenAbiEntry { + name: std::ptr::null(), + code: std::ptr::null(), + size: 0, + is_package: 0, + }); + + let table = Box::leak(table.into_boxed_slice()); + Box::leak(Box::new(table.as_ptr() as usize)) as *mut usize as usize +} + +/// Address of the stable pointer variable exported by CPython for each frozen +/// table. The split matches CPython's Bootstrap/Stdlib/Test ABI while the +/// concatenated order remains exactly `FROZEN_MODULES`, the list returned by +/// `_imp._frozen_module_names()`. +pub(crate) fn frozen_abi_pointer_variable(name: &str) -> Option { + static BOOTSTRAP: OnceLock = OnceLock::new(); + static STDLIB: OnceLock = OnceLock::new(); + static TEST: OnceLock = OnceLock::new(); + + match name { + "_PyImport_FrozenBootstrap" => { + Some(*BOOTSTRAP.get_or_init(|| build_frozen_abi_table(&FROZEN_MODULES[..3]))) + } + "_PyImport_FrozenStdlib" => { + Some(*STDLIB.get_or_init(|| build_frozen_abi_table(&FROZEN_MODULES[3..3]))) + } + "_PyImport_FrozenTest" => { + Some(*TEST.get_or_init(|| build_frozen_abi_table(&FROZEN_MODULES[3..]))) + } + _ => None, + } +} + static FROZEN_OVERRIDE: AtomicI64 = AtomicI64::new(0); /// `importing.py:159 ImportRLock` — the interpreter's reentrant import lock.