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