Integrating V8 Into {Insert your C++ Windows Application Here}

V8_JavaScript_engine_logoI have been absolutely enthralled with Javascript for the last two or so years. Node.js and V8 in particular, but over the last 2-4 years I have put time aside to bind in the three major C# implementations of javascript {Jint, Javascript.NET, Jurassic} into applications that didn’t even need to have scripting languages supported. Needless to say, it has been a bit of a challenge of mine to implement JS wherever I have been working.

This may be because I am currently working as  Mobile JS Engineer, but there is really something about JS that makes me smile. Something about the way the code flows and how I have to think to get things done makes me feel like I am achieving something amazing – even when all I am doing is pooping out a stupid express app to maintain a user list or something.

Either way, Integrating V8 is NOT as straight forward as I would like. As such, here is what I had to do to get V8 to integrate into a C++ application I am working on. Be aware that this is not for the faint of heart, it will take some determination if you are a novice.

And if you aren’t a novice – what the hell are you doing reading tutorials!?

Compile V8

To get started you will need to checkout/clone the V8 repo, which means you will need to install an SVN or Git-SVN client of some type.

svn checkout http://v8.googlecode.com/svn/trunk/ v8

or

git clone git://github.com/v8/v8.git v8

Once you are fully cloned it is time to get into the guts of the process. First things first, get into that directory and you will need to checkout four more libraries – gyp, python2.6, icu and cygwin. V8 uses gyp to build out the libraries and handle the linking, python is required to run gyp, cygwin is required for some other dependencies and icu is an internationalization library that V8 leverages.

> cd v8
> svn co http://gyp.googlecode.com/svn/trunk build/gyp
> svn co http:[email protected] third_party/python_26
> svn co http:[email protected] third_party/cygwin
> svn co https://src.chromium.org/chrome/trunk/deps/third_party/icu46 third_party/icu

Next you will need to generate your project & solution files. I targeted x64 for my build, you may target anything you wish

> third_party/python_26/python.exe build\gyp_v8 -Dtarget_arch=x64

With that you have enough in place to be able to build the libraries, with one caveat. The project files that are generated are configured for building in MSVS 2010, and Microsoft has graced us with some very important warnings…

  1. Each version of MSVS is bound to a different version of the c++ compiler, bundled with the install
  2. More importantly, binding a library compiled with a different version of the compiler does not work

So this is when things get fun and start branching. I am working on a project that is built in MSVS 2012 and so there is an upgrade process needed. Open up the solution in the MSVS of your choosing, and once all of the projects are loaded (or as MSVS attempts to load them) it will prompt you to see if you would like to upgrade immediately. If you missed this prompt and or skipped it you are free to upgrade your projects via the Project > Update VC++ project menu option.

From this point you can build your libraries to your heart’s content, just remember to differentiate between debug and release modes. You will want to be using release mode libraries when you finally release your application.

You may want to ensure a couple of other configurations are set, as they gave me a bit of problem when I was playing with things:

  1. V8 needs to be configured to link as a static library, so open up the properties for the V8 Projects and ensure that Configuration Properties > General > Project Defaults > Configuration Type should be set to
    Static library (.lib)
  2. You will need to target the same type of runtime library as well,  so please ensure your Configuration Properties > C/C++ > Code Generation > Runtime Library is configured as your application is. Mine is set to
    Multi-threaded DLL (/MD)

Linking the libraries

When you compile things out the library files will land in the build\Release\lib directory, and the includes you will need to reference reside in the include directory, off of your base V8 directory. You will likely want to copy these to your application’s build or lib path for versioning and maintenance. Remember: one of the reasons that you checked all of this out instead of downloading prebuilt binaries (or at least libraries in this situation) is to have:

  1. the ability to update when V8 is updated and
  2. so you can target your own implementation

