• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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/js_thread.h"
17 
18 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
19 #include <sys/resource.h>
20 #endif
21 
22 #if defined(ENABLE_EXCEPTION_BACKTRACE)
23 #include "ecmascript/platform/backtrace.h"
24 #endif
25 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
26 #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
27 #endif
28 #include "ecmascript/ecma_global_storage.h"
29 #include "ecmascript/ecma_param_configuration.h"
30 #include "ecmascript/global_env_constants-inl.h"
31 #include "ecmascript/ic/properties_cache.h"
32 #include "ecmascript/interpreter/interpreter-inl.h"
33 #include "ecmascript/log_wrapper.h"
34 #include "ecmascript/mem/mark_word.h"
35 #include "ecmascript/napi/include/dfx_jsnapi.h"
36 #include "ecmascript/platform/file.h"
37 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
38 
39 namespace panda::ecmascript {
40 using CommonStubCSigns = panda::ecmascript::kungfu::CommonStubCSigns;
41 using BytecodeStubCSigns = panda::ecmascript::kungfu::BytecodeStubCSigns;
42 
43 // static
Create(EcmaVM * vm)44 JSThread *JSThread::Create(EcmaVM *vm)
45 {
46     auto jsThread = new JSThread(vm);
47     AsmInterParsedOption asmInterOpt = vm->GetJSOptions().GetAsmInterParsedOption();
48     if (asmInterOpt.enableAsm) {
49         jsThread->EnableAsmInterpreter();
50     }
51 
52     jsThread->nativeAreaAllocator_ = vm->GetNativeAreaAllocator();
53     jsThread->heapRegionAllocator_ = vm->GetHeapRegionAllocator();
54     // algin with 16
55     size_t maxStackSize = vm->GetEcmaParamConfiguration().GetMaxStackSize();
56     jsThread->glueData_.frameBase_ = static_cast<JSTaggedType *>(
57         vm->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize));
58     jsThread->glueData_.currentFrame_ = jsThread->glueData_.frameBase_ + maxStackSize;
59     EcmaInterpreter::InitStackFrame(jsThread);
60 
61     if (jsThread->IsAsmInterpreter()) {
62         jsThread->glueData_.stackLimit_ = GetAsmStackLimit();
63         jsThread->glueData_.stackStart_ = GetCurrentStackPosition();
64     }
65     return jsThread;
66 }
67 
JSThread(EcmaVM * vm)68 JSThread::JSThread(EcmaVM *vm) : id_(os::thread::GetCurrentThreadId()), vm_(vm)
69 {
70     auto chunk = vm->GetChunk();
71     if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
72         globalStorage_ = chunk->New<EcmaGlobalStorage<Node>>(this, vm->GetNativeAreaAllocator());
73         newGlobalHandle_ = std::bind(&EcmaGlobalStorage<Node>::NewGlobalHandle, globalStorage_, std::placeholders::_1);
74         disposeGlobalHandle_ = std::bind(&EcmaGlobalStorage<Node>::DisposeGlobalHandle, globalStorage_,
75             std::placeholders::_1);
76         setWeak_ = std::bind(&EcmaGlobalStorage<Node>::SetWeak, globalStorage_, std::placeholders::_1,
77             std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
78         clearWeak_ = std::bind(&EcmaGlobalStorage<Node>::ClearWeak, globalStorage_, std::placeholders::_1);
79         isWeak_ = std::bind(&EcmaGlobalStorage<Node>::IsWeak, globalStorage_, std::placeholders::_1);
80     } else {
81         globalDebugStorage_ =
82             chunk->New<EcmaGlobalStorage<DebugNode>>(this, vm->GetNativeAreaAllocator());
83         newGlobalHandle_ = std::bind(&EcmaGlobalStorage<DebugNode>::NewGlobalHandle, globalDebugStorage_,
84             std::placeholders::_1);
85         disposeGlobalHandle_ = std::bind(&EcmaGlobalStorage<DebugNode>::DisposeGlobalHandle, globalDebugStorage_,
86             std::placeholders::_1);
87         setWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::SetWeak, globalDebugStorage_, std::placeholders::_1,
88             std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
89         clearWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::ClearWeak, globalDebugStorage_, std::placeholders::_1);
90         isWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::IsWeak, globalDebugStorage_, std::placeholders::_1);
91     }
92     vmThreadControl_ = new VmThreadControl(this);
93     SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
94 }
95 
~JSThread()96 JSThread::~JSThread()
97 {
98     if (globalStorage_ != nullptr) {
99         GetEcmaVM()->GetChunk()->Delete(globalStorage_);
100         globalStorage_ = nullptr;
101     }
102     if (globalDebugStorage_ != nullptr) {
103         GetEcmaVM()->GetChunk()->Delete(globalDebugStorage_);
104         globalDebugStorage_ = nullptr;
105     }
106 
107     for (auto item : contexts_) {
108         GetNativeAreaAllocator()->Free(item->GetFrameBase(), sizeof(JSTaggedType) *
109             vm_->GetEcmaParamConfiguration().GetMaxStackSize());
110         item->SetFrameBase(nullptr);
111         delete item;
112     }
113     contexts_.clear();
114 
115     glueData_.frameBase_ = nullptr;
116     nativeAreaAllocator_ = nullptr;
117     heapRegionAllocator_ = nullptr;
118     if (vmThreadControl_ != nullptr) {
119         delete vmThreadControl_;
120         vmThreadControl_ = nullptr;
121     }
122 }
123 
SetException(JSTaggedValue exception)124 void JSThread::SetException(JSTaggedValue exception)
125 {
126     glueData_.exception_ = exception;
127 #if defined(ENABLE_EXCEPTION_BACKTRACE)
128     if (vm_->GetJSOptions().EnableExceptionBacktrace()) {
129         LOG_ECMA(INFO) << "SetException:" << exception.GetRawData();
130         std::ostringstream stack;
131         Backtrace(stack);
132         LOG_ECMA(INFO) << stack.str();
133     }
134 #endif
135 }
136 
ClearException()137 void JSThread::ClearException()
138 {
139     glueData_.exception_ = JSTaggedValue::Hole();
140 }
141 
GetCurrentLexenv() const142 JSTaggedValue JSThread::GetCurrentLexenv() const
143 {
144     FrameHandler frameHandler(this);
145     return frameHandler.GetEnv();
146 }
147 
GetCurrentFrame() const148 const JSTaggedType *JSThread::GetCurrentFrame() const
149 {
150     if (IsAsmInterpreter()) {
151         return GetLastLeaveFrame();
152     }
153     return GetCurrentSPFrame();
154 }
155 
SetCurrentFrame(JSTaggedType * sp)156 void JSThread::SetCurrentFrame(JSTaggedType *sp)
157 {
158     if (IsAsmInterpreter()) {
159         return SetLastLeaveFrame(sp);
160     }
161     return SetCurrentSPFrame(sp);
162 }
163 
GetCurrentInterpretedFrame() const164 const JSTaggedType *JSThread::GetCurrentInterpretedFrame() const
165 {
166     if (IsAsmInterpreter()) {
167         auto frameHandler = FrameHandler(this);
168         return frameHandler.GetSp();
169     }
170     return GetCurrentSPFrame();
171 }
172 
IsStartGlobalLeakCheck() const173 bool JSThread::IsStartGlobalLeakCheck() const
174 {
175     return GetEcmaVM()->GetJSOptions().IsStartGlobalLeakCheck();
176 }
177 
EnableGlobalObjectLeakCheck() const178 bool JSThread::EnableGlobalObjectLeakCheck() const
179 {
180     return GetEcmaVM()->GetJSOptions().EnableGlobalObjectLeakCheck();
181 }
182 
EnableGlobalPrimitiveLeakCheck() const183 bool JSThread::EnableGlobalPrimitiveLeakCheck() const
184 {
185     return GetEcmaVM()->GetJSOptions().EnableGlobalPrimitiveLeakCheck();
186 }
187 
WriteToStackTraceFd(std::ostringstream & buffer) const188 void JSThread::WriteToStackTraceFd(std::ostringstream &buffer) const
189 {
190     if (stackTraceFd_ < 0) {
191         return;
192     }
193     buffer << std::endl;
194     DPrintf(reinterpret_cast<fd_t>(stackTraceFd_), buffer.str());
195     buffer.str("");
196 }
197 
SetStackTraceFd(int32_t fd)198 void JSThread::SetStackTraceFd(int32_t fd)
199 {
200     stackTraceFd_ = fd;
201 }
202 
CloseStackTraceFd()203 void JSThread::CloseStackTraceFd()
204 {
205     if (stackTraceFd_ != -1) {
206         FSync(reinterpret_cast<fd_t>(stackTraceFd_));
207         Close(reinterpret_cast<fd_t>(stackTraceFd_));
208         stackTraceFd_ = -1;
209     }
210 }
211 
Iterate(const RootVisitor & visitor,const RootRangeVisitor & rangeVisitor,const RootBaseAndDerivedVisitor & derivedVisitor)212 void JSThread::Iterate(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor,
213     const RootBaseAndDerivedVisitor &derivedVisitor)
214 {
215     if (!glueData_.exception_.IsHole()) {
216         visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.exception_)));
217     }
218 
219     EcmaContext *tempContext = currentContext_;
220     for (EcmaContext *context : contexts_) {
221         // visit stack roots
222         SwitchCurrentContext(context, true);
223         FrameHandler frameHandler(this);
224         frameHandler.Iterate(visitor, rangeVisitor, derivedVisitor);
225         context->Iterate(visitor, rangeVisitor);
226     }
227     SwitchCurrentContext(tempContext, true);
228     // visit tagged handle storage roots
229     if (vm_->GetJSOptions().EnableGlobalLeakCheck()) {
230         IterateHandleWithCheck(visitor, rangeVisitor);
231     } else {
232         globalStorage_->IterateUsageGlobal([visitor](Node *node) {
233             JSTaggedValue value(node->GetObject());
234             if (value.IsHeapObject()) {
235                 visitor(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
236             }
237         });
238     }
239 }
240 
IterateHandleWithCheck(const RootVisitor & visitor,const RootRangeVisitor & rangeVisitor)241 void JSThread::IterateHandleWithCheck(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor)
242 {
243     size_t handleCount = 0;
244     for (EcmaContext *context : contexts_) {
245         handleCount += context->IterateHandle(rangeVisitor);
246     }
247 
248     size_t globalCount = 0;
249     static const int JS_TYPE_LAST = static_cast<int>(JSType::TYPE_LAST);
250     int typeCount[JS_TYPE_LAST] = { 0 };
251     int primitiveCount = 0;
252     bool isStopObjectLeakCheck = EnableGlobalObjectLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
253     bool isStopPrimitiveLeakCheck = EnableGlobalPrimitiveLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
254     std::ostringstream buffer;
255     globalDebugStorage_->IterateUsageGlobal([this, visitor, &globalCount, &typeCount, &primitiveCount,
256         isStopObjectLeakCheck, isStopPrimitiveLeakCheck, &buffer](DebugNode *node) {
257         node->MarkCount();
258         JSTaggedValue value(node->GetObject());
259         if (value.IsHeapObject()) {
260             visitor(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
261             TaggedObject *object = value.GetTaggedObject();
262             MarkWord word(value.GetTaggedObject());
263             if (word.IsForwardingAddress()) {
264                 object = word.ToForwardingAddress();
265             }
266             typeCount[static_cast<int>(object->GetClass()->GetObjectType())]++;
267 
268             // Print global information about possible memory leaks.
269             // You can print the global new stack within the range of the leaked global number.
270             if (isStopObjectLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
271                 buffer << "Global maybe leak object address:" << std::hex << object <<
272                     ", type:" << JSHClass::DumpJSType(JSType(object->GetClass()->GetObjectType())) <<
273                     ", node address:" << node << ", number:" << std::dec <<  node->GetGlobalNumber() <<
274                     ", markCount:" << node->GetMarkCount();
275                 WriteToStackTraceFd(buffer);
276             }
277         } else {
278             primitiveCount++;
279             if (isStopPrimitiveLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
280                 buffer << "Global maybe leak primitive:" << std::hex << value.GetRawData() <<
281                     ", node address:" << node << ", number:" << std::dec <<  node->GetGlobalNumber() <<
282                     ", markCount:" << node->GetMarkCount();
283                 WriteToStackTraceFd(buffer);
284             }
285         }
286         globalCount++;
287     });
288 
289     if (isStopObjectLeakCheck || isStopPrimitiveLeakCheck) {
290         buffer << "Global leak check success!";
291         WriteToStackTraceFd(buffer);
292         CloseStackTraceFd();
293     }
294     // Determine whether memory leakage by checking handle and global count.
295     LOG_ECMA(INFO) << "Iterate root handle count:" << handleCount << ", global handle count:" << globalCount;
296     OPTIONAL_LOG(GetEcmaVM(), INFO) << "Global type Primitive count:" << primitiveCount;
297     // Print global object type statistic.
298     static const int MIN_COUNT_THRESHOLD = 50;
299     for (int i = 0; i < JS_TYPE_LAST; i++) {
300         if (typeCount[i] > MIN_COUNT_THRESHOLD) {
301             OPTIONAL_LOG(GetEcmaVM(), INFO) << "Global type " << JSHClass::DumpJSType(JSType(i))
302                                             << " count:" << typeCount[i];
303         }
304     }
305 }
306 
IterateWeakEcmaGlobalStorage(const WeakRootVisitor & visitor)307 void JSThread::IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor)
308 {
309     auto callBack = [this, visitor](WeakNode *node) {
310         JSTaggedValue value(node->GetObject());
311         if (!value.IsHeapObject()) {
312             return;
313         }
314         auto object = value.GetTaggedObject();
315         auto fwd = visitor(object);
316         if (fwd == nullptr) {
317             // undefind
318             node->SetObject(JSTaggedValue::Undefined().GetRawData());
319             auto nativeFinalizeCallback = node->GetNativeFinalizeCallback();
320             if (nativeFinalizeCallback) {
321                 weakNodeNativeFinalizeCallbacks_.push_back(std::make_pair(nativeFinalizeCallback,
322                                                                           node->GetReference()));
323             }
324             if (!node->CallFreeGlobalCallback()) {
325                 DisposeGlobalHandle(ToUintPtr(node));
326             }
327         } else if (fwd != object) {
328             // update
329             node->SetObject(JSTaggedValue(fwd).GetRawData());
330         }
331     };
332     if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
333         globalStorage_->IterateWeakUsageGlobal(callBack);
334     } else {
335         globalDebugStorage_->IterateWeakUsageGlobal(callBack);
336     }
337 }
338 
DoStackOverflowCheck(const JSTaggedType * sp)339 bool JSThread::DoStackOverflowCheck(const JSTaggedType *sp)
340 {
341     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
342     if (UNLIKELY(sp <= glueData_.frameBase_ + RESERVE_STACK_SIZE)) {
343         LOG_ECMA(ERROR) << "Stack overflow! Remaining stack size is: " << (sp - glueData_.frameBase_);
344         if (LIKELY(!HasPendingException())) {
345             ObjectFactory *factory = GetEcmaVM()->GetFactory();
346             JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!", false);
347             SetException(error.GetTaggedValue());
348         }
349         return true;
350     }
351     return false;
352 }
353 
DoStackLimitCheck()354 bool JSThread::DoStackLimitCheck()
355 {
356     if (UNLIKELY(GetCurrentStackPosition() < GetStackLimit())) {
357         vm_->CheckThread();
358         LOG_ECMA(ERROR) << "Stack overflow! current:" << GetCurrentStackPosition() << " limit:" << GetStackLimit();
359         if (!IsCrossThreadExecutionEnable() && LIKELY(!HasPendingException())) {
360             ObjectFactory *factory = GetEcmaVM()->GetFactory();
361             JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!", false);
362             SetException(error.GetTaggedValue());
363         }
364         return true;
365     }
366     return false;
367 }
368 
DoAsmStackOverflowCheck()369 bool JSThread::DoAsmStackOverflowCheck()
370 {
371     // check stack overflow because infinite recursion may occur
372     return (IsAsmInterpreter() && DoStackLimitCheck());
373 }
374 
ExpandHandleStorage()375 uintptr_t *JSThread::ExpandHandleStorage()
376 {
377     return GetCurrentEcmaContext()->ExpandHandleStorage();
378 }
379 
ShrinkHandleStorage(int prevIndex)380 void JSThread::ShrinkHandleStorage(int prevIndex)
381 {
382     GetCurrentEcmaContext()->ShrinkHandleStorage(prevIndex);
383 }
384 
NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver,StableArrayChangeKind changeKind)385 void JSThread::NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver, StableArrayChangeKind changeKind)
386 {
387     if (!glueData_.stableArrayElementsGuardians_) {
388         return;
389     }
390     if (!receiver->GetJSHClass()->IsPrototype() && !receiver->IsJSArray()) {
391         return;
392     }
393     auto env = GetEcmaVM()->GetGlobalEnv();
394     if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() ||
395         receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) {
396         glueData_.stableArrayElementsGuardians_ = false;
397         return;
398     }
399     if (changeKind == StableArrayChangeKind::PROTO && receiver->IsJSArray()) {
400         glueData_.stableArrayElementsGuardians_ = false;
401     }
402 }
403 
ResetGuardians()404 void JSThread::ResetGuardians()
405 {
406     glueData_.stableArrayElementsGuardians_ = true;
407 }
408 
CheckSwitchDebuggerBCStub()409 void JSThread::CheckSwitchDebuggerBCStub()
410 {
411     auto isDebug = GetEcmaVM()->GetJsDebuggerManager()->IsDebugMode();
412     if (isDebug &&
413         glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) {
414         for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
415             auto stubEntry = glueData_.bcStubEntries_.Get(i);
416             auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i);
417             glueData_.bcDebuggerStubEntries_.Set(i, stubEntry);
418             glueData_.bcStubEntries_.Set(i, debuggerStubEbtry);
419         }
420     } else if (!isDebug &&
421         glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) {
422         for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
423             auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i);
424             auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i);
425             glueData_.bcStubEntries_.Set(i, stubEntry);
426             glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry);
427         }
428     }
429 }
430 
CheckOrSwitchPGOStubs()431 void JSThread::CheckOrSwitchPGOStubs()
432 {
433     bool isSwitch = false;
434     if (IsPGOProfilerEnable()) {
435         if (GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
436             SetBCStubStatus(BCStubStatus::PROFILE_BC_STUB);
437             isSwitch = true;
438         }
439     } else {
440         if (GetBCStubStatus() == BCStubStatus::PROFILE_BC_STUB) {
441             SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
442             isSwitch = true;
443         }
444     }
445     if (isSwitch) {
446         Address curAddress;
447 #define SWITCH_PGO_STUB_ENTRY(fromName, toName)                                                             \
448         curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName);                                     \
449         SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \
450         SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress);
451         ASM_INTERPRETER_BC_PROFILER_STUB_LIST(SWITCH_PGO_STUB_ENTRY)
452 #undef SWITCH_PGO_STUB_ENTRY
453     }
454 }
455 
CheckSafepoint()456 bool JSThread::CheckSafepoint()
457 {
458     ResetCheckSafePointStatus();
459     if (vmThreadControl_->VMNeedSuspension()) {
460         vmThreadControl_->SuspendVM();
461     }
462 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
463     if (needProfiling_.load() && !isProfiling_) {
464         DFXJSNApi::StartCpuProfilerForFile(vm_, profileName_, CpuProfiler::INTERVAL_OF_INNER_START);
465         SetNeedProfiling(false);
466     }
467 #endif // ECMASCRIPT_SUPPORT_CPUPROFILER
468     bool gcTriggered = false;
469 #ifndef NDEBUG
470     if (vm_->GetJSOptions().EnableForceGC()) {
471         GetEcmaVM()->CollectGarbage(TriggerGCType::FULL_GC);
472         gcTriggered = true;
473     }
474 #endif
475     if (IsMarkFinished() && GetEcmaVM()->GetHeap()->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
476         auto heap = GetEcmaVM()->GetHeap();
477         heap->GetConcurrentMarker()->HandleMarkingFinished();
478         gcTriggered = true;
479     }
480     return gcTriggered;
481 }
482 
CheckJSTaggedType(JSTaggedType value) const483 void JSThread::CheckJSTaggedType(JSTaggedType value) const
484 {
485     if (JSTaggedValue(value).IsHeapObject() &&
486         !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
487         LOG_FULL(FATAL) << "value:" << value << " is invalid!";
488     }
489 }
490 
CpuProfilerCheckJSTaggedType(JSTaggedType value) const491 bool JSThread::CpuProfilerCheckJSTaggedType(JSTaggedType value) const
492 {
493     if (JSTaggedValue(value).IsHeapObject() &&
494         !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
495         return false;
496     }
497     return true;
498 }
499 
500 // static
GetAsmStackLimit()501 size_t JSThread::GetAsmStackLimit()
502 {
503 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
504     // js stack limit
505     size_t result = GetCurrentStackPosition() - EcmaParamConfiguration::GetDefalutStackSize();
506     pthread_attr_t attr;
507     int ret = pthread_getattr_np(pthread_self(), &attr);
508     if (ret != 0) {
509         LOG_ECMA(ERROR) << "Get current thread attr failed";
510         return result;
511     }
512 
513     void *stackAddr = nullptr;
514     size_t size = 0;
515     ret = pthread_attr_getstack(&attr, &stackAddr, &size);
516     if (pthread_attr_destroy(&attr) != 0) {
517         LOG_ECMA(ERROR) << "Destroy current thread attr failed";
518     }
519     if (ret != 0) {
520         LOG_ECMA(ERROR) << "Get current thread stack size failed";
521         return result;
522     }
523 
524     bool isMainThread = IsMainThread();
525     uintptr_t threadStackLimit = reinterpret_cast<uintptr_t>(stackAddr);
526     uintptr_t threadStackStart = threadStackLimit + size;
527     if (isMainThread) {
528         struct rlimit rl;
529         ret = getrlimit(RLIMIT_STACK, &rl);
530         if (ret != 0) {
531             LOG_ECMA(ERROR) << "Get current thread stack size failed";
532             return result;
533         }
534         if (rl.rlim_cur > DEFAULT_MAX_SYSTEM_STACK_SIZE) {
535             LOG_ECMA(ERROR) << "Get current thread stack size exceed " << DEFAULT_MAX_SYSTEM_STACK_SIZE
536                             << " : " << rl.rlim_cur;
537             return result;
538         }
539         threadStackLimit = threadStackStart - rl.rlim_cur;
540     }
541 
542     if (result < threadStackLimit) {
543         result = threadStackLimit;
544     }
545 
546     LOG_INTERPRETER(DEBUG) << "Current thread stack start: " << reinterpret_cast<void *>(threadStackStart);
547     LOG_INTERPRETER(DEBUG) << "Used stack before js stack start: "
548                            << reinterpret_cast<void *>(threadStackStart - GetCurrentStackPosition());
549     LOG_INTERPRETER(DEBUG) << "Current thread asm stack limit: " << reinterpret_cast<void *>(result);
550 
551     // To avoid too much times of stack overflow checking, we only check stack overflow before push vregs or
552     // parameters of variable length. So we need a reserved size of stack to make sure stack won't be overflowed
553     // when push other data.
554     result += EcmaParamConfiguration::GetDefaultReservedStackSize();
555     if (threadStackStart <= result) {
556         LOG_FULL(FATAL) << "Too small stackSize to run jsvm";
557     }
558     return result;
559 #else
560     return 0;
561 #endif
562 }
563 
IsLegalAsmSp(uintptr_t sp) const564 bool JSThread::IsLegalAsmSp(uintptr_t sp) const
565 {
566     uint64_t bottom = GetStackLimit() - EcmaParamConfiguration::GetDefaultReservedStackSize();
567     uint64_t top = GetStackStart();
568     return (bottom <= sp && sp <= top);
569 }
570 
IsLegalThreadSp(uintptr_t sp) const571 bool JSThread::IsLegalThreadSp(uintptr_t sp) const
572 {
573     uintptr_t bottom = reinterpret_cast<uintptr_t>(glueData_.frameBase_);
574     size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
575     uintptr_t top = bottom + maxStackSize;
576     return (bottom <= sp && sp <= top);
577 }
578 
IsLegalSp(uintptr_t sp) const579 bool JSThread::IsLegalSp(uintptr_t sp) const
580 {
581     return IsLegalAsmSp(sp) || IsLegalThreadSp(sp);
582 }
583 
IsMainThread()584 bool JSThread::IsMainThread()
585 {
586 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
587     return getpid() == syscall(SYS_gettid);
588 #else
589     return true;
590 #endif
591 }
592 
PushContext(EcmaContext * context)593 void JSThread::PushContext(EcmaContext *context)
594 {
595     const_cast<Heap *>(vm_->GetHeap())->WaitAllTasksFinished();
596     contexts_.emplace_back(context);
597 
598     if (!currentContext_) {
599         // The first context in ecma vm.
600         currentContext_ = context;
601         context->SetFramePointers(const_cast<JSTaggedType *>(GetCurrentSPFrame()),
602             const_cast<JSTaggedType *>(GetLastLeaveFrame()),
603             const_cast<JSTaggedType *>(GetLastFp()));
604         context->SetFrameBase(glueData_.frameBase_);
605         context->SetStackLimit(glueData_.stackLimit_);
606         context->SetStackStart(glueData_.stackStart_);
607     } else {
608         // algin with 16
609         size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
610         context->SetFrameBase(static_cast<JSTaggedType *>(
611             vm_->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize)));
612         context->SetFramePointers(context->GetFrameBase() + maxStackSize, nullptr, nullptr);
613         if (IsAsmInterpreter()) {
614             context->SetStackLimit(GetAsmStackLimit());
615             context->SetStackStart(GetCurrentStackPosition());
616         }
617         EcmaInterpreter::InitStackFrame(context);
618     }
619 }
620 
PopContext()621 void JSThread::PopContext()
622 {
623     contexts_.pop_back();
624     currentContext_ = contexts_.back();
625 }
626 
SwitchCurrentContext(EcmaContext * currentContext,bool isInIterate)627 void JSThread::SwitchCurrentContext(EcmaContext *currentContext, bool isInIterate)
628 {
629     ASSERT(std::count(contexts_.begin(), contexts_.end(), currentContext));
630 
631     currentContext_->SetFramePointers(const_cast<JSTaggedType *>(GetCurrentSPFrame()),
632         const_cast<JSTaggedType *>(GetLastLeaveFrame()),
633         const_cast<JSTaggedType *>(GetLastFp()));
634     currentContext_->SetFrameBase(glueData_.frameBase_);
635     currentContext_->SetStackLimit(GetStackLimit());
636     currentContext_->SetStackStart(GetStackStart());
637     currentContext_->SetGlobalEnv(GetGlueGlobalEnv());
638     currentContext_->GetGlobalEnv()->SetJSGlobalObject(this, glueData_.globalObject_);
639 
640     SetCurrentSPFrame(currentContext->GetCurrentFrame());
641     SetLastLeaveFrame(currentContext->GetLeaveFrame());
642     SetLastFp(currentContext->GetLastFp());
643     glueData_.frameBase_ = currentContext->GetFrameBase();
644     glueData_.stackLimit_ = currentContext->GetStackLimit();
645     glueData_.stackStart_ = currentContext->GetStackStart();
646     if (!currentContext->GlobalEnvIsHole()) {
647         SetGlueGlobalEnv(*(currentContext->GetGlobalEnv()));
648         SetGlobalObject(currentContext->GetGlobalEnv()->GetGlobalObject());
649     }
650     if (!isInIterate) {
651         // If isInIterate is true, it means it is in GC iterate and global variables are no need to change.
652         glueData_.globalConst_ = const_cast<GlobalEnvConstants *>(currentContext->GlobalConstants());
653     }
654 
655     currentContext_ = currentContext;
656 }
657 
EraseContext(EcmaContext * context)658 bool JSThread::EraseContext(EcmaContext *context)
659 {
660     const_cast<Heap *>(vm_->GetHeap())->WaitAllTasksFinished();
661     bool isCurrentContext = false;
662     auto iter = std::find(contexts_.begin(), contexts_.end(), context);
663     if (*iter == context) {
664         if (currentContext_ == context) {
665             isCurrentContext = true;
666         }
667         contexts_.erase(iter);
668         if (isCurrentContext) {
669             SwitchCurrentContext(contexts_.back());
670         }
671         return true;
672     }
673     return false;
674 }
675 
GetPropertiesCache() const676 PropertiesCache *JSThread::GetPropertiesCache() const
677 {
678     return currentContext_->GetPropertiesCache();
679 }
680 
GetFirstGlobalConst() const681 const GlobalEnvConstants *JSThread::GetFirstGlobalConst() const
682 {
683     return contexts_[0]->GlobalConstants();
684 }
685 
IsAllContextsInitialized() const686 bool JSThread::IsAllContextsInitialized() const
687 {
688     for (auto item : contexts_) {
689         if (!item->IsInitialized()) {
690             return false;
691         }
692     }
693     return true;
694 }
695 }  // namespace panda::ecmascript
696