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/global_env_constants.h"
18 #include "ecmascript/js_tagged_value-inl.h"
19 #include "ecmascript/tagged_array-inl.h"
20
21 namespace panda::ecmascript {
InitializeHClassMap()22 CMap<ElementsKind, ConstantIndex> Elements::InitializeHClassMap()
23 {
24 CMap<ElementsKind, ConstantIndex> result;
25 result.emplace(ElementsKind::NONE, ConstantIndex::ELEMENT_NONE_HCLASS_INDEX);
26 result.emplace(ElementsKind::HOLE, ConstantIndex::ELEMENT_HOLE_HCLASS_INDEX);
27 result.emplace(ElementsKind::INT, ConstantIndex::ELEMENT_INT_HCLASS_INDEX);
28 result.emplace(ElementsKind::NUMBER, ConstantIndex::ELEMENT_NUMBER_HCLASS_INDEX);
29 result.emplace(ElementsKind::STRING, ConstantIndex::ELEMENT_STRING_HCLASS_INDEX);
30 result.emplace(ElementsKind::OBJECT, ConstantIndex::ELEMENT_OBJECT_HCLASS_INDEX);
31 result.emplace(ElementsKind::TAGGED, ConstantIndex::ELEMENT_TAGGED_HCLASS_INDEX);
32 result.emplace(ElementsKind::HOLE_INT, ConstantIndex::ELEMENT_HOLE_INT_HCLASS_INDEX);
33 result.emplace(ElementsKind::HOLE_NUMBER, ConstantIndex::ELEMENT_HOLE_NUMBER_HCLASS_INDEX);
34 result.emplace(ElementsKind::HOLE_STRING, ConstantIndex::ELEMENT_HOLE_STRING_HCLASS_INDEX);
35 result.emplace(ElementsKind::HOLE_OBJECT, ConstantIndex::ELEMENT_HOLE_OBJECT_HCLASS_INDEX);
36 result.emplace(ElementsKind::HOLE_TAGGED, ConstantIndex::ELEMENT_HOLE_TAGGED_HCLASS_INDEX);
37 return result;
38 }
39
GetString(ElementsKind kind)40 std::string Elements::GetString(ElementsKind kind)
41 {
42 return std::to_string(static_cast<uint32_t>(kind));
43 }
44
IsInt(ElementsKind kind)45 bool Elements::IsInt(ElementsKind kind)
46 {
47 return kind == ElementsKind::INT;
48 }
49
IsNumber(ElementsKind kind)50 bool Elements::IsNumber(ElementsKind kind)
51 {
52 return kind == ElementsKind::NUMBER;
53 }
54
IsTagged(ElementsKind kind)55 bool Elements::IsTagged(ElementsKind kind)
56 {
57 return kind == ElementsKind::TAGGED;
58 }
59
IsObject(ElementsKind kind)60 bool Elements::IsObject(ElementsKind kind)
61 {
62 return kind == ElementsKind::OBJECT;
63 }
64
IsHole(ElementsKind kind)65 bool Elements::IsHole(ElementsKind kind)
66 {
67 static constexpr uint8_t EVEN_NUMBER = 2;
68 return static_cast<uint8_t>(kind) % EVEN_NUMBER == 1;
69 }
70
MergeElementsKind(ElementsKind curKind,ElementsKind newKind)71 ElementsKind Elements::MergeElementsKind(ElementsKind curKind, ElementsKind newKind)
72 {
73 auto result = ElementsKind(static_cast<uint8_t>(curKind) | static_cast<uint8_t>(newKind));
74 ASSERT(result != ElementsKind::NONE);
75 result = FixElementsKind(result);
76 return result;
77 }
78
FixElementsKind(ElementsKind oldKind)79 ElementsKind Elements::FixElementsKind(ElementsKind oldKind)
80 {
81 auto result = oldKind;
82 switch (result) {
83 case ElementsKind::NONE:
84 case ElementsKind::INT:
85 case ElementsKind::NUMBER:
86 case ElementsKind::STRING:
87 case ElementsKind::OBJECT:
88 case ElementsKind::HOLE:
89 case ElementsKind::HOLE_INT:
90 case ElementsKind::HOLE_NUMBER:
91 case ElementsKind::HOLE_STRING:
92 case ElementsKind::HOLE_OBJECT:
93 break;
94 default:
95 if (IsHole(result)) {
96 result = ElementsKind::HOLE_TAGGED;
97 } else {
98 result = ElementsKind::TAGGED;
99 }
100 break;
101 }
102 return result;
103 }
104
ToElementsKind(JSTaggedValue value,ElementsKind kind)105 ElementsKind Elements::ToElementsKind(JSTaggedValue value, ElementsKind kind)
106 {
107 ElementsKind valueKind = ElementsKind::NONE;
108 if (value.IsInt()) {
109 valueKind = ElementsKind::INT;
110 } else if (value.IsDouble()) {
111 valueKind = ElementsKind::NUMBER;
112 } else if (value.IsString()) {
113 valueKind = ElementsKind::STRING;
114 } else if (value.IsHeapObject()) {
115 valueKind = ElementsKind::OBJECT;
116 } else if (value.IsHole()) {
117 valueKind = ElementsKind::HOLE;
118 } else {
119 valueKind = ElementsKind::TAGGED;
120 }
121 return MergeElementsKind(valueKind, kind);
122 }
123
MigrateArrayWithKind(const JSThread * thread,const JSHandle<JSObject> & object,const ElementsKind oldKind,const ElementsKind newKind)124 void Elements::MigrateArrayWithKind(const JSThread *thread, const JSHandle<JSObject> &object,
125 const ElementsKind oldKind, const ElementsKind newKind)
126 {
127 if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
128 return;
129 }
130 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
131 if (oldKind == newKind) {
132 return;
133 }
134 // When create ArrayLiteral from constantPool, we need to preserve the COW property if necessary.
135 // When transition happens to Array, e.g. arr.x = 1, we need to preserve the COW property if necessary.
136 bool needCOW = object->GetElements().IsCOWArray();
137 if ((oldKind == ElementsKind::INT && newKind == ElementsKind::HOLE_INT) ||
138 (oldKind == ElementsKind::NUMBER && newKind == ElementsKind::HOLE_NUMBER)) {
139 return;
140 } else if (oldKind == ElementsKind::INT || oldKind == ElementsKind::HOLE_INT) {
141 JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements());
142 uint32_t length = elements->GetLength();
143 if (static_cast<uint32_t>(newKind) >= static_cast<uint32_t>(ElementsKind::STRING)) {
144 JSMutableHandle<TaggedArray> newElements(thread, JSTaggedValue::Undefined());
145 if (needCOW) {
146 newElements.Update(factory->NewCOWTaggedArray(length));
147 } else {
148 newElements.Update(factory->NewTaggedArray(length));
149 }
150 for (uint32_t i = 0; i < length; i++) {
151 JSTaggedType value = elements->Get(i).GetRawData();
152 if (value == base::SPECIAL_HOLE) {
153 newElements->Set(thread, i, JSTaggedValue::Hole());
154 } else {
155 int convertedValue = static_cast<int>(value);
156 newElements->Set(thread, i, JSTaggedValue(convertedValue));
157 }
158 }
159 object->SetElements(thread, newElements);
160 } else {
161 for (uint32_t i = 0; i < length; i++) {
162 JSTaggedType value = elements->Get(i).GetRawData();
163 if (value == base::SPECIAL_HOLE) {
164 continue;
165 }
166 int intValue = static_cast<int>(elements->Get(i).GetRawData());
167 double convertedValue = static_cast<double>(intValue);
168 elements->Set<false>(thread, i, JSTaggedValue(base::bit_cast<JSTaggedType>(convertedValue)));
169 }
170 }
171 } else if (static_cast<uint32_t>(oldKind) >= static_cast<uint32_t>(ElementsKind::NUMBER) &&
172 static_cast<uint32_t>(oldKind) <= static_cast<uint32_t>(ElementsKind::HOLE_NUMBER)) {
173 JSHandle<MutantTaggedArray> elements = JSHandle<MutantTaggedArray>(thread, object->GetElements());
174 uint32_t length = elements->GetLength();
175 if (static_cast<uint32_t>(newKind) >= static_cast<uint32_t>(ElementsKind::STRING)) {
176 JSMutableHandle<TaggedArray> newElements(thread, JSTaggedValue::Undefined());
177 if (needCOW) {
178 newElements.Update(factory->NewCOWTaggedArray(length));
179 } else {
180 newElements.Update(factory->NewTaggedArray(length));
181 }
182 for (uint32_t i = 0; i < length; i++) {
183 JSTaggedType value = elements->Get(i).GetRawData();
184 if (value == base::SPECIAL_HOLE) {
185 newElements->Set(thread, i, JSTaggedValue::Hole());
186 } else {
187 double convertedValue = base::bit_cast<double>(value);
188 newElements->Set(thread, i, JSTaggedValue(convertedValue));
189 }
190 }
191 object->SetElements(thread, newElements);
192 } else {
193 LOG_ECMA(FATAL) << "This Branch is Unreachable in ConvertArray" << static_cast<uint32_t>(newKind);
194 }
195 } else {
196 JSHandle<TaggedArray> elements = JSHandle<TaggedArray>(thread, object->GetElements());
197 uint32_t length = elements->GetLength();
198 if (newKind == ElementsKind::INT || newKind == ElementsKind::HOLE_INT) {
199 JSMutableHandle<MutantTaggedArray> newElements(thread, JSTaggedValue::Undefined());
200 if (needCOW) {
201 newElements.Update(factory->NewCOWMutantTaggedArray(length));
202 } else {
203 newElements.Update(factory->NewMutantTaggedArray(length));
204 }
205 for (uint32_t i = 0; i < length; i++) {
206 JSTaggedValue value = elements->Get(i);
207 JSTaggedType convertedValue = 0;
208 // To distinguish Hole (0x5) in taggedvalue with Interger 5
209 if (value.IsHole()) {
210 convertedValue = base::SPECIAL_HOLE;
211 } else {
212 convertedValue = static_cast<JSTaggedType>(value.GetInt());
213 }
214 newElements->Set<false>(thread, i, JSTaggedValue(convertedValue));
215 }
216 object->SetElements(thread, newElements);
217 } else if (static_cast<uint32_t>(newKind) >= static_cast<uint32_t>(ElementsKind::NUMBER) &&
218 static_cast<uint32_t>(newKind) <= static_cast<uint32_t>(ElementsKind::HOLE_NUMBER)) {
219 JSMutableHandle<MutantTaggedArray> newElements(thread, JSTaggedValue::Undefined());
220 if (needCOW) {
221 newElements.Update(factory->NewCOWMutantTaggedArray(length));
222 } else {
223 newElements.Update(factory->NewMutantTaggedArray(length));
224 }
225 for (uint32_t i = 0; i < length; i++) {
226 JSTaggedValue value = elements->Get(i);
227 JSTaggedType convertedValue = 0;
228 // To distinguish Hole (0x5) in taggedvalue with Interger 5
229 if (value.IsHole()) {
230 convertedValue = base::SPECIAL_HOLE;
231 } else {
232 if (value.IsInt()) {
233 int intValue = value.GetInt();
234 convertedValue = base::bit_cast<JSTaggedType>(static_cast<double>(intValue));
235 } else {
236 convertedValue = base::bit_cast<JSTaggedType>(value.GetDouble());
237 }
238 }
239 newElements->Set<false>(thread, i, JSTaggedValue(convertedValue));
240 }
241 object->SetElements(thread, newElements);
242 }
243 }
244 }
245 } // namespace panda::ecmascript
246