V8 is Chromium’s JavaScript interpreter. Not only can you use the included D8 utility to open a console and execute JavaScript code directly, but it is not as tough as you would think to integrate your native functions from your JavaScript and your JavaScript functions from your native functions. Of course, you have to get your head around all of the layers of isolates, context, and scoping.
For a walkthrough of embedding V8, start here.
In order to build a quick example of how to implement a native function for consumption from your JavaScript, go ahead and build V8. You will have to choose how to build the project. You’ll provide the name of a particular build configuration, which will write a few build parameters (called “args”) to the “out.gn//args.gn” file. This is a GN thing. GN is a build tool from the Chromium depot-tools project that generates Ninja scripts for doing the actual build.
For a list of build configurations, run:
$ tools/dev/v8gen.py list ... s390.optdebug s390.optdebug.sim s390.release s390.release.sim s390x.debug s390x.debug.sim s390x.optdebug s390x.optdebug.sim s390x.release s390x.release.sim x64.debug x64.optdebug x64.release x64.release.sample
(list shortened for simplicity)
We’ve used the “x64.release.sample” configuration for our example. It is straightforward for the purpose of this example. Once you have V8 downloaded and the dependencies installed, the actual project builds in about fifteen-minutes (with sixteen threads).
Drop this example into a file named “native_function.cpp” (or update the build command below to whatever you go with). This is an amalgam of excerpts from the samples in the project, examples in the documentation, and some customization on our part.
#include <sstream> #include "libplatform/libplatform.h" #include "v8.h" // This is the function that we'll register into JS as a global function. static void TestCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // Return [the arbitrary integer] 55 as our result. auto result = v8::Integer::New(isolate, 55); // Set the result. args.GetReturnValue().Set(result); } int main(int argc, char* argv[]) { // Initialize V8. v8::V8::InitializeICUDefaultLocation(argv[0]); v8::V8::InitializeExternalStartupData(argv[0]); std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); v8::V8::InitializePlatform(platform.get()); v8::V8::Initialize(); // Create a new Isolate and make it the current one. v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); // Scoping in the C++ is tightly related to scoping in the JS. So, we'll // retain the organizational blocks from the samples. { v8::Isolate::Scope isolate_scope(isolate); // Create a stack-allocated handle scope. v8::HandleScope handle_scope(isolate); // Create a global object to install our function into. v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); // Register our global function. global->Set( v8::String::NewFromUtf8(isolate, "testCall", v8::NewStringType::kNormal).ToLocalChecked(), v8::FunctionTemplate::New(isolate, TestCallback) ); // Create a new context and apply the global object. v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); // Enter the context for compiling and running the hello world script. v8::Context::Scope context_scope(context); // Load the script. std::stringstream sourceStream; sourceStream << "testCall();" << std::endl; v8::Local<v8::String> source = v8::String::NewFromUtf8( isolate, sourceStream.str().c_str(), v8::NewStringType::kNormal) .ToLocalChecked(); // Compile the source code. v8::Local<v8::Script> script = v8::Script::Compile(context, source) .ToLocalChecked(); // Run the script. v8::Local<v8::Value> result = script->Run(context).ToLocalChecked(); // Print the screen output. v8::String::Utf8Value output(isolate, result); printf("%s\n", *output); } // Dispose the isolate and tear down V8. isolate->Dispose(); v8::V8::Dispose(); v8::V8::ShutdownPlatform(); delete create_params.array_buffer_allocator; return 0; }
To build, set V8PATH
to the absolute path of your “v8/v8” directory (the main V8 path established in the getting-started document above), and run the following:
$ g++ "-I${V8PATH}/include" -o native_function native_function.cpp -lv8_monolith "-L${V8PATH}/out.gn/x64.release.sample/obj" -pthread -std=c++0x
The example implements a simple, native function that just returns (55), registers it as a global JS function, creates a simple JS script that calls it, and then runs the script and prints the screen output. The screen output is just the result of that function printed to the screen since it was not otherwise assigned to a variable:
$ ./native_function 55
For a couple of more examples, see the project repository.