1 /*
2 * Copyright (c) 2023-2024 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/builtins/builtins.h"
19 #include "ecmascript/builtins/builtins_regexp.h"
20 #include "ecmascript/builtins/builtins_number.h"
21 #include "ecmascript/compiler/aot_constantpool_patcher.h"
22 #include "ecmascript/compiler/pgo_type/pgo_type_manager.h"
23 #include "ecmascript/compiler/rt_call_signature.h"
24 #include "ecmascript/dfx/vmstat/opt_code_profiler.h"
25 #include "ecmascript/ecma_string.h"
26 #include "ecmascript/ecma_string_table.h"
27 #include "ecmascript/ecma_vm.h"
28 #include "ecmascript/global_env.h"
29 #include "ecmascript/global_env_constants-inl.h"
30 #include "ecmascript/ic/mega_ic_cache.h"
31 #include "ecmascript/interpreter/interpreter-inl.h"
32 #include "ecmascript/jit/jit.h"
33 #include "ecmascript/linked_hash_table.h"
34 #include "ecmascript/module/module_logger.h"
35 #include "ecmascript/jspandafile/abc_buffer_cache.h"
36 #include "ecmascript/platform/aot_crash_info.h"
37 #include "ecmascript/platform/ecma_context.h"
38 #include "ecmascript/platform/log.h"
39 #include "ecmascript/regexp/regexp_parser_cache.h"
40 #include "ecmascript/require/js_require_manager.h"
41 #include "ecmascript/snapshot/mem/snapshot.h"
42 #include "ecmascript/stubs/runtime_stubs.h"
43 #include "ecmascript/sustaining_js_handle.h"
44
45 namespace panda::ecmascript {
46 using PathHelper = base::PathHelper;
47
EcmaContext(JSThread * thread)48 EcmaContext::EcmaContext(JSThread *thread)
49 : thread_(thread),
50 vm_(thread->GetEcmaVM()),
51 factory_(vm_->GetFactory()),
52 aotFileManager_(vm_->GetAOTFileManager())
53 {
54 }
55
56 /* static */
Create(JSThread * thread)57 EcmaContext *EcmaContext::Create(JSThread *thread)
58 {
59 LOG_ECMA(INFO) << "EcmaContext::Create";
60 auto context = new EcmaContext(thread);
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 ecmaData_.propertiesCache_ = new PropertiesCache();
81 if (vm_->GetJSOptions().IsEnableMegaIC()) {
82 ecmaData_.loadMegaICCache_ = new MegaICCache();
83 ecmaData_.storeMegaICCache_ = new MegaICCache();
84 }
85 regExpParserCache_ = new RegExpParserCache();
86 unsharedConstpools_ = new(std::nothrow) JSTaggedValue[GetUnsharedConstpoolsArrayLen()];
87 if (unsharedConstpools_ == nullptr) {
88 LOG_ECMA(FATAL) << "allocate unshared constpool array fail during initing";
89 UNREACHABLE();
90 }
91 std::fill(unsharedConstpools_, unsharedConstpools_ + GetUnsharedConstpoolsArrayLen(), JSTaggedValue::Hole());
92 thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(unsharedConstpools_));
93 thread_->SetUnsharedConstpoolsArrayLen(unsharedConstpoolsArrayLen_);
94
95 thread_->SetGlobalConst(&globalConst_);
96 globalConst_.Init(thread_);
97 JSHandle<JSHClass> hClassHandle = JSHandle<JSHClass>(thread_, globalConst_.GetHClassClass());
98 JSHandle<JSHClass> globalEnvClass = factory_->NewEcmaHClass(
99 *hClassHandle,
100 GlobalEnv::SIZE,
101 JSType::GLOBAL_ENV);
102 JSHandle<GlobalEnv> globalEnv = factory_->NewGlobalEnv(*globalEnvClass);
103 globalEnv->Init(thread_);
104 globalEnv_ = globalEnv.GetTaggedValue();
105 Builtins builtins;
106 bool builtinsLazyEnabled = vm_->GetJSOptions().IsWorker() && vm_->GetJSOptions().GetEnableBuiltinsLazy();
107 thread_->SetEnableLazyBuiltins(builtinsLazyEnabled);
108 builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled);
109
110 SetupRegExpResultCache();
111 SetupRegExpGlobalResult();
112 SetupNumberToStringResultCache();
113 SetupStringSplitResultCache();
114 SetupStringToListResultCache();
115 microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
116 moduleManager_ = new ModuleManager(vm_);
117 ptManager_ = new kungfu::PGOTypeManager(vm_);
118 optCodeProfiler_ = new OptCodeProfiler();
119 abcBufferCache_ = new AbcBufferCache();
120 if (vm_->GetJSOptions().GetTypedOpProfiler()) {
121 typedOpProfiler_ = new TypedOpProfiler();
122 }
123 if (vm_->GetJSOptions().EnableModuleLog()) {
124 moduleLogger_ = new ModuleLogger(vm_);
125 }
126 functionProtoTransitionTable_ = new FunctionProtoTransitionTable(thread_);
127 sustainingJSHandleList_ = new SustainingJSHandleList();
128 initialized_ = true;
129 return true;
130 }
131
~EcmaContext()132 EcmaContext::~EcmaContext()
133 {
134 for (auto n : handleStorageNodes_) {
135 delete n;
136 }
137 handleStorageNodes_.clear();
138 currentHandleStorageIndex_ = -1;
139 handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
140
141 for (auto n : primitiveStorageNodes_) {
142 delete n;
143 }
144 primitiveStorageNodes_.clear();
145 currentPrimitiveStorageIndex_ = -1;
146 primitiveScopeStorageNext_ = primitiveScopeStorageEnd_ = nullptr;
147
148 if (vm_->IsEnableBaselineJit() || vm_->IsEnableFastJit()) {
149 // clear jit task
150 vm_->GetJit()->ClearTask(this);
151 }
152
153 ClearBufferData();
154 // clear c_address: c++ pointer delete
155 if (!vm_->IsBundlePack()) {
156 std::shared_ptr<JSPandaFile> jsPandaFile =
157 JSPandaFileManager::GetInstance()->FindJSPandaFile(vm_->GetAssetPath());
158 if (jsPandaFile != nullptr) {
159 jsPandaFile->DeleteParsedConstpoolVM(vm_);
160 }
161 }
162
163 if (optCodeProfiler_ != nullptr) {
164 delete optCodeProfiler_;
165 optCodeProfiler_ = nullptr;
166 }
167 if (typedOpProfiler_ != nullptr) {
168 delete typedOpProfiler_;
169 typedOpProfiler_ = nullptr;
170 }
171 if (moduleManager_ != nullptr) {
172 delete moduleManager_;
173 moduleManager_ = nullptr;
174 }
175 if (moduleLogger_ != nullptr) {
176 delete moduleLogger_;
177 moduleLogger_ = nullptr;
178 }
179 if (ptManager_ != nullptr) {
180 delete ptManager_;
181 ptManager_ = nullptr;
182 }
183 if (regExpParserCache_ != nullptr) {
184 delete regExpParserCache_;
185 regExpParserCache_ = nullptr;
186 }
187 if (aotFileManager_ != nullptr) {
188 aotFileManager_ = nullptr;
189 }
190 if (ecmaData_.loadMegaICCache_ != nullptr) {
191 delete ecmaData_.loadMegaICCache_;
192 ecmaData_.loadMegaICCache_ = nullptr;
193 }
194 if (ecmaData_.storeMegaICCache_ != nullptr) {
195 delete ecmaData_.storeMegaICCache_;
196 ecmaData_.storeMegaICCache_ = nullptr;
197 }
198 if (ecmaData_.propertiesCache_ != nullptr) {
199 delete ecmaData_.propertiesCache_;
200 ecmaData_.propertiesCache_ = nullptr;
201 }
202 if (sustainingJSHandleList_ != nullptr) {
203 delete sustainingJSHandleList_;
204 sustainingJSHandleList_ = nullptr;
205 }
206 if (functionProtoTransitionTable_ != nullptr) {
207 delete functionProtoTransitionTable_;
208 functionProtoTransitionTable_ = nullptr;
209 }
210 if (abcBufferCache_ != nullptr) {
211 delete abcBufferCache_;
212 abcBufferCache_ = nullptr;
213 }
214 if (unsharedConstpools_ != nullptr) {
215 delete[] unsharedConstpools_;
216 unsharedConstpools_ = nullptr;
217 thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(nullptr));
218 thread_->SetUnsharedConstpoolsArrayLen(0);
219 }
220 // clear join stack
221 joinStack_.clear();
222 }
223
InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint,CJSInfo * cjsInfo)224 JSTaggedValue EcmaContext::InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg,
225 const JSPandaFile *jsPandaFile, std::string_view entryPoint,
226 CJSInfo* cjsInfo)
227 {
228 aotFileManager_->SetAOTMainFuncEntry(mainFunc, jsPandaFile, entryPoint);
229 return JSFunction::InvokeOptimizedEntrypoint(thread_, mainFunc, thisArg, cjsInfo);
230 }
231
ExecuteAot(size_t actualNumArgs,JSTaggedType * args,const JSTaggedType * prevFp,bool needPushArgv)232 JSTaggedValue EcmaContext::ExecuteAot(size_t actualNumArgs, JSTaggedType *args,
233 const JSTaggedType *prevFp, bool needPushArgv)
234 {
235 INTERPRETER_TRACE(thread_, ExecuteAot);
236 ASSERT(thread_->IsInManagedState());
237 auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_JSFunctionEntry);
238 // entry of aot
239 auto res = reinterpret_cast<JSFunctionEntryType>(entry)(thread_->GetGlueAddr(),
240 actualNumArgs,
241 args,
242 reinterpret_cast<uintptr_t>(prevFp),
243 needPushArgv);
244 return res;
245 }
246
CommonInvokeEcmaEntrypoint(const JSPandaFile * jsPandaFile,std::string_view entryPoint,JSHandle<JSFunction> & func,const ExecuteTypes & executeType)247 Expected<JSTaggedValue, bool> EcmaContext::CommonInvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
248 std::string_view entryPoint, JSHandle<JSFunction> &func, const ExecuteTypes &executeType)
249 {
250 ASSERT(thread_->IsInManagedState());
251 JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
252 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
253 if (vm_->IsEnablePGOProfiler()) {
254 JSHandle<JSFunction> objectFunction(GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetObjectFunction());
255 JSHandle<JSHClass> protoOrHClass(GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetObjectFunctionNapiClass());
256 vm_->GetPGOProfiler()->ProfileNapiRootHClass(
257 objectFunction.GetTaggedType(), protoOrHClass.GetTaggedType(), pgo::ProfileType::Kind::NapiId);
258 }
259 CString entry = entryPoint.data();
260 JSRecordInfo *recordInfo = jsPandaFile->CheckAndGetRecordInfo(entry);
261 if (recordInfo == nullptr) {
262 CString msg = "Cannot find module '" + entry + "' , which is application Entry Point";
263 THROW_REFERENCE_ERROR_AND_RETURN(thread_, msg.c_str(), Unexpected(false));
264 }
265
266 ModuleLogger *moduleLogger = GetModuleLogger();
267 if (moduleLogger != nullptr) {
268 moduleLogger->SetStartTime(entry);
269 }
270 if (jsPandaFile->IsModule(recordInfo)) {
271 global = undefined;
272 CString moduleName = jsPandaFile->GetJSPandaFileDesc();
273 if (!jsPandaFile->IsBundlePack()) {
274 moduleName = entry;
275 }
276 JSHandle<SourceTextModule> module;
277 if (jsPandaFile->IsSharedModule(recordInfo)) {
278 module = SharedModuleManager::GetInstance()->GetSModule(thread_, entry);
279 } else {
280 module = moduleManager_->HostGetImportedModule(moduleName);
281 }
282 // esm -> SourceTextModule; cjs or script -> string of recordName
283 module->SetSendableEnv(thread_, JSTaggedValue::Undefined());
284 func->SetModule(thread_, module);
285 } else {
286 // if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName,
287 // which can avoid the problem of larger memory caused by the new slot
288 JSHandle<EcmaString> recordName = factory_->NewFromUtf8(entry);
289 func->SetModule(thread_, recordName);
290 }
291 vm_->CheckStartCpuProfiler();
292
293 JSTaggedValue result;
294 if (jsPandaFile->IsCjs(recordInfo)) {
295 CJSExecution(func, global, jsPandaFile, entryPoint);
296 if (moduleLogger != nullptr) {
297 moduleLogger->SetEndTime(entry);
298 }
299 } else {
300 if (aotFileManager_->IsLoadMain(jsPandaFile, entry)) {
301 EcmaRuntimeStatScope runtimeStatScope(vm_);
302 result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint);
303 } else if (vm_->GetJSOptions().IsEnableForceJitCompileMain()) {
304 Jit::Compile(vm_, func, CompilerTier::Tier::FAST);
305 EcmaRuntimeStatScope runtimeStatScope(vm_);
306 result = JSFunction::InvokeOptimizedEntrypoint(thread_, func, global, nullptr);
307 } else if (vm_->GetJSOptions().IsEnableForceBaselineCompileMain()) {
308 Jit::Compile(vm_, func, CompilerTier::Tier::BASELINE);
309 EcmaRuntimeCallInfo *info =
310 EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
311 EcmaRuntimeStatScope runtimeStatScope(vm_);
312 result = EcmaInterpreter::Execute(info);
313 } else {
314 EcmaRuntimeCallInfo *info =
315 EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
316 EcmaRuntimeStatScope runtimeStatScope(vm_);
317 result = EcmaInterpreter::Execute(info);
318 }
319 if (moduleLogger != nullptr) {
320 moduleLogger->SetEndTime(entry);
321 }
322
323 if (!thread_->HasPendingException() && IsStaticImport(executeType)) {
324 JSHandle<JSTaggedValue> handleResult(thread_, result);
325 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
326 result = handleResult.GetTaggedValue();
327 }
328 }
329
330 if (thread_->HasPendingException()) {
331 return GetPendingExceptionResult(result);
332 }
333 return result;
334 }
335
InvokeEcmaEntrypoint(const JSPandaFile * jsPandaFile,std::string_view entryPoint,const ExecuteTypes & executeType)336 Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
337 std::string_view entryPoint,
338 const ExecuteTypes &executeType)
339 {
340 [[maybe_unused]] EcmaHandleScope scope(thread_);
341 auto &options = const_cast<EcmaVM *>(thread_->GetEcmaVM())->GetJSOptions();
342 if (options.EnableModuleLog()) {
343 LOG_FULL(INFO) << "current executing file's name " << entryPoint.data();
344 }
345
346 JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
347 if (program.IsEmpty()) {
348 LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
349 return Unexpected(false);
350 }
351 // for debugger
352 vm_->GetJsDebuggerManager()->GetNotificationManager()->LoadModuleEvent(
353 jsPandaFile->GetJSPandaFileDesc(), entryPoint);
354
355 JSHandle<JSFunction> func(thread_, program->GetMainFunction());
356 Expected<JSTaggedValue, bool> result = CommonInvokeEcmaEntrypoint(jsPandaFile, entryPoint, func, executeType);
357
358 CheckHasPendingException(this, thread_);
359 return result;
360 }
361
InvokeEcmaEntrypointForHotReload(const JSPandaFile * jsPandaFile,std::string_view entryPoint,const ExecuteTypes & executeType)362 Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypointForHotReload(
363 const JSPandaFile *jsPandaFile, std::string_view entryPoint, const ExecuteTypes &executeType)
364 {
365 [[maybe_unused]] EcmaHandleScope scope(thread_);
366 JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
367
368 JSHandle<JSFunction> func(thread_, program->GetMainFunction());
369 Expected<JSTaggedValue, bool> result = CommonInvokeEcmaEntrypoint(jsPandaFile, entryPoint, func, executeType);
370
371 JSHandle<JSTaggedValue> finalModuleRecord(thread_, func->GetModule());
372 // avoid GC problems.
373 GlobalHandleCollection gloalHandleCollection(thread_);
374 JSHandle<JSTaggedValue> moduleRecordHandle =
375 gloalHandleCollection.NewHandle<JSTaggedValue>(finalModuleRecord->GetRawData());
376 CString recordName = entryPoint.data();
377 AddPatchModule(recordName, moduleRecordHandle);
378
379 // print exception information
380 if (thread_->HasPendingException() &&
381 Method::Cast(func->GetMethod())->GetMethodName() != JSPandaFile::PATCH_FUNCTION_NAME_0) {
382 return Unexpected(false);
383 }
384 return result;
385 }
386
CJSExecution(JSHandle<JSFunction> & func,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint)387 void EcmaContext::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
388 const JSPandaFile *jsPandaFile, std::string_view entryPoint)
389 {
390 // create "module", "exports", "require", "filename", "dirname"
391 JSHandle<CjsModule> module = factory_->NewCjsModule();
392 JSHandle<JSTaggedValue> require = GetGlobalEnv()->GetCjsRequireFunction();
393 JSHandle<CjsExports> exports = factory_->NewCjsExports();
394 CString fileNameStr;
395 CString dirNameStr;
396 if (jsPandaFile->IsBundlePack()) {
397 ModulePathHelper::ResolveCurrentPath(dirNameStr, fileNameStr, jsPandaFile);
398 } else {
399 JSTaggedValue funcFileName = func->GetModule();
400 ASSERT(funcFileName.IsString());
401 fileNameStr = ModulePathHelper::Utf8ConvertToString(funcFileName);
402 dirNameStr = PathHelper::ResolveDirPath(fileNameStr);
403 }
404 JSHandle<JSTaggedValue> fileName = JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8(fileNameStr));
405 JSHandle<JSTaggedValue> dirName = JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8(dirNameStr));
406 CJSInfo cjsInfo(module, require, exports, fileName, dirName);
407 RequireManager::InitializeCommonJS(thread_, cjsInfo);
408 if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) {
409 EcmaRuntimeStatScope runtimeStateScope(vm_);
410 InvokeEcmaAotEntrypoint(func, thisArg, jsPandaFile, entryPoint, &cjsInfo);
411 } else {
412 // Execute main function
413 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
414 EcmaRuntimeCallInfo *info =
415 EcmaInterpreter::NewRuntimeCallInfo(thread_,
416 JSHandle<JSTaggedValue>(func),
417 thisArg, undefined, 5); // 5 : argument numbers
418 RETURN_IF_ABRUPT_COMPLETION(thread_);
419 info->SetCallArg(cjsInfo.exportsHdl.GetTaggedValue(),
420 cjsInfo.requireHdl.GetTaggedValue(),
421 cjsInfo.moduleHdl.GetTaggedValue(),
422 cjsInfo.filenameHdl.GetTaggedValue(),
423 cjsInfo.dirnameHdl.GetTaggedValue());
424 EcmaRuntimeStatScope runtimeStatScope(vm_);
425 EcmaInterpreter::Execute(info);
426 }
427 if (!thread_->HasPendingException()) {
428 // Collecting module.exports : exports ---> module.exports --->Module._cache
429 RequireManager::CollectExecutedExp(thread_, cjsInfo);
430 }
431 }
432
LoadProtoTransitionTable(JSTaggedValue constpool)433 void EcmaContext::LoadProtoTransitionTable(JSTaggedValue constpool)
434 {
435 JSTaggedValue protoTransitionTable = ConstantPool::Cast(constpool.GetTaggedObject())->GetProtoTransTableInfo();
436 functionProtoTransitionTable_->UpdateProtoTransitionTable(
437 thread_, JSHandle<PointerToIndexDictionary>(thread_, protoTransitionTable));
438 }
439
ResetProtoTransitionTableOnConstpool(JSTaggedValue constpool)440 void EcmaContext::ResetProtoTransitionTableOnConstpool(JSTaggedValue constpool)
441 {
442 ConstantPool::Cast(constpool.GetTaggedObject())->SetProtoTransTableInfo(thread_, JSTaggedValue::Undefined());
443 }
444
445 // just find unshared constpool, not create
FindUnsharedConstpool(JSTaggedValue sharedConstpool)446 JSTaggedValue EcmaContext::FindUnsharedConstpool(JSTaggedValue sharedConstpool)
447 {
448 ConstantPool *shareCp = ConstantPool::Cast(sharedConstpool.GetTaggedObject());
449 int32_t constpoolIndex = shareCp->GetUnsharedConstpoolIndex();
450 // unshared constpool index is default INT32_MAX.
451 ASSERT(0 <= constpoolIndex && constpoolIndex != ConstantPool::CONSTPOOL_TYPE_FLAG);
452 if (constpoolIndex >= GetUnsharedConstpoolsArrayLen()) {
453 return JSTaggedValue::Hole();
454 }
455 return unsharedConstpools_[constpoolIndex];
456 }
457
FindOrCreateUnsharedConstpool(JSTaggedValue sharedConstpool)458 JSTaggedValue EcmaContext::FindOrCreateUnsharedConstpool(JSTaggedValue sharedConstpool)
459 {
460 JSTaggedValue unsharedConstpool = FindUnsharedConstpool(sharedConstpool);
461 if (unsharedConstpool.IsHole()) {
462 ConstantPool *shareCp = ConstantPool::Cast(sharedConstpool.GetTaggedObject());
463 int32_t constpoolIndex = shareCp->GetUnsharedConstpoolIndex();
464 // unshared constpool index is default INT32_MAX.
465 ASSERT(0 <= constpoolIndex && constpoolIndex != INT32_MAX);
466 JSHandle<ConstantPool> unshareCp =
467 ConstantPool::CreateUnSharedConstPoolBySharedConstpool(vm_, shareCp->GetJSPandaFile(), shareCp);
468 unsharedConstpool = unshareCp.GetTaggedValue();
469 SetUnsharedConstpool(constpoolIndex, unsharedConstpool);
470 }
471 return unsharedConstpool;
472 }
473
EraseUnusedConstpool(const JSPandaFile * jsPandaFile,int32_t index,int32_t constpoolIndex)474 void EcmaContext::EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex)
475 {
476 // unshared constpool index is default INT32_MAX.
477 ASSERT(0 <= constpoolIndex && constpoolIndex < GetUnsharedConstpoolsArrayLen());
478
479 SetUnsharedConstpool(constpoolIndex, JSTaggedValue::Hole());
480 auto iter = cachedSharedConstpools_.find(jsPandaFile);
481 if (iter == cachedSharedConstpools_.end()) {
482 return;
483 }
484 auto constpoolIter = iter->second.find(index);
485 if (constpoolIter == iter->second.end()) {
486 return;
487 }
488
489 iter->second.erase(constpoolIter);
490 if (iter->second.size() == 0) {
491 cachedSharedConstpools_.erase(iter);
492 }
493 }
494
FindConstpools(const JSPandaFile * jsPandaFile)495 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> EcmaContext::FindConstpools(
496 const JSPandaFile *jsPandaFile)
497 {
498 return Runtime::GetInstance()->FindConstpools(jsPandaFile);
499 }
500
501 // For new version instruction.
FindConstpool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)502 JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
503 {
504 panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
505 int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
506 return FindConstpool(jsPandaFile, index);
507 }
508
FindConstpool(const JSPandaFile * jsPandaFile,int32_t index)509 JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
510 {
511 JSTaggedValue contextCache = FindConstpoolFromContextCache(jsPandaFile, index);
512 if (!contextCache.IsHole()) {
513 return contextCache;
514 }
515 return Runtime::GetInstance()->FindConstpool(jsPandaFile, index);
516 }
517
FindConstpoolFromContextCache(const JSPandaFile * jsPandaFile,int32_t index)518 JSTaggedValue EcmaContext::FindConstpoolFromContextCache(const JSPandaFile *jsPandaFile, int32_t index)
519 {
520 auto iter = cachedSharedConstpools_.find(jsPandaFile);
521 if (iter != cachedSharedConstpools_.end()) {
522 auto constpoolIter = iter->second.find(index);
523 if (constpoolIter != iter->second.end()) {
524 return constpoolIter->second;
525 }
526 }
527 return JSTaggedValue::Hole();
528 }
529
HasCachedConstpool(const JSPandaFile * jsPandaFile) const530 bool EcmaContext::HasCachedConstpool(const JSPandaFile *jsPandaFile) const
531 {
532 if (cachedSharedConstpools_.find(jsPandaFile) != cachedSharedConstpools_.end()) {
533 return true;
534 }
535
536 return Runtime::GetInstance()->HasCachedConstpool(jsPandaFile);
537 }
538
AddOrUpdateConstpool(const JSPandaFile * jsPandaFile,JSHandle<ConstantPool> constpool,int32_t index)539 JSHandle<ConstantPool> EcmaContext::AddOrUpdateConstpool(const JSPandaFile *jsPandaFile,
540 JSHandle<ConstantPool> constpool,
541 int32_t index)
542 {
543 constpool = Runtime::GetInstance()->AddOrUpdateConstpool(jsPandaFile, constpool, index);
544 AddContextConstpoolCache(jsPandaFile, constpool, index);
545 return constpool;
546 }
547
AddContextConstpoolCache(const JSPandaFile * jsPandaFile,JSHandle<ConstantPool> constpool,int32_t index)548 void EcmaContext::AddContextConstpoolCache(const JSPandaFile *jsPandaFile,
549 JSHandle<ConstantPool> constpool,
550 int32_t index)
551 {
552 if (cachedSharedConstpools_.find(jsPandaFile) == cachedSharedConstpools_.end()) {
553 cachedSharedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
554 }
555 auto &constpoolMap = cachedSharedConstpools_[jsPandaFile];
556 ASSERT(constpoolMap.find(index) == constpoolMap.end());
557 constpoolMap.insert({index, constpool.GetTaggedValue()});
558 }
559
SetUnsharedConstpool(JSHandle<ConstantPool> sharedConstpool,JSTaggedValue unsharedConstpool)560 void EcmaContext::SetUnsharedConstpool(JSHandle<ConstantPool> sharedConstpool, JSTaggedValue unsharedConstpool)
561 {
562 int32_t constpoolIndex = sharedConstpool->GetUnsharedConstpoolIndex();
563 SetUnsharedConstpool(constpoolIndex, unsharedConstpool);
564 }
565
SetUnsharedConstpool(int32_t constpoolIndex,JSTaggedValue unsharedConstpool)566 void EcmaContext::SetUnsharedConstpool(int32_t constpoolIndex, JSTaggedValue unsharedConstpool)
567 {
568 GrowUnsharedConstpoolArray(constpoolIndex);
569 ASSERT(0 <= constpoolIndex && constpoolIndex < ConstantPool::CONSTPOOL_TYPE_FLAG);
570 unsharedConstpools_[constpoolIndex] = unsharedConstpool;
571 }
572
GrowUnsharedConstpoolArray(int32_t index)573 void EcmaContext::GrowUnsharedConstpoolArray(int32_t index)
574 {
575 if (index == ConstantPool::CONSTPOOL_TYPE_FLAG) {
576 LOG_ECMA(FATAL) << "index has exceed unshared constpool array limit";
577 UNREACHABLE();
578 }
579 int32_t oldCapacity = GetUnsharedConstpoolsArrayLen();
580 if (index >= oldCapacity) {
581 int32_t minCapacity = index + 1;
582 ResizeUnsharedConstpoolArray(oldCapacity, minCapacity);
583 }
584 }
585
ResizeUnsharedConstpoolArray(int32_t oldCapacity,int32_t minCapacity)586 void EcmaContext::ResizeUnsharedConstpoolArray(int32_t oldCapacity, int32_t minCapacity)
587 {
588 int32_t newCapacity = oldCapacity * 2; // 2: Double the value
589 if (newCapacity - minCapacity < 0) {
590 newCapacity = minCapacity;
591 }
592
593 if (newCapacity >= (INT32_MAX >> 1)) {
594 newCapacity = INT32_MAX;
595 }
596
597 JSTaggedValue *newUnsharedConstpools = new(std::nothrow) JSTaggedValue[newCapacity];
598 if (newUnsharedConstpools == nullptr) {
599 LOG_ECMA(FATAL) << "allocate unshared constpool array fail during resizing";
600 UNREACHABLE();
601 }
602 std::fill(newUnsharedConstpools, newUnsharedConstpools + newCapacity, JSTaggedValue::Hole());
603 std::copy(unsharedConstpools_, unsharedConstpools_ + GetUnsharedConstpoolsArrayLen(), newUnsharedConstpools);
604 ClearUnsharedConstpoolArray();
605 unsharedConstpools_ = newUnsharedConstpools;
606 thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(unsharedConstpools_));
607 thread_->SetUnsharedConstpoolsArrayLen(newCapacity);
608 SetUnsharedConstpoolsArrayLen(newCapacity);
609 }
610
UpdateConstpoolWhenDeserialAI(const std::string & fileName,JSHandle<ConstantPool> aiCP,int32_t index)611 void EcmaContext::UpdateConstpoolWhenDeserialAI(const std::string& fileName,
612 JSHandle<ConstantPool> aiCP, int32_t index)
613 {
614 auto pf = JSPandaFileManager::GetInstance()->FindJSPandaFile(fileName.c_str());
615 if (pf == nullptr) {
616 return;
617 }
618 JSTaggedValue sharedConstpool = FindConstpool(pf.get(), index);
619 JSHandle<ConstantPool> sharedCPHandle = JSHandle<ConstantPool>(thread_, sharedConstpool);
620 if (sharedConstpool.IsHole()) {
621 return;
622 }
623 JSTaggedValue unsharedConstpool = FindOrCreateUnsharedConstpool(sharedCPHandle.GetTaggedValue());
624 JSHandle<ConstantPool> unsharedCP = JSHandle<ConstantPool>(thread_, unsharedConstpool);
625 JSHandle<ConstantPool> sharedCP = JSHandle<ConstantPool>(thread_, sharedCPHandle.GetTaggedValue());
626 ConstantPool::UpdateConstpoolWhenDeserialAI(vm_, aiCP, sharedCP, unsharedCP);
627 }
628
FindCachedConstpoolAndLoadAiIfNeeded(const JSPandaFile * jsPandaFile,int32_t index)629 JSTaggedValue EcmaContext::FindCachedConstpoolAndLoadAiIfNeeded(const JSPandaFile *jsPandaFile, int32_t index)
630 {
631 JSTaggedValue constpool = FindConstpoolFromContextCache(jsPandaFile, index);
632 if (!constpool.IsHole()) {
633 return constpool;
634 }
635 constpool = Runtime::GetInstance()->FindConstpool(jsPandaFile, index);
636 if (!constpool.IsHole()) {
637 AddContextConstpoolCache(jsPandaFile, JSHandle<ConstantPool>(thread_, constpool), index);
638 }
639 // Getting the cached constpool in runtime means the ai data has not been loaded in current thread.
640 // And we need to reload it
641 aotFileManager_->LoadAiFile(jsPandaFile);
642 return constpool;
643 }
644
FindOrCreateConstPool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)645 JSHandle<ConstantPool> EcmaContext::FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
646 {
647 panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
648 int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
649 JSTaggedValue constpool = FindCachedConstpoolAndLoadAiIfNeeded(jsPandaFile, index);
650 if (constpool.IsHole()) {
651 JSHandle<ConstantPool> newConstpool = ConstantPool::CreateUnSharedConstPool(vm_, jsPandaFile, id);
652 JSHandle<ConstantPool> newSConstpool;
653 if (jsPandaFile->IsLoadedAOT()) {
654 AotConstantpoolPatcher::SetObjectFunctionFromConstPool(thread_, newConstpool);
655 newSConstpool = ConstantPool::CreateSharedConstPoolForAOT(vm_, newConstpool, index);
656 } else {
657 newSConstpool = ConstantPool::CreateSharedConstPool(vm_, jsPandaFile, id, index);
658 }
659 newSConstpool = AddOrUpdateConstpool(jsPandaFile, newSConstpool, index);
660 SetUnsharedConstpool(newSConstpool, newConstpool.GetTaggedValue());
661 return newSConstpool;
662 } else if (jsPandaFile->IsLoadedAOT()) {
663 // For aot, after getting the cached shared constpool,
664 // worker thread need to create and bind the correspoding unshared constpool.
665 JSHandle<ConstantPool> newConstpool = JSHandle<ConstantPool>(thread_, FindOrCreateUnsharedConstpool(constpool));
666 AotConstantpoolPatcher::SetObjectFunctionFromConstPool(thread_, newConstpool);
667 }
668 return JSHandle<ConstantPool>(thread_, constpool);
669 }
670
CreateAllConstpool(const JSPandaFile * jsPandaFile)671 void EcmaContext::CreateAllConstpool(const JSPandaFile *jsPandaFile)
672 {
673 auto headers = jsPandaFile->GetPandaFile()->GetIndexHeaders();
674 uint32_t index = 0;
675 for (const auto &header : headers) {
676 auto constpoolSize = header.method_idx_size;
677 JSHandle<ConstantPool> sconstpool = factory_->NewSConstantPool(constpoolSize);
678 sconstpool->SetJSPandaFile(jsPandaFile);
679 sconstpool->SetIndexHeader(&header);
680 sconstpool->SetSharedConstpoolId(JSTaggedValue(index));
681 sconstpool = AddOrUpdateConstpool(jsPandaFile, sconstpool, index);
682 index++;
683
684 JSHandle<ConstantPool> constpool = factory_->NewConstantPool(constpoolSize);
685 constpool->SetJSPandaFile(jsPandaFile);
686 constpool->SetIndexHeader(&header);
687 SetUnsharedConstpool(sconstpool, constpool.GetTaggedValue());
688 }
689 }
690
GetAndClearEcmaUncaughtException() const691 JSHandle<JSTaggedValue> EcmaContext::GetAndClearEcmaUncaughtException() const
692 {
693 JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
694 thread_->ClearException(); // clear for ohos app
695 return exceptionHandle;
696 }
697
RelocateConstantString(const JSPandaFile * jsPandaFile)698 void EcmaContext::RelocateConstantString(const JSPandaFile *jsPandaFile)
699 {
700 if (!jsPandaFile->IsFirstMergedAbc()) {
701 return;
702 }
703 vm_->GetEcmaStringTable()->RelocateConstantData(vm_, jsPandaFile);
704 }
705
GetEcmaUncaughtException() const706 JSHandle<JSTaggedValue> EcmaContext::GetEcmaUncaughtException() const
707 {
708 if (!thread_->HasPendingException()) {
709 return JSHandle<JSTaggedValue>();
710 }
711 JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
712 return exceptionHandle;
713 }
714
EnableUserUncaughtErrorHandler()715 void EcmaContext::EnableUserUncaughtErrorHandler()
716 {
717 isUncaughtExceptionRegistered_ = true;
718 }
719
HandleUncaughtException(JSTaggedValue exception)720 void EcmaContext::HandleUncaughtException(JSTaggedValue exception)
721 {
722 [[maybe_unused]] EcmaHandleScope handleScope(thread_);
723 JSHandle<JSTaggedValue> exceptionHandle(thread_, exception);
724 if (isUncaughtExceptionRegistered_) {
725 if (vm_->GetJSThread()->IsMainThread()) {
726 return;
727 }
728 auto callback = vm_->GetOnErrorCallback();
729 if (callback) {
730 thread_->ClearException();
731 Local<ObjectRef> exceptionRef = JSNApiHelper::ToLocal<ObjectRef>(exceptionHandle);
732 callback(exceptionRef, vm_->GetOnAllData());
733 }
734 }
735 // if caught exceptionHandle type is JSError
736 thread_->ClearException();
737 if (exceptionHandle->IsJSError()) {
738 PrintJSErrorInfo(thread_, exceptionHandle);
739 return;
740 }
741 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
742 CString string = ConvertToString(*result);
743 LOG_NO_TAG(ERROR) << string;
744 }
745
HandleUncaughtException()746 void EcmaContext::HandleUncaughtException()
747 {
748 if (!thread_->HasPendingException()) {
749 return;
750 }
751 JSTaggedValue exception = thread_->GetException();
752 HandleUncaughtException(exception);
753 }
754
755 // static
PrintJSErrorInfo(JSThread * thread,const JSHandle<JSTaggedValue> & exceptionInfo)756 void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> &exceptionInfo)
757 {
758 CString nameBuffer = GetJSErrorInfo(thread, exceptionInfo, JSErrorProps::NAME);
759 CString msgBuffer = GetJSErrorInfo(thread, exceptionInfo, JSErrorProps::MESSAGE);
760 CString stackBuffer = GetJSErrorInfo(thread, exceptionInfo, JSErrorProps::STACK);
761 LOG_NO_TAG(ERROR) << panda::ecmascript::previewerTag << nameBuffer << ": " << msgBuffer << "\n"
762 << (panda::ecmascript::previewerTag.empty()
763 ? stackBuffer
764 : std::regex_replace(stackBuffer, std::regex(".+(\n|$)"),
765 panda::ecmascript::previewerTag + "$0"));
766 }
767
GetJSErrorInfo(JSThread * thread,const JSHandle<JSTaggedValue> exceptionInfo,JSErrorProps key)768 CString EcmaContext::GetJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> exceptionInfo, JSErrorProps key)
769 {
770 JSHandle<JSTaggedValue> keyStr(thread, JSTaggedValue::Undefined());
771 switch (key) {
772 case JSErrorProps::NAME:
773 keyStr = thread->GlobalConstants()->GetHandledNameString();
774 break;
775 case JSErrorProps::MESSAGE:
776 keyStr = thread->GlobalConstants()->GetHandledMessageString();
777 break;
778 case JSErrorProps::STACK:
779 keyStr = thread->GlobalConstants()->GetHandledStackString();
780 break;
781 default:
782 LOG_ECMA(FATAL) << "this branch is unreachable " << key;
783 UNREACHABLE();
784 }
785 JSHandle<JSTaggedValue> value = JSObject::GetProperty(thread, exceptionInfo, keyStr).GetValue();
786 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CString());
787 JSHandle<EcmaString> errStr = JSTaggedValue::ToString(thread, value);
788 // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
789 if (thread->HasPendingException()) {
790 thread->ClearException();
791 errStr = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
792 }
793 return ConvertToString(*errStr);
794 }
795
HasPendingJob()796 bool EcmaContext::HasPendingJob()
797 {
798 // This interface only determines whether PromiseJobQueue is empty, rather than ScriptJobQueue.
799 if (UNLIKELY(thread_->HasTerminated())) {
800 return false;
801 }
802 TaggedQueue* promiseQueue = TaggedQueue::Cast(GetMicroJobQueue()->GetPromiseJobQueue().GetTaggedObject());
803 return !promiseQueue->Empty();
804 }
805
ExecutePromisePendingJob()806 bool EcmaContext::ExecutePromisePendingJob()
807 {
808 if (isProcessingPendingJob_) {
809 LOG_ECMA(DEBUG) << "EcmaVM::ExecutePromisePendingJob can not reentrant";
810 return false;
811 }
812 if (!thread_->HasPendingException()) {
813 isProcessingPendingJob_ = true;
814 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
815 if (thread_->HasPendingException()) {
816 JsStackInfo::BuildCrashInfo(thread_);
817 }
818 isProcessingPendingJob_ = false;
819 return true;
820 }
821 return false;
822 }
823
ClearBufferData()824 void EcmaContext::ClearBufferData()
825 {
826 cachedSharedConstpools_.clear();
827 thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(nullptr));
828 thread_->SetUnsharedConstpoolsArrayLen(0);
829 }
830
SetGlobalEnv(GlobalEnv * global)831 void EcmaContext::SetGlobalEnv(GlobalEnv *global)
832 {
833 // In jsthread iteration, SwitchCurrentContext is called to iterate each context.
834 // If the target context is not fully initialized, the variable "global" will be nullptr.
835 if (global != nullptr) {
836 globalEnv_ = JSTaggedValue(global);
837 }
838 }
839
SetMicroJobQueue(job::MicroJobQueue * queue)840 void EcmaContext::SetMicroJobQueue(job::MicroJobQueue *queue)
841 {
842 ASSERT(queue != nullptr);
843 microJobQueue_ = JSTaggedValue(queue);
844 }
845
GetGlobalEnv() const846 JSHandle<GlobalEnv> EcmaContext::GetGlobalEnv() const
847 {
848 return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
849 }
850
GetMicroJobQueue() const851 JSHandle<job::MicroJobQueue> EcmaContext::GetMicroJobQueue() const
852 {
853 return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(µJobQueue_));
854 }
855
MountContext(JSThread * thread)856 void EcmaContext::MountContext(JSThread *thread)
857 {
858 EcmaContext *context = EcmaContext::CreateAndInitialize(thread);
859 thread->SwitchCurrentContext(context);
860 }
861
UnmountContext(JSThread * thread)862 void EcmaContext::UnmountContext(JSThread *thread)
863 {
864 EcmaContext *context = thread->GetCurrentEcmaContext();
865 thread->PopContext();
866 Destroy(context);
867 }
868
CreateAndInitialize(JSThread * thread)869 EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread)
870 {
871 EcmaContext *context = EcmaContext::Create(thread);
872 thread->PushContext(context);
873 context->Initialize();
874 return context;
875 }
876
CheckAndDestroy(JSThread * thread,EcmaContext * context)877 void EcmaContext::CheckAndDestroy(JSThread *thread, EcmaContext *context)
878 {
879 if (thread->EraseContext(context)) {
880 Destroy(context);
881 return;
882 }
883 LOG_ECMA(FATAL) << "CheckAndDestroy a nonexistent context.";
884 }
885
SetupRegExpResultCache()886 void EcmaContext::SetupRegExpResultCache()
887 {
888 regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
889 }
890
SetupRegExpGlobalResult()891 void EcmaContext::SetupRegExpGlobalResult()
892 {
893 regexpGlobal_ = builtins::RegExpGlobalResult::CreateGlobalResultTable(thread_);
894 }
895
SetupNumberToStringResultCache()896 void EcmaContext::SetupNumberToStringResultCache()
897 {
898 numberToStringResultCache_ = builtins::NumberToStringResultCache::CreateCacheTable(thread_);
899 }
900
SetupStringSplitResultCache()901 void EcmaContext::SetupStringSplitResultCache()
902 {
903 stringSplitResultCache_ = builtins::StringSplitResultCache::CreateCacheTable(thread_);
904 }
905
SetupStringToListResultCache()906 void EcmaContext::SetupStringToListResultCache()
907 {
908 stringToListResultCache_ = builtins::StringToListResultCache::CreateCacheTable(thread_);
909 }
910
IterateMegaIC(RootVisitor & v)911 void EcmaContext::IterateMegaIC(RootVisitor &v)
912 {
913 if (ecmaData_.loadMegaICCache_ != nullptr) {
914 ecmaData_.loadMegaICCache_->Iterate(v);
915 }
916 if (ecmaData_.storeMegaICCache_ != nullptr) {
917 ecmaData_.storeMegaICCache_->Iterate(v);
918 }
919 }
920
Iterate(RootVisitor & v)921 void EcmaContext::Iterate(RootVisitor &v)
922 {
923 // visit global Constant
924 globalConst_.Iterate(v);
925
926 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
927 if (!regexpCache_.IsHole()) {
928 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(®expCache_)));
929 }
930 if (!regexpGlobal_.IsHole()) {
931 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(®expGlobal_)));
932 }
933 if (!numberToStringResultCache_.IsHole()) {
934 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&numberToStringResultCache_)));
935 }
936 if (!stringSplitResultCache_.IsHole()) {
937 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&stringSplitResultCache_)));
938 }
939 if (!stringToListResultCache_.IsHole()) {
940 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&stringToListResultCache_)));
941 }
942 if (!microJobQueue_.IsHole()) {
943 v.VisitRoot(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(µJobQueue_)));
944 }
945
946 if (functionProtoTransitionTable_) {
947 functionProtoTransitionTable_->Iterate(v);
948 }
949 if (moduleManager_) {
950 moduleManager_->Iterate(v);
951 }
952 if (ptManager_) {
953 ptManager_->Iterate(v);
954 }
955 if (ecmaData_.propertiesCache_ != nullptr) {
956 ecmaData_.propertiesCache_->Clear();
957 }
958 if (regExpParserCache_ != nullptr) {
959 regExpParserCache_->Clear();
960 }
961 IterateMegaIC(v);
962 if (!vm_->GetJSOptions().EnableGlobalLeakCheck() && currentHandleStorageIndex_ != -1) {
963 // IterateHandle when disableGlobalLeakCheck.
964 int32_t nid = currentHandleStorageIndex_;
965 for (int32_t i = 0; i <= nid; ++i) {
966 auto node = handleStorageNodes_.at(i);
967 auto start = node->data();
968 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
969 v.VisitRangeRoot(Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
970 }
971 }
972
973 if (sustainingJSHandleList_) {
974 sustainingJSHandleList_->Iterate(v);
975 }
976
977 if (!joinStack_.empty()) {
978 v.VisitRangeRoot(Root::ROOT_VM, ObjectSlot(ToUintPtr(&joinStack_.front())),
979 ObjectSlot(ToUintPtr(&joinStack_.back()) + JSTaggedValue::TaggedTypeSize()));
980 }
981
982 auto start = ObjectSlot(ToUintPtr(unsharedConstpools_));
983 auto end = ObjectSlot(ToUintPtr(&unsharedConstpools_[GetUnsharedConstpoolsArrayLen() - 1]) +
984 JSTaggedValue::TaggedTypeSize());
985 v.VisitRangeRoot(Root::ROOT_VM, start, end);
986 }
987
IterateHandle(RootVisitor & visitor)988 size_t EcmaContext::IterateHandle(RootVisitor &visitor)
989 {
990 // EnableGlobalLeakCheck.
991 size_t handleCount = 0;
992 if (currentHandleStorageIndex_ != -1) {
993 int32_t nid = currentHandleStorageIndex_;
994 for (int32_t i = 0; i <= nid; ++i) {
995 auto node = handleStorageNodes_.at(i);
996 auto start = node->data();
997 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
998 visitor.VisitRangeRoot(Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
999 handleCount += (ToUintPtr(end) - ToUintPtr(start)) / sizeof(JSTaggedType);
1000 }
1001 }
1002 return handleCount;
1003 }
1004
ExpandHandleStorage()1005 uintptr_t *EcmaContext::ExpandHandleStorage()
1006 {
1007 uintptr_t *result = nullptr;
1008 int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size()) - 1;
1009 if (currentHandleStorageIndex_ == lastIndex) {
1010 auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
1011 handleStorageNodes_.push_back(n);
1012 currentHandleStorageIndex_++;
1013 result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
1014 handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
1015 } else {
1016 currentHandleStorageIndex_++;
1017 auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
1018 result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
1019 handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
1020 }
1021
1022 return result;
1023 }
1024
ShrinkHandleStorage(int prevIndex)1025 void EcmaContext::ShrinkHandleStorage(int prevIndex)
1026 {
1027 currentHandleStorageIndex_ = prevIndex;
1028 int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size()) - 1;
1029 #if ECMASCRIPT_ENABLE_ZAP_MEM
1030 uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
1031 if (currentHandleStorageIndex_ != -1) {
1032 if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
1033 LOG_FULL(FATAL) << "memset_s failed";
1034 UNREACHABLE();
1035 }
1036 }
1037 for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
1038 if (memset_s(handleStorageNodes_[i],
1039 NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
1040 NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
1041 EOK) {
1042 LOG_FULL(FATAL) << "memset_s failed";
1043 UNREACHABLE();
1044 }
1045 }
1046 #endif
1047
1048 if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
1049 for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
1050 auto node = handleStorageNodes_.back();
1051 delete node;
1052 handleStorageNodes_.pop_back();
1053 }
1054 }
1055 }
1056
ExpandPrimitiveStorage()1057 uintptr_t *EcmaContext::ExpandPrimitiveStorage()
1058 {
1059 uintptr_t *result = nullptr;
1060 int32_t lastIndex = static_cast<int32_t>(primitiveStorageNodes_.size()) - 1;
1061 if (currentPrimitiveStorageIndex_ == lastIndex) {
1062 auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
1063 primitiveStorageNodes_.push_back(n);
1064 currentPrimitiveStorageIndex_++;
1065 result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
1066 primitiveScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
1067 } else {
1068 currentPrimitiveStorageIndex_++;
1069 auto lastNode = primitiveStorageNodes_[currentPrimitiveStorageIndex_];
1070 result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
1071 primitiveScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
1072 }
1073
1074 return result;
1075 }
1076
ShrinkPrimitiveStorage(int prevIndex)1077 void EcmaContext::ShrinkPrimitiveStorage(int prevIndex)
1078 {
1079 currentPrimitiveStorageIndex_ = prevIndex;
1080 int32_t lastIndex = static_cast<int32_t>(primitiveStorageNodes_.size()) - 1;
1081 #if ECMASCRIPT_ENABLE_ZAP_MEM
1082 uintptr_t size = ToUintPtr(primitiveScopeStorageEnd_) - ToUintPtr(primitiveScopeStorageNext_);
1083 if (currentPrimitiveStorageIndex_ != -1) {
1084 if (memset_s(primitiveScopeStorageNext_, size, 0, size) != EOK) {
1085 LOG_FULL(FATAL) << "memset_s failed";
1086 UNREACHABLE();
1087 }
1088 }
1089 for (int32_t i = currentPrimitiveStorageIndex_ + 1; i < lastIndex; i++) {
1090 if (memset_s(primitiveStorageNodes_[i],
1091 NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
1092 NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
1093 EOK) {
1094 LOG_FULL(FATAL) << "memset_s failed";
1095 UNREACHABLE();
1096 }
1097 }
1098 #endif
1099
1100 if (lastIndex > MIN_PRIMITIVE_STORAGE_SIZE && currentPrimitiveStorageIndex_ < MIN_PRIMITIVE_STORAGE_SIZE) {
1101 for (int i = MIN_PRIMITIVE_STORAGE_SIZE; i < lastIndex; i++) {
1102 auto node = primitiveStorageNodes_.back();
1103 delete node;
1104 primitiveStorageNodes_.pop_back();
1105 }
1106 }
1107 }
1108
LoadStubFile()1109 void EcmaContext::LoadStubFile()
1110 {
1111 std::string stubFile = "";
1112 if (vm_->GetJSOptions().WasStubFileSet()) {
1113 stubFile = vm_->GetJSOptions().GetStubFile();
1114 }
1115 aotFileManager_->LoadStubFile(stubFile);
1116 }
1117
LoadAOTFilesInternal(const std::string & aotFileName)1118 bool EcmaContext::LoadAOTFilesInternal(const std::string& aotFileName)
1119 {
1120 #ifdef AOT_ESCAPE_ENABLE
1121 std::string bundleName = pgo::PGOProfilerManager::GetInstance()->GetBundleName();
1122 if (AotCrashInfo::GetInstance().IsAotEscapedOrNotInEnableList(vm_, bundleName)) {
1123 return false;
1124 }
1125 #endif
1126 std::string anFile = aotFileName + AOTFileManager::FILE_EXTENSION_AN;
1127 if (!aotFileManager_->LoadAnFile(anFile)) {
1128 LOG_ECMA(WARN) << "Load " << anFile << " failed. Destroy aot data and rollback to interpreter";
1129 ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
1130 return false;
1131 }
1132
1133 std::string aiFile = aotFileName + AOTFileManager::FILE_EXTENSION_AI;
1134 if (!aotFileManager_->LoadAiFile(aiFile)) {
1135 LOG_ECMA(WARN) << "Load " << aiFile << " failed. Destroy aot data and rollback to interpreter";
1136 ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
1137 return false;
1138 }
1139 return true;
1140 }
1141
LoadAOTFiles(const std::string & aotFileName)1142 bool EcmaContext::LoadAOTFiles(const std::string& aotFileName)
1143 {
1144 return LoadAOTFilesInternal(aotFileName);
1145 }
1146
1147 #if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
LoadAOTFiles(const std::string & aotFileName,std::function<bool (std::string fileName,uint8_t ** buff,size_t * buffSize)> cb)1148 bool EcmaContext::LoadAOTFiles(const std::string& aotFileName,
1149 std::function<bool(std::string fileName, uint8_t **buff, size_t *buffSize)> cb)
1150 {
1151 aotFileManager_->SetJsAotReader(cb);
1152 return LoadAOTFilesInternal(aotFileName);
1153 }
1154 #endif
1155
PrintOptStat()1156 void EcmaContext::PrintOptStat()
1157 {
1158 if (optCodeProfiler_ != nullptr) {
1159 optCodeProfiler_->PrintAndReset();
1160 }
1161 }
1162
DumpAOTInfo() const1163 void EcmaContext::DumpAOTInfo() const
1164 {
1165 aotFileManager_->DumpAOTInfo();
1166 }
1167
JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)1168 bool EcmaContext::JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)
1169 {
1170 if (JSTaggedValue::SameValue(joinStack_[0], JSTaggedValue::Hole())) {
1171 joinStack_[0] = receiver.GetTaggedValue();
1172 return true;
1173 }
1174 return JoinStackPush(receiver);
1175 }
1176
JoinStackPush(JSHandle<JSTaggedValue> receiver)1177 bool EcmaContext::JoinStackPush(JSHandle<JSTaggedValue> receiver)
1178 {
1179 uint32_t capacity = joinStack_.size();
1180 JSTaggedValue receiverValue = receiver.GetTaggedValue();
1181 for (size_t i = 0; i < capacity; ++i) {
1182 if (JSTaggedValue::SameValue(joinStack_[i], JSTaggedValue::Hole())) {
1183 joinStack_[i] = receiverValue;
1184 return true;
1185 }
1186 if (JSTaggedValue::SameValue(joinStack_[i], receiverValue)) {
1187 return false;
1188 }
1189 }
1190 joinStack_.emplace_back(receiverValue);
1191 return true;
1192 }
1193
JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)1194 void EcmaContext::JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)
1195 {
1196 uint32_t length = joinStack_.size();
1197 if (JSTaggedValue::SameValue(joinStack_[0], receiver.GetTaggedValue()) && length == MIN_JOIN_STACK_SIZE) {
1198 joinStack_[0] = JSTaggedValue::Hole();
1199 } else {
1200 JoinStackPop(receiver);
1201 }
1202 }
1203
JoinStackPop(JSHandle<JSTaggedValue> receiver)1204 void EcmaContext::JoinStackPop(JSHandle<JSTaggedValue> receiver)
1205 {
1206 uint32_t length = joinStack_.size();
1207 for (size_t i = 0; i < length; ++i) {
1208 if (JSTaggedValue::SameValue(joinStack_[i], receiver.GetTaggedValue())) {
1209 if (i == 0 && length > MIN_JOIN_STACK_SIZE) {
1210 joinStack_ = {JSTaggedValue::Hole(), JSTaggedValue::Hole()};
1211 break;
1212 } else {
1213 joinStack_[i] = JSTaggedValue::Hole();
1214 break;
1215 }
1216 }
1217 }
1218 }
1219
CalCallSiteInfo(uintptr_t retAddr,bool isDeopt) const1220 std::tuple<uint64_t, uint8_t *, int, kungfu::CalleeRegAndOffsetVec> EcmaContext::CalCallSiteInfo(
1221 uintptr_t retAddr, bool isDeopt) const
1222 {
1223 auto loader = aotFileManager_;
1224 return loader->CalCallSiteInfo(retAddr, isDeopt);
1225 }
1226
AddSustainingJSHandle(SustainingJSHandle * sustainingHandle)1227 void EcmaContext::AddSustainingJSHandle(SustainingJSHandle *sustainingHandle)
1228 {
1229 if (sustainingJSHandleList_) {
1230 sustainingJSHandleList_->AddSustainingJSHandle(sustainingHandle);
1231 }
1232 }
1233
RemoveSustainingJSHandle(SustainingJSHandle * sustainingHandle)1234 void EcmaContext::RemoveSustainingJSHandle(SustainingJSHandle *sustainingHandle)
1235 {
1236 if (sustainingJSHandleList_) {
1237 sustainingJSHandleList_->RemoveSustainingJSHandle(sustainingHandle);
1238 }
1239 }
1240
ClearKeptObjects()1241 void EcmaContext::ClearKeptObjects()
1242 {
1243 if (LIKELY(GetGlobalEnv()->GetTaggedWeakRefKeepObjects().IsUndefined())) {
1244 return;
1245 }
1246 GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined());
1247 }
1248
AddToKeptObjects(JSHandle<JSTaggedValue> value)1249 void EcmaContext::AddToKeptObjects(JSHandle<JSTaggedValue> value)
1250 {
1251 if (value->IsInSharedHeap()) {
1252 return;
1253 }
1254
1255 JSHandle<GlobalEnv> globalEnv = GetGlobalEnv();
1256 JSHandle<LinkedHashSet> linkedSet;
1257 if (globalEnv->GetWeakRefKeepObjects()->IsUndefined()) {
1258 linkedSet = LinkedHashSet::Create(thread_);
1259 } else {
1260 linkedSet = JSHandle<LinkedHashSet>(thread_,
1261 LinkedHashSet::Cast(globalEnv->GetWeakRefKeepObjects()->GetTaggedObject()));
1262 }
1263 linkedSet = LinkedHashSet::Add(thread_, linkedSet, value);
1264 globalEnv->SetWeakRefKeepObjects(thread_, linkedSet);
1265 }
1266 } // namespace panda::ecmascript
1267