* 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/js_thread.h"
#include "ecmascript/base/config.h"
#include "ecmascript/mem/local_cmc/cc_evacuator-inl.h"
#include "ecmascript/mem/local_cmc/concurrent_copy_gc.h"
#include "ecmascript/mem/tagged_state_word.h"
#include "ecmascript/runtime.h"
#include "ecmascript/debugger/js_debugger_manager.h"
#include "ecmascript/dependent_infos.h"
#include "ecmascript/ic/mega_ic_cache.h"
#include "ecmascript/js_date.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/module/module_logger.h"
#include "ecmascript/module/js_module_manager.h"
#include "ecmascript/runtime_call_id.h"
#include "macros.h"
#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
#include <sys/resource.h>
#endif
#if defined(ENABLE_EXCEPTION_BACKTRACE)
#include "ecmascript/platform/backtrace.h"
#endif
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#endif
#include "common_components/heap/allocator/region_desc.h"
#include "ecmascript/dfx/vm_thread_control.h"
#include "ecmascript/ecma_global_storage.h"
#include "ecmascript/ic/properties_cache.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/jit/jit.h"
#include "thread/thread_holder_manager.h"
#include "ecmascript/checkpoint/thread_state_transition.h"
#include "ecmascript/platform/asm_stack.h"
namespace panda::ecmascript {
uintptr_t TaggedStateWord::BASE_ADDRESS = 0;
using CommonStubCSigns = panda::ecmascript::kungfu::CommonStubCSigns;
using BytecodeStubCSigns = panda::ecmascript::kungfu::BytecodeStubCSigns;
using BuiltinsStubCSigns = panda::ecmascript::kungfu::BuiltinsStubCSigns;
void SuspendBarrier::Wait()
{
while (true) {
int32_t curCount = passBarrierCount_.load(std::memory_order_relaxed);
if (LIKELY(curCount > 0)) {
#if defined(PANDA_USE_FUTEX)
int32_t *addr = reinterpret_cast<int32_t*>(&passBarrierCount_);
if (futex(addr, FUTEX_WAIT_PRIVATE, curCount, nullptr, nullptr, 0) != 0) {
if (errno != EAGAIN && errno != EINTR) {
LOG_GC(FATAL) << "SuspendBarrier::Wait failed, errno = " << errno;
UNREACHABLE();
}
}
#else
sched_yield();
#endif
} else {
curCount = passBarrierCount_.load(std::memory_order_seq_cst);
ASSERT(curCount == 0);
break;
}
}
}
thread_local JSThread *currentThread = nullptr;
JSThread *JSThread::GetCurrent()
{
return currentThread;
}
void JSThread::RegisterThread(JSThread *jsThread)
{
Runtime::GetInstance()->RegisterThread(jsThread);
if (currentThread == nullptr) {
currentThread = jsThread;
if (LIKELY(!g_isEnableCMCGC)) {
jsThread->UpdateState(ThreadState::NATIVE);
} else {
jsThread->GetThreadHolder()->TransferToNative();
}
}
}
void JSThread::UnregisterThread(JSThread *jsThread)
{
if (currentThread == jsThread) {
if (LIKELY(!g_isEnableCMCGC)) {
jsThread->UpdateState(ThreadState::TERMINATED);
} else {
jsThread->GetThreadHolder()->TransferToNative();
jsThread->SetAllocBuffer(nullptr);
}
currentThread = nullptr;
} else {
if (LIKELY(!g_isEnableCMCGC)) {
ASSERT(jsThread->GetState() == ThreadState::CREATED);
jsThread->UpdateState(ThreadState::TERMINATED);
} else {
jsThread->GetThreadHolder()->TransferToNative();
jsThread->SetAllocBuffer(nullptr);
}
}
Runtime::GetInstance()->UnregisterThread(jsThread);
}
JSThread *JSThread::Create(EcmaVM *vm)
{
auto jsThread = new JSThread(vm);
vm->SetJSThread(jsThread);
AsmInterParsedOption asmInterOpt = vm->GetJSOptions().GetAsmInterParsedOption();
if (asmInterOpt.enableAsm) {
jsThread->EnableAsmInterpreter();
}
jsThread->nativeAreaAllocator_ = vm->GetNativeAreaAllocator();
jsThread->heapRegionAllocator_ = vm->GetHeapRegionAllocator();
size_t maxStackSize = vm->GetEcmaParamConfiguration().GetMaxStackSize();
jsThread->glueData_.frameBase_ = static_cast<JSTaggedType *>(
vm->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize));
jsThread->glueData_.currentFrame_ = jsThread->glueData_.frameBase_ + maxStackSize;
EcmaInterpreter::InitStackFrame(jsThread);
jsThread->glueData_.stackLimit_ = GetAsmStackLimit();
jsThread->glueData_.stackStart_ = GetCurrentStackPosition();
jsThread->glueData_.isEnableMutantArray_ = vm->IsEnableMutantArray();
jsThread->glueData_.IsEnableElementsKind_ = vm->IsEnableElementsKind();
jsThread->SetThreadId();
if (UNLIKELY(g_isEnableCMCGC)) {
jsThread->glueData_.threadHolder_ = ToUintPtr(ThreadHolder::CreateAndRegisterNewThreadHolder(vm));
}
RegisterThread(jsThread);
return jsThread;
}
JSThread::JSThread(EcmaVM *vm) : id_(os::thread::GetCurrentThreadId()), vm_(vm)
{
auto chunk = vm->GetChunk();
if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
globalStorage_ = chunk->New<EcmaGlobalStorage<Node>>(this, vm->GetNativeAreaAllocator());
newGlobalHandle_ = [this](JSTaggedType value) {
return globalStorage_->NewGlobalHandle<NodeKind::NORMAL_NODE>(value);
};
disposeGlobalHandle_ = [this](uintptr_t nodeAddr) {
globalStorage_->DisposeGlobalHandle<NodeKind::NORMAL_NODE>(nodeAddr);
};
if (Runtime::GetInstance()->IsHybridVm()) {
newXRefGlobalHandle_ = [this](JSTaggedType value) {
return globalStorage_->NewGlobalHandle<NodeKind::UNIFIED_NODE>(value);
};
disposeXRefGlobalHandle_ = [this](uintptr_t nodeAddr) {
globalStorage_->DisposeGlobalHandle<NodeKind::UNIFIED_NODE>(nodeAddr);
};
setNodeKind_ = [this](NodeKind nodeKind) { globalStorage_->SetNodeKind(nodeKind); };
}
setWeak_ = [this](uintptr_t nodeAddr, void *ref, WeakClearCallback freeGlobalCallBack,
WeakClearCallback nativeFinalizeCallBack) {
return globalStorage_->SetWeak(nodeAddr, ref, freeGlobalCallBack, nativeFinalizeCallBack);
};
clearWeak_ = [this](uintptr_t nodeAddr) { return globalStorage_->ClearWeak(nodeAddr); };
isWeak_ = [this](uintptr_t addr) { return globalStorage_->IsWeak(addr); };
} else {
globalDebugStorage_ = chunk->New<EcmaGlobalStorage<DebugNode>>(this, vm->GetNativeAreaAllocator());
newGlobalHandle_ = [this](JSTaggedType value) {
return globalDebugStorage_->NewGlobalHandle<NodeKind::NORMAL_NODE>(value);
};
disposeGlobalHandle_ = [this](uintptr_t nodeAddr) {
globalDebugStorage_->DisposeGlobalHandle<NodeKind::NORMAL_NODE>(nodeAddr);
};
setWeak_ = [this](uintptr_t nodeAddr, void *ref, WeakClearCallback freeGlobalCallBack,
WeakClearCallback nativeFinalizeCallBack) {
return globalDebugStorage_->SetWeak(nodeAddr, ref, freeGlobalCallBack, nativeFinalizeCallBack);
};
clearWeak_ = [this](uintptr_t nodeAddr) { return globalDebugStorage_->ClearWeak(nodeAddr); };
isWeak_ = [this](uintptr_t addr) { return globalDebugStorage_->IsWeak(addr); };
if (Runtime::GetInstance()->IsHybridVm()) {
newXRefGlobalHandle_ = [this](JSTaggedType value) {
return globalDebugStorage_->NewGlobalHandle<NodeKind::UNIFIED_NODE>(value);
};
disposeXRefGlobalHandle_ = [this](uintptr_t nodeAddr) {
globalDebugStorage_->DisposeGlobalHandle<NodeKind::UNIFIED_NODE>(nodeAddr);
};
setNodeKind_ = [this](NodeKind nodeKind) { globalDebugStorage_->SetNodeKind(nodeKind); };
}
}
vmThreadControl_ = new VmThreadControl(this);
SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
dateUtils_ = new DateUtils();
glueData_.propertiesCache_ = new PropertiesCache();
if (vm_->GetJSOptions().IsEnableMegaIC()) {
glueData_.loadMegaICCache_ = new MegaICCache();
glueData_.storeMegaICCache_ = new MegaICCache();
}
glueData_.globalConst_ = new GlobalEnvConstants();
glueData_.baseAddress_ = TaggedStateWord::BASE_ADDRESS;
glueData_.isEnableCMCGC_ = g_isEnableCMCGC;
}
JSThread::JSThread(EcmaVM *vm, ThreadType threadType) : id_(os::thread::GetCurrentThreadId()),
vm_(vm), threadType_(threadType)
{
ASSERT(threadType == ThreadType::JIT_THREAD);
readyForGCIterating_ = false;
glueData_.isEnableCMCGC_ = g_isEnableCMCGC;
if (UNLIKELY(g_isEnableCMCGC)) {
glueData_.threadHolder_ = ToUintPtr(ThreadHolder::CreateAndRegisterNewThreadHolder(nullptr));
}
RegisterThread(this);
};
JSThread::JSThread(ThreadType threadType) : threadType_(threadType)
{
ASSERT(threadType == ThreadType::DAEMON_THREAD);
readyForGCIterating_ = false;
glueData_.isEnableCMCGC_ = g_isEnableCMCGC;
}
JSThread::~JSThread()
{
readyForGCIterating_ = false;
if (globalStorage_ != nullptr) {
vm_->GetChunk()->Delete(globalStorage_);
globalStorage_ = nullptr;
}
if (globalDebugStorage_ != nullptr) {
vm_->GetChunk()->Delete(globalDebugStorage_);
globalDebugStorage_ = nullptr;
}
ClearMegaIC();
if (glueData_.propertiesCache_ != nullptr) {
delete glueData_.propertiesCache_;
glueData_.propertiesCache_ = nullptr;
}
if (threadType_ == ThreadType::JS_THREAD) {
GetNativeAreaAllocator()->Free(glueData_.frameBase_, sizeof(JSTaggedType) *
vm_->GetEcmaParamConfiguration().GetMaxStackSize());
}
GetNativeAreaAllocator()->FreeArea(regExpCacheArea_);
glueData_.frameBase_ = nullptr;
nativeAreaAllocator_ = nullptr;
heapRegionAllocator_ = nullptr;
regExpCacheArea_ = nullptr;
if (vmThreadControl_ != nullptr) {
delete vmThreadControl_;
vmThreadControl_ = nullptr;
}
if (!IsDaemonThread()) {
UnregisterThread(this);
}
if (dateUtils_ != nullptr) {
delete dateUtils_;
dateUtils_ = nullptr;
}
if (localEvacuator_ != nullptr) {
delete localEvacuator_;
localEvacuator_ = nullptr;
}
if (glueData_.moduleLogger_ != nullptr) {
delete glueData_.moduleLogger_;
glueData_.moduleLogger_ = nullptr;
}
if (glueData_.globalConst_ != nullptr) {
delete glueData_.globalConst_;
glueData_.globalConst_ = nullptr;
}
}
ThreadId JSThread::GetCurrentThreadId()
{
return GetCurrentThreadOrTaskId();
}
void JSThread::SetException(JSTaggedValue exception)
{
glueData_.exception_ = exception;
#if defined(ENABLE_EXCEPTION_BACKTRACE)
if (vm_->GetJSOptions().EnableExceptionBacktrace()) {
LOG_ECMA(INFO) << "SetException:" << exception.GetRawData();
std::ostringstream stack;
Backtrace(stack);
LOG_ECMA(INFO) << stack.str();
}
#endif
}
void JSThread::HandleUncaughtException(JSTaggedValue exception)
{
[[maybe_unused]] EcmaHandleScope handleScope(this);
JSHandle<JSTaggedValue> exceptionHandle(this, exception);
if (isUncaughtExceptionRegistered_) {
if (vm_->GetJSThread()->IsMainThread()) {
return;
}
auto callback = GetOnErrorCallback();
if (callback) {
ClearException();
Local<ObjectRef> exceptionRef = JSNApiHelper::ToLocal<ObjectRef>(exceptionHandle);
ThreadNativeScope nativeScope(this);
callback(exceptionRef, GetOnErrorData());
}
}
ClearException();
if (exceptionHandle->IsJSError()) {
base::ErrorHelper::PrintJSErrorInfo(this, exceptionHandle);
return;
}
JSHandle<EcmaString> result = JSTaggedValue::ToString(this, exceptionHandle);
LOG_NO_TAG(ERROR) << ConvertToString(this, *result);
}
void JSThread::HandleUncaughtException()
{
if (!HasPendingException()) {
return;
}
JSTaggedValue exception = GetException();
HandleUncaughtException(exception);
}
JSTaggedValue JSThread::GetCurrentLexenv() const
{
FrameHandler frameHandler(const_cast<JSThread*>(this));
return frameHandler.GetEnv();
}
JSTaggedValue JSThread::GetCurrentFunction() const
{
FrameHandler frameHandler(const_cast<JSThread*>(this));
return frameHandler.GetFunction();
}
const JSTaggedType *JSThread::GetCurrentFrame() const
{
if (IsAsmInterpreter()) {
return GetLastLeaveFrame();
}
return GetCurrentSPFrame();
}
void JSThread::SetCurrentFrame(JSTaggedType *sp)
{
if (IsAsmInterpreter()) {
return SetLastLeaveFrame(sp);
}
return SetCurrentSPFrame(sp);
}
const JSTaggedType *JSThread::GetCurrentInterpretedFrame() const
{
if (IsAsmInterpreter()) {
auto frameHandler = FrameHandler(const_cast<JSThread*>(this));
return frameHandler.GetSp();
}
return GetCurrentSPFrame();
}
void JSThread::InvokeWeakNodeFreeGlobalCallBack()
{
while (!weakNodeFreeGlobalCallbacks_.empty()) {
auto callbackPair = weakNodeFreeGlobalCallbacks_.back();
weakNodeFreeGlobalCallbacks_.pop_back();
ASSERT(callbackPair.first != nullptr && callbackPair.second != nullptr);
auto callback = callbackPair.first;
(*callback)(callbackPair.second);
}
}
void JSThread::InvokeWeakNodeNativeFinalizeCallback()
{
if (ShouldIgnoreFinalizeCallback()) {
weakNodeNativeFinalizeCallbacks_.clear();
return;
}
if (runningNativeFinalizeCallbacks_) {
return;
}
runningNativeFinalizeCallbacks_ = true;
TRACE_GC(GCStats::Scope::ScopeId::InvokeNativeFinalizeCallbacks, vm_->GetEcmaGCStats());
OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, ("InvokeNativeFinalizeCallbacks num:"
+ std::to_string(weakNodeNativeFinalizeCallbacks_.size())).c_str(), "");
while (!weakNodeNativeFinalizeCallbacks_.empty()) {
auto callbackPair = weakNodeNativeFinalizeCallbacks_.back();
weakNodeNativeFinalizeCallbacks_.pop_back();
ASSERT(callbackPair.first != nullptr && callbackPair.second != nullptr);
auto callback = callbackPair.first;
(*callback)(callbackPair.second);
}
if (finalizeTaskCallback_ != nullptr) {
finalizeTaskCallback_();
}
runningNativeFinalizeCallbacks_ = false;
}
bool JSThread::IsStartGlobalLeakCheck() const
{
return vm_->GetJSOptions().IsStartGlobalLeakCheck();
}
bool JSThread::EnableGlobalObjectLeakCheck() const
{
return vm_->GetJSOptions().EnableGlobalObjectLeakCheck();
}
bool JSThread::EnableGlobalPrimitiveLeakCheck() const
{
return vm_->GetJSOptions().EnableGlobalPrimitiveLeakCheck();
}
bool JSThread::IsInRunningStateOrProfiling() const
{
bool result = IsInRunningState();
#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER)
result |= vm_->GetHeapProfile() != nullptr;
#endif
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
result |= GetIsProfiling();
#endif
return result;
}
void JSThread::WriteToStackTraceFd(std::ostringstream &buffer) const
{
if (stackTraceFd_ < 0) {
return;
}
buffer << std::endl;
DPrintf(reinterpret_cast<fd_t>(stackTraceFd_), buffer.str());
buffer.str("");
}
void JSThread::SetStackTraceFd(int32_t fd)
{
FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
stackTraceFd_ = fd;
}
void JSThread::CloseStackTraceFd()
{
if (stackTraceFd_ != -1) {
FSync(reinterpret_cast<fd_t>(stackTraceFd_));
Close(reinterpret_cast<fd_t>(stackTraceFd_));
stackTraceFd_ = -1;
}
}
void JSThread::SetJitCodeMap(JSTaggedType exception, MachineCode* machineCode, std::string &methodName,
uintptr_t offset)
{
auto it = jitCodeMaps_.find(exception);
if (it != jitCodeMaps_.end()) {
it->second->push_back(std::make_tuple(machineCode, methodName, offset));
} else {
JitCodeVector *jitCode = new JitCodeVector {std::make_tuple(machineCode, methodName, offset)};
jitCodeMaps_.emplace(exception, jitCode);
}
}
void JSThread::IterateMegaIC(RootVisitor &v)
{
if (glueData_.loadMegaICCache_ != nullptr) {
glueData_.loadMegaICCache_->Iterate(v);
}
if (glueData_.storeMegaICCache_ != nullptr) {
glueData_.storeMegaICCache_->Iterate(v);
}
}
void JSThread::ClearMegaIC()
{
if (glueData_.loadMegaICCache_ != nullptr) {
delete glueData_.loadMegaICCache_;
glueData_.loadMegaICCache_ = nullptr;
}
if (glueData_.storeMegaICCache_ != nullptr) {
delete glueData_.storeMegaICCache_;
glueData_.storeMegaICCache_ = nullptr;
}
}
void JSThread::Iterate(RootVisitor &visitor)
{
if (!glueData_.exception_.IsHole()) {
visitor.VisitRoot(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.exception_)));
}
if (!glueData_.currentEnv_.IsHole()) {
visitor.VisitRoot(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.currentEnv_)));
}
if (!hotReloadDependInfo_.IsUndefined()) {
visitor.VisitRoot(Root::ROOT_VM, ObjectSlot(ToUintPtr(&hotReloadDependInfo_)));
}
visitor.VisitRangeRoot(Root::ROOT_VM,
ObjectSlot(glueData_.builtinEntries_.Begin()), ObjectSlot(glueData_.builtinEntries_.End()));
FrameHandler frameHandler(this);
frameHandler.Iterate(visitor);
if (vm_->GetJSOptions().EnableGlobalLeakCheck()) {
IterateHandleWithCheck(visitor);
} else {
OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::VisitRootGlobalRefHandle", "");
size_t globalCount = 0;
auto callback = [&visitor, &globalCount](Node *node) {
JSTaggedValue value(node->GetObject());
if (value.IsHeapObject()) {
visitor.VisitRoot(Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
}
globalCount++;
};
globalStorage_->IterateUsageGlobal(callback);
static bool hasCheckedGlobalCount = false;
static const size_t WARN_GLOBAL_COUNT = 100000;
if (!hasCheckedGlobalCount && globalCount >= WARN_GLOBAL_COUNT) {
LOG_ECMA(WARN) << "Global reference count is " << globalCount << ",It exceed the upper limit 100000!";
hasCheckedGlobalCount = true;
}
}
IterateMegaIC(visitor);
if (glueData_.propertiesCache_ != nullptr) {
glueData_.propertiesCache_->Clear();
}
if (glueData_.globalConst_ != nullptr) {
glueData_.globalConst_->Iterate(visitor);
}
}
void JSThread::ClearCache()
{
JSHandle<GlobalEnv> env = GetGlobalEnv();
if (!env.GetTaggedValue().IsHole()) {
env->ClearCache(this);
}
}
void JSThread::IterateJitCodeMap(const JitCodeMapVisitor &jitCodeMapVisitor)
{
jitCodeMapVisitor(jitCodeMaps_);
}
void JSThread::IterateHandleWithCheck(RootVisitor &visitor)
{
size_t handleCount = vm_->IterateHandle(visitor);
size_t globalCount = 0;
static const int JS_TYPE_SUM = static_cast<int>(JSType::TYPE_LAST) + 1;
int typeCount[JS_TYPE_SUM] = { 0 };
int primitiveCount = 0;
bool isStopObjectLeakCheck = EnableGlobalObjectLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
bool isStopPrimitiveLeakCheck = EnableGlobalPrimitiveLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
std::ostringstream buffer;
auto callback = [this, &visitor, &globalCount, &typeCount, &primitiveCount,
isStopObjectLeakCheck, isStopPrimitiveLeakCheck, &buffer](DebugNode *node) {
node->MarkCount();
JSTaggedValue value(node->GetObject());
if (value.IsHeapObject()) {
visitor.VisitRoot(Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
auto object = reinterpret_cast<TaggedObject *>(node->GetObject());
typeCount[static_cast<int>(object->GetClass()->GetObjectType())]++;
if (isStopObjectLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
buffer << "Global maybe leak object address:" << std::hex << object <<
", type:" << JSHClass::DumpJSType(JSType(object->GetClass()->GetObjectType())) <<
", node address:" << node << ", number:" << std::dec << node->GetGlobalNumber() <<
", markCount:" << node->GetMarkCount();
WriteToStackTraceFd(buffer);
}
} else {
primitiveCount++;
if (isStopPrimitiveLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
buffer << "Global maybe leak primitive:" << std::hex << value.GetRawData() <<
", node address:" << node << ", number:" << std::dec << node->GetGlobalNumber() <<
", markCount:" << node->GetMarkCount();
WriteToStackTraceFd(buffer);
}
}
globalCount++;
};
globalDebugStorage_->IterateUsageGlobal(callback);
if (isStopObjectLeakCheck || isStopPrimitiveLeakCheck) {
buffer << "Global leak check success!";
WriteToStackTraceFd(buffer);
CloseStackTraceFd();
}
LOG_ECMA(INFO) << "Iterate root handle count:" << handleCount << ", global handle count:" << globalCount;
OPTIONAL_LOG(vm_, INFO) << "Global type Primitive count:" << primitiveCount;
static const int MIN_COUNT_THRESHOLD = 50;
for (int i = 0; i < JS_TYPE_SUM; i++) {
if (typeCount[i] > MIN_COUNT_THRESHOLD) {
OPTIONAL_LOG(vm_, INFO) << "Global type " << JSHClass::DumpJSType(JSType(i))
<< " count:" << typeCount[i];
}
}
}
void JSThread::IterateWeakEcmaGlobalStorage(WeakVisitor &visitor)
{
auto callBack = [this, &visitor](WeakNode *node) {
JSTaggedValue value(node->GetObject());
if (!value.IsHeapObject()) {
return;
};
bool isAlive = visitor.VisitRoot(Root::ROOT_VM, ecmascript::ObjectSlot(node->GetObjectAddress()));
if (!isAlive) {
node->SetObject(JSTaggedValue::Undefined().GetRawData());
auto nativeFinalizeCallback = node->GetNativeFinalizeCallback();
if (nativeFinalizeCallback) {
weakNodeNativeFinalizeCallbacks_.push_back(std::make_pair(nativeFinalizeCallback,
node->GetReference()));
}
auto freeGlobalCallBack = node->GetFreeGlobalCallback();
if (!freeGlobalCallBack) {
DisposeGlobalHandle(ToUintPtr(node));
} else {
weakNodeFreeGlobalCallbacks_.push_back(std::make_pair(freeGlobalCallBack, node->GetReference()));
}
}
};
if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
globalStorage_->IterateWeakUsageGlobal(callBack);
} else {
globalDebugStorage_->IterateWeakUsageGlobal(callBack);
}
}
void JSThread::IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor, GCKind gcKind)
{
auto callBack = [this, visitor, gcKind](WeakNode *node) {
JSTaggedValue value(node->GetObject());
if (!value.IsHeapObject()) {
return;
}
auto object = value.GetTaggedObject();
auto fwd = visitor(object);
if (fwd == nullptr) {
node->SetObject(JSTaggedValue::Undefined().GetRawData());
auto nativeFinalizeCallback = node->GetNativeFinalizeCallback();
if (nativeFinalizeCallback) {
weakNodeNativeFinalizeCallbacks_.push_back(std::make_pair(nativeFinalizeCallback,
node->GetReference()));
}
auto freeGlobalCallBack = node->GetFreeGlobalCallback();
if (!freeGlobalCallBack) {
DisposeGlobalHandle(ToUintPtr(node));
} else if (gcKind == GCKind::SHARED_GC) {
weakNodeFreeGlobalCallbacks_.push_back(std::make_pair(freeGlobalCallBack, node->GetReference()));
} else {
node->CallFreeGlobalCallback();
}
} else if (fwd != object) {
node->SetObject(JSTaggedValue(fwd).GetRawData());
}
};
if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
globalStorage_->IterateWeakUsageGlobal(callBack);
} else {
globalDebugStorage_->IterateWeakUsageGlobal(callBack);
}
}
void JSThread::UpdateJitCodeMapReference(const WeakRootVisitor &visitor)
{
auto it = jitCodeMaps_.begin();
while (it != jitCodeMaps_.end()) {
auto obj = reinterpret_cast<TaggedObject *>(it->first);
auto fwd = visitor(obj);
if (fwd == nullptr) {
delete it->second;
it = jitCodeMaps_.erase(it);
} else if (fwd != obj) {
jitCodeMaps_.emplace(JSTaggedValue(fwd).GetRawData(), it->second);
it = jitCodeMaps_.erase(it);
} else {
++it;
}
}
}
bool JSThread::DoStackOverflowCheck(const JSTaggedType *sp)
{
if (UNLIKELY(!IsCrossThreadExecutionEnable() && sp <= glueData_.frameBase_ + RESERVE_STACK_SIZE)) {
vm_->CheckThread();
LOG_ECMA(ERROR) << "Stack overflow! Remaining stack size is: " << (sp - glueData_.frameBase_);
if (LIKELY(!HasPendingException())) {
ObjectFactory *factory = vm_->GetFactory();
JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR,
"Stack overflow!", StackCheck::NO);
SetException(error.GetTaggedValue());
}
return true;
}
return false;
}
bool JSThread::DoStackLimitCheck()
{
if (UNLIKELY(!IsCrossThreadExecutionEnable() && GetCurrentStackPosition() < GetStackLimit())) {
vm_->CheckThread();
LOG_ECMA(ERROR) << "Stack overflow! current:" << GetCurrentStackPosition() << " limit:" << GetStackLimit();
if (LIKELY(!HasPendingException())) {
ObjectFactory *factory = vm_->GetFactory();
JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR,
"Stack overflow!", StackCheck::NO);
SetException(error.GetTaggedValue());
}
return true;
}
return false;
}
void JSThread::SetInitialBuiltinHClass(
BuiltinTypeId type, JSHClass *builtinHClass, JSHClass *instanceHClass,
JSHClass *prototypeHClass, JSHClass *prototypeOfPrototypeHClass, JSHClass *extraHClass)
{
size_t index = BuiltinHClassEntries::GetEntryIndex(type);
auto &entry = glueData_.builtinHClassEntries_.entries[index];
LOG_ECMA(DEBUG) << "JSThread::SetInitialBuiltinHClass: "
<< "Builtin = " << ToString(type)
<< ", builtinHClass = " << builtinHClass
<< ", instanceHClass = " << instanceHClass
<< ", prototypeHClass = " << prototypeHClass
<< ", prototypeOfPrototypeHClass = " << prototypeOfPrototypeHClass
<< ", extraHClass = " << extraHClass;
entry.builtinHClass = builtinHClass;
entry.instanceHClass = instanceHClass;
entry.prototypeHClass = prototypeHClass;
entry.prototypeOfPrototypeHClass = prototypeOfPrototypeHClass;
entry.extraHClass = extraHClass;
}
void JSThread::SetInitialBuiltinGlobalHClass(
JSHClass *builtinHClass, GlobalIndex globalIndex)
{
auto &map = ctorHclassEntries_;
map[builtinHClass] = globalIndex;
}
JSHClass *JSThread::GetBuiltinHClass(BuiltinTypeId type) const
{
size_t index = BuiltinHClassEntries::GetEntryIndex(type);
return glueData_.builtinHClassEntries_.entries[index].builtinHClass;
}
JSHClass *JSThread::GetBuiltinInstanceHClass(BuiltinTypeId type) const
{
size_t index = BuiltinHClassEntries::GetEntryIndex(type);
return glueData_.builtinHClassEntries_.entries[index].instanceHClass;
}
JSHClass *JSThread::GetBuiltinExtraHClass(BuiltinTypeId type) const
{
size_t index = BuiltinHClassEntries::GetEntryIndex(type);
return glueData_.builtinHClassEntries_.entries[index].extraHClass;
}
JSHClass *JSThread::GetBuiltinPrototypeHClass(BuiltinTypeId type) const
{
size_t index = BuiltinHClassEntries::GetEntryIndex(type);
return glueData_.builtinHClassEntries_.entries[index].prototypeHClass;
}
JSHClass *JSThread::GetBuiltinPrototypeOfPrototypeHClass(BuiltinTypeId type) const
{
size_t index = BuiltinHClassEntries::GetEntryIndex(type);
return glueData_.builtinHClassEntries_.entries[index].prototypeOfPrototypeHClass;
}
void JSThread::CheckSwitchDebuggerBCStub()
{
auto isDebug = vm_->GetJsDebuggerManager()->IsDebugMode();
if (LIKELY(!isDebug)) {
if (glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) {
for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i);
auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i);
glueData_.bcStubEntries_.Set(i, stubEntry);
glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry);
}
}
} else {
if (glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) {
for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
auto stubEntry = glueData_.bcStubEntries_.Get(i);
auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i);
glueData_.bcDebuggerStubEntries_.Set(i, stubEntry);
glueData_.bcStubEntries_.Set(i, debuggerStubEbtry);
}
}
}
}
void JSThread::CheckOrSwitchPGOStubs()
{
bool isSwitch = false;
bool isSwitchToNormal = false;
if (IsPGOProfilerEnable()) {
if (GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
SetBCStubStatus(BCStubStatus::PROFILE_BC_STUB);
isSwitch = true;
} else if (GetBCStubStatus() == BCStubStatus::STW_COPY_BC_STUB) {
SwitchStwCopyBCStubs(false);
ASSERT(GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB);
SetBCStubStatus(BCStubStatus::PROFILE_BC_STUB);
isSwitch = true;
}
} else {
if (GetBCStubStatus() == BCStubStatus::PROFILE_BC_STUB) {
SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
isSwitch = true;
isSwitchToNormal = true;
}
}
if (isSwitch) {
Address curAddress;
#define SWITCH_PGO_STUB_ENTRY(fromName, toName, ...) \
curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName); \
SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \
SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress);
ASM_INTERPRETER_BC_PROFILER_STUB_LIST(SWITCH_PGO_STUB_ENTRY)
#undef SWITCH_PGO_STUB_ENTRY
}
if (isSwitchToNormal && !g_isEnableCMCGC) {
SwitchStwCopyBCStubs(true);
}
}
void JSThread::SwitchJitProfileStubs(bool isEnablePgo)
{
if (isEnablePgo) {
SetPGOProfilerEnable(true);
CheckOrSwitchPGOStubs();
return;
}
bool isSwitch = false;
if (GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
SetBCStubStatus(BCStubStatus::JIT_PROFILE_BC_STUB);
isSwitch = true;
} else if (GetBCStubStatus() == BCStubStatus::STW_COPY_BC_STUB) {
SwitchStwCopyBCStubs(false);
ASSERT(GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB);
SetBCStubStatus(BCStubStatus::JIT_PROFILE_BC_STUB);
isSwitch = true;
}
if (isSwitch) {
Address curAddress;
#define SWITCH_PGO_STUB_ENTRY(fromName, toName, ...) \
curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName); \
SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \
SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress);
ASM_INTERPRETER_BC_JIT_PROFILER_STUB_LIST(SWITCH_PGO_STUB_ENTRY)
#undef SWITCH_PGO_STUB_ENTRY
}
}
void JSThread::SwitchStwCopyBCStubs(bool isStwCopy)
{
bool isSwitch = false;
if (isStwCopy && GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
SetBCStubStatus(BCStubStatus::STW_COPY_BC_STUB);
isSwitch = true;
} else if (!isStwCopy && GetBCStubStatus() == BCStubStatus::STW_COPY_BC_STUB) {
SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
isSwitch = true;
}
if (isSwitch) {
Address curAddress;
#define SWITCH_STW_COPY_STUB_ENTRY(base) \
curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##base); \
SetBCStubEntry(BytecodeStubCSigns::ID_##base, \
GetBCStubEntry(BytecodeStubCSigns::ID_##base##StwCopy)); \
SetBCStubEntry(BytecodeStubCSigns::ID_##base##StwCopy, curAddress);
ASM_INTERPRETER_BC_STW_COPY_STUB_LIST(SWITCH_STW_COPY_STUB_ENTRY)
#undef SWITCH_STW_COPY_STUB_ENTRY
}
}
void JSThread::SwitchStwCopyCommonStubs(bool isStwCopy)
{
bool isSwitch = false;
if (isStwCopy && GetCommonStubStatus() == CommonStubStatus::NORMAL_COMMON_STUB) {
SetCommonStubStatus(CommonStubStatus::STW_COPY_COMMON_STUB);
isSwitch = true;
} else if (!isStwCopy && GetCommonStubStatus() == CommonStubStatus::STW_COPY_COMMON_STUB) {
SetCommonStubStatus(CommonStubStatus::NORMAL_COMMON_STUB);
isSwitch = true;
}
if (isSwitch) {
Address curAddress;
#define SWITCH_STW_COPY_STUB_ENTRY(base) \
curAddress = GetFastStubEntry(CommonStubCSigns::base); \
SetFastStubEntry(CommonStubCSigns::base, \
GetFastStubEntry(CommonStubCSigns::base##StwCopy)); \
SetFastStubEntry(CommonStubCSigns::base##StwCopy, curAddress);
COMMON_STW_COPY_STUB_LIST(SWITCH_STW_COPY_STUB_ENTRY)
#undef SWITCH_STW_COPY_STUB_ENTRY
}
}
void JSThread::SwitchStwCopyBuiltinsStubs(bool isStwCopy)
{
bool isSwitch = false;
if (isStwCopy && GetBuiltinsStubStatus() == BuiltinsStubStatus::NORMAL_BUILTINS_STUB) {
SetBuiltinsStubStatus(BuiltinsStubStatus::STW_COPY_BUILTINS_STUB);
isSwitch = true;
} else if (!isStwCopy && GetBuiltinsStubStatus() == BuiltinsStubStatus::STW_COPY_BUILTINS_STUB) {
SetBuiltinsStubStatus(BuiltinsStubStatus::NORMAL_BUILTINS_STUB);
isSwitch = true;
}
if (isSwitch) {
Address curAddress;
#define SWITCH_STW_COPY_STUB_ENTRY(base) \
curAddress = GetBuiltinStubEntry(BuiltinsStubCSigns::ID_##base); \
SetBuiltinStubEntry(BuiltinsStubCSigns::ID_##base, \
GetBuiltinStubEntry(BuiltinsStubCSigns::ID_##base##StwCopy)); \
SetBuiltinStubEntry(BuiltinsStubCSigns::ID_##base##StwCopy, curAddress);
#define SWITCH_STW_COPY_STUB_ENTRY_DYN(base, type, ...) \
SWITCH_STW_COPY_STUB_ENTRY(type##base)
BUILTINS_STW_COPY_STUB_LIST(SWITCH_STW_COPY_STUB_ENTRY, SWITCH_STW_COPY_STUB_ENTRY_DYN, \
SWITCH_STW_COPY_STUB_ENTRY)
#undef SWITCH_STW_COPY_STUB_ENTRY_DYN
#undef SWITCH_STW_COPY_STUB_ENTRY
}
}
JSThread::BCDebugStubSwitchScope::BCDebugStubSwitchScope(JSThread *thread) : thread_(thread)
{
isDebugging_ = thread_->GetBCStubEntry(0) == thread_->GetBCStubEntry(1);
if (isDebugging_) {
SwitchStub();
}
}
void JSThread::BCDebugStubSwitchScope::SwitchStub()
{
ASSERT(isDebugging_);
for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
auto stubEntry = thread_->GetBCStubEntry(i);
auto debuggerStubEbtry = thread_->GetBCDebugStubEntry(i);
thread_->SetBCDebugStubEntry(i, stubEntry);
thread_->SetBCStubEntry(i, debuggerStubEbtry);
}
}
JSThread::BCDebugStubSwitchScope::~BCDebugStubSwitchScope()
{
if (isDebugging_) {
SwitchStub();
}
}
void JSThread::SwitchAllStub(bool isStwCopy)
{
BCDebugStubSwitchScope scope(this);
SwitchStwCopyBCStubs(isStwCopy);
SwitchStwCopyCommonStubs(isStwCopy);
SwitchStwCopyBuiltinsStubs(isStwCopy);
}
void JSThread::TerminateExecution()
{
ObjectFactory *factory = vm_->GetFactory();
JSHandle<JSObject> error = factory->GetJSError(ErrorType::TERMINATION_ERROR,
"Terminate execution!", StackCheck::NO);
SetException(error.GetTaggedValue());
}
void JSThread::CheckAndPassActiveBarrier()
{
ASSERT(!g_isEnableCMCGC);
ThreadStateAndFlags oldStateAndFlags;
oldStateAndFlags.asNonvolatileInt = glueData_.stateAndFlags_.asInt;
if ((oldStateAndFlags.asNonvolatileStruct.flags & ThreadFlag::ACTIVE_BARRIER) != 0) {
PassSuspendBarrier();
}
}
bool JSThread::PassSuspendBarrier()
{
LockHolder lock(suspendLock_);
if (suspendBarrier_ != nullptr) {
suspendBarrier_->PassStrongly();
suspendBarrier_ = nullptr;
ClearFlag(ThreadFlag::ACTIVE_BARRIER);
return true;
}
return false;
}
bool JSThread::ShouldHandleMarkOrCopyFinishedInSafepoint()
{
auto heap = const_cast<Heap *>(vm_->GetHeap());
bool isMarkOrCopyFinished = IsCopyFinished() ||
(IsMarkFinished() && heap->GetConcurrentMarker()->IsTriggeredConcurrentMark());
return isMarkOrCopyFinished && !heap->GetOnSerializeEvent() &&
!heap->InSensitiveStatus() && !heap->CheckIfNeedStopCollectionByStartup();
}
bool JSThread::CheckSafepoint()
{
ResetCheckSafePointStatus();
if UNLIKELY(HasTerminationRequest()) {
TerminateExecution();
SetVMTerminated(true);
SetTerminationRequest(false);
}
if UNLIKELY(HasSuspendRequest()) {
WaitSuspension();
}
if UNLIKELY(VMNeedSuspension()) {
vmThreadControl_->SuspendVM();
}
if (HasInstallMachineCode()) {
Jit::JitGCLockHolder lock(this);
vm_->GetJit()->InstallTasks(this);
SetInstallMachineCode(false);
}
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
if UNLIKELY(needProfiling_.load() && !isProfiling_) {
DFXJSNApi::StartCpuProfilerForFile(vm_, profileName_, CpuProfiler::INTERVAL_OF_INNER_START);
SetNeedProfiling(false);
}
#endif
bool gcTriggered = false;
#ifndef NDEBUG
if (vm_->GetJSOptions().EnableForceGC()) {
if (g_isEnableCMCGC) {
common::BaseRuntime::RequestGC(common::GC_REASON_USER, false,
common::GC_TYPE_FULL);
} else {
vm_->CollectGarbage(TriggerGCType::FULL_GC);
}
gcTriggered = true;
}
#endif
auto heap = const_cast<Heap *>(vm_->GetHeap());
heap->HandleExitHighSensitiveEvent();
if UNLIKELY(IsProcessingLocalToSharedRset()) {
return false;
}
if UNLIKELY(ShouldHandleMarkOrCopyFinishedInSafepoint()) {
if (IsMarkFinished()) {
heap->SetCanThrowOOMError(false);
heap->GetConcurrentMarker()->HandleMarkingFinished();
heap->SetCanThrowOOMError(true);
gcTriggered = true;
} else {
heap->GetCCGC()->HandleUpdateFinished();
}
}
return gcTriggered;
}
void JSThread::CheckJSTaggedType(JSTaggedType value) const
{
if (JSTaggedValue(value).IsHeapObject() &&
!vm_->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
LOG_FULL(FATAL) << "value:" << value << " is invalid!";
}
}
bool JSThread::CpuProfilerCheckJSTaggedType(JSTaggedType value) const
{
if (JSTaggedValue(value).IsHeapObject() &&
!vm_->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
return false;
}
return true;
}
uintptr_t JSThread::GetAndClearCallSiteReturnAddr(uintptr_t callSiteSp)
{
auto iter = callSiteSpToReturnAddrTable_.find(callSiteSp);
ASSERT(iter != callSiteSpToReturnAddrTable_.end());
uintptr_t returnAddr = iter->second;
callSiteSpToReturnAddrTable_.erase(iter);
return returnAddr;
}
bool JSThread::IsLegalAsmSp(uintptr_t sp) const
{
uint64_t bottom = GetStackLimit() - EcmaParamConfiguration::GetDefaultReservedStackSize();
uint64_t top = GetStackStart() + EcmaParamConfiguration::GetAllowedUpperStackDiff();
return (bottom <= sp && sp <= top);
}
bool JSThread::IsLegalThreadSp(uintptr_t sp) const
{
uintptr_t bottom = reinterpret_cast<uintptr_t>(glueData_.frameBase_);
size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
uintptr_t top = bottom + maxStackSize;
return (bottom <= sp && sp <= top);
}
bool JSThread::IsLegalSp(uintptr_t sp) const
{
return IsLegalAsmSp(sp) || IsLegalThreadSp(sp);
}
bool JSThread::IsMainThread()
{
#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
return getpid() == syscall(SYS_gettid);
#else
return true;
#endif
}
void JSThread::ClearVMCachedConstantPool()
{
vm_->ClearCachedConstantPool();
}
JSHandle<GlobalEnv> JSThread::GetGlobalEnv() const
{
return JSHandle<GlobalEnv>(ToUintPtr(&glueData_.currentEnv_));
}
JSHandle<DependentInfos> JSThread::GetDependentInfo() const
{
return JSHandle<DependentInfos>(ToUintPtr(&hotReloadDependInfo_));
}
void JSThread::SetDependentInfo(JSTaggedValue info)
{
hotReloadDependInfo_ = info;
}
JSHandle<DependentInfos> JSThread::GetOrCreateThreadDependentInfo()
{
if (hotReloadDependInfo_.IsUndefined()) {
return GetEcmaVM()->GetFactory()->NewDependentInfos(0);
}
return GetDependentInfo();
}
void JSThread::NotifyHotReloadDeoptimize()
{
if (!hotReloadDependInfo_.IsHeapObject()) {
return;
}
DependentInfos::TriggerLazyDeoptimization(GetDependentInfo(),
this, DependentInfos::DependentState::HOTRELOAD_PATCHMAIN);
hotReloadDependInfo_ = JSTaggedValue::Undefined();
}
PropertiesCache *JSThread::GetPropertiesCache() const
{
return glueData_.propertiesCache_;
}
MegaICCache *JSThread::GetLoadMegaICCache() const
{
return glueData_.loadMegaICCache_;
}
MegaICCache *JSThread::GetStoreMegaICCache() const
{
return glueData_.storeMegaICCache_;
}
bool JSThread::IsReadyToUpdateDetector() const
{
return !GetEnableLazyBuiltins() && !InGlobalEnvInitialize();
}
Area *JSThread::GetOrCreateRegExpCacheArea()
{
if (regExpCacheArea_ == nullptr) {
regExpCacheArea_ = nativeAreaAllocator_->AllocateArea(MAX_REGEXP_CACHE_SIZE);
}
return regExpCacheArea_;
}
void JSThread::InitializeBuiltinObject(const JSHandle<GlobalEnv>& env, const std::string& key)
{
BuiltinIndex& builtins = BuiltinIndex::GetInstance();
auto index = builtins.GetBuiltinIndex(key);
ASSERT(index != BuiltinIndex::NOT_FOUND);
If using `auto globalObject = vm_->GetGlobalEnv()->GetGlobalObject()` here,
it will cause incorrect result in multi-context environment. For example:
```ts
let obj = {};
print(obj instanceof Object); // instead of true, will print false
```
*/
auto globalObject = env->GetGlobalObject();
auto jsObject = JSHandle<JSObject>(this, globalObject);
auto box = jsObject->GetGlobalPropertyBox(this, key);
if (box == nullptr) {
return;
}
auto& entry = glueData_.builtinEntries_.builtin_[index];
entry.box_ = JSTaggedValue::Cast(box);
auto builtin = JSHandle<JSObject>(this, box->GetValue(this));
auto hclass = builtin->GetJSHClass();
entry.hClass_ = JSTaggedValue::Cast(hclass);
}
void JSThread::InitializeBuiltinObject(const JSHandle<GlobalEnv>& env)
{
BuiltinIndex& builtins = BuiltinIndex::GetInstance();
for (auto key: builtins.GetBuiltinKeys()) {
InitializeBuiltinObject(env, key);
}
}
bool JSThread::IsPropertyCacheCleared() const
{
if (!GetPropertiesCache()->IsCleared()) {
return false;
}
return true;
}
ThreadState JSThread::UpdateState(ThreadState newState)
{
ASSERT(!IsEnableCMCGC());
ThreadState oldState = GetState();
if (LIKELY(oldState != newState)) {
if (oldState == ThreadState::RUNNING) {
TransferFromRunningToSuspended(newState);
} else if (newState == ThreadState::RUNNING) {
TransferToRunning();
} else {
StoreState(newState);
}
}
return oldState;
}
void JSThread::SuspendThread(bool internalSuspend, SuspendBarrier* barrier)
{
ASSERT(!g_isEnableCMCGC);
LockHolder lock(suspendLock_);
if (!internalSuspend) {
}
uint32_t old_count = suspendCount_++;
if (old_count == 0) {
SetFlag(ThreadFlag::SUSPEND_REQUEST);
SetCheckSafePointStatus();
}
if (barrier != nullptr) {
ASSERT(suspendBarrier_ == nullptr);
suspendBarrier_ = barrier;
SetFlag(ThreadFlag::ACTIVE_BARRIER);
SetCheckSafePointStatus();
}
}
void JSThread::ResumeThread(bool internalSuspend)
{
ASSERT(!g_isEnableCMCGC);
LockHolder lock(suspendLock_);
if (!internalSuspend) {
}
if (suspendCount_ > 0) {
suspendCount_--;
if (suspendCount_ == 0) {
ClearFlag(ThreadFlag::SUSPEND_REQUEST);
ResetCheckSafePointStatus();
}
}
suspendCondVar_.Signal();
}
void JSThread::WaitSuspension()
{
if (UNLIKELY(g_isEnableCMCGC)) {
GetThreadHolder()->WaitSuspension();
} else {
constexpr int TIMEOUT = 100;
ThreadState oldState = GetState();
UpdateState(ThreadState::IS_SUSPENDED);
{
OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "SuspendTime::WaitSuspension", "");
LockHolder lock(suspendLock_);
while (suspendCount_ > 0) {
suspendCondVar_.TimedWait(&suspendLock_, TIMEOUT);
}
}
UpdateState(oldState);
}
}
void JSThread::ManagedCodeBegin()
{
ASSERT(!IsInManagedState());
if (LIKELY(!g_isEnableCMCGC)) {
UpdateState(ThreadState::RUNNING);
} else {
GetThreadHolder()->TransferToRunning();
}
}
void JSThread::ManagedCodeEnd()
{
ASSERT(IsInManagedState());
if (LIKELY(!g_isEnableCMCGC)) {
UpdateState(ThreadState::NATIVE);
} else {
GetThreadHolder()->TransferToNative();
}
}
void JSThread::TransferFromRunningToSuspended(ThreadState newState)
{
ASSERT(!g_isEnableCMCGC);
ASSERT(currentThread == this);
StoreSuspendedState(newState);
CheckAndPassActiveBarrier();
}
void JSThread::UpdateStackInfo(void *stackInfo, StackInfoOpKind opKind)
{
switch (opKind) {
case SwitchToSubStackInfo: {
StackInfo *subStackInfo = reinterpret_cast<StackInfo*>(stackInfo);
if (subStackInfo == nullptr) {
LOG_ECMA(ERROR) << "fatal error, subStack not exist";
break;
}
mainStackInfo_.stackLimit = glueData_.stackLimit_;
glueData_.stackLimit_ = subStackInfo->stackLimit;
mainStackInfo_.lastLeaveFrame = reinterpret_cast<uint64_t>(glueData_.leaveFrame_);
glueData_.leaveFrame_ =
reinterpret_cast<uint64_t *>(subStackInfo->lastLeaveFrame);
isInSubStack_ = true;
LOG_ECMA(DEBUG) << "Switch to subStack: "
<< ", stack limit: " << glueData_.stackLimit_
<< ", stack lastLeaveFrame: " << glueData_.leaveFrame_;
break;
}
case SwitchToMainStackInfo: {
glueData_.stackLimit_ = mainStackInfo_.stackLimit;
glueData_.leaveFrame_ = reinterpret_cast<uint64_t *>(mainStackInfo_.lastLeaveFrame);
isInSubStack_ = false;
LOG_ECMA(DEBUG) << "Switch to mainStack: "
<< ", main stack limit: " << mainStackInfo_.stackLimit
<< ", main stack lastLeaveFrame: " << mainStackInfo_.lastLeaveFrame;
break;
}
default:
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
}
void JSThread::TransferToRunning()
{
ASSERT(!g_isEnableCMCGC);
ASSERT(!IsDaemonThread());
ASSERT(currentThread == this);
StoreRunningState(ThreadState::RUNNING);
}
void JSThread::TransferDaemonThreadToRunning()
{
ASSERT(!g_isEnableCMCGC);
ASSERT(IsDaemonThread());
ASSERT(currentThread == this);
StoreRunningState(ThreadState::RUNNING);
}
void JSThread::StoreState(ThreadState newState)
{
ASSERT(!g_isEnableCMCGC);
while (true) {
ThreadStateAndFlags oldStateAndFlags;
oldStateAndFlags.asNonvolatileInt = glueData_.stateAndFlags_.asInt;
ThreadStateAndFlags newStateAndFlags;
newStateAndFlags.asNonvolatileStruct.flags = oldStateAndFlags.asNonvolatileStruct.flags;
newStateAndFlags.asNonvolatileStruct.state = newState;
bool done = glueData_.stateAndFlags_.asAtomicInt.compare_exchange_weak(oldStateAndFlags.asNonvolatileInt,
newStateAndFlags.asNonvolatileInt,
std::memory_order_release);
if (LIKELY(done)) {
break;
}
}
}
void JSThread::StoreRunningState([[maybe_unused]] ThreadState newState)
{
ASSERT(!g_isEnableCMCGC);
ASSERT(newState == ThreadState::RUNNING);
while (true) {
ThreadStateAndFlags oldStateAndFlags;
oldStateAndFlags.asNonvolatileInt = glueData_.stateAndFlags_.asInt;
ASSERT(oldStateAndFlags.asNonvolatileStruct.state != ThreadState::RUNNING);
if (LIKELY(oldStateAndFlags.asNonvolatileStruct.flags == ThreadFlag::NO_FLAGS)) {
ThreadStateAndFlags newStateAndFlags;
newStateAndFlags.asNonvolatileStruct.flags = oldStateAndFlags.asNonvolatileStruct.flags;
newStateAndFlags.asNonvolatileStruct.state = newState;
if (glueData_.stateAndFlags_.asAtomicInt.compare_exchange_weak(oldStateAndFlags.asNonvolatileInt,
newStateAndFlags.asNonvolatileInt,
std::memory_order_release)) {
break;
}
} else if ((oldStateAndFlags.asNonvolatileStruct.flags & ThreadFlag::ACTIVE_BARRIER) != 0) {
PassSuspendBarrier();
} else if ((oldStateAndFlags.asNonvolatileStruct.flags & ThreadFlag::SUSPEND_REQUEST) != 0) {
constexpr int TIMEOUT = 100;
OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "SuspendTime::StoreRunningState", "");
LockHolder lock(suspendLock_);
while (suspendCount_ > 0) {
suspendCondVar_.TimedWait(&suspendLock_, TIMEOUT);
}
ASSERT(!HasSuspendRequest());
} else {
ThreadStateAndFlags newStateAndFlags;
newStateAndFlags.asNonvolatileStruct.flags = oldStateAndFlags.asNonvolatileStruct.flags;
newStateAndFlags.asNonvolatileStruct.state = newState;
if (glueData_.stateAndFlags_.asAtomicInt.compare_exchange_weak(oldStateAndFlags.asNonvolatileInt,
newStateAndFlags.asNonvolatileInt,
std::memory_order_release)) {
if ((oldStateAndFlags.asNonvolatileStruct.flags & ThreadFlag::PENDING_GC_CALLBACKS) != 0) {
ClearFlag(ThreadFlag::PENDING_GC_CALLBACKS);
const_cast<Heap*>(vm_->GetHeap())->ProcessGCCallback();
}
if ((oldStateAndFlags.asNonvolatileStruct.flags & ThreadFlag::FULL_MARK_REQUEST) != 0) {
ClearFlag(ThreadFlag::FULL_MARK_REQUEST);
fullMarkRequest_ = const_cast<Heap*>(vm_->GetHeap())->TryTriggerFullMarkBySharedLimit();
}
if (fullMarkRequest_) {
SetFlag(ThreadFlag::FULL_MARK_REQUEST);
}
break;
}
}
}
}
inline void JSThread::StoreSuspendedState(ThreadState newState)
{
ASSERT(!g_isEnableCMCGC);
ASSERT(newState != ThreadState::RUNNING);
StoreState(newState);
}
void JSThread::PostFork()
{
SetThreadId();
if (currentThread == nullptr) {
currentThread = this;
if (LIKELY(!g_isEnableCMCGC)) {
ASSERT(GetState() == ThreadState::CREATED);
UpdateState(ThreadState::NATIVE);
} else {
GetThreadHolder()->TransferToNative();
}
} else {
ASSERT(currentThread == this);
if (LIKELY(!g_isEnableCMCGC)) {
ASSERT(GetState() == ThreadState::NATIVE);
} else {
GetThreadHolder()->TransferToNative();
}
}
}
#ifndef NDEBUG
bool JSThread::IsInManagedState() const
{
ASSERT(this == JSThread::GetCurrent());
return IsInRunningState();
}
MutatorLock::MutatorLockState JSThread::GetMutatorLockState() const
{
return mutatorLockState_;
}
void JSThread::SetMutatorLockState(MutatorLock::MutatorLockState newState)
{
mutatorLockState_ = newState;
}
#endif
JSHClass *JSThread::GetArrayInstanceHClass(ElementsKind kind, bool isPrototype) const
{
JSHandle<GlobalEnv> env = GetGlobalEnv();
return GetArrayInstanceHClass(env, kind, isPrototype, JSThread::ThreadKind::OtherThread);
}
JSHClass *JSThread::GetArrayInstanceHClass(JSHandle<GlobalEnv> env, ElementsKind kind,
bool isPrototype, ThreadKind threadKind) const
{
GlobalEnvField index = glueData_.arrayHClassIndexes_.GetArrayInstanceHClassIndex(kind, isPrototype);
JSHandle<JSTaggedValue> exceptArrayHClassHandle;
if (threadKind == JSThread::ThreadKind::JitThread) {
exceptArrayHClassHandle = env->GetGlobalEnvObjectWithBarrierByIndex(static_cast<size_t>(index));
} else {
exceptArrayHClassHandle = env->GetGlobalEnvObjectByIndex(static_cast<size_t>(index));
}
auto exceptRecvHClass = JSHClass::Cast(exceptArrayHClassHandle.GetTaggedValue().GetTaggedObject());
ASSERT(exceptRecvHClass->IsJSArray());
return exceptRecvHClass;
}
ModuleManager *JSThread::GetModuleManager() const
{
JSHandle<GlobalEnv> globalEnv = GetGlobalEnv();
JSHandle<JSNativePointer> nativePointer(globalEnv->GetModuleManagerNativePointer());
ModuleManager *moduleManager = reinterpret_cast<ModuleManager *>(nativePointer->GetExternalPointer());
return moduleManager;
}
JSTaggedValue JSThread::GetCurrentGlobalEnv(JSTaggedValue currentEnv)
{
auto globalEnv = BaseEnv::Cast(currentEnv.GetTaggedObject())->GetGlobalEnv(this);
if (globalEnv.IsHole()) {
return GetGlueGlobalEnv();
}
return globalEnv;
}
}