microsoft / ClearScript

A library for adding scripting to .NET applications. Supports V8 (Windows, Linux, macOS) and JScript/VBScript (Windows).
https://microsoft.github.io/ClearScript/
MIT License
1.74k stars 148 forks source link

How to respect Host funtions's default value? #432

Closed TomasPospisil closed 1 year ago

TomasPospisil commented 1 year ago

Hello,

I am using ClearScript pretty regularly, and everything is great. But now I am struggling with optional parameters and need some help. Let's assume I have a function in C#

public void Write(string resource, string content = "default text", string encoding = "utf-8") {
   // do some work
}

Is it possible somehow to respect default values of parameters while using undefined as argument from JS?

What I need is, e.g. write("my/path", undefined, "utf-16be"); would actually call keep default text as default value for content parameter. I know, there is property engine.UndefinedImportValue which is set to Undefined.Value by default , but with this, it will throw exception that the function I am calling does not exist, which makes sence. I can set engine.UndefinedImportValue = null, which works pretty fine, however content property will be mapped to null, and with that I am loosing the default value.

Am I missing something, or there is no support for this?

ClearScriptLib commented 1 year ago

Hi @TomasPospisil,

One possibility is to implement a facade or extension method that provides the behavior you're looking for. Let's say your C# class looks like this:

public class MyClass {
    public void Write(string resource, string content = "default text", string encoding = "utf-8") {
        Console.WriteLine($"'{resource}' '{content}' '{encoding}'");
    }
}

Using standard object.method(arg1, arg2, ...) syntax, there's no way to specify an argument for encoding while retaining the default argument for content – not even in C#. However, you can provide an extension method that uses C#'s alternate syntax:

public static class MyClassExtensions {
    public static void Write(this MyClass myObj, string resource, Undefined content, string encoding = "utf-8") {
        myObj.Write(resource, encoding: encoding);
    }
}

And now all argument combinations should work:

engine.AddHostObject("myObj", new MyClass());
engine.AddHostType(typeof(MyClassExtensions));
engine.Execute(@"
    myObj.Write('foo');                       // OUTPUT: 'foo' 'default text' 'utf-8'
    myObj.Write('foo', 'bar');                // OUTPUT: 'foo' 'bar' 'utf-8'
    myObj.Write('foo', 'bar', 'utf-16');      // OUTPUT: 'foo' 'bar' 'utf-16'
    myObj.Write('foo', undefined);            // OUTPUT: 'foo' 'default text' 'utf-8'
    myObj.Write('foo', undefined, 'utf-16');  // OUTPUT: 'foo' 'default text' 'utf-16'
");

Good luck!

TomasPospisil commented 1 year ago

Hello,

thank you for quick reply! JS standard supports this approach (https://www.typescripttutorial.net/typescript-tutorial/typescript-default-parameters), so if you have a function like this:

function write(path, content = "default text") {
   console.log(content);
}

then calling write("my/path", undefined) will result in output default text

I was therefore hoping, there is a way to do this easily with ClearScript without neccesity to have extension methods for them since we have a lot of methods supporting optional parameters.

I think it would be very nice feature :) However thanks a lot for your help!

ClearScriptLib commented 1 year ago

Hi @TomasPospisil,

Yes, JavaScript lets you to use undefined as an alias for default arguments, but C# has no similar capability, and ClearScript uses (portions of) the C# compiler for method binding.

Unlike JavaScript, C# supports things like overloads, generic methods, parameter arrays, out/ref arguments, named parameters, etc. Its method binder is very complex, but it simply doesn't support default argument aliasing. And because the target method isn't known upfront, ClearScript has no way to perform the substitution itself.

Sorry!

TomasPospisil commented 1 year ago

Hello,

sure, I understand that it would not be easy to implement this in Clearscript. Thanks for your reply and your help!