this repo has no description
at trunk 412 lines 14 kB view raw
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