jagotu / BACIL

An experimental interpreter for .NET CIL binaries for GraalVM
MIT License
37 stars 4 forks source link

Plan for calling C# symbols arbitrarily #5

Closed s5bug closed 2 years ago

s5bug commented 2 years ago

I know this project is nowhere near the point of Java <-> C# interop yet, but I can't get it off my mind.

What will calling C# code from Java code look like? I'm not familiar with how namespaces, statics, etc. work in .NET libraries. I want to assume it can't be similar to JS, because unlike in JS, in C# not every identifier is a value, but I haven't yet dived as deep into Truffle as I would like.

jagotu commented 2 years ago

Usually when figuring out what the future design might look like, you can start by checking how Espresso does it, as it's a very similiar project and is developed in-house so can be understood as a reference implentation. I guess you're asking about the host Java, so the relevant doc is https://www.graalvm.org/22.1/reference-manual/java-on-truffle/interoperability/#embedding-in-host-java

So from that, it seems calling into C# would look more akin to reflection than direct calls on the Java side. Your entrypoint to the .NET world would be finding a Type (from the root namespace) and then calling various methods on it. I'd expect it to look something like this:

// host java
import org.graalvm.polyglot.*;

class Embedding {
    public static void main(String[] args) {
        Context polyglot = Context.newBuilder().allowAllAccess(true).build();

        Value mathClass = polyglot.getBindings("CIL").getMember("System.Math");
        double sqrt2 = mathClass.invokeMember("Sqrt", 2).asDouble();
        double pi = mathClass.getMember("PI").asDouble();
        System.out.println(sqrt2);
        System.out.println(pi);
    }
}

The situation would be different for calling from guest Java and other languages, as then its a matter of what API the guest language exposes for interop.

s5bug commented 2 years ago

Of course, one could write a wrapper to make things look like Java calls...

public class Math {
    private final Value mathClass;

    public Math(Value mathClass) {
        this.mathClass = mathClass;
    }

    public double sqrt(double a) {

Cool! Thanks for the response!

jagotu commented 2 years ago

I was curious what it would take to make BACIL a proper polyglot target and for barebones support (primitives only, no overloads etc.) it seems to be more straighforward than I expected. Obviously the real issue comes with passing objects back and forth. You can try it in the polyglot branch.

test.js:

assemblyName='BakakaCsharp';
typeName='BakakaCsharp.Program';
methodName='addone';
console.log(Polyglot.eval("cil", "<Bindings>")[assemblyName][typeName][methodName](42));
> js --vm.Dtruffle.class.path.append=language/target/language-1.0-SNAPSHOT.jar --polyglot "--cil.libraryPath=..\dotnet-sdk-6.0.301-win-x64\shared\Microsoft.NETCore.App\6.0.6;..\BakakaCsharp\BakakaCsharp\BakakaCsharp\bin\Debug\net5.0" test.js
43