* Copyright (c) 2023-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_handle_scope.h"
#include "ecmascript/ecma_vm.h"
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
#include "ecmascript/dfx/hprof/heap_profiler.h"
#endif
#if defined(ENABLE_HITRACE_LOCAL_HANDLE_DETECT)
#ifdef HOOK_ENABLE
#include "memory_trace.h"
#include "musl_preinit_common.h"
#endif
#endif
namespace panda::ecmascript {
EcmaHandleScope::EcmaHandleScope(JSThread *thread) : thread_(thread)
{
auto vm = thread_->GetEcmaVM();
OpenHandleScope(vm);
OpenPrimitiveScope(vm);
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
auto heapProfiler = reinterpret_cast<HeapProfiler *>(HeapProfilerInterface::GetInstance(vm));
heapProfiler->IncreaseScopeCount();
heapProfiler->PushToActiveScopeStack(nullptr, this);
#endif
#if defined(ENABLE_HITRACE_LOCAL_HANDLE_DETECT)
const_cast<EcmaVM *>(vm)->IncreaseOpenHandleScopes();
scopeLevel_ = vm->GetOpenHandleScopes();
#endif
}
void EcmaHandleScope::OpenHandleScope(EcmaVM *vm)
{
prevNext_ = vm->GetHandleScopeStorageNext();
prevEnd_ = vm->GetHandleScopeStorageEnd();
prevHandleStorageIndex_ = vm->GetCurrentHandleStorageIndex();
}
void EcmaHandleScope::OpenPrimitiveScope(EcmaVM *vm)
{
prevPrimitiveNext_ = vm->GetPrimitiveScopeStorageNext();
prevPrimitiveEnd_ = vm->GetPrimitiveScopeStorageEnd();
prevPrimitiveStorageIndex_ = vm->GetCurrentPrimitiveStorageIndex();
}
EcmaHandleScope::~EcmaHandleScope()
{
auto vm = thread_->GetEcmaVM();
CloseHandleScope(vm);
ClosePrimitiveScope(vm);
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
auto heapProfiler = reinterpret_cast<HeapProfiler *>(HeapProfilerInterface::GetInstance(vm));
heapProfiler->DecreaseScopeCount();
heapProfiler->PopFromActiveScopeStack();
#endif
#if defined(ENABLE_HITRACE_LOCAL_HANDLE_DETECT)
if (scopeLevel_ != vm->GetOpenHandleScopes()) {
#ifdef HOOK_ENABLE
restraceExt(RES_ARK_LOCAL_HANDLE, (void *)this, sizeof(JSTaggedType), TAG_RES_ARK_LOCAL_HANDLE, true, false);
#endif
}
const_cast<EcmaVM *>(vm)->DecreaseOpenHandleScopes();
#endif
}
void EcmaHandleScope::CloseHandleScope(EcmaVM *vm)
{
vm->SetHandleScopeStorageNext(prevNext_);
if (vm->GetHandleScopeStorageEnd() != prevEnd_) {
vm->SetHandleScopeStorageEnd(prevEnd_);
vm->ShrinkHandleStorage(prevHandleStorageIndex_);
}
}
void EcmaHandleScope::ClosePrimitiveScope(EcmaVM *vm)
{
vm->SetPrimitiveScopeStorageNext(prevPrimitiveNext_);
if (vm->GetPrimitiveScopeStorageEnd() != prevPrimitiveEnd_) {
vm->SetPrimitiveScopeStorageEnd(prevPrimitiveEnd_);
vm->ShrinkPrimitiveStorage(prevPrimitiveStorageIndex_);
}
}
uintptr_t EcmaHandleScope::NewHandle(JSThread *thread, JSTaggedType value)
{
CHECK_NO_HANDLE_ALLOC;
#if ECMASCRIPT_ENABLE_THREAD_STATE_CHECK
if (UNLIKELY(!thread->IsInRunningStateOrProfiling())) {
LOG_ECMA(FATAL) << "New handle must be in jsthread running state";
UNREACHABLE();
}
#endif
ASSERT(!JSTaggedValue(value).IsWeak());
auto vm = thread->GetEcmaVM();
auto result = vm->GetHandleScopeStorageNext();
if (result == vm->GetHandleScopeStorageEnd()) {
result = reinterpret_cast<JSTaggedType *>(vm->ExpandHandleStorage());
}
#if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK
thread->CheckJSTaggedType(value);
if (result == nullptr) {
LOG_ECMA(ERROR) << "result is nullptr, New handle fail!";
return 0U;
}
#endif
vm->SetHandleScopeStorageNext(result + 1);
*result = value;
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
auto heapProfiler = reinterpret_cast<HeapProfiler *>(HeapProfilerInterface::GetInstance(vm));
if (heapProfiler->IsStartLocalHandleLeakDetect()) {
heapProfiler->StorePotentiallyLeakHandles(reinterpret_cast<uintptr_t>(result));
}
#endif
#ifdef ENABLE_HITRACE_LOCAL_HANDLE_DETECT
if (vm->GetOpenHandleScopes() == 0) {
#ifdef HOOK_ENABLE
restraceExt(RES_ARK_LOCAL_HANDLE, (void *)result, sizeof(JSTaggedType), TAG_RES_ARK_LOCAL_HANDLE, true, false);
#endif
}
#endif
return reinterpret_cast<uintptr_t>(result);
}
uintptr_t EcmaHandleScope::NewPrimitiveHandle(JSThread *thread, JSTaggedType value)
{
CHECK_NO_HANDLE_ALLOC;
auto vm = thread->GetEcmaVM();
auto result = vm->GetPrimitiveScopeStorageNext();
if (result == vm->GetPrimitiveScopeStorageEnd()) {
result = reinterpret_cast<JSTaggedType *>(vm->ExpandPrimitiveStorage());
}
#if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK
thread->CheckJSTaggedType(value);
if (result == nullptr) {
LOG_ECMA(ERROR) << "result is nullptr, New primitiveHandle fail!";
return 0U;
}
#endif
vm->SetPrimitiveScopeStorageNext(result + 1);
*result = value;
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
auto heapProfiler = reinterpret_cast<HeapProfiler *>(HeapProfilerInterface::GetInstance(vm));
if (heapProfiler->IsStartLocalHandleLeakDetect()) {
heapProfiler->StorePotentiallyLeakHandles(reinterpret_cast<uintptr_t>(result));
}
#endif
#ifdef ENABLE_HITRACE_LOCAL_HANDLE_DETECT
if (vm->GetOpenHandleScopes() == 0) {
#ifdef HOOK_ENABLE
restraceExt(RES_ARK_LOCAL_HANDLE, (void *)result, sizeof(JSTaggedType), TAG_RES_ARK_LOCAL_HANDLE, true, false);
#endif
}
#endif
return reinterpret_cast<uintptr_t>(result);
}
}