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