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 
19 namespace panda::ecmascript {
20 using PGOHandler = pgo::PGOHandler;
Initialize(const JSThread *thread, int num)21 void 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 
GetAllKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, const JSHandle<JSObject> object)31 void 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 }
GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfProps, uint32_t &keyArrayEffectivelength, TaggedArray *keyArray, const JSHandle<JSObject> object, uint32_t filter)61 void 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 
GetAllKeysForSerialization(int end, std::vector<JSTaggedValue> &keyVector)103 void 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 
GetNumOfEnumKeys(int end, const JSObject *object) const114 std::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 
GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys, JSHandle<TaggedQueue> shadowQueue, const JSHandle<JSObject> object, int32_t lastLength)136 void 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 
GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys, const JSHandle<JSObject> object)167 void 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 
IsUninitializedProperty(const JSObject *object, uint32_t index) const188 bool 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 
GetSymbolKeyString(JSTaggedValue key)199 CString 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 
DumpFieldIndexByPGO(int index, pgo::HClassLayoutDesc* desc)220 void 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 
UpdateFieldIndexByPGO(int index, pgo::HClassLayoutDesc* desc)239 bool 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