Skip to content

Commit

Permalink
feat: add more for FreeRuntime
Browse files Browse the repository at this point in the history
  • Loading branch information
LazuliKao committed Jul 31, 2023
1 parent 76823b0 commit 29e98ed
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 21 deletions.
5 changes: 3 additions & 2 deletions src/Hook/QuickJS/FreeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ public FreeContext()
if (ctx is null)
return;
//ref #L2278
if (--ctx->header.ref_count > 0)
return;
Log.Logger.Trace(
"JS_FreeContext ctx: " + (nint)ctx + " ctx->refCount: " + ctx->header.ref_count
);
if (--ctx->header.ref_count > 0)
return;
Loader.Manager.FreeContext(ctx);
Original.Invoke(ctx);
};
Expand Down
19 changes: 19 additions & 0 deletions src/Hook/QuickJS/FreeRuntime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Hosihikari.NativeInterop.Hook.ObjectOriented;

Check failure on line 1 in src/Hook/QuickJS/FreeRuntime.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'NativeInterop' does not exist in the namespace 'Hosihikari' (are you missing an assembly reference?)
using Hosihikari.VanillaScript.QuickJS.Types;

namespace Hosihikari.VanillaScript.Hook.QuickJS;

internal class FreeRuntime : HookBase<FreeRuntime.HookDelegate>
{
internal unsafe delegate void HookDelegate(JsRuntime* rt);

public FreeRuntime()
: base("JS_FreeRuntime") { }

public override unsafe HookDelegate HookedFunc =>
rt =>
{
Loader.Manager.FreeRuntime(rt);
Original.Invoke(rt);
};
}
21 changes: 21 additions & 0 deletions src/Hook/QuickJS/NewRuntime2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Hosihikari.NativeInterop.Hook.ObjectOriented;

Check failure on line 1 in src/Hook/QuickJS/NewRuntime2.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'NativeInterop' does not exist in the namespace 'Hosihikari' (are you missing an assembly reference?)
using Hosihikari.VanillaScript.QuickJS.Types;

namespace Hosihikari.VanillaScript.Hook.QuickJS;

internal class NewRuntime2 : HookBase<NewRuntime2.HookDelegate>
{
internal unsafe delegate JsRuntime* HookDelegate(JsMallocFunctions* mf, void* opaque);

public NewRuntime2()
: base("JS_NewRuntime2") { }

public override unsafe HookDelegate HookedFunc =>
(mf, opaque) =>
{
var runtime = Original(mf, opaque);
Loader.Manager.AddRuntime(runtime);
return runtime;
};
}
//JS_FreeRuntime
3 changes: 2 additions & 1 deletion src/Hook/RequestReload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ public RequestReload()
public override unsafe HookDelegate HookedFunc =>
minecraft =>
{
Original.Invoke(minecraft);
Config.Reload();
Loader.Manager.FreeAllContext();
Original.Invoke(minecraft);
};
}
}
51 changes: 51 additions & 0 deletions src/Loader/Manager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Hosihikari.VanillaScript.Loader;
public static partial class Manager
{
internal static readonly List<JsContextWrapper> LoadedScriptsContext = new();
internal static readonly List<JsRuntimeWrapper> LoadedScriptsRuntime = new();

internal static unsafe void AddContext(JsContext* ctx, bool isLoaderContext)
{
Expand All @@ -18,8 +19,32 @@ internal static unsafe void AddContext(JsContext* ctx, bool isLoaderContext)
}
}

internal static void FreeAllContext()
{
unsafe
{
foreach (var toFree in LoadedScriptsContext.ToArray())
{
try
{
toFree.Free();
}
catch (Exception ex)
{
Log.Logger.Error("Free JsContext " + ((nint)toFree.Context).ToString("X"), ex);
}
finally
{
LoadedScriptsContext.Remove(toFree);
}
}
}
Log.Logger.Trace("Free All JsContext.");
}

internal static unsafe void FreeContext(JsContext* ctx)
{
Log.Logger.Trace("JsContext Free " + ((nint)ctx).ToString("X"));
foreach (var toFree in LoadedScriptsContext.FindAll(x => x.Context == ctx).ToArray())
{
try
Expand All @@ -36,4 +61,30 @@ internal static unsafe void FreeContext(JsContext* ctx)
}
}
}

