• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 namespace panda::ecmascript {
EcmaHandleScope(JSThread * thread)21 EcmaHandleScope::EcmaHandleScope(JSThread *thread) : thread_(thread)
22 {
23     auto context = thread_->GetCurrentEcmaContext();
24     OpenHandleScope(context);
25     OpenPrimitiveScope(context);
26 }
27 
OpenHandleScope(EcmaContext * context)28 void 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 
OpenPrimitiveScope(EcmaContext * context)40 void 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 
~EcmaHandleScope()52 EcmaHandleScope::~EcmaHandleScope()
53 {
54     auto context = thread_->GetCurrentEcmaContext();
55     CloseHandleScope(context);
56     ClosePrimitiveScope(context);
57 }
58 
CloseHandleScope(EcmaContext * context)59 void 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 
ClosePrimitiveScope(EcmaContext * context)73 void 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 
NewHandle(JSThread * thread,JSTaggedType value)87 uintptr_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 
NewPrimitiveHandle(JSThread * thread,JSTaggedType value)133 uintptr_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
171