• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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