internal static unsafe void AddRuntime(JsRuntime* rt)
{
var rtInstance = JsRuntimeWrapper.FetchOrCreate(rt); //add to LoadedScriptsRuntime
Log.Logger.Trace("JsRuntime Add" + ((nint)rtInstance.Runtime).ToString("X"));
}

internal static unsafe void FreeRuntime(JsRuntime* rt)
{
foreach (var toFree in LoadedScriptsRuntime.FindAll(x => x.Runtime == rt).ToArray())
{
try
{
Log.Logger.Trace("JsRuntime Free" + ((nint)rt).ToString("X"));
toFree.Free();
}
catch (Exception ex)
{
Log.Logger.Error("Free JsRuntime " + ((nint)rt).ToString("X"), ex);
}
finally
{
LoadedScriptsRuntime.Remove(toFree);
}
}
}
}
13 changes: 13 additions & 0 deletions src/Loader/SetupRuntime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Hosihikari.VanillaScript.QuickJS.Wrapper;

namespace Hosihikari.VanillaScript.Loader;

public static partial class Manager
{
public static event Action<JsRuntimeWrapper>? OnRuntimeCreated;

internal static void SetupRuntime(JsRuntimeWrapper ctx)
{
OnRuntimeCreated?.Invoke(ctx);
}
}
3 changes: 3 additions & 0 deletions src/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ public void Initialize(AssemblyPlugin plugin)
new EnableScriptingHook().Install();
new Hook.QuickJS.Eval().Install();
new Hook.QuickJS.FreeContext().Install();
new Hook.QuickJS.FreeRuntime().Install();
new Hook.QuickJS.NewRuntime2().Install();
new Hook.QuickJS.AddIntrinsicBaseObjects().Install();
new Hook.RequestReload().Install();
new Hook.JsLog.ContextObjectBindPrint().Install();
Assets.Prepare.Init();
}
Expand Down
4 changes: 2 additions & 2 deletions src/QuickJS/Helper/JsPromiseHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static void AwaitTask(
Task tasks
)
{
var safeThis = new SafeJsValue(thisObj);
var safeThis = new SafeJsValue(thisObj, ctxPtr);
Task.Run(async () =>
{
try
Expand Down Expand Up @@ -105,7 +105,7 @@ public static void AwaitTask<T>(
Func<T, JsValue> fetchResult
)
{
var safeThis = new SafeJsValue(thisObj);
var safeThis = new SafeJsValue(thisObj, ctxPtr);
Task.Run(async () =>
{
try
Expand Down
3 changes: 3 additions & 0 deletions src/QuickJS/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,9 @@ out SafeJsValue reject
var result = func(ctx, resolvingFunc);
if (result.IsException())
ThrowPendingException(ctx);
Console.WriteLine("resolve " + resolvingFunc[0].GetRefCount());
Console.WriteLine("reject " + resolvingFunc[1].GetRefCount());
Console.WriteLine("result " + result.GetRefCount());
resolve = new SafeJsValue(new AutoDropJsValue(resolvingFunc[0], ctx));
reject = new SafeJsValue(new AutoDropJsValue(resolvingFunc[1], ctx));
return new AutoDropJsValue(result, ctx);
Expand Down
26 changes: 26 additions & 0 deletions src/QuickJS/Types/JsMallocFunctions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Hosihikari.VanillaScript.QuickJS.Types;

using size_t = UIntPtr;

public ref struct JsMallocState
{
public size_t MallocCount;
public size_t MallocSize;
public size_t MallocLimit;
public unsafe void* Opaque; /* user opaque */
}

public unsafe ref struct JsMallocFunctions
{
//void *(*js_malloc)(JSMallocState *s, size_t size);
public delegate* unmanaged<JsMallocState*, nuint, void*> JsMalloc;

//void (*js_free)(JSMallocState *s, void *ptr);
public delegate* unmanaged<JsMallocState*, void*, void> JsFree;

//void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
public delegate* unmanaged<JsMallocState*, void*, nuint, void*> JsReAlloc;

//size_t (*js_malloc_usable_size)(const void *ptr);
public delegate* unmanaged<void*, size_t> JsMallocUsableSize;
}
15 changes: 9 additions & 6 deletions src/QuickJS/Wrapper/AutoDropJsValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ public unsafe AutoDropJsValue(JsValue value, JsContext* context)
{
_value = value;
_context = context;
if (JsContextWrapper.TryGet((nint)context, out var tCtx))
{
tCtx.FreeContextCallback += FreeThis;
}
}

/// <summary>
Expand All @@ -44,24 +48,23 @@ public JsValue Steal()

public void Dispose()
{
ReleaseUnmanagedResources();
FreeThis();
GC.SuppressFinalize(this); //prevent call ~SafeJsValue()
}

~AutoDropJsValue()
{
ReleaseUnmanagedResources();
FreeThis();
}

//bool _disposed = false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReleaseUnmanagedResources()
private void FreeThis()
{
unsafe
{
//if (_disposed)
// return;
//_disposed = true;
if (JsContextWrapper.TryGet((nint)_context, out var tCtx))
tCtx.FreeContextCallback -= FreeThis;
#if DEBUG
//var stack = Environment.StackTrace;
//LevelTick.PostTick(() =>
Expand Down
28 changes: 23 additions & 5 deletions src/QuickJS/Wrapper/JsContextWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using Hosihikari.VanillaScript.Loader;
using Hosihikari.VanillaScript.QuickJS.Helper;
Expand All @@ -10,11 +9,12 @@ namespace Hosihikari.VanillaScript.QuickJS.Wrapper;
public class JsContextWrapper
{
public unsafe JsContext* Context { get; }
private List<GCHandle> savedObject = new();
private readonly List<GCHandle> _savedObject = new();
public event Action? FreeContextCallback;

internal void Pin(object obj)
{
savedObject.Add(GCHandle.Alloc(obj));
_savedObject.Add(GCHandle.Alloc(obj));
}

public static unsafe implicit operator JsContextWrapper(JsContext* ctx)
Expand Down Expand Up @@ -57,11 +57,29 @@ public static unsafe JsContextWrapper FetchOrCreate(JsContext* ctx)
return newInstance;
}

private bool _freed = false;

internal void ThrowIfFree()
{
if (_freed)
{
throw new ObjectDisposedException("JsContextWrapper");
}
}

internal void Free()
{
foreach (var pinedItem in savedObject)
try
{
foreach (var pinedItem in _savedObject)
{
pinedItem.Free();
}
FreeContextCallback?.Invoke();
}
finally
{
pinedItem.Free();
_freed = true;
}
}

Expand Down
65 changes: 65 additions & 0 deletions src/QuickJS/Wrapper/JsRuntimeWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Hosihikari.VanillaScript.QuickJS.Types;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Hosihikari.VanillaScript.Loader;

namespace Hosihikari.VanillaScript.QuickJS.Wrapper;

public class JsRuntimeWrapper
{
public unsafe JsRuntime* Runtime { get; }
private readonly List<GCHandle> _savedObject = new();

internal void Pin(object obj)
{
_savedObject.Add(GCHandle.Alloc(obj));
}

public static unsafe implicit operator JsRuntimeWrapper(JsRuntime* rt)
{
return FetchOrCreate(rt);
}

private unsafe JsRuntimeWrapper(JsRuntime* rt)
{
Runtime = rt;
Manager.SetupRuntime(this);
}

public static bool TryGet(nint ctxPtr, [NotNullWhen(true)] out JsRuntimeWrapper? ctx)
{
unsafe
{
if (
Manager.LoadedScriptsRuntime.FirstOrDefault(x => x.Runtime == ctxPtr.ToPointer()) is
{ } oldCtx
)
{
ctx = oldCtx;
return true;
}

ctx = null;
return false;
}
}

public static unsafe JsRuntimeWrapper FetchOrCreate(JsRuntime* ctx)
{
if (Manager.LoadedScriptsRuntime.FirstOrDefault(x => x.Runtime == ctx) is { } oldCtx)
{
return oldCtx;
}
var newInstance = new JsRuntimeWrapper(ctx);
Manager.LoadedScriptsRuntime.Add(newInstance);
return newInstance;
}

internal void Free()
{
foreach (var pinedItem in _savedObject)
{
pinedItem.Free();
}
}
}
Loading

0 comments on commit 29e98ed

Please sign in to comment.