1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/compiler/builtins/linked_hashtable_stub_builder.h"
17
18#include "ecmascript/compiler/builtins/builtins_stubs.h"
19#include "ecmascript/compiler/call_stub_builder.h"
20#include "ecmascript/compiler/hash_stub_builder.h"
21#include "ecmascript/compiler/new_object_stub_builder.h"
22#include "ecmascript/linked_hash_table.h"
23#include "ecmascript/js_set.h"
24#include "ecmascript/js_map.h"
25
26namespace panda::ecmascript::kungfu {
27template <typename LinkedHashTableType, typename LinkedHashTableObject>
28void LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Rehash(
29    GateRef linkedTable, GateRef newTable)
30{
31    auto env = GetEnvironment();
32    Label entryLabel(env);
33    env->SubCfgEntry(&entryLabel);
34
35    GateRef numberOfAllElements = Int32Add(GetNumberOfElements(linkedTable),
36        GetNumberOfDeletedElements(linkedTable));
37
38    DEFVARIABLE(desEntry, VariableType::INT32(), Int32(0));
39    DEFVARIABLE(currentDeletedElements, VariableType::INT32(), Int32(0));
40    SetNextTable(linkedTable, newTable);
41
42    Label loopHead(env);
43    Label loopEnd(env);
44    Label next(env);
45    Label loopExit(env);
46
47    DEFVARIABLE(i, VariableType::INT32(), Int32(0));
48    Jump(&loopHead);
49    LoopBegin(&loopHead);
50    {
51        BRANCH(Int32LessThan(*i, numberOfAllElements), &next, &loopExit);
52        Bind(&next);
53
54        GateRef fromIndex = EntryToIndex(linkedTable, *i);
55        DEFVARIABLE(key, VariableType::JS_ANY(), GetElement(linkedTable, fromIndex));
56        Label hole(env);
57        Label notHole(env);
58        BRANCH(TaggedIsHole(*key), &hole, &notHole);
59        Bind(&hole);
60        {
61            currentDeletedElements = Int32Add(*currentDeletedElements, Int32(1));
62            SetDeletedNum(linkedTable, *i, *currentDeletedElements);
63            Jump(&loopEnd);
64        }
65        Bind(&notHole);
66        {
67            Label weak(env);
68            Label notWeak(env);
69            BRANCH(TaggedIsWeak(*key), &weak, &notWeak);
70            Bind(&weak);
71            {
72                key = RemoveTaggedWeakTag(*key);
73                Jump(&notWeak);
74            }
75            Bind(&notWeak);
76
77            HashStubBuilder hashBuilder(this, glue_);
78            GateRef hash = hashBuilder.GetHash(*key);
79            GateRef bucket = HashToBucket(newTable, hash);
80            InsertNewEntry(newTable, bucket, *desEntry);
81            GateRef desIndex = EntryToIndex(newTable, *desEntry);
82
83            Label loopHead1(env);
84            Label loopEnd1(env);
85            Label next1(env);
86            Label loopExit1(env);
87            DEFVARIABLE(j, VariableType::INT32(), Int32(0));
88            Jump(&loopHead1);
89            LoopBegin(&loopHead1);
90            {
91                BRANCH(Int32LessThan(*j, Int32(LinkedHashTableObject::ENTRY_SIZE)), &next1, &loopExit1);
92                Bind(&next1);
93                GateRef ele = GetElement(linkedTable, Int32Add(fromIndex, *j));
94                SetElement(newTable, Int32Add(desIndex, *j), ele);
95                Jump(&loopEnd1);
96            }
97            Bind(&loopEnd1);
98            j = Int32Add(*j, Int32(1));
99            LoopEnd(&loopHead1);
100            Bind(&loopExit1);
101            desEntry = Int32Add(*desEntry, Int32(1));
102            Jump(&loopEnd);
103        }
104    }
105    Bind(&loopEnd);
106    i = Int32Add(*i, Int32(1));
107    LoopEnd(&loopHead, env, glue_);
108    Bind(&loopExit);
109
110    SetNumberOfElements(newTable, GetNumberOfElements(linkedTable));
111    SetNumberOfDeletedElements(newTable, Int32(0));
112    env->SubCfgExit();
113}
114
115template <typename LinkedHashTableType, typename LinkedHashTableObject>
116GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::GrowCapacity(
117    GateRef linkedTable, GateRef numberOfAddedElements)
118{
119    auto env = GetEnvironment();
120    Label entryLabel(env);
121    env->SubCfgEntry(&entryLabel);
122    Label exit(env);
123    DEFVARIABLE(res, VariableType::JS_ANY(), linkedTable);
124
125    GateRef hasSufficient = HasSufficientCapacity(linkedTable, numberOfAddedElements);
126    Label grow(env);
127    BRANCH(hasSufficient, &exit, &grow);
128    Bind(&grow);
129    {
130        GateRef newCapacity = ComputeCapacity(Int32Add(GetNumberOfElements(linkedTable), numberOfAddedElements));
131        GateRef newTable = Create(newCapacity);
132        Rehash(linkedTable, newTable);
133        res = newTable;
134        Jump(&exit);
135    }
136    Bind(&exit);
137    auto ret = *res;
138    env->SubCfgExit();
139    return ret;
140}
141
142template <typename LinkedHashTableType, typename LinkedHashTableObject>
143GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::ComputeCapacity(
144    GateRef atLeastSpaceFor)
145{
146    if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashMap>) {
147        return TaggedGetInt(CallRuntime(glue_, RTSTUB_ID(LinkedHashMapComputeCapacity), {
148        IntToTaggedInt(atLeastSpaceFor) }));
149    } else {
150        return TaggedGetInt(CallRuntime(glue_, RTSTUB_ID(LinkedHashSetComputeCapacity), {
151        IntToTaggedInt(atLeastSpaceFor) }));
152    }
153}
154
155template <typename LinkedHashTableType, typename LinkedHashTableObject>
156void LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::RemoveEntry(
157    GateRef linkedTable, GateRef entry)
158{
159    auto env = GetEnvironment();
160    Label entryLabel(env);
161    Label exit(env);
162    env->SubCfgEntry(&entryLabel);
163    DEFVARIABLE(i, VariableType::INT32(), Int32(0));
164
165    Label loopHead(env);
166    Label loopEnd(env);
167    Label next(env);
168    Label loopExit(env);
169    GateRef index = EntryToIndex(linkedTable, entry);
170    Jump(&loopHead);
171    LoopBegin(&loopHead);
172    {
173        BRANCH(Int32LessThan(*i, Int32(LinkedHashTableObject::ENTRY_SIZE)), &next, &loopExit);
174        Bind(&next);
175
176        GateRef idx = Int32Add(index, *i);
177        SetElement(linkedTable, idx, Hole());
178        Jump(&loopEnd);
179    }
180    Bind(&loopEnd);
181    i = Int32Add(*i, Int32(1));
182    LoopEnd(&loopHead, env, glue_);
183    Bind(&loopExit);
184
185    GateRef newNofe = Int32Sub(GetNumberOfElements(linkedTable), Int32(1));
186    SetNumberOfElements(linkedTable, newNofe);
187    GateRef newNofd = Int32Add(GetNumberOfDeletedElements(linkedTable), Int32(1));
188    SetNumberOfDeletedElements(linkedTable, newNofd);
189    env->SubCfgExit();
190}
191
192template <typename LinkedHashTableType, typename LinkedHashTableObject>
193GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::HasSufficientCapacity(
194    GateRef linkedTable, GateRef numOfAddElements)
195{
196    auto env = GetEnvironment();
197    Label entryLabel(env);
198    Label exit(env);
199    env->SubCfgEntry(&entryLabel);
200    DEFVARIABLE(res, VariableType::BOOL(), False());
201
202    GateRef numberOfElements = GetNumberOfElements(linkedTable);
203    GateRef numOfDelElements = GetNumberOfDeletedElements(linkedTable);
204    GateRef nof = Int32Add(numberOfElements, numOfAddElements);
205    GateRef capacity = GetCapacity(linkedTable);
206    GateRef isLess = LogicAndBuilder(env)
207        .And(Int32LessThan(nof, capacity))
208        .And(Int32LessThanOrEqual(numOfDelElements, Int32Div(Int32Sub(capacity, nof), Int32(2))))
209        .Done();
210    Label lessLable(env);
211    BRANCH(isLess, &lessLable, &exit);
212    Bind(&lessLable);
213    {
214        Label need(env);
215        BRANCH(Int32LessThanOrEqual(Int32Add(nof, Int32Div(nof, Int32(2))), capacity), &need, &exit);  // 2: half
216        Bind(&need);
217        {
218            res = True();
219            Jump(&exit);
220        }
221    }
222    Bind(&exit);
223    auto ret = *res;
224    env->SubCfgExit();
225    return ret;
226}
227
228template <typename LinkedHashTableType, typename LinkedHashTableObject>
229GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::HashObjectIsMatch(
230    GateRef key, GateRef other)
231{
232    return SameValueZero(glue_, key, other);
233}
234
235template <typename LinkedHashTableType, typename LinkedHashTableObject>
236GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::FindElement(
237    GateRef linkedTable, GateRef key, GateRef hash)
238{
239    auto env = GetEnvironment();
240    Label entryLabel(env);
241    env->SubCfgEntry(&entryLabel);
242
243    DEFVARIABLE(res, VariableType::INT32(), Int32(-1));
244    Label exit(env);
245    Label isKey(env);
246    BRANCH(IsKey(key), &isKey, &exit);
247    Bind(&isKey);
248    {
249        GateRef bucket = HashToBucket(linkedTable, hash);
250        GateRef index = BucketToIndex(bucket);
251        DEFVARIABLE(entry, VariableType::JS_ANY(), GetElement(linkedTable, index));
252        Label loopHead(env);
253        Label loopEnd(env);
254        Label next(env);
255        Label loopExit(env);
256
257        Jump(&loopHead);
258        LoopBegin(&loopHead);
259        {
260            BRANCH(TaggedIsHole(*entry), &loopExit, &next);
261            Bind(&next);
262
263            DEFVARIABLE(element, VariableType::JS_ANY(), GetKey(linkedTable, TaggedGetInt(*entry)));
264            Label notHole(env);
265            BRANCH(TaggedIsHole(*element), &loopEnd, &notHole);
266            Bind(&notHole);
267            {
268                Label weak(env);
269                Label notWeak(env);
270                BRANCH(TaggedIsWeak(*element), &weak, &notWeak);
271                Bind(&weak);
272                {
273                    element = RemoveTaggedWeakTag(*element);
274                    Jump(&notWeak);
275                }
276                Bind(&notWeak);
277                Label match(env);
278                BRANCH(HashObjectIsMatch(key, *element), &match, &loopEnd);
279                Bind(&match);
280                {
281                    res = TaggedGetInt(*entry);
282                    Jump(&loopExit);
283                }
284            }
285        }
286        Bind(&loopEnd);
287        entry = GetNextEntry(linkedTable, TaggedGetInt(*entry));
288        LoopEnd(&loopHead, env, glue_);
289        Bind(&loopExit);
290        Jump(&exit);
291    }
292    Bind(&exit);
293    auto ret = *res;
294    env->SubCfgExit();
295    return ret;
296}
297
298template <typename LinkedHashTableType, typename LinkedHashTableObject>
299GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::GetDeletedElementsAt(
300    GateRef linkedTable, GateRef entry)
301{
302    auto env = GetEnvironment();
303    Label entryLabel(env);
304    env->SubCfgEntry(&entryLabel);
305    Label exit(env);
306    DEFVARIABLE(res, VariableType::INT32(), Int32(0));
307    DEFVARIABLE(currentEntry, VariableType::INT32(), Int32Sub(entry, Int32(1)));
308    Label loopHead(env);
309    Label loopEnd(env);
310    Label next(env);
311    Label loopExit(env);
312
313    Jump(&loopHead);
314    LoopBegin(&loopHead);
315    {
316        BRANCH(Int32GreaterThanOrEqual(*currentEntry, Int32(0)), &next, &loopExit);
317        Bind(&next);
318        GateRef key = GetKey(linkedTable, *currentEntry);
319        Label hole(env);
320        BRANCH(TaggedIsHole(key), &hole, &loopEnd);
321        Bind(&hole);
322        {
323            GateRef deletedNum = GetDeletedNum(linkedTable, *currentEntry);
324            res = deletedNum;
325            Jump(&exit);
326        }
327    }
328    Bind(&loopEnd);
329    currentEntry = Int32Sub(*currentEntry, Int32(1));
330    LoopEnd(&loopHead, env, glue_);
331    Bind(&loopExit);
332    Jump(&exit);
333    Bind(&exit);
334    auto ret = *res;
335    env->SubCfgExit();
336    return ret;
337}
338
339template<typename LinkedHashTableType, typename LinkedHashTableObject>
340GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Create(GateRef numberOfElements)
341{
342    auto env = GetEnvironment();
343    Label entry(env);
344    env->SubCfgEntry(&entry);
345    Label exit(env);
346
347    // new LinkedHashTable
348    GateRef length = CalNewTaggedArrayLength(numberOfElements);
349    NewObjectStubBuilder newBuilder(this);
350    GateRef array = newBuilder.NewTaggedArray(glue_, length);
351
352    Label noException(env);
353    BRANCH(TaggedIsException(array), &exit, &noException);
354    Bind(&noException);
355    {
356        // SetNumberOfElements
357        SetNumberOfElements(array, Int32(0));
358        // SetNumberOfDeletedElements
359        SetNumberOfDeletedElements(array, Int32(0));
360        // SetCapacity
361        SetCapacity(array, numberOfElements);
362        Jump(&exit);
363    }
364    Bind(&exit);
365    env->SubCfgExit();
366    return array;
367}
368
369template<typename LinkedHashTableType, typename LinkedHashTableObject>
370GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Clear(GateRef linkedTable)
371{
372    auto env = GetEnvironment();
373    Label entry(env);
374    env->SubCfgEntry(&entry);
375    Label exit(env);
376    Label setLinked(env);
377    DEFVARIABLE(result, VariableType::JS_ANY(), linkedTable);
378
379    Label reuseExistingTable(env);
380    Label createNewTable(env);
381    GateRef cap = GetCapacity(linkedTable);
382    GateRef minCapacity = Int32(LinkedHashTableType::MIN_CAPACITY);
383    BRANCH(Equal(cap, minCapacity), &reuseExistingTable, &createNewTable);
384
385    Bind(&reuseExistingTable);
386    size_t length = static_cast<size_t>(LinkedHashTableType::GetLengthOfTable(LinkedHashTableType::MIN_CAPACITY));
387    for (size_t i = LinkedHashTableType::ELEMENTS_START_INDEX; i < length; ++i) {
388        SetValueToTaggedArray(VariableType::JS_NOT_POINTER(), glue_, linkedTable, Int32(i), Hole());
389    }
390    GateRef numberOfElements = GetNumberOfElements(linkedTable);
391    GateRef numberOfDeletedElements = GetNumberOfDeletedElements(linkedTable);
392    SetNumberOfElements(linkedTable, Int32(0));
393    SetNumberOfDeletedElements(linkedTable, Int32Add(numberOfElements, numberOfDeletedElements));
394    Jump(&exit);
395
396    Bind(&createNewTable);
397    GateRef newTable = Create(minCapacity);
398    result = newTable;
399    Label noException(env);
400    BRANCH(TaggedIsException(newTable), &exit, &noException);
401    Bind(&noException);
402
403    Label capGreaterZero(env);
404    BRANCH(Int32GreaterThan(cap, Int32(0)), &capGreaterZero, &exit);
405    Bind(&capGreaterZero);
406    {
407        // NextTable
408        SetNextTable(linkedTable, newTable);
409        // SetNumberOfDeletedElements
410        SetNumberOfDeletedElements(linkedTable, Int32(-1));
411        Jump(&exit);
412    }
413
414    Bind(&exit);
415    GateRef res = *result;
416    env->SubCfgExit();
417    return res;
418}
419
420template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::Clear(GateRef);
421template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::Clear(GateRef);
422
423template <typename LinkedHashTableType, typename LinkedHashTableObject>
424GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::ForEach(GateRef thisValue,
425    GateRef srcLinkedTable, GateRef callbackFnHandle, GateRef thisArg)
426{
427    auto env = GetEnvironment();
428    Label entry(env);
429    env->SubCfgEntry(&entry);
430    Label exit(env);
431    DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
432    DEFVARIABLE(linkedTable, VariableType::JS_ANY(), srcLinkedTable);
433
434    GateRef numberOfElements = GetNumberOfElements(*linkedTable);
435    GateRef numberOfDeletedElements = GetNumberOfDeletedElements(*linkedTable);
436    GateRef tmpTotalElements = Int32Add(numberOfElements, numberOfDeletedElements);
437    DEFVARIABLE(totalElements, VariableType::INT32(), tmpTotalElements);
438    DEFVARIABLE(index, VariableType::INT32(), Int32(0));
439
440    Label loopHead(env);
441    Label loopEnd(env);
442    Label next(env);
443    Label loopExit(env);
444    Jump(&loopHead);
445    LoopBegin(&loopHead);
446    {
447        BRANCH(Int32LessThan(*index, *totalElements), &next, &loopExit);
448        Bind(&next);
449        GateRef valueIndex = *index;
450
451        GateRef key = GetKey(*linkedTable, *index);
452        index = Int32Add(*index, Int32(1));
453        Label keyNotHole(env);
454        BRANCH(TaggedIsHole(key), &loopEnd, &keyNotHole);
455        Bind(&keyNotHole);
456
457        GateRef value = key;
458        if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashMap>) {
459            value = GetValue(*linkedTable, valueIndex);
460        }
461        Label hasException(env);
462        Label notHasException(env);
463        JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
464        callArgs.callThisArg3WithReturnArgs = { thisArg, value, key, thisValue };
465        CallStubBuilder callBuilder(this, glue_, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
466            Circuit::NullGate(), callArgs, ProfileOperation(), false);
467        GateRef retValue = callBuilder.JSCallDispatch();
468        BRANCH(HasPendingException(glue_), &hasException, &notHasException);
469        Bind(&hasException);
470        {
471            res = retValue;
472            Jump(&exit);
473        }
474        Bind(&notHasException);
475        {
476            // Maybe add or delete, get next table
477            GateRef tmpNextTable = GetNextTable(*linkedTable);
478            DEFVARIABLE(nextTable, VariableType::JS_ANY(), tmpNextTable);
479            Label loopHead1(env);
480            Label loopEnd1(env);
481            Label next1(env);
482            Label loopExit1(env);
483            Jump(&loopHead1);
484            LoopBegin(&loopHead1);
485            {
486                BRANCH(TaggedIsHole(*nextTable), &loopExit1, &next1);
487                Bind(&next1);
488                GateRef deleted = GetDeletedElementsAt(*linkedTable, *index);
489                index = Int32Sub(*index, deleted);
490                linkedTable = *nextTable;
491                nextTable = GetNextTable(*linkedTable);
492                Jump(&loopEnd1);
493            }
494            Bind(&loopEnd1);
495            LoopEnd(&loopHead1);
496            Bind(&loopExit1);
497            // update totalElements
498            GateRef numberOfEle = GetNumberOfElements(*linkedTable);
499            GateRef numberOfDeletedEle = GetNumberOfDeletedElements(*linkedTable);
500            totalElements = Int32Add(numberOfEle, numberOfDeletedEle);
501            Jump(&loopEnd);
502        }
503    }
504    Bind(&loopEnd);
505    LoopEnd(&loopHead);
506    Bind(&loopExit);
507    Jump(&exit);
508
509    Bind(&exit);
510    env->SubCfgExit();
511    return *res;
512}
513
514template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::ForEach(GateRef thisValue,
515    GateRef linkedTable, GateRef callbackFnHandle, GateRef thisArg);
516template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::ForEach(GateRef thisValue,
517    GateRef linkedTable, GateRef callbackFnHandle, GateRef thisArg);
518
519template <typename LinkedHashTableType, typename LinkedHashTableObject>
520GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Insert(
521    GateRef linkedTable, GateRef key, GateRef value)
522{
523    auto env = GetEnvironment();
524    Label cfgEntry(env);
525    env->SubCfgEntry(&cfgEntry);
526    Label exit(env);
527    DEFVARIABLE(res, VariableType::JS_ANY(), linkedTable);
528    HashStubBuilder hashBuilder(this, glue_);
529    GateRef hash = hashBuilder.GetHash(key);
530    GateRef entry = FindElement(linkedTable, key, hash);
531    Label findEntry(env);
532    Label notFind(env);
533    BRANCH(Int32Equal(entry, Int32(-1)), &notFind, &findEntry);
534    Bind(&findEntry);
535    {
536        SetValue(linkedTable, entry, value);
537        Jump(&exit);
538    }
539    Bind(&notFind);
540    {
541        GateRef newTable = GrowCapacity(linkedTable, Int32(1));
542        res = newTable;
543        GateRef bucket = HashToBucket(newTable, hash);
544        GateRef numberOfElements = GetNumberOfElements(newTable);
545
546        GateRef newEntry = Int32Add(numberOfElements, GetNumberOfDeletedElements(newTable));
547        InsertNewEntry(newTable, bucket, newEntry);
548        SetKey(newTable, newEntry, key);
549        SetValue(newTable, newEntry, value);
550        GateRef newNumberOfElements = Int32Add(numberOfElements, Int32(1));
551        SetNumberOfElements(newTable, newNumberOfElements);
552        Jump(&exit);
553    }
554
555    Bind(&exit);
556    auto ret = *res;
557    env->SubCfgExit();
558    return ret;
559}
560
561template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::Insert(
562    GateRef linkedTable, GateRef key, GateRef value);
563template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::Insert(
564    GateRef linkedTable, GateRef key, GateRef value);
565
566template <typename LinkedHashTableType, typename LinkedHashTableObject>
567GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Delete(
568    GateRef linkedTable, GateRef key)
569{
570    auto env = GetEnvironment();
571    Label cfgEntry(env);
572    env->SubCfgEntry(&cfgEntry);
573    Label exit(env);
574    DEFVARIABLE(res, VariableType::BOOL(), False());
575    HashStubBuilder hashBuilder(this, glue_);
576    GateRef hash = hashBuilder.GetHash(key);
577    GateRef entry = FindElement(linkedTable, key, hash);
578    Label findEntry(env);
579    BRANCH(Int32Equal(entry, Int32(-1)), &exit, &findEntry);
580    Bind(&findEntry);
581    {
582        RemoveEntry(linkedTable, entry);
583        res = True();
584        Jump(&exit);
585    }
586
587    Bind(&exit);
588    auto ret = *res;
589    env->SubCfgExit();
590    return ret;
591}
592
593template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::Delete(
594    GateRef linkedTable, GateRef key);
595template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::Delete(
596    GateRef linkedTable, GateRef key);
597
598template <typename LinkedHashTableType, typename LinkedHashTableObject>
599GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Has(
600    GateRef linkedTable, GateRef key)
601{
602    auto env = GetEnvironment();
603    Label cfgEntry(env);
604    env->SubCfgEntry(&cfgEntry);
605    HashStubBuilder hashBuilder(this, glue_);
606    GateRef hash = hashBuilder.GetHash(key);
607    GateRef entry = FindElement(linkedTable, key, hash);
608    GateRef ret = Int32NotEqual(entry, Int32(-1));
609    env->SubCfgExit();
610    return ret;
611}
612
613template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::Has(
614    GateRef linkedTable, GateRef key);
615template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::Has(
616    GateRef linkedTable, GateRef key);
617
618template <typename LinkedHashTableType, typename LinkedHashTableObject>
619void LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::StoreHashTableToNewObject(
620    GateRef newTargetHClass, Variable& returnValue)
621{
622    NewObjectStubBuilder newBuilder(this);
623    GateRef res = newBuilder.NewJSObject(glue_, newTargetHClass);
624    returnValue.WriteVariable(res);
625    GateRef table;
626    if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashMap>) {
627        table = Create(Int32(LinkedHashMap::MIN_CAPACITY));
628        Store(VariableType::JS_ANY(), glue_, *returnValue, IntPtr(JSMap::LINKED_MAP_OFFSET), table);
629    } else if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashSet>) {
630        table = Create(Int32(LinkedHashSet::MIN_CAPACITY));
631        Store(VariableType::JS_ANY(), glue_, *returnValue, IntPtr(JSSet::LINKED_SET_OFFSET), table);
632    }
633}
634
635template void LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::StoreHashTableToNewObject(
636    GateRef newTargetHClass, Variable& returnValue);
637template void LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::StoreHashTableToNewObject(
638    GateRef newTargetHClass, Variable& returnValue);
639
640template <typename LinkedHashTableType, typename LinkedHashTableObject>
641void LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::GenMapSetConstructor(
642    GateRef nativeCode, GateRef func, GateRef newTarget, GateRef thisValue, GateRef numArgs, GateRef arg0, GateRef argv)
643{
644    auto env = GetEnvironment();
645    DEFVARIABLE(returnValue, VariableType::JS_ANY(), Undefined());
646
647    Label newTargetObject(env);
648    Label newTargetNotObject(env);
649    Label newTargetFunction(env);
650    Label slowPath(env);
651    Label exit(env);
652
653    // 1.If NewTarget is undefined, throw a TypeError exception
654    BRANCH(TaggedIsHeapObject(newTarget), &newTargetObject, &newTargetNotObject);
655
656    Bind(&newTargetObject);
657    BRANCH(IsJSFunction(newTarget), &newTargetFunction, &slowPath);
658
659    Bind(&newTargetFunction);
660    Label fastGetHClass(env);
661    Label intialHClassIsHClass(env);
662    GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit()));
663    GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue_, glueGlobalEnvOffset);
664    GateRef mapOrSetFunc;
665    if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashMap>) {
666        mapOrSetFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv,
667                                         GlobalEnv::BUILTINS_MAP_FUNCTION_INDEX);
668    } else if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashSet>) {
669        mapOrSetFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv,
670                                         GlobalEnv::BUILTINS_SET_FUNCTION_INDEX);
671    }
672    GateRef newTargetHClass = Load(VariableType::JS_ANY(), newTarget, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET));
673    BRANCH(LogicAndBuilder(env).And(Equal(mapOrSetFunc, newTarget)).And(IsJSHClass(newTargetHClass)).Done(),
674        &fastGetHClass, &slowPath);
675
676    Bind(&fastGetHClass);
677    Label isUndefinedOrNull(env);
678    BRANCH(TaggedIsUndefinedOrNull(arg0), &isUndefinedOrNull, &slowPath);
679
680    Bind(&isUndefinedOrNull);
681    StoreHashTableToNewObject(newTargetHClass, returnValue);
682    Jump(&exit);
683
684    Bind(&newTargetNotObject);
685    GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(InvalidNewTarget));
686    CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) });
687    returnValue = Exception();
688    Jump(&exit);
689
690    Bind(&slowPath);
691    returnValue = CallBuiltinRuntimeWithNewTarget(glue_, {glue_, nativeCode, func, thisValue,
692        numArgs, argv, newTarget});
693    Jump(&exit);
694
695    Bind(&exit);
696    Return(*returnValue);
697}
698
699template void LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::GenMapSetConstructor(
700    GateRef nativeCode, GateRef func, GateRef newTarget, GateRef thisValue, GateRef numArgs,
701    GateRef arg0, GateRef argv);
702template void LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::GenMapSetConstructor(
703    GateRef nativeCode, GateRef func, GateRef newTarget, GateRef thisValue, GateRef numArgs,
704    GateRef arg0, GateRef argv);
705
706template <typename LinkedHashTableType, typename LinkedHashTableObject>
707GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::GetLinked(GateRef jsThis)
708{
709    GateRef linkedTableOffset = GetLinkedOffset();
710    return Load(VariableType::JS_ANY(), jsThis, linkedTableOffset);
711}
712
713template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::GetLinked(
714    GateRef jsThis);
715template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::GetLinked(
716    GateRef jsThis);
717
718template <typename LinkedHashTableType, typename LinkedHashTableObject>
719void LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::SetLinked(GateRef jsThis, GateRef newTable)
720{
721    GateRef linkedTableOffset = GetLinkedOffset();
722    Store(VariableType::JS_ANY(), glue_, jsThis, linkedTableOffset, newTable);
723}
724
725template void LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::SetLinked(
726    GateRef jsThis, GateRef newTable);
727template void LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::SetLinked(
728    GateRef jsThis, GateRef newTable);
729
730template <typename LinkedHashTableType, typename LinkedHashTableObject>
731GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::Get(
732    GateRef linkedTable, GateRef key)
733{
734    auto env = GetEnvironment();
735    Label cfgEntry(env);
736    env->SubCfgEntry(&cfgEntry);
737    Label exit(env);
738    DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
739    HashStubBuilder hashBuilder(this, glue_);
740    GateRef hash = hashBuilder.GetHash(key);
741    GateRef entry = FindElement(linkedTable, key, hash);
742    Label findEntry(env);
743    Branch(Int32Equal(entry, Int32(-1)), &exit, &findEntry);
744    Bind(&findEntry);
745    {
746        res = GetValue(linkedTable, entry);
747        Jump(&exit);
748    }
749
750    Bind(&exit);
751    auto ret = *res;
752    env->SubCfgExit();
753    return ret;
754}
755
756template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::Get(
757    GateRef linkedTable, GateRef key);
758
759template <typename LinkedHashTableType, typename LinkedHashTableObject>
760GateRef LinkedHashTableStubBuilder<LinkedHashTableType, LinkedHashTableObject>::GetLinkedOffset()
761{
762    int32_t linkedTableOffset = 0;
763    if constexpr (std::is_same_v<LinkedHashTableType, LinkedHashMap>) {
764        linkedTableOffset = JSMap::LINKED_MAP_OFFSET;
765    } else {
766        static_assert(std::is_same_v<LinkedHashTableType, LinkedHashSet>);
767        linkedTableOffset = JSSet::LINKED_SET_OFFSET;
768    }
769    return IntPtr(linkedTableOffset);
770}
771
772template GateRef LinkedHashTableStubBuilder<LinkedHashMap, LinkedHashMapObject>::GetLinkedOffset();
773template GateRef LinkedHashTableStubBuilder<LinkedHashSet, LinkedHashSetObject>::GetLinkedOffset();
774
775}  // namespace panda::ecmascript::kungfu
776