runtime/sys-module.cpp (321 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "sys-module.h"
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include "cpython-types.h"
#include "builtins-module.h"
#include "builtins.h"
#include "bytes-builtins.h"
#include "capi.h"
#include "dict-builtins.h"
#include "exception-builtins.h"
#include "file.h"
#include "frame.h"
#include "frozen-modules.h"
#include "globals.h"
#include "handles.h"
#include "int-builtins.h"
#include "module-builtins.h"
#include "modules.h"
#include "objects.h"
#include "os.h"
#include "runtime.h"
#include "str-builtins.h"
#include "thread.h"
#include "version.h"
namespace py {
void FUNC(sys, __init_module__)(Thread* thread, const Module& module,
View<byte> bytecode) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object modules(&scope, runtime->modules());
moduleAtPutById(thread, module, ID(modules), modules);
// Fill in sys...
Object platform(&scope, runtime->newStrFromCStr(OS::name()));
moduleAtPutById(thread, module, ID(platform), platform);
Object stderr_fd_val(&scope, SmallInt::fromWord(File::kStderr));
moduleAtPutById(thread, module, ID(_stderr_fd), stderr_fd_val);
Object stdin_fd_val(&scope, SmallInt::fromWord(File::kStdin));
moduleAtPutById(thread, module, ID(_stdin_fd), stdin_fd_val);
Object stdout_fd_val(&scope, SmallInt::fromWord(File::kStdout));
moduleAtPutById(thread, module, ID(_stdout_fd), stdout_fd_val);
Object byteorder(
&scope,
SmallStr::fromCStr(endian::native == endian::little ? "little" : "big"));
moduleAtPutById(thread, module, ID(byteorder), byteorder);
// maxsize is defined as the largest supported length of containers which
// would be `SmallInt::kMaxValue`. However in practice it is used to
// determine the size of a machine word which is kMaxWord.
Object maxsize(&scope, runtime->newInt(kMaxWord));
moduleAtPutById(thread, module, ID(maxsize), maxsize);
// Count the number of builtin modules and create a tuple
uword num_extension_modules = 0;
while (PyImport_Inittab[num_extension_modules].name != nullptr) {
num_extension_modules++;
}
word num_builtin_modules = kNumFrozenModules + num_extension_modules;
MutableTuple builtin_module_names(
&scope, runtime->newMutableTuple(num_builtin_modules));
for (word i = 0; i < kNumFrozenModules; i++) {
builtin_module_names.atPut(
i, Runtime::internStrFromCStr(thread, kFrozenModules[i].name));
}
for (int i = 0; PyImport_Inittab[i].name != nullptr; i++) {
builtin_module_names.atPut(
kNumFrozenModules + i,
Runtime::internStrFromCStr(thread, PyImport_Inittab[i].name));
}
Tuple builtin_module_names_tuple(&scope,
builtin_module_names.becomeImmutable());
moduleAtPutById(thread, module, ID(builtin_module_names),
builtin_module_names_tuple);
// Fill in version-related fields.
Int hexversion(&scope, SmallInt::fromWord(kVersionHex));
moduleAtPutById(thread, module, ID(hexversion), hexversion);
Str version(&scope, runtime->newStrFromCStr(kVersionInfo));
moduleAtPutById(thread, module, ID(version), version);
Object release_level(&scope, runtime->newStrFromCStr(kReleaseLevel));
moduleAtPutById(thread, module, ID(_version_releaselevel), release_level);
executeFrozenModule(thread, module, bytecode);
// Fill in hash_info.
Object hash_width(&scope, SmallInt::fromWord(SmallInt::kBits));
Object hash_modulus(&scope, SmallInt::fromWord(kArithmeticHashModulus));
Object hash_inf(&scope, SmallInt::fromWord(kHashInf));
Object hash_nan(&scope, SmallInt::fromWord(kHashNan));
Object hash_imag(&scope, SmallInt::fromWord(kHashImag));
Object hash_algorithm(&scope, runtime->symbols()->at(ID(siphash24)));
Object hash_bits(&scope, SmallInt::fromWord(64));
Object hash_seed_bits(&scope, SmallInt::fromWord(128));
Object hash_cutoff(&scope, SmallInt::fromWord(SmallStr::kMaxLength));
Tuple hash_info_data(&scope, runtime->newTupleWithN(
9, &hash_width, &hash_modulus, &hash_inf,
&hash_nan, &hash_imag, &hash_algorithm,
&hash_bits, &hash_seed_bits, &hash_cutoff));
Object hash_info(
&scope, thread->invokeFunction1(ID(sys), ID(_HashInfo), hash_info_data));
moduleAtPutById(thread, module, ID(hash_info), hash_info);
runtime->cacheSysInstances(thread, module);
}
int flushStdFiles() {
Thread* thread = Thread::current();
HandleScope scope(thread);
int return_value = 0;
// PyErr_Fetch
Object exc(&scope, thread->pendingExceptionType());
Object val(&scope, thread->pendingExceptionValue());
Object tb(&scope, thread->pendingExceptionTraceback());
thread->clearPendingException();
Runtime* runtime = thread->runtime();
Module sys(&scope, runtime->findModuleById(ID(sys)));
Object stdout_obj(&scope, moduleAtById(thread, sys, ID(stdout)));
if (!stdout_obj.isErrorNotFound()) {
if (thread->invokeMethod1(stdout_obj, ID(flush)).isErrorException()) {
thread->clearPendingException();
return_value = -1;
}
}
Object stderr_obj(&scope, moduleAtById(thread, sys, ID(stderr)));
if (!stderr_obj.isErrorNotFound()) {
if (thread->invokeMethod1(stderr_obj, ID(flush)).isErrorException()) {
thread->clearPendingException();
return_value = -1;
}
}
// PyErr_Restore
thread->setPendingExceptionType(*exc);
thread->setPendingExceptionValue(*val);
thread->setPendingExceptionTraceback(*tb);
return return_value;
}
void initializeRuntimePaths(Thread* thread) {
HandleScope scope(thread);
Object result(&scope, thread->invokeFunction0(ID(sys), ID(_calculate_path)));
if (result.isError()) {
thread->clearPendingException();
thread->raiseBadInternalCall();
return;
}
CHECK(result.isTuple(), "sys._calculate_path must return tuple");
Tuple paths(&scope, *result);
Str prefix(&scope, paths.at(0));
word prefix_codepoints = prefix.codePointLength();
std::unique_ptr<wchar_t[]> prefix_wstr(new wchar_t[prefix_codepoints + 1]);
strCopyToWCStr(prefix_wstr.get(), prefix_codepoints + 1, prefix);
Runtime::setPrefix(prefix_wstr.get());
Str exec_prefix(&scope, paths.at(1));
word exec_prefix_codepoints = exec_prefix.codePointLength();
std::unique_ptr<wchar_t[]> exec_prefix_wstr(
new wchar_t[exec_prefix_codepoints + 1]);
strCopyToWCStr(exec_prefix_wstr.get(), exec_prefix_codepoints + 1,
exec_prefix);
Runtime::setExecPrefix(exec_prefix_wstr.get());
Str module_search_path(&scope, paths.at(2));
word module_search_path_codepoints = module_search_path.codePointLength();
std::unique_ptr<wchar_t[]> module_search_path_wstr(
new wchar_t[module_search_path_codepoints + 1]);
strCopyToWCStr(module_search_path_wstr.get(),
module_search_path_codepoints + 1, module_search_path);
Runtime::setModuleSearchPath(module_search_path_wstr.get());
}
RawObject initializeSys(Thread* thread, const Str& executable,
const List& python_path, const Tuple& flags_data,
const List& warnoptions,
bool extend_python_path_with_stdlib) {
HandleScope scope(thread);
Object extend_python_path_with_stdlib_obj(
&scope, Bool::fromBool(extend_python_path_with_stdlib));
return thread->invokeFunction5(ID(sys), ID(_init), executable, python_path,
flags_data, warnoptions,
extend_python_path_with_stdlib_obj);
}
void setPycachePrefix(Thread* thread, const Object& pycache_prefix) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Module module(&scope, runtime->findModuleById(ID(sys)));
moduleAtPutById(thread, module, ID(pycache_prefix), pycache_prefix);
}
static void writeImpl(Thread* thread, const Object& file, FILE* fallback_fp,
const char* format, va_list va) {
HandleScope scope(thread);
Object type(&scope, thread->pendingExceptionType());
Object value(&scope, thread->pendingExceptionValue());
Object tb(&scope, thread->pendingExceptionTraceback());
thread->clearPendingException();
static const char truncated[] = "... truncated";
static constexpr int message_size = 1001;
char buffer[message_size + sizeof(truncated) - 1];
int written = std::vsnprintf(buffer, message_size, format, va);
CHECK(written >= 0, "vsnprintf failed");
if (written >= message_size) {
std::memcpy(&buffer[message_size - 1], truncated, sizeof(truncated));
written = message_size + sizeof(truncated) - 2;
}
Str str(&scope, thread->runtime()->newStrWithAll(
View<byte>(reinterpret_cast<byte*>(buffer), written)));
if (file.isNoneType() ||
thread->invokeMethod2(file, ID(write), str).isError()) {
fwrite(buffer, 1, written, fallback_fp);
}
thread->clearPendingException();
thread->setPendingExceptionType(*type);
thread->setPendingExceptionValue(*value);
thread->setPendingExceptionTraceback(*tb);
}
void writeStdout(Thread* thread, const char* format, ...) {
va_list va;
va_start(va, format);
writeStdoutV(thread, format, va);
va_end(va);
}
void writeStdoutV(Thread* thread, const char* format, va_list va) {
HandleScope scope(thread);
ValueCell sys_stdout_cell(&scope, thread->runtime()->sysStdout());
Object sys_stdout(&scope, NoneType::object());
if (!sys_stdout_cell.isUnbound()) {
sys_stdout = sys_stdout_cell.value();
}
writeImpl(thread, sys_stdout, stdout, format, va);
}
void writeStderr(Thread* thread, const char* format, ...) {
va_list va;
va_start(va, format);
writeStderrV(thread, format, va);
va_end(va);
}
void writeStderrV(Thread* thread, const char* format, va_list va) {
HandleScope scope(thread);
ValueCell sys_stderr_cell(&scope, thread->runtime()->sysStderr());
Object sys_stderr(&scope, NoneType::object());
if (!sys_stderr_cell.isUnbound()) {
sys_stderr = sys_stderr_cell.value();
}
writeImpl(thread, sys_stderr, stderr, format, va);
}
RawObject FUNC(sys, _getframe)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object depth_obj(&scope, args.get(0));
if (!runtime->isInstanceOfInt(*depth_obj)) {
return thread->raiseRequiresType(depth_obj, ID(int));
}
word depth = intUnderlying(*depth_obj).asWordSaturated();
if (depth < 0) {
depth = 0;
}
// Increment the requested depth to skip the frame for sys._getframe itself.
// TODO(T64005113): This should be deleted.
depth++;
Object result(&scope, thread->heapFrameAtDepth(depth));
if (result.isNoneType()) {
return thread->raiseWithFmt(LayoutId::kValueError,
"call stack is not deep enough");
}
return *result;
}
RawObject FUNC(sys, _program_name)(Thread* thread, Arguments) {
return newStrFromWideChar(thread, Runtime::programName());
}
RawObject FUNC(sys, excepthook)(Thread* thread, Arguments args) {
HandleScope scope(thread);
// type argument is ignored
Object value(&scope, args.get(1));
Object tb(&scope, args.get(2));
return displayException(thread, value, tb);
}
RawObject FUNC(sys, exc_info)(Thread* thread, Arguments) {
HandleScope scope(thread);
Object caught_exc_state_obj(&scope, thread->topmostCaughtExceptionState());
if (caught_exc_state_obj.isNoneType()) {
Object type(&scope, NoneType::object());
Object value(&scope, NoneType::object());
Object traceback(&scope, NoneType::object());
return thread->runtime()->newTupleWith3(type, value, traceback);
}
ExceptionState caught_exc_state(&scope, *caught_exc_state_obj);
Object type(&scope, caught_exc_state.type());
Object value(&scope, caught_exc_state.value());
Object traceback(&scope, caught_exc_state.traceback());
return thread->runtime()->newTupleWith3(type, value, traceback);
}
RawObject FUNC(sys, intern)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object string(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfStr(*string)) {
return thread->raiseRequiresType(string, ID(str));
}
if (!string.isStr()) {
return thread->raiseWithFmt(LayoutId::kTypeError, "can't intern %T",
&string);
}
return Runtime::internStr(thread, string);
}
RawObject FUNC(sys, getrecursionlimit)(Thread* thread, Arguments) {
return thread->runtime()->newInt(thread->recursionLimit());
}
RawObject FUNC(sys, is_finalizing)(Thread* thread, Arguments) {
return Bool::fromBool(thread->runtime()->isFinalizing());
}
RawObject FUNC(sys, setrecursionlimit)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Int limit(&scope, args.get(0));
OptInt<int> opt_val = limit.asInt<int>();
if (opt_val.error != CastError::None) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C int");
}
// TODO(T62600497) Raise RecursionError if new limit is too low at current
// recursion depth
thread->setRecursionLimit(opt_val.value);
return NoneType::object();
}
RawObject FUNC(sys, set_asyncgen_hooks)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object finalizer(&scope, args.get(1));
Runtime* runtime = thread->runtime();
Object first_iter(&scope, args.get(0));
if (!first_iter.isUnbound()) {
if (!first_iter.isNoneType() && !runtime->isCallable(thread, first_iter)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"callable firstiter expected, got %T",
&first_iter);
}
thread->setAsyncgenHooksFirstIter(*first_iter);
}
if (!finalizer.isUnbound()) {
if (!finalizer.isNoneType() && !runtime->isCallable(thread, finalizer)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"callable finalizer expected, got %T",
&finalizer);
}
thread->setAsyncgenHooksFinalizer(*finalizer);
}
return NoneType::object();
}
} // namespace py