-
-
Notifications
You must be signed in to change notification settings - Fork 213
/
context.go
182 lines (157 loc) · 4.32 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
173
174
175
176
177
178
179
180
181
182
// Copyright 2019 Roger Chapman and the v8go contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package v8go
// #include <stdlib.h>
// #include "v8go.h"
import "C"
import (
"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
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 {
opts := contextOptions{}
for _, o := range opt {
if o != nil {
o.apply(&opts)
}
}
if opts.iso == nil {
opts.iso = NewIsolate()
}
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,
}
ctx.register()
runtime.KeepAlive(opts.gTmpl)
return ctx
}
// Isolate gets the current context's parent isolate.
func (c *Context) Isolate() *Isolate {
return c.iso
}
func (c *Context) RetainedValueCount() int {
ctxMutex.Lock()
defer ctxMutex.Unlock()
return int(C.ContextRetainedValueCount(c.ptr))
}
// RunScript executes the source JavaScript; origin (a.k.a. filename) provides a
// reference for the script and used in the stack trace if there is an error.
// error will be of type `JSError` if 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))
rtn := C.RunScript(c.ptr, cSource, cOrigin)
return valueResult(c, rtn)
}
// Global returns the global proxy object.
// Global proxy object is a thin wrapper whose prototype points to actual
// context's global object with the properties like Object, etc. This is
// done that way for security reasons.
// Please note that changes to global proxy object prototype most probably
// would break the VM — V8 expects only global object as a prototype of
// global proxy object.
func (c *Context) Global() *Object {
valPtr := C.ContextGlobal(c.ptr)
v := &Value{valPtr, c}
return &Object{v}
}
// PerformMicrotaskCheckpoint runs the default MicrotaskQueue until empty.
// This is used to make progress on Promises.
func (c *Context) PerformMicrotaskCheckpoint() {
C.IsolatePerformMicrotaskCheckpoint(c.iso.ptr)
}
// Close will dispose the context and free the memory.
// Access to any values associated with the context after calling Close may panic.
func (c *Context) Close() {
c.deregister()
C.ContextFree(c.ptr)
c.ptr = 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
}
//export goContext
func goContext(ref int) C.ContextPtr {
ctx := getContext(ref)
return ctx.ptr
}
func valueResult(ctx *Context, rtn C.RtnValue) (*Value, error) {
if rtn.value == nil {
return nil, newJSError(rtn.error)
}
return &Value{rtn.value, ctx}, nil
}
func objectResult(ctx *Context, rtn C.RtnValue) (*Object, error) {
if rtn.value == nil {
return nil, newJSError(rtn.error)
}
return &Object{&Value{rtn.value, ctx}}, nil
}