this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "range-builtins.h"
3
4#include "builtins.h"
5#include "frame.h"
6#include "globals.h"
7#include "int-builtins.h"
8#include "runtime.h"
9#include "type-builtins.h"
10
11namespace py {
12
13RawObject rangeLen(Thread* thread, const Object& start_obj,
14 const Object& stop_obj, const Object& step_obj) {
15 HandleScope scope(thread);
16 Int start(&scope, intUnderlying(*start_obj));
17 Int stop(&scope, intUnderlying(*stop_obj));
18 Int step(&scope, intUnderlying(*step_obj));
19 if (!(start.isLargeInt() || stop.isLargeInt() || step.isLargeInt())) {
20 return thread->runtime()->newInt(
21 Slice::length(start.asWord(), stop.asWord(), step.asWord()));
22 }
23 word diff = start.compare(*stop);
24 if (step.isNegative()) {
25 if (diff > 0) {
26 Runtime* runtime = thread->runtime();
27 Int tmp1(&scope, runtime->intSubtract(thread, start, stop));
28 Int one(&scope, SmallInt::fromWord(1));
29 tmp1 = runtime->intSubtract(thread, tmp1, one);
30 Int tmp2(&scope, runtime->intNegate(thread, step));
31 Object quotient(&scope, NoneType::object());
32 bool division_succeeded =
33 runtime->intDivideModulo(thread, tmp1, tmp2, "ient, nullptr);
34 DCHECK(division_succeeded, "step must be nonzero");
35 tmp1 = *quotient;
36 return runtime->intAdd(thread, tmp1, one);
37 }
38 } else if (diff < 0) {
39 Runtime* runtime = thread->runtime();
40 Int tmp(&scope, runtime->intSubtract(thread, stop, start));
41 Int one(&scope, SmallInt::fromWord(1));
42 tmp = runtime->intSubtract(thread, tmp, one);
43 Object quotient(&scope, NoneType::object());
44 bool division_succeeded =
45 runtime->intDivideModulo(thread, tmp, step, "ient, nullptr);
46 DCHECK(division_succeeded, "step must be nonzero");
47 tmp = *quotient;
48 return runtime->intAdd(thread, tmp, one);
49 }
50 return SmallInt::fromWord(0);
51}
52
53RawObject rangeIteratorNext(const RangeIterator& iter) {
54 word length = iter.length();
55 if (length == 0) {
56 return Error::noMoreItems();
57 }
58 iter.setLength(length - 1);
59 word next = iter.next();
60 if (length > 1) {
61 word step = iter.step();
62 iter.setNext(next + step);
63 }
64 return SmallInt::fromWord(next);
65}
66
67RawObject METH(longrange_iterator, __iter__)(Thread* thread, Arguments args) {
68 HandleScope scope(thread);
69 Object self(&scope, args.get(0));
70 if (!self.isLongRangeIterator()) {
71 return thread->raiseRequiresType(self, ID(longrange_iterator));
72 }
73 return *self;
74}
75
76RawObject METH(longrange_iterator, __length_hint__)(Thread* thread,
77 Arguments args) {
78 HandleScope scope(thread);
79 Object self(&scope, args.get(0));
80 if (!self.isLongRangeIterator()) {
81 return thread->raiseRequiresType(self, ID(longrange_iterator));
82 }
83 LongRangeIterator iter(&scope, *self);
84 Object next(&scope, iter.next());
85 Object stop(&scope, iter.stop());
86 Object step(&scope, iter.step());
87 return rangeLen(thread, next, stop, step);
88}
89
90RawObject METH(longrange_iterator, __next__)(Thread* thread, Arguments args) {
91 HandleScope scope(thread);
92 Object self(&scope, args.get(0));
93 if (!self.isLongRangeIterator()) {
94 return thread->raiseRequiresType(self, ID(longrange_iterator));
95 }
96 LongRangeIterator iter(&scope, *self);
97 Int next(&scope, iter.next());
98 Int stop(&scope, iter.stop());
99 Int step(&scope, iter.step());
100 word diff = next.compare(*stop);
101 if ((step.isNegative() && diff <= 0) || (step.isPositive() && diff >= 0)) {
102 return thread->raise(LayoutId::kStopIteration, NoneType::object());
103 }
104 iter.setNext(thread->runtime()->intAdd(thread, next, step));
105 return *next;
106}
107
108static const BuiltinAttribute kLongRangeIteratorAttributes[] = {
109 {ID(_longrange_iterator__next), RawLongRangeIterator::kNextOffset,
110 AttributeFlags::kHidden},
111 {ID(_longrange_iterator__stop), RawLongRangeIterator::kStopOffset,
112 AttributeFlags::kHidden},
113 {ID(_longrange_iterator__step), RawLongRangeIterator::kStepOffset,
114 AttributeFlags::kHidden},
115};
116
117static const BuiltinAttribute kRangeAttributes[] = {
118 {ID(start), RawRange::kStartOffset, AttributeFlags::kReadOnly},
119 {ID(stop), RawRange::kStopOffset, AttributeFlags::kReadOnly},
120 {ID(step), RawRange::kStepOffset, AttributeFlags::kReadOnly},
121};
122
123static const BuiltinAttribute kRangeIteratorAttributes[] = {
124 {ID(_range_iterator__next), RawRangeIterator::kNextOffset,
125 AttributeFlags::kHidden},
126 {ID(_range_iterator__step), RawRangeIterator::kStepOffset,
127 AttributeFlags::kHidden},
128 {ID(_range_iterator__length), RawRangeIterator::kLengthOffset,
129 AttributeFlags::kHidden},
130};
131
132void initializeRangeTypes(Thread* thread) {
133 addBuiltinType(thread, ID(range), LayoutId::kRange,
134 /*superclass_id=*/LayoutId::kObject, kRangeAttributes,
135 Range::kSize, /*basetype=*/false);
136
137 addBuiltinType(thread, ID(range_iterator), LayoutId::kRangeIterator,
138 /*superclass_id=*/LayoutId::kObject, kRangeIteratorAttributes,
139 RangeIterator::kSize, /*basetype=*/false);
140
141 addBuiltinType(thread, ID(longrange_iterator), LayoutId::kLongRangeIterator,
142 /*superclass_id=*/LayoutId::kObject,
143 kLongRangeIteratorAttributes, LongRangeIterator::kSize,
144 /*basetype=*/false);
145}
146
147RawObject METH(range, __iter__)(Thread* thread, Arguments args) {
148 HandleScope scope(thread);
149 Object self(&scope, args.get(0));
150 if (!self.isRange()) {
151 return thread->raiseRequiresType(self, ID(range));
152 }
153 Range range(&scope, *self);
154 Object start_obj(&scope, range.start());
155 Object stop_obj(&scope, range.stop());
156 Object step_obj(&scope, range.step());
157 Int start_int(&scope, intUnderlying(*start_obj));
158 Int stop_int(&scope, intUnderlying(*stop_obj));
159 Int step_int(&scope, intUnderlying(*step_obj));
160 Runtime* runtime = thread->runtime();
161 if (start_int.isLargeInt() || stop_int.isLargeInt() ||
162 step_int.isLargeInt()) {
163 return runtime->newLongRangeIterator(start_int, stop_int, step_int);
164 }
165 word start = start_int.asWord();
166 word stop = stop_int.asWord();
167 word step = step_int.asWord();
168 word length = Slice::length(start, stop, step);
169 if (SmallInt::isValid(length)) {
170 return runtime->newRangeIterator(start, step, length);
171 }
172 return runtime->newLongRangeIterator(start_int, stop_int, step_int);
173}
174
175RawObject METH(range, __len__)(Thread* thread, Arguments args) {
176 HandleScope scope(thread);
177 Object self_obj(&scope, args.get(0));
178 if (!self_obj.isRange()) {
179 return thread->raiseRequiresType(self_obj, ID(range));
180 }
181 Range self(&scope, *self_obj);
182 Object start(&scope, self.start());
183 Object stop(&scope, self.stop());
184 Object step(&scope, self.step());
185 Int len(&scope, rangeLen(thread, start, stop, step));
186 // TODO(T55084422): Streamline int bounds checking in Pyro
187 if (len.isLargeInt() && LargeInt::cast(*len).numDigits() > 1) {
188 return thread->raiseWithFmt(LayoutId::kOverflowError,
189 "Python int too large to convert to C ssize_t");
190 }
191 return *len;
192}
193
194RawObject METH(range, __new__)(Thread* thread, Arguments args) {
195 HandleScope scope(thread);
196 Object cls(&scope, args.get(0));
197 Runtime* runtime = thread->runtime();
198 if (!runtime->isInstanceOfType(*cls)) {
199 return thread->raiseWithFmt(LayoutId::kTypeError,
200 "range.__new__(X): X is not a type object (%T)",
201 &cls);
202 }
203 Type type(&scope, *cls);
204 if (type.builtinBase() != LayoutId::kRange) {
205 Str name(&scope, type.name());
206 return thread->raiseWithFmt(
207 LayoutId::kTypeError, "range.__new__(%S): %S is not a subtype of range",
208 &name, &name);
209 }
210
211 Object start_or_stop(&scope, args.get(1));
212 Object maybe_stop(&scope, args.get(2));
213 Object maybe_step(&scope, args.get(3));
214 if (maybe_stop.isUnbound()) {
215 DCHECK(maybe_step.isUnbound(),
216 "cannot provide step without providing both start and stop");
217 Object start(&scope, SmallInt::fromWord(0));
218 Object stop(&scope, intFromIndex(thread, start_or_stop));
219 if (stop.isError()) return *stop;
220 Object step(&scope, SmallInt::fromWord(1));
221 return runtime->newRange(start, stop, step);
222 }
223
224 if (maybe_step.isUnbound()) {
225 Object start(&scope, intFromIndex(thread, start_or_stop));
226 if (start.isError()) return *start;
227 Object stop(&scope, intFromIndex(thread, maybe_stop));
228 if (stop.isError()) return *stop;
229 Object step(&scope, SmallInt::fromWord(1));
230 return runtime->newRange(start, stop, step);
231 }
232
233 Object start(&scope, intFromIndex(thread, start_or_stop));
234 if (start.isError()) return *start;
235 Object stop(&scope, intFromIndex(thread, maybe_stop));
236 if (stop.isError()) return *stop;
237 Object step(&scope, intFromIndex(thread, maybe_step));
238 if (step.isError()) return *step;
239 Int step_int(&scope, intUnderlying(*step));
240 if (step_int.isZero()) {
241 return thread->raiseWithFmt(LayoutId::kValueError,
242 "range() arg 3 must not be zero");
243 }
244 return runtime->newRange(start, stop, step);
245}
246
247RawObject METH(range_iterator, __iter__)(Thread* thread, Arguments args) {
248 HandleScope scope(thread);
249 Object self(&scope, args.get(0));
250 if (!self.isRangeIterator()) {
251 return thread->raiseRequiresType(self, ID(range_iterator));
252 }
253 return *self;
254}
255
256RawObject METH(range_iterator, __length_hint__)(Thread* thread,
257 Arguments args) {
258 HandleScope scope(thread);
259 Object self(&scope, args.get(0));
260 if (!self.isRangeIterator()) {
261 return thread->raiseRequiresType(self, ID(range_iterator));
262 }
263 RangeIterator iter(&scope, *self);
264 return SmallInt::fromWord(iter.length());
265}
266
267RawObject METH(range_iterator, __next__)(Thread* thread, Arguments args) {
268 HandleScope scope(thread);
269 Object self(&scope, args.get(0));
270 if (!self.isRangeIterator()) {
271 return thread->raiseRequiresType(self, ID(range_iterator));
272 }
273 RangeIterator iter(&scope, *self);
274 RawObject result = rangeIteratorNext(iter);
275 if (result.isErrorNoMoreItems()) {
276 return thread->raise(LayoutId::kStopIteration, NoneType::object());
277 }
278 return result;
279}
280
281} // namespace py