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