/* * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/ecma_vm.h" #include "ecmascript/builtins/builtins_ark_tools.h" #ifdef ARK_SUPPORT_INTL #include "ecmascript/builtins/builtins_collator.h" #include "ecmascript/builtins/builtins_date_time_format.h" #include "ecmascript/builtins/builtins_number_format.h" #endif #include "ecmascript/builtins/builtins_global.h" #include "ecmascript/builtins/builtins_object.h" #include "ecmascript/builtins/builtins_promise_handler.h" #include "ecmascript/builtins/builtins_proxy.h" #include "ecmascript/jit/jit_task.h" #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h" #endif #if !WIN_OR_MAC_OR_IOS_PLATFORM #include "ecmascript/dfx/hprof/heap_profiler.h" #include "ecmascript/dfx/hprof/heap_profiler_interface.h" #endif #include "ecmascript/dfx/tracing/tracing.h" #include "ecmascript/dfx/vmstat/function_call_timer.h" #include "ecmascript/mem/shared_heap/shared_concurrent_marker.h" #include "ecmascript/module/module_logger.h" #include "ecmascript/pgo_profiler/pgo_trace.h" #include "ecmascript/regexp/regexp_parser_cache.h" #include "ecmascript/runtime.h" #include "ecmascript/snapshot/mem/snapshot.h" #include "ecmascript/stubs/runtime_stubs.h" #include "ecmascript/ohos/jit_tools.h" #include "ecmascript/ohos/aot_tools.h" #include "ecmascript/checkpoint/thread_state_transition.h" #include "ecmascript/mem/heap-inl.h" #if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE) #include "parameters.h" #endif namespace panda::ecmascript { using RandomGenerator = base::RandomGenerator; using PGOProfilerManager = pgo::PGOProfilerManager; using JitTools = ohos::JitTools; AOTFileManager *JsStackInfo::loader = nullptr; JSRuntimeOptions *JsStackInfo::options = nullptr; bool EcmaVM::multiThreadCheck_ = false; bool EcmaVM::errorInfoEnhanced_ = false; EcmaVM *EcmaVM::Create(const JSRuntimeOptions &options) { Runtime::CreateIfFirstVm(options); auto heapType = options.IsWorker() ? EcmaParamConfiguration::HeapType::WORKER_HEAP : EcmaParamConfiguration::HeapType::DEFAULT_HEAP; size_t heapSize = options.GetHeapSize(); #if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE) switch (heapType) { case EcmaParamConfiguration::HeapType::WORKER_HEAP: heapSize = OHOS::system::GetUintParameter("persist.ark.heap.workersize", 0) * 1_MB; break; default: heapSize = OHOS::system::GetUintParameter("persist.ark.heap.defaultsize", 0) * 1_MB; break; } #endif auto config = EcmaParamConfiguration(heapType, MemMapAllocator::GetInstance()->GetCapacity(), heapSize); JSRuntimeOptions newOptions = options; // only define SUPPORT_ENABLE_ASM_INTERP can enable asm-interpreter #if !defined(SUPPORT_ENABLE_ASM_INTERP) newOptions.SetEnableAsmInterpreter(false); #endif auto vm = new EcmaVM(newOptions, config); auto jsThread = JSThread::Create(vm); vm->thread_ = jsThread; Runtime::GetInstance()->InitializeIfFirstVm(vm); if (JsStackInfo::loader == nullptr) { JsStackInfo::loader = vm->GetAOTFileManager(); } if (JsStackInfo::options == nullptr) { JsStackInfo::options = &(vm->GetJSOptions()); } #if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE) int arkProperties = OHOS::system::GetIntParameter("persist.ark.properties", -1); vm->GetJSOptions().SetArkProperties(arkProperties); #endif return vm; } // static bool EcmaVM::Destroy(EcmaVM *vm) { if (UNLIKELY(vm == nullptr)) { return false; } delete vm; Runtime::DestroyIfLastVm(); return true; } void EcmaVM::PreFork() { heap_->CompactHeapBeforeFork(); heap_->AdjustSpaceSizeForAppSpawn(); heap_->GetReadOnlySpace()->SetReadOnly(); heap_->DisableParallelGC(); SetPostForked(false); auto sHeap = SharedHeap::GetInstance(); sHeap->CompactHeapBeforeFork(thread_); sHeap->DisableParallelGC(thread_); } void EcmaVM::PostFork() { RandomGenerator::InitRandom(GetAssociatedJSThread()); heap_->SetHeapMode(HeapMode::SHARE); GetAssociatedJSThread()->PostFork(); Taskpool::GetCurrentTaskpool()->Initialize(); SetPostForked(true); LOG_ECMA(INFO) << "multi-thread check enabled: " << GetThreadCheckStatus(); SignalAllReg(); SharedHeap::GetInstance()->EnableParallelGC(GetJSOptions()); DaemonThread::GetInstance()->StartRunning(); heap_->EnableParallelGC(); options_.SetPgoForceDump(false); std::string bundleName = PGOProfilerManager::GetInstance()->GetBundleName(); pgo::PGOTrace::GetInstance()->SetEnable(ohos::AotTools::GetPgoTraceEnable()); AotCrashInfo::GetInstance().SetOptionPGOProfiler(&options_, bundleName); ResetPGOProfiler(); processStartRealtime_ = InitializeStartRealTime(); Jit::GetInstance()->SetJitEnablePostFork(this, bundleName); #ifdef ENABLE_POSTFORK_FORCEEXPAND heap_->NotifyPostFork(); heap_->NotifyFinishColdStartSoon(); #endif ModuleLogger *moduleLogger = thread_->GetCurrentEcmaContext()->GetModuleLogger(); if (moduleLogger != nullptr) { moduleLogger->PostModuleLoggerTask(thread_->GetThreadId(), this); } #if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE) int arkProperties = OHOS::system::GetIntParameter("persist.ark.properties", -1); GetJSOptions().SetArkProperties(arkProperties); #endif auto startIdleMonitor = JSNApi::GetStartIdleMonitorCallback(); if (startIdleMonitor != nullptr) { startIdleMonitor(); } } EcmaVM::EcmaVM(JSRuntimeOptions options, EcmaParamConfiguration config) : nativeAreaAllocator_(std::make_unique()), heapRegionAllocator_(std::make_unique(options)), chunk_(nativeAreaAllocator_.get()), ecmaParamConfiguration_(std::move(config)) { options_ = std::move(options); LOG_ECMA(DEBUG) << "multi-thread check enabled: " << GetThreadCheckStatus(); icEnabled_ = options_.EnableIC(); optionalLogEnabled_ = options_.EnableOptionalLog(); options_.ParseAsmInterOption(); SetEnableOsr(options_.IsEnableOSR() && options_.IsEnableJIT() && options_.GetEnableAsmInterpreter()); processStartRealtime_ = InitializeStartRealTime(); } // for jit EcmaVM::EcmaVM() : nativeAreaAllocator_(std::make_unique()), heapRegionAllocator_(nullptr), chunk_(nativeAreaAllocator_.get()) {} void EcmaVM::InitializeForJit(JitThread *jitThread) { thread_ = jitThread; stringTable_ = Runtime::GetInstance()->GetEcmaStringTable(); ASSERT(stringTable_); // ObjectFactory only sypport alloc string in sharedheap factory_ = chunk_.New(thread_, nullptr, SharedHeap::GetInstance()); SetIsJitCompileVM(true); } void EcmaVM::InitializePGOProfiler() { bool isEnablePGOProfiler = IsEnablePGOProfiler(); if (pgoProfiler_ == nullptr) { pgoProfiler_ = PGOProfilerManager::GetInstance()->Build(this, isEnablePGOProfiler); } pgo::PGOTrace::GetInstance()->SetEnable(options_.GetPGOTrace() || ohos::AotTools::GetPgoTraceEnable()); thread_->SetPGOProfilerEnable(isEnablePGOProfiler); } void EcmaVM::ResetPGOProfiler() { if (pgoProfiler_ != nullptr) { bool isEnablePGOProfiler = IsEnablePGOProfiler(); PGOProfilerManager::GetInstance()->Reset(pgoProfiler_, isEnablePGOProfiler); thread_->SetPGOProfilerEnable(isEnablePGOProfiler); thread_->CheckOrSwitchPGOStubs(); } } void EcmaVM::DisablePGOProfilerWithAOTFile(const std::string &aotFileName) { if (AOTFileManager::AOTFileExist(aotFileName, AOTFileManager::FILE_EXTENSION_AN) || AOTFileManager::AOTFileExist(aotFileName, AOTFileManager::FILE_EXTENSION_AI)) { options_.SetEnablePGOProfiler(false); PGOProfilerManager::GetInstance()->SetDisablePGO(true); ResetPGOProfiler(); } } bool EcmaVM::IsEnablePGOProfiler() const { if (options_.IsWorker()) { return PGOProfilerManager::GetInstance()->IsEnable(); } return options_.GetEnableAsmInterpreter() && options_.IsEnablePGOProfiler(); } bool EcmaVM::IsEnableElementsKind() const { return options_.GetEnableAsmInterpreter() && options_.IsEnableElementsKind(); } bool EcmaVM::IsEnableFastJit() const { return GetJit()->IsEnableFastJit(); } bool EcmaVM::IsEnableBaselineJit() const { return GetJit()->IsEnableBaselineJit(); } uint32_t EcmaVM::GetTid() const { return thread_->GetThreadId(); } Jit *EcmaVM::GetJit() const { return Jit::GetInstance(); } bool EcmaVM::Initialize() { ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "EcmaVM::Initialize"); stringTable_ = Runtime::GetInstance()->GetEcmaStringTable(); InitializePGOProfiler(); Taskpool::GetCurrentTaskpool()->Initialize(); #ifndef PANDA_TARGET_WINDOWS RuntimeStubs::Initialize(thread_); #endif heap_ = new Heap(this); heap_->Initialize(); gcStats_ = chunk_.New(heap_, options_.GetLongPauseTime()); gcKeyStats_ = chunk_.New(heap_, gcStats_); factory_ = chunk_.New(thread_, heap_, SharedHeap::GetInstance()); if (UNLIKELY(factory_ == nullptr)) { LOG_FULL(FATAL) << "alloc factory_ failed"; UNREACHABLE(); } debuggerManager_ = new tooling::JsDebuggerManager(this); aotFileManager_ = new AOTFileManager(this); auto context = new EcmaContext(thread_); thread_->PushContext(context); [[maybe_unused]] EcmaHandleScope scope(thread_); thread_->SetReadyForGCIterating(true); thread_->SetSharedMarkStatus(DaemonThread::GetInstance()->GetSharedMarkStatus()); snapshotEnv_ = new SnapshotEnv(this); context->Initialize(); snapshotEnv_->AddGlobalConstToMap(); thread_->SetGlueGlobalEnv(reinterpret_cast(context->GetGlobalEnv().GetTaggedType())); thread_->SetGlobalObject(GetGlobalEnv()->GetGlobalObject()); thread_->SetCurrentEcmaContext(context); GenerateInternalNativeMethods(); quickFixManager_ = new QuickFixManager(); if (options_.GetEnableAsmInterpreter()) { thread_->GetCurrentEcmaContext()->LoadStubFile(); } if (options_.EnableEdenGC()) { heap_->EnableEdenGC(); } callTimer_ = new FunctionCallTimer(); strategy_ = new ThroughputJSObjectResizingStrategy(); if (IsEnableFastJit() || IsEnableBaselineJit()) { Jit::GetInstance()->ConfigJit(this); } initialized_ = true; return true; } EcmaVM::~EcmaVM() { if (isJitCompileVM_) { if (factory_ != nullptr) { chunk_.Delete(factory_); factory_ = nullptr; } stringTable_ = nullptr; thread_ = nullptr; return; } #if ECMASCRIPT_ENABLE_THREAD_STATE_CHECK if (UNLIKELY(!thread_->IsInRunningStateOrProfiling())) { LOG_ECMA(FATAL) << "Destruct VM must be in jsthread running state"; UNREACHABLE(); } #endif initialized_ = false; #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) if (profiler_ != nullptr) { if (profiler_->GetOutToFile()) { DFXJSNApi::StopCpuProfilerForFile(this); } else { DFXJSNApi::StopCpuProfilerForInfo(this); } } if (profiler_ != nullptr) { delete profiler_; profiler_ = nullptr; } #endif #if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) DeleteHeapProfile(); #endif if (IsEnableFastJit() || IsEnableBaselineJit()) { GetJit()->ClearTaskWithVm(this); } // clear c_address: c++ pointer delete ClearBufferData(); heap_->WaitAllTasksFinished(); Taskpool::GetCurrentTaskpool()->Destroy(thread_->GetThreadId()); if (pgoProfiler_ != nullptr) { PGOProfilerManager::GetInstance()->Destroy(pgoProfiler_); pgoProfiler_ = nullptr; } #if ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER DumpCallTimeInfo(); #endif #if defined(ECMASCRIPT_SUPPORT_TRACING) if (tracing_) { DFXJSNApi::StopTracing(this); } #endif thread_->GetCurrentEcmaContext()->GetModuleManager()->NativeObjDestory(); if (!isBundlePack_) { std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(assetPath_); if (jsPandaFile != nullptr) { jsPandaFile->DeleteParsedConstpoolVM(this); } } if (gcStats_ != nullptr) { if (options_.EnableGCStatsPrint()) { gcStats_->PrintStatisticResult(); } chunk_.Delete(gcStats_); gcStats_ = nullptr; } if (gcKeyStats_ != nullptr) { chunk_.Delete(gcKeyStats_); gcKeyStats_ = nullptr; } if (JsStackInfo::loader == aotFileManager_) { JsStackInfo::loader = nullptr; } if (heap_ != nullptr) { heap_->Destroy(); delete heap_; heap_ = nullptr; } SharedHeap *sHeap = SharedHeap::GetInstance(); const Heap *heap = Runtime::GetInstance()->GetMainThread()->GetEcmaVM()->GetHeap(); if (IsWorkerThread() && Runtime::SharedGCRequest()) { // destory workervm to release mem. thread_->SetReadyForGCIterating(false); if (sHeap->CheckCanTriggerConcurrentMarking(thread_)) { sHeap->TriggerConcurrentMarking(thread_); } else if (heap && !heap->InSensitiveStatus() && !sHeap->GetConcurrentMarker()->IsEnabled()) { sHeap->CollectGarbage(thread_); } } if (debuggerManager_ != nullptr) { delete debuggerManager_; debuggerManager_ = nullptr; } if (aotFileManager_ != nullptr) { delete aotFileManager_; aotFileManager_ = nullptr; } if (factory_ != nullptr) { chunk_.Delete(factory_); factory_ = nullptr; } if (stringTable_ != nullptr) { stringTable_ = nullptr; } if (quickFixManager_ != nullptr) { delete quickFixManager_; quickFixManager_ = nullptr; } if (snapshotEnv_ != nullptr) { snapshotEnv_->ClearEnvMap(); delete snapshotEnv_; snapshotEnv_ = nullptr; } if (callTimer_ != nullptr) { delete callTimer_; callTimer_ = nullptr; } if (strategy_ != nullptr) { delete strategy_; strategy_ = nullptr; } if (thread_ != nullptr) { delete thread_; thread_ = nullptr; } } JSHandle EcmaVM::GetGlobalEnv() const { return thread_->GetCurrentEcmaContext()->GetGlobalEnv(); } void EcmaVM::CheckThread() const { // Exclude GC thread if (thread_ == nullptr) { LOG_FULL(FATAL) << "Fatal: ecma_vm has been destructed! vm address is: " << this; UNREACHABLE(); } if (!Taskpool::GetCurrentTaskpool()->IsDaemonThreadOrInThreadPool(std::this_thread::get_id()) && thread_->GetThreadId() != JSThread::GetCurrentThreadId() && !thread_->IsCrossThreadExecutionEnable()) { LOG_FULL(FATAL) << "Fatal: ecma_vm cannot run in multi-thread!" << " thread:" << thread_->GetThreadId() << " currentThread:" << JSThread::GetCurrentThreadId(); UNREACHABLE(); } } bool EcmaVM::CheckSingleThread() const { if (thread_ == nullptr) { LOG_FULL(FATAL) << "Fatal: ecma_vm has been destructed! vm address is: " << this; return false; } if (thread_->GetThreadId() != JSThread::GetCurrentThreadId()) { LOG_FULL(FATAL) << "Fatal: ecma_vm cannot run in multi-thread!" << " thread:" << thread_->GetThreadId() << " currentThread:" << JSThread::GetCurrentThreadId(); return false; } return true; } JSTaggedValue EcmaVM::FastCallAot(size_t actualNumArgs, JSTaggedType *args, const JSTaggedType *prevFp) { INTERPRETER_TRACE(thread_, ExecuteAot); ASSERT(thread_->IsInManagedState()); auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_OptimizedFastCallEntry); // entry of aot auto res = reinterpret_cast(entry)(thread_->GetGlueAddr(), actualNumArgs, args, reinterpret_cast(prevFp)); return res; } void EcmaVM::CheckStartCpuProfiler() { #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) if (options_.EnableCpuProfilerColdStartMainThread() && options_.GetArkBundleName().compare(bundleName_) == 0 && !options_.IsWorker() && profiler_ == nullptr) { std::string fileName = options_.GetArkBundleName() + ".cpuprofile"; if (!builtins::BuiltinsArkTools::CreateFile(fileName)) { LOG_ECMA(ERROR) << "createFile failed " << fileName; return; } else { DFXJSNApi::StartCpuProfilerForFile(this, fileName, CpuProfiler::INTERVAL_OF_INNER_START); return; } } if (options_.EnableCpuProfilerColdStartWorkerThread() && options_.GetArkBundleName().compare(bundleName_) == 0 && options_.IsWorker() && profiler_ == nullptr) { std::string fileName = options_.GetArkBundleName() + "_" + std::to_string(thread_->GetThreadId()) + ".cpuprofile"; if (!builtins::BuiltinsArkTools::CreateFile(fileName)) { LOG_ECMA(ERROR) << "createFile failed " << fileName; return; } else { DFXJSNApi::StartCpuProfilerForFile(this, fileName, CpuProfiler::INTERVAL_OF_INNER_START); return; } } #endif } JSHandle EcmaVM::GetAndClearEcmaUncaughtException() const { JSHandle exceptionHandle = GetEcmaUncaughtException(); thread_->ClearException(); // clear for ohos app return exceptionHandle; } JSHandle EcmaVM::GetEcmaUncaughtException() const { if (!thread_->HasPendingException()) { return JSHandle(); } JSHandle exceptionHandle(thread_, thread_->GetException()); return exceptionHandle; } void EcmaVM::PrintAOTSnapShotStats() { static constexpr int nameRightAdjustment = 30; static constexpr int numberRightAdjustment = 30; LOG_ECMA(ERROR) << std::right << std::setw(nameRightAdjustment) << "AOT Snapshot Genre" << std::setw(numberRightAdjustment) << "Count"; LOG_ECMA(ERROR) << "=========================================================================="; for (const auto &iter: aotSnapShotStatsMap_) { LOG_ECMA(ERROR) << std::right << std::setw(nameRightAdjustment) << iter.first << std::setw(numberRightAdjustment) << iter.second; } LOG_ECMA(ERROR) << "=========================================================================="; aotSnapShotStatsMap_.clear(); } void EcmaVM::PrintJSErrorInfo(const JSHandle &exceptionInfo) const { EcmaContext::PrintJSErrorInfo(thread_, exceptionInfo); } void EcmaVM::ProcessNativeDelete(const WeakRootVisitor& visitor) { heap_->ProcessNativeDelete(visitor); } void EcmaVM::ProcessSharedNativeDelete(const WeakRootVisitor& visitor) { heap_->ProcessSharedNativeDelete(visitor); } void EcmaVM::ProcessReferences(const WeakRootVisitor& visitor) { heap_->ProcessReferences(visitor); GetPGOProfiler()->ProcessReferences(visitor); } void EcmaVM::PushToNativePointerList(JSNativePointer* pointer, Concurrent isConcurrent) { heap_->PushToNativePointerList(pointer, isConcurrent == Concurrent::YES); } void EcmaVM::PushToSharedNativePointerList(JSNativePointer* pointer) { heap_->PushToSharedNativePointerList(pointer); } void EcmaVM::RemoveFromNativePointerList(JSNativePointer* pointer) { heap_->RemoveFromNativePointerList(pointer); } void EcmaVM::PushToDeregisterModuleList(const CString &module) { deregisterModuleList_.emplace_back(module); } void EcmaVM::RemoveFromDeregisterModuleList(CString module) { auto iter = std::find(deregisterModuleList_.begin(), deregisterModuleList_.end(), module); if (iter != deregisterModuleList_.end()) { deregisterModuleList_.erase(iter); } } bool EcmaVM::ContainInDeregisterModuleList(CString module) { return (std::find(deregisterModuleList_.begin(), deregisterModuleList_.end(), module) != deregisterModuleList_.end()); } void EcmaVM::ClearBufferData() { heap_->ClearNativePointerList(); thread_->GetCurrentEcmaContext()->ClearBufferData(); internalNativeMethods_.clear(); workerList_.clear(); deregisterModuleList_.clear(); } void EcmaVM::CollectGarbage(TriggerGCType gcType, panda::ecmascript::GCReason reason) const { heap_->CollectGarbage(gcType, reason); } void EcmaVM::Iterate(const RootVisitor &v, const RootRangeVisitor &rv, VMRootVisitType type) { rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&internalNativeMethods_.front())), ObjectSlot(ToUintPtr(&internalNativeMethods_.back()) + JSTaggedValue::TaggedTypeSize())); if (!WIN_OR_MAC_OR_IOS_PLATFORM && snapshotEnv_!= nullptr) { snapshotEnv_->Iterate(v, type); } if (pgoProfiler_ != nullptr) { pgoProfiler_->Iterate(v); } if (aotFileManager_) { aotFileManager_->Iterate(v); } } #if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) void EcmaVM::DeleteHeapProfile() { if (heapProfile_ == nullptr) { return; } delete heapProfile_; heapProfile_ = nullptr; } HeapProfilerInterface *EcmaVM::GetHeapProfile() { if (heapProfile_ != nullptr) { return heapProfile_; } return nullptr; } HeapProfilerInterface *EcmaVM::GetOrNewHeapProfile() { if (heapProfile_ != nullptr) { return heapProfile_; } heapProfile_ = new HeapProfiler(this); ASSERT(heapProfile_ != nullptr); return heapProfile_; } void EcmaVM::StartHeapTracking() { heap_->StartHeapTracking(); } void EcmaVM::StopHeapTracking() { heap_->StopHeapTracking(); } #endif // NOLINTNEXTLINE(modernize-avoid-c-arrays) void *EcmaVM::InternalMethodTable[] = { reinterpret_cast(builtins::BuiltinsGlobal::CallJsBoundFunction), reinterpret_cast(builtins::BuiltinsGlobal::CallJsProxy), reinterpret_cast(builtins::BuiltinsObject::CreateDataPropertyOnObjectFunctions), #ifdef ARK_SUPPORT_INTL reinterpret_cast(builtins::BuiltinsCollator::AnonymousCollator), reinterpret_cast(builtins::BuiltinsDateTimeFormat::AnonymousDateTimeFormat), reinterpret_cast(builtins::BuiltinsNumberFormat::NumberFormatInternalFormatNumber), #endif reinterpret_cast(builtins::BuiltinsProxy::InvalidateProxyFunction), reinterpret_cast(builtins::BuiltinsPromiseHandler::AsyncAwaitFulfilled), reinterpret_cast(builtins::BuiltinsPromiseHandler::AsyncAwaitRejected), reinterpret_cast(builtins::BuiltinsPromiseHandler::ResolveElementFunction), reinterpret_cast(builtins::BuiltinsPromiseHandler::Resolve), reinterpret_cast(builtins::BuiltinsPromiseHandler::Reject), reinterpret_cast(builtins::BuiltinsPromiseHandler::Executor), reinterpret_cast(builtins::BuiltinsPromiseHandler::AnyRejectElementFunction), reinterpret_cast(builtins::BuiltinsPromiseHandler::AllSettledResolveElementFunction), reinterpret_cast(builtins::BuiltinsPromiseHandler::AllSettledRejectElementFunction), reinterpret_cast(builtins::BuiltinsPromiseHandler::ThenFinally), reinterpret_cast(builtins::BuiltinsPromiseHandler::CatchFinally), reinterpret_cast(builtins::BuiltinsPromiseHandler::valueThunkFunction), reinterpret_cast(builtins::BuiltinsPromiseHandler::throwerFunction), reinterpret_cast(JSAsyncGeneratorObject::ProcessorFulfilledFunc), reinterpret_cast(JSAsyncGeneratorObject::ProcessorRejectedFunc), reinterpret_cast(JSAsyncFromSyncIterator::AsyncFromSyncIterUnwarpFunction), reinterpret_cast(SourceTextModule::AsyncModuleFulfilledFunc), reinterpret_cast(SourceTextModule::AsyncModuleRejectedFunc) }; void EcmaVM::GenerateInternalNativeMethods() { size_t length = static_cast(MethodIndex::METHOD_END); constexpr uint32_t numArgs = 2; // function object and this for (size_t i = 0; i < length; i++) { auto method = factory_->NewSMethod(nullptr, MemSpaceType::SHARED_NON_MOVABLE); method->SetNativePointer(InternalMethodTable[i]); method->SetNativeBit(true); method->SetNumArgsWithCallField(numArgs); method->SetFunctionKind(FunctionKind::NORMAL_FUNCTION); internalNativeMethods_.emplace_back(method.GetTaggedValue()); } // cache to global constants shared because context may change CacheToGlobalConstants(GetMethodByIndex(MethodIndex::BUILTINS_GLOBAL_CALL_JS_BOUND_FUNCTION), ConstantIndex::BOUND_FUNCTION_METHOD_INDEX); CacheToGlobalConstants(GetMethodByIndex(MethodIndex::BUILTINS_GLOBAL_CALL_JS_PROXY), ConstantIndex::PROXY_METHOD_INDEX); } void EcmaVM::CacheToGlobalConstants(JSTaggedValue value, ConstantIndex idx) { auto thread = GetJSThread(); auto context = thread->GetCurrentEcmaContext(); auto constants = const_cast(context->GlobalConstants()); constants->SetConstant(idx, value); } JSTaggedValue EcmaVM::GetMethodByIndex(MethodIndex idx) { auto index = static_cast(idx); ASSERT(index < internalNativeMethods_.size()); return internalNativeMethods_[index]; } void EcmaVM::TriggerConcurrentCallback(JSTaggedValue result, JSTaggedValue hint) { if (concurrentCallback_ == nullptr) { LOG_ECMA(DEBUG) << "Only trigger concurrent callback in taskpool thread"; return; } bool success = true; if (result.IsJSPromise()) { // Async concurrent will return Promise auto promise = JSPromise::Cast(result.GetTaggedObject()); auto status = promise->GetPromiseState(); if (status == PromiseState::PENDING) { result = JSHandle::Cast(factory_->GetJSError( ErrorType::ERROR, "Can't return Promise in pending state", StackCheck::NO)).GetTaggedValue(); } else { result = promise->GetPromiseResult(); } if (status != PromiseState::FULFILLED) { success = false; } } JSHandle functionValue(thread_, hint); if (!functionValue->IsJSFunction()) { LOG_ECMA(ERROR) << "TriggerConcurrentCallback hint is not function"; return; } JSHandle functionInfo(functionValue); if (!functionInfo->GetTaskConcurrentFuncFlag()) { LOG_ECMA(INFO) << "Function is not Concurrent Function"; return; } void *taskInfo = reinterpret_cast(thread_->GetTaskInfo()); if (UNLIKELY(taskInfo == nullptr)) { JSTaggedValue extraInfoValue = functionInfo->GetFunctionExtraInfo(); if (!extraInfoValue.IsJSNativePointer()) { LOG_ECMA(INFO) << "FunctionExtraInfo is not JSNativePointer"; return; } JSHandle extraInfo(thread_, extraInfoValue); taskInfo = extraInfo->GetData(); } // clear the taskInfo when return, which can prevent the callback to get it thread_->SetTaskInfo(reinterpret_cast(nullptr)); auto localResultRef = JSNApiHelper::ToLocal(JSHandle(thread_, result)); ThreadNativeScope nativeScope(thread_); concurrentCallback_(localResultRef, success, taskInfo, concurrentData_); } void EcmaVM::DumpCallTimeInfo() { if (callTimer_ != nullptr) { callTimer_->PrintAllStats(); } } void EcmaVM::WorkersetInfo(EcmaVM *workerVm) { LockHolder lock(mutex_); auto thread = workerVm->GetJSThread(); if (thread != nullptr) { auto tid = thread->GetThreadId(); if (tid != 0) { workerList_.emplace(tid, workerVm); } } } EcmaVM *EcmaVM::GetWorkerVm(uint32_t tid) { LockHolder lock(mutex_); EcmaVM *workerVm = nullptr; if (!workerList_.empty()) { auto iter = workerList_.find(tid); if (iter != workerList_.end()) { workerVm = iter->second; } } return workerVm; } bool EcmaVM::DeleteWorker(EcmaVM *workerVm) { LockHolder lock(mutex_); auto thread = workerVm->GetJSThread(); if (thread != nullptr) { auto tid = thread->GetThreadId(); if (tid == 0) { return false; } auto iter = workerList_.find(tid); if (iter != workerList_.end()) { workerList_.erase(iter); return true; } return false; } return false; } bool EcmaVM::SuspendWorkerVm(uint32_t tid) { LockHolder lock(mutex_); if (!workerList_.empty()) { auto iter = workerList_.find(tid); if (iter != workerList_.end()) { return DFXJSNApi::SuspendVM(iter->second); } } return false; } void EcmaVM::ResumeWorkerVm(uint32_t tid) { LockHolder lock(mutex_); if (!workerList_.empty()) { auto iter = workerList_.find(tid); if (iter != workerList_.end()) { DFXJSNApi::ResumeVM(iter->second); } } } /* This moduleName is a readOnly variable for napi, represent which abc is running in current vm. * Get Current recordName: bundleName/moduleName/ets/xxx/xxx * pkg_modules@xxx/xxx/xxx * Get Current fileName: /data/storage/el1/bundle/moduleName/ets/modules.abc * output: moduleName: moduleName * if needRecordName then fileName is: moduleName/ets/modules.abc */ std::pair EcmaVM::GetCurrentModuleInfo(bool needRecordName) { std::pair moduleInfo = EcmaInterpreter::GetCurrentEntryPoint(thread_); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, std::make_pair("", "")); CString recordName = moduleInfo.first; CString fileName = moduleInfo.second; LOG_FULL(INFO) << "Current recordName is " << recordName <<", current fileName is " << fileName; if (needRecordName) { if (fileName.length() > ModulePathHelper::BUNDLE_INSTALL_PATH_LEN && fileName.find(ModulePathHelper::BUNDLE_INSTALL_PATH) == 0) { fileName = fileName.substr(ModulePathHelper::BUNDLE_INSTALL_PATH_LEN); } else { LOG_FULL(ERROR) << " GetCurrentModuleName Fail, fileName is " << fileName; } return std::make_pair(recordName.c_str(), fileName.c_str()); } CString moduleName; if (IsNormalizedOhmUrlPack()) { moduleName = ModulePathHelper::GetModuleNameWithNormalizedName(recordName); } else { moduleName = ModulePathHelper::GetModuleName(recordName); } if (moduleName.empty()) { LOG_FULL(ERROR) << " GetCurrentModuleName Fail, recordName is " << recordName; } return std::make_pair(moduleName.c_str(), fileName.c_str()); } void EcmaVM::SetHmsModuleList(const std::vector &list) { for (size_t i = 0; i < list.size(); i++) { HmsMap hmsMap = list[i]; hmsModuleList_.emplace(hmsMap.originalPath.c_str(), hmsMap); } } CString EcmaVM::GetHmsModule(const CString &module) const { auto it = hmsModuleList_.find(module); if (it == hmsModuleList_.end()) { LOG_ECMA(FATAL) << " Get Hms Module failed"; } HmsMap hmsMap = it->second; return hmsMap.targetPath.c_str(); } bool EcmaVM::IsHmsModule(const CString &moduleStr) const { if (hmsModuleList_.empty()) { return false; } auto it = hmsModuleList_.find(moduleStr); if (it == hmsModuleList_.end()) { return false; } return true; } void EcmaVM::SetpkgContextInfoList(const CMap>> &list) { pkgContextInfoList_ = list; } // Initialize IcuData Path void EcmaVM::InitializeIcuData(const JSRuntimeOptions &options) { std::string icuPath = options.GetIcuDataPath(); if (icuPath == "default") { #if !WIN_OR_MAC_OR_IOS_PLATFORM && !defined(PANDA_TARGET_LINUX) SetHwIcuDirectory(); #endif } else { std::string absPath; if (ecmascript::RealPath(icuPath, absPath)) { u_setDataDirectory(absPath.c_str()); } } } // Initialize Process StartRealTime int EcmaVM::InitializeStartRealTime() { int startRealTime = 0; struct timespec timespro = {0, 0}; struct timespec timessys = {0, 0}; auto res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ×pro); if (res) { return startRealTime; } auto res1 = clock_gettime(CLOCK_MONOTONIC, ×sys); if (res1) { return startRealTime; } int whenpro = int(timespro.tv_sec * 1000) + int(timespro.tv_nsec / 1000000); int whensys = int(timessys.tv_sec * 1000) + int(timessys.tv_nsec / 1000000); startRealTime = (whensys - whenpro); return startRealTime; } } // namespace panda::ecmascript