Iteration.h (28899B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_Iteration_h 8 #define vm_Iteration_h 9 10 /* 11 * JavaScript iterators. 12 */ 13 14 #include "mozilla/MemoryReporting.h" 15 16 #include "builtin/SelfHostingDefines.h" 17 #include "gc/Barrier.h" 18 #include "vm/NativeObject.h" 19 #include "vm/TypedArrayObject.h" 20 21 /* 22 * [SMDOC] For-in enumeration 23 * 24 * A for-in loop in JS iterates over the string-valued, enumerable 25 * property keys of an object and its prototype chain. The order in 26 * which keys appear is specified to the extent that implementations 27 * historically agreed, and implementation-defined beyond that. See 28 * https://tc39.es/ecma262/#sec-enumerate-object-properties for the 29 * gory details. Each key appears only once in the enumeration. 30 * 31 * We enumerate properties using PropertyEnumerator, which creates an 32 * ordered list of PropertyKeys, using ShapePropertyIter for native 33 * objects and calling enumerate hooks where necessary. This list is 34 * used to create a NativeIterator, which contains (among other 35 * things) a trailing array of strings representing the property keys 36 * of the object, and a cursor pointing into that array. This 37 * NativeIterator is wrapped in a PropertyIteratorObject, which is 38 * pushed by JSOp::Iter and used by JSOp::MoreIter and JSOp::EndIter. 39 * 40 * While active, a NativeIterator is registered in a doubly linked 41 * list, rooted in the compartment. When any property is deleted from 42 * an object, this list is used to remove the deleted property from 43 * any active enumerations. See SuppressDeletedProperty. This slows 44 * down deletion but speeds up enumeration, which is generally a good 45 * tradeoff. 46 * 47 * In many cases, objects with the same shape will have the same set 48 * of property keys. (The most common exception is objects with dense 49 * elements, which can be added or removed without changing the shape 50 * of the object.) In such cases, we can reuse an existing iterator by 51 * storing a pointer to the PropertyIteratorObject in the shape's 52 * |cache_| pointer. Before reusing an iterator, we have to verify 53 * that the prototype chain has not changed and no dense elements have 54 * been added, which is done by storing a trailing array of prototype 55 * shapes in the NativeIterator and comparing it against the shapes of 56 * the prototype chain. 57 * 58 * One of the most frequent uses of for-in loops is in loops that look 59 * like this, which iterate over each property of an object and do 60 * something with those values: 61 * for (var key in obj) { 62 * if (obj.hasOwnProperty(key)) { 63 * doSomethingWith(obj[key]); 64 * } 65 * } 66 * Most objects don't have any enumerable properties on the prototype 67 * chain. In such cases, we can speed up property access inside the 68 * loop by precomputing some information and storing it in the 69 * iterator. When we see a pattern like this in Ion, we generate a 70 * call to GetIteratorWithIndices instead of GetIterator. In this 71 * case, in addition to the list of property keys, PropertyEnumerator 72 * will try to generate a list of corresponding PropertyIndex values, 73 * which represent the location of the own property key in the object 74 * (fixed slot/dynamic slot/dense element + offset). This list will be 75 * stored in NativeIterator as yet another trailing array. When 76 * present, it can be used by Ion code to speed up property access 77 * inside for-in loops. See OptimizeIteratorIndices in 78 * IonAnalysis.cpp. 79 */ 80 81 namespace js { 82 83 class ArrayObject; 84 class PlainObject; 85 class PropertyIteratorObject; 86 87 // A PropertyIndex stores information about the location of an own data 88 // property in a format that can be stored in a NativeIterator and consumed by 89 // jitcode to access properties without needing to use the megamorphic cache. 90 struct PropertyIndex { 91 private: 92 uint32_t asBits_; 93 94 public: 95 enum class Kind : uint32_t { DynamicSlot, FixedSlot, Element, Invalid }; 96 97 PropertyIndex(Kind kind, uint32_t index) : asBits_(encode(kind, index)) {} 98 99 static PropertyIndex Invalid() { return PropertyIndex(Kind::Invalid, 0); } 100 101 static PropertyIndex ForElement(uint32_t index) { 102 return PropertyIndex(Kind::Element, index); 103 } 104 105 static PropertyIndex ForSlot(NativeObject* obj, uint32_t index) { 106 if (index < obj->numFixedSlots()) { 107 return PropertyIndex(Kind::FixedSlot, index); 108 } else { 109 return PropertyIndex(Kind::DynamicSlot, index - obj->numFixedSlots()); 110 } 111 } 112 113 static constexpr uint32_t KindBits = 2; 114 115 static constexpr uint32_t IndexBits = 32 - KindBits; 116 static constexpr uint32_t IndexLimit = 1 << IndexBits; 117 static constexpr uint32_t IndexMask = (1 << IndexBits) - 1; 118 119 static constexpr uint32_t KindShift = IndexBits; 120 121 static_assert(NativeObject::MAX_FIXED_SLOTS < IndexLimit); 122 static_assert(NativeObject::MAX_SLOTS_COUNT < IndexLimit); 123 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < IndexLimit); 124 125 private: 126 uint32_t encode(Kind kind, uint32_t index) { 127 MOZ_ASSERT(index < IndexLimit); 128 return (uint32_t(kind) << KindShift) | index; 129 } 130 131 public: 132 Kind kind() const { return Kind(asBits_ >> KindShift); } 133 uint32_t index() const { return asBits_ & IndexMask; } 134 }; 135 136 using PropertyIndexVector = js::Vector<PropertyIndex, 8, js::TempAllocPolicy>; 137 138 struct NativeIterator; 139 140 class NativeIteratorListNode { 141 protected: 142 // While in compartment->enumerators, these form a doubly linked list. 143 NativeIteratorListNode* prev_ = nullptr; 144 NativeIteratorListNode* next_ = nullptr; 145 146 public: 147 NativeIteratorListNode* prev() { return prev_; } 148 NativeIteratorListNode* next() { return next_; } 149 150 void setPrev(NativeIteratorListNode* prev) { prev_ = prev; } 151 void setNext(NativeIteratorListNode* next) { next_ = next; } 152 153 static constexpr size_t offsetOfNext() { 154 return offsetof(NativeIteratorListNode, next_); 155 } 156 157 static constexpr size_t offsetOfPrev() { 158 return offsetof(NativeIteratorListNode, prev_); 159 } 160 161 private: 162 NativeIterator* asNativeIterator() { 163 return reinterpret_cast<NativeIterator*>(this); 164 } 165 166 friend class NativeIteratorListIter; 167 }; 168 169 class NativeIteratorListHead : public NativeIteratorListNode { 170 private: 171 // Initialize a |Compartment::enumerators| sentinel. 172 NativeIteratorListHead() { prev_ = next_ = this; } 173 friend class JS::Compartment; 174 }; 175 176 class NativeIteratorListIter { 177 private: 178 NativeIteratorListHead* head_; 179 NativeIteratorListNode* curr_; 180 181 public: 182 explicit NativeIteratorListIter(NativeIteratorListHead* head) 183 : head_(head), curr_(head->next()) {} 184 185 bool done() const { return curr_ == head_; } 186 187 NativeIterator* next() { 188 MOZ_ASSERT(!done()); 189 NativeIterator* result = curr_->asNativeIterator(); 190 curr_ = curr_->next(); 191 return result; 192 } 193 }; 194 195 class IteratorProperty { 196 uintptr_t raw_ = 0; 197 198 public: 199 static constexpr uintptr_t DeletedBit = 0x1; 200 201 // The copy constructor is deleted as a safeguard against writing code which 202 // would overwrite existing IteratorProperties on NativeIterators. They are 203 // intended to be written once and never changed, outside of being moved in 204 // GC callbacks. 205 IteratorProperty(const IteratorProperty&) = delete; 206 207 IteratorProperty() = default; 208 explicit IteratorProperty(JSLinearString* str) : raw_(uintptr_t(str)) {} 209 IteratorProperty(JSLinearString* str, bool deleted) 210 : raw_(uintptr_t(str) | (deleted ? DeletedBit : 0)) {} 211 212 JSLinearString* asString() const { 213 return reinterpret_cast<JSLinearString*>(raw_ & ~DeletedBit); 214 } 215 216 bool deleted() const { return raw_ & DeletedBit; } 217 void markDeleted() { raw_ |= DeletedBit; } 218 void clearDeleted() { raw_ &= ~DeletedBit; } 219 220 void traceString(JSTracer* trc); 221 } JS_HAZ_GC_POINTER; 222 223 struct NativeIterator : public NativeIteratorListNode { 224 private: 225 // Object being iterated. Non-null except in NativeIterator sentinels, 226 // the empty iterator singleton (for iterating |null| or |undefined|), and 227 // inactive iterators. 228 GCPtr<JSObject*> objectBeingIterated_ = {}; 229 230 // Internal iterator object. 231 const GCPtr<JSObject*> iterObj_ = {}; 232 const GCPtr<Shape*> objShape_ = {}; 233 uint32_t propertyCount_ = 0; 234 uint32_t propertyCursor_; // initialized by constructor 235 uint32_t ownPropertyCount_; // initialized by constructor 236 HashNumber shapesHash_; // initialized by constructor 237 uint16_t protoShapeCount_ = 0; 238 uint8_t flags_ = 0; 239 240 public: 241 // For cacheable native iterators, whether the iterator is currently 242 // active. Not serialized by XDR. 243 struct Flags { 244 // This flag is set when all shapes and properties associated with this 245 // NativeIterator have been initialized, such that |shapesEnd_|, in 246 // addition to being the end of shapes, is also the beginning of 247 // properties. 248 // 249 // This flag is only *not* set when a NativeIterator is in the process 250 // of being constructed. At such time |shapesEnd_| accounts only for 251 // shapes that have been initialized -- potentially none of them. 252 // Instead, |propertyCursor_| is initialized to the ultimate/actual 253 // start of properties and must be used instead of |propertiesBegin()|, 254 // which asserts that this flag is present to guard against misuse. 255 static constexpr uint32_t Initialized = 0x1; 256 257 // This flag indicates that this NativeIterator is currently being used 258 // to enumerate an object's properties and has not yet been closed. 259 static constexpr uint32_t Active = 0x2; 260 261 // This flag indicates that the object being enumerated by this 262 // |NativeIterator| had a property deleted from it before it was 263 // visited, forcing the properties array in this to be mutated to 264 // remove it. 265 static constexpr uint32_t HasUnvisitedPropertyDeletion = 0x4; 266 267 // Whether this is the shared empty iterator object used for iterating over 268 // null/undefined. 269 static constexpr uint32_t IsEmptyIteratorSingleton = 0x8; 270 271 // NOTE: the three flags below pertain to iterator indices optimizations. 272 // If an object only has own data properties, we can store a list of 273 // PropertyIndex that can be used in Ion to more efficiently access those 274 // properties in cases like `for (var key in obj) { ...obj[key]... }`. 275 276 // Whether the object supports indices, in the event that they are 277 // requested. Note that this is exclusive with IndicesAvailable 278 static constexpr uint32_t IndicesSupported = 0x10; 279 280 // Whether space was initially reserved for indices for this iterator. 281 static constexpr uint32_t IndicesAllocated = 0x20; 282 283 // Whether indices are actually valid in the reserved area 284 static constexpr uint32_t IndicesAvailable = 0x40; 285 286 // If this iterator was created only for own properties 287 static constexpr uint32_t OwnPropertiesOnly = 0x80; 288 289 // If any of these bits are set on a |NativeIterator|, it isn't 290 // currently reusable. (An active |NativeIterator| can't be stolen 291 // *right now*; a |NativeIterator| that's had its properties mutated 292 // can never be reused, because it would give incorrect results.) 293 static constexpr uint32_t NotReusable = 294 Active | HasUnvisitedPropertyDeletion | OwnPropertiesOnly; 295 }; 296 297 // We have a full u32 for this, but due to the way we compute the address 298 // of indices in the MacroAssembler, we want to have a few extra bits of 299 // wiggle room for shifting 300 static constexpr uint32_t PropCountLimit = 1 << 30; 301 302 // If it's really important we can increase the size of protoShapeCount_, 303 // but increasing it to 32 bits would add another word. 304 static constexpr uint32_t ShapeCountLimit = 1 << 16; 305 306 private: 307 #ifdef DEBUG 308 // If true, this iterator may contain indexed properties that came from 309 // objects on the prototype chain. This is used by certain debug assertions. 310 bool maybeHasIndexedPropertiesFromProto_ = false; 311 #endif 312 313 // END OF PROPERTIES 314 315 // No further fields appear after here *in NativeIterator*, but this class is 316 // always allocated with space tacked on immediately after |this| to store 317 // propertyCount_ IteratorProperty values, optionally propertyCount_ 318 // PropertyIndex values, and protoShapeCount_ GCPtr<Shape*> values. 319 public: 320 /** 321 * Initialize a NativeIterator properly allocated for |props.length()| 322 * properties and |numShapes| shapes. If |indices| is non-null, also 323 * allocates room for |indices.length()| PropertyIndex values. In this case, 324 * |indices.length()| must equal |props.length()|. 325 * 326 * Despite being a constructor, THIS FUNCTION CAN REPORT ERRORS. Users 327 * MUST set |*hadError = false| on entry and consider |*hadError| on return 328 * to mean this function failed. 329 */ 330 NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter, 331 Handle<JSObject*> objBeingIterated, HandleIdVector props, 332 bool supportsIndices, PropertyIndexVector* indices, 333 uint32_t numShapes, uint32_t ownPropertyCount, 334 bool forObjectKeys, bool* hadError); 335 336 JSObject* objectBeingIterated() const { return objectBeingIterated_; } 337 338 void initObjectBeingIterated(JSObject& obj) { 339 MOZ_ASSERT(!objectBeingIterated_); 340 objectBeingIterated_.init(&obj); 341 } 342 void clearObjectBeingIterated() { 343 MOZ_ASSERT(objectBeingIterated_); 344 objectBeingIterated_ = nullptr; 345 } 346 347 const GCPtr<Shape*>& objShape() const { return objShape_; } 348 349 GCPtr<Shape*>* protoShapesBegin(size_t numProperties) const { 350 uintptr_t raw = reinterpret_cast<uintptr_t>(this); 351 uintptr_t propertiesStart = raw + offsetOfFirstProperty(); 352 uintptr_t propertiesEnd = 353 propertiesStart + numProperties * sizeof(IteratorProperty); 354 uintptr_t result = propertiesEnd; 355 if (flags_ & Flags::IndicesAllocated) { 356 result += numProperties * sizeof(PropertyIndex); 357 } 358 return reinterpret_cast<GCPtr<Shape*>*>(result); 359 } 360 361 GCPtr<Shape*>* protoShapesBegin() const { 362 return protoShapesBegin(allocatedPropertyCount()); 363 } 364 365 GCPtr<Shape*>* protoShapesEnd() const { 366 return protoShapesBegin() + protoShapeCount_; 367 } 368 369 uint32_t protoShapeCount() const { return protoShapeCount_; } 370 371 IteratorProperty* propertiesBegin() const { 372 static_assert( 373 alignof(GCPtr<Shape*>) >= alignof(IteratorProperty), 374 "IteratorPropertys for properties must be able to appear " 375 "directly after any GCPtr<Shape*>s after this NativeIterator, " 376 "with no padding space required for correct alignment"); 377 static_assert( 378 alignof(NativeIterator) >= alignof(IteratorProperty), 379 "IteratorPropertys for properties must be able to appear " 380 "directly after this NativeIterator when no GCPtr<Shape*>s are " 381 "present, with no padding space required for correct " 382 "alignment"); 383 384 return reinterpret_cast<IteratorProperty*>(uintptr_t(this) + sizeof(*this)); 385 } 386 387 IteratorProperty* propertiesEnd() const { 388 return propertiesBegin() + propertyCount_; 389 } 390 391 IteratorProperty* nextProperty() const { 392 return propertiesBegin() + propertyCursor_; 393 } 394 395 PropertyIndex* indicesBegin() const { 396 // PropertyIndex must be able to be appear directly after the properties 397 // array, with no padding required for correct alignment. 398 static_assert(alignof(IteratorProperty) >= alignof(PropertyIndex)); 399 return reinterpret_cast<PropertyIndex*>(propertiesEnd()); 400 } 401 402 PropertyIndex* indicesEnd() const { 403 MOZ_ASSERT(flags_ & Flags::IndicesAllocated); 404 return indicesBegin() + propertyCount_ * sizeof(PropertyIndex); 405 } 406 407 MOZ_ALWAYS_INLINE JS::Value nextIteratedValueAndAdvance() { 408 while (propertyCursor_ < propertyCount_) { 409 IteratorProperty& prop = *nextProperty(); 410 incCursor(); 411 if (prop.deleted()) { 412 continue; 413 } 414 return JS::StringValue(prop.asString()); 415 } 416 417 return JS::MagicValue(JS_NO_ITER_VALUE); 418 } 419 420 void resetPropertyCursorForReuse() { 421 MOZ_ASSERT(isInitialized()); 422 423 // This function is called unconditionally on IteratorClose, so 424 // unvisited properties might have been deleted, so we can't assert 425 // this NativeIterator is reusable. (Should we not bother resetting 426 // the cursor in that case?) 427 428 // If properties were marked as deleted, unmark them. 429 if (hasUnvisitedPropertyDeletion()) { 430 for (IteratorProperty* prop = propertiesBegin(); prop < propertiesEnd(); 431 prop++) { 432 prop->clearDeleted(); 433 } 434 unmarkHasUnvisitedPropertyDeletion(); 435 } 436 437 // Note: JIT code inlines |propertyCursor_| resetting when an iterator 438 // ends: see |MacroAssembler::iteratorClose|. 439 propertyCursor_ = 0; 440 } 441 442 bool previousPropertyWas(JS::Handle<JSLinearString*> str) { 443 MOZ_ASSERT(isInitialized()); 444 return propertyCursor_ > 0 && 445 propertiesBegin()[propertyCursor_ - 1].asString() == str; 446 } 447 448 size_t numKeys() const { return propertyCount_; } 449 450 size_t ownPropertyCount() const { return ownPropertyCount_; } 451 452 size_t allocatedPropertyCount() const { 453 // propertyCursor_ holds the number of allocated properties until 454 // the iterator is initialized. This is so we can know the proper layout 455 // of the trailing bytes if we trigger a GC inside the constructor. 456 if (!isInitialized()) { 457 return propertyCursor_; 458 } 459 return propertyCount_; 460 } 461 462 JSObject* iterObj() const { return iterObj_; } 463 464 void incCursor() { 465 MOZ_ASSERT(isInitialized()); 466 propertyCursor_++; 467 } 468 469 HashNumber shapesHash() const { return shapesHash_; } 470 471 bool isInitialized() const { return flags_ & Flags::Initialized; } 472 473 size_t allocationSize() const; 474 475 #ifdef DEBUG 476 void setMaybeHasIndexedPropertiesFromProto() { 477 maybeHasIndexedPropertiesFromProto_ = true; 478 } 479 bool maybeHasIndexedPropertiesFromProto() const { 480 return maybeHasIndexedPropertiesFromProto_; 481 } 482 #endif 483 484 private: 485 bool indicesAllocated() const { return flags_ & Flags::IndicesAllocated; } 486 487 bool isUnlinked() const { return !prev_ && !next_; } 488 489 public: 490 bool indicesAvailable() const { return flags_ & Flags::IndicesAvailable; } 491 492 bool indicesSupported() const { return flags_ & Flags::IndicesSupported; } 493 494 bool ownPropertiesOnly() const { return flags_ & Flags::OwnPropertiesOnly; } 495 496 // Whether this is the shared empty iterator object used for iterating over 497 // null/undefined. 498 bool isEmptyIteratorSingleton() const { 499 // Note: equivalent code is inlined in MacroAssembler::iteratorClose. 500 bool res = flags_ & Flags::IsEmptyIteratorSingleton; 501 MOZ_ASSERT_IF( 502 res, flags_ == (Flags::Initialized | Flags::IsEmptyIteratorSingleton)); 503 MOZ_ASSERT_IF(res, !objectBeingIterated_); 504 MOZ_ASSERT_IF(res, propertyCount_ == 0); 505 MOZ_ASSERT_IF(res, protoShapeCount_ == 0); 506 MOZ_ASSERT_IF(res, isUnlinked()); 507 return res; 508 } 509 void markEmptyIteratorSingleton() { 510 flags_ |= Flags::IsEmptyIteratorSingleton; 511 512 // isEmptyIteratorSingleton() has various debug assertions. 513 MOZ_ASSERT(isEmptyIteratorSingleton()); 514 } 515 516 bool isActive() const { 517 MOZ_ASSERT(isInitialized()); 518 519 return flags_ & Flags::Active; 520 } 521 522 void markActive() { 523 MOZ_ASSERT(isInitialized()); 524 MOZ_ASSERT(!isEmptyIteratorSingleton()); 525 526 flags_ |= Flags::Active; 527 } 528 529 void markInactive() { 530 MOZ_ASSERT(isInitialized()); 531 MOZ_ASSERT(!isEmptyIteratorSingleton()); 532 533 flags_ &= ~Flags::Active; 534 } 535 536 bool isReusable() const { 537 MOZ_ASSERT(isInitialized()); 538 539 if (!(flags_ & Flags::Initialized)) { 540 return false; 541 } 542 if (flags_ & Flags::Active) { 543 return false; 544 } 545 return true; 546 } 547 548 void markHasUnvisitedPropertyDeletion() { 549 MOZ_ASSERT(isInitialized()); 550 MOZ_ASSERT(!isEmptyIteratorSingleton()); 551 552 flags_ |= Flags::HasUnvisitedPropertyDeletion; 553 } 554 555 void unmarkHasUnvisitedPropertyDeletion() { 556 MOZ_ASSERT(isInitialized()); 557 MOZ_ASSERT(!isEmptyIteratorSingleton()); 558 MOZ_ASSERT(hasUnvisitedPropertyDeletion()); 559 560 flags_ &= ~Flags::HasUnvisitedPropertyDeletion; 561 } 562 563 bool hasUnvisitedPropertyDeletion() const { 564 MOZ_ASSERT(isInitialized()); 565 566 return flags_ & Flags::HasUnvisitedPropertyDeletion; 567 } 568 569 // Indicates the native iterator may walk prototype properties. 570 bool mayHavePrototypeProperties() { 571 // If we can use indices for this iterator, we know it doesn't have 572 // prototype properties, and so we use this as a check for prototype 573 // properties. 574 return !indicesAvailable() && !indicesSupported(); 575 } 576 577 void disableIndices() { 578 // Clear the IndicesAvailable flag so we won't use the indices on this 579 // iterator, and ensure IndicesSupported is cleared as well, so we don't 580 // re-request an iterator with indices. However, we leave the 581 // IndicesAllocated flag because we need to free them later, and skip them 582 // when looking for shapes. 583 flags_ &= ~(Flags::IndicesAvailable | Flags::IndicesSupported); 584 } 585 586 void link(NativeIteratorListNode* other) { 587 MOZ_ASSERT(isInitialized()); 588 589 // The shared iterator used for for-in with null/undefined is immutable and 590 // shouldn't be linked. 591 MOZ_ASSERT(!isEmptyIteratorSingleton()); 592 593 // A NativeIterator cannot appear in the enumerator list twice. 594 MOZ_ASSERT(isUnlinked()); 595 596 setNext(other); 597 setPrev(other->prev()); 598 599 other->prev()->setNext(this); 600 other->setPrev(this); 601 } 602 void unlink() { 603 MOZ_ASSERT(isInitialized()); 604 MOZ_ASSERT(!isEmptyIteratorSingleton()); 605 606 next()->setPrev(prev()); 607 prev()->setNext(next()); 608 setNext(nullptr); 609 setPrev(nullptr); 610 } 611 612 void trace(JSTracer* trc); 613 614 static constexpr size_t offsetOfObjectBeingIterated() { 615 return offsetof(NativeIterator, objectBeingIterated_); 616 } 617 618 static constexpr size_t offsetOfProtoShapeCount() { 619 return offsetof(NativeIterator, protoShapeCount_); 620 } 621 622 static constexpr size_t offsetOfPropertyCursor() { 623 return offsetof(NativeIterator, propertyCursor_); 624 } 625 626 static constexpr size_t offsetOfPropertyCount() { 627 return offsetof(NativeIterator, propertyCount_); 628 } 629 630 static constexpr size_t offsetOfOwnPropertyCount() { 631 return offsetof(NativeIterator, ownPropertyCount_); 632 } 633 634 static constexpr size_t offsetOfFlags() { 635 return offsetof(NativeIterator, flags_); 636 } 637 638 static constexpr size_t offsetOfObjectShape() { 639 return offsetof(NativeIterator, objShape_); 640 } 641 642 static constexpr size_t offsetOfFirstProperty() { 643 // Properties are stored directly after |this|. 644 return sizeof(NativeIterator); 645 } 646 }; 647 648 class PropertyIteratorObject : public NativeObject { 649 static const JSClassOps classOps_; 650 651 enum { IteratorSlot, SlotCount }; 652 653 public: 654 static const JSClass class_; 655 656 NativeIterator* getNativeIterator() const { 657 return maybePtrFromReservedSlot<NativeIterator>(IteratorSlot); 658 } 659 void initNativeIterator(js::NativeIterator* ni) { 660 initReservedSlot(IteratorSlot, PrivateValue(ni)); 661 } 662 663 size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const; 664 665 static size_t offsetOfIteratorSlot() { 666 return getFixedSlotOffset(IteratorSlot); 667 } 668 669 private: 670 static void trace(JSTracer* trc, JSObject* obj); 671 static void finalize(JS::GCContext* gcx, JSObject* obj); 672 }; 673 674 class ArrayIteratorObject : public NativeObject { 675 public: 676 static const JSClass class_; 677 }; 678 679 ArrayIteratorObject* NewArrayIteratorTemplate(JSContext* cx); 680 ArrayIteratorObject* NewArrayIterator(JSContext* cx); 681 682 class StringIteratorObject : public NativeObject { 683 public: 684 static const JSClass class_; 685 }; 686 687 StringIteratorObject* NewStringIteratorTemplate(JSContext* cx); 688 StringIteratorObject* NewStringIterator(JSContext* cx); 689 690 class RegExpStringIteratorObject : public NativeObject { 691 public: 692 static const JSClass class_; 693 }; 694 695 RegExpStringIteratorObject* NewRegExpStringIteratorTemplate(JSContext* cx); 696 RegExpStringIteratorObject* NewRegExpStringIterator(JSContext* cx); 697 698 #ifdef NIGHTLY_BUILD 699 class IteratorRangeObject : public NativeObject { 700 public: 701 static const JSClass class_; 702 }; 703 704 IteratorRangeObject* NewIteratorRange(JSContext* cx); 705 #endif 706 707 [[nodiscard]] bool EnumerateProperties(JSContext* cx, HandleObject obj, 708 MutableHandleIdVector props); 709 710 PropertyIteratorObject* LookupInIteratorCache(JSContext* cx, HandleObject obj); 711 PropertyIteratorObject* LookupInShapeIteratorCache(JSContext* cx, 712 HandleObject obj); 713 714 PropertyIteratorObject* GetIterator(JSContext* cx, HandleObject obj); 715 PropertyIteratorObject* GetIteratorWithIndices(JSContext* cx, HandleObject obj); 716 717 PropertyIteratorObject* GetIteratorForObjectKeys(JSContext* cx, 718 HandleObject obj); 719 PropertyIteratorObject* GetIteratorWithIndicesForObjectKeys(JSContext* cx, 720 HandleObject obj); 721 722 PropertyIteratorObject* ValueToIterator(JSContext* cx, HandleValue vp); 723 724 void CloseIterator(JSObject* obj); 725 726 bool IteratorCloseForException(JSContext* cx, HandleObject obj); 727 728 void UnwindIteratorForUncatchableException(JSObject* obj); 729 730 extern bool SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id); 731 732 extern bool SuppressDeletedElement(JSContext* cx, HandleObject obj, 733 uint32_t index); 734 735 #ifdef DEBUG 736 extern void AssertDenseElementsNotIterated(NativeObject* obj); 737 #else 738 inline void AssertDenseElementsNotIterated(NativeObject* obj) {} 739 #endif 740 741 /* 742 * IteratorMore() returns the next iteration value. If no value is available, 743 * MagicValue(JS_NO_ITER_VALUE) is returned. 744 */ 745 inline Value IteratorMore(JSObject* iterobj) { 746 NativeIterator* ni = 747 iterobj->as<PropertyIteratorObject>().getNativeIterator(); 748 return ni->nextIteratedValueAndAdvance(); 749 } 750 751 /* 752 * Create an object of the form { value: VALUE, done: DONE }. 753 * ES 2017 draft 7.4.7. 754 */ 755 extern PlainObject* CreateIterResultObject(JSContext* cx, HandleValue value, 756 bool done); 757 758 /* 759 * Global Iterator constructor. 760 * Iterator Helpers proposal 2.1.3. 761 */ 762 class IteratorObject : public NativeObject { 763 public: 764 static const JSClass class_; 765 static const JSClass protoClass_; 766 767 static bool finishInit(JSContext* cx, HandleObject ctor, HandleObject proto); 768 }; 769 770 /* 771 * Wrapper for iterators created via Iterator.from. 772 * Iterator Helpers proposal 2.1.3.3.1.1. 773 */ 774 class WrapForValidIteratorObject : public NativeObject { 775 public: 776 static const JSClass class_; 777 778 enum { IteratorSlot, NextMethodSlot, SlotCount }; 779 780 static_assert( 781 IteratorSlot == WRAP_FOR_VALID_ITERATOR_ITERATOR_SLOT, 782 "IteratedSlot must match self-hosting define for iterator object slot."); 783 784 static_assert( 785 NextMethodSlot == WRAP_FOR_VALID_ITERATOR_NEXT_METHOD_SLOT, 786 "NextMethodSlot must match self-hosting define for next method slot."); 787 }; 788 789 WrapForValidIteratorObject* NewWrapForValidIterator(JSContext* cx); 790 791 /* 792 * Generator-esque object returned by Iterator Helper methods. 793 */ 794 class IteratorHelperObject : public NativeObject { 795 public: 796 static const JSClass class_; 797 798 enum { 799 // The implementation (an instance of one of the generators in 800 // builtin/Iterator.js). 801 // Never null. 802 GeneratorSlot, 803 804 // The [[UnderlyingIterator]] internal slot. Either an object or undefined 805 // in the case of IteratorConcat. In the spec, the internal slot stores an 806 // "Iterator Record", but our implementation only stores the actual iterator 807 // object. 808 UnderlyingIteratorSlot, 809 810 SlotCount, 811 }; 812 813 static_assert(GeneratorSlot == ITERATOR_HELPER_GENERATOR_SLOT, 814 "GeneratorSlot must match self-hosting define for generator " 815 "object slot."); 816 817 static_assert(UnderlyingIteratorSlot == 818 ITERATOR_HELPER_UNDERLYING_ITERATOR_SLOT, 819 "UnderlyingIteratorSlot must match self-hosting define for " 820 "underlying iterator slot."); 821 }; 822 823 IteratorHelperObject* NewIteratorHelper(JSContext* cx); 824 825 bool IterableToArray(JSContext* cx, HandleValue iterable, 826 MutableHandle<ArrayObject*> array); 827 828 bool HasOptimizableArrayIteratorPrototype(JSContext* cx); 829 830 enum class MustBePacked { No, Yes }; 831 832 template <MustBePacked Packed> 833 bool IsArrayWithDefaultIterator(JSObject* obj, JSContext* cx); 834 835 bool IsMapObjectWithDefaultIterator(JSObject* obj, JSContext* cx); 836 bool IsSetObjectWithDefaultIterator(JSObject* obj, JSContext* cx); 837 838 // Typed arrays and classes with an enumerate hook can have extra properties not 839 // included in the shape's property map or the object's dense elements. 840 static inline bool ClassCanHaveExtraEnumeratedProperties(const JSClass* clasp) { 841 return IsTypedArrayClass(clasp) || clasp->getNewEnumerate() || 842 clasp->getEnumerate(); 843 } 844 845 } /* namespace js */ 846 847 #endif /* vm_Iteration_h */