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