* Copyright (c) 2021-2026 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.
*/
#ifndef FOUNDATION_ACE_NAPI_NATIVE_ENGINE_IMPL_ARK_ARK_NATIVE_ENGINE_H
#define FOUNDATION_ACE_NAPI_NATIVE_ENGINE_IMPL_ARK_ARK_NATIVE_ENGINE_H
#include <memory>
#if !defined(PREVIEW) && !defined(IOS_PLATFORM)
#include <sys/wait.h>
#include <sys/types.h>
#endif
#include <iostream>
#include <map>
#include <mutex>
#include <regex>
#include <thread>
#include <unistd.h>
#include "ark_idle_monitor.h"
#include "ark_native_options.h"
#include "ecmascript/napi/include/dfx_jsnapi.h"
#include "ecmascript/napi/include/jsnapi.h"
#include "native_engine/impl/ark/ark_finalizers_pack.h"
#include "native_engine/native_engine.h"
namespace panda::ecmascript {
struct JsHeapDumpWork;
struct JsFrameInfo {
std::string functionName;
std::string fileName;
std::string packageName;
std::string pos;
};
struct ApiCheckContext {
NativeModuleManager* moduleManager;
EcmaVM* ecmaVm;
panda::Local<panda::StringRef>& moduleName;
panda::Local<panda::ObjectRef>& exportObj;
panda::EscapeLocalScope& scope;
};
}
using JsFrameInfo = panda::ecmascript::JsFrameInfo;
using DFXJSNApi = panda::DFXJSNApi;
using LocalScope = panda::LocalScope;
using JSNApi = panda::JSNApi;
using JSValueRef = panda::JSValueRef;
using JsiRuntimeCallInfo = panda::JsiRuntimeCallInfo;
using panda::Local;
using GCTaskFinishedCallback = std::function<void()>;
using TaskPoolShrinkCallback = std::function<void(bool inBackground)>;
typedef bool (*NapiModuleValidateCallback)(const char* moduleName);
class NativeTimerCallbackInfo;
template <bool changeState = true>
panda::JSValueRef ArkNativeFunctionCallBack(JsiRuntimeCallInfo *runtimeInfo);
void NapiDefinePropertyInner(napi_env env,
Local<panda::ObjectRef> &obj,
NapiPropertyDescriptor &propertyDescriptor,
Local<panda::JSValueRef> &propertyName,
bool &result);
bool NapiDefineProperty(napi_env env, panda::Local<panda::ObjectRef> &obj, NapiPropertyDescriptor propertyDescriptor);
NAPI_EXPORT panda::Local<panda::JSValueRef> NapiValueToLocalValue(napi_value v);
NAPI_EXPORT napi_value LocalValueToLocalNapiValue(panda::Local<panda::JSValueRef> local);
#ifdef ENABLE_CONTAINER_SCOPE
void FunctionSetContainerId(napi_env env, panda::Local<panda::JSValueRef> &local);
#endif
panda::Local<panda::JSValueRef> NapiDefineClass(napi_env env, const char* name, NapiNativeCallback callback,
void* data, const NapiPropertyDescriptor* properties, size_t length);
Local<JSValueRef> NapiDefineSendableClass(napi_env env,
const char* name,
NapiNativeCallback callback,
void* data,
const NapiPropertyDescriptor* properties,
size_t propertiesLength,
napi_value parent);
panda::Local<panda::ObjectRef> NapiCreateSObjectWithProperties(napi_env env,
size_t propertyCount,
const NapiPropertyDescriptor* properties);
panda::Local<panda::ObjectRef> NapiCreateObjectWithProperties(napi_env env, size_t propertyCount,
const napi_property_descriptor *properties,
Local<panda::JSValueRef> *keys,
panda::PropertyAttribute *attrs);
void CommonDeleter(void *env, void *externalPointer, void *data);
enum class ForceExpandState : int32_t {
FINISH_COLD_START = 0,
START_HIGH_SENSITIVE,
FINISH_HIGH_SENSITIVE,
WARM_START,
};
enum class ModuleTypes : uint8_t {
NATIVE_MODULE = 0x01,
MODULE_INNER_FILE,
UNKNOWN
};
class SerializationData {
public:
SerializationData() : data_(nullptr), size_(0) {}
~SerializationData() = default;
uint8_t* GetData() const
{
return data_.get();
}
size_t GetSize() const
{
return size_;
}
private:
struct DataDeleter {
void operator()(uint8_t* p) const
{
free(p);
}
};
std::unique_ptr<uint8_t, DataDeleter> data_;
size_t size_;
};
enum class ArkNativeEngineState : uint8_t {
RUNNING,
STOPPED,
RELEASING,
};
class AppStateNotifier {
public:
void SetCallback(NapiAppStateCallback callback)
{
callback_ = callback;
}
void Notify(NapiAppState state, int64_t timestamp = 0)
{
if (callback_ != nullptr) {
callback_(state, timestamp);
}
}
private:
NapiAppStateCallback callback_ {nullptr};
};
class NAPI_EXPORT ArkNativeEngine : public NativeEngine {
friend struct MoudleNameLocker;
public:
ArkNativeEngine(EcmaVM* vm, void* jsEngine, bool isLimitedWorker = false);
~ArkNativeEngine() override;
static ArkNativeEngine* New(NativeEngine* engine, EcmaVM* vm, const Local<JSValueRef>& context);
bool IsReadyToDelete();
void Delete();
const EcmaVM* GetEcmaVm() const override;
__attribute__((noinline)) void GetEcmaVmFatal() const;
const EcmaVM* GetEcmaVmCritical() const override;
const ArkNativeEngine* GetParent() const override;
void Loop(LoopMode mode, bool needSync = false) override;
void SetPromiseRejectCallback(NativeReference* rejectCallbackRef, NativeReference* checkCallbackRef) override;
static bool SetModuleValidateCallback(NapiModuleValidateCallback validateCallback);
bool InitTaskPoolThread(NativeEngine* engine, NapiConcurrentCallback callback) override;
bool InitTaskPoolThread(napi_env env, NapiConcurrentCallback callback) override;
bool InitTaskPoolFunc(napi_env env, napi_value func, void* taskInfo) override;
void ClearCurrentTaskInfo() override;
bool HasPendingJob() const override;
bool IsProfiling() const override;
bool IsExecutingPendingJob() const override;
void* GetCurrentTaskInfo() const override;
void TerminateExecution() const override;
void NotifyTaskBegin() const override;
void NotifyTaskFinished() const override;
bool NapiNewTypedArray(NativeTypedArrayType typedArrayType,
Local<panda::ArrayBufferRef> arrayBuf,
size_t byte_offset, size_t length, napi_value* result) override;
bool NapiNewSendableTypedArray(NativeTypedArrayType typedArrayType,
Local<panda::SendableArrayBufferRef> arrayBuf,
size_t byte_offset, size_t length, napi_value* result) override;
NativeTypedArrayType GetTypedArrayType(panda::Local<panda::TypedArrayRef> typedArray) override;
NativeTypedArrayType GetSendableTypedArrayType(panda::Local<panda::SendableTypedArrayRef> typedArray) override;
napi_value CallFunction(napi_value thisVar,
napi_value function,
napi_value const* argv,
size_t argc) override;
bool RunScriptPath(const char* path, bool checkPath = false) override;
napi_value RunScriptBuffer(const char* path, std::vector<uint8_t>& buffer, bool isBundle,
bool needUpdate = false) override;
bool RunScriptBuffer(const std::string& path, uint8_t* buffer, size_t size, bool isBundle) override;
napi_value RunBufferScript(std::vector<uint8_t>& buffer) override;
napi_value RunActor(uint8_t* buffer, size_t size, const char* descriptor,
char* entryPoint = nullptr, bool checkPath = false, void* fileMapper = nullptr) override;
NAPI_EXPORT void SetPackagePath(const std::string appLinPathKey, const std::vector<std::string>& packagePath);
napi_value CreateInstance(napi_value constructor, napi_value const* argv, size_t argc) override;
NativeReference* CreateReference(napi_value value, uint32_t initialRefcount, bool flag = false,
NapiNativeFinalize callback = nullptr, void* data = nullptr, void* hint = nullptr,
size_t nativeBindingSize = 0) override;
NativeReference* CreateXRefReference(napi_value value, uint32_t initialRefcount, bool flag = false,
NapiNativeFinalize callback = nullptr, void* data = nullptr);
NativeReference* CreateAsyncReference(napi_value value, uint32_t initialRefcount, bool flag = false,
NapiNativeFinalize callback = nullptr, void* data = nullptr, void* hint = nullptr) override;
napi_value CreatePromise(NativeDeferred** deferred) override;
void* CreateRuntime(bool isLimitedWorker = false) override;
panda::Local<panda::ObjectRef> LoadArkModule(const void *buffer, int32_t len, const std::string& fileName);
napi_value ValueToNapiValue(JSValueWrapper& value) override;
NAPI_EXPORT static napi_value ArkValueToNapiValue(napi_env env, Local<JSValueRef> value);
std::string GetSourceCodeInfo(napi_value value, ErrorPos pos) override;
NAPI_EXPORT bool ExecuteJsBin(const std::string& fileName, bool checkPath = false);
static bool IsValidPandaFile(const char* path);
static bool IsValidScriptBuffer(uint8_t* scriptBuffer, size_t bufferSize);
NAPI_EXPORT panda::Local<panda::ObjectRef> LoadModuleByName(const std::string& moduleName, bool isAppModule,
const std::string& param, const std::string& instanceName, void* instance, const std::string& path = "");
void TriggerFatalException(panda::Local<panda::JSValueRef> exceptionValue) override;
bool AdjustExternalMemory(int64_t ChangeInBytes, int64_t* AdjustedValue) override;
void SetJsDumpThresholds(size_t thresholds) override;
void SetAppFreezeFilterCallback(AppFreezeFilterCallback callback) override;
void SetRawHeapTrimLevel(uint32_t level) override;
void SetProcDumpInSharedOOM(bool enable) override;
void StartCpuProfiler(const std::string& fileName = "") override;
void StopCpuProfiler() override;
void ResumeVM() override;
bool SuspendVM() override;
bool IsSuspended() override;
bool CheckSafepoint() override;
bool SuspendVMById(uint32_t tid) override;
void ResumeVMById(uint32_t tid) override;
void DumpHeapSnapshot(const std::string &path, bool isVmMode = true, DumpFormat dumpFormat = DumpFormat::JSON,
bool isPrivate = false, bool captureNumericValue = false, bool isJSLeakWatcher = false,
bool isClearNodeIdCache = false) override;
void DumpHeapSnapshot(bool isFullGC, const std::string &path, const std::function<void(uint8_t)> &callback,
bool isClearNodeIdCache = false, bool isProcDump = false) override;
void DumpCpuProfile() override;
void DumpHeapSnapshot(bool isVmMode = true, DumpFormat dumpFormat = DumpFormat::JSON,
bool isPrivate = false, bool isFullGC = true, bool isClearNodeIdCache = false) override;
bool BuildNativeAndJsStackTrace(std::string& stackTraceStr) override;
bool BuildJsStackTrace(std::string& stackTraceStr) override;
bool BuildJsStackInfoListWithCustomDepth(std::vector<JsFrameInfo>& jsFrames)
override;
void GetMainThreadStackTrace(napi_env env, std::string &stackTraceStr) override;
bool OnVMHeapMemoryPressure(napi_env env, HeapMemoryThreshold heapMemoryThreshold,
napi_value callback) override;
void OffVMHeapMemoryPressure(napi_env env) override;
void SetMultithreadingDetectionEnabled(napi_env env, bool enabled, bool abort,
int64_t frequency, int64_t interval) override;
bool GetDetectionAbort() override;
int64_t GetDetectionFrequency() override;
int64_t GetDetectionInterval() override;
std::vector<HeapMemoryInfo> GetAllVMHeapMemoryInfo() override;
bool EnableLocalHandleDetection() override;
void GetHybridStackTraceForCrash(napi_env env, std::string &stackTraceStr) override;
void GetJsonExtraInfoForCrash(napi_env env, std::string &jsonStr, uint32_t &position) override;
void SerializeJSError(napi_env env, napi_value object,
const SerializeOptions& options, void** result) override;
void SerializeJSErrorWithError(napi_env env, napi_value object,
const SerializeOptions& options, void** result,
std::string& error) override;
bool DeleteWorker(NativeEngine* workerEngine) override;
bool StartHeapTracking(double timeInterval, bool isVmMode = true) override;
bool StopHeapTracking(const std::string& filePath) override;
void PrintStatisticResult() override;
void StartRuntimeStat() override;
void StopRuntimeStat() override;
size_t GetArrayBufferSize() override;
size_t GetHeapTotalSize() override;
size_t GetHeapUsedSize() override;
size_t GetHeapObjectSize() override;
size_t GetHeapLimitSize() override;
size_t GetProcessHeapLimitSize() override;
size_t GetGCCount() override;
size_t GetGCDuration() override;
size_t GetAccumulatedAllocateSize() override;
size_t GetAccumulatedFreeSize() override;
size_t GetFullGCLongTimeCount() override;
void NotifyApplicationState(bool inBackground) override;
void NotifyIdleStatusControl(std::function<void(bool)> callback) override;
void NotifyIdleTime(int idleMicroSec) override;
void NotifyMemoryPressure(bool inHighMemoryPressure = false) override;
void NotifyForceExpandState(int32_t value) override;
void NotifyForceExpandState(uint64_t tid, int32_t value) override;
void RegisterAppStateCallback(NapiAppStateCallback callback) override;
void AllowCrossThreadExecution() const override;
void SetThreadName(const std::string &name) override;
static void PromiseRejectCallback(void* values);
void SetMockModuleList(const std::map<std::string, std::string> &list) override;
int32_t GetObjectHash(napi_env env, napi_value src) override;
bool IsMixedDebugEnabled();
void JsHeapStart();
uint64_t GetCurrentTickMillseconds();
bool JudgmentDumpExecuteTask(int pid);
void JudgmentDump(size_t limitSize);
void NotifyNativeCalling(const void *nativeAddress);
void PostFinalizeTasks();
void PostAsyncTask(panda::AsyncNativeCallbacksPack *callbacksPack);
void PostTriggerGCTask(panda::TriggerGCData& data, GCTaskFinishedCallback callback = nullptr) override;
void InitPostTaskToThreadCallback();
ArkFinalizersPack &GetArkFinalizersPack()
{
return arkFinalizersPack_;
}
std::vector<RefAsyncFinalizer> &GetPendingAsyncFinalizers()
{
return pendingAsyncFinalizers_;
}
void RegisterNapiUncaughtExceptionHandler(NapiUncaughtExceptionCallback callback) override;
void HandleUncaughtException() override;
bool HasPendingException() override;
void RegisterPermissionCheck(PermissionCheckCallback callback) override;
bool ExecutePermissionCheck() override;
panda::Local<panda::ObjectRef> GetModuleFromName(
const std::string& moduleName, bool isAppModule, const std::string& id, const std::string& param,
const std::string& instanceName, void** instance);
napi_value NapiLoadModule(const char* path) override;
napi_value NapiLoadModuleWithInfo(const char* path, const char* module_info) override;
napi_value NapiLoadModuleWithInfoForHybridApp(
const char* path, const char* module_info, const char* ohmurl, const char* abcFilePath) override;
std::string GetOhmurl(std::string str);
Local<JSValueRef> NapiLoadNativeModule(std::string path);
NativeReference* GetPromiseRejectCallBackRef()
{
return promiseRejectCallbackRef_;
}
void RegisterAllPromiseCallback(NapiAllPromiseRejectCallback callback) override;
void SetPromiseRejectCallBackRef(NativeReference* rejectCallbackRef) override
{
promiseRejectCallbackRef_ = rejectCallbackRef;
}
NapiConcurrentCallback GetConcurrentCallbackFunc()
{
return concurrentCallbackFunc_;
}
NativeReference* GetCheckCallbackRef()
{
return checkCallbackRef_;
}
void SetCheckCallbackRef(NativeReference* checkCallbackRef) override
{
checkCallbackRef_ = checkCallbackRef;
}
NapiUncaughtExceptionCallback GetNapiUncaughtExceptionCallback() override
{
return napiUncaughtExceptionCallback_;
}
void* GetPromiseRejectCallback() override
{
return reinterpret_cast<void*>(PromiseRejectCallback);
}
void SetModuleName(panda::Local<panda::ObjectRef> &nativeObj, std::string moduleName);
void GetCurrentModuleInfo(std::string& moduleName, std::string& fileName, bool needRecordName) override;
bool GetIsBundle() override;
bool GetIsNormalizedOhmUrlPack() override;
bool GetIsDebugModeEnabled() override;
std::string GetBundleName() override;
std::string GetPkgName(const std::string &moduleName) override;
bool IsExecuteModuleInAbcFile(std::string bundleName, std::string moduleName, std::string ohmurl) override;
int GetProcessStartRealTime() override;
static bool napiProfilerEnabled;
static int napiApiTraceEnabled;
static std::string tempModuleName_;
static void* GetNativePtrCallBack(void* data);
static void* GetNativeReferenceData(void* ref);
static void CopyPropertyApiFilter(const std::unique_ptr<ApiAllowListChecker>& apiAllowListChecker,
const EcmaVM* ecmaVm, const panda::Local<panda::ObjectRef> exportObj,
panda::Local<panda::ObjectRef>& exportCopy, const std::string& apiPath);
static constexpr size_t FINALIZERS_PACK_PENDING_NATIVE_BINDING_SIZE_THRESHOLD = 500 * 1024 * 1024;
bool IsContainerScopeEnabled() const override
{
return containerScopeEnable_;
}
NativeTimerCallbackInfo* GetTimerListHead() const
{
return TimerListHead_;
}
void SetTimerListHead(NativeTimerCallbackInfo* info)
{
TimerListHead_ = info;
}
inline bool IsMainEnvContext() const override
{
return isMainEnvContext_;
}
inline bool IsMultiContextEnabled() const override
{
return isMultiContextEnabled_;
}
inline void SetMultiContextEnabled(bool enabled) override
{
isMultiContextEnabled_ = enabled;
}
Local<JSValueRef> GetContext() const override;
napi_status SwitchContext() override;
napi_status DestroyContext() override;
void NotifyVMIgnoreFinalizeCallback() const override;
#if defined(PREVIEW)
static void SetCurrentPreviewenv(bool enableFileOperation);
#endif
void SetTaskpoolShrinkCallback(TaskPoolShrinkCallback callback) override;
private:
ArkNativeEngine(NativeEngine* parent, EcmaVM* vm, const Local<JSValueRef>& context);
void DeconstructCtxEnv();
static void EnvironmentCleanup(void* arg);
int CheckAndGetModule(
JsiRuntimeCallInfo *info,
NativeModuleManager* moduleManager,
bool isAppModule,
Local<panda::StringRef> &moduleName,
NativeModule *&module,
Local<JSValueRef> &exports,
std::string &errInfo,
std::string &loadErrInfo);
Local<JSValueRef> LoadNativeModule(
NativeModuleManager* moduleManager,
Local<panda::StringRef> &moduleName,
NativeModule* module,
Local<JSValueRef> exports,
std::string &errInfo);
static Local<JSValueRef> RequireNapi(JsiRuntimeCallInfo *info);
static Local<JSValueRef> RequireNapiForCtxEnv(JsiRuntimeCallInfo *info);
static Local<JSValueRef> RequireInternal(JsiRuntimeCallInfo *info);
inline NapiOptions *GetNapiOptions() const override
{
return options_;
}
void EnableNapiProfiler() override;
static void RunCallbacks(ArkFinalizersPack *finalizersPack);
static void RunAsyncCallbacks(std::vector<RefAsyncFinalizer> *finalizers);
static void RunCallbacks(panda::AsyncNativeCallbacksPack *callbacks);
static void RunCallbacks(panda::TriggerGCData *triggerGCData);
static void SetAttribute(bool isLimitedWorker, panda::RuntimeOption &option);
static NativeEngine* CreateRuntimeFunc(NativeEngine* engine, void* jsEngine, bool isLimitedWorker = false);
static NativeEngine* GetArkNativeEngineByID(uint64_t tid);
static bool CheckArkApiAllowList(
NativeModule* module, panda::ecmascript::ApiCheckContext context, panda::Local<panda::ObjectRef>& exportCopy);
void IncreasePendingFinalizersPackNativeBindingSize(size_t nativeBindingSize)
{
pendingFinalizersPackNativeBindingSize_ += nativeBindingSize;
}
void DecreasePendingFinalizersPackNativeBindingSize(size_t nativeBindingSize)
{
pendingFinalizersPackNativeBindingSize_ -= nativeBindingSize;
}
bool IsLimitWorker() const override
{
return isLimitedWorker_;
}
void SetExternalClearCallback(std::function<void()>&& func) override
{
panda::ecmascript::ArkIdleMonitor::GetInstance()->SetExternalClearCallback(std::move(func));
}
EcmaVM* vm_ = nullptr;
bool needStop_ = false;
panda::Global<panda::JSValueRef> context_;
ArkNativeEngine* parentEngine_ { nullptr };
static std::atomic<NapiModuleValidateCallback> moduleValidateCallback_;
#if defined(PREVIEW)
static bool enableFileOperation_;
#endif
NapiConcurrentCallback concurrentCallbackFunc_ { nullptr };
NativeReference* promiseRejectCallbackRef_ { nullptr };
NativeReference* checkCallbackRef_ { nullptr };
std::map<NativeModule*, panda::Global<panda::JSValueRef>> loadedModules_ {};
static PermissionCheckCallback permissionCheckCallback_;
NapiUncaughtExceptionCallback napiUncaughtExceptionCallback_ { nullptr };
NapiAllPromiseRejectCallback allPromiseRejectCallback_ {nullptr};
static bool napiProfilerParamReaded;
bool isLimitedWorker_ = false;
size_t pendingFinalizersPackNativeBindingSize_ {0};
ArkFinalizersPack arkFinalizersPack_ {};
std::vector<RefAsyncFinalizer> pendingAsyncFinalizers_ {};
NapiOptions* options_ { nullptr };
bool containerScopeEnable_ { false };
NativeTimerCallbackInfo* TimerListHead_ {nullptr};
bool isMainEnvContext_ = false;
bool isMultiContextEnabled_ = false;
ArkNativeEngineState engineState_ { ArkNativeEngineState::RUNNING };
AppStateNotifier interopAppState_ {};
NativeReference *globalCheckCallbackRef_ { nullptr };
uv_async_t threadTaskAsync_ {};
bool threadTaskAsyncInitialized_ = false;
std::mutex threadTaskMutex_;
std::function<void()> pendingThreadTask_;
};
#endif