V8 is the JavaScript engine developed for and used within Chrome. It’s powerful and fast, though seemingly complex to embed within existing C++ applications. The V8 internal API changes over time and keeping up to date with those changes can be challenging.

Another issue with attempting to integrate with V8 directly is that of compiling. V8 is a fairly complex project and building it may not be as straightforward as you would hope.

Thankfully, there are three libraries that make the embedding process a lot easier.

The first is v8pp, a C++ library that allows you to more easily integrate C++ and JavaScript, using the V8 engine. It simplifies the overall process of integrating with V8.

The author of that library (Pavel Medvedev) also has two NuGet packages that offer both the header files necessary to use V8, as well as prebuilt binaries.

Together, these tools make the basic process of integrating with V8 very easy. This blog post serves as a short introduction on how you can use them, with the example of a basic integration. Note that this isn’t meant to be a complete integration guide, it only demonstrates how to get far enough to be able to successfully run JavaScript within a C++ application.

Adding the necessary NuGet dependencies

As mentioned above, there are two NuGet packages that simplify the process of embedding V8. These are:

  • v8
  • v8.redist

You’ll need to add both of these as dependencies of your project. Note that there are various variations of both packages available. The variation that you will use will depend on which version of Visual Studio you’re using and whether you’re targeting 32-bit or 64-bit. See https://github.com/pmed/v8-nuget for more details.

Also note that the v8pp library references a particular version of V8, so you’ll want to make sure that the package version you use matches that version. As of the time of writing this post, the most recent version of v8pp is 1.6.0, which references V8 6.9.427.13.

Including v8pp

v8pp is largely a header-only library. If, however, you decide to use the v8pp::context class, you’ll need to compile the library.

That class contains a few pieces of functionality that I think are very useful:

  • It wraps v8::Context and sets up the v8::Isolate instance.
  • It exports a require function that can be called from JavaScript to load other JavaScript files. Potentially useful if you’re creating a plugin system and want to allow plugins to themselves load other libraries.
  • It allows you to easily run a file or script (specified as a string) in the Javascript context.

Due to these advantages, I think it makes sense to use the v8pp::context class. That does, however, mean that v8pp will need to be compiled.

To do that, first grab the most recent release from the GitHub project page:

https://github.com/pmed/v8pp/releases

Open the solution with Visual Studio and build it. For each configuration in which you want to use v8pp (debug/release, x86/x64), you’ll need to build a corresponding version of v8pp.

Be aware that v8pp only references the x64 versions of the above NuGet libraries. If you’re targeting x86 as well, you’ll need to update the packages referenced by the solution to include the x86 versions.

Next, add the v8pp directory as an additional include directory. Also add the necessary library path to your project and add v8pp.lib as a dependency.

Add the v8pp header include directory

Add the v8pp library directory

Add the v8pp library dependency

If you’re going through these steps with a real application, you’ll want to make sure that whatever build system you’re using builds the v8pp solution first so that the v8pp.lib file is always available for use in your project.

Setting up a basic JavaScript context

Now that the necessary dependencies have been added to your application, you can go about using V8. As an example, lets say that you have some JavaScript code contained within a string that you’d like to execute.

First, include the necessary header files:

#include "v8pp/context.hpp"
#include <libplatform/libplatform.h>
#include <v8.h>

When your application starts, it will need to set the path to V8’s snapshot files (natives_blob.bin and snapshot_blob.bin). natives_blob.bin contains the minified source code for some builtin functions, while snapshot_blob.bin contains the startup and context snapshots (see https://groups.google.com/d/msg/v8-users/N4GGCkuKnfA/PzVZkc8QCAAJ).

The v8-redist package will take care of copying the snapshot files into the build output directory. Assuming that you don’t want to place the snapshot files in another directory, the location of the snapshot files will then simply be the directory the executable is in.

You can set the snapshot path using the following call:

v8::V8::InitializeExternalStartupData(argv[0]);

Next, you’ll need to initialize the V8 platform:

auto platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();

At this point, you should be able to create a context that will allow you to run JavaScript code. Doing that with v8pp is actually very easy. Some example code is shown below:

v8pp::context context;
v8::HandleScope handleScope(context.isolate());

auto result = context.run_script(
	"function fib(n) {"
		"if (n == 0) {"
			"return 0;"
		"}"
		"else if (n == 1) {"
			"return 1;"
		"}"

		"return fib(n - 1) + fib(n - 2);"
	"}"

	"fib(8);"
);

v8::String::Utf8Value resultString(context.isolate(), result);

std::cout << "The result of the script was: " << *resultString << "\n";

This will give the following output:

The result of the script was: 21

Finally, you’ll need to shutdown the V8 platform before your application exits:

v8::V8::Dispose();
v8::V8::ShutdownPlatform();

Releasing

To allow end users to use the JavaScript functionality, you’ll need to distribute several files with your application. These currently are:

  • icudtl.dat
  • icui18n.dll
  • icuuc.dll
  • natives_blob.bin
  • snapshot_blob.bin
  • v8.dll
  • v8_libbase.dll
  • v8_libplatform.dll

The v8-redist NuGet package takes care of copying these into your build output directory, but you’ll need to redistribute each of the files with your executable.

As part of deciding whether to use V8, you may want to take into account the size of these files. While they aren’t too big (the x64 versions for V8 6.9.427.13 come out to about 30 MB together), they could very easily be larger than the size of a small executable.

Overall thoughts

Getting a basic integration with V8 working using the above tools isn’t that difficult. They collectively make it a straightforward process.

However, this article hasn’t really considered how you’d do anything non-trivial with v8pp/V8. I plan on covering more of the functionality in a later post, where I discuss how you might go about binding in a set of API methods from C++ to JavaScript.

Finally, V8 certainly isn’t the only option for embedding a JavaScript engine into C++. Duktape is one option, perhaps in combination with Dukglue (it’s a wrapper around Duktape that allows easier integration with C++).

Outside of JavaScript, Lua is a popular choice and the embedding process can be very easy.

The code for this post can be found in the following GitHub repository: https://github.com/derceg/v8-embedding-example. Also see the test project in the v8pp repository, which demonstrates how v8pp can be used within a C++ application.