Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 40e3f49

Browse filesBrowse files
authored
_ctypes pt. 3 (#5530)
* Initial CFuncPtr implementation * function calling via libffi * working no-arg function call Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
1 parent 5619689 commit 40e3f49
Copy full SHA for 40e3f49

File tree

8 files changed

+386
-61
lines changed
Filter options

8 files changed

+386
-61
lines changed

‎Cargo.lock

Copy file name to clipboardExpand all lines: Cargo.lock
+18-17Lines changed: 18 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎extra_tests/snippets/builtins_ctypes.py

Copy file name to clipboardExpand all lines: extra_tests/snippets/builtins_ctypes.py
+134Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
11
import os as _os, sys as _sys
2+
import types as _types
23

4+
from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
35
from _ctypes import sizeof
46
from _ctypes import _SimpleCData
7+
from _ctypes import CFuncPtr as _CFuncPtr
8+
59
from struct import calcsize as _calcsize
610

11+
12+
DEFAULT_MODE = RTLD_LOCAL
13+
if _os.name == "posix" and _sys.platform == "darwin":
14+
# On OS X 10.3, we use RTLD_GLOBAL as default mode
15+
# because RTLD_LOCAL does not work at least on some
16+
# libraries. OS X 10.3 is Darwin 7, so we check for
17+
# that.
18+
19+
if int(_os.uname().release.split('.')[0]) < 8:
20+
DEFAULT_MODE = RTLD_GLOBAL
21+
22+
from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
23+
FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
24+
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
25+
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
26+
727
def create_string_buffer(init, size=None):
828
"""create_string_buffer(aBytes) -> character array
929
create_string_buffer(anInteger) -> character array
@@ -131,3 +151,117 @@ class c_bool(_SimpleCData):
131151
# s = create_string_buffer(b'\000' * 32)
132152
assert i.value == 42
133153
assert abs(f.value - 3.14) < 1e-06
154+
155+
if _os.name == "nt":
156+
from _ctypes import LoadLibrary as _dlopen
157+
from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL
158+
elif _os.name == "posix":
159+
from _ctypes import dlopen as _dlopen
160+
161+
class CDLL(object):
162+
"""An instance of this class represents a loaded dll/shared
163+
library, exporting functions using the standard C calling
164+
convention (named 'cdecl' on Windows).
165+
166+
The exported functions can be accessed as attributes, or by
167+
indexing with the function name. Examples:
168+
169+
<obj>.qsort -> callable object
170+
<obj>['qsort'] -> callable object
171+
172+
Calling the functions releases the Python GIL during the call and
173+
reacquires it afterwards.
174+
"""
175+
_func_flags_ = _FUNCFLAG_CDECL
176+
_func_restype_ = c_int
177+
# default values for repr
178+
_name = '<uninitialized>'
179+
_handle = 0
180+
_FuncPtr = None
181+
182+
def __init__(self, name, mode=DEFAULT_MODE, handle=None,
183+
use_errno=False,
184+
use_last_error=False,
185+
winmode=None):
186+
self._name = name
187+
flags = self._func_flags_
188+
if use_errno:
189+
flags |= _FUNCFLAG_USE_ERRNO
190+
if use_last_error:
191+
flags |= _FUNCFLAG_USE_LASTERROR
192+
if _sys.platform.startswith("aix"):
193+
"""When the name contains ".a(" and ends with ")",
194+
e.g., "libFOO.a(libFOO.so)" - this is taken to be an
195+
archive(member) syntax for dlopen(), and the mode is adjusted.
196+
Otherwise, name is presented to dlopen() as a file argument.
197+
"""
198+
if name and name.endswith(")") and ".a(" in name:
199+
mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW )
200+
if _os.name == "nt":
201+
if winmode is not None:
202+
mode = winmode
203+
else:
204+
import nt
205+
mode = 4096
206+
if '/' in name or '\\' in name:
207+
self._name = nt._getfullpathname(self._name)
208+
mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
209+
210+
class _FuncPtr(_CFuncPtr):
211+
_flags_ = flags
212+
_restype_ = self._func_restype_
213+
self._FuncPtr = _FuncPtr
214+
215+
if handle is None:
216+
self._handle = _dlopen(self._name, mode)
217+
else:
218+
self._handle = handle
219+
220+
def __repr__(self):
221+
return "<%s '%s', handle %x at %#x>" % \
222+
(self.__class__.__name__, self._name,
223+
(self._handle & (_sys.maxsize*2 + 1)),
224+
id(self) & (_sys.maxsize*2 + 1))
225+
226+
def __getattr__(self, name):
227+
if name.startswith('__') and name.endswith('__'):
228+
raise AttributeError(name)
229+
func = self.__getitem__(name)
230+
setattr(self, name, func)
231+
return func
232+
233+
def __getitem__(self, name_or_ordinal):
234+
func = self._FuncPtr((name_or_ordinal, self))
235+
if not isinstance(name_or_ordinal, int):
236+
func.__name__ = name_or_ordinal
237+
return func
238+
239+
class LibraryLoader(object):
240+
def __init__(self, dlltype):
241+
self._dlltype = dlltype
242+
243+
def __getattr__(self, name):
244+
if name[0] == '_':
245+
raise AttributeError(name)
246+
try:
247+
dll = self._dlltype(name)
248+
except OSError:
249+
raise AttributeError(name)
250+
setattr(self, name, dll)
251+
return dll
252+
253+
def __getitem__(self, name):
254+
return getattr(self, name)
255+
256+
def LoadLibrary(self, name):
257+
return self._dlltype(name)
258+
259+
__class_getitem__ = classmethod(_types.GenericAlias)
260+
261+
cdll = LibraryLoader(CDLL)
262+
263+
if _os.name == "posix" or _sys.platform == "darwin":
264+
pass
265+
else:
266+
libc = cdll.msvcrt
267+
print("rand", libc.rand())

‎vm/Cargo.toml

Copy file name to clipboardExpand all lines: vm/Cargo.toml
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,12 @@ uname = "0.1.1"
100100
rustyline = { workspace = true }
101101
which = "6"
102102
errno = "0.3"
103-
libloading = "0.8"
104103
widestring = { workspace = true }
105104

105+
[target.'cfg(all(any(target_os = "linux", target_os = "macos", target_os = "windows"), not(any(target_env = "musl", target_env = "sgx"))))'.dependencies]
106+
libffi = "3.2"
107+
libloading = "0.8"
108+
106109
[target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies]
107110
num_cpus = "1.13.1"
108111

‎vm/src/stdlib/ctypes.rs

Copy file name to clipboardExpand all lines: vm/src/stdlib/ctypes.rs
+23-4Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub(crate) mod _ctypes {
3737
use super::base::PyCSimple;
3838
use crate::builtins::PyTypeRef;
3939
use crate::class::StaticType;
40-
use crate::function::Either;
40+
use crate::function::{Either, OptionalArg};
4141
use crate::stdlib::ctypes::library;
4242
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
4343
use crossbeam_utils::atomic::AtomicCell;
@@ -180,12 +180,31 @@ pub(crate) mod _ctypes {
180180
}
181181

182182
#[pyfunction(name = "LoadLibrary")]
183-
fn load_library(name: String, vm: &VirtualMachine) -> PyResult<usize> {
183+
fn load_library_windows(
184+
name: String,
185+
_load_flags: OptionalArg<i32>,
186+
vm: &VirtualMachine,
187+
) -> PyResult<usize> {
188+
// TODO: audit functions first
189+
// TODO: load_flags
190+
let cache = library::libcache();
191+
let mut cache_write = cache.write();
192+
let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap();
193+
Ok(id)
194+
}
195+
196+
#[pyfunction(name = "dlopen")]
197+
fn load_library_unix(
198+
name: String,
199+
_load_flags: OptionalArg<i32>,
200+
vm: &VirtualMachine,
201+
) -> PyResult<usize> {
184202
// TODO: audit functions first
203+
// TODO: load_flags
185204
let cache = library::libcache();
186205
let mut cache_write = cache.write();
187-
let lib_ref = cache_write.get_or_insert_lib(&name, vm).unwrap();
188-
Ok(lib_ref.get_pointer())
206+
let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap();
207+
Ok(id)
189208
}
190209

191210
#[pyfunction(name = "FreeLibrary")]

‎vm/src/stdlib/ctypes/base.rs

Copy file name to clipboardExpand all lines: vm/src/stdlib/ctypes/base.rs
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ use num_traits::ToPrimitive;
1010
use rustpython_common::lock::PyRwLock;
1111
use std::fmt::Debug;
1212

13+
pub fn ffi_type_from_str(_type_: &str) -> Option<libffi::middle::Type> {
14+
match _type_ {
15+
"c" => Some(libffi::middle::Type::u8()),
16+
"u" => Some(libffi::middle::Type::u32()),
17+
"b" => Some(libffi::middle::Type::i8()),
18+
"B" => Some(libffi::middle::Type::u8()),
19+
"h" => Some(libffi::middle::Type::i16()),
20+
"H" => Some(libffi::middle::Type::u16()),
21+
"i" => Some(libffi::middle::Type::i32()),
22+
"I" => Some(libffi::middle::Type::u32()),
23+
"l" => Some(libffi::middle::Type::i32()),
24+
"L" => Some(libffi::middle::Type::u32()),
25+
"q" => Some(libffi::middle::Type::i64()),
26+
"Q" => Some(libffi::middle::Type::u64()),
27+
"f" => Some(libffi::middle::Type::f32()),
28+
"d" => Some(libffi::middle::Type::f64()),
29+
"g" => Some(libffi::middle::Type::f64()),
30+
"?" => Some(libffi::middle::Type::u8()),
31+
"z" => Some(libffi::middle::Type::u64()),
32+
"Z" => Some(libffi::middle::Type::u64()),
33+
"P" => Some(libffi::middle::Type::u64()),
34+
_ => None,
35+
}
36+
}
37+
1338
#[allow(dead_code)]
1439
fn set_primitive(_type_: &str, value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
1540
match _type_ {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.