• 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 #include "ecmascript/builtin_entries.h"
18 #include "ecmascript/js_tagged_value.h"
19 #include "ecmascript/runtime_call_id.h"
20 
21 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
22 #include <sys/resource.h>
23 #endif
24 
25 #if defined(ENABLE_EXCEPTION_BACKTRACE)
26 #include "ecmascript/platform/backtrace.h"
27 #endif
28 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
29 #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
30 #endif
31 #include "ecmascript/dfx/vm_thread_control.h"
32 #include "ecmascript/ecma_global_storage.h"
33 #include "ecmascript/ecma_param_configuration.h"
34 #include "ecmascript/global_env_constants-inl.h"
35 #include "ecmascript/ic/properties_cache.h"
36 #include "ecmascript/interpreter/interpreter-inl.h"
37 #include "ecmascript/log_wrapper.h"
38 #include "ecmascript/mem/mark_word.h"
39 #include "ecmascript/napi/include/dfx_jsnapi.h"
40 #include "ecmascript/platform/file.h"
41 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
42 #include "ecmascript/builtin_entries.h"
43 
44 namespace panda::ecmascript {
45 using CommonStubCSigns = panda::ecmascript::kungfu::CommonStubCSigns;
46 using BytecodeStubCSigns = panda::ecmascript::kungfu::BytecodeStubCSigns;
47 
48 // static
Create(EcmaVM * vm)49 JSThread *JSThread::Create(EcmaVM *vm)
50 {
51     auto jsThread = new JSThread(vm);
52     AsmInterParsedOption asmInterOpt = vm->GetJSOptions().GetAsmInterParsedOption();
53     if (asmInterOpt.enableAsm) {
54         jsThread->EnableAsmInterpreter();
55     }
56 
57     jsThread->nativeAreaAllocator_ = vm->GetNativeAreaAllocator();
58     jsThread->heapRegionAllocator_ = vm->GetHeapRegionAllocator();
59     // algin with 16
60     size_t maxStackSize = vm->GetEcmaParamConfiguration().GetMaxStackSize();
61     jsThread->glueData_.frameBase_ = static_cast<JSTaggedType *>(
62         vm->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize));
63     jsThread->glueData_.currentFrame_ = jsThread->glueData_.frameBase_ + maxStackSize;
64     EcmaInterpreter::InitStackFrame(jsThread);
65 
66     jsThread->glueData_.stackLimit_ = GetAsmStackLimit();
67     jsThread->glueData_.stackStart_ = GetCurrentStackPosition();
68     return jsThread;
69 }
70 
JSThread(EcmaVM * vm)71 JSThread::JSThread(EcmaVM *vm) : id_(os::thread::GetCurrentThreadId()), vm_(vm)
72 {
73     auto chunk = vm->GetChunk();
74     if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
75         globalStorage_ = chunk->New<EcmaGlobalStorage<Node>>(this, vm->GetNativeAreaAllocator());
76         newGlobalHandle_ = std::bind(&EcmaGlobalStorage<Node>::NewGlobalHandle, globalStorage_, std::placeholders::_1);
77         disposeGlobalHandle_ = std::bind(&EcmaGlobalStorage<Node>::DisposeGlobalHandle, globalStorage_,
78             std::placeholders::_1);
79         setWeak_ = std::bind(&EcmaGlobalStorage<Node>::SetWeak, globalStorage_, std::placeholders::_1,
80             std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
81         clearWeak_ = std::bind(&EcmaGlobalStorage<Node>::ClearWeak, globalStorage_, std::placeholders::_1);
82         isWeak_ = std::bind(&EcmaGlobalStorage<Node>::IsWeak, globalStorage_, std::placeholders::_1);
83     } else {
84         globalDebugStorage_ =
85             chunk->New<EcmaGlobalStorage<DebugNode>>(this, vm->GetNativeAreaAllocator());
86         newGlobalHandle_ = std::bind(&EcmaGlobalStorage<DebugNode>::NewGlobalHandle, globalDebugStorage_,
87             std::placeholders::_1);
88         disposeGlobalHandle_ = std::bind(&EcmaGlobalStorage<DebugNode>::DisposeGlobalHandle, globalDebugStorage_,
89             std::placeholders::_1);
90         setWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::SetWeak, globalDebugStorage_, std::placeholders::_1,
91             std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
92         clearWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::ClearWeak, globalDebugStorage_, std::placeholders::_1);
93         isWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::IsWeak, globalDebugStorage_, std::placeholders::_1);
94     }
95     vmThreadControl_ = new VmThreadControl(this);
96     SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
97 }
98 
~JSThread()99 JSThread::~JSThread()
100 {
101     if (globalStorage_ != nullptr) {
102         GetEcmaVM()->GetChunk()->Delete(globalStorage_);
103         globalStorage_ = nullptr;
104     }
105     if (globalDebugStorage_ != nullptr) {
106         GetEcmaVM()->GetChunk()->Delete(globalDebugStorage_);
107         globalDebugStorage_ = nullptr;
108     }
109 
110     for (auto item : contexts_) {
111         GetNativeAreaAllocator()->Free(item->GetFrameBase(), sizeof(JSTaggedType) *
112             vm_->GetEcmaParamConfiguration().GetMaxStackSize());
113         item->SetFrameBase(nullptr);
114         delete item;
115     }
116     contexts_.clear();
117     GetNativeAreaAllocator()->FreeArea(regExpCache_);
118 
119     glueData_.frameBase_ = nullptr;
120     nativeAreaAllocator_ = nullptr;
121     heapRegionAllocator_ = nullptr;
122     regExpCache_ = nullptr;
123     if (vmThreadControl_ != nullptr) {
124         delete vmThreadControl_;
125         vmThreadControl_ = nullptr;
126     }
127 }
128 
SetException(JSTaggedValue exception)129 void JSThread::SetException(JSTaggedValue exception)
130 {
131     glueData_.exception_ = exception;
132 #if defined(ENABLE_EXCEPTION_BACKTRACE)
133     if (vm_->GetJSOptions().EnableExceptionBacktrace()) {
134         LOG_ECMA(INFO) << "SetException:" << exception.GetRawData();
135         std::ostringstream stack;
136         Backtrace(stack);
137         LOG_ECMA(INFO) << stack.str();
138     }
139 #endif
140 }
141 
ClearException()142 void JSThread::ClearException()
143 {
144     glueData_.exception_ = JSTaggedValue::Hole();
145 }
146 
GetCurrentLexenv() const147 JSTaggedValue JSThread::GetCurrentLexenv() const
148 {
149     FrameHandler frameHandler(this);
150     return frameHandler.GetEnv();
151 }
152 
GetCurrentFrame() const153 const JSTaggedType *JSThread::GetCurrentFrame() const
154 {
155     if (IsAsmInterpreter()) {
156         return GetLastLeaveFrame();
157     }
158     return GetCurrentSPFrame();
159 }
160 
SetCurrentFrame(JSTaggedType * sp)161 void JSThread::SetCurrentFrame(JSTaggedType *sp)
162 {
163     if (IsAsmInterpreter()) {
164         return SetLastLeaveFrame(sp);
165     }
166     return SetCurrentSPFrame(sp);
167 }
168 
GetCurrentInterpretedFrame() const169 const JSTaggedType *JSThread::GetCurrentInterpretedFrame() const
170 {
171     if (IsAsmInterpreter()) {
172         auto frameHandler = FrameHandler(this);
173         return frameHandler.GetSp();
174     }
175     return GetCurrentSPFrame();
176 }
177 
IsStartGlobalLeakCheck() const178 bool JSThread::IsStartGlobalLeakCheck() const
179 {
180     return GetEcmaVM()->GetJSOptions().IsStartGlobalLeakCheck();
181 }
182 
EnableGlobalObjectLeakCheck() const183 bool JSThread::EnableGlobalObjectLeakCheck() const
184 {
185     return GetEcmaVM()->GetJSOptions().EnableGlobalObjectLeakCheck();
186 }
187 
EnableGlobalPrimitiveLeakCheck() const188 bool JSThread::EnableGlobalPrimitiveLeakCheck() const
189 {
190     return GetEcmaVM()->GetJSOptions().EnableGlobalPrimitiveLeakCheck();
191 }
192 
WriteToStackTraceFd(std::ostringstream & buffer) const193 void JSThread::WriteToStackTraceFd(std::ostringstream &buffer) const
194 {
195     if (stackTraceFd_ < 0) {
196         return;
197     }
198     buffer << std::endl;
199     DPrintf(reinterpret_cast<fd_t>(stackTraceFd_), buffer.str());
200     buffer.str("");
201 }
202 
SetStackTraceFd(int32_t fd)203 void JSThread::SetStackTraceFd(int32_t fd)
204 {
205     stackTraceFd_ = fd;
206 }
207 
CloseStackTraceFd()208 void JSThread::CloseStackTraceFd()
209 {
210     if (stackTraceFd_ != -1) {
211         FSync(reinterpret_cast<fd_t>(stackTraceFd_));
212         Close(reinterpret_cast<fd_t>(stackTraceFd_));
213         stackTraceFd_ = -1;
214     }
215 }
216 
Iterate(const RootVisitor & visitor,const RootRangeVisitor & rangeVisitor,const RootBaseAndDerivedVisitor & derivedVisitor)217 void JSThread::Iterate(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor,
218     const RootBaseAndDerivedVisitor &derivedVisitor)
219 {
220     if (!glueData_.exception_.IsHole()) {
221         visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.exception_)));
222     }
223     visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.singleCharTable_)));
224     rangeVisitor(
225         Root::ROOT_VM, ObjectSlot(glueData_.builtinEntries_.Begin()), ObjectSlot(glueData_.builtinEntries_.End()));
226 
227     EcmaContext *tempContext = glueData_.currentContext_;
228     for (EcmaContext *context : contexts_) {
229         // visit stack roots
230         SwitchCurrentContext(context, true);
231         FrameHandler frameHandler(this);
232         frameHandler.Iterate(visitor, rangeVisitor, derivedVisitor);
233         context->Iterate(visitor, rangeVisitor);
234     }
235     SwitchCurrentContext(tempContext, true);
236     // visit tagged handle storage roots
237     if (vm_->GetJSOptions().EnableGlobalLeakCheck()) {
238         IterateHandleWithCheck(visitor, rangeVisitor);
239     } else {
240         size_t globalCount = 0;
241         globalStorage_->IterateUsageGlobal([visitor, &globalCount](Node *node) {
242             JSTaggedValue value(node->GetObject());
243             if (value.IsHeapObject()) {
244                 visitor(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
245             }
246             globalCount++;
247         });
248         static bool hasCheckedGlobalCount = false;
249         static const size_t WARN_GLOBAL_COUNT = 100000;
250         if (!hasCheckedGlobalCount && globalCount >= WARN_GLOBAL_COUNT) {
251             LOG_ECMA(WARN) << "Global reference count is " << globalCount << ",It exceed the upper limit 100000!";
252             hasCheckedGlobalCount = true;
253         }
254     }
255 }
256 
IterateHandleWithCheck(const RootVisitor & visitor,const RootRangeVisitor & rangeVisitor)257 void JSThread::IterateHandleWithCheck(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor)
258 {
259     size_t handleCount = 0;
260     for (EcmaContext *context : contexts_) {
261         handleCount += context->IterateHandle(rangeVisitor);
262     }
263 
264     size_t globalCount = 0;
265     static const int JS_TYPE_LAST = static_cast<int>(JSType::TYPE_LAST);
266     int typeCount[JS_TYPE_LAST] = { 0 };
267     int primitiveCount = 0;
268     bool isStopObjectLeakCheck = EnableGlobalObjectLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
269     bool isStopPrimitiveLeakCheck = EnableGlobalPrimitiveLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
270     std::ostringstream buffer;
271     globalDebugStorage_->IterateUsageGlobal([this, visitor, &globalCount, &typeCount, &primitiveCount,
272         isStopObjectLeakCheck, isStopPrimitiveLeakCheck, &buffer](DebugNode *node) {
273         node->MarkCount();
274         JSTaggedValue value(node->GetObject());
275         if (value.IsHeapObject()) {
276             visitor(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
277             TaggedObject *object = value.GetTaggedObject();
278             MarkWord word(value.GetTaggedObject());
279             if (word.IsForwardingAddress()) {
280                 object = word.ToForwardingAddress();
281             }
282             typeCount[static_cast<int>(object->GetClass()->GetObjectType())]++;
283 
284             // Print global information about possible memory leaks.
285             // You can print the global new stack within the range of the leaked global number.
286             if (isStopObjectLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
287                 buffer << "Global maybe leak object address:" << std::hex << object <<
288                     ", type:" << JSHClass::DumpJSType(JSType(object->GetClass()->GetObjectType())) <<
289                     ", node address:" << node << ", number:" << std::dec <<  node->GetGlobalNumber() <<
290                     ", markCount:" << node->GetMarkCount();
291                 WriteToStackTraceFd(buffer);
292             }
293         } else {
294             primitiveCount++;
295             if (isStopPrimitiveLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
296                 buffer << "Global maybe leak primitive:" << std::hex << value.GetRawData() <<
297                     ", node address:" << node << ", number:" << std::dec <<  node->GetGlobalNumber() <<
298                     ", markCount:" << node->GetMarkCount();
299                 WriteToStackTraceFd(buffer);
300             }
301         }
302         globalCount++;
303     });
304 
305     if (isStopObjectLeakCheck || isStopPrimitiveLeakCheck) {
306         buffer << "Global leak check success!";
307         WriteToStackTraceFd(buffer);
308         CloseStackTraceFd();
309     }
310     // Determine whether memory leakage by checking handle and global count.
311     LOG_ECMA(INFO) << "Iterate root handle count:" << handleCount << ", global handle count:" << globalCount;
312     OPTIONAL_LOG(GetEcmaVM(), INFO) << "Global type Primitive count:" << primitiveCount;
313     // Print global object type statistic.
314     static const int MIN_COUNT_THRESHOLD = 50;
315     for (int i = 0; i < JS_TYPE_LAST; i++) {
316         if (typeCount[i] > MIN_COUNT_THRESHOLD) {
317             OPTIONAL_LOG(GetEcmaVM(), INFO) << "Global type " << JSHClass::DumpJSType(JSType(i))
318                                             << " count:" << typeCount[i];
319         }
320     }
321 }
322 
IterateWeakEcmaGlobalStorage(const WeakRootVisitor & visitor)323 void JSThread::IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor)
324 {
325     auto callBack = [this, visitor](WeakNode *node) {
326         JSTaggedValue value(node->GetObject());
327         if (!value.IsHeapObject()) {
328             return;
329         }
330         auto object = value.GetTaggedObject();
331         auto fwd = visitor(object);
332         if (fwd == nullptr) {
333             // undefind
334             node->SetObject(JSTaggedValue::Undefined().GetRawData());
335             auto nativeFinalizeCallback = node->GetNativeFinalizeCallback();
336             if (nativeFinalizeCallback) {
337                 weakNodeNativeFinalizeCallbacks_.push_back(std::make_pair(nativeFinalizeCallback,
338                                                                           node->GetReference()));
339             }
340             if (!node->CallFreeGlobalCallback()) {
341                 DisposeGlobalHandle(ToUintPtr(node));
342             }
343         } else if (fwd != object) {
344             // update
345             node->SetObject(JSTaggedValue(fwd).GetRawData());
346         }
347     };
348     if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
349         globalStorage_->IterateWeakUsageGlobal(callBack);
350     } else {
351         globalDebugStorage_->IterateWeakUsageGlobal(callBack);
352     }
353 }
354 
DoStackOverflowCheck(const JSTaggedType * sp)355 bool JSThread::DoStackOverflowCheck(const JSTaggedType *sp)
356 {
357     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
358     if (UNLIKELY(sp <= glueData_.frameBase_ + RESERVE_STACK_SIZE)) {
359         vm_->CheckThread();
360         LOG_ECMA(ERROR) << "Stack overflow! Remaining stack size is: " << (sp - glueData_.frameBase_);
361         if (!IsCrossThreadExecutionEnable() && LIKELY(!HasPendingException())) {
362             ObjectFactory *factory = GetEcmaVM()->GetFactory();
363             JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!", false);
364             SetException(error.GetTaggedValue());
365         }
366         return true;
367     }
368     return false;
369 }
370 
DoStackLimitCheck()371 bool JSThread::DoStackLimitCheck()
372 {
373     if (UNLIKELY(GetCurrentStackPosition() < GetStackLimit())) {
374         vm_->CheckThread();
375         LOG_ECMA(ERROR) << "Stack overflow! current:" << GetCurrentStackPosition() << " limit:" << GetStackLimit();
376         if (!IsCrossThreadExecutionEnable() && LIKELY(!HasPendingException())) {
377             ObjectFactory *factory = GetEcmaVM()->GetFactory();
378             JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!", false);
379             SetException(error.GetTaggedValue());
380         }
381         return true;
382     }
383     return false;
384 }
385 
DoAsmStackOverflowCheck()386 bool JSThread::DoAsmStackOverflowCheck()
387 {
388     // check stack overflow because infinite recursion may occur
389     return (IsAsmInterpreter() && DoStackLimitCheck());
390 }
391 
ExpandHandleStorage()392 uintptr_t *JSThread::ExpandHandleStorage()
393 {
394     return GetCurrentEcmaContext()->ExpandHandleStorage();
395 }
396 
ShrinkHandleStorage(int prevIndex)397 void JSThread::ShrinkHandleStorage(int prevIndex)
398 {
399     GetCurrentEcmaContext()->ShrinkHandleStorage(prevIndex);
400 }
401 
NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver,StableArrayChangeKind changeKind)402 void JSThread::NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver, StableArrayChangeKind changeKind)
403 {
404     if (!glueData_.stableArrayElementsGuardians_) {
405         return;
406     }
407     if (!receiver->GetJSHClass()->IsPrototype() && !receiver->IsJSArray()) {
408         return;
409     }
410     auto env = GetEcmaVM()->GetGlobalEnv();
411     if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() ||
412         receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) {
413         glueData_.stableArrayElementsGuardians_ = false;
414         return;
415     }
416     if (changeKind == StableArrayChangeKind::PROTO && receiver->IsJSArray()) {
417         glueData_.stableArrayElementsGuardians_ = false;
418     }
419 }
420 
ResetGuardians()421 void JSThread::ResetGuardians()
422 {
423     glueData_.stableArrayElementsGuardians_ = true;
424 }
425 
SetInitialBuiltinHClass(BuiltinTypeId type,JSHClass * builtinHClass,JSHClass * prototypeHClass)426 void JSThread::SetInitialBuiltinHClass(BuiltinTypeId type, JSHClass *builtinHClass, JSHClass *prototypeHClass)
427 {
428     size_t index = BuiltinHClassEntries::GetEntryIndex(type);
429     auto &entry = glueData_.builtinHClassEntries_.entries[index];
430     LOG_ECMA(DEBUG) << "JSThread::SetInitialBuiltinHClass: "
431                     << "Builtin = " << ToString(type)
432                     << ", builtinHClass = " << builtinHClass
433                     << ", prototypeHClass = " << prototypeHClass;
434     entry.builtinHClass = builtinHClass;
435     entry.prototypeHClass = prototypeHClass;
436 }
437 
GetBuiltinHClass(BuiltinTypeId type) const438 JSHClass *JSThread::GetBuiltinHClass(BuiltinTypeId type) const
439 {
440     size_t index = BuiltinHClassEntries::GetEntryIndex(type);
441     return glueData_.builtinHClassEntries_.entries[index].builtinHClass;
442 }
443 
GetBuiltinPrototypeHClass(BuiltinTypeId type) const444 JSHClass *JSThread::GetBuiltinPrototypeHClass(BuiltinTypeId type) const
445 {
446     size_t index = BuiltinHClassEntries::GetEntryIndex(type);
447     return glueData_.builtinHClassEntries_.entries[index].prototypeHClass;
448 }
449 
GetBuiltinHClassOffset(BuiltinTypeId type,bool isArch32)450 size_t JSThread::GetBuiltinHClassOffset(BuiltinTypeId type, bool isArch32)
451 {
452     return GetGlueDataOffset() + GlueData::GetBuiltinHClassOffset(type, isArch32);
453 }
454 
GetBuiltinPrototypeHClassOffset(BuiltinTypeId type,bool isArch32)455 size_t JSThread::GetBuiltinPrototypeHClassOffset(BuiltinTypeId type, bool isArch32)
456 {
457     return GetGlueDataOffset() + GlueData::GetBuiltinPrototypeHClassOffset(type, isArch32);
458 }
459 
CheckSwitchDebuggerBCStub()460 void JSThread::CheckSwitchDebuggerBCStub()
461 {
462     auto isDebug = GetEcmaVM()->GetJsDebuggerManager()->IsDebugMode();
463     if (isDebug &&
464         glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) {
465         for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
466             auto stubEntry = glueData_.bcStubEntries_.Get(i);
467             auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i);
468             glueData_.bcDebuggerStubEntries_.Set(i, stubEntry);
469             glueData_.bcStubEntries_.Set(i, debuggerStubEbtry);
470         }
471     } else if (!isDebug &&
472         glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) {
473         for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
474             auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i);
475             auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i);
476             glueData_.bcStubEntries_.Set(i, stubEntry);
477             glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry);
478         }
479     }
480 }
481 
CheckOrSwitchPGOStubs()482 void JSThread::CheckOrSwitchPGOStubs()
483 {
484     bool isSwitch = false;
485     if (IsPGOProfilerEnable()) {
486         if (GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
487             SetBCStubStatus(BCStubStatus::PROFILE_BC_STUB);
488             isSwitch = true;
489         }
490     } else {
491         if (GetBCStubStatus() == BCStubStatus::PROFILE_BC_STUB) {
492             SetBCStubStatus(BCStubStatus::NORMAL_BC_STUB);
493             isSwitch = true;
494         }
495     }
496     if (isSwitch) {
497         Address curAddress;
498 #define SWITCH_PGO_STUB_ENTRY(fromName, toName, ...)                                                        \
499         curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName);                                     \
500         SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \
501         SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress);
502         ASM_INTERPRETER_BC_PROFILER_STUB_LIST(SWITCH_PGO_STUB_ENTRY)
503 #undef SWITCH_PGO_STUB_ENTRY
504     }
505 }
506 
SwitchJitProfileStubsIfNeeded()507 void JSThread::SwitchJitProfileStubsIfNeeded()
508 {
509     bool isSwitch = false;
510     bool isEnableJit = vm_->IsEnableJit();
511     if (isEnableJit) {
512         if (GetBCStubStatus() == BCStubStatus::NORMAL_BC_STUB) {
513             SetBCStubStatus(BCStubStatus::JIT_PROFILE_BC_STUB);
514             isSwitch = true;
515         }
516     }
517     if (isSwitch) {
518         Address curAddress;
519 #define SWITCH_PGO_STUB_ENTRY(fromName, toName, ...)                                                        \
520         curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName);                                     \
521         SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \
522         SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress);
523         ASM_INTERPRETER_BC_JIT_PROFILER_STUB_LIST(SWITCH_PGO_STUB_ENTRY)
524 #undef SWITCH_PGO_STUB_ENTRY
525     }
526 }
527 
TerminateExecution()528 void JSThread::TerminateExecution()
529 {
530     // set the TERMINATE_ERROR to exception
531     ObjectFactory *factory = GetEcmaVM()->GetFactory();
532     JSHandle<JSObject> error = factory->GetJSError(ErrorType::TERMINATION_ERROR, "Terminate execution!");
533     SetException(error.GetTaggedValue());
534 }
535 
CheckSafepoint()536 bool JSThread::CheckSafepoint()
537 {
538     interruptMutex_.Lock();
539     ResetCheckSafePointStatusWithoutLock();
540 
541     if (HasTerminationRequestWithoutLock()) {
542         TerminateExecution();
543         SetVMTerminatedWithoutLock(true);
544         SetTerminationRequestWithoutLock(false);
545     }
546 
547     if (vm_->IsEnableJit() && HasInstallMachineCodeWithoutLock()) {
548         vm_->GetJit()->InstallTasksWithoutClearFlag();
549         // jit 's thread_ is current JSThread's this.
550         SetInstallMachineCodeWithoutLock(false);
551     }
552 
553     // vmThreadControl_ 's thread_ is current JSThread's this.
554     if (VMNeedSuspensionWithoutLock()) {
555         interruptMutex_.Unlock();
556         vmThreadControl_->SuspendVM();
557     } else {
558         interruptMutex_.Unlock();
559     }
560 
561 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
562     if (needProfiling_.load() && !isProfiling_) {
563         DFXJSNApi::StartCpuProfilerForFile(vm_, profileName_, CpuProfiler::INTERVAL_OF_INNER_START);
564         SetNeedProfiling(false);
565     }
566 #endif // ECMASCRIPT_SUPPORT_CPUPROFILER
567     bool gcTriggered = false;
568 #ifndef NDEBUG
569     if (vm_->GetJSOptions().EnableForceGC()) {
570         GetEcmaVM()->CollectGarbage(TriggerGCType::FULL_GC);
571         gcTriggered = true;
572     }
573 #endif
574     auto heap = const_cast<Heap *>(GetEcmaVM()->GetHeap());
575     // Handle exit app senstive scene
576     heap->HandleExitHighSensitiveEvent();
577 
578     if (IsMarkFinished() && heap->GetConcurrentMarker()->IsTriggeredConcurrentMark()
579         && !heap->GetOnSerializeEvent()) {
580         heap->GetConcurrentMarker()->HandleMarkingFinished();
581         gcTriggered = true;
582     }
583     return gcTriggered;
584 }
585 
CheckJSTaggedType(JSTaggedType value) const586 void JSThread::CheckJSTaggedType(JSTaggedType value) const
587 {
588     if (JSTaggedValue(value).IsHeapObject() &&
589         !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
590         LOG_FULL(FATAL) << "value:" << value << " is invalid!";
591     }
592 }
593 
CpuProfilerCheckJSTaggedType(JSTaggedType value) const594 bool JSThread::CpuProfilerCheckJSTaggedType(JSTaggedType value) const
595 {
596     if (JSTaggedValue(value).IsHeapObject() &&
597         !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
598         return false;
599     }
600     return true;
601 }
602 
603 // static
GetAsmStackLimit()604 size_t JSThread::GetAsmStackLimit()
605 {
606 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
607     // js stack limit
608     size_t result = GetCurrentStackPosition() - EcmaParamConfiguration::GetDefalutStackSize();
609     pthread_attr_t attr;
610     int ret = pthread_getattr_np(pthread_self(), &attr);
611     if (ret != 0) {
612         LOG_ECMA(ERROR) << "Get current thread attr failed";
613         return result;
614     }
615 
616     void *stackAddr = nullptr;
617     size_t size = 0;
618     ret = pthread_attr_getstack(&attr, &stackAddr, &size);
619     if (pthread_attr_destroy(&attr) != 0) {
620         LOG_ECMA(ERROR) << "Destroy current thread attr failed";
621     }
622     if (ret != 0) {
623         LOG_ECMA(ERROR) << "Get current thread stack size failed";
624         return result;
625     }
626 
627     bool isMainThread = IsMainThread();
628     uintptr_t threadStackLimit = reinterpret_cast<uintptr_t>(stackAddr);
629     uintptr_t threadStackStart = threadStackLimit + size;
630     if (isMainThread) {
631         struct rlimit rl;
632         ret = getrlimit(RLIMIT_STACK, &rl);
633         if (ret != 0) {
634             LOG_ECMA(ERROR) << "Get current thread stack size failed";
635             return result;
636         }
637         if (rl.rlim_cur > DEFAULT_MAX_SYSTEM_STACK_SIZE) {
638             LOG_ECMA(ERROR) << "Get current thread stack size exceed " << DEFAULT_MAX_SYSTEM_STACK_SIZE
639                             << " : " << rl.rlim_cur;
640             return result;
641         }
642         threadStackLimit = threadStackStart - rl.rlim_cur;
643     }
644 
645     if (result < threadStackLimit) {
646         result = threadStackLimit;
647     }
648 
649     LOG_INTERPRETER(DEBUG) << "Current thread stack start: " << reinterpret_cast<void *>(threadStackStart);
650     LOG_INTERPRETER(DEBUG) << "Used stack before js stack start: "
651                            << reinterpret_cast<void *>(threadStackStart - GetCurrentStackPosition());
652     LOG_INTERPRETER(DEBUG) << "Current thread asm stack limit: " << reinterpret_cast<void *>(result);
653 
654     // To avoid too much times of stack overflow checking, we only check stack overflow before push vregs or
655     // parameters of variable length. So we need a reserved size of stack to make sure stack won't be overflowed
656     // when push other data.
657     result += EcmaParamConfiguration::GetDefaultReservedStackSize();
658     if (threadStackStart <= result) {
659         LOG_FULL(FATAL) << "Too small stackSize to run jsvm";
660     }
661     return result;
662 #else
663     return 0;
664 #endif
665 }
666 
IsLegalAsmSp(uintptr_t sp) const667 bool JSThread::IsLegalAsmSp(uintptr_t sp) const
668 {
669     uint64_t bottom = GetStackLimit() - EcmaParamConfiguration::GetDefaultReservedStackSize();
670     uint64_t top = GetStackStart();
671     return (bottom <= sp && sp <= top);
672 }
673 
IsLegalThreadSp(uintptr_t sp) const674 bool JSThread::IsLegalThreadSp(uintptr_t sp) const
675 {
676     uintptr_t bottom = reinterpret_cast<uintptr_t>(glueData_.frameBase_);
677     size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
678     uintptr_t top = bottom + maxStackSize;
679     return (bottom <= sp && sp <= top);
680 }
681 
IsLegalSp(uintptr_t sp) const682 bool JSThread::IsLegalSp(uintptr_t sp) const
683 {
684     return IsLegalAsmSp(sp) || IsLegalThreadSp(sp);
685 }
686 
IsMainThread()687 bool JSThread::IsMainThread()
688 {
689 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
690     return getpid() == syscall(SYS_gettid);
691 #else
692     return true;
693 #endif
694 }
695 
PushContext(EcmaContext * context)696 void JSThread::PushContext(EcmaContext *context)
697 {
698     const_cast<Heap *>(vm_->GetHeap())->WaitAllTasksFinished();
699     contexts_.emplace_back(context);
700 
701     if (!glueData_.currentContext_) {
702         // The first context in ecma vm.
703         glueData_.currentContext_ = context;
704         context->SetFramePointers(const_cast<JSTaggedType *>(GetCurrentSPFrame()),
705             const_cast<JSTaggedType *>(GetLastLeaveFrame()),
706             const_cast<JSTaggedType *>(GetLastFp()));
707         context->SetFrameBase(glueData_.frameBase_);
708         context->SetStackLimit(glueData_.stackLimit_);
709         context->SetStackStart(glueData_.stackStart_);
710     } else {
711         // algin with 16
712         size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
713         context->SetFrameBase(static_cast<JSTaggedType *>(
714             vm_->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize)));
715         context->SetFramePointers(context->GetFrameBase() + maxStackSize, nullptr, nullptr);
716         context->SetStackLimit(GetAsmStackLimit());
717         context->SetStackStart(GetCurrentStackPosition());
718         EcmaInterpreter::InitStackFrame(context);
719     }
720 }
721 
PopContext()722 void JSThread::PopContext()
723 {
724     contexts_.pop_back();
725     glueData_.currentContext_ = contexts_.back();
726 }
727 
SwitchCurrentContext(EcmaContext * currentContext,bool isInIterate)728 void JSThread::SwitchCurrentContext(EcmaContext *currentContext, bool isInIterate)
729 {
730     ASSERT(std::count(contexts_.begin(), contexts_.end(), currentContext));
731 
732     glueData_.currentContext_->SetFramePointers(const_cast<JSTaggedType *>(GetCurrentSPFrame()),
733         const_cast<JSTaggedType *>(GetLastLeaveFrame()),
734         const_cast<JSTaggedType *>(GetLastFp()));
735     glueData_.currentContext_->SetFrameBase(glueData_.frameBase_);
736     glueData_.currentContext_->SetStackLimit(GetStackLimit());
737     glueData_.currentContext_->SetStackStart(GetStackStart());
738     glueData_.currentContext_->SetGlobalEnv(GetGlueGlobalEnv());
739     glueData_.currentContext_->GetGlobalEnv()->SetJSGlobalObject(this, glueData_.globalObject_);
740 
741     SetCurrentSPFrame(currentContext->GetCurrentFrame());
742     SetLastLeaveFrame(currentContext->GetLeaveFrame());
743     SetLastFp(currentContext->GetLastFp());
744     glueData_.frameBase_ = currentContext->GetFrameBase();
745     glueData_.stackLimit_ = currentContext->GetStackLimit();
746     glueData_.stackStart_ = currentContext->GetStackStart();
747     if (!currentContext->GlobalEnvIsHole()) {
748         SetGlueGlobalEnv(*(currentContext->GetGlobalEnv()));
749         SetGlobalObject(currentContext->GetGlobalEnv()->GetGlobalObject());
750     }
751     if (!isInIterate) {
752         // If isInIterate is true, it means it is in GC iterate and global variables are no need to change.
753         glueData_.globalConst_ = const_cast<GlobalEnvConstants *>(currentContext->GlobalConstants());
754     }
755 
756     glueData_.currentContext_ = currentContext;
757 }
758 
EraseContext(EcmaContext * context)759 bool JSThread::EraseContext(EcmaContext *context)
760 {
761     const_cast<Heap *>(vm_->GetHeap())->WaitAllTasksFinished();
762     bool isCurrentContext = false;
763     auto iter = std::find(contexts_.begin(), contexts_.end(), context);
764     if (*iter == context) {
765         if (glueData_.currentContext_ == context) {
766             isCurrentContext = true;
767         }
768         contexts_.erase(iter);
769         if (isCurrentContext) {
770             SwitchCurrentContext(contexts_.back());
771         }
772         return true;
773     }
774     return false;
775 }
776 
GetPropertiesCache() const777 PropertiesCache *JSThread::GetPropertiesCache() const
778 {
779     return glueData_.currentContext_->GetPropertiesCache();
780 }
781 
GetFirstGlobalConst() const782 const GlobalEnvConstants *JSThread::GetFirstGlobalConst() const
783 {
784     return contexts_[0]->GlobalConstants();
785 }
786 
IsAllContextsInitialized() const787 bool JSThread::IsAllContextsInitialized() const
788 {
789     return contexts_.back()->IsInitialized();
790 }
791 
IsReadyToUpdateDetector() const792 bool JSThread::IsReadyToUpdateDetector() const
793 {
794     return !GetEnableLazyBuiltins() && IsAllContextsInitialized();
795 }
796 
GetOrCreateRegExpCache()797 Area *JSThread::GetOrCreateRegExpCache()
798 {
799     if (regExpCache_ == nullptr) {
800         regExpCache_ = nativeAreaAllocator_->AllocateArea(MAX_REGEXP_CACHE_SIZE);
801     }
802     return regExpCache_;
803 }
804 
InitializeBuiltinObject(const std::string & key)805 void JSThread::InitializeBuiltinObject(const std::string& key)
806 {
807     BuiltinIndex& builtins = BuiltinIndex::GetInstance();
808     auto index = builtins.GetBuiltinIndex(key);
809     ASSERT(index != BuiltinIndex::NOT_FOUND);
810     /*
811         If using `auto globalObject = GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()` here,
812         it will cause incorrect result in multi-context environment. For example:
813 
814         ```ts
815         let obj = {};
816         print(obj instanceof Object); // instead of true, will print false
817         ```
818     */
819     auto globalObject = contexts_.back()->GetGlobalEnv()->GetGlobalObject();
820     auto jsObject = JSHandle<JSObject>(this, globalObject);
821     auto box = jsObject->GetGlobalPropertyBox(this, key);
822     if (box == nullptr) {
823         return;
824     }
825     auto& entry = glueData_.builtinEntries_.builtin_[index];
826     entry.box_ = JSTaggedValue::Cast(box);
827     auto builtin = JSHandle<JSObject>(this, box->GetValue());
828     auto hclass = builtin->GetJSHClass();
829     entry.hClass_ = JSTaggedValue::Cast(hclass);
830 }
831 
InitializeBuiltinObject()832 void JSThread::InitializeBuiltinObject()
833 {
834     BuiltinIndex& builtins = BuiltinIndex::GetInstance();
835     for (auto key: builtins.GetBuiltinKeys()) {
836         InitializeBuiltinObject(key);
837     }
838 }
839 
IsPropertyCacheCleared() const840 bool JSThread::IsPropertyCacheCleared() const
841 {
842     for (EcmaContext *context : contexts_) {
843         if (!context->GetPropertiesCache()->IsCleared()) {
844             return false;
845         }
846     }
847     return true;
848 }
849 }  // namespace panda::ecmascript
850