• 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 
ExpandHandleStorage()354 uintptr_t *JSThread::ExpandHandleStorage()
355 {
356     return GetCurrentEcmaContext()->ExpandHandleStorage();
357 }
358 
ShrinkHandleStorage(int prevIndex)359 void JSThread::ShrinkHandleStorage(int prevIndex)
360 {
361     GetCurrentEcmaContext()->ShrinkHandleStorage(prevIndex);
362 }
363 
NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver,StableArrayChangeKind changeKind)364 void JSThread::NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver, StableArrayChangeKind changeKind)
365 {
366     if (!glueData_.stableArrayElementsGuardians_) {
367         return;
368     }
369     if (!receiver->GetJSHClass()->IsPrototype() && !receiver->IsJSArray()) {
370         return;
371     }
372     auto env = GetEcmaVM()->GetGlobalEnv();
373     if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() ||
374         receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) {
375         glueData_.stableArrayElementsGuardians_ = false;
376         return;
377     }
378     if (changeKind == StableArrayChangeKind::PROTO && receiver->IsJSArray()) {
379         glueData_.stableArrayElementsGuardians_ = false;
380     }
381 }
382 
ResetGuardians()383 void JSThread::ResetGuardians()
384 {
385     glueData_.stableArrayElementsGuardians_ = true;
386 }
387 
CheckSwitchDebuggerBCStub()388 void JSThread::CheckSwitchDebuggerBCStub()
389 {
390     auto isDebug = GetEcmaVM()->GetJsDebuggerManager()->IsDebugMode();
391     if (isDebug &&
392         glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) {
393         for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
394             auto stubEntry = glueData_.bcStubEntries_.Get(i);
395             auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i);
396             glueData_.bcDebuggerStubEntries_.Set(i, stubEntry);
397             glueData_.bcStubEntries_.Set(i, debuggerStubEbtry);
398         }
399     } else if (!isDebug &&
400         glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) {
401         for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
402             auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i);
403             auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i);
404             glueData_.bcStubEntries_.Set(i, stubEntry);
405             glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry);
406         }
407     }
408 }
409 
CheckOrSwitchPGOStubs()410 void JSThread::CheckOrSwitchPGOStubs()
411 {
412     bool isSwitch = false;
413     if (IsPGOProfilerEnable()) {
414         if (GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
415             SetBCStubStatus(BCStubStatus::PROFILE_BC_STUB);
416             isSwitch = true;
417         }
418     } else {
419         if (GetBCStubStatus() == BCStubStatus::PROFILE_BC_STUB) {
420             SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
421             isSwitch = true;
422         }
423     }
424     if (isSwitch) {
425         Address curAddress;
426 #define SWITCH_PGO_STUB_ENTRY(fromName, toName)                                                             \
427         curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName);                                     \
428         SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \
429         SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress);
430         ASM_INTERPRETER_BC_PROFILER_STUB_LIST(SWITCH_PGO_STUB_ENTRY)
431 #undef SWITCH_PGO_STUB_ENTRY
432     }
433 }
434 
CheckSafepoint()435 bool JSThread::CheckSafepoint()
436 {
437     ResetCheckSafePointStatus();
438     if (vmThreadControl_->VMNeedSuspension()) {
439         vmThreadControl_->SuspendVM();
440     }
441 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
442     if (needProfiling_.load() && !isProfiling_) {
443         DFXJSNApi::StartCpuProfilerForFile(vm_, profileName_, CpuProfiler::INTERVAL_OF_INNER_START);
444         SetNeedProfiling(false);
445     }
446 #endif // ECMASCRIPT_SUPPORT_CPUPROFILER
447     bool gcTriggered = false;
448 #ifndef NDEBUG
449     if (vm_->GetJSOptions().EnableForceGC()) {
450         GetEcmaVM()->CollectGarbage(TriggerGCType::FULL_GC);
451         gcTriggered = true;
452     }
453 #endif
454     if (IsMarkFinished() && GetEcmaVM()->GetHeap()->GetConcurrentMarker()->IsTriggeredConcurrentMark()) {
455         auto heap = GetEcmaVM()->GetHeap();
456         heap->GetConcurrentMarker()->HandleMarkingFinished();
457         gcTriggered = true;
458     }
459     return gcTriggered;
460 }
461 
CheckJSTaggedType(JSTaggedType value) const462 void JSThread::CheckJSTaggedType(JSTaggedType value) const
463 {
464     if (JSTaggedValue(value).IsHeapObject() &&
465         !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
466         LOG_FULL(FATAL) << "value:" << value << " is invalid!";
467     }
468 }
469 
CpuProfilerCheckJSTaggedType(JSTaggedType value) const470 bool JSThread::CpuProfilerCheckJSTaggedType(JSTaggedType value) const
471 {
472     if (JSTaggedValue(value).IsHeapObject() &&
473         !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
474         return false;
475     }
476     return true;
477 }
478 
479 // static
GetAsmStackLimit()480 size_t JSThread::GetAsmStackLimit()
481 {
482 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
483     // js stack limit
484     size_t result = GetCurrentStackPosition() - EcmaParamConfiguration::GetDefalutStackSize();
485     pthread_attr_t attr;
486     int ret = pthread_getattr_np(pthread_self(), &attr);
487     if (ret != 0) {
488         LOG_ECMA(ERROR) << "Get current thread attr failed";
489         return result;
490     }
491 
492     void *stackAddr = nullptr;
493     size_t size = 0;
494     ret = pthread_attr_getstack(&attr, &stackAddr, &size);
495     if (pthread_attr_destroy(&attr) != 0) {
496         LOG_ECMA(ERROR) << "Destroy current thread attr failed";
497     }
498     if (ret != 0) {
499         LOG_ECMA(ERROR) << "Get current thread stack size failed";
500         return result;
501     }
502 
503     bool isMainThread = IsMainThread();
504     uintptr_t threadStackLimit = reinterpret_cast<uintptr_t>(stackAddr);
505     uintptr_t threadStackStart = threadStackLimit + size;
506     if (isMainThread) {
507         struct rlimit rl;
508         ret = getrlimit(RLIMIT_STACK, &rl);
509         if (ret != 0) {
510             LOG_ECMA(ERROR) << "Get current thread stack size failed";
511             return result;
512         }
513         if (rl.rlim_cur > DEFAULT_MAX_SYSTEM_STACK_SIZE) {
514             LOG_ECMA(ERROR) << "Get current thread stack size exceed " << DEFAULT_MAX_SYSTEM_STACK_SIZE
515                             << " : " << rl.rlim_cur;
516             return result;
517         }
518         threadStackLimit = threadStackStart - rl.rlim_cur;
519     }
520 
521     if (result < threadStackLimit) {
522         result = threadStackLimit;
523     }
524 
525     LOG_INTERPRETER(DEBUG) << "Current thread stack start: " << reinterpret_cast<void *>(threadStackStart);
526     LOG_INTERPRETER(DEBUG) << "Used stack before js stack start: "
527                            << reinterpret_cast<void *>(threadStackStart - GetCurrentStackPosition());
528     LOG_INTERPRETER(DEBUG) << "Current thread asm stack limit: " << reinterpret_cast<void *>(result);
529 
530     // To avoid too much times of stack overflow checking, we only check stack overflow before push vregs or
531     // parameters of variable length. So we need a reserved size of stack to make sure stack won't be overflowed
532     // when push other data.
533     result += EcmaParamConfiguration::GetDefaultReservedStackSize();
534     if (threadStackStart <= result) {
535         LOG_FULL(FATAL) << "Too small stackSize to run jsvm";
536     }
537     return result;
538 #else
539     return 0;
540 #endif
541 }
542 
IsLegalAsmSp(uintptr_t sp) const543 bool JSThread::IsLegalAsmSp(uintptr_t sp) const
544 {
545     uint64_t bottom = GetStackLimit() - EcmaParamConfiguration::GetDefaultReservedStackSize();
546     uint64_t top = GetStackStart();
547     return (bottom <= sp && sp <= top);
548 }
549 
IsLegalThreadSp(uintptr_t sp) const550 bool JSThread::IsLegalThreadSp(uintptr_t sp) const
551 {
552     uintptr_t bottom = reinterpret_cast<uintptr_t>(glueData_.frameBase_);
553     size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
554     uintptr_t top = bottom + maxStackSize;
555     return (bottom <= sp && sp <= top);
556 }
557 
IsLegalSp(uintptr_t sp) const558 bool JSThread::IsLegalSp(uintptr_t sp) const
559 {
560     return IsLegalAsmSp(sp) || IsLegalThreadSp(sp);
561 }
562 
IsMainThread()563 bool JSThread::IsMainThread()
564 {
565 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
566     return getpid() == syscall(SYS_gettid);
567 #else
568     return true;
569 #endif
570 }
571 
PushContext(EcmaContext * context)572 void JSThread::PushContext(EcmaContext *context)
573 {
574     const_cast<Heap *>(vm_->GetHeap())->WaitAllTasksFinished();
575     contexts_.emplace_back(context);
576 
577     if (!currentContext_) {
578         // The first context in ecma vm.
579         currentContext_ = context;
580         context->SetFramePointers(const_cast<JSTaggedType *>(GetCurrentSPFrame()),
581             const_cast<JSTaggedType *>(GetLastLeaveFrame()),
582             const_cast<JSTaggedType *>(GetLastFp()));
583         context->SetFrameBase(glueData_.frameBase_);
584         context->SetStackLimit(glueData_.stackLimit_);
585         context->SetStackStart(glueData_.stackStart_);
586     } else {
587         // algin with 16
588         size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
589         context->SetFrameBase(static_cast<JSTaggedType *>(
590             vm_->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize)));
591         context->SetFramePointers(context->GetFrameBase() + maxStackSize, nullptr, nullptr);
592         if (IsAsmInterpreter()) {
593             context->SetStackLimit(GetAsmStackLimit());
594             context->SetStackStart(GetCurrentStackPosition());
595         }
596         EcmaInterpreter::InitStackFrame(context);
597     }
598 }
599 
PopContext()600 void JSThread::PopContext()
601 {
602     contexts_.pop_back();
603     currentContext_ = contexts_.back();
604 }
605 
SwitchCurrentContext(EcmaContext * currentContext,bool isInIterate)606 void JSThread::SwitchCurrentContext(EcmaContext *currentContext, bool isInIterate)
607 {
608     ASSERT(std::count(contexts_.begin(), contexts_.end(), currentContext));
609 
610     currentContext_->SetFramePointers(const_cast<JSTaggedType *>(GetCurrentSPFrame()),
611         const_cast<JSTaggedType *>(GetLastLeaveFrame()),
612         const_cast<JSTaggedType *>(GetLastFp()));
613     currentContext_->SetFrameBase(glueData_.frameBase_);
614     currentContext_->SetStackLimit(GetStackLimit());
615     currentContext_->SetStackStart(GetStackStart());
616     currentContext_->SetGlobalEnv(GetGlueGlobalEnv());
617     currentContext_->GetGlobalEnv()->SetJSGlobalObject(this, glueData_.globalObject_);
618 
619     SetCurrentSPFrame(currentContext->GetCurrentFrame());
620     SetLastLeaveFrame(currentContext->GetLeaveFrame());
621     SetLastFp(currentContext->GetLastFp());
622     glueData_.frameBase_ = currentContext->GetFrameBase();
623     glueData_.stackLimit_ = currentContext->GetStackLimit();
624     glueData_.stackStart_ = currentContext->GetStackStart();
625     if (!currentContext->GlobalEnvIsHole()) {
626         SetGlueGlobalEnv(*(currentContext->GetGlobalEnv()));
627         SetGlobalObject(currentContext->GetGlobalEnv()->GetGlobalObject());
628     }
629     if (!isInIterate) {
630         // If isInIterate is true, it means it is in GC iterate and global variables are no need to change.
631         glueData_.globalConst_ = const_cast<GlobalEnvConstants *>(currentContext->GlobalConstants());
632     }
633 
634     currentContext_ = currentContext;
635 }
636 
EraseContext(EcmaContext * context)637 bool JSThread::EraseContext(EcmaContext *context)
638 {
639     const_cast<Heap *>(vm_->GetHeap())->WaitAllTasksFinished();
640     bool isCurrentContext = false;
641     auto iter = std::find(contexts_.begin(), contexts_.end(), context);
642     if (*iter == context) {
643         if (currentContext_ == context) {
644             isCurrentContext = true;
645         }
646         contexts_.erase(iter);
647         if (isCurrentContext) {
648             SwitchCurrentContext(contexts_.back());
649         }
650         return true;
651     }
652     return false;
653 }
654 
GetPropertiesCache() const655 PropertiesCache *JSThread::GetPropertiesCache() const
656 {
657     return currentContext_->GetPropertiesCache();
658 }
659 
GetFirstGlobalConst() const660 const GlobalEnvConstants *JSThread::GetFirstGlobalConst() const
661 {
662     return contexts_[0]->GlobalConstants();
663 }
664 
IsAllContextsInitialized() const665 bool JSThread::IsAllContextsInitialized() const
666 {
667     for (auto item : contexts_) {
668         if (!item->IsInitialized()) {
669             return false;
670         }
671     }
672     return true;
673 }
674 }  // namespace panda::ecmascript
675