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, ¬_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(¬_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, ¬_found);
1305
1306 BIND(¬_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, ¬_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(¬_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, ¬_found);
1468
1469 BIND(¬_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, ¬_found);
1705
1706 BIND(&if_key_smi);
1707 {
1708 FindOrderedHashTableEntryForSmiKey<OrderedHashSet>(
1709 table, key, &entry_start_position, &entry_found, ¬_found);
1710 }
1711
1712 BIND(&if_key_string);
1713 {
1714 FindOrderedHashTableEntryForStringKey<OrderedHashSet>(
1715 context, table, key, &entry_start_position, &entry_found, ¬_found);
1716 }
1717
1718 BIND(&if_key_heap_number);
1719 {
1720 FindOrderedHashTableEntryForHeapNumberKey<OrderedHashSet>(
1721 context, table, key, &entry_start_position, &entry_found, ¬_found);
1722 }
1723
1724 BIND(&if_key_bigint);
1725 {
1726 FindOrderedHashTableEntryForBigIntKey<OrderedHashSet>(
1727 context, table, key, &entry_start_position, &entry_found, ¬_found);
1728 }
1729
1730 BIND(&entry_found);
1731 Return(TrueConstant());
1732
1733 BIND(¬_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, ¬_found);
1942
1943 BIND(&entry_found);
1944 Return(SmiTag(entry_start_position.value()));
1945
1946 BIND(¬_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