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