ext/Objects/abstract.cpp (1,532 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include <cstdarg> #include "../Python/modsupport-internal.h" #include "cpython-data.h" #include "cpython-func.h" #include "api-handle.h" #include "array-module.h" #include "attributedict.h" #include "bytearrayobject-utils.h" #include "bytesobject-utils.h" #include "capi-typeslots.h" #include "capi.h" #include "exception-builtins.h" #include "formatter.h" #include "frame.h" #include "int-builtins.h" #include "list-builtins.h" #include "object-builtins.h" #include "runtime.h" #include "type-builtins.h" namespace py { static PyObject* nullError(Thread* thread) { if (!thread->hasPendingException()) { thread->raiseWithFmt(LayoutId::kSystemError, "null argument to internal routine"); } return nullptr; } static PyObject* doUnaryOp(SymbolId op, PyObject* obj) { Thread* thread = Thread::current(); if (obj == nullptr) { return nullError(thread); } HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object result(&scope, thread->invokeFunction1(ID(operator), op, object)); return result.isError() ? nullptr : ApiHandle::newReference(thread->runtime(), *result); } static PyObject* doBinaryOp(SymbolId op, PyObject* left, PyObject* right) { Thread* thread = Thread::current(); DCHECK(left != nullptr && right != nullptr, "null argument to binary op %s", Symbols::predefinedSymbolAt(op)); HandleScope scope(thread); Object left_obj(&scope, ApiHandle::fromPyObject(left)->asObject()); Object right_obj(&scope, ApiHandle::fromPyObject(right)->asObject()); Object result(&scope, thread->invokeFunction2(ID(operator), op, left_obj, right_obj)); return result.isError() ? nullptr : ApiHandle::newReference(thread->runtime(), *result); } static Py_ssize_t objectLength(PyObject* pyobj) { Thread* thread = Thread::current(); if (pyobj == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Object len_index(&scope, thread->invokeMethod1(obj, ID(__len__))); if (len_index.isError()) { if (len_index.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "object has no len()"); } return -1; } Object len(&scope, intFromIndex(thread, len_index)); if (len.isError()) { return -1; } Int index(&scope, intUnderlying(*len)); if (index.isNegative()) { thread->raiseWithFmt(LayoutId::kValueError, "__len__() should return >= 0"); return -1; } if (index.numDigits() > 1) { thread->raiseWithFmt(LayoutId::kOverflowError, "cannot fit '%T' into an index-sized integer", &len_index); return -1; } return index.asWord(); } // Buffer Protocol static RawObject raiseBufferError(Thread* thread, const Object& obj) { return thread->raiseWithFmt( LayoutId::kTypeError, "a bytes-like object is required, not '%T'", &obj); } RawObject newBytesFromBuffer(Thread* thread, const Object& obj) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Type type(&scope, runtime->typeOf(*obj)); if (!typeHasSlots(type)) { return raiseBufferError(thread, obj); } void* get_slot = typeSlotAt(type, Py_bf_getbuffer); if (get_slot == nullptr) { return raiseBufferError(thread, obj); } Py_buffer view; int flags = PyBUF_SIMPLE; ApiHandle* handle = ApiHandle::borrowedReference(runtime, *obj); int get_result = reinterpret_cast<getbufferproc>(get_slot)(handle, &view, flags); if (get_result != 0) { return Error::exception(); } DCHECK(view.readonly, "writable buffers not supported"); DCHECK(view.ndim == 1, "multi-dimensional buffers not supported"); Bytes result(&scope, runtime->newBytesWithAll( {reinterpret_cast<byte*>(view.buf), view.len})); void* release_slot = typeSlotAt(type, Py_bf_releasebuffer); // The release slot may not be defined. That's allowed. if (release_slot != nullptr) { reinterpret_cast<releasebufferproc>(release_slot)(handle, &view); } return *result; } PY_EXPORT int PyBuffer_FillInfo(Py_buffer* view, PyObject* exporter, void* buf, Py_ssize_t len, int readonly, int flags) { if (view == nullptr) { Thread::current()->raiseWithFmt( LayoutId::kBufferError, "PyBuffer_FillInfo: view==NULL argument is obsolete"); return -1; } if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && readonly == 1) { Thread::current()->raiseWithFmt(LayoutId::kBufferError, "Object is not writable."); return -1; } if (exporter != nullptr) { Py_INCREF(exporter); } view->obj = exporter; view->buf = buf; view->len = len; view->readonly = readonly; view->itemsize = 1; view->format = nullptr; if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { view->format = const_cast<char*>("B"); } view->ndim = 1; view->shape = nullptr; if ((flags & PyBUF_ND) == PyBUF_ND) { view->shape = &(view->len); } view->strides = nullptr; if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { view->strides = &(view->itemsize); } view->suboffsets = nullptr; view->internal = nullptr; return 0; } static bool isContiguousWithRowMajorOrder(const Py_buffer* view) { if (view->suboffsets != nullptr) return false; if (view->strides == nullptr) return true; if (view->len == 0) return true; Py_ssize_t dim_stride = view->itemsize; for (int d = view->ndim - 1; d >= 0; d--) { Py_ssize_t dim_size = view->shape[d]; if (dim_size > 1 && view->strides[d] != dim_stride) { return false; } dim_stride *= dim_size; } return true; } static bool isContiguousWithColumnMajorOrder(const Py_buffer* view) { if (view->suboffsets != nullptr) return false; if (view->len == 0) return true; if (view->strides == nullptr) { if (view->ndim <= 1) return true; // Non-contiguous if there is more than 1 dimension with size > 0. bool had_nonempty_dim = false; for (int d = 0; d < view->ndim; d++) { if (view->shape[d] > 1) { if (had_nonempty_dim) return false; had_nonempty_dim = true; } } return true; } Py_ssize_t dim_stride = view->itemsize; for (int d = 0; d < view->ndim; d++) { Py_ssize_t dim_size = view->shape[d]; if (dim_size > 1 && view->strides[d] != dim_stride) { return false; } dim_stride *= dim_size; } return true; } PY_EXPORT int PyBuffer_IsContiguous(const Py_buffer* view, char order) { if (order == 'C') { return isContiguousWithRowMajorOrder(view); } if (order == 'F') { return isContiguousWithColumnMajorOrder(view); } if (order == 'A') { return isContiguousWithRowMajorOrder(view) || isContiguousWithColumnMajorOrder(view); } return false; } PY_EXPORT void PyBuffer_Release(Py_buffer* view) { DCHECK(view != nullptr, "view must not be nullptr"); PyObject* pyobj = view->obj; if (pyobj == nullptr) return; Thread* thread = Thread::current(); HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object object(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Type type(&scope, runtime->typeOf(*object)); if (typeHasSlots(type)) { // Call Py_bf_releasebuffer slot if defined void* releasebuffer_fn = PyType_GetSlot(Py_TYPE(pyobj), Py_bf_releasebuffer); if (releasebuffer_fn != nullptr) { reinterpret_cast<releasebufferproc>(releasebuffer_fn)(pyobj, view); } } view->obj = nullptr; Py_DECREF(pyobj); } // PyIndex_Check PY_EXPORT int PyIndex_Check_Func(PyObject* obj) { DCHECK(obj != nullptr, "Got null argument"); Thread* thread = Thread::current(); HandleScope scope(thread); Object num(&scope, ApiHandle::fromPyObject(obj)->asObject()); Type type(&scope, thread->runtime()->typeOf(*num)); return !typeLookupInMroById(thread, *type, ID(__index__)).isErrorNotFound(); } // PyIter_Next PY_EXPORT PyObject* PyIter_Next(PyObject* iter) { Thread* thread = Thread::current(); HandleScope scope(thread); Object iter_obj(&scope, ApiHandle::fromPyObject(iter)->asObject()); Object next(&scope, thread->invokeMethod1(iter_obj, ID(__next__))); if (thread->clearPendingStopIteration()) { // End of iterable return nullptr; } if (next.isError()) { // Method lookup or call failed if (next.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "failed to call __next__ on iterable"); } return nullptr; } return ApiHandle::newReference(thread->runtime(), *next); } // Mapping Protocol PY_EXPORT int PyMapping_Check(PyObject* py_obj) { Thread* thread = Thread::current(); HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(py_obj)->asObject()); return thread->runtime()->isMapping(thread, obj); } PY_EXPORT int PyMapping_DelItemString(PyObject* obj, const char* attr_name) { return PyObject_DelItemString(obj, attr_name); } PY_EXPORT int PyMapping_DelItem(PyObject* obj, PyObject* attr_name) { return PyObject_DelItem(obj, attr_name); } PY_EXPORT PyObject* PyMapping_GetItemString(PyObject* obj, const char* key) { Thread* thread = Thread::current(); if (obj == nullptr || key == nullptr) { return nullError(thread); } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object key_obj(&scope, runtime->newStrFromCStr(key)); Object result(&scope, objectGetItem(thread, object, key_obj)); if (result.isErrorException()) return nullptr; return ApiHandle::newReference(runtime, *result); } PY_EXPORT int PyMapping_HasKey(PyObject* obj, PyObject* key) { PyObject* v = PyObject_GetItem(obj, key); if (v != nullptr) { Py_DECREF(v); return 1; } PyErr_Clear(); return 0; } PY_EXPORT int PyMapping_HasKeyString(PyObject* obj, const char* key) { PyObject* v = PyMapping_GetItemString(obj, key); if (v != nullptr) { Py_DECREF(v); return 1; } PyErr_Clear(); return 0; } PY_EXPORT PyObject* PyMapping_Items(PyObject* mapping) { if (PyDict_CheckExact(mapping)) { return PyDict_Items(mapping); } PyObject* items = PyObject_CallMethod(mapping, "items", nullptr); if (items == nullptr) { return nullptr; } PyObject* fast = PySequence_Fast(items, "mapping.items() are not iterable"); Py_DECREF(items); return fast; } PY_EXPORT PyObject* PyMapping_Keys(PyObject* mapping) { DCHECK(mapping != nullptr, "mapping was null"); if (PyDict_CheckExact(mapping)) { return PyDict_Keys(mapping); } PyObject* keys = PyObject_CallMethod(mapping, "keys", nullptr); if (keys == nullptr) { return nullptr; } PyObject* fast = PySequence_Fast(keys, "mapping.keys() are not iterable"); Py_DECREF(keys); return fast; } PY_EXPORT Py_ssize_t PyMapping_Length(PyObject* pyobj) { return objectLength(pyobj); } PY_EXPORT int PyMapping_SetItemString(PyObject* obj, const char* key, PyObject* value) { if (key == nullptr) { nullError(Thread::current()); return -1; } PyObject* key_obj = PyUnicode_FromString(key); if (key_obj == nullptr) { return -1; } int r = PyObject_SetItem(obj, key_obj, value); Py_DECREF(key_obj); return r; } PY_EXPORT Py_ssize_t PyMapping_Size(PyObject* pyobj) { return objectLength(pyobj); } PY_EXPORT PyObject* PyMapping_Values(PyObject* mapping) { if (PyDict_CheckExact(mapping)) { return PyDict_Values(mapping); } PyObject* values = PyObject_CallMethod(mapping, "values", nullptr); if (values == nullptr) { return nullptr; } PyObject* fast = PySequence_Fast(values, "mapping.values() are not iterable"); Py_DECREF(values); return fast; } // Number Protocol PY_EXPORT PyObject* PyNumber_Absolute(PyObject* obj) { return doUnaryOp(ID(abs), obj); } static PyObject* smallIntAdd(PyObject* left, PyObject* right) { RawObject left_obj = ApiHandle::fromPyObject(left)->asObject(); RawObject right_obj = ApiHandle::fromPyObject(right)->asObject(); if (left_obj.isSmallInt() && right_obj.isSmallInt()) { Runtime* runtime = Thread::current()->runtime(); return ApiHandle::newReference( runtime, runtime->newInt(SmallInt::cast(left_obj).value() + SmallInt::cast(right_obj).value())); } return nullptr; } PY_EXPORT PyObject* PyNumber_Add(PyObject* left, PyObject* right) { PyObject* result = smallIntAdd(left, right); if (result != nullptr) { // Fast path: smallint + smallint. return result; } return doBinaryOp(ID(add), left, right); } PY_EXPORT PyObject* PyNumber_And(PyObject* left, PyObject* right) { return doBinaryOp(ID(and_), left, right); } PY_EXPORT Py_ssize_t PyNumber_AsSsize_t(PyObject* obj, PyObject* overflow_err) { Thread* thread = Thread::current(); if (obj == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object index(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object num(&scope, intFromIndex(thread, index)); if (num.isError()) return -1; Int number(&scope, intUnderlying(*num)); if (overflow_err == nullptr || number.numDigits() == 1) { // Overflows should be clipped, or value is already in range. return number.asWordSaturated(); } // Value overflows, raise an exception. thread->setPendingExceptionType( ApiHandle::fromPyObject(overflow_err)->asObject()); thread->setPendingExceptionValue(thread->runtime()->newStrFromFmt( "cannot fit '%T' into an index-sized integer", &index)); return -1; } PY_EXPORT int PyNumber_Check(PyObject* obj) { if (obj == nullptr) { return false; } Thread* thread = Thread::current(); HandleScope scope(thread); Object num(&scope, ApiHandle::fromPyObject(obj)->asObject()); Type type(&scope, thread->runtime()->typeOf(*num)); if (!typeLookupInMroById(thread, *type, ID(__int__)).isErrorNotFound()) { return true; } if (!typeLookupInMroById(thread, *type, ID(__float__)).isErrorNotFound()) { return true; } return false; } PY_EXPORT PyObject* PyNumber_Divmod(PyObject* left, PyObject* right) { return doBinaryOp(ID(divmod), left, right); } PY_EXPORT PyObject* PyNumber_Float(PyObject* obj) { Thread* thread = Thread::current(); if (obj == nullptr) { return nullError(thread); } HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object flt(&scope, thread->invokeFunction1(ID(builtins), ID(float), object)); return flt.isError() ? nullptr : ApiHandle::newReference(thread->runtime(), *flt); } PY_EXPORT PyObject* PyNumber_FloorDivide(PyObject* left, PyObject* right) { return doBinaryOp(ID(floordiv), left, right); } PY_EXPORT PyObject* PyNumber_Index(PyObject* item) { Thread* thread = Thread::current(); if (item == nullptr) { return nullError(thread); } HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(item)->asObject()); Object index(&scope, intFromIndex(thread, obj)); return index.isError() ? nullptr : ApiHandle::newReference(thread->runtime(), *index); } PY_EXPORT PyObject* PyNumber_InPlaceAdd(PyObject* left, PyObject* right) { PyObject* result = smallIntAdd(left, right); if (result != nullptr) { // Fast path: smallint + smallint. // In case operands are SmallInts, InPlaceAdd doesn't mutate them. return result; } return doBinaryOp(ID(iadd), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceAnd(PyObject* left, PyObject* right) { return doBinaryOp(ID(iand), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceFloorDivide(PyObject* left, PyObject* right) { return doBinaryOp(ID(ifloordiv), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceLshift(PyObject* left, PyObject* right) { return doBinaryOp(ID(ilshift), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceMatrixMultiply(PyObject* left, PyObject* right) { return doBinaryOp(ID(imatmul), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceMultiply(PyObject* left, PyObject* right) { return doBinaryOp(ID(imul), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceOr(PyObject* left, PyObject* right) { return doBinaryOp(ID(ior), left, right); } PY_EXPORT PyObject* PyNumber_InPlacePower(PyObject* base, PyObject* exponent, PyObject* divisor) { if (divisor == Py_None) { return doBinaryOp(ID(ipow), base, exponent); } UNIMPLEMENTED("ipow(base, exponent, divisor)"); } PY_EXPORT PyObject* PyNumber_InPlaceRemainder(PyObject* left, PyObject* right) { return doBinaryOp(ID(imod), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceRshift(PyObject* left, PyObject* right) { return doBinaryOp(ID(irshift), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceSubtract(PyObject* left, PyObject* right) { return doBinaryOp(ID(isub), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceTrueDivide(PyObject* left, PyObject* right) { return doBinaryOp(ID(itruediv), left, right); } PY_EXPORT PyObject* PyNumber_InPlaceXor(PyObject* left, PyObject* right) { return doBinaryOp(ID(ixor), left, right); } PY_EXPORT PyObject* PyNumber_Invert(PyObject* pyobj) { return doUnaryOp(ID(invert), pyobj); } PY_EXPORT PyObject* PyNumber_Long(PyObject* obj) { Thread* thread = Thread::current(); if (obj == nullptr) { return nullError(thread); } HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object result(&scope, thread->invokeFunction1(ID(builtins), ID(int), object)); if (result.isError()) { return nullptr; } return ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT PyObject* PyNumber_Lshift(PyObject* left, PyObject* right) { return doBinaryOp(ID(lshift), left, right); } PY_EXPORT PyObject* PyNumber_MatrixMultiply(PyObject* left, PyObject* right) { return doBinaryOp(ID(matmul), left, right); } PY_EXPORT PyObject* PyNumber_Multiply(PyObject* left, PyObject* right) { return doBinaryOp(ID(mul), left, right); } PY_EXPORT PyObject* PyNumber_Negative(PyObject* pyobj) { return doUnaryOp(ID(neg), pyobj); } PY_EXPORT PyObject* PyNumber_Or(PyObject* left, PyObject* right) { return doBinaryOp(ID(or_), left, right); } PY_EXPORT PyObject* PyNumber_Positive(PyObject* pyobj) { return doUnaryOp(ID(pos), pyobj); } PY_EXPORT PyObject* PyNumber_Power(PyObject* base, PyObject* exponent, PyObject* divisor) { if (divisor == Py_None) { return doBinaryOp(ID(pow), base, exponent); } UNIMPLEMENTED("pow(base, exponent, divisor)"); } PY_EXPORT PyObject* PyNumber_Remainder(PyObject* left, PyObject* right) { return doBinaryOp(ID(mod), left, right); } PY_EXPORT PyObject* PyNumber_Rshift(PyObject* left, PyObject* right) { return doBinaryOp(ID(rshift), left, right); } PY_EXPORT PyObject* PyNumber_Subtract(PyObject* left, PyObject* right) { return doBinaryOp(ID(sub), left, right); } PY_EXPORT PyObject* PyNumber_ToBase(PyObject* n, int base) { Thread* thread = Thread::current(); HandleScope scope(thread); Object n_object(&scope, ApiHandle::fromPyObject(n)->asObject()); n_object = intFromIndex(thread, n_object); if (n_object.isError()) { return nullptr; } Int number(&scope, intUnderlying(*n_object)); Object formatted(&scope, NoneType::object()); switch (base) { case 2: formatted = formatIntBinarySimple(thread, number); break; case 8: formatted = formatIntOctalSimple(thread, number); break; case 10: formatted = formatIntDecimalSimple(thread, number); break; case 16: formatted = formatIntHexadecimalSimple(thread, number); break; default: thread->raiseWithFmt(LayoutId::kSystemError, "PyNumber_ToBase: base must be 2, 8, 10 or 16"); return nullptr; } return ApiHandle::newReference(thread->runtime(), *formatted); } PY_EXPORT PyObject* PyNumber_TrueDivide(PyObject* left, PyObject* right) { return doBinaryOp(ID(truediv), left, right); } PY_EXPORT PyObject* PyNumber_Xor(PyObject* left, PyObject* right) { return doBinaryOp(ID(xor), left, right); } // Object Protocol PY_EXPORT int PyObject_AsCharBuffer(PyObject* /* j */, const char** /* buffer */, Py_ssize_t* /* n */) { UNIMPLEMENTED("PyObject_AsCharBuffer"); } PY_EXPORT int PyObject_AsReadBuffer(PyObject* /* j */, const void** /* buffer */, Py_ssize_t* /* n */) { UNIMPLEMENTED("PyObject_AsReadBuffer"); } PY_EXPORT int PyObject_AsWriteBuffer(PyObject* /* j */, void** /* buffer */, Py_ssize_t* /* n */) { UNIMPLEMENTED("PyObject_AsWriteBuffer"); } PY_EXPORT PyObject* PyObject_Call(PyObject* callable, PyObject* args, PyObject* kwargs) { Thread* thread = Thread::current(); if (callable == nullptr) { return nullError(thread); } DCHECK(!thread->hasPendingException(), "may accidentally clear pending exception"); HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object callable_obj(&scope, ApiHandle::fromPyObject(callable)->asObject()); Type callable_type(&scope, runtime->typeOf(*callable_obj)); if (typeHasSlots(callable_type)) { // Attempt to call tp_call directly for native types to avoid // recursive interpreter calls. void* tp_call_value = typeSlotAt(callable_type, Py_tp_call); if (tp_call_value != nullptr) { ternaryfunc call = reinterpret_cast<ternaryfunc>(tp_call_value); return call(callable, args, kwargs); } } thread->stackPush(*callable_obj); Object args_obj(&scope, ApiHandle::fromPyObject(args)->asObject()); DCHECK(runtime->isInstanceOfTuple(*args_obj), "args mut be a tuple"); thread->stackPush(*args_obj); word flags = 0; if (kwargs != nullptr) { Object kwargs_obj(&scope, ApiHandle::fromPyObject(kwargs)->asObject()); DCHECK(thread->runtime()->isInstanceOfDict(*kwargs_obj), "kwargs must be a dict"); thread->stackPush(*kwargs_obj); flags |= CallFunctionExFlag::VAR_KEYWORDS; } // TODO(T30925218): Protect against native stack overflow. Object result(&scope, Interpreter::callEx(thread, flags)); if (result.isError()) return nullptr; return ApiHandle::newReference(runtime, *result); } static PyObject* makeInterpreterCall(Thread* thread, word nargs) { HandleScope scope(thread); Object result(&scope, Interpreter::call(thread, nargs)); if (result.isError()) return nullptr; return ApiHandle::newReference(thread->runtime(), *result); } static PyObject* callWithVarArgs(Thread* thread, const Object& callable, const char* format, std::va_list* va, int build_value_flags) { thread->stackPush(*callable); if (format == nullptr) { return makeInterpreterCall(thread, /*nargs=*/0); } word nargs = countFormat(format, '\0'); if (nargs == 1) { PyObject* value = makeValueFromFormat(&format, va, build_value_flags); if (!PyTuple_Check(value)) { thread->stackPush(ApiHandle::stealReference(value)); return makeInterpreterCall(thread, nargs); } // If the only argument passed is a tuple, splat the tuple as positional // arguments nargs = PyTuple_Size(value); for (word i = 0; i < nargs; i++) { PyObject* arg = PyTuple_GetItem(value, i); thread->stackPush(ApiHandle::fromPyObject(arg)->asObject()); } return makeInterpreterCall(thread, nargs); } for (const char* f = format; *f != '\0';) { PyObject* value = makeValueFromFormat(&f, va, build_value_flags); if (value == nullptr) break; thread->stackPush(ApiHandle::stealReference(value)); } return makeInterpreterCall(thread, nargs); } static PyObject* callFunction(PyObject* callable, const char* format, std::va_list* va) { Thread* thread = Thread::current(); if (callable == nullptr) { return nullError(thread); } HandleScope scope(thread); Object callable_obj(&scope, ApiHandle::fromPyObject(callable)->asObject()); PyObject* result = callWithVarArgs(thread, callable_obj, format, va, 0); return result; } PY_EXPORT PyObject* PyObject_CallFunction(PyObject* callable, const char* format, ...) { va_list va; va_start(va, format); PyObject* result = callFunction(callable, format, &va); va_end(va); return result; } PY_EXPORT PyObject* PyEval_CallFunction(PyObject* callable, const char* format, ...) { va_list va; va_start(va, format); PyObject* result = callFunction(callable, format, &va); va_end(va); return result; } static PyObject* callWithObjArgs(Thread* thread, const Object& callable, std::va_list va) { DCHECK(!thread->hasPendingException(), "may accidentally clear pending exception"); thread->stackPush(*callable); word nargs = 0; for (PyObject* arg; (arg = va_arg(va, PyObject*)) != nullptr; nargs++) { thread->stackPush(ApiHandle::fromPyObject(arg)->asObject()); } // TODO(T30925218): CPython tracks recursive calls before calling the function // through Py_EnterRecursiveCall, and we should probably do the same HandleScope scope(thread); Object result(&scope, Interpreter::call(thread, nargs)); if (result.isError()) return nullptr; return ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, ...) { Thread* thread = Thread::current(); if (callable == nullptr) { return nullError(thread); } HandleScope scope(thread); Object callable_obj(&scope, ApiHandle::fromPyObject(callable)->asObject()); va_list va; va_start(va, callable); PyObject* result = callWithObjArgs(thread, callable_obj, va); va_end(va); return result; } PY_EXPORT PyObject* _PyObject_CallFunction_SizeT(PyObject* callable, const char* format, ...) { Thread* thread = Thread::current(); if (callable == nullptr) { return nullError(thread); } HandleScope scope(thread); Object callable_obj(&scope, ApiHandle::fromPyObject(callable)->asObject()); va_list va; va_start(va, format); PyObject* result = callWithVarArgs(thread, callable_obj, format, &va, kFlagSizeT); va_end(va); return result; } static PyObject* callMethod(PyObject* pyobj, const char* name, const char* format, std::va_list* va) { Thread* thread = Thread::current(); if (pyobj == nullptr) { return nullError(thread); } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object obj(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Object callable(&scope, runtime->attributeAtByCStr(thread, obj, name)); if (callable.isError()) return nullptr; PyObject* result = callWithVarArgs(thread, callable, format, va, 0); return result; } PY_EXPORT PyObject* PyObject_CallMethod(PyObject* pyobj, const char* name, const char* format, ...) { va_list va; va_start(va, format); PyObject* result = callMethod(pyobj, name, format, &va); va_end(va); return result; } PY_EXPORT PyObject* PyEval_CallMethod(PyObject* pyobj, const char* name, const char* format, ...) { va_list va; va_start(va, format); PyObject* result = callMethod(pyobj, name, format, &va); va_end(va); return result; } PY_EXPORT PyObject* PyObject_CallMethodObjArgs(PyObject* pyobj, PyObject* py_method_name, ...) { Thread* thread = Thread::current(); if (pyobj == nullptr || py_method_name == nullptr) { return nullError(thread); } HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Object name(&scope, ApiHandle::fromPyObject(py_method_name)->asObject()); name = attributeName(thread, name); if (name.isErrorException()) return nullptr; Object callable(&scope, thread->runtime()->attributeAt(thread, obj, name)); if (callable.isError()) return nullptr; va_list va; va_start(va, py_method_name); PyObject* result = callWithObjArgs(thread, callable, va); va_end(va); return result; } PY_EXPORT PyObject* _PyObject_CallMethod_SizeT(PyObject* pyobj, const char* name, const char* format, ...) { Thread* thread = Thread::current(); if (pyobj == nullptr) { return nullError(thread); } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object obj(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Object callable(&scope, runtime->attributeAtByCStr(thread, obj, name)); if (callable.isError()) return nullptr; va_list va; va_start(va, format); PyObject* result = callWithVarArgs(thread, callable, format, &va, kFlagSizeT); va_end(va); return result; } PY_EXPORT PyObject* PyObject_CallObject(PyObject* callable, PyObject* args) { Thread* thread = Thread::current(); if (callable == nullptr) { return nullError(thread); } DCHECK(!thread->hasPendingException(), "may accidentally clear pending exception"); HandleScope scope(thread); thread->stackPush(ApiHandle::fromPyObject(callable)->asObject()); Object result(&scope, NoneType::object()); Runtime* runtime = thread->runtime(); if (args != nullptr) { Object args_obj(&scope, ApiHandle::fromPyObject(args)->asObject()); if (!runtime->isInstanceOfTuple(*args_obj)) { thread->raiseWithFmt(LayoutId::kTypeError, "argument list must be a tuple"); return nullptr; } thread->stackPush(*args_obj); // TODO(T30925218): Protect against native stack overflow. result = Interpreter::callEx(thread, 0); } else { result = Interpreter::call(thread, 0); } if (result.isError()) return nullptr; return ApiHandle::newReference(runtime, *result); } PY_EXPORT int PyObject_CheckBuffer_Func(PyObject* pyobj) { // TODO(T38246066): Collapse all the cases into Runtime::isByteslike and make // this function a small wrapper around that Thread* thread = Thread::current(); HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Runtime* runtime = thread->runtime(); if (runtime->isInstanceOfBytes(*obj) || runtime->isInstanceOfBytearray(*obj)) { return true; } if (runtime->isByteslike(*obj)) { UNIMPLEMENTED("PyObject_CheckBuffer with builtin byteslike"); } Type type(&scope, runtime->typeOf(*obj)); if (type.isBuiltin()) return false; if (!typeHasSlots(type)) return false; return typeSlotAt(type, Py_bf_getbuffer) != nullptr; } PY_EXPORT int PyObject_CheckReadBuffer(PyObject* /* j */) { UNIMPLEMENTED("PyObject_CheckReadBuffer"); } PY_EXPORT int PyObject_DelItem(PyObject* obj, PyObject* key) { Thread* thread = Thread::current(); if (obj == nullptr || key == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object key_obj(&scope, ApiHandle::fromPyObject(key)->asObject()); Object result(&scope, objectDelItem(thread, object, key_obj)); if (result.isErrorException()) { return -1; } return 0; } PY_EXPORT int PyObject_DelItemString(PyObject* obj, const char* key) { Thread* thread = Thread::current(); if (obj == nullptr || key == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object key_obj(&scope, runtime->newStrFromCStr(key)); Object result(&scope, objectDelItem(thread, object, key_obj)); if (result.isErrorException()) { return -1; } return 0; } PY_EXPORT PyObject* _PyObject_CallNoArg(PyObject* callable) { return _PyObject_FastCall(callable, nullptr, 0); } PY_EXPORT PyObject* _PyObject_FastCall(PyObject* callable, PyObject** pyargs, Py_ssize_t n_args) { return _PyObject_FastCallDict(callable, pyargs, n_args, nullptr); } PY_EXPORT PyObject* _PyObject_FastCallDict(PyObject* callable, PyObject** pyargs, Py_ssize_t n_args, PyObject* kwargs) { DCHECK(callable != nullptr, "callable must not be nullptr"); Thread* thread = Thread::current(); DCHECK(!thread->hasPendingException(), "may accidentally clear pending exception"); DCHECK(n_args >= 0, "n_args must not be negative"); HandleScope scope(thread); thread->stackPush(ApiHandle::fromPyObject(callable)->asObject()); DCHECK(n_args == 0 || pyargs != nullptr, "Args array must not be nullptr"); Object result(&scope, NoneType::object()); Runtime* runtime = thread->runtime(); if (kwargs != nullptr) { Object args_obj(&scope, NoneType::object()); if (n_args > 0) { MutableTuple args(&scope, runtime->newMutableTuple(n_args)); for (Py_ssize_t i = 0; i < n_args; i++) { args.atPut(i, ApiHandle::fromPyObject(pyargs[i])->asObject()); } args_obj = args.becomeImmutable(); } else { args_obj = runtime->emptyTuple(); } thread->stackPush(*args_obj); Object kwargs_obj(&scope, ApiHandle::fromPyObject(kwargs)->asObject()); DCHECK(runtime->isInstanceOfDict(*kwargs_obj), "kwargs must be a dict"); thread->stackPush(*kwargs_obj); // TODO(T30925218): Protect against native stack overflow. result = Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS); } else { for (Py_ssize_t i = 0; i < n_args; i++) { thread->stackPush(ApiHandle::fromPyObject(pyargs[i])->asObject()); } // TODO(T30925218): Protect against native stack overflow. result = Interpreter::call(thread, n_args); } if (result.isError()) return nullptr; return ApiHandle::newReference(runtime, *result); } PY_EXPORT PyObject* _PyObject_FastCallKeywords(PyObject* /* e */, PyObject** /* k */, Py_ssize_t /* s */, PyObject* /* s */) { UNIMPLEMENTED("_PyObject_FastCallKeywords"); } PY_EXPORT PyObject* PyObject_Format(PyObject* obj, PyObject* format_spec) { DCHECK(obj != nullptr, "obj should not be null"); Thread* thread = Thread::current(); HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object format_spec_obj( &scope, format_spec == nullptr ? Str::empty() : ApiHandle::fromPyObject(format_spec)->asObject()); Object result(&scope, thread->invokeFunction2(ID(builtins), ID(format), object, format_spec_obj)); if (result.isError()) { return nullptr; } return ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT int PyObject_GetBuffer(PyObject* obj, Py_buffer* view, int flags) { DCHECK(obj != nullptr, "obj must not be nullptr"); Thread* thread = Thread::current(); ApiHandle* handle = ApiHandle::fromPyObject(obj); HandleScope scope(thread); Object obj_obj(&scope, handle->asObject()); Runtime* runtime = thread->runtime(); if (runtime->isInstanceOfBytes(*obj_obj)) { Bytes bytes(&scope, bytesUnderlying(*obj_obj)); char* buffer = bytesAsString(runtime, handle, bytes); if (buffer == nullptr) { return -1; } return PyBuffer_FillInfo(view, handle, buffer, bytes.length(), /*readonly=*/1, flags); } if (runtime->isInstanceOfBytearray(*obj_obj)) { // TODO(T54579154): This creates a copy of the object which does not stay // in sync. We should have a way to pin the memory to allow direct access. Bytearray array(&scope, *obj_obj); char* buffer = bytearrayAsString(runtime, handle, array); if (buffer == nullptr) { return -1; } return PyBuffer_FillInfo(view, handle, buffer, array.numItems(), /*readonly=*/1, flags); } if (obj_obj.isMemoryView()) { MemoryView memoryview(&scope, *obj_obj); Object buffer(&scope, memoryview.buffer()); // A MemoryView's underlying buffer is either a bytes object or a raw // pointer. if (runtime->isInstanceOfBytes(*buffer)) { Bytes bytes(&scope, bytesUnderlying(*obj_obj)); // We use the memoryview handle's cache directly to store the buffer. char* underlying_buffer = bytesAsString(runtime, handle, bytes); if (underlying_buffer == nullptr) { return -1; } return PyBuffer_FillInfo(view, handle, underlying_buffer, memoryview.length(), /*readonly=*/1, flags); } Pointer underlying_pointer(&scope, *buffer); char* underlying_buffer = reinterpret_cast<char*>(underlying_pointer.cptr()); return PyBuffer_FillInfo(view, handle, underlying_buffer, memoryview.length(), /*readonly=*/1, flags); } if (runtime->isInstanceOfArray(*obj_obj)) { Array array(&scope, *obj_obj); word length = arrayByteLength(*array); // We create a copy of the array's buffer and place it in the API handle's // cache to ensure it gets reaped. if (void* cache = handle->cache(runtime)) { std::free(cache); } byte* buffer = static_cast<byte*>(std::malloc(length + 1)); if (buffer == nullptr) { return -1; } MutableBytes::cast(array.buffer()).copyTo(buffer, length); handle->setCache(runtime, buffer); return PyBuffer_FillInfo(view, handle, reinterpret_cast<char*>(buffer), length, /*readonly=*/1, flags); } // We must be dealing with a buffer protocol or an incompatible type. Type type(&scope, runtime->typeOf(*obj_obj)); if (type.isBuiltin()) { raiseBufferError(thread, obj_obj); return -1; } if (!typeHasSlots(type)) { raiseBufferError(thread, obj_obj); return -1; } void* slot = typeSlotAt(type, Py_bf_getbuffer); if (slot == nullptr) { raiseBufferError(thread, obj_obj); return -1; } return reinterpret_cast<getbufferproc>(slot)(handle, view, flags); } PY_EXPORT PyObject* PyObject_GetItem(PyObject* obj, PyObject* key) { Thread* thread = Thread::current(); if (obj == nullptr || key == nullptr) { return nullError(thread); } HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object key_obj(&scope, ApiHandle::fromPyObject(key)->asObject()); Object result(&scope, objectGetItem(thread, object, key_obj)); if (result.isErrorException()) return nullptr; return ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT PyObject* PyObject_GetIter(PyObject* pyobj) { Thread* thread = Thread::current(); HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(pyobj)->asObject()); Object result(&scope, Interpreter::createIterator(thread, obj)); if (result.isError()) { return nullptr; } return ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT int PyObject_IsInstance(PyObject* instance, PyObject* cls) { Thread* thread = Thread::current(); HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(instance)->asObject()); Object classinfo(&scope, ApiHandle::fromPyObject(cls)->asObject()); Object result(&scope, thread->invokeFunction2(ID(builtins), ID(isinstance), object, classinfo)); return result.isError() ? -1 : Bool::cast(*result).value(); } PY_EXPORT int PyObject_IsSubclass(PyObject* derived, PyObject* cls) { Thread* thread = Thread::current(); HandleScope scope(thread); Object subclass(&scope, ApiHandle::fromPyObject(derived)->asObject()); Object classinfo(&scope, ApiHandle::fromPyObject(cls)->asObject()); Object result(&scope, thread->invokeFunction2(ID(builtins), ID(issubclass), subclass, classinfo)); return result.isError() ? -1 : Bool::cast(*result).value(); } PY_EXPORT Py_ssize_t PyObject_Length(PyObject* pyobj) { return objectLength(pyobj); } PY_EXPORT Py_ssize_t PyObject_LengthHint(PyObject* obj, Py_ssize_t default_value) { Py_ssize_t res = objectLength(obj); Thread* thread = Thread::current(); Runtime* runtime = thread->runtime(); HandleScope scope(thread); if (res < 0 && thread->hasPendingException()) { Object given_obj(&scope, thread->pendingExceptionType()); Object exc_obj(&scope, runtime->typeAt(LayoutId::kTypeError)); if (!givenExceptionMatches(thread, given_obj, exc_obj)) { return -1; } // Catch TypeError when obj does not have __len__. thread->clearPendingException(); } else { return res; } Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object length_hint(&scope, thread->invokeMethod1(object, ID(__length_hint__))); if (length_hint.isErrorNotFound() || length_hint.isNotImplementedType()) { return default_value; } if (length_hint.isError()) { return -1; } if (!thread->runtime()->isInstanceOfInt(*length_hint)) { thread->raiseWithFmt(LayoutId::kTypeError, "__length_hint__ must be an integer, not %T", &length_hint); return -1; } Int index(&scope, intUnderlying(*length_hint)); if (!index.isSmallInt()) { thread->raiseWithFmt(LayoutId::kOverflowError, "cannot fit '%T' into an index-sized integer", &length_hint); return -1; } if (index.isNegative()) { thread->raiseWithFmt(LayoutId::kValueError, "__len__() should return >= 0"); return -1; } return index.asWord(); } PY_EXPORT int PyObject_SetItem(PyObject* obj, PyObject* key, PyObject* value) { Thread* thread = Thread::current(); if (obj == nullptr || key == nullptr || value == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object key_obj(&scope, ApiHandle::fromPyObject(key)->asObject()); Object value_obj(&scope, ApiHandle::fromPyObject(value)->asObject()); Object result(&scope, objectSetItem(thread, object, key_obj, value_obj)); return result.isErrorException() ? -1 : 0; } PY_EXPORT Py_ssize_t PyObject_Size(PyObject* pyobj) { return objectLength(pyobj); } PY_EXPORT PyTypeObject* Py_TYPE_Func(PyObject* pyobj) { Thread* thread = Thread::current(); if (pyobj == nullptr) { nullError(thread); return nullptr; } Runtime* runtime = thread->runtime(); return reinterpret_cast<PyTypeObject*>(ApiHandle::borrowedReference( runtime, runtime->typeOf(ApiHandle::fromPyObject(pyobj)->asObject()))); } PY_EXPORT void Py_SET_TYPE_Func(PyObject* obj, PyTypeObject* type) { DCHECK(obj != nullptr, "obj must be non-null"); DCHECK(type != nullptr, "type must be non-null"); Thread* thread = Thread::current(); HandleScope scope(thread); Object self(&scope, ApiHandle::fromPyObject(obj)->asObject()); Type new_type(&scope, ApiHandle::fromPyTypeObject(type)->asObject()); Object result(&scope, typeSetDunderClass(thread, self, new_type)); if (result.isError()) { UNIMPLEMENTED("unhandled case in __class__ setter"); } } PY_EXPORT PyObject* PyObject_Type(PyObject* pyobj) { Thread* thread = Thread::current(); if (pyobj == nullptr) { return nullError(thread); } Runtime* runtime = thread->runtime(); return ApiHandle::newReference( runtime, runtime->typeOf(ApiHandle::fromPyObject(pyobj)->asObject())); } PY_EXPORT const char* PyObject_TypeName(PyObject* /* obj */) { UNIMPLEMENTED("PyObject_TypeName"); } // Sequence Protocol PY_EXPORT void _Py_FreeCharPArray(char* const array[]) { for (Py_ssize_t i = 0; array[i] != nullptr; ++i) { PyMem_Free(array[i]); } PyMem_Free(const_cast<char**>(array)); } PY_EXPORT char* const* _PySequence_BytesToCharpArray(PyObject* self) { Py_ssize_t argc = PySequence_Size(self); if (argc < 0) { DCHECK(argc == -1, "size cannot be negative (-1 denotes an error)"); return nullptr; } if (argc > (kMaxWord / kPointerSize) - 1) { PyErr_NoMemory(); return nullptr; } char** result = static_cast<char**>(PyMem_Malloc((argc + 1) * kPointerSize)); if (result == nullptr) { PyErr_NoMemory(); return nullptr; } for (Py_ssize_t i = 0; i < argc; ++i) { PyObject* item = PySequence_GetItem(self, i); if (item == nullptr) { // NULL terminate before freeing. result[i] = nullptr; _Py_FreeCharPArray(result); return nullptr; } char* data; if (PyBytes_AsStringAndSize(item, &data, nullptr) < 0) { // NULL terminate before freeing. result[i] = nullptr; Py_DECREF(item); _Py_FreeCharPArray(result); return nullptr; } Py_ssize_t size = PyBytes_GET_SIZE(item) + 1; result[i] = static_cast<char*>(PyMem_Malloc(size)); if (result[i] == nullptr) { PyErr_NoMemory(); Py_DECREF(item); _Py_FreeCharPArray(result); return nullptr; } std::memcpy(result[i], data, size); Py_DECREF(item); } result[argc] = nullptr; return result; } PY_EXPORT int PySequence_Check(PyObject* py_obj) { Thread* thread = Thread::current(); HandleScope scope(thread); Object obj(&scope, ApiHandle::fromPyObject(py_obj)->asObject()); return thread->runtime()->isSequence(thread, obj); } PY_EXPORT PyObject* PySequence_Concat(PyObject* left, PyObject* right) { Thread* thread = Thread::current(); if (left == nullptr || right == nullptr) { return nullError(thread); } if (!PySequence_Check(left) || !PySequence_Check(right)) { thread->raiseWithFmt(LayoutId::kTypeError, "objects cannot be concatenated"); return nullptr; } return PyNumber_Add(left, right); } PY_EXPORT int PySequence_Contains(PyObject* seq, PyObject* obj) { Thread* thread = Thread::current(); if (seq == nullptr || obj == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object result(&scope, thread->invokeFunction2(ID(operator), ID(contains), seq_obj, object)); if (result.isError()) { return -1; } return Bool::cast(*result).value() ? 1 : 0; } PY_EXPORT Py_ssize_t PySequence_Count(PyObject* seq, PyObject* obj) { Thread* thread = Thread::current(); if (seq == nullptr || obj == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object result(&scope, thread->invokeFunction2(ID(operator), ID(countOf), seq_obj, object)); if (result.isError()) { return -1; } return SmallInt::cast(*result).value(); } PY_EXPORT int PySequence_DelItem(PyObject* seq, Py_ssize_t idx) { Thread* thread = Thread::current(); if (seq == nullptr) { return -1; } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object idx_obj(&scope, thread->runtime()->newInt(idx)); Object result(&scope, thread->invokeMethod2(seq_obj, ID(__delitem__), idx_obj)); if (result.isError()) { return -1; } return 0; } static RawObject makeSlice(Thread* thread, Py_ssize_t low, Py_ssize_t high) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object start(&scope, runtime->newInt(low)); Object stop(&scope, runtime->newInt(high)); Object step(&scope, NoneType::object()); return runtime->newSlice(start, stop, step); } PY_EXPORT int PySequence_DelSlice(PyObject* seq, Py_ssize_t low, Py_ssize_t high) { Thread* thread = Thread::current(); if (seq == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object slice(&scope, makeSlice(thread, low, high)); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object result(&scope, thread->invokeMethod2(seq_obj, ID(__delitem__), slice)); if (result.isError()) { if (result.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "object does not support slice deletion"); } return -1; } return 0; } PY_EXPORT PyObject* PySequence_Fast(PyObject* seq, const char* msg) { Thread* thread = Thread::current(); if (seq == nullptr) { return nullError(thread); } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Runtime* runtime = thread->runtime(); if (seq_obj.isList() || seq_obj.isTuple()) { return ApiHandle::newReference(runtime, *seq_obj); } Object iter(&scope, Interpreter::createIterator(thread, seq_obj)); if (iter.isError()) { Object given(&scope, thread->pendingExceptionType()); Object exc(&scope, runtime->typeAt(LayoutId::kTypeError)); if (givenExceptionMatches(thread, given, exc)) { thread->setPendingExceptionValue(runtime->newStrFromCStr(msg)); } return nullptr; } Object result(&scope, thread->invokeFunction1(ID(builtins), ID(list), seq_obj)); if (result.isError()) { return nullptr; } return ApiHandle::newReference(runtime, *result); } PY_EXPORT Py_ssize_t PySequence_Fast_GET_SIZE_Func(PyObject* seq) { return PyList_Check(seq) ? PyList_GET_SIZE(seq) : PyTuple_GET_SIZE(seq); } PY_EXPORT PyObject* PySequence_Fast_GET_ITEM_Func(PyObject* seq, Py_ssize_t idx) { return PyList_Check(seq) ? PyList_GET_ITEM(seq, idx) : PyTuple_GET_ITEM(seq, idx); } PY_EXPORT PyObject* PySequence_GetItem(PyObject* seq, Py_ssize_t idx) { Thread* thread = Thread::current(); if (seq == nullptr) { return nullError(thread); } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); if (seq_obj.isTuple()) { // Fast path: return `tuple`'s element directly. RawTuple tuple = Tuple::cast(*seq_obj); if (0 <= idx && idx < tuple.length()) { return ApiHandle::newReference(runtime, tuple.at(idx)); } } else if (seq_obj.isList()) { // Fast path: return `list`'s element directly. RawList list = List::cast(*seq_obj); if (0 <= idx && idx < list.numItems()) { return ApiHandle::newReference(runtime, list.at(idx)); } } Object idx_obj(&scope, thread->runtime()->newInt(idx)); Object result(&scope, thread->invokeMethod2(seq_obj, ID(__getitem__), idx_obj)); if (result.isError()) { if (result.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "could not call __getitem__"); } return nullptr; } return ApiHandle::newReference(runtime, *result); } PY_EXPORT PyObject* PySequence_ITEM_Func(PyObject* seq, Py_ssize_t i) { DCHECK(seq != nullptr, "sequence must not be nullptr"); DCHECK(i >= 0, "index can't be negative"); Thread* thread = Thread::current(); HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Runtime* runtime = thread->runtime(); DCHECK(runtime->isSequence(thread, seq_obj), "seq must be a sequence"); Object idx(&scope, runtime->newInt(i)); Object result(&scope, thread->invokeMethod2(seq_obj, ID(__getitem__), idx)); if (result.isError()) return nullptr; return ApiHandle::newReference(runtime, *result); } PY_EXPORT PyObject* PySequence_GetSlice(PyObject* seq, Py_ssize_t low, Py_ssize_t high) { Thread* thread = Thread::current(); if (seq == nullptr) { return nullError(thread); } HandleScope scope(thread); Object slice(&scope, makeSlice(thread, low, high)); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object result(&scope, thread->invokeMethod2(seq_obj, ID(__getitem__), slice)); if (result.isError()) { if (result.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "could not call __getitem__"); } return nullptr; } return ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT int PySequence_In(PyObject* pyseq, PyObject* pyobj) { return PySequence_Contains(pyseq, pyobj); } PY_EXPORT Py_ssize_t PySequence_Index(PyObject* seq, PyObject* obj) { Thread* thread = Thread::current(); if (seq == nullptr || obj == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); Object result(&scope, thread->invokeFunction2(ID(operator), ID(indexOf), seq_obj, object)); if (result.isError()) { return -1; } return SmallInt::cast(*result).value(); } PY_EXPORT PyObject* PySequence_InPlaceConcat(PyObject* left, PyObject* right) { Thread* thread = Thread::current(); if (left == nullptr || right == nullptr) { return nullError(thread); } HandleScope scope(thread); Object left_obj(&scope, ApiHandle::fromPyObject(left)->asObject()); Object right_obj(&scope, ApiHandle::fromPyObject(right)->asObject()); Object result(&scope, thread->invokeFunction2(ID(operator), ID(iconcat), left_obj, right_obj)); return result.isError() ? nullptr : ApiHandle::newReference(thread->runtime(), *result); } PY_EXPORT PyObject* PySequence_InPlaceRepeat(PyObject* seq, Py_ssize_t count) { Thread* thread = Thread::current(); if (seq == nullptr) { return nullError(thread); } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object sequence(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object count_obj(&scope, runtime->newInt(count)); Object result(&scope, thread->invokeFunction2(ID(operator), ID(irepeat), sequence, count_obj)); return result.isError() ? nullptr : ApiHandle::newReference(runtime, *result); } PY_EXPORT Py_ssize_t PySequence_Length(PyObject* pyobj) { return objectLength(pyobj); } PY_EXPORT PyObject* PySequence_List(PyObject* seq) { Thread* thread = Thread::current(); if (seq == nullptr) { return nullError(thread); } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); RawObject result = thread->invokeFunction1(ID(builtins), ID(list), seq_obj); return result.isError() ? nullptr : ApiHandle::newReference(thread->runtime(), result); } PY_EXPORT PyObject* PySequence_Repeat(PyObject* pyseq, Py_ssize_t count) { Thread* thread = Thread::current(); if (pyseq == nullptr) { return nullError(thread); } if (!PySequence_Check(pyseq)) { thread->raiseWithFmt(LayoutId::kTypeError, "object cannot be repeated"); return nullptr; } PyObject* count_obj(PyLong_FromSsize_t(count)); PyObject* result = PyNumber_Multiply(pyseq, count_obj); Py_DECREF(count_obj); return result; } PY_EXPORT int PySequence_SetItem(PyObject* seq, Py_ssize_t idx, PyObject* obj) { Thread* thread = Thread::current(); if (seq == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object idx_obj(&scope, thread->runtime()->newInt(idx)); Object result(&scope, NoneType::object()); if (obj == nullptr) { // Equivalent to PySequence_DelItem result = thread->invokeMethod2(seq_obj, ID(__delitem__), idx_obj); } else { Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); result = thread->invokeMethod3(seq_obj, ID(__setitem__), idx_obj, object); } if (result.isError()) { if (result.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "object is not subscriptable"); } return -1; } return 0; } PY_EXPORT int PySequence_SetSlice(PyObject* seq, Py_ssize_t low, Py_ssize_t high, PyObject* obj) { Thread* thread = Thread::current(); if (seq == nullptr) { nullError(thread); return -1; } HandleScope scope(thread); Object slice(&scope, makeSlice(thread, low, high)); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Object result(&scope, NoneType::object()); if (obj == nullptr) { result = thread->invokeMethod2(seq_obj, ID(__delitem__), slice); } else { Object object(&scope, ApiHandle::fromPyObject(obj)->asObject()); result = thread->invokeMethod3(seq_obj, ID(__setitem__), slice, object); } if (result.isError()) { if (result.isErrorNotFound()) { thread->raiseWithFmt(LayoutId::kTypeError, "object does not support slice assignment"); } return -1; } return 0; } PY_EXPORT Py_ssize_t PySequence_Size(PyObject* pyobj) { return objectLength(pyobj); } PY_EXPORT PyObject* PySequence_Tuple(PyObject* seq) { Thread* thread = Thread::current(); if (seq == nullptr) { return nullError(thread); } HandleScope scope(thread); Object seq_obj(&scope, ApiHandle::fromPyObject(seq)->asObject()); Runtime* runtime = thread->runtime(); if (seq_obj.isTuple()) { return ApiHandle::newReference(runtime, *seq_obj); } Object result(&scope, thread->invokeFunction1(ID(builtins), ID(tuple), seq_obj)); if (result.isError()) { return nullptr; } return ApiHandle::newReference(runtime, *result); } } // namespace py