• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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