this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "tuple-builtins.h"
3
4#include "builtins.h"
5#include "dict-builtins.h"
6#include "frame.h"
7#include "globals.h"
8#include "int-builtins.h"
9#include "interpreter.h"
10#include "object-builtins.h"
11#include "objects.h"
12#include "runtime.h"
13#include "slice-builtins.h"
14#include "thread.h"
15#include "type-builtins.h"
16
17namespace py {
18
19RawObject tupleIteratorNext(Thread* thread, const TupleIterator& iter) {
20 word idx = iter.index();
21 if (idx == iter.length()) {
22 return Error::noMoreItems();
23 }
24 HandleScope scope(thread);
25 Tuple underlying(&scope, iter.iterable());
26 RawObject item = underlying.at(idx);
27 iter.setIndex(idx + 1);
28 return item;
29}
30
31RawObject tupleSlice(Thread* thread, const Tuple& tuple, word start, word stop,
32 word step) {
33 if (start == 0 && stop >= tuple.length() && step == 1) {
34 return *tuple;
35 }
36
37 HandleScope scope(thread);
38 word length = Slice::length(start, stop, step);
39 Runtime* runtime = thread->runtime();
40 if (length == 0) {
41 return runtime->emptyTuple();
42 }
43 MutableTuple items(&scope, runtime->newMutableTuple(length));
44 for (word i = 0, index = start; i < length; i++, index += step) {
45 items.atPut(i, tuple.at(index));
46 }
47 return items.becomeImmutable();
48}
49
50RawObject tupleHash(Thread* thread, const Tuple& tuple) {
51 HandleScope scope(thread);
52 Object elt(&scope, NoneType::object());
53 Object elt_hash(&scope, NoneType::object());
54 uword result = 0x345678UL;
55 uword mult = 1000003UL; // 0xf4243
56 word len = tuple.length();
57 for (word i = len - 1; i >= 0; i--) {
58 elt = tuple.at(i);
59 elt_hash = Interpreter::hash(thread, elt);
60 if (elt_hash.isErrorException()) return *elt_hash;
61 word hash_result = SmallInt::cast(*elt_hash).value();
62 result = (result ^ hash_result) * mult;
63 mult += word{82520} + len + len;
64 }
65 result += 97531UL;
66 if (result == kMaxUword) {
67 return SmallInt::fromWord(-2);
68 }
69 return SmallInt::fromWordTruncated(result);
70}
71
72static const BuiltinAttribute kUserTupleBaseAttributes[] = {
73 {ID(_UserTuple__value), RawUserTupleBase::kValueOffset,
74 AttributeFlags::kHidden},
75};
76
77static const BuiltinAttribute kTupleIteratorAttributes[] = {
78 {ID(_tuple_iterator__iterable), RawTupleIterator::kIterableOffset,
79 AttributeFlags::kHidden},
80 {ID(_tuple_iterator__index), RawTupleIterator::kIndexOffset,
81 AttributeFlags::kHidden},
82 {ID(_tuple_iterator__length), RawTupleIterator::kLengthOffset,
83 AttributeFlags::kHidden},
84};
85
86void initializeTupleTypes(Thread* thread) {
87 addBuiltinType(thread, ID(tuple), LayoutId::kTuple,
88 /*superclass_id=*/LayoutId::kObject, kUserTupleBaseAttributes,
89 UserTupleBase::kSize, /*basetype=*/true);
90
91 addBuiltinType(thread, ID(tuple_iterator), LayoutId::kTupleIterator,
92 /*superclass_id=*/LayoutId::kObject, kTupleIteratorAttributes,
93 TupleIterator::kSize, /*basetype=*/false);
94}
95
96RawObject METH(tuple, __add__)(Thread* thread, Arguments args) {
97 Runtime* runtime = thread->runtime();
98 HandleScope scope(thread);
99 Object lhs(&scope, args.get(0));
100 if (!runtime->isInstanceOfTuple(*lhs)) {
101 return thread->raiseRequiresType(lhs, ID(tuple));
102 }
103 Tuple left(&scope, tupleUnderlying(*lhs));
104 Object rhs(&scope, args.get(1));
105 if (!runtime->isInstanceOfTuple(*rhs)) {
106 return thread->raiseWithFmt(LayoutId::kTypeError,
107 "can only concatenate tuple to tuple, got %T",
108 &rhs);
109 }
110 Tuple right(&scope, tupleUnderlying(*rhs));
111 word llength = left.length();
112 word rlength = right.length();
113
114 word new_length = llength + rlength;
115 if (new_length > SmallInt::kMaxValue) {
116 return thread->raiseWithFmt(LayoutId::kOverflowError,
117 "cannot fit 'int' into an index-sized integer");
118 }
119 if (new_length == 0) {
120 return runtime->emptyTuple();
121 }
122 MutableTuple new_tuple(&scope, runtime->newMutableTuple(new_length));
123 new_tuple.replaceFromWith(0, *left, llength);
124 new_tuple.replaceFromWith(llength, *right, rlength);
125 return new_tuple.becomeImmutable();
126}
127
128RawObject tupleContains(Thread* thread, const Tuple& tuple,
129 const Object& value) {
130 for (word i = 0, num_items = tuple.length(); i < num_items; ++i) {
131 RawObject eq = Runtime::objectEquals(thread, *value, tuple.at(i));
132 if (eq != Bool::falseObj()) return eq;
133 }
134 return Bool::falseObj();
135}
136
137RawObject METH(tuple, __contains__)(Thread* thread, Arguments args) {
138 HandleScope scope(thread);
139 Object self_obj(&scope, args.get(0));
140 if (!thread->runtime()->isInstanceOfTuple(*self_obj)) {
141 return thread->raiseRequiresType(self_obj, ID(tuple));
142 }
143 Tuple self(&scope, tupleUnderlying(*self_obj));
144 Object value(&scope, args.get(1));
145 return tupleContains(thread, self, value);
146}
147
148RawObject METH(tuple, __hash__)(Thread* thread, Arguments args) {
149 HandleScope scope(thread);
150 Object self_obj(&scope, args.get(0));
151 Runtime* runtime = thread->runtime();
152 if (!runtime->isInstanceOfTuple(*self_obj)) {
153 return thread->raiseRequiresType(self_obj, ID(tuple));
154 }
155 Tuple self(&scope, tupleUnderlying(*self_obj));
156 return tupleHash(thread, self);
157}
158
159RawObject METH(tuple, __len__)(Thread* thread, Arguments args) {
160 HandleScope scope(thread);
161 Object obj(&scope, args.get(0));
162 Runtime* runtime = thread->runtime();
163 if (!runtime->isInstanceOfTuple(*obj)) {
164 return thread->raiseRequiresType(obj, ID(tuple));
165 }
166 Tuple self(&scope, tupleUnderlying(*obj));
167 return runtime->newInt(self.length());
168}
169
170RawObject METH(tuple, __mul__)(Thread* thread, Arguments args) {
171 HandleScope scope(thread);
172 Object self_obj(&scope, args.get(0));
173 Runtime* runtime = thread->runtime();
174 if (!runtime->isInstanceOfTuple(*self_obj)) {
175 return thread->raiseRequiresType(self_obj, ID(tuple));
176 }
177 Tuple self(&scope, tupleUnderlying(*self_obj));
178 Object rhs(&scope, args.get(1));
179 Object rhs_index(&scope, intFromIndex(thread, rhs));
180 if (rhs_index.isError()) return *rhs_index;
181 Int right(&scope, intUnderlying(*rhs_index));
182 if (right.isLargeInt()) {
183 return thread->raiseWithFmt(LayoutId::kOverflowError,
184 "cannot fit '%T' into an index-sized integer",
185 &rhs);
186 }
187 word length = self.length();
188 word times = right.asWord();
189 if (length == 0 || times <= 0) {
190 return runtime->emptyTuple();
191 }
192 if (times == 1) {
193 return *self;
194 }
195
196 word new_length = length * times;
197 // If the new length overflows, raise an OverflowError.
198 if ((new_length / length) != times) {
199 return thread->raiseWithFmt(LayoutId::kOverflowError,
200 "cannot fit 'int' into an index-sized integer");
201 }
202
203 MutableTuple new_tuple(&scope, runtime->newMutableTuple(new_length));
204 if (length == 1) {
205 // Fast path for single-element tuples
206 new_tuple.fill(self.at(0));
207 return new_tuple.becomeImmutable();
208 }
209 for (word i = 0; i < times; i++) {
210 new_tuple.replaceFromWith(i * length, *self, length);
211 }
212 return new_tuple.becomeImmutable();
213}
214
215RawObject METH(tuple, __iter__)(Thread* thread, Arguments args) {
216 HandleScope scope(thread);
217 Object self(&scope, args.get(0));
218 Runtime* runtime = thread->runtime();
219 if (!runtime->isInstanceOfTuple(*self)) {
220 return thread->raiseRequiresType(self, ID(tuple));
221 }
222 Tuple tuple(&scope, tupleUnderlying(*self));
223 return runtime->newTupleIterator(tuple, tuple.length());
224}
225
226RawObject METH(tuple_iterator, __iter__)(Thread* thread, Arguments args) {
227 HandleScope scope(thread);
228 Object self(&scope, args.get(0));
229 if (!self.isTupleIterator()) {
230 return thread->raiseRequiresType(self, ID(tuple_iterator));
231 }
232 return *self;
233}
234
235RawObject METH(tuple_iterator, __next__)(Thread* thread, Arguments args) {
236 HandleScope scope(thread);
237 Object self_obj(&scope, args.get(0));
238 if (!self_obj.isTupleIterator()) {
239 return thread->raiseRequiresType(self_obj, ID(tuple_iterator));
240 }
241 TupleIterator self(&scope, *self_obj);
242 Object value(&scope, tupleIteratorNext(thread, self));
243 if (value.isError()) {
244 return thread->raise(LayoutId::kStopIteration, NoneType::object());
245 }
246 return *value;
247}
248
249RawObject METH(tuple_iterator, __length_hint__)(Thread* thread,
250 Arguments args) {
251 HandleScope scope(thread);
252 Object self(&scope, args.get(0));
253 if (!self.isTupleIterator()) {
254 return thread->raiseRequiresType(self, ID(tuple_iterator));
255 }
256 TupleIterator tuple_iterator(&scope, *self);
257 Tuple tuple(&scope, tuple_iterator.iterable());
258 return SmallInt::fromWord(tuple.length() - tuple_iterator.index());
259}
260
261RawObject METH(tuple_iterator, __reduce__)(Thread* thread, Arguments args) {
262 HandleScope scope(thread);
263 Object self(&scope, args.get(0));
264 if (!self.isTupleIterator()) {
265 return thread->raiseRequiresType(self, ID(tuple_iterator));
266 }
267 TupleIterator tuple_iterator(&scope, *self);
268 Tuple tuple(&scope, tuple_iterator.iterable());
269
270 // __reduce__ returns a 3-tuple
271 // * A callable object to recreate the tuple iterator
272 // * A tuple of arguments to pass to the recreate function
273 // * An argument to be passed to __setstate__ (see below)
274 Runtime* runtime = thread->runtime();
275 Object iter(&scope,
276 runtime->lookupNameInModule(thread, ID(builtins), ID(iter)));
277 if (iter.isError()) {
278 return thread->raiseWithFmt(LayoutId::kAttributeError,
279 "expected __builtins__.iter to exist");
280 }
281 Object index(&scope, SmallInt::fromWord(tuple_iterator.index()));
282 Object newargs(&scope, runtime->newTupleWith1(tuple));
283 return runtime->newTupleWith3(iter, newargs, index);
284}
285
286RawObject METH(tuple_iterator, __setstate__)(Thread* thread, Arguments args) {
287 // tupleiter check
288 HandleScope scope(thread);
289 Object self(&scope, args.get(0));
290 if (!self.isTupleIterator()) {
291 return thread->raiseRequiresType(self, ID(tuple_iterator));
292 }
293 TupleIterator tuple_iterator(&scope, *self);
294
295 // Argument must be an integer
296 if (!thread->runtime()->isInstanceOfInt(args.get(1))) {
297 return thread->raiseWithFmt(LayoutId::kTypeError, "an integer is required");
298 }
299
300 // cpython restricted to ints that fit in ssize_t
301 Int idx(&scope, intUnderlying(args.get(1)));
302 OptInt<ssize_t> idx_opt = idx.asInt<ssize_t>();
303 if (idx_opt.error != CastError::None) {
304 return thread->raiseWithFmt(LayoutId::kOverflowError,
305 "Python int too large to convert to C ssize_t");
306 }
307
308 // cpython underflows to 0 and overflows to length
309 if (idx_opt.value <= 0) {
310 tuple_iterator.setIndex(0);
311 } else {
312 Tuple tuple(&scope, tuple_iterator.iterable());
313 word length = tuple.length();
314 if (idx_opt.value > length) {
315 tuple_iterator.setIndex(length);
316 } else {
317 tuple_iterator.setIndex(idx_opt.value);
318 }
319 }
320 return NoneType::object();
321}
322
323} // namespace py