1/* 2 * Copyright (c) 2023-2024 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#include "ecmascript/ecma_handle_scope.h" 17 18#include "ecmascript/ecma_context.h" 19 20namespace panda::ecmascript { 21EcmaHandleScope::EcmaHandleScope(JSThread *thread) : thread_(thread) 22{ 23 auto context = thread_->GetCurrentEcmaContext(); 24 OpenHandleScope(context); 25 OpenPrimitiveScope(context); 26} 27 28void EcmaHandleScope::OpenHandleScope(EcmaContext *context) 29{ 30 prevNext_ = context->handleScopeStorageNext_; 31 prevEnd_ = context->handleScopeStorageEnd_; 32 prevHandleStorageIndex_ = context->currentHandleStorageIndex_; 33#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK 34 context->HandleScopeCountAdd(); 35 prevHandleScope_ = context->GetLastHandleScope(); 36 context->SetLastHandleScope(this); 37#endif 38} 39 40void EcmaHandleScope::OpenPrimitiveScope(EcmaContext *context) 41{ 42 prevPrimitiveNext_ = context->primitiveScopeStorageNext_; 43 prevPrimitiveEnd_ = context->primitiveScopeStorageEnd_; 44 prevPrimitiveStorageIndex_ = context->currentPrimitiveStorageIndex_; 45#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK 46 context->PrimitiveScopeCountAdd(); 47 prevPrimitiveScope_ = context->GetLastPrimitiveScope(); 48 context->SetLastPrimitiveScope(this); 49#endif 50} 51 52EcmaHandleScope::~EcmaHandleScope() 53{ 54 auto context = thread_->GetCurrentEcmaContext(); 55 CloseHandleScope(context); 56 ClosePrimitiveScope(context); 57} 58 59void EcmaHandleScope::CloseHandleScope(EcmaContext *context) 60{ 61#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK 62 context->HandleScopeCountDec(); 63 context->SetLastHandleScope(prevHandleScope_); 64 prevHandleScope_ = nullptr; 65#endif 66 context->handleScopeStorageNext_ = prevNext_; 67 if (context->handleScopeStorageEnd_ != prevEnd_) { 68 context->handleScopeStorageEnd_ = prevEnd_; 69 context->ShrinkHandleStorage(prevHandleStorageIndex_); 70 } 71} 72 73void EcmaHandleScope::ClosePrimitiveScope(EcmaContext *context) 74{ 75#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK 76 context->PrimitiveScopeCountDec(); 77 context->SetLastPrimitiveScope(prevPrimitiveScope_); 78 prevPrimitiveScope_ = nullptr; 79#endif 80 context->primitiveScopeStorageNext_ = prevPrimitiveNext_; 81 if (context->primitiveScopeStorageEnd_ != prevPrimitiveEnd_) { 82 context->primitiveScopeStorageEnd_ = prevPrimitiveEnd_; 83 context->ShrinkPrimitiveStorage(prevPrimitiveStorageIndex_); 84 } 85} 86 87uintptr_t EcmaHandleScope::NewHandle(JSThread *thread, JSTaggedType value) 88{ 89 CHECK_NO_HANDLE_ALLOC; 90#if ECMASCRIPT_ENABLE_THREAD_STATE_CHECK 91 if (UNLIKELY(!thread->IsInRunningStateOrProfiling())) { 92 LOG_ECMA(FATAL) << "New handle must be in jsthread running state"; 93 UNREACHABLE(); 94 } 95#endif 96 // Handle is a kind of GC_ROOT, and should only directly hold Obejct or Primitive, not a weak reference. 97 ASSERT(!JSTaggedValue(value).IsWeak()); 98 auto context = thread->GetCurrentEcmaContext(); 99#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK 100 // Each Handle must be managed by HandleScope, otherwise it may cause Handle leakage. 101 if (context->handleScopeCount_ <= 0) { 102 LOG_ECMA(ERROR) << "New handle must be in handlescope" << context->handleScopeCount_; 103 } 104 static const long MAYBE_HANDLE_LEAK_TIME_MS = 5000; 105 if (context->GetLastHandleScope() != nullptr) { 106 float totalSpentTime = context->GetLastHandleScope()->scope_.TotalSpentTime(); 107 if (totalSpentTime >= MAYBE_HANDLE_LEAK_TIME_MS) { 108 LOG_ECMA(INFO) << "New handle in scope count:" << context->handleScopeCount_ 109 << ", time:" << totalSpentTime << "ms"; 110 std::ostringstream stack; 111 Backtrace(stack, true); 112 LOG_ECMA(INFO) << stack.str(); 113 } 114 } 115#endif 116 auto result = context->handleScopeStorageNext_; 117 if (result == context->handleScopeStorageEnd_) { 118 result = reinterpret_cast<JSTaggedType *>(context->ExpandHandleStorage()); 119 } 120#if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 121 thread->CheckJSTaggedType(value); 122 if (result == nullptr) { 123 LOG_ECMA(ERROR) << "result is nullptr, New handle fail!"; 124 return 0U; 125 } 126#endif 127 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 128 context->handleScopeStorageNext_ = result + 1; 129 *result = value; 130 return reinterpret_cast<uintptr_t>(result); 131} 132 133uintptr_t EcmaHandleScope::NewPrimitiveHandle(JSThread *thread, JSTaggedType value) 134{ 135 CHECK_NO_HANDLE_ALLOC; 136 auto context = thread->GetCurrentEcmaContext(); 137#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK 138 // Each PrimitiveHandle must be managed by PrimitiveScope, otherwise it may cause Handle leakage. 139 if (context->primitiveScopeCount_ <= 0) { 140 LOG_ECMA(ERROR) << "New primitive must be in primitivescope" << context->primitiveScopeCount_; 141 } 142 static const long MAYBE_PRIMITIVE_LEAK_TIME_MS = 5000; 143 if (context->GetLastPrimitiveScope() != nullptr) { 144 float totalSpentTime = context->GetLastPrimitiveScope()->scope_.TotalSpentTime(); 145 if (totalSpentTime >= MAYBE_PRIMITIVE_LEAK_TIME_MS) { 146 LOG_ECMA(INFO) << "New primitiveHandle in scope count:" << context->primitiveScopeCount_ 147 << ", time:" << totalSpentTime << "ms"; 148 std::ostringstream stack; 149 Backtrace(stack, true); 150 LOG_ECMA(INFO) << stack.str(); 151 } 152 } 153#endif 154 auto result = context->primitiveScopeStorageNext_; 155 if (result == context->primitiveScopeStorageEnd_) { 156 result = reinterpret_cast<JSTaggedType *>(context->ExpandPrimitiveStorage()); 157 } 158#if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK 159 thread->CheckJSTaggedType(value); 160 if (result == nullptr) { 161 LOG_ECMA(ERROR) << "result is nullptr, New primitiveHandle fail!"; 162 return 0U; 163 } 164#endif 165 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 166 context->primitiveScopeStorageNext_ = result + 1; 167 *result = value; 168 return reinterpret_cast<uintptr_t>(result); 169} 170} // namespace panda::ecmascript