runtime/thread.cpp (743 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "thread.h"
#include <signal.h>
#include <cerrno>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include "builtins-module.h"
#include "exception-builtins.h"
#include "file.h"
#include "frame.h"
#include "globals.h"
#include "handles.h"
#include "interpreter.h"
#include "module-builtins.h"
#include "objects.h"
#include "profiling.h"
#include "runtime.h"
#include "tuple-builtins.h"
#include "type-builtins.h"
#include "visitor.h"
namespace py {
void Handles::visitPointers(PointerVisitor* visitor) {
for (Object* handle = head_; handle != nullptr;
handle = handle->nextHandle()) {
visitor->visitPointer(handle, PointerKind::kHandle);
}
}
RawObject uninitializedInterpreterFunc(Thread*) {
UNREACHABLE("interpreter main loop not initialized on this thread");
}
thread_local Thread* Thread::current_thread_ = nullptr;
Thread::Thread(Runtime* runtime, word size) : runtime_(runtime) {
CHECK(size % kPointerSize == 0, "size must be a multiple of kPointerSize");
start_ = new byte[size](); // Zero-initialize the stack
// Stack growns down in order to match machine convention
end_ = start_ + size;
limit_ = start_;
stack_pointer_ = reinterpret_cast<RawObject*>(end_);
current_frame_ = pushInitialFrame();
setCaughtExceptionState(runtime_->newExceptionState());
}
Thread::~Thread() { delete[] start_; }
void Thread::begin() {
Thread::setCurrentThread(this);
runtime_->interpreter()->setupThread(this);
}
void Thread::visitRoots(PointerVisitor* visitor) {
visitStackRoots(visitor);
handles()->visitPointers(visitor);
visitor->visitPointer(&api_repr_list_, PointerKind::kThread);
visitor->visitPointer(&asyncgen_hooks_finalizer_, PointerKind::kThread);
visitor->visitPointer(&asyncgen_hooks_first_iter_, PointerKind::kThread);
visitor->visitPointer(&caught_exc_stack_, PointerKind::kThread);
visitor->visitPointer(&contextvars_context_, PointerKind::kThread);
visitor->visitPointer(&pending_exc_traceback_, PointerKind::kThread);
visitor->visitPointer(&pending_exc_type_, PointerKind::kThread);
visitor->visitPointer(&pending_exc_value_, PointerKind::kThread);
visitor->visitPointer(&profiling_data_, PointerKind::kThread);
visitor->visitPointer(&str_offset_str_, PointerKind::kThread);
}
void Thread::visitStackRoots(PointerVisitor* visitor) {
auto address = reinterpret_cast<uword>(stackPointer());
auto end = reinterpret_cast<uword>(end_);
std::memset(start_, 0, reinterpret_cast<byte*>(address) - start_);
for (; address < end; address += kPointerSize) {
visitor->visitPointer(reinterpret_cast<RawObject*>(address),
PointerKind::kStack);
}
}
Thread* Thread::current() { return Thread::current_thread_; }
bool Thread::isMainThread() { return this == runtime_->mainThread(); }
namespace {
class UserVisibleFrameVisitor : public FrameVisitor {
public:
UserVisibleFrameVisitor(Thread* thread, word depth)
: thread_(thread),
scope_(thread),
result_(&scope_, NoneType::object()),
heap_frame_(&scope_, NoneType::object()),
next_heap_frame_(&scope_, NoneType::object()),
depth_(depth) {}
bool visit(Frame* frame) {
if (isHiddenFrame(frame)) {
return true;
}
if (call_ < depth_) {
call_++;
return true;
}
// Once visitor reaches the target depth, start creating a linked list of
// FrameProxys objects.
// TOOD(T63960421): Cache an already created object in the stack frame.
Function function(&scope_, frame->function());
Object lasti(&scope_, NoneType::object());
if (!frame->isNative()) {
lasti = SmallInt::fromWord(frame->virtualPC());
}
heap_frame_ = thread_->runtime()->newFrameProxy(thread_, function, lasti);
// TODO(T89882231) unconditionally add frameLocals. We cannot currently do
// this because uninitialized variables are not cleared and will read
// arbitrary values.
if (function.hasOptimizedOrNewlocals()) {
FrameProxy::cast(*heap_frame_).setLocals(thread_->runtime()->newDict());
} else {
FrameProxy::cast(*heap_frame_).setLocals(frameLocals(thread_, frame));
}
if (result_.isNoneType()) {
// The head of the linked list is returned as the result.
result_ = *heap_frame_;
} else {
FrameProxy::cast(*next_heap_frame_).setBack(*heap_frame_);
}
next_heap_frame_ = *heap_frame_;
return true;
}
RawObject result() { return *result_; }
private:
bool isHiddenFrame(Frame* frame) {
if (frame == nullptr || frame->isSentinel()) {
return true;
}
RawFunction function = Function::cast(frame->function());
word builtins_module_id = thread_->runtime()->builtinsModuleId();
// PyCFunction do not generate a frame in cpython and therefore do
// not show up in sys._getframe(). Our builtin functions do create a
// frame so we hide frames of functions in builtins from the user.
// TODO(T64005113): This logic should be applied to each function.
if (function.moduleObject().isModule() &&
Module::cast(function.moduleObject()).id() == builtins_module_id) {
return true;
}
return false;
}
Thread* thread_;
HandleScope scope_;
Object result_;
Object heap_frame_;
Object next_heap_frame_;
word call_ = 0;
word depth_;
DISALLOW_HEAP_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(UserVisibleFrameVisitor);
};
} // namespace
RawObject Thread::heapFrameAtDepth(word depth) {
UserVisibleFrameVisitor visitor(this, depth);
visitFrames(&visitor);
return visitor.result();
}
void Thread::setCurrentThread(Thread* thread) {
Thread::current_thread_ = thread;
}
void Thread::clearInterrupt(InterruptKind kind) {
interrupt_flags_ &= ~kind;
if (interrupt_flags_ == 0) {
limit_ = start_;
}
}
void Thread::interrupt(InterruptKind kind) {
interrupt_flags_ |= kind;
limit_ = end_;
}
bool Thread::handleInterrupt(word size) {
// Is it a real stack overflow?
if (reinterpret_cast<byte*>(stackPointer()) - size < start_) {
raiseWithFmt(LayoutId::kRecursionError, "maximum recursion depth exceeded");
return true;
}
uint8_t interrupt_flags = interrupt_flags_;
if ((interrupt_flags & kSignal) != 0 &&
!runtime_->handlePendingSignals(this).isNoneType()) {
return true;
}
if (interrupt_flags & kReinitInterpreter) {
clearInterrupt(kReinitInterpreter);
runtime_->interpreter()->setupThread(this);
}
return false;
}
void Thread::handleInterruptWithFrame() {
if ((interrupt_flags_ & kProfile) != 0) {
profiling_call(this);
}
}
void Thread::linkFrame(Frame* frame) {
frame->setPreviousFrame(current_frame_);
current_frame_ = frame;
}
inline Frame* Thread::openAndLinkFrame(word size, word locals_offset) {
// Initialize the frame.
byte* new_sp = reinterpret_cast<byte*>(stack_pointer_) - size;
stack_pointer_ = reinterpret_cast<RawObject*>(new_sp);
Frame* frame = reinterpret_cast<Frame*>(new_sp);
frame->setLocalsOffset(locals_offset);
// return a pointer to the base of the frame
linkFrame(frame);
DCHECK(frame->isInvalid() == nullptr, "invalid frame");
return frame;
}
ALWAYS_INLINE Frame* Thread::pushNativeFrameImpl(word locals_offset) {
return openAndLinkFrame(Frame::kSize, locals_offset);
}
NEVER_INLINE Frame* Thread::handleInterruptPushNativeFrame(word locals_offset) {
if (handleInterrupt(Frame::kSize)) {
return nullptr;
}
Frame* result = pushNativeFrameImpl(locals_offset);
handleInterruptWithFrame();
return result;
}
Frame* Thread::pushNativeFrame(word nargs) {
word locals_offset = Frame::kSize + nargs * kPointerSize;
if (UNLIKELY(wouldStackOverflow(Frame::kSize))) {
return handleInterruptPushNativeFrame(locals_offset);
}
return pushNativeFrameImpl(locals_offset);
}
ALWAYS_INLINE Frame* Thread::pushCallFrameImpl(RawFunction function,
word stack_size,
word locals_offset) {
Frame* result = openAndLinkFrame(stack_size, locals_offset);
result->setBytecode(MutableBytes::cast(function.rewrittenBytecode()));
result->setCaches(function.caches());
result->setVirtualPC(0);
result->setBlockStackDepthReturnMode(0);
return result;
}
NEVER_INLINE Frame* Thread::handleInterruptPushCallFrame(
RawFunction function, word max_stack_size, word initial_stack_size,
word locals_offset) {
if (handleInterrupt(max_stack_size)) {
return nullptr;
}
Frame* result =
pushCallFrameImpl(function, initial_stack_size, locals_offset);
handleInterruptWithFrame();
return result;
}
Frame* Thread::pushCallFrame(RawFunction function) {
word initial_stack_size = Frame::kSize + function.totalVars() * kPointerSize;
word stack_size = SmallInt::cast(function.stacksizeOrBuiltin()).value();
word max_stack_size = initial_stack_size + stack_size * kPointerSize;
word locals_offset = initial_stack_size + function.totalArgs() * kPointerSize;
if (UNLIKELY(wouldStackOverflow(max_stack_size))) {
return handleInterruptPushCallFrame(function, max_stack_size,
initial_stack_size, locals_offset);
}
return pushCallFrameImpl(function, initial_stack_size, locals_offset);
}
ALWAYS_INLINE Frame* Thread::pushGeneratorFrameImpl(
const GeneratorFrame& generator_frame, word size) {
word max_stack_size = generator_frame.maxStackSize();
word stack_size = generator_frame.stackSize();
word unused_stack = max_stack_size - stack_size;
size -= unused_stack * kPointerSize;
byte* src = reinterpret_cast<byte*>(generator_frame.address() +
RawGeneratorFrame::kFrameOffset +
unused_stack * kPointerSize);
byte* dest = reinterpret_cast<byte*>(stack_pointer_) - size;
std::memcpy(dest, src, size);
Frame* result = reinterpret_cast<Frame*>(dest + stack_size * kPointerSize);
setStackPointer(reinterpret_cast<RawObject*>(dest));
linkFrame(result);
DCHECK(result->isInvalid() == nullptr, "invalid frame");
return result;
}
NEVER_INLINE Frame* Thread::handleInterruptPushGeneratorFrame(
const GeneratorFrame& generator_frame, word size) {
if (handleInterrupt(size)) {
return nullptr;
}
Frame* result = pushGeneratorFrameImpl(generator_frame, size);
handleInterruptWithFrame();
return result;
}
Frame* Thread::pushGeneratorFrame(const GeneratorFrame& generator_frame) {
word num_frame_words = generator_frame.numFrameWords();
word size = num_frame_words * kPointerSize;
if (UNLIKELY(wouldStackOverflow(size))) {
return handleInterruptPushGeneratorFrame(generator_frame, size);
}
return pushGeneratorFrameImpl(generator_frame, size);
}
Frame* Thread::pushInitialFrame() {
byte* sp = end_ - Frame::kSize;
CHECK(sp > start_, "no space for initial frame");
Frame* frame = reinterpret_cast<Frame*>(sp);
frame->setLocalsOffset(Frame::kSize);
stack_pointer_ = reinterpret_cast<RawObject*>(sp);
frame->setPreviousFrame(nullptr);
return frame;
}
Frame* Thread::popFrameToGeneratorFrame(const GeneratorFrame& generator_frame) {
word max_stack_size = generator_frame.maxStackSize();
word stack_size = valueStackSize();
word unused_stack = max_stack_size - stack_size;
DCHECK(stack_size <= max_stack_size,
"not enough space in RawGeneratorBase to save live stack");
byte* dest = reinterpret_cast<byte*>(generator_frame.address() +
RawGeneratorFrame::kFrameOffset +
unused_stack * kPointerSize);
byte* src = reinterpret_cast<byte*>(valueStackBase() - stack_size);
word copy_size =
(generator_frame.numFrameWords() - unused_stack) * kPointerSize;
std::memcpy(dest, src, copy_size);
generator_frame.setStackSize(stack_size);
return popFrame();
}
RawObject Thread::exec(const Code& code, const Module& module,
const Object& implicit_globals) {
HandleScope scope(this);
Object qualname(&scope, code.name());
if (code.hasOptimizedOrNewlocals()) {
UNIMPLEMENTED("exec() on code with CO_OPTIMIZED / CO_NEWLOCALS");
}
Runtime* runtime = this->runtime();
Object builtins_module_obj(&scope,
moduleAtById(this, module, ID(__builtins__)));
if (builtins_module_obj.isErrorNotFound()) {
Module builtins(&scope, runtime->findModuleById(ID(builtins)));
DCHECK(!builtins.isErrorNotFound(), "invalid builtins module");
Object proxy(&scope, builtins.moduleProxy());
moduleAtPutById(this, module, ID(__builtins__), proxy);
}
Function function(&scope,
runtime->newFunctionWithCode(this, qualname, code, module));
return callFunctionWithImplicitGlobals(function, implicit_globals);
}
RawObject Thread::callFunctionWithImplicitGlobals(
const Function& function, const Object& implicit_globals) {
CHECK(!function.hasOptimizedOrNewlocals(),
"function must not have CO_OPTIMIZED or CO_NEWLOCALS");
// Push implicit globals and function.
stackPush(*implicit_globals);
stackPush(*function);
Frame* frame = pushCallFrame(*function);
if (frame == nullptr) {
return Error::exception();
}
if (function.hasFreevarsOrCellvars()) {
processFreevarsAndCellvars(this, frame);
}
RawObject result = Interpreter::execute(this);
DCHECK(stackTop() == *implicit_globals, "stack mismatch");
stackDrop(1);
return result;
}
RawObject Thread::invokeMethod1(const Object& receiver, SymbolId selector) {
HandleScope scope(this);
Object method(&scope, Interpreter::lookupMethod(this, receiver, selector));
if (method.isError()) return *method;
return Interpreter::callMethod1(this, method, receiver);
}
RawObject Thread::invokeMethod2(const Object& receiver, SymbolId selector,
const Object& arg1) {
HandleScope scope(this);
Object method(&scope, Interpreter::lookupMethod(this, receiver, selector));
if (method.isError()) return *method;
return Interpreter::callMethod2(this, method, receiver, arg1);
}
RawObject Thread::invokeMethod3(const Object& receiver, SymbolId selector,
const Object& arg1, const Object& arg2) {
HandleScope scope(this);
Object method(&scope, Interpreter::lookupMethod(this, receiver, selector));
if (method.isError()) return *method;
return Interpreter::callMethod3(this, method, receiver, arg1, arg2);
}
RawObject Thread::invokeMethodStatic1(LayoutId type, SymbolId method_name,
const Object& receiver) {
HandleScope scope(this);
Object type_obj(&scope, runtime()->typeAt(type));
if (type_obj.isError()) return *type_obj;
Type type_handle(&scope, *type_obj);
Object method(&scope, typeLookupInMroById(this, *type_handle, method_name));
if (method.isError()) return *method;
return Interpreter::callMethod1(this, method, receiver);
}
RawObject Thread::invokeMethodStatic2(LayoutId type, SymbolId method_name,
const Object& receiver,
const Object& arg1) {
HandleScope scope(this);
Object type_obj(&scope, runtime()->typeAt(type));
if (type_obj.isError()) return *type_obj;
Type type_handle(&scope, *type_obj);
Object method(&scope, typeLookupInMroById(this, *type_handle, method_name));
if (method.isError()) return *method;
return Interpreter::callMethod2(this, method, receiver, arg1);
}
RawObject Thread::invokeMethodStatic3(LayoutId type, SymbolId method_name,
const Object& receiver,
const Object& arg1, const Object& arg2) {
HandleScope scope(this);
Object type_obj(&scope, runtime()->typeAt(type));
if (type_obj.isError()) return *type_obj;
Type type_handle(&scope, *type_obj);
Object method(&scope, typeLookupInMroById(this, *type_handle, method_name));
if (method.isError()) return *method;
return Interpreter::callMethod3(this, method, receiver, arg1, arg2);
}
RawObject Thread::invokeMethodStatic4(LayoutId type, SymbolId method_name,
const Object& receiver,
const Object& arg1, const Object& arg2,
const Object& arg3) {
HandleScope scope(this);
Object type_obj(&scope, runtime()->typeAt(type));
if (type_obj.isError()) return *type_obj;
Type type_handle(&scope, *type_obj);
Object method(&scope, typeLookupInMroById(this, *type_handle, method_name));
if (method.isError()) return *method;
return Interpreter::callMethod4(this, method, receiver, arg1, arg2, arg3);
}
RawObject Thread::invokeFunction0(SymbolId module, SymbolId name) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call0(this, func);
}
RawObject Thread::invokeFunction1(SymbolId module, SymbolId name,
const Object& arg1) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call1(this, func, arg1);
}
RawObject Thread::invokeFunction2(SymbolId module, SymbolId name,
const Object& arg1, const Object& arg2) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call2(this, func, arg1, arg2);
}
RawObject Thread::invokeFunction3(SymbolId module, SymbolId name,
const Object& arg1, const Object& arg2,
const Object& arg3) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call3(this, func, arg1, arg2, arg3);
}
RawObject Thread::invokeFunction4(SymbolId module, SymbolId name,
const Object& arg1, const Object& arg2,
const Object& arg3, const Object& arg4) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call4(this, func, arg1, arg2, arg3, arg4);
}
RawObject Thread::invokeFunction5(SymbolId module, SymbolId name,
const Object& arg1, const Object& arg2,
const Object& arg3, const Object& arg4,
const Object& arg5) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call5(this, func, arg1, arg2, arg3, arg4, arg5);
}
RawObject Thread::invokeFunction6(SymbolId module, SymbolId name,
const Object& arg1, const Object& arg2,
const Object& arg3, const Object& arg4,
const Object& arg5, const Object& arg6) {
HandleScope scope(this);
Object func(&scope, runtime()->lookupNameInModule(this, module, name));
if (func.isError()) return *func;
return Interpreter::call6(this, func, arg1, arg2, arg3, arg4, arg5, arg6);
}
RawObject Thread::raise(LayoutId type, RawObject value) {
return raiseWithType(runtime()->typeAt(type), value);
}
RawObject Thread::raiseWithType(RawObject type, RawObject value) {
DCHECK(!hasPendingException(), "unhandled exception lingering");
HandleScope scope(this);
Type type_obj(&scope, type);
Object value_obj(&scope, value);
Object traceback_obj(&scope, NoneType::object());
// If raise is called with an exception instance use the original traceback
if (runtime()->isInstanceOfBaseException(*value_obj)) {
traceback_obj = value_obj.rawCast<RawBaseException>().traceback();
}
value_obj = chainExceptionContext(type_obj, value_obj);
if (value_obj.isErrorException()) return Error::exception();
setPendingExceptionType(*type_obj);
setPendingExceptionValue(*value_obj);
setPendingExceptionTraceback(*traceback_obj);
return Error::exception();
}
RawObject Thread::chainExceptionContext(const Type& type, const Object& value) {
HandleScope scope(this);
Object caught_exc_state_obj(&scope, topmostCaughtExceptionState());
if (caught_exc_state_obj.isNoneType()) {
return *value;
}
ExceptionState caught_exc_state(&scope, *caught_exc_state_obj);
Object fixed_value(&scope, *value);
if (!runtime()->isInstanceOfBaseException(*value)) {
// Perform partial normalization before attempting to set __context__.
fixed_value = createException(this, type, value);
if (fixed_value.isError()) return *fixed_value;
}
// Avoid creating cycles by breaking any link from caught_value to value
// before setting value's __context__.
BaseException caught_value(&scope, caught_exc_state.value());
if (*fixed_value == *caught_value) return *fixed_value;
BaseException exc(&scope, *caught_value);
Object context(&scope, NoneType::object());
while (!(context = exc.context()).isNoneType()) {
if (*context == *fixed_value) {
exc.setContext(Unbound::object());
break;
}
exc = *context;
}
BaseException(&scope, *fixed_value).setContext(*caught_value);
return *fixed_value;
}
RawObject Thread::raiseWithFmt(LayoutId type, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
HandleScope scope(this);
Object message(&scope, runtime()->newStrFromFmtV(this, fmt, args));
va_end(args);
return raise(type, *message);
}
RawObject Thread::raiseWithFmtChainingPendingAsCause(LayoutId type,
const char* fmt, ...) {
HandleScope scope(this);
va_list args;
va_start(args, fmt);
Object message(&scope, runtime()->newStrFromFmtV(this, fmt, args));
va_end(args);
Object pending_type(&scope, pendingExceptionType());
Object pending_value(&scope, pendingExceptionValue());
Object pending_traceback(&scope, pendingExceptionTraceback());
clearPendingException();
normalizeException(this, &pending_type, &pending_value, &pending_traceback);
Type new_exc_type(&scope, runtime()->typeAt(type));
BaseException new_exc(&scope, createException(this, new_exc_type, message));
new_exc.setCause(*pending_value);
new_exc.setContext(*pending_value);
setPendingExceptionType(*new_exc_type);
setPendingExceptionValue(*new_exc);
setPendingExceptionTraceback(NoneType::object());
return Error::exception();
}
// Convenience method for throwing a binary-operation-specific TypeError
// exception with an error message.
RawObject Thread::raiseUnsupportedBinaryOperation(
const Handle<RawObject>& left, const Handle<RawObject>& right,
SymbolId op_name) {
return raiseWithFmt(LayoutId::kTypeError, "%T.%Y(%T) is not supported", &left,
op_name, &right);
}
void Thread::raiseBadArgument() {
raiseWithFmt(LayoutId::kTypeError,
"bad argument type for built-in operation");
}
RawObject Thread::raiseBadInternalCall() {
return raiseWithFmt(LayoutId::kSystemError,
"bad argument to internal function");
}
RawObject Thread::raiseMemoryError() {
return raise(LayoutId::kMemoryError, NoneType::object());
}
RawObject Thread::raiseOSErrorFromErrno(int errno_value) {
HandleScope scope(this);
Object type(&scope, runtime()->typeAt(LayoutId::kOSError));
NoneType none(&scope, NoneType::object());
return raiseFromErrnoWithFilenames(type, errno_value, none, none);
}
RawObject Thread::raiseFromErrnoWithFilenames(const Object& type,
int errno_value,
const Object& filename0,
const Object& filename1) {
HandleScope scope(this);
if (errno_value == EINTR) {
Object result(&scope, runtime_->handlePendingSignals(this));
if (result.isErrorException()) return *result;
}
stackPush(*type);
stackPush(SmallInt::fromWord(errno_value));
stackPush(errno_value == 0
? runtime_->symbols()->at(ID(Error))
: Runtime::internStrFromCStr(this, std::strerror(errno_value)));
word nargs = 2;
if (!filename0.isNoneType()) {
stackPush(*filename0);
++nargs;
if (!filename1.isNoneType()) {
stackPush(SmallInt::fromWord(0));
stackPush(*filename1);
nargs += 2;
}
} else {
DCHECK(filename1.isNoneType(), "expected filename1 to be None");
}
Object exception(&scope, Interpreter::call(this, nargs));
if (exception.isErrorException()) return *exception;
return raiseWithType(runtime_->typeOf(*exception), *exception);
}
RawObject Thread::raiseRequiresType(const Object& obj, SymbolId expected_type) {
HandleScope scope(this);
Function function(&scope, currentFrame()->function());
Str function_name(&scope, function.name());
return raiseWithFmt(LayoutId::kTypeError,
"'%S' for '%Y' objects doesn't apply to a '%T' object",
&function_name, expected_type, &obj);
}
RawObject Thread::raiseStopAsyncIteration() {
return raise(LayoutId::kStopAsyncIteration, NoneType::object());
}
RawObject Thread::raiseStopIteration() {
return raise(LayoutId::kStopIteration, NoneType::object());
}
RawObject Thread::raiseStopIterationWithValue(const Object& value) {
if (runtime()->isInstanceOfTuple(*value) ||
runtime()->isInstanceOfBaseException(*value)) {
// TODO(T67598788): Remove this special case. For now this works around
// the behavior of normalizeException() when it's called in
// Interpreter::unwind() as part of returning values from generators. Our
// desired end-state is StopIterations will be treated as a special
// optimized path which, among other properties, are not processed by
// normalization.
HandleScope scope(this);
Layout layout(&scope, runtime()->layoutAt(LayoutId::kStopIteration));
StopIteration stop_iteration(&scope, runtime()->newInstance(layout));
stop_iteration.setArgs(runtime()->newTupleWith1(value));
stop_iteration.setValue(*value);
stop_iteration.setCause(Unbound::object());
stop_iteration.setContext(Unbound::object());
stop_iteration.setTraceback(Unbound::object());
stop_iteration.setSuppressContext(RawBool::falseObj());
return raise(LayoutId::kStopIteration, *stop_iteration);
}
return raise(LayoutId::kStopIteration, *value);
}
bool Thread::hasPendingException() { return !pending_exc_type_.isNoneType(); }
bool Thread::hasPendingStopIteration() {
if (pending_exc_type_.isType()) {
return Type::cast(pending_exc_type_).builtinBase() ==
LayoutId::kStopIteration;
}
if (runtime()->isInstanceOfType(pending_exc_type_)) {
HandleScope scope(this);
Type type(&scope, pending_exc_type_);
return type.builtinBase() == LayoutId::kStopIteration;
}
return false;
}
bool Thread::clearPendingStopIteration() {
if (hasPendingStopIteration()) {
clearPendingException();
return true;
}
return false;
}
RawObject Thread::pendingStopIterationValue() {
DCHECK(hasPendingStopIteration(),
"Shouldn't be called without a pending StopIteration");
HandleScope scope(this);
Object exc_value(&scope, pendingExceptionValue());
if (runtime()->isInstanceOfStopIteration(*exc_value)) {
StopIteration si(&scope, *exc_value);
return si.value();
}
if (runtime()->isInstanceOfTuple(*exc_value)) {
return tupleUnderlying(*exc_value).at(0);
}
return *exc_value;
}
void Thread::ignorePendingException() {
if (!hasPendingException()) {
return;
}
fprintf(stderr, "ignore pending exception");
if (pendingExceptionValue().isStr()) {
RawStr message = Str::cast(pendingExceptionValue());
word len = message.length();
byte* buffer = new byte[len + 1];
message.copyTo(buffer, len);
buffer[len] = 0;
fprintf(stderr, ": %s", buffer);
delete[] buffer;
}
fprintf(stderr, "\n");
clearPendingException();
runtime_->printTraceback(this, File::kStderr);
}
void Thread::clearPendingException() {
setPendingExceptionType(NoneType::object());
setPendingExceptionValue(NoneType::object());
setPendingExceptionTraceback(NoneType::object());
}
bool Thread::pendingExceptionMatches(LayoutId type) {
return typeIsSubclass(pendingExceptionType(), runtime()->typeAt(type));
}
void Thread::setCaughtExceptionType(RawObject type) {
ExceptionState::cast(caught_exc_stack_).setType(type);
}
void Thread::setCaughtExceptionValue(RawObject value) {
ExceptionState::cast(caught_exc_stack_).setValue(value);
}
void Thread::setCaughtExceptionTraceback(RawObject traceback) {
ExceptionState::cast(caught_exc_stack_).setTraceback(traceback);
}
RawObject Thread::caughtExceptionState() { return caught_exc_stack_; }
void Thread::setCaughtExceptionState(RawObject state) {
caught_exc_stack_ = state;
}
RawObject Thread::topmostCaughtExceptionState() {
HandleScope scope(this);
Object exc_state(&scope, caught_exc_stack_);
while (!exc_state.isNoneType() &&
ExceptionState::cast(*exc_state).type().isNoneType()) {
exc_state = ExceptionState::cast(*exc_state).previous();
}
return *exc_state;
}
bool Thread::isErrorValueOk(RawObject obj) {
return (!obj.isError() && !hasPendingException()) ||
(obj.isErrorException() && hasPendingException());
}
void Thread::visitFrames(FrameVisitor* visitor) {
Frame* frame = currentFrame();
while (!frame->isSentinel()) {
if (!visitor->visit(frame)) {
break;
}
frame = frame->previousFrame();
}
}
void Thread::enableProfiling() { interrupt(kProfile); }
void Thread::disableProfiling() { clearInterrupt(kProfile); }
RawObject Thread::reprEnter(const Object& obj) {
HandleScope scope(this);
if (api_repr_list_.isNoneType()) {
api_repr_list_ = runtime_->newList();
}
List list(&scope, api_repr_list_);
for (word i = list.numItems() - 1; i >= 0; i--) {
if (list.at(i) == *obj) {
return RawBool::trueObj();
}
}
// TODO(emacs): When there is better error handling, raise an exception.
runtime_->listAdd(this, list, obj);
return RawBool::falseObj();
}
void Thread::reprLeave(const Object& obj) {
HandleScope scope(this);
List list(&scope, api_repr_list_);
for (word i = list.numItems() - 1; i >= 0; i--) {
if (list.at(i) == *obj) {
list.atPut(i, Unbound::object());
break;
}
}
}
word Thread::strOffset(const Str& str, word index) {
if (str != str_offset_str_) {
str_offset_str_ = *str;
str_offset_index_ = index;
str_offset_offset_ = str.offsetByCodePoints(0, index);
return str_offset_offset_;
}
word index_diff = index - str_offset_index_;
word offset = str.offsetByCodePoints(str_offset_offset_, index_diff);
if (0 <= offset && offset < str.length()) {
str_offset_index_ = index;
str_offset_offset_ = offset;
}
return offset;
}
} // namespace py