forked from rogchap/v8go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontext.go
172 lines (150 loc) · 3.87 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package v8go
// #include <stdlib.h>
// #include "v8go.h"
import "C"
import (
"fmt"
"runtime"
"sync"
"unsafe"
)
// Due to the limitations of passing pointers to C from Go we need to create
// a registry so that we can lookup the Context from any given callback from V8.
// This is similar to what is described here: https://github.com/golang/go/wiki/cgo#function-variables
// To make sure we can still GC *Context we register the context only when we are
// running a script inside the context and then deregister.
type ctxRef struct {
ctx *Context
refCount int
}
var ctxMutex sync.RWMutex
var ctxRegistry = make(map[int]*ctxRef)
var ctxSeq = 0
// Context is a global root execution environment that allows separate,
// unrelated, JavaScript applications to run in a single instance of V8.
type Context struct {
ref int
ptr C.ContextPtr
iso *Isolate
}
type contextOptions struct {
iso *Isolate
gTmpl *ObjectTemplate
}
// ContextOption sets options such as Isolate and Global Template to the NewContext
type ContextOption interface {
apply(*contextOptions)
}
// NewContext creates a new JavaScript context; if no Isolate is passed as a
// ContextOption than a new Isolate will be created.
func NewContext(opt ...ContextOption) (*Context, error) {
opts := contextOptions{}
for _, o := range opt {
if o != nil {
o.apply(&opts)
}
}
if opts.iso == nil {
var err error
opts.iso, err = NewIsolate()
if err != nil {
return nil, fmt.Errorf("v8go: failed to create new Isolate: %v", err)
}
}
if opts.gTmpl == nil {
opts.gTmpl = &ObjectTemplate{&template{}}
}
ctxMutex.Lock()
ctxSeq++
ref := ctxSeq
ctxMutex.Unlock()
ctx := &Context{
ref: ref,
ptr: C.NewContext(opts.iso.ptr, opts.gTmpl.ptr, C.int(ref)),
iso: opts.iso,
}
runtime.SetFinalizer(ctx, (*Context).finalizer)
// TODO: [RC] catch any C++ exceptions and return as error
return ctx, nil
}
// Isolate gets the current context's parent isolate.An error is returned
// if the isolate has been terninated.
func (c *Context) Isolate() (*Isolate, error) {
// TODO: [RC] check to see if the isolate has not been terninated
return c.iso, nil
}
// RunScript executes the source JavaScript; origin or filename provides a
// reference for the script and used in the stack trace if there is an error.
// error will be of type `JSError` of not nil.
func (c *Context) RunScript(source string, origin string) (*Value, error) {
cSource := C.CString(source)
cOrigin := C.CString(origin)
defer C.free(unsafe.Pointer(cSource))
defer C.free(unsafe.Pointer(cOrigin))
c.register()
rtn := C.RunScript(c.ptr, cSource, cOrigin)
c.deregister()
return getValue(c, rtn), getError(rtn)
}
// Close will dispose the context and free the memory.
func (c *Context) Close() {
c.finalizer()
}
func (c *Context) finalizer() {
C.ContextFree(c.ptr)
c.ptr = nil
runtime.SetFinalizer(c, nil)
}
func (c *Context) register() {
ctxMutex.Lock()
r := ctxRegistry[c.ref]
if r == nil {
r = &ctxRef{ctx: c}
ctxRegistry[c.ref] = r
}
r.refCount++
ctxMutex.Unlock()
}
func (c *Context) deregister() {
ctxMutex.Lock()
defer ctxMutex.Unlock()
r := ctxRegistry[c.ref]
if r == nil {
return
}
r.refCount--
if r.refCount <= 0 {
delete(ctxRegistry, c.ref)
}
}
func getContext(ref int) *Context {
ctxMutex.RLock()
defer ctxMutex.RUnlock()
r := ctxRegistry[ref]
if r == nil {
return nil
}
return r.ctx
}
func getValue(ctx *Context, rtn C.RtnValue) *Value {
if rtn.value == nil {
return nil
}
v := &Value{rtn.value, ctx}
runtime.SetFinalizer(v, (*Value).finalizer)
return v
}
func getError(rtn C.RtnValue) error {
if rtn.error.msg == nil {
return nil
}
err := &JSError{
Message: C.GoString(rtn.error.msg),
Location: C.GoString(rtn.error.location),
StackTrace: C.GoString(rtn.error.stack),
}
C.free(unsafe.Pointer(rtn.error.msg))
C.free(unsafe.Pointer(rtn.error.location))
C.free(unsafe.Pointer(rtn.error.stack))
return err
}