v8.go
V8 JavaScript engine bindings for Go. Support V8 5.1
中文介绍
请参考中文说明《中文说明》
Features
- Thread safe
- Thorough and careful testing
- Boolean, Number, String, Object, Array, Regexp, Function
- Compile and run JavaScript
- Create JavaScript context with global object template
- Operate JavaScript object properties and array elements in Go
- Define JavaScript object template in Go with property accessors and interceptors
- Define JavaScript function template in Go
- Catch JavaScript exception in Go
- Throw JavaScript exception by Go
- JSON parse and generate
- Powerful binding API
- C++ plugin
Install
For 'curl' user. please run this shell command:
curl -OL https://raw.github.com/idada/v8.go/master/get.sh && chmod +x get.sh && ./get.sh v8.go
For 'wget' user. Please run this shell command:
wget https://raw.github.com/idada/v8.go/master/get.sh && chmod +x get.sh && ./get.sh v8.go
Note: require Go version 1.2 and Git.
Hello World
This 'Hello World' program shows how to use v8.go to compile and run JavaScript code then get the result.
package main
import "github.com/idada/v8.go"
func main() {
engine := v8.NewEngine()
script := engine.Compile([]byte("'Hello ' + 'World!'"), nil)
context := engine.NewContext(nil)
context.Scope(func(cs v8.ContextScope) {
result := cs.Run(script)
println(result.ToString())
})
}
Fast Binding
package main
import "fmt"
import "github.com/idada/v8.go"
type MyType struct {
Id int
Name string
Data map[string]int
Callback func(a int, b string)
}
func (mt *MyType) Dump(info string) {
fmt.Printf(
"Info: \"%s\", Id: %d, Name: \"%s\", Data: %v\n",
info, mt.Id, mt.Name, mt.Data,
)
}
func main() {
engine := v8.NewEngine()
global := engine.NewObjectTemplate()
global.Bind("MyType", MyType{})
global.Bind("print", func(v ...interface{}) {
fmt.Println(v...)
})
global.Bind("test", func(obj *v8.Object) {
raw := obj.GetInternalField(0).(*v8.BindObject)
raw.Target.Interface().(*MyType).Callback(123, "dada")
})
engine.NewContext(global).Scope(func(cs v8.ContextScope) {
cs.Eval(`
var a = new MyType();
a.Dump("old");
a.Id = 10;
a.Name = "Hello";
a.Data = {
'x': 1,
'y': 2
};
a.Dump("new");
a.Callback = function(a, b) {
print(a, b);
}
a.Callback(10, "Hello");
test(a);
`)
})
}
C++ plugin
You can implement plugin in C++.
#include "v8.h"
#include "v8_plugin.h"
using namespace v8;
extern "C" {
static void LogCallback(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 1) return;
HandleScope scope(args.GetIsolate());
Handle<Value> arg = args[0];
String::Utf8Value value(arg);
printf("%s\n", *value);
}
v8_export_plugin(log, {
global->Set(isolate, "log",
FunctionTemplate::New(isolate, LogCallback)
);
});
}
And load the plugin in Go.
package main
/*
#cgo pkg-config: ../../v8.pc
#include "v8_plugin.h"
v8_import_plugin(log);
*/
import "C"
import "github.com/idada/v8.go"
func main() {
engine := v8.NewEngine()
global := engine.NewObjectTemplate()
// C.v8_plugin_log generate by v8_import_plugin(log)
global.Plugin(C.v8_plugin_log)
engine.NewContext(global).Scope(func(cs v8.ContextScope) {
cs.Eval(`
log("Hello Plugin!")
`)
})
}
Performance and Stability
The benchmark result on my iMac:
Benchmark_NewContext 285869 ns/op
Benchmark_NewInteger 707 ns/op
Benchmark_NewString 1869 ns/op
Benchmark_NewObject 3292 ns/op
Benchmark_NewArray0 1004 ns/op
Benchmark_NewArray5 4024 ns/op
Benchmark_NewArray20 8601 ns/op
Benchmark_NewArray100 31963 ns/op
Benchmark_Compile 640988 ns/op
Benchmark_RunScript 888 ns/op
Benchmark_JsFunction 1148 ns/op
Benchmark_GoFunction 1491 ns/op
Benchmark_Getter 2215 ns/op
Benchmark_Setter 3261 ns/op
Benchmark_TryCatch 47366 ns/op
Concepts
Engine
In v8.go, engine type is the wrapper of v8::Isolate.
Because V8 engine use thread-local storage but cgo calls may be execute in different thread. So v8.go use v8::Locker to make sure V8 engine's thread-local data initialized. And the locker make v8.go thread safe.
You can create different engine instance for data isolate or improve efficiency of concurrent purpose.
engine1 := v8.NewEngine()
engine2 := v8.NewEngine()
Script
When you want to run some JavaScript. You need to compile first.
Scripts can run many times or run in different context.
script := engine.Compile([]byte(`"Hello " + "World!"`), nil)
The Engine.Compile() method take 2 arguments.
The first is the code.
The second is a ScriptOrigin, it stores script's file name or line number offset etc. You can use ScriptOrigin to make error message and stack trace friendly.
name := "my_file.js"
real := ReadFile(name)
code := "function(_export){\n" + real + "\n}"
origin := engine.NewScriptOrigin(name, 1, 0)
script := engine.Compile(code, origin, nil)
Context
The description in V8 embedding guide:
In V8, a context is an execution environment that allows separate, unrelated, JavaScript applications to run in a single instance of V8. You must explicitly specify the context in which you want any JavaScript code to be run.
In v8.go, you can create many contexts from a V8 engine instance. When you want to run some JavaScript in a context. You need to enter the context by calling Scope() and run the JavaScript in the callback.
context.Scope(func(cs v8.ContextScope){
script.Run()
})
Context in V8 is necessary. So in v8.go you can do this:
context.Scope(func(cs v8.ContextScope) {
context2 := engine.NewContext(nil)
context2.Scope(func(cs2 v8.ContextScope) {
})
})
More
Please read v8_all_test.go
and the codes in samples
folder.