this repo has no description
at trunk 592 lines 20 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "list-builtins.h" 3 4#include "builtins.h" 5#include "frame.h" 6#include "globals.h" 7#include "int-builtins.h" 8#include "objects.h" 9#include "runtime.h" 10#include "slice-builtins.h" 11#include "thread.h" 12#include "tuple-builtins.h" 13#include "type-builtins.h" 14 15namespace py { 16 17void listExtend(Thread* thread, const List& dst, const Tuple& src, 18 word src_length) { 19 if (src_length == 0) { 20 return; 21 } 22 23 word old_length = dst.numItems(); 24 word new_length = old_length + src_length; 25 thread->runtime()->listEnsureCapacity(thread, dst, new_length); 26 dst.setNumItems(new_length); 27 MutableTuple::cast(dst.items()).replaceFromWith(old_length, *src, src_length); 28} 29 30void listInsert(Thread* thread, const List& list, const Object& value, 31 word index) { 32 thread->runtime()->listAdd(thread, list, value); 33 word last_index = list.numItems() - 1; 34 if (index < 0) { 35 index = last_index + index; 36 } 37 index = Utils::maximum(word{0}, Utils::minimum(last_index, index)); 38 // Shift elements over to the right 39 list.replaceFromWithStartAt(index + 1, *list, last_index - index, index); 40 list.atPut(index, *value); 41} 42 43RawObject listPop(Thread* thread, const List& list, word index) { 44 HandleScope scope(thread); 45 Object popped(&scope, list.at(index)); 46 list.atPut(index, NoneType::object()); 47 word last_index = list.numItems() - 1; 48 if (index < last_index) { 49 list.replaceFromWithStartAt(index, *list, last_index - index, index + 1); 50 } 51 list.setNumItems(last_index); 52 return *popped; 53} 54 55RawObject listReplicate(Thread* thread, const List& list, word ntimes) { 56 Runtime* runtime = thread->runtime(); 57 word len = list.numItems(); 58 word result_len = ntimes * len; 59 if (result_len == 0) { 60 return runtime->newList(); 61 } 62 HandleScope scope(thread); 63 Tuple list_items(&scope, list.items()); 64 MutableTuple items(&scope, runtime->newMutableTuple(result_len)); 65 for (word i = 0; i < result_len; i += len) { 66 items.replaceFromWith(i, *list_items, len); 67 } 68 List result(&scope, runtime->newList()); 69 result.setItems(*items); 70 result.setNumItems(result_len); 71 return *result; 72} 73 74void listReverse(Thread* thread, const List& list) { 75 if (list.numItems() == 0) { 76 return; 77 } 78 HandleScope scope(thread); 79 Object elt(&scope, NoneType::object()); 80 for (word low = 0, high = list.numItems() - 1; low < high; ++low, --high) { 81 elt = list.at(low); 82 list.atPut(low, list.at(high)); 83 list.atPut(high, *elt); 84 } 85} 86 87RawObject listSlice(Thread* thread, const List& list, word start, word stop, 88 word step) { 89 Runtime* runtime = thread->runtime(); 90 word length = Slice::length(start, stop, step); 91 if (length == 0) { 92 return runtime->newList(); 93 } 94 95 HandleScope scope(thread); 96 MutableTuple items(&scope, runtime->newMutableTuple(length)); 97 Tuple src(&scope, list.items()); 98 for (word i = 0, j = start; i < length; i++, j += step) { 99 items.atPut(i, src.at(j)); 100 } 101 List result(&scope, runtime->newList()); 102 result.setItems(*items); 103 result.setNumItems(length); 104 return *result; 105} 106 107// TODO(T63900795): Investigate this threshold on a realistic benchmark. 108static const int kListInsertionSortSize = 8; 109 110static RawObject objectLessThan(Thread* thread, const Object& left, 111 const Object& right, 112 const Object& compare_func) { 113 if (left.isSmallInt() && right.isSmallInt()) { 114 return Bool::fromBool(SmallInt::cast(*left).value() < 115 SmallInt::cast(*right).value()); 116 } 117 if (left.isStr() && right.isStr()) { 118 return Bool::fromBool(Str::cast(*left).compare(Str::cast(*right)) < 0); 119 } 120 return Interpreter::call2(thread, compare_func, left, right); 121} 122 123static RawObject listInsertionSort(Thread* thread, const MutableTuple& data, 124 const Object& compare_func, word start, 125 word end) { 126 HandleScope scope(thread); 127 Object item_i(&scope, NoneType::object()); 128 Object item_j(&scope, NoneType::object()); 129 Object compare_result(&scope, NoneType::object()); 130 for (word i = start + 1; i < end; i++) { 131 item_i = data.at(i); 132 word j = i - 1; 133 for (; j >= start; j--) { 134 item_j = data.at(j); 135 compare_result = objectLessThan(thread, item_i, item_j, compare_func); 136 if (compare_result.isError()) { 137 return *compare_result; 138 } 139 compare_result = Interpreter::isTrue(thread, *compare_result); 140 if (compare_result.isError()) { 141 return *compare_result; 142 } 143 if (!Bool::cast(*compare_result).value()) { 144 break; 145 } 146 data.atPut(j + 1, *item_j); 147 } 148 data.atPut(j + 1, *item_i); 149 } 150 return NoneType::object(); 151} 152 153// Merge 2 sublists, input[left: right], input[right: end] into 154// output[left: end]. 155static RawObject listMerge(Thread* thread, const MutableTuple& input, 156 const MutableTuple& output, 157 const Object& compare_func, word left, word right, 158 word end) { 159 HandleScope scope(thread); 160 Object item_i(&scope, NoneType::object()); 161 Object item_j(&scope, NoneType::object()); 162 Object compare_result(&scope, NoneType::object()); 163 word i = left; 164 word j = right; 165 word k = left; 166 while (i < right && j < end) { 167 item_i = input.at(i); 168 item_j = input.at(j); 169 compare_result = objectLessThan(thread, item_j, item_i, compare_func); 170 if (compare_result.isError()) { 171 return *compare_result; 172 } 173 compare_result = Interpreter::isTrue(thread, *compare_result); 174 if (compare_result.isError()) { 175 return *compare_result; 176 } 177 if (*compare_result == Bool::trueObj()) { 178 output.atPut(k++, *item_j); 179 j++; 180 } else { 181 DCHECK(compare_result == Bool::falseObj(), "expected to be false"); 182 output.atPut(k++, *item_i); 183 i++; 184 } 185 } 186 for (; i < right; i++) { 187 output.atPut(k++, input.at(i)); 188 } 189 for (; j < end; j++) { 190 output.atPut(k++, input.at(j)); 191 } 192 DCHECK(k == end, "sublists were not fully copied"); 193 return NoneType::object(); 194} 195 196RawObject listSort(Thread* thread, const List& list) { 197 return listSortWithCompareMethod(thread, list, ID(_lt)); 198} 199 200// This uses an adaptation of merge sort. It sorts sublists of size 201// kListInsertionSortSize or less using insertion sort first. Afterwards, it is 202// using a bottom-up merge sort to increase the sublists' size by power of 2. 203// Also, this function allocates a temporary space to ease merging and swaps 204// `input` and `output` to avoid further allocation. 205// TODO(T39107329): Consider using Timsort for further optimization. 206RawObject listSortWithCompareMethod(Thread* thread, const List& list, 207 SymbolId compare_method) { 208 word num_items = list.numItems(); 209 if (num_items == 0) { 210 return NoneType::object(); 211 } 212 HandleScope scope(thread); 213 Runtime* runtime = thread->runtime(); 214 Object compare_func(&scope, runtime->lookupNameInModule(thread, ID(_builtins), 215 compare_method)); 216 if (compare_func.isError()) return *compare_func; 217 MutableTuple input(&scope, list.items()); 218 Object compare_result(&scope, NoneType::object()); 219 for (word left = 0; left < num_items; left += kListInsertionSortSize) { 220 word right = Utils::minimum(left + kListInsertionSortSize, num_items); 221 compare_result = 222 listInsertionSort(thread, input, compare_func, left, right); 223 if (compare_result.isError()) { 224 return *compare_result; 225 } 226 } 227 if (num_items <= kListInsertionSortSize) { 228 // The input list is small enough to be sorted by insertion sort. 229 return NoneType::object(); 230 } 231 MutableTuple output(&scope, runtime->newMutableTuple(input.length())); 232 Object tmp(&scope, NoneType::object()); 233 for (word width = kListInsertionSortSize; width < num_items; width *= 2) { 234 for (word left = 0; left < num_items; left += width * 2) { 235 word right = Utils::minimum(num_items, left + width); 236 word end = Utils::minimum(num_items, left + width * 2); 237 compare_result = 238 listMerge(thread, input, output, compare_func, left, right, end); 239 if (compare_result.isError()) { 240 return *compare_result; 241 } 242 } 243 tmp = *output; 244 output = *input; 245 input = MutableTuple::cast(*tmp); 246 } 247 list.setItems(*input); 248 return NoneType::object(); 249} 250 251RawObject listIteratorNext(Thread* thread, const ListIterator& iter) { 252 HandleScope scope(thread); 253 word idx = iter.index(); 254 List underlying(&scope, iter.iterable()); 255 if (idx >= underlying.numItems()) { 256 return Error::outOfBounds(); 257 } 258 RawObject item = underlying.at(idx); 259 iter.setIndex(idx + 1); 260 return item; 261} 262 263static const BuiltinAttribute kListAttributes[] = { 264 {ID(_list__items), RawList::kItemsOffset, AttributeFlags::kHidden}, 265 {ID(_list__num_items), RawList::kNumItemsOffset, AttributeFlags::kHidden}, 266}; 267 268static const BuiltinAttribute kListIteratorAttributes[] = { 269 {ID(_list_iterator__iterable), RawListIterator::kIterableOffset, 270 AttributeFlags::kHidden}, 271 {ID(_list_iterator__index), RawListIterator::kIndexOffset, 272 AttributeFlags::kHidden}, 273}; 274 275void initializeListTypes(Thread* thread) { 276 addBuiltinType(thread, ID(list), LayoutId::kList, 277 /*superclass_id=*/LayoutId::kObject, kListAttributes, 278 List::kSize, /*basetype=*/true); 279 280 addBuiltinType(thread, ID(list_iterator), LayoutId::kListIterator, 281 /*superclass_id=*/LayoutId::kObject, kListIteratorAttributes, 282 ListIterator::kSize, /*basetype=*/false); 283} 284 285RawObject METH(list, __new__)(Thread* thread, Arguments args) { 286 HandleScope scope(thread); 287 Object type_obj(&scope, args.get(0)); 288 Runtime* runtime = thread->runtime(); 289 if (!runtime->isInstanceOfType(*type_obj)) { 290 return thread->raiseWithFmt(LayoutId::kTypeError, "not a type object"); 291 } 292 Type type(&scope, *type_obj); 293 if (type.builtinBase() != LayoutId::kList) { 294 return thread->raiseWithFmt(LayoutId::kTypeError, "not a subtype of list"); 295 } 296 Layout layout(&scope, type.instanceLayout()); 297 List result(&scope, runtime->newInstance(layout)); 298 result.setNumItems(0); 299 result.setItems(runtime->emptyTuple()); 300 return *result; 301} 302 303RawObject METH(list, __add__)(Thread* thread, Arguments args) { 304 HandleScope scope(thread); 305 Object self_obj(&scope, args.get(0)); 306 Runtime* runtime = thread->runtime(); 307 if (!runtime->isInstanceOfList(*self_obj)) { 308 return thread->raiseRequiresType(self_obj, ID(list)); 309 } 310 Object other_obj(&scope, args.get(1)); 311 if (!runtime->isInstanceOfList(*other_obj)) { 312 return thread->raiseWithFmt(LayoutId::kTypeError, 313 "can only concatenate list to list"); 314 } 315 List self(&scope, *self_obj); 316 List other(&scope, *other_obj); 317 List new_list(&scope, runtime->newList()); 318 word self_len = self.numItems(); 319 word other_len = other.numItems(); 320 runtime->listEnsureCapacity(thread, new_list, self_len + other_len); 321 Tuple self_items(&scope, self.items()); 322 Tuple other_items(&scope, other.items()); 323 listExtend(thread, new_list, self_items, self_len); 324 listExtend(thread, new_list, other_items, other_len); 325 return *new_list; 326} 327 328RawObject listContains(Thread* thread, const List& list, const Object& key) { 329 for (word i = 0, num_items = list.numItems(); i < num_items; ++i) { 330 RawObject result = Runtime::objectEquals(thread, *key, list.at(i)); 331 if (result == Bool::trueObj()) { 332 return Bool::trueObj(); 333 } 334 if (result.isErrorException()) return result; 335 } 336 return Bool::falseObj(); 337} 338 339RawObject METH(list, __contains__)(Thread* thread, Arguments args) { 340 HandleScope scope(thread); 341 Object self_obj(&scope, args.get(0)); 342 if (!thread->runtime()->isInstanceOfList(*self_obj)) { 343 return thread->raiseRequiresType(self_obj, ID(list)); 344 } 345 List self(&scope, *self_obj); 346 Object key(&scope, args.get(1)); 347 return listContains(thread, self, key); 348} 349 350RawObject METH(list, __eq__)(Thread* thread, Arguments args) { 351 HandleScope scope(thread); 352 Runtime* runtime = thread->runtime(); 353 Object self_obj(&scope, args.get(0)); 354 if (!runtime->isInstanceOfList(*self_obj)) { 355 return thread->raiseRequiresType(self_obj, ID(list)); 356 } 357 Object other_obj(&scope, args.get(1)); 358 if (!runtime->isInstanceOfList(*other_obj)) { 359 return NotImplementedType::object(); 360 } 361 if (self_obj == other_obj) { 362 return Bool::trueObj(); 363 } 364 List self(&scope, *self_obj); 365 List other(&scope, *other_obj); 366 word num_items = self.numItems(); 367 if (num_items != other.numItems()) { 368 return Bool::falseObj(); 369 } 370 Tuple self_items(&scope, self.items()); 371 Tuple other_items(&scope, other.items()); 372 for (word i = 0; i < num_items; i++) { 373 RawObject self_item = self_items.at(i); 374 RawObject other_item = other_items.at(i); 375 if (self_item != other_item) { 376 RawObject equals = Runtime::objectEquals(thread, self_item, other_item); 377 if (equals != Bool::trueObj()) { 378 return equals; 379 } 380 } 381 } 382 return Bool::trueObj(); 383} 384 385RawObject METH(list, clear)(Thread* thread, Arguments args) { 386 HandleScope scope(thread); 387 Object self(&scope, args.get(0)); 388 if (!thread->runtime()->isInstanceOfList(*self)) { 389 return thread->raiseRequiresType(self, ID(list)); 390 } 391 List list(&scope, *self); 392 list.clearFrom(0); 393 return NoneType::object(); 394} 395 396RawObject METH(list, __len__)(Thread* thread, Arguments args) { 397 HandleScope scope(thread); 398 Object self(&scope, args.get(0)); 399 if (!thread->runtime()->isInstanceOfList(*self)) { 400 return thread->raiseRequiresType(self, ID(list)); 401 } 402 List list(&scope, *self); 403 return SmallInt::fromWord(list.numItems()); 404} 405 406RawObject METH(list, insert)(Thread* thread, Arguments args) { 407 HandleScope scope(thread); 408 Object self(&scope, args.get(0)); 409 if (!thread->runtime()->isInstanceOfList(*self)) { 410 return thread->raiseRequiresType(self, ID(list)); 411 } 412 List list(&scope, *self); 413 Object index_obj(&scope, args.get(1)); 414 index_obj = intFromIndex(thread, index_obj); 415 if (index_obj.isError()) return *index_obj; 416 Int index_int(&scope, intUnderlying(*index_obj)); 417 if (index_int.isLargeInt()) { 418 return thread->raiseWithFmt(LayoutId::kOverflowError, 419 "Python int too large to convert to C ssize_t"); 420 } 421 word index = index_int.asWord(); 422 Object value(&scope, args.get(2)); 423 listInsert(thread, list, value, index); 424 return NoneType::object(); 425} 426 427RawObject METH(list, __mul__)(Thread* thread, Arguments args) { 428 RawObject other = args.get(1); 429 HandleScope scope(thread); 430 Object self(&scope, args.get(0)); 431 Runtime* runtime = thread->runtime(); 432 if (!runtime->isInstanceOfList(*self)) { 433 return thread->raiseRequiresType(self, ID(list)); 434 } 435 if (other.isSmallInt()) { 436 word ntimes = SmallInt::cast(other).value(); 437 if (ntimes <= 0) return runtime->newList(); 438 List list(&scope, *self); 439 return listReplicate(thread, list, ntimes); 440 } 441 return thread->raiseWithFmt(LayoutId::kTypeError, 442 "can't multiply list by non-int"); 443} 444 445RawObject METH(list, pop)(Thread* thread, Arguments args) { 446 if (!args.get(1).isUnbound() && !args.get(1).isSmallInt()) { 447 return thread->raiseWithFmt( 448 LayoutId::kTypeError, 449 "index object cannot be interpreted as an integer"); 450 } 451 452 HandleScope scope(thread); 453 Object self(&scope, args.get(0)); 454 if (!thread->runtime()->isInstanceOfList(*self)) { 455 return thread->raiseRequiresType(self, ID(list)); 456 } 457 List list(&scope, *self); 458 word length = list.numItems(); 459 if (length == 0) { 460 return thread->raiseWithFmt(LayoutId::kIndexError, "pop from empty list"); 461 } 462 word index = length - 1; 463 if (!args.get(1).isUnbound()) { 464 index = SmallInt::cast(args.get(1)).value(); 465 if (index < 0) index += length; 466 } 467 if (index < 0 || index >= length) { 468 return thread->raiseWithFmt(LayoutId::kIndexError, 469 "pop index out of range"); 470 } 471 472 return listPop(thread, list, index); 473} 474 475RawObject METH(list, remove)(Thread* thread, Arguments args) { 476 HandleScope scope(thread); 477 Object self_obj(&scope, args.get(0)); 478 if (!thread->runtime()->isInstanceOfList(*self_obj)) { 479 return thread->raiseRequiresType(self_obj, ID(list)); 480 } 481 Object value(&scope, args.get(1)); 482 List self(&scope, *self_obj); 483 Object item(&scope, NoneType::object()); 484 Object comp_result(&scope, NoneType::object()); 485 Object found(&scope, NoneType::object()); 486 for (word i = 0, num_items = self.numItems(); i < num_items; ++i) { 487 item = self.at(i); 488 if (*value == *item) { 489 listPop(thread, self, i); 490 return NoneType::object(); 491 } 492 comp_result = 493 Interpreter::compareOperation(thread, CompareOp::EQ, item, value); 494 if (comp_result.isError()) return *comp_result; 495 found = Interpreter::isTrue(thread, *comp_result); 496 if (found.isError()) return *found; 497 if (found == Bool::trueObj()) { 498 listPop(thread, self, i); 499 return NoneType::object(); 500 } 501 } 502 return thread->raiseWithFmt(LayoutId::kValueError, 503 "list.remove(x) x not in list"); 504} 505 506RawObject METH(list, __imul__)(Thread* thread, Arguments args) { 507 RawObject other = args.get(1); 508 HandleScope scope(thread); 509 Object self(&scope, args.get(0)); 510 Runtime* runtime = thread->runtime(); 511 if (!runtime->isInstanceOfList(*self)) { 512 return thread->raiseRequiresType(self, ID(list)); 513 } 514 Object count_index(&scope, args.get(1)); 515 Object count_obj(&scope, intFromIndex(thread, count_index)); 516 if (count_obj.isError()) return *count_obj; 517 word count = intUnderlying(*count_obj).asWordSaturated(); 518 if (!SmallInt::isValid(count)) { 519 return thread->raiseWithFmt(LayoutId::kOverflowError, 520 "cannot fit '%T' into an index-sized integer", 521 &count_index); 522 } 523 word ntimes = SmallInt::cast(other).value(); 524 if (ntimes == 1) { 525 return *self; 526 } 527 List list(&scope, *self); 528 if (ntimes <= 0) { 529 list.clearFrom(0); 530 return *list; 531 } 532 word new_length; 533 word len = list.numItems(); 534 if (__builtin_mul_overflow(len, ntimes, &new_length) || 535 !SmallInt::isValid(new_length)) { 536 return thread->raiseMemoryError(); 537 } 538 if (new_length == len) { 539 return *list; 540 } 541 runtime->listEnsureCapacity(thread, list, new_length); 542 list.setNumItems(new_length); 543 for (word i = 0; i < ntimes; i++) { 544 list.replaceFromWith(i * len, *list, len); 545 } 546 return *list; 547} 548 549RawObject METH(list, __iter__)(Thread* thread, Arguments args) { 550 HandleScope scope(thread); 551 Object self(&scope, args.get(0)); 552 if (!thread->runtime()->isInstanceOfList(*self)) { 553 return thread->raiseRequiresType(self, ID(list)); 554 } 555 return thread->runtime()->newListIterator(self); 556} 557 558RawObject METH(list_iterator, __iter__)(Thread* thread, Arguments args) { 559 HandleScope scope(thread); 560 Object self(&scope, args.get(0)); 561 if (!self.isListIterator()) { 562 return thread->raiseRequiresType(self, ID(list_iterator)); 563 } 564 return *self; 565} 566 567RawObject METH(list_iterator, __next__)(Thread* thread, Arguments args) { 568 HandleScope scope(thread); 569 Object self_obj(&scope, args.get(0)); 570 if (!self_obj.isListIterator()) { 571 return thread->raiseRequiresType(self_obj, ID(list_iterator)); 572 } 573 ListIterator self(&scope, *self_obj); 574 Object value(&scope, listIteratorNext(thread, self)); 575 if (value.isError()) { 576 return thread->raise(LayoutId::kStopIteration, NoneType::object()); 577 } 578 return *value; 579} 580 581RawObject METH(list_iterator, __length_hint__)(Thread* thread, Arguments args) { 582 HandleScope scope(thread); 583 Object self(&scope, args.get(0)); 584 if (!self.isListIterator()) { 585 return thread->raiseRequiresType(self, ID(list_iterator)); 586 } 587 ListIterator list_iterator(&scope, *self); 588 List list(&scope, list_iterator.iterable()); 589 return SmallInt::fromWord(list.numItems() - list_iterator.index()); 590} 591 592} // namespace py