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 have a return type known only at runtime? #437

Closed vbornand closed 1 year ago

vbornand commented 1 year ago

Hi,

I have a very specific use case, I want to return an object with a specific type only known on the runtime, and I don't want the javascript caller need to specify it.

I have currently the 2 methods:

[ScriptUsage]
public T GetDeviceByCode2<T>(string code) where T : class
{
    return this.GetDeviceByCode(code) as T;
}

[ScriptUsage]
public object GetDeviceByCode3(string code)
{ 
    return this.GetDeviceByCode(code) as ITelegramBot; //ITelegramBot is here hardcoded, but in real world, the type is not always the same and depends of the parameter value.
}

The first method works well, but I need to specify the type (T) in the javascript:

var bot = house.getDeviceByCode2(ITelegramBot, "MyBot");

Only the methods defined on the ITelegramBot interface can be invoked from the javascript. It is the expected behavior.

With the second method, I don't need to specify the type in the javascript:

var bot = house.getDeviceByCode3("MyBot");

But here all the methods of the runtime object are visible and can be called. And I would like to limit the methods callable.

How can I have receive the same object returned by the method the method getDeviceByCode2 in the method getDeviceByCode3 ?

To complexify, the type ITelegramBot provides from a plugin, and I don't want the plugin have a reference on ClearScript, so I can't use the ScriptAccessattributes.

Thanks for your help.

ClearScriptLib commented 1 year ago

Hi @vbornand,

How can I have receive the same object returned by the method getDeviceByCode2

ClearScript provides an extension method – ToRestrictedHostObject – that may be of assistance. For example:

[ScriptUsage]
public object GetDeviceByCode3(string code)
{ 
    return this.GetDeviceByCode(code).ToRestrictedHostObject<ITelegramBot>();
}

Please note that ToRestrictedHostObject returns a host object – an instance of an internal ClearScript class intended to be passed or returned to script code. Such an object isn't useful to the host application.

To complexify, the type ITelegramBot provides from a plugin, and I don't want the plugin have a reference on ClearScript, so I can't use the ScriptAccess attributes.

There's a way to "virtually" add ClearScript attributes to external resources. You can read about it here.

Good luck!

vbornand commented 1 year ago

Thanks @ClearScriptLib for your quick answer.

I successfully attended my goal using the ToRestrictedHostObject function. (Using reflection before to call it, because ITelegramBot is defined in a plugin and not known at the compile time by the app hosting the script engine.)

It's not clean code, but if it can help somebody:

[ScriptUsage]
public object GetDeviceByCode3(string code)
{
    var tBot = this.GetDeviceByCode(code);

    var telegramBotType = this.sharedTypes.First(t => t.Name.Contains("ITelegramBot"));

    //Dirty way to get the method
    var ext = typeof(Microsoft.ClearScript.Extensions)
             .GetMethods(BindingFlags.Static | BindingFlags.Public)
             .FirstOrDefault(m => m.Name == "ToRestrictedHostObject" && m.GetParameters().Length == 2);
    var r = ext.MakeGenericMethod(telegramBotType).Invoke(null, new object[] { tBot, jsEngine });

    return r;
}

If the method ToRestrictedHostObject provided an overload with the type as parameter (and not only available with genericity), it would be great.

Thanks for the link about to add attributes on external resources. Maybe it is the best solution, and I can use pretty complex logic to know on which methods are script visible depending which interface the method implements. I think it would be a cleaner solution as the one with the ToRestrictedHostObject.

Thanks again

Valentin

ClearScriptLib commented 1 year ago

Hi Valentin,

If the method ToRestrictedHostObject provided an overload with the type as parameter (and not only available with genericity), it would be great.

Good idea! We'll add that in the next release.

Thank you!

ClearScriptLib commented 1 year ago

Version 7.3.5 added a pair of ToRestrictedHostObject overloads.