1 /*
2 * Copyright (c) 2021 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/compiler/llvm/llvm_stackmap_parser.h"
18 #include "ecmascript/global_env_constants-inl.h"
19 #include "ecmascript/ic/properties_cache-inl.h"
20 #include "ecmascript/internal_call_params.h"
21 #include "ecmascript/interpreter/interpreter-inl.h"
22 #include "ecmascript/mem/machine_code.h"
23 #include "ecmascript/stub_module.h"
24 #include "include/panda_vm.h"
25
26 namespace panda::ecmascript {
27 // static
Create(Runtime * runtime,PandaVM * vm)28 JSThread *JSThread::Create(Runtime *runtime, PandaVM *vm)
29 {
30 auto jsThread = new JSThread(runtime, vm);
31 jsThread->heapRegionAllocator_ = EcmaVM::Cast(vm)->GetHeapRegionAllocator();
32 // algin with 16
33 jsThread->frameBase_ = static_cast<JSTaggedType *>(
34 EcmaVM::Cast(vm)->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * MAX_STACK_SIZE));
35 jsThread->currentFrame_ = jsThread->frameBase_ + MAX_STACK_SIZE;
36 JSThread::SetCurrent(jsThread);
37 EcmaInterpreter::InitStackFrame(jsThread);
38 return jsThread;
39 }
40
JSThread(Runtime * runtime,PandaVM * vm)41 JSThread::JSThread(Runtime *runtime, PandaVM *vm)
42 : ManagedThread(GetCurrentThreadId(), runtime->GetInternalAllocator(), vm,
43 Thread::ThreadType::THREAD_TYPE_MANAGED)
44 {
45 nativeAreaAllocator_ = EcmaVM::Cast(vm)->GetNativeAreaAllocator();
46 SetLanguageContext(runtime->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT));
47 auto chunk = EcmaVM::Cast(vm)->GetChunk();
48 globalStorage_ = chunk->New<EcmaGlobalStorage>(nativeAreaAllocator_);
49 internalCallParams_ = new InternalCallParams();
50 propertiesCache_ = new PropertiesCache();
51 }
52
~JSThread()53 JSThread::~JSThread()
54 {
55 for (auto n : handleStorageNodes_) {
56 delete n;
57 }
58 handleStorageNodes_.clear();
59 currentHandleStorageIndex_ = -1;
60 handleScopeCount_ = 0;
61 handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
62 EcmaVM::Cast(GetVM())->GetChunk()->Delete(globalStorage_);
63
64 GetNativeAreaAllocator()->Free(frameBase_, sizeof(JSTaggedType) * MAX_STACK_SIZE);
65 frameBase_ = nullptr;
66 nativeAreaAllocator_ = nullptr;
67 heapRegionAllocator_ = nullptr;
68 if (internalCallParams_ != nullptr) {
69 delete internalCallParams_;
70 internalCallParams_ = nullptr;
71 }
72 if (propertiesCache_ != nullptr) {
73 delete propertiesCache_;
74 propertiesCache_ = nullptr;
75 }
76 }
77
GetEcmaVM() const78 EcmaVM *JSThread::GetEcmaVM() const
79 {
80 return EcmaVM::Cast(GetVM());
81 }
82
SetException(JSTaggedValue exception)83 void JSThread::SetException(JSTaggedValue exception)
84 {
85 exception_ = exception;
86 }
87
ClearException()88 void JSThread::ClearException()
89 {
90 exception_ = JSTaggedValue::Hole();
91 }
92
GetCurrentLexenv() const93 JSTaggedValue JSThread::GetCurrentLexenv() const
94 {
95 return InterpretedFrameHandler(currentFrame_).GetEnv();
96 }
97
Iterate(const RootVisitor & v0,const RootRangeVisitor & v1)98 void JSThread::Iterate(const RootVisitor &v0, const RootRangeVisitor &v1)
99 {
100 if (propertiesCache_ != nullptr) {
101 propertiesCache_->Clear();
102 }
103
104 if (!exception_.IsHole()) {
105 v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(&exception_)));
106 }
107 if (!stubCode_.IsHole()) {
108 v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(&stubCode_)));
109 }
110 // visit global Constant
111 globalConst_.VisitRangeSlot(v1);
112 // visit stack roots
113 FrameIterator iterator(currentFrame_, this);
114 iterator.Iterate(v0, v1);
115 // visit internal call params;
116 internalCallParams_->Iterate(v1);
117 // visit tagged handle storage roots
118 if (currentHandleStorageIndex_ != -1) {
119 int32_t nid = currentHandleStorageIndex_;
120 for (int32_t i = 0; i <= nid; ++i) {
121 auto node = handleStorageNodes_.at(i);
122 auto start = node->data();
123 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
124 v1(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
125 }
126 }
127 globalStorage_->IterateUsageGlobal([v0](EcmaGlobalStorage::Node *node) {
128 JSTaggedValue value(node->GetObject());
129 if (value.IsHeapObject()) {
130 v0(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
131 }
132 });
133 }
134
IterateWeakEcmaGlobalStorage(const WeakRootVisitor & visitor)135 void JSThread::IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor)
136 {
137 globalStorage_->IterateWeakUsageGlobal([visitor](EcmaGlobalStorage::Node *node) {
138 JSTaggedValue value(node->GetObject());
139 if (value.IsHeapObject()) {
140 auto object = value.GetTaggedObject();
141 auto fwd = visitor(object);
142 if (fwd == nullptr) {
143 // undefind
144 node->SetObject(JSTaggedValue::Undefined().GetRawData());
145 } else if (fwd != object) {
146 // update
147 node->SetObject(JSTaggedValue(fwd).GetRawData());
148 }
149 }
150 });
151 }
152
DoStackOverflowCheck(const JSTaggedType * sp)153 bool JSThread::DoStackOverflowCheck(const JSTaggedType *sp)
154 {
155 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
156 if (UNLIKELY(sp <= frameBase_ + RESERVE_STACK_SIZE)) {
157 ObjectFactory *factory = GetEcmaVM()->GetFactory();
158 JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!");
159 if (LIKELY(!HasPendingException())) {
160 SetException(error.GetTaggedValue());
161 }
162 return true;
163 }
164 return false;
165 }
166
ExpandHandleStorage()167 uintptr_t *JSThread::ExpandHandleStorage()
168 {
169 uintptr_t *result = nullptr;
170 int32_t lastIndex = handleStorageNodes_.size() - 1;
171 if (currentHandleStorageIndex_ == lastIndex) {
172 auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
173 handleStorageNodes_.push_back(n);
174 currentHandleStorageIndex_++;
175 result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
176 handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
177 } else {
178 currentHandleStorageIndex_++;
179 auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
180 result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
181 handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
182 }
183
184 return result;
185 }
186
ShrinkHandleStorage(int prevIndex)187 void JSThread::ShrinkHandleStorage(int prevIndex)
188 {
189 currentHandleStorageIndex_ = prevIndex;
190 int32_t lastIndex = handleStorageNodes_.size() - 1;
191 #if ECMASCRIPT_ENABLE_ZAP_MEM
192 uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
193 if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
194 LOG_ECMA(FATAL) << "memset_s failed";
195 UNREACHABLE();
196 }
197 for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
198 if (memset_s(handleStorageNodes_[i], NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
199 NODE_BLOCK_SIZE * sizeof(JSTaggedType)) != EOK) {
200 LOG_ECMA(FATAL) << "memset_s failed";
201 UNREACHABLE();
202 }
203 }
204 #endif
205
206 if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
207 for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
208 auto node = handleStorageNodes_.back();
209 delete node;
210 handleStorageNodes_.pop_back();
211 }
212 }
213 }
214
NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver)215 void JSThread::NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver)
216 {
217 if (!receiver->GetJSHClass()->IsPrototype()) {
218 return;
219 }
220 if (!stableArrayElementsGuardians_) {
221 return;
222 }
223 auto env = GetEcmaVM()->GetGlobalEnv();
224 if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() ||
225 receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) {
226 stableArrayElementsGuardians_ = false;
227 }
228 }
229
ResetGuardians()230 void JSThread::ResetGuardians()
231 {
232 stableArrayElementsGuardians_ = true;
233 }
234
LoadStubModule(const char * moduleFile)235 void JSThread::LoadStubModule(const char *moduleFile)
236 {
237 StubModule stubModule;
238 std::string fileName(moduleFile);
239 stubModule.Load(this, fileName);
240 for (uint32_t i = 0; i < kungfu::FAST_STUB_MAXCOUNT; i++) {
241 fastStubEntries_[i] = stubModule.GetStubEntry(i);
242 }
243 #if ECMASCRIPT_COMPILE_INTERPRETER_ASM
244 for (uint32_t i = 0; i < MAX_BYTECODE_HANDLERS; i++) {
245 bytecodeHandlers_[i] = stubModule.GetStubEntry(kungfu::StubId::STUB_SingleStepDebugging);
246 }
247 #define DEF_STUB(name, counter) \
248 bytecodeHandlers_[kungfu::InterpreterStubId::name##Id] = \
249 stubModule.GetStubEntry(kungfu::StubId::STUB_##name);
250 INTERPRETER_STUB_LIST(DEF_STUB)
251 #undef DEF_STUB
252 #endif
253
254 #ifdef NDEBUG
255 kungfu::LLVMStackMapParser::GetInstance().Print();
256 #endif
257 stubCode_ = stubModule.GetCode();
258 }
259
CheckSafepoint()260 bool JSThread::CheckSafepoint()
261 {
262 if (VMNeedSuspension()) {
263 SuspendVM();
264 }
265 #ifndef NDEBUG
266 EcmaVM::Cast(GetVM())->CollectGarbage(TriggerGCType::FULL_GC);
267 return true;
268 #endif
269 if (IsMarkFinished()) {
270 auto heap = EcmaVM::Cast(GetVM())->GetHeap();
271 heap->GetConcurrentMarker()->HandleMarkFinished();
272 return true;
273 }
274 return false;
275 }
276
NotifyVMThreadSuspension()277 bool JSThread::NotifyVMThreadSuspension() // block caller thread
278 {
279 if (VMNeedSuspension()) { // only enable one thread to post suspension
280 return false;
281 }
282 SetVMNeedSuspension(true);
283 os::memory::LockHolder lock(vmThreadSuspensionMutex_);
284 while (!IsSuspended()) {
285 vmThreadNeedSuspensionCV_.Wait(&vmThreadSuspensionMutex_);
286 }
287 return true;
288 }
289
SuspendVM()290 void JSThread::SuspendVM() // block vm thread
291 {
292 os::memory::LockHolder lock(vmThreadSuspensionMutex_);
293 SetVMSuspened(true);
294 vmThreadNeedSuspensionCV_.Signal(); // wake up the thread who needs suspend vmthread
295 while (VMNeedSuspension()) {
296 vmThreadHasSuspendedCV_.Wait(&vmThreadSuspensionMutex_);
297 }
298 SetVMSuspened(false);
299 }
300
ResumeVM()301 void JSThread::ResumeVM()
302 {
303 os::memory::LockHolder lock(vmThreadSuspensionMutex_);
304 SetVMNeedSuspension(false);
305 vmThreadHasSuspendedCV_.Signal();
306 }
307 } // namespace panda::ecmascript
308