1/*
2 * Copyright (c) 2021 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/js_object-inl.h"
17#include "pgo_profiler/pgo_profiler_layout.h"
18
19namespace panda::ecmascript {
20using PGOHandler = pgo::PGOHandler;
21void LayoutInfo::Initialize(const JSThread *thread, int num)
22{
23    SetExtraLength(num);
24    int propNum = GetPropertiesCapacity();
25    auto attr = PropertyAttributes();
26    for (int i = 0; i < propNum; i++) {
27        SetPropertyInit(thread, i, JSTaggedValue::Hole(), attr);
28    }
29}
30
31void LayoutInfo::GetAllKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray,
32                            const JSHandle<JSObject> object)
33{
34    ASSERT(end <= NumberOfElements());
35    ASSERT_PRINT(offset + end <= static_cast<int>(keyArray->GetLength()),
36                 "keyArray capacity is not enough for dictionary");
37
38    DISALLOW_GARBAGE_COLLECTION;
39    int enumKeys = 0;
40    for (int i = 0; i < end; i++) {
41        JSTaggedValue key = GetKey(i);
42        if (key.IsString()) {
43            if (IsUninitializedProperty(*object, i)) {
44                continue;
45            }
46            keyArray->Set(thread, enumKeys + offset, key);
47            enumKeys++;
48        }
49    }
50
51    if (enumKeys < end) {
52        for (int i = 0; i < end; i++) {
53            JSTaggedValue key = GetKey(i);
54            if (key.IsSymbol()) {
55                keyArray->Set(thread, enumKeys + offset, key);
56                enumKeys++;
57            }
58        }
59    }
60}
61void LayoutInfo::GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfProps, uint32_t &keyArrayEffectivelength,
62                                    TaggedArray *keyArray, const JSHandle<JSObject> object, uint32_t filter)
63{
64    ASSERT(numberOfProps <= static_cast<uint32_t>(NumberOfElements()));
65    ASSERT_PRINT(keyArrayEffectivelength + numberOfProps <= keyArray->GetLength(),
66                 "keyArray capacity is not enough for dictionary");
67
68    DISALLOW_GARBAGE_COLLECTION;
69    uint32_t enumKeys = 0;
70    for (uint32_t i = 0; i < numberOfProps; i++) {
71        JSTaggedValue key = GetKey(static_cast<int>(i));
72        if (key.IsString() && !(filter & NATIVE_KEY_SKIP_STRINGS)) {
73            if (IsUninitializedProperty(*object, i)) {
74                continue;
75            }
76            PropertyAttributes attr = GetAttr(static_cast<int>(i));
77            bool bIgnore = FilterHelper::IgnoreKeyByFilter<PropertyAttributes>(attr, filter);
78            if (bIgnore) {
79                continue;
80            }
81            keyArray->Set(thread, keyArrayEffectivelength, key);
82            keyArrayEffectivelength++;
83            enumKeys++;
84        }
85    }
86
87    if (enumKeys < numberOfProps) {
88        for (uint32_t i = 0; i < numberOfProps; i++) {
89            JSTaggedValue key = GetKey(static_cast<int>(i));
90            if (key.IsSymbol() && !(filter & NATIVE_KEY_SKIP_SYMBOLS)) {
91                PropertyAttributes attr = GetAttr(static_cast<int>(i));
92                bool bIgnore = FilterHelper::IgnoreKeyByFilter<PropertyAttributes>(attr, filter);
93                if (bIgnore) {
94                    continue;
95                }
96                keyArray->Set(thread, keyArrayEffectivelength, key);
97                keyArrayEffectivelength++;
98            }
99        }
100    }
101}
102
103void LayoutInfo::GetAllKeysForSerialization(int end, std::vector<JSTaggedValue> &keyVector)
104{
105    ASSERT(end <= NumberOfElements());
106    for (int i = 0; i < end; i++) {
107        JSTaggedValue key = GetKey(i);
108        if (key.IsString() || key.IsSymbol()) {
109            keyVector.emplace_back(key);
110        }
111    }
112}
113
114std::pair<uint32_t, uint32_t> LayoutInfo::GetNumOfEnumKeys(int end, const JSObject *object) const
115{
116    ASSERT(end <= NumberOfElements());
117    uint32_t enumKeys = 0;
118    uint32_t shadowKeys = 0;
119    for (int i = 0; i < end; i++) {
120        JSTaggedValue key = GetKey(i);
121        if (!key.IsString()) {
122            continue;
123        }
124        if (IsUninitializedProperty(object, i)) {
125            continue;
126        }
127        if (GetAttr(i).IsEnumerable()) {
128            enumKeys++;
129        } else {
130            shadowKeys++;
131        }
132    }
133    return std::make_pair(enumKeys, shadowKeys);
134}
135
136void LayoutInfo::GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray,
137                                uint32_t *keys, JSHandle<TaggedQueue> shadowQueue, const JSHandle<JSObject> object,
138                                int32_t lastLength)
139{
140    ASSERT(end <= NumberOfElements());
141    ASSERT_PRINT(offset <= static_cast<int>(keyArray->GetLength()),
142                 "keyArray capacity is not enough for dictionary");
143    JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
144    int enumKeys = 0;
145    for (int i = 0; i < end; i++) {
146        keyHandle.Update(GetKey(i));
147        if (!keyHandle->IsString()) {
148            continue;
149        }
150        if (IsUninitializedProperty(*object, i)) {
151            continue;
152        }
153        if (GetAttr(i).IsEnumerable()) {
154            bool isDuplicated = JSObject::IsDepulicateKeys(thread, keyArray, lastLength, shadowQueue, keyHandle);
155            if (isDuplicated) {
156                continue;
157            }
158            keyArray->Set(thread, enumKeys + offset, keyHandle);
159            enumKeys++;
160        } else {
161            TaggedQueue::PushFixedQueue(thread, shadowQueue, keyHandle);
162        }
163    }
164    *keys += enumKeys;
165}
166
167void LayoutInfo::GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray,
168                                uint32_t *keys, const JSHandle<JSObject> object)
169{
170    ASSERT(end <= NumberOfElements());
171    ASSERT_PRINT(offset <= static_cast<int>(keyArray->GetLength()),
172                 "keyArray capacity is not enough for dictionary");
173    JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
174    int enumKeys = 0;
175    for (int i = 0; i < end; i++) {
176        keyHandle.Update(GetKey(i));
177        if (keyHandle->IsString() && GetAttr(i).IsEnumerable()) {
178            if (IsUninitializedProperty(*object, i)) {
179                continue;
180            }
181            keyArray->Set(thread, enumKeys + offset, keyHandle);
182            enumKeys++;
183        }
184    }
185    *keys += enumKeys;
186}
187
188bool LayoutInfo::IsUninitializedProperty(const JSObject *object, uint32_t index) const
189{
190    PropertyAttributes attr = GetAttr(index);
191    if (!attr.IsInlinedProps()) {
192        return false;
193    }
194
195    JSTaggedValue val = object->GetPropertyInlinedPropsWithRep(attr.GetOffset(), attr);
196    return val.IsHole();
197}
198
199CString LayoutInfo::GetSymbolKeyString(JSTaggedValue key)
200{
201    auto symbol = JSSymbol::Cast(key);
202    if (!symbol->HasId()) {
203        return "";
204    }
205    auto id = symbol->GetPrivateId();
206    auto symbolDesc = symbol->GetDescription();
207    if (symbolDesc.IsUndefined()) {
208        return ToCString(id);
209    }
210    if (!symbolDesc.IsString()) {
211        return "";
212    }
213    CString str = EcmaStringAccessor(symbolDesc).ToCString();
214    if (str != "method") {
215        return "";
216    }
217    return str + '_' + ToCString(id);
218}
219
220void LayoutInfo::DumpFieldIndexByPGO(int index, pgo::HClassLayoutDesc* desc)
221{
222    auto key = GetKey(index);
223    auto attr = GetAttr(index);
224    SetIsPGODumped(index);
225    TrackType type = attr.GetTrackType();
226    int propertyMeta = attr.GetPropertyMetaData();
227    if (key.IsString()) {
228        auto keyString = EcmaStringAccessor(key).ToCString();
229        desc->InsertKeyAndDesc(keyString, PGOHandler(type, propertyMeta, false));
230    } else if (key.IsSymbol()) {
231        auto keyString = GetSymbolKeyString(key);
232        if (keyString.empty()) {
233            return;
234        }
235        desc->InsertKeyAndDesc(keyString, PGOHandler(type, propertyMeta, true));
236    }
237}
238
239bool LayoutInfo::UpdateFieldIndexByPGO(int index, pgo::HClassLayoutDesc* desc)
240{
241    auto key = GetKey(index);
242    auto attr = GetAttr(index);
243    if (attr.IsPGODumped()) {
244        return true;
245    }
246    SetIsPGODumped(index);
247    TrackType type = attr.GetTrackType();
248    int propertyMeta = attr.GetPropertyMetaData();
249    if (key.IsString()) {
250        auto keyString = EcmaStringAccessor(key).ToCString();
251        return desc->UpdateKeyAndDesc(keyString, PGOHandler(type, propertyMeta, false));
252    } else if (key.IsSymbol()) {
253        auto keyString = GetSymbolKeyString(key);
254        if (keyString.empty()) {
255            return false;
256        }
257        return desc->UpdateKeyAndDesc(keyString, PGOHandler(type, propertyMeta, true));
258    }
259    return false;
260}
261}  // namespace panda::ecmascript
262