From eeb3b91a476bae7f1b62f07f8ae72f1e522a1fce Mon Sep 17 00:00:00 2001 From: johnche Date: Wed, 18 Dec 2024 17:25:00 +0800 Subject: [PATCH] =?UTF-8?q?[unity]pinvoke=E7=89=88=E6=9C=AC=E7=9A=84create?= =?UTF-8?q?Function=E7=94=9F=E6=88=90=E7=9A=84=E5=87=BD=E6=95=B0gc?= =?UTF-8?q?=E5=90=8E=E5=8E=9F=E7=94=9F=E4=BB=A5=E5=8F=8Ac#=E5=86=85?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E9=87=8A=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/upm/Runtime/Src/Default/JsEnv.cs | 22 ++++++- .../Runtime/Src/Default/Native/PuertsDLL.cs | 21 ++++++- .../Src/Default/Native/StaticCallbacks.cs | 15 +++++ unity/native_src/Inc/IPuertsPlugin.h | 2 + unity/native_src/Inc/JSEngine.h | 20 +++++++ unity/native_src/Src/JSEngine.cpp | 59 +++++++++++++++++++ unity/native_src/Src/PluginImpl.cpp | 21 +++++++ unity/native_src/Src/Puerts.cpp | 18 ++++++ unity/test/Src/Cases/CrossLang/GenericTest.cs | 2 + 9 files changed, 177 insertions(+), 3 deletions(-) diff --git a/unity/Assets/core/upm/Runtime/Src/Default/JsEnv.cs b/unity/Assets/core/upm/Runtime/Src/Default/JsEnv.cs index a51c6547440..80669cf82dd 100644 --- a/unity/Assets/core/upm/Runtime/Src/Default/JsEnv.cs +++ b/unity/Assets/core/upm/Runtime/Src/Default/JsEnv.cs @@ -424,6 +424,24 @@ internal long AddCallback(JSFunctionCallback callback) return Utils.TwoIntToLong(Idx, callbackIdx); } + internal long AddCallbackSlow(JSFunctionCallback callback) + { + for(int i = 0; i < callbacks.Count; ++i) + { + if (callbacks[i] == null) + { + callbacks[i] = callback; + return Utils.TwoIntToLong(Idx, i); + } + } + return AddCallback(callback); + } + + internal void ReleaseCallback(int callbackIdx) + { + callbacks[callbackIdx] = null; + } + private readonly List constructorCallbacks = new List(); internal IntPtr InvokeConstructor(IntPtr isolate, int callbackIdx, IntPtr info, int paramLen) @@ -606,10 +624,10 @@ void CreateFunction(IntPtr isolate, IntPtr info, IntPtr self, int paramLen) return; } - var callbackID = AddCallback(new MethodReflectionWrap(methodInfos[0].Name, + var callbackID = AddCallbackSlow(new MethodReflectionWrap(methodInfos[0].Name, methodInfos.Select(m => new OverloadReflectionWrap(m, this, false)).ToList() ).Invoke); - PuertsDLL.ReturnCSharpFunctionCallback(isolate, info, StaticCallbacks.JsEnvCallbackWrap, callbackID); + PuertsDLL.ReturnCSharpFunctionCallback2(isolate, info, StaticCallbacks.JsEnvCallbackWrap, StaticCallbacks.FunctionFinalizeCallback, callbackID); } catch (Exception e) { diff --git a/unity/Assets/core/upm/Runtime/Src/Default/Native/PuertsDLL.cs b/unity/Assets/core/upm/Runtime/Src/Default/Native/PuertsDLL.cs index bf95373b269..0f7bc76d161 100644 --- a/unity/Assets/core/upm/Runtime/Src/Default/Native/PuertsDLL.cs +++ b/unity/Assets/core/upm/Runtime/Src/Default/Native/PuertsDLL.cs @@ -25,7 +25,12 @@ public MonoPInvokeCallbackAttribute(Type t) #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || PUERTS_GENERAL || (UNITY_WSA && !UNITY_EDITOR) [UnmanagedFunctionPointer(CallingConvention.Cdecl)] #endif - public delegate void V8FunctionCallback(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data); + public delegate void V8FunctionCallback(IntPtr isolate, IntPtr info, IntPtr self, int paramLen, long data); + +#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || PUERTS_GENERAL || (UNITY_WSA && !UNITY_EDITOR) + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] +#endif + public delegate void JsFunctionFinalizeCallback(IntPtr isolate, long data); #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || PUERTS_GENERAL || (UNITY_WSA && !UNITY_EDITOR) [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -345,6 +350,20 @@ public static void ReturnCSharpFunctionCallback(IntPtr isolate, IntPtr info, V8F ReturnCSharpFunctionCallback(isolate, info, fn, data); } + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + private static extern void ReturnCSharpFunctionCallback2(IntPtr isolate, IntPtr info, IntPtr v8FunctionCallback, IntPtr JsFunctionFinalize, long data); + + public static void ReturnCSharpFunctionCallback2(IntPtr isolate, IntPtr info, V8FunctionCallback v8FunctionCallback, JsFunctionFinalizeCallback JsFunctionFinalize, long data) + { +#if PUERTS_GENERAL || (UNITY_WSA && !UNITY_EDITOR) + GCHandle.Alloc(v8FunctionCallback); + GCHandle.Alloc(JsFunctionFinalize); +#endif + IntPtr fn = v8FunctionCallback == null ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(v8FunctionCallback); + IntPtr fn2 = JsFunctionFinalize == null ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(JsFunctionFinalize); + ReturnCSharpFunctionCallback2(isolate, info, fn, fn2, data); + } + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern void ReturnJSObject(IntPtr isolate, IntPtr info, IntPtr JSObject); diff --git a/unity/Assets/core/upm/Runtime/Src/Default/Native/StaticCallbacks.cs b/unity/Assets/core/upm/Runtime/Src/Default/Native/StaticCallbacks.cs index 97267f8f660..a5c2ec41ccd 100644 --- a/unity/Assets/core/upm/Runtime/Src/Default/Native/StaticCallbacks.cs +++ b/unity/Assets/core/upm/Runtime/Src/Default/Native/StaticCallbacks.cs @@ -28,6 +28,21 @@ internal static void JsEnvCallbackWrap(IntPtr isolate, IntPtr info, IntPtr self, } } + [MonoPInvokeCallback(typeof(JsFunctionFinalizeCallback))] + internal static void FunctionFinalizeCallback(IntPtr isolate, long data) + { + try + { + int jsEnvIdx, callbackIdx; + Utils.LongToTwoInt(data, out jsEnvIdx, out callbackIdx); + JsEnv.jsEnvs[jsEnvIdx].ReleaseCallback(callbackIdx); + } + catch (Exception e) + { + PuertsDLL.ThrowException(isolate, "JsEnvCallbackWrap c# exception:" + e.Message + ",stack:" + e.StackTrace); + } + } + [MonoPInvokeCallback(typeof(V8DestructorCallback))] internal static void GeneralDestructor(IntPtr self, long data) { diff --git a/unity/native_src/Inc/IPuertsPlugin.h b/unity/native_src/Inc/IPuertsPlugin.h index f369a5c7ad0..6e12f2d717c 100644 --- a/unity/native_src/Inc/IPuertsPlugin.h +++ b/unity/native_src/Inc/IPuertsPlugin.h @@ -123,6 +123,8 @@ class IPuertsPlugin virtual void ReturnFunction(const void* Info, void* Function) = 0; virtual void ReturnCSharpFunctionCallback(const void* Info, FuncPtr Callback, int64_t Data) = 0; + + virtual void ReturnCSharpFunctionCallback(const void* Info, FuncPtr Callback, FuncPtr Finalize, int64_t Data) = 0; virtual void ReturnJSObject(const void* Info, void* Object) = 0; //-------------------------- end js call cs -------------------------- diff --git a/unity/native_src/Inc/JSEngine.h b/unity/native_src/Inc/JSEngine.h index e57a6151f83..3ca958981ee 100644 --- a/unity/native_src/Inc/JSEngine.h +++ b/unity/native_src/Inc/JSEngine.h @@ -43,10 +43,14 @@ typedef char* (*CSharpModuleResolveCallback)(const char* identifer, int32_t jsEn typedef void(*CSharpFunctionCallback)(puerts::IPuertsPlugin* plugin, const v8::FunctionCallbackInfo& Info, void* Self, int ParamLen, int64_t UserData); typedef void* (*CSharpConstructorCallback)(puerts::IPuertsPlugin* plugin, const v8::FunctionCallbackInfo& Info, int ParamLen, int64_t UserData); + +typedef void (*JsFunctionFinalizeCallback)(puerts::IPuertsPlugin* plugin, int64_t UserData); #else typedef void(*CSharpFunctionCallback)(v8::Isolate* Isolate, const v8::FunctionCallbackInfo& Info, void* Self, int ParamLen, int64_t UserData); typedef void* (*CSharpConstructorCallback)(v8::Isolate* Isolate, const v8::FunctionCallbackInfo& Info, int ParamLen, int64_t UserData); + +typedef void (*JsFunctionFinalizeCallback)(v8::Isolate* Isolate, int64_t UserData); #endif typedef void(*CSharpDestructorCallback)(void* Self, int64_t UserData); @@ -59,6 +63,16 @@ struct FCallbackInfo int64_t Data; }; +struct FCallbackInfoWithFinalize : public FCallbackInfo +{ + FCallbackInfoWithFinalize(bool InIsStatic, CSharpFunctionCallback InCallback, int64_t InData, JsFunctionFinalizeCallback InFinalize, class JSEngine* InJSE) + : FCallbackInfo(InIsStatic, InCallback, InData), Finalize(InFinalize), JSE(InJSE) + {} + JsFunctionFinalizeCallback Finalize; + class JSEngine* JSE; + v8::Global JsFunction; +}; + struct FLifeCycleInfo { FLifeCycleInfo(int InClassID, CSharpConstructorCallback InConstructor, CSharpDestructorCallback InDestructor, int64_t InData, int InSize) @@ -142,6 +156,8 @@ class JSEngine bool InspectorTick(); void LogicTick(); + + static void CallbackDataGarbageCollected(const v8::WeakCallbackInfo& Data); v8::Isolate* MainIsolate; @@ -164,6 +180,8 @@ class JSEngine private: std::vector CallbackInfos; + + std::vector CallbackWithFinalizeInfos; std::vector LifeCycleInfos; @@ -199,6 +217,8 @@ class JSEngine JSFunction* GetModuleExecutor(); v8::Local ToTemplate(v8::Isolate* Isolate, bool IsStatic, CSharpFunctionCallback Callback, int64_t Data); + + v8::MaybeLocal CreateFunction(CSharpFunctionCallback Callback, JsFunctionFinalizeCallback Finalize, int64_t Data); std::string GetJSStackTrace(); }; diff --git a/unity/native_src/Src/JSEngine.cpp b/unity/native_src/Src/JSEngine.cpp index 3107b0ffd3d..d1088205697 100644 --- a/unity/native_src/Src/JSEngine.cpp +++ b/unity/native_src/Src/JSEngine.cpp @@ -216,6 +216,11 @@ namespace PUERTS_NAMESPACE { delete CallbackInfos[i]; } + + for (int i = 0; i < CallbackWithFinalizeInfos.size(); ++i) + { + delete CallbackWithFinalizeInfos[i]; + } for (int i = 0; i < LifeCycleInfos.size(); ++i) { @@ -437,6 +442,60 @@ namespace PUERTS_NAMESPACE return v8::FunctionTemplate::New(Isolate, CSharpFunctionCallbackWrap, v8::External::New(Isolate, CallbackInfos[Pos]), v8::Local(), 0, v8::ConstructorBehavior::kThrow); #endif } + + void JSEngine::CallbackDataGarbageCollected(const v8::WeakCallbackInfo& Data) + { + FCallbackInfoWithFinalize* CallbackData = Data.GetParameter(); + auto Isolate = Data.GetIsolate(); + if (CallbackData->Finalize) + { +#ifdef MULT_BACKENDS + auto JsEngine = FV8Utils::IsolateData(Isolate); + CallbackData->Finalize(JsEngine->ResultInfo.PuertsPlugin, CallbackData->Data); +#else + CallbackData->Finalize(Isolate, CallbackData->Data); +#endif + } + for (auto it = CallbackData->JSE->CallbackWithFinalizeInfos.begin(); it != CallbackData->JSE->CallbackWithFinalizeInfos.end(); ) + { + if (*it == CallbackData) + { + it = CallbackData->JSE->CallbackWithFinalizeInfos.erase(it); + } + else + { + ++it; + } + } + delete CallbackData; + } + + v8::MaybeLocal JSEngine::CreateFunction(CSharpFunctionCallback Callback, JsFunctionFinalizeCallback Finalize, int64_t Data) + { + v8::Isolate* Isolate = BackendEnv.MainIsolate; + v8::Local Context = Isolate->GetCurrentContext(); + auto CallbackData = new FCallbackInfoWithFinalize(false, Callback, Data, Finalize, this); + +#if defined(WITH_QUICKJS) + auto Template = v8::FunctionTemplate::New(Isolate, CSharpFunctionCallbackWrap, v8::External::New(Isolate, CallbackData)); +#else + auto Template = v8::FunctionTemplate::New(Isolate, CSharpFunctionCallbackWrap, v8::External::New(Isolate, CallbackData), v8::Local(), 0, v8::ConstructorBehavior::kThrow); + Template->Set(Isolate, "__do_not_cache", v8::ObjectTemplate::New(Isolate)); +#endif + auto Ret = Template->GetFunction(Context); + if (!Ret.IsEmpty()) + { + CallbackData->JsFunction.Reset(Isolate, Ret.ToLocalChecked()); + CallbackData->JsFunction.SetWeak( + CallbackData, CallbackDataGarbageCollected, v8::WeakCallbackType::kInternalFields); + CallbackWithFinalizeInfos.push_back(CallbackData); + } + else + { + delete CallbackData; + } + return Ret; + } void JSEngine::SetGlobalFunction(const char *Name, CSharpFunctionCallback Callback, int64_t Data) { diff --git a/unity/native_src/Src/PluginImpl.cpp b/unity/native_src/Src/PluginImpl.cpp index 406cd435b6c..7cbb70ad2da 100644 --- a/unity/native_src/Src/PluginImpl.cpp +++ b/unity/native_src/Src/PluginImpl.cpp @@ -132,6 +132,8 @@ class V8Plugin : public puerts::IPuertsPlugin virtual void ReturnFunction(const void* Info, void* Function) override; virtual void ReturnCSharpFunctionCallback(const void* Info, puerts::FuncPtr Callback, int64_t Data) override; + + virtual void ReturnCSharpFunctionCallback(const void* Info, puerts::FuncPtr Callback, puerts::FuncPtr Finalize, int64_t Data) override; virtual void ReturnJSObject(const void* Info, void* Object) override; //-------------------------- end js call cs -------------------------- @@ -805,6 +807,25 @@ void V8Plugin::ReturnCSharpFunctionCallback(const void* pInfo, puerts::FuncPtr C Info.GetReturnValue().Set(jsEngine.ToTemplate(Isolate, false, (PUERTS_NAMESPACE::CSharpFunctionCallback)Callback, Data)->GetFunction(Context).ToLocalChecked()); } +void V8Plugin::ReturnCSharpFunctionCallback(const void* pInfo, puerts::FuncPtr Callback, puerts::FuncPtr Finalize, int64_t Data) +{ + v8::Isolate* Isolate = jsEngine.MainIsolate; + const v8::FunctionCallbackInfo& Info = *(const v8::FunctionCallbackInfo*)pInfo; +#ifdef THREAD_SAFE + v8::Locker Locker(Isolate); +#endif + v8::Isolate::Scope IsolateScope(Isolate); + v8::HandleScope HandleScope(Isolate); + v8::Local Context = jsEngine.ResultInfo.Context.Get(Isolate); + v8::Context::Scope ContextScope(Context); + + auto Func = jsEngine.CreateFunction((PUERTS_NAMESPACE::CSharpFunctionCallback)Callback, (PUERTS_NAMESPACE::JsFunctionFinalizeCallback)Finalize, Data); + if (!Func.IsEmpty()) + { + Info.GetReturnValue().Set(Func.ToLocalChecked()); + } +} + void V8Plugin::ReturnJSObject(const void* pInfo, void* pObject) { v8::Isolate* Isolate = jsEngine.MainIsolate; diff --git a/unity/native_src/Src/Puerts.cpp b/unity/native_src/Src/Puerts.cpp index 91c0bf82971..7df4dd754c2 100644 --- a/unity/native_src/Src/Puerts.cpp +++ b/unity/native_src/Src/Puerts.cpp @@ -642,6 +642,24 @@ V8_EXPORT void ReturnCSharpFunctionCallback(v8::Isolate* Isolate, const v8::Func Info.GetReturnValue().Set(JsEngine->ToTemplate(Isolate, false, Callback, Data)->GetFunction(Context).ToLocalChecked()); } +V8_EXPORT void ReturnCSharpFunctionCallback2(v8::Isolate* Isolate, const v8::FunctionCallbackInfo& Info, puerts::CSharpFunctionCallback Callback, puerts::JsFunctionFinalizeCallback Finalize, int64_t Data) +{ + auto JsEngine = FV8Utils::IsolateData(Isolate); +#ifdef THREAD_SAFE + v8::Locker Locker(Isolate); +#endif + v8::Isolate::Scope IsolateScope(Isolate); + v8::HandleScope HandleScope(Isolate); + v8::Local Context = JsEngine->ResultInfo.Context.Get(Isolate); + v8::Context::Scope ContextScope(Context); + + auto Func = JsEngine->CreateFunction(Callback, Finalize, Data); + if (!Func.IsEmpty()) + { + Info.GetReturnValue().Set(Func.ToLocalChecked()); + } +} + V8_EXPORT void ReturnJSObject(v8::Isolate* Isolate, const v8::FunctionCallbackInfo& Info, puerts::JSObject *Object) { Info.GetReturnValue().Set(Object->GObject.Get(Isolate)); diff --git a/unity/test/Src/Cases/CrossLang/GenericTest.cs b/unity/test/Src/Cases/CrossLang/GenericTest.cs index 6fa62619b4e..aa68bc774c3 100644 --- a/unity/test/Src/Cases/CrossLang/GenericTest.cs +++ b/unity/test/Src/Cases/CrossLang/GenericTest.cs @@ -218,6 +218,8 @@ public void CreateFunctionByMethodInfoTest() "); Assert.AreEqual(ret, "Int321024"); + jsEnv.Eval("gc()"); + jsEnv.Tick(); } }