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_ECMA_VM_H 17 #define ECMASCRIPT_ECMA_VM_H 18 19 #include <tuple> 20 21 #include "ecmascript/base/config.h" 22 #include "ecmascript/ecma_string_table.h" 23 #include "ecmascript/global_handle_collection.h" 24 #include "ecmascript/js_handle.h" 25 #include "ecmascript/js_method.h" 26 #include "ecmascript/js_runtime_options.h" 27 #include "ecmascript/mem/c_containers.h" 28 #include "ecmascript/mem/c_string.h" 29 #include "ecmascript/mem/chunk_containers.h" 30 #include "ecmascript/mem/gc_stats.h" 31 #include "ecmascript/mem/heap.h" 32 #include "ecmascript/mem/object_xray.h" 33 #include "ecmascript/mem/space.h" 34 #include "ecmascript/platform/task.h" 35 #include "ecmascript/snapshot/mem/snapshot_serialize.h" 36 #include "ecmascript/tooling/interface/js_debugger_manager.h" 37 #include "ecmascript/tooling/backend/js_pt_extractor.h" 38 #include "include/panda_vm.h" 39 #include "libpandabase/macros.h" 40 41 namespace panda { 42 namespace panda_file { 43 class File; 44 } // namespace panda_file 45 46 namespace ecmascript { 47 class GlobalEnv; 48 class ObjectFactory; 49 class RegExpParserCache; 50 class EcmaRuntimeStat; 51 class Heap; 52 class HeapTracker; 53 class JSNativePointer; 54 class Program; 55 class RegExpExecResultCache; 56 class JSPromise; 57 enum class PromiseRejectionEvent : uint8_t; 58 class JSPandaFileManager; 59 class JSPandaFile; 60 namespace job { 61 class MicroJobQueue; 62 } // namespace job 63 64 namespace tooling { 65 class JsDebuggerManager; 66 } // namespace tooling 67 68 template<typename T> 69 class JSHandle; 70 class JSArrayBuffer; 71 class JSFunction; 72 class Program; 73 class ModuleManager; 74 class EcmaModule; 75 class TSLoader; 76 struct BytecodeTranslationInfo; 77 using HostPromiseRejectionTracker = void (*)(const EcmaVM* vm, 78 const JSHandle<JSPromise> promise, 79 const JSHandle<JSTaggedValue> reason, 80 PromiseRejectionEvent operation, 81 void* data); 82 using PromiseRejectCallback = void (*)(void* info); 83 using IcuDeleteEntry = void(*)(void *pointer, void *data); 84 85 enum class IcuFormatterType { 86 SimpleDateFormatDefault, 87 SimpleDateFormatDate, 88 SimpleDateFormatTime, 89 NumberFormatter, 90 Collator 91 }; 92 93 class EcmaVM : public PandaVM { 94 public: Cast(PandaVM * object)95 static EcmaVM *Cast(PandaVM *object) 96 { 97 return reinterpret_cast<EcmaVM *>(object); 98 } 99 100 static EcmaVM *Create(const JSRuntimeOptions &options); 101 102 static bool Destroy(PandaVM *vm); 103 104 explicit EcmaVM(JSRuntimeOptions options); 105 106 static Expected<EcmaVM *, CString> Create([[maybe_unused]] Runtime *runtime); 107 108 EcmaVM(); 109 110 ~EcmaVM() override; 111 112 bool ExecuteFromPf(const std::string &filename, std::string_view entryPoint, 113 const std::vector<std::string> &args, bool isModule = false); 114 115 bool ExecuteFromBuffer(const void *buffer, size_t size, std::string_view entryPoint, 116 const std::vector<std::string> &args, const std::string &filename = ""); 117 118 bool PUBLIC_API CollectInfoOfPandaFile(const std::string &filename, std::string_view entryPoint, 119 std::vector<BytecodeTranslationInfo> *infoList, 120 const panda_file::File *&pf); 121 IsInitialized()122 bool IsInitialized() const 123 { 124 return vmInitialized_; 125 } 126 GetFactory()127 ObjectFactory *GetFactory() const 128 { 129 return factory_; 130 } 131 132 bool Initialize() override; 133 134 bool InitializeFinish() override; UninitializeThreads()135 void UninitializeThreads() override {} PreStartup()136 void PreStartup() override {} PreZygoteFork()137 void PreZygoteFork() override {} PostZygoteFork()138 void PostZygoteFork() override {} InitializeGC()139 void InitializeGC() override {} StartGC()140 void StartGC() override {} StopGC()141 void StopGC() override {} 142 VisitVmRoots(const GCRootVisitor & visitor)143 void VisitVmRoots([[maybe_unused]] const GCRootVisitor &visitor) override {} UpdateVmRefs()144 void UpdateVmRefs() override {} 145 GetPandaVMType()146 PandaVMType GetPandaVMType() const override 147 { 148 return PandaVMType::ECMA_VM; 149 } 150 GetLanguageContext()151 LanguageContext GetLanguageContext() const override 152 { 153 return Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT); 154 } 155 GetHeapManager()156 panda::mem::HeapManager *GetHeapManager() const override 157 { 158 return nullptr; 159 } 160 GetGC()161 panda::mem::GC *GetGC() const override 162 { 163 return nullptr; 164 } 165 GetGCTrigger()166 panda::mem::GCTrigger *GetGCTrigger() const override 167 { 168 return nullptr; 169 } 170 GetStringTable()171 StringTable *GetStringTable() const override 172 { 173 return nullptr; 174 } 175 GetGCStats()176 panda::mem::GCStats *GetGCStats() const override 177 { 178 return nullptr; 179 } 180 GetMemStats()181 panda::mem::MemStatsType *GetMemStats() const override 182 { 183 return nullptr; 184 } 185 GetEcmaGCStats()186 GCStats *GetEcmaGCStats() const 187 { 188 return gcStats_; 189 } 190 GetGlobalObjectStorage()191 panda::mem::GlobalObjectStorage *GetGlobalObjectStorage() const override 192 { 193 return nullptr; 194 } 195 GetMonitorPool()196 MonitorPool *GetMonitorPool() const override 197 { 198 return nullptr; 199 } 200 GetThreadManager()201 ThreadManager *GetThreadManager() const override 202 { 203 return nullptr; 204 } 205 GetAssociatedThread()206 ManagedThread *GetAssociatedThread() const override 207 { 208 return thread_; 209 } 210 GetAssociatedJSThread()211 JSThread *GetAssociatedJSThread() const 212 { 213 return thread_; 214 } 215 GetCompiler()216 CompilerInterface *GetCompiler() const override 217 { 218 return nullptr; 219 } 220 GetRendezvous()221 Rendezvous *GetRendezvous() const override 222 { 223 return nullptr; 224 } 225 GetOOMErrorObject()226 ObjectHeader *GetOOMErrorObject() override 227 { 228 // preallocated OOM is not implemented for JS 229 UNREACHABLE(); 230 } 231 GetReferenceProcessor()232 panda::mem::ReferenceProcessor *GetReferenceProcessor() const override 233 { 234 return nullptr; 235 } 236 GetOptions()237 const RuntimeOptions &GetOptions() const override 238 { 239 return Runtime::GetOptions(); 240 } 241 GetJSOptions()242 static const JSRuntimeOptions &GetJSOptions() 243 { 244 return options_; 245 } 246 247 JSHandle<GlobalEnv> GetGlobalEnv() const; 248 249 JSHandle<job::MicroJobQueue> GetMicroJobQueue() const; 250 251 bool ExecutePromisePendingJob() const; 252 GetRegExpParserCache()253 RegExpParserCache *GetRegExpParserCache() const 254 { 255 ASSERT(regExpParserCache_ != nullptr); 256 return regExpParserCache_; 257 } 258 259 JSMethod *GetMethodForNativeFunction(const void *func); 260 GetEcmaStringTable()261 EcmaStringTable *GetEcmaStringTable() const 262 { 263 ASSERT(stringTable_ != nullptr); 264 return stringTable_; 265 } 266 GetJSThread()267 JSThread *GetJSThread() const 268 { 269 #if defined(ECMASCRIPT_ENABLE_THREAD_CHECK) && ECMASCRIPT_ENABLE_THREAD_CHECK 270 // Exclude GC thread 271 if (options_.IsEnableThreadCheck()) { 272 if (!Platform::GetCurrentPlatform()->IsInThreadPool(std::this_thread::get_id()) && 273 thread_->GetThreadId() != JSThread::GetCurrentThreadId()) { 274 LOG(FATAL, RUNTIME) << "Fatal: ecma_vm cannot run in multi-thread!"; 275 } 276 } 277 #endif 278 return thread_; 279 } 280 GetJSThreadNoCheck()281 JSThread *GetJSThreadNoCheck() const 282 { 283 return thread_; 284 } 285 ICEnable()286 bool ICEnable() const 287 { 288 return icEnable_; 289 } 290 291 void PushToArrayDataList(JSNativePointer *array); 292 void RemoveArrayDataList(JSNativePointer *array); 293 294 JSHandle<ecmascript::JSTaggedValue> GetAndClearEcmaUncaughtException() const; 295 JSHandle<ecmascript::JSTaggedValue> GetEcmaUncaughtException() const; 296 void EnableUserUncaughtErrorHandler(); 297 GetRuntimeStat()298 EcmaRuntimeStat *GetRuntimeStat() const 299 { 300 return runtimeStat_; 301 } 302 303 void SetRuntimeStatEnable(bool flag); 304 IsRuntimeStatEnabled()305 bool IsRuntimeStatEnabled() const 306 { 307 return runtimeStatEnabled_; 308 } 309 IsOptionalLogEnabled()310 bool IsOptionalLogEnabled() const 311 { 312 return optionalLogEnabled_; 313 } 314 315 void Iterate(const RootVisitor &v); 316 GetHeap()317 const Heap *GetHeap() const 318 { 319 return heap_; 320 } 321 322 void CollectGarbage(TriggerGCType gcType) const; 323 324 void StartHeapTracking(HeapTracker *tracker); 325 326 void StopHeapTracking(); 327 GetNativeAreaAllocator()328 NativeAreaAllocator *GetNativeAreaAllocator() const 329 { 330 return nativeAreaAllocator_.get(); 331 } 332 GetHeapRegionAllocator()333 HeapRegionAllocator *GetHeapRegionAllocator() const 334 { 335 return heapRegionAllocator_.get(); 336 } 337 GetChunk()338 Chunk *GetChunk() const 339 { 340 return const_cast<Chunk *>(&chunk_); 341 } 342 void ProcessNativeDelete(const WeakRootVisitor &v0); 343 void ProcessReferences(const WeakRootVisitor &v0); 344 345 JSHandle<JSTaggedValue> GetModuleByName(JSHandle<JSTaggedValue> moduleName); 346 347 void ExecuteModule(const std::string &moduleFile, std::string_view entryPoint, 348 const std::vector<std::string> &args); 349 GetModuleManager()350 ModuleManager *GetModuleManager() const 351 { 352 return moduleManager_; 353 } 354 GetTSLoader()355 TSLoader *GetTSLoader() const 356 { 357 return tsLoader_; 358 } 359 void SetupRegExpResultCache(); 360 GetRegExpCache()361 JSHandle<JSTaggedValue> GetRegExpCache() const 362 { 363 return JSHandle<JSTaggedValue>(reinterpret_cast<uintptr_t>(®expCache_)); 364 } 365 SetRegExpCache(JSTaggedValue newCache)366 void SetRegExpCache(JSTaggedValue newCache) 367 { 368 regexpCache_ = newCache; 369 } 370 GetJsDebuggerManager()371 tooling::JsDebuggerManager *GetJsDebuggerManager() const 372 { 373 return debuggerManager_; 374 } 375 SetEnableForceGC(bool enable)376 void SetEnableForceGC(bool enable) 377 { 378 options_.SetEnableForceGC(enable); 379 } 380 SetData(void * data)381 void SetData(void* data) 382 { 383 data_ = data; 384 } 385 SetPromiseRejectCallback(PromiseRejectCallback cb)386 void SetPromiseRejectCallback(PromiseRejectCallback cb) 387 { 388 promiseRejectCallback_ = cb; 389 } 390 GetPromiseRejectCallback()391 PromiseRejectCallback GetPromiseRejectCallback() const 392 { 393 return promiseRejectCallback_; 394 } 395 SetHostPromiseRejectionTracker(HostPromiseRejectionTracker cb)396 void SetHostPromiseRejectionTracker(HostPromiseRejectionTracker cb) 397 { 398 hostPromiseRejectionTracker_ = cb; 399 } 400 PromiseRejectionTracker(const JSHandle<JSPromise> & promise,const JSHandle<JSTaggedValue> & reason,PromiseRejectionEvent operation)401 void PromiseRejectionTracker(const JSHandle<JSPromise> &promise, 402 const JSHandle<JSTaggedValue> &reason, PromiseRejectionEvent operation) 403 { 404 if (hostPromiseRejectionTracker_ != nullptr) { 405 hostPromiseRejectionTracker_(this, promise, reason, operation, data_); 406 } 407 } 408 409 void SetConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool); 410 411 JSTaggedValue FindConstpool(const JSPandaFile *jsPandaFile); 412 413 // For icu objects cache 414 void SetIcuFormatterToCache(IcuFormatterType type, const std::string &locale, void *icuObj, 415 IcuDeleteEntry deleteEntry = nullptr) 416 { 417 EcmaVM::IcuFormatter icuFormatter = IcuFormatter(locale, icuObj, deleteEntry); 418 icuObjCache_.insert_or_assign(type, std::move(icuFormatter)); 419 } 420 GetIcuFormatterFromCache(IcuFormatterType type,std::string locale)421 void *GetIcuFormatterFromCache(IcuFormatterType type, std::string locale) 422 { 423 auto iter = icuObjCache_.find(type); 424 if (iter != icuObjCache_.end()) { 425 EcmaVM::IcuFormatter icuFormatter = iter->second; 426 if (icuFormatter.locale == locale) { 427 return icuFormatter.icuObj; 428 } 429 } 430 return nullptr; 431 } 432 ClearIcuCache()433 void ClearIcuCache() 434 { 435 auto iter = icuObjCache_.begin(); 436 while (iter != icuObjCache_.end()) { 437 EcmaVM::IcuFormatter icuFormatter = iter->second; 438 IcuDeleteEntry deleteEntry = icuFormatter.deleteEntry; 439 if (deleteEntry != nullptr) { 440 deleteEntry(icuFormatter.icuObj, this); 441 } 442 iter->second = EcmaVM::IcuFormatter{}; 443 iter++; 444 } 445 } 446 447 protected: CheckEntrypointSignature(Method * entrypoint)448 bool CheckEntrypointSignature([[maybe_unused]] Method *entrypoint) override 449 { 450 return true; 451 } 452 453 Expected<int, Runtime::Error> InvokeEntrypointImpl(Method *entrypoint, 454 const std::vector<std::string> &args) override; 455 456 void HandleUncaughtException(ObjectHeader *exception) override; 457 458 void PrintJSErrorInfo(const JSHandle<JSTaggedValue> &exceptionInfo); 459 460 private: 461 bool IsFrameworkPandaFile(std::string_view filename) const; 462 463 void SetGlobalEnv(GlobalEnv *global); 464 465 void SetMicroJobQueue(job::MicroJobQueue *queue); 466 467 bool Execute(const JSPandaFile *jsPandaFile, std::string_view entryPoint, const std::vector<std::string> &args); 468 469 Expected<int, Runtime::Error> InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile, const CString &methodName, 470 const std::vector<std::string> &args); 471 472 void InitializeEcmaScriptRunStat(); 473 474 void RedirectMethod(const panda_file::File &pf); 475 476 bool VerifyFilePath(const CString &filePath) const; 477 478 void ClearBufferData(); 479 480 void ClearNativeMethodsData(); 481 482 NO_MOVE_SEMANTIC(EcmaVM); 483 NO_COPY_SEMANTIC(EcmaVM); 484 485 // Useless/deprecated fields in the future: 486 bool isTestMode_ {false}; 487 488 // VM startup states. 489 PUBLIC_API static JSRuntimeOptions options_; 490 bool icEnable_ {true}; 491 bool vmInitialized_ {false}; 492 GCStats *gcStats_ {nullptr}; 493 bool snapshotSerializeEnable_ {false}; 494 bool snapshotDeserializeEnable_ {false}; 495 bool isUncaughtExceptionRegistered_ {false}; 496 497 // VM memory management. 498 EcmaStringTable *stringTable_ {nullptr}; 499 std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_; 500 std::unique_ptr<HeapRegionAllocator> heapRegionAllocator_; 501 Chunk chunk_; 502 Heap *heap_ {nullptr}; 503 ObjectFactory *factory_ {nullptr}; 504 ChunkVector<JSNativePointer *> arrayBufferDataList_; 505 506 // VM execution states. 507 JSThread *thread_ {nullptr}; 508 RegExpParserCache *regExpParserCache_ {nullptr}; 509 JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; 510 JSTaggedValue regexpCache_ {JSTaggedValue::Hole()}; 511 JSTaggedValue microJobQueue_ {JSTaggedValue::Hole()}; 512 bool runtimeStatEnabled_ {false}; 513 EcmaRuntimeStat *runtimeStat_ {nullptr}; 514 515 // App framework resources. 516 JSTaggedValue frameworkProgram_ {JSTaggedValue::Hole()}; 517 CString frameworkAbcFileName_; 518 const JSPandaFile *frameworkPandaFile_ {nullptr}; 519 ChunkVector<JSMethod *> frameworkProgramMethods_; 520 CMap<const JSPandaFile *, JSTaggedValue> pandaFileWithConstpool_ {}; 521 522 // VM resources. 523 CString snapshotFileName_; 524 ChunkUnorderedMap<const void *, JSMethod *> nativeMethodMaps_; 525 ModuleManager *moduleManager_ {nullptr}; 526 TSLoader *tsLoader_ {nullptr}; 527 bool optionalLogEnabled_ {false}; 528 529 // Debugger 530 tooling::JsDebuggerManager *debuggerManager_ {nullptr}; 531 532 // Registered Callbacks 533 PromiseRejectCallback promiseRejectCallback_ {nullptr}; 534 HostPromiseRejectionTracker hostPromiseRejectionTracker_ {nullptr}; 535 void* data_ {nullptr}; 536 537 // For icu objects cache 538 struct IcuFormatter { 539 std::string locale; 540 void *icuObj {nullptr}; 541 IcuDeleteEntry deleteEntry {nullptr}; 542 543 IcuFormatter() = default; 544 IcuFormatter(const std::string &locale, void *icuObj, IcuDeleteEntry deleteEntry = nullptr) localeIcuFormatter545 : locale(locale), icuObj(icuObj), deleteEntry(deleteEntry) {} 546 }; 547 std::unordered_map<IcuFormatterType, IcuFormatter> icuObjCache_; 548 549 friend class SnapShotSerialize; 550 friend class ObjectFactory; 551 friend class ValueSerializer; 552 friend class panda::JSNApi; 553 }; 554 } // namespace ecmascript 555 } // namespace panda 556 557 #endif 558