v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-collections-gen.cc
Go to the documentation of this file.
1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
13#include "src/heap/heap-inl.h"
17#include "src/roots/roots.h"
18
19namespace v8 {
20namespace internal {
21
23
24template <class T>
26
28 Variant variant, TNode<Context> context, TNode<JSAny> collection,
29 TNode<Object> add_function, TNode<JSAny> key_value,
30 Label* if_may_have_side_effects, Label* if_exception,
31 TVariable<Object>* var_exception) {
32 compiler::ScopedExceptionHandler handler(this, if_exception, var_exception);
33 CSA_DCHECK(this, Word32BinaryNot(IsHashTableHole(key_value)));
34 if (variant == kMap || variant == kWeakMap) {
35 TorqueStructKeyValuePair pair =
36 if_may_have_side_effects != nullptr
37 ? LoadKeyValuePairNoSideEffects(context, key_value,
38 if_may_have_side_effects)
39 : LoadKeyValuePair(context, key_value);
40 TNode<Object> key_n = pair.key;
41 TNode<Object> value_n = pair.value;
42 Call(context, add_function, collection, key_n, value_n);
43 } else {
44 DCHECK(variant == kSet || variant == kWeakSet);
45 Call(context, add_function, collection, key_value);
46 }
47}
48
50 Variant variant, TNode<Context> context,
52 TNode<JSAny> initial_entries) {
53 CSA_DCHECK(this, Word32BinaryNot(IsNullOrUndefined(initial_entries)));
54
55 enum Mode { kSlow, kFastJSArray, kFastCollection };
56 TVARIABLE(IntPtrT, var_at_least_space_for, IntPtrConstant(0));
57 TVARIABLE(HeapObject, var_entries_table, UndefinedConstant());
58 TVARIABLE(Int32T, var_mode, Int32Constant(kSlow));
59 Label if_fast_js_array(this), allocate_table(this);
60
61 // The slow path is taken if the initial add function is modified. This check
62 // must precede the kSet fast path below, which has the side effect of
63 // exhausting {initial_entries} if it is a JSSetIterator.
65 &allocate_table);
66
67 GotoIf(IsFastJSArrayWithNoCustomIteration(context, initial_entries),
68 &if_fast_js_array);
69 if (variant == Variant::kSet) {
71 variant, initial_entries, context, &var_entries_table,
72 &var_at_least_space_for, &allocate_table);
73 var_mode = Int32Constant(kFastCollection);
74 Goto(&allocate_table);
75 } else {
76 Goto(&allocate_table);
77 }
78 BIND(&if_fast_js_array);
79 {
80 var_mode = Int32Constant(kFastJSArray);
81 if (variant == kWeakSet || variant == kWeakMap) {
82 var_at_least_space_for =
84 } else {
85 // TODO(ishell): consider using array length for all collections
88 var_at_least_space_for = IntPtrConstant(OrderedHashSet::kInitialCapacity);
89 }
90 Goto(&allocate_table);
91 }
92 TVARIABLE(JSReceiver, var_iterator_object);
93 TVARIABLE(Object, var_exception);
94 Label exit(this), from_fast_jsarray(this), from_fast_collection(this),
95 slow_loop(this, Label::kDeferred), if_exception(this, Label::kDeferred);
96 BIND(&allocate_table);
97 {
98 TNode<HeapObject> table =
99 AllocateTable(variant, var_at_least_space_for.value());
100 StoreObjectField(collection, GetTableOffset(variant), table);
101 if (variant == Variant::kSet) {
102 GotoIf(Word32Equal(var_mode.value(), Int32Constant(kFastCollection)),
103 &from_fast_collection);
104 }
105 Branch(Word32Equal(var_mode.value(), Int32Constant(kFastJSArray)),
106 &from_fast_jsarray, &slow_loop);
107 }
108 BIND(&from_fast_jsarray);
109 {
110 Label if_exception_during_fast_iteration(this, Label::kDeferred);
111 TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
112 TNode<JSArray> initial_entries_jsarray =
113 UncheckedCast<JSArray>(initial_entries);
114#if DEBUG
115 CSA_DCHECK(this, IsFastJSArrayWithNoCustomIteration(
116 context, initial_entries_jsarray));
117 TNode<Map> original_initial_entries_map = LoadMap(initial_entries_jsarray);
118#endif
119
120 Label if_may_have_side_effects(this, Label::kDeferred);
121 {
123 this, &if_exception_during_fast_iteration, &var_exception);
125 variant, context, native_context, collection, initial_entries_jsarray,
126 &if_may_have_side_effects, var_index);
127 }
128 Goto(&exit);
129
130 if (variant == kMap || variant == kWeakMap) {
131 BIND(&if_may_have_side_effects);
132#if DEBUG
133 {
134 // Check that add/set function has not been modified.
135 Label if_not_modified(this), if_modified(this);
137 &if_modified);
138 Goto(&if_not_modified);
139 BIND(&if_modified);
140 Unreachable();
141 BIND(&if_not_modified);
142 }
143 CSA_DCHECK(this, TaggedEqual(original_initial_entries_map,
144 LoadMap(initial_entries_jsarray)));
145#endif
146 var_mode = Int32Constant(kSlow);
147 Goto(&allocate_table);
148 }
149 BIND(&if_exception_during_fast_iteration);
150 {
151 // In case exception is thrown during collection population, materialize
152 // the iteator and execute iterator closing protocol. It might be
153 // non-trivial in case "return" callback is added somewhere in the
154 // iterator's prototype chain.
155 TNode<IntPtrT> next_index =
156 IntPtrAdd(var_index.value(), IntPtrConstant(1));
157 var_iterator_object = CreateArrayIterator(
158 LoadNativeContext(context), UncheckedCast<JSArray>(initial_entries),
159 IterationKind::kValues, SmiTag(next_index));
160 Goto(&if_exception);
161 }
162 }
163 if (variant == Variant::kSet) {
164 BIND(&from_fast_collection);
165 {
166 AddConstructorEntriesFromFastCollection(variant, collection,
167 var_entries_table.value());
168 Goto(&exit);
169 }
170 }
171 BIND(&slow_loop);
172 {
174 variant, context, native_context, collection, initial_entries,
175 &if_exception, &var_iterator_object, &var_exception);
176 Goto(&exit);
177 }
178 BIND(&if_exception);
179 {
181 SetPendingMessage(TheHoleConstant());
182 // iterator.next field is not used by IteratorCloseOnException.
183 TorqueStructIteratorRecord iterator = {var_iterator_object.value(), {}};
184 IteratorCloseOnException(context, iterator.object);
185 CallRuntime(Runtime::kReThrowWithMessage, context, var_exception.value(),
186 message);
187 Unreachable();
188 }
189 BIND(&exit);
190}
191
194 TNode<JSAny> collection, TNode<JSArray> fast_jsarray,
195 Label* if_may_have_side_effects, TVariable<IntPtrT>& var_current_index) {
196 TNode<FixedArrayBase> elements = LoadElements(fast_jsarray);
197 TNode<Int32T> elements_kind = LoadElementsKind(fast_jsarray);
199 CSA_DCHECK(this,
200 TaggedEqual(GetAddFunction(variant, native_context, collection),
201 add_func));
202 CSA_DCHECK(this, IsFastJSArrayWithNoCustomIteration(context, fast_jsarray));
205 this, HasInitialCollectionPrototype(variant, native_context, collection));
206
207#if DEBUG
208 TNode<Map> original_collection_map = LoadMap(CAST(collection));
209 TNode<Map> original_fast_js_array_map = LoadMap(fast_jsarray);
210#endif
211 Label exit(this), if_doubles(this), if_smiorobjects(this);
212 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &exit);
213 Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects,
214 &if_doubles);
215 BIND(&if_smiorobjects);
216 {
217 auto set_entry = [&](TNode<IntPtrT> index) {
218 TNode<JSAny> element =
220 AddConstructorEntry(variant, context, collection, add_func, element,
221 if_may_have_side_effects);
222 };
223
224 // Instead of using the slower iteration protocol to iterate over the
225 // elements, a fast loop is used. This assumes that adding an element
226 // to the collection does not call user code that could mutate the elements
227 // or collection.
228 BuildFastLoop<IntPtrT>(var_current_index, IntPtrConstant(0), length,
229 set_entry, 1, LoopUnrollingMode::kNo,
231 Goto(&exit);
232 }
233 BIND(&if_doubles);
234 {
235 // A Map constructor requires entries to be arrays (ex. [key, value]),
236 // so a FixedDoubleArray can never succeed.
237 if (variant == kMap || variant == kWeakMap) {
238 CSA_DCHECK(this, IntPtrGreaterThan(length, IntPtrConstant(0)));
239 TNode<Object> element =
241 ThrowTypeError(context, MessageTemplate::kIteratorValueNotAnObject,
242 element);
243 } else {
244 DCHECK(variant == kSet || variant == kWeakSet);
245 auto set_entry = [&](TNode<IntPtrT> index) {
247 elements, UncheckedCast<IntPtrT>(index)));
248 AddConstructorEntry(variant, context, collection, add_func, entry);
249 };
250 BuildFastLoop<IntPtrT>(var_current_index, IntPtrConstant(0), length,
251 set_entry, 1, LoopUnrollingMode::kNo,
253 Goto(&exit);
254 }
255 }
256 BIND(&exit);
257#if DEBUG
258 CSA_DCHECK(this,
259 TaggedEqual(original_collection_map, LoadMap(CAST(collection))));
260 CSA_DCHECK(this,
261 TaggedEqual(original_fast_js_array_map, LoadMap(fast_jsarray)));
262#endif
263}
264
267 TNode<JSAny> collection, TNode<JSAny> iterable, Label* if_exception,
268 TVariable<JSReceiver>* var_iterator_object,
269 TVariable<Object>* var_exception) {
270 Label exit(this), loop(this);
271 CSA_DCHECK(this, Word32BinaryNot(IsNullOrUndefined(iterable)));
272 TNode<Object> add_func = GetAddFunction(variant, context, collection);
273 IteratorBuiltinsAssembler iterator_assembler(this->state());
274 TorqueStructIteratorRecord iterator =
275 iterator_assembler.GetIterator(context, iterable);
276 *var_iterator_object = iterator.object;
277
278 CSA_DCHECK(this, Word32BinaryNot(IsUndefined(iterator.object)));
279
280 TNode<Map> fast_iterator_result_map = CAST(
281 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX));
282
283 Goto(&loop);
284 BIND(&loop);
285 {
286 TNode<JSReceiver> next = iterator_assembler.IteratorStep(
287 context, iterator, &exit, fast_iterator_result_map);
288 TNode<JSAny> next_value = iterator_assembler.IteratorValue(
289 context, next, fast_iterator_result_map);
290 AddConstructorEntry(variant, context, collection, add_func, next_value,
291 nullptr, if_exception, var_exception);
292 Goto(&loop);
293 }
294 BIND(&exit);
295}
296
298 switch (variant) {
299 case kMap:
300 case kWeakMap:
301 return RootIndex::kset_string;
302 case kSet:
303 case kWeakSet:
304 return RootIndex::kadd_string;
305 }
306 UNREACHABLE();
307}
308
311 TNode<HeapObject> collection, Label* if_modified) {
314
315 // TODO(jgruber): Investigate if this should also fall back to full prototype
316 // verification.
317 static constexpr PrototypeCheckAssembler::Flags flags{
319
320 static constexpr int kNoContextIndex = -1;
321 static_assert(
323
324 using DescriptorIndexNameValue =
326
327 DescriptorIndexNameValue property_to_check{
329 GetAddFunctionNameIndex(variant), kNoContextIndex};
330
331 PrototypeCheckAssembler prototype_check_assembler(
332 state(), flags, native_context,
334 base::Vector<DescriptorIndexNameValue>(&property_to_check, 1));
335
336 TNode<HeapObject> prototype = LoadMapPrototype(LoadMap(collection));
337 Label if_unmodified(this);
338 prototype_check_assembler.CheckAndBranch(prototype, &if_unmodified,
339 if_modified);
340
341 BIND(&if_unmodified);
342}
343
345 TNode<Context> context, TNode<JSFunction> constructor,
347 TNode<BoolT> is_target_unmodified = TaggedEqual(constructor, new_target);
348
349 return Select<JSObject>(
350 is_target_unmodified,
351 [=, this] { return AllocateJSCollectionFast(constructor); },
352 [=, this] {
353 return AllocateJSCollectionSlow(context, constructor, new_target);
354 });
355}
356
358 TNode<JSFunction> constructor) {
359 CSA_DCHECK(this, IsConstructorMap(LoadMap(constructor)));
360 TNode<Map> initial_map =
361 CAST(LoadJSFunctionPrototypeOrInitialMap(constructor));
362 return AllocateJSObjectFromMap(initial_map);
363}
364
366 TNode<Context> context, TNode<JSFunction> constructor,
368 ConstructorBuiltinsAssembler constructor_assembler(this->state());
369 return constructor_assembler.FastNewObject(context, constructor, new_target);
370}
371
373 Variant variant, Handle<String> constructor_function_name,
375 const int kIterableArg = 0;
376 CodeStubArguments args(this, argc);
377 TNode<JSAny> iterable = args.GetOptionalArgumentValue(kIterableArg);
378
379 Label if_undefined(this, Label::kDeferred);
380 GotoIf(IsUndefined(new_target), &if_undefined);
381
384 context, GetConstructor(variant, native_context), CAST(new_target));
385
386 Label add_constructor_entries(this);
387
388 // The empty case.
389 //
390 // This is handled specially to simplify AddConstructorEntries, which is
391 // complex and contains multiple fast paths.
392 GotoIfNot(IsNullOrUndefined(iterable), &add_constructor_entries);
394 StoreObjectField(collection, GetTableOffset(variant), table);
395 Return(collection);
396
397 BIND(&add_constructor_entries);
398 AddConstructorEntries(variant, context, native_context, collection, iterable);
399 Return(collection);
400
401 BIND(&if_undefined);
402 ThrowTypeError(context, MessageTemplate::kConstructorNotFunction,
403 HeapConstantNoHole(constructor_function_name));
404}
405
407 Variant variant, TNode<Context> context, TNode<JSAny> collection) {
408 Handle<String> add_func_name = (variant == kMap || variant == kWeakMap)
409 ? isolate()->factory()->set_string()
410 : isolate()->factory()->add_string();
411 TNode<Object> add_func = GetProperty(context, collection, add_func_name);
412
413 Label exit(this), if_notcallable(this, Label::kDeferred);
414 GotoIf(TaggedIsSmi(add_func), &if_notcallable);
415 GotoIfNot(IsCallable(CAST(add_func)), &if_notcallable);
416 Goto(&exit);
417
418 BIND(&if_notcallable);
419 ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, add_func,
420 HeapConstantNoHole(add_func_name), collection);
421
422 BIND(&exit);
423 return add_func;
424}
425
428 int index;
429 switch (variant) {
430 case kMap:
431 index = Context::JS_MAP_FUN_INDEX;
432 break;
433 case kSet:
434 index = Context::JS_SET_FUN_INDEX;
435 break;
436 case kWeakMap:
437 index = Context::JS_WEAK_MAP_FUN_INDEX;
438 break;
439 case kWeakSet:
440 index = Context::JS_WEAK_SET_FUN_INDEX;
441 break;
442 }
443 return CAST(LoadContextElement(native_context, index));
444}
445
448 int index;
449 switch (variant) {
450 case kMap:
451 index = Context::MAP_SET_INDEX;
452 break;
453 case kSet:
454 index = Context::SET_ADD_INDEX;
455 break;
456 case kWeakMap:
457 index = Context::WEAKMAP_SET_INDEX;
458 break;
459 case kWeakSet:
460 index = Context::WEAKSET_ADD_INDEX;
461 break;
462 }
463 return CAST(LoadContextElement(native_context, index));
464}
465
467 switch (variant) {
468 case kMap:
469 return JSMap::kTableOffset;
470 case kSet:
471 return JSSet::kTableOffset;
472 case kWeakMap:
473 return JSWeakMap::kTableOffset;
474 case kWeakSet:
475 return JSWeakSet::kTableOffset;
476 }
477 UNREACHABLE();
478}
479
480// https://tc39.es/ecma262/#sec-canbeheldweakly
482 const TNode<Object> obj, Label* if_cannot_be_held_weakly) {
483 Label check_symbol_key(this);
484 Label end(this);
485 GotoIf(TaggedIsSmi(obj), if_cannot_be_held_weakly);
486 TNode<Uint16T> instance_type = LoadMapInstanceType(LoadMap(CAST(obj)));
487 GotoIfNot(IsJSReceiverInstanceType(instance_type), &check_symbol_key);
488 // TODO(v8:12547) Shared structs and arrays should only be able to point
489 // to shared values in weak collections. For now, disallow them as weak
490 // collection keys.
492 if_cannot_be_held_weakly);
493 Goto(&end);
494 Bind(&check_symbol_key);
495 GotoIfNot(IsSymbolInstanceType(instance_type), if_cannot_be_held_weakly);
496 TNode<Uint32T> flags = LoadSymbolFlags(CAST(obj));
498 if_cannot_be_held_weakly);
499 Goto(&end);
500 Bind(&end);
501}
502
505 int initial_prototype_index;
506 switch (variant) {
507 case kMap:
508 initial_prototype_index = Context::INITIAL_MAP_PROTOTYPE_MAP_INDEX;
509 break;
510 case kSet:
511 initial_prototype_index = Context::INITIAL_SET_PROTOTYPE_MAP_INDEX;
512 break;
513 case kWeakMap:
514 initial_prototype_index = Context::INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX;
515 break;
516 case kWeakSet:
517 initial_prototype_index = Context::INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX;
518 break;
519 }
520 return CAST(LoadContextElement(native_context, initial_prototype_index));
521}
522
524 Variant variant, TNode<Context> native_context, TNode<JSAny> collection) {
525 TNode<Map> collection_proto_map =
526 LoadMap(LoadMapPrototype(LoadMap(CAST(collection))));
527
528 return TaggedEqual(collection_proto_map,
530}
531
533 TNode<FixedArray> elements, TNode<IntPtrT> index) {
534 TNode<Object> element = UnsafeLoadFixedArrayElement(elements, index);
535 return Select<Object>(
536 IsTheHole(element), [=, this] { return UndefinedConstant(); },
537 [=] { return element; });
538}
539
541 TNode<HeapObject> elements, TNode<IntPtrT> index) {
542 TVARIABLE(Object, entry);
543 Label if_hole(this, Label::kDeferred), next(this);
544 TNode<Float64T> element =
545 LoadFixedDoubleArrayElement(CAST(elements), index, &if_hole);
546 { // not hole
547 entry = AllocateHeapNumberWithValue(element);
548 Goto(&next);
549 }
550 BIND(&if_hole);
551 {
552 entry = UndefinedConstant();
553 Goto(&next);
554 }
555 BIND(&next);
556 return entry.value();
557}
558
559template <typename CollectionType>
561 const TNode<CollectionType> table, const TNode<Uint32T> hash,
562 const std::function<void(TNode<Object>, Label*, Label*)>& key_compare,
563 TVariable<IntPtrT>* entry_start_position, Label* entry_found,
564 Label* not_found) {
565 // Get the index of the bucket.
566 const TNode<Uint32T> number_of_buckets =
568 table, CollectionType::NumberOfBucketsIndex())));
569 const TNode<Uint32T> bucket =
570 Word32And(hash, Uint32Sub(number_of_buckets, Uint32Constant(1)));
572 table, Signed(ChangeUint32ToWord(bucket)),
573 CollectionType::HashTableStartIndex() * kTaggedSize)));
574 const TNode<IntPtrT> number_of_buckets_intptr =
575 Signed(ChangeUint32ToWord(number_of_buckets));
576
577 // Walk the bucket chain.
578 TNode<IntPtrT> entry_start;
579 Label if_key_found(this);
580 {
581 TVARIABLE(IntPtrT, var_entry, first_entry);
582 Label loop(this, {&var_entry, entry_start_position}),
583 continue_next_entry(this);
584 Goto(&loop);
585 BIND(&loop);
586
587 // If the entry index is the not-found sentinel, we are done.
588 GotoIf(IntPtrEqual(var_entry.value(),
589 IntPtrConstant(CollectionType::kNotFound)),
590 not_found);
591
592 // Make sure the entry index is within range.
594 this,
595 UintPtrLessThan(
596 var_entry.value(),
597 PositiveSmiUntag(SmiAdd(
599 table, CollectionType::NumberOfElementsIndex())),
601 table, CollectionType::NumberOfDeletedElementsIndex()))))));
602
603 // Compute the index of the entry relative to kHashTableStartIndex.
604 entry_start =
605 IntPtrAdd(IntPtrMul(var_entry.value(),
606 IntPtrConstant(CollectionType::kEntrySize)),
607 number_of_buckets_intptr);
608
609 // Load the key from the entry.
610 const TNode<Object> candidate_key =
611 UnsafeLoadKeyFromOrderedHashTableEntry(table, entry_start);
612
613 key_compare(candidate_key, &if_key_found, &continue_next_entry);
614
615 BIND(&continue_next_entry);
616 // Load the index of the next entry in the bucket chain.
618 table, entry_start,
619 (CollectionType::HashTableStartIndex() + CollectionType::kChainOffset) *
620 kTaggedSize)));
621
622 Goto(&loop);
623 }
624
625 BIND(&if_key_found);
626 *entry_start_position = entry_start;
627 Goto(entry_found);
628}
629
630// a helper function to unwrap a fast js collection and load its length.
631// var_entries_table is a variable meant to store the unwrapped collection.
632// var_number_of_elements is a variable meant to store the length of the
633// unwrapped collection. the function jumps to if_not_fast_collection if the
634// collection is not a fast js collection.
636 Variant variant, TNode<Object> initial_entries, TNode<Context> context,
637 TVariable<HeapObject>* var_entries_table,
638 TVariable<IntPtrT>* var_number_of_elements, Label* if_not_fast_collection) {
639 Label if_fast_js_set(this), exit(this);
640 DCHECK_EQ(variant, kSet);
642 initial_entries, context, &if_fast_js_set, if_not_fast_collection);
643 BIND(&if_fast_js_set);
644 {
645 *var_entries_table = SetOrSetIteratorToSet(initial_entries);
647 var_entries_table->value(), OrderedHashMap::NumberOfElementsOffset());
648 *var_number_of_elements = PositiveSmiUntag(size_smi);
649 Goto(&exit);
650 }
651 BIND(&exit);
652}
653
655 TNode<JSSet> collection, TNode<OrderedHashSet> table) {
657 collection, GetTableOffset(Variant::kSet));
658
659 TNode<IntPtrT> number_of_buckets =
664 TNode<IntPtrT> number_of_deleted_elements = PositiveSmiUntag(CAST(
666 TNode<IntPtrT> used_capacity =
667 IntPtrAdd(number_of_elements, number_of_deleted_elements);
668 TNode<IntPtrT> loop_bound = IntPtrAdd(
670 number_of_buckets);
671
672 TNode<IntPtrT> number_of_buckets_entry_table =
674 entry_table, OrderedHashSet::NumberOfBucketsIndex())));
675
676 TVARIABLE(JSAny, entry_key);
677 TVARIABLE(IntPtrT, var_entry_table_occupancy, IntPtrConstant(0));
678 VariableList loop_vars({&var_entry_table_occupancy}, zone());
679 Label exit(this);
680
681 auto set_entry = [&](TNode<IntPtrT> index) {
682 entry_key = UnsafeLoadKeyFromOrderedHashTableEntry(table, index);
683 Label if_key_is_not_hole(this), continue_loop(this);
684 Branch(IsHashTableHole(entry_key.value()), &continue_loop,
685 &if_key_is_not_hole);
686 BIND(&if_key_is_not_hole);
687 {
688 AddNewToOrderedHashSet(entry_table, entry_key.value(),
689 number_of_buckets_entry_table,
690 var_entry_table_occupancy.value());
691 Increment(&var_entry_table_occupancy, 1);
692 Goto(&continue_loop);
693 }
694 BIND(&continue_loop);
695 return;
696 };
697
698 // Instead of using the slower iteration protocol to iterate over the
699 // elements, a fast loop is used. This assumes that adding an element
700 // to the collection does not call user code that could mutate the elements
701 // or collection. The iteration is based on the layout of the ordered hash
702 // table.
703 BuildFastLoop<IntPtrT>(loop_vars, number_of_buckets, loop_bound, set_entry,
706 Goto(&exit);
707 BIND(&exit);
708}
709
711 Variant variant, TNode<HeapObject> collection,
712 TNode<HeapObject> source_table) {
713 if (variant == kSet) {
714 AddConstructorEntriesFromSet(CAST(collection), CAST(source_table));
715 return;
716 }
717}
718
719template <typename IteratorType>
721 const TNode<Context> context, int map_index,
722 const TNode<HeapObject> collection) {
723 const TNode<Object> table =
724 LoadObjectField(collection, JSCollection::kTableOffset);
726 const TNode<Map> iterator_map =
727 CAST(LoadContextElement(native_context, map_index));
728 const TNode<HeapObject> iterator =
729 AllocateInNewSpace(IteratorType::kHeaderSize);
730 StoreMapNoWriteBarrier(iterator, iterator_map);
731 StoreObjectFieldRoot(iterator, IteratorType::kPropertiesOrHashOffset,
732 RootIndex::kEmptyFixedArray);
733 StoreObjectFieldRoot(iterator, IteratorType::kElementsOffset,
734 RootIndex::kEmptyFixedArray);
735 StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kTableOffset, table);
736 StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset,
737 SmiConstant(0));
738 return iterator;
739}
740
742 Variant variant, TNode<IntPtrT> at_least_space_for) {
743 if (variant == kMap) {
744 return AllocateOrderedHashMap();
745 } else {
746 DCHECK_EQ(variant, kSet);
747 TNode<IntPtrT> capacity = HashTableComputeCapacity(at_least_space_for);
748 return AllocateOrderedHashSet(capacity);
749 }
750}
751
753 auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
754 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
755 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
756 auto context = Parameter<Context>(Descriptor::kContext);
757
758 GenerateConstructor(kMap, isolate()->factory()->Map_string(), new_target,
759 argc, context);
760}
761
763 auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
764 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
765 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
766 auto context = Parameter<Context>(Descriptor::kContext);
767
768 GenerateConstructor(kSet, isolate()->factory()->Set_string(), new_target,
769 argc, context);
770}
771
773 const TNode<HeapObject> key) {
774 const TNode<ExternalReference> function_addr =
775 ExternalConstant(ExternalReference::get_or_create_hash_raw());
776 const TNode<ExternalReference> isolate_ptr =
778
780 MachineType type_tagged = MachineType::AnyTagged();
781
782 TNode<Smi> result = CAST(CallCFunction(function_addr, type_tagged,
783 std::make_pair(type_ptr, isolate_ptr),
784 std::make_pair(type_tagged, key)));
785
786 return result;
787}
788
790 const TNode<HeapObject> key) {
791 const TNode<ExternalReference> function_addr =
792 ExternalConstant(ExternalReference::orderedhashmap_gethash_raw());
793 const TNode<ExternalReference> isolate_ptr =
795
797 MachineType type_tagged = MachineType::AnyTagged();
798
799 TNode<Smi> result = CAST(CallCFunction(function_addr, type_tagged,
800 std::make_pair(type_ptr, isolate_ptr),
801 std::make_pair(type_tagged, key)));
803}
804
806 const TNode<HeapObject> key) {
807 TVARIABLE(Uint32T, var_hash);
808 Label if_receiver(this), if_other(this), done(this);
809 Branch(IsJSReceiver(key), &if_receiver, &if_other);
810
811 BIND(&if_receiver);
812 {
814 Goto(&done);
815 }
816
817 BIND(&if_other);
818 {
819 var_hash = CallGetHashRaw(key);
820 Goto(&done);
821 }
822
823 BIND(&done);
824 return var_hash.value();
825}
826
828 TNode<Object> candidate_key,
829 Label* if_same,
830 Label* if_not_same) {
831 // If the key is the same, we are done.
832 GotoIf(TaggedEqual(candidate_key, key_smi), if_same);
833
834 // If the candidate key is smi, then it must be different (because
835 // we already checked for equality above).
836 GotoIf(TaggedIsSmi(candidate_key), if_not_same);
837
838 // If the candidate key is not smi, we still have to check if it is a
839 // heap number with the same value.
840 GotoIfNot(IsHeapNumber(CAST(candidate_key)), if_not_same);
841
842 const TNode<Float64T> candidate_key_number =
843 LoadHeapNumberValue(CAST(candidate_key));
844 const TNode<Float64T> key_number = SmiToFloat64(key_smi);
845
846 GotoIf(Float64Equal(candidate_key_number, key_number), if_same);
847
848 Goto(if_not_same);
849}
850
852 Label* if_true, Label* if_false) {
853 TNode<PropertyCell> protector_cell = MapIteratorProtectorConstant();
854 DCHECK(i::IsPropertyCell(isolate()->heap()->map_iterator_protector()));
855 Branch(
856 TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
858 if_true, if_false);
859}
860
863 TNode<Context> context,
864 Label* if_true,
865 Label* if_false) {
866 Label if_key_or_value_iterator(this), extra_checks(this);
867
868 // Check if iterator is a keys or values JSMapIterator.
869 GotoIf(TaggedIsSmi(iterator), if_false);
870 TNode<Map> iter_map = LoadMap(CAST(iterator));
871 const TNode<Uint16T> instance_type = LoadMapInstanceType(iter_map);
872 GotoIf(InstanceTypeEqual(instance_type, JS_MAP_KEY_ITERATOR_TYPE),
873 &if_key_or_value_iterator);
874 Branch(InstanceTypeEqual(instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
875 &if_key_or_value_iterator, if_false);
876
877 BIND(&if_key_or_value_iterator);
878 // Check that the iterator is not partially consumed.
879 const TNode<Object> index =
880 LoadObjectField(CAST(iterator), JSMapIterator::kIndexOffset);
881 GotoIfNot(TaggedEqual(index, SmiConstant(0)), if_false);
882 BranchIfMapIteratorProtectorValid(&extra_checks, if_false);
883
884 BIND(&extra_checks);
885 // Check if the iterator object has the original %MapIteratorPrototype%.
887 const TNode<Object> initial_map_iter_proto = LoadContextElement(
888 native_context, Context::INITIAL_MAP_ITERATOR_PROTOTYPE_INDEX);
889 const TNode<HeapObject> map_iter_proto = LoadMapPrototype(iter_map);
890 GotoIfNot(TaggedEqual(map_iter_proto, initial_map_iter_proto), if_false);
891
892 // Check if the original MapIterator prototype has the original
893 // %IteratorPrototype%.
894 const TNode<Object> initial_iter_proto = LoadContextElement(
895 native_context, Context::INITIAL_ITERATOR_PROTOTYPE_INDEX);
896 const TNode<HeapObject> iter_proto =
897 LoadMapPrototype(LoadMap(map_iter_proto));
898 Branch(TaggedEqual(iter_proto, initial_iter_proto), if_true, if_false);
899}
900
906 assembler.BranchIfIterableWithOriginalKeyOrValueMapIterator(
907 iterable, context, if_true, if_false);
908}
909
911 Label* if_true, Label* if_false) {
912 const TNode<PropertyCell> protector_cell = SetIteratorProtectorConstant();
913 DCHECK(i::IsPropertyCell(isolate()->heap()->set_iterator_protector()));
914 Branch(
915 TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
917 if_true, if_false);
918}
919
921 TNode<Object> iterable, TNode<Context> context, Label* if_true,
922 Label* if_false) {
923 Label if_set(this), if_value_iterator(this), check_protector(this);
924 TVARIABLE(BoolT, var_result);
925
926 GotoIf(TaggedIsSmi(iterable), if_false);
927 TNode<Map> iterable_map = LoadMap(CAST(iterable));
928 const TNode<Uint16T> instance_type = LoadMapInstanceType(iterable_map);
929
930 GotoIf(InstanceTypeEqual(instance_type, JS_SET_TYPE), &if_set);
931 Branch(InstanceTypeEqual(instance_type, JS_SET_VALUE_ITERATOR_TYPE),
932 &if_value_iterator, if_false);
933
934 BIND(&if_set);
935 // Check if the set object has the original Set prototype.
936 const TNode<Object> initial_set_proto = LoadContextElement(
937 LoadNativeContext(context), Context::INITIAL_SET_PROTOTYPE_INDEX);
938 const TNode<HeapObject> set_proto = LoadMapPrototype(iterable_map);
939 GotoIfNot(TaggedEqual(set_proto, initial_set_proto), if_false);
940 Goto(&check_protector);
941
942 BIND(&if_value_iterator);
943 // Check that the iterator is not partially consumed.
944 const TNode<Object> index =
945 LoadObjectField(CAST(iterable), JSSetIterator::kIndexOffset);
946 GotoIfNot(TaggedEqual(index, SmiConstant(0)), if_false);
947
948 // Check if the iterator object has the original SetIterator prototype.
950 const TNode<Object> initial_set_iter_proto = LoadContextElement(
951 native_context, Context::INITIAL_SET_ITERATOR_PROTOTYPE_INDEX);
952 const TNode<HeapObject> set_iter_proto = LoadMapPrototype(iterable_map);
953 GotoIfNot(TaggedEqual(set_iter_proto, initial_set_iter_proto), if_false);
954
955 // Check if the original SetIterator prototype has the original
956 // %IteratorPrototype%.
957 const TNode<Object> initial_iter_proto = LoadContextElement(
958 native_context, Context::INITIAL_ITERATOR_PROTOTYPE_INDEX);
959 const TNode<HeapObject> iter_proto =
960 LoadMapPrototype(LoadMap(set_iter_proto));
961 GotoIfNot(TaggedEqual(iter_proto, initial_iter_proto), if_false);
962 Goto(&check_protector);
963
964 BIND(&check_protector);
965 BranchIfSetIteratorProtectorValid(if_true, if_false);
966}
967
973 assembler.BranchIfIterableWithOriginalValueSetIterator(iterable, context,
974 if_true, if_false);
975}
976
977// A helper function to help extract the {table} from either a Set or
978// SetIterator. The function has a side effect of marking the
979// SetIterator (if SetIterator is passed) as exhausted.
981 TNode<Object> iterable) {
982 TVARIABLE(OrderedHashSet, var_table);
983 Label if_set(this), if_iterator(this), done(this);
984
985 const TNode<Uint16T> instance_type = LoadInstanceType(CAST(iterable));
986 Branch(InstanceTypeEqual(instance_type, JS_SET_TYPE), &if_set, &if_iterator);
987
988 BIND(&if_set);
989 {
990 // {iterable} is a JSSet.
991 var_table = LoadObjectField<OrderedHashSet>(CAST(iterable),
993 Goto(&done);
994 }
995
996 BIND(&if_iterator);
997 {
998 // {iterable} is a JSSetIterator.
999 // Transition the {iterable} table if necessary.
1000 TNode<JSSetIterator> iterator = CAST(iterable);
1003 std::tie(table, index) =
1005 CSA_DCHECK(this, IntPtrEqual(index, IntPtrConstant(0)));
1006 var_table = table;
1007 // Set the {iterable} to exhausted if it's an iterator.
1008 StoreObjectFieldRoot(iterator, JSSetIterator::kTableOffset,
1009 RootIndex::kEmptyOrderedHashSet);
1012 StoreObjectFieldNoWriteBarrier(iterator, JSSetIterator::kIndexOffset,
1013 SmiTag(number_of_elements));
1014 Goto(&done);
1015 }
1016
1017 BIND(&done);
1018 return var_table.value();
1019}
1020
1022 TNode<Context> context, TNode<JSMapIterator> iterator) {
1023 // Transition the {iterator} table if necessary.
1026 std::tie(table, index) =
1028 CSA_DCHECK(this, IntPtrEqual(index, IntPtrConstant(0)));
1029
1030 TNode<Smi> size_smi =
1032 TNode<IntPtrT> size = PositiveSmiUntag(size_smi);
1033
1035 TNode<Map> array_map =
1037 TNode<JSArray> array = AllocateJSArray(kind, array_map, size, size_smi);
1038 TNode<FixedArray> elements = CAST(LoadElements(array));
1039
1040 const int first_element_offset =
1042 TNode<IntPtrT> first_to_element_offset =
1044 TVARIABLE(
1045 IntPtrT, var_offset,
1046 IntPtrAdd(first_to_element_offset, IntPtrConstant(first_element_offset)));
1047 TVARIABLE(IntPtrT, var_index, index);
1048 VariableList vars({&var_index, &var_offset}, zone());
1049 Label done(this, {&var_index}), loop(this, vars), continue_loop(this, vars),
1050 write_key(this, vars), write_value(this, vars);
1051
1052 Goto(&loop);
1053
1054 BIND(&loop);
1055 {
1056 // Read the next entry from the {table}, skipping holes.
1057 TNode<Object> entry_key;
1058 TNode<IntPtrT> entry_start_position;
1059 TNode<IntPtrT> cur_index;
1060 std::tie(entry_key, entry_start_position, cur_index) =
1061 NextSkipHashTableHoles<OrderedHashMap>(table, var_index.value(), &done);
1062
1063 // Decide to write key or value.
1064 Branch(
1065 InstanceTypeEqual(LoadInstanceType(iterator), JS_MAP_KEY_ITERATOR_TYPE),
1066 &write_key, &write_value);
1067
1068 BIND(&write_key);
1069 {
1070 Store(elements, var_offset.value(), entry_key);
1071 Goto(&continue_loop);
1072 }
1073
1074 BIND(&write_value);
1075 {
1077 JS_MAP_VALUE_ITERATOR_TYPE));
1078 TNode<Object> entry_value =
1079 UnsafeLoadValueFromOrderedHashMapEntry(table, entry_start_position);
1080
1081 Store(elements, var_offset.value(), entry_value);
1082 Goto(&continue_loop);
1083 }
1084
1085 BIND(&continue_loop);
1086 {
1087 // Increment the array offset and continue the loop to the next entry.
1088 var_index = cur_index;
1089 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(kTaggedSize));
1090 Goto(&loop);
1091 }
1092 }
1093
1094 BIND(&done);
1095 // Set the {iterator} to exhausted.
1096 StoreObjectFieldRoot(iterator, JSMapIterator::kTableOffset,
1097 RootIndex::kEmptyOrderedHashMap);
1098 StoreObjectFieldNoWriteBarrier(iterator, JSMapIterator::kIndexOffset,
1099 SmiTag(var_index.value()));
1100 return UncheckedCast<JSArray>(array);
1101}
1102
1104 auto context = Parameter<Context>(Descriptor::kContext);
1105 auto iterator = Parameter<JSMapIterator>(Descriptor::kSource);
1106 Return(MapIteratorToList(context, iterator));
1107}
1108
1110 TNode<Context> context, TNode<HeapObject> iterable) {
1112 TNode<Smi> size_smi =
1114 TNode<IntPtrT> size = PositiveSmiUntag(size_smi);
1115
1117 TNode<Map> array_map =
1119 TNode<JSArray> array = AllocateJSArray(kind, array_map, size, size_smi);
1120 TNode<FixedArray> elements = CAST(LoadElements(array));
1121
1122 const int first_element_offset =
1124 TNode<IntPtrT> first_to_element_offset =
1126 TVARIABLE(
1127 IntPtrT, var_offset,
1128 IntPtrAdd(first_to_element_offset, IntPtrConstant(first_element_offset)));
1129 TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
1130 Label done(this), loop(this, {&var_index, &var_offset});
1131
1132 Goto(&loop);
1133
1134 BIND(&loop);
1135 {
1136 // Read the next entry from the {table}, skipping holes.
1137 TNode<Object> entry_key;
1138 TNode<IntPtrT> entry_start_position;
1139 TNode<IntPtrT> cur_index;
1140 std::tie(entry_key, entry_start_position, cur_index) =
1141 NextSkipHashTableHoles<OrderedHashSet>(table, var_index.value(), &done);
1142
1143 Store(elements, var_offset.value(), entry_key);
1144
1145 var_index = cur_index;
1146 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(kTaggedSize));
1147 Goto(&loop);
1148 }
1149
1150 BIND(&done);
1151 return UncheckedCast<JSArray>(array);
1152}
1153
1155 auto context = Parameter<Context>(Descriptor::kContext);
1156 auto object = Parameter<HeapObject>(Descriptor::kSource);
1157 Return(SetOrSetIteratorToList(context, object));
1158}
1159
1162 // See v8::internal::ComputeUnseededHash()
1164 hash = Int32Add(Word32Xor(hash, Int32Constant(0xFFFFFFFF)),
1165 Word32Shl(hash, Int32Constant(15)));
1166 hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(12)));
1167 hash = Int32Add(hash, Word32Shl(hash, Int32Constant(2)));
1168 hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(4)));
1169 hash = Int32Mul(hash, Int32Constant(2057));
1170 hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(16)));
1171 return Word32And(hash, Int32Constant(0x3FFFFFFF));
1172}
1173
1174template <typename CollectionType>
1177 Label* entry_found, Label* not_found) {
1178 const TNode<IntPtrT> key_untagged = SmiUntag(smi_key);
1179 const TNode<Uint32T> hash = Unsigned(ComputeUnseededHash(key_untagged));
1182 table, hash,
1183 [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1184 SameValueZeroSmi(smi_key, other_key, if_same, if_not_same);
1185 },
1186 result, entry_found, not_found);
1187}
1188
1189template <typename CollectionType>
1191 TNode<CollectionType> table, TNode<String> key_tagged,
1192 TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1193 const TNode<Uint32T> hash = ComputeStringHash(key_tagged);
1196 table, hash,
1197 [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1198 SameValueZeroString(key_tagged, other_key, if_same, if_not_same);
1199 },
1200 result, entry_found, not_found);
1201}
1202
1203template <typename CollectionType>
1205 TNode<CollectionType> table, TNode<HeapNumber> key_heap_number,
1206 TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1207 const TNode<Uint32T> hash = CallGetHashRaw(key_heap_number);
1209 const TNode<Float64T> key_float = LoadHeapNumberValue(key_heap_number);
1211 table, hash,
1212 [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1213 SameValueZeroHeapNumber(key_float, other_key, if_same, if_not_same);
1214 },
1215 result, entry_found, not_found);
1216}
1217
1218template <typename CollectionType>
1220 TNode<CollectionType> table, TNode<BigInt> key_big_int,
1221 TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1222 const TNode<Uint32T> hash = CallGetHashRaw(key_big_int);
1225 table, hash,
1226 [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1227 SameValueZeroBigInt(key_big_int, other_key, if_same, if_not_same);
1228 },
1229 result, entry_found, not_found);
1230}
1231
1232template <typename CollectionType>
1234 TNode<CollectionType> table, TNode<HeapObject> key_heap_object,
1235 TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1236 const TNode<Uint32T> hash = GetHash(key_heap_object);
1239 table, hash,
1240 [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1241 Branch(TaggedEqual(key_heap_object, other_key), if_same, if_not_same);
1242 },
1243 result, entry_found, not_found);
1244}
1245
1247 TNode<String> string_key) {
1248 TVARIABLE(Uint32T, var_result);
1249
1250 Label hash_not_computed(this), done(this, &var_result);
1251 const TNode<Uint32T> hash = LoadNameHash(string_key, &hash_not_computed);
1252 var_result = hash;
1253 Goto(&done);
1254
1255 BIND(&hash_not_computed);
1256 var_result = CallGetHashRaw(string_key);
1257 Goto(&done);
1258
1259 BIND(&done);
1260 return var_result.value();
1261}
1262
1264 TNode<String> key_string, TNode<Object> candidate_key, Label* if_same,
1265 Label* if_not_same) {
1266 // If the candidate is not a string, the keys are not equal.
1267 GotoIf(TaggedIsSmi(candidate_key), if_not_same);
1268 GotoIfNot(IsString(CAST(candidate_key)), if_not_same);
1269
1270 GotoIf(TaggedEqual(key_string, candidate_key), if_same);
1271 BranchIfStringEqual(key_string, CAST(candidate_key), if_same, if_not_same);
1272}
1273
1275 TNode<BigInt> key, TNode<Object> candidate_key, Label* if_same,
1276 Label* if_not_same) {
1277 GotoIf(TaggedIsSmi(candidate_key), if_not_same);
1278 GotoIfNot(IsBigInt(CAST(candidate_key)), if_not_same);
1279
1280 Branch(TaggedEqual(CallRuntime(Runtime::kBigIntEqualToBigInt,
1281 NoContextConstant(), key, candidate_key),
1282 TrueConstant()),
1283 if_same, if_not_same);
1284}
1285
1287 TNode<Float64T> key_float, TNode<Object> candidate_key, Label* if_same,
1288 Label* if_not_same) {
1289 Label if_smi(this), if_keyisnan(this);
1290
1291 GotoIf(TaggedIsSmi(candidate_key), &if_smi);
1292 GotoIfNot(IsHeapNumber(CAST(candidate_key)), if_not_same);
1293
1294 {
1295 // {candidate_key} is a heap number.
1296 const TNode<Float64T> candidate_float =
1297 LoadHeapNumberValue(CAST(candidate_key));
1298 GotoIf(Float64Equal(key_float, candidate_float), if_same);
1299
1300 // SameValueZero needs to treat NaNs as equal. First check if {key_float}
1301 // is NaN.
1302 BranchIfFloat64IsNaN(key_float, &if_keyisnan, if_not_same);
1303
1304 BIND(&if_keyisnan);
1305 {
1306 // Return true iff {candidate_key} is NaN.
1307 Branch(Float64Equal(candidate_float, candidate_float), if_not_same,
1308 if_same);
1309 }
1310 }
1311
1312 BIND(&if_smi);
1313 {
1314 const TNode<Float64T> candidate_float = SmiToFloat64(CAST(candidate_key));
1315 Branch(Float64Equal(key_float, candidate_float), if_same, if_not_same);
1316 }
1317}
1318
1319TF_BUILTIN(OrderedHashTableHealIndex, CollectionsBuiltinsAssembler) {
1320 auto table = Parameter<HeapObject>(Descriptor::kTable);
1321 auto index = Parameter<Smi>(Descriptor::kIndex);
1322 Label return_index(this), return_zero(this);
1323
1324 // Check if we need to update the {index}.
1325 GotoIfNot(SmiLessThan(SmiConstant(0), index), &return_zero);
1326
1327 // Check if the {table} was cleared.
1330 TNode<Int32T> number_of_deleted_elements = LoadAndUntagToWord32ObjectField(
1334 GotoIf(Word32Equal(number_of_deleted_elements,
1336 &return_zero);
1337
1338 TVARIABLE(Int32T, var_i, Int32Constant(0));
1339 TVARIABLE(Smi, var_index, index);
1340 Label loop(this, {&var_i, &var_index});
1341 Goto(&loop);
1342 BIND(&loop);
1343 {
1344 TNode<Int32T> i = var_i.value();
1345 GotoIfNot(Int32LessThan(i, number_of_deleted_elements), &return_index);
1346 static_assert(OrderedHashMap::RemovedHolesIndex() ==
1348 TNode<Smi> removed_index = CAST(LoadFixedArrayElement(
1349 CAST(table), ChangeUint32ToWord(i),
1351 GotoIf(SmiGreaterThanOrEqual(removed_index, index), &return_index);
1352 Decrement(&var_index);
1353 var_i = Int32Add(var_i.value(), Int32Constant(1));
1354 Goto(&loop);
1355 }
1356
1357 BIND(&return_index);
1358 Return(var_index.value());
1359
1360 BIND(&return_zero);
1361 Return(SmiConstant(0));
1362}
1363
1364template <typename TableType>
1365std::pair<TNode<TableType>, TNode<IntPtrT>>
1367 const TNode<TableType> table, const TNode<IntPtrT> index,
1368 UpdateInTransition<TableType> const& update_in_transition) {
1369 TVARIABLE(IntPtrT, var_index, index);
1370 TVARIABLE(TableType, var_table, table);
1371 Label if_done(this), if_transition(this, Label::kDeferred);
1373 LoadObjectField(var_table.value(), TableType::NextTableOffset())),
1374 &if_done, &if_transition);
1375
1376 BIND(&if_transition);
1377 {
1378 Label loop(this, {&var_table, &var_index}), done_loop(this);
1379 Goto(&loop);
1380 BIND(&loop);
1381 {
1382 TNode<TableType> current_table = var_table.value();
1383 TNode<IntPtrT> current_index = var_index.value();
1384
1385 TNode<Object> next_table =
1386 LoadObjectField(current_table, TableType::NextTableOffset());
1387 GotoIf(TaggedIsSmi(next_table), &done_loop);
1388
1389 var_table = CAST(next_table);
1390 var_index = SmiUntag(CAST(CallBuiltin(Builtin::kOrderedHashTableHealIndex,
1391 NoContextConstant(), current_table,
1392 SmiTag(current_index))));
1393 Goto(&loop);
1394 }
1395 BIND(&done_loop);
1396
1397 // Update with the new {table} and {index}.
1398 update_in_transition(var_table.value(), var_index.value());
1399 Goto(&if_done);
1400 }
1401
1402 BIND(&if_done);
1403 return {var_table.value(), var_index.value()};
1404}
1405
1406template <typename IteratorType, typename TableType>
1407std::pair<TNode<TableType>, TNode<IntPtrT>>
1409 const TNode<IteratorType> iterator) {
1410 return Transition<TableType>(
1411 CAST(LoadObjectField(iterator, IteratorType::kTableOffset)),
1412 LoadAndUntagPositiveSmiObjectField(iterator, IteratorType::kIndexOffset),
1413 [this, iterator](const TNode<TableType> table,
1414 const TNode<IntPtrT> index) {
1415 // Update the {iterator} with the new state.
1416 StoreObjectField(iterator, IteratorType::kTableOffset, table);
1417 StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset,
1418 SmiTag(index));
1419 });
1420}
1421
1422TorqueStructOrderedHashSetIndexPair
1424 const TNode<OrderedHashSet> table_arg, const TNode<IntPtrT> index_arg) {
1427 std::tie(table, index) = Transition<OrderedHashSet>(
1428 table_arg, index_arg,
1429 [](const TNode<OrderedHashSet>, const TNode<IntPtrT>) {});
1430 return TorqueStructOrderedHashSetIndexPair{table, index};
1431}
1432
1433template <typename TableType>
1434std::tuple<TNode<JSAny>, TNode<IntPtrT>, TNode<IntPtrT>>
1436 TNode<IntPtrT> index,
1437 Label* if_end) {
1438 // Compute the used capacity for the {table}.
1440 table, TableType::NumberOfBucketsOffset());
1442 table, TableType::NumberOfElementsOffset());
1443 TNode<Int32T> number_of_deleted_elements = LoadAndUntagToWord32ObjectField(
1444 table, TableType::NumberOfDeletedElementsOffset());
1445 TNode<Int32T> used_capacity =
1446 Int32Add(number_of_elements, number_of_deleted_elements);
1447
1448 return NextSkipHashTableHoles(table, number_of_buckets, used_capacity, index,
1449 if_end);
1450}
1451
1452template <typename TableType>
1453std::tuple<TNode<JSAny>, TNode<IntPtrT>, TNode<IntPtrT>>
1455 TNode<TableType> table, TNode<Int32T> number_of_buckets,
1456 TNode<Int32T> used_capacity, TNode<IntPtrT> index, Label* if_end) {
1457 CSA_DCHECK(this, Word32Equal(number_of_buckets,
1459 table, TableType::NumberOfBucketsOffset())));
1460 CSA_DCHECK(
1461 this,
1463 used_capacity,
1465 table, TableType::NumberOfElementsOffset()),
1467 table, TableType::NumberOfDeletedElementsOffset()))));
1468
1469 TNode<JSAny> entry_key;
1470 TNode<Int32T> entry_start_position;
1471 TVARIABLE(Int32T, var_index, TruncateIntPtrToInt32(index));
1472 Label loop(this, &var_index), done_loop(this);
1473 Goto(&loop);
1474 BIND(&loop);
1475 {
1476 GotoIfNot(Int32LessThan(var_index.value(), used_capacity), if_end);
1477 entry_start_position = Int32Add(
1478 Int32Mul(var_index.value(), Int32Constant(TableType::kEntrySize)),
1479 number_of_buckets);
1481 table, ChangePositiveInt32ToIntPtr(entry_start_position));
1482 var_index = Int32Add(var_index.value(), Int32Constant(1));
1483 Branch(IsHashTableHole(entry_key), &loop, &done_loop);
1484 }
1485
1486 BIND(&done_loop);
1487 return {entry_key, ChangePositiveInt32ToIntPtr(entry_start_position),
1488 ChangePositiveInt32ToIntPtr(var_index.value())};
1489}
1490
1491template <typename CollectionType>
1492TorqueStructKeyIndexPair
1494 const TNode<CollectionType> table, const TNode<Int32T> number_of_buckets,
1495 const TNode<Int32T> used_capacity, const TNode<IntPtrT> index,
1496 Label* if_end) {
1497 // Unmodified tables do not have transitions.
1499 table, CollectionType::NextTableOffset())));
1500
1502 TNode<IntPtrT> entry_start_position;
1503 TNode<IntPtrT> next_index;
1504
1505 std::tie(key, entry_start_position, next_index) = NextSkipHashTableHoles(
1506 table, number_of_buckets, used_capacity, index, if_end);
1507
1508 return TorqueStructKeyIndexPair{key, next_index};
1509}
1510
1511template TorqueStructKeyIndexPair
1513 const TNode<OrderedHashMap> table, const TNode<Int32T> number_of_buckets,
1514 const TNode<Int32T> used_capacity, const TNode<IntPtrT> index,
1515 Label* if_end);
1516template TorqueStructKeyIndexPair
1518 const TNode<OrderedHashSet> table, const TNode<Int32T> number_of_buckets,
1519 const TNode<Int32T> used_capacity, const TNode<IntPtrT> index,
1520 Label* if_end);
1521
1522template <typename CollectionType>
1524 const TNode<CollectionType> table, const TNode<IntPtrT> index,
1525 Label* if_end) {
1527 TNode<IntPtrT> entry_start_position;
1528 TNode<IntPtrT> next_index;
1529
1530 std::tie(key, entry_start_position, next_index) =
1531 NextSkipHashTableHoles<CollectionType>(table, index, if_end);
1532
1533 return TorqueStructKeyIndexPair{key, next_index};
1534}
1535
1536template TorqueStructKeyIndexPair
1538 const TNode<OrderedHashMap> table, const TNode<IntPtrT> index,
1539 Label* if_end);
1540template TorqueStructKeyIndexPair
1542 const TNode<OrderedHashSet> table, const TNode<IntPtrT> index,
1543 Label* if_end);
1544
1545TorqueStructKeyValueIndexTuple
1547 const TNode<OrderedHashMap> table, const TNode<Int32T> number_of_buckets,
1548 const TNode<Int32T> used_capacity, const TNode<IntPtrT> index,
1549 Label* if_end) {
1551 TNode<IntPtrT> entry_start_position;
1552 TNode<IntPtrT> next_index;
1553
1554 std::tie(key, entry_start_position, next_index) = NextSkipHashTableHoles(
1555 table, number_of_buckets, used_capacity, index, if_end);
1556
1557 TNode<JSAny> value =
1558 CAST(UnsafeLoadValueFromOrderedHashMapEntry(table, entry_start_position));
1559
1560 return TorqueStructKeyValueIndexTuple{key, value, next_index};
1561}
1562
1563TorqueStructKeyValueIndexTuple
1565 const TNode<OrderedHashMap> table, const TNode<IntPtrT> index,
1566 Label* if_end) {
1568 TNode<IntPtrT> entry_start_position;
1569 TNode<IntPtrT> next_index;
1570
1571 std::tie(key, entry_start_position, next_index) =
1572 NextSkipHashTableHoles(table, index, if_end);
1573
1574 TNode<JSAny> value =
1575 CAST(UnsafeLoadValueFromOrderedHashMapEntry(table, entry_start_position));
1576
1577 return TorqueStructKeyValueIndexTuple{key, value, next_index};
1578}
1579
1581 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1582 const auto key = Parameter<Object>(Descriptor::kKey);
1583 const auto context = Parameter<Context>(Descriptor::kContext);
1584
1585 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.get");
1586
1587 const TNode<Object> table =
1588 LoadObjectField<Object>(CAST(receiver), JSMap::kTableOffset);
1589 TNode<Smi> index =
1590 CAST(CallBuiltin(Builtin::kFindOrderedHashMapEntry, context, table, key));
1591
1592 Label if_found(this), if_not_found(this);
1593 Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found,
1594 &if_not_found);
1595
1596 BIND(&if_found);
1597 Return(LoadValueFromOrderedHashMapEntry(CAST(table), SmiUntag(index)));
1598
1599 BIND(&if_not_found);
1600 Return(UndefinedConstant());
1601}
1602
1604 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1605 const auto key = Parameter<Object>(Descriptor::kKey);
1606 const auto context = Parameter<Context>(Descriptor::kContext);
1607
1608 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.has");
1609
1610 const TNode<OrderedHashMap> table =
1611 CAST(LoadObjectField(CAST(receiver), JSMap::kTableOffset));
1612
1613 Label if_found(this), if_not_found(this);
1614 Branch(TableHasKey(context, table, key), &if_found, &if_not_found);
1615
1616 BIND(&if_found);
1617 Return(TrueConstant());
1618
1619 BIND(&if_not_found);
1620 Return(FalseConstant());
1621}
1622
1624 const TNode<Object> context, TNode<OrderedHashMap> table,
1626 TNode<Smi> index =
1627 CAST(CallBuiltin(Builtin::kFindOrderedHashMapEntry, context, table, key));
1628
1629 return SmiGreaterThanOrEqual(index, SmiConstant(0));
1630}
1631
1633 const TNode<JSAny> key) {
1635 Label done(this);
1636
1637 GotoIf(TaggedIsSmi(key), &done);
1638 GotoIfNot(IsHeapNumber(CAST(key)), &done);
1640 GotoIfNot(Float64Equal(number, Float64Constant(0.0)), &done);
1641 // We know the value is zero, so we take the key to be Smi 0.
1642 // Another option would be to normalize to Smi here.
1643 result = SmiConstant(0);
1644 Goto(&done);
1645
1646 BIND(&done);
1647 return result.value();
1648}
1649
1650template <typename CollectionType>
1652 const TNode<CollectionType> table, const TNode<Object> key,
1654 const StoreAtEntry<CollectionType>& store_at_new_entry,
1655 const StoreAtEntry<CollectionType>& store_at_existing_entry) {
1656 TVARIABLE(CollectionType, table_var, table);
1657 TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
1658 Label entry_found(this), not_found(this), done(this);
1659
1661 table, key, &entry_start_position_or_hash, &entry_found, &not_found);
1662
1663 BIND(&entry_found);
1664 {
1665 // If we found the entry, we just store the value there.
1666 store_at_existing_entry(table, entry_start_position_or_hash.value());
1667 Goto(&done);
1668 }
1669
1670 Label no_hash(this), add_entry(this), store_new_entry(this);
1671 BIND(&not_found);
1672 {
1673 // If we have a hash code, we can start adding the new entry.
1674 GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(),
1675 IntPtrConstant(0)),
1676 &add_entry);
1677
1678 // Otherwise, go to runtime to compute the hash code.
1679 entry_start_position_or_hash = SmiUntag(CallGetOrCreateHashRaw(CAST(key)));
1680 Goto(&add_entry);
1681 }
1682
1683 BIND(&add_entry);
1684 TVARIABLE(IntPtrT, number_of_buckets);
1685 TVARIABLE(IntPtrT, occupancy);
1686 {
1687 // Check we have enough space for the entry.
1689 table, CollectionType::NumberOfBucketsIndex())));
1690
1691 static_assert(CollectionType::kLoadFactor == 2);
1692 const TNode<WordT> capacity = WordShl(number_of_buckets.value(), 1);
1693 const TNode<IntPtrT> number_of_elements =
1695 table, CollectionType::NumberOfElementsOffset());
1696 const TNode<IntPtrT> number_of_deleted =
1698 table, CollectionType::NumberOfDeletedElementsOffset())));
1699 occupancy = IntPtrAdd(number_of_elements, number_of_deleted);
1700 GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry);
1701
1702 // We do not have enough space, grow the table and reload the relevant
1703 // fields.
1704 table_var = grow();
1706 table_var.value(), CollectionType::NumberOfBucketsIndex())));
1707 const TNode<IntPtrT> new_number_of_elements =
1709 table_var.value(), CollectionType::NumberOfElementsOffset());
1710 const TNode<IntPtrT> new_number_of_deleted = PositiveSmiUntag(
1711 CAST(LoadObjectField(table_var.value(),
1712 CollectionType::NumberOfDeletedElementsOffset())));
1713 occupancy = IntPtrAdd(new_number_of_elements, new_number_of_deleted);
1714 Goto(&store_new_entry);
1715 }
1716
1717 BIND(&store_new_entry);
1718 {
1720 table_var.value(), entry_start_position_or_hash.value(),
1721 number_of_buckets.value(), occupancy.value(), store_at_new_entry);
1722 Goto(&done);
1723 }
1724
1725 BIND(&done);
1726 return table_var.value();
1727}
1728
1730 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1731 auto key = Parameter<JSAny>(Descriptor::kKey);
1732 const auto value = Parameter<Object>(Descriptor::kValue);
1733 const auto context = Parameter<Context>(Descriptor::kContext);
1734
1735 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.set");
1736
1737 key = NormalizeNumberKey(key);
1738
1739 GrowCollection<OrderedHashMap> grow = [this, context, receiver]() {
1740 CallRuntime(Runtime::kMapGrow, context, receiver);
1741 return LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1742 };
1743
1744 StoreAtEntry<OrderedHashMap> store_at_new_entry =
1745 [this, key, value](const TNode<OrderedHashMap> table,
1746 const TNode<IntPtrT> entry_start) {
1747 UnsafeStoreKeyValueInOrderedHashMapEntry(table, key, value,
1748 entry_start);
1749 };
1750
1751 StoreAtEntry<OrderedHashMap> store_at_existing_entry =
1752 [this, value](const TNode<OrderedHashMap> table,
1753 const TNode<IntPtrT> entry_start) {
1754 UnsafeStoreValueInOrderedHashMapEntry(table, value, entry_start);
1755 };
1756
1757 const TNode<OrderedHashMap> table =
1758 LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1759 AddToOrderedHashTable(table, key, grow, store_at_new_entry,
1760 store_at_existing_entry);
1761 Return(receiver);
1762}
1763
1764template <typename CollectionType>
1766 const TNode<CollectionType> table, const TNode<IntPtrT> hash,
1767 const TNode<IntPtrT> number_of_buckets, const TNode<IntPtrT> occupancy,
1768 const StoreAtEntry<CollectionType>& store_at_new_entry) {
1769 const TNode<IntPtrT> bucket =
1770 WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1)));
1772 table, bucket, CollectionType::HashTableStartIndex() * kTaggedSize));
1773
1774 // Store the entry elements.
1775 const TNode<IntPtrT> entry_start = IntPtrAdd(
1776 IntPtrMul(occupancy, IntPtrConstant(CollectionType::kEntrySize)),
1777 number_of_buckets);
1778 store_at_new_entry(table, entry_start);
1779
1780 // Connect the element to the bucket chain.
1782 table, entry_start, bucket_entry,
1783 kTaggedSize * (CollectionType::HashTableStartIndex() +
1784 CollectionType::kChainOffset));
1785
1786 // Update the bucket head.
1788 table, bucket, SmiTag(occupancy),
1789 CollectionType::HashTableStartIndex() * kTaggedSize);
1790
1791 // Bump the elements count.
1792 const TNode<Smi> number_of_elements =
1793 CAST(LoadObjectField(table, CollectionType::NumberOfElementsOffset()));
1795 CollectionType::NumberOfElementsOffset(),
1796 SmiAdd(number_of_elements, SmiConstant(1)));
1797}
1798
1799// This is a helper function to add a new entry to an ordered hash table,
1800// when we are adding new entries from a Set.
1801template <typename CollectionType>
1803 const TNode<CollectionType> table, const TNode<Object> normalised_key,
1804 const TNode<IntPtrT> number_of_buckets, const TNode<IntPtrT> occupancy,
1805 const StoreAtEntry<CollectionType>& store_at_new_entry) {
1806 Label if_key_smi(this), if_key_string(this), if_key_heap_number(this),
1807 if_key_bigint(this), if_key_other(this), call_store(this);
1808 TVARIABLE(IntPtrT, hash, IntPtrConstant(0));
1809
1810 GotoIf(TaggedIsSmi(normalised_key), &if_key_smi);
1811 TNode<Map> key_map = LoadMap(CAST(normalised_key));
1812 TNode<Uint16T> key_instance_type = LoadMapInstanceType(key_map);
1813
1814 GotoIf(IsStringInstanceType(key_instance_type), &if_key_string);
1815 GotoIf(IsHeapNumberMap(key_map), &if_key_heap_number);
1816 GotoIf(IsBigIntInstanceType(key_instance_type), &if_key_bigint);
1817 Goto(&if_key_other);
1818
1819 BIND(&if_key_other);
1820 {
1821 hash = Signed(ChangeUint32ToWord(GetHash(CAST(normalised_key))));
1822 Goto(&call_store);
1823 }
1824
1825 BIND(&if_key_smi);
1826 {
1827 hash = ChangeInt32ToIntPtr(
1828 ComputeUnseededHash(SmiUntag(CAST(normalised_key))));
1829 Goto(&call_store);
1830 }
1831
1832 BIND(&if_key_string);
1833 {
1834 hash = Signed(ChangeUint32ToWord(ComputeStringHash(CAST(normalised_key))));
1835 Goto(&call_store);
1836 }
1837
1838 BIND(&if_key_heap_number);
1839 {
1840 hash = Signed(ChangeUint32ToWord(GetHash(CAST(normalised_key))));
1841 Goto(&call_store);
1842 }
1843
1844 BIND(&if_key_bigint);
1845 {
1846 hash = Signed(ChangeUint32ToWord(GetHash(CAST(normalised_key))));
1847 Goto(&call_store);
1848 }
1849
1850 BIND(&call_store);
1851 StoreOrderedHashTableNewEntry(table, hash.value(), number_of_buckets,
1852 occupancy, store_at_new_entry);
1853}
1854
1856 const TNode<OrderedHashMap> table, const TNode<Object> value,
1857 const TNode<IntPtrT> entry_start, CheckBounds check_bounds) {
1858 StoreFixedArrayElement(table, entry_start, value, UPDATE_WRITE_BARRIER,
1861 check_bounds);
1862}
1863
1865 const TNode<OrderedHashMap> table, const TNode<Object> key,
1866 const TNode<Object> value, const TNode<IntPtrT> entry_start,
1867 CheckBounds check_bounds) {
1868 StoreFixedArrayElement(table, entry_start, key, UPDATE_WRITE_BARRIER,
1870 check_bounds);
1871 StoreValueInOrderedHashMapEntry(table, value, entry_start, check_bounds);
1872}
1873
1875 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1876 const auto key = Parameter<Object>(Descriptor::kKey);
1877 const auto context = Parameter<Context>(Descriptor::kContext);
1878
1879 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1880 "Map.prototype.delete");
1881
1882 const TNode<OrderedHashMap> table =
1883 LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1884
1885 TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
1886 Label entry_found(this), not_found(this);
1887
1888 TryLookupOrderedHashTableIndex<OrderedHashMap>(
1889 table, key, &entry_start_position_or_hash, &entry_found, &not_found);
1890
1891 BIND(&not_found);
1892 Return(FalseConstant());
1893
1894 BIND(&entry_found);
1895 // If we found the entry, mark the entry as deleted.
1896 StoreKeyValueInOrderedHashMapEntry(table, HashTableHoleConstant(),
1897 HashTableHoleConstant(),
1898 entry_start_position_or_hash.value());
1899
1900 // Decrement the number of elements, increment the number of deleted elements.
1901 const TNode<Smi> number_of_elements = SmiSub(
1902 CAST(LoadObjectField(table, OrderedHashMap::NumberOfElementsOffset())),
1903 SmiConstant(1));
1904 StoreObjectFieldNoWriteBarrier(
1905 table, OrderedHashMap::NumberOfElementsOffset(), number_of_elements);
1906 const TNode<Smi> number_of_deleted =
1907 SmiAdd(CAST(LoadObjectField(
1909 SmiConstant(1));
1910 StoreObjectFieldNoWriteBarrier(
1912 number_of_deleted);
1913
1914 const TNode<Smi> number_of_buckets = CAST(
1915 LoadFixedArrayElement(table, OrderedHashMap::NumberOfBucketsIndex()));
1916
1917 // If there fewer elements than #buckets / 2, shrink the table.
1918 Label shrink(this);
1919 GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements),
1920 number_of_buckets),
1921 &shrink);
1922 Return(TrueConstant());
1923
1924 BIND(&shrink);
1925 CallRuntime(Runtime::kMapShrink, context, receiver);
1926 Return(TrueConstant());
1927}
1928
1930 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1931 auto key = Parameter<JSAny>(Descriptor::kKey);
1932 const auto context = Parameter<Context>(Descriptor::kContext);
1933
1934 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.add");
1935
1936 key = NormalizeNumberKey(key);
1937
1938 GrowCollection<OrderedHashSet> grow = [this, context, receiver]() {
1939 CallRuntime(Runtime::kSetGrow, context, receiver);
1940 return LoadObjectField<OrderedHashSet>(CAST(receiver), JSSet::kTableOffset);
1941 };
1942
1943 StoreAtEntry<OrderedHashSet> store_at_new_entry =
1944 [this, key](const TNode<OrderedHashSet> table,
1945 const TNode<IntPtrT> entry_start) {
1946 UnsafeStoreKeyInOrderedHashSetEntry(table, key, entry_start);
1947 };
1948
1949 StoreAtEntry<OrderedHashSet> store_at_existing_entry =
1950 [](const TNode<OrderedHashSet>, const TNode<IntPtrT>) {
1951 // If the entry was found, there is nothing to do.
1952 };
1953
1954 const TNode<OrderedHashSet> table =
1955 LoadObjectField<OrderedHashSet>(CAST(receiver), JSSet::kTableOffset);
1956 AddToOrderedHashTable(table, key, grow, store_at_new_entry,
1957 store_at_existing_entry);
1958 Return(receiver);
1959}
1960
1963 TNode<String> method_name) {
1965
1966 GrowCollection<OrderedHashSet> grow = [this, context, table, method_name]() {
1967 TNode<OrderedHashSet> new_table = Cast(
1968 CallRuntime(Runtime::kOrderedHashSetGrow, context, table, method_name));
1969 // TODO(v8:13556): check if the table is updated and remove pointer to the
1970 // new table.
1971 return new_table;
1972 };
1973
1974 StoreAtEntry<OrderedHashSet> store_at_new_entry =
1975 [this, key](const TNode<OrderedHashSet> table,
1976 const TNode<IntPtrT> entry_start) {
1977 UnsafeStoreKeyInOrderedHashSetEntry(table, key, entry_start);
1978 };
1979
1980 StoreAtEntry<OrderedHashSet> store_at_existing_entry =
1981 [](const TNode<OrderedHashSet>, const TNode<IntPtrT>) {
1982 // If the entry was found, there is nothing to do.
1983 };
1984
1985 return AddToOrderedHashTable(table, key, grow, store_at_new_entry,
1986 store_at_existing_entry);
1987}
1988
1990 const TNode<OrderedHashSet> table, const TNode<Object> key,
1991 const TNode<IntPtrT> entry_start, CheckBounds check_bounds) {
1992 StoreFixedArrayElement(table, entry_start, key, UPDATE_WRITE_BARRIER,
1994 check_bounds);
1995}
1996
1997template <typename CollectionType>
1999 const TNode<CollectionType> table, const TNode<IntPtrT> entry,
2000 CheckBounds check_bounds) {
2002 table, entry, kTaggedSize * CollectionType::HashTableStartIndex(),
2003 check_bounds));
2004}
2005
2016
2018 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2019 const auto key = Parameter<Object>(Descriptor::kKey);
2020 const auto context = Parameter<Context>(Descriptor::kContext);
2021
2022 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2023 "Set.prototype.delete");
2024
2025 // This check breaks a known exploitation technique. See crbug.com/1263462
2026 CSA_HOLE_SECURITY_CHECK(this, TaggedNotEqual(key, HashTableHoleConstant()));
2027
2028 const TNode<OrderedHashSet> table =
2029 LoadObjectField<OrderedHashSet>(CAST(receiver), JSMap::kTableOffset);
2030
2031 Label not_found(this);
2032 const TNode<Smi> number_of_elements =
2033 DeleteFromSetTable(context, table, key, &not_found);
2034
2035 const TNode<Smi> number_of_buckets = CAST(
2036 LoadFixedArrayElement(table, OrderedHashSet::NumberOfBucketsIndex()));
2037
2038 // If there fewer elements than #buckets / 2, shrink the table.
2039 Label shrink(this);
2040 GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements),
2041 number_of_buckets),
2042 &shrink);
2043 Return(TrueConstant());
2044
2045 BIND(&shrink);
2046 CallRuntime(Runtime::kSetShrink, context, receiver);
2047 Return(TrueConstant());
2048
2049 BIND(&not_found);
2050 Return(FalseConstant());
2051}
2052
2055 Label* not_found) {
2056 TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
2057 Label entry_found(this);
2058
2060 table, key, &entry_start_position_or_hash, &entry_found, not_found);
2061
2062 BIND(&entry_found);
2063 // If we found the entry, mark the entry as deleted.
2064 StoreKeyInOrderedHashSetEntry(table, HashTableHoleConstant(),
2065 entry_start_position_or_hash.value());
2066
2067 // Decrement the number of elements, increment the number of deleted elements.
2068 const TNode<Smi> number_of_elements = SmiSub(
2070 SmiConstant(1));
2072 table, OrderedHashSet::NumberOfElementsOffset(), number_of_elements);
2073 const TNode<Smi> number_of_deleted =
2074 SmiAdd(CAST(LoadObjectField(
2076 SmiConstant(1));
2079 number_of_deleted);
2080
2081 return number_of_elements;
2082}
2083
2085 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2086 const auto context = Parameter<Context>(Descriptor::kContext);
2087 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
2088 "Map.prototype.entries");
2089 Return(AllocateJSCollectionIterator<JSMapIterator>(
2090 context, Context::MAP_KEY_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2091}
2092
2094 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2095 const auto context = Parameter<Context>(Descriptor::kContext);
2096 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
2097 "get Map.prototype.size");
2098 const TNode<OrderedHashMap> table =
2099 LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
2100 Return(LoadObjectField(table, OrderedHashMap::NumberOfElementsOffset()));
2101}
2102
2104 const char* const kMethodName = "Map.prototype.forEach";
2105 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
2106 const auto context = Parameter<Context>(Descriptor::kContext);
2107 CodeStubArguments args(this, argc);
2108 const TNode<JSAny> receiver = args.GetReceiver();
2109 const TNode<JSAny> callback = args.GetOptionalArgumentValue(0);
2110 const TNode<JSAny> this_arg = args.GetOptionalArgumentValue(1);
2111
2112 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, kMethodName);
2113
2114 // Ensure that {callback} is actually callable.
2115 Label callback_not_callable(this, Label::kDeferred);
2116 GotoIf(TaggedIsSmi(callback), &callback_not_callable);
2117 GotoIfNot(IsCallable(CAST(callback)), &callback_not_callable);
2118
2119 TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
2120 TVARIABLE(OrderedHashMap, var_table,
2121 CAST(LoadObjectField(CAST(receiver), JSMap::kTableOffset)));
2122 Label loop(this, {&var_index, &var_table}), done_loop(this);
2123 Goto(&loop);
2124 BIND(&loop);
2125 {
2126 // Transition {table} and {index} if there was any modification to
2127 // the {receiver} while we're iterating.
2128 TNode<IntPtrT> index = var_index.value();
2129 TNode<OrderedHashMap> table = var_table.value();
2130 std::tie(table, index) = Transition<OrderedHashMap>(
2131 table, index, [](const TNode<OrderedHashMap>, const TNode<IntPtrT>) {});
2132
2133 // Read the next entry from the {table}, skipping holes.
2134 TNode<JSAny> entry_key;
2135 TNode<IntPtrT> entry_start_position;
2136 std::tie(entry_key, entry_start_position, index) =
2137 NextSkipHashTableHoles<OrderedHashMap>(table, index, &done_loop);
2138
2139 // Load the entry value as well.
2140 TNode<Object> entry_value =
2141 LoadValueFromOrderedHashMapEntry(table, entry_start_position);
2142
2143 // Invoke the {callback} passing the {entry_key}, {entry_value} and the
2144 // {receiver}.
2145 Call(context, callback, this_arg, entry_value, entry_key, receiver);
2146
2147 // Continue with the next entry.
2148 var_index = index;
2149 var_table = table;
2150 Goto(&loop);
2151 }
2152
2153 BIND(&done_loop);
2154 args.PopAndReturn(UndefinedConstant());
2155
2156 BIND(&callback_not_callable);
2157 {
2158 CallRuntime(Runtime::kThrowCalledNonCallable, context, callback);
2159 Unreachable();
2160 }
2161}
2162
2164 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2165 const auto context = Parameter<Context>(Descriptor::kContext);
2166 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.keys");
2167 Return(AllocateJSCollectionIterator<JSMapIterator>(
2168 context, Context::MAP_KEY_ITERATOR_MAP_INDEX, CAST(receiver)));
2169}
2170
2172 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2173 const auto context = Parameter<Context>(Descriptor::kContext);
2174 ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
2175 "Map.prototype.values");
2176 Return(AllocateJSCollectionIterator<JSMapIterator>(
2177 context, Context::MAP_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2178}
2179
2180TF_BUILTIN(MapIteratorPrototypeNext, CollectionsBuiltinsAssembler) {
2181 const char* const kMethodName = "Map Iterator.prototype.next";
2182 const auto maybe_receiver = Parameter<Object>(Descriptor::kReceiver);
2183 const auto context = Parameter<Context>(Descriptor::kContext);
2184
2185 // Ensure that {maybe_receiver} is actually a JSMapIterator.
2186 Label if_receiver_valid(this), if_receiver_invalid(this, Label::kDeferred);
2187 GotoIf(TaggedIsSmi(maybe_receiver), &if_receiver_invalid);
2188 const TNode<Uint16T> receiver_instance_type =
2189 LoadInstanceType(CAST(maybe_receiver));
2190 GotoIf(
2191 InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_VALUE_ITERATOR_TYPE),
2192 &if_receiver_valid);
2193 GotoIf(InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_ITERATOR_TYPE),
2194 &if_receiver_valid);
2195 Branch(InstanceTypeEqual(receiver_instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
2196 &if_receiver_valid, &if_receiver_invalid);
2197 BIND(&if_receiver_invalid);
2198 ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2199 StringConstant(kMethodName), maybe_receiver);
2200 BIND(&if_receiver_valid);
2201 TNode<JSMapIterator> receiver = CAST(maybe_receiver);
2202
2203 // Check if the {receiver} is exhausted.
2204 TVARIABLE(Boolean, var_done, TrueConstant());
2205 TVARIABLE(Object, var_value, UndefinedConstant());
2206 Label return_value(this, {&var_done, &var_value}), return_entry(this),
2207 return_end(this, Label::kDeferred);
2208
2209 // Transition the {receiver} table if necessary.
2212 std::tie(table, index) =
2213 TransitionAndUpdate<JSMapIterator, OrderedHashMap>(receiver);
2214
2215 // Read the next entry from the {table}, skipping holes.
2216 TNode<Object> entry_key;
2217 TNode<IntPtrT> entry_start_position;
2218 std::tie(entry_key, entry_start_position, index) =
2219 NextSkipHashTableHoles<OrderedHashMap>(table, index, &return_end);
2220 StoreObjectFieldNoWriteBarrier(receiver, JSMapIterator::kIndexOffset,
2221 SmiTag(index));
2222 var_value = entry_key;
2223 var_done = FalseConstant();
2224
2225 // Check how to return the {key} (depending on {receiver} type).
2226 GotoIf(InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_ITERATOR_TYPE),
2227 &return_value);
2228 var_value = LoadValueFromOrderedHashMapEntry(table, entry_start_position);
2229 Branch(InstanceTypeEqual(receiver_instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
2230 &return_value, &return_entry);
2231
2232 BIND(&return_entry);
2233 {
2235 AllocateJSIteratorResultForEntry(context, entry_key, var_value.value());
2236 Return(result);
2237 }
2238
2239 BIND(&return_value);
2240 {
2242 AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2243 Return(result);
2244 }
2245
2246 BIND(&return_end);
2247 {
2248 StoreObjectFieldRoot(receiver, JSMapIterator::kTableOffset,
2249 RootIndex::kEmptyOrderedHashMap);
2250 Goto(&return_value);
2251 }
2252}
2253
2255 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2256 const auto key = Parameter<Object>(Descriptor::kKey);
2257 const auto context = Parameter<Context>(Descriptor::kContext);
2258
2259 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.has");
2260
2261 const TNode<OrderedHashSet> table =
2262 CAST(LoadObjectField(CAST(receiver), JSMap::kTableOffset));
2263
2264 Label if_found(this), if_not_found(this);
2265 Branch(TableHasKey(context, table, key), &if_found, &if_not_found);
2266
2267 BIND(&if_found);
2268 Return(TrueConstant());
2269
2270 BIND(&if_not_found);
2271 Return(FalseConstant());
2272}
2273
2275 const TNode<Object> context, TNode<OrderedHashSet> table,
2277 TNode<Smi> index =
2278 CAST(CallBuiltin(Builtin::kFindOrderedHashSetEntry, context, table, key));
2279
2280 return SmiGreaterThanOrEqual(index, SmiConstant(0));
2281}
2282
2284 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2285 const auto context = Parameter<Context>(Descriptor::kContext);
2286 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2287 "Set.prototype.entries");
2288 Return(AllocateJSCollectionIterator<JSSetIterator>(
2289 context, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2290}
2291
2293 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2294 const auto context = Parameter<Context>(Descriptor::kContext);
2295 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2296 "get Set.prototype.size");
2297 const TNode<OrderedHashSet> table =
2298 LoadObjectField<OrderedHashSet>(CAST(receiver), JSSet::kTableOffset);
2299 Return(LoadObjectField(table, OrderedHashSet::NumberOfElementsOffset()));
2300}
2301
2303 const char* const kMethodName = "Set.prototype.forEach";
2304 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
2305 const auto context = Parameter<Context>(Descriptor::kContext);
2306 CodeStubArguments args(this, argc);
2307 const TNode<JSAny> receiver = args.GetReceiver();
2308 const TNode<JSAny> callback = args.GetOptionalArgumentValue(0);
2309 const TNode<JSAny> this_arg = args.GetOptionalArgumentValue(1);
2310
2311 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, kMethodName);
2312
2313 // Ensure that {callback} is actually callable.
2314 Label callback_not_callable(this, Label::kDeferred);
2315 GotoIf(TaggedIsSmi(callback), &callback_not_callable);
2316 GotoIfNot(IsCallable(CAST(callback)), &callback_not_callable);
2317
2318 TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
2319 TVARIABLE(OrderedHashSet, var_table,
2320 CAST(LoadObjectField(CAST(receiver), JSSet::kTableOffset)));
2321 Label loop(this, {&var_index, &var_table}), done_loop(this);
2322 Goto(&loop);
2323 BIND(&loop);
2324 {
2325 // Transition {table} and {index} if there was any modification to
2326 // the {receiver} while we're iterating.
2327 TNode<IntPtrT> index = var_index.value();
2328 TNode<OrderedHashSet> table = var_table.value();
2329 std::tie(table, index) = Transition<OrderedHashSet>(
2330 table, index, [](const TNode<OrderedHashSet>, const TNode<IntPtrT>) {});
2331
2332 // Read the next entry from the {table}, skipping holes.
2333 TNode<JSAny> entry_key;
2334 TNode<IntPtrT> entry_start_position;
2335 std::tie(entry_key, entry_start_position, index) =
2336 NextSkipHashTableHoles<OrderedHashSet>(table, index, &done_loop);
2337
2338 // Invoke the {callback} passing the {entry_key} (twice) and the {receiver}.
2339 Call(context, callback, this_arg, entry_key, entry_key, receiver);
2340
2341 // Continue with the next entry.
2342 var_index = index;
2343 var_table = table;
2344 Goto(&loop);
2345 }
2346
2347 BIND(&done_loop);
2348 args.PopAndReturn(UndefinedConstant());
2349
2350 BIND(&callback_not_callable);
2351 {
2352 CallRuntime(Runtime::kThrowCalledNonCallable, context, callback);
2353 Unreachable();
2354 }
2355}
2356
2358 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2359 const auto context = Parameter<Context>(Descriptor::kContext);
2360 ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2361 "Set.prototype.values");
2362 Return(AllocateJSCollectionIterator<JSSetIterator>(
2363 context, Context::SET_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2364}
2365
2366TF_BUILTIN(SetIteratorPrototypeNext, CollectionsBuiltinsAssembler) {
2367 const char* const kMethodName = "Set Iterator.prototype.next";
2368 const auto maybe_receiver = Parameter<Object>(Descriptor::kReceiver);
2369 const auto context = Parameter<Context>(Descriptor::kContext);
2370
2371 // Ensure that {maybe_receiver} is actually a JSSetIterator.
2372 Label if_receiver_valid(this), if_receiver_invalid(this, Label::kDeferred);
2373 GotoIf(TaggedIsSmi(maybe_receiver), &if_receiver_invalid);
2374 const TNode<Uint16T> receiver_instance_type =
2375 LoadInstanceType(CAST(maybe_receiver));
2376 GotoIf(InstanceTypeEqual(receiver_instance_type, JS_SET_VALUE_ITERATOR_TYPE),
2377 &if_receiver_valid);
2378 Branch(
2379 InstanceTypeEqual(receiver_instance_type, JS_SET_KEY_VALUE_ITERATOR_TYPE),
2380 &if_receiver_valid, &if_receiver_invalid);
2381 BIND(&if_receiver_invalid);
2382 ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2383 StringConstant(kMethodName), maybe_receiver);
2384 BIND(&if_receiver_valid);
2385
2386 TNode<JSSetIterator> receiver = CAST(maybe_receiver);
2387
2388 // Check if the {receiver} is exhausted.
2389 TVARIABLE(Boolean, var_done, TrueConstant());
2390 TVARIABLE(Object, var_value, UndefinedConstant());
2391 Label return_value(this, {&var_done, &var_value}), return_entry(this),
2392 return_end(this, Label::kDeferred);
2393
2394 // Transition the {receiver} table if necessary.
2397 std::tie(table, index) =
2398 TransitionAndUpdate<JSSetIterator, OrderedHashSet>(receiver);
2399
2400 // Read the next entry from the {table}, skipping holes.
2401 TNode<Object> entry_key;
2402 TNode<IntPtrT> entry_start_position;
2403 std::tie(entry_key, entry_start_position, index) =
2404 NextSkipHashTableHoles<OrderedHashSet>(table, index, &return_end);
2405 StoreObjectFieldNoWriteBarrier(receiver, JSSetIterator::kIndexOffset,
2406 SmiTag(index));
2407 var_value = entry_key;
2408 var_done = FalseConstant();
2409
2410 // Check how to return the {key} (depending on {receiver} type).
2411 Branch(InstanceTypeEqual(receiver_instance_type, JS_SET_VALUE_ITERATOR_TYPE),
2412 &return_value, &return_entry);
2413
2414 BIND(&return_entry);
2415 {
2416 TNode<JSObject> result = AllocateJSIteratorResultForEntry(
2417 context, var_value.value(), var_value.value());
2418 Return(result);
2419 }
2420
2421 BIND(&return_value);
2422 {
2424 AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2425 Return(result);
2426 }
2427
2428 BIND(&return_end);
2429 {
2430 StoreObjectFieldRoot(receiver, JSSetIterator::kTableOffset,
2431 RootIndex::kEmptyOrderedHashSet);
2432 Goto(&return_value);
2433 }
2434}
2435
2436template <typename CollectionType>
2438 const TNode<CollectionType> table, const TNode<Object> key,
2439 TVariable<IntPtrT>* result, Label* if_entry_found, Label* if_not_found) {
2440 Label if_key_smi(this), if_key_string(this), if_key_heap_number(this),
2441 if_key_bigint(this);
2442
2443 GotoIf(TaggedIsSmi(key), &if_key_smi);
2444
2445 TNode<Map> key_map = LoadMap(CAST(key));
2446 TNode<Uint16T> key_instance_type = LoadMapInstanceType(key_map);
2447
2448 GotoIf(IsStringInstanceType(key_instance_type), &if_key_string);
2449 GotoIf(IsHeapNumberMap(key_map), &if_key_heap_number);
2450 GotoIf(IsBigIntInstanceType(key_instance_type), &if_key_bigint);
2451
2453 table, CAST(key), result, if_entry_found, if_not_found);
2454
2455 BIND(&if_key_smi);
2456 {
2458 table, CAST(key), result, if_entry_found, if_not_found);
2459 }
2460
2461 BIND(&if_key_string);
2462 {
2464 table, CAST(key), result, if_entry_found, if_not_found);
2465 }
2466
2467 BIND(&if_key_heap_number);
2468 {
2470 table, CAST(key), result, if_entry_found, if_not_found);
2471 }
2472
2473 BIND(&if_key_bigint);
2474 {
2476 table, CAST(key), result, if_entry_found, if_not_found);
2477 }
2478}
2479
2480TF_BUILTIN(FindOrderedHashMapEntry, CollectionsBuiltinsAssembler) {
2481 const auto table = Parameter<OrderedHashMap>(Descriptor::kTable);
2482 const auto key = Parameter<Object>(Descriptor::kKey);
2483
2484 TVARIABLE(IntPtrT, entry_start_position, IntPtrConstant(0));
2485 Label entry_found(this), not_found(this);
2486
2487 TryLookupOrderedHashTableIndex<OrderedHashMap>(
2488 table, key, &entry_start_position, &entry_found, &not_found);
2489
2490 BIND(&entry_found);
2491 Return(SmiTag(entry_start_position.value()));
2492
2493 BIND(&not_found);
2494 Return(SmiConstant(-1));
2495}
2496
2497TF_BUILTIN(FindOrderedHashSetEntry, CollectionsBuiltinsAssembler) {
2498 const auto table = Parameter<OrderedHashSet>(Descriptor::kTable);
2499 const auto key = Parameter<Object>(Descriptor::kKey);
2500
2501 TVARIABLE(IntPtrT, entry_start_position, IntPtrConstant(0));
2502 Label entry_found(this), not_found(this);
2503
2504 TryLookupOrderedHashTableIndex<OrderedHashSet>(
2505 table, key, &entry_start_position, &entry_found, &not_found);
2506
2507 BIND(&entry_found);
2508 Return(SmiTag(entry_start_position.value()));
2509
2510 BIND(&not_found);
2511 Return(SmiConstant(-1));
2512}
2513
2515 const TNode<OrderedHashMap> groups, const TNode<Object> key,
2516 const TNode<Object> value, const TNode<String> methodName) {
2517 GrowCollection<OrderedHashMap> grow = [this, groups, methodName]() {
2519 Runtime::kOrderedHashMapGrow, NoContextConstant(), groups, methodName));
2520 // The groups OrderedHashMap is not escaped to user script while grouping
2521 // items, so there can't be live iterators. So we don't need to keep the
2522 // pointer from the old table to the new one.
2523 Label did_grow(this), done(this);
2524 Branch(TaggedEqual(groups, new_groups), &done, &did_grow);
2525 BIND(&did_grow);
2526 {
2528 SmiConstant(0));
2529 Goto(&done);
2530 }
2531 BIND(&done);
2532 return new_groups;
2533 };
2534
2535 StoreAtEntry<OrderedHashMap> store_at_new_entry =
2536 [this, key, value](const TNode<OrderedHashMap> table,
2537 const TNode<IntPtrT> entry_start) {
2539 ArrayListSet(array, SmiConstant(0), value);
2541 StoreKeyValueInOrderedHashMapEntry(table, key, array, entry_start);
2542 };
2543
2544 StoreAtEntry<OrderedHashMap> store_at_existing_entry =
2545 [this, key, value](const TNode<OrderedHashMap> table,
2546 const TNode<IntPtrT> entry_start) {
2547 TNode<ArrayList> array =
2548 CAST(LoadValueFromOrderedHashMapEntry(table, entry_start));
2549 TNode<ArrayList> new_array = ArrayListAdd(array, value);
2550 StoreKeyValueInOrderedHashMapEntry(table, key, new_array, entry_start);
2551 };
2552
2553 return AddToOrderedHashTable(groups, key, grow, store_at_new_entry,
2554 store_at_existing_entry);
2555}
2556
2559 TNode<Object> key, TNode<Object> value, TNode<Int32T> number_of_elements) {
2560 // See EphemeronHashTable::AddEntry().
2561 TNode<IntPtrT> value_index = ValueIndexFromKeyIndex(key_index);
2562 UnsafeStoreFixedArrayElement(table, key_index, key,
2564 UnsafeStoreFixedArrayElement(table, value_index, value);
2565
2566 // See HashTableBase::ElementAdded().
2568 EphemeronHashTable::kNumberOfElementsIndex,
2569 SmiFromInt32(number_of_elements));
2570}
2571
2573 const TNode<HeapObject> key, Label* if_no_hash) {
2574 TVARIABLE(IntPtrT, var_hash);
2575 Label if_symbol(this);
2576 Label return_result(this);
2577 GotoIfNot(IsJSReceiver(key), &if_symbol);
2578 var_hash = Signed(
2580 Goto(&return_result);
2581 Bind(&if_symbol);
2582 CSA_DCHECK(this, IsSymbol(key));
2583 CSA_DCHECK(this, Word32BinaryNot(
2584 Word32And(LoadSymbolFlags(CAST(key)),
2586 var_hash = Signed(ChangeUint32ToWord(LoadNameHash(CAST(key), nullptr)));
2587 Goto(&return_result);
2588 Bind(&return_result);
2589 return var_hash.value();
2590}
2591
2593 Variant variant, TNode<IntPtrT> at_least_space_for) {
2594 // See HashTable::New().
2595 DCHECK(variant == kWeakSet || variant == kWeakMap);
2596 CSA_DCHECK(this,
2597 IntPtrLessThanOrEqual(IntPtrConstant(0), at_least_space_for));
2598 TNode<IntPtrT> capacity = HashTableComputeCapacity(at_least_space_for);
2599
2600 // See HashTable::NewInternal().
2601 TNode<IntPtrT> length = KeyIndexFromEntry(capacity);
2603
2604 TNode<Map> map =
2605 HeapConstantNoHole(EphemeronHashTable::GetMap(isolate()->roots_table()));
2606 StoreMapNoWriteBarrier(table, map);
2607 StoreFixedArrayElement(table, EphemeronHashTable::kNumberOfElementsIndex,
2610 EphemeronHashTable::kNumberOfDeletedElementsIndex,
2612 StoreFixedArrayElement(table, EphemeronHashTable::kCapacityIndex,
2614
2617 RootIndex::kUndefinedValue);
2618 return table;
2619}
2620
2623 TNode<ExternalReference> function_addr =
2624 ExternalConstant(ExternalReference::jsreceiver_create_identity_hash());
2625 TNode<ExternalReference> isolate_ptr =
2627
2628 MachineType type_ptr = MachineType::Pointer();
2629 MachineType type_tagged = MachineType::AnyTagged();
2630
2631 return CAST(CallCFunction(function_addr, type_tagged,
2632 std::make_pair(type_ptr, isolate_ptr),
2633 std::make_pair(type_tagged, key)));
2634}
2635
2640
2642 TNode<IntPtrT> capacity) {
2646 TVARIABLE(IntPtrT, coeff, IntPtrConstant(1));
2647 Label done(this, &coeff);
2648 GotoIf(IntPtrLessThan(
2650 &done);
2651 coeff = Signed(
2653 Goto(&done);
2654 BIND(&done);
2655 return coeff.value();
2656}
2657
2659 TNode<HeapObject> table, TNode<IntPtrT> key_hash, TNode<IntPtrT> capacity,
2660 const KeyComparator& key_compare) {
2661 // See HashTable::FirstProbe().
2662 TNode<IntPtrT> entry_mask = EntryMask(capacity);
2663 TVARIABLE(IntPtrT, var_entry,
2664 WordAnd(IntPtrMul(key_hash, Coefficient(capacity)), entry_mask));
2665 TVARIABLE(IntPtrT, var_count, IntPtrConstant(0));
2666
2667 Label loop(this, {&var_count, &var_entry}), if_found(this);
2668 Goto(&loop);
2669 BIND(&loop);
2670 TNode<IntPtrT> key_index;
2671 {
2672 key_index = KeyIndexFromEntry(var_entry.value());
2673 TNode<Object> entry_key =
2674 UnsafeLoadFixedArrayElement(CAST(table), key_index);
2675
2676 key_compare(entry_key, &if_found);
2677
2678 // See HashTable::NextProbe().
2679 Increment(&var_count);
2680 var_entry =
2681 WordAnd(IntPtrAdd(var_entry.value(), var_count.value()), entry_mask);
2682 Goto(&loop);
2683 }
2684
2685 BIND(&if_found);
2686 return key_index;
2687}
2688
2690 TNode<HeapObject> table, TNode<IntPtrT> key_hash, TNode<IntPtrT> capacity) {
2691 // See HashTable::FindInsertionEntry().
2692 auto is_not_live = [&](TNode<Object> entry_key, Label* if_found) {
2693 // This is the the negative form BaseShape::IsLive().
2694 GotoIf(Word32Or(IsTheHole(entry_key), IsUndefined(entry_key)), if_found);
2695 };
2696 return FindKeyIndex(table, key_hash, capacity, is_not_live);
2697}
2698
2701 TNode<IntPtrT> capacity, Label* if_not_found) {
2702 // See HashTable::FindEntry().
2703 auto match_key_or_exit_on_empty = [&](TNode<Object> entry_key,
2704 Label* if_same) {
2705 GotoIf(IsUndefined(entry_key), if_not_found);
2706 GotoIf(TaggedEqual(entry_key, key), if_same);
2707 };
2708 return FindKeyIndex(table, hash, capacity, match_key_or_exit_on_empty);
2709}
2710
2712 TNode<IntPtrT> entry) {
2713 // See HashTable::KeyAt().
2714 // (entry * kEntrySize) + kElementsStartIndex + kEntryKeyIndex
2715 return IntPtrAdd(
2716 IntPtrMul(entry, IntPtrConstant(EphemeronHashTable::kEntrySize)),
2717 IntPtrConstant(EphemeronHashTable::kElementsStartIndex +
2718 EphemeronHashTable::kEntryKeyIndex));
2719}
2720
2722 TNode<EphemeronHashTable> table, int offset) {
2723 TNode<Int32T> number_of_elements =
2725 table, EphemeronHashTable::kNumberOfElementsIndex)));
2726 return Int32Add(number_of_elements, Int32Constant(offset));
2727}
2728
2730 TNode<EphemeronHashTable> table, int offset) {
2732 table, EphemeronHashTable::kNumberOfDeletedElementsIndex)));
2733 return Int32Add(number_of_deleted, Int32Constant(offset));
2734}
2735
2737 TNode<JSWeakCollection> collection) {
2738 return CAST(LoadObjectField(collection, JSWeakCollection::kTableOffset));
2739}
2740
2746
2748 TNode<Int32T> capacity, TNode<Int32T> number_of_elements,
2749 TNode<Int32T> number_of_deleted) {
2750 // This is the negative form of HashTable::HasSufficientCapacityToAdd().
2751 // Return true if:
2752 // - more than 50% of the available space are deleted elements
2753 // - less than 50% will be available
2754 TNode<Int32T> available = Int32Sub(capacity, number_of_elements);
2755 TNode<Int32T> half_available = Signed(Word32Shr(available, 1));
2756 TNode<Int32T> needed_available = Signed(Word32Shr(number_of_elements, 1));
2757 return Word32Or(
2758 // deleted > half
2759 Int32GreaterThan(number_of_deleted, half_available),
2760 // elements + needed available > capacity
2761 Int32GreaterThan(Int32Add(number_of_elements, needed_available),
2762 capacity));
2763}
2764
2767 TNode<IntPtrT> number_of_elements) {
2768 // See EphemeronHashTable::RemoveEntry().
2769 TNode<IntPtrT> value_index = ValueIndexFromKeyIndex(key_index);
2770 StoreFixedArrayElement(table, key_index, TheHoleConstant());
2771 StoreFixedArrayElement(table, value_index, TheHoleConstant());
2772
2773 // See HashTableBase::ElementRemoved().
2774 TNode<Int32T> number_of_deleted = LoadNumberOfDeleted(table, 1);
2775 StoreFixedArrayElement(table, EphemeronHashTable::kNumberOfElementsIndex,
2776 SmiFromIntPtr(number_of_elements), SKIP_WRITE_BARRIER);
2778 EphemeronHashTable::kNumberOfDeletedElementsIndex,
2779 SmiFromInt32(number_of_deleted), SKIP_WRITE_BARRIER);
2780}
2781
2783 TNode<Int32T> number_of_elements, TNode<Int32T> number_of_deleted) {
2784 // Rehash if more than 33% of the entries are deleted.
2785 return Int32GreaterThanOrEqual(Word32Shl(number_of_deleted, 1),
2786 number_of_elements);
2787}
2788
2790 TNode<IntPtrT> capacity, TNode<IntPtrT> number_of_elements) {
2791 // See HashTable::Shrink().
2792 TNode<IntPtrT> quarter_capacity = WordShr(capacity, 2);
2793 return Word32And(
2794 // Shrink to fit the number of elements if only a quarter of the
2795 // capacity is filled with elements.
2796 IntPtrLessThanOrEqual(number_of_elements, quarter_capacity),
2797
2798 // Allocate a new dictionary with room for at least the current
2799 // number of elements. The allocation method will make sure that
2800 // there is extra room in the dictionary for additions. Don't go
2801 // lower than room for 16 elements.
2802 IntPtrGreaterThanOrEqual(number_of_elements, IntPtrConstant(16)));
2803}
2804
2806 TNode<IntPtrT> key_index) {
2807 return IntPtrAdd(
2808 key_index,
2809 IntPtrConstant(EphemeronHashTable::TodoShape::kEntryValueIndex -
2810 EphemeronHashTable::kEntryKeyIndex));
2811}
2812
2814 auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
2815 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
2816 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
2817 auto context = Parameter<Context>(Descriptor::kContext);
2818
2819 GenerateConstructor(kWeakMap, isolate()->factory()->WeakMap_string(),
2820 new_target, argc, context);
2821}
2822
2824 auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
2825 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
2826 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
2827 auto context = Parameter<Context>(Descriptor::kContext);
2828
2829 GenerateConstructor(kWeakSet, isolate()->factory()->WeakSet_string(),
2830 new_target, argc, context);
2831}
2832
2834 auto table = Parameter<EphemeronHashTable>(Descriptor::kTable);
2835 auto key = Parameter<Object>(Descriptor::kKey);
2836
2837 Label if_cannot_be_held_weakly(this);
2838
2839 GotoIfCannotBeHeldWeakly(key, &if_cannot_be_held_weakly);
2840
2841 TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_held_weakly);
2842 TNode<IntPtrT> capacity = LoadTableCapacity(table);
2843 TNode<IntPtrT> key_index =
2844 FindKeyIndexForKey(table, key, hash, capacity, &if_cannot_be_held_weakly);
2845 Return(SmiTag(ValueIndexFromKeyIndex(key_index)));
2846
2847 BIND(&if_cannot_be_held_weakly);
2848 Return(SmiConstant(-1));
2849}
2850
2852 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2853 const auto key = Parameter<Object>(Descriptor::kKey);
2854 const auto context = Parameter<Context>(Descriptor::kContext);
2855
2856 Label return_undefined(this);
2857
2858 ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2859 "WeakMap.prototype.get");
2860
2861 const TNode<EphemeronHashTable> table = LoadTable(CAST(receiver));
2862 const TNode<Smi> index =
2863 CAST(CallBuiltin(Builtin::kWeakMapLookupHashIndex, context, table, key));
2864
2865 GotoIf(TaggedEqual(index, SmiConstant(-1)), &return_undefined);
2866
2867 Return(LoadFixedArrayElement(table, SmiUntag(index)));
2868
2869 BIND(&return_undefined);
2870 Return(UndefinedConstant());
2871}
2872
2874 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2875 const auto key = Parameter<Object>(Descriptor::kKey);
2876 const auto context = Parameter<Context>(Descriptor::kContext);
2877
2878 Label return_false(this);
2879
2880 ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2881 "WeakMap.prototype.has");
2882
2883 const TNode<EphemeronHashTable> table = LoadTable(CAST(receiver));
2884 const TNode<Object> index =
2885 CallBuiltin(Builtin::kWeakMapLookupHashIndex, context, table, key);
2886
2887 GotoIf(TaggedEqual(index, SmiConstant(-1)), &return_false);
2888
2889 Return(TrueConstant());
2890
2891 BIND(&return_false);
2892 Return(FalseConstant());
2893}
2894
2895// Helper that removes the entry with a given key from the backing store
2896// (EphemeronHashTable) of a WeakMap or WeakSet.
2898 auto context = Parameter<Context>(Descriptor::kContext);
2899 auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
2900 auto key = Parameter<Object>(Descriptor::kKey);
2901
2902 Label call_runtime(this), if_cannot_be_held_weakly(this);
2903
2904 GotoIfCannotBeHeldWeakly(key, &if_cannot_be_held_weakly);
2905
2906 TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_held_weakly);
2907 TNode<EphemeronHashTable> table = LoadTable(collection);
2908 TNode<IntPtrT> capacity = LoadTableCapacity(table);
2909 TNode<IntPtrT> key_index =
2910 FindKeyIndexForKey(table, key, hash, capacity, &if_cannot_be_held_weakly);
2911 TNode<Int32T> number_of_elements = LoadNumberOfElements(table, -1);
2912 GotoIf(ShouldShrink(capacity, ChangeInt32ToIntPtr(number_of_elements)),
2913 &call_runtime);
2914
2915 RemoveEntry(table, key_index, ChangeInt32ToIntPtr(number_of_elements));
2916 Return(TrueConstant());
2917
2918 BIND(&if_cannot_be_held_weakly);
2919 Return(FalseConstant());
2920
2921 BIND(&call_runtime);
2922 Return(CallRuntime(Runtime::kWeakCollectionDelete, context, collection, key,
2923 SmiTag(hash)));
2924}
2925
2926// Helper that sets the key and value to the backing store (EphemeronHashTable)
2927// of a WeakMap or WeakSet.
2929 auto context = Parameter<Context>(Descriptor::kContext);
2930 auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
2931 auto key = Parameter<HeapObject>(Descriptor::kKey);
2932 auto value = Parameter<Object>(Descriptor::kValue);
2933
2934 CSA_DCHECK(this, Word32Or(IsJSReceiver(key), IsSymbol(key)));
2935
2936 Label call_runtime(this), if_no_hash(this), if_not_found(this);
2937
2938 TNode<EphemeronHashTable> table = LoadTable(collection);
2939 TNode<IntPtrT> capacity = LoadTableCapacity(table);
2940
2941 TVARIABLE(IntPtrT, var_hash, GetHash(key, &if_no_hash));
2942 TNode<IntPtrT> key_index =
2943 FindKeyIndexForKey(table, key, var_hash.value(), capacity, &if_not_found);
2944
2945 StoreFixedArrayElement(table, ValueIndexFromKeyIndex(key_index), value);
2946 Return(collection);
2947
2948 BIND(&if_no_hash);
2949 {
2950 CSA_DCHECK(this, IsJSReceiver(key));
2951 var_hash = SmiUntag(CreateIdentityHash(key));
2952 Goto(&if_not_found);
2953 }
2954 BIND(&if_not_found);
2955 {
2956 TNode<Int32T> number_of_deleted = LoadNumberOfDeleted(table);
2957 TNode<Int32T> number_of_elements = LoadNumberOfElements(table, 1);
2958
2959 CSA_DCHECK(this,
2960 IntPtrLessThanOrEqual(capacity, IntPtrConstant(INT32_MAX)));
2961 CSA_DCHECK(this,
2962 IntPtrGreaterThanOrEqual(capacity, IntPtrConstant(INT32_MIN)));
2963 // TODO(pwong): Port HashTable's Rehash() and EnsureCapacity() to CSA.
2964 GotoIf(Word32Or(ShouldRehash(number_of_elements, number_of_deleted),
2965 InsufficientCapacityToAdd(TruncateIntPtrToInt32(capacity),
2966 number_of_elements,
2967 number_of_deleted)),
2968 &call_runtime);
2969
2970 TNode<IntPtrT> insertion_key_index =
2971 FindKeyIndexForInsertion(table, var_hash.value(), capacity);
2972 AddEntry(table, insertion_key_index, key, value, number_of_elements);
2973 Return(collection);
2974 }
2975 BIND(&call_runtime);
2976 {
2977 CallRuntime(Runtime::kWeakCollectionSet, context, collection, key, value,
2978 SmiTag(var_hash.value()));
2979 Return(collection);
2980 }
2981}
2982
2983TF_BUILTIN(WeakMapPrototypeDelete, CodeStubAssembler) {
2984 auto context = Parameter<Context>(Descriptor::kContext);
2985 auto receiver = Parameter<Object>(Descriptor::kReceiver);
2986 auto key = Parameter<Object>(Descriptor::kKey);
2987
2988 ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2989 "WeakMap.prototype.delete");
2990
2991 // This check breaks a known exploitation technique. See crbug.com/1263462
2992 CSA_HOLE_SECURITY_CHECK(this, TaggedNotEqual(key, TheHoleConstant()));
2993
2994 Return(CallBuiltin(Builtin::kWeakCollectionDelete, context, receiver, key));
2995}
2996
2998 auto context = Parameter<Context>(Descriptor::kContext);
2999 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3000 auto key = Parameter<Object>(Descriptor::kKey);
3001 auto value = Parameter<Object>(Descriptor::kValue);
3002
3003 ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
3004 "WeakMap.prototype.set");
3005
3006 Label throw_invalid_key(this);
3007 GotoIfCannotBeHeldWeakly(key, &throw_invalid_key);
3008
3009 Return(
3010 CallBuiltin(Builtin::kWeakCollectionSet, context, receiver, key, value));
3011
3012 BIND(&throw_invalid_key);
3013 ThrowTypeError(context, MessageTemplate::kInvalidWeakMapKey, key);
3014}
3015
3017 auto context = Parameter<Context>(Descriptor::kContext);
3018 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3019 auto value = Parameter<Object>(Descriptor::kValue);
3020
3021 ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
3022 "WeakSet.prototype.add");
3023
3024 Label throw_invalid_value(this);
3025 GotoIfCannotBeHeldWeakly(value, &throw_invalid_value);
3026
3027 Return(CallBuiltin(Builtin::kWeakCollectionSet, context, receiver, value,
3028 TrueConstant()));
3029
3030 BIND(&throw_invalid_value);
3031 ThrowTypeError(context, MessageTemplate::kInvalidWeakSetValue, value);
3032}
3033
3034TF_BUILTIN(WeakSetPrototypeDelete, CodeStubAssembler) {
3035 auto context = Parameter<Context>(Descriptor::kContext);
3036 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3037 auto value = Parameter<Object>(Descriptor::kValue);
3038
3039 ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
3040 "WeakSet.prototype.delete");
3041
3042 // This check breaks a known exploitation technique. See crbug.com/1263462
3043 CSA_HOLE_SECURITY_CHECK(this, TaggedNotEqual(value, TheHoleConstant()));
3044
3045 Return(CallBuiltin(Builtin::kWeakCollectionDelete, context, receiver, value));
3046}
3047
3049 const auto receiver = Parameter<Object>(Descriptor::kReceiver);
3050 const auto key = Parameter<Object>(Descriptor::kKey);
3051 const auto context = Parameter<Context>(Descriptor::kContext);
3052
3053 Label return_false(this);
3054
3055 ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
3056 "WeakSet.prototype.has");
3057
3058 const TNode<EphemeronHashTable> table = LoadTable(CAST(receiver));
3059 const TNode<Object> index =
3060 CallBuiltin(Builtin::kWeakMapLookupHashIndex, context, table, key);
3061
3062 GotoIf(TaggedEqual(index, SmiConstant(-1)), &return_false);
3063
3064 Return(TrueConstant());
3065
3066 BIND(&return_false);
3067 Return(FalseConstant());
3068}
3069
3071
3072} // namespace internal
3073} // namespace v8
#define BIND(label)
#define TVARIABLE(...)
#define CSA_DCHECK(csa,...)
#define CSA_HOLE_SECURITY_CHECK(csa, x)
#define TF_BUILTIN(Name, AssemblerBase)
Builtins::Kind kind
Definition builtins.cc:40
static constexpr U kMask
Definition bit-field.h:41
TNode< Object > LoadAndNormalizeFixedDoubleArrayElement(TNode< HeapObject > elements, TNode< IntPtrT > index)
TNode< Map > GetInitialCollectionPrototype(Variant variant, TNode< Context > native_context)
virtual void GetEntriesIfFastCollectionOrIterable(Variant variant, TNode< Object > initial_entries, TNode< Context > context, TVariable< HeapObject > *var_entries_table, TVariable< IntPtrT > *var_number_of_elements, Label *if_not_fast_collection)=0
TNode< JSFunction > GetInitialAddFunction(Variant variant, TNode< Context > native_context)
void AddConstructorEntriesFromFastJSArray(Variant variant, TNode< Context > context, TNode< Context > native_context, TNode< JSAny > collection, TNode< JSArray > fast_jsarray, Label *if_may_have_side_effects, TVariable< IntPtrT > &var_current_index)
TNode< JSFunction > GetConstructor(Variant variant, TNode< Context > native_context)
TNode< Object > LoadAndNormalizeFixedArrayElement(TNode< FixedArray > elements, TNode< IntPtrT > index)
TNode< Object > GetAddFunction(Variant variant, TNode< Context > context, TNode< JSAny > collection)
TNode< JSObject > AllocateJSCollection(TNode< Context > context, TNode< JSFunction > constructor, TNode< JSReceiver > new_target)
virtual TNode< HeapObject > AllocateTable(Variant variant, TNode< IntPtrT > at_least_space_for)=0
void AddConstructorEntries(Variant variant, TNode< Context > context, TNode< NativeContext > native_context, TNode< JSAnyNotSmi > collection, TNode< JSAny > initial_entries)
virtual void AddConstructorEntriesFromFastCollection(Variant variant, TNode< HeapObject > collection, TNode< HeapObject > source_table)=0
void GenerateConstructor(Variant variant, Handle< String > constructor_function_name, TNode< Object > new_target, TNode< IntPtrT > argc, TNode< Context > context)
void GotoIfInitialAddFunctionModified(Variant variant, TNode< NativeContext > native_context, TNode< HeapObject > collection, Label *if_modified)
void GotoIfCannotBeHeldWeakly(const TNode< Object > obj, Label *if_cannot_be_held_weakly)
void AddConstructorEntry(Variant variant, TNode< Context > context, TNode< JSAny > collection, TNode< Object > add_function, TNode< JSAny > key_value, Label *if_may_have_side_effects=nullptr, Label *if_exception=nullptr, TVariable< Object > *var_exception=nullptr)
TNode< BoolT > HasInitialCollectionPrototype(Variant variant, TNode< Context > native_context, TNode< JSAny > collection)
TNode< JSObject > AllocateJSCollectionSlow(TNode< Context > context, TNode< JSFunction > constructor, TNode< JSReceiver > new_target)
void AddConstructorEntriesFromIterable(Variant variant, TNode< Context > context, TNode< Context > native_context, TNode< JSAny > collection, TNode< JSAny > iterable, Label *if_exception, TVariable< JSReceiver > *var_iterator, TVariable< Object > *var_exception)
TNode< JSObject > AllocateJSCollectionFast(TNode< JSFunction > constructor)
TNode< JSArray > AllocateJSArray(ElementsKind kind, TNode< Map > array_map, TNode< IntPtrT > capacity, TNode< Smi > length, std::optional< TNode< AllocationSite > > allocation_site, AllocationFlags allocation_flags=AllocationFlag::kNone)
TNode< Smi > SmiFromInt32(TNode< Int32T > value)
TNode< BoolT > IsNullOrUndefined(TNode< Object > object)
TNode< FixedArrayBase > AllocateFixedArray(ElementsKind kind, TNode< TIndex > capacity, AllocationFlags flags=AllocationFlag::kNone, std::optional< TNode< Map > > fixed_array_map=std::nullopt)
TNode< Uint32T > LoadNameHash(TNode< Name > name, Label *if_hash_not_computed=nullptr)
TNode< IntPtrT > LoadAndUntagPositiveSmiObjectField(TNode< HeapObject > object, int offset)
TNode< Int32T > TruncateIntPtrToInt32(TNode< IntPtrT > value)
void SetPendingMessage(TNode< Union< Hole, JSMessageObject > > message)
TNode< BoolT > InstanceTypeEqual(TNode< Int32T > instance_type, int type)
TNode< BoolT > IsFastSmiOrTaggedElementsKind(TNode< Int32T > elements_kind)
TNode< BoolT > IsSymbolInstanceType(TNode< Int32T > instance_type)
TNode< Uint32T > PositiveSmiToUint32(TNode< Smi > value)
void StoreFixedArrayElement(TNode< FixedArray > object, int index, TNode< Object > value, WriteBarrierMode barrier_mode=UPDATE_WRITE_BARRIER, CheckBounds check_bounds=CheckBounds::kAlways)
TNode< Object > UnsafeLoadFixedArrayElement(TNode< FixedArray > object, TNode< IntPtrT > index, int additional_offset=0)
TNode< HeapObject > AllocateInNewSpace(TNode< IntPtrT > size, AllocationFlags flags=AllocationFlag::kNone)
TNode< BoolT > IsJSReceiver(TNode< HeapObject > object)
TNode< Smi > SmiFromIntPtr(TNode< IntPtrT > value)
void ThrowTypeError(TNode< Context > context, MessageTemplate message, char const *arg0=nullptr, char const *arg1=nullptr)
TNode< JSAny > GetProperty(TNode< Context > context, TNode< JSAny > receiver, Handle< Name > name)
TNode< Smi > SmiTag(TNode< IntPtrT > value)
void Increment(TVariable< TIndex > *variable, int value=1)
void BranchIfStringEqual(TNode< String > lhs, TNode< String > rhs, Label *if_true, Label *if_false, TVariable< Boolean > *result=nullptr)
TNode< BoolT > TaggedEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
void StoreObjectFieldRoot(TNode< HeapObject > object, int offset, RootIndex root)
TNode< FixedArrayBase > LoadElements(TNode< JSObject > object)
TNode< T > LoadObjectField(TNode< HeapObject > object, int offset)
TNode< IntPtrT > ChangePositiveInt32ToIntPtr(TNode< Int32T > input)
TNode< JSPrototype > LoadMapPrototype(TNode< Map > map)
TNode< ArrayList > ArrayListAdd(TNode< ArrayList > array, TNode< Object > object)
TNode< Uint32T > LoadJSReceiverIdentityHash(TNode< JSReceiver > receiver, Label *if_no_hash=nullptr)
TNode< IntPtrT > SmiUntag(TNode< Smi > value)
TNode< Map > LoadJSArrayElementsMap(ElementsKind kind, TNode< NativeContext > native_context)
void BuildFastLoop(const VariableList &vars, TVariable< TIndex > &var_index, TNode< TIndex > start_index, TNode< TIndex > end_index, const FastLoopBody< TIndex > &body, TNode< TIndex > increment, LoopUnrollingMode unrolling_mode, IndexAdvanceMode advance_mode, IndexAdvanceDirection advance_direction)
TNode< ArrayList > AllocateArrayList(TNode< Smi > size)
TNode< Float64T > SmiToFloat64(TNode< Smi > value)
TNode< Int32T > LoadElementsKind(TNode< HeapObject > object)
TNode< BoolT > IsBigIntInstanceType(TNode< Int32T > instance_type)
TNode< Int32T > LoadAndUntagToWord32ObjectField(TNode< HeapObject > object, int offset)
void StoreObjectFieldNoWriteBarrier(TNode< HeapObject > object, TNode< IntPtrT > offset, TNode< T > value)
TNode< Object > LoadFixedArrayElement(TNode< FixedArray > object, TNode< TIndex > index, int additional_offset=0, CheckBounds check_bounds=CheckBounds::kAlways)
TNode< JSAny > Call(TNode< Context > context, TNode< TCallable > callable, ConvertReceiverMode mode, TNode< JSAny > receiver, TArgs... args)
TNode< BoolT > IsJSReceiverInstanceType(TNode< Int32T > instance_type)
TNode< Uint16T > LoadMapInstanceType(TNode< Map > map)
TNode< IntPtrT > ElementOffsetFromIndex(TNode< TIndex > index, ElementsKind kind, int base_size=0)
void UnsafeStoreFixedArrayElement(TNode< FixedArray > object, int index, TNode< Object > value, WriteBarrierMode barrier_mode=UPDATE_WRITE_BARRIER)
TNode< HeapNumber > AllocateHeapNumberWithValue(TNode< Float64T > value)
void ArrayListSet(TNode< ArrayList > array, TNode< Smi > index, TNode< Object > object)
TNode< Uint16T > LoadInstanceType(TNode< HeapObject > object)
void ArrayListSetLength(TNode< ArrayList > array, TNode< Smi > length)
TNode< BoolT > IsConstructorMap(TNode< Map > map)
TNode< NativeContext > LoadNativeContext(TNode< Context > context)
TNode< OrderedHashSet > AllocateOrderedHashSet()
TNode< T > Select(TNode< BoolT > condition, const NodeGenerator< T > &true_body, const NodeGenerator< T > &false_body, BranchHint branch_hint=BranchHint::kNone)
TNode< BoolT > TaggedIsSmi(TNode< MaybeObject > a)
TNode< Smi > LoadFastJSArrayLength(TNode< JSArray > array)
TNode< IntPtrT > HashTableComputeCapacity(TNode< IntPtrT > at_least_space_for)
TNode< Float64T > LoadHeapNumberValue(TNode< HeapObject > object)
TNode< OrderedHashMap > AllocateOrderedHashMap()
TNode< Float64T > LoadFixedDoubleArrayElement(TNode< FixedDoubleArray > object, TNode< IntPtrT > index, Label *if_hole=nullptr, MachineType machine_type=MachineType::Float64())
TNode< Map > LoadMap(TNode< HeapObject > object)
TNode< BoolT > IsString(TNode< HeapObject > object)
TNode< Int32T > SmiToInt32(TNode< Smi > value)
TNode< JSObject > AllocateJSObjectFromMap(TNode< Map > map, std::optional< TNode< HeapObject > > properties=std::nullopt, std::optional< TNode< FixedArray > > elements=std::nullopt, AllocationFlags flags=AllocationFlag::kNone, SlackTrackingMode slack_tracking_mode=kNoSlackTracking)
TNode< Union< Hole, JSMessageObject > > GetPendingMessage()
Uint32LessThanOrEqual IntPtrGreaterThanOrEqual
TNode< BoolT > IsStringInstanceType(TNode< Int32T > instance_type)
TNode< BoolT > IsAlwaysSharedSpaceJSObjectInstanceType(TNode< Int32T > instance_type)
TNode< IntPtrT > PositiveSmiUntag(TNode< Smi > value)
TNode< BoolT > IsCallable(TNode< HeapObject > object)
TNode< BoolT > IsBigInt(TNode< HeapObject > object)
void StoreObjectField(TNode< HeapObject > object, int offset, TNode< Smi > value)
void BranchIfFloat64IsNaN(TNode< Float64T > value, Label *if_true, Label *if_false)
void FillFixedArrayWithValue(ElementsKind kind, TNode< FixedArrayBase > array, TNode< TIndex > from_index, TNode< TIndex > to_index, RootIndex value_root_index)
void StoreMapNoWriteBarrier(TNode< HeapObject > object, RootIndex map_root_index)
void BranchIfSetIteratorProtectorValid(Label *if_true, Label *if_false)
TNode< BoolT > TableHasKey(const TNode< Object > context, TNode< OrderedHashSet > table, TNode< Object > key)
void SameValueZeroBigInt(TNode< BigInt > key, TNode< Object > candidate_key, Label *if_same, Label *if_not_same)
void BranchIfIterableWithOriginalValueSetIterator(TNode< Object > iterable, TNode< Context > context, Label *if_true, Label *if_false)
const TNode< JSAny > NormalizeNumberKey(const TNode< JSAny > key)
void SameValueZeroHeapNumber(TNode< Float64T > key_float, TNode< Object > candidate_key, Label *if_same, Label *if_not_same)
TorqueStructKeyValueIndexTuple NextKeyValueIndexTuple(const TNode< OrderedHashMap > table, const TNode< IntPtrT > index, Label *if_end)
void TryLookupOrderedHashTableIndex(const TNode< CollectionType > table, const TNode< Object > key, TVariable< IntPtrT > *result, Label *if_entry_found, Label *if_not_found)
void AddConstructorEntriesFromFastCollection(Variant variant, TNode< HeapObject > collection, TNode< HeapObject > source_table) override
TorqueStructKeyValueIndexTuple NextKeyValueIndexTupleUnmodifiedTable(const TNode< OrderedHashMap > table, const TNode< Int32T > number_of_buckets, const TNode< Int32T > used_capacity, const TNode< IntPtrT > index, Label *if_end)
TNode< Smi > CallGetOrCreateHashRaw(const TNode< HeapObject > key)
void StoreKeyInOrderedHashSetEntry(const TNode< OrderedHashSet > table, const TNode< Object > key, const TNode< IntPtrT > entry, CheckBounds check_bounds=CheckBounds::kAlways)
TNode< JSArray > MapIteratorToList(TNode< Context > context, TNode< JSMapIterator > iterator)
const TNode< OrderedHashMap > AddValueToKeyedGroup(const TNode< OrderedHashMap > groups, const TNode< Object > key, const TNode< Object > value, const TNode< String > methodName)
void StoreKeyValueInOrderedHashMapEntry(const TNode< OrderedHashMap > table, const TNode< Object > key, const TNode< Object > value, const TNode< IntPtrT > entry, CheckBounds check_bounds=CheckBounds::kAlways)
TNode< JSAny > LoadKeyFromOrderedHashTableEntry(const TNode< CollectionType > table, const TNode< IntPtrT > entry, CheckBounds check_bounds=CheckBounds::kAlways)
void FindOrderedHashTableEntry(const TNode< CollectionType > table, const TNode< Uint32T > hash, const std::function< void(TNode< Object >, Label *, Label *)> &key_compare, TVariable< IntPtrT > *entry_start_position, Label *entry_found, Label *not_found)
void StoreOrderedHashTableNewEntry(const TNode< CollectionType > table, const TNode< IntPtrT > hash, const TNode< IntPtrT > number_of_buckets, const TNode< IntPtrT > occupancy, const StoreAtEntry< CollectionType > &store_at_new_entry)
TNode< UnionOf< JSAny, ArrayList > > LoadValueFromOrderedHashMapEntry(const TNode< OrderedHashMap > table, const TNode< IntPtrT > entry, CheckBounds check_bounds=CheckBounds::kAlways)
void FindOrderedHashTableEntryForSmiKey(TNode< CollectionType > table, TNode< Smi > key_tagged, TVariable< IntPtrT > *result, Label *entry_found, Label *not_found)
void BranchIfIterableWithOriginalKeyOrValueMapIterator(TNode< Object > iterable, TNode< Context > context, Label *if_true, Label *if_false)
void SameValueZeroString(TNode< String > key_string, TNode< Object > candidate_key, Label *if_same, Label *if_not_same)
void FindOrderedHashTableEntryForStringKey(TNode< CollectionType > table, TNode< String > key_tagged, TVariable< IntPtrT > *result, Label *entry_found, Label *not_found)
TNode< JSAny > UnsafeLoadKeyFromOrderedHashTableEntry(const TNode< CollectionType > table, const TNode< IntPtrT > entry)
void BranchIfMapIteratorProtectorValid(Label *if_true, Label *if_false)
TNode< JSArray > SetOrSetIteratorToList(TNode< Context > context, TNode< HeapObject > iterable)
void FindOrderedHashTableEntryForOtherKey(TNode< CollectionType > table, TNode< HeapObject > key_heap_object, TVariable< IntPtrT > *result, Label *entry_found, Label *not_found)
std::tuple< TNode< JSAny >, TNode< IntPtrT >, TNode< IntPtrT > > NextSkipHashTableHoles(TNode< TableType > table, TNode< IntPtrT > index, Label *if_end)
TNode< Uint32T > ComputeStringHash(TNode< String > string_key)
std::pair< TNode< TableType >, TNode< IntPtrT > > TransitionAndUpdate(const TNode< IteratorType > iterator)
TorqueStructKeyIndexPair NextKeyIndexPairUnmodifiedTable(const TNode< CollectionType > table, const TNode< Int32T > number_of_buckets, const TNode< Int32T > used_capacity, const TNode< IntPtrT > index, Label *if_end)
void FindOrderedHashTableEntryForHeapNumberKey(TNode< CollectionType > table, TNode< HeapNumber > key_heap_number, TVariable< IntPtrT > *result, Label *entry_found, Label *not_found)
TorqueStructOrderedHashSetIndexPair TransitionOrderedHashSetNoUpdate(const TNode< OrderedHashSet > table, const TNode< IntPtrT > index)
void AddNewToOrderedHashTable(const TNode< CollectionType > table, const TNode< Object > normalised_key, const TNode< IntPtrT > number_of_buckets, const TNode< IntPtrT > occupancy, const StoreAtEntry< CollectionType > &store_at_new_entry)
void SameValueZeroSmi(TNode< Smi > key_smi, TNode< Object > candidate_key, Label *if_same, Label *if_not_same)
std::function< void(const TNode< TableType > table, const TNode< IntPtrT > index)> UpdateInTransition
void FindOrderedHashTableEntryForBigIntKey(TNode< CollectionType > table, TNode< BigInt > key_big_int, TVariable< IntPtrT > *result, Label *entry_found, Label *not_found)
TNode< UnionOf< JSAny, ArrayList > > UnsafeLoadValueFromOrderedHashMapEntry(const TNode< OrderedHashMap > table, const TNode< IntPtrT > entry)
TorqueStructKeyIndexPair NextKeyIndexPair(const TNode< CollectionType > table, const TNode< IntPtrT > index, Label *if_end)
TNode< HeapObject > AllocateJSCollectionIterator(const TNode< Context > context, int map_index, const TNode< HeapObject > collection)
void GetEntriesIfFastCollectionOrIterable(Variant variant, TNode< Object > initial_entries, TNode< Context > context, TVariable< HeapObject > *var_entries_table, TVariable< IntPtrT > *var_number_of_elements, Label *if_not_fast_collection) override
std::function< void(const TNode< CollectionType > table, const TNode< IntPtrT > entry_start)> StoreAtEntry
TNode< OrderedHashSet > AddToSetTable(TNode< Object > context, TNode< OrderedHashSet > table, TNode< JSAny > key, TNode< String > method_name)
void StoreValueInOrderedHashMapEntry(const TNode< OrderedHashMap > table, const TNode< Object > value, const TNode< IntPtrT > entry, CheckBounds check_bounds=CheckBounds::kAlways)
void AddNewToOrderedHashSet(const TNode< OrderedHashSet > table, const TNode< JSAny > key, const TNode< IntPtrT > number_of_buckets, const TNode< IntPtrT > occupancy)
void UnsafeStoreKeyInOrderedHashSetEntry(const TNode< OrderedHashSet > table, const TNode< Object > key, const TNode< IntPtrT > entry)
TNode< Word32T > ComputeUnseededHash(TNode< IntPtrT > key)
TNode< CollectionType > AddToOrderedHashTable(const TNode< CollectionType > table, const TNode< Object > key, const GrowCollection< CollectionType > &grow, const StoreAtEntry< CollectionType > &store_at_new_entry, const StoreAtEntry< CollectionType > &store_at_existing_entry)
TNode< Uint32T > CallGetHashRaw(const TNode< HeapObject > key)
TNode< Smi > DeleteFromSetTable(const TNode< Object > context, TNode< OrderedHashSet > table, TNode< Object > key, Label *not_found)
TNode< OrderedHashSet > SetOrSetIteratorToSet(TNode< Object > iterator)
std::pair< TNode< TableType >, TNode< IntPtrT > > Transition(const TNode< TableType > table, const TNode< IntPtrT > index, UpdateInTransition< TableType > const &update_in_transition)
TNode< HeapObject > AllocateTable(Variant variant, TNode< IntPtrT > at_least_space_for) override
std::function< const TNode< CollectionType >()> GrowCollection
void AddConstructorEntriesFromSet(TNode< JSSet > collection, TNode< OrderedHashSet > table)
TNode< Uint32T > GetHash(const TNode< HeapObject > key)
TNode< JSObject > FastNewObject(TNode< Context > context, TNode< JSFunction > target, TNode< JSReceiver > new_target)
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
v8::internal::Factory * factory()
Definition isolate.h:1527
TNode< JSReceiver > IteratorStep(TNode< Context > context, const IteratorRecord &iterator, Label *if_done, std::optional< TNode< Map > > fast_iterator_result_map=std::nullopt)
TNode< JSAny > IteratorValue(TNode< Context > context, TNode< JSReceiver > result, std::optional< TNode< Map > > fast_iterator_result_map=std::nullopt)
IteratorRecord GetIterator(TNode< Context > context, TNode< JSAny > object)
static const int kAddFunctionDescriptorIndex
static const int kAddFunctionDescriptorIndex
static constexpr MachineType Pointer()
static constexpr MachineType AnyTagged()
static const int kProtectorValid
Definition protectors.h:15
void CheckAndBranch(TNode< HeapObject > prototype, Label *if_unmodified, Label *if_modified)
TNode< Int32T > LoadNumberOfElements(TNode< EphemeronHashTable > table, int offset)
TNode< IntPtrT > GetHash(const TNode< HeapObject > key, Label *if_no_hash)
TNode< IntPtrT > LoadTableCapacity(TNode< EphemeronHashTable > table)
TNode< BoolT > ShouldRehash(TNode< Int32T > number_of_elements, TNode< Int32T > number_of_deleted)
TNode< IntPtrT > KeyIndexFromEntry(TNode< IntPtrT > entry)
std::function< void(TNode< Object > entry_key, Label *if_same)> KeyComparator
TNode< IntPtrT > FindKeyIndex(TNode< HeapObject > table, TNode< IntPtrT > key_hash, TNode< IntPtrT > capacity, const KeyComparator &key_compare)
TNode< Smi > CreateIdentityHash(TNode< Object > receiver)
TNode< Word32T > InsufficientCapacityToAdd(TNode< Int32T > capacity, TNode< Int32T > number_of_elements, TNode< Int32T > number_of_deleted)
void AddEntry(TNode< EphemeronHashTable > table, TNode< IntPtrT > key_index, TNode< Object > key, TNode< Object > value, TNode< Int32T > number_of_elements)
TNode< HeapObject > AllocateTable(Variant variant, TNode< IntPtrT > at_least_space_for) override
TNode< IntPtrT > FindKeyIndexForInsertion(TNode< HeapObject > table, TNode< IntPtrT > key_hash, TNode< IntPtrT > capacity)
void RemoveEntry(TNode< EphemeronHashTable > table, TNode< IntPtrT > key_index, TNode< IntPtrT > number_of_elements)
TNode< Word32T > ShouldShrink(TNode< IntPtrT > capacity, TNode< IntPtrT > number_of_elements)
TNode< IntPtrT > ValueIndexFromKeyIndex(TNode< IntPtrT > key_index)
TNode< IntPtrT > FindKeyIndexForKey(TNode< HeapObject > table, TNode< Object > key, TNode< IntPtrT > hash, TNode< IntPtrT > capacity, Label *if_not_found)
TNode< IntPtrT > EntryMask(TNode< IntPtrT > capacity)
TNode< EphemeronHashTable > LoadTable(TNode< JSWeakCollection > collection)
TNode< IntPtrT > Coefficient(TNode< IntPtrT > capacity)
TNode< Int32T > LoadNumberOfDeleted(TNode< EphemeronHashTable > table, int offset=0)
TNode< IntPtrT > IntPtrMul(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< IntPtrT > IntPtrAdd(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< Int32T > Signed(TNode< Word32T > x)
TNode< IntPtrT > IntPtrConstant(intptr_t value)
TNode< UintPtrT > ChangeUint32ToWord(TNode< Word32T > value)
TNode< T > UncheckedCast(Node *value)
TNode< IntPtrT > WordShl(TNode< IntPtrT > left, TNode< IntegralT > right)
TNode< Int32T > Int32Mul(TNode< Int32T > left, TNode< Int32T > right)
void GotoIfNot(TNode< IntegralT > condition, Label *false_label, GotoHint goto_hint=GotoHint::kNone)
void Return(TNode< Object > value)
TNode< Uint32T > Unsigned(TNode< Word32T > x)
TNode< Int32T > Word32And(TNode< Int32T > left, TNode< Int32T > right)
TNode< Int32T > Int32Add(TNode< Int32T > left, TNode< Int32T > right)
TNode< Smi > SmiConstant(Tagged< Smi > value)
void GotoIf(TNode< IntegralT > condition, Label *true_label, GotoHint goto_hint=GotoHint::kNone)
TNode< IntPtrT > ChangeInt32ToIntPtr(TNode< Word32T > value)
TNode< Int32T > Int32Sub(TNode< Int32T > left, TNode< Int32T > right)
TNode< Int32T > Word32Or(TNode< Int32T > left, TNode< Int32T > right)
TNode< Int32T > Word32Shl(TNode< Int32T > left, TNode< Int32T > right)
TNode< BoolT > IntPtrEqual(TNode< WordT > left, TNode< WordT > right)
TNode< IntPtrT > WordAnd(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< IntPtrT > IntPtrSub(TNode< IntPtrT > left, TNode< IntPtrT > right)
TNode< Uint32T > Uint32Sub(TNode< Uint32T > left, TNode< Uint32T > right)
TNode< Float64T > Float64Constant(double value)
TNode< Uint32T > Word32Shr(TNode< Uint32T > left, TNode< Uint32T > right)
TNode< ExternalReference > ExternalConstant(ExternalReference address)
TNode< Int32T > Int32Constant(int32_t value)
Node * CallCFunction(Node *function, std::optional< MachineType > return_type, CArgs... cargs)
TNode< Uint32T > Uint32Constant(uint32_t value)
TNode< Type > HeapConstantNoHole(Handle< Type > object)
TNode< BoolT > Word32Equal(TNode< Word32T > left, TNode< Word32T > right)
TNode< T > CallRuntime(Runtime::FunctionId function, TNode< Object > context, TArgs... args)
void Store(Node *base, Node *value)
TNode< UintPtrT > WordShr(TNode< UintPtrT > left, TNode< IntegralT > right)
TNode< T > CallBuiltin(Builtin id, TNode< Object > context, TArgs... args)
void Branch(TNode< IntegralT > condition, Label *true_label, Label *false_label, BranchHint branch_hint=BranchHint::kNone)
#define CAST(x)
int start
int end
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
DirectHandle< Object > new_target
Definition execution.cc:75
Isolate * isolate
AssemblerT assembler
int32_t offset
TNode< Context > context
TNode< Object > this_arg
TNode< Object > receiver
TNode< Object > callback
ZoneVector< RpoNumber > & result
MovableLabel handler
void BranchIfIterableWithOriginalKeyOrValueMapIterator(compiler::CodeAssemblerState *state, TNode< Object > iterable, TNode< Context > context, compiler::CodeAssemblerLabel *if_true, compiler::CodeAssemblerLabel *if_false)
constexpr int kTaggedSize
Definition globals.h:542
@ SKIP_WRITE_BARRIER
Definition objects.h:52
@ UPDATE_WRITE_BARRIER
Definition objects.h:55
@ UPDATE_EPHEMERON_KEY_WRITE_BARRIER
Definition objects.h:54
Handle< To > UncheckedCast(Handle< From > value)
Definition handles-inl.h:55
DONT_OVERRIDE DISABLE_ALLOCATION_SITES HOLEY_ELEMENTS
const int kHeapObjectTag
Definition v8-internal.h:72
void BranchIfIterableWithOriginalValueSetIterator(compiler::CodeAssemblerState *state, TNode< Object > iterable, TNode< Context > context, compiler::CodeAssemblerLabel *if_true, compiler::CodeAssemblerLabel *if_false)
return value
Definition map-inl.h:893
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define OFFSET_OF_DATA_START(Type)