this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <ffi.h>
3
4#include "cpython-data.h"
5
6#include "builtins.h"
7#include "bytes-builtins.h"
8#include "frame.h"
9#include "globals.h"
10#include "module-builtins.h"
11#include "modules.h"
12#include "objects.h"
13#include "os.h"
14#include "thread.h"
15#include "type-builtins.h"
16
17namespace py {
18
19// a table entry describing a predefined ctypes type
20struct FieldDesc {
21 char code;
22 ffi_type* pffi_type;
23};
24
25static_assert(sizeof(long long) == 8, "expected sizof(long long) == 8");
26static_assert(sizeof(wchar_t) == 4, "expected sizeof(wchar) == 4");
27static_assert(sizeof(bool) == 1, "expected sizeof(bool) == 1");
28
29// clang-format off
30static FieldDesc format_table[] = {
31 {'s', &ffi_type_pointer},
32 {'b', &ffi_type_schar},
33 {'B', &ffi_type_uchar},
34 {'c', &ffi_type_schar},
35 {'d', &ffi_type_double},
36 {'g', &ffi_type_longdouble},
37 {'f', &ffi_type_float},
38 {'h', &ffi_type_sshort},
39 {'H', &ffi_type_ushort},
40 {'i', &ffi_type_sint},
41 {'I', &ffi_type_uint},
42 {'l', &ffi_type_slong},
43 {'L', &ffi_type_ulong},
44 {'q', &ffi_type_sint64}, // 'q' and 'Q' are `long long`
45 {'Q', &ffi_type_uint64},
46 {'P', &ffi_type_pointer},
47 {'z', &ffi_type_pointer},
48 {'u', &ffi_type_sint32}, // 'u' and 'U' are `wchar_t`
49 {'U', &ffi_type_pointer},
50 {'Z', &ffi_type_pointer},
51 {'?', &ffi_type_uint8}, // '?' is `bool`
52 {'O', &ffi_type_pointer},
53 {0, nullptr},
54};
55// clang-format on
56
57static FieldDesc* fieldDesc(const char fmt) {
58 for (FieldDesc* entry = format_table; entry->code != 0; ++entry) {
59 if (entry->code == fmt) return entry;
60 }
61 return nullptr;
62}
63
64static PyObject* cast(void* /* ptr */, PyObject* /* src */,
65 PyObject* /* ctype */) {
66 UNIMPLEMENTED("_ctypes cast");
67}
68
69static PyObject* stringAt(const char* /* ptr */, int /* size */) {
70 UNIMPLEMENTED("_ctypes stringAt");
71}
72
73static PyObject* wstringAt(const wchar_t* /* ptr */, int /* size */) {
74 UNIMPLEMENTED("_ctypes wstringAt");
75}
76
77void FUNC(_ctypes, __init_module__)(Thread* thread, const Module& module,
78 View<byte> bytecode) {
79 executeFrozenModule(thread, module, bytecode);
80
81 HandleScope scope(thread);
82 Runtime* runtime = thread->runtime();
83
84 Object rtld_local_name(&scope, runtime->symbols()->at(ID(RTLD_LOCAL)));
85 Object rtld_local(&scope, runtime->newInt(OS::kRtldLocal));
86 Object rtld_global_name(&scope, runtime->symbols()->at(ID(RTLD_GLOBAL)));
87 Object rtld_global(&scope, runtime->newInt(OS::kRtldGlobal));
88 moduleAtPut(thread, module, rtld_local_name, rtld_local);
89 moduleAtPut(thread, module, rtld_global_name, rtld_global);
90
91 Object cast_addr_name(&scope, runtime->symbols()->at(ID(_cast_addr)));
92 Object cast_addr(&scope, runtime->newIntFromCPtr(bit_cast<void*>(&cast)));
93 moduleAtPut(thread, module, cast_addr_name, cast_addr);
94
95 Object memmove_addr_name(&scope, runtime->symbols()->at(ID(_memmove_addr)));
96 Object memmove_addr(&scope,
97 runtime->newIntFromCPtr(bit_cast<void*>(&memmove)));
98 moduleAtPut(thread, module, memmove_addr_name, memmove_addr);
99
100 Object memset_addr_name(&scope, runtime->symbols()->at(ID(_memset_addr)));
101 Object memset_addr(&scope, runtime->newIntFromCPtr(bit_cast<void*>(&memset)));
102 moduleAtPut(thread, module, memset_addr_name, memset_addr);
103
104 Object string_at_addr_name(&scope,
105 runtime->symbols()->at(ID(_string_at_addr)));
106 Object string_at_addr(&scope,
107 runtime->newIntFromCPtr(bit_cast<void*>(&stringAt)));
108 moduleAtPut(thread, module, string_at_addr_name, string_at_addr);
109
110 Object wstring_at_addr_name(&scope,
111 runtime->symbols()->at(ID(_wstring_at_addr)));
112 Object wstring_at_addr(&scope,
113 runtime->newIntFromCPtr(bit_cast<void*>(&wstringAt)));
114 moduleAtPut(thread, module, wstring_at_addr_name, wstring_at_addr);
115}
116
117RawObject FUNC(_ctypes, _CharArray_value_to_bytes)(Thread* thread,
118 Arguments args) {
119 HandleScope scope(thread);
120 Object value(&scope, args.get(0));
121 word length = intUnderlying(args.get(1)).asWord();
122 if (value.isMmap()) {
123 Pointer value_ptr(&scope, Mmap::cast(*value).data());
124 DCHECK(value_ptr.length() >= length, "Mmap shorter than ctypes.Array");
125 byte* cptr = reinterpret_cast<byte*>(value_ptr.cptr());
126 word first_nul = Utils::memoryFindChar(cptr, length, '\0');
127 return thread->runtime()->newBytesWithAll(
128 {cptr, first_nul == -1 ? length : first_nul});
129 }
130 CHECK(value.isBytearray(), "unexpected ctypes.Array._value type");
131 Bytes value_bytes(&scope, Bytearray::cast(*value).items());
132 word first_nul = value_bytes.findByte(0, '\0', length);
133 return bytesSubseq(thread, value_bytes, 0,
134 first_nul == -1 ? length : first_nul);
135}
136
137RawObject FUNC(_ctypes, _call_cfuncptr)(Thread* thread, Arguments args) {
138 HandleScope scope(thread);
139
140 void* addr = Int::cast(args.get(0)).asCPtr();
141 Str type(&scope, args.get(1));
142 switch (type.byteAt(0)) {
143 case 'i': {
144 int int_result = reinterpret_cast<int (*)()>(addr)();
145 return thread->runtime()->newInt(int_result);
146 }
147 case 'z': {
148 char* bytes_result = reinterpret_cast<char* (*)()>(addr)();
149 return thread->runtime()->newBytesWithAll(View<byte>(
150 reinterpret_cast<byte*>(bytes_result), std::strlen(bytes_result)));
151 }
152 default: {
153 UNIMPLEMENTED("Not yet supported return value type");
154 }
155 }
156}
157
158RawObject FUNC(_ctypes, _memset)(Thread*, Arguments args) {
159 void* addr = Int::cast(args.get(0)).asCPtr();
160 OptInt<int> val = Int::cast(args.get(1)).asInt<int>();
161 if (val.error != CastError::None) {
162 UNIMPLEMENTED("ctypes.memset called with non-integer value");
163 }
164 OptInt<size_t> size = Int::cast(args.get(2)).asInt<size_t>();
165 if (size.error != CastError::None) {
166 UNIMPLEMENTED("ctypes.memset called with non-size_t size");
167 }
168 std::memset(addr, val.value, size.value);
169 return args.get(0);
170}
171
172RawObject FUNC(_ctypes, _shared_object_symbol_address)(Thread* thread,
173 Arguments args) {
174 HandleScope scope(thread);
175 Int handle(&scope, args.get(0));
176 Str name(&scope, args.get(1));
177 unique_c_ptr<char> name_cstr(name.toCStr());
178 const char* error_msg = nullptr;
179 void* address = OS::sharedObjectSymbolAddress(handle.asCPtr(),
180 name_cstr.get(), &error_msg);
181 if (address == nullptr) {
182 return thread->raiseWithFmt(LayoutId::kAttributeError, "%s",
183 error_msg != nullptr ? error_msg : "");
184 }
185 return thread->runtime()->newIntFromCPtr(address);
186}
187
188RawObject FUNC(_ctypes, _addressof)(Thread* thread, Arguments args) {
189 HandleScope scope(thread);
190 Object value(&scope, args.get(0));
191 if (value.isMmap()) {
192 Object buf(&scope, Mmap::cast(*value).data());
193 Pointer pointer(&scope, *buf);
194 return thread->runtime()->newIntFromCPtr(pointer.cptr());
195 }
196 UNIMPLEMENTED("Cannot construct pointer from non-mmap yet");
197}
198
199RawObject FUNC(_ctypes, _SimpleCData_value_to_type)(Thread* thread,
200 Arguments args) {
201 HandleScope scope(thread);
202 Runtime* runtime = thread->runtime();
203 Object value(&scope, args.get(0));
204 Str type(&scope, args.get(1));
205 Int offset(&scope, args.get(2));
206 switch (type.byteAt(0)) {
207 case 'H':
208 if (value.isMmap()) {
209 Pointer value_ptr(&scope, Mmap::cast(*value).data());
210 CHECK(value_ptr.length() >= 2, "Not enough memory");
211 uint16_t* cptr = reinterpret_cast<uint16_t*>(value_ptr.cptr());
212 return runtime->newInt(cptr[offset.asWord()]);
213 }
214 if (value.isUnbound()) {
215 return SmallInt::fromWord(0);
216 }
217 return *value;
218 case 'L':
219 if (value.isMmap()) {
220 Pointer value_ptr(&scope, Mmap::cast(*value).data());
221 CHECK(static_cast<uword>(value_ptr.length()) >= sizeof(unsigned long),
222 "Not enough memory");
223 long* cptr = reinterpret_cast<long*>(value_ptr.cptr());
224 return runtime->newInt(cptr[offset.asWord()]);
225 }
226 if (value.isUnbound()) {
227 return SmallInt::fromWord(0);
228 }
229 return *value;
230 default:
231 UNIMPLEMENTED("Cannot get values of non-uint16 objects yet");
232 }
233}
234
235RawObject FUNC(_ctypes, dlopen)(Thread* thread, Arguments args) {
236 HandleScope scope(thread);
237 Runtime* runtime = thread->runtime();
238
239 Object mode_obj(&scope, args.get(1));
240 if (!mode_obj.isInt()) {
241 return thread->raiseRequiresType(mode_obj, ID(int));
242 }
243 OptInt<int> mode_opt = Int::cast(*mode_obj).asInt<int>();
244 if (mode_opt.error != CastError::None) {
245 return thread->raiseWithFmt(LayoutId::kOverflowError,
246 "Python int too large to convert to C long");
247 }
248 int mode = mode_opt.value | OS::kRtldNow;
249 unique_c_ptr<char> name_cstr;
250 Object name_obj(&scope, args.get(0));
251 if (name_obj.isNoneType()) {
252 name_cstr.reset(nullptr);
253 } else if (name_obj.isStr()) {
254 name_cstr.reset(Str::cast(*name_obj).toCStr());
255 } else {
256 return thread->raiseRequiresType(name_obj, ID(str));
257 }
258
259 const char* error_msg = nullptr;
260 void* handle = OS::openSharedObject(name_cstr.get(), mode, &error_msg);
261 if (handle == nullptr) {
262 return thread->raiseWithFmt(LayoutId::kOSError, "%s",
263 error_msg != nullptr ? error_msg : "");
264 }
265 return runtime->newIntFromCPtr(handle);
266}
267
268RawObject FUNC(_ctypes, _sizeof_typeclass)(Thread*, Arguments args) {
269 DCHECK(args.get(0).isStr(), "bad internal call");
270 FieldDesc* field_desc = fieldDesc(Str::cast(args.get(0)).byteAt(0));
271 size_t size = field_desc->pffi_type->size;
272 return SmallInt::fromWord(size);
273}
274
275} // namespace py