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