runtime/tuple-builtins.cpp (282 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "tuple-builtins.h"
#include "builtins.h"
#include "dict-builtins.h"
#include "frame.h"
#include "globals.h"
#include "int-builtins.h"
#include "interpreter.h"
#include "object-builtins.h"
#include "objects.h"
#include "runtime.h"
#include "slice-builtins.h"
#include "thread.h"
#include "type-builtins.h"
namespace py {
RawObject tupleIteratorNext(Thread* thread, const TupleIterator& iter) {
word idx = iter.index();
if (idx == iter.length()) {
return Error::noMoreItems();
}
HandleScope scope(thread);
Tuple underlying(&scope, iter.iterable());
RawObject item = underlying.at(idx);
iter.setIndex(idx + 1);
return item;
}
RawObject tupleSlice(Thread* thread, const Tuple& tuple, word start, word stop,
word step) {
if (start == 0 && stop >= tuple.length() && step == 1) {
return *tuple;
}
HandleScope scope(thread);
word length = Slice::length(start, stop, step);
Runtime* runtime = thread->runtime();
if (length == 0) {
return runtime->emptyTuple();
}
MutableTuple items(&scope, runtime->newMutableTuple(length));
for (word i = 0, index = start; i < length; i++, index += step) {
items.atPut(i, tuple.at(index));
}
return items.becomeImmutable();
}
RawObject tupleHash(Thread* thread, const Tuple& tuple) {
HandleScope scope(thread);
Object elt(&scope, NoneType::object());
Object elt_hash(&scope, NoneType::object());
uword result = 0x345678UL;
uword mult = 1000003UL; // 0xf4243
word len = tuple.length();
for (word i = len - 1; i >= 0; i--) {
elt = tuple.at(i);
elt_hash = Interpreter::hash(thread, elt);
if (elt_hash.isErrorException()) return *elt_hash;
word hash_result = SmallInt::cast(*elt_hash).value();
result = (result ^ hash_result) * mult;
mult += word{82520} + len + len;
}
result += 97531UL;
if (result == kMaxUword) {
return SmallInt::fromWord(-2);
}
return SmallInt::fromWordTruncated(result);
}
static const BuiltinAttribute kUserTupleBaseAttributes[] = {
{ID(_UserTuple__value), RawUserTupleBase::kValueOffset,
AttributeFlags::kHidden},
};
static const BuiltinAttribute kTupleIteratorAttributes[] = {
{ID(_tuple_iterator__iterable), RawTupleIterator::kIterableOffset,
AttributeFlags::kHidden},
{ID(_tuple_iterator__index), RawTupleIterator::kIndexOffset,
AttributeFlags::kHidden},
{ID(_tuple_iterator__length), RawTupleIterator::kLengthOffset,
AttributeFlags::kHidden},
};
void initializeTupleTypes(Thread* thread) {
addBuiltinType(thread, ID(tuple), LayoutId::kTuple,
/*superclass_id=*/LayoutId::kObject, kUserTupleBaseAttributes,
UserTupleBase::kSize, /*basetype=*/true);
addBuiltinType(thread, ID(tuple_iterator), LayoutId::kTupleIterator,
/*superclass_id=*/LayoutId::kObject, kTupleIteratorAttributes,
TupleIterator::kSize, /*basetype=*/false);
}
RawObject METH(tuple, __add__)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object lhs(&scope, args.get(0));
if (!runtime->isInstanceOfTuple(*lhs)) {
return thread->raiseRequiresType(lhs, ID(tuple));
}
Tuple left(&scope, tupleUnderlying(*lhs));
Object rhs(&scope, args.get(1));
if (!runtime->isInstanceOfTuple(*rhs)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"can only concatenate tuple to tuple, got %T",
&rhs);
}
Tuple right(&scope, tupleUnderlying(*rhs));
word llength = left.length();
word rlength = right.length();
word new_length = llength + rlength;
if (new_length > SmallInt::kMaxValue) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"cannot fit 'int' into an index-sized integer");
}
if (new_length == 0) {
return runtime->emptyTuple();
}
MutableTuple new_tuple(&scope, runtime->newMutableTuple(new_length));
new_tuple.replaceFromWith(0, *left, llength);
new_tuple.replaceFromWith(llength, *right, rlength);
return new_tuple.becomeImmutable();
}
RawObject tupleContains(Thread* thread, const Tuple& tuple,
const Object& value) {
for (word i = 0, num_items = tuple.length(); i < num_items; ++i) {
RawObject eq = Runtime::objectEquals(thread, *value, tuple.at(i));
if (eq != Bool::falseObj()) return eq;
}
return Bool::falseObj();
}
RawObject METH(tuple, __contains__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!thread->runtime()->isInstanceOfTuple(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(tuple));
}
Tuple self(&scope, tupleUnderlying(*self_obj));
Object value(&scope, args.get(1));
return tupleContains(thread, self, value);
}
RawObject METH(tuple, __hash__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfTuple(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(tuple));
}
Tuple self(&scope, tupleUnderlying(*self_obj));
return tupleHash(thread, self);
}
RawObject METH(tuple, __len__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfTuple(*obj)) {
return thread->raiseRequiresType(obj, ID(tuple));
}
Tuple self(&scope, tupleUnderlying(*obj));
return runtime->newInt(self.length());
}
RawObject METH(tuple, __mul__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfTuple(*self_obj)) {
return thread->raiseRequiresType(self_obj, ID(tuple));
}
Tuple self(&scope, tupleUnderlying(*self_obj));
Object rhs(&scope, args.get(1));
Object rhs_index(&scope, intFromIndex(thread, rhs));
if (rhs_index.isError()) return *rhs_index;
Int right(&scope, intUnderlying(*rhs_index));
if (right.isLargeInt()) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"cannot fit '%T' into an index-sized integer",
&rhs);
}
word length = self.length();
word times = right.asWord();
if (length == 0 || times <= 0) {
return runtime->emptyTuple();
}
if (times == 1) {
return *self;
}
word new_length = length * times;
// If the new length overflows, raise an OverflowError.
if ((new_length / length) != times) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"cannot fit 'int' into an index-sized integer");
}
MutableTuple new_tuple(&scope, runtime->newMutableTuple(new_length));
if (length == 1) {
// Fast path for single-element tuples
new_tuple.fill(self.at(0));
return new_tuple.becomeImmutable();
}
for (word i = 0; i < times; i++) {
new_tuple.replaceFromWith(i * length, *self, length);
}
return new_tuple.becomeImmutable();
}
RawObject METH(tuple, __iter__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
Runtime* runtime = thread->runtime();
if (!runtime->isInstanceOfTuple(*self)) {
return thread->raiseRequiresType(self, ID(tuple));
}
Tuple tuple(&scope, tupleUnderlying(*self));
return runtime->newTupleIterator(tuple, tuple.length());
}
RawObject METH(tuple_iterator, __iter__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isTupleIterator()) {
return thread->raiseRequiresType(self, ID(tuple_iterator));
}
return *self;
}
RawObject METH(tuple_iterator, __next__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isTupleIterator()) {
return thread->raiseRequiresType(self_obj, ID(tuple_iterator));
}
TupleIterator self(&scope, *self_obj);
Object value(&scope, tupleIteratorNext(thread, self));
if (value.isError()) {
return thread->raise(LayoutId::kStopIteration, NoneType::object());
}
return *value;
}
RawObject METH(tuple_iterator, __length_hint__)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isTupleIterator()) {
return thread->raiseRequiresType(self, ID(tuple_iterator));
}
TupleIterator tuple_iterator(&scope, *self);
Tuple tuple(&scope, tuple_iterator.iterable());
return SmallInt::fromWord(tuple.length() - tuple_iterator.index());
}
RawObject METH(tuple_iterator, __reduce__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isTupleIterator()) {
return thread->raiseRequiresType(self, ID(tuple_iterator));
}
TupleIterator tuple_iterator(&scope, *self);
Tuple tuple(&scope, tuple_iterator.iterable());
// __reduce__ returns a 3-tuple
// * A callable object to recreate the tuple iterator
// * A tuple of arguments to pass to the recreate function
// * An argument to be passed to __setstate__ (see below)
Runtime* runtime = thread->runtime();
Object iter(&scope,
runtime->lookupNameInModule(thread, ID(builtins), ID(iter)));
if (iter.isError()) {
return thread->raiseWithFmt(LayoutId::kAttributeError,
"expected __builtins__.iter to exist");
}
Object index(&scope, SmallInt::fromWord(tuple_iterator.index()));
Object newargs(&scope, runtime->newTupleWith1(tuple));
return runtime->newTupleWith3(iter, newargs, index);
}
RawObject METH(tuple_iterator, __setstate__)(Thread* thread, Arguments args) {
// tupleiter check
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isTupleIterator()) {
return thread->raiseRequiresType(self, ID(tuple_iterator));
}
TupleIterator tuple_iterator(&scope, *self);
// Argument must be an integer
if (!thread->runtime()->isInstanceOfInt(args.get(1))) {
return thread->raiseWithFmt(LayoutId::kTypeError, "an integer is required");
}
// cpython restricted to ints that fit in ssize_t
Int idx(&scope, intUnderlying(args.get(1)));
OptInt<ssize_t> idx_opt = idx.asInt<ssize_t>();
if (idx_opt.error != CastError::None) {
return thread->raiseWithFmt(LayoutId::kOverflowError,
"Python int too large to convert to C ssize_t");
}
// cpython underflows to 0 and overflows to length
if (idx_opt.value <= 0) {
tuple_iterator.setIndex(0);
} else {
Tuple tuple(&scope, tuple_iterator.iterable());
word length = tuple.length();
if (idx_opt.value > length) {
tuple_iterator.setIndex(length);
} else {
tuple_iterator.setIndex(idx_opt.value);
}
}
return NoneType::object();
}
} // namespace py