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