I'm learning of how to use Jint (version 4.2.1), but there is something I probably missed.
We can register an Action like this :
var engine = new Engine()
.SetValue("log", new Action<object>(Console.WriteLine));
engine.Execute(@"
function hello() {
log('Hello World');
};
hello();
");
but when I add a call to a log with other arguments like log('Hello', true);
it seems the second argument is not catched.
Then if I register a class like this in the engine, a call like console.log('A', 'B', 3, false)
works absolutelly fine.
internal class ConsoleObject
{
public void Log(string message, params object[] args)
{
if (args != null)
message += " " + string.Join(' ', args);
Console.WriteLine("[INTERNAL LOG JS] " + message, args);
}
}
I wanted to explore also extensible objects, but the methods are not accessible from the JS anymore, we have to add them to the Engine using the FastSetProperty
so I tried to register my log function, and found many examples using ClrFunctionInstance, but it doesn't seem to be part of Jint (in the newest version at least).
I currently have this class :
internal class ConsoleObject : ObjectInstance
{
public override bool Extensible => true;
public ConsoleObject(Engine engine) : base(engine)
{
AddMethod("log", Console.WriteLine);
AddMethod("warn", msg => Console.WriteLine("[WARN] " + msg));
AddMethod("error", msg => Console.WriteLine("[ERROR] " + msg));
AddMethod("debug", msg => Console.WriteLine("[DEBUG] " + msg));
}
private void AddMethod(string name, Action<string> action)
{
// This is the equivalent to my previous method public void Log(string message, params object[] args)
Func<JsValue[], JsValue> method = (args) =>
{
var output = args != null
? string.Join(" ", args.Select(a => a.IsObject() ? a.ToString() : a.ToString()))
: "";
action("[INTERNAL] " + output);
return JsValue.Undefined;
};
Func<JsValue, JsValue[], JsValue> delegateWrapper = (thisObj, args) =>
{
// If args are null or empty, fallback to thisObj as arg[0]
List<JsValue> fixedArgs = [thisObj];
if (args?.Length > 0)
{
fixedArgs.AddRange(args);
}
return method([.. fixedArgs]);
};
JsValue fn = JsValue.FromObject(Engine, delegateWrapper);
var descriptor = new PropertyDescriptor(fn, writable: true, enumerable: true, configurable: true);
FastSetProperty(name, descriptor);
}
}
For this, I created a delegateWrapper to mix the arguments, but the delegate arguments must match the one called in the JS. I'm trying to find a generic way, not only for logs, that's why the code looks a bit complex for a simple log.
If I call log('Hello', true);
it throws an exception because it is expecting an array of object, but recieves a bool.
If I call log('Hello', [true]);
this works because it matchs my delegate argument.
So, my question is, how can I mix the extensible class and methods with dynamic arguments ? Maybe the inherance from ObjectInstance is not a good choice.