mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-08 00:24:00 +00:00
25f6530062
1. Collect method id of calltarget when call js function 2. Load gt by method id from pgo 3. Set call insts' calltarget type 4. Fix process method bug 5. Support pgo-aot testcases Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I7NMOO?from=project-issue Signed-off-by: zhangyukun8 <zhangyukun8@huawei.com> Change-Id: Ib7e92ab1b529195367af16f923c2e620f40f8a36
840 lines
31 KiB
C++
840 lines
31 KiB
C++
/*
|
|
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "ecmascript/ecma_context.h"
|
|
|
|
#include "ecmascript/base/path_helper.h"
|
|
#include "ecmascript/builtins/builtins.h"
|
|
#include "ecmascript/builtins/builtins_global.h"
|
|
#include "ecmascript/builtins/builtins_regexp.h"
|
|
#include "ecmascript/compiler/aot_file/an_file_data_manager.h"
|
|
#include "ecmascript/compiler/common_stubs.h"
|
|
#include "ecmascript/ecma_string_table.h"
|
|
#include "ecmascript/ecma_vm.h"
|
|
#include "ecmascript/global_env.h"
|
|
#include "ecmascript/global_env_constants-inl.h"
|
|
#include "ecmascript/interpreter/interpreter-inl.h"
|
|
#include "ecmascript/jobs/micro_job_queue.h"
|
|
#include "ecmascript/jspandafile/js_pandafile.h"
|
|
#include "ecmascript/jspandafile/js_pandafile_manager.h"
|
|
#include "ecmascript/jspandafile/program_object.h"
|
|
#include "ecmascript/js_function.h"
|
|
#include "ecmascript/js_thread.h"
|
|
#include "ecmascript/module/module_path_helper.h"
|
|
#include "ecmascript/object_factory.h"
|
|
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
|
|
#include "ecmascript/require/js_cjs_module_cache.h"
|
|
#include "ecmascript/require/js_require_manager.h"
|
|
#include "ecmascript/snapshot/mem/snapshot.h"
|
|
|
|
namespace panda::ecmascript {
|
|
using PathHelper = base::PathHelper;
|
|
|
|
EcmaContext::EcmaContext(JSThread *thread)
|
|
: thread_(thread),
|
|
vm_(thread->GetEcmaVM()),
|
|
factory_(vm_->GetFactory())
|
|
{
|
|
}
|
|
|
|
/* static */
|
|
EcmaContext *EcmaContext::Create(JSThread *thread)
|
|
{
|
|
LOG_ECMA(INFO) << "EcmaContext::Create";
|
|
auto context = new EcmaContext(thread);
|
|
if (UNLIKELY(context == nullptr)) {
|
|
LOG_ECMA(ERROR) << "Failed to create ecma context";
|
|
return nullptr;
|
|
}
|
|
return context;
|
|
}
|
|
|
|
// static
|
|
bool EcmaContext::Destroy(EcmaContext *context)
|
|
{
|
|
if (context != nullptr) {
|
|
delete context;
|
|
context = nullptr;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool EcmaContext::Initialize()
|
|
{
|
|
LOG_ECMA(DEBUG) << "EcmaContext::Initialize";
|
|
ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "EcmaContext::Initialize");
|
|
[[maybe_unused]] EcmaHandleScope scope(thread_);
|
|
aotFileManager_ = new AOTFileManager(vm_);
|
|
propertiesCache_ = new PropertiesCache();
|
|
regExpParserCache_ = new RegExpParserCache();
|
|
|
|
JSHandle<JSHClass> hClassHandle = factory_->InitClassClass();
|
|
|
|
JSHandle<JSHClass> globalEnvClass = factory_->NewEcmaHClass(
|
|
*hClassHandle,
|
|
GlobalEnv::SIZE,
|
|
JSType::GLOBAL_ENV);
|
|
thread_->SetGlobalConst(&globalConst_);
|
|
globalConst_.Init(thread_, *hClassHandle);
|
|
|
|
JSHandle<GlobalEnv> globalEnv = factory_->NewGlobalEnv(*globalEnvClass);
|
|
globalEnv->Init(thread_);
|
|
globalEnv_ = globalEnv.GetTaggedValue();
|
|
Builtins builtins;
|
|
bool builtinsLazyEnabled = vm_->GetJSOptions().IsWorker() && vm_->GetJSOptions().GetEnableBuiltinsLazy();
|
|
builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled);
|
|
|
|
SetupRegExpResultCache();
|
|
microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
|
|
moduleManager_ = new ModuleManager(vm_);
|
|
tsManager_ = new TSManager(vm_);
|
|
optCodeProfiler_ = new OptCodeProfiler();
|
|
initialized_ = true;
|
|
return true;
|
|
}
|
|
|
|
void EcmaContext::InitializeEcmaScriptRunStat()
|
|
{
|
|
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
|
static const char *runtimeCallerNames[] = {
|
|
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
|
#define INTERPRETER_CALLER_NAME(name) "Interpreter::" #name,
|
|
INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME) // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
|
|
#undef INTERPRETER_CALLER_NAME
|
|
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
|
#define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name,
|
|
BUILTINS_API_LIST(BUILTINS_API_NAME)
|
|
#undef BUILTINS_API_NAME
|
|
#define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name,
|
|
ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME)
|
|
#undef ABSTRACT_OPERATION_NAME
|
|
#define MEM_ALLOCATE_AND_GC_NAME(name) "Memory::" #name,
|
|
MEM_ALLOCATE_AND_GC_LIST(MEM_ALLOCATE_AND_GC_NAME)
|
|
#undef MEM_ALLOCATE_AND_GC_NAME
|
|
#define DEF_RUNTIME_ID(name) "Runtime::" #name,
|
|
RUNTIME_STUB_WITH_GC_LIST(DEF_RUNTIME_ID)
|
|
#undef DEF_RUNTIME_ID
|
|
};
|
|
static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER,
|
|
"Invalid runtime caller number");
|
|
runtimeStat_ = vm_->GetChunk()->New<EcmaRuntimeStat>(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER);
|
|
if (UNLIKELY(runtimeStat_ == nullptr)) {
|
|
LOG_FULL(FATAL) << "alloc runtimeStat_ failed";
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void EcmaContext::SetRuntimeStatEnable(bool flag)
|
|
{
|
|
static uint64_t start = 0;
|
|
if (flag) {
|
|
start = PandaRuntimeTimer::Now();
|
|
if (runtimeStat_ == nullptr) {
|
|
InitializeEcmaScriptRunStat();
|
|
}
|
|
} else {
|
|
LOG_ECMA(INFO) << "Runtime State duration:" << PandaRuntimeTimer::Now() - start << "(ns)";
|
|
if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
|
|
runtimeStat_->Print();
|
|
runtimeStat_->ResetAllCount();
|
|
}
|
|
}
|
|
if (runtimeStat_ != nullptr) {
|
|
runtimeStat_->SetRuntimeStatEnabled(flag);
|
|
}
|
|
}
|
|
|
|
EcmaContext::~EcmaContext()
|
|
{
|
|
if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
|
|
runtimeStat_->Print();
|
|
}
|
|
for (auto n : handleStorageNodes_) {
|
|
delete n;
|
|
}
|
|
handleStorageNodes_.clear();
|
|
currentHandleStorageIndex_ = -1;
|
|
handleScopeCount_ = 0;
|
|
handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
|
|
ClearBufferData();
|
|
// clear c_address: c++ pointer delete
|
|
if (!vm_->IsBundlePack()) {
|
|
std::shared_ptr<JSPandaFile> jsPandaFile =
|
|
JSPandaFileManager::GetInstance()->FindJSPandaFile(vm_->GetAssetPath());
|
|
if (jsPandaFile != nullptr) {
|
|
jsPandaFile->DeleteParsedConstpoolVM(vm_);
|
|
}
|
|
}
|
|
// clear icu cache
|
|
ClearIcuCache();
|
|
|
|
if (runtimeStat_ != nullptr) {
|
|
vm_->GetChunk()->Delete(runtimeStat_);
|
|
runtimeStat_ = nullptr;
|
|
}
|
|
if (optCodeProfiler_ != nullptr) {
|
|
delete optCodeProfiler_;
|
|
optCodeProfiler_ = nullptr;
|
|
}
|
|
if (moduleManager_ != nullptr) {
|
|
delete moduleManager_;
|
|
moduleManager_ = nullptr;
|
|
}
|
|
if (tsManager_ != nullptr) {
|
|
delete tsManager_;
|
|
tsManager_ = nullptr;
|
|
}
|
|
if (regExpParserCache_ != nullptr) {
|
|
delete regExpParserCache_;
|
|
regExpParserCache_ = nullptr;
|
|
}
|
|
if (aotFileManager_ != nullptr) {
|
|
delete aotFileManager_;
|
|
aotFileManager_ = nullptr;
|
|
}
|
|
if (propertiesCache_ != nullptr) {
|
|
delete propertiesCache_;
|
|
propertiesCache_ = nullptr;
|
|
}
|
|
// clear join stack
|
|
joinStack_.clear();
|
|
}
|
|
|
|
JSTaggedValue EcmaContext::InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg,
|
|
const JSPandaFile *jsPandaFile, std::string_view entryPoint,
|
|
CJSInfo* cjsInfo)
|
|
{
|
|
aotFileManager_->SetAOTMainFuncEntry(mainFunc, jsPandaFile, entryPoint);
|
|
return JSFunction::InvokeOptimizedEntrypoint(thread_, mainFunc, thisArg, entryPoint, cjsInfo);
|
|
}
|
|
|
|
JSTaggedValue EcmaContext::ExecuteAot(size_t actualNumArgs, JSTaggedType *args,
|
|
const JSTaggedType *prevFp, bool needPushUndefined)
|
|
{
|
|
INTERPRETER_TRACE(thread_, ExecuteAot);
|
|
auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_JSFunctionEntry);
|
|
// do not modify this log to INFO, this will call many times
|
|
LOG_ECMA(DEBUG) << "start to execute aot entry: " << (void*)entry;
|
|
auto res = reinterpret_cast<JSFunctionEntryType>(entry)(thread_->GetGlueAddr(),
|
|
actualNumArgs,
|
|
args,
|
|
reinterpret_cast<uintptr_t>(prevFp),
|
|
needPushUndefined);
|
|
return res;
|
|
}
|
|
|
|
Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
|
|
std::string_view entryPoint, bool excuteFromJob)
|
|
{
|
|
[[maybe_unused]] EcmaHandleScope scope(thread_);
|
|
JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
|
|
if (program.IsEmpty()) {
|
|
LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
|
|
return Unexpected(false);
|
|
}
|
|
// for debugger
|
|
vm_->GetJsDebuggerManager()->GetNotificationManager()->LoadModuleEvent(
|
|
jsPandaFile->GetJSPandaFileDesc(), entryPoint);
|
|
|
|
JSHandle<JSFunction> func(thread_, program->GetMainFunction());
|
|
JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
|
|
JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
|
|
CString entry = entryPoint.data();
|
|
JSRecordInfo recordInfo;
|
|
bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
|
|
if (!hasRecord) {
|
|
CString msg = "cannot find record '" + entry + "', please check the request path.";
|
|
LOG_FULL(ERROR) << msg;
|
|
THROW_REFERENCE_ERROR_AND_RETURN(thread_, msg.c_str(), Unexpected(false));
|
|
}
|
|
if (jsPandaFile->IsModule(recordInfo)) {
|
|
global = undefined;
|
|
CString moduleName = jsPandaFile->GetJSPandaFileDesc();
|
|
if (!jsPandaFile->IsBundlePack()) {
|
|
moduleName = entry;
|
|
}
|
|
JSHandle<SourceTextModule> module = moduleManager_->HostGetImportedModule(moduleName);
|
|
func->SetModule(thread_, module);
|
|
} else {
|
|
// if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName,
|
|
// which can avoid the problem of larger memory caused by the new slot
|
|
JSHandle<EcmaString> recordName = factory_->NewFromUtf8(entry);
|
|
func->SetModule(thread_, recordName);
|
|
}
|
|
vm_->CheckStartCpuProfiler();
|
|
|
|
JSTaggedValue result;
|
|
if (jsPandaFile->IsCjs(recordInfo)) {
|
|
CJSExecution(func, global, jsPandaFile, entryPoint);
|
|
} else {
|
|
if (aotFileManager_->IsLoadMain(jsPandaFile, entry)) {
|
|
EcmaRuntimeStatScope runtimeStatScope(vm_);
|
|
result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint);
|
|
} else {
|
|
if (thread_->IsPGOProfilerEnable()) {
|
|
vm_->GetPGOProfiler()->ProfileCall(JSTaggedValue::VALUE_UNDEFINED, func.GetTaggedType());
|
|
}
|
|
EcmaRuntimeCallInfo *info =
|
|
EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
|
|
EcmaRuntimeStatScope runtimeStatScope(vm_);
|
|
EcmaInterpreter::Execute(info);
|
|
}
|
|
}
|
|
if (!thread_->HasPendingException()) {
|
|
job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
|
|
}
|
|
|
|
// print exception information
|
|
if (!excuteFromJob && thread_->HasPendingException()) {
|
|
HandleUncaughtException(thread_->GetException());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void EcmaContext::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
|
|
const JSPandaFile *jsPandaFile, std::string_view entryPoint)
|
|
{
|
|
// create "module", "exports", "require", "filename", "dirname"
|
|
JSHandle<CjsModule> module = factory_->NewCjsModule();
|
|
JSHandle<JSTaggedValue> require = GetGlobalEnv()->GetCjsRequireFunction();
|
|
JSHandle<CjsExports> exports = factory_->NewCjsExports();
|
|
JSMutableHandle<JSTaggedValue> filename(thread_, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> dirname(thread_, JSTaggedValue::Undefined());
|
|
if (jsPandaFile->IsBundlePack()) {
|
|
ModulePathHelper::ResolveCurrentPath(thread_, dirname, filename, jsPandaFile);
|
|
} else {
|
|
filename.Update(func->GetModule());
|
|
ASSERT(filename->IsString());
|
|
CString fullName = ConvertToString(filename.GetTaggedValue());
|
|
dirname.Update(PathHelper::ResolveDirPath(thread_, fullName));
|
|
}
|
|
CJSInfo cjsInfo(module, require, exports, filename, dirname);
|
|
RequireManager::InitializeCommonJS(thread_, cjsInfo);
|
|
if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) {
|
|
EcmaRuntimeStatScope runtimeStateScope(vm_);
|
|
InvokeEcmaAotEntrypoint(func, thisArg, jsPandaFile, entryPoint, &cjsInfo);
|
|
} else {
|
|
// Execute main function
|
|
JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info =
|
|
EcmaInterpreter::NewRuntimeCallInfo(thread_,
|
|
JSHandle<JSTaggedValue>(func),
|
|
thisArg, undefined, 5); // 5 : argument numbers
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
if (info == nullptr) {
|
|
LOG_ECMA(ERROR) << "CJSExecution Stack overflow!";
|
|
return;
|
|
}
|
|
info->SetCallArg(cjsInfo.exportsHdl.GetTaggedValue(),
|
|
cjsInfo.requireHdl.GetTaggedValue(),
|
|
cjsInfo.moduleHdl.GetTaggedValue(),
|
|
cjsInfo.filenameHdl.GetTaggedValue(),
|
|
cjsInfo.dirnameHdl.GetTaggedValue());
|
|
EcmaRuntimeStatScope runtimeStatScope(vm_);
|
|
EcmaInterpreter::Execute(info);
|
|
}
|
|
if (!thread_->HasPendingException()) {
|
|
job::MicroJobQueue::ExecutePendingJob(thread_, thread_->GetCurrentEcmaContext()->GetMicroJobQueue());
|
|
}
|
|
|
|
if (!thread_->HasPendingException()) {
|
|
// Collecting module.exports : exports ---> module.exports --->Module._cache
|
|
RequireManager::CollectExecutedExp(thread_, cjsInfo);
|
|
}
|
|
}
|
|
|
|
bool EcmaContext::HasCachedConstpool(const JSPandaFile *jsPandaFile) const
|
|
{
|
|
return cachedConstpools_.find(jsPandaFile) != cachedConstpools_.end();
|
|
}
|
|
|
|
JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
|
|
{
|
|
auto iter = cachedConstpools_.find(jsPandaFile);
|
|
if (iter == cachedConstpools_.end()) {
|
|
return JSTaggedValue::Hole();
|
|
}
|
|
auto constpoolIter = iter->second.find(index);
|
|
if (constpoolIter == iter->second.end()) {
|
|
return JSTaggedValue::Hole();
|
|
}
|
|
return constpoolIter->second;
|
|
}
|
|
|
|
std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> EcmaContext::FindConstpools(
|
|
const JSPandaFile *jsPandaFile)
|
|
{
|
|
auto iter = cachedConstpools_.find(jsPandaFile);
|
|
if (iter == cachedConstpools_.end()) {
|
|
return std::nullopt;
|
|
}
|
|
return iter->second;
|
|
}
|
|
|
|
// For new version instruction.
|
|
JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
|
|
{
|
|
panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
|
|
int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
|
|
return FindConstpool(jsPandaFile, index);
|
|
}
|
|
|
|
JSHandle<ConstantPool> EcmaContext::FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
|
|
{
|
|
panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
|
|
int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
|
|
JSTaggedValue constpool = FindConstpool(jsPandaFile, index);
|
|
if (constpool.IsHole()) {
|
|
JSHandle<ConstantPool> newConstpool = ConstantPool::CreateConstPool(vm_, jsPandaFile, id);
|
|
AddConstpool(jsPandaFile, newConstpool.GetTaggedValue(), index);
|
|
return newConstpool;
|
|
}
|
|
|
|
return JSHandle<ConstantPool>(thread_, constpool);
|
|
}
|
|
|
|
void EcmaContext::CreateAllConstpool(const JSPandaFile *jsPandaFile)
|
|
{
|
|
auto headers = jsPandaFile->GetPandaFile()->GetIndexHeaders();
|
|
uint32_t index = 0;
|
|
for (const auto &header : headers) {
|
|
auto constpoolSize = header.method_idx_size;
|
|
JSHandle<ConstantPool> constpool = factory_->NewConstantPool(constpoolSize);
|
|
constpool->SetJSPandaFile(jsPandaFile);
|
|
constpool->SetIndexHeader(&header);
|
|
AddConstpool(jsPandaFile, constpool.GetTaggedValue(), index++);
|
|
}
|
|
}
|
|
|
|
void EcmaContext::AddConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool, int32_t index)
|
|
{
|
|
ASSERT(constpool.IsConstantPool());
|
|
if (cachedConstpools_.find(jsPandaFile) == cachedConstpools_.end()) {
|
|
cachedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
|
|
}
|
|
auto &constpoolMap = cachedConstpools_[jsPandaFile];
|
|
ASSERT(constpoolMap.find(index) == constpoolMap.end());
|
|
constpoolMap.insert({index, constpool});
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> EcmaContext::GetAndClearEcmaUncaughtException() const
|
|
{
|
|
JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
|
|
thread_->ClearException(); // clear for ohos app
|
|
return exceptionHandle;
|
|
}
|
|
|
|
void EcmaContext::ProcessNativeDelete(const WeakRootVisitor &visitor)
|
|
{
|
|
auto iterator = cachedConstpools_.begin();
|
|
while (iterator != cachedConstpools_.end()) {
|
|
auto &constpools = iterator->second;
|
|
auto constpoolIter = constpools.begin();
|
|
while (constpoolIter != constpools.end()) {
|
|
JSTaggedValue constpoolVal = constpoolIter->second;
|
|
if (constpoolVal.IsHeapObject()) {
|
|
TaggedObject *obj = constpoolVal.GetTaggedObject();
|
|
auto fwd = visitor(obj);
|
|
if (fwd == nullptr) {
|
|
constpoolIter = constpools.erase(constpoolIter);
|
|
continue;
|
|
}
|
|
}
|
|
++constpoolIter;
|
|
}
|
|
if (constpools.size() == 0) {
|
|
LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
|
|
JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iterator->first);
|
|
iterator = cachedConstpools_.erase(iterator);
|
|
} else {
|
|
++iterator;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EcmaContext::ProcessReferences(const WeakRootVisitor &visitor)
|
|
{
|
|
auto iterator = cachedConstpools_.begin();
|
|
while (iterator != cachedConstpools_.end()) {
|
|
auto &constpools = iterator->second;
|
|
auto constpoolIter = constpools.begin();
|
|
while (constpoolIter != constpools.end()) {
|
|
JSTaggedValue constpoolVal = constpoolIter->second;
|
|
if (constpoolVal.IsHeapObject()) {
|
|
TaggedObject *obj = constpoolVal.GetTaggedObject();
|
|
auto fwd = visitor(obj);
|
|
if (fwd == nullptr) {
|
|
constpoolIter = constpools.erase(constpoolIter);
|
|
continue;
|
|
} else if (fwd != obj) {
|
|
constpoolIter->second = JSTaggedValue(fwd);
|
|
}
|
|
}
|
|
++constpoolIter;
|
|
}
|
|
if (constpools.size() == 0) {
|
|
LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
|
|
JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iterator->first);
|
|
iterator = cachedConstpools_.erase(iterator);
|
|
} else {
|
|
++iterator;
|
|
}
|
|
}
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> EcmaContext::GetEcmaUncaughtException() const
|
|
{
|
|
if (!thread_->HasPendingException()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
|
|
return exceptionHandle;
|
|
}
|
|
|
|
void EcmaContext::EnableUserUncaughtErrorHandler()
|
|
{
|
|
isUncaughtExceptionRegistered_ = true;
|
|
}
|
|
|
|
void EcmaContext::HandleUncaughtException(JSTaggedValue exception)
|
|
{
|
|
if (isUncaughtExceptionRegistered_) {
|
|
return;
|
|
}
|
|
[[maybe_unused]] EcmaHandleScope handleScope(thread_);
|
|
JSHandle<JSTaggedValue> exceptionHandle(thread_, exception);
|
|
// if caught exceptionHandle type is JSError
|
|
thread_->ClearException();
|
|
if (exceptionHandle->IsJSError()) {
|
|
PrintJSErrorInfo(thread_, exceptionHandle);
|
|
return;
|
|
}
|
|
JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
|
|
CString string = ConvertToString(*result);
|
|
LOG_NO_TAG(ERROR) << string;
|
|
}
|
|
|
|
// static
|
|
void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> &exceptionInfo)
|
|
{
|
|
JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
|
|
JSHandle<JSTaggedValue> nameValue = JSObject::GetProperty(thread, exceptionInfo, nameKey).GetValue();
|
|
JSHandle<EcmaString> name = JSTaggedValue::ToString(thread, nameValue);
|
|
// JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
|
|
if (thread->HasPendingException()) {
|
|
thread->ClearException();
|
|
name = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
|
|
}
|
|
JSHandle<JSTaggedValue> msgKey = thread->GlobalConstants()->GetHandledMessageString();
|
|
JSHandle<JSTaggedValue> msgValue = JSObject::GetProperty(thread, exceptionInfo, msgKey).GetValue();
|
|
JSHandle<EcmaString> msg = JSTaggedValue::ToString(thread, msgValue);
|
|
// JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
|
|
if (thread->HasPendingException()) {
|
|
thread->ClearException();
|
|
msg = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
|
|
}
|
|
JSHandle<JSTaggedValue> stackKey = thread->GlobalConstants()->GetHandledStackString();
|
|
JSHandle<JSTaggedValue> stackValue = JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue();
|
|
JSHandle<EcmaString> stack = JSTaggedValue::ToString(thread, stackValue);
|
|
// JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
|
|
if (thread->HasPendingException()) {
|
|
thread->ClearException();
|
|
stack = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
|
|
}
|
|
|
|
CString nameBuffer = ConvertToString(*name);
|
|
CString msgBuffer = ConvertToString(*msg);
|
|
CString stackBuffer = ConvertToString(*stack);
|
|
LOG_NO_TAG(ERROR) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer;
|
|
}
|
|
|
|
bool EcmaContext::ExecutePromisePendingJob()
|
|
{
|
|
if (isProcessingPendingJob_) {
|
|
LOG_ECMA(DEBUG) << "EcmaVM::ExecutePromisePendingJob can not reentrant";
|
|
return false;
|
|
}
|
|
if (!thread_->HasPendingException()) {
|
|
isProcessingPendingJob_ = true;
|
|
job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
|
|
isProcessingPendingJob_ = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void EcmaContext::ClearBufferData()
|
|
{
|
|
auto iter = cachedConstpools_.begin();
|
|
while (iter != cachedConstpools_.end()) {
|
|
LOG_ECMA(INFO) << "remove js pandafile by vm destruct, file:" << iter->first->GetJSPandaFileDesc();
|
|
JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iter->first);
|
|
iter++;
|
|
}
|
|
cachedConstpools_.clear();
|
|
}
|
|
|
|
void EcmaContext::SetGlobalEnv(GlobalEnv *global)
|
|
{
|
|
ASSERT(global != nullptr);
|
|
globalEnv_ = JSTaggedValue(global);
|
|
}
|
|
|
|
void EcmaContext::SetMicroJobQueue(job::MicroJobQueue *queue)
|
|
{
|
|
ASSERT(queue != nullptr);
|
|
microJobQueue_ = JSTaggedValue(queue);
|
|
}
|
|
|
|
JSHandle<GlobalEnv> EcmaContext::GetGlobalEnv() const
|
|
{
|
|
return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
|
|
}
|
|
|
|
JSHandle<job::MicroJobQueue> EcmaContext::GetMicroJobQueue() const
|
|
{
|
|
return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(µJobQueue_));
|
|
}
|
|
|
|
void EcmaContext::MountContext(JSThread *thread)
|
|
{
|
|
EcmaContext *context = EcmaContext::CreateAndInitialize(thread);
|
|
thread->SwitchCurrentContext(context);
|
|
}
|
|
|
|
void EcmaContext::UnmountContext(JSThread *thread)
|
|
{
|
|
EcmaContext *context = thread->GetCurrentEcmaContext();
|
|
thread->PopContext();
|
|
Destroy(context);
|
|
}
|
|
|
|
EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread)
|
|
{
|
|
EcmaContext *context = EcmaContext::Create(thread);
|
|
thread->PushContext(context);
|
|
context->Initialize();
|
|
return context;
|
|
}
|
|
|
|
void EcmaContext::CheckAndDestroy(JSThread *thread, EcmaContext *context)
|
|
{
|
|
if (thread->EraseContext(context)) {
|
|
Destroy(context);
|
|
return;
|
|
}
|
|
LOG_ECMA(FATAL) << "CheckAndDestroy a nonexistent context.";
|
|
}
|
|
|
|
void EcmaContext::SetupRegExpResultCache()
|
|
{
|
|
regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
|
|
}
|
|
|
|
void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv)
|
|
{
|
|
// visit global Constant
|
|
globalConst_.VisitRangeSlot(rv);
|
|
|
|
v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
|
|
v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(®expCache_)));
|
|
v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(µJobQueue_)));
|
|
if (moduleManager_) {
|
|
moduleManager_->Iterate(v);
|
|
}
|
|
if (tsManager_) {
|
|
tsManager_->Iterate(v);
|
|
}
|
|
if (aotFileManager_) {
|
|
aotFileManager_->Iterate(v);
|
|
}
|
|
if (propertiesCache_ != nullptr) {
|
|
propertiesCache_->Clear();
|
|
}
|
|
if (!vm_->GetJSOptions().EnableGlobalLeakCheck() && currentHandleStorageIndex_ != -1) {
|
|
// IterateHandle when disableGlobalLeakCheck.
|
|
int32_t nid = currentHandleStorageIndex_;
|
|
for (int32_t i = 0; i <= nid; ++i) {
|
|
auto node = handleStorageNodes_.at(i);
|
|
auto start = node->data();
|
|
auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
|
|
rv(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
|
|
}
|
|
}
|
|
|
|
if (!joinStack_.empty()) {
|
|
rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&joinStack_.front())),
|
|
ObjectSlot(ToUintPtr(&joinStack_.back()) + JSTaggedValue::TaggedTypeSize()));
|
|
}
|
|
}
|
|
|
|
size_t EcmaContext::IterateHandle(const RootRangeVisitor &rangeVisitor)
|
|
{
|
|
// EnableGlobalLeakCheck.
|
|
size_t handleCount = 0;
|
|
if (currentHandleStorageIndex_ != -1) {
|
|
int32_t nid = currentHandleStorageIndex_;
|
|
for (int32_t i = 0; i <= nid; ++i) {
|
|
auto node = handleStorageNodes_.at(i);
|
|
auto start = node->data();
|
|
auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
|
|
rangeVisitor(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
|
|
handleCount += (ToUintPtr(end) - ToUintPtr(start)) / sizeof(JSTaggedType);
|
|
}
|
|
}
|
|
return handleCount;
|
|
}
|
|
|
|
uintptr_t *EcmaContext::ExpandHandleStorage()
|
|
{
|
|
uintptr_t *result = nullptr;
|
|
int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
|
|
if (currentHandleStorageIndex_ == lastIndex) {
|
|
auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
|
|
handleStorageNodes_.push_back(n);
|
|
currentHandleStorageIndex_++;
|
|
result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
|
|
handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
|
|
} else {
|
|
currentHandleStorageIndex_++;
|
|
auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
|
|
result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
|
|
handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void EcmaContext::ShrinkHandleStorage(int prevIndex)
|
|
{
|
|
currentHandleStorageIndex_ = prevIndex;
|
|
int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
|
|
#if ECMASCRIPT_ENABLE_ZAP_MEM
|
|
uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
|
|
if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
|
|
LOG_FULL(FATAL) << "memset_s failed";
|
|
UNREACHABLE();
|
|
}
|
|
for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
|
|
if (memset_s(handleStorageNodes_[i],
|
|
NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
|
|
NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
|
|
EOK) {
|
|
LOG_FULL(FATAL) << "memset_s failed";
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
|
|
for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
|
|
auto node = handleStorageNodes_.back();
|
|
delete node;
|
|
handleStorageNodes_.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
void EcmaContext::LoadStubFile()
|
|
{
|
|
std::string stubFile = vm_->GetJSOptions().GetStubFile();
|
|
aotFileManager_->LoadStubFile(stubFile);
|
|
}
|
|
|
|
bool EcmaContext::LoadAOTFiles(const std::string& aotFileName)
|
|
{
|
|
std::string anFile = aotFileName + AOTFileManager::FILE_EXTENSION_AN;
|
|
if (!aotFileManager_->LoadAnFile(anFile)) {
|
|
LOG_ECMA(ERROR) << "Load " << anFile << " failed. Destroy aot data and rollback to interpreter";
|
|
ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
|
|
return false;
|
|
}
|
|
|
|
std::string aiFile = aotFileName + AOTFileManager::FILE_EXTENSION_AI;
|
|
if (!aotFileManager_->LoadAiFile(aiFile)) {
|
|
LOG_ECMA(ERROR) << "Load " << aiFile << " failed. Destroy aot data and rollback to interpreter";
|
|
ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void EcmaContext::PrintOptStat()
|
|
{
|
|
if (optCodeProfiler_ != nullptr) {
|
|
optCodeProfiler_->PrintAndReset();
|
|
}
|
|
}
|
|
|
|
void EcmaContext::DumpAOTInfo() const
|
|
{
|
|
aotFileManager_->DumpAOTInfo();
|
|
}
|
|
|
|
bool EcmaContext::JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)
|
|
{
|
|
if (JSTaggedValue::SameValue(joinStack_[0], JSTaggedValue::Hole())) {
|
|
joinStack_[0] = receiver.GetTaggedValue();
|
|
return true;
|
|
}
|
|
return JoinStackPush(receiver);
|
|
}
|
|
|
|
bool EcmaContext::JoinStackPush(JSHandle<JSTaggedValue> receiver)
|
|
{
|
|
uint32_t capacity = joinStack_.size();
|
|
JSTaggedValue receiverValue = receiver.GetTaggedValue();
|
|
for (size_t i = 0; i < capacity; ++i) {
|
|
if (JSTaggedValue::SameValue(joinStack_[i], JSTaggedValue::Hole())) {
|
|
joinStack_[i] = receiverValue;
|
|
return true;
|
|
}
|
|
if (JSTaggedValue::SameValue(joinStack_[i], receiverValue)) {
|
|
return false;
|
|
}
|
|
}
|
|
joinStack_.emplace_back(receiverValue);
|
|
return true;
|
|
}
|
|
|
|
void EcmaContext::JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)
|
|
{
|
|
uint32_t length = joinStack_.size();
|
|
if (JSTaggedValue::SameValue(joinStack_[0], receiver.GetTaggedValue()) && length == MIN_JOIN_STACK_SIZE) {
|
|
joinStack_[0] = JSTaggedValue::Hole();
|
|
} else {
|
|
JoinStackPop(receiver);
|
|
}
|
|
}
|
|
|
|
void EcmaContext::JoinStackPop(JSHandle<JSTaggedValue> receiver)
|
|
{
|
|
uint32_t length = joinStack_.size();
|
|
for (size_t i = 0; i < length; ++i) {
|
|
if (JSTaggedValue::SameValue(joinStack_[i], receiver.GetTaggedValue())) {
|
|
if (i == 0 && length > MIN_JOIN_STACK_SIZE) {
|
|
joinStack_ = {JSTaggedValue::Hole(), JSTaggedValue::Hole()};
|
|
break;
|
|
} else {
|
|
joinStack_[i] = JSTaggedValue::Hole();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace panda::ecmascript
|