Skip to content

Commit

Permalink
feat: redirect original console
Browse files Browse the repository at this point in the history
allow to config
```json
"EnableLogger":true
```
  • Loading branch information
LazuliKao committed Jul 29, 2023
1 parent fcf85a4 commit e7ca448
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 18 deletions.
2 changes: 2 additions & 0 deletions src/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ internal static class Config
internal class ConfigData
{
public bool EnableEval { get; set; } = false;

public bool EnableLogger { get; set; } = true;
}

internal static ConfigData Data { get; private set; }
Expand Down
25 changes: 25 additions & 0 deletions src/Hook/JsLog/ContextObjectBindPrint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Hosihikari.NativeInterop.Hook.ObjectOriented;

Check failure on line 1 in src/Hook/JsLog/ContextObjectBindPrint.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;
using Hosihikari.VanillaScript.QuickJS.Types;

namespace Hosihikari.VanillaScript.Hook.JsLog;

internal class ContextObjectBindPrint : HookBase<ContextObjectBindPrint.HookDelegate>
{
internal unsafe delegate JsValue HookDelegate(void* contextObject, JsContext* ctx);

public ContextObjectBindPrint()
: base("_ZN9Scripting7QuickJS13ContextObject10_bindPrintEP9JSContext") { }

public override unsafe HookDelegate HookedFunc =>
(contextObject, ctx) =>
{
if (Config.Data.EnableLogger)
{
using var globalObject = Native.JS_GetGlobalObject(ctx);
JsLog.Bind(ctx, globalObject.Value);
return globalObject.Value;
}
return Original.Invoke(contextObject, ctx);
};
}
172 changes: 172 additions & 0 deletions src/Hook/JsLog/JsLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using System.Runtime.InteropServices;
using System.Text;
using Hosihikari.VanillaScript.QuickJS;
using Hosihikari.VanillaScript.QuickJS.Extensions;
using Hosihikari.VanillaScript.QuickJS.Helper;
using Hosihikari.VanillaScript.QuickJS.Types;

namespace Hosihikari.VanillaScript.Hook.JsLog;