Copy over the contents of both folders, to your applications directory. I used %BetterTech%/third_party/v8_v.3.24/ where I made Libraries and Includes folders for examples sake. You will need to add these to your project’s include and libraries paths, which can be updated by going to Configuration Properties > VC++ Directories and adding in your entries. These are used similar to the %PATH% environment variable to lighten the load of referring to your libraries. Otherwise you would need to refer to the libraries by their full/absolute paths for the project to compile, which is no good.

Now you should reference the libraries, there are four that I am using – icui18n.lib, icuuc.lib, v8_base.x64.lib, v8_nosnapshot.x64.lib and there are two routes to doing this. The first way you can use is to continue using your application’s properties page, Configuration Properties > General > Linker > Input  and add each of the libraries there. This will absolutely work, but it makes it a bit more involved to see where your classes are actually using this library, so lets look at a second way.

You can actually include libraries, even by full paths, via a #pragma statement in your cpp file, which in my eyes is supportive of encapsulation, while playing around in this wizard like interface can be frustrating at times,  and not to mention it but it will also look pretty neat. In the next section we will also need a #pragma statement, so keep your wits about you!

Create a new class – mine is called V8Binding, and in its header add the following:

#pragma comment(lib, "icui18n.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "v8_base.x64.lib")
#pragma comment(lib, "v8_nosnapshot.x64.lib")

This tells the compiler to attempt to find and link in these libraries, as long as they are in the libraries paths we added in earlier. Building at this time will exercise our code and should create no errors. If you run into any make sure that you comment in below and make some time where you are available so maybe I can help you out directly.

As I said though, this is not for the faint of heart.

Initialization

First we will need to include the v8 header file:

#include "v8.h"

Now we are able to reference the V8 elements that we linked in earlier.

We need to make a quick pitstop though, to travel through some V8 setup and configuration details. V8 is a pretty great implementation of the JS standard, and it has to be configured correctly to be able to interact across the boundary between C++ and JS.

  • V8 calls its JS VMs Isolates, which has its own heap, and all of the contexts and such you will be creating.
  • V8 uses Handles to reach across to JS objects and elements from C++, this is because V8 has its own garbage collector and these handles maintain reference counting and such.
  • You will define your context, defining global elements for it, and then enter it.

With these details in mind, lets get into some fun:

If you are a monster about programming, like I absolutely am, and you run your code in strict mode, or with warnings as errors, you will run into a problem in about 12 seconds.

Add this in to your cpp file:

// Extracts a C string from a V8 Utf8Value.
const char* ToCString(const v8::String::Utf8Value& value) 
{
    return *value ? *value : "<string conversion failed>";
}

// The callback that is invoked by v8 whenever the JavaScript 'print'
// function is called.  Prints its arguments on stdout separated by
// spaces and ending with a newline.
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) 
{
    bool first = true;
    for (int i = 0; i < args.Length(); i++) 
    {
        v8::HandleScope handle_scope(args.GetIsolate());
        if (first)
            first = false;
        else
            printf(" ");
        v8::String::Utf8Value str(args[i]);
    const char* cstr = ToCString(str);
        printf("%s", cstr);
    }
}

The following block of code is used to initialize the V8 library for use, and can put this in any function, just make sure that when you instantiate your binding it is called.

v8::V8::InitializeICU();
v8::Isolate *isolate = v8::Isolate::GetCurrent(); // JS VM created
v8::HandleScope handle_scope(isolate); // Create an isolate scope 
v8::Handle global<v8::ObjectTemplate> = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "print"),
    v8::FunctionTemplate::New(isolate, Print));

v8::Handle<v8::Context> context =  v8::Context::New(isolate, NULL, global);
context->Enter();

We have created a global function, print, which is bound to a function in our Application, specifically to a global function that outputs its values to stdout. Now we can execute JS.

Warnings as errors!? Oh noes!

If you compiled the previous code with warnings treated as errors you are likely to have run aground. There is a reinterpret_cast call being used on the set method that is not exactly correct, according to the compiler at least. The code is fine, however, as it is nearly identical to the sample provided by Google. To fix it we will be ignoring this warning explicitly.

