runtime/under-builtins-module.cpp (5,543 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include <unistd.h>
#include <cerrno>
#include <cmath>
#include "attributedict.h"
#include "builtins.h"
#include "bytearray-builtins.h"
#include "bytes-builtins.h"
#include "byteslike.h"
#include "capi.h"
#include "dict-builtins.h"
#include "exception-builtins.h"
#include "file.h"
#include "float-builtins.h"
#include "float-conversion.h"
#include "heap-profiler.h"
#include "int-builtins.h"
#include "list-builtins.h"
#include "memoryview-builtins.h"
#include "module-builtins.h"
#include "modules.h"
#include "mro.h"
#include "object-builtins.h"
#include "range-builtins.h"
#include "slice-builtins.h"
#include "str-builtins.h"
#include "strarray-builtins.h"
#include "structseq-builtins.h"
#include "super-builtins.h"
#include "traceback-builtins.h"
#include "tuple-builtins.h"
#include "type-builtins.h"
#include "unicode.h"
#include "vector.h"
namespace py {
static RawObject raiseRequiresFromCaller(Thread* thread, Arguments args,
SymbolId expected_type) {
HandleScope scope(thread);
Function function(&scope,
thread->currentFrame()->previousFrame()->function());
Str function_name(&scope, function.name());
Object obj(&scope, args.get(0));
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'%S' for '%Y' objects doesn't apply to a '%T' object", &function_name,
expected_type, &obj);
}
bool FUNC(_builtins, _bool_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isBool()));
return true;
}
bool FUNC(_builtins, _bool_guard_intrinsic)(Thread* thread) {
if (thread->stackTop().isBool()) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _bytearray_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(
thread->runtime()->isInstanceOfBytearray(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _bytearray_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfBytearray(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _bytearray_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPop();
if (arg.isBytearray()) {
thread->stackSetTop(SmallInt::fromWord(Bytearray::cast(arg).numItems()));
return true;
}
return false;
}
bool FUNC(_builtins, _bytes_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfBytes(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _bytes_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfBytes(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _bytes_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPeek(0);
if (arg.isBytes()) {
thread->stackPop();
thread->stackSetTop(SmallInt::fromWord(Bytes::cast(arg).length()));
return true;
}
return false;
}
bool FUNC(_builtins, _byteslike_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isByteslike(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _byteslike_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isByteslike(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _complex_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(
thread->runtime()->isInstanceOfComplex(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _deque_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfDeque(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _dict_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfDict(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _dict_check_exact_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isDict()));
return true;
}
bool FUNC(_builtins, _dict_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfDict(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _dict_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPeek(0);
if (arg.isDict()) {
thread->stackPop();
thread->stackSetTop(SmallInt::fromWord(Dict::cast(arg).numItems()));
return true;
}
return false;
}
bool FUNC(_builtins, _float_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfFloat(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _float_check_exact_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isFloat()));
return true;
}
bool FUNC(_builtins, _float_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfFloat(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _frozenset_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(
thread->runtime()->isInstanceOfFrozenSet(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _frozenset_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfFrozenSet(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _function_guard_intrinsic)(Thread* thread) {
if (thread->stackTop().isFunction()) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _int_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfInt(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _int_check_exact_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPop();
thread->stackSetTop(Bool::fromBool(arg.isSmallInt() || arg.isLargeInt()));
return true;
}
bool FUNC(_builtins, _int_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfInt(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _list_append_intrinsic)(Thread* thread) {
RawObject arg0 = thread->stackPeek(1);
if (!thread->runtime()->isInstanceOfList(arg0)) {
return false;
}
RawList self = arg0.rawCast<RawList>();
word num_items = self.numItems();
if (self.capacity() > num_items) {
self.setNumItems(num_items + 1);
self.atPut(num_items, thread->stackPeek(0));
thread->stackDrop(2);
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _list_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfList(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _list_check_exact_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isList()));
return true;
}
RawObject FUNC(_builtins, _list_ctor)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(args.get(0) == runtime->typeAt(LayoutId::kList), "unexpected cls");
RawObject iterable_raw = args.get(1);
if (iterable_raw == runtime->emptyTuple()) {
return runtime->newList();
}
HandleScope scope(thread);
Object iterable(&scope, iterable_raw);
List self(&scope, runtime->newList());
word src_length;
Tuple src(&scope, runtime->emptyTuple());
if (iterable.isList()) {
src = List::cast(*iterable).items();
src_length = List::cast(*iterable).numItems();
} else if (iterable.isTuple()) {
src = *iterable;
src_length = src.length();
} else {
Object result(&scope, thread->invokeMethod2(self, ID(extend), iterable));
if (result.isError()) return *result;
return *self;
}
listExtend(thread, self, src, src_length);
return *self;
}
bool FUNC(_builtins, _list_getitem_intrinsic)(Thread* thread) {
RawObject arg0 = thread->stackPeek(1);
if (!arg0.isList()) {
return false;
}
RawObject arg1 = thread->stackPeek(0);
word idx;
if (arg1.isSmallInt()) {
idx = SmallInt::cast(arg1).value();
} else if (arg1.isBool()) {
idx = Bool::cast(arg1).value();
} else {
return false;
}
RawList self = List::cast(arg0);
if (0 <= idx && idx < self.numItems()) {
thread->stackDrop(2);
thread->stackSetTop(self.at(idx));
return true;
}
return false;
}
bool FUNC(_builtins, _list_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfList(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _list_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPeek(0);
if (arg.isList()) {
thread->stackPop();
thread->stackSetTop(SmallInt::fromWord(List::cast(arg).numItems()));
return true;
}
return false;
}
bool FUNC(_builtins, _list_setitem_intrinsic)(Thread* thread) {
RawObject arg0 = thread->stackPeek(2);
if (!arg0.isList()) {
return false;
}
RawObject arg1 = thread->stackPeek(1);
word idx;
if (arg1.isSmallInt()) {
idx = SmallInt::cast(arg1).value();
} else if (arg1.isBool()) {
idx = Bool::cast(arg1).value();
} else {
return false;
}
RawList self = List::cast(arg0);
if (idx < 0 || idx >= self.numItems()) {
return false;
}
self.atPut(idx, thread->stackPeek(0));
thread->stackDrop(3);
thread->stackSetTop(NoneType::object());
return true;
}
bool FUNC(_builtins, _memoryview_guard_intrinsic)(Thread* thread) {
if (thread->stackTop().isMemoryView()) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _range_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isRange()));
return true;
}
bool FUNC(_builtins, _range_guard_intrinsic)(Thread* thread) {
if (thread->stackTop().isRange()) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _seq_index_intrinsic)(Thread* thread) {
thread->stackSetTop(
SmallInt::fromWord(SeqIterator::cast(thread->stackPop()).index()));
return true;
}
bool FUNC(_builtins, _seq_iterable_intrinsic)(Thread* thread) {
thread->stackSetTop(SeqIterator::cast(thread->stackPop()).iterable());
return true;
}
bool FUNC(_builtins, _seq_set_index_intrinsic)(Thread* thread) {
RawObject index = thread->stackPop();
RawObject seq_iter = thread->stackPop();
SeqIterator::cast(seq_iter).setIndex(Int::cast(index).asWord());
return true;
}
bool FUNC(_builtins, _seq_set_iterable_intrinsic)(Thread* thread) {
RawObject iterable = thread->stackPop();
RawObject seq_iter = thread->stackPop();
SeqIterator::cast(seq_iter).setIterable(iterable);
return true;
}
bool FUNC(_builtins, _set_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfSet(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _set_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfSet(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _set_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPeek(0);
if (arg.isSet()) {
thread->stackPop();
thread->stackSetTop(SmallInt::fromWord(Set::cast(arg).numItems()));
return true;
}
return false;
}
bool FUNC(_builtins, _slice_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isSlice()));
return true;
}
bool FUNC(_builtins, _slice_guard_intrinsic)(Thread* thread) {
if (thread->stackTop().isSlice()) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _str_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfStr(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _str_check_exact_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isStr()));
return true;
}
bool FUNC(_builtins, _str_ctor_intrinsic)(Thread* thread) {
// The type signature of _str_ctor is
// def _str_ctor(cls, obj=_Unbound, encoding=_Unbound, errors=_Unbound):
//
// `_str_ctor` is available internally so locating it in the stack tells us
// how many arguments are given in the presence of optional arguments.
RawObject callee =
Type::cast(thread->runtime()->typeAt(LayoutId::kStr)).ctor();
if (callee == thread->stackPeek(1)) {
// Only `cls` is given: `str()` is executed.
thread->stackDrop(1);
thread->stackSetTop(Str::empty());
return true;
}
if (callee == thread->stackPeek(2)) {
// `cls` and `obj` are given: `str(s)` is executed.
RawObject obj = thread->stackPeek(0);
if (obj.isStr()) {
thread->stackSetAt(2, obj);
thread->stackDrop(2);
return true;
}
}
return false;
}
bool FUNC(_builtins, _str_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfStr(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _str_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPeek(0);
if (arg.isStr()) {
thread->stackPop();
thread->stackSetTop(SmallInt::fromWord(Str::cast(arg).codePointLength()));
return true;
}
return false;
}
bool FUNC(_builtins, _tuple_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfTuple(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _tuple_check_exact_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isTuple()));
return true;
}
bool FUNC(_builtins, _tuple_getitem_intrinsic)(Thread* thread) {
RawObject arg0 = thread->stackPeek(1);
if (!arg0.isTuple()) {
return false;
}
RawObject arg1 = thread->stackPeek(0);
word idx;
if (arg1.isSmallInt()) {
idx = SmallInt::cast(arg1).value();
} else if (arg1.isBool()) {
idx = Bool::cast(arg1).value();
} else {
return false;
}
RawTuple self = Tuple::cast(arg0);
if (0 <= idx && idx < self.length()) {
thread->stackDrop(2);
thread->stackSetTop(self.at(idx));
return true;
}
return false;
}
bool FUNC(_builtins, _tuple_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfTuple(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _tuple_len_intrinsic)(Thread* thread) {
RawObject arg = thread->stackPeek(0);
if (arg.isTuple()) {
thread->stackPop();
thread->stackSetTop(SmallInt::fromWord(Tuple::cast(arg).length()));
return true;
}
return false;
}
bool FUNC(_builtins, _type_intrinsic)(Thread* thread) {
thread->stackSetTop(thread->runtime()->typeOf(thread->stackPop()));
return true;
}
bool FUNC(_builtins, _type_check_intrinsic)(Thread* thread) {
thread->stackSetTop(
Bool::fromBool(thread->runtime()->isInstanceOfType(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _type_check_exact_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(thread->stackPop().isType()));
return true;
}
bool FUNC(_builtins, _type_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfType(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _type_subclass_guard_intrinsic)(Thread* thread) {
RawObject subclass = thread->stackPeek(0);
RawObject superclass = thread->stackPeek(1);
if (subclass == superclass && subclass.isType()) {
thread->stackDrop(2);
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
bool FUNC(_builtins, _weakref_check_intrinsic)(Thread* thread) {
thread->stackSetTop(Bool::fromBool(
thread->runtime()->isInstanceOfWeakRef(thread->stackPop())));
return true;
}
bool FUNC(_builtins, _weakref_guard_intrinsic)(Thread* thread) {
if (thread->runtime()->isInstanceOfWeakRef(thread->stackTop())) {
thread->stackPop();
thread->stackSetTop(NoneType::object());
return true;
}
return false;
}
void FUNC(_builtins, __init_module__)(Thread* thread, const Module& module,
View<byte> bytecode) {
HandleScope scope(thread);
Object unbound_value(&scope, Unbound::object());
moduleAtPutById(thread, module, ID(_Unbound), unbound_value);
Object compile_flags_mask(&scope,
SmallInt::fromWord(Code::kCompileFlagsMask));
moduleAtPutById(thread, module, ID(_compile_flags_mask), compile_flags_mask);
Object maxunicode(&scope, SmallInt::fromWord(kMaxUnicode));
moduleAtPutById(thread, module, ID(maxunicode), maxunicode);
// We did not initialize the `builtins` module yet, so we point
// `__builtins__` to this module instead.
moduleAtPutById(thread, module, ID(__builtins__), module);
executeFrozenModule(thread, module, bytecode);
}
RawObject FUNC(_builtins, _ContextVar_guard)(Thread* thread, Arguments args) {
if (args.get(0).isContextVar()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(ContextVar));
}
RawObject FUNC(_builtins, _Token_guard)(Thread* thread, Arguments args) {
if (args.get(0).isToken()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(Token));
}
RawObject FUNC(_builtins, _address)(Thread* thread, Arguments args) {
return thread->runtime()->newInt(args.get(0).raw());
}
RawObject FUNC(_builtins, _anyset_check)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
RawObject arg = args.get(0);
return Bool::fromBool(runtime->isInstanceOfSet(arg) ||
runtime->isInstanceOfFrozenSet(arg));
}
RawObject FUNC(_builtins, _async_generator_guard)(Thread* thread,
Arguments args) {
if (args.get(0).isAsyncGenerator()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(async_generator));
}
RawObject FUNC(_builtins, _base_exception_cause)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
if (!runtime->isInstanceOfBaseException(*self)) {
return thread->raiseRequiresType(self, ID(BaseException));
}
BaseException exc(&scope, *self);
return exc.cause();
}
RawObject FUNC(_builtins, _base_exception_context)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
if (!runtime->isInstanceOfBaseException(*self)) {
return thread->raiseRequiresType(self, ID(BaseException));
}
BaseException exc(&scope, *self);
return exc.context();
}
RawObject FUNC(_builtins, _base_exception_set_cause)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
if (!runtime->isInstanceOfBaseException(*self)) {
return thread->raiseRequiresType(self, ID(BaseException));
}
BaseException exc(&scope, *self);
Object value(&scope, args.get(1));
if (!value.isNoneType() && !runtime->isInstanceOfBaseException(*value)) {
return thread->raiseRequiresType(value, ID(BaseException));
}
exc.setCause(*value);
return NoneType::object();
}
RawObject FUNC(_builtins, _base_exception_set_context)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
if (!runtime->isInstanceOfBaseException(*self)) {
return thread->raiseRequiresType(self, ID(BaseException));
}
BaseException exc(&scope, *self);
Object value(&scope, args.get(1));
if (!value.isNoneType() && !runtime->isInstanceOfBaseException(*value)) {
return thread->raiseRequiresType(value, ID(BaseException));
}
exc.setContext(*value);
return NoneType::object();
}
RawObject FUNC(_builtins, _base_exception_set_traceback)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
if (!runtime->isInstanceOfBaseException(*self)) {
return thread->raiseRequiresType(self, ID(BaseException));
}
BaseException exc(&scope, *self);
Object value(&scope, args.get(1));
if (!value.isNoneType() && !value.isTraceback()) {
return thread->raiseRequiresType(value, ID(traceback));
}
exc.setTraceback(*value);
return NoneType::object();
}
RawObject FUNC(_builtins, _base_exception_traceback)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
if (!runtime->isInstanceOfBaseException(*self)) {
return thread->raiseRequiresType(self, ID(BaseException));
}
BaseException exc(&scope, *self);
return exc.traceback();
}
RawObject FUNC(_builtins, _bool_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isBool());
}
RawObject FUNC(_builtins, _bool_guard)(Thread* thread, Arguments args) {
if (args.get(0).isBool()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(bool));
}
RawObject FUNC(_builtins, _bound_method)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object function(&scope, args.get(0));
Object owner(&scope, args.get(1));
return thread->runtime()->newBoundMethod(function, owner);
}
RawObject FUNC(_builtins, _bound_method_guard)(Thread* thread, Arguments args) {
if (args.get(0).isBoundMethod()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(method));
}
RawObject FUNC(_builtins, _builtin_type)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object name(&scope, args.get(0));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
Object result(&scope, findBuiltinTypeWithName(thread, name));
CHECK(!result.isErrorNotFound(), "Built-in type not found");
return *result;
}
RawObject FUNC(_builtins, _byte_guard)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object obj(&scope, args.get(0));
if (runtime->isInstanceOfBytes(*obj)) {
Bytes bytes(&scope, bytesUnderlying(*obj));
if (bytes.length() == 1) {
return SmallInt::fromWord(bytes.byteAt(0));
}
} else if (runtime->isInstanceOfBytearray(*obj)) {
Bytearray array(&scope, *obj);
if (array.numItems() == 1) {
return SmallInt::fromWord(array.byteAt(0));
}
}
Function function(&scope,
thread->currentFrame()->previousFrame()->function());
Str function_name(&scope, function.name());
return thread->raiseWithFmt(
LayoutId::kTypeError,
"%S() argument 2 must be a byte string of length 1, not %T",
&function_name, &obj);
}
RawObject FUNC(_builtins, _bytearray_append)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
Bytearray self(&scope, *self_obj);
Object item_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*item_obj)) {
return Unbound::object();
}
OptInt<byte> item_opt = intUnderlying(*item_obj).asInt<byte>();
if (item_opt.error != CastError::None) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
bytearrayAdd(thread, runtime, self, item_opt.value);
return NoneType::object();
}
RawObject FUNC(_builtins, _bytearray_clear)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
self.downsize(0);
return NoneType::object();
}
RawObject FUNC(_builtins, _bytearray_contains)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
Object key_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*key_obj)) {
return Unbound::object();
}
OptInt<byte> key_opt = intUnderlying(*key_obj).asInt<byte>();
if (key_opt.error != CastError::None) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
Bytearray self(&scope, *self_obj);
MutableBytes bytes(&scope, self.items());
return Bool::fromBool(bytes.findByte(key_opt.value, 0, self.numItems()) >= 0);
}
RawObject FUNC(_builtins, _bytearray_contains_byteslike)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
Byteslike key(&scope, thread, args.get(1));
if (!key.isValid()) {
Object key_obj(&scope, args.get(1));
return thread->raiseWithFmt(LayoutId::kTypeError,
"a bytes-like object is required, not '%T'",
&key_obj);
}
Bytearray self(&scope, *self_obj);
MutableBytes bytes(&scope, self.items());
if (key.length() == 0) {
// CPython returns true for: b'' in b'abc'.
return Bool::fromBool(true);
}
return Bool::fromBool(
Utils::memoryFind(reinterpret_cast<byte*>(bytes.address()),
bytes.length(), reinterpret_cast<byte*>(key.address()),
key.length()) != -1);
}
RawObject FUNC(_builtins, _bytearray_copy)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
Bytearray self(&scope, *self_obj);
Bytes src(&scope, self.items());
MutableBytes dst(&scope, runtime->mutableBytesFromBytes(thread, src));
Bytearray result(&scope, runtime->newBytearray());
result.setItems(*dst);
result.setNumItems(self.numItems());
return *result;
}
RawObject FUNC(_builtins, _bytearray_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfBytearray(args.get(0)));
}
RawObject FUNC(_builtins, _bytearray_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfBytearray(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
RawObject FUNC(_builtins, _bytearray_delitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
word length = self.numItems();
word idx = intUnderlying(args.get(1)).asWordSaturated();
if (idx < 0) {
idx += length;
}
if (idx < 0 || idx >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"bytearray index out of range");
}
word last_idx = length - 1;
MutableBytes self_bytes(&scope, self.items());
self_bytes.replaceFromWithStartAt(idx, DataArray::cast(self.items()),
last_idx - idx, idx + 1);
self.setNumItems(last_idx);
return NoneType::object();
}
RawObject FUNC(_builtins, _bytearray_delslice)(Thread* thread, Arguments args) {
// This function deletes elements that are specified by a slice by copying.
// It compacts to the left elements in the slice range and then copies
// elements after the slice into the free area. The self element count is
// decremented and elements in the unused part of the self are overwritten
// with None.
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
word slice_length = Slice::length(start, stop, step);
DCHECK_BOUND(slice_length, self.numItems());
if (slice_length == 0) {
// Nothing to delete
return NoneType::object();
}
if (slice_length == self.numItems()) {
// Delete all the items
self.setNumItems(0);
return NoneType::object();
}
if (step < 0) {
// Adjust step to make iterating easier
start = start + step * (slice_length - 1);
step = -step;
}
DCHECK_INDEX(start, self.numItems());
DCHECK(step <= self.numItems() || slice_length == 1,
"Step should be in bounds or only one element should be sliced");
// Sliding compaction of elements out of the slice to the left
// Invariant: At each iteration of the loop, `fast` is the index of an
// element addressed by the slice.
// Invariant: At each iteration of the inner loop, `slow` is the index of a
// location to where we are relocating a slice addressed element. It is *not*
// addressed by the slice.
word fast = start;
MutableBytes self_bytes(&scope, self.items());
for (word i = 1; i < slice_length; i++) {
DCHECK_INDEX(fast, self.numItems());
word slow = fast + 1;
fast += step;
for (; slow < fast; slow++) {
self_bytes.byteAtPut(slow - i, self_bytes.byteAt(slow));
}
}
// Copy elements into the space where the deleted elements were
for (word i = fast + 1; i < self.numItems(); i++) {
self_bytes.byteAtPut(i - slice_length, self_bytes.byteAt(i));
}
self.setNumItems(self.numItems() - slice_length);
return NoneType::object();
}
RawObject FUNC(_builtins, _bytearray_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
Bytearray self(&scope, *self_obj);
Object key(&scope, args.get(1));
if (runtime->isInstanceOfInt(*key)) {
key = intUnderlying(*key);
if (key.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key);
}
word index = Int::cast(*key).asWord();
word length = self.numItems();
if (index < 0 || index >= length) {
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"bytearray index out of range");
}
}
return SmallInt::fromWord(self.byteAt(index));
}
word start, stop;
if (!tryUnpackSlice(key, &start, &stop)) {
return Unbound::object();
}
word result_len = Slice::adjustIndices(self.numItems(), &start, &stop, 1);
if (result_len == 0) {
return runtime->newBytearray();
}
Bytearray result(&scope, runtime->newBytearray());
MutableBytes result_bytes(&scope,
runtime->newMutableBytesUninitialized(result_len));
MutableBytes src_bytes(&scope, self.items());
result_bytes.replaceFromWithStartAt(0, *src_bytes, result_len, start);
result.setItems(*result_bytes);
result.setNumItems(result_len);
return *result;
}
RawObject FUNC(_builtins, _bytearray_getslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
word len = Slice::length(start, stop, step);
Runtime* runtime = thread->runtime();
Bytearray result(&scope, runtime->newBytearray());
runtime->bytearrayEnsureCapacity(thread, result, len);
result.setNumItems(len);
for (word i = 0, idx = start; i < len; i++, idx += step) {
result.byteAtPut(i, self.byteAt(idx));
}
return *result;
}
RawObject FUNC(_builtins, _bytearray_ljust)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
word width;
Object width_obj(&scope, args.get(1));
if (runtime->isInstanceOfInt(*width_obj)) {
width = intUnderlying(args.get(1)).asWordSaturated();
if (!SmallInt::isValid(width)) {
return thread->raiseWithFmt(
LayoutId::kOverflowError,
"Python int too large to convert to C ssize_t");
}
} else {
return Unbound::object();
}
byte fill;
Object fillbyte_obj(&scope, args.get(2));
if (runtime->isInstanceOfBytes(*fillbyte_obj)) {
Bytes fillbyte(&scope, bytesUnderlying(*fillbyte_obj));
if (fillbyte.length() != 1) {
return Unbound::object();
}
fill = fillbyte.byteAt(0);
} else if (runtime->isInstanceOfBytearray(*fillbyte_obj)) {
Bytearray fillbyte(&scope, *fillbyte_obj);
if (fillbyte.numItems() != 1) {
return Unbound::object();
}
fill = fillbyte.byteAt(0);
} else {
return Unbound::object();
}
Bytearray self(&scope, *self_obj);
word self_length = self.numItems();
word result_length = Utils::maximum(width, self_length);
MutableBytes buffer(&scope,
runtime->newMutableBytesUninitialized(result_length));
buffer.replaceFromWith(0, DataArray::cast(self.items()), self_length);
buffer.replaceFromWithByte(self_length, fill, result_length - self_length);
Bytearray result(&scope, runtime->newBytearray());
result.setItems(*buffer);
result.setNumItems(result_length);
return *result;
}
RawObject FUNC(_builtins, _bytearray_rjust)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytearray(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
word width;
Object width_obj(&scope, args.get(1));
if (runtime->isInstanceOfInt(*width_obj)) {
width = intUnderlying(args.get(1)).asWordSaturated();
if (!SmallInt::isValid(width)) {
return thread->raiseWithFmt(
LayoutId::kOverflowError,
"Python int too large to convert to C ssize_t");
}
} else {
return Unbound::object();
}
byte fill;
Object fillbyte_obj(&scope, args.get(2));
if (runtime->isInstanceOfBytes(*fillbyte_obj)) {
Bytes fillbyte(&scope, bytesUnderlying(*fillbyte_obj));
if (fillbyte.length() != 1) {
return Unbound::object();
}
fill = fillbyte.byteAt(0);
} else if (runtime->isInstanceOfBytearray(*fillbyte_obj)) {
Bytearray fillbyte(&scope, *fillbyte_obj);
if (fillbyte.numItems() != 1) {
return Unbound::object();
}
fill = fillbyte.byteAt(0);
} else {
return Unbound::object();
}
Bytearray self(&scope, *self_obj);
word self_length = self.numItems();
word result_length = Utils::maximum(width, self_length);
word pad_length = result_length - self_length;
MutableBytes buffer(&scope,
runtime->newMutableBytesUninitialized(result_length));
buffer.replaceFromWithByte(0, fill, pad_length);
buffer.replaceFromWith(pad_length, DataArray::cast(self.items()),
self_length);
Bytearray result(&scope, runtime->newBytearray());
result.setItems(*buffer);
result.setNumItems(result_length);
return *result;
}
RawObject FUNC(_builtins, _bytearray_setitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
word index = intUnderlying(args.get(1)).asWordSaturated();
if (!SmallInt::isValid(index)) {
Object key_obj(&scope, args.get(1));
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key_obj);
}
word length = self.numItems();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError, "index out of range");
}
word val = intUnderlying(args.get(2)).asWordSaturated();
if (val < 0 || val > kMaxByte) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
self.byteAtPut(index, val);
return NoneType::object();
}
RawObject FUNC(_builtins, _bytearray_setslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
Byteslike src(&scope, thread, args.get(4));
DCHECK(src.isValid(), "argument must be a byteslike");
// Make sure that the degenerate case of a slice assignment where start is
// greater than stop inserts before the start and not the stop. For example,
// b[5:2] = ... should inserts before 5, not before 2.
if ((step < 0 && start < stop) || (step > 0 && start > stop)) {
stop = start;
}
Runtime* runtime = thread->runtime();
word src_length = src.length();
if (step == 1) {
word growth = src_length - (stop - start);
word new_length = self.numItems() + growth;
if (self == args.get(4)) {
// Rare case when replacing lhs with elements of rhs when lhs == rhs.
// Will always have growth >= 0.
if (growth == 0) {
return NoneType::object();
}
runtime->bytearrayEnsureCapacity(thread, self, new_length);
self.setNumItems(new_length);
MutableBytes dst_bytes(&scope, self.items());
dst_bytes.replaceFromWith(start, *dst_bytes, src_length);
dst_bytes.replaceFromWithStartAt(start + src_length, *dst_bytes,
src_length - stop, start + stop);
return NoneType::object();
}
if (growth == 0) {
// Assignment does not change the length of the bytearray. Do nothing.
} else if (growth > 0) {
// Assignment grows the length of the bytearray. Ensure there is enough
// free space in the underlying tuple for the new bytes and move stuff
// out of the way.
runtime->bytearrayEnsureCapacity(thread, self, new_length);
// Make the free space part of the bytearray. Must happen before shifting
// so we can index into the free space.
self.setNumItems(new_length);
// Shift some bytes to the right.
self.replaceFromWithStartAt(start + growth, *self,
new_length - growth - start, start);
} else {
// Growth is negative so assignment shrinks the length of the bytearray.
// Shift some bytes to the left.
self.replaceFromWithStartAt(start, *self, new_length - start,
start - growth);
// Remove the free space from the length of the bytearray. Must happen
// after shifting and clearing so we can index into the free space.
self.setNumItems(new_length);
}
MutableBytes dst_bytes(&scope, self.items());
// Copy new elements into the middle
dst_bytes.replaceFromWithByteslike(start, src, src_length);
return NoneType::object();
}
word slice_length = Slice::length(start, stop, step);
if (slice_length != src_length) {
return thread->raiseWithFmt(
LayoutId::kValueError,
"attempt to assign bytes of size %w to extended slice of size %w",
src_length, slice_length);
}
MutableBytes dst_bytes(&scope, self.items());
for (word dst_idx = start, src_idx = 0; src_idx < src_length;
dst_idx += step, src_idx++) {
dst_bytes.byteAtPut(dst_idx, src.byteAt(src_idx));
}
return NoneType::object();
}
RawObject FUNC(_builtins, _bytes_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfBytes(args.get(0)));
}
RawObject FUNC(_builtins, _bytes_contains)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytes(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytes));
}
Object key_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*key_obj)) {
return Unbound::object();
}
OptInt<byte> key_opt = intUnderlying(*key_obj).asInt<byte>();
if (key_opt.error != CastError::None) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
Bytes self(&scope, *self_obj);
return Bool::fromBool(self.findByte(key_opt.value, 0, self.length()) >= 0);
}
RawObject FUNC(_builtins, _bytes_decode)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object bytes_obj(&scope, args.get(0));
if (!bytes_obj.isBytes()) {
return Unbound::object();
}
Bytes bytes(&scope, *bytes_obj);
static RawSmallStr ascii = SmallStr::fromCStr("ascii");
static RawSmallStr utf8 = SmallStr::fromCStr("utf-8");
static RawSmallStr latin1 = SmallStr::fromCStr("latin-1");
Str enc(&scope, args.get(1));
if (enc != ascii && enc != utf8 && enc != latin1 &&
enc.compareCStr("iso-8859-1") != 0) {
return Unbound::object();
}
return bytesDecodeASCII(thread, bytes);
}
RawObject FUNC(_builtins, _bytes_decode_ascii)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object bytes_obj(&scope, args.get(0));
if (!bytes_obj.isBytes()) {
return Unbound::object();
}
Bytes bytes(&scope, *bytes_obj);
return bytesDecodeASCII(thread, bytes);
}
RawObject FUNC(_builtins, _bytes_decode_utf_8)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object bytes_obj(&scope, args.get(0));
if (!bytes_obj.isBytes()) {
return Unbound::object();
}
Bytes bytes(&scope, *bytes_obj);
return bytesDecodeASCII(thread, bytes);
}
RawObject FUNC(_builtins, _bytes_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfBytes(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(bytes));
}
RawObject FUNC(_builtins, _bytearray_join)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object sep_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfBytearray(*sep_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytearray));
}
Bytearray sep(&scope, args.get(0));
Bytes sep_bytes(&scope, sep.items());
Object iterable(&scope, args.get(1));
Tuple tuple(&scope, runtime->emptyTuple());
word length;
if (iterable.isList()) {
tuple = List::cast(*iterable).items();
length = List::cast(*iterable).numItems();
} else if (iterable.isTuple()) {
tuple = *iterable;
length = tuple.length();
} else {
// Collect items into list in Python and call again
return Unbound::object();
}
Object joined(&scope,
bytesJoin(thread, sep_bytes, sep.numItems(), tuple, length));
if (joined.isErrorException()) {
return *joined;
}
Bytearray result(&scope, runtime->newBytearray());
result.setItems(*joined);
result.setNumItems(Bytes::cast(*joined).length());
return *result;
}
RawObject FUNC(_builtins, _bytearray_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytearray self(&scope, args.get(0));
return SmallInt::fromWord(self.numItems());
}
RawObject FUNC(_builtins, _bytes_from_bytes)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
DCHECK(type.builtinBase() == LayoutId::kBytes, "type must subclass bytes");
Object value(&scope, bytesUnderlying(args.get(1)));
if (type.isBuiltin()) return *value;
Layout type_layout(&scope, type.instanceLayout());
UserBytesBase instance(&scope, thread->runtime()->newInstance(type_layout));
instance.setValue(*value);
return *instance;
}
RawObject FUNC(_builtins, _bytes_from_ints)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object src(&scope, args.get(0));
Runtime* runtime = thread->runtime();
// TODO(T38246066): buffers other than bytes, bytearray
if (runtime->isInstanceOfBytes(*src)) {
return *src;
}
if (runtime->isInstanceOfBytearray(*src)) {
Bytearray source(&scope, *src);
return bytearrayAsBytes(thread, source);
}
if (src.isList()) {
List source(&scope, *src);
Tuple items(&scope, source.items());
return runtime->bytesFromTuple(thread, items, source.numItems());
}
if (src.isTuple()) {
Tuple source(&scope, *src);
return runtime->bytesFromTuple(thread, source, source.length());
}
if (runtime->isInstanceOfStr(*src)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"cannot convert '%T' object to bytes", &src);
}
// Slow path: iterate over source in Python, collect into list, and call again
return NoneType::object();
}
RawObject FUNC(_builtins, _bytes_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytes(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytes));
}
Bytes self(&scope, bytesUnderlying(*self_obj));
Object key(&scope, args.get(1));
if (runtime->isInstanceOfInt(*key)) {
key = intUnderlying(*key);
if (key.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key);
}
word index = Int::cast(*key).asWord();
word length = self.length();
if (index < 0 || index >= length) {
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"index out of range");
}
}
return SmallInt::fromWord(self.byteAt(index));
}
word start, stop;
if (!tryUnpackSlice(key, &start, &stop)) {
return Unbound::object();
}
word result_len = Slice::adjustIndices(self.length(), &start, &stop, 1);
return bytesSubseq(thread, self, start, result_len);
}
RawObject FUNC(_builtins, _bytes_getslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytes self(&scope, bytesUnderlying(args.get(0)));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
return thread->runtime()->bytesSlice(thread, self, start, stop, step);
}
RawObject FUNC(_builtins, _bytes_join)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfBytes(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytes));
}
Bytes self(&scope, bytesUnderlying(*self_obj));
Object iterable(&scope, args.get(1));
Tuple tuple(&scope, runtime->emptyTuple());
word length;
if (iterable.isList()) {
tuple = List::cast(*iterable).items();
length = List::cast(*iterable).numItems();
} else if (iterable.isTuple()) {
tuple = *iterable;
length = Tuple::cast(*iterable).length();
} else {
// Collect items into list in Python and call again
return Unbound::object();
}
return bytesJoin(thread, self, self.length(), tuple, length);
}
RawObject FUNC(_builtins, _bytes_len)(Thread*, Arguments args) {
return SmallInt::fromWord(bytesUnderlying(args.get(0)).length());
}
RawObject FUNC(_builtins, _bytes_ljust)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfBytes(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytes));
}
Object width_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*width_obj)) {
return Unbound::object();
}
Int width_int(&scope, intUnderlying(*width_obj));
if (width_int.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C ssize_t");
}
word width = width_int.asWord();
byte fill;
Object fillbyte_obj(&scope, args.get(2));
if (runtime->isInstanceOfBytes(*fillbyte_obj)) {
Bytes fillbyte(&scope, bytesUnderlying(*fillbyte_obj));
if (fillbyte.length() != 1) {
return Unbound::object();
}
fill = fillbyte.byteAt(0);
} else if (runtime->isInstanceOfBytearray(*fillbyte_obj)) {
Bytearray fillbyte(&scope, *fillbyte_obj);
if (fillbyte.numItems() != 1) {
return Unbound::object();
}
fill = fillbyte.byteAt(0);
} else {
return Unbound::object();
}
Bytes self(&scope, *self_obj);
word self_length = self.length();
if (self_length >= width) {
return *self_obj;
}
if (width <= SmallBytes::kMaxLength) {
byte buffer[SmallBytes::kMaxLength];
std::memset(buffer, fill, SmallBytes::kMaxLength);
self.copyTo(buffer, self_length);
return SmallBytes::fromBytes({buffer, width});
}
MutableBytes buffer(&scope, runtime->newMutableBytesUninitialized(width));
buffer.replaceFromWithBytes(0, *self, self_length);
buffer.replaceFromWithByte(self_length, fill, width - self_length);
return buffer.becomeImmutable();
}
RawObject FUNC(_builtins, _bytes_maketrans)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object from_obj(&scope, args.get(0));
Object to_obj(&scope, args.get(1));
word length;
Runtime* runtime = thread->runtime();
if (runtime->isInstanceOfBytes(*from_obj)) {
Bytes bytes(&scope, bytesUnderlying(*from_obj));
length = bytes.length();
from_obj = *bytes;
} else if (runtime->isInstanceOfBytearray(*from_obj)) {
Bytearray array(&scope, *from_obj);
length = array.numItems();
from_obj = array.items();
} else {
UNIMPLEMENTED("bytes-like other than bytes or bytearray");
}
if (runtime->isInstanceOfBytes(*to_obj)) {
Bytes bytes(&scope, bytesUnderlying(*to_obj));
DCHECK(bytes.length() == length, "lengths should already be the same");
to_obj = *bytes;
} else if (runtime->isInstanceOfBytearray(*to_obj)) {
Bytearray array(&scope, *to_obj);
DCHECK(array.numItems() == length, "lengths should already be the same");
to_obj = array.items();
} else {
UNIMPLEMENTED("bytes-like other than bytes or bytearray");
}
Bytes from(&scope, *from_obj);
Bytes to(&scope, *to_obj);
byte table[kByteTranslationTableLength];
for (word i = 0; i < kByteTranslationTableLength; i++) {
table[i] = i;
}
for (word i = 0; i < length; i++) {
table[from.byteAt(i)] = to.byteAt(i);
}
return runtime->newBytesWithAll(table);
}
RawObject FUNC(_builtins, _bytes_repeat)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytes self(&scope, bytesUnderlying(args.get(0)));
// TODO(T55084422): unify bounds checking
word count = intUnderlying(args.get(1)).asWordSaturated();
if (!SmallInt::isValid(count)) {
Object count_obj(&scope, args.get(1));
return thread->raiseWithFmt(LayoutId::kOverflowError,
"cannot fit '%T' into an index-sized integer",
&count_obj);
}
// NOTE: unlike __mul__, we raise a value error for negative count
if (count < 0) {
return thread->raiseWithFmt(LayoutId::kValueError, "negative count");
}
return thread->runtime()->bytesRepeat(thread, self, self.length(), count);
}
RawObject FUNC(_builtins, _bytes_replace)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Object old_bytes_obj(&scope, args.get(1));
Object new_bytes_obj(&scope, args.get(2));
Object count_obj(&scope, args.get(3));
// Type Checks
if (!runtime->isInstanceOfBytes(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(bytes));
}
if (!runtime->isByteslike(*old_bytes_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"a bytes-like object is required, not '%T'",
&old_bytes_obj);
}
if (!runtime->isByteslike(*new_bytes_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"a bytes-like object is required, not '%T'",
&new_bytes_obj);
}
if (runtime->isInstanceOfFloat(*count_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"integer argument expected, got float",
&count_obj);
}
if (!runtime->isInstanceOfInt(*count_obj)) {
return Unbound::object();
}
if (!count_obj.isSmallInt()) {
UNIMPLEMENTED("handle if count is a LargeInt");
}
// Byteslike breakdown for oldbytes and newbytes
word old_bytes_len;
if (runtime->isInstanceOfBytes(*old_bytes_obj)) {
Bytes bytes(&scope, bytesUnderlying(*old_bytes_obj));
old_bytes_obj = *bytes;
old_bytes_len = bytes.length();
} else if (runtime->isInstanceOfBytearray(*old_bytes_obj)) {
Bytearray bytearray(&scope, *old_bytes_obj);
old_bytes_obj = bytearray.items();
old_bytes_len = bytearray.numItems();
} else {
// TODO(T38246066): support buffer protocol
UNIMPLEMENTED("bytes-like other than bytes or bytearray");
}
word new_bytes_len;
if (runtime->isInstanceOfBytes(*new_bytes_obj)) {
Bytes bytes(&scope, bytesUnderlying(*new_bytes_obj));
new_bytes_obj = *bytes;
new_bytes_len = bytes.length();
} else if (runtime->isInstanceOfBytearray(*new_bytes_obj)) {
Bytearray bytearray(&scope, *new_bytes_obj);
new_bytes_obj = bytearray.items();
new_bytes_len = bytearray.numItems();
} else {
// TODO(T38246066): support buffer protocol
UNIMPLEMENTED("bytes-like other than bytes or bytearray");
}
Bytes self(&scope, *self_obj);
Bytes old_bytes(&scope, *old_bytes_obj);
Bytes new_bytes(&scope, *new_bytes_obj);
word count = intUnderlying(*count_obj).asWordSaturated();
return runtime->bytesReplace(thread, self, old_bytes, old_bytes_len,
new_bytes, new_bytes_len, count);
}
RawObject FUNC(_builtins, _bytes_split)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Bytes self(&scope, bytesUnderlying(args.get(0)));
Object sep_obj(&scope, args.get(1));
Int maxsplit_int(&scope, intUnderlying(args.get(2)));
if (maxsplit_int.numDigits() > 1) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C ssize_t");
}
word maxsplit = maxsplit_int.asWord();
if (maxsplit < 0) {
maxsplit = kMaxWord;
}
word sep_len;
Runtime* runtime = thread->runtime();
if (runtime->isInstanceOfBytes(*sep_obj)) {
Bytes sep(&scope, bytesUnderlying(*sep_obj));
sep_obj = *sep;
sep_len = sep.length();
} else if (runtime->isInstanceOfBytearray(*sep_obj)) {
Bytearray sep(&scope, *sep_obj);
sep_obj = sep.items();
sep_len = sep.numItems();
} else {
// TODO(T38246066): support buffer protocol
UNIMPLEMENTED("bytes-like other than bytes or bytearray");
}
if (sep_len == 0) {
return thread->raiseWithFmt(LayoutId::kValueError, "empty separator");
}
Bytes sep(&scope, *sep_obj);
word self_len = self.length();
// First pass: calculate the length of the result list.
word splits = 0;
word start = 0;
while (splits < maxsplit) {
word end = bytesFind(self, self_len, sep, sep_len, start, self_len);
if (end < 0) {
break;
}
splits++;
start = end + sep_len;
}
word result_len = splits + 1;
// Second pass: write subsequences into result list.
List result(&scope, runtime->newList());
MutableTuple buffer(&scope, runtime->newMutableTuple(result_len));
start = 0;
for (word i = 0; i < splits; i++) {
word end = bytesFind(self, self_len, sep, sep_len, start, self_len);
DCHECK(end != -1, "already found in first pass");
buffer.atPut(i, bytesSubseq(thread, self, start, end - start));
start = end + sep_len;
}
buffer.atPut(splits, bytesSubseq(thread, self, start, self_len - start));
result.setItems(*buffer);
result.setNumItems(result_len);
return *result;
}
RawObject FUNC(_builtins, _bytes_split_whitespace)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Bytes self(&scope, bytesUnderlying(args.get(0)));
Int maxsplit_int(&scope, intUnderlying(args.get(1)));
if (maxsplit_int.numDigits() > 1) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C ssize_t");
}
word self_len = self.length();
word maxsplit = maxsplit_int.asWord();
if (maxsplit < 0) {
maxsplit = kMaxWord;
}
// First pass: calculate the length of the result list.
word splits = 0;
word index = 0;
while (splits < maxsplit) {
while (index < self_len && ASCII::isSpace(self.byteAt(index))) {
index++;
}
if (index == self_len) break;
index++;
while (index < self_len && !ASCII::isSpace(self.byteAt(index))) {
index++;
}
splits++;
}
while (index < self_len && ASCII::isSpace(self.byteAt(index))) {
index++;
}
bool has_remaining = index < self_len;
word result_len = has_remaining ? splits + 1 : splits;
// Second pass: write subsequences into result list.
Runtime* runtime = thread->runtime();
List result(&scope, runtime->newList());
if (result_len == 0) return *result;
MutableTuple buffer(&scope, runtime->newMutableTuple(result_len));
index = 0;
for (word i = 0; i < splits; i++) {
while (ASCII::isSpace(self.byteAt(index))) {
index++;
}
word start = index++;
while (index < self_len && !ASCII::isSpace(self.byteAt(index))) {
index++;
}
buffer.atPut(i, bytesSubseq(thread, self, start, index - start));
}
if (has_remaining) {
while (ASCII::isSpace(self.byteAt(index))) {
index++;
}
buffer.atPut(splits, bytesSubseq(thread, self, index, self_len - index));
}
result.setItems(*buffer);
result.setNumItems(result_len);
return *result;
}
RawObject FUNC(_builtins, _byteslike_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isByteslike(args.get(0)));
}
RawObject FUNC(_builtins, _byteslike_compare_digest)(Thread* thread,
Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object left_obj(&scope, args.get(0));
Object right_obj(&scope, args.get(1));
DCHECK(runtime->isInstanceOfBytes(*left_obj) ||
runtime->isInstanceOfBytearray(*left_obj),
"_byteslike_compare_digest requires 'bytes' or 'bytearray' instance");
DCHECK(runtime->isInstanceOfBytes(*right_obj) ||
runtime->isInstanceOfBytearray(*right_obj),
"_byteslike_compare_digest requires 'bytes' or 'bytearray' instance");
// TODO(T57794178): Use volatile
Bytes left(&scope, Bytes::empty());
Bytes right(&scope, Bytes::empty());
word left_len = 0;
word right_len = 0;
if (runtime->isInstanceOfBytes(*left_obj)) {
left = bytesUnderlying(*left_obj);
left_len = left.length();
} else {
Bytearray byte_array(&scope, *left_obj);
left = byte_array.items();
left_len = byte_array.numItems();
}
if (runtime->isInstanceOfBytes(*right_obj)) {
right = bytesUnderlying(*right_obj);
right_len = right.length();
} else {
Bytearray byte_array(&scope, *right_obj);
right = byte_array.items();
right_len = byte_array.numItems();
}
word length = Utils::minimum(left_len, right_len);
word result = (right_len == left_len) ? 0 : 1;
for (word i = 0; i < length; i++) {
result |= left.byteAt(i) ^ right.byteAt(i);
}
return Bool::fromBool(result == 0);
}
RawObject FUNC(_builtins, _byteslike_count)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
word haystack_len;
if (runtime->isInstanceOfBytes(*self_obj)) {
Bytes self(&scope, bytesUnderlying(*self_obj));
self_obj = *self;
haystack_len = self.length();
} else if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
self_obj = self.items();
haystack_len = self.numItems();
} else {
// TODO(T38246066): support buffer protocol
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Object sub_obj(&scope, args.get(1));
word needle_len;
if (runtime->isInstanceOfBytes(*sub_obj)) {
Bytes sub(&scope, bytesUnderlying(*sub_obj));
sub_obj = *sub;
needle_len = sub.length();
} else if (runtime->isInstanceOfBytearray(*sub_obj)) {
Bytearray sub(&scope, *sub_obj);
sub_obj = sub.items();
needle_len = sub.numItems();
} else if (runtime->isInstanceOfInt(*sub_obj)) {
word sub = intUnderlying(*sub_obj).asWordSaturated();
if (sub < 0 || sub > kMaxByte) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
sub_obj = runtime->newBytes(1, sub);
needle_len = 1;
} else {
// TODO(T38246066): support buffer protocol
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Bytes haystack(&scope, *self_obj);
Bytes needle(&scope, *sub_obj);
Object start_obj(&scope, args.get(2));
Object stop_obj(&scope, args.get(3));
word start = intUnderlying(*start_obj).asWordSaturated();
word end = intUnderlying(*stop_obj).asWordSaturated();
return SmallInt::fromWord(
bytesCount(haystack, haystack_len, needle, needle_len, start, end));
}
RawObject FUNC(_builtins, _byteslike_endswith)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
word self_len;
if (runtime->isInstanceOfBytes(*self_obj)) {
Bytes self(&scope, bytesUnderlying(*self_obj));
self_obj = *self;
self_len = self.length();
} else if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
self_obj = self.items();
self_len = self.numItems();
} else {
UNREACHABLE("self has an unexpected type");
}
DCHECK(self_obj.isBytes(),
"bytes-like object not resolved to underlying bytes");
Object suffix_obj(&scope, args.get(1));
word suffix_len;
if (runtime->isInstanceOfBytes(*suffix_obj)) {
Bytes suffix(&scope, bytesUnderlying(*suffix_obj));
suffix_obj = *suffix;
suffix_len = suffix.length();
} else if (runtime->isInstanceOfBytearray(*suffix_obj)) {
Bytearray suffix(&scope, *suffix_obj);
suffix_obj = suffix.items();
suffix_len = suffix.numItems();
} else {
// TODO(T38246066): support buffer protocol
return thread->raiseWithFmt(
LayoutId::kTypeError,
"endswith first arg must be bytes or a tuple of bytes, not %T",
&suffix_obj);
}
Bytes self(&scope, *self_obj);
Bytes suffix(&scope, *suffix_obj);
Object start_obj(&scope, args.get(2));
Object end_obj(&scope, args.get(3));
Int start(&scope, start_obj.isUnbound() ? Int::cast(SmallInt::fromWord(0))
: intUnderlying(*start_obj));
Int end(&scope, end_obj.isUnbound() ? Int::cast(SmallInt::fromWord(self_len))
: intUnderlying(*end_obj));
return runtime->bytesEndsWith(self, self_len, suffix, suffix_len,
start.asWordSaturated(), end.asWordSaturated());
}
RawObject FUNC(_builtins, _byteslike_find_byteslike)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
word haystack_len;
if (runtime->isInstanceOfBytes(*self_obj)) {
Bytes self(&scope, bytesUnderlying(*self_obj));
self_obj = *self;
haystack_len = self.length();
} else if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
self_obj = self.items();
haystack_len = self.numItems();
} else {
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Object sub_obj(&scope, args.get(1));
word needle_len;
if (runtime->isInstanceOfBytes(*sub_obj)) {
Bytes sub(&scope, bytesUnderlying(*sub_obj));
sub_obj = *sub;
needle_len = sub.length();
} else if (runtime->isInstanceOfBytearray(*sub_obj)) {
Bytearray sub(&scope, *sub_obj);
sub_obj = sub.items();
needle_len = sub.numItems();
} else {
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Bytes haystack(&scope, *self_obj);
Bytes needle(&scope, *sub_obj);
word start = intUnderlying(args.get(2)).asWordSaturated();
word end = intUnderlying(args.get(3)).asWordSaturated();
return SmallInt::fromWord(
bytesFind(haystack, haystack_len, needle, needle_len, start, end));
}
RawObject FUNC(_builtins, _byteslike_find_int)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
word needle = intUnderlying(args.get(1)).asWordSaturated();
if (needle < 0 || needle > kMaxByte) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
Object self_obj(&scope, args.get(0));
word start = intUnderlying(args.get(2)).asWordSaturated();
word end = intUnderlying(args.get(3)).asWordSaturated();
Bytes haystack(&scope, Bytes::empty());
word length;
if (runtime->isInstanceOfBytes(*self_obj)) {
haystack = bytesUnderlying(*self_obj);
length = haystack.length();
} else if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
haystack = self.items();
length = self.numItems();
} else {
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Slice::adjustSearchIndices(&start, &end, length);
return SmallInt::fromWord(haystack.findByte(needle, start, end - start));
}
RawObject FUNC(_builtins, _byteslike_guard)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
if (thread->runtime()->isByteslike(*obj)) {
return NoneType::object();
}
return thread->raiseWithFmt(
LayoutId::kTypeError, "a bytes-like object is required, not '%T'", &obj);
}
RawObject FUNC(_builtins, _byteslike_rfind_byteslike)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
word haystack_len;
if (runtime->isInstanceOfBytes(*self_obj)) {
Bytes self(&scope, bytesUnderlying(*self_obj));
self_obj = *self;
haystack_len = self.length();
} else if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
self_obj = self.items();
haystack_len = self.numItems();
} else {
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Object sub_obj(&scope, args.get(1));
word needle_len;
if (runtime->isInstanceOfBytes(*sub_obj)) {
Bytes sub(&scope, bytesUnderlying(*sub_obj));
sub_obj = *sub;
needle_len = sub.length();
} else if (runtime->isInstanceOfBytearray(*sub_obj)) {
Bytearray sub(&scope, *sub_obj);
sub_obj = sub.items();
needle_len = sub.numItems();
} else {
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
Bytes haystack(&scope, *self_obj);
Bytes needle(&scope, *sub_obj);
word start = intUnderlying(args.get(2)).asWordSaturated();
word end = intUnderlying(args.get(3)).asWordSaturated();
return SmallInt::fromWord(
bytesRFind(haystack, haystack_len, needle, needle_len, start, end));
}
RawObject FUNC(_builtins, _byteslike_rfind_int)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
word sub = intUnderlying(args.get(1)).asWordSaturated();
if (sub < 0 || sub > kMaxByte) {
return thread->raiseWithFmt(LayoutId::kValueError,
"byte must be in range(0, 256)");
}
Bytes needle(&scope, runtime->newBytes(1, sub));
Object self_obj(&scope, args.get(0));
word start = intUnderlying(args.get(2)).asWordSaturated();
word end = intUnderlying(args.get(3)).asWordSaturated();
if (runtime->isInstanceOfBytes(*self_obj)) {
Bytes haystack(&scope, bytesUnderlying(*self_obj));
return SmallInt::fromWord(bytesRFind(haystack, haystack.length(), needle,
needle.length(), start, end));
}
if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
Bytes haystack(&scope, self.items());
return SmallInt::fromWord(bytesRFind(haystack, self.numItems(), needle,
needle.length(), start, end));
}
UNIMPLEMENTED("bytes-like other than bytes, bytearray");
}
RawObject FUNC(_builtins, _byteslike_startswith)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
word self_len;
if (runtime->isInstanceOfBytes(*self_obj)) {
Bytes self(&scope, bytesUnderlying(*self_obj));
self_obj = *self;
self_len = self.length();
} else if (runtime->isInstanceOfBytearray(*self_obj)) {
Bytearray self(&scope, *self_obj);
self_obj = self.items();
self_len = self.numItems();
} else {
UNREACHABLE("self has an unexpected type");
}
DCHECK(self_obj.isBytes(),
"bytes-like object not resolved to underlying bytes");
Object prefix_obj(&scope, args.get(1));
word prefix_len;
if (runtime->isInstanceOfBytes(*prefix_obj)) {
Bytes prefix(&scope, bytesUnderlying(*prefix_obj));
prefix_obj = *prefix;
prefix_len = prefix.length();
} else if (runtime->isInstanceOfBytearray(*prefix_obj)) {
Bytearray prefix(&scope, *prefix_obj);
prefix_obj = prefix.items();
prefix_len = prefix.numItems();
} else {
// TODO(T38246066): support buffer protocol
return thread->raiseWithFmt(
LayoutId::kTypeError,
"startswith first arg must be bytes or a tuple of bytes, not %T",
&prefix_obj);
}
Bytes self(&scope, *self_obj);
Bytes prefix(&scope, *prefix_obj);
word start = intUnderlying(args.get(2)).asWordSaturated();
word end = intUnderlying(args.get(3)).asWordSaturated();
return runtime->bytesStartsWith(self, self_len, prefix, prefix_len, start,
end);
}
RawObject FUNC(_builtins, _caller_function)(Thread* thread, Arguments) {
return thread->currentFrame()->previousFrame()->previousFrame()->function();
}
RawObject FUNC(_builtins, _caller_locals)(Thread* thread, Arguments) {
return frameLocals(thread,
thread->currentFrame()->previousFrame()->previousFrame());
}
RawObject FUNC(_builtins, _classmethod)(Thread* thread, Arguments args) {
HandleScope scope(thread);
ClassMethod result(&scope, thread->runtime()->newClassMethod());
result.setFunction(args.get(0));
return *result;
}
static RawObject isAbstract(Thread* thread, const Object& obj) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
// TODO(T47800709): make this lookup more efficient
Object abstract(
&scope, runtime->attributeAtById(thread, obj, ID(__isabstractmethod__)));
if (abstract.isError()) {
Object given(&scope, thread->pendingExceptionType());
Object exc(&scope, runtime->typeAt(LayoutId::kAttributeError));
if (givenExceptionMatches(thread, given, exc)) {
thread->clearPendingException();
return Bool::falseObj();
}
return *abstract;
}
return Interpreter::isTrue(thread, *abstract);
}
RawObject FUNC(_builtins, _classmethod_isabstract)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfClassMethod(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(classmethod));
}
ClassMethod self(&scope, *self_obj);
Object func(&scope, self.function());
return isAbstract(thread, func);
}
RawObject FUNC(_builtins, _code_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isCode());
}
RawObject FUNC(_builtins, _code_guard)(Thread* thread, Arguments args) {
if (args.get(0).isCode()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(code));
}
RawObject FUNC(_builtins, _code_new)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object cls(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (cls != runtime->typeAt(LayoutId::kCode)) {
return thread->raiseWithFmt(LayoutId::kTypeError, "require code class");
}
word argcount = intUnderlying(args.get(1)).asWord();
if (argcount < 0) {
return thread->raiseWithFmt(LayoutId::kValueError,
"argcount must not be negative");
}
word posonlyargcount = intUnderlying(args.get(2)).asWord();
if (posonlyargcount < 0) {
return thread->raiseWithFmt(LayoutId::kValueError,
"posonlyargcount must not be negative");
}
word kwonlyargcount = intUnderlying(args.get(3)).asWord();
if (kwonlyargcount < 0) {
return thread->raiseWithFmt(LayoutId::kValueError,
"kwonlyargcount must not be negative");
}
word nlocals = intUnderlying(args.get(4)).asWord();
if (nlocals < 0) {
return thread->raiseWithFmt(LayoutId::kValueError,
"nlocals must not be negative");
}
word stacksize = intUnderlying(args.get(5)).asWord();
word flags = intUnderlying(args.get(6)).asWord();
if (argcount < posonlyargcount || stacksize < 0 || flags < 0) {
return thread->raiseBadInternalCall();
}
Object code(&scope, args.get(7));
Object consts(&scope, args.get(8));
Object names(&scope, args.get(9));
Object varnames(&scope, args.get(10));
Object filename(&scope, args.get(11));
Object name(&scope, args.get(12));
word firstlineno = intUnderlying(args.get(13)).asWord();
Object lnotab(&scope, args.get(14));
Object freevars(&scope, args.get(15));
Object cellvars(&scope, args.get(16));
return runtime->newCode(argcount, posonlyargcount, kwonlyargcount, nlocals,
stacksize, flags, code, consts, names, varnames,
freevars, cellvars, filename, name, firstlineno,
lnotab);
}
RawObject FUNC(_builtins, _code_set_filename)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object code_obj(&scope, args.get(0));
CHECK(code_obj.isCode(), "Expected code to be a code object");
Code code(&scope, *code_obj);
Object filename(&scope, args.get(1));
CHECK(thread->runtime()->isInstanceOfStr(*filename),
"Expected value to be a str");
code.setFilename(*filename);
return NoneType::object();
}
RawObject FUNC(_builtins, _complex_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfComplex(args.get(0)));
}
RawObject FUNC(_builtins, _complex_checkexact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isComplex());
}
RawObject FUNC(_builtins, _complex_imag)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfComplex(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(complex));
}
Complex self(&scope, complexUnderlying(*self_obj));
return runtime->newFloat(self.imag());
}
static bool unpackNumeric(const Object& val, double* real, double* imag) {
switch (val.layoutId()) {
case LayoutId::kBool:
*real = Bool::cast(*val).value();
*imag = 0.0;
return true;
case LayoutId::kComplex:
*real = Complex::cast(*val).real();
*imag = Complex::cast(*val).imag();
return true;
case LayoutId::kFloat:
*real = Float::cast(*val).value();
*imag = 0.0;
return true;
case LayoutId::kSmallInt:
*real = SmallInt::cast(*val).value();
*imag = 0.0;
return true;
case LayoutId::kUnbound:
*real = 0.0;
*imag = 0.0;
return true;
default:
return false;
}
}
RawObject FUNC(_builtins, _complex_new)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type cls(&scope, args.get(0));
DCHECK(cls.builtinBase() == LayoutId::kComplex, "cls must subclass complex");
Object real_obj(&scope, args.get(1));
Object imag_obj(&scope, args.get(2));
if (real_obj.isComplex() && imag_obj.isUnbound() && cls.isBuiltin()) {
return *real_obj;
}
double real1, imag1, real2, imag2;
if (!unpackNumeric(real_obj, &real1, &imag1) ||
!unpackNumeric(imag_obj, &real2, &imag2)) {
return Unbound::object();
}
double real = real1 - imag2;
double imag = imag1 + real2;
Runtime* runtime = thread->runtime();
if (cls.isBuiltin()) {
return runtime->newComplex(real, imag);
}
Layout layout(&scope, cls.instanceLayout());
UserComplexBase result(&scope, runtime->newInstance(layout));
result.setValue(runtime->newComplex(real, imag));
return *result;
}
RawObject FUNC(_builtins, _complex_real)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfComplex(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(complex));
}
Complex self(&scope, complexUnderlying(*self_obj));
return runtime->newFloat(self.real());
}
RawObject FUNC(_builtins, _compute_mro)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
return computeMro(thread, type);
}
RawObject FUNC(_builtins, _deque_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfDeque(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(deque));
}
RawObject FUNC(_builtins, _dict_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfDict(args.get(0)));
}
RawObject FUNC(_builtins, _dict_check_exact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isDict());
}
RawObject FUNC(_builtins, _dict_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
Object key(&scope, args.get(1));
Object default_obj(&scope, args.get(2));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfDict(*self)) {
return thread->raiseRequiresType(self, ID(dict));
}
Dict dict(&scope, *self);
// Check key hash
Object hash_obj(&scope, Interpreter::hash(thread, key));
if (hash_obj.isErrorException()) return *hash_obj;
word hash = SmallInt::cast(*hash_obj).value();
Object result(&scope, dictAt(thread, dict, key, hash));
if (result.isErrorNotFound()) return *default_obj;
return *result;
}
RawObject FUNC(_builtins, _dict_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfDict(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(dict));
}
RawObject FUNC(_builtins, _dict_items_guard)(Thread* thread, Arguments args) {
if (args.get(0).isDictItems()) return NoneType::object();
return raiseRequiresFromCaller(thread, args, ID(dict_items));
}
RawObject FUNC(_builtins, _dict_keys_guard)(Thread* thread, Arguments args) {
if (args.get(0).isDictKeys()) return NoneType::object();
return raiseRequiresFromCaller(thread, args, ID(dict_keys));
}
RawObject FUNC(_builtins, _dict_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Dict self(&scope, args.get(0));
return SmallInt::fromWord(self.numItems());
}
RawObject FUNC(_builtins, _dict_setitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
Object key(&scope, args.get(1));
Object value(&scope, args.get(2));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfDict(*self)) {
return thread->raiseRequiresType(self, ID(dict));
}
Dict dict(&scope, *self);
Object hash_obj(&scope, Interpreter::hash(thread, key));
if (hash_obj.isErrorException()) return *hash_obj;
word hash = SmallInt::cast(*hash_obj).value();
Object result(&scope, dictAtPut(thread, dict, key, hash, value));
if (result.isErrorException()) return *result;
return NoneType::object();
}
RawObject FUNC(_builtins, _dict_update)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfDict(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(dict));
}
Dict self(&scope, *self_obj);
Object other(&scope, args.get(1));
if (!other.isUnbound()) {
RawObject result = dictMergeOverride(thread, self, other);
if (result.isError()) {
if (thread->pendingExceptionMatches(LayoutId::kAttributeError)) {
// no `keys` attribute, bail out to managed code to try tuple unpacking
thread->clearPendingException();
return Unbound::object();
}
return result;
}
}
Object kwargs(&scope, args.get(2));
return dictMergeOverride(thread, self, kwargs);
}
RawObject FUNC(_builtins, _divmod)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object number(&scope, args.get(0));
Object divisor(&scope, args.get(1));
return Interpreter::binaryOperation(thread, Interpreter::BinaryOp::DIVMOD,
number, divisor);
}
RawObject FUNC(_builtins, _exec)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Code code(&scope, args.get(0));
Module module(&scope, args.get(1));
Object implicit_globals(&scope, args.get(2));
return thread->exec(code, module, implicit_globals);
}
RawObject FUNC(_builtins, _float_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfFloat(args.get(0)));
}
RawObject FUNC(_builtins, _float_check_exact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isFloat());
}
static double floatDivmod(double x, double y, double* remainder) {
double mod = std::fmod(x, y);
double div = (x - mod) / y;
if (mod != 0.0) {
if ((y < 0.0) != (mod < 0.0)) {
mod += y;
div -= 1.0;
}
} else {
mod = std::copysign(0.0, y);
}
double floordiv = 0;
if (div != 0.0) {
floordiv = std::floor(div);
if (div - floordiv > 0.5) {
floordiv += 1.0;
}
} else {
floordiv = std::copysign(0.0, x / y);
}
*remainder = mod;
return floordiv;
}
RawObject FUNC(_builtins, _float_divmod)(Thread* thread, Arguments args) {
HandleScope scope(thread);
double left = floatUnderlying(args.get(0)).value();
double divisor = floatUnderlying(args.get(1)).value();
if (divisor == 0.0) {
return thread->raiseWithFmt(LayoutId::kZeroDivisionError, "float divmod()");
}
double remainder;
double quotient = floatDivmod(left, divisor, &remainder);
Runtime* runtime = thread->runtime();
Object quotient_obj(&scope, runtime->newFloat(quotient));
Object remainder_obj(&scope, runtime->newFloat(remainder));
return runtime->newTupleWith2(quotient_obj, remainder_obj);
}
RawObject FUNC(_builtins, _float_format)(Thread* thread, Arguments args) {
HandleScope scope(thread);
double value = floatUnderlying(args.get(0)).value();
Str format_code(&scope, args.get(1));
DCHECK(format_code.length() == 1, "expected len(format_code) == 1");
char format_code_char = format_code.byteAt(0);
DCHECK(format_code_char == 'e' || format_code_char == 'E' ||
format_code_char == 'f' || format_code_char == 'F' ||
format_code_char == 'g' || format_code_char == 'G' ||
format_code_char == 'r',
"expected format_code in 'eEfFgGr'");
SmallInt precision(&scope, args.get(2));
Bool always_add_sign(&scope, args.get(3));
Bool add_dot_0(&scope, args.get(4));
Bool use_alt_formatting(&scope, args.get(5));
unique_c_ptr<char> c_str(doubleToString(
value, format_code_char, precision.value(), always_add_sign.value(),
add_dot_0.value(), use_alt_formatting.value(), nullptr));
return thread->runtime()->newStrFromCStr(c_str.get());
}
RawObject FUNC(_builtins, _float_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfFloat(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(float));
}
static RawObject floatNew(Thread* thread, const Type& type, RawObject flt) {
DCHECK(flt.isFloat(), "unexpected type when creating float");
if (type.isBuiltin()) return flt;
HandleScope scope(thread);
Layout type_layout(&scope, type.instanceLayout());
UserFloatBase instance(&scope, thread->runtime()->newInstance(type_layout));
instance.setValue(flt);
return *instance;
}
static RawObject floatNewFromDigits(Thread* thread, const Type& type,
const char* str, word length) {
const char* last = str + length - 1;
// strip spaces
while (str < last && ASCII::isSpace(*str)) {
str++;
}
while (last > str && ASCII::isSpace(*last)) {
last--;
}
RawObject float_obj = floatFromDigits(thread, str, last - str + 1);
if (float_obj.isErrorException()) {
return float_obj;
}
return floatNew(thread, type, float_obj);
}
RawObject FUNC(_builtins, _float_new_from_byteslike)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
Object arg(&scope, args.get(1));
Runtime* runtime = thread->runtime();
Bytes underlying(&scope, Bytes::empty());
word length;
if (runtime->isInstanceOfBytes(*arg)) {
underlying = bytesUnderlying(*arg);
length = underlying.length();
} else {
// TODO(T57022841): follow full CPython conversion for bytes-like objects
UNIMPLEMENTED("float.__new__ from byteslike");
}
unique_c_ptr<byte> c_str(reinterpret_cast<byte*>(std::malloc(length + 1)));
c_str.get()[length] = '\0';
underlying.copyTo(c_str.get(), length);
return floatNewFromDigits(thread, type, reinterpret_cast<char*>(c_str.get()),
length);
}
RawObject FUNC(_builtins, _float_new_from_float)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
return floatNew(thread, type, args.get(1));
}
RawObject FUNC(_builtins, _float_new_from_str)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
Object arg(&scope, args.get(1));
Str str(&scope, strUnderlying(*arg));
unique_c_ptr<char> c_str(str.toCStr());
word length = str.length();
return floatNewFromDigits(thread, type, c_str.get(), length);
}
RawObject FUNC(_builtins, _float_signbit)(Thread*, Arguments args) {
double value = floatUnderlying(args.get(0)).value();
return Bool::fromBool(std::signbit(value));
}
RawObject FUNC(_builtins, _frozenset_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfFrozenSet(args.get(0)));
}
RawObject FUNC(_builtins, _frozenset_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfFrozenSet(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(frozenset));
}
RawObject FUNC(_builtins, _function_annotations)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
Object annotations(&scope, function.annotations());
if (annotations.isNoneType()) {
annotations = thread->runtime()->newDict();
function.setAnnotations(*annotations);
}
return *annotations;
}
RawObject FUNC(_builtins, _function_closure)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
return function.closure();
}
RawObject FUNC(_builtins, _function_defaults)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
return function.defaults();
}
RawObject FUNC(_builtins, _function_globals)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
// extension functions created via C-API have no associated module.
if (function.moduleObject().isNoneType()) {
return thread->runtime()->newDict();
}
Module module(&scope, function.moduleObject());
return module.moduleProxy();
}
RawObject FUNC(_builtins, _function_guard)(Thread* thread, Arguments args) {
if (args.get(0).isFunction()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(function));
}
RawObject FUNC(_builtins, _function_kwdefaults)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
return function.kwDefaults();
}
RawObject FUNC(_builtins, _function_lineno)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Function function(&scope, args.get(0));
SmallInt pc(&scope, args.get(1));
Code code(&scope, function.code());
return SmallInt::fromWord(code.offsetToLineNum(pc.value()));
}
RawObject FUNC(_builtins, _function_new)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object cls_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*cls_obj)) {
return thread->raiseRequiresType(cls_obj, ID(function));
}
Type cls(&scope, *cls_obj);
if (cls.builtinBase() != LayoutId::kFunction) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"not a subtype of function");
}
Object code_obj(&scope, args.get(1));
if (!code_obj.isCode()) {
return thread->raiseRequiresType(code_obj, ID(code));
}
Object module(&scope, args.get(2));
if (!runtime->isInstanceOfModule(*module)) {
return thread->raiseRequiresType(module, ID(module));
}
Code code(&scope, *code_obj);
Object empty_qualname(&scope, NoneType::object());
Object result(&scope, runtime->newFunctionWithCode(thread, empty_qualname,
code, module));
if (result.isFunction()) {
Function new_function(&scope, *result);
Object name(&scope, args.get(3));
if (runtime->isInstanceOfStr(*name)) {
new_function.setName(*name);
} else if (!name.isNoneType()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"arg 3 (name) must be None or string", &name);
}
Object defaults(&scope, args.get(4));
if (runtime->isInstanceOfTuple(*defaults)) {
new_function.setDefaults(*defaults);
} else if (!defaults.isNoneType()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"arg 4 (defaults) must be None or tuple",
&defaults);
}
Object closure(&scope, args.get(5));
if (runtime->isInstanceOfTuple(*closure)) {
new_function.setClosure(*closure);
} else if (!closure.isNoneType()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"arg 5 (closure) must be None or tuple",
&closure);
}
return *new_function;
}
return *result;
}
RawObject FUNC(_builtins, _function_set_annotations)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
Object annotations(&scope, args.get(1));
if (thread->runtime()->isInstanceOfDict(*annotations) ||
annotations.isNoneType()) {
function.setAnnotations(*annotations);
return NoneType::object();
}
return thread->raiseRequiresType(annotations, ID(dict));
}
RawObject FUNC(_builtins, _function_set_defaults)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
Object defaults(&scope, args.get(1));
if (defaults.isNoneType()) {
function.setDefaults(*defaults);
return NoneType::object();
}
if (thread->runtime()->isInstanceOfTuple(*defaults)) {
function.setDefaults(tupleUnderlying(*defaults));
return NoneType::object();
}
return thread->raiseRequiresType(defaults, ID(tuple));
}
RawObject FUNC(_builtins, _function_set_kwdefaults)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isFunction()) {
return thread->raiseRequiresType(self, ID(function));
}
Function function(&scope, *self);
Object kwdefaults(&scope, args.get(1));
if (thread->runtime()->isInstanceOfDict(*kwdefaults) ||
kwdefaults.isNoneType()) {
function.setKwDefaults(*kwdefaults);
return NoneType::object();
}
return thread->raiseRequiresType(kwdefaults, ID(dict));
}
RawObject FUNC(_builtins, _gc)(Thread* thread, Arguments) {
thread->runtime()->collectGarbage();
return NoneType::object();
}
RawObject FUNC(_builtins, _get_asyncgen_hooks)(Thread* thread, Arguments) {
HandleScope scope(thread);
Object firstiter(&scope, thread->asyncgenHooksFirstIter());
Object finalizer(&scope, thread->asyncgenHooksFinalizer());
return thread->runtime()->newTupleWith2(firstiter, finalizer);
}
RawObject FUNC(_builtins, _get_member_byte)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
char value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), 1);
return thread->runtime()->newInt(value);
}
RawObject FUNC(_builtins, _get_member_char)(Thread*, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
return SmallStr::fromCodePoint(*reinterpret_cast<byte*>(addr));
}
RawObject FUNC(_builtins, _get_member_double)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
double value = 0.0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newFloat(value);
}
RawObject FUNC(_builtins, _get_member_float)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
float value = 0.0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newFloat(value);
}
RawObject FUNC(_builtins, _get_member_int)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
int value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newInt(value);
}
RawObject FUNC(_builtins, _get_member_long)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
long value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newInt(value);
}
RawObject FUNC(_builtins, _get_member_pyobject)(Thread* thread,
Arguments args) {
return objectGetMember(thread, args.get(0), args.get(1));
}
RawObject FUNC(_builtins, _get_member_short)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
short value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newInt(value);
}
RawObject FUNC(_builtins, _get_member_string)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
if (*reinterpret_cast<char**>(addr) == nullptr) return NoneType::object();
return thread->runtime()->newStrFromCStr(*reinterpret_cast<char**>(addr));
}
RawObject FUNC(_builtins, _get_member_ubyte)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
unsigned char value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newIntFromUnsigned(value);
}
RawObject FUNC(_builtins, _get_member_uint)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
unsigned int value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newIntFromUnsigned(value);
}
RawObject FUNC(_builtins, _get_member_ulong)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
unsigned long value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newIntFromUnsigned(value);
}
RawObject FUNC(_builtins, _get_member_ushort)(Thread* thread, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
unsigned short value = 0;
std::memcpy(&value, reinterpret_cast<void*>(addr), sizeof(value));
return thread->runtime()->newIntFromUnsigned(value);
}
RawObject FUNC(_builtins, _heap_dump)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Str filename(&scope, args.get(0));
unique_c_ptr<char> filename_str(filename.toCStr());
return heapDump(thread, filename_str.get());
}
RawObject FUNC(_builtins, _instance_dunder_dict_set)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Instance instance(&scope, args.get(0));
Object dict_obj(&scope, args.get(1));
Runtime* runtime = thread->runtime();
if (!dict_obj.isDict()) {
if (runtime->isInstanceOfDict(*dict_obj)) {
// TODO(T64971317): Support dict subclass.
UNIMPLEMENTED("dict subclass is not supported yet.");
}
return thread->raiseWithFmt(LayoutId::kTypeError,
"__dict__ must be set to a dictionary, "
"not a '%T'",
&dict_obj);
}
Layout layout(&scope, runtime->layoutAt(instance.layoutId()));
// Set in-object attribute values to None.
Tuple in_object(&scope, layout.inObjectAttributes());
word num_in_object_attr = in_object.length();
for (word i = 0; i < num_in_object_attr; i++) {
Tuple entry(&scope, in_object.at(i));
AttributeInfo info(entry.at(1));
instance.instanceVariableAtPut(info.offset(), NoneType::object());
}
Type type(&scope, layout.describedType());
if (type.isBuiltin()) {
// TODO(T65043421): Support builtin type.
UNIMPLEMENTED("_instance_dunder_dict_set(builtin_type_object)");
}
Layout new_layout(
&scope, runtime->typeDictOnlyLayout(thread, type, num_in_object_attr));
DCHECK(new_layout.hasDictOverflow(), "dict overflow is expected");
instance.setLayoutId(new_layout.id());
instance.instanceVariableAtPut(new_layout.dictOverflowOffset(), *dict_obj);
return NoneType::object();
}
RawObject FUNC(_builtins, _instance_delattr)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Instance instance(&scope, args.get(0));
Object name(&scope, args.get(1));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
return instanceDelAttr(thread, instance, name);
}
RawObject FUNC(_builtins, _instance_getattr)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Instance instance(&scope, args.get(0));
Object name(&scope, args.get(1));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
Object result(&scope, instanceGetAttribute(thread, instance, name));
return result.isErrorNotFound() ? Unbound::object() : *result;
}
RawObject FUNC(_builtins, _instance_guard)(Thread* thread, Arguments args) {
if (args.get(0).isInstance()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(instance));
}
RawObject FUNC(_builtins, _instance_overflow_dict)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object object(&scope, args.get(0));
Runtime* runtime = thread->runtime();
Layout layout(&scope, runtime->layoutOf(*object));
CHECK(layout.hasDictOverflow(), "expected dict overflow layout");
word offset = layout.dictOverflowOffset();
Instance instance(&scope, *object);
Object overflow_dict_obj(&scope, instance.instanceVariableAt(offset));
if (overflow_dict_obj.isNoneType()) {
overflow_dict_obj = runtime->newDict();
instance.instanceVariableAtPut(offset, *overflow_dict_obj);
}
return *overflow_dict_obj;
}
RawObject FUNC(_builtins, _instance_setattr)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Instance instance(&scope, args.get(0));
Object name(&scope, args.get(1));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
Object value(&scope, args.get(2));
return instanceSetAttr(thread, instance, name, value);
}
RawObject FUNC(_builtins, _int_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfInt(args.get(0)));
}
RawObject FUNC(_builtins, _int_check_exact)(Thread*, Arguments args) {
RawObject arg = args.get(0);
return Bool::fromBool(arg.isSmallInt() || arg.isLargeInt());
}
RawObject FUNC(_builtins, _instancemethod_func)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
if (!obj.isInstanceMethod()) {
return thread->raiseRequiresType(obj, ID(instancemethod));
}
return InstanceMethod::cast(*obj).function();
}
static RawObject positiveIntFromSmallStrWithBase10(RawSmallStr str) {
word length = str.length();
if (length == 0) {
return NoneType::object();
}
word result = 0;
for (word i = 0; i < length; i++) {
byte b = str.byteAt(i);
if ('0' <= b && b <= '9') {
result *= 10;
result += b - '0';
} else {
return NoneType::object();
}
}
return SmallInt::fromWord(result);
}
RawObject FUNC(_builtins, _int_ctor)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(args.get(0) == runtime->typeAt(LayoutId::kInt), "unexpected cls");
RawObject x_raw = args.get(1);
RawObject base_raw = args.get(2);
LayoutId x_layout_id = x_raw.layoutId();
if (base_raw.isUnbound()) {
switch (x_layout_id) {
case LayoutId::kSmallInt:
return x_raw;
case LayoutId::kBool:
return SmallInt::fromWord(Bool::cast(x_raw).value());
case LayoutId::kFloat:
return intFromDouble(thread, Float::cast(x_raw).value());
case LayoutId::kSmallStr: {
RawObject result =
positiveIntFromSmallStrWithBase10(SmallStr::cast(x_raw));
if (!result.isNoneType()) {
return result;
}
break;
}
case LayoutId::kUnbound:
return SmallInt::fromWord(0);
default:
break;
}
}
HandleScope scope(thread);
Type cls(&scope, args.get(0));
Object x(&scope, x_raw);
Object base(&scope, base_raw);
return thread->invokeFunction3(ID(_builtins), ID(_type_dunder_call), cls, x,
base);
}
RawObject FUNC(_builtins, _int_ctor_obj)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(args.get(0) == runtime->typeAt(LayoutId::kInt), "unexpected cls");
RawObject x_raw = args.get(1);
LayoutId x_layout_id = x_raw.layoutId();
switch (x_layout_id) {
case LayoutId::kSmallInt:
return x_raw;
case LayoutId::kBool:
return SmallInt::fromWord(Bool::cast(x_raw).value());
case LayoutId::kFloat:
return intFromDouble(thread, Float::cast(x_raw).value());
case LayoutId::kSmallStr: {
RawObject result =
positiveIntFromSmallStrWithBase10(SmallStr::cast(x_raw));
if (!result.isNoneType()) {
return result;
}
break;
}
default:
break;
}
HandleScope scope(thread);
Type cls(&scope, args.get(0));
Object x(&scope, x_raw);
Object base(&scope, Unbound::object());
return thread->invokeFunction3(ID(_builtins), ID(_type_dunder_call), cls, x,
base);
}
static RawObject intOrUserSubclass(Thread* thread, const Type& type,
const Object& value) {
DCHECK(value.isSmallInt() || value.isLargeInt(),
"builtin value should have type int");
DCHECK(type.builtinBase() == LayoutId::kInt, "type must subclass int");
if (type.isBuiltin()) return *value;
HandleScope scope(thread);
Layout layout(&scope, type.instanceLayout());
UserIntBase instance(&scope, thread->runtime()->newInstance(layout));
instance.setValue(*value);
return *instance;
}
RawObject FUNC(_builtins, _int_from_bytes)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Type type(&scope, args.get(0));
Bytes bytes(&scope, args.get(1));
Bool byteorder_big(&scope, args.get(2));
endian endianness = byteorder_big.value() ? endian::big : endian::little;
Bool signed_arg(&scope, args.get(3));
bool is_signed = signed_arg == Bool::trueObj();
Int value(&scope, runtime->bytesToInt(thread, bytes, endianness, is_signed));
return intOrUserSubclass(thread, type, value);
}
RawObject FUNC(_builtins, _int_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfInt(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(int));
}
static word digitValue(byte digit, word base) {
if ('0' <= digit && digit < '0' + base) return digit - '0';
// Bases 2-10 are limited to numerals, but all greater bases can use letters
// too.
if (base <= 10) return -1;
if ('a' <= digit && digit < 'a' + base - 10) return digit - 'a' + 10;
if ('A' <= digit && digit < 'A' + base - 10) return digit - 'A' + 10;
return -1;
}
static word inferBase(byte second_byte) {
switch (second_byte) {
case 'x':
case 'X':
return 16;
case 'o':
case 'O':
return 8;
case 'b':
case 'B':
return 2;
default:
return 10;
}
}
static RawObject intFromByteslike(Thread* thread, const Byteslike& byteslike,
word length, word base) {
DCHECK_BOUND(length, byteslike.length());
DCHECK(base == 0 || (base >= 2 && base <= 36), "invalid base");
if (length <= 0) return Error::error();
// Clamp the length at the last whitespace character.
word idx = length;
byte b = byteslike.byteAt(--idx);
while (ASCII::isSpace(b)) {
if (idx <= 0) return Error::error();
b = byteslike.byteAt(--idx);
}
length = idx + 1;
// Start the index from the first non-zero whitespace character.
idx = 0;
if (idx >= length) return Error::error();
b = byteslike.byteAt(idx++);
while (ASCII::isSpace(b)) {
if (idx >= length) return Error::error();
b = byteslike.byteAt(idx++);
}
word sign = 1;
switch (b) {
case '-':
sign = -1;
// fall through
case '+':
if (idx >= length) return Error::error();
b = byteslike.byteAt(idx++);
break;
}
word inferred_base = 10;
if (b == '0') {
if (idx >= length) return SmallInt::fromWord(0);
inferred_base = inferBase(byteslike.byteAt(idx));
if (base == 0) base = inferred_base;
if (inferred_base != 10 && base == inferred_base) {
if (++idx >= length) return Error::error();
b = byteslike.byteAt(idx++);
}
} else if (base == 0) {
base = 10;
}
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Int result(&scope, SmallInt::fromWord(0));
Int digit(&scope, SmallInt::fromWord(0));
Int base_obj(&scope, SmallInt::fromWord(base));
word num_start = idx;
for (;;) {
if (b == '_') {
// No leading underscores unless the number has a prefix
if (idx == num_start && inferred_base == 10) return Error::error();
// No trailing underscores
if (idx >= length) return Error::error();
b = byteslike.byteAt(idx++);
}
word digit_val = digitValue(b, base);
if (digit_val == -1) return Error::error();
digit = Int::cast(SmallInt::fromWord(digit_val));
result = runtime->intAdd(thread, result, digit);
if (idx >= length) break;
b = byteslike.byteAt(idx++);
result = runtime->intMultiply(thread, result, base_obj);
}
if (sign < 0) {
result = runtime->intNegate(thread, result);
}
return *result;
}
RawObject FUNC(_builtins, _int_new_from_byteslike)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
Byteslike byteslike(&scope, thread, args.get(1));
word base = intUnderlying(args.get(2)).asWord();
Object result(&scope,
intFromByteslike(thread, byteslike, byteslike.length(), base));
if (result.isError()) {
Str repr(&scope, byteslikeReprSmartQuotes(thread, byteslike));
return thread->raiseWithFmt(LayoutId::kValueError,
"invalid literal for int() with base %w: %S",
base, &repr);
}
return intOrUserSubclass(thread, type, result);
}
RawObject FUNC(_builtins, _int_new_from_int)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
Object value(&scope, args.get(1));
if (value.isBool()) {
value = convertBoolToInt(*value);
} else if (!value.isSmallInt() && !value.isLargeInt()) {
value = intUnderlying(*value);
}
return intOrUserSubclass(thread, type, value);
}
static RawObject intFromStr(Thread* thread, const Str& str, word base) {
DCHECK(base == 0 || (base >= 2 && base <= 36), "invalid base");
// CPython allows leading whitespace in the integer literal
word start = strFindFirstNonWhitespace(str);
if (str.length() - start == 0) {
return Error::error();
}
word sign = 1;
if (str.byteAt(start) == '-') {
sign = -1;
start += 1;
} else if (str.byteAt(start) == '+') {
start += 1;
}
if (str.length() - start == 0) {
// Just the sign
return Error::error();
}
if (str.length() - start == 1) {
// Single digit, potentially with +/-
word result = digitValue(str.byteAt(start), base == 0 ? 10 : base);
if (result == -1) return Error::error();
return SmallInt::fromWord(sign * result);
}
// Decimal literals start at the index 0 (no prefix).
// Octal literals (0oFOO), hex literals (0xFOO), and binary literals (0bFOO)
// start at index 2.
word inferred_base = 10;
if (str.byteAt(start) == '0' && start + 1 < str.length()) {
inferred_base = inferBase(str.byteAt(start + 1));
}
if (base == 0) {
base = inferred_base;
}
if (base == 2 || base == 8 || base == 16) {
if (base == inferred_base) {
// This handles integer literals with a base prefix, e.g.
// * int("0b1", 0) => 1, where the base is inferred from the prefix
// * int("0b1", 2) => 1, where the prefix matches the provided base
//
// If the prefix does not match the provided base, then we treat it as
// part as part of the number, e.g.
// * int("0b1", 10) => ValueError
// * int("0b1", 16) => 177
start += 2;
}
if (str.length() - start == 0) {
// Just the prefix: 0x, 0b, 0o, etc
return Error::error();
}
}
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Int result(&scope, SmallInt::fromWord(0));
Int digit(&scope, SmallInt::fromWord(0));
Int base_obj(&scope, SmallInt::fromWord(base));
for (word i = start; i < str.length(); i++) {
byte digit_char = str.byteAt(i);
if (digit_char == '_') {
// No leading underscores unless the number has a prefix
if (i == start && inferred_base == 10) return Error::error();
// No trailing underscores
if (i + 1 == str.length()) return Error::error();
digit_char = str.byteAt(++i);
}
word digit_val = digitValue(digit_char, base);
if (digit_val == -1) return Error::error();
digit = Int::cast(SmallInt::fromWord(digit_val));
result = runtime->intMultiply(thread, result, base_obj);
result = runtime->intAdd(thread, result, digit);
}
if (sign < 0) {
result = runtime->intNegate(thread, result);
}
return *result;
}
RawObject FUNC(_builtins, _int_new_from_str)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
Str str(&scope, args.get(1));
word base = intUnderlying(args.get(2)).asWord();
Object result(&scope, intFromStr(thread, str, base));
if (result.isError()) {
Str repr(&scope, thread->invokeMethod1(str, ID(__repr__)));
return thread->raiseWithFmt(LayoutId::kValueError,
"invalid literal for int() with base %w: %S",
base == 0 ? 10 : base, &repr);
}
return intOrUserSubclass(thread, type, result);
}
RawObject FUNC(_builtins, _iter)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object object(&scope, args.get(0));
return Interpreter::createIterator(thread, object);
}
static RawObject unpackFunction(const Object& obj) {
if (obj.isStaticMethod()) {
return StaticMethod::cast(*obj).function();
}
if (obj.isClassMethod()) {
return ClassMethod::cast(*obj).function();
}
if (obj.isBoundMethod()) {
return BoundMethod::cast(*obj).function();
}
if (obj.isInstanceMethod()) {
return InstanceMethod::cast(*obj).function();
}
return *obj;
}
RawObject FUNC(_builtins, _jit)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
obj = unpackFunction(obj);
if (!obj.isFunction()) {
// TODO(T90869918): Support unpacking property (fget, fset, fdel).
return Bool::falseObj();
}
Function function(&scope, *obj);
if (!canCompileFunction(thread, function)) {
return Bool::falseObj();
}
compileFunction(thread, function);
return Bool::trueObj();
}
RawObject FUNC(_builtins, _jit_iscompiled)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
obj = unpackFunction(obj);
if (!obj.isFunction()) {
// TODO(T90869918): Support unpacking property (fget, fset, fdel).
return Bool::falseObj();
}
Function function(&scope, *obj);
return Bool::fromBool(function.isCompiled());
}
RawObject FUNC(_builtins, _list_append)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfList(*self)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'append' for 'list' objects doesn't apply to a '%T' object", &self);
}
List list(&scope, *self);
Object value(&scope, args.get(1));
runtime->listAdd(thread, list, value);
return NoneType::object();
}
RawObject FUNC(_builtins, _list_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfList(args.get(0)));
}
RawObject FUNC(_builtins, _list_check_exact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isList());
}
RawObject FUNC(_builtins, _list_delitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
List self(&scope, args.get(0));
word length = self.numItems();
word idx = intUnderlying(args.get(1)).asWordSaturated();
if (idx < 0) {
idx += length;
}
if (idx < 0 || idx >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"list assignment index out of range");
}
listPop(thread, self, idx);
return NoneType::object();
}
RawObject FUNC(_builtins, _list_delslice)(Thread* thread, Arguments args) {
// This function deletes elements that are specified by a slice by copying.
// It compacts to the left elements in the slice range and then copies
// elements after the slice into the free area. The list element count is
// decremented and elements in the unused part of the list are overwritten
// with None.
HandleScope scope(thread);
List list(&scope, args.get(0));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
word slice_length = Slice::length(start, stop, step);
DCHECK(slice_length >= 0, "slice length should be positive");
if (slice_length == 0) {
// Nothing to delete
return NoneType::object();
}
if (slice_length == list.numItems()) {
// Delete all the items
list.clearFrom(0);
return NoneType::object();
}
if (step < 0) {
// Adjust step to make iterating easier
start = start + step * (slice_length - 1);
step = -step;
}
DCHECK(start >= 0, "start should be positive");
DCHECK(start < list.numItems(), "start should be in bounds");
DCHECK(step <= list.numItems() || slice_length == 1,
"Step should be in bounds or only one element should be sliced");
// Sliding compaction of elements out of the slice to the left
// Invariant: At each iteration of the loop, `fast` is the index of an
// element addressed by the slice.
// Invariant: At each iteration of the inner loop, `slow` is the index of a
// location to where we are relocating a slice addressed element. It is *not*
// addressed by the slice.
word fast = start;
for (word i = 1; i < slice_length; i++) {
DCHECK_INDEX(fast, list.numItems());
word slow = fast + 1;
fast += step;
for (; slow < fast; slow++) {
list.atPut(slow - i, list.at(slow));
}
}
// Copy elements into the space where the deleted elements were
for (word i = fast + 1; i < list.numItems(); i++) {
list.atPut(i - slice_length, list.at(i));
}
word new_length = list.numItems() - slice_length;
DCHECK(new_length >= 0, "new_length must be positive");
// Untrack all deleted elements
list.clearFrom(new_length);
return NoneType::object();
}
RawObject FUNC(_builtins, _list_extend)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfList(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(list));
}
List self(&scope, *self_obj);
Object other(&scope, args.get(1));
word src_length;
Tuple src(&scope, runtime->emptyTuple());
if (other.isList()) {
src = List::cast(*other).items();
src_length = List::cast(*other).numItems();
} else if (other.isTuple()) {
src = *other;
src_length = src.length();
} else {
return Unbound::object();
}
listExtend(thread, self, src, src_length);
return NoneType::object();
}
RawObject FUNC(_builtins, _list_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfList(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(list));
}
Object key(&scope, args.get(1));
if (runtime->isInstanceOfInt(*key)) {
word index = intUnderlying(*key).asWordSaturated();
if (!SmallInt::isValid(index)) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key);
}
List self(&scope, *self_obj);
word length = self.numItems();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"list index out of range");
}
return self.at(index);
}
word start, stop;
if (!tryUnpackSlice(key, &start, &stop)) {
return Unbound::object();
}
List self(&scope, *self_obj);
word result_len = Slice::adjustIndices(self.numItems(), &start, &stop, 1);
if (result_len == 0) {
return runtime->newList();
}
Tuple src(&scope, self.items());
MutableTuple dst(&scope, runtime->newMutableTuple(result_len));
dst.replaceFromWithStartAt(0, *src, result_len, start);
List result(&scope, runtime->newList());
result.setItems(*dst);
result.setNumItems(result_len);
return *result;
}
RawObject FUNC(_builtins, _list_getslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
List self(&scope, args.get(0));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
return listSlice(thread, self, start, stop, step);
}
RawObject FUNC(_builtins, _list_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfList(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(list));
}
RawObject FUNC(_builtins, _list_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
List self(&scope, args.get(0));
return SmallInt::fromWord(self.numItems());
}
RawObject FUNC(_builtins, _list_new)(Thread* thread, Arguments args) {
HandleScope scope(thread);
word size = SmallInt::cast(args.get(0)).value();
Runtime* runtime = thread->runtime();
List result(&scope, runtime->newList());
if (size > 0) {
MutableTuple items(&scope, runtime->newMutableTuple(size));
result.setItems(*items);
result.setNumItems(size);
items.fill(args.get(1));
}
return *result;
}
RawObject FUNC(_builtins, _list_sort)(Thread* thread, Arguments args) {
HandleScope scope(thread);
CHECK(thread->runtime()->isInstanceOfList(args.get(0)),
"Unsupported argument type for 'ls'");
List list(&scope, args.get(0));
return listSort(thread, list);
}
RawObject FUNC(_builtins, _list_sort_by_key)(Thread* thread, Arguments args) {
HandleScope scope(thread);
CHECK(thread->runtime()->isInstanceOfList(args.get(0)),
"Unsupported argument type for 'ls'");
List list(&scope, args.get(0));
return listSortWithCompareMethod(thread, list, ID(_lt_key));
}
static RawObject listSetSlice(Thread* thread, const List& self, word start,
word stop, word step, const Tuple& src,
word src_length) {
// Make sure that the degenerate case of a slice assignment where start is
// greater than stop inserts before the start and not the stop. For example,
// b[5:2] = ... should inserts before 5, not before 2.
if ((step < 0 && start < stop) || (step > 0 && start > stop)) {
stop = start;
}
if (step == 1) {
word growth = src_length - (stop - start);
word new_length = self.numItems() + growth;
if (growth == 0) {
// Assignment does not change the length of the list. Do nothing.
} else if (growth > 0) {
// Assignment grows the length of the list. Ensure there is enough free
// space in the underlying tuple for the new items and move stuff out of
// the way.
thread->runtime()->listEnsureCapacity(thread, self, new_length);
// Make the free space part of the list. Must happen before shifting so
// we can index into the free space.
self.setNumItems(new_length);
// Shift some items to the right.
self.replaceFromWithStartAt(start + growth, *self,
new_length - growth - start, start);
} else {
// Growth is negative so assignment shrinks the length of the list.
// Shift some items to the left.
self.replaceFromWithStartAt(start, *self, new_length - start,
start - growth);
// Do not retain references in the unused part of the list.
self.clearFrom(new_length);
// Remove the free space from the length of the list. Must happen after
// shifting and clearing so we can index into the free space.
self.setNumItems(new_length);
}
// Copy new elements into the middle
if (new_length > 0) {
MutableTuple::cast(self.items()).replaceFromWith(start, *src, src_length);
}
return NoneType::object();
}
word slice_length = Slice::length(start, stop, step);
if (slice_length != src_length) {
return thread->raiseWithFmt(
LayoutId::kValueError,
"attempt to assign sequence of size %w to extended slice of size "
"%w",
src_length, slice_length);
}
HandleScope scope(thread);
MutableTuple dst_items(&scope, self.items());
for (word dst_idx = start, src_idx = 0; src_idx < src_length;
dst_idx += step, src_idx++) {
dst_items.atPut(dst_idx, src.at(src_idx));
}
return NoneType::object();
}
RawObject FUNC(_builtins, _list_setitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfList(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(list));
}
Object key(&scope, args.get(1));
if (runtime->isInstanceOfInt(*key)) {
word index = intUnderlying(*key).asWordSaturated();
if (!SmallInt::isValid(index)) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key);
}
List self(&scope, *self_obj);
word length = self.numItems();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"list assignment index out of range");
}
self.atPut(index, args.get(2));
return NoneType::object();
}
word start, stop;
if (!tryUnpackSlice(key, &start, &stop)) {
return Unbound::object();
}
Object src(&scope, args.get(2));
Tuple src_tuple(&scope, runtime->emptyTuple());
word src_length;
if (src.isList()) {
if (self_obj == src) {
return Unbound::object();
}
RawList src_list = List::cast(*src);
src_tuple = src_list.items();
src_length = src_list.numItems();
} else if (src.isTuple()) {
src_tuple = *src;
src_length = src_tuple.length();
} else {
return Unbound::object();
}
List self(&scope, *self_obj);
Slice::adjustIndices(self.numItems(), &start, &stop, 1);
return listSetSlice(thread, self, start, stop, 1, src_tuple, src_length);
}
RawObject FUNC(_builtins, _list_setslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
List self(&scope, args.get(0));
Object src(&scope, args.get(4));
Tuple src_tuple(&scope, runtime->emptyTuple());
word src_length;
if (src.isList()) {
RawList src_list = List::cast(*src);
src_tuple = src_list.items();
src_length = src_list.numItems();
if (self == src) {
// This copy avoids complicated indexing logic in a rare case of
// replacing lhs with elements of rhs when lhs == rhs. It can likely be
// re-written to avoid allocation if necessary.
src_tuple = runtime->tupleSubseq(thread, src_tuple, 0, src_length);
}
} else if (src.isTuple()) {
src_tuple = *src;
src_length = src_tuple.length();
} else {
return Unbound::object();
}
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
return listSetSlice(thread, self, start, stop, step, src_tuple, src_length);
}
RawObject FUNC(_builtins, _list_swap)(Thread* thread, Arguments args) {
HandleScope scope(thread);
List list(&scope, args.get(0));
word i = SmallInt::cast(args.get(1)).value();
word j = SmallInt::cast(args.get(2)).value();
list.swap(i, j);
return NoneType::object();
}
RawObject FUNC(_builtins, _memoryview_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isMemoryView()) {
return raiseRequiresFromCaller(thread, args, ID(memoryview));
}
MemoryView self(&scope, *self_obj);
Object key_obj(&scope, args.get(1));
if (!thread->runtime()->isInstanceOfInt(*key_obj)) {
return Unbound::object();
}
word index = intUnderlying(*key_obj).asWordSaturated();
if (!SmallInt::isValid(index)) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key_obj);
}
word index_abs = std::abs(index);
word length = self.length();
word item_size = memoryviewItemsize(thread, self);
word byte_index;
if (__builtin_mul_overflow(index_abs, item_size, &byte_index) ||
length == 0) {
return thread->raiseWithFmt(LayoutId::kIndexError, "index out of bounds");
}
if (index < 0) {
byte_index = length - byte_index;
}
if (byte_index + (item_size - 1) >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError, "index out of bounds");
}
return memoryviewGetitem(thread, self, byte_index);
}
RawObject FUNC(_builtins, _memoryview_getslice)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isMemoryView()) {
return thread->raiseRequiresType(self_obj, ID(memoryview));
}
MemoryView self(&scope, *self_obj);
Int start_int(&scope, intUnderlying(args.get(1)));
word start = start_int.asWord();
Int stop_int(&scope, intUnderlying(args.get(2)));
word stop = stop_int.asWord();
Int step_int(&scope, intUnderlying(args.get(3)));
word step = step_int.asWord();
return memoryviewGetslice(thread, self, start, stop, step);
}
RawObject FUNC(_builtins, _mappingproxy_guard)(Thread* thread, Arguments args) {
if (args.get(0).isMappingProxy()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(mappingproxy));
}
RawObject FUNC(_builtins, _mappingproxy_mapping)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
MappingProxy mappingproxy(&scope, args.get(0));
return mappingproxy.mapping();
}
RawObject FUNC(_builtins, _mappingproxy_set_mapping)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
MappingProxy mappingproxy(&scope, args.get(0));
mappingproxy.setMapping(args.get(1));
return *mappingproxy;
}
RawObject FUNC(_builtins, _memoryview_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isMemoryView());
}
RawObject FUNC(_builtins, _memoryview_guard)(Thread* thread, Arguments args) {
if (args.get(0).isMemoryView()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(memoryview));
}
RawObject FUNC(_builtins, _memoryview_itemsize)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isMemoryView()) {
return thread->raiseRequiresType(self_obj, ID(memoryview));
}
MemoryView self(&scope, *self_obj);
return SmallInt::fromWord(memoryviewItemsize(thread, self));
}
RawObject FUNC(_builtins, _memoryview_nbytes)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isMemoryView()) {
return thread->raiseRequiresType(self_obj, ID(memoryview));
}
MemoryView self(&scope, *self_obj);
return SmallInt::fromWord(self.length());
}
RawObject FUNC(_builtins, _memoryview_setitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isMemoryView()) {
return raiseRequiresFromCaller(thread, args, ID(memoryview));
}
MemoryView self(&scope, *self_obj);
if (self.readOnly()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"cannot modify read-only memory");
}
Object index_obj(&scope, args.get(1));
if (!index_obj.isInt()) return Unbound::object();
Int index_int(&scope, *index_obj);
word index = index_int.asWord();
word item_size = memoryviewItemsize(thread, self);
word byte_index = (index < 0 ? -index : index) * item_size;
if (byte_index + item_size > self.length()) {
return thread->raiseWithFmt(LayoutId::kIndexError, "index out of bounds");
}
if (index < 0) {
byte_index = self.length() - byte_index;
}
Object value(&scope, args.get(2));
return memoryviewSetitem(thread, self, byte_index, value);
}
RawObject FUNC(_builtins, _memoryview_setslice)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isMemoryView()) {
return thread->raiseRequiresType(self_obj, ID(memoryview));
}
MemoryView self(&scope, *self_obj);
if (self.readOnly()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"cannot modify read-only memory");
}
Int start_int(&scope, intUnderlying(args.get(1)));
word start = start_int.asWord();
Int stop_int(&scope, intUnderlying(args.get(2)));
word stop = stop_int.asWord();
Int step_int(&scope, intUnderlying(args.get(3)));
word step = step_int.asWord();
word slice_len = Slice::adjustIndices(self.length(), &start, &stop, step);
Object value(&scope, args.get(4));
return memoryviewSetslice(thread, self, start, stop, step, slice_len, value);
}
RawObject FUNC(_builtins, _memoryview_start)(Thread*, Arguments args) {
return SmallInt::fromWord(MemoryView::cast(args.get(0)).start());
}
RawObject FUNC(_builtins, _mmap_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfMmap(args.get(0)));
}
RawObject FUNC(_builtins, _module_dir)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Module self(&scope, args.get(0));
return moduleKeys(thread, self);
}
RawObject FUNC(_builtins, _module_proxy)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfModule(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(module));
}
Module module(&scope, *self_obj);
return module.moduleProxy();
}
RawObject FUNC(_builtins, _module_proxy_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isModuleProxy());
}
RawObject FUNC(_builtins, _module_proxy_guard)(Thread* thread, Arguments args) {
if (args.get(0).isModuleProxy()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(module_proxy));
}
RawObject FUNC(_builtins, _module_proxy_keys)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isModuleProxy()) {
return thread->raiseRequiresType(self_obj, ID(module_proxy));
}
ModuleProxy self(&scope, *self_obj);
Module module(&scope, self.module());
DCHECK(module.moduleProxy() == self, "module.proxy != proxy.module");
return moduleKeys(thread, module);
}
RawObject FUNC(_builtins, _module_proxy_setitem)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isModuleProxy()) {
return thread->raiseRequiresType(self_obj, ID(module_proxy));
}
ModuleProxy self(&scope, *self_obj);
Object name(&scope, args.get(1));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
Object value(&scope, args.get(2));
Module module(&scope, self.module());
DCHECK(module.moduleProxy() == self, "module.proxy != proxy.module");
return moduleAtPut(thread, module, name, value);
}
RawObject FUNC(_builtins, _module_proxy_values)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isModuleProxy()) {
return thread->raiseRequiresType(self_obj, ID(module_proxy));
}
ModuleProxy self(&scope, *self_obj);
Module module(&scope, self.module());
DCHECK(module.moduleProxy() == self, "module.proxy != proxy.module");
return moduleValues(thread, module);
}
RawObject FUNC(_builtins, _object_class_set)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self(&scope, args.get(0));
// Disallow setting __class__ on builtin instances
Type instance_type(&scope, runtime->typeOf(*self));
if (instance_type.isBuiltin()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"__class__ assignment only supported for user types");
}
// The new class must be an instance of type
Object new_type_object(&scope, args.get(1));
if (!runtime->isInstanceOfType(*new_type_object)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"__class__ must be a type, not a '%T' object",
&new_type_object);
}
Type new_type(&scope, *new_type_object);
return typeSetDunderClass(thread, self, new_type);
}
RawObject FUNC(_builtins, _object_keys)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object object(&scope, args.get(0));
Runtime* runtime = thread->runtime();
Layout layout(&scope, runtime->layoutOf(*object));
List result(&scope, runtime->newList());
// Add in-object attributes
Tuple in_object(&scope, layout.inObjectAttributes());
word in_object_length = in_object.length();
word result_length = in_object_length;
if (layout.hasTupleOverflow()) {
result_length += Tuple::cast(layout.overflowAttributes()).length();
}
for (word i = 0; i < in_object_length; i++) {
Tuple pair(&scope, in_object.at(i));
Object name(&scope, pair.at(0));
if (name.isNoneType()) continue;
AttributeInfo info(pair.at(1));
if (info.isHidden()) continue;
runtime->listAdd(thread, result, name);
}
// Add overflow attributes
if (layout.hasTupleOverflow()) {
Tuple overflow(&scope, layout.overflowAttributes());
for (word i = 0, length = overflow.length(); i < length; i++) {
Tuple pair(&scope, overflow.at(i));
Object name(&scope, pair.at(0));
if (name == SmallInt::fromWord(0)) continue;
runtime->listAdd(thread, result, name);
}
} else if (layout.hasDictOverflow()) {
// TODO(T57446141): Dict overflow should be handled by a __dict__ descriptor
// on the type, like `type` or `function`
Instance instance(&scope, *object);
Object overflow_obj(
&scope, instance.instanceVariableAt(layout.dictOverflowOffset()));
if (!overflow_obj.isNoneType()) {
Dict overflow(&scope, *overflow_obj);
Object key(&scope, NoneType::object());
Object value(&scope, NoneType::object());
for (word i = 0; dictNextItem(overflow, &i, &key, &value);) {
runtime->listAdd(thread, result, key);
}
}
}
return *result;
}
RawObject FUNC(_builtins, _object_type_getattr)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object instance(&scope, args.get(0));
Object name(&scope, args.get(1));
DCHECK(name.isStr(),
"_object_type_hasattr should only receive string literals");
Runtime* runtime = thread->runtime();
DCHECK(runtime->isInternedStr(thread, name),
"string literals that look like names should be interned");
Type type(&scope, runtime->typeOf(*instance));
Object attr(&scope, typeLookupInMro(thread, *type, *name));
if (attr.isErrorNotFound()) {
return Unbound::object();
}
if (attr.isFunction()) {
return runtime->newBoundMethod(attr, instance);
}
return resolveDescriptorGet(thread, attr, instance, type);
}
RawObject FUNC(_builtins, _object_type_hasattr)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Type type(&scope, thread->runtime()->typeOf(args.get(0)));
Object name(&scope, args.get(1));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
Object result(&scope, typeLookupInMro(thread, *type, *name));
return Bool::fromBool(!result.isErrorNotFound());
}
RawObject FUNC(_builtins, _os_write)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object fd_obj(&scope, args.get(0));
CHECK(fd_obj.isSmallInt(), "fd must be small int");
Object byteslike_obj(&scope, args.get(1));
Byteslike byteslike_buf(&scope, thread, *byteslike_obj);
DCHECK(byteslike_buf.isValid(), "bytes-like object is invalid");
size_t count = byteslike_buf.length();
std::unique_ptr<byte[]> buffer(new byte[count]);
// TODO(T87798648): We should avoid copies for LargeBytes/DataArray.
byteslike_buf.copyTo(buffer.get(), count);
int fd = SmallInt::cast(*fd_obj).value();
ssize_t result = File::write(fd, buffer.get(), count);
if (result < 0) {
return thread->raiseOSErrorFromErrno(-result);
}
return SmallInt::fromWord(result);
}
RawObject FUNC(_builtins, _os_error_subclass_from_errno)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Int errno_value(&scope, intUnderlying(args.get(0)));
LayoutId subclass = errorLayoutFromErrno(errno_value.asWord());
return thread->runtime()->typeAt(subclass);
}
RawObject FUNC(_builtins, _profiler_exclude)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object callable(&scope, args.get(0));
word opcodes_begin = thread->opcodeCount();
bool enabled = thread->profilingEnabled();
thread->disableProfiling();
Object result(&scope, Interpreter::call0(thread, callable));
word slack = thread->opcodeCount() - opcodes_begin;
thread->countOpcodes(-slack);
if (enabled) {
thread->enableProfiling();
}
return *result;
}
static void warnImpreciseCounting(Thread* thread) {
// The interpreter switching mechanism is currently only applied in a
// lightweight fashion meaning that recursive interpreter instances further
// up the stackframe won't actually switch to a counting interpreter.
bool should_warn = false;
for (Frame* frame = thread->currentFrame()->previousFrame();
!frame->isSentinel() && !frame->previousFrame()->isSentinel();
frame = frame->previousFrame()) {
if (frame->isNative() ||
(frame->returnMode() & Frame::kExitRecursiveInterpreter) != 0) {
should_warn = true;
break;
}
}
if (should_warn) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object message(&scope, runtime->newStrFromCStr(
"Interpreter switching to count opcodes does "
"not affect outer stackframes yet."));
Object category(&scope, runtime->typeAt(LayoutId::kRuntimeWarning));
if (ensureBuiltinModuleById(thread, ID(warnings)).isErrorException() ||
thread->invokeFunction2(ID(warnings), ID(warn), message, category)
.isErrorException()) {
thread->ignorePendingException();
}
}
}
RawObject FUNC(_builtins, _profiler_install)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object new_thread_func(&scope, args.get(0));
if (!new_thread_func.isNoneType() &&
!runtime->isCallable(thread, new_thread_func)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'_install_profiler' requires a callable or None but got '%T'",
&new_thread_func);
}
Object call_func(&scope, args.get(1));
Object return_func(&scope, args.get(2));
if (!call_func.isNoneType() || !return_func.isNoneType()) {
if (!runtime->isCallable(thread, call_func)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'_install_profiler' requires a callable but got '%T'", &call_func);
}
if (!runtime->isCallable(thread, return_func)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'_install_profiler' requires a callable but got '%T'", &return_func);
}
}
if (new_thread_func != runtime->profilingNewThread()) {
if (!new_thread_func.isNoneType()) {
Object thread_data(&scope, Interpreter::call0(thread, new_thread_func));
if (thread_data.isErrorException()) return *thread_data;
thread->setProfilingData(*thread_data);
} else {
thread->setProfilingData(NoneType::object());
}
}
runtime->setProfiling(new_thread_func, call_func, return_func);
if (call_func.isNoneType()) {
runtime->interpreter()->setOpcodeCounting(false);
runtime->reinitInterpreter();
thread->disableProfiling();
} else {
warnImpreciseCounting(thread);
runtime->interpreter()->setOpcodeCounting(true);
runtime->reinitInterpreter();
thread->enableProfiling();
}
return NoneType::object();
}
RawObject FUNC(_builtins, _property)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object getter(&scope, args.get(0));
Object setter(&scope, args.get(1));
Object deleter(&scope, args.get(2));
// TODO(T42363565) Do something with the doc argument.
return thread->runtime()->newProperty(getter, setter, deleter);
}
RawObject FUNC(_builtins, _property_isabstract)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfProperty(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(property));
}
Property self(&scope, *self_obj);
Object getter(&scope, self.getter());
Object abstract(&scope, isAbstract(thread, getter));
if (abstract != Bool::falseObj()) {
return *abstract;
}
Object setter(&scope, self.setter());
if ((abstract = isAbstract(thread, setter)) != Bool::falseObj()) {
return *abstract;
}
Object deleter(&scope, self.deleter());
return isAbstract(thread, deleter);
}
RawObject FUNC(_builtins, _pyobject_offset)(Thread* thread, Arguments args) {
HandleScope scope(thread);
NativeProxy proxy(&scope, args.get(0));
uword addr = reinterpret_cast<uword>(Int::cast(proxy.native()).asCPtr());
addr += Int::cast(args.get(1)).asWord();
return thread->runtime()->newIntFromCPtr(reinterpret_cast<void*>(addr));
}
RawObject FUNC(_builtins, _range_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isRange());
}
RawObject FUNC(_builtins, _range_guard)(Thread* thread, Arguments args) {
if (args.get(0).isRange()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(range));
}
RawObject FUNC(_builtins, _range_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Range self(&scope, args.get(0));
Object start(&scope, self.start());
Object stop(&scope, self.stop());
Object step(&scope, self.step());
return rangeLen(thread, start, stop, step);
}
RawObject FUNC(_builtins, _readline)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Str prompt(&scope, strUnderlying(args.get(0)));
word length = prompt.length();
std::unique_ptr<char[]> prompt_buf(new char[length + 1]);
prompt.copyTo(reinterpret_cast<byte*>(prompt_buf.get()), length);
prompt_buf.get()[length] = '\0';
char* line = PyOS_Readline(stdin, stdout, prompt_buf.get());
if (line == nullptr) {
CHECK(thread->hasPendingException(), "there must be an exception raised");
return Error::exception();
}
Object result(&scope, thread->runtime()->newStrFromCStr(line));
std::free(line);
return *result;
}
RawObject FUNC(_builtins, _repr_enter)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
return thread->reprEnter(obj);
}
RawObject FUNC(_builtins, _repr_leave)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
thread->reprLeave(obj);
return NoneType::object();
}
RawObject FUNC(_builtins, _seq_index)(Thread* thread, Arguments args) {
HandleScope scope(thread);
SeqIterator self(&scope, args.get(0));
return SmallInt::fromWord(self.index());
}
RawObject FUNC(_builtins, _seq_iterable)(Thread* thread, Arguments args) {
HandleScope scope(thread);
SeqIterator self(&scope, args.get(0));
return self.iterable();
}
RawObject FUNC(_builtins, _seq_set_index)(Thread* thread, Arguments args) {
HandleScope scope(thread);
SeqIterator self(&scope, args.get(0));
Int index(&scope, args.get(1));
self.setIndex(index.asWord());
return NoneType::object();
}
RawObject FUNC(_builtins, _seq_set_iterable)(Thread* thread, Arguments args) {
HandleScope scope(thread);
SeqIterator self(&scope, args.get(0));
Object iterable(&scope, args.get(1));
self.setIterable(*iterable);
return NoneType::object();
}
RawObject FUNC(_builtins, _set_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfSet(args.get(0)));
}
RawObject FUNC(_builtins,
_set_function_flag_iterable_coroutine)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object function_obj(&scope, args.get(0));
if (!function_obj.isFunction()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"can only be called with a 'function' object");
}
Function function(&scope, *function_obj);
Code original_code(&scope, function.code());
Object code_code(&scope, original_code.code());
Object consts(&scope, original_code.consts());
Object names(&scope, original_code.names());
Object varnames(&scope, original_code.varnames());
Object freevars(&scope, original_code.freevars());
Object cellvars(&scope, original_code.cellvars());
Object filename(&scope, original_code.filename());
Object name(&scope, original_code.name());
Object lnotab(&scope, original_code.lnotab());
Code new_code(
&scope,
thread->runtime()->newCode(
original_code.argcount(), original_code.posonlyargcount(),
original_code.kwonlyargcount(), original_code.nlocals(),
original_code.stacksize(),
original_code.flags() | RawFunction::Flags::kIterableCoroutine,
code_code, consts, names, varnames, freevars, cellvars, filename,
name, original_code.firstlineno(), lnotab));
new_code.setIntrinsic(original_code.intrinsic());
function.setCode(*new_code);
function.setFlags(function.flags() | Function::Flags::kIterableCoroutine);
return NoneType::object();
}
RawObject FUNC(_builtins, _set_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfSet(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(set));
}
RawObject FUNC(_builtins, _set_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Set self(&scope, args.get(0));
return SmallInt::fromWord(self.numItems());
}
RawObject FUNC(_builtins, _set_member_double)(Thread*, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
double value = Float::cast(args.get(1)).value();
std::memcpy(reinterpret_cast<void*>(addr), &value, sizeof(value));
return NoneType::object();
}
RawObject FUNC(_builtins, _set_member_float)(Thread*, Arguments args) {
auto addr = Int::cast(args.get(0)).asCPtr();
float value = Float::cast(args.get(1)).value();
std::memcpy(reinterpret_cast<void*>(addr), &value, sizeof(value));
return NoneType::object();
}
RawObject FUNC(_builtins, _set_member_integral)(Thread*, Arguments args) {
void* addr = Int::cast(args.get(0)).asCPtr();
OptInt<long long> optint = RawInt::cast(args.get(1)).asInt<long long>();
CHECK(optint.error == CastError::None, "Overflow casting to primitive type");
long long value = optint.value;
word num_bytes = RawInt::cast(args.get(2)).asWord();
static_assert(endian::native == endian::little, "expected little endian");
std::memcpy(reinterpret_cast<void*>(addr), &value, num_bytes);
return NoneType::object();
}
RawObject FUNC(_builtins, _set_member_integral_unsigned)(Thread*,
Arguments args) {
void* addr = Int::cast(args.get(0)).asCPtr();
OptInt<unsigned long long> optint =
RawInt::cast(args.get(1)).asInt<unsigned long long>();
CHECK(optint.error == CastError::None, "Overflow casting to primitive type");
unsigned long long value = optint.value;
word num_bytes = RawInt::cast(args.get(2)).asWord();
static_assert(endian::native == endian::little, "expected little endian");
std::memcpy(reinterpret_cast<void*>(addr), &value, num_bytes);
return NoneType::object();
}
RawObject FUNC(_builtins, _set_member_pyobject)(Thread* thread,
Arguments args) {
objectSetMember(thread->runtime(), args.get(0), args.get(1));
return NoneType::object();
}
RawObject FUNC(_builtins, _slice_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isSlice());
}
RawObject FUNC(_builtins, _slice_guard)(Thread* thread, Arguments args) {
if (args.get(0).isSlice()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(slice));
}
RawObject FUNC(_builtins, _slice_start)(Thread*, Arguments args) {
RawObject start_obj = args.get(0);
word step = SmallInt::cast(args.get(1)).value();
word length = SmallInt::cast(args.get(2)).value();
if (start_obj.isNoneType()) {
return SmallInt::fromWord(step < 0 ? length - 1 : 0);
}
word lower, upper;
if (step < 0) {
lower = -1;
upper = length - 1;
} else {
lower = 0;
upper = length;
}
word start = intUnderlying(start_obj).asWordSaturated();
if (start < 0) {
start = Utils::maximum(start + length, lower);
} else {
start = Utils::minimum(start, upper);
}
return SmallInt::fromWord(start);
}
RawObject FUNC(_builtins, _staticmethod)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object function(&scope, args.get(0));
StaticMethod method(&scope, thread->runtime()->newStaticMethod());
method.setFunction(*function);
return *method;
}
RawObject FUNC(_builtins, _slice_start_long)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Int step(&scope, intUnderlying(args.get(1)));
Int length(&scope, intUnderlying(args.get(2)));
bool negative_step = step.isNegative();
Int lower(&scope, SmallInt::fromWord(negative_step ? -1 : 0));
Runtime* runtime = thread->runtime();
// upper = length + lower; if step < 0, then lower = 0 anyway
Int upper(&scope,
negative_step ? runtime->intAdd(thread, length, lower) : *length);
Object start_obj(&scope, args.get(0));
if (start_obj.isNoneType()) {
return negative_step ? *upper : *lower;
}
Int start(&scope, intUnderlying(*start_obj));
if (start.isNegative()) {
start = runtime->intAdd(thread, start, length);
if (start.compare(*lower) < 0) {
start = *lower;
}
} else if (start.compare(*upper) > 0) {
start = *upper;
}
return *start;
}
RawObject FUNC(_builtins, _slice_step)(Thread* thread, Arguments args) {
RawObject step_obj = args.get(0);
if (step_obj.isNoneType()) return SmallInt::fromWord(1);
RawInt step = intUnderlying(step_obj);
if (step == SmallInt::fromWord(0) || step == Bool::falseObj()) {
return thread->raiseWithFmt(LayoutId::kValueError,
"slice step cannot be zero");
}
if (step.isSmallInt()) {
return step;
}
if (step == Bool::trueObj()) {
return SmallInt::fromWord(1);
}
return SmallInt::fromWord(step.isNegative() ? SmallInt::kMinValue
: SmallInt::kMaxValue);
}
RawObject FUNC(_builtins, _slice_step_long)(Thread* thread, Arguments args) {
RawObject step_obj = args.get(0);
if (step_obj.isNoneType()) return SmallInt::fromWord(1);
RawInt step = intUnderlying(step_obj);
if (step == SmallInt::fromWord(0) || step == Bool::falseObj()) {
return thread->raiseWithFmt(LayoutId::kValueError,
"slice step cannot be zero");
}
if (step.isSmallInt()) {
return step;
}
if (step == Bool::trueObj()) {
return SmallInt::fromWord(1);
}
return step;
}
RawObject FUNC(_builtins, _slice_stop)(Thread*, Arguments args) {
RawObject stop_obj = args.get(0);
word step = SmallInt::cast(args.get(1)).value();
word length = SmallInt::cast(args.get(2)).value();
if (stop_obj.isNoneType()) {
return SmallInt::fromWord(step < 0 ? -1 : length);
}
word lower, upper;
if (step < 0) {
lower = -1;
upper = length - 1;
} else {
lower = 0;
upper = length;
}
word stop = intUnderlying(stop_obj).asWordSaturated();
if (stop < 0) {
stop = Utils::maximum(stop + length, lower);
} else {
stop = Utils::minimum(stop, upper);
}
return SmallInt::fromWord(stop);
}
RawObject FUNC(_builtins, _slice_stop_long)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Int step(&scope, intUnderlying(args.get(1)));
Int length(&scope, intUnderlying(args.get(2)));
bool negative_step = step.isNegative();
Int lower(&scope, SmallInt::fromWord(negative_step ? -1 : 0));
Runtime* runtime = thread->runtime();
// upper = length + lower; if step < 0, then lower = 0 anyway
Int upper(&scope,
negative_step ? runtime->intAdd(thread, length, lower) : *length);
Object stop_obj(&scope, args.get(0));
if (stop_obj.isNoneType()) {
return negative_step ? *lower : *upper;
}
Int stop(&scope, intUnderlying(*stop_obj));
if (stop.isNegative()) {
stop = runtime->intAdd(thread, stop, length);
if (stop.compare(*lower) < 0) {
stop = *lower;
}
} else if (stop.compare(*upper) > 0) {
stop = *upper;
}
return *stop;
}
RawObject FUNC(_builtins, _staticmethod_isabstract)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfStaticMethod(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(staticmethod));
}
StaticMethod self(&scope, *self_obj);
Object func(&scope, self.function());
return isAbstract(thread, func);
}
RawObject FUNC(_builtins, _stop_iteration_ctor)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
DCHECK(args.get(0) == runtime->typeAt(LayoutId::kStopIteration),
"unexpected type; should be StopIteration");
Layout layout(&scope, runtime->layoutAt(LayoutId::kStopIteration));
StopIteration self(&scope, runtime->newInstance(layout));
Object args_obj(&scope, args.get(1));
self.setArgs(*args_obj);
self.setCause(Unbound::object());
self.setContext(Unbound::object());
self.setTraceback(Unbound::object());
self.setSuppressContext(RawBool::falseObj());
Tuple tuple(&scope, self.args());
if (tuple.length() > 0) self.setValue(tuple.at(0));
return *self;
}
RawObject FUNC(_builtins, _str_array_clear)(Thread* thread, Arguments args) {
HandleScope scope(thread);
StrArray self(&scope, args.get(0));
self.setNumItems(0);
return NoneType::object();
}
RawObject FUNC(_builtins, _str_array_ctor)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
DCHECK(args.get(0) == runtime->typeAt(LayoutId::kStrArray),
"_str_array.__new__(X): X is not '_str_array'");
Object self_obj(&scope, runtime->newStrArray());
if (self_obj.isError()) return *self_obj;
StrArray self(&scope, *self_obj);
self.setNumItems(0);
Object source_obj(&scope, args.get(1));
if (source_obj.isUnbound()) {
return *self;
}
if (!runtime->isInstanceOfStr(*source_obj)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"_str_array can only be initialized with str");
}
Str source(&scope, strUnderlying(*source_obj));
runtime->strArrayAddStr(thread, self, source);
return *self;
}
RawObject FUNC(_builtins, _str_array_iadd)(Thread* thread, Arguments args) {
HandleScope scope(thread);
StrArray self(&scope, args.get(0));
Str other(&scope, strUnderlying(args.get(1)));
thread->runtime()->strArrayAddStr(thread, self, other);
return *self;
}
RawObject FUNC(_builtins, _structseq_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
word index = SmallInt::cast(args.get(1)).value();
return structseqGetItem(thread, self, index);
}
RawObject FUNC(_builtins, _structseq_new_type)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Str name(&scope, strUnderlying(args.get(0)));
name = Runtime::internStr(thread, name);
Tuple field_names(&scope, args.get(1));
Bool is_heaptype(&scope, args.get(2));
word num_fields = field_names.length();
if (num_fields > 0) {
MutableTuple field_names_interned(&scope,
runtime->newMutableTuple(num_fields));
Object field_name(&scope, NoneType::object());
for (word i = 0; i < num_fields; i++) {
field_name = field_names.at(i);
if (!field_name.isNoneType()) {
field_name = Runtime::internStr(thread, field_name);
}
field_names_interned.atPut(i, *field_name);
}
field_names = field_names_interned.becomeImmutable();
}
word num_in_sequence = args.get(3).isUnbound()
? num_fields
: SmallInt::cast(args.get(3)).value();
word flags =
is_heaptype.value() ? Type::Flag::kIsCPythonHeaptype : Type::Flag::kNone;
return structseqNewType(thread, name, field_names, num_in_sequence, flags);
}
RawObject FUNC(_builtins, _structseq_setitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
word index = SmallInt::cast(args.get(1)).value();
Object value(&scope, args.get(2));
return structseqSetItem(thread, self, index, value);
}
static RawObject padString(Thread* thread, const Str& str,
const SmallStr& fillchar, word str_length,
word left_padding, word fill_char_length,
word result_length) {
// Optimize to use SmallStr for results less than the small string max length
if (result_length <= SmallStr::kMaxLength) {
byte buffer[SmallStr::kMaxLength];
for (word i = 0; i < left_padding; i += fill_char_length) {
fillchar.copyTo(&buffer[i], fill_char_length);
}
str.copyTo(&buffer[left_padding], str_length);
for (word i = left_padding + str_length; i < result_length;
i += fill_char_length) {
fillchar.copyTo(&buffer[i], fill_char_length);
}
return SmallStr::fromBytes({buffer, result_length});
}
HandleScope scope(thread);
MutableBytes buffer(
&scope, thread->runtime()->newMutableBytesUninitialized(result_length));
{
// In order to improve performance for string operations we write directly
// to the memory address of the buffer. This operation requires NO calls
// which could potentially trigger allocations in order to ensure memory
// consistency.
byte* dst = reinterpret_cast<byte*>(buffer.address());
for (word i = 0; i < left_padding; i += fill_char_length) {
fillchar.copyTo(&dst[i], fill_char_length);
}
str.copyTo(&dst[left_padding], str_length);
for (word i = left_padding + str_length; i < result_length;
i += fill_char_length) {
fillchar.copyTo(&dst[i], fill_char_length);
}
}
return buffer.becomeStr();
}
RawObject FUNC(_builtins, _str_center)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfStr(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(str));
}
Object width_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*width_obj)) {
return Unbound::object();
}
Int width_int(&scope, intUnderlying(*width_obj));
if (width_int.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"int too large to convert to an index");
}
word width = width_int.asWord();
Object fillchar_obj(&scope, args.get(2));
if (!runtime->isInstanceOfStr(*fillchar_obj)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"The fill character must be a unicode character, not a '%T'",
&fillchar_obj);
}
Str fillchar_str(&scope, strUnderlying(*fillchar_obj));
if (!fillchar_str.isSmallStr() || fillchar_str.codePointLength() != 1) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"The fill character must be exactly one character long");
}
SmallStr fillchar(&scope, *fillchar_str);
Str self(&scope, strUnderlying(*self_obj));
word self_codepoints = self.codePointLength();
if (self_codepoints >= width) {
return *self;
}
word self_length = self.length();
word fill_char_length = fillchar.length();
word fill_char_count = width - self_codepoints;
word fill_length = fill_char_length * fill_char_count;
word result_length = self_length + fill_length;
word left_padding = fill_char_count / 2;
// When fill characters cannot be evenly distributed place the extra on the
// left.
if (fill_char_count % 2 != 0 && width % 2 != 0) {
left_padding += 1;
}
left_padding *= fill_char_length;
return padString(thread, self, fillchar, self_length, left_padding,
fill_char_length, result_length);
}
RawObject FUNC(_builtins, _str_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfStr(args.get(0)));
}
RawObject FUNC(_builtins, _str_encode)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object str_obj(&scope, args.get(0));
if (!str_obj.isStr()) {
return Unbound::object();
}
Str str(&scope, *str_obj);
static RawSmallStr ascii = SmallStr::fromCStr("ascii");
static RawSmallStr utf8 = SmallStr::fromCStr("utf-8");
static RawSmallStr latin1 = SmallStr::fromCStr("latin-1");
Str enc(&scope, args.get(1));
if (enc != ascii && enc != utf8 && enc != latin1 &&
enc.compareCStr("iso-8859-1") != 0) {
return Unbound::object();
}
return strEncodeASCII(thread, str);
}
RawObject FUNC(_builtins, _str_encode_ascii)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object str_obj(&scope, args.get(0));
if (!str_obj.isStr()) {
return Unbound::object();
}
Str str(&scope, *str_obj);
return strEncodeASCII(thread, str);
}
RawObject FUNC(_builtins, _str_check_exact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isStr());
}
RawObject FUNC(_builtins, _str_compare_digest)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
// TODO(T57794178): Use volatile
Object left_obj(&scope, args.get(0));
Object right_obj(&scope, args.get(1));
DCHECK(runtime->isInstanceOfStr(*left_obj),
"_str_compare_digest requires 'str' instance");
DCHECK(runtime->isInstanceOfStr(*right_obj),
"_str_compare_digest requires 'str' instance");
Str left(&scope, strUnderlying(*left_obj));
Str right(&scope, strUnderlying(*right_obj));
if (!left.isASCII() || !right.isASCII()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"comparing strings with non-ASCII characters is not supported");
}
word left_len = left.length();
word right_len = right.length();
word length = Utils::minimum(left_len, right_len);
word result = (right_len == left_len) ? 0 : 1;
for (word i = 0; i < length; i++) {
result |= left.byteAt(i) ^ right.byteAt(i);
}
return Bool::fromBool(result == 0);
}
RawObject FUNC(_builtins, _str_count)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(runtime->isInstanceOfStr(args.get(0)),
"_str_count requires 'str' instance");
DCHECK(runtime->isInstanceOfStr(args.get(1)),
"_str_count requires 'str' instance");
HandleScope scope(thread);
Str haystack(&scope, strUnderlying(args.get(0)));
Str needle(&scope, strUnderlying(args.get(1)));
Object start_obj(&scope, args.get(2));
Object end_obj(&scope, args.get(3));
word start = 0;
if (!start_obj.isNoneType()) {
start = intUnderlying(*start_obj).asWordSaturated();
}
word end = kMaxWord;
if (!end_obj.isNoneType()) {
end = intUnderlying(*end_obj).asWordSaturated();
}
return strCount(haystack, needle, start, end);
}
RawObject FUNC(_builtins, _str_ctor)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object cls(&scope, args.get(0));
Object obj(&scope, args.get(1));
Object encoding(&scope, args.get(2));
Object errors(&scope, args.get(3));
Runtime* runtime = thread->runtime();
DCHECK(*cls == runtime->typeAt(LayoutId::kStr), "unexpected cls");
if (obj.isUnbound()) {
return Str::empty();
}
if (encoding.isUnbound() && errors.isUnbound() && obj.isStr()) {
return *obj;
}
Type str_type(&scope, runtime->typeAt(LayoutId::kStr));
Object dunder_new(&scope, runtime->symbols()->at(ID(__new__)));
Function str_dunder_new(&scope,
typeGetAttribute(thread, str_type, dunder_new));
// TODO(T76654356): Use Thread::invokeMethodStatic.
return Interpreter::call4(thread, str_dunder_new, cls, obj, encoding, errors);
}
RawObject FUNC(_builtins, _str_ctor_obj)(Thread* thread, Arguments args) {
{
// Warning: This code is using `RawXXX` variables for performance! This is
// despite the fact that we call functions that do potentially perform
// memory allocations. Be careful not to break this invariant if you change
// the code!
DCHECK(args.get(0) == thread->runtime()->typeAt(LayoutId::kStr),
"expected cls==str");
RawObject obj_raw = args.get(1);
if (obj_raw.isStr()) {
return obj_raw;
}
}
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Type str_type(&scope, runtime->typeAt(LayoutId::kStr));
Object dunder_new(&scope, runtime->symbols()->at(ID(__new__)));
Function str_dunder_new(&scope,
typeGetAttribute(thread, str_type, dunder_new));
Object cls(&scope, args.get(0));
Object obj(&scope, args.get(1));
Object encoding(&scope, Unbound::object());
Object errors(&scope, Unbound::object());
// TODO(T76654356): Use Thread::invokeMethodStatic.
return Interpreter::call4(thread, str_dunder_new, cls, obj, encoding, errors);
}
RawObject FUNC(_builtins, _str_endswith)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object start_obj(&scope, args.get(2));
Object end_obj(&scope, args.get(3));
Str self(&scope, strUnderlying(args.get(0)));
Str suffix(&scope, strUnderlying(args.get(1)));
word len = self.codePointLength();
word start = 0;
word end = len;
if (!start_obj.isNoneType()) {
// TODO(T55084422): bounds checking
start = intUnderlying(*start_obj).asWordSaturated();
}
if (!end_obj.isNoneType()) {
// TODO(T55084422): bounds checking
end = intUnderlying(*end_obj).asWordSaturated();
}
Slice::adjustSearchIndices(&start, &end, len);
word suffix_len = suffix.codePointLength();
if (start + suffix_len > end) {
return Bool::falseObj();
}
word start_offset = thread->strOffset(self, end - suffix_len);
word suffix_chars = suffix.length();
for (word i = start_offset, j = 0; j < suffix_chars; i++, j++) {
if (self.byteAt(i) != suffix.byteAt(j)) {
return Bool::falseObj();
}
}
return Bool::trueObj();
}
RawObject FUNC(_builtins, _str_escape_non_ascii)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
CHECK(thread->runtime()->isInstanceOfStr(args.get(0)),
"_str_escape_non_ascii expected str instance");
Str obj(&scope, strUnderlying(args.get(0)));
return strEscapeNonASCII(thread, obj);
}
RawObject FUNC(_builtins, _str_find)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(runtime->isInstanceOfStr(args.get(0)),
"_str_find requires 'str' instance");
DCHECK(runtime->isInstanceOfStr(args.get(1)),
"_str_find requires 'str' instance");
HandleScope scope(thread);
Str haystack(&scope, strUnderlying(args.get(0)));
Str needle(&scope, strUnderlying(args.get(1)));
Object start_obj(&scope, args.get(2));
Object end_obj(&scope, args.get(3));
if ((start_obj.isNoneType() || start_obj == SmallInt::fromWord(0)) &&
end_obj.isNoneType()) {
return SmallInt::fromWord(strFind(haystack, needle));
}
word start = 0;
if (!start_obj.isNoneType()) {
start = intUnderlying(*start_obj).asWordSaturated();
}
word end = kMaxWord;
if (!end_obj.isNoneType()) {
end = intUnderlying(*end_obj).asWordSaturated();
}
word result = strFindWithRange(haystack, needle, start, end);
return SmallInt::fromWord(result);
}
RawObject FUNC(_builtins, _str_from_str)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
DCHECK(type.builtinBase() == LayoutId::kStr, "type must subclass str");
Str value(&scope, strUnderlying(args.get(1)));
if (type.isBuiltin()) return *value;
Layout type_layout(&scope, type.instanceLayout());
UserStrBase instance(&scope, thread->runtime()->newInstance(type_layout));
instance.setValue(*value);
return *instance;
}
RawObject FUNC(_builtins, _str_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfStr(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(str));
}
Object key(&scope, args.get(1));
if (runtime->isInstanceOfInt(*key)) {
Str self(&scope, strUnderlying(*self_obj));
word index = intUnderlying(*key).asWordSaturated();
if (!SmallInt::isValid(index)) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key);
}
if (index < 0) {
index += self.codePointLength();
}
if (index >= 0) {
word offset = thread->strOffset(self, index);
if (offset < self.length()) {
word ignored;
return SmallStr::fromCodePoint(self.codePointAt(offset, &ignored));
}
}
return thread->raiseWithFmt(LayoutId::kIndexError,
"string index out of range");
}
word start, stop;
if (!tryUnpackSlice(key, &start, &stop)) {
return Unbound::object();
}
// Manually adjust slice bounds to avoid an extra call to codePointLength
Str self(&scope, strUnderlying(*self_obj));
word start_index = adjustedStrIndex(self, start);
word stop_index = adjustedStrIndex(self, stop);
word length = stop_index - start_index;
if (length <= 0) return Str::empty();
return strSubstr(thread, self, start_index, length);
}
RawObject FUNC(_builtins, _str_getslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Str self(&scope, strUnderlying(args.get(0)));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
return thread->runtime()->strSlice(thread, self, start, stop, step);
}
RawObject FUNC(_builtins, _str_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfStr(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(str));
}
RawObject FUNC(_builtins, _str_ischr)(Thread*, Arguments args) {
RawStr str = strUnderlying(args.get(0));
return Bool::fromBool(str.isSmallStr() && str.codePointLength() == 1);
}
RawObject FUNC(_builtins, _str_join)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object sep_obj(&scope, args.get(0));
if (!runtime->isInstanceOfStr(*sep_obj)) {
return raiseRequiresFromCaller(thread, args, ID(str));
}
Str sep(&scope, strUnderlying(*sep_obj));
Object iterable(&scope, args.get(1));
return strJoinWithTupleOrList(thread, sep, iterable);
}
RawObject FUNC(_builtins, _str_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Str self(&scope, strUnderlying(args.get(0)));
return SmallInt::fromWord(self.codePointLength());
}
RawObject FUNC(_builtins, _str_ljust)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfStr(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(str));
}
Object width_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*width_obj)) {
return Unbound::object();
}
Int width_int(&scope, intUnderlying(*width_obj));
if (width_int.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"int too large to convert to an index");
}
word width = width_int.asWord();
Object fillchar_obj(&scope, args.get(2));
if (!runtime->isInstanceOfStr(*fillchar_obj)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"The fill character must be a unicode character, not a '%T'",
&fillchar_obj);
}
Str fillchar_str(&scope, strUnderlying(*fillchar_obj));
if (!fillchar_str.isSmallStr() || fillchar_str.codePointLength() != 1) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"The fill character must be exactly one character long");
}
SmallStr fillchar(&scope, *fillchar_str);
Str self(&scope, strUnderlying(*self_obj));
word self_codepoints = self.codePointLength();
if (self_codepoints >= width) {
return *self;
}
word self_length = self.length();
word fill_char_length = fillchar.length();
word fill_char_count = width - self_codepoints;
word fill_length = fill_char_length * fill_char_count;
word result_length = self_length + fill_length;
word left_padding = 0;
return padString(thread, self, fillchar, self_length, left_padding,
fill_char_length, result_length);
}
RawObject FUNC(_builtins, _str_mod_fast_path)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfStr(args.get(0)) ||
!runtime->isInstanceOfTuple(args.get(1))) {
return Unbound::object();
}
HandleScope scope(thread);
Str str(&scope, strUnderlying(args.get(0)));
Tuple args_tuple(&scope, tupleUnderlying(args.get(1)));
const word max_args = 16;
word num_args = args_tuple.length();
if (num_args > max_args) {
return Unbound::object();
}
// Scan format string for occurences of %s and remember their indexes. Also
// check that the corresponding arguments are strings.
word arg_indexes[max_args];
word arg_idx = 0;
word result_length = 0;
Object arg(&scope, Unbound::object());
word fmt_length = str.length();
for (word i = 0; i < fmt_length; i++) {
if (str.byteAt(i) != '%') {
result_length++;
continue;
}
i++;
if (i >= fmt_length || str.byteAt(i) != 's' || arg_idx >= num_args) {
return Unbound::object();
}
arg = args_tuple.at(arg_idx);
if (!arg.isStr()) {
return Unbound::object();
}
result_length += Str::cast(*arg).length();
arg_indexes[arg_idx] = i - 1;
arg_idx++;
}
if (arg_idx < num_args) {
return Unbound::object();
}
// Construct resulting string.
if (arg_idx == 0) {
return *str;
}
MutableBytes result(&scope,
runtime->newMutableBytesUninitialized(result_length));
word result_idx = 0;
word fmt_idx = 0;
Str arg_str(&scope, Str::empty());
for (word a = 0; a < num_args; a++) {
word fragment_begin = fmt_idx;
word fragment_length = arg_indexes[a] - fragment_begin;
result.replaceFromWithStrStartAt(result_idx, *str, fragment_length,
fragment_begin);
result_idx += fragment_length;
fmt_idx += fragment_length + 2;
arg_str = args_tuple.at(a);
word arg_length = arg_str.length();
result.replaceFromWithStr(result_idx, *arg_str, arg_length);
result_idx += arg_length;
}
word fragment_begin = fmt_idx;
word fragment_length = fmt_length - fmt_idx;
result.replaceFromWithStrStartAt(result_idx, *str, fragment_length,
fragment_begin);
return result.becomeStr();
}
static word strScan(const Str& haystack, word haystack_len, const Str& needle,
word needle_len,
word (*find_func)(const byte* haystack, word haystack_len,
const byte* needle, word needle_len)) {
byte haystack_buf[SmallStr::kMaxLength];
byte* haystack_ptr = haystack_buf;
if (haystack.isSmallStr()) {
haystack.copyTo(haystack_buf, haystack_len);
} else {
haystack_ptr = reinterpret_cast<byte*>(LargeStr::cast(*haystack).address());
}
byte needle_buf[SmallStr::kMaxLength];
byte* needle_ptr = needle_buf;
if (needle.isSmallStr()) {
needle.copyTo(needle_buf, needle_len);
} else {
needle_ptr = reinterpret_cast<byte*>(LargeStr::cast(*needle).address());
}
return (*find_func)(haystack_ptr, haystack_len, needle_ptr, needle_len);
}
// Look for needle in haystack, starting from the left. Return a tuple
// containing:
// * haystack up to but not including needle
// * needle
// * haystack after and not including needle
// If needle is not found in haystack, return (haystack, "", "")
RawObject FUNC(_builtins, _str_partition)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Str haystack(&scope, strUnderlying(args.get(0)));
Str needle(&scope, strUnderlying(args.get(1)));
Runtime* runtime = thread->runtime();
MutableTuple result(&scope, runtime->newMutableTuple(3));
result.atPut(0, *haystack);
result.atPut(1, Str::empty());
result.atPut(2, Str::empty());
word haystack_len = haystack.length();
word needle_len = needle.length();
if (haystack_len < needle_len) {
// Fast path when needle is bigger than haystack
return result.becomeImmutable();
}
word prefix_len =
strScan(haystack, haystack_len, needle, needle_len, Utils::memoryFind);
if (prefix_len < 0) return result.becomeImmutable();
result.atPut(0, strSubstr(thread, haystack, 0, prefix_len));
result.atPut(1, *needle);
word suffix_start = prefix_len + needle_len;
word suffix_len = haystack_len - suffix_start;
result.atPut(2, strSubstr(thread, haystack, suffix_start, suffix_len));
return result.becomeImmutable();
}
RawObject FUNC(_builtins, _str_replace)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Str self(&scope, strUnderlying(args.get(0)));
Str oldstr(&scope, strUnderlying(args.get(1)));
Str newstr(&scope, strUnderlying(args.get(2)));
word count = intUnderlying(args.get(3)).asWordSaturated();
return runtime->strReplace(thread, self, oldstr, newstr, count);
}
RawObject FUNC(_builtins, _str_rfind)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(runtime->isInstanceOfStr(args.get(0)),
"_str_rfind requires 'str' instance");
DCHECK(runtime->isInstanceOfStr(args.get(1)),
"_str_rfind requires 'str' instance");
HandleScope scope(thread);
Str haystack(&scope, strUnderlying(args.get(0)));
Str needle(&scope, strUnderlying(args.get(1)));
Object start_obj(&scope, args.get(2));
Object end_obj(&scope, args.get(3));
word start = 0;
if (!start_obj.isNoneType()) {
start = intUnderlying(*start_obj).asWordSaturated();
}
word end = kMaxWord;
if (!end_obj.isNoneType()) {
end = intUnderlying(*end_obj).asWordSaturated();
}
Slice::adjustSearchIndices(&start, &end, haystack.codePointLength());
word result = strRFind(haystack, needle, start, end);
return SmallInt::fromWord(result);
}
RawObject FUNC(_builtins, _str_rjust)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
if (!runtime->isInstanceOfStr(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(str));
}
Object width_obj(&scope, args.get(1));
if (!runtime->isInstanceOfInt(*width_obj)) {
return Unbound::object();
}
Int width_int(&scope, intUnderlying(*width_obj));
if (width_int.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"int too large to convert to an index");
}
word width = width_int.asWord();
Object fillchar_obj(&scope, args.get(2));
if (!runtime->isInstanceOfStr(*fillchar_obj)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"The fill character must be a unicode character, not a '%T'",
&fillchar_obj);
}
Str fillchar_str(&scope, strUnderlying(*fillchar_obj));
if (!fillchar_str.isSmallStr() || fillchar_str.codePointLength() != 1) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"The fill character must be exactly one character long");
}
SmallStr fillchar(&scope, *fillchar_str);
Str self(&scope, strUnderlying(*self_obj));
word self_codepoints = self.codePointLength();
if (self_codepoints >= width) {
return *self;
}
word self_length = self.length();
word fill_char_length = fillchar.length();
word fill_char_count = width - self_codepoints;
word fill_length = fill_char_length * fill_char_count;
word result_length = self_length + fill_length;
word left_padding = fill_length;
return padString(thread, self, fillchar, self_length, left_padding,
fill_char_length, result_length);
}
// Look for needle in haystack, starting from the right. Return a tuple
// containing:
// * haystack up to but not including needle
// * needle
// * haystack after and not including needle
// If needle is not found in haystack, return ("", "", haystack)
RawObject FUNC(_builtins, _str_rpartition)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Str haystack(&scope, strUnderlying(args.get(0)));
Str needle(&scope, strUnderlying(args.get(1)));
MutableTuple result(&scope, runtime->newMutableTuple(3));
result.atPut(0, Str::empty());
result.atPut(1, Str::empty());
result.atPut(2, *haystack);
word haystack_len = haystack.length();
word needle_len = needle.length();
if (haystack_len < needle_len) {
// Fast path when needle is bigger than haystack
return result.becomeImmutable();
}
word prefix_len = strScan(haystack, haystack_len, needle, needle_len,
Utils::memoryFindReverse);
if (prefix_len < 0) return result.becomeImmutable();
result.atPut(0, strSubstr(thread, haystack, 0, prefix_len));
result.atPut(1, *needle);
word suffix_start = prefix_len + needle_len;
word suffix_len = haystack_len - suffix_start;
result.atPut(2, strSubstr(thread, haystack, suffix_start, suffix_len));
return result.becomeImmutable();
}
static RawObject strSplitWhitespace(Thread* thread, const Str& self,
word maxsplit) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
List result(&scope, runtime->newList());
if (maxsplit < 0) {
maxsplit = kMaxWord;
}
word self_length = self.length();
word num_split = 0;
Str substr(&scope, Str::empty());
for (word i = 0, j = 0; j < self_length; i = self.offsetByCodePoints(j, 1)) {
// Find beginning of next word
{
word num_bytes;
while (i < self_length &&
Unicode::isSpace(self.codePointAt(i, &num_bytes))) {
i += num_bytes;
}
}
if (i == self_length) {
// End of string; finished
break;
}
// Find end of next word
if (maxsplit == num_split) {
// Take the rest of the string
j = self_length;
} else {
j = self.offsetByCodePoints(i, 1);
{
word num_bytes;
while (j < self_length &&
!Unicode::isSpace(self.codePointAt(j, &num_bytes))) {
j += num_bytes;
}
}
num_split += 1;
}
substr = strSubstr(thread, self, i, j - i);
runtime->listAdd(thread, result, substr);
}
return *result;
}
RawObject FUNC(_builtins, _str_split)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Str self(&scope, strUnderlying(args.get(0)));
Object sep_obj(&scope, args.get(1));
word maxsplit = intUnderlying(args.get(2)).asWordSaturated();
if (sep_obj.isNoneType()) {
return strSplitWhitespace(thread, self, maxsplit);
}
Str sep(&scope, strUnderlying(*sep_obj));
if (sep.length() == 0) {
return thread->raiseWithFmt(LayoutId::kValueError, "empty separator");
}
if (maxsplit < 0) {
maxsplit = kMaxWord;
}
return strSplit(thread, self, sep, maxsplit);
}
RawObject FUNC(_builtins, _str_splitlines)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
DCHECK(runtime->isInstanceOfStr(args.get(0)),
"_str_splitlines requires 'str' instance");
DCHECK(runtime->isInstanceOfInt(args.get(1)),
"_str_splitlines requires 'int' instance");
HandleScope scope(thread);
Str self(&scope, args.get(0));
bool keepends = !intUnderlying(args.get(1)).isZero();
return strSplitlines(thread, self, keepends);
}
RawObject FUNC(_builtins, _str_startswith)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object start_obj(&scope, args.get(2));
Object end_obj(&scope, args.get(3));
Str self(&scope, strUnderlying(args.get(0)));
Str prefix(&scope, strUnderlying(args.get(1)));
word len = self.codePointLength();
word start = 0;
word end = len;
if (!start_obj.isNoneType()) {
// TODO(T55084422): bounds checking
start = intUnderlying(*start_obj).asWordSaturated();
}
if (!end_obj.isNoneType()) {
// TODO(T55084422): bounds checking
end = intUnderlying(*end_obj).asWordSaturated();
}
Slice::adjustSearchIndices(&start, &end, len);
if (start + prefix.codePointLength() > end) {
return Bool::falseObj();
}
word start_offset = thread->strOffset(self, start);
word prefix_chars = prefix.length();
for (word i = start_offset, j = 0; j < prefix_chars; i++, j++) {
if (self.byteAt(i) != prefix.byteAt(j)) {
return Bool::falseObj();
}
}
return Bool::trueObj();
}
RawObject FUNC(_builtins, _str_translate)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, strUnderlying(args.get(0)));
if (!runtime->isInstanceOfStr(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(str));
}
Str self(&scope, *self_obj);
Object table_obj(&scope, args.get(1));
if (!runtime->isInstanceOfStr(*table_obj)) {
return Unbound::object();
}
Str table(&scope, strUnderlying(*table_obj));
return strTranslateASCII(thread, self, table);
}
RawObject FUNC(_builtins, _super)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object cls(&scope, args.get(0));
Super result(&scope, thread->runtime()->newSuper());
result.setType(*cls);
result.setObject(*cls);
result.setObjectType(*cls);
return *result;
}
RawObject FUNC(_builtins, _super_ctor)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
DCHECK(args.get(0) == runtime->typeAt(LayoutId::kSuper),
"super.__new__(X): X is not 'super'");
Super self(&scope, runtime->newSuper());
Object type(&scope, args.get(1));
Object type_or_obj(&scope, args.get(2));
Frame* frame = thread->currentFrame();
// frame is for _super_ctor, previous frame is caller of super()
DCHECK(!frame->isSentinel(), "_super_ctor must have a frame");
return superInit(thread, self, type, type_or_obj, frame->previousFrame());
}
RawObject FUNC(_builtins, _traceback_frame_get)(Thread* thread,
Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isTraceback()) {
return raiseRequiresFromCaller(thread, args, ID(traceback));
}
Traceback self(&scope, *self_obj);
Object function(&scope, self.function());
Object lasti(&scope, self.lasti());
FrameProxy new_frame(&scope, runtime->newFrameProxy(thread, function, lasti));
// TODO(T91250285): Figure out a way to initialize the frame's locals dict
new_frame.setLocals(runtime->newDict());
return *new_frame;
}
RawObject FUNC(_builtins, _traceback_lineno_get)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isTraceback()) {
return raiseRequiresFromCaller(thread, args, ID(traceback));
}
Traceback self(&scope, *self_obj);
Object lineno(&scope, self.lineno());
if (!lineno.isNoneType()) {
return *lineno;
}
Function function(&scope, self.function());
Object code_obj(&scope, function.code());
if (code_obj.isCode()) {
Code code(&scope, *code_obj);
if (!code.isNative() && code.lnotab().isBytes()) {
word lasti = SmallInt::cast(self.lasti()).value();
lineno = SmallInt::fromWord(code.offsetToLineNum(lasti));
self.setLineno(*lineno);
}
}
return *lineno;
}
RawObject FUNC(_builtins, _traceback_next_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isTraceback()) {
return raiseRequiresFromCaller(thread, args, ID(traceback));
}
Traceback self(&scope, *self_obj);
return self.next();
}
RawObject FUNC(_builtins, _traceback_next_set)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isTraceback()) {
return raiseRequiresFromCaller(thread, args, ID(traceback));
}
Traceback self(&scope, *self_obj);
Object next(&scope, args.get(1));
if (next.isNoneType()) {
self.setNext(NoneType::object());
return NoneType::object();
}
if (!next.isTraceback()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"expected traceback object, got '%T", &next);
}
Object cursor(&scope, *next);
while (cursor.isTraceback()) {
if (cursor == self) {
return thread->raiseWithFmt(LayoutId::kValueError,
"traceback loop detected");
}
cursor = Traceback::cast(*cursor).next();
}
DCHECK(cursor.isNoneType(), "tb_next should be a traceback or None");
self.setNext(*next);
return NoneType::object();
}
RawObject FUNC(_builtins, _tuple_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfTuple(args.get(0)));
}
RawObject FUNC(_builtins, _tuple_check_exact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isTuple());
}
RawObject FUNC(_builtins, _tuple_getitem)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfTuple(*self_obj)) {
return raiseRequiresFromCaller(thread, args, ID(tuple));
}
Object key(&scope, args.get(1));
if (runtime->isInstanceOfInt(*key)) {
word index = intUnderlying(*key).asWordSaturated();
if (!SmallInt::isValid(index)) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"cannot fit '%T' into an index-sized integer",
&key);
}
Tuple self(&scope, tupleUnderlying(*self_obj));
word length = self.length();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
return thread->raiseWithFmt(LayoutId::kIndexError,
"tuple index out of range");
}
return self.at(index);
}
word start, stop;
if (!tryUnpackSlice(key, &start, &stop)) {
return Unbound::object();
}
Tuple self(&scope, tupleUnderlying(*self_obj));
word length = self.length();
word result_len = Slice::adjustIndices(length, &start, &stop, 1);
if (result_len == length) {
return *self;
}
return runtime->tupleSubseq(thread, self, start, result_len);
}
RawObject FUNC(_builtins, _tuple_getslice)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Tuple self(&scope, tupleUnderlying(args.get(0)));
word start = SmallInt::cast(args.get(1)).value();
word stop = SmallInt::cast(args.get(2)).value();
word step = SmallInt::cast(args.get(3)).value();
return tupleSlice(thread, self, start, stop, step);
}
RawObject FUNC(_builtins, _tuple_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfTuple(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(tuple));
}
RawObject FUNC(_builtins, _tuple_len)(Thread*, Arguments args) {
return SmallInt::fromWord(tupleUnderlying(args.get(0)).length());
}
RawObject FUNC(_builtins, _tuple_new)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
Runtime* runtime = thread->runtime();
DCHECK(type != runtime->typeAt(LayoutId::kTuple), "cls must not be tuple");
DCHECK(args.get(1).isTuple(), "old_tuple must be exact tuple");
Layout layout(&scope, type.instanceLayout());
UserTupleBase instance(&scope, runtime->newInstance(layout));
instance.setValue(args.get(1));
return *instance;
}
RawObject FUNC(_builtins, _type)(Thread* thread, Arguments args) {
return thread->runtime()->typeOf(args.get(0));
}
RawObject FUNC(_builtins, _type_ctor)(Thread* thread, Arguments args) {
DCHECK(args.get(0) == thread->runtime()->typeAt(LayoutId::kType),
"expected cls==type");
return thread->runtime()->typeOf(args.get(1));
}
RawObject FUNC(_builtins, _type_abstractmethods_del)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
if (type.abstractMethods().isUnbound()) {
Object name(&scope,
thread->runtime()->symbols()->at(ID(__abstractmethods__)));
return thread->raise(LayoutId::kAttributeError, *name);
}
type.setAbstractMethods(Unbound::object());
type.setFlagsAndBuiltinBase(
static_cast<Type::Flag>(type.flags() & ~Type::Flag::kIsAbstract),
type.builtinBase());
return NoneType::object();
}
RawObject FUNC(_builtins, _type_abstractmethods_get)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
Object methods(&scope, type.abstractMethods());
if (!methods.isUnbound()) {
return *methods;
}
Object name(&scope,
thread->runtime()->symbols()->at(ID(__abstractmethods__)));
return thread->raise(LayoutId::kAttributeError, *name);
}
RawObject FUNC(_builtins, _type_abstractmethods_set)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
Object abstract(&scope, Interpreter::isTrue(thread, args.get(1)));
if (abstract.isError()) return *abstract;
type.setAbstractMethods(args.get(1));
if (Bool::cast(*abstract).value()) {
type.setFlagsAndBuiltinBase(
static_cast<Type::Flag>(type.flags() | Type::Flag::kIsAbstract),
type.builtinBase());
}
return NoneType::object();
}
RawObject FUNC(_builtins, _type_bases_del)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
Str name(&scope, strUnderlying(type.name()));
return thread->raiseWithFmt(LayoutId::kTypeError, "can't delete %S.__bases__",
&name);
}
RawObject FUNC(_builtins, _type_bases_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
return type.bases();
}
RawObject FUNC(_builtins, _type_bases_set)(Thread*, Arguments) {
UNIMPLEMENTED("type.__bases__ setter");
}
RawObject FUNC(_builtins, _type_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfType(args.get(0)));
}
RawObject FUNC(_builtins, _type_check_exact)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isType());
}
RawObject FUNC(_builtins, _type_dunder_call)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object self_obj(&scope, args.get(0));
Tuple pargs(&scope, args.get(1));
Dict kwargs(&scope, args.get(2));
word pargs_length = pargs.length();
bool is_kwargs_empty = kwargs.numItems() == 0;
// Shortcut for type(x) calls.
if (pargs_length == 1 && is_kwargs_empty &&
self_obj == runtime->typeAt(LayoutId::kType)) {
return runtime->typeOf(pargs.at(0));
}
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"'__call__' for '%Y' objects doesn't apply to a '%T' object", ID(type),
&self_obj);
}
Type self(&scope, *self_obj);
// `instance = self.__new__(...)`
Object instance(&scope, NoneType::object());
Object call_args_obj(&scope, NoneType::object());
bool use_object_dunder_new =
self.isType() && self.hasFlag(Type::Flag::kHasObjectDunderNew);
if (use_object_dunder_new) {
// Most common case `__new__` was not overridden and is just
// `object.__new__`.
instance = objectNew(thread, self);
if (instance.isErrorException()) return *instance;
} else {
Object dunder_new(&scope, Unbound::object());
Object dunder_new_name(&scope, runtime->symbols()->at(ID(__new__)));
if (self.isType()) {
// Metaclass is "type" so we do not need to check for __new__ being a
// datadescriptor and we can look it up directly on the type.
dunder_new = typeLookupInMro(thread, *self, *dunder_new_name);
}
if (dunder_new.isStaticMethod()) {
// Next most common case `__new__` is overridden with a normal function
dunder_new = StaticMethod::cast(*dunder_new).function();
} else {
// Finally fallback to complete lookup for corner cases
dunder_new = typeGetAttribute(thread, self, dunder_new_name);
}
CHECK(!dunder_new.isError(), "self must have __new__");
thread->stackPush(*dunder_new);
if (is_kwargs_empty) {
thread->stackPush(*self);
for (word i = 0; i < pargs_length; ++i) {
thread->stackPush(pargs.at(i));
}
instance = Interpreter::call(thread, pargs_length + 1);
} else {
MutableTuple call_args(&scope,
runtime->newMutableTuple(pargs_length + 1));
call_args.atPut(0, *self);
call_args.replaceFromWith(1, *pargs, pargs_length);
thread->stackPush(call_args.becomeImmutable());
thread->stackPush(*kwargs);
instance = Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS);
call_args_obj = *call_args;
}
if (instance.isErrorException()) return *instance;
if (!typeIsSubclass(runtime->typeOf(*instance), *self)) {
return *instance;
}
}
// instance.__init__(...)
Object dunder_init_name(&scope, runtime->symbols()->at(ID(__init__)));
Object dunder_init(&scope, typeGetAttribute(thread, self, dunder_init_name));
// `object.__init__` does nothing, we may be able to just skip things.
// The exception to the rule being `object.__init__` raising errors when
// arguments are provided and nothing is overridden.
if (dunder_init != runtime->objectDunderInit() ||
(use_object_dunder_new &&
(pargs.length() != 0 || kwargs.numItems() != 0))) {
CHECK(!dunder_init.isError(), "self must have __init__");
Object result(&scope, NoneType::object());
thread->stackPush(*dunder_init);
if (is_kwargs_empty) {
thread->stackPush(*instance);
for (word i = 0; i < pargs_length; ++i) {
thread->stackPush(pargs.at(i));
}
result = Interpreter::call(thread, pargs_length + 1);
} else {
if (!call_args_obj.isMutableTuple()) {
MutableTuple call_args(&scope,
runtime->newMutableTuple(pargs_length + 1));
call_args.atPut(0, *instance);
call_args.replaceFromWith(1, *pargs, pargs_length);
call_args_obj = *call_args;
} else {
MutableTuple::cast(*call_args_obj).atPut(0, *instance);
}
thread->stackPush(*call_args_obj);
thread->stackPush(*kwargs);
result = Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS);
}
if (result.isErrorException()) return *result;
if (!result.isNoneType()) {
Object type_name(&scope, self.name());
return thread->raiseWithFmt(LayoutId::kTypeError,
"%S.__init__ returned non None", &type_name);
}
}
return *instance;
}
RawObject FUNC(_builtins, _type_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfType(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(type));
}
RawObject FUNC(_builtins, _type_issubclass)(Thread*, Arguments args) {
return Bool::fromBool(typeIsSubclass(args.get(0), args.get(1)));
}
bool FUNC(_builtins, _type_issubclass_intrinsic)(Thread* thread) {
RawObject subclass = thread->stackPeek(1);
RawObject superclass = thread->stackPeek(0);
thread->stackDrop(2);
thread->stackSetTop(Bool::fromBool(typeIsSubclass(subclass, superclass)));
return true;
}
RawObject FUNC(_builtins, _type_module_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
// If this is called on Type itself, typeAtById won't find __module__, and
// the type isn't a CPython heap type, but it should still return builtins
if (type.isBuiltin() && type.builtinBase() == LayoutId::kType) {
return runtime->symbols()->at(ID(builtins));
}
Object result(&scope, typeAtById(thread, type, ID(__module__)));
if (result.isErrorNotFound()) {
if (!type.isCPythonHeaptype()) {
return runtime->symbols()->at(ID(builtins));
}
Object name(&scope, runtime->symbols()->at(ID(__module__)));
return objectRaiseAttributeError(thread, type, name);
}
return *result;
}
RawObject FUNC(_builtins, _type_module_set)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
Object value(&scope, args.get(1));
if (!type.hasMutableDict()) {
return raiseTypeErrorCannotSetImmutable(thread, type);
}
typeAtPutById(thread, type, ID(__module__), value);
return NoneType::object();
}
RawObject FUNC(_builtins, _type_name_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
return type.name();
}
RawObject FUNC(_builtins, _type_name_set)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
if (!type.hasMutableDict()) {
return raiseTypeErrorCannotSetImmutable(thread, type);
}
Object value(&scope, args.get(1));
if (!runtime->isInstanceOfStr(*value)) {
Object type_name(&scope, type.name());
return thread->raiseWithFmt(
LayoutId::kTypeError, "can only assign string to %S.__name__, not '%T'",
&type_name, &value);
}
type.setName(*value);
return NoneType::object();
}
RawObject FUNC(_builtins, _type_new)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type metaclass(&scope, args.get(0));
Str name(&scope, strUnderlying(args.get(1)));
Tuple bases(&scope, tupleUnderlying(args.get(2)));
Dict dict(&scope, args.get(3));
Bool is_heaptype(&scope, args.get(4));
word flags = Type::Flag::kIsBasetype;
flags |=
is_heaptype.value() ? Type::Flag::kIsCPythonHeaptype : Type::Flag::kNone;
return typeNew(thread, metaclass, name, bases, dict, flags,
/*inherit_slots=*/true, /*add_instance_dict=*/true);
}
RawObject FUNC(_builtins, _type_proxy)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Type type(&scope, args.get(0));
if (type.proxy().isNoneType()) {
type.setProxy(thread->runtime()->newTypeProxy(type));
}
return type.proxy();
}
RawObject FUNC(_builtins, _type_proxy_check)(Thread*, Arguments args) {
return Bool::fromBool(args.get(0).isTypeProxy());
}
RawObject FUNC(_builtins, _type_proxy_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
TypeProxy self(&scope, args.get(0));
Object name(&scope, args.get(1));
name = attributeName(thread, name);
if (name.isErrorException()) return *name;
Object default_obj(&scope, args.get(2));
Type type(&scope, self.type());
Object result(&scope, typeAt(type, name));
if (result.isError()) {
return *default_obj;
}
return *result;
}
RawObject FUNC(_builtins, _type_proxy_guard)(Thread* thread, Arguments args) {
if (args.get(0).isTypeProxy()) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(type_proxy));
}
RawObject FUNC(_builtins, _type_proxy_keys)(Thread* thread, Arguments args) {
HandleScope scope(thread);
TypeProxy self(&scope, args.get(0));
Type type(&scope, self.type());
return typeKeys(thread, type);
}
RawObject FUNC(_builtins, _type_proxy_len)(Thread* thread, Arguments args) {
HandleScope scope(thread);
TypeProxy self(&scope, args.get(0));
Type type(&scope, self.type());
return SmallInt::fromWord(typeLen(thread, type));
}
RawObject FUNC(_builtins, _type_proxy_values)(Thread* thread, Arguments args) {
HandleScope scope(thread);
TypeProxy self(&scope, args.get(0));
Type type(&scope, self.type());
return typeValues(thread, type);
}
RawObject FUNC(_builtins, _type_qualname_get)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
return type.qualname();
}
RawObject FUNC(_builtins, _type_qualname_set)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfType(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(type));
}
Type type(&scope, *self_obj);
if (!type.hasMutableDict()) {
return raiseTypeErrorCannotSetImmutable(thread, type);
}
Object value(&scope, args.get(1));
if (!runtime->isInstanceOfStr(*value)) {
Object type_name(&scope, type.name());
return thread->raiseWithFmt(
LayoutId::kTypeError,
"can only assign string to %S.__qualname__, not '%T'", &type_name,
&value);
}
type.setQualname(*value);
return NoneType::object();
}
RawObject FUNC(_builtins, _type_subclass_guard)(Thread* thread,
Arguments args) {
RawObject subclass_obj = args.get(0);
if (!thread->runtime()->isInstanceOfType(subclass_obj)) {
return raiseRequiresFromCaller(thread, args, ID(type));
}
RawObject superclass_obj = args.get(1);
if (typeIsSubclass(subclass_obj, superclass_obj)) {
return NoneType::object();
}
HandleScope scope(thread);
Type subclass(&scope, subclass_obj);
Type superclass(&scope, superclass_obj);
Function function(&scope,
thread->currentFrame()->previousFrame()->function());
Str function_name(&scope, function.name());
Str subclass_name(&scope, subclass.name());
Str superclass_name(&scope, superclass.name());
return thread->raiseWithFmt(LayoutId::kTypeError,
"'%S': '%S' is not a subclass of '%S'",
&function_name, &subclass_name, &superclass_name);
}
RawObject FUNC(_builtins, _unimplemented)(Thread* thread, Arguments) {
HandleScope scope(thread);
// Environment override?
const char* pyro_raise_on_unimplemented =
std::getenv("PYRO_RAISE_ON_UNIMPLEMENTED");
bool raise_instead_of_abort =
(pyro_raise_on_unimplemented != nullptr &&
::strcmp(pyro_raise_on_unimplemented, "1") == 0);
// If sys.PYRO_RAISE_ON_UNIMPLEMENTED is set to a true value
if (!raise_instead_of_abort) {
Object sys_dot_pyro_raise_on_unimplemented(
&scope, thread->runtime()->lookupNameInModule(
thread, ID(sys), ID(PYRO_RAISE_ON_UNIMPLEMENTED)));
if (!sys_dot_pyro_raise_on_unimplemented.isError()) {
Object o(&scope, Interpreter::isTrue(
thread, *sys_dot_pyro_raise_on_unimplemented));
raise_instead_of_abort = (!o.isError()) && (*o == Bool::trueObj());
}
}
if (raise_instead_of_abort) {
return thread->raiseWithFmt(LayoutId::kNotImplementedError,
"overrode _unimplemented abort");
}
thread->runtime()->printTraceback(thread, File::kStderr);
// Attempt to identify the calling function.
Object function_obj(&scope,
thread->currentFrame()->previousFrame()->function());
if (!function_obj.isError()) {
Function function(&scope, *function_obj);
Str function_name(&scope, function.name());
unique_c_ptr<char> name_cstr(function_name.toCStr());
fprintf(stderr, "\n'_unimplemented' called in function '%s'\n",
name_cstr.get());
} else {
fputs("\n'_unimplemented' called.\n", stderr);
}
fputs(
"\nuse env PYRO_RAISE_ON_UNIMPLEMENTED=1 or"
"\nsys.PYRO_RAISE_ON_UNIMPLEMENTED=True to raise instead of abort.\n",
stderr);
std::abort();
}
RawObject FUNC(_builtins, _warn)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object message(&scope, args.get(0));
Object category(&scope, args.get(1));
Object stacklevel(&scope, args.get(2));
Object source(&scope, args.get(3));
return thread->invokeFunction4(ID(warnings), ID(warn), message, category,
stacklevel, source);
}
RawObject FUNC(_builtins, _weakref_callback)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfWeakRef(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(weakref));
}
WeakRef self(&scope, weakRefUnderlying(*self_obj));
Object callback(&scope, self.callback());
if (callback.isNoneType()) {
return *callback;
}
return BoundMethod::cast(*callback).function();
}
RawObject FUNC(_builtins, _weakref_check)(Thread* thread, Arguments args) {
return Bool::fromBool(thread->runtime()->isInstanceOfWeakRef(args.get(0)));
}
RawObject FUNC(_builtins, _weakref_guard)(Thread* thread, Arguments args) {
if (thread->runtime()->isInstanceOfWeakRef(args.get(0))) {
return NoneType::object();
}
return raiseRequiresFromCaller(thread, args, ID(weakref));
}
RawObject FUNC(_builtins, _weakref_referent)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfWeakRef(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(weakref));
}
WeakRef self(&scope, weakRefUnderlying(*self_obj));
return self.referent();
}
} // namespace py