1 /*
2  * Copyright (c) 2023 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/elements.h"
17 #include "ecmascript/js_tagged_value-inl.h"
18 #include "ecmascript/tagged_array-inl.h"
19 
20 namespace panda::ecmascript {
InitializeHClassMap()21 CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> Elements::InitializeHClassMap()
22 {
23     CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> result;
24 #define INIT_ARRAY_HCLASS_INDEX_MAPS(name)                                                                       \
25     result.emplace(ElementsKind::name, std::make_pair(ConstantIndex::ELEMENT_##name##_HCLASS_INDEX,              \
26                                                       ConstantIndex::ELEMENT_##name##_PROTO_HCLASS_INDEX));
27     ELEMENTS_KIND_INIT_HCLASS_LIST(INIT_ARRAY_HCLASS_INDEX_MAPS)
28 #undef INIT_ARRAY_HCLASS_INDEX_MAPS
29     return result;
30 }
31 
GetString(ElementsKind kind)32 std::string Elements::GetString(ElementsKind kind)
33 {
34     return std::to_string(static_cast<uint32_t>(kind));
35 }
36 
IsInt(ElementsKind kind)37 bool Elements::IsInt(ElementsKind kind)
38 {
39     return kind == ElementsKind::INT;
40 }
41 
IsNumber(ElementsKind kind)42 bool Elements::IsNumber(ElementsKind kind)
43 {
44     return kind == ElementsKind::NUMBER;
45 }
46 
IsTagged(ElementsKind kind)47 bool Elements::IsTagged(ElementsKind kind)
48 {
49     return kind == ElementsKind::TAGGED;
50 }
51 
IsObject(ElementsKind kind)52 bool Elements::IsObject(ElementsKind kind)
53 {
54     return kind == ElementsKind::OBJECT;
55 }
56 
IsHole(ElementsKind kind)57 bool Elements::IsHole(ElementsKind kind)
58 {
59     static constexpr uint8_t EVEN_NUMBER = 2;
60     return static_cast<uint8_t>(kind) % EVEN_NUMBER == 1;
61 }
62 
GetGlobalContantIndexByKind(ElementsKind kind)63 ConstantIndex Elements::GetGlobalContantIndexByKind(ElementsKind kind)
64 {
65     switch (kind) {
66         case ElementsKind::NONE:
67             return ConstantIndex::ELEMENT_NONE_HCLASS_INDEX;
68         case ElementsKind::INT:
69             return ConstantIndex::ELEMENT_INT_HCLASS_INDEX;
70         case ElementsKind::NUMBER:
71             return ConstantIndex::ELEMENT_NUMBER_HCLASS_INDEX;
72         case ElementsKind::STRING:
73             return ConstantIndex::ELEMENT_STRING_HCLASS_INDEX;
74         case ElementsKind::OBJECT:
75             return ConstantIndex::ELEMENT_OBJECT_HCLASS_INDEX;
76         case ElementsKind::TAGGED:
77             return ConstantIndex::ELEMENT_TAGGED_HCLASS_INDEX;
78         case ElementsKind::HOLE:
79             return ConstantIndex::ELEMENT_HOLE_HCLASS_INDEX;
80         case ElementsKind::HOLE_INT:
81             return ConstantIndex::ELEMENT_HOLE_INT_HCLASS_INDEX;
82         case ElementsKind::HOLE_NUMBER:
83             return ConstantIndex::ELEMENT_HOLE_NUMBER_HCLASS_INDEX;
84         case ElementsKind::HOLE_STRING:
85             return ConstantIndex::ELEMENT_HOLE_STRING_HCLASS_INDEX;
86         case ElementsKind::HOLE_OBJECT:
87             return ConstantIndex::ELEMENT_HOLE_OBJECT_HCLASS_INDEX;
88         case ElementsKind::HOLE_TAGGED:
89             return ConstantIndex::ELEMENT_HOLE_TAGGED_HCLASS_INDEX;
90         default:
91             LOG_ECMA(FATAL) << "Unknown elementsKind when getting constantIndx: " << static_cast<int32_t>(kind);
92     }
93 }
94 
MergeElementsKind(ElementsKind curKind, ElementsKind newKind)95 ElementsKind Elements::MergeElementsKind(ElementsKind curKind, ElementsKind newKind)
96 {
97     auto result = ElementsKind(static_cast<uint8_t>(curKind) | static_cast<uint8_t>(newKind));
98     result = FixElementsKind(result);
99     return result;
100 }
101 
FixElementsKind(ElementsKind oldKind)102 ElementsKind Elements::FixElementsKind(ElementsKind oldKind)
103 {
104     auto result = oldKind;
105     switch (result) {
106         case ElementsKind::NONE:
107         case ElementsKind::INT:
108         case ElementsKind::NUMBER:
109         case ElementsKind::STRING:
110         case ElementsKind::OBJECT:
111         case ElementsKind::HOLE:
112         case ElementsKind::HOLE_INT:
113         case ElementsKind::HOLE_NUMBER:
114         case ElementsKind::HOLE_STRING:
115         case ElementsKind::HOLE_OBJECT:
116             break;
117         default:
118             if (IsHole(result)) {
119                 result = ElementsKind::HOLE_TAGGED;
120             } else {
121                 result = ElementsKind::TAGGED;
122             }
123             break;
124     }
125     return result;
126 }
127 
ToElementsKind(JSTaggedValue value, ElementsKind kind)128 ElementsKind Elements::ToElementsKind(JSTaggedValue value, ElementsKind kind)
129 {
130     ElementsKind valueKind = ElementsKind::NONE;
131     if (value.IsInt()) {
132         valueKind = ElementsKind::INT;
133     } else if (value.IsDouble()) {
134         valueKind = ElementsKind::NUMBER;
135     } else if (value.IsString()) {
136         valueKind = ElementsKind::STRING;
137     } else if (value.IsHeapObject()) {
138         valueKind = ElementsKind::OBJECT;
139     } else if (value.IsHole()) {
140         valueKind = ElementsKind::HOLE;
141     } else {
142         valueKind = ElementsKind::TAGGED;
143     }
144     return MergeElementsKind(valueKind, kind);
145 }
146 
HandleIntKindMigration(const JSThread *thread, const JSHandle<JSObject> &object, const ElementsKind newKind, bool needCOW)147 void Elements::HandleIntKindMigration(const JSThread *thread, const JSHandle<JSObject> &object,
148                                       const ElementsKind newKind, bool needCOW)
149 {
150     if (IsStringOrNoneOrHole(newKind)) {
151         JSTaggedValue newElements = MigrateFromRawValueToHeapValue(thread, object, needCOW, true);
152         object->SetElements(thread, newElements);
153     } else if (newKind == ElementsKind::NUMBER || newKind == ElementsKind::HOLE_NUMBER) {
154         MigrateFromHoleIntToHoleNumber(thread, object);
155     }
156 }
157 
IsNumberKind(const ElementsKind kind)158 bool Elements::IsNumberKind(const ElementsKind kind)
159 {
160     return static_cast<uint32_t>(kind) >= static_cast<uint32_t>(ElementsKind::NUMBER) &&
161            static_cast<uint32_t>(kind) <= static_cast<uint32_t>(ElementsKind::HOLE_NUMBER);
162 }
163 
IsStringOrNoneOrHole(const ElementsKind kind)164 bool Elements::IsStringOrNoneOrHole(const ElementsKind kind)
165 {
166     return static_cast<uint32_t>(kind) >= static_cast<uint32_t>(ElementsKind::STRING) ||
167            kind == ElementsKind::NONE || kind == ElementsKind::HOLE;
168 }
169 
HandleNumberKindMigration(const JSThread *thread, const JSHandle<JSObject> &object, const ElementsKind newKind, bool needCOW)170 void Elements::HandleNumberKindMigration(const JSThread *thread, const JSHandle<JSObject> &object,
171                                          const ElementsKind newKind, bool needCOW)
172 {
173     if (IsStringOrNoneOrHole(newKind)) {
174         JSTaggedValue newElements = MigrateFromRawValueToHeapValue(thread, object, needCOW, false);
175         object->SetElements(thread, newElements);
176     } else if (newKind == ElementsKind::INT || newKind == ElementsKind::HOLE_INT) {
177         MigrateFromHoleNumberToHoleInt(thread, object);
178     }
179 }
180 
HandleOtherKindMigration(const JSThread *thread, const JSHandle<JSObject> &object, const ElementsKind newKind, bool needCOW)181 void Elements::HandleOtherKindMigration(const JSThread *thread, const JSHandle<JSObject> &object,
182                                         const ElementsKind newKind, bool needCOW)
183 {
184     if (newKind == ElementsKind::INT || newKind == ElementsKind::HOLE_INT) {
185         JSTaggedValue newElements = MigrateFromHeapValueToRawValue(thread, object, needCOW, true);
186         object->SetElements(thread, newElements);
187     } else if (IsNumberKind(newKind)) {
188         JSTaggedValue newElements = MigrateFromHeapValueToRawValue(thread, object, needCOW, false);
189         object->SetElements(thread, newElements);
190     }
191 }
192 
MigrateArrayWithKind(const JSThread *thread, const JSHandle<JSObject> &object, const ElementsKind oldKind, const ElementsKind newKind)193 void Elements::MigrateArrayWithKind(const JSThread *thread, const JSHandle<JSObject> &object,
194                                     const ElementsKind oldKind, const ElementsKind newKind)
195 {
196     if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
197         return;
198     }
199 
200     if (oldKind == newKind ||
201         (oldKind == ElementsKind::INT && newKind == ElementsKind::HOLE_INT) ||
202         (oldKind == ElementsKind::NUMBER && newKind == ElementsKind::HOLE_NUMBER)) {
203         return;
204     }
205 
206     bool needCOW = object->GetElements().IsCOWArray();
207 
208     if (oldKind == ElementsKind::INT || oldKind == ElementsKind::HOLE_INT) {
209         HandleIntKindMigration(thread, object, newKind, needCOW);
210     } else if ((IsNumberKind(oldKind))) {
211         HandleNumberKindMigration(thread, object, newKind, needCOW);
212     } else {
213         HandleOtherKindMigration(thread, object, newKind, needCOW);
214     }
215 }
216 
MigrateFromRawValueToHeapValue(const JSThread *thread, const JSHandle<JSObject> object, bool needCOW, bool isIntKind)217 JSTaggedValue Elements::MigrateFromRawValueToHeapValue(const JSThread *thread, const JSHandle<JSObject> object,
218                                                        bool needCOW, bool isIntKind)
219 {
220     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
221     JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements());
222     uint32_t length = elements->GetLength();
223     JSMutableHandle<TaggedArray> newElements(thread, JSTaggedValue::Undefined());
224     if (needCOW) {
225         newElements.Update(factory->NewCOWTaggedArray(length));
226     } else {
227         newElements.Update(factory->NewTaggedArray(length));
228     }
229     for (uint32_t i = 0; i < length; i++) {
230         JSTaggedType value = elements->Get(i).GetRawData();
231         if (value == base::SPECIAL_HOLE) {
232             newElements->Set(thread, i, JSTaggedValue::Hole());
233         } else if (isIntKind) {
234             int convertedValue = static_cast<int>(value);
235             newElements->Set(thread, i, JSTaggedValue(convertedValue));
236         } else {
237             double convertedValue = base::bit_cast<double>(value);
238             newElements->Set(thread, i, JSTaggedValue(convertedValue));
239         }
240     }
241     return newElements.GetTaggedValue();
242 }
243 
MigrateFromHeapValueToRawValue(const JSThread *thread, const JSHandle<JSObject> object, bool needCOW, bool isIntKind)244 JSTaggedValue Elements::MigrateFromHeapValueToRawValue(const JSThread *thread, const JSHandle<JSObject> object,
245                                                        bool needCOW, bool isIntKind)
246 {
247     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
248     JSHandle<TaggedArray> elements = JSHandle<TaggedArray>(thread, object->GetElements());
249     uint32_t length = elements->GetLength();
250     JSMutableHandle<MutantTaggedArray> newElements(thread, JSTaggedValue::Undefined());
251     if (needCOW) {
252         newElements.Update(factory->NewCOWMutantTaggedArray(length));
253     } else {
254         newElements.Update(factory->NewMutantTaggedArray(length));
255     }
256     for (uint32_t i = 0; i < length; i++) {
257         JSTaggedValue value = elements->Get(i);
258         JSTaggedType convertedValue = 0;
259         // To distinguish Hole (0x5) in taggedvalue with Interger 5
260         if (value.IsHole()) {
261             convertedValue = base::SPECIAL_HOLE;
262         } else if (isIntKind) {
263             convertedValue = static_cast<JSTaggedType>(value.GetInt());
264         } else if (value.IsInt()) {
265             int intValue = value.GetInt();
266             convertedValue = base::bit_cast<JSTaggedType>(static_cast<double>(intValue));
267         } else {
268             convertedValue = base::bit_cast<JSTaggedType>(value.GetDouble());
269         }
270         newElements->Set<false>(thread, i, JSTaggedValue(convertedValue));
271     }
272     return newElements.GetTaggedValue();
273 }
274 
MigrateFromHoleIntToHoleNumber(const JSThread *thread, const JSHandle<JSObject> object)275 void Elements::MigrateFromHoleIntToHoleNumber(const JSThread *thread, const JSHandle<JSObject> object)
276 {
277     JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements());
278     uint32_t length = elements->GetLength();
279     for (uint32_t i = 0; i < length; i++) {
280         JSTaggedType value = elements->Get(i).GetRawData();
281         if (value == base::SPECIAL_HOLE) {
282             continue;
283         }
284         int intValue = static_cast<int>(elements->Get(i).GetRawData());
285         double convertedValue = static_cast<double>(intValue);
286         elements->Set<false>(thread, i, JSTaggedValue(base::bit_cast<JSTaggedType>(convertedValue)));
287     }
288 }
289 
MigrateFromHoleNumberToHoleInt(const JSThread *thread, const JSHandle<JSObject> object)290 void Elements::MigrateFromHoleNumberToHoleInt(const JSThread *thread, const JSHandle<JSObject> object)
291 {
292     JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements());
293     uint32_t length = elements->GetLength();
294     for (uint32_t i = 0; i < length; i++) {
295         JSTaggedType value = elements->Get(i).GetRawData();
296         if (value == base::SPECIAL_HOLE) {
297             continue;
298         }
299         double intValue = base::bit_cast<double>(elements->Get(i).GetRawData());
300         int64_t convertedValue = static_cast<int64_t>(intValue);
301         elements->Set<false>(thread, i, JSTaggedValue(base::bit_cast<JSTaggedType>(convertedValue)));
302     }
303 }
304 }  // namespace panda::ecmascript
305