Around the call to include the v8 header you will need to wrap it in pragma calls to ignore this warning.

// Disable warning messages 4946 - reinterpret_cast of similar type
// This is done to allow for v8 Initialization 
#pragma warning( push )
#pragma warning( disable : 4946 )

#include "v8.h"

// Resume standard warnings
#pragma warning( pop )

Now to execute some JS code

To be able to execute the code we will need to define a couple things. First, the source code itself needs to be defined, as a v8::String, then a name will need to be defined, im not exactly sure what this does but its needed. Once those are defined you are going to following a common process:

  1. Compile the script out through the v8 runtime
  2. Run the compiled script, the result of running it is returned as a handle.
  3. Convert the output to a cString if you wish and do whatever you wish with it.
v8::Handle<v8::Source> = v8::String::NewFromUtf8(context->GetIsolate(), "print(42);");
v8::Handle<v8::Name> = v8::String::NewFromUtf8(context->GetIsolate(), "(shell)");
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
v8::Handle<Value> result = script->Run();
v8::String::Utf8Value str(result);
const char* cstr = ToCString(str);
printf("%s\n", cstr);

For the purposes of this example I am calling the print function defined above, which does not return anything. Lets see what happens when we run it.

> BetterTech.exe
42

> _

Where to go from here

All and all, this was pretty interesting and I found the process, while complicated and clearly quite technical, to be a lot of fun. At this point I can expose any object in my application to the JS world and it can modify the contents of values, execute events and call functions back and forth.  I will think about doing a second installment of this where I show the ability to reach back and forth, but now its time to get back to development of BetterTech.

As always, Questions and/or Comments below.

> V8 Embedders Guide
> V8 Design

  • Please guide me to build v8 on windows for android-arm. I’ve downloaded python, gyp, v8, git, android-ndk, sdk, cygwin.

    Thanks.
    Ashish

  • I am not a bot

    This is a great start! Looking forward to do an integration…

  • Heya Bob,

    First of all great tutorial !
    Building V8 on Linux creates separates static object files. . .and recommended way is to link to the shared library (libv8.so). Have you linked V8 statically to a program on Linux based OS ?

    • Bob

      Hello good sir!

      I have not build V8 on Linux, but I have recently started the movement to open up my V8 middleware – Flathead – and with it I have started the port to Mac & Linux, so that should be cleared up in the next couple months. Are you having specific problems with V8?

      • Hi Bob,
        No not at all. I prefer to use CMake for my C++ projects and was trying to integrate V8 in one of my projects as a static library.
        But as recommended on some stackoverflow Q&As, I linked my application to V8 library dynamically.
        It works as expected.
        Actually it is a game engine I’m trying to build (just to learn how things work under the hood) and need V8 for that.

        • Bob

          I actually started down the exact same road. Statically linking V8 is a rather complicated process considering it depends on so many sub libraries and their linking. Might take a few days but you should be able to pull it off. In fact, if you can live without i18n, you can actually drop unicode support and then its just v8 and its supporting libraries to worry about.

  • D

    > And if you aren’t a novice – what the hell are you doing reading tutorials!?

    😛

    Jokes aside – Found this article after doing a quick search to get some insight regarding embedding v8.

    Great write up!

  • quasimotoca

    Hi Bob: Great article. I’m glad to see somebody actually attempting this without running up a tree. I’m going to attempt this on the Linux side. Have you had any experience in this area?
    Cheers,
    Dave

    • Bob

      Good Day!

      No I haven’t, not on strict linux. I did get it working on MacOSX at one point but it was some time ago. If you would like some assistance, I would gladly help out. Hit me up in email/irc some time and we can try to bang out a solution.

      Otherwise, good luck.

      If there are any issues with the notes above please let me know. I would rather have the above be generic to getting it caught up on the windows side.