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