1 /*
2 * Copyright (c) 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/ecma_context.h"
17
18 #include "ecmascript/base/path_helper.h"
19 #include "ecmascript/builtins/builtins.h"
20 #include "ecmascript/builtins/builtins_global.h"
21 #include "ecmascript/builtins/builtins_regexp.h"
22 #include "ecmascript/compiler/aot_file/an_file_data_manager.h"
23 #include "ecmascript/compiler/common_stubs.h"
24 #include "ecmascript/ecma_string_table.h"
25 #include "ecmascript/ecma_vm.h"
26 #include "ecmascript/global_env.h"
27 #include "ecmascript/global_env_constants-inl.h"
28 #include "ecmascript/interpreter/interpreter-inl.h"
29 #include "ecmascript/jobs/micro_job_queue.h"
30 #include "ecmascript/jspandafile/js_pandafile.h"
31 #include "ecmascript/jspandafile/js_pandafile_manager.h"
32 #include "ecmascript/jspandafile/program_object.h"
33 #include "ecmascript/js_function.h"
34 #include "ecmascript/js_thread.h"
35 #include "ecmascript/module/module_path_helper.h"
36 #include "ecmascript/object_factory.h"
37 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
38 #include "ecmascript/require/js_cjs_module_cache.h"
39 #include "ecmascript/require/js_require_manager.h"
40 #include "ecmascript/snapshot/mem/snapshot.h"
41
42 namespace panda::ecmascript {
43 using PathHelper = base::PathHelper;
44
EcmaContext(JSThread * thread)45 EcmaContext::EcmaContext(JSThread *thread)
46 : thread_(thread),
47 vm_(thread->GetEcmaVM()),
48 factory_(vm_->GetFactory())
49 {
50 }
51
52 /* static */
Create(JSThread * thread)53 EcmaContext *EcmaContext::Create(JSThread *thread)
54 {
55 LOG_ECMA(INFO) << "EcmaContext::Create";
56 auto context = new EcmaContext(thread);
57 if (UNLIKELY(context == nullptr)) {
58 LOG_ECMA(ERROR) << "Failed to create ecma context";
59 return nullptr;
60 }
61 return context;
62 }
63
64 // static
Destroy(EcmaContext * context)65 bool EcmaContext::Destroy(EcmaContext *context)
66 {
67 if (context != nullptr) {
68 delete context;
69 context = nullptr;
70 return true;
71 }
72 return false;
73 }
74
Initialize()75 bool EcmaContext::Initialize()
76 {
77 LOG_ECMA(DEBUG) << "EcmaContext::Initialize";
78 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "EcmaContext::Initialize");
79 [[maybe_unused]] EcmaHandleScope scope(thread_);
80 aotFileManager_ = new AOTFileManager(vm_);
81 propertiesCache_ = new PropertiesCache();
82 regExpParserCache_ = new RegExpParserCache();
83
84 JSHandle<JSHClass> hClassHandle = factory_->InitClassClass();
85
86 JSHandle<JSHClass> globalEnvClass = factory_->NewEcmaHClass(
87 *hClassHandle,
88 GlobalEnv::SIZE,
89 JSType::GLOBAL_ENV);
90 thread_->SetGlobalConst(&globalConst_);
91 globalConst_.Init(thread_, *hClassHandle);
92 auto arrayHClassIndexMaps = Elements::InitializeHClassMap();
93 thread_->SetArrayHClassIndexMap(arrayHClassIndexMaps);
94
95 JSHandle<GlobalEnv> globalEnv = factory_->NewGlobalEnv(*globalEnvClass);
96 globalEnv->Init(thread_);
97 globalEnv_ = globalEnv.GetTaggedValue();
98 Builtins builtins;
99 bool builtinsLazyEnabled = vm_->GetJSOptions().IsWorker() && vm_->GetJSOptions().GetEnableBuiltinsLazy();
100 builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled);
101
102 SetupRegExpResultCache();
103 microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
104 moduleManager_ = new ModuleManager(vm_);
105 tsManager_ = new TSManager(vm_);
106 optCodeProfiler_ = new OptCodeProfiler();
107 initialized_ = true;
108 return true;
109 }
110
InitializeEcmaScriptRunStat()111 void EcmaContext::InitializeEcmaScriptRunStat()
112 {
113 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
114 static const char *runtimeCallerNames[] = {
115 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
116 #define INTERPRETER_CALLER_NAME(name) "Interpreter::" #name,
117 INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME) // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
118 #undef INTERPRETER_CALLER_NAME
119 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
120 #define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name,
121 BUILTINS_API_LIST(BUILTINS_API_NAME)
122 #undef BUILTINS_API_NAME
123 #define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name,
124 ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME)
125 #undef ABSTRACT_OPERATION_NAME
126 #define MEM_ALLOCATE_AND_GC_NAME(name) "Memory::" #name,
127 MEM_ALLOCATE_AND_GC_LIST(MEM_ALLOCATE_AND_GC_NAME)
128 #undef MEM_ALLOCATE_AND_GC_NAME
129 #define DEF_RUNTIME_ID(name) "Runtime::" #name,
130 RUNTIME_STUB_WITH_GC_LIST(DEF_RUNTIME_ID)
131 #undef DEF_RUNTIME_ID
132 };
133 static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER,
134 "Invalid runtime caller number");
135 runtimeStat_ = vm_->GetChunk()->New<EcmaRuntimeStat>(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER);
136 if (UNLIKELY(runtimeStat_ == nullptr)) {
137 LOG_FULL(FATAL) << "alloc runtimeStat_ failed";
138 UNREACHABLE();
139 }
140 }
141
SetRuntimeStatEnable(bool flag)142 void EcmaContext::SetRuntimeStatEnable(bool flag)
143 {
144 static uint64_t start = 0;
145 if (flag) {
146 start = PandaRuntimeTimer::Now();
147 if (runtimeStat_ == nullptr) {
148 InitializeEcmaScriptRunStat();
149 }
150 } else {
151 LOG_ECMA(INFO) << "Runtime State duration:" << PandaRuntimeTimer::Now() - start << "(ns)";
152 if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
153 runtimeStat_->Print();
154 runtimeStat_->ResetAllCount();
155 }
156 }
157 if (runtimeStat_ != nullptr) {
158 runtimeStat_->SetRuntimeStatEnabled(flag);
159 }
160 }
161
~EcmaContext()162 EcmaContext::~EcmaContext()
163 {
164 if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
165 runtimeStat_->Print();
166 }
167 for (auto n : handleStorageNodes_) {
168 delete n;
169 }
170 handleStorageNodes_.clear();
171 currentHandleStorageIndex_ = -1;
172 handleScopeCount_ = 0;
173 handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
174 ClearBufferData();
175 // clear c_address: c++ pointer delete
176 if (!vm_->IsBundlePack()) {
177 std::shared_ptr<JSPandaFile> jsPandaFile =
178 JSPandaFileManager::GetInstance()->FindJSPandaFile(vm_->GetAssetPath());
179 if (jsPandaFile != nullptr) {
180 jsPandaFile->DeleteParsedConstpoolVM(vm_);
181 }
182 }
183 // clear icu cache
184 ClearIcuCache();
185
186 if (runtimeStat_ != nullptr) {
187 vm_->GetChunk()->Delete(runtimeStat_);
188 runtimeStat_ = nullptr;
189 }
190 if (optCodeProfiler_ != nullptr) {
191 delete optCodeProfiler_;
192 optCodeProfiler_ = nullptr;
193 }
194 if (moduleManager_ != nullptr) {
195 delete moduleManager_;
196 moduleManager_ = nullptr;
197 }
198 if (tsManager_ != nullptr) {
199 delete tsManager_;
200 tsManager_ = nullptr;
201 }
202 if (regExpParserCache_ != nullptr) {
203 delete regExpParserCache_;
204 regExpParserCache_ = nullptr;
205 }
206 if (aotFileManager_ != nullptr) {
207 delete aotFileManager_;
208 aotFileManager_ = nullptr;
209 }
210 if (propertiesCache_ != nullptr) {
211 delete propertiesCache_;
212 propertiesCache_ = nullptr;
213 }
214 // clear join stack
215 joinStack_.clear();
216 }
217
InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint,CJSInfo * cjsInfo)218 JSTaggedValue EcmaContext::InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg,
219 const JSPandaFile *jsPandaFile, std::string_view entryPoint,
220 CJSInfo* cjsInfo)
221 {
222 aotFileManager_->SetAOTMainFuncEntry(mainFunc, jsPandaFile, entryPoint);
223 return JSFunction::InvokeOptimizedEntrypoint(thread_, mainFunc, thisArg, entryPoint, cjsInfo);
224 }
225
ExecuteAot(size_t actualNumArgs,JSTaggedType * args,const JSTaggedType * prevFp,bool needPushUndefined)226 JSTaggedValue EcmaContext::ExecuteAot(size_t actualNumArgs, JSTaggedType *args,
227 const JSTaggedType *prevFp, bool needPushUndefined)
228 {
229 INTERPRETER_TRACE(thread_, ExecuteAot);
230 auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_JSFunctionEntry);
231 // do not modify this log to INFO, this will call many times
232 LOG_ECMA(DEBUG) << "start to execute aot entry: " << (void*)entry;
233 auto res = reinterpret_cast<JSFunctionEntryType>(entry)(thread_->GetGlueAddr(),
234 actualNumArgs,
235 args,
236 reinterpret_cast<uintptr_t>(prevFp),
237 needPushUndefined);
238 return res;
239 }
240
InvokeEcmaEntrypoint(const JSPandaFile * jsPandaFile,std::string_view entryPoint,bool excuteFromJob)241 Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
242 std::string_view entryPoint, bool excuteFromJob)
243 {
244 [[maybe_unused]] EcmaHandleScope scope(thread_);
245 JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
246 if (program.IsEmpty()) {
247 LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
248 return Unexpected(false);
249 }
250 // for debugger
251 vm_->GetJsDebuggerManager()->GetNotificationManager()->LoadModuleEvent(
252 jsPandaFile->GetJSPandaFileDesc(), entryPoint);
253
254 JSHandle<JSFunction> func(thread_, program->GetMainFunction());
255 JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
256 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
257 CString entry = entryPoint.data();
258 JSRecordInfo recordInfo;
259 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
260 if (!hasRecord) {
261 CString msg = "cannot find record '" + entry + "', please check the request path.";
262 LOG_FULL(ERROR) << msg;
263 THROW_REFERENCE_ERROR_AND_RETURN(thread_, msg.c_str(), Unexpected(false));
264 }
265 if (jsPandaFile->IsModule(recordInfo)) {
266 global = undefined;
267 CString moduleName = jsPandaFile->GetJSPandaFileDesc();
268 if (!jsPandaFile->IsBundlePack()) {
269 moduleName = entry;
270 }
271 JSHandle<SourceTextModule> module = moduleManager_->HostGetImportedModule(moduleName);
272 func->SetModule(thread_, module);
273 } else {
274 // if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName,
275 // which can avoid the problem of larger memory caused by the new slot
276 JSHandle<EcmaString> recordName = factory_->NewFromUtf8(entry);
277 func->SetModule(thread_, recordName);
278 }
279 vm_->CheckStartCpuProfiler();
280
281 JSTaggedValue result;
282 if (jsPandaFile->IsCjs(recordInfo)) {
283 CJSExecution(func, global, jsPandaFile, entryPoint);
284 } else {
285 if (aotFileManager_->IsLoadMain(jsPandaFile, entry)) {
286 EcmaRuntimeStatScope runtimeStatScope(vm_);
287 result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint);
288 } else {
289 if (thread_->IsPGOProfilerEnable()) {
290 vm_->GetPGOProfiler()->ProfileCall(JSTaggedValue::VALUE_UNDEFINED, func.GetTaggedType());
291 }
292 EcmaRuntimeCallInfo *info =
293 EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
294 EcmaRuntimeStatScope runtimeStatScope(vm_);
295 EcmaInterpreter::Execute(info);
296 }
297 }
298 if (!thread_->HasPendingException()) {
299 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
300 }
301
302 // print exception information
303 if (!excuteFromJob && thread_->HasPendingException()) {
304 HandleUncaughtException(thread_->GetException());
305 }
306 return result;
307 }
308
CJSExecution(JSHandle<JSFunction> & func,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint)309 void EcmaContext::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
310 const JSPandaFile *jsPandaFile, std::string_view entryPoint)
311 {
312 // create "module", "exports", "require", "filename", "dirname"
313 JSHandle<CjsModule> module = factory_->NewCjsModule();
314 JSHandle<JSTaggedValue> require = GetGlobalEnv()->GetCjsRequireFunction();
315 JSHandle<CjsExports> exports = factory_->NewCjsExports();
316 JSMutableHandle<JSTaggedValue> filename(thread_, JSTaggedValue::Undefined());
317 JSMutableHandle<JSTaggedValue> dirname(thread_, JSTaggedValue::Undefined());
318 if (jsPandaFile->IsBundlePack()) {
319 ModulePathHelper::ResolveCurrentPath(thread_, dirname, filename, jsPandaFile);
320 } else {
321 filename.Update(func->GetModule());
322 ASSERT(filename->IsString());
323 CString fullName = ConvertToString(filename.GetTaggedValue());
324 dirname.Update(PathHelper::ResolveDirPath(thread_, fullName));
325 }
326 CJSInfo cjsInfo(module, require, exports, filename, dirname);
327 RequireManager::InitializeCommonJS(thread_, cjsInfo);
328 if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) {
329 EcmaRuntimeStatScope runtimeStateScope(vm_);
330 InvokeEcmaAotEntrypoint(func, thisArg, jsPandaFile, entryPoint, &cjsInfo);
331 } else {
332 // Execute main function
333 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
334 EcmaRuntimeCallInfo *info =
335 EcmaInterpreter::NewRuntimeCallInfo(thread_,
336 JSHandle<JSTaggedValue>(func),
337 thisArg, undefined, 5); // 5 : argument numbers
338 RETURN_IF_ABRUPT_COMPLETION(thread_);
339 if (info == nullptr) {
340 LOG_ECMA(ERROR) << "CJSExecution Stack overflow!";
341 return;
342 }
343 info->SetCallArg(cjsInfo.exportsHdl.GetTaggedValue(),
344 cjsInfo.requireHdl.GetTaggedValue(),
345 cjsInfo.moduleHdl.GetTaggedValue(),
346 cjsInfo.filenameHdl.GetTaggedValue(),
347 cjsInfo.dirnameHdl.GetTaggedValue());
348 EcmaRuntimeStatScope runtimeStatScope(vm_);
349 EcmaInterpreter::Execute(info);
350 }
351 if (!thread_->HasPendingException()) {
352 job::MicroJobQueue::ExecutePendingJob(thread_, thread_->GetCurrentEcmaContext()->GetMicroJobQueue());
353 }
354
355 if (!thread_->HasPendingException()) {
356 // Collecting module.exports : exports ---> module.exports --->Module._cache
357 RequireManager::CollectExecutedExp(thread_, cjsInfo);
358 }
359 }
360
HasCachedConstpool(const JSPandaFile * jsPandaFile) const361 bool EcmaContext::HasCachedConstpool(const JSPandaFile *jsPandaFile) const
362 {
363 return cachedConstpools_.find(jsPandaFile) != cachedConstpools_.end();
364 }
365
FindConstpool(const JSPandaFile * jsPandaFile,int32_t index)366 JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
367 {
368 auto iter = cachedConstpools_.find(jsPandaFile);
369 if (iter == cachedConstpools_.end()) {
370 return JSTaggedValue::Hole();
371 }
372 auto constpoolIter = iter->second.find(index);
373 if (constpoolIter == iter->second.end()) {
374 return JSTaggedValue::Hole();
375 }
376 return constpoolIter->second;
377 }
378
FindConstpools(const JSPandaFile * jsPandaFile)379 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> EcmaContext::FindConstpools(
380 const JSPandaFile *jsPandaFile)
381 {
382 auto iter = cachedConstpools_.find(jsPandaFile);
383 if (iter == cachedConstpools_.end()) {
384 return std::nullopt;
385 }
386 return iter->second;
387 }
388
389 // For new version instruction.
FindConstpool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)390 JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
391 {
392 panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
393 int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
394 return FindConstpool(jsPandaFile, index);
395 }
396
FindOrCreateConstPool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)397 JSHandle<ConstantPool> EcmaContext::FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
398 {
399 panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
400 int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
401 JSTaggedValue constpool = FindConstpool(jsPandaFile, index);
402 if (constpool.IsHole()) {
403 JSHandle<ConstantPool> newConstpool = ConstantPool::CreateConstPool(vm_, jsPandaFile, id);
404 AddConstpool(jsPandaFile, newConstpool.GetTaggedValue(), index);
405 return newConstpool;
406 }
407
408 return JSHandle<ConstantPool>(thread_, constpool);
409 }
410
CreateAllConstpool(const JSPandaFile * jsPandaFile)411 void EcmaContext::CreateAllConstpool(const JSPandaFile *jsPandaFile)
412 {
413 auto headers = jsPandaFile->GetPandaFile()->GetIndexHeaders();
414 uint32_t index = 0;
415 for (const auto &header : headers) {
416 auto constpoolSize = header.method_idx_size;
417 JSHandle<ConstantPool> constpool = factory_->NewConstantPool(constpoolSize);
418 constpool->SetJSPandaFile(jsPandaFile);
419 constpool->SetIndexHeader(&header);
420 AddConstpool(jsPandaFile, constpool.GetTaggedValue(), index++);
421 }
422 }
423
AddConstpool(const JSPandaFile * jsPandaFile,JSTaggedValue constpool,int32_t index)424 void EcmaContext::AddConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool, int32_t index)
425 {
426 ASSERT(constpool.IsConstantPool());
427 if (cachedConstpools_.find(jsPandaFile) == cachedConstpools_.end()) {
428 cachedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
429 }
430 auto &constpoolMap = cachedConstpools_[jsPandaFile];
431 ASSERT(constpoolMap.find(index) == constpoolMap.end());
432 constpoolMap.insert({index, constpool});
433 }
434
GetAndClearEcmaUncaughtException() const435 JSHandle<JSTaggedValue> EcmaContext::GetAndClearEcmaUncaughtException() const
436 {
437 JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
438 thread_->ClearException(); // clear for ohos app
439 return exceptionHandle;
440 }
441
ProcessNativeDelete(const WeakRootVisitor & visitor)442 void EcmaContext::ProcessNativeDelete(const WeakRootVisitor &visitor)
443 {
444 auto iterator = cachedConstpools_.begin();
445 while (iterator != cachedConstpools_.end()) {
446 auto &constpools = iterator->second;
447 auto constpoolIter = constpools.begin();
448 while (constpoolIter != constpools.end()) {
449 JSTaggedValue constpoolVal = constpoolIter->second;
450 if (constpoolVal.IsHeapObject()) {
451 TaggedObject *obj = constpoolVal.GetTaggedObject();
452 auto fwd = visitor(obj);
453 if (fwd == nullptr) {
454 constpoolIter = constpools.erase(constpoolIter);
455 continue;
456 }
457 }
458 ++constpoolIter;
459 }
460 if (constpools.size() == 0) {
461 LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
462 JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iterator->first);
463 iterator = cachedConstpools_.erase(iterator);
464 } else {
465 ++iterator;
466 }
467 }
468 }
469
ProcessReferences(const WeakRootVisitor & visitor)470 void EcmaContext::ProcessReferences(const WeakRootVisitor &visitor)
471 {
472 auto iterator = cachedConstpools_.begin();
473 while (iterator != cachedConstpools_.end()) {
474 auto &constpools = iterator->second;
475 auto constpoolIter = constpools.begin();
476 while (constpoolIter != constpools.end()) {
477 JSTaggedValue constpoolVal = constpoolIter->second;
478 if (constpoolVal.IsHeapObject()) {
479 TaggedObject *obj = constpoolVal.GetTaggedObject();
480 auto fwd = visitor(obj);
481 if (fwd == nullptr) {
482 constpoolIter = constpools.erase(constpoolIter);
483 continue;
484 } else if (fwd != obj) {
485 constpoolIter->second = JSTaggedValue(fwd);
486 }
487 }
488 ++constpoolIter;
489 }
490 if (constpools.size() == 0) {
491 LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
492 JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iterator->first);
493 iterator = cachedConstpools_.erase(iterator);
494 } else {
495 ++iterator;
496 }
497 }
498 }
499
GetEcmaUncaughtException() const500 JSHandle<JSTaggedValue> EcmaContext::GetEcmaUncaughtException() const
501 {
502 if (!thread_->HasPendingException()) {
503 return JSHandle<JSTaggedValue>();
504 }
505 JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
506 return exceptionHandle;
507 }
508
EnableUserUncaughtErrorHandler()509 void EcmaContext::EnableUserUncaughtErrorHandler()
510 {
511 isUncaughtExceptionRegistered_ = true;
512 }
513
HandleUncaughtException(JSTaggedValue exception)514 void EcmaContext::HandleUncaughtException(JSTaggedValue exception)
515 {
516 if (isUncaughtExceptionRegistered_) {
517 return;
518 }
519 [[maybe_unused]] EcmaHandleScope handleScope(thread_);
520 JSHandle<JSTaggedValue> exceptionHandle(thread_, exception);
521 // if caught exceptionHandle type is JSError
522 thread_->ClearException();
523 if (exceptionHandle->IsJSError()) {
524 PrintJSErrorInfo(thread_, exceptionHandle);
525 return;
526 }
527 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
528 CString string = ConvertToString(*result);
529 LOG_NO_TAG(ERROR) << string;
530 }
531
532 // static
PrintJSErrorInfo(JSThread * thread,const JSHandle<JSTaggedValue> & exceptionInfo)533 void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> &exceptionInfo)
534 {
535 JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
536 JSHandle<JSTaggedValue> nameValue = JSObject::GetProperty(thread, exceptionInfo, nameKey).GetValue();
537 JSHandle<EcmaString> name = JSTaggedValue::ToString(thread, nameValue);
538 // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
539 if (thread->HasPendingException()) {
540 thread->ClearException();
541 name = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
542 }
543 JSHandle<JSTaggedValue> msgKey = thread->GlobalConstants()->GetHandledMessageString();
544 JSHandle<JSTaggedValue> msgValue = JSObject::GetProperty(thread, exceptionInfo, msgKey).GetValue();
545 JSHandle<EcmaString> msg = JSTaggedValue::ToString(thread, msgValue);
546 // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
547 if (thread->HasPendingException()) {
548 thread->ClearException();
549 msg = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
550 }
551 JSHandle<JSTaggedValue> stackKey = thread->GlobalConstants()->GetHandledStackString();
552 JSHandle<JSTaggedValue> stackValue = JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue();
553 JSHandle<EcmaString> stack = JSTaggedValue::ToString(thread, stackValue);
554 // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
555 if (thread->HasPendingException()) {
556 thread->ClearException();
557 stack = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
558 }
559
560 CString nameBuffer = ConvertToString(*name);
561 CString msgBuffer = ConvertToString(*msg);
562 CString stackBuffer = ConvertToString(*stack);
563 LOG_NO_TAG(ERROR) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer;
564 }
565
ExecutePromisePendingJob()566 bool EcmaContext::ExecutePromisePendingJob()
567 {
568 if (isProcessingPendingJob_) {
569 LOG_ECMA(DEBUG) << "EcmaVM::ExecutePromisePendingJob can not reentrant";
570 return false;
571 }
572 if (!thread_->HasPendingException()) {
573 isProcessingPendingJob_ = true;
574 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
575 isProcessingPendingJob_ = false;
576 return true;
577 }
578 return false;
579 }
580
ClearBufferData()581 void EcmaContext::ClearBufferData()
582 {
583 auto iter = cachedConstpools_.begin();
584 while (iter != cachedConstpools_.end()) {
585 LOG_ECMA(INFO) << "remove js pandafile by vm destruct, file:" << iter->first->GetJSPandaFileDesc();
586 JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iter->first);
587 iter++;
588 }
589 cachedConstpools_.clear();
590 }
591
SetGlobalEnv(GlobalEnv * global)592 void EcmaContext::SetGlobalEnv(GlobalEnv *global)
593 {
594 ASSERT(global != nullptr);
595 globalEnv_ = JSTaggedValue(global);
596 }
597
SetMicroJobQueue(job::MicroJobQueue * queue)598 void EcmaContext::SetMicroJobQueue(job::MicroJobQueue *queue)
599 {
600 ASSERT(queue != nullptr);
601 microJobQueue_ = JSTaggedValue(queue);
602 }
603
GetGlobalEnv() const604 JSHandle<GlobalEnv> EcmaContext::GetGlobalEnv() const
605 {
606 return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
607 }
608
GetMicroJobQueue() const609 JSHandle<job::MicroJobQueue> EcmaContext::GetMicroJobQueue() const
610 {
611 return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(µJobQueue_));
612 }
613
MountContext(JSThread * thread)614 void EcmaContext::MountContext(JSThread *thread)
615 {
616 EcmaContext *context = EcmaContext::CreateAndInitialize(thread);
617 thread->SwitchCurrentContext(context);
618 }
619
UnmountContext(JSThread * thread)620 void EcmaContext::UnmountContext(JSThread *thread)
621 {
622 EcmaContext *context = thread->GetCurrentEcmaContext();
623 thread->PopContext();
624 Destroy(context);
625 }
626
CreateAndInitialize(JSThread * thread)627 EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread)
628 {
629 EcmaContext *context = EcmaContext::Create(thread);
630 thread->PushContext(context);
631 context->Initialize();
632 return context;
633 }
634
CheckAndDestroy(JSThread * thread,EcmaContext * context)635 void EcmaContext::CheckAndDestroy(JSThread *thread, EcmaContext *context)
636 {
637 if (thread->EraseContext(context)) {
638 Destroy(context);
639 return;
640 }
641 LOG_ECMA(FATAL) << "CheckAndDestroy a nonexistent context.";
642 }
643
SetupRegExpResultCache()644 void EcmaContext::SetupRegExpResultCache()
645 {
646 regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
647 }
648
Iterate(const RootVisitor & v,const RootRangeVisitor & rv)649 void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv)
650 {
651 // visit global Constant
652 globalConst_.VisitRangeSlot(rv);
653
654 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
655 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(®expCache_)));
656 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(µJobQueue_)));
657 if (moduleManager_) {
658 moduleManager_->Iterate(v);
659 }
660 if (tsManager_) {
661 tsManager_->Iterate(v);
662 }
663 if (aotFileManager_) {
664 aotFileManager_->Iterate(v);
665 }
666 if (propertiesCache_ != nullptr) {
667 propertiesCache_->Clear();
668 }
669 if (!vm_->GetJSOptions().EnableGlobalLeakCheck() && currentHandleStorageIndex_ != -1) {
670 // IterateHandle when disableGlobalLeakCheck.
671 int32_t nid = currentHandleStorageIndex_;
672 for (int32_t i = 0; i <= nid; ++i) {
673 auto node = handleStorageNodes_.at(i);
674 auto start = node->data();
675 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
676 rv(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
677 }
678 }
679
680 if (!joinStack_.empty()) {
681 rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&joinStack_.front())),
682 ObjectSlot(ToUintPtr(&joinStack_.back()) + JSTaggedValue::TaggedTypeSize()));
683 }
684 }
685
IterateHandle(const RootRangeVisitor & rangeVisitor)686 size_t EcmaContext::IterateHandle(const RootRangeVisitor &rangeVisitor)
687 {
688 // EnableGlobalLeakCheck.
689 size_t handleCount = 0;
690 if (currentHandleStorageIndex_ != -1) {
691 int32_t nid = currentHandleStorageIndex_;
692 for (int32_t i = 0; i <= nid; ++i) {
693 auto node = handleStorageNodes_.at(i);
694 auto start = node->data();
695 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
696 rangeVisitor(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
697 handleCount += (ToUintPtr(end) - ToUintPtr(start)) / sizeof(JSTaggedType);
698 }
699 }
700 return handleCount;
701 }
702
ExpandHandleStorage()703 uintptr_t *EcmaContext::ExpandHandleStorage()
704 {
705 uintptr_t *result = nullptr;
706 int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
707 if (currentHandleStorageIndex_ == lastIndex) {
708 auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
709 handleStorageNodes_.push_back(n);
710 currentHandleStorageIndex_++;
711 result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
712 handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
713 } else {
714 currentHandleStorageIndex_++;
715 auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
716 result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
717 handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
718 }
719
720 return result;
721 }
722
ShrinkHandleStorage(int prevIndex)723 void EcmaContext::ShrinkHandleStorage(int prevIndex)
724 {
725 currentHandleStorageIndex_ = prevIndex;
726 int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
727 #if ECMASCRIPT_ENABLE_ZAP_MEM
728 uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
729 if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
730 LOG_FULL(FATAL) << "memset_s failed";
731 UNREACHABLE();
732 }
733 for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
734 if (memset_s(handleStorageNodes_[i],
735 NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
736 NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
737 EOK) {
738 LOG_FULL(FATAL) << "memset_s failed";
739 UNREACHABLE();
740 }
741 }
742 #endif
743
744 if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
745 for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
746 auto node = handleStorageNodes_.back();
747 delete node;
748 handleStorageNodes_.pop_back();
749 }
750 }
751 }
752
LoadStubFile()753 void EcmaContext::LoadStubFile()
754 {
755 std::string stubFile = vm_->GetJSOptions().GetStubFile();
756 aotFileManager_->LoadStubFile(stubFile);
757 }
758
LoadAOTFiles(const std::string & aotFileName)759 bool EcmaContext::LoadAOTFiles(const std::string& aotFileName)
760 {
761 std::string anFile = aotFileName + AOTFileManager::FILE_EXTENSION_AN;
762 if (!aotFileManager_->LoadAnFile(anFile)) {
763 LOG_ECMA(ERROR) << "Load " << anFile << " failed. Destroy aot data and rollback to interpreter";
764 ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
765 return false;
766 }
767
768 std::string aiFile = aotFileName + AOTFileManager::FILE_EXTENSION_AI;
769 if (!aotFileManager_->LoadAiFile(aiFile)) {
770 LOG_ECMA(ERROR) << "Load " << aiFile << " failed. Destroy aot data and rollback to interpreter";
771 ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
772 return false;
773 }
774 return true;
775 }
776
PrintOptStat()777 void EcmaContext::PrintOptStat()
778 {
779 if (optCodeProfiler_ != nullptr) {
780 optCodeProfiler_->PrintAndReset();
781 }
782 }
783
DumpAOTInfo() const784 void EcmaContext::DumpAOTInfo() const
785 {
786 aotFileManager_->DumpAOTInfo();
787 }
788
JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)789 bool EcmaContext::JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)
790 {
791 if (JSTaggedValue::SameValue(joinStack_[0], JSTaggedValue::Hole())) {
792 joinStack_[0] = receiver.GetTaggedValue();
793 return true;
794 }
795 return JoinStackPush(receiver);
796 }
797
JoinStackPush(JSHandle<JSTaggedValue> receiver)798 bool EcmaContext::JoinStackPush(JSHandle<JSTaggedValue> receiver)
799 {
800 uint32_t capacity = joinStack_.size();
801 JSTaggedValue receiverValue = receiver.GetTaggedValue();
802 for (size_t i = 0; i < capacity; ++i) {
803 if (JSTaggedValue::SameValue(joinStack_[i], JSTaggedValue::Hole())) {
804 joinStack_[i] = receiverValue;
805 return true;
806 }
807 if (JSTaggedValue::SameValue(joinStack_[i], receiverValue)) {
808 return false;
809 }
810 }
811 joinStack_.emplace_back(receiverValue);
812 return true;
813 }
814
JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)815 void EcmaContext::JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)
816 {
817 uint32_t length = joinStack_.size();
818 if (JSTaggedValue::SameValue(joinStack_[0], receiver.GetTaggedValue()) && length == MIN_JOIN_STACK_SIZE) {
819 joinStack_[0] = JSTaggedValue::Hole();
820 } else {
821 JoinStackPop(receiver);
822 }
823 }
824
JoinStackPop(JSHandle<JSTaggedValue> receiver)825 void EcmaContext::JoinStackPop(JSHandle<JSTaggedValue> receiver)
826 {
827 uint32_t length = joinStack_.size();
828 for (size_t i = 0; i < length; ++i) {
829 if (JSTaggedValue::SameValue(joinStack_[i], receiver.GetTaggedValue())) {
830 if (i == 0 && length > MIN_JOIN_STACK_SIZE) {
831 joinStack_ = {JSTaggedValue::Hole(), JSTaggedValue::Hole()};
832 break;
833 } else {
834 joinStack_[i] = JSTaggedValue::Hole();
835 break;
836 }
837 }
838 }
839 }
840 } // namespace panda::ecmascript
841