Skip to content

A problem about exposing extension method from host object #198

Description

@qwerty472123

Firstly, I hope Task can be directly await in script without calling ToPromise() in addition, so I wrote the code below which provided then method for await to call:

public static class TaskExtension
{
	public static dynamic then(this Task task, dynamic ok, dynamic failed = null)
	{
		dynamic promise = task.ToPromise();
		return promise.then(ok, failed);
	}
}

However, after invoking engine.AddHostType(typeof(TaskExtension));, using await to any host object which does not have then method(or await any promise which return such object after resolving, or simply use a promise resolved by such object) will raise exception. Here are one example:

This code output no error:

public class Bird
{
}
class Program
{
	static void Main(string[] args)
	{
		V8ScriptEngine engine = new V8ScriptEngine();
		engine.AddHostType(typeof(Console));
		engine.AddHostObject("bird", new Bird());
		engine.Execute("Promise.resolve(bird).then(e=>{Console.WriteLine('no error');},e=>{Console.WriteLine(e.toString());})");
	}
}

This code output Error: 'ExampleApp.Bird' does not contain a definition for 'then':

public static class TaskExtension
{
	public static dynamic then(this Task task, dynamic ok, dynamic failed = null)
	{
		dynamic promise = task.ToPromise();
		return promise.then(ok, failed);
	}
}
public class Bird
{
}
class Program
{
	static void Main(string[] args)
	{
		V8ScriptEngine engine = new V8ScriptEngine();
		engine.AddHostType(typeof(Console));
		engine.AddHostType(typeof(TaskExtension));
		engine.AddHostObject("bird", new Bird());
		engine.Execute("Promise.resolve(bird).then(e=>{Console.WriteLine('no error');},e=>{Console.WriteLine(e.toString());})");
	}
}

In fact, this script engine will get then property of resolved value, and check whether this property is function. If so, the engine regard such value as another promise and call the property internally.

Here are an example which show the problem more clearly:

public class Bird
{
}
public class Dog
{
}
public static class BirdExtension
{
	public static void fly(this Bird bird)
	{
		// fly
	}
}
class Program
{
	static void Main(string[] args)
	{
		V8ScriptEngine engine = new V8ScriptEngine();
		engine.AddHostType(typeof(Console));
		engine.AddHostObject("dog", new Dog());
		engine.Execute("Console.WriteLine(typeof dog.fly);");
		engine.AddHostType(typeof(BirdExtension));
		engine.Execute("Console.WriteLine(typeof dog.fly);");
		engine.Execute("try{ dog.fly(); }catch(e){ Console.WriteLine(e.toString()) }");
	}
}

The output is

undefined
function
Error: 'ExampleApp.Dog' does not contain a definition for 'fly'

which show extension method will be added to all host object, although some of them actually cannot invoke them.

In my opinion, this is a bug caused by GetAllMethodNames method in https://github.com/microsoft/ClearScript/blob/master/ClearScript/HostItem.cs.

In this method, all extension method name will be added to result without checking the type of this arg.

By the way, could you please provide a optional feature to convert Task and Promise automatically(without calling ToTask and ToPromise) like V8ScriptEngineFlags.EnableDateTimeConversion do for DateTime and Date?

Or more generally, can we custom the process of marshaling object?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions