How to pass short int from script to C# methods

Sep 24, 2015 at 9:33 PM
I have the following code that fails with an exception:
    short myValue;
    public void SetValue(short v) {
        myValue = v;
    }
    void ClearScriptTest2(object sender, EventArgs e) {
        using (var engine = new V8ScriptEngine()) {
            engine.AddHostObject("Me", this);
            engine.Execute("Me.SetValue(123);");
            MessageBox.Show(myValue.ToString());
        }
    }
When I change the type of the parameter, v, for SetValue() from short to int, the code works.
It looks like Me.SetValue() expects a value of short type. So, my
question is: How do I call a method in script with parameter with type of short? Is there a way to let the script engine to convert a constant like "123" automatically to short?
Coordinator
Sep 25, 2015 at 12:54 PM
Hi James2015Li,

The native value of a JavaScript number is double-precision floating-point. When you pass such a value to a .NET method, ClearScript converts it to one of the following managed types, choosing the first one that results in no numeric data loss:
  • System.Int32
  • System.Int64
  • System.Single
  • System.Double
In your sample above the argument 123 is converted to System.Int32, which is not implicitly convertible to System.Int16, so the call fails. However, ClearScript provides a helper function you can use in this scenario:
engine.AddHostObject("host", new HostFunctions());
engine.Execute("Me.SetValue(host.toInt16(123));");
The HostFunctions class includes methods for converting script numbers to most managed numeric types. Unfortunately at the moment there's no way to change the default conversion algorithm.

Good luck!
Sep 25, 2015 at 4:18 PM
Thanks for the response. This is really a awkward limitation. I don't know any language that does not support such
conversion in a standard way. With the suggested work-around, every embedded script engine might have different
way to do conversion (since the name 'host' is implementation dependent.)

Anyway, I found it also strange that ClearScript do support some implicit conversion like the following code works just fine:
    public short MyValue;
    void ClearScriptTest2(object sender, EventArgs e) {
        using (var engine = new V8ScriptEngine()) {
            engine.AddHostObject("Me", this);
            engine.Execute("Me.MyValue = 123;");
            MessageBox.Show(MyValue.ToString());
        }
    }
Coordinator
Sep 25, 2015 at 5:26 PM
Hi again,

This is really a awkward limitation. I don't know any language that does not support such conversion in a standard way.

The problem is the ambiguity created by bridging two dissimilar runtimes. JavaScript has no method overloading, so once you find the right method, you can coerce or fill in the arguments as necessary. The CLR is exactly the opposite; you need to examine the arguments to find (or even construct) the right method.

Suppose an instance of the following class is exposed to JavaScript:
public class Thing {
    public void Foo(byte arg) {}
    public void Foo(sbyte arg) {}
}
Which method should be called by the JavaScript expression thing.Foo(123)?

On the other hand, this is only a problem when overloaded or generic methods are involved. If we do maximum narrowing of numeric values by default, we may be able to cover more common scenarios without explicit conversion, but no algorithm will work as expected in every situation.

I found it also strange that ClearScript do support some implicit conversion

.NET fields and (normal) properties can't be generic or overloaded, so there's no ambiguity. The type of a field or property is always known, so ClearScript can always make an attempt to convert the argument to the correct type.

Thanks!
Sep 26, 2015 at 12:43 AM
Which method should be called by the JavaScript expression thing.Foo(123)?
Ideally the engine should try to find Foo(int arg); then Foo(short arg), Foo(byte arg), Foo(sbyte), till it finds a match. I guess C/C++ uses this kind of promotion chain.
.NET fields and (normal) properties can't be generic or overloaded, so there's no ambiguity. The type of a field or property is always known, so ClearScript can always make an attempt to convert the argument to the correct type.
Unfortunately, the above sample code fails when you change the data type from "short" to "float":
public float MyValue;
void ClearScriptTest2(object sender, EventArgs e) {
    using (var engine = new V8ScriptEngine()) {
        engine.AddHostObject("Me", this);
        engine.Execute("Me.MyValue = 123.45;");
        MessageBox.Show(MyValue.ToString());
    }
}
Coordinator
Sep 30, 2015 at 1:30 PM
Edited Sep 30, 2015 at 1:31 PM
Hi James2015Li,

Based on your comments, we've taken a closer look and decided to make some changes.

Ideally the engine should try to find Foo(int arg); then Foo(short arg), Foo(byte arg), Foo(sbyte), till it finds a match. I guess C/C++ uses this kind of promotion chain.

ClearScript uses the actual C# invocation binder (part of the C# compiler) to provide access to .NET methods using rules that are documented and well-understood. As you've discovered, however, some ambiguity and suboptimal behavior exists when numeric arguments are passed from script code.

We've identified a bug in ClearScript that breaks some scenarios for integer arguments, and we'll fix it in the next point release. That should make your original sample above (with the short parameter) work as expected. Unfortunately there's no simple solution for single-precision floating-point parameters, so explicit conversion via host.toSingle() will continue to be required for float binding from JavaScript.

Unfortunately, the above sample code fails when you change the [field] data type from "short" to "float":

Yes, that's another bug. The next point release will relax the rules for numeric field and property assignment, allowing lossy conversion to take place implicitly.

Thank you very much for reporting these issues!
Sep 30, 2015 at 4:03 PM
Thanks for the clarification. Just in the same venue, you probably can also enhance the handling of += operator for singles. With the current ClearScript version statement like "icon.position.x += 2.5" has to be expended to "icon.position.x = host.toSingle(icon.position.x + 2.5)".
Coordinator
Oct 1, 2015 at 5:01 PM
Hi again,

V8 doesn't allow hosts to alter the behavior of the arithmetic operators, but with the fix mentioned above no casting should be necessary in this situation.

Cheers!
Oct 4, 2015 at 3:48 PM
I have tried the above code with JScriptEngine, it has the same issues with singles. What are the main differences between the V8ScriptEngine and JScriptEngine? How should I make the choice between the two?
Coordinator
Oct 5, 2015 at 5:20 PM
Hi James2015Li,

Those classes provide access to two very different JavaScript engines.

JScript is the engine used by Internet Explorer 8 and earlier. It's relatively slow and doesn't support newer JavaScript features. It also has thread affinity, meaning that each instance can only be used on the thread that created it. On the positive side, it's lightweight, it supports debugging in Visual Studio, and it's easy to deploy.

V8 is Google's JavaScript engine. It's extremely fast, it doesn't suffer from thread affinity, and it supports most of the latest JavaScript standards. The downside is that it's memory-intensive, it doesn't support debugging in Visual Studio, and it requires the deployment of several native assemblies with your application.

Good luck!
Oct 5, 2015 at 6:45 PM
Thank you very much for the clarification.
Coordinator
Oct 20, 2015 at 5:43 PM
Edited Oct 20, 2015 at 5:46 PM
We're tracking this issue here; it'll be fixed in the next point release, along with the issue affecting field and property assignment. Thanks again!