Skip to content

Commit

Permalink
[unity]pinvoke版本的createFunction生成的函数gc后原生以及c#内存的释放
Browse files Browse the repository at this point in the history
  • Loading branch information
chexiongsheng committed Dec 18, 2024
1 parent 5937b65 commit eeb3b91
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 3 deletions.
22 changes: 20 additions & 2 deletions unity/Assets/core/upm/Runtime/Src/Default/JsEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<JSConstructorCallback> constructorCallbacks = new List<JSConstructorCallback>();

internal IntPtr InvokeConstructor(IntPtr isolate, int callbackIdx, IntPtr info, int paramLen)
Expand Down Expand Up @@ -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)
{
Expand Down
21 changes: 20 additions & 1 deletion unity/Assets/core/upm/Runtime/Src/Default/Native/PuertsDLL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions unity/native_src/Inc/IPuertsPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 --------------------------
Expand Down
20 changes: 20 additions & 0 deletions unity/native_src/Inc/JSEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ typedef char* (*CSharpModuleResolveCallback)(const char* identifer, int32_t jsEn
typedef void(*CSharpFunctionCallback)(puerts::IPuertsPlugin* plugin, const v8::FunctionCallbackInfo<v8::Value>& Info, void* Self, int ParamLen, int64_t UserData);

typedef void* (*CSharpConstructorCallback)(puerts::IPuertsPlugin* plugin, const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Value>& Info, void* Self, int ParamLen, int64_t UserData);

typedef void* (*CSharpConstructorCallback)(v8::Isolate* Isolate, const v8::FunctionCallbackInfo<v8::Value>& Info, int ParamLen, int64_t UserData);

typedef void (*JsFunctionFinalizeCallback)(v8::Isolate* Isolate, int64_t UserData);
#endif

typedef void(*CSharpDestructorCallback)(void* Self, int64_t UserData);
Expand All @@ -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<v8::Function> JsFunction;
};

struct FLifeCycleInfo
{
FLifeCycleInfo(int InClassID, CSharpConstructorCallback InConstructor, CSharpDestructorCallback InDestructor, int64_t InData, int InSize)
Expand Down Expand Up @@ -142,6 +156,8 @@ class JSEngine
bool InspectorTick();

void LogicTick();

static void CallbackDataGarbageCollected(const v8::WeakCallbackInfo<FCallbackInfoWithFinalize>& Data);

v8::Isolate* MainIsolate;

Expand All @@ -164,6 +180,8 @@ class JSEngine

private:
std::vector<FCallbackInfo*> CallbackInfos;

std::vector<FCallbackInfoWithFinalize*> CallbackWithFinalizeInfos;

std::vector<FLifeCycleInfo*> LifeCycleInfos;

Expand Down Expand Up @@ -199,6 +217,8 @@ class JSEngine
JSFunction* GetModuleExecutor();

v8::Local<v8::FunctionTemplate> ToTemplate(v8::Isolate* Isolate, bool IsStatic, CSharpFunctionCallback Callback, int64_t Data);

v8::MaybeLocal<v8::Function> CreateFunction(CSharpFunctionCallback Callback, JsFunctionFinalizeCallback Finalize, int64_t Data);

std::string GetJSStackTrace();
};
Expand Down
59 changes: 59 additions & 0 deletions unity/native_src/Src/JSEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -437,6 +442,60 @@ namespace PUERTS_NAMESPACE
return v8::FunctionTemplate::New(Isolate, CSharpFunctionCallbackWrap, v8::External::New(Isolate, CallbackInfos[Pos]), v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kThrow);
#endif
}

void JSEngine::CallbackDataGarbageCollected(const v8::WeakCallbackInfo<FCallbackInfoWithFinalize>& Data)
{
FCallbackInfoWithFinalize* CallbackData = Data.GetParameter();
auto Isolate = Data.GetIsolate();
if (CallbackData->Finalize)
{
#ifdef MULT_BACKENDS
auto JsEngine = FV8Utils::IsolateData<JSEngine>(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<v8::Function> JSEngine::CreateFunction(CSharpFunctionCallback Callback, JsFunctionFinalizeCallback Finalize, int64_t Data)
{
v8::Isolate* Isolate = BackendEnv.MainIsolate;
v8::Local<v8::Context> 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<v8::Signature>(), 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<FCallbackInfoWithFinalize>(
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)
{
Expand Down
21 changes: 21 additions & 0 deletions unity/native_src/Src/PluginImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 --------------------------
Expand Down Expand Up @@ -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<v8::Value>& Info = *(const v8::FunctionCallbackInfo<v8::Value>*)pInfo;
#ifdef THREAD_SAFE
v8::Locker Locker(Isolate);
#endif
v8::Isolate::Scope IsolateScope(Isolate);
v8::HandleScope HandleScope(Isolate);
v8::Local<v8::Context> 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;
Expand Down
18 changes: 18 additions & 0 deletions unity/native_src/Src/Puerts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<v8::Value>& Info, puerts::CSharpFunctionCallback Callback, puerts::JsFunctionFinalizeCallback Finalize, int64_t Data)
{
auto JsEngine = FV8Utils::IsolateData<JSEngine>(Isolate);
#ifdef THREAD_SAFE
v8::Locker Locker(Isolate);
#endif
v8::Isolate::Scope IsolateScope(Isolate);
v8::HandleScope HandleScope(Isolate);
v8::Local<v8::Context> 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<v8::Value>& Info, puerts::JSObject *Object)
{
Info.GetReturnValue().Set(Object->GObject.Get(Isolate));
Expand Down
2 changes: 2 additions & 0 deletions unity/test/Src/Cases/CrossLang/GenericTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ public void CreateFunctionByMethodInfoTest()
");
Assert.AreEqual(ret, "Int321024");

jsEnv.Eval("gc()");

jsEnv.Tick();
}
}
Expand Down

0 comments on commit eeb3b91

Please sign in to comment.