2013-05-03 00:00:00 -0700
A good plan violently executed now is better than a perfect plan executed next week.
George S. Patton
Wherein we instantiate a V8 context and violently execute some good javascript.
Well, it’s decent javascript.
Well, it’s ok.
We’re keeping this one short and sweet. The previous articles have been long by necessity, but now we can slowly build up functionality without too many modifications.
V8 is a complex piece of software architecture, and discussing all the nuances of development would bloat this article. We’ll be doing a companion series on developing with V8 which will expand on the V8 code seen here, so stay tuned for that.
git clone https://github.com/lorinbeer/vatedroid.git cd vatedroid git branch --track tutorial origin/ensufire_tutorial git checkout tutorial
we’ll be dealing with well commented code, and explaining both the V8 and JNI portions explicitly. If you know JavaScript, you’ll probably be able to puzzle through the C/C++ stuff.
don’t stress on this too much, but having the Android SDK and Android NDK setup wouldn’t hurt
Part 2’s cliffhanger left us with a compiled Android NDK module referencing the included V8 libraries, but no instantiation of V8 classes and no JNI hooks into Android Java.
Check out the p2s7 tag in the vatedroid tutorial branch.
we defined a JNI style c function called feedVatedroid in the vatedroid module
static { System.loadLibrary("vatewrap"); }
private native String feedVatedroid(String name, String code);
execute the function and log value
String result = feedVatedroid( "VateDroid Activity", "function concat(x,y){ return x+y; } concat('foo','bar');"); Log.d("VATEDROID ACTIVITY VATEDROID 'PRODUCED' RESULT", result);
VateDroidActivity should look something like
public class VateDroidActivity extends Activity { // static initializer loads native library static { System.loadLibrary("vatedroid"); } // native instance method declaration private native String feedVatedroid(String name, String code); /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String result = feedVatedroid("VateDroid Activity", "var blank;"); Log.d("VATEDROID ACTIVITY VATEDROID 'PRODUCED' RESULT", result); } }
What this does The previous is a general usage of the JNI on the Android platform. C style functions are implemented in a native library and compiled by the NDK. The Android Java side includes these libraries with the static syntax above, and declares functions which look for a native implementation, rather than a local java one.
Run this code on a device or simulator and see the results on the console log with
$ adb logcat
Complexity requires harsh environmental legislation (modularization) Note the total lack of anything V8 related here: complexity is a pollutant in software. We’re going to limit V8’s complexity to the native implementation. Android Java will know nothing of what is actually running the javascript it feeds into the jni api it uses; it’ll just expect a native implementation following a certain loose api: provide code, expect results.
It’s time to get V8 running. vroom. vroom.
declare a global persistent V8 context add the following global variable to vatedroid.cpp
v8::Persistent<v8::Context> PrimaryContext;
A V8::Context
is a javascript execution environment. This allows a single instance of V8 to execute multiple, independent js applications.
A V8::Persistent
class is a template class which instructs the V8 garbage collector to leave the wrapped object alone until specifically told to destroy it.
declare an initVatedroid function the code gets more verbose as we move forward. Follow the links to github or checkout the repo to see the full implementation.
extern "C" void Java_com_vatedroid_VateDroidActivity_initVatedroid( JNIEnv * env, jobject obj) ;
and add the following to initVatedroid
using namespace v8; HandleScope localscope; Local< ObjectTemplate > global = ObjectTemplate::New(); PrimaryContext = Context::New(NULL, global);
Line by line:
3 lines of V8 API code and there is a lot to take in. The important thing to take away is that you need to pay very close attention to what scope your v8 objects are declared in. You’ll see the ObjectTemplate do more and more as our tutorial progresses
add the initVatedroid hook to the main activity private native void initVatedroid();
and call it from onCreate
initVatedroid();again, I’m avoiding pasting the entire file here. This is no different than the feedVatedroid function we defined earlier, except that it accepts and returns nothing.
the code below is the important bits from the feedVatedroid function, this takes it from a stub to a function which accepts a string from Java, compiles it to a v8 script and executes it a v8 context.
Handle<String> nme = String::New(env->GetStringChars(name, &isCopy)); Handle<String> cmd = String::New(env->GetStringChars(message, &isCopy)); Handle<Script> script = Script::Compile(cmd, nme); if (script.IsEmpty()) { return env->NewStringUTF("Error: Script is empty!"); } Local< Value > result = script->Run(); String::Utf8Value retstr(result); retval = env->NewStringUTF(*retstr); return retval;
it’s getting complicated but the gist is this: We convert the JNI strings to V8 strings We compile the ‘message’ string to a V8 Script We run the V8 script inside the V8 context created in initVatedroid We convert the return value from the script to a jstring, and return to caller
there’s a bit more to it, in the file, you can see an error handler which will print any exceptions thrown by the javascript we are trying to execute
Running this code will yield a log message VATEDROID ‘PRODUCED’ RESULT(11200): undefined This is because our message didn’t return anything!
let’s violently execute a plan
change the message sent to vatedroid to:
function main() { var nothing = "foobar"; return nothing; }
like so:
String okjs = "function main() { var nothing = \"foobar\"; return nothing; } \n main();"; String result = feedVatedroid("VateDroid Activity", okjs);
launch! Running the app will produce something like:
VATEDROID 'PRODUCED' RESULT(11296): foobar
There’s a lot to do from here. Next issue will deal with moving the V8 executing to a separate thread, then we can profile!
VATEDROID - Part 3 - Executing JS
VATEDROID - Part 2 - Linking V8
VATEDROID - Part 1 - Compiling V8