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