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