Embedding V8 Javascript Engine and Go
This is two common examples merged together; how to run V8 as embedded and how to
call C modules from Go language. I’m using Ubuntu 10.04 x64 with standard gcc toolchain.
Get v8 source and build v8 as shared library.
Use this command line and copy libv8.so to to your project directory:
$ scons mode=release library=shared snapshot=on arch=x64 $ cp libv8.so ~/v8exampleStep 2. C Wrapper for V8
Write C++ function that accepts javascript source code as argument and compiles and runs it in v8.
Header file:
#ifndef _V8WRAPPER_H #define _V8WRAPPER_H #ifdef __cplusplus extern "C" { #endif // compiles and executes javascript and returns the script return value as string char * runv8(const char *jssrc); #ifdef __cplusplus #endif #endif // _V8WRAPPER_H
Source file, this is slightly modified version from official v8 C++ embedders guide.
#include v8.h #include string.h #include "v8wrapper.h" using namespace v8; char * runv8(const char *jssrc) // Create a stack-allocated handle scope. HandleScope handle_scope; // Create a new context. Persistent Context context = Context::New(); // Enter the created context for compiling and // running the script. Context::Scope context_scope(context); // Create a string containing the Javascript source code. Handle String source = String::New(jssrc); // Compile the source code. Handle script script = script::Compile(source); // Run the script Handle Value result = script- Run(); // Dispose the persistent context. context.Dispose(); // return result as string, must be deallocated in cgo wrapper String::AsciiValue ascii(result); return strdup(*ascii); }
Makefile.wrapper
V8_INC=/home/user/builds/v8/include CC=g++ CFLAGS=-c -fPIC -I$(V8_INC) SOURCES=v8wrapper.cc OBJECTS=$(SOURCES:.cc=.o) TARGET=libv8wrapper.so all: $(TARGET) .cc.o: $(CC) $(CFLAGS) $ -o $@ $(TARGET): $(OBJECTS) ld -G -o $@ $(OBJECTS)
Compile to get the shared library
$ make -f Makefile.wrapper
You should end up with file libv8wrapper.so
Step 3. CGO Wrapper for GoNow define a CGO wrapper source file that exposes the v8 to the Go language.
Go source file for the CGO compiler. Note that the comments are functional and contain instructions to cgo compiler. The libv8.so and just compiled libv8wrapper.so are assumed to be in current working directory for linking.
// #cgo LDFLAGS: -L. -lv8wrapper -lv8 -lstdc++ -pthread // #include stdlib.h // #include "v8wrapper.h" import "C" import "unsafe" func RunV8(script string) string { // convert Go string to nul terminated C-string cstr := C.CString(script) defer C.free(unsafe.Pointer(cstr)) // run script and convert returned C-string to Go string rcstr := C.runv8(cstr) defer C.free(unsafe.Pointer(rcstr)) return C.GoString(rcstr) }
CGO Makefile. Note here that you need to have GOROOT defined. The OS and Architecture are defined here too.
include $(GOROOT)/src/Make.inc GOOS=linux GOARCH=amd64 TARG=v8runner CGOFILES= v8runner.go include $(GOROOT)/src/Make.pkg
Compile to Go package v8runner and install it
$ make -f Makefile.cgo $ make -f Makefile.cgo install
Install copies the package file to the $GOROOT/pkg/linux_amd64/v8runner.a where it can be imported by Go compiler and linker.
Step 4. The GO programNow you’re finally ready to make plain Go program that runs v8.
package main import "v8runner" import "fmt" func main() { r: = v8runner.RunV8("'Hello Go World'") fmt.Println(r) }
Makefile.hello
include $(GOROOT)/src/Make.inc TARG=hello GOFILES=hello.go
Compile
$ make -f Makefile.hello
Set LD_LIBRARY_PATH to current directory, assuming you have libv8.so and libv8wrapper.so there.
$ export LD_LIBRARY_PATH=.
Run the program
$ ./hello Hello Go World
To recap the steps
Shared C++ library that exposes C-function to run javascript : libv8wrapper.so CGO compiled wrapper that passes arguments between Go and C world and calls the C functions: v8runner Go program that imports the package and uses it normally.This hack has some caveats.
There is currently no way to link everything statically, as the CGO does not support it. You need to use shared libraries. I’m not aware of any easy way to call back Go from the CGO wrapped C++. You need wrappers over wrappers as demonstrated by this post: groups.google/group/golang-nuts/msg/c98b4c63ba739240. Matroska ftw. only one thread at a time can use v8 instance. You need to use Isolates (See v8 source for more information) how to support multiple instances. Still only one thread at a time can use specific instance