V8 JavaScript engine bindings for Go. Support V8 5.1
请参考中文说明《中文说明》
- 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
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.
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())
})
}
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);
`)
})
}
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!")
`)
})
}
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
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()
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)
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) {
})
})
Please read v8_all_test.go
and the codes in samples
folder.