internal class JsLog
{
public static unsafe void Bind(JsContext* ctx, JsValue globalObject)
{
using var consoleInstance = Native.JS_NewObject(ctx, false);
var console = consoleInstance.Value;
#region Trace
console.DefineFunction(ctx, "trace", &PrintTrace, 1, flags: JsPropertyFlags.Normal);
[UnmanagedCallersOnly]
static JsValue PrintTrace(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
{
try
{
var (file, line) = GetJsSourceInfo(ctx);
Log.Logger.Trace(
ParseLog(ctx, new ReadOnlySpan<JsValue>(argvIn, argCount)),
sourceFile: file,
sourceLine: line
);
}
catch (Exception ex)
{
Log.Logger.Error("PrintTrace", "Invoke Failed", ex);
}
return JsValueCreateHelper.Undefined;
}
#endregion
#region Info
globalObject.DefineFunction(ctx, "print", &PrintInfo, 1, flags: JsPropertyFlags.Normal);
console.DefineFunction(ctx, "log", &PrintInfo, 1, flags: JsPropertyFlags.Normal);
console.DefineFunction(ctx, "info", &PrintInfo, 1, flags: JsPropertyFlags.Normal);
[UnmanagedCallersOnly]
static JsValue PrintInfo(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
{
try
{
Log.Logger.Info(ParseLog(ctx, new ReadOnlySpan<JsValue>(argvIn, argCount)));
}
catch (Exception ex)
{
Log.Logger.Error("PrintInfo", "Invoke Failed", ex);
}
return JsValueCreateHelper.Undefined;
}
#endregion
#region Debug
console.DefineFunction(ctx, "debug", &PrintDebug, 1, flags: JsPropertyFlags.Normal);
[UnmanagedCallersOnly]
static JsValue PrintDebug(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
{
try
{
Log.Logger.Debug(ParseLog(ctx, new ReadOnlySpan<JsValue>(argvIn, argCount)));
}
catch (Exception ex)
{
Log.Logger.Error("PrintDebug", "Invoke Failed", ex);
}
return JsValueCreateHelper.Undefined;
}
#endregion
#region Warn
console.DefineFunction(ctx, "warn", &PrintWarn, 1, flags: JsPropertyFlags.Normal);
[UnmanagedCallersOnly]
static JsValue PrintWarn(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
{
try
{
Log.Logger.Warn(ParseLog(ctx, new ReadOnlySpan<JsValue>(argvIn, argCount)));
}
catch (Exception ex)
{
Log.Logger.Error("PrintWarn", "Invoke Failed", ex);
}
return JsValueCreateHelper.Undefined;
}
#endregion
#region Error
console.DefineFunction(ctx, "error", &PrintError, 1, flags: JsPropertyFlags.Normal);
[UnmanagedCallersOnly]
static JsValue PrintError(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
{
try
{
var (file, line) = GetJsSourceInfo(ctx);
Log.Logger.Error(
ParseLog(ctx, new ReadOnlySpan<JsValue>(argvIn, argCount)),
sourceFile: file,
sourceLine: line
);
}
catch (Exception ex)
{
Log.Logger.Error("PrintError", "Invoke Failed", ex);
}
return JsValueCreateHelper.Undefined;
}
#endregion
#region Assert
//todo Assert
//console.DefineFunction(ctx, "assert", &Assert, 2, flags: JsPropertyFlags.Normal);

//[UnmanagedCallersOnly]
//static JsValue Assert(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
//{
// var argv = new ReadOnlySpan<JsValue>(argvIn, argCount);
// if (argv[0].IsFalsey(ctx))
// {
// Log.Logger.Error(ParseLog(ctx, argv.Slice(1)));
// }
// return JsValueCreateHelper.Undefined;
//}
#endregion
#region Clear
console.DefineFunction(ctx, "clear", &Clear, 0, flags: JsPropertyFlags.Normal);
[UnmanagedCallersOnly]
static JsValue Clear(JsContext* ctx, JsValue thisObj, int argCount, JsValue* argvIn)
{
Console.Clear();
return JsValueCreateHelper.Undefined;
}
#endregion
globalObject.DefineProperty(ctx, "console", console, flags: JsPropertyFlags.Normal);
}

static unsafe (string file, int line) GetJsSourceInfo(JsContext* ctx)
{
try //get full stack from Error
{
Native.JS_ThrowError(ctx, "");
var error = Native.JS_GetException(ctx);
var stack = error.Value.GetStringProperty(ctx, "stack");
var lines = stack.Split('\n');
var file = lines[1].Split(' ')[1];
var line = int.Parse(lines[1].Split(' ')[2]);
return (file, line);
}
catch //get only file name from JS_GetScriptOrModuleName
{
var file = Native.JS_GetScriptOrModuleName(ctx, 1);
return (file, -1);
}
}

static unsafe string ParseLog(JsContext* ctx, ReadOnlySpan<JsValue> argv)
{
var sb = new StringBuilder();
foreach (var arg in argv)
{
sb.Append(arg.ToString(ctx));
if (arg.Data.tag == JsTag.Object && Native.JS_IsError(ctx, arg))
{
sb.AppendLine();
sb.AppendLine(arg.GetStringProperty(ctx, "stack"));
}
else
{
sb.Append(" ");
}
}
return sb.ToString();
}
}
19 changes: 19 additions & 0 deletions src/Hook/RequestReload.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/RequestReload.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?)

namespace Hosihikari.VanillaScript.Hook
{
internal class RequestReload : HookBase<RequestReload.HookDelegate>
{
internal unsafe delegate void HookDelegate(void* minecraft);

public RequestReload()
: base("_ZN9Minecraft21requestResourceReloadEv") { }

public override unsafe HookDelegate HookedFunc =>
minecraft =>
{
Original.Invoke(minecraft);
Config.Reload();
};
}
}
2 changes: 1 addition & 1 deletion src/Loader/CustomScriptLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void LoadScript(string path)
{
var bytes = File.ReadAllText(path);
var relativePath = Path.GetRelativePath(pluginsDir, path);
var ret = Native.JS_Eval(ctx, relativePath, bytes);
using var ret = Native.JS_Eval(ctx, relativePath, bytes);
ScriptLoaded?.Invoke(
(nint)ctx,
new ScriptLoadedEventArgs(
Expand Down
1 change: 1 addition & 0 deletions src/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public void Initialize(AssemblyPlugin plugin)
new Hook.QuickJS.Eval().Install();
new Hook.QuickJS.FreeContext().Install();
new Hook.QuickJS.AddIntrinsicBaseObjects().Install();
new Hook.JsLog.ContextObjectBindPrint().Install();
Assets.Prepare.Init();
}
}
4 changes: 4 additions & 0 deletions src/QuickJS/Exceptions/QuickJSException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public override string? StackTrace
var originalStackTrace = base.StackTrace;
if (JsStack is not null)
{
if (originalStackTrace is null)
{
return JsStack;
}
return JsStack + Environment.NewLine + originalStackTrace;
}
return originalStackTrace;
Expand Down
15 changes: 12 additions & 3 deletions src/QuickJS/Extensions/JsValueExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public static unsafe string GetStringProperty(
string propertyName
)
{
var val = Native.JS_GetPropertyStr(ctx, @this, propertyName);
using var val = Native.JS_GetPropertyStr(ctx, @this, propertyName);
return Native.JS_ToCString(ctx, val.Value);
}

Expand Down Expand Up @@ -104,10 +104,19 @@ public static unsafe bool DefineFunction(
int argumentLength,
JscFunctionEnum cproto = JscFunctionEnum.Generic,
int magic = 0,
JsPropertyFlags flags = JsPropertyFlags.CWE
JsPropertyFlags flags = JsPropertyFlags.CWE,
bool autoDrop = false
)
{
var value = Native.JS_NewCFunction2(ctx, func, funcName, argumentLength, cproto, magic);
using var value = Native.JS_NewCFunction2(
ctx,
func,
funcName,
argumentLength,
cproto,
magic,
autoDrop
);
return Native.JS_DefinePropertyValueStr(ctx, @this, funcName, value.Value, flags);
}

Expand Down
Loading

0 comments on commit e7ca448

Please sign in to comment.