this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2// moduleobject.c implementation
3#include "cpython-data.h"
4#include "cpython-func.h"
5
6#include "api-handle.h"
7#include "builtins-module.h"
8#include "function-builtins.h"
9#include "function-utils.h"
10#include "handles.h"
11#include "module-builtins.h"
12#include "objects.h"
13#include "os.h"
14#include "runtime.h"
15#include "trampolines.h"
16
17namespace py {
18
19using ExtensionModuleInitFunc = PyObject* (*)();
20extern struct _inittab _PyImport_Inittab[];
21struct _inittab* PyImport_Inittab = _PyImport_Inittab;
22
23PY_EXPORT int PyModule_CheckExact_Func(PyObject* obj) {
24 return ApiHandle::asObject(ApiHandle::fromPyObject(obj)).isModule();
25}
26
27PY_EXPORT int PyModule_Check_Func(PyObject* obj) {
28 return Thread::current()->runtime()->isInstanceOfModule(
29 ApiHandle::asObject(ApiHandle::fromPyObject(obj)));
30}
31
32static void moduleDefInit(PyModuleDef* def) {
33 if (def->m_base.m_index != 0) return;
34 def->m_base.m_index = Runtime::nextModuleIndex();
35}
36
37PY_EXPORT PyObject* PyModule_Create2(PyModuleDef* def, int) {
38 moduleDefInit(def);
39 Thread* thread = Thread::current();
40 HandleScope scope(thread);
41 Runtime* runtime = thread->runtime();
42 Object module_name(&scope, Runtime::internStrFromCStr(thread, def->m_name));
43 Module module(&scope, runtime->newModule(module_name));
44 module.setDef(runtime->newIntFromCPtr(def));
45
46 if (def->m_methods != nullptr) {
47 Object function_name(&scope, NoneType::object());
48 Object function(&scope, NoneType::object());
49 for (PyMethodDef* method = def->m_methods; method->ml_name != nullptr;
50 method++) {
51 if (method->ml_flags & METH_CLASS || method->ml_flags & METH_STATIC) {
52 thread->raiseWithFmt(
53 LayoutId::kValueError,
54 "module functions cannot set METH_CLASS or METH_STATIC");
55 return nullptr;
56 }
57 function_name = Runtime::internStrFromCStr(thread, method->ml_name);
58 function =
59 newCFunction(thread, method, function_name, module, module_name);
60 moduleAtPut(thread, module, function_name, function);
61 }
62 }
63
64 if (def->m_doc != nullptr) {
65 Object doc(&scope, runtime->newStrFromCStr(def->m_doc));
66 moduleAtPutById(thread, module, ID(__doc__), doc);
67 }
68
69 Py_ssize_t m_size = def->m_size;
70 void* state = nullptr;
71 if (m_size > 0) {
72 state = std::calloc(1, m_size);
73 if (state == nullptr) {
74 PyErr_NoMemory();
75 return nullptr;
76 }
77 }
78 module.setState(runtime->newIntFromCPtr(state));
79
80 // TODO(eelizondo): Check m_slots
81 // TODO(eelizondo): Set md_state
82 return ApiHandle::newReference(runtime, *module);
83}
84
85PY_EXPORT PyModuleDef* PyModule_GetDef(PyObject* pymodule) {
86 Thread* thread = Thread::current();
87 HandleScope scope(thread);
88 Object module_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pymodule)));
89 if (!thread->runtime()->isInstanceOfModule(*module_obj)) {
90 thread->raiseBadArgument();
91 return nullptr;
92 }
93 Module module(&scope, *module_obj);
94 Int def(&scope, module.def());
95 return static_cast<PyModuleDef*>(def.asCPtr());
96}
97
98PY_EXPORT PyObject* PyModule_GetDict(PyObject* pymodule) {
99 // Return the module_proxy object. Note that this is not a `PyDict` instance
100 // so contrary to cpython it will not work with `PyDict_xxx` functions. It
101 // does work with PyEval_EvalCode.
102 Thread* thread = Thread::current();
103 HandleScope scope(thread);
104 Object module_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pymodule)));
105 Runtime* runtime = thread->runtime();
106 if (!runtime->isInstanceOfModule(*module_obj)) {
107 thread->raiseBadArgument();
108 return nullptr;
109 }
110 Module module(&scope, *module_obj);
111 return ApiHandle::borrowedReference(runtime, module.moduleProxy());
112}
113
114PY_EXPORT PyObject* PyModule_GetNameObject(PyObject* mod) {
115 Thread* thread = Thread::current();
116 Runtime* runtime = thread->runtime();
117 HandleScope scope(thread);
118
119 Object module_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(mod)));
120 if (!runtime->isInstanceOfModule(*module_obj)) {
121 thread->raiseBadArgument();
122 return nullptr;
123 }
124 Module module(&scope, *module_obj);
125 Object name(&scope, moduleAtById(thread, module, ID(__name__)));
126 if (!runtime->isInstanceOfStr(*name)) {
127 thread->raiseWithFmt(LayoutId::kSystemError, "nameless module");
128 return nullptr;
129 }
130 return ApiHandle::newReference(runtime, *name);
131}
132
133PY_EXPORT void* PyModule_GetState(PyObject* mod) {
134 Thread* thread = Thread::current();
135 RawObject module_obj = ApiHandle::asObject(ApiHandle::fromPyObject(mod));
136 if (!thread->runtime()->isInstanceOfModule(module_obj)) {
137 thread->raiseBadArgument();
138 return nullptr;
139 }
140 RawModule module = module_obj.rawCast<RawModule>();
141 return Int::cast(module.state()).asCPtr();
142}
143
144PY_EXPORT PyObject* PyModuleDef_Init(PyModuleDef* def) {
145 moduleDefInit(def);
146 return reinterpret_cast<PyObject*>(def);
147}
148
149PY_EXPORT int PyModule_AddFunctions(PyObject* /* m */, PyMethodDef* /* s */) {
150 UNIMPLEMENTED("PyModule_AddFunctions");
151}
152
153word moduleExecDef(Thread* thread, const Module& module, PyModuleDef* def) {
154 Runtime* runtime = thread->runtime();
155 HandleScope scope(thread);
156 Object name_obj(&scope, moduleAtById(thread, module, ID(__name__)));
157 if (!runtime->isInstanceOfStr(*name_obj)) {
158 thread->raiseWithFmt(LayoutId::kSystemError, "nameless module");
159 return -1;
160 }
161
162 ApiHandle* handle = ApiHandle::borrowedReference(runtime, *module);
163 if (def->m_size >= 0) {
164 if (ApiHandle::cache(runtime, handle) == nullptr) {
165 DCHECK(ApiHandle::isBorrowedNoImmediate(handle), "handle must be marked borrowed");
166 ApiHandle::setCache(runtime, handle, std::calloc(def->m_size, 1));
167 if (!ApiHandle::cache(runtime, handle)) {
168 thread->raiseMemoryError();
169 return -1;
170 }
171 }
172 }
173
174 if (def->m_slots == nullptr) {
175 return 0;
176 }
177
178 Str name_str(&scope, *name_obj);
179 for (PyModuleDef_Slot* cur_slot = def->m_slots;
180 cur_slot != nullptr && cur_slot->slot != 0; cur_slot++) {
181 switch (cur_slot->slot) {
182 case Py_mod_create:
183 break;
184 case Py_mod_exec: {
185 typedef int (*slot_func)(PyObject*);
186 slot_func thunk = reinterpret_cast<slot_func>(cur_slot->value);
187 if ((*thunk)(handle) != 0) {
188 if (!thread->hasPendingException()) {
189 thread->raiseWithFmt(
190 LayoutId::kSystemError,
191 "execution of module %S failed without setting an exception",
192 &name_str);
193 }
194 return -1;
195 }
196 if (thread->hasPendingException()) {
197 thread->clearPendingException();
198 thread->raiseWithFmt(
199 LayoutId::kSystemError,
200 "execution of module %S failed without setting an exception",
201 &name_str);
202 return -1;
203 }
204 break;
205 }
206 default: {
207 thread->raiseWithFmt(LayoutId::kSystemError,
208 "module %S initialized with unknown slot %d",
209 &name_str, cur_slot->slot);
210 return -1;
211 }
212 }
213 }
214 return 0;
215}
216
217PY_EXPORT int PyModule_ExecDef(PyObject* pymodule, PyModuleDef* def) {
218 Thread* thread = Thread::current();
219 HandleScope scope(thread);
220 Object module_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pymodule)));
221 if (!thread->runtime()->isInstanceOfModule(*module_obj)) {
222 return -1;
223 }
224 Module module(&scope, *module_obj);
225 return moduleExecDef(thread, module, def);
226}
227
228PY_EXPORT PyObject* PyModule_FromDefAndSpec2(PyModuleDef* /* f */,
229 PyObject* /* c */, int /* n */) {
230 UNIMPLEMENTED("PyModule_FromDefAndSpec2");
231}
232
233PY_EXPORT const char* PyModule_GetFilename(PyObject* /* m */) {
234 UNIMPLEMENTED("PyModule_GetFilename");
235}
236
237PY_EXPORT PyObject* PyModule_GetFilenameObject(PyObject* pymodule) {
238 Thread* thread = Thread::current();
239 Runtime* runtime = thread->runtime();
240 HandleScope scope(thread);
241
242 Object module_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(pymodule)));
243 if (!runtime->isInstanceOfModule(*module_obj)) {
244 thread->raiseBadArgument();
245 return nullptr;
246 }
247 Module module(&scope, *module_obj);
248 Object filename(&scope, moduleAtById(thread, module, ID(__file__)));
249 if (!runtime->isInstanceOfStr(*filename)) {
250 thread->raiseWithFmt(LayoutId::kSystemError, "module filename missing");
251 return nullptr;
252 }
253 return ApiHandle::newReference(runtime, *filename);
254}
255
256PY_EXPORT const char* PyModule_GetName(PyObject* pymodule) {
257 PyObject* name = PyModule_GetNameObject(pymodule);
258 if (name == nullptr) return nullptr;
259 const char* result = PyUnicode_AsUTF8(name);
260 Py_DECREF(name);
261 return result;
262}
263
264PY_EXPORT PyObject* PyModule_New(const char* c_name) {
265 DCHECK(c_name != nullptr, "PyModule_New takes a valid string");
266 Thread* thread = Thread::current();
267 Runtime* runtime = thread->runtime();
268 HandleScope scope(thread);
269
270 Str name(&scope, runtime->newStrFromCStr(c_name));
271 return ApiHandle::newReference(runtime, runtime->newModule(name));
272}
273
274PY_EXPORT PyObject* PyModule_NewObject(PyObject* name) {
275 Thread* thread = Thread::current();
276 HandleScope scope(thread);
277 Object name_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(name)));
278 Runtime* runtime = thread->runtime();
279 return ApiHandle::newReference(runtime, runtime->newModule(name_obj));
280}
281
282PY_EXPORT int PyModule_SetDocString(PyObject* m, const char* doc) {
283 Thread* thread = Thread::current();
284 Runtime* runtime = thread->runtime();
285 HandleScope scope(thread);
286 Object module(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(m)));
287 Object uni(&scope, runtime->newStrFromCStr(doc));
288 if (!uni.isStr()) {
289 return -1;
290 }
291 Object name(&scope, runtime->symbols()->at(ID(__doc__)));
292 if (thread->invokeMethod3(module, ID(__setattr__), name, uni)
293 .isErrorException()) {
294 return -1;
295 }
296 return 0;
297}
298
299PY_EXPORT PyTypeObject* PyModule_Type_Ptr() {
300 Runtime* runtime = Thread::current()->runtime();
301 return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference(
302 runtime, runtime->typeAt(LayoutId::kModule)));
303}
304
305static RawObject initializeModule(Thread* thread, ExtensionModuleInitFunc init,
306 const Str& name) {
307 PyObject* module_or_def = (*init)();
308 if (module_or_def == nullptr) {
309 if (!thread->hasPendingException()) {
310 return thread->raiseWithFmt(
311 LayoutId::kSystemError,
312 "Initialization of '%S' failed without raising", &name);
313 }
314 return Error::exception();
315 }
316 HandleScope scope(thread);
317 Runtime* runtime = thread->runtime();
318 Object module_obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(module_or_def)));
319 if (!runtime->isInstanceOfModule(*module_obj)) {
320 // TODO(T39542987): Enable multi-phase module initialization
321 UNIMPLEMENTED("Multi-phase module initialization");
322 }
323
324 Module module(&scope, *module_obj);
325 PyModuleDef* def =
326 static_cast<PyModuleDef*>(Int::cast(module.def()).asCPtr());
327 if (_PyState_AddModule(module_or_def, def) < 0) {
328 return Error::exception();
329 }
330 return *module;
331}
332
333RawObject moduleLoadDynamicExtension(Thread* thread, const Str& name,
334 const Str& path) {
335 unique_c_ptr<char> path_cstr(path.toCStr());
336 const char* error_msg;
337 void* handle =
338 OS::openSharedObject(path_cstr.get(), OS::kRtldNow, &error_msg);
339 if (handle == nullptr) {
340 return thread->raiseWithFmt(
341 LayoutId::kImportError, "dlerror: '%s' importing: '%S' from '%S'",
342 error_msg != nullptr ? error_msg : "", &name, &path);
343 }
344
345 // Resolve PyInit_xxx symbol.
346 unique_c_ptr<char> name_cstr(name.toCStr());
347 char init_name[256];
348 std::snprintf(init_name, sizeof(init_name), "PyInit_%s", name_cstr.get());
349 ExtensionModuleInitFunc init = reinterpret_cast<ExtensionModuleInitFunc>(
350 OS::sharedObjectSymbolAddress(handle, init_name, /*error_msg=*/nullptr));
351 if (init == nullptr) {
352 return thread->raiseWithFmt(LayoutId::kImportError,
353 "dlsym error: dynamic module '%S' does not "
354 "define export function: '%s'",
355 &name, init_name);
356 }
357
358 return initializeModule(thread, init, name);
359}
360
361static word inittabIndex(const Str& name) {
362 for (word i = 0; PyImport_Inittab[i].name != nullptr; i++) {
363 if (name.equalsCStr(PyImport_Inittab[i].name)) {
364 return i;
365 }
366 }
367 return -1;
368}
369
370bool isBuiltinExtensionModule(const Str& name) {
371 return inittabIndex(name) >= 0;
372}
373
374RawObject moduleInitBuiltinExtension(Thread* thread, const Str& name) {
375 word index = inittabIndex(name);
376 if (index < 0) return Error::notFound();
377 return initializeModule(thread, PyImport_Inittab[index].initfunc, name);
378}
379
380PY_EXPORT int PyImport_AppendInittab(const char* name,
381 PyObject* (*initfunc)(void)) {
382 word old_inittab_length = 0;
383 while (PyImport_Inittab[old_inittab_length].name != nullptr) {
384 old_inittab_length++;
385 }
386 // The copy needs 2 more slots than the non-null entries:
387 // 1 for the sentinel at the end, and 1 for the inittab we're adding.
388 word new_inittab_length = old_inittab_length + 2;
389 struct _inittab* inittab_copy = static_cast<struct _inittab*>(
390 std::malloc(sizeof(struct _inittab) * (new_inittab_length)));
391 if (inittab_copy == nullptr) {
392 return -1;
393 }
394 int i = 0;
395 for (; i < old_inittab_length; i++) {
396 inittab_copy[i] = PyImport_Inittab[i];
397 }
398 inittab_copy[i++] = {name, initfunc};
399 inittab_copy[i++] = {nullptr, nullptr};
400 DCHECK(i == new_inittab_length,
401 "length mismatch when populating new inittab");
402
403 // Only attempt to deallocate PyImport_Inittab if it was heap-allocated.
404 struct _inittab* old_inittab_pointer = PyImport_Inittab;
405 PyImport_Inittab = inittab_copy;
406 if (old_inittab_pointer != _PyImport_Inittab) {
407 std::free(old_inittab_pointer);
408 }
409 return 0;
410}
411
412} // namespace py