1 /* 2 * Copyright (c) 2021-2022 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 #ifndef ECMASCRIPT_JSPANDAFILE_PROGRAM_OBJECT_H 17 #define ECMASCRIPT_JSPANDAFILE_PROGRAM_OBJECT_H 18 19 #include "ecmascript/compiler/aot_file/aot_file_manager.h" 20 #include "ecmascript/ecma_macros.h" 21 #include "ecmascript/js_tagged_value-inl.h" 22 #include "ecmascript/jspandafile/class_info_extractor.h" 23 #include "ecmascript/jspandafile/class_literal.h" 24 #include "ecmascript/jspandafile/constpool_value.h" 25 #include "ecmascript/jspandafile/js_pandafile_manager.h" 26 #include "ecmascript/jspandafile/literal_data_extractor.h" 27 #include "ecmascript/module/js_module_manager.h" 28 #include "ecmascript/patch/quick_fix_manager.h" 29 #include "ecmascript/pgo_profiler/pgo_profiler.h" 30 31 #include "libpandafile/class_data_accessor-inl.h" 32 #include "libpandafile/index_accessor.h" 33 34 namespace panda { 35 namespace ecmascript { 36 class JSThread; 37 38 class Program : public ECMAObject { 39 public: 40 DECL_CAST(Program) 41 42 static constexpr size_t MAIN_FUNCTION_OFFSET = ECMAObject::SIZE; 43 ACCESSORS(MainFunction, MAIN_FUNCTION_OFFSET, SIZE) 44 45 DECL_VISIT_OBJECT(MAIN_FUNCTION_OFFSET, SIZE) 46 DECL_DUMP() 47 }; 48 49 /* ConstantPool 50 * +--------------------------------+---- 51 * | cache | ^ 52 * | ... | | 53 * | string(EcmaString) | | 54 * | method(Method) |cacheLength 55 * | array literal(JSArray) | | 56 * | object literal(JSObject) | | 57 * | class literal(ClassLiteral) | v 58 * +--------------------------------+---- 59 * | IndexHeader | 60 * +--------------------------------+ 61 * | JSPandaFile | 62 * +--------------------------------+ 63 */ 64 class ConstantPool : public TaggedArray { 65 public: 66 static constexpr size_t JS_PANDA_FILE_INDEX = 1; 67 static constexpr size_t INDEX_HEADER_INDEX = 2; 68 static constexpr size_t RESERVED_POOL_LENGTH = INDEX_HEADER_INDEX; 69 Cast(TaggedObject * object)70 static ConstantPool *Cast(TaggedObject *object) 71 { 72 ASSERT(JSTaggedValue(object).IsConstantPool()); 73 return static_cast<ConstantPool *>(object); 74 } 75 CreateConstPool(EcmaVM * vm,const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)76 static JSHandle<ConstantPool> CreateConstPool(EcmaVM *vm, const JSPandaFile *jsPandaFile, 77 panda_file::File::EntityId id) 78 { 79 const panda_file::File::IndexHeader *mainIndex = jsPandaFile->GetPandaFile()->GetIndexHeader(id); 80 LOG_ECMA_IF(mainIndex == nullptr, FATAL) << "Unknown methodId: " << id.GetOffset(); 81 auto constpoolSize = mainIndex->method_idx_size; 82 83 JSHandle<ConstantPool> constpool(vm->GetJSThread(), JSTaggedValue::Hole()); 84 bool isLoadedAOT = jsPandaFile->IsLoadedAOT(); 85 if (isLoadedAOT) { 86 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) 87 panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id); 88 int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex()); 89 constpool = GetDeserializedConstantPool(vm, jsPandaFile, index); 90 #else 91 LOG_FULL(FATAL) << "Aot don't support Windows and MacOS platform"; 92 UNREACHABLE(); 93 #endif 94 } 95 if (constpool.GetTaggedValue().IsHole()) { 96 ObjectFactory *factory = vm->GetFactory(); 97 constpool = factory->NewConstantPool(constpoolSize); 98 } 99 100 constpool->SetJSPandaFile(jsPandaFile); 101 constpool->SetIndexHeader(mainIndex); 102 103 return constpool; 104 } 105 GetEntityId(uint32_t index)106 panda_file::File::EntityId GetEntityId(uint32_t index) const 107 { 108 JSPandaFile *jsPandaFile = GetJSPandaFile(); 109 panda_file::File::IndexHeader *indexHeader = GetIndexHeader(); 110 Span<const panda_file::File::EntityId> indexs = jsPandaFile->GetMethodIndex(indexHeader); 111 return indexs[index]; 112 } 113 GetMethodIndexByEntityId(panda_file::File::EntityId entityId)114 int GetMethodIndexByEntityId(panda_file::File::EntityId entityId) const 115 { 116 JSPandaFile *jsPandaFile = GetJSPandaFile(); 117 panda_file::File::IndexHeader *indexHeader = GetIndexHeader(); 118 Span<const panda_file::File::EntityId> indexs = jsPandaFile->GetMethodIndex(indexHeader); 119 int size = static_cast<int>(indexs.size()); 120 for (int i = 0; i < size; i++) { 121 if (indexs[i] == entityId) { 122 return i; 123 } 124 } 125 return -1; 126 } 127 SetIndexHeader(const panda_file::File::IndexHeader * indexHeader)128 inline void SetIndexHeader(const panda_file::File::IndexHeader *indexHeader) 129 { 130 Barriers::SetPrimitive(GetData(), GetIndexHeaderOffset(), indexHeader); 131 } 132 GetIndexHeader()133 inline panda_file::File::IndexHeader *GetIndexHeader() const 134 { 135 return Barriers::GetValue<panda_file::File::IndexHeader *>(GetData(), GetIndexHeaderOffset()); 136 } 137 ComputeSize(uint32_t cacheSize)138 static size_t ComputeSize(uint32_t cacheSize) 139 { 140 return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), cacheSize + RESERVED_POOL_LENGTH); 141 } 142 143 inline void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t capacity, uint32_t extraLength = 0) 144 { 145 ASSERT(initValue.IsSpecial()); 146 SetLength(capacity + RESERVED_POOL_LENGTH); 147 SetExtraLength(extraLength); 148 for (uint32_t i = 0; i < capacity; i++) { 149 size_t offset = JSTaggedValue::TaggedTypeSize() * i; 150 Barriers::SetPrimitive<JSTaggedType>(GetData(), offset, initValue.GetRawData()); 151 } 152 SetJSPandaFile(nullptr); 153 SetIndexHeader(nullptr); 154 } 155 GetCacheLength()156 inline uint32_t GetCacheLength() const 157 { 158 return GetLength() - RESERVED_POOL_LENGTH; 159 } 160 SetJSPandaFile(const void * jsPandaFile)161 inline void SetJSPandaFile(const void *jsPandaFile) 162 { 163 Barriers::SetPrimitive(GetData(), GetJSPandaFileOffset(), jsPandaFile); 164 } 165 GetJSPandaFile()166 inline JSPandaFile *GetJSPandaFile() const 167 { 168 return Barriers::GetValue<JSPandaFile *>(GetData(), GetJSPandaFileOffset()); 169 } 170 SetObjectToCache(JSThread * thread,uint32_t index,JSTaggedValue value)171 inline void SetObjectToCache(JSThread *thread, uint32_t index, JSTaggedValue value) 172 { 173 Set(thread, index, value); 174 } 175 GetObjectFromCache(uint32_t index)176 inline JSTaggedValue GetObjectFromCache(uint32_t index) const 177 { 178 return Get(index); 179 } 180 GetMethodFromCache(JSThread * thread,JSTaggedValue constpool,uint32_t index)181 static JSTaggedValue GetMethodFromCache(JSThread *thread, JSTaggedValue constpool, uint32_t index) 182 { 183 const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); 184 auto val = taggedPool->GetObjectFromCache(index); 185 JSPandaFile *jsPandaFile = taggedPool->GetJSPandaFile(); 186 187 // For AOT 188 bool isLoadedAOT = jsPandaFile->IsLoadedAOT(); 189 bool hasEntryIndex = false; 190 uint32_t entryIndex = 0; 191 if (isLoadedAOT && val.IsInt()) { 192 entryIndex = static_cast<uint32_t>(val.GetInt()); 193 hasEntryIndex = true; 194 val = JSTaggedValue::Hole(); 195 } 196 197 if (!val.IsHole()) { 198 return val; 199 } 200 201 [[maybe_unused]] EcmaHandleScope handleScope(thread); 202 ASSERT(jsPandaFile->IsNewVersion()); 203 JSHandle<ConstantPool> constpoolHandle(thread, constpool); 204 EcmaVM *vm = thread->GetEcmaVM(); 205 206 EntityId id = constpoolHandle->GetEntityId(index); 207 MethodLiteral *methodLiteral = jsPandaFile->FindMethodLiteral(id.GetOffset()); 208 ASSERT(methodLiteral != nullptr); 209 ObjectFactory *factory = vm->GetFactory(); 210 JSHandle<Method> method = factory->NewMethod(jsPandaFile, methodLiteral, constpoolHandle, 211 entryIndex, isLoadedAOT && hasEntryIndex); 212 constpoolHandle->SetObjectToCache(thread, index, method.GetTaggedValue()); 213 return method.GetTaggedValue(); 214 } 215 GetClassLiteralFromCache(JSThread * thread,JSHandle<ConstantPool> constpool,uint32_t literal,CString entry)216 static JSTaggedValue GetClassLiteralFromCache(JSThread *thread, JSHandle<ConstantPool> constpool, 217 uint32_t literal, CString entry) 218 { 219 [[maybe_unused]] EcmaHandleScope handleScope(thread); 220 auto val = constpool->GetObjectFromCache(literal); 221 JSPandaFile *jsPandaFile = constpool->GetJSPandaFile(); 222 223 // For AOT 224 bool isLoadedAOT = jsPandaFile->IsLoadedAOT(); 225 JSHandle<AOTLiteralInfo> entryIndexes(thread, JSTaggedValue::Undefined()); 226 if (isLoadedAOT && val.IsAOTLiteralInfo()) { 227 entryIndexes = JSHandle<AOTLiteralInfo>(thread, val); 228 val = JSTaggedValue::Hole(); 229 } 230 231 if (val.IsHole()) { 232 EcmaVM *vm = thread->GetEcmaVM(); 233 ObjectFactory *factory = vm->GetFactory(); 234 ASSERT(jsPandaFile->IsNewVersion()); 235 panda_file::File::EntityId literalId = constpool->GetEntityId(literal); 236 bool needSetAotFlag = isLoadedAOT && !entryIndexes.GetTaggedValue().IsUndefined(); 237 JSHandle<TaggedArray> literalArray = LiteralDataExtractor::GetDatasIgnoreType( 238 thread, jsPandaFile, literalId, constpool, entry, needSetAotFlag, entryIndexes); 239 JSHandle<ClassLiteral> classLiteral = factory->NewClassLiteral(); 240 classLiteral->SetArray(thread, literalArray); 241 val = classLiteral.GetTaggedValue(); 242 constpool->SetObjectToCache(thread, literal, val); 243 } 244 245 return val; 246 } 247 248 template <ConstPoolType type> GetLiteralFromCache(JSThread * thread,JSTaggedValue constpool,uint32_t index,CString entry)249 static JSTaggedValue GetLiteralFromCache(JSThread *thread, JSTaggedValue constpool, uint32_t index, CString entry) 250 { 251 static_assert(type == ConstPoolType::OBJECT_LITERAL || type == ConstPoolType::ARRAY_LITERAL); 252 [[maybe_unused]] EcmaHandleScope handleScope(thread); 253 const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); 254 auto val = taggedPool->GetObjectFromCache(index); 255 JSPandaFile *jsPandaFile = taggedPool->GetJSPandaFile(); 256 257 // For AOT 258 bool isLoadedAOT = jsPandaFile->IsLoadedAOT(); 259 JSHandle<AOTLiteralInfo> entryIndexes(thread, JSTaggedValue::Undefined()); 260 if (isLoadedAOT && val.IsAOTLiteralInfo()) { 261 entryIndexes = JSHandle<AOTLiteralInfo>(thread, val); 262 val = JSTaggedValue::Hole(); 263 } 264 265 if (val.IsHole()) { 266 JSHandle<ConstantPool> constpoolHandle(thread, constpool); 267 268 ASSERT(jsPandaFile->IsNewVersion()); 269 panda_file::File::EntityId id = taggedPool->GetEntityId(index); 270 bool needSetAotFlag = isLoadedAOT && !entryIndexes.GetTaggedValue().IsUndefined(); 271 // New inst 272 switch (type) { 273 case ConstPoolType::OBJECT_LITERAL: { 274 JSMutableHandle<TaggedArray> elements(thread, JSTaggedValue::Undefined()); 275 JSMutableHandle<TaggedArray> properties(thread, JSTaggedValue::Undefined()); 276 LiteralDataExtractor::ExtractObjectDatas(thread, jsPandaFile, id, elements, 277 properties, constpoolHandle, entry, needSetAotFlag, entryIndexes); 278 JSTaggedValue ihcVal = JSTaggedValue::Undefined(); 279 if (needSetAotFlag) { 280 ihcVal = entryIndexes->GetIhc(); 281 } 282 JSHandle<JSObject> obj = JSObject::CreateObjectFromProperties(thread, properties, ihcVal); 283 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined()); 284 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined()); 285 size_t elementsLen = elements->GetLength(); 286 for (size_t i = 0; i < elementsLen; i += 2) { // 2: Each literal buffer has a pair of key-value. 287 key.Update(elements->Get(i)); 288 if (key->IsHole()) { 289 break; 290 } 291 valueHandle.Update(elements->Get(i + 1)); 292 JSObject::DefinePropertyByLiteral(thread, obj, key, valueHandle); 293 } 294 295 val = obj.GetTaggedValue(); 296 break; 297 } 298 case ConstPoolType::ARRAY_LITERAL: { 299 JSHandle<TaggedArray> literal = LiteralDataExtractor::GetDatasIgnoreType(thread, jsPandaFile, id, 300 constpoolHandle, entry, needSetAotFlag, entryIndexes); 301 uint32_t length = literal->GetLength(); 302 JSHandle<JSArray> arr(JSArray::ArrayCreate(thread, JSTaggedNumber(length), ArrayMode::LITERAL)); 303 arr->SetElements(thread, literal); 304 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { 305 JSHClass::TransitToElementsKind(thread, arr); 306 } 307 val = arr.GetTaggedValue(); 308 break; 309 } 310 default: 311 LOG_FULL(FATAL) << "Unknown type: " << static_cast<uint8_t>(type); 312 UNREACHABLE(); 313 } 314 constpoolHandle->SetObjectToCache(thread, index, val); 315 } 316 317 return val; 318 } 319 GetIdFromCache(JSTaggedValue constpool,uint32_t index)320 static panda_file::File::EntityId GetIdFromCache(JSTaggedValue constpool, uint32_t index) 321 { 322 const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); 323 panda_file::File::EntityId id = taggedPool->GetEntityId(index); 324 return id; 325 } 326 327 template <ConstPoolType type> GetLiteralFromCache(JSThread * thread,JSTaggedValue constpool,uint32_t index,JSTaggedValue module)328 static JSTaggedValue GetLiteralFromCache(JSThread *thread, JSTaggedValue constpool, 329 uint32_t index, JSTaggedValue module) 330 { 331 CString entry = ModuleManager::GetRecordName(module); 332 return GetLiteralFromCache<type>(thread, constpool, index, entry); 333 } 334 GetStringFromCache(JSThread * thread,JSTaggedValue constpool,uint32_t index)335 static JSTaggedValue PUBLIC_API GetStringFromCache(JSThread *thread, JSTaggedValue constpool, uint32_t index) 336 { 337 const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); 338 auto val = taggedPool->Get(index); 339 if (val.IsHole()) { 340 [[maybe_unused]] EcmaHandleScope handleScope(thread); 341 342 JSPandaFile *jsPandaFile = taggedPool->GetJSPandaFile(); 343 panda_file::File::EntityId id = taggedPool->GetEntityId(index); 344 auto foundStr = jsPandaFile->GetStringData(id); 345 346 EcmaVM *vm = thread->GetEcmaVM(); 347 ObjectFactory *factory = vm->GetFactory(); 348 JSHandle<ConstantPool> constpoolHandle(thread, constpool); 349 auto string = factory->GetRawStringFromStringTable(foundStr, MemSpaceType::OLD_SPACE, 350 jsPandaFile->IsFirstMergedAbc(), id.GetOffset()); 351 352 val = JSTaggedValue(string); 353 constpoolHandle->SetObjectToCache(thread, index, val); 354 } 355 356 return val; 357 } 358 359 DECL_VISIT_ARRAY(DATA_OFFSET, GetCacheLength()); 360 DECL_VISIT_NATIVE_FIELD(GetLastOffset() - JSTaggedValue::TaggedTypeSize() * RESERVED_POOL_LENGTH, GetLastOffset()); 361 DECL_DUMP()362 DECL_DUMP() 363 364 private: 365 inline size_t GetJSPandaFileOffset() const 366 { 367 return JSTaggedValue::TaggedTypeSize() * (GetLength() - JS_PANDA_FILE_INDEX); 368 } 369 GetIndexHeaderOffset()370 inline size_t GetIndexHeaderOffset() const 371 { 372 return JSTaggedValue::TaggedTypeSize() * (GetLength() - INDEX_HEADER_INDEX); 373 } 374 GetLastOffset()375 inline size_t GetLastOffset() const 376 { 377 return JSTaggedValue::TaggedTypeSize() * GetLength() + DATA_OFFSET; 378 } 379 380 static JSHandle<ConstantPool> GetDeserializedConstantPool(EcmaVM *vm, const JSPandaFile *jsPandaFile, int32_t cpID); 381 }; 382 } // namespace ecmascript 383 } // namespace panda 384 #endif // ECMASCRIPT_JSPANDAFILE_PROGRAM_OBJECT_H 385