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 
5 #include "src/builtins/builtins-constructor-gen.h"
6 #include "src/builtins/builtins-iterator-gen.h"
7 #include "src/builtins/builtins-utils-gen.h"
8 #include "src/code-stub-assembler.h"
9 #include "src/heap/factory-inl.h"
10 #include "src/objects/hash-table-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 using compiler::Node;
16 template <class T>
17 using TNode = compiler::TNode<T>;
18 template <class T>
19 using TVariable = compiler::TypedCodeAssemblerVariable<T>;
20 
21 class BaseCollectionsAssembler : public CodeStubAssembler {
22  public:
BaseCollectionsAssembler(compiler::CodeAssemblerState * state)23   explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state)
24       : CodeStubAssembler(state) {}
25 
~BaseCollectionsAssembler()26   virtual ~BaseCollectionsAssembler() {}
27 
28  protected:
29   enum Variant { kMap, kSet, kWeakMap, kWeakSet };
30 
31   // Adds an entry to a collection.  For Maps, properly handles extracting the
32   // key and value from the entry (see LoadKeyValue()).
33   void AddConstructorEntry(Variant variant, TNode<Context> context,
34                            TNode<Object> collection, TNode<Object> add_function,
35                            TNode<Object> key_value,
36                            Label* if_may_have_side_effects = nullptr,
37                            Label* if_exception = nullptr,
38                            TVariable<Object>* var_exception = nullptr);
39 
40   // Adds constructor entries to a collection.  Choosing a fast path when
41   // possible.
42   void AddConstructorEntries(Variant variant, TNode<Context> context,
43                              TNode<Context> native_context,
44                              TNode<Object> collection,
45                              TNode<Object> initial_entries);
46 
47   // Fast path for adding constructor entries.  Assumes the entries are a fast
48   // JS array (see CodeStubAssembler::BranchIfFastJSArray()).
49   void AddConstructorEntriesFromFastJSArray(Variant variant,
50                                             TNode<Context> context,
51                                             TNode<Context> native_context,
52                                             TNode<Object> collection,
53                                             TNode<JSArray> fast_jsarray,
54                                             Label* if_may_have_side_effects);
55 
56   // Adds constructor entries to a collection using the iterator protocol.
57   void AddConstructorEntriesFromIterable(Variant variant,
58                                          TNode<Context> context,
59                                          TNode<Context> native_context,
60                                          TNode<Object> collection,
61                                          TNode<Object> iterable);
62 
63   // Constructs a collection instance. Choosing a fast path when possible.
64   TNode<Object> AllocateJSCollection(TNode<Context> context,
65                                      TNode<JSFunction> constructor,
66                                      TNode<Object> new_target);
67 
68   // Fast path for constructing a collection instance if the constructor
69   // function has not been modified.
70   TNode<Object> AllocateJSCollectionFast(TNode<HeapObject> constructor);
71 
72   // Fallback for constructing a collection instance if the constructor function
73   // has been modified.
74   TNode<Object> AllocateJSCollectionSlow(TNode<Context> context,
75                                          TNode<JSFunction> constructor,
76                                          TNode<Object> new_target);
77 
78   // Allocates the backing store for a collection.
79   virtual TNode<Object> AllocateTable(Variant variant, TNode<Context> context,
80                                       TNode<IntPtrT> at_least_space_for) = 0;
81 
82   // Main entry point for a collection constructor builtin.
83   void GenerateConstructor(Variant variant,
84                            Handle<String> constructor_function_name);
85 
86   // Retrieves the collection function that adds an entry. `set` for Maps and
87   // `add` for Sets.
88   TNode<Object> GetAddFunction(Variant variant, TNode<Context> context,
89                                TNode<Object> collection);
90 
91   // Retrieves the collection constructor function.
92   TNode<JSFunction> GetConstructor(Variant variant,
93                                    TNode<Context> native_context);
94 
95   // Retrieves the initial collection function that adds an entry. Should only
96   // be called when it is certain that a collection prototype's map hasn't been
97   // changed.
98   TNode<JSFunction> GetInitialAddFunction(Variant variant,
99                                           TNode<Context> native_context);
100 
101   // Retrieves the offset to access the backing table from the collection.
102   int GetTableOffset(Variant variant);
103 
104   // Estimates the number of entries the collection will have after adding the
105   // entries passed in the constructor. AllocateTable() can use this to avoid
106   // the time of growing/rehashing when adding the constructor entries.
107   TNode<IntPtrT> EstimatedInitialSize(TNode<Object> initial_entries,
108                                       TNode<BoolT> is_fast_jsarray);
109 
110   void GotoIfNotJSReceiver(Node* const obj, Label* if_not_receiver);
111 
112   // Determines whether the collection's prototype has been modified.
113   TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
114                                              TNode<Context> native_context,
115                                              TNode<Object> collection);
116 
117   // Loads an element from a fixed array.  If the element is the hole, returns
118   // `undefined`.
119   TNode<Object> LoadAndNormalizeFixedArrayElement(TNode<HeapObject> elements,
120                                                   TNode<IntPtrT> index);
121 
122   // Loads an element from a fixed double array.  If the element is the hole,
123   // returns `undefined`.
124   TNode<Object> LoadAndNormalizeFixedDoubleArrayElement(
125       TNode<HeapObject> elements, TNode<IntPtrT> index);
126 
127   // Loads key and value variables with the first and second elements of an
128   // array.  If the array lacks 2 elements, undefined is used.
129   void LoadKeyValue(TNode<Context> context, TNode<Object> maybe_array,
130                     TVariable<Object>* key, TVariable<Object>* value,
131                     Label* if_may_have_side_effects = nullptr,
132                     Label* if_exception = nullptr,
133                     TVariable<Object>* var_exception = nullptr);
134 };
135 
AddConstructorEntry(Variant variant,TNode<Context> context,TNode<Object> collection,TNode<Object> add_function,TNode<Object> key_value,Label * if_may_have_side_effects,Label * if_exception,TVariable<Object> * var_exception)136 void BaseCollectionsAssembler::AddConstructorEntry(
137     Variant variant, TNode<Context> context, TNode<Object> collection,
138     TNode<Object> add_function, TNode<Object> key_value,
139     Label* if_may_have_side_effects, Label* if_exception,
140     TVariable<Object>* var_exception) {
141   CSA_ASSERT(this, Word32BinaryNot(IsTheHole(key_value)));
142   if (variant == kMap || variant == kWeakMap) {
143     TVARIABLE(Object, key);
144     TVARIABLE(Object, value);
145     LoadKeyValue(context, key_value, &key, &value, if_may_have_side_effects,
146                  if_exception, var_exception);
147     Node* key_n = key.value();
148     Node* value_n = value.value();
149     Node* ret = CallJS(CodeFactory::Call(isolate()), context, add_function,
150                        collection, key_n, value_n);
151     GotoIfException(ret, if_exception, var_exception);
152   } else {
153     DCHECK(variant == kSet || variant == kWeakSet);
154     Node* ret = CallJS(CodeFactory::Call(isolate()), context, add_function,
155                        collection, key_value);
156     GotoIfException(ret, if_exception, var_exception);
157   }
158 }
159 
AddConstructorEntries(Variant variant,TNode<Context> context,TNode<Context> native_context,TNode<Object> collection,TNode<Object> initial_entries)160 void BaseCollectionsAssembler::AddConstructorEntries(
161     Variant variant, TNode<Context> context, TNode<Context> native_context,
162     TNode<Object> collection, TNode<Object> initial_entries) {
163   TVARIABLE(BoolT, use_fast_loop,
164             IsFastJSArrayWithNoCustomIteration(initial_entries, context,
165                                                native_context));
166   TNode<IntPtrT> at_least_space_for =
167       EstimatedInitialSize(initial_entries, use_fast_loop.value());
168   Label allocate_table(this, &use_fast_loop), exit(this), fast_loop(this),
169       slow_loop(this, Label::kDeferred);
170   Goto(&allocate_table);
171   BIND(&allocate_table);
172   {
173     TNode<Object> table = AllocateTable(variant, context, at_least_space_for);
174     StoreObjectField(collection, GetTableOffset(variant), table);
175     GotoIf(IsNullOrUndefined(initial_entries), &exit);
176     GotoIfNot(
177         HasInitialCollectionPrototype(variant, native_context, collection),
178         &slow_loop);
179     Branch(use_fast_loop.value(), &fast_loop, &slow_loop);
180   }
181   BIND(&fast_loop);
182   {
183     TNode<JSArray> initial_entries_jsarray =
184         UncheckedCast<JSArray>(initial_entries);
185 #if DEBUG
186     CSA_ASSERT(this, IsFastJSArrayWithNoCustomIteration(
187                          initial_entries_jsarray, context, native_context));
188     TNode<Map> original_initial_entries_map = LoadMap(initial_entries_jsarray);
189 #endif
190 
191     Label if_may_have_side_effects(this, Label::kDeferred);
192     AddConstructorEntriesFromFastJSArray(variant, context, native_context,
193                                          collection, initial_entries_jsarray,
194                                          &if_may_have_side_effects);
195     Goto(&exit);
196 
197     if (variant == kMap || variant == kWeakMap) {
198       BIND(&if_may_have_side_effects);
199 #if DEBUG
200       CSA_ASSERT(this, HasInitialCollectionPrototype(variant, native_context,
201                                                      collection));
202       CSA_ASSERT(this, WordEqual(original_initial_entries_map,
203                                  LoadMap(initial_entries_jsarray)));
204 #endif
205       use_fast_loop = Int32FalseConstant();
206       Goto(&allocate_table);
207     }
208   }
209   BIND(&slow_loop);
210   {
211     AddConstructorEntriesFromIterable(variant, context, native_context,
212                                       collection, initial_entries);
213     Goto(&exit);
214   }
215   BIND(&exit);
216 }
217 
AddConstructorEntriesFromFastJSArray(Variant variant,TNode<Context> context,TNode<Context> native_context,TNode<Object> collection,TNode<JSArray> fast_jsarray,Label * if_may_have_side_effects)218 void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray(
219     Variant variant, TNode<Context> context, TNode<Context> native_context,
220     TNode<Object> collection, TNode<JSArray> fast_jsarray,
221     Label* if_may_have_side_effects) {
222   TNode<FixedArrayBase> elements = LoadElements(fast_jsarray);
223   TNode<Int32T> elements_kind = LoadMapElementsKind(LoadMap(fast_jsarray));
224   TNode<JSFunction> add_func = GetInitialAddFunction(variant, native_context);
225   CSA_ASSERT(
226       this,
227       WordEqual(GetAddFunction(variant, native_context, collection), add_func));
228   CSA_ASSERT(this, IsFastJSArrayWithNoCustomIteration(fast_jsarray, context,
229                                                       native_context));
230   TNode<IntPtrT> length = SmiUntag(LoadFastJSArrayLength(fast_jsarray));
231   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(length, IntPtrConstant(0)));
232   CSA_ASSERT(
233       this, HasInitialCollectionPrototype(variant, native_context, collection));
234 
235 #if DEBUG
236   TNode<Map> original_collection_map = LoadMap(CAST(collection));
237   TNode<Map> original_fast_js_array_map = LoadMap(fast_jsarray);
238 #endif
239   Label exit(this), if_doubles(this), if_smiorobjects(this);
240   GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &exit);
241   Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects,
242          &if_doubles);
243   BIND(&if_smiorobjects);
244   {
245     auto set_entry = [&](Node* index) {
246       TNode<Object> element = LoadAndNormalizeFixedArrayElement(
247           elements, UncheckedCast<IntPtrT>(index));
248       AddConstructorEntry(variant, context, collection, add_func, element,
249                           if_may_have_side_effects);
250     };
251 
252     // Instead of using the slower iteration protocol to iterate over the
253     // elements, a fast loop is used.  This assumes that adding an element
254     // to the collection does not call user code that could mutate the elements
255     // or collection.
256     BuildFastLoop(IntPtrConstant(0), length, set_entry, 1,
257                   ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
258     Goto(&exit);
259   }
260   BIND(&if_doubles);
261   {
262     // A Map constructor requires entries to be arrays (ex. [key, value]),
263     // so a FixedDoubleArray can never succeed.
264     if (variant == kMap || variant == kWeakMap) {
265       CSA_ASSERT(this, IntPtrGreaterThan(length, IntPtrConstant(0)));
266       TNode<Object> element =
267           LoadAndNormalizeFixedDoubleArrayElement(elements, IntPtrConstant(0));
268       ThrowTypeError(context, MessageTemplate::kIteratorValueNotAnObject,
269                      element);
270     } else {
271       DCHECK(variant == kSet || variant == kWeakSet);
272       auto set_entry = [&](Node* index) {
273         TNode<Object> entry = LoadAndNormalizeFixedDoubleArrayElement(
274             elements, UncheckedCast<IntPtrT>(index));
275         AddConstructorEntry(variant, context, collection, add_func, entry);
276       };
277       BuildFastLoop(IntPtrConstant(0), length, set_entry, 1,
278                     ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
279       Goto(&exit);
280     }
281   }
282   BIND(&exit);
283 #if DEBUG
284   CSA_ASSERT(this,
285              WordEqual(original_collection_map, LoadMap(CAST(collection))));
286   CSA_ASSERT(this,
287              WordEqual(original_fast_js_array_map, LoadMap(fast_jsarray)));
288 #endif
289 }
290 
AddConstructorEntriesFromIterable(Variant variant,TNode<Context> context,TNode<Context> native_context,TNode<Object> collection,TNode<Object> iterable)291 void BaseCollectionsAssembler::AddConstructorEntriesFromIterable(
292     Variant variant, TNode<Context> context, TNode<Context> native_context,
293     TNode<Object> collection, TNode<Object> iterable) {
294   Label exit(this), loop(this), if_exception(this, Label::kDeferred);
295   CSA_ASSERT(this, Word32BinaryNot(IsNullOrUndefined(iterable)));
296 
297   TNode<Object> add_func = GetAddFunction(variant, context, collection);
298   IteratorBuiltinsAssembler iterator_assembler(this->state());
299   IteratorRecord iterator = iterator_assembler.GetIterator(context, iterable);
300 
301   CSA_ASSERT(this, Word32BinaryNot(IsUndefined(iterator.object)));
302 
303   TNode<Object> fast_iterator_result_map =
304       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
305   TVARIABLE(Object, var_exception);
306 
307   Goto(&loop);
308   BIND(&loop);
309   {
310     TNode<Object> next = CAST(iterator_assembler.IteratorStep(
311         context, iterator, &exit, fast_iterator_result_map));
312     TNode<Object> next_value = CAST(iterator_assembler.IteratorValue(
313         context, next, fast_iterator_result_map));
314     AddConstructorEntry(variant, context, collection, add_func, next_value,
315                         nullptr, &if_exception, &var_exception);
316     Goto(&loop);
317   }
318   BIND(&if_exception);
319   {
320     iterator_assembler.IteratorCloseOnException(context, iterator,
321                                                 &var_exception);
322   }
323   BIND(&exit);
324 }
325 
AllocateJSCollection(TNode<Context> context,TNode<JSFunction> constructor,TNode<Object> new_target)326 TNode<Object> BaseCollectionsAssembler::AllocateJSCollection(
327     TNode<Context> context, TNode<JSFunction> constructor,
328     TNode<Object> new_target) {
329   TNode<BoolT> is_target_unmodified = WordEqual(constructor, new_target);
330 
331   return Select<Object>(is_target_unmodified,
332                         [=] { return AllocateJSCollectionFast(constructor); },
333                         [=] {
334                           return AllocateJSCollectionSlow(context, constructor,
335                                                           new_target);
336                         });
337 }
338 
AllocateJSCollectionFast(TNode<HeapObject> constructor)339 TNode<Object> BaseCollectionsAssembler::AllocateJSCollectionFast(
340     TNode<HeapObject> constructor) {
341   CSA_ASSERT(this, IsConstructorMap(LoadMap(constructor)));
342   TNode<Object> initial_map =
343       LoadObjectField(constructor, JSFunction::kPrototypeOrInitialMapOffset);
344   return CAST(AllocateJSObjectFromMap(initial_map));
345 }
346 
AllocateJSCollectionSlow(TNode<Context> context,TNode<JSFunction> constructor,TNode<Object> new_target)347 TNode<Object> BaseCollectionsAssembler::AllocateJSCollectionSlow(
348     TNode<Context> context, TNode<JSFunction> constructor,
349     TNode<Object> new_target) {
350   ConstructorBuiltinsAssembler constructor_assembler(this->state());
351   return CAST(constructor_assembler.EmitFastNewObject(context, constructor,
352                                                       new_target));
353 }
354 
GenerateConstructor(Variant variant,Handle<String> constructor_function_name)355 void BaseCollectionsAssembler::GenerateConstructor(
356     Variant variant, Handle<String> constructor_function_name) {
357   const int kIterableArg = 0;
358   CodeStubArguments args(
359       this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
360   TNode<Object> iterable = args.GetOptionalArgumentValue(kIterableArg);
361   TNode<Object> new_target = CAST(Parameter(BuiltinDescriptor::kNewTarget));
362   TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
363 
364   Label if_undefined(this, Label::kDeferred);
365   GotoIf(IsUndefined(new_target), &if_undefined);
366 
367   TNode<Context> native_context = LoadNativeContext(context);
368   TNode<Object> collection = AllocateJSCollection(
369       context, GetConstructor(variant, native_context), new_target);
370 
371   AddConstructorEntries(variant, context, native_context, collection, iterable);
372   Return(collection);
373 
374   BIND(&if_undefined);
375   ThrowTypeError(context, MessageTemplate::kConstructorNotFunction,
376                  HeapConstant(constructor_function_name));
377 }
378 
GetAddFunction(Variant variant,TNode<Context> context,TNode<Object> collection)379 TNode<Object> BaseCollectionsAssembler::GetAddFunction(
380     Variant variant, TNode<Context> context, TNode<Object> collection) {
381   Handle<String> add_func_name = (variant == kMap || variant == kWeakMap)
382                                      ? isolate()->factory()->set_string()
383                                      : isolate()->factory()->add_string();
384   TNode<Object> add_func = GetProperty(context, collection, add_func_name);
385 
386   Label exit(this), if_notcallable(this, Label::kDeferred);
387   GotoIf(TaggedIsSmi(add_func), &if_notcallable);
388   GotoIfNot(IsCallable(CAST(add_func)), &if_notcallable);
389   Goto(&exit);
390 
391   BIND(&if_notcallable);
392   ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, add_func,
393                  HeapConstant(add_func_name), collection);
394 
395   BIND(&exit);
396   return add_func;
397 }
398 
GetConstructor(Variant variant,TNode<Context> native_context)399 TNode<JSFunction> BaseCollectionsAssembler::GetConstructor(
400     Variant variant, TNode<Context> native_context) {
401   int index;
402   switch (variant) {
403     case kMap:
404       index = Context::JS_MAP_FUN_INDEX;
405       break;
406     case kSet:
407       index = Context::JS_SET_FUN_INDEX;
408       break;
409     case kWeakMap:
410       index = Context::JS_WEAK_MAP_FUN_INDEX;
411       break;
412     case kWeakSet:
413       index = Context::JS_WEAK_SET_FUN_INDEX;
414       break;
415   }
416   return CAST(LoadContextElement(native_context, index));
417 }
418 
GetInitialAddFunction(Variant variant,TNode<Context> native_context)419 TNode<JSFunction> BaseCollectionsAssembler::GetInitialAddFunction(
420     Variant variant, TNode<Context> native_context) {
421   int index;
422   switch (variant) {
423     case kMap:
424       index = Context::MAP_SET_INDEX;
425       break;
426     case kSet:
427       index = Context::SET_ADD_INDEX;
428       break;
429     case kWeakMap:
430       index = Context::WEAKMAP_SET_INDEX;
431       break;
432     case kWeakSet:
433       index = Context::WEAKSET_ADD_INDEX;
434       break;
435   }
436   return CAST(LoadContextElement(native_context, index));
437 }
438 
GetTableOffset(Variant variant)439 int BaseCollectionsAssembler::GetTableOffset(Variant variant) {
440   switch (variant) {
441     case kMap:
442       return JSMap::kTableOffset;
443     case kSet:
444       return JSSet::kTableOffset;
445     case kWeakMap:
446       return JSWeakMap::kTableOffset;
447     case kWeakSet:
448       return JSWeakSet::kTableOffset;
449   }
450   UNREACHABLE();
451 }
452 
EstimatedInitialSize(TNode<Object> initial_entries,TNode<BoolT> is_fast_jsarray)453 TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(
454     TNode<Object> initial_entries, TNode<BoolT> is_fast_jsarray) {
455   return Select<IntPtrT>(
456       is_fast_jsarray,
457       [=] { return SmiUntag(LoadFastJSArrayLength(CAST(initial_entries))); },
458       [=] { return IntPtrConstant(0); });
459 }
460 
GotoIfNotJSReceiver(Node * const obj,Label * if_not_receiver)461 void BaseCollectionsAssembler::GotoIfNotJSReceiver(Node* const obj,
462                                                    Label* if_not_receiver) {
463   GotoIf(TaggedIsSmi(obj), if_not_receiver);
464   GotoIfNot(IsJSReceiver(obj), if_not_receiver);
465 }
466 
HasInitialCollectionPrototype(Variant variant,TNode<Context> native_context,TNode<Object> collection)467 TNode<BoolT> BaseCollectionsAssembler::HasInitialCollectionPrototype(
468     Variant variant, TNode<Context> native_context, TNode<Object> collection) {
469   int initial_prototype_index;
470   switch (variant) {
471     case kMap:
472       initial_prototype_index = Context::INITIAL_MAP_PROTOTYPE_MAP_INDEX;
473       break;
474     case kSet:
475       initial_prototype_index = Context::INITIAL_SET_PROTOTYPE_MAP_INDEX;
476       break;
477     case kWeakMap:
478       initial_prototype_index = Context::INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX;
479       break;
480     case kWeakSet:
481       initial_prototype_index = Context::INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX;
482       break;
483   }
484   TNode<Map> initial_prototype_map =
485       CAST(LoadContextElement(native_context, initial_prototype_index));
486   TNode<Map> collection_proto_map =
487       LoadMap(LoadMapPrototype(LoadMap(CAST(collection))));
488 
489   return WordEqual(collection_proto_map, initial_prototype_map);
490 }
491 
LoadAndNormalizeFixedArrayElement(TNode<HeapObject> elements,TNode<IntPtrT> index)492 TNode<Object> BaseCollectionsAssembler::LoadAndNormalizeFixedArrayElement(
493     TNode<HeapObject> elements, TNode<IntPtrT> index) {
494   TNode<Object> element = LoadFixedArrayElement(elements, index);
495   return Select<Object>(IsTheHole(element), [=] { return UndefinedConstant(); },
496                         [=] { return element; });
497 }
498 
LoadAndNormalizeFixedDoubleArrayElement(TNode<HeapObject> elements,TNode<IntPtrT> index)499 TNode<Object> BaseCollectionsAssembler::LoadAndNormalizeFixedDoubleArrayElement(
500     TNode<HeapObject> elements, TNode<IntPtrT> index) {
501   TVARIABLE(Object, entry);
502   Label if_hole(this, Label::kDeferred), next(this);
503   TNode<Float64T> element = UncheckedCast<Float64T>(LoadFixedDoubleArrayElement(
504       elements, index, MachineType::Float64(), 0, INTPTR_PARAMETERS, &if_hole));
505   {  // not hole
506     entry = AllocateHeapNumberWithValue(element);
507     Goto(&next);
508   }
509   BIND(&if_hole);
510   {
511     entry = UndefinedConstant();
512     Goto(&next);
513   }
514   BIND(&next);
515   return entry.value();
516 }
517 
LoadKeyValue(TNode<Context> context,TNode<Object> maybe_array,TVariable<Object> * key,TVariable<Object> * value,Label * if_may_have_side_effects,Label * if_exception,TVariable<Object> * var_exception)518 void BaseCollectionsAssembler::LoadKeyValue(
519     TNode<Context> context, TNode<Object> maybe_array, TVariable<Object>* key,
520     TVariable<Object>* value, Label* if_may_have_side_effects,
521     Label* if_exception, TVariable<Object>* var_exception) {
522   CSA_ASSERT(this, Word32BinaryNot(IsTheHole(maybe_array)));
523 
524   Label exit(this), if_fast(this), if_slow(this, Label::kDeferred);
525   BranchIfFastJSArray(maybe_array, context, &if_fast, &if_slow);
526   BIND(&if_fast);
527   {
528     TNode<JSArray> array = CAST(maybe_array);
529     TNode<Smi> length = LoadFastJSArrayLength(array);
530     TNode<FixedArrayBase> elements = LoadElements(array);
531     TNode<Int32T> elements_kind = LoadMapElementsKind(LoadMap(array));
532 
533     Label if_smiorobjects(this), if_doubles(this);
534     Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects,
535            &if_doubles);
536     BIND(&if_smiorobjects);
537     {
538       Label if_one(this), if_two(this);
539       GotoIf(SmiGreaterThan(length, SmiConstant(1)), &if_two);
540       GotoIf(SmiEqual(length, SmiConstant(1)), &if_one);
541       {  // empty array
542         *key = UndefinedConstant();
543         *value = UndefinedConstant();
544         Goto(&exit);
545       }
546       BIND(&if_one);
547       {
548         *key = LoadAndNormalizeFixedArrayElement(elements, IntPtrConstant(0));
549         *value = UndefinedConstant();
550         Goto(&exit);
551       }
552       BIND(&if_two);
553       {
554         *key = LoadAndNormalizeFixedArrayElement(elements, IntPtrConstant(0));
555         *value = LoadAndNormalizeFixedArrayElement(elements, IntPtrConstant(1));
556         Goto(&exit);
557       }
558     }
559     BIND(&if_doubles);
560     {
561       Label if_one(this), if_two(this);
562       GotoIf(SmiGreaterThan(length, SmiConstant(1)), &if_two);
563       GotoIf(SmiEqual(length, SmiConstant(1)), &if_one);
564       {  // empty array
565         *key = UndefinedConstant();
566         *value = UndefinedConstant();
567         Goto(&exit);
568       }
569       BIND(&if_one);
570       {
571         *key = LoadAndNormalizeFixedDoubleArrayElement(elements,
572                                                        IntPtrConstant(0));
573         *value = UndefinedConstant();
574         Goto(&exit);
575       }
576       BIND(&if_two);
577       {
578         *key = LoadAndNormalizeFixedDoubleArrayElement(elements,
579                                                        IntPtrConstant(0));
580         *value = LoadAndNormalizeFixedDoubleArrayElement(elements,
581                                                          IntPtrConstant(1));
582         Goto(&exit);
583       }
584     }
585   }
586   BIND(&if_slow);
587   {
588     Label if_notobject(this, Label::kDeferred);
589     GotoIfNotJSReceiver(maybe_array, &if_notobject);
590     if (if_may_have_side_effects != nullptr) {
591       // If the element is not a fast array, we cannot guarantee accessing the
592       // key and value won't execute user code that will break fast path
593       // assumptions.
594       Goto(if_may_have_side_effects);
595     } else {
596       *key = UncheckedCast<Object>(GetProperty(
597           context, maybe_array, isolate()->factory()->zero_string()));
598       GotoIfException(key->value(), if_exception, var_exception);
599 
600       *value = UncheckedCast<Object>(GetProperty(
601           context, maybe_array, isolate()->factory()->one_string()));
602       GotoIfException(value->value(), if_exception, var_exception);
603       Goto(&exit);
604     }
605     BIND(&if_notobject);
606     {
607       Node* ret = CallRuntime(
608           Runtime::kThrowTypeError, context,
609           SmiConstant(MessageTemplate::kIteratorValueNotAnObject), maybe_array);
610       GotoIfException(ret, if_exception, var_exception);
611       Unreachable();
612     }
613   }
614   BIND(&exit);
615 }
616 
617 class CollectionsBuiltinsAssembler : public BaseCollectionsAssembler {
618  public:
CollectionsBuiltinsAssembler(compiler::CodeAssemblerState * state)619   explicit CollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state)
620       : BaseCollectionsAssembler(state) {}
621 
622  protected:
623   template <typename IteratorType>
624   Node* AllocateJSCollectionIterator(Node* context, int map_index,
625                                      Node* collection);
626   TNode<Object> AllocateTable(Variant variant, TNode<Context> context,
627                               TNode<IntPtrT> at_least_space_for);
628   Node* GetHash(Node* const key);
629   Node* CallGetHashRaw(Node* const key);
630   Node* CallGetOrCreateHashRaw(Node* const key);
631 
632   // Transitions the iterator to the non obsolete backing store.
633   // This is a NOP if the [table] is not obsolete.
634   typedef std::function<void(Node* const table, Node* const index)>
635       UpdateInTransition;
636   template <typename TableType>
637   std::tuple<Node*, Node*> Transition(
638       Node* const table, Node* const index,
639       UpdateInTransition const& update_in_transition);
640   template <typename IteratorType, typename TableType>
641   std::tuple<Node*, Node*> TransitionAndUpdate(Node* const iterator);
642   template <typename TableType>
643   std::tuple<Node*, Node*, Node*> NextSkipHoles(Node* table, Node* index,
644                                                 Label* if_end);
645 
646   // Specialization for Smi.
647   // The {result} variable will contain the entry index if the key was found,
648   // or the hash code otherwise.
649   template <typename CollectionType>
650   void FindOrderedHashTableEntryForSmiKey(Node* table, Node* key_tagged,
651                                           Variable* result, Label* entry_found,
652                                           Label* not_found);
653   void SameValueZeroSmi(Node* key_smi, Node* candidate_key, Label* if_same,
654                         Label* if_not_same);
655 
656   // Specialization for heap numbers.
657   // The {result} variable will contain the entry index if the key was found,
658   // or the hash code otherwise.
659   void SameValueZeroHeapNumber(Node* key_string, Node* candidate_key,
660                                Label* if_same, Label* if_not_same);
661   template <typename CollectionType>
662   void FindOrderedHashTableEntryForHeapNumberKey(Node* context, Node* table,
663                                                  Node* key_heap_number,
664                                                  Variable* result,
665                                                  Label* entry_found,
666                                                  Label* not_found);
667 
668   // Specialization for bigints.
669   // The {result} variable will contain the entry index if the key was found,
670   // or the hash code otherwise.
671   void SameValueZeroBigInt(Node* key, Node* candidate_key, Label* if_same,
672                            Label* if_not_same);
673   template <typename CollectionType>
674   void FindOrderedHashTableEntryForBigIntKey(Node* context, Node* table,
675                                              Node* key, Variable* result,
676                                              Label* entry_found,
677                                              Label* not_found);
678 
679   // Specialization for string.
680   // The {result} variable will contain the entry index if the key was found,
681   // or the hash code otherwise.
682   template <typename CollectionType>
683   void FindOrderedHashTableEntryForStringKey(Node* context, Node* table,
684                                              Node* key_tagged, Variable* result,
685                                              Label* entry_found,
686                                              Label* not_found);
687   Node* ComputeStringHash(Node* context, Node* string_key);
688   void SameValueZeroString(Node* context, Node* key_string, Node* candidate_key,
689                            Label* if_same, Label* if_not_same);
690 
691   // Specialization for non-strings, non-numbers. For those we only need
692   // reference equality to compare the keys.
693   // The {result} variable will contain the entry index if the key was found,
694   // or the hash code otherwise. If the hash-code has not been computed, it
695   // should be Smi -1.
696   template <typename CollectionType>
697   void FindOrderedHashTableEntryForOtherKey(Node* context, Node* table,
698                                             Node* key, Variable* result,
699                                             Label* entry_found,
700                                             Label* not_found);
701 
702   template <typename CollectionType>
703   void TryLookupOrderedHashTableIndex(Node* const table, Node* const key,
704                                       Node* const context, Variable* result,
705                                       Label* if_entry_found,
706                                       Label* if_not_found);
707 
708   Node* NormalizeNumberKey(Node* key);
709   void StoreOrderedHashMapNewEntry(Node* const table, Node* const key,
710                                    Node* const value, Node* const hash,
711                                    Node* const number_of_buckets,
712                                    Node* const occupancy);
713   void StoreOrderedHashSetNewEntry(Node* const table, Node* const key,
714                                    Node* const hash,
715                                    Node* const number_of_buckets,
716                                    Node* const occupancy);
717 };
718 
719 template <typename IteratorType>
AllocateJSCollectionIterator(Node * context,int map_index,Node * collection)720 Node* CollectionsBuiltinsAssembler::AllocateJSCollectionIterator(
721     Node* context, int map_index, Node* collection) {
722   Node* const table = LoadObjectField(collection, JSCollection::kTableOffset);
723   Node* const native_context = LoadNativeContext(context);
724   Node* const iterator_map = LoadContextElement(native_context, map_index);
725   Node* const iterator = AllocateInNewSpace(IteratorType::kSize);
726   StoreMapNoWriteBarrier(iterator, iterator_map);
727   StoreObjectFieldRoot(iterator, IteratorType::kPropertiesOrHashOffset,
728                        Heap::kEmptyFixedArrayRootIndex);
729   StoreObjectFieldRoot(iterator, IteratorType::kElementsOffset,
730                        Heap::kEmptyFixedArrayRootIndex);
731   StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kTableOffset, table);
732   StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset,
733                                  SmiConstant(0));
734   return iterator;
735 }
736 
AllocateTable(Variant variant,TNode<Context> context,TNode<IntPtrT> at_least_space_for)737 TNode<Object> CollectionsBuiltinsAssembler::AllocateTable(
738     Variant variant, TNode<Context> context,
739     TNode<IntPtrT> at_least_space_for) {
740   return CAST((variant == kMap || variant == kWeakMap)
741                   ? AllocateOrderedHashTable<OrderedHashMap>()
742                   : AllocateOrderedHashTable<OrderedHashSet>());
743 }
744 
TF_BUILTIN(MapConstructor,CollectionsBuiltinsAssembler)745 TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
746   GenerateConstructor(kMap, isolate()->factory()->Map_string());
747 }
748 
TF_BUILTIN(SetConstructor,CollectionsBuiltinsAssembler)749 TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
750   GenerateConstructor(kSet, isolate()->factory()->Set_string());
751 }
752 
CallGetOrCreateHashRaw(Node * const key)753 Node* CollectionsBuiltinsAssembler::CallGetOrCreateHashRaw(Node* const key) {
754   Node* const function_addr =
755       ExternalConstant(ExternalReference::get_or_create_hash_raw(isolate()));
756   Node* const isolate_ptr =
757       ExternalConstant(ExternalReference::isolate_address(isolate()));
758 
759   MachineType type_ptr = MachineType::Pointer();
760   MachineType type_tagged = MachineType::AnyTagged();
761 
762   Node* const result = CallCFunction2(type_tagged, type_ptr, type_tagged,
763                                       function_addr, isolate_ptr, key);
764 
765   return result;
766 }
767 
CallGetHashRaw(Node * const key)768 Node* CollectionsBuiltinsAssembler::CallGetHashRaw(Node* const key) {
769   Node* const function_addr =
770       ExternalConstant(ExternalReference::orderedhashmap_gethash_raw());
771   Node* const isolate_ptr =
772       ExternalConstant(ExternalReference::isolate_address(isolate()));
773 
774   MachineType type_ptr = MachineType::Pointer();
775   MachineType type_tagged = MachineType::AnyTagged();
776 
777   Node* const result = CallCFunction2(type_tagged, type_ptr, type_tagged,
778                                       function_addr, isolate_ptr, key);
779   return SmiUntag(result);
780 }
781 
GetHash(Node * const key)782 Node* CollectionsBuiltinsAssembler::GetHash(Node* const key) {
783   VARIABLE(var_hash, MachineType::PointerRepresentation());
784   Label if_receiver(this), if_other(this), done(this);
785   Branch(IsJSReceiver(key), &if_receiver, &if_other);
786 
787   BIND(&if_receiver);
788   {
789     var_hash.Bind(LoadJSReceiverIdentityHash(key));
790     Goto(&done);
791   }
792 
793   BIND(&if_other);
794   {
795     var_hash.Bind(CallGetHashRaw(key));
796     Goto(&done);
797   }
798 
799   BIND(&done);
800   return var_hash.value();
801 }
802 
SameValueZeroSmi(Node * key_smi,Node * candidate_key,Label * if_same,Label * if_not_same)803 void CollectionsBuiltinsAssembler::SameValueZeroSmi(Node* key_smi,
804                                                     Node* candidate_key,
805                                                     Label* if_same,
806                                                     Label* if_not_same) {
807   // If the key is the same, we are done.
808   GotoIf(WordEqual(candidate_key, key_smi), if_same);
809 
810   // If the candidate key is smi, then it must be different (because
811   // we already checked for equality above).
812   GotoIf(TaggedIsSmi(candidate_key), if_not_same);
813 
814   // If the candidate key is not smi, we still have to check if it is a
815   // heap number with the same value.
816   GotoIfNot(IsHeapNumber(candidate_key), if_not_same);
817 
818   Node* const candidate_key_number = LoadHeapNumberValue(candidate_key);
819   Node* const key_number = SmiToFloat64(key_smi);
820 
821   GotoIf(Float64Equal(candidate_key_number, key_number), if_same);
822 
823   Goto(if_not_same);
824 }
825 
826 template <typename CollectionType>
FindOrderedHashTableEntryForSmiKey(Node * table,Node * smi_key,Variable * result,Label * entry_found,Label * not_found)827 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForSmiKey(
828     Node* table, Node* smi_key, Variable* result, Label* entry_found,
829     Label* not_found) {
830   Node* const key_untagged = SmiUntag(smi_key);
831   Node* const hash = ChangeInt32ToIntPtr(ComputeUnseededHash(key_untagged));
832   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
833   result->Bind(hash);
834   FindOrderedHashTableEntry<CollectionType>(
835       table, hash,
836       [&](Node* other_key, Label* if_same, Label* if_not_same) {
837         SameValueZeroSmi(smi_key, other_key, if_same, if_not_same);
838       },
839       result, entry_found, not_found);
840 }
841 
842 template <typename CollectionType>
FindOrderedHashTableEntryForStringKey(Node * context,Node * table,Node * key_tagged,Variable * result,Label * entry_found,Label * not_found)843 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForStringKey(
844     Node* context, Node* table, Node* key_tagged, Variable* result,
845     Label* entry_found, Label* not_found) {
846   Node* const hash = ComputeStringHash(context, key_tagged);
847   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
848   result->Bind(hash);
849   FindOrderedHashTableEntry<CollectionType>(
850       table, hash,
851       [&](Node* other_key, Label* if_same, Label* if_not_same) {
852         SameValueZeroString(context, key_tagged, other_key, if_same,
853                             if_not_same);
854       },
855       result, entry_found, not_found);
856 }
857 
858 template <typename CollectionType>
FindOrderedHashTableEntryForHeapNumberKey(Node * context,Node * table,Node * key_heap_number,Variable * result,Label * entry_found,Label * not_found)859 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForHeapNumberKey(
860     Node* context, Node* table, Node* key_heap_number, Variable* result,
861     Label* entry_found, Label* not_found) {
862   Node* hash = CallGetHashRaw(key_heap_number);
863   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
864   result->Bind(hash);
865   Node* const key_float = LoadHeapNumberValue(key_heap_number);
866   FindOrderedHashTableEntry<CollectionType>(
867       table, hash,
868       [&](Node* other_key, Label* if_same, Label* if_not_same) {
869         SameValueZeroHeapNumber(key_float, other_key, if_same, if_not_same);
870       },
871       result, entry_found, not_found);
872 }
873 
874 template <typename CollectionType>
FindOrderedHashTableEntryForBigIntKey(Node * context,Node * table,Node * key,Variable * result,Label * entry_found,Label * not_found)875 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForBigIntKey(
876     Node* context, Node* table, Node* key, Variable* result, Label* entry_found,
877     Label* not_found) {
878   Node* hash = CallGetHashRaw(key);
879   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
880   result->Bind(hash);
881   FindOrderedHashTableEntry<CollectionType>(
882       table, hash,
883       [&](Node* other_key, Label* if_same, Label* if_not_same) {
884         SameValueZeroBigInt(key, other_key, if_same, if_not_same);
885       },
886       result, entry_found, not_found);
887 }
888 
889 template <typename CollectionType>
FindOrderedHashTableEntryForOtherKey(Node * context,Node * table,Node * key,Variable * result,Label * entry_found,Label * not_found)890 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForOtherKey(
891     Node* context, Node* table, Node* key, Variable* result, Label* entry_found,
892     Label* not_found) {
893   Node* hash = GetHash(key);
894   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
895   result->Bind(hash);
896   FindOrderedHashTableEntry<CollectionType>(
897       table, hash,
898       [&](Node* other_key, Label* if_same, Label* if_not_same) {
899         Branch(WordEqual(key, other_key), if_same, if_not_same);
900       },
901       result, entry_found, not_found);
902 }
903 
ComputeStringHash(Node * context,Node * string_key)904 Node* CollectionsBuiltinsAssembler::ComputeStringHash(Node* context,
905                                                       Node* string_key) {
906   VARIABLE(var_result, MachineType::PointerRepresentation());
907 
908   Label hash_not_computed(this), done(this, &var_result);
909   Node* hash =
910       ChangeInt32ToIntPtr(LoadNameHash(string_key, &hash_not_computed));
911   var_result.Bind(hash);
912   Goto(&done);
913 
914   BIND(&hash_not_computed);
915   var_result.Bind(CallGetHashRaw(string_key));
916   Goto(&done);
917 
918   BIND(&done);
919   return var_result.value();
920 }
921 
SameValueZeroString(Node * context,Node * key_string,Node * candidate_key,Label * if_same,Label * if_not_same)922 void CollectionsBuiltinsAssembler::SameValueZeroString(Node* context,
923                                                        Node* key_string,
924                                                        Node* candidate_key,
925                                                        Label* if_same,
926                                                        Label* if_not_same) {
927   // If the candidate is not a string, the keys are not equal.
928   GotoIf(TaggedIsSmi(candidate_key), if_not_same);
929   GotoIfNot(IsString(candidate_key), if_not_same);
930 
931   Branch(WordEqual(CallBuiltin(Builtins::kStringEqual, context, key_string,
932                                candidate_key),
933                    TrueConstant()),
934          if_same, if_not_same);
935 }
936 
SameValueZeroBigInt(Node * key,Node * candidate_key,Label * if_same,Label * if_not_same)937 void CollectionsBuiltinsAssembler::SameValueZeroBigInt(Node* key,
938                                                        Node* candidate_key,
939                                                        Label* if_same,
940                                                        Label* if_not_same) {
941   CSA_ASSERT(this, IsBigInt(key));
942   GotoIf(TaggedIsSmi(candidate_key), if_not_same);
943   GotoIfNot(IsBigInt(candidate_key), if_not_same);
944 
945   Branch(WordEqual(CallRuntime(Runtime::kBigIntEqualToBigInt,
946                                NoContextConstant(), key, candidate_key),
947                    TrueConstant()),
948          if_same, if_not_same);
949 }
950 
SameValueZeroHeapNumber(Node * key_float,Node * candidate_key,Label * if_same,Label * if_not_same)951 void CollectionsBuiltinsAssembler::SameValueZeroHeapNumber(Node* key_float,
952                                                            Node* candidate_key,
953                                                            Label* if_same,
954                                                            Label* if_not_same) {
955   Label if_smi(this), if_keyisnan(this);
956 
957   GotoIf(TaggedIsSmi(candidate_key), &if_smi);
958   GotoIfNot(IsHeapNumber(candidate_key), if_not_same);
959 
960   {
961     // {candidate_key} is a heap number.
962     Node* const candidate_float = LoadHeapNumberValue(candidate_key);
963     GotoIf(Float64Equal(key_float, candidate_float), if_same);
964 
965     // SameValueZero needs to treat NaNs as equal. First check if {key_float}
966     // is NaN.
967     BranchIfFloat64IsNaN(key_float, &if_keyisnan, if_not_same);
968 
969     BIND(&if_keyisnan);
970     {
971       // Return true iff {candidate_key} is NaN.
972       Branch(Float64Equal(candidate_float, candidate_float), if_not_same,
973              if_same);
974     }
975   }
976 
977   BIND(&if_smi);
978   {
979     Node* const candidate_float = SmiToFloat64(candidate_key);
980     Branch(Float64Equal(key_float, candidate_float), if_same, if_not_same);
981   }
982 }
983 
TF_BUILTIN(OrderedHashTableHealIndex,CollectionsBuiltinsAssembler)984 TF_BUILTIN(OrderedHashTableHealIndex, CollectionsBuiltinsAssembler) {
985   TNode<HeapObject> table = CAST(Parameter(Descriptor::kTable));
986   TNode<Smi> index = CAST(Parameter(Descriptor::kIndex));
987   Label return_index(this), return_zero(this);
988 
989   // Check if we need to update the {index}.
990   GotoIfNot(SmiLessThan(SmiConstant(0), index), &return_zero);
991 
992   // Check if the {table} was cleared.
993   Node* number_of_deleted_elements = LoadAndUntagObjectField(
994       table, OrderedHashTableBase::kNumberOfDeletedElementsOffset);
995   GotoIf(WordEqual(number_of_deleted_elements,
996                    IntPtrConstant(OrderedHashTableBase::kClearedTableSentinel)),
997          &return_zero);
998 
999   VARIABLE(var_i, MachineType::PointerRepresentation(), IntPtrConstant(0));
1000   VARIABLE(var_index, MachineRepresentation::kTagged, index);
1001   Label loop(this, {&var_i, &var_index});
1002   Goto(&loop);
1003   BIND(&loop);
1004   {
1005     Node* i = var_i.value();
1006     GotoIfNot(IntPtrLessThan(i, number_of_deleted_elements), &return_index);
1007     TNode<Smi> removed_index = CAST(LoadFixedArrayElement(
1008         table, i, OrderedHashTableBase::kRemovedHolesIndex * kPointerSize));
1009     GotoIf(SmiGreaterThanOrEqual(removed_index, index), &return_index);
1010     Decrement(&var_index, 1, SMI_PARAMETERS);
1011     Increment(&var_i);
1012     Goto(&loop);
1013   }
1014 
1015   BIND(&return_index);
1016   Return(var_index.value());
1017 
1018   BIND(&return_zero);
1019   Return(SmiConstant(0));
1020 }
1021 
1022 template <typename TableType>
Transition(Node * const table,Node * const index,UpdateInTransition const & update_in_transition)1023 std::tuple<Node*, Node*> CollectionsBuiltinsAssembler::Transition(
1024     Node* const table, Node* const index,
1025     UpdateInTransition const& update_in_transition) {
1026   VARIABLE(var_index, MachineType::PointerRepresentation(), index);
1027   VARIABLE(var_table, MachineRepresentation::kTagged, table);
1028   Label if_done(this), if_transition(this, Label::kDeferred);
1029   Branch(TaggedIsSmi(
1030              LoadObjectField(var_table.value(), TableType::kNextTableOffset)),
1031          &if_done, &if_transition);
1032 
1033   BIND(&if_transition);
1034   {
1035     Label loop(this, {&var_table, &var_index}), done_loop(this);
1036     Goto(&loop);
1037     BIND(&loop);
1038     {
1039       Node* table = var_table.value();
1040       Node* index = var_index.value();
1041 
1042       Node* next_table = LoadObjectField(table, TableType::kNextTableOffset);
1043       GotoIf(TaggedIsSmi(next_table), &done_loop);
1044 
1045       var_table.Bind(next_table);
1046       var_index.Bind(SmiUntag(
1047           CAST(CallBuiltin(Builtins::kOrderedHashTableHealIndex,
1048                            NoContextConstant(), table, SmiTag(index)))));
1049       Goto(&loop);
1050     }
1051     BIND(&done_loop);
1052 
1053     // Update with the new {table} and {index}.
1054     update_in_transition(var_table.value(), var_index.value());
1055     Goto(&if_done);
1056   }
1057 
1058   BIND(&if_done);
1059   return std::tuple<Node*, Node*>(var_table.value(), var_index.value());
1060 }
1061 
1062 template <typename IteratorType, typename TableType>
TransitionAndUpdate(Node * const iterator)1063 std::tuple<Node*, Node*> CollectionsBuiltinsAssembler::TransitionAndUpdate(
1064     Node* const iterator) {
1065   return Transition<TableType>(
1066       LoadObjectField(iterator, IteratorType::kTableOffset),
1067       LoadAndUntagObjectField(iterator, IteratorType::kIndexOffset),
1068       [this, iterator](Node* const table, Node* const index) {
1069         // Update the {iterator} with the new state.
1070         StoreObjectField(iterator, IteratorType::kTableOffset, table);
1071         StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset,
1072                                        SmiTag(index));
1073       });
1074 }
1075 
1076 template <typename TableType>
NextSkipHoles(Node * table,Node * index,Label * if_end)1077 std::tuple<Node*, Node*, Node*> CollectionsBuiltinsAssembler::NextSkipHoles(
1078     Node* table, Node* index, Label* if_end) {
1079   // Compute the used capacity for the {table}.
1080   Node* number_of_buckets =
1081       LoadAndUntagObjectField(table, TableType::kNumberOfBucketsOffset);
1082   Node* number_of_elements =
1083       LoadAndUntagObjectField(table, TableType::kNumberOfElementsOffset);
1084   Node* number_of_deleted_elements =
1085       LoadAndUntagObjectField(table, TableType::kNumberOfDeletedElementsOffset);
1086   Node* used_capacity =
1087       IntPtrAdd(number_of_elements, number_of_deleted_elements);
1088 
1089   Node* entry_key;
1090   Node* entry_start_position;
1091   VARIABLE(var_index, MachineType::PointerRepresentation(), index);
1092   Label loop(this, &var_index), done_loop(this);
1093   Goto(&loop);
1094   BIND(&loop);
1095   {
1096     GotoIfNot(IntPtrLessThan(var_index.value(), used_capacity), if_end);
1097     entry_start_position = IntPtrAdd(
1098         IntPtrMul(var_index.value(), IntPtrConstant(TableType::kEntrySize)),
1099         number_of_buckets);
1100     entry_key =
1101         LoadFixedArrayElement(table, entry_start_position,
1102                               TableType::kHashTableStartIndex * kPointerSize);
1103     Increment(&var_index);
1104     Branch(IsTheHole(entry_key), &loop, &done_loop);
1105   }
1106 
1107   BIND(&done_loop);
1108   return std::tuple<Node*, Node*, Node*>(entry_key, entry_start_position,
1109                                          var_index.value());
1110 }
1111 
TF_BUILTIN(MapPrototypeGet,CollectionsBuiltinsAssembler)1112 TF_BUILTIN(MapPrototypeGet, CollectionsBuiltinsAssembler) {
1113   Node* const receiver = Parameter(Descriptor::kReceiver);
1114   Node* const key = Parameter(Descriptor::kKey);
1115   Node* const context = Parameter(Descriptor::kContext);
1116 
1117   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.get");
1118 
1119   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1120   TNode<Smi> index = CAST(
1121       CallBuiltin(Builtins::kFindOrderedHashMapEntry, context, table, key));
1122 
1123   Label if_found(this), if_not_found(this);
1124   Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found,
1125          &if_not_found);
1126 
1127   BIND(&if_found);
1128   Return(LoadFixedArrayElement(
1129       table, SmiUntag(index),
1130       (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset) *
1131           kPointerSize));
1132 
1133   BIND(&if_not_found);
1134   Return(UndefinedConstant());
1135 }
1136 
TF_BUILTIN(MapPrototypeHas,CollectionsBuiltinsAssembler)1137 TF_BUILTIN(MapPrototypeHas, CollectionsBuiltinsAssembler) {
1138   Node* const receiver = Parameter(Descriptor::kReceiver);
1139   Node* const key = Parameter(Descriptor::kKey);
1140   Node* const context = Parameter(Descriptor::kContext);
1141 
1142   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.has");
1143 
1144   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1145   TNode<Smi> index = CAST(
1146       CallBuiltin(Builtins::kFindOrderedHashMapEntry, context, table, key));
1147 
1148   Label if_found(this), if_not_found(this);
1149   Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found,
1150          &if_not_found);
1151 
1152   BIND(&if_found);
1153   Return(TrueConstant());
1154 
1155   BIND(&if_not_found);
1156   Return(FalseConstant());
1157 }
1158 
NormalizeNumberKey(Node * const key)1159 Node* CollectionsBuiltinsAssembler::NormalizeNumberKey(Node* const key) {
1160   VARIABLE(result, MachineRepresentation::kTagged, key);
1161   Label done(this);
1162 
1163   GotoIf(TaggedIsSmi(key), &done);
1164   GotoIfNot(IsHeapNumber(key), &done);
1165   Node* const number = LoadHeapNumberValue(key);
1166   GotoIfNot(Float64Equal(number, Float64Constant(0.0)), &done);
1167   // We know the value is zero, so we take the key to be Smi 0.
1168   // Another option would be to normalize to Smi here.
1169   result.Bind(SmiConstant(0));
1170   Goto(&done);
1171 
1172   BIND(&done);
1173   return result.value();
1174 }
1175 
TF_BUILTIN(MapPrototypeSet,CollectionsBuiltinsAssembler)1176 TF_BUILTIN(MapPrototypeSet, CollectionsBuiltinsAssembler) {
1177   Node* const receiver = Parameter(Descriptor::kReceiver);
1178   Node* key = Parameter(Descriptor::kKey);
1179   Node* const value = Parameter(Descriptor::kValue);
1180   Node* const context = Parameter(Descriptor::kContext);
1181 
1182   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.set");
1183 
1184   key = NormalizeNumberKey(key);
1185 
1186   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1187 
1188   VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(),
1189            IntPtrConstant(0));
1190   Label entry_found(this), not_found(this);
1191 
1192   TryLookupOrderedHashTableIndex<OrderedHashMap>(table, key, context,
1193                                                  &entry_start_position_or_hash,
1194                                                  &entry_found, &not_found);
1195 
1196   BIND(&entry_found);
1197   // If we found the entry, we just store the value there.
1198   StoreFixedArrayElement(table, entry_start_position_or_hash.value(), value,
1199                          UPDATE_WRITE_BARRIER,
1200                          kPointerSize * (OrderedHashMap::kHashTableStartIndex +
1201                                          OrderedHashMap::kValueOffset));
1202   Return(receiver);
1203 
1204   Label no_hash(this), add_entry(this), store_new_entry(this);
1205   BIND(&not_found);
1206   {
1207     // If we have a hash code, we can start adding the new entry.
1208     GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(),
1209                              IntPtrConstant(0)),
1210            &add_entry);
1211 
1212     // Otherwise, go to runtime to compute the hash code.
1213     entry_start_position_or_hash.Bind(SmiUntag(CallGetOrCreateHashRaw(key)));
1214     Goto(&add_entry);
1215   }
1216 
1217   BIND(&add_entry);
1218   VARIABLE(number_of_buckets, MachineType::PointerRepresentation());
1219   VARIABLE(occupancy, MachineType::PointerRepresentation());
1220   VARIABLE(table_var, MachineRepresentation::kTaggedPointer, table);
1221   {
1222     // Check we have enough space for the entry.
1223     number_of_buckets.Bind(SmiUntag(CAST(
1224         LoadFixedArrayElement(table, OrderedHashMap::kNumberOfBucketsIndex))));
1225 
1226     STATIC_ASSERT(OrderedHashMap::kLoadFactor == 2);
1227     Node* const capacity = WordShl(number_of_buckets.value(), 1);
1228     Node* const number_of_elements = SmiUntag(
1229         CAST(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset)));
1230     Node* const number_of_deleted = SmiUntag(CAST(LoadObjectField(
1231         table, OrderedHashMap::kNumberOfDeletedElementsOffset)));
1232     occupancy.Bind(IntPtrAdd(number_of_elements, number_of_deleted));
1233     GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry);
1234 
1235     // We do not have enough space, grow the table and reload the relevant
1236     // fields.
1237     CallRuntime(Runtime::kMapGrow, context, receiver);
1238     table_var.Bind(LoadObjectField(receiver, JSMap::kTableOffset));
1239     number_of_buckets.Bind(SmiUntag(CAST(LoadFixedArrayElement(
1240         table_var.value(), OrderedHashMap::kNumberOfBucketsIndex))));
1241     Node* const new_number_of_elements = SmiUntag(CAST(LoadObjectField(
1242         table_var.value(), OrderedHashMap::kNumberOfElementsOffset)));
1243     Node* const new_number_of_deleted = SmiUntag(CAST(LoadObjectField(
1244         table_var.value(), OrderedHashMap::kNumberOfDeletedElementsOffset)));
1245     occupancy.Bind(IntPtrAdd(new_number_of_elements, new_number_of_deleted));
1246     Goto(&store_new_entry);
1247   }
1248   BIND(&store_new_entry);
1249   // Store the key, value and connect the element to the bucket chain.
1250   StoreOrderedHashMapNewEntry(table_var.value(), key, value,
1251                               entry_start_position_or_hash.value(),
1252                               number_of_buckets.value(), occupancy.value());
1253   Return(receiver);
1254 }
1255 
StoreOrderedHashMapNewEntry(Node * const table,Node * const key,Node * const value,Node * const hash,Node * const number_of_buckets,Node * const occupancy)1256 void CollectionsBuiltinsAssembler::StoreOrderedHashMapNewEntry(
1257     Node* const table, Node* const key, Node* const value, Node* const hash,
1258     Node* const number_of_buckets, Node* const occupancy) {
1259   Node* const bucket =
1260       WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1)));
1261   Node* const bucket_entry = LoadFixedArrayElement(
1262       table, bucket, OrderedHashMap::kHashTableStartIndex * kPointerSize);
1263 
1264   // Store the entry elements.
1265   Node* const entry_start = IntPtrAdd(
1266       IntPtrMul(occupancy, IntPtrConstant(OrderedHashMap::kEntrySize)),
1267       number_of_buckets);
1268   StoreFixedArrayElement(table, entry_start, key, UPDATE_WRITE_BARRIER,
1269                          kPointerSize * OrderedHashMap::kHashTableStartIndex);
1270   StoreFixedArrayElement(table, entry_start, value, UPDATE_WRITE_BARRIER,
1271                          kPointerSize * (OrderedHashMap::kHashTableStartIndex +
1272                                          OrderedHashMap::kValueOffset));
1273   StoreFixedArrayElement(table, entry_start, bucket_entry, SKIP_WRITE_BARRIER,
1274                          kPointerSize * (OrderedHashMap::kHashTableStartIndex +
1275                                          OrderedHashMap::kChainOffset));
1276 
1277   // Update the bucket head.
1278   StoreFixedArrayElement(table, bucket, SmiTag(occupancy), SKIP_WRITE_BARRIER,
1279                          OrderedHashMap::kHashTableStartIndex * kPointerSize);
1280 
1281   // Bump the elements count.
1282   TNode<Smi> const number_of_elements =
1283       CAST(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset));
1284   StoreObjectFieldNoWriteBarrier(table, OrderedHashMap::kNumberOfElementsOffset,
1285                                  SmiAdd(number_of_elements, SmiConstant(1)));
1286 }
1287 
TF_BUILTIN(MapPrototypeDelete,CollectionsBuiltinsAssembler)1288 TF_BUILTIN(MapPrototypeDelete, CollectionsBuiltinsAssembler) {
1289   Node* const receiver = Parameter(Descriptor::kReceiver);
1290   Node* key = Parameter(Descriptor::kKey);
1291   Node* const context = Parameter(Descriptor::kContext);
1292 
1293   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1294                          "Map.prototype.delete");
1295 
1296   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1297 
1298   VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(),
1299            IntPtrConstant(0));
1300   Label entry_found(this), not_found(this);
1301 
1302   TryLookupOrderedHashTableIndex<OrderedHashMap>(table, key, context,
1303                                                  &entry_start_position_or_hash,
1304                                                  &entry_found, &not_found);
1305 
1306   BIND(&not_found);
1307   Return(FalseConstant());
1308 
1309   BIND(&entry_found);
1310   // If we found the entry, mark the entry as deleted.
1311   StoreFixedArrayElement(table, entry_start_position_or_hash.value(),
1312                          TheHoleConstant(), UPDATE_WRITE_BARRIER,
1313                          kPointerSize * OrderedHashMap::kHashTableStartIndex);
1314   StoreFixedArrayElement(table, entry_start_position_or_hash.value(),
1315                          TheHoleConstant(), UPDATE_WRITE_BARRIER,
1316                          kPointerSize * (OrderedHashMap::kHashTableStartIndex +
1317                                          OrderedHashMap::kValueOffset));
1318 
1319   // Decrement the number of elements, increment the number of deleted elements.
1320   TNode<Smi> const number_of_elements = SmiSub(
1321       CAST(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset)),
1322       SmiConstant(1));
1323   StoreObjectFieldNoWriteBarrier(table, OrderedHashMap::kNumberOfElementsOffset,
1324                                  number_of_elements);
1325   TNode<Smi> const number_of_deleted =
1326       SmiAdd(CAST(LoadObjectField(
1327                  table, OrderedHashMap::kNumberOfDeletedElementsOffset)),
1328              SmiConstant(1));
1329   StoreObjectFieldNoWriteBarrier(
1330       table, OrderedHashMap::kNumberOfDeletedElementsOffset, number_of_deleted);
1331 
1332   TNode<Smi> const number_of_buckets =
1333       CAST(LoadFixedArrayElement(table, OrderedHashMap::kNumberOfBucketsIndex));
1334 
1335   // If there fewer elements than #buckets / 2, shrink the table.
1336   Label shrink(this);
1337   GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements),
1338                      number_of_buckets),
1339          &shrink);
1340   Return(TrueConstant());
1341 
1342   BIND(&shrink);
1343   CallRuntime(Runtime::kMapShrink, context, receiver);
1344   Return(TrueConstant());
1345 }
1346 
TF_BUILTIN(SetPrototypeAdd,CollectionsBuiltinsAssembler)1347 TF_BUILTIN(SetPrototypeAdd, CollectionsBuiltinsAssembler) {
1348   Node* const receiver = Parameter(Descriptor::kReceiver);
1349   Node* key = Parameter(Descriptor::kKey);
1350   Node* const context = Parameter(Descriptor::kContext);
1351 
1352   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.add");
1353 
1354   key = NormalizeNumberKey(key);
1355 
1356   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1357 
1358   VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(),
1359            IntPtrConstant(0));
1360   Label entry_found(this), not_found(this);
1361 
1362   TryLookupOrderedHashTableIndex<OrderedHashSet>(table, key, context,
1363                                                  &entry_start_position_or_hash,
1364                                                  &entry_found, &not_found);
1365 
1366   BIND(&entry_found);
1367   // The entry was found, there is nothing to do.
1368   Return(receiver);
1369 
1370   Label no_hash(this), add_entry(this), store_new_entry(this);
1371   BIND(&not_found);
1372   {
1373     // If we have a hash code, we can start adding the new entry.
1374     GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(),
1375                              IntPtrConstant(0)),
1376            &add_entry);
1377 
1378     // Otherwise, go to runtime to compute the hash code.
1379     entry_start_position_or_hash.Bind(SmiUntag(CallGetOrCreateHashRaw(key)));
1380     Goto(&add_entry);
1381   }
1382 
1383   BIND(&add_entry);
1384   VARIABLE(number_of_buckets, MachineType::PointerRepresentation());
1385   VARIABLE(occupancy, MachineType::PointerRepresentation());
1386   VARIABLE(table_var, MachineRepresentation::kTaggedPointer, table);
1387   {
1388     // Check we have enough space for the entry.
1389     number_of_buckets.Bind(SmiUntag(CAST(
1390         LoadFixedArrayElement(table, OrderedHashSet::kNumberOfBucketsIndex))));
1391 
1392     STATIC_ASSERT(OrderedHashSet::kLoadFactor == 2);
1393     Node* const capacity = WordShl(number_of_buckets.value(), 1);
1394     Node* const number_of_elements = SmiUntag(
1395         CAST(LoadObjectField(table, OrderedHashSet::kNumberOfElementsOffset)));
1396     Node* const number_of_deleted = SmiUntag(CAST(LoadObjectField(
1397         table, OrderedHashSet::kNumberOfDeletedElementsOffset)));
1398     occupancy.Bind(IntPtrAdd(number_of_elements, number_of_deleted));
1399     GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry);
1400 
1401     // We do not have enough space, grow the table and reload the relevant
1402     // fields.
1403     CallRuntime(Runtime::kSetGrow, context, receiver);
1404     table_var.Bind(LoadObjectField(receiver, JSMap::kTableOffset));
1405     number_of_buckets.Bind(SmiUntag(CAST(LoadFixedArrayElement(
1406         table_var.value(), OrderedHashSet::kNumberOfBucketsIndex))));
1407     Node* const new_number_of_elements = SmiUntag(CAST(LoadObjectField(
1408         table_var.value(), OrderedHashSet::kNumberOfElementsOffset)));
1409     Node* const new_number_of_deleted = SmiUntag(CAST(LoadObjectField(
1410         table_var.value(), OrderedHashSet::kNumberOfDeletedElementsOffset)));
1411     occupancy.Bind(IntPtrAdd(new_number_of_elements, new_number_of_deleted));
1412     Goto(&store_new_entry);
1413   }
1414   BIND(&store_new_entry);
1415   // Store the key, value and connect the element to the bucket chain.
1416   StoreOrderedHashSetNewEntry(table_var.value(), key,
1417                               entry_start_position_or_hash.value(),
1418                               number_of_buckets.value(), occupancy.value());
1419   Return(receiver);
1420 }
1421 
StoreOrderedHashSetNewEntry(Node * const table,Node * const key,Node * const hash,Node * const number_of_buckets,Node * const occupancy)1422 void CollectionsBuiltinsAssembler::StoreOrderedHashSetNewEntry(
1423     Node* const table, Node* const key, Node* const hash,
1424     Node* const number_of_buckets, Node* const occupancy) {
1425   Node* const bucket =
1426       WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1)));
1427   Node* const bucket_entry = LoadFixedArrayElement(
1428       table, bucket, OrderedHashSet::kHashTableStartIndex * kPointerSize);
1429 
1430   // Store the entry elements.
1431   Node* const entry_start = IntPtrAdd(
1432       IntPtrMul(occupancy, IntPtrConstant(OrderedHashSet::kEntrySize)),
1433       number_of_buckets);
1434   StoreFixedArrayElement(table, entry_start, key, UPDATE_WRITE_BARRIER,
1435                          kPointerSize * OrderedHashSet::kHashTableStartIndex);
1436   StoreFixedArrayElement(table, entry_start, bucket_entry, SKIP_WRITE_BARRIER,
1437                          kPointerSize * (OrderedHashSet::kHashTableStartIndex +
1438                                          OrderedHashSet::kChainOffset));
1439 
1440   // Update the bucket head.
1441   StoreFixedArrayElement(table, bucket, SmiTag(occupancy), SKIP_WRITE_BARRIER,
1442                          OrderedHashSet::kHashTableStartIndex * kPointerSize);
1443 
1444   // Bump the elements count.
1445   TNode<Smi> const number_of_elements =
1446       CAST(LoadObjectField(table, OrderedHashSet::kNumberOfElementsOffset));
1447   StoreObjectFieldNoWriteBarrier(table, OrderedHashSet::kNumberOfElementsOffset,
1448                                  SmiAdd(number_of_elements, SmiConstant(1)));
1449 }
1450 
TF_BUILTIN(SetPrototypeDelete,CollectionsBuiltinsAssembler)1451 TF_BUILTIN(SetPrototypeDelete, CollectionsBuiltinsAssembler) {
1452   Node* const receiver = Parameter(Descriptor::kReceiver);
1453   Node* key = Parameter(Descriptor::kKey);
1454   Node* const context = Parameter(Descriptor::kContext);
1455 
1456   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
1457                          "Set.prototype.delete");
1458 
1459   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1460 
1461   VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(),
1462            IntPtrConstant(0));
1463   Label entry_found(this), not_found(this);
1464 
1465   TryLookupOrderedHashTableIndex<OrderedHashSet>(table, key, context,
1466                                                  &entry_start_position_or_hash,
1467                                                  &entry_found, &not_found);
1468 
1469   BIND(&not_found);
1470   Return(FalseConstant());
1471 
1472   BIND(&entry_found);
1473   // If we found the entry, mark the entry as deleted.
1474   StoreFixedArrayElement(table, entry_start_position_or_hash.value(),
1475                          TheHoleConstant(), UPDATE_WRITE_BARRIER,
1476                          kPointerSize * OrderedHashSet::kHashTableStartIndex);
1477 
1478   // Decrement the number of elements, increment the number of deleted elements.
1479   TNode<Smi> const number_of_elements = SmiSub(
1480       CAST(LoadObjectField(table, OrderedHashSet::kNumberOfElementsOffset)),
1481       SmiConstant(1));
1482   StoreObjectFieldNoWriteBarrier(table, OrderedHashSet::kNumberOfElementsOffset,
1483                                  number_of_elements);
1484   TNode<Smi> const number_of_deleted =
1485       SmiAdd(CAST(LoadObjectField(
1486                  table, OrderedHashSet::kNumberOfDeletedElementsOffset)),
1487              SmiConstant(1));
1488   StoreObjectFieldNoWriteBarrier(
1489       table, OrderedHashSet::kNumberOfDeletedElementsOffset, number_of_deleted);
1490 
1491   TNode<Smi> const number_of_buckets =
1492       CAST(LoadFixedArrayElement(table, OrderedHashSet::kNumberOfBucketsIndex));
1493 
1494   // If there fewer elements than #buckets / 2, shrink the table.
1495   Label shrink(this);
1496   GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements),
1497                      number_of_buckets),
1498          &shrink);
1499   Return(TrueConstant());
1500 
1501   BIND(&shrink);
1502   CallRuntime(Runtime::kSetShrink, context, receiver);
1503   Return(TrueConstant());
1504 }
1505 
TF_BUILTIN(MapPrototypeEntries,CollectionsBuiltinsAssembler)1506 TF_BUILTIN(MapPrototypeEntries, CollectionsBuiltinsAssembler) {
1507   Node* const receiver = Parameter(Descriptor::kReceiver);
1508   Node* const context = Parameter(Descriptor::kContext);
1509   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1510                          "Map.prototype.entries");
1511   Return(AllocateJSCollectionIterator<JSMapIterator>(
1512       context, Context::MAP_KEY_VALUE_ITERATOR_MAP_INDEX, receiver));
1513 }
1514 
TF_BUILTIN(MapPrototypeGetSize,CollectionsBuiltinsAssembler)1515 TF_BUILTIN(MapPrototypeGetSize, CollectionsBuiltinsAssembler) {
1516   Node* const receiver = Parameter(Descriptor::kReceiver);
1517   Node* const context = Parameter(Descriptor::kContext);
1518   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1519                          "get Map.prototype.size");
1520   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1521   Return(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset));
1522 }
1523 
TF_BUILTIN(MapPrototypeForEach,CollectionsBuiltinsAssembler)1524 TF_BUILTIN(MapPrototypeForEach, CollectionsBuiltinsAssembler) {
1525   const char* const kMethodName = "Map.prototype.forEach";
1526   Node* const argc = Parameter(BuiltinDescriptor::kArgumentsCount);
1527   Node* const context = Parameter(BuiltinDescriptor::kContext);
1528   CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
1529   Node* const receiver = args.GetReceiver();
1530   Node* const callback = args.GetOptionalArgumentValue(0);
1531   Node* const this_arg = args.GetOptionalArgumentValue(1);
1532 
1533   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, kMethodName);
1534 
1535   // Ensure that {callback} is actually callable.
1536   Label callback_not_callable(this, Label::kDeferred);
1537   GotoIf(TaggedIsSmi(callback), &callback_not_callable);
1538   GotoIfNot(IsCallable(callback), &callback_not_callable);
1539 
1540   VARIABLE(var_index, MachineType::PointerRepresentation(), IntPtrConstant(0));
1541   VARIABLE(var_table, MachineRepresentation::kTagged,
1542            LoadObjectField(receiver, JSMap::kTableOffset));
1543   Label loop(this, {&var_index, &var_table}), done_loop(this);
1544   Goto(&loop);
1545   BIND(&loop);
1546   {
1547     // Transition {table} and {index} if there was any modification to
1548     // the {receiver} while we're iterating.
1549     Node* index = var_index.value();
1550     Node* table = var_table.value();
1551     std::tie(table, index) =
1552         Transition<OrderedHashMap>(table, index, [](Node*, Node*) {});
1553 
1554     // Read the next entry from the {table}, skipping holes.
1555     Node* entry_key;
1556     Node* entry_start_position;
1557     std::tie(entry_key, entry_start_position, index) =
1558         NextSkipHoles<OrderedHashMap>(table, index, &done_loop);
1559 
1560     // Load the entry value as well.
1561     Node* entry_value = LoadFixedArrayElement(
1562         table, entry_start_position,
1563         (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset) *
1564             kPointerSize);
1565 
1566     // Invoke the {callback} passing the {entry_key}, {entry_value} and the
1567     // {receiver}.
1568     CallJS(CodeFactory::Call(isolate()), context, callback, this_arg,
1569            entry_value, entry_key, receiver);
1570 
1571     // Continue with the next entry.
1572     var_index.Bind(index);
1573     var_table.Bind(table);
1574     Goto(&loop);
1575   }
1576 
1577   BIND(&done_loop);
1578   args.PopAndReturn(UndefinedConstant());
1579 
1580   BIND(&callback_not_callable);
1581   {
1582     CallRuntime(Runtime::kThrowCalledNonCallable, context, callback);
1583     Unreachable();
1584   }
1585 }
1586 
TF_BUILTIN(MapPrototypeKeys,CollectionsBuiltinsAssembler)1587 TF_BUILTIN(MapPrototypeKeys, CollectionsBuiltinsAssembler) {
1588   Node* const receiver = Parameter(Descriptor::kReceiver);
1589   Node* const context = Parameter(Descriptor::kContext);
1590   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.keys");
1591   Return(AllocateJSCollectionIterator<JSMapIterator>(
1592       context, Context::MAP_KEY_ITERATOR_MAP_INDEX, receiver));
1593 }
1594 
TF_BUILTIN(MapPrototypeValues,CollectionsBuiltinsAssembler)1595 TF_BUILTIN(MapPrototypeValues, CollectionsBuiltinsAssembler) {
1596   Node* const receiver = Parameter(Descriptor::kReceiver);
1597   Node* const context = Parameter(Descriptor::kContext);
1598   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1599                          "Map.prototype.values");
1600   Return(AllocateJSCollectionIterator<JSMapIterator>(
1601       context, Context::MAP_VALUE_ITERATOR_MAP_INDEX, receiver));
1602 }
1603 
TF_BUILTIN(MapIteratorPrototypeNext,CollectionsBuiltinsAssembler)1604 TF_BUILTIN(MapIteratorPrototypeNext, CollectionsBuiltinsAssembler) {
1605   const char* const kMethodName = "Map Iterator.prototype.next";
1606   Node* const receiver = Parameter(Descriptor::kReceiver);
1607   Node* const context = Parameter(Descriptor::kContext);
1608 
1609   // Ensure that the {receiver} is actually a JSMapIterator.
1610   Label if_receiver_valid(this), if_receiver_invalid(this, Label::kDeferred);
1611   GotoIf(TaggedIsSmi(receiver), &if_receiver_invalid);
1612   Node* const receiver_instance_type = LoadInstanceType(receiver);
1613   GotoIf(
1614       InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_VALUE_ITERATOR_TYPE),
1615       &if_receiver_valid);
1616   GotoIf(InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_ITERATOR_TYPE),
1617          &if_receiver_valid);
1618   Branch(InstanceTypeEqual(receiver_instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
1619          &if_receiver_valid, &if_receiver_invalid);
1620   BIND(&if_receiver_invalid);
1621   ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
1622                  StringConstant(kMethodName), receiver);
1623   BIND(&if_receiver_valid);
1624 
1625   // Check if the {receiver} is exhausted.
1626   VARIABLE(var_done, MachineRepresentation::kTagged, TrueConstant());
1627   VARIABLE(var_value, MachineRepresentation::kTagged, UndefinedConstant());
1628   Label return_value(this, {&var_done, &var_value}), return_entry(this),
1629       return_end(this, Label::kDeferred);
1630 
1631   // Transition the {receiver} table if necessary.
1632   Node* table;
1633   Node* index;
1634   std::tie(table, index) =
1635       TransitionAndUpdate<JSMapIterator, OrderedHashMap>(receiver);
1636 
1637   // Read the next entry from the {table}, skipping holes.
1638   Node* entry_key;
1639   Node* entry_start_position;
1640   std::tie(entry_key, entry_start_position, index) =
1641       NextSkipHoles<OrderedHashMap>(table, index, &return_end);
1642   StoreObjectFieldNoWriteBarrier(receiver, JSMapIterator::kIndexOffset,
1643                                  SmiTag(index));
1644   var_value.Bind(entry_key);
1645   var_done.Bind(FalseConstant());
1646 
1647   // Check how to return the {key} (depending on {receiver} type).
1648   GotoIf(InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_ITERATOR_TYPE),
1649          &return_value);
1650   var_value.Bind(LoadFixedArrayElement(
1651       table, entry_start_position,
1652       (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset) *
1653           kPointerSize));
1654   Branch(InstanceTypeEqual(receiver_instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
1655          &return_value, &return_entry);
1656 
1657   BIND(&return_entry);
1658   {
1659     Node* result =
1660         AllocateJSIteratorResultForEntry(context, entry_key, var_value.value());
1661     Return(result);
1662   }
1663 
1664   BIND(&return_value);
1665   {
1666     Node* result =
1667         AllocateJSIteratorResult(context, var_value.value(), var_done.value());
1668     Return(result);
1669   }
1670 
1671   BIND(&return_end);
1672   {
1673     StoreObjectFieldRoot(receiver, JSMapIterator::kTableOffset,
1674                          Heap::kEmptyOrderedHashMapRootIndex);
1675     Goto(&return_value);
1676   }
1677 }
1678 
TF_BUILTIN(SetPrototypeHas,CollectionsBuiltinsAssembler)1679 TF_BUILTIN(SetPrototypeHas, CollectionsBuiltinsAssembler) {
1680   Node* const receiver = Parameter(Descriptor::kReceiver);
1681   Node* const key = Parameter(Descriptor::kKey);
1682   Node* const context = Parameter(Descriptor::kContext);
1683 
1684   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.has");
1685 
1686   Node* const table = LoadObjectField(receiver, JSMap::kTableOffset);
1687 
1688   VARIABLE(entry_start_position, MachineType::PointerRepresentation(),
1689            IntPtrConstant(0));
1690   VARIABLE(result, MachineRepresentation::kTaggedSigned, IntPtrConstant(0));
1691   Label if_key_smi(this), if_key_string(this), if_key_heap_number(this),
1692       if_key_bigint(this), entry_found(this), not_found(this), done(this);
1693 
1694   GotoIf(TaggedIsSmi(key), &if_key_smi);
1695 
1696   Node* key_map = LoadMap(key);
1697   Node* key_instance_type = LoadMapInstanceType(key_map);
1698 
1699   GotoIf(IsStringInstanceType(key_instance_type), &if_key_string);
1700   GotoIf(IsHeapNumberMap(key_map), &if_key_heap_number);
1701   GotoIf(IsBigIntInstanceType(key_instance_type), &if_key_bigint);
1702 
1703   FindOrderedHashTableEntryForOtherKey<OrderedHashSet>(
1704       context, table, key, &entry_start_position, &entry_found, &not_found);
1705 
1706   BIND(&if_key_smi);
1707   {
1708     FindOrderedHashTableEntryForSmiKey<OrderedHashSet>(
1709         table, key, &entry_start_position, &entry_found, &not_found);
1710   }
1711 
1712   BIND(&if_key_string);
1713   {
1714     FindOrderedHashTableEntryForStringKey<OrderedHashSet>(
1715         context, table, key, &entry_start_position, &entry_found, &not_found);
1716   }
1717 
1718   BIND(&if_key_heap_number);
1719   {
1720     FindOrderedHashTableEntryForHeapNumberKey<OrderedHashSet>(
1721         context, table, key, &entry_start_position, &entry_found, &not_found);
1722   }
1723 
1724   BIND(&if_key_bigint);
1725   {
1726     FindOrderedHashTableEntryForBigIntKey<OrderedHashSet>(
1727         context, table, key, &entry_start_position, &entry_found, &not_found);
1728   }
1729 
1730   BIND(&entry_found);
1731   Return(TrueConstant());
1732 
1733   BIND(&not_found);
1734   Return(FalseConstant());
1735 }
1736 
TF_BUILTIN(SetPrototypeEntries,CollectionsBuiltinsAssembler)1737 TF_BUILTIN(SetPrototypeEntries, CollectionsBuiltinsAssembler) {
1738   Node* const receiver = Parameter(Descriptor::kReceiver);
1739   Node* const context = Parameter(Descriptor::kContext);
1740   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
1741                          "Set.prototype.entries");
1742   Return(AllocateJSCollectionIterator<JSSetIterator>(
1743       context, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX, receiver));
1744 }
1745 
TF_BUILTIN(SetPrototypeGetSize,CollectionsBuiltinsAssembler)1746 TF_BUILTIN(SetPrototypeGetSize, CollectionsBuiltinsAssembler) {
1747   Node* const receiver = Parameter(Descriptor::kReceiver);
1748   Node* const context = Parameter(Descriptor::kContext);
1749   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
1750                          "get Set.prototype.size");
1751   Node* const table = LoadObjectField(receiver, JSSet::kTableOffset);
1752   Return(LoadObjectField(table, OrderedHashSet::kNumberOfElementsOffset));
1753 }
1754 
TF_BUILTIN(SetPrototypeForEach,CollectionsBuiltinsAssembler)1755 TF_BUILTIN(SetPrototypeForEach, CollectionsBuiltinsAssembler) {
1756   const char* const kMethodName = "Set.prototype.forEach";
1757   Node* const argc = Parameter(BuiltinDescriptor::kArgumentsCount);
1758   Node* const context = Parameter(BuiltinDescriptor::kContext);
1759   CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
1760   Node* const receiver = args.GetReceiver();
1761   Node* const callback = args.GetOptionalArgumentValue(0);
1762   Node* const this_arg = args.GetOptionalArgumentValue(1);
1763 
1764   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, kMethodName);
1765 
1766   // Ensure that {callback} is actually callable.
1767   Label callback_not_callable(this, Label::kDeferred);
1768   GotoIf(TaggedIsSmi(callback), &callback_not_callable);
1769   GotoIfNot(IsCallable(callback), &callback_not_callable);
1770 
1771   VARIABLE(var_index, MachineType::PointerRepresentation(), IntPtrConstant(0));
1772   VARIABLE(var_table, MachineRepresentation::kTagged,
1773            LoadObjectField(receiver, JSSet::kTableOffset));
1774   Label loop(this, {&var_index, &var_table}), done_loop(this);
1775   Goto(&loop);
1776   BIND(&loop);
1777   {
1778     // Transition {table} and {index} if there was any modification to
1779     // the {receiver} while we're iterating.
1780     Node* index = var_index.value();
1781     Node* table = var_table.value();
1782     std::tie(table, index) =
1783         Transition<OrderedHashSet>(table, index, [](Node*, Node*) {});
1784 
1785     // Read the next entry from the {table}, skipping holes.
1786     Node* entry_key;
1787     Node* entry_start_position;
1788     std::tie(entry_key, entry_start_position, index) =
1789         NextSkipHoles<OrderedHashSet>(table, index, &done_loop);
1790 
1791     // Invoke the {callback} passing the {entry_key} (twice) and the {receiver}.
1792     CallJS(CodeFactory::Call(isolate()), context, callback, this_arg, entry_key,
1793            entry_key, receiver);
1794 
1795     // Continue with the next entry.
1796     var_index.Bind(index);
1797     var_table.Bind(table);
1798     Goto(&loop);
1799   }
1800 
1801   BIND(&done_loop);
1802   args.PopAndReturn(UndefinedConstant());
1803 
1804   BIND(&callback_not_callable);
1805   {
1806     CallRuntime(Runtime::kThrowCalledNonCallable, context, callback);
1807     Unreachable();
1808   }
1809 }
1810 
TF_BUILTIN(SetPrototypeValues,CollectionsBuiltinsAssembler)1811 TF_BUILTIN(SetPrototypeValues, CollectionsBuiltinsAssembler) {
1812   Node* const receiver = Parameter(Descriptor::kReceiver);
1813   Node* const context = Parameter(Descriptor::kContext);
1814   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
1815                          "Set.prototype.values");
1816   Return(AllocateJSCollectionIterator<JSSetIterator>(
1817       context, Context::SET_VALUE_ITERATOR_MAP_INDEX, receiver));
1818 }
1819 
TF_BUILTIN(SetIteratorPrototypeNext,CollectionsBuiltinsAssembler)1820 TF_BUILTIN(SetIteratorPrototypeNext, CollectionsBuiltinsAssembler) {
1821   const char* const kMethodName = "Set Iterator.prototype.next";
1822   Node* const receiver = Parameter(Descriptor::kReceiver);
1823   Node* const context = Parameter(Descriptor::kContext);
1824 
1825   // Ensure that the {receiver} is actually a JSSetIterator.
1826   Label if_receiver_valid(this), if_receiver_invalid(this, Label::kDeferred);
1827   GotoIf(TaggedIsSmi(receiver), &if_receiver_invalid);
1828   Node* const receiver_instance_type = LoadInstanceType(receiver);
1829   GotoIf(InstanceTypeEqual(receiver_instance_type, JS_SET_VALUE_ITERATOR_TYPE),
1830          &if_receiver_valid);
1831   Branch(
1832       InstanceTypeEqual(receiver_instance_type, JS_SET_KEY_VALUE_ITERATOR_TYPE),
1833       &if_receiver_valid, &if_receiver_invalid);
1834   BIND(&if_receiver_invalid);
1835   ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
1836                  StringConstant(kMethodName), receiver);
1837   BIND(&if_receiver_valid);
1838 
1839   // Check if the {receiver} is exhausted.
1840   VARIABLE(var_done, MachineRepresentation::kTagged, TrueConstant());
1841   VARIABLE(var_value, MachineRepresentation::kTagged, UndefinedConstant());
1842   Label return_value(this, {&var_done, &var_value}), return_entry(this),
1843       return_end(this, Label::kDeferred);
1844 
1845   // Transition the {receiver} table if necessary.
1846   Node* table;
1847   Node* index;
1848   std::tie(table, index) =
1849       TransitionAndUpdate<JSSetIterator, OrderedHashSet>(receiver);
1850 
1851   // Read the next entry from the {table}, skipping holes.
1852   Node* entry_key;
1853   Node* entry_start_position;
1854   std::tie(entry_key, entry_start_position, index) =
1855       NextSkipHoles<OrderedHashSet>(table, index, &return_end);
1856   StoreObjectFieldNoWriteBarrier(receiver, JSSetIterator::kIndexOffset,
1857                                  SmiTag(index));
1858   var_value.Bind(entry_key);
1859   var_done.Bind(FalseConstant());
1860 
1861   // Check how to return the {key} (depending on {receiver} type).
1862   Branch(InstanceTypeEqual(receiver_instance_type, JS_SET_VALUE_ITERATOR_TYPE),
1863          &return_value, &return_entry);
1864 
1865   BIND(&return_entry);
1866   {
1867     Node* result = AllocateJSIteratorResultForEntry(context, var_value.value(),
1868                                                     var_value.value());
1869     Return(result);
1870   }
1871 
1872   BIND(&return_value);
1873   {
1874     Node* result =
1875         AllocateJSIteratorResult(context, var_value.value(), var_done.value());
1876     Return(result);
1877   }
1878 
1879   BIND(&return_end);
1880   {
1881     StoreObjectFieldRoot(receiver, JSSetIterator::kTableOffset,
1882                          Heap::kEmptyOrderedHashSetRootIndex);
1883     Goto(&return_value);
1884   }
1885 }
1886 
1887 template <typename CollectionType>
TryLookupOrderedHashTableIndex(Node * const table,Node * const key,Node * const context,Variable * result,Label * if_entry_found,Label * if_not_found)1888 void CollectionsBuiltinsAssembler::TryLookupOrderedHashTableIndex(
1889     Node* const table, Node* const key, Node* const context, Variable* result,
1890     Label* if_entry_found, Label* if_not_found) {
1891   Label if_key_smi(this), if_key_string(this), if_key_heap_number(this),
1892       if_key_bigint(this);
1893 
1894   GotoIf(TaggedIsSmi(key), &if_key_smi);
1895 
1896   Node* key_map = LoadMap(key);
1897   Node* key_instance_type = LoadMapInstanceType(key_map);
1898 
1899   GotoIf(IsStringInstanceType(key_instance_type), &if_key_string);
1900   GotoIf(IsHeapNumberMap(key_map), &if_key_heap_number);
1901   GotoIf(IsBigIntInstanceType(key_instance_type), &if_key_bigint);
1902 
1903   FindOrderedHashTableEntryForOtherKey<CollectionType>(
1904       context, table, key, result, if_entry_found, if_not_found);
1905 
1906   BIND(&if_key_smi);
1907   {
1908     FindOrderedHashTableEntryForSmiKey<CollectionType>(
1909         table, key, result, if_entry_found, if_not_found);
1910   }
1911 
1912   BIND(&if_key_string);
1913   {
1914     FindOrderedHashTableEntryForStringKey<CollectionType>(
1915         context, table, key, result, if_entry_found, if_not_found);
1916   }
1917 
1918   BIND(&if_key_heap_number);
1919   {
1920     FindOrderedHashTableEntryForHeapNumberKey<CollectionType>(
1921         context, table, key, result, if_entry_found, if_not_found);
1922   }
1923 
1924   BIND(&if_key_bigint);
1925   {
1926     FindOrderedHashTableEntryForBigIntKey<CollectionType>(
1927         context, table, key, result, if_entry_found, if_not_found);
1928   }
1929 }
1930 
TF_BUILTIN(FindOrderedHashMapEntry,CollectionsBuiltinsAssembler)1931 TF_BUILTIN(FindOrderedHashMapEntry, CollectionsBuiltinsAssembler) {
1932   Node* const table = Parameter(Descriptor::kTable);
1933   Node* const key = Parameter(Descriptor::kKey);
1934   Node* const context = Parameter(Descriptor::kContext);
1935 
1936   VARIABLE(entry_start_position, MachineType::PointerRepresentation(),
1937            IntPtrConstant(0));
1938   Label entry_found(this), not_found(this);
1939 
1940   TryLookupOrderedHashTableIndex<OrderedHashMap>(
1941       table, key, context, &entry_start_position, &entry_found, &not_found);
1942 
1943   BIND(&entry_found);
1944   Return(SmiTag(entry_start_position.value()));
1945 
1946   BIND(&not_found);
1947   Return(SmiConstant(-1));
1948 }
1949 
1950 class WeakCollectionsBuiltinsAssembler : public BaseCollectionsAssembler {
1951  public:
WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState * state)1952   explicit WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state)
1953       : BaseCollectionsAssembler(state) {}
1954 
1955  protected:
1956   void AddEntry(TNode<HeapObject> table, TNode<IntPtrT> key_index,
1957                 TNode<Object> key, TNode<Object> value,
1958                 TNode<IntPtrT> number_of_elements);
1959 
1960   TNode<Object> AllocateTable(Variant variant, TNode<Context> context,
1961                               TNode<IntPtrT> at_least_space_for);
1962 
1963   // Generates and sets the identity for a JSRececiver.
1964   TNode<Smi> CreateIdentityHash(TNode<Object> receiver);
1965   TNode<IntPtrT> EntryMask(TNode<IntPtrT> capacity);
1966 
1967   // Builds code that finds the ObjectHashTable entry for a {key} using the
1968   // comparison code generated by {key_compare}. The key index is returned if
1969   // the {key} is found.
1970   typedef std::function<void(TNode<Object> entry_key, Label* if_same)>
1971       KeyComparator;
1972   TNode<IntPtrT> FindKeyIndex(TNode<HeapObject> table, TNode<IntPtrT> key_hash,
1973                               TNode<IntPtrT> entry_mask,
1974                               const KeyComparator& key_compare);
1975 
1976   // Builds code that finds an ObjectHashTable entry available for a new entry.
1977   TNode<IntPtrT> FindKeyIndexForInsertion(TNode<HeapObject> table,
1978                                           TNode<IntPtrT> key_hash,
1979                                           TNode<IntPtrT> entry_mask);
1980 
1981   // Builds code that finds the ObjectHashTable entry with key that matches
1982   // {key} and returns the entry's key index. If {key} cannot be found, jumps to
1983   // {if_not_found}.
1984   TNode<IntPtrT> FindKeyIndexForKey(TNode<HeapObject> table, TNode<Object> key,
1985                                     TNode<IntPtrT> hash,
1986                                     TNode<IntPtrT> entry_mask,
1987                                     Label* if_not_found);
1988 
1989   TNode<Word32T> InsufficientCapacityToAdd(TNode<IntPtrT> capacity,
1990                                            TNode<IntPtrT> number_of_elements,
1991                                            TNode<IntPtrT> number_of_deleted);
1992   TNode<IntPtrT> KeyIndexFromEntry(TNode<IntPtrT> entry);
1993 
1994   TNode<IntPtrT> LoadNumberOfElements(TNode<HeapObject> table, int offset);
1995   TNode<IntPtrT> LoadNumberOfDeleted(TNode<HeapObject> table, int offset = 0);
1996   TNode<HeapObject> LoadTable(SloppyTNode<HeapObject> collection);
1997   TNode<IntPtrT> LoadTableCapacity(TNode<HeapObject> table);
1998 
1999   void RemoveEntry(TNode<HeapObject> table, TNode<IntPtrT> key_index,
2000                    TNode<IntPtrT> number_of_elements);
2001   TNode<BoolT> ShouldRehash(TNode<IntPtrT> number_of_elements,
2002                             TNode<IntPtrT> number_of_deleted);
2003   TNode<Word32T> ShouldShrink(TNode<IntPtrT> capacity,
2004                               TNode<IntPtrT> number_of_elements);
2005   TNode<IntPtrT> ValueIndexFromKeyIndex(TNode<IntPtrT> key_index);
2006 };
2007 
AddEntry(TNode<HeapObject> table,TNode<IntPtrT> key_index,TNode<Object> key,TNode<Object> value,TNode<IntPtrT> number_of_elements)2008 void WeakCollectionsBuiltinsAssembler::AddEntry(
2009     TNode<HeapObject> table, TNode<IntPtrT> key_index, TNode<Object> key,
2010     TNode<Object> value, TNode<IntPtrT> number_of_elements) {
2011   // See ObjectHashTable::AddEntry().
2012   TNode<IntPtrT> value_index = ValueIndexFromKeyIndex(key_index);
2013   StoreFixedArrayElement(table, key_index, key);
2014   StoreFixedArrayElement(table, value_index, value);
2015 
2016   // See HashTableBase::ElementAdded().
2017   StoreFixedArrayElement(table, ObjectHashTable::kNumberOfElementsIndex,
2018                          SmiFromIntPtr(number_of_elements), SKIP_WRITE_BARRIER);
2019 }
2020 
AllocateTable(Variant variant,TNode<Context> context,TNode<IntPtrT> at_least_space_for)2021 TNode<Object> WeakCollectionsBuiltinsAssembler::AllocateTable(
2022     Variant variant, TNode<Context> context,
2023     TNode<IntPtrT> at_least_space_for) {
2024   // See HashTable::New().
2025   CSA_ASSERT(this,
2026              IntPtrLessThanOrEqual(IntPtrConstant(0), at_least_space_for));
2027   TNode<IntPtrT> capacity = HashTableComputeCapacity(at_least_space_for);
2028 
2029   // See HashTable::NewInternal().
2030   TNode<IntPtrT> length = KeyIndexFromEntry(capacity);
2031   TNode<FixedArray> table = AllocateFixedArray(
2032       HOLEY_ELEMENTS, length, INTPTR_PARAMETERS, kAllowLargeObjectAllocation);
2033 
2034   Heap::RootListIndex map_root_index =
2035       static_cast<Heap::RootListIndex>(ObjectHashTableShape::GetMapRootIndex());
2036   StoreMapNoWriteBarrier(table, map_root_index);
2037   StoreFixedArrayElement(table, ObjectHashTable::kNumberOfElementsIndex,
2038                          SmiConstant(0), SKIP_WRITE_BARRIER);
2039   StoreFixedArrayElement(table, ObjectHashTable::kNumberOfDeletedElementsIndex,
2040                          SmiConstant(0), SKIP_WRITE_BARRIER);
2041   StoreFixedArrayElement(table, ObjectHashTable::kCapacityIndex,
2042                          SmiFromIntPtr(capacity), SKIP_WRITE_BARRIER);
2043 
2044   TNode<IntPtrT> start = KeyIndexFromEntry(IntPtrConstant(0));
2045   FillFixedArrayWithValue(HOLEY_ELEMENTS, table, start, length,
2046                           Heap::kUndefinedValueRootIndex);
2047   return table;
2048 }
2049 
CreateIdentityHash(TNode<Object> key)2050 TNode<Smi> WeakCollectionsBuiltinsAssembler::CreateIdentityHash(
2051     TNode<Object> key) {
2052   TNode<ExternalReference> function_addr = ExternalConstant(
2053       ExternalReference::jsreceiver_create_identity_hash(isolate()));
2054   TNode<ExternalReference> isolate_ptr =
2055       ExternalConstant(ExternalReference::isolate_address(isolate()));
2056 
2057   MachineType type_ptr = MachineType::Pointer();
2058   MachineType type_tagged = MachineType::AnyTagged();
2059 
2060   return CAST(CallCFunction2(type_tagged, type_ptr, type_tagged, function_addr,
2061                              isolate_ptr, key));
2062 }
2063 
EntryMask(TNode<IntPtrT> capacity)2064 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::EntryMask(
2065     TNode<IntPtrT> capacity) {
2066   return IntPtrSub(capacity, IntPtrConstant(1));
2067 }
2068 
FindKeyIndex(TNode<HeapObject> table,TNode<IntPtrT> key_hash,TNode<IntPtrT> entry_mask,const KeyComparator & key_compare)2069 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::FindKeyIndex(
2070     TNode<HeapObject> table, TNode<IntPtrT> key_hash, TNode<IntPtrT> entry_mask,
2071     const KeyComparator& key_compare) {
2072   // See HashTable::FirstProbe().
2073   TVARIABLE(IntPtrT, var_entry, WordAnd(key_hash, entry_mask));
2074   TVARIABLE(IntPtrT, var_count, IntPtrConstant(0));
2075 
2076   Variable* loop_vars[] = {&var_count, &var_entry};
2077   Label loop(this, arraysize(loop_vars), loop_vars), if_found(this);
2078   Goto(&loop);
2079   BIND(&loop);
2080   TNode<IntPtrT> key_index;
2081   {
2082     key_index = KeyIndexFromEntry(var_entry.value());
2083     TNode<Object> entry_key = LoadFixedArrayElement(table, key_index);
2084 
2085     key_compare(entry_key, &if_found);
2086 
2087     // See HashTable::NextProbe().
2088     Increment(&var_count);
2089     var_entry =
2090         WordAnd(IntPtrAdd(var_entry.value(), var_count.value()), entry_mask);
2091     Goto(&loop);
2092   }
2093 
2094   BIND(&if_found);
2095   return key_index;
2096 }
2097 
FindKeyIndexForInsertion(TNode<HeapObject> table,TNode<IntPtrT> key_hash,TNode<IntPtrT> entry_mask)2098 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::FindKeyIndexForInsertion(
2099     TNode<HeapObject> table, TNode<IntPtrT> key_hash,
2100     TNode<IntPtrT> entry_mask) {
2101   // See HashTable::FindInsertionEntry().
2102   auto is_not_live = [&](TNode<Object> entry_key, Label* if_found) {
2103     // This is the the negative form BaseShape::IsLive().
2104     GotoIf(Word32Or(IsTheHole(entry_key), IsUndefined(entry_key)), if_found);
2105   };
2106   return FindKeyIndex(table, key_hash, entry_mask, is_not_live);
2107 }
2108 
FindKeyIndexForKey(TNode<HeapObject> table,TNode<Object> key,TNode<IntPtrT> hash,TNode<IntPtrT> entry_mask,Label * if_not_found)2109 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::FindKeyIndexForKey(
2110     TNode<HeapObject> table, TNode<Object> key, TNode<IntPtrT> hash,
2111     TNode<IntPtrT> entry_mask, Label* if_not_found) {
2112   // See HashTable::FindEntry().
2113   auto match_key_or_exit_on_empty = [&](TNode<Object> entry_key,
2114                                         Label* if_same) {
2115     GotoIf(IsUndefined(entry_key), if_not_found);
2116     GotoIf(WordEqual(entry_key, key), if_same);
2117   };
2118   return FindKeyIndex(table, hash, entry_mask, match_key_or_exit_on_empty);
2119 }
2120 
KeyIndexFromEntry(TNode<IntPtrT> entry)2121 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::KeyIndexFromEntry(
2122     TNode<IntPtrT> entry) {
2123   // See HashTable::KeyAt().
2124   // (entry * kEntrySize) + kElementsStartIndex + kEntryKeyIndex
2125   return IntPtrAdd(
2126       IntPtrMul(entry, IntPtrConstant(ObjectHashTable::kEntrySize)),
2127       IntPtrConstant(ObjectHashTable::kElementsStartIndex +
2128                      ObjectHashTable::kEntryKeyIndex));
2129 }
2130 
LoadNumberOfElements(TNode<HeapObject> table,int offset)2131 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::LoadNumberOfElements(
2132     TNode<HeapObject> table, int offset) {
2133   TNode<IntPtrT> number_of_elements = SmiUntag(CAST(
2134       LoadFixedArrayElement(table, ObjectHashTable::kNumberOfElementsIndex)));
2135   return IntPtrAdd(number_of_elements, IntPtrConstant(offset));
2136 }
2137 
LoadNumberOfDeleted(TNode<HeapObject> table,int offset)2138 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::LoadNumberOfDeleted(
2139     TNode<HeapObject> table, int offset) {
2140   TNode<IntPtrT> number_of_deleted = SmiUntag(CAST(LoadFixedArrayElement(
2141       table, ObjectHashTable::kNumberOfDeletedElementsIndex)));
2142   return IntPtrAdd(number_of_deleted, IntPtrConstant(offset));
2143 }
2144 
LoadTable(SloppyTNode<HeapObject> collection)2145 TNode<HeapObject> WeakCollectionsBuiltinsAssembler::LoadTable(
2146     SloppyTNode<HeapObject> collection) {
2147   return CAST(LoadObjectField(collection, JSWeakCollection::kTableOffset));
2148 }
2149 
LoadTableCapacity(TNode<HeapObject> table)2150 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::LoadTableCapacity(
2151     TNode<HeapObject> table) {
2152   return SmiUntag(
2153       CAST(LoadFixedArrayElement(table, ObjectHashTable::kCapacityIndex)));
2154 }
2155 
InsufficientCapacityToAdd(TNode<IntPtrT> capacity,TNode<IntPtrT> number_of_elements,TNode<IntPtrT> number_of_deleted)2156 TNode<Word32T> WeakCollectionsBuiltinsAssembler::InsufficientCapacityToAdd(
2157     TNode<IntPtrT> capacity, TNode<IntPtrT> number_of_elements,
2158     TNode<IntPtrT> number_of_deleted) {
2159   // This is the negative form of HashTable::HasSufficientCapacityToAdd().
2160   // Return true if:
2161   //   - more than 50% of the available space are deleted elements
2162   //   - less than 50% will be available
2163   TNode<IntPtrT> available = IntPtrSub(capacity, number_of_elements);
2164   TNode<IntPtrT> half_available = WordShr(available, 1);
2165   TNode<IntPtrT> needed_available = WordShr(number_of_elements, 1);
2166   return Word32Or(
2167       // deleted > half
2168       IntPtrGreaterThan(number_of_deleted, half_available),
2169       // elements + needed available > capacity
2170       IntPtrGreaterThan(IntPtrAdd(number_of_elements, needed_available),
2171                         capacity));
2172 }
2173 
RemoveEntry(TNode<HeapObject> table,TNode<IntPtrT> key_index,TNode<IntPtrT> number_of_elements)2174 void WeakCollectionsBuiltinsAssembler::RemoveEntry(
2175     TNode<HeapObject> table, TNode<IntPtrT> key_index,
2176     TNode<IntPtrT> number_of_elements) {
2177   // See ObjectHashTable::RemoveEntry().
2178   TNode<IntPtrT> value_index = ValueIndexFromKeyIndex(key_index);
2179   StoreFixedArrayElement(table, key_index, TheHoleConstant());
2180   StoreFixedArrayElement(table, value_index, TheHoleConstant());
2181 
2182   // See HashTableBase::ElementRemoved().
2183   TNode<IntPtrT> number_of_deleted = LoadNumberOfDeleted(table, 1);
2184   StoreFixedArrayElement(table, ObjectHashTable::kNumberOfElementsIndex,
2185                          SmiFromIntPtr(number_of_elements), SKIP_WRITE_BARRIER);
2186   StoreFixedArrayElement(table, ObjectHashTable::kNumberOfDeletedElementsIndex,
2187                          SmiFromIntPtr(number_of_deleted), SKIP_WRITE_BARRIER);
2188 }
2189 
ShouldRehash(TNode<IntPtrT> number_of_elements,TNode<IntPtrT> number_of_deleted)2190 TNode<BoolT> WeakCollectionsBuiltinsAssembler::ShouldRehash(
2191     TNode<IntPtrT> number_of_elements, TNode<IntPtrT> number_of_deleted) {
2192   // Rehash if more than 33% of the entries are deleted.
2193   return IntPtrGreaterThanOrEqual(WordShl(number_of_deleted, 1),
2194                                   number_of_elements);
2195 }
2196 
ShouldShrink(TNode<IntPtrT> capacity,TNode<IntPtrT> number_of_elements)2197 TNode<Word32T> WeakCollectionsBuiltinsAssembler::ShouldShrink(
2198     TNode<IntPtrT> capacity, TNode<IntPtrT> number_of_elements) {
2199   // See HashTable::Shrink().
2200   TNode<IntPtrT> quarter_capacity = WordShr(capacity, 2);
2201   return Word32And(
2202       // Shrink to fit the number of elements if only a quarter of the
2203       // capacity is filled with elements.
2204       IntPtrLessThanOrEqual(number_of_elements, quarter_capacity),
2205 
2206       // Allocate a new dictionary with room for at least the current
2207       // number of elements. The allocation method will make sure that
2208       // there is extra room in the dictionary for additions. Don't go
2209       // lower than room for 16 elements.
2210       IntPtrGreaterThanOrEqual(number_of_elements, IntPtrConstant(16)));
2211 }
2212 
ValueIndexFromKeyIndex(TNode<IntPtrT> key_index)2213 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::ValueIndexFromKeyIndex(
2214     TNode<IntPtrT> key_index) {
2215   return IntPtrAdd(key_index,
2216                    IntPtrConstant(ObjectHashTableShape::kEntryValueIndex -
2217                                   ObjectHashTable::kEntryKeyIndex));
2218 }
2219 
TF_BUILTIN(WeakMapConstructor,WeakCollectionsBuiltinsAssembler)2220 TF_BUILTIN(WeakMapConstructor, WeakCollectionsBuiltinsAssembler) {
2221   GenerateConstructor(kWeakMap, isolate()->factory()->WeakMap_string());
2222 }
2223 
TF_BUILTIN(WeakSetConstructor,WeakCollectionsBuiltinsAssembler)2224 TF_BUILTIN(WeakSetConstructor, WeakCollectionsBuiltinsAssembler) {
2225   GenerateConstructor(kWeakSet, isolate()->factory()->WeakSet_string());
2226 }
2227 
TF_BUILTIN(WeakMapLookupHashIndex,WeakCollectionsBuiltinsAssembler)2228 TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {
2229   TNode<HeapObject> table = CAST(Parameter(Descriptor::kTable));
2230   TNode<Object> key = CAST(Parameter(Descriptor::kKey));
2231 
2232   Label if_not_found(this);
2233 
2234   GotoIfNotJSReceiver(key, &if_not_found);
2235 
2236   TNode<IntPtrT> hash = LoadJSReceiverIdentityHash(key, &if_not_found);
2237   TNode<IntPtrT> capacity = LoadTableCapacity(table);
2238   TNode<IntPtrT> key_index =
2239       FindKeyIndexForKey(table, key, hash, EntryMask(capacity), &if_not_found);
2240   Return(SmiTag(ValueIndexFromKeyIndex(key_index)));
2241 
2242   BIND(&if_not_found);
2243   Return(SmiConstant(-1));
2244 }
2245 
TF_BUILTIN(WeakMapGet,WeakCollectionsBuiltinsAssembler)2246 TF_BUILTIN(WeakMapGet, WeakCollectionsBuiltinsAssembler) {
2247   Node* const receiver = Parameter(Descriptor::kReceiver);
2248   Node* const key = Parameter(Descriptor::kKey);
2249   Node* const context = Parameter(Descriptor::kContext);
2250 
2251   Label return_undefined(this);
2252 
2253   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2254                          "WeakMap.prototype.get");
2255 
2256   Node* const table = LoadTable(receiver);
2257   Node* const index =
2258       CallBuiltin(Builtins::kWeakMapLookupHashIndex, context, table, key);
2259 
2260   GotoIf(WordEqual(index, SmiConstant(-1)), &return_undefined);
2261 
2262   Return(LoadFixedArrayElement(table, SmiUntag(index)));
2263 
2264   BIND(&return_undefined);
2265   Return(UndefinedConstant());
2266 }
2267 
TF_BUILTIN(WeakMapHas,WeakCollectionsBuiltinsAssembler)2268 TF_BUILTIN(WeakMapHas, WeakCollectionsBuiltinsAssembler) {
2269   Node* const receiver = Parameter(Descriptor::kReceiver);
2270   Node* const key = Parameter(Descriptor::kKey);
2271   Node* const context = Parameter(Descriptor::kContext);
2272 
2273   Label return_false(this);
2274 
2275   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2276                          "WeakMap.prototype.has");
2277 
2278   Node* const table = LoadTable(receiver);
2279   Node* const index =
2280       CallBuiltin(Builtins::kWeakMapLookupHashIndex, context, table, key);
2281 
2282   GotoIf(WordEqual(index, SmiConstant(-1)), &return_false);
2283 
2284   Return(TrueConstant());
2285 
2286   BIND(&return_false);
2287   Return(FalseConstant());
2288 }
2289 
2290 // Helper that removes the entry with a given key from the backing store
2291 // (ObjectHashTable) of a WeakMap or WeakSet.
TF_BUILTIN(WeakCollectionDelete,WeakCollectionsBuiltinsAssembler)2292 TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
2293   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2294   TNode<HeapObject> collection = CAST(Parameter(Descriptor::kCollection));
2295   TNode<Object> key = CAST(Parameter(Descriptor::kKey));
2296 
2297   Label call_runtime(this), if_not_found(this);
2298 
2299   GotoIfNotJSReceiver(key, &if_not_found);
2300 
2301   TNode<IntPtrT> hash = LoadJSReceiverIdentityHash(key, &if_not_found);
2302   TNode<HeapObject> table = LoadTable(collection);
2303   TNode<IntPtrT> capacity = LoadTableCapacity(table);
2304   TNode<IntPtrT> key_index =
2305       FindKeyIndexForKey(table, key, hash, EntryMask(capacity), &if_not_found);
2306   TNode<IntPtrT> number_of_elements = LoadNumberOfElements(table, -1);
2307   GotoIf(ShouldShrink(capacity, number_of_elements), &call_runtime);
2308 
2309   RemoveEntry(table, key_index, number_of_elements);
2310   Return(TrueConstant());
2311 
2312   BIND(&if_not_found);
2313   Return(FalseConstant());
2314 
2315   BIND(&call_runtime);
2316   Return(CallRuntime(Runtime::kWeakCollectionDelete, context, collection, key,
2317                      SmiTag(hash)));
2318 }
2319 
2320 // Helper that sets the key and value to the backing store (ObjectHashTable) of
2321 // a WeakMap or WeakSet.
TF_BUILTIN(WeakCollectionSet,WeakCollectionsBuiltinsAssembler)2322 TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
2323   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2324   TNode<HeapObject> collection = CAST(Parameter(Descriptor::kCollection));
2325   TNode<JSReceiver> key = CAST(Parameter(Descriptor::kKey));
2326   TNode<Object> value = CAST(Parameter(Descriptor::kValue));
2327 
2328   CSA_ASSERT(this, IsJSReceiver(key));
2329 
2330   Label call_runtime(this), if_no_hash(this), if_not_found(this);
2331 
2332   TNode<HeapObject> table = LoadTable(collection);
2333   TNode<IntPtrT> capacity = LoadTableCapacity(table);
2334   TNode<IntPtrT> entry_mask = EntryMask(capacity);
2335 
2336   TVARIABLE(IntPtrT, var_hash, LoadJSReceiverIdentityHash(key, &if_no_hash));
2337   TNode<IntPtrT> key_index = FindKeyIndexForKey(table, key, var_hash.value(),
2338                                                 entry_mask, &if_not_found);
2339 
2340   StoreFixedArrayElement(table, ValueIndexFromKeyIndex(key_index), value);
2341   Return(collection);
2342 
2343   BIND(&if_no_hash);
2344   {
2345     var_hash = SmiUntag(CreateIdentityHash(key));
2346     Goto(&if_not_found);
2347   }
2348   BIND(&if_not_found);
2349   {
2350     TNode<IntPtrT> number_of_deleted = LoadNumberOfDeleted(table);
2351     TNode<IntPtrT> number_of_elements = LoadNumberOfElements(table, 1);
2352 
2353     // TODO(pwong): Port HashTable's Rehash() and EnsureCapacity() to CSA.
2354     GotoIf(Word32Or(ShouldRehash(number_of_elements, number_of_deleted),
2355                     InsufficientCapacityToAdd(capacity, number_of_elements,
2356                                               number_of_deleted)),
2357            &call_runtime);
2358 
2359     TNode<IntPtrT> insertion_key_index =
2360         FindKeyIndexForInsertion(table, var_hash.value(), entry_mask);
2361     AddEntry(table, insertion_key_index, key, value, number_of_elements);
2362     Return(collection);
2363   }
2364   BIND(&call_runtime);
2365   {
2366     CallRuntime(Runtime::kWeakCollectionSet, context, collection, key, value,
2367                 SmiTag(var_hash.value()));
2368     Return(collection);
2369   }
2370 }
2371 
TF_BUILTIN(WeakMapPrototypeDelete,CodeStubAssembler)2372 TF_BUILTIN(WeakMapPrototypeDelete, CodeStubAssembler) {
2373   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2374   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2375   TNode<Object> key = CAST(Parameter(Descriptor::kKey));
2376 
2377   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2378                          "WeakMap.prototype.delete");
2379 
2380   Return(CallBuiltin(Builtins::kWeakCollectionDelete, context, receiver, key));
2381 }
2382 
TF_BUILTIN(WeakMapPrototypeSet,WeakCollectionsBuiltinsAssembler)2383 TF_BUILTIN(WeakMapPrototypeSet, WeakCollectionsBuiltinsAssembler) {
2384   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2385   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2386   TNode<Object> key = CAST(Parameter(Descriptor::kKey));
2387   TNode<Object> value = CAST(Parameter(Descriptor::kValue));
2388 
2389   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2390                          "WeakMap.prototype.set");
2391 
2392   Label throw_invalid_key(this);
2393   GotoIfNotJSReceiver(key, &throw_invalid_key);
2394 
2395   Return(
2396       CallBuiltin(Builtins::kWeakCollectionSet, context, receiver, key, value));
2397 
2398   BIND(&throw_invalid_key);
2399   ThrowTypeError(context, MessageTemplate::kInvalidWeakMapKey, key);
2400 }
2401 
TF_BUILTIN(WeakSetPrototypeAdd,WeakCollectionsBuiltinsAssembler)2402 TF_BUILTIN(WeakSetPrototypeAdd, WeakCollectionsBuiltinsAssembler) {
2403   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2404   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2405   TNode<Object> value = CAST(Parameter(Descriptor::kValue));
2406 
2407   ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
2408                          "WeakSet.prototype.add");
2409 
2410   Label throw_invalid_value(this);
2411   GotoIfNotJSReceiver(value, &throw_invalid_value);
2412 
2413   Return(CallBuiltin(Builtins::kWeakCollectionSet, context, receiver, value,
2414                      TrueConstant()));
2415 
2416   BIND(&throw_invalid_value);
2417   ThrowTypeError(context, MessageTemplate::kInvalidWeakSetValue, value);
2418 }
2419 
TF_BUILTIN(WeakSetPrototypeDelete,CodeStubAssembler)2420 TF_BUILTIN(WeakSetPrototypeDelete, CodeStubAssembler) {
2421   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2422   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2423   TNode<Object> value = CAST(Parameter(Descriptor::kValue));
2424 
2425   ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
2426                          "WeakSet.prototype.delete");
2427 
2428   Return(
2429       CallBuiltin(Builtins::kWeakCollectionDelete, context, receiver, value));
2430 }
2431 
TF_BUILTIN(WeakSetHas,WeakCollectionsBuiltinsAssembler)2432 TF_BUILTIN(WeakSetHas, WeakCollectionsBuiltinsAssembler) {
2433   Node* const receiver = Parameter(Descriptor::kReceiver);
2434   Node* const key = Parameter(Descriptor::kKey);
2435   Node* const context = Parameter(Descriptor::kContext);
2436 
2437   Label return_false(this);
2438 
2439   ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
2440                          "WeakSet.prototype.has");
2441 
2442   Node* const table = LoadTable(receiver);
2443   Node* const index =
2444       CallBuiltin(Builtins::kWeakMapLookupHashIndex, context, table, key);
2445 
2446   GotoIf(WordEqual(index, SmiConstant(-1)), &return_false);
2447 
2448   Return(TrueConstant());
2449 
2450   BIND(&return_false);
2451   Return(FalseConstant());
2452 }
2453 
2454 }  // namespace internal
2455 }  // namespace v8
2456