Embedding Rhino in an Android app

1919 views
Skip to first unread message

Nolan Darilek

unread,
Oct 14, 2009, 9:10:33 PM10/14/09
to dev-tech-js-...@lists.mozilla.org
Hi, all, hoping someone can give me a few pointers as to what I'm doing
wrong.

I'm trying to embed Rhino in my Android app to add scripting
functionality. According to Google, the Rhino shell works fine on
Android, which I've confirmed by building and running it from source
under Donut. So unless I'm comparing apples and orangutangs, my use case
should work too.

First I tried the basic embedding examples from the documentation,
though I called cx.setOptimization(-1) to disable all compilation. No
luck. I then thought that perhaps something was being optimized in the
context before I could turn off optimization, so I created my own
ContextFactory, and set the optimization level in the onContextCreated()
method, but no luck. Here's my current Scala code for creating an
interpreter and evaling an empty string for now:

package info.thewordnerd.spiel.scripting

import org.mozilla.javascript.Context
import org.mozilla.javascript.ContextFactory
import org.mozilla.javascript.Scriptable

class MyContextFactory extends ContextFactory {
override protected def onContextCreated(cx:Context) {
cx.setOptimizationLevel(-1)
}
}

object Scripter {

val js = """
"""

def apply() = {
ContextFactory.initGlobal(new MyContextFactory)
val cx = Context.enter
cx.setOptimizationLevel(-1)
val scope = cx.initStandardObjects
val result = cx.evaluateString(scope, js, "<spiel>", 1, null)
Context.exit
true
}
}

Running this on the emulator gives the following error (line 20 in
scripts.scala is the Context.enter call):

E/AndroidRuntime( 771): Uncaught handler: thread main exiting due to
uncaught exception
E/AndroidRuntime( 771): java.lang.ExceptionInInitializerError
E/AndroidRuntime( 771): at
org.mozilla.javascript.Context.enter(Context.java:411)
E/AndroidRuntime( 771): at
org.mozilla.javascript.Context.enter(Context.java:406)
E/AndroidRuntime( 771): at
org.mozilla.javascript.Context.enter(Context.java:386)
E/AndroidRuntime( 771): at
info.thewordnerd.spiel.scripting.Scripter$.apply(scripting.scala:20)
E/AndroidRuntime( 771): at
info.thewordnerd.spiel.services.Spiel.onServiceConnected(Spiel.scala:28)
E/AndroidRuntime( 771): at
android.accessibilityservice.AccessibilityService$IEventListenerWrapper.executeMessage(AccessibilityService.java:222)

E/AndroidRuntime( 771): at
com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:45)

E/AndroidRuntime( 771): at
android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 771): at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 771): at
android.app.ActivityThread.main(ActivityThread.java:4203)
E/AndroidRuntime( 771): at
java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 771): at
java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime( 771): at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)

E/AndroidRuntime( 771): at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
E/AndroidRuntime( 771): at dalvik.system.NativeStart.main(Native
Method)
E/AndroidRuntime( 771): Caused by: java.lang.IllegalStateException:
Failed to create VMBridge instance
E/AndroidRuntime( 771): at
org.mozilla.javascript.VMBridge.makeInstance(VMBridge.java:69)
E/AndroidRuntime( 771): at
org.mozilla.javascript.VMBridge.<clinit>(VMBridge.java:49)
E/AndroidRuntime( 771): ... 15 more

So what am I missing that lets the shell, compiled on a very similar
toolchain, work just fine? Is there some setup step I'm missing?

Thanks.

Nolan Darilek

unread,
Oct 15, 2009, 5:37:03 PM10/15/09
to dev-tech-js-...@lists.mozilla.org
OK, I promise not to spam folks with daily updates on this, and this
will be my last message on the topic not in response to something else,
but I'm still at a loss with this and would very much appreciate some help.

Ultimately, the problem seems to boil down to Context.java:411, which reads:

Object helper = VMBridge.instance.getThreadContextHelper();

I've scoured the shell example, trying to figure out what it does
differently from the very simple Context.enter() case, and while it goes
about things more circuitously, my attempts to do things similarly
always hit Context.java:411. It's starting to make me think that, while
I do ultimately have to disable optimization, my initial impression was
off. I think this because I've tried adding debugging log statements to
my factory's makeContext, and have even tried to insert a ContextAction
via contextFactory.call(). But none of this ever triggers.

In short, if I call Context.enter(), or contextFactory.call(), I get the
error detailed below, and none of my attempts to get *anything* running
before this error throws seem to work. I'm not clever enough to delve
into the Dalvik internals, so I've hit my limit as far as knowing what
to try.

Any thoughts?

Simon Kaegi

unread,
Oct 16, 2009, 12:57:41 PM10/16/09
to
Try walking through VMBridge.makeInstance() in a debugger. The problem might
be an inability to map Dalvik to one of the known JDK VMBridges.
HTH
-Simon

"Nolan Darilek" <no...@thewordnerd.info> wrote in message
news:mailman.5459.1255642648.42...@lists.mozilla.org...

Mikael Kindborg

unread,
Nov 20, 2009, 5:36:18 PM11/20/09
to
Hi!

Here is a working example. I used the rhino jar from the ASE-
reporsitory (placed in libs).

Best regards, Micke

package miki.jsapp;

import android.app.Activity;
import android.os.Bundle;
import android.widget.*;

import org.mozilla.javascript.*;

public class JSApp extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TextView view = new TextView(this);
view.setText("Hello JavaScript!");
setContentView(view);
doit(view,
"var x = 'Kalle\\nLisa';\n" +
"TheView.append(x);"
);
}

void doit(TextView view, String code)
{
// Creates and enters a Context. The Context stores
information
// about the execution environment of a script.
Context cx = Context.enter();
cx.setOptimizationLevel(-1);
try
{
// Initialize the standard objects (Object, Function,
etc.)
// This must be done before scripts can be executed.
Returns
// a scope object that we use in later calls.
Scriptable scope = cx.initStandardObjects();
ScriptableObject.putProperty(scope, "TheView",
Context.javaToJS(view, scope));

// Now evaluate the string we've colected.
Object result = cx.evaluateString(scope, code, "doit:", 1,
null);

// Convert the result to a string and print it.
view.append(Context.toString(result));

}
finally
{
// Exit from the context.
Context.exit();

Nolan Darilek

unread,
Dec 3, 2009, 1:56:12 PM12/3/09
to dev-tech-js-...@lists.mozilla.org
Cool, thanks, actually I figured it out. I'm using Proguard to slim down
my app, and apparently the crazy amount of introspection and reflection
being used was tripping Rhino up. Also, the exception I was getting was
a wrapper for another, which cued me into the fact that classes weren't
being loaded as they should. Including the entire Rhino jar fixed this,
and I now have a scriptable app.

In any case, thanks for showing me that it could be done and that I
wasn't wasting my time. :)

> _______________________________________________
> dev-tech-js-engine-rhino mailing list
> dev-tech-js-...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino
>

sandeepupe...@gmail.com

unread,
Jun 24, 2013, 6:41:50 PM6/24/13
to
Hi Nolan,

I have written an application using HTMLUnit and am obfuscating the code using proguard to reduce memory used by the final jar created. On running the jar i am getting the same exception that you have mentioned in this post:

java.lang.IllegalStateException: Failed to create VMBridge instance

Did you find a fix for it. I am trying to change the proguard config to see if i can get past this hurdle. Any assistance or pointers would be greatly appreciated.

Thank you.
Sandeep Kamath
Reply all
Reply to author
Forward
0 new messages