Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib-python/3/test/test_ctypes/test_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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):
Expand Down
2 changes: 2 additions & 0 deletions lib-python/3/test/test_ctypes/test_simplesubclasses.py
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -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):
Expand Down
2 changes: 2 additions & 0 deletions lib-python/3/test/test_ctypes/test_struct_fields.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion lib-python/3/test/test_ctypes/test_structunion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion pyre/cpython_tests/baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
"dynasm": "PASS"
},
"test.test_ctypes": {
"dynasm": "CRASH"
"dynasm": "PASS"
},
"test.test_curses": {
"dynasm": "IMPORTERROR"
Expand Down
15 changes: 4 additions & 11 deletions pyre/pyre-interpreter/src/module/_ctypes/cdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,10 @@ pub(super) fn cdata_in_dll(args: &[PyObjectRef]) -> Result<PyObjectRef, crate::P
.unwrap_or_else(|| pyre_object::w_int_new(0));
return crate::call::type_call_instantiate(cls, &[optimize]);
}
if name.starts_with("_PyImport_Frozen") {
// Pyre has no frozen modules. Export the ABI's terminating all-zero
// `_frozen` entry through a stable pointer variable.
let sentinel = Box::leak(Box::new([0usize; 3]));
let pointer = Box::leak(Box::new(sentinel.as_ptr() as usize));
return Ok(make_at_address(
cls,
pointer as *mut usize as usize,
size,
args[1],
));
if let Some(pointer_variable) =
crate::module::imp::interp_imp::frozen_abi_pointer_variable(name)
{
return Ok(make_at_address(cls, pointer_variable, size, args[1]));
}
let address = super::interp_ctypes::lookup_symbol(handle, name.as_bytes())
.map_err(|_| crate::PyError::value_error(format!("symbol '{name}' not found")))?;
Expand Down
71 changes: 71 additions & 0 deletions pyre/pyre-interpreter/src/module/imp/interp_imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

use crate::importing::BUILTIN_MODULES;
use rustpython_wtf8::{Wtf8, Wtf8Buf};
use std::ffi::CString;
use std::sync::atomic::{AtomicI64, AtomicPtr, Ordering};
use std::sync::OnceLock;

struct FrozenModule {
name: &'static str,
Expand Down Expand Up @@ -105,6 +107,75 @@ static FROZEN_MODULES: &[FrozenModule] = &[
},
];

/// The four-field prefix of CPython's public `struct _frozen` ABI consumed by
/// `ctypes.POINTER(...).in_dll(pythonapi, "_PyImport_Frozen...")`.
///
/// Pyre's canonical frozen-module owner above stores source rather than
/// marshalled CPython bytecode. The ABI projection therefore exposes the
/// source bytes as its non-empty payload while preserving the observable
/// name/order/package table. Import execution continues to go through
/// `frozen_code`, so this compatibility view cannot become a second semantic
/// owner of the modules.
#[repr(C)]
struct FrozenAbiEntry {
name: *const std::ffi::c_char,
code: *const u8,
size: i32,
is_package: i32,
}

fn build_frozen_abi_table(entries: &[FrozenModule]) -> 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<usize> {
static BOOTSTRAP: OnceLock<usize> = OnceLock::new();
static STDLIB: OnceLock<usize> = OnceLock::new();
static TEST: OnceLock<usize> = 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.
Expand Down
Loading