[Coding] Rhino Engine with E4X (Java) to .NET (via IKVM)

Posted by Khatharsis on December 1, 2015

A lot has happened since I was last able to post. The main gist is I’m no longer able to compose entries at work, where most of my topics take place. I sort of don’t have time anymore. However, this particular problem has been bugging me and gave me an excuse to fire up my neglected VM at home to solve it. I can be particularly stubborn, especially when it involves doing all I can to work around Java.

One of my projects is based on an engine that runs from Java. We’re mostly a Microsoft shop. While Java was my first programming language, I have developed a strong dislike for it for a variety of reasons. But, we’re stuck with it. The engine is capable of running JavaScript code using Mozilla’s Rhino engine and it’s got a nifty XML library called E4X. The problem is we can’t easily unit test the JavaScript without firing up the engine and the app.

So, I dug into ways of using the Rhino engine in .NET.

Actually, the more common sense thing to do is to just use the Rhino JAR in a Java project, but, recall I don’t want to touch Java more than necessary. Plus, there wasn’t much of a challenge if I couldn’t use it in .NET somehow, right?

Well, turns out a very smart person wrote IKVM.NET, which has a variety of Java to .NET applications. But what was really cool was converting a .jar to a .dll for use in a .NET project. It seemed like a pretty straightforward thing. Convert the Rhino JAR to a DLL, include it in a test project, and voila!

..and it was sort of a voila. Until I hit a snag where I needed to use the XML function of E4X in JavaScript. It took me a while to narrow down my JavaScript code to realize I was having issues with the XML function. The unfortunate part was the error didn’t provide any information, just that there was an EcmaError. I later found out (after much googling) that this was because the XML function was undefined. It didn’t even exist. But it should! Everything else worked, why didn’t this?

When I first converted the Rhino JAR to DLL, there were a bunch of apache.xmlbeans missing warnings that I ignored. So I dug around and figured out how to convert the Rhino JAR with the apache.xmlbeans dependency. Still no luck. I dug around more and got myself going in circles knowing the answer was some combination of just figuring out the right formula for conversion and it would magically work.

Nope.

Then, I decided to convert the Rhino JAR to EXE. When I ran the EXE, it showed up just fine and ran the rather simple command “typeof(XML);” and it returned function. /headdesk Why was XML being found in the EXE, but not the DLL? I did another runaround trying to figure out classpaths and other arcane Java things, but not really getting anywhere.

Somewhere during this time, I did a “sanity check” and ported my C# test project to Java. It worked perfectly fine. XML was a function. I didn’t have to include the apache.xmlbeans JAR to my project – Rhino worked without it.

I finally took a step back and did a simpler search for IKVM.NET tutorials. The solution was actually rather simple: Add all of the IKVM DLLs to the project and see if it my test case passed. If it did (and I wasn’t expecting it to, but imagine my surprise), then systematically remove each one until my test case failed. Turns out all I needed was the IKVM.OpenJDK.XML.Transform DLL.

I’ll need to test this out back at work, whether I need to convert with the apache.xmlbeans JAR or not (the EXE worked fine without it) but maybe I can sleep peacefully tonight.

Some rough steps to help others who need the Rhino engine with E4X functionality –

Converting JAR to DLL
1. Obtain IKVM.NET, unzip somewhere convenient. For brevity, I will use the shorthand ‘~’ to refer to that location.
2. Copy rhino.jar to ~/bin
3. Open a command window to ~/bin
4. Run: ikvmc -target:library rhino.jar

-You should see some output with a rhino.dll generated.

Using rhino.dll in .NET
1. In a .NET project, add a reference to rhino.dll, IKVM.OpenJDK.Core.dll, and IKVM.OpenJDK.XML.Transform.dll
2. I used NUnit, but you can probably modify the following code to whatever test framework you’re using:

using org.mozilla.javascript;
// ...

[Test]
public void RhinoJS_typeofXML_Function()
{
    Context ctx = Context.enter();
    Console.WriteLine(ctx.getImplementationVersion());

    try
    {
        Scriptable scope = ctx.initStandardObjects();
        string script = "var result = typeof(XML);";

        Object obj = ctx.evaluateString(scope, script, "result", 1, null);
        var result = scope.get("result", scope);
        Assert.AreEqual("function", Context.toString(result));
    }
    finally
    {
        Context.exit();
    }
}

-If all goes well, this test should pass.