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