Embedding V8 Javascript Engine and Go

12-27 生活常识 投稿:陪着眼泪
Embedding V8 Javascript Engine and Go
March 30, 2011 1 Comment

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.

Step 1. Compile v8

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 ~/v8example
Step 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 Go

Now 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 program

Now 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
标签: # 标签 # 标题
声明:伯乐人生活网所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系ttnweb@126.com