From 891813672c295b8abccb2a66a44312c6d0e7a7bc Mon Sep 17 00:00:00 2001 From: chenraozhong Date: Tue, 26 Nov 2024 16:42:27 +0800 Subject: [PATCH] Add handler for gc Signed-off-by: chenraozhong --- src/js_native_api_v8.cc | 197 ++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 40 ++++++++ src/jsvm_types.h | 72 +++++++++++++++ 3 files changed, 309 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index a6103839..7f1130a6 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -143,12 +143,65 @@ namespace { enum IsolateDataSlot { kIsolateData = 0, kIsolateSnapshotCreatorSlot = 1, + kIsolateHandlerPoolSlot = 2, }; +// Always compare the final element of IsolateDataSlot with v8 limit. +static_assert(kIsolateHandlerPoolSlot < v8::internal::Internals::kNumIsolateDataSlots); + +struct GCHandlerWrapper { + GCHandlerWrapper(JSVM_GCType gcType, JSVM_HandlerForGC handler, void* userData) + : gcType(gcType), handler(handler), userData(userData) {} + + JSVM_GCType gcType; + JSVM_HandlerForGC handler; + void *userData; +}; + +using GCHandlerWrappers = std::list; + +struct IsolateHandlerPool { + JSVM_HandlerForOOMError handlerForOOMError = nullptr; + JSVM_HandlerForFatalError handlerForFatalError = nullptr; + JSVM_HandlerForPromiseReject handlerForPromiseReject = nullptr; + GCHandlerWrappers handlerWrappersBeforeGC; + GCHandlerWrappers handlerWrappersAfterGC; + + ~IsolateHandlerPool() { + for (auto *handler : handlerWrappersBeforeGC) { + delete handler; + } + handlerWrappersBeforeGC.clear(); + for (auto *handler : handlerWrappersAfterGC) { + delete handler; + } + handlerWrappersAfterGC.clear(); + } +}; + +static IsolateHandlerPool* GetIsolateHandlerPool(v8::Isolate* isolate) { + auto pool = isolate->GetData(v8impl::kIsolateHandlerPoolSlot); + return reinterpret_cast(pool); +} + +static IsolateHandlerPool* GetOrCreateIsolateHandlerPool(v8::Isolate* isolate) { + auto *pool = isolate->GetData(v8impl::kIsolateHandlerPoolSlot); + if (pool != nullptr) { + return reinterpret_cast(pool); + } + auto *createdPool = new v8impl::IsolateHandlerPool(); + isolate->SetData(v8impl::kIsolateHandlerPoolSlot, createdPool); + return createdPool; +} enum ContextEmbedderIndex { kContextEnvIndex = 1, }; +static JSVM_Env GetEnvByContext(v8::Local context) { + auto env = context->GetAlignedPointerFromEmbedderData(v8impl::kContextEnvIndex); + return reinterpret_cast(env); +} + struct IsolateData { IsolateData(v8::StartupData* blob) : blob(blob) {} @@ -1480,6 +1533,8 @@ OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) { } v8impl::CreateIsolateData(isolate, snapshotBlob); *result = reinterpret_cast(isolate); + // Create nullptr placeholder + isolate->SetData(v8impl::kIsolateHandlerPoolSlot, nullptr); return JSVM_OK; } @@ -1493,6 +1548,7 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { auto creator = v8impl::GetIsolateSnapshotCreator(isolate); auto data = v8impl::GetIsolateData(isolate); + auto *handlerPool = v8impl::GetIsolateHandlerPool(isolate); if (creator != nullptr) { delete creator; } else { @@ -1502,6 +1558,10 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { delete data; } + if (handlerPool != nullptr) { + delete handlerPool; + } + return JSVM_OK; } @@ -2791,6 +2851,143 @@ JSVM_Status JSVM_CDECL OH_JSVM_ObjectSeal(JSVM_Env env, JSVM_Value object) { return GET_RETURN_STATUS(env); } + +JSVM_GCType GetJSVMGCType(v8::GCType gcType) { + switch (gcType) { + case v8::GCType::kGCTypeScavenge: + return JSVM_GC_TYPE_SCAVENGE; + case v8::GCType::kGCTypeMinorMarkCompact: + return JSVM_GC_TYPE_MINOR_MARK_COMPACT; + case v8::GCType::kGCTypeMarkSweepCompact: + return JSVM_GC_TYPE_MARK_SWEEP_COMPACT; + case v8::GCType::kGCTypeIncrementalMarking: + return JSVM_GC_TYPE_INCREMENTAL_MARKING; + case v8::GCType::kGCTypeProcessWeakCallbacks: + return JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS; + default: + return JSVM_GC_TYPE_ALL; + } +} + +static v8::GCType GetV8GCType(JSVM_GCType gcType) { + switch(gcType) { + case JSVM_GC_TYPE_SCAVENGE: + return v8::GCType::kGCTypeScavenge; + case JSVM_GC_TYPE_MINOR_MARK_COMPACT: + return v8::GCType::kGCTypeMinorMarkCompact; + case JSVM_GC_TYPE_MARK_SWEEP_COMPACT: + return v8::GCType::kGCTypeMarkSweepCompact; + case JSVM_GC_TYPE_INCREMENTAL_MARKING: + return v8::GCType::kGCTypeIncrementalMarking; + case JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS: + return v8::GCType::kGCTypeProcessWeakCallbacks; + default: + return v8::GCType::kGCTypeAll; + } +} + +JSVM_GCCallbackFlags GetJSVMGCCallbackFlags(v8::GCCallbackFlags flag) { + switch (flag) { + case v8::GCCallbackFlags::kGCCallbackFlagConstructRetainedObjectInfos: + return JSVM_kGCCallbackFlagConstructRetainedObjectInfos; + case v8::GCCallbackFlags::kGCCallbackFlagForced: + return JSVM_kGCCallbackFlagForced; + case v8::GCCallbackFlags::kGCCallbackFlagSynchronousPhantomCallbackProcessing: + return JSVM_kGCCallbackFlagSynchronousPhantomCallbackProcessing; + case v8::GCCallbackFlags::kGCCallbackFlagCollectAllAvailableGarbage: + return JSVM_kGCCallbackFlagCollectAllAvailableGarbage; + case v8::GCCallbackFlags::kGCCallbackFlagCollectAllExternalMemory: + return JSVM_kGCCallbackFlagCollectAllExternalMemory; + case v8::GCCallbackFlags::kGCCallbackScheduleIdleGarbageCollection: + return JSVM_kGCCallbackScheduleIdleGarbageCollection; + default: + return JSVM_kNoGCCallbackFlags; + } +} + +static void OnBeforeGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { + auto *pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + JSVM_GCType gcType = GetJSVMGCType(type); + JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); + + auto *gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; + gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); +} + +static void OnAfterGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { + auto *pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + JSVM_GCType gcType = GetJSVMGCType(type); + JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); + + auto *gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; + gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AddHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + JSVM_GCType gcType, + void* data) { + if (!handler) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, data](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == data; + }); + if (it != handlers.end()) { + return JSVM_INVALID_ARG; + } + auto *callbackData = new v8impl::GCHandlerWrapper(gcType, handler, data); + handlers.push_back(callbackData); + + if (beforeGC) { + isolate->AddGCPrologueCallback( + OnBeforeGC, callbackData, GetV8GCType(gcType)); + } else { + isolate->AddGCEpilogueCallback( + OnAfterGC, callbackData, GetV8GCType(gcType)); + } + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + void* userData) { + if (!handler) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + if (pool == nullptr) { + return JSVM_INVALID_ARG; + } + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, userData](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == userData; + }); + if (it == handlers.end()) { + return JSVM_INVALID_ARG; + } + handlers.erase(it); + if (beforeGC) { + isolate->RemoveGCPrologueCallback(OnBeforeGC, (*it)); + } else { + isolate->RemoveGCEpilogueCallback(OnAfterGC, (*it)); + } + delete (*it); + return JSVM_OK; +} + JSVM_Status JSVM_CDECL OH_JSVM_IsArray(JSVM_Env env, JSVM_Value value, bool* result) { diff --git a/src/jsvm.h b/src/jsvm.h index b5771f10..ee8d3452 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -1515,6 +1515,46 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, JSVM_Value constructor, bool* result); + +/** + * @brief Add VM GC Callback. + * + * @param vm: The environment that the API is invoked under. + * @param gcTime: The garbage collection moment. + * @param callbackptr: When Trigger gc, the callback function will be called. + * @param gcType: The type of gc. + * @param user_data: The native pointer data. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL or gc callback has called before.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_AddHandlerForGC(JSVM_VM vm, + bool beforeGC, + JSVM_HandlerForGC handler, + JSVM_GCType gcType, + void* user_data); + +/** + * @brief Remove VM GC Callback. + * + * @param vm: The environment that the API is invoked under. + * @param gcTime: The garbage collection moment. + * @param callbackptr: When Trigger gc, the callback function will be called. + * @param user_data: The native pointer data. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL or gc callback + * has been removed before.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, + bool beforeGC, + JSVM_HandlerForGC callbackptr, + void* user_data); + /** * @brief This API represents invoking the IsArray operation on the object * diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 4ee1793b..8d170767 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -162,6 +162,78 @@ typedef struct { */ typedef JSVM_CallbackStruct* JSVM_Callback; +/** + * @brief The timing of GC callback trigger. + * + * @since 16 + */ +typedef enum { + /** Trigger GC callback before GC. */ + JSVM_CB_TRIGGER_BEFORE_GC, + /** Trigger GC callback after GC. */ + JSVM_CB_TRIGGER_AFTER_GC, +} JSVM_CBTriggerTimeForGC; + +/** + * @brief The GC type. + * + * @since 16 + */ +typedef enum { + /** The GC algorithm is Scavenge. */ + JSVM_GC_TYPE_SCAVENGE = 1 << 0, + /** The GC algorithm is Minor-Mark-Compact. */ + JSVM_GC_TYPE_MINOR_MARK_COMPACT = 1 << 1, + /** The GC algorithm is Mark-Sweep-Compact. */ + JSVM_GC_TYPE_MARK_SWEEP_COMPACT = 1 << 2, + /** The GC algorithm is Incremental-Marking. */ + JSVM_GC_TYPE_INCREMENTAL_MARKING = 1 << 3, + /** The GC algorithm is Weak-Callbacks. */ + JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS = 1 << 4, + /** All GC algorithm. */ + JSVM_GC_TYPE_ALL = JSVM_GC_TYPE_SCAVENGE | JSVM_GC_TYPE_MINOR_MARK_COMPACT | + JSVM_GC_TYPE_MARK_SWEEP_COMPACT | JSVM_GC_TYPE_INCREMENTAL_MARKING | + JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS, +} JSVM_GCType; + +/** + * @brief The GC callback flags. + * + * @since 16 + */ +typedef enum { + /** No GC callback falgs. */ + JSVM_NO_GC_CALLBACK_FLAGS, + /** Reserved object information will be built in the garbage collection callback. */ + JSVM_GC_CALLBACK_CONSTRUCT_RETAINED_OBJECT_INFOS, + /** Enforce Garbage Collection Callback. */ + JSVM_GC_CALLBACK_FORCED, + /** Synchronous phantom callback processing. */ + JSVM_GC_CALLBACK_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING, + /** All available garbage objects are collected during garbage collection. */ + JSVM_GC_CALLBACK_COLLECT_ALL_AVAILABLE_GARBAGE, + /** Garbage collection collects all external memory. */ + JSVM_GC_CALLBACK_COLLECT_ALL_EXTERNAL_MEMORY, + /** Schedule Garbage Collection at Idle Time. */ + JSVM_GC_CALLBACK_SCHEDULE_IDLE_GARBAGE_COLLECTION, +} JSVM_GCCallbackFlags; + +/** + * @brief Function pointer type of GC callback. + * + * @param vm The VM instance that the JSVM-API call is invoked under. + * @param gcType The gc type. + * @param flags The GC callback flags. + * @param data The native pointer data. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForGC)(JSVM_VM vm, + JSVM_GCType gcType, + JSVM_GCCallbackFlags flags, + void* data); + + /** * @brief Function pointer type for add-on provided function that allow the user to be notified. * -- Gitee