• 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 
MergeElementsKind(ElementsKind curKind,ElementsKind newKind)52 ElementsKind Elements::MergeElementsKind(ElementsKind curKind, ElementsKind newKind)
53 {
54     auto result = ElementsKind(static_cast<uint8_t>(curKind) | static_cast<uint8_t>(newKind));
55     result = FixElementsKind(result);
56     return result;
57 }
58 
FixElementsKind(ElementsKind oldKind)59 ElementsKind Elements::FixElementsKind(ElementsKind oldKind)
60 {
61     auto result = oldKind;
62     switch (result) {
63         case ElementsKind::NONE:
64         case ElementsKind::INT:
65         case ElementsKind::NUMBER:
66         case ElementsKind::STRING:
67         case ElementsKind::OBJECT:
68         case ElementsKind::HOLE:
69         case ElementsKind::HOLE_INT:
70         case ElementsKind::HOLE_NUMBER:
71         case ElementsKind::HOLE_STRING:
72         case ElementsKind::HOLE_OBJECT:
73             break;
74         default:
75             if (IsHole(result)) {
76                 result = ElementsKind::HOLE_TAGGED;
77             } else {
78                 result = ElementsKind::TAGGED;
79             }
80             break;
81     }
82     return result;
83 }
84 
85 
ToElementsKind(JSTaggedValue value,ElementsKind kind)86 ElementsKind Elements::ToElementsKind(JSTaggedValue value, ElementsKind kind)
87 {
88     return MergeElementsKind(ToElementsKind(value), kind);
89 }
90 
HandleIntKindMigration(const JSThread * thread,const JSHandle<JSObject> & object,const ElementsKind newKind,bool needCOW)91 void Elements::HandleIntKindMigration(const JSThread *thread, const JSHandle<JSObject> &object,
92                                       const ElementsKind newKind, bool needCOW)
93 {
94     if (IsStringOrNoneOrHole(newKind)) {
95         JSTaggedValue newElements = MigrateFromRawValueToHeapValue(thread, object, needCOW, true);
96         object->SetElements(thread, newElements);
97     } else if (newKind == ElementsKind::NUMBER || newKind == ElementsKind::HOLE_NUMBER) {
98         MigrateFromHoleIntToHoleNumber(thread, object);
99     }
100 }
101 
IsNumberKind(const ElementsKind kind)102 bool Elements::IsNumberKind(const ElementsKind kind)
103 {
104     return ToUint(kind) >= Elements::ToUint(ElementsKind::NUMBER) &&
105            ToUint(kind) <= Elements::ToUint(ElementsKind::HOLE_NUMBER);
106 }
107 
IsStringOrNoneOrHole(const ElementsKind kind)108 bool Elements::IsStringOrNoneOrHole(const ElementsKind kind)
109 {
110     return ToUint(kind) >= ToUint(ElementsKind::STRING) ||
111            kind == ElementsKind::NONE || kind == ElementsKind::HOLE;
112 }
113 
HandleNumberKindMigration(const JSThread * thread,const JSHandle<JSObject> & object,const ElementsKind newKind,bool needCOW)114 void Elements::HandleNumberKindMigration(const JSThread *thread, const JSHandle<JSObject> &object,
115                                          const ElementsKind newKind, bool needCOW)
116 {
117     if (IsStringOrNoneOrHole(newKind)) {
118         JSTaggedValue newElements = MigrateFromRawValueToHeapValue(thread, object, needCOW, false);
119         object->SetElements(thread, newElements);
120     } else if (newKind == ElementsKind::INT || newKind == ElementsKind::HOLE_INT) {
121         MigrateFromHoleNumberToHoleInt(thread, object);
122     }
123 }
124 
HandleOtherKindMigration(const JSThread * thread,const JSHandle<JSObject> & object,const ElementsKind newKind,bool needCOW)125 void Elements::HandleOtherKindMigration(const JSThread *thread, const JSHandle<JSObject> &object,
126                                         const ElementsKind newKind, bool needCOW)
127 {
128     if (newKind == ElementsKind::INT || newKind == ElementsKind::HOLE_INT) {
129         JSTaggedValue newElements = MigrateFromHeapValueToRawValue(thread, object, needCOW, true);
130         object->SetElements(thread, newElements);
131     } else if (IsNumberKind(newKind)) {
132         JSTaggedValue newElements = MigrateFromHeapValueToRawValue(thread, object, needCOW, false);
133         object->SetElements(thread, newElements);
134     }
135 }
136 
MigrateArrayWithKind(const JSThread * thread,const JSHandle<JSObject> & object,const ElementsKind oldKind,const ElementsKind newKind)137 void Elements::MigrateArrayWithKind(const JSThread *thread, const JSHandle<JSObject> &object,
138                                     const ElementsKind oldKind, const ElementsKind newKind)
139 {
140     if (!thread->IsEnableMutantArray()) {
141         return;
142     }
143 
144     if (oldKind == newKind ||
145         (oldKind == ElementsKind::INT && newKind == ElementsKind::HOLE_INT) ||
146         (oldKind == ElementsKind::NUMBER && newKind == ElementsKind::HOLE_NUMBER)) {
147         return;
148     }
149 
150     bool needCOW = object->GetElements(thread).IsCOWArray();
151 
152     if (oldKind == ElementsKind::INT || oldKind == ElementsKind::HOLE_INT) {
153         HandleIntKindMigration(thread, object, newKind, needCOW);
154     } else if ((IsNumberKind(oldKind))) {
155         HandleNumberKindMigration(thread, object, newKind, needCOW);
156     } else {
157         HandleOtherKindMigration(thread, object, newKind, needCOW);
158     }
159 }
160 
MigrateFromRawValueToHeapValue(const JSThread * thread,const JSHandle<JSObject> object,bool needCOW,bool isIntKind)161 JSTaggedValue Elements::MigrateFromRawValueToHeapValue(const JSThread *thread, const JSHandle<JSObject> object,
162                                                        bool needCOW, bool isIntKind)
163 {
164     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
165     JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements(thread));
166     uint32_t length = elements->GetLength();
167     JSMutableHandle<TaggedArray> newElements(thread, JSTaggedValue::Undefined());
168     if (needCOW) {
169         newElements.Update(factory->NewCOWTaggedArray(length));
170     } else {
171         newElements.Update(factory->NewTaggedArray(length));
172     }
173     for (uint32_t i = 0; i < length; i++) {
174         JSTaggedType value = elements->Get(thread, i).GetRawData();
175         if (value == base::SPECIAL_HOLE) {
176             newElements->Set(thread, i, JSTaggedValue::Hole());
177         } else if (isIntKind) {
178             int convertedValue = static_cast<int>(value);
179             newElements->Set(thread, i, JSTaggedValue(convertedValue));
180         } else {
181             double convertedValue = base::bit_cast<double>(value);
182             newElements->Set(thread, i, JSTaggedValue(convertedValue));
183         }
184     }
185     return newElements.GetTaggedValue();
186 }
187 
MigrateFromHeapValueToRawValue(const JSThread * thread,const JSHandle<JSObject> object,bool needCOW,bool isIntKind)188 JSTaggedValue Elements::MigrateFromHeapValueToRawValue(const JSThread *thread, const JSHandle<JSObject> object,
189                                                        bool needCOW, bool isIntKind)
190 {
191     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
192     JSHandle<TaggedArray> elements = JSHandle<TaggedArray>(thread, object->GetElements(thread));
193     uint32_t length = elements->GetLength();
194     JSMutableHandle<MutantTaggedArray> newElements(thread, JSTaggedValue::Undefined());
195     if (needCOW) {
196         newElements.Update(factory->NewCOWMutantTaggedArray(length));
197     } else {
198         newElements.Update(factory->NewMutantTaggedArray(length));
199     }
200     for (uint32_t i = 0; i < length; i++) {
201         JSTaggedValue value = elements->Get(thread, i);
202         JSTaggedType convertedValue = 0;
203         // To distinguish Hole (0x5) in taggedvalue with Interger 5
204         if (value.IsHole()) {
205             convertedValue = base::SPECIAL_HOLE;
206         } else if (isIntKind) {
207             convertedValue = static_cast<JSTaggedType>(value.GetInt());
208         } else if (value.IsInt()) {
209             int intValue = value.GetInt();
210             convertedValue = base::bit_cast<JSTaggedType>(static_cast<double>(intValue));
211         } else {
212             convertedValue = base::bit_cast<JSTaggedType>(value.GetDouble());
213         }
214         newElements->Set<false>(thread, i, JSTaggedValue(convertedValue));
215     }
216     return newElements.GetTaggedValue();
217 }
218 
MigrateFromHoleIntToHoleNumber(const JSThread * thread,const JSHandle<JSObject> object)219 void Elements::MigrateFromHoleIntToHoleNumber(const JSThread *thread, const JSHandle<JSObject> object)
220 {
221     JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements(thread));
222     uint32_t length = elements->GetLength();
223     for (uint32_t i = 0; i < length; i++) {
224         JSTaggedType value = elements->Get(thread, i).GetRawData();
225         if (value == base::SPECIAL_HOLE) {
226             continue;
227         }
228         int intValue = static_cast<int>(elements->Get(thread, i).GetRawData());
229         double convertedValue = static_cast<double>(intValue);
230         elements->Set<false>(thread, i, JSTaggedValue(base::bit_cast<JSTaggedType>(convertedValue)));
231     }
232 }
233 
MigrateFromHoleNumberToHoleInt(const JSThread * thread,const JSHandle<JSObject> object)234 void Elements::MigrateFromHoleNumberToHoleInt(const JSThread *thread, const JSHandle<JSObject> object)
235 {
236     JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements(thread));
237     uint32_t length = elements->GetLength();
238     for (uint32_t i = 0; i < length; i++) {
239         JSTaggedType value = elements->Get(thread, i).GetRawData();
240         if (value == base::SPECIAL_HOLE) {
241             continue;
242         }
243         double intValue = base::bit_cast<double>(elements->Get(thread, i).GetRawData());
244         int64_t convertedValue = static_cast<int64_t>(intValue);
245         elements->Set<false>(thread, i, JSTaggedValue(base::bit_cast<JSTaggedType>(convertedValue)));
246     }
247 }
248 }  // namespace panda::ecmascript
249