func (v *V8Context) AddRawFunc(name string, f RawFunction) error {
	v.rawFuncs[name] = f
	jsCall := fmt.Sprintf(`function %v() {
			return _go_call_raw(%v, "%v", [].slice.call(arguments));
		}`, name, v.id, name)
	funcname, filepath, line := funcInfo(f)
	_, err := v.Eval(jsCall, fmt.Sprintf("native callback to %s [%s:%d]",
		path.Ext(funcname)[1:], path.Base(filepath), line))
	return err
}
AddRawFunc adds a raw function into the V8 context.
package v8
import "C"
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"reflect"
	"runtime"
	"sync"
	"text/template"
	"unsafe"
)
var contexts = make(map[uint]*V8Context)
var contextsMutex sync.RWMutex
var highestContextId uint
var ErrTerminated = errors.New("Operation terminated prematurely")
const NO_FILE = ""
type Value struct {
	ptr C.PersistentValuePtr
	ctx *V8Context
}
func (v *Value) ToJSON() (string, error) {
	if v.ctx == nil || v.ptr == nil {
		panic("Value or context were reset.")
	}
	str := C.PersistentToJSON(v.ctx.v8context, v.ptr)
	if str == nil {
		if C.v8_context_has_terminated(v.ctx.v8context) {
			return "", ErrTerminated
		}
		err := C.v8_error(v.ctx.v8context)
		out := C.GoString(err)
		C.free(unsafe.Pointer(err))
		return "", errors.New(out)
	}
	defer C.free(unsafe.Pointer(str))
	return C.GoString(str), nil
}
func (v *Value) ToString() (string, error) {
	if v.ctx == nil || v.ptr == nil {
		panic("Value or context were reset.")
	}
	jsonStr, err := v.ToJSON()
	if err != nil {
		return "", err
	}
	var str string
	err = json.Unmarshal([]byte(jsonStr), &str)
	return str, err
}
func (v *Value) Burst() (map[string]*Value, error) {
	if v.ctx == nil || v.ptr == nil {
		panic("Value or context were reset.")
	}
	var numKeys C.int
	keyValuesPtr := C.v8_BurstPersistent(v.ctx.v8context, v.ptr, &numKeys)
	if keyValuesPtr == nil {
		if C.v8_context_has_terminated(v.ctx.v8context) {
			return nil, ErrTerminated
		}
		err := C.v8_error(v.ctx.v8context)
		defer C.free(unsafe.Pointer(err))
		return nil, errors.New(C.GoString(err))
	}
	var keyValues []C.struct_KeyValuePair
	sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&keyValues)))
	sliceHeader.Cap = int(numKeys)
	sliceHeader.Len = int(numKeys)
	sliceHeader.Data = uintptr(unsafe.Pointer(keyValuesPtr))
	result := make(map[string]*Value)
	for _, keyVal := range keyValues {
		key := C.GoString(keyVal.keyName)
		val := v.ctx.newValue(keyVal.value)
		result[key] = val
		C.free(unsafe.Pointer(keyVal.keyName))
	}
	return result, nil
}
func (v *Value) Get(field string) (*Value, error) {
	if v == nil {
		panic("nil value")
	}
	if v.ctx == nil || v.ptr == nil {
		panic("Value or context were reset.")
	}
	fields, err := v.Burst()
	if err != nil {
		return nil, err
	}
	res, exists := fields[field]
	if !exists || res == nil {
		return nil, fmt.Errorf("field '%s' is undefined.", field)
	}
	return res, nil
}
func (v *Value) Set(field string, val *Value) error {
	if v.ctx == nil || v.ptr == nil {
		panic("Value or context were reset.")
	}
	fieldPtr := C.CString(field)
	defer C.free(unsafe.Pointer(fieldPtr))
	errmsg := C.v8_setPersistentField(v.ctx.v8context, v.ptr, fieldPtr, val.ptr)
	if errmsg != nil {
		return errors.New(C.GoString(errmsg))
	}
	return nil
}
func _go_v8_callback(ctxID uint, name, args *C.char) *C.char {
	runtime.UnlockOSThread()
	defer runtime.LockOSThread()
	contextsMutex.RLock()
	c := contexts[ctxID]
	contextsMutex.RUnlock()
	f := c.funcs[C.GoString(name)]
	if f != nil {
		var argv []interface{}
		json.Unmarshal([]byte(C.GoString(args)), &argv)
		ret := f(argv...)
		if ret != nil {
			b, _ := json.Marshal(ret)
			return C.CString(string(b))
		}
		return nil
	}
	return C.CString("undefined")
}
func _go_v8_callback_raw(
	ctxID uint,
	name *C.char,
	callerFuncName, callerScriptName *C.char,
	callerLineNumber, callerColumn C.int,
	argc C.int,
	argvptr C.PersistentValuePtr,
) C.PersistentValuePtr {
	runtime.UnlockOSThread()
	defer runtime.LockOSThread()
	funcname := C.GoString(name)
	caller := Loc{
		Funcname: C.GoString(callerFuncName),
		Filename: C.GoString(callerScriptName),
		Line:     int(callerLineNumber),
		Column:   int(callerColumn),
	}
	contextsMutex.RLock()
	ctx := contexts[ctxID]
	contextsMutex.RUnlock()
	function := ctx.rawFuncs[funcname]
	var argv []C.PersistentValuePtr
	sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&argv)))
	sliceHeader.Cap = int(argc)
	sliceHeader.Len = int(argc)
	sliceHeader.Data = uintptr(unsafe.Pointer(argvptr))
	if function == nil {
		panic(fmt.Errorf("No such registered raw function: %s", C.GoString(name)))
	}
	args := make([]*Value, argc)
	for i := 0; i < int(argc); i++ {
		args[i] = ctx.newValue(argv[i])
	}
	res, err := function(caller, args...)
	if err != nil {
		ctx.throw(err)
		return nil
	}
	if res == nil {
		return nil
	}
	if res.ctx.v8context != ctx.v8context {
		panic(fmt.Errorf("Error processing return value of raw function callback %s: "+
			"Return value was generated from another context.", C.GoString(name)))
	}
	return res.ptr
}
type Function func(...interface{}) interface{}
type Loc struct {
	Funcname, Filename string
	Line, Column       int
}
type RawFunction func(from Loc, args ...*Value) (*Value, error)
type V8Isolate struct {
	v8isolate C.IsolatePtr
}
type V8Context struct {
	id        uint
	v8context C.ContextPtr
	v8isolate *V8Isolate
	funcs     map[string]Function
	rawFuncs  map[string]RawFunction
	values    map[*Value]bool
	valuesMu  *sync.Mutex
}
var platform C.PlatformPtr
var defaultIsolate *V8Isolate
func init() {
	platform = C.v8_init()
	defaultIsolate = NewIsolate()
}
func NewIsolate() *V8Isolate {
	res := &V8Isolate{C.v8_create_isolate()}
	runtime.SetFinalizer(res, func(i *V8Isolate) {
		C.v8_release_isolate(i.v8isolate)
	})
	return res
}
func NewIsolateWithSnapshot(js string) (*V8Isolate, error) {
	jsCstr := C.CString(js)
	defer C.free(unsafe.Pointer(jsCstr))
	snapshot := C.v8_create_snapshot(jsCstr)
	if snapshot == nil {
		return nil, errors.New("Unable to create snapshot from provided javascript")
	}
	res := &V8Isolate{C.v8_create_isolate_with_snapshot(snapshot)}
	runtime.SetFinalizer(res, func(i *V8Isolate) {
		C.v8_release_isolate(i.v8isolate)
		C.v8_release_snapshot(snapshot)
	})
	return res, nil
}
func NewContext() *V8Context {
	return NewContextInIsolate(defaultIsolate)
}
func NewContextInIsolate(isolate *V8Isolate) *V8Context {
	v := &V8Context{
		v8context: C.v8_create_context(isolate.v8isolate),
		v8isolate: isolate,
		funcs:     make(map[string]Function),
		rawFuncs:  make(map[string]RawFunction),
		values:    make(map[*Value]bool),
		valuesMu:  &sync.Mutex{},
	}
	contextsMutex.Lock()
	highestContextId += 1
	v.id = highestContextId
	contexts[v.id] = v
	contextsMutex.Unlock()
	runtime.SetFinalizer(v, func(p *V8Context) {
		p.Destroy()
	})
	return v
}
func (v *V8Context) Destroy() error {
	if v.v8context == nil {
		return errors.New("Context is uninitialized.")
	}
	v.ClearValues()
	contextsMutex.Lock()
	delete(contexts, v.id)
	contextsMutex.Unlock()
	C.v8_release_context(v.v8context)
	v.v8context = nil
	v.v8isolate = nil
	return nil
}
func (v *V8Context) ClearValues() error {
	if v.v8context == nil {
		panic("Context is uninitialized.")
	}
	v.valuesMu.Lock()
	for val, _ := range v.values {
		v.releaseValueLocked(val)
	}
	v.valuesMu.Unlock()
	return nil
}
func (v *V8Context) ReleaseValue(val *Value) error {
	if v.v8context == nil {
		panic("Context is uninitialized.")
	}
	v.valuesMu.Lock()
	defer v.valuesMu.Unlock()
	if val.ptr == nil {
		return errors.New("Value has been already released.")
	}
	v.releaseValueLocked(val)
	return nil
}
func (v *V8Context) releaseValueLocked(val *Value) {
	val.ctx = nil
	delete(v.values, val)
	C.v8_release_persistent(v.v8context, val.ptr)
}
func (v *V8Context) newValue(ptr C.PersistentValuePtr) *Value {
	res := &Value{ptr, v}
	v.valuesMu.Lock()
	v.values[res] = true
	v.valuesMu.Unlock()
	return res
}
func (iso *V8Isolate) Terminate() {
	C.v8_terminate(iso.v8isolate)
}
func (v *V8Context) Eval(javascript string, filename string) (res interface{}, err error) {
	if v.v8context == nil {
		panic("Context is uninitialized.")
	}
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()
	jsPtr := C.CString(javascript)
	defer C.free(unsafe.Pointer(jsPtr))
	var filenamePtr *C.char
	if len(filename) > 0 {
		filenamePtr = C.CString(filename)
		defer C.free(unsafe.Pointer(filenamePtr))
	}
	ret := C.v8_execute(v.v8context, jsPtr, filenamePtr)
	if ret != nil {
		out := C.GoString(ret)
		if out != "" {
			C.free(unsafe.Pointer(ret))
			err := json.Unmarshal([]byte(out), &res)
			return res, err
		}
		return out, nil
	}
	if C.v8_context_has_terminated(v.v8context) {
		return "", ErrTerminated
	}
	ret = C.v8_error(v.v8context)
	out := C.GoString(ret)
	C.free(unsafe.Pointer(ret))
	return nil, errors.New(out)
}
func (v *V8Context) convertToValue(e error) *Value {
	strdata, err := json.Marshal(e.Error())
	if err != nil {
		panic(err)
	}
	val, err := v.FromJSON(string(strdata))
	if err != nil {
		panic(err)
	}
	return val
}
func (v *V8Context) throw(err error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()
	msg := C.CString(err.Error())
	defer C.free(unsafe.Pointer(msg))
	C.v8_throw(v.v8context, msg)
}
func (v *V8Context) Run(funcname string, args ...interface{}) (interface{}, error) {
	if v.v8context == nil {
		panic("Context is uninitialized.")
	}
	var cmd bytes.Buffer
	fmt.Fprint(&cmd, funcname, "(")
	for i, arg := range args {
		if i > 0 {
			fmt.Fprint(&cmd, ",")
		}
		err := json.NewEncoder(&cmd).Encode(arg)
		if err != nil {
			return nil, err
		}
	}
	fmt.Fprint(&cmd, ")")
	return v.Eval(cmd.String(), fmt.Sprintf("[RUN:%v]", funcname))
}
func (v *V8Context) FromJSON(s string) (*Value, error) {
	if v.v8context == nil {
		panic("Context is uninitialized.")
	}
	return v.EvalRaw("JSON.parse('"+template.JSEscapeString(s)+"')", "FromJSON")
}
func (v *V8Context) CreateJS(js, filename string) (*Value, error) {
	if v.v8context == nil {
		panic("Context is uninitialized.")
	}
	script := fmt.Sprintf(`(function() { return %s; })()`, js)
	return v.EvalRaw(script, filename)
}
func (ctx *V8Context) EvalRaw(js string, filename string) (*Value, error) {
	if ctx.v8context == nil {
		panic("Context is uninitialized.")
	}
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()
	jsPtr := C.CString(js)
	defer C.free(unsafe.Pointer(jsPtr))
	filenamePtr := C.CString(filename)
	defer C.free(unsafe.Pointer(filenamePtr))
	ret := C.v8_eval(ctx.v8context, jsPtr, filenamePtr)
	if ret == nil {
		if C.v8_context_has_terminated(ctx.v8context) {
			return nil, ErrTerminated
		}
		err := C.v8_error(ctx.v8context)
		defer C.free(unsafe.Pointer(err))
		return nil, fmt.Errorf("Failed to execute JS (%s): %s", filename, C.GoString(err))
	}
	val := ctx.newValue(ret)
	return val, nil
}
func (ctx *V8Context) Apply(f, this *Value, args ...*Value) (*Value, error) {
	if ctx.v8context == nil {
		panic("Context is uninitialized.")
	}
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()
	argPtrs := make([]C.PersistentValuePtr, len(args)+1)
	for i := range args {
		argPtrs[i] = args[i].ptr
	}
	var thisPtr C.PersistentValuePtr
	if this != nil {
		thisPtr = this.ptr
	}
	ret := C.v8_apply(ctx.v8context, f.ptr, thisPtr, C.int(len(args)), &argPtrs[0])
	if ret == nil {
		if C.v8_context_has_terminated(ctx.v8context) {
			return nil, ErrTerminated
		}
		err := C.v8_error(ctx.v8context)
		defer C.free(unsafe.Pointer(err))
		return nil, errors.New(C.GoString(err))
	}
	val := ctx.newValue(ret)
	return val, nil
}
func (v *V8Context) Terminate() {
	v.v8isolate.Terminate()
}
func (v *V8Context) AddFunc(name string, f Function) error {
	v.funcs[name] = f
	jsCall := fmt.Sprintf(`function %v() {
		  return _go_call(%v, "%v", JSON.stringify([].slice.call(arguments)));
		}`, name, v.id, name)
	funcname, filepath, line := funcInfo(f)
	_, err := v.Eval(jsCall, fmt.Sprintf("native callback to %s [%s:%d]",
		path.Ext(funcname)[1:], path.Base(filepath), line))
	return err
}