Skip to content

Commit 1e572ef

Browse files
authored
Fix function callback usage outside of RunScript (rogchap#187)
1 parent fd090e9 commit 1e572ef

File tree

5 files changed

+49
-43
lines changed

5 files changed

+49
-43
lines changed

context.go

+3-7
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import (
1616
// Due to the limitations of passing pointers to C from Go we need to create
1717
// a registry so that we can lookup the Context from any given callback from V8.
1818
// This is similar to what is described here: https://github.com/golang/go/wiki/cgo#function-variables
19-
// To make sure we can still GC *Context we register the context only when we are
20-
// running a script inside the context and then deregister.
2119
type ctxRef struct {
2220
ctx *Context
2321
refCount int
@@ -73,6 +71,7 @@ func NewContext(opt ...ContextOption) *Context {
7371
ptr: C.NewContext(opts.iso.ptr, opts.gTmpl.ptr, C.int(ref)),
7472
iso: opts.iso,
7573
}
74+
ctx.register()
7675
runtime.KeepAlive(opts.gTmpl)
7776
return ctx
7877
}
@@ -92,9 +91,7 @@ func (c *Context) RunScript(source string, origin string) (*Value, error) {
9291
defer C.free(unsafe.Pointer(cSource))
9392
defer C.free(unsafe.Pointer(cOrigin))
9493

95-
c.register()
9694
rtn := C.RunScript(c.ptr, cSource, cOrigin)
97-
c.deregister()
9895

9996
return valueResult(c, rtn)
10097
}
@@ -115,14 +112,13 @@ func (c *Context) Global() *Object {
115112
// PerformMicrotaskCheckpoint runs the default MicrotaskQueue until empty.
116113
// This is used to make progress on Promises.
117114
func (c *Context) PerformMicrotaskCheckpoint() {
118-
c.register()
119-
defer c.deregister()
120115
C.IsolatePerformMicrotaskCheckpoint(c.iso.ptr)
121116
}
122117

123118
// Close will dispose the context and free the memory.
124-
// Access to any values assosiated with the context after calling Close may panic.
119+
// Access to any values associated with the context after calling Close may panic.
125120
func (c *Context) Close() {
121+
c.deregister()
126122
C.ContextFree(c.ptr)
127123
c.ptr = nil
128124
}

context_test.go

+46-13
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,18 @@ func TestContextRegistry(t *testing.T) {
8080
ctxref := ctx.Ref()
8181

8282
c1 := v8.GetContext(ctxref)
83-
if c1 != nil {
84-
t.Error("expected context to be <nil>")
85-
}
86-
87-
ctx.Register()
88-
c2 := v8.GetContext(ctxref)
89-
if c2 == nil {
83+
if c1 == nil {
9084
t.Error("expected context, but got <nil>")
9185
}
92-
if c2 != ctx {
93-
t.Errorf("contexts should match %p != %p", c2, ctx)
86+
if c1 != ctx {
87+
t.Errorf("contexts should match %p != %p", c1, ctx)
9488
}
95-
ctx.Deregister()
9689

97-
c3 := v8.GetContext(ctxref)
98-
if c3 != nil {
99-
t.Error("expected context to be <nil>")
90+
ctx.Close()
91+
92+
c2 := v8.GetContext(ctxref)
93+
if c2 != nil {
94+
t.Error("expected context to be <nil> after close")
10095
}
10196
}
10297

@@ -118,6 +113,44 @@ func TestMemoryLeak(t *testing.T) {
118113
}
119114
}
120115

116+
// https://github.com/rogchap/v8go/issues/186
117+
func TestRegistryFromJSON(t *testing.T) {
118+
t.Parallel()
119+
120+
iso := v8.NewIsolate()
121+
defer iso.Dispose()
122+
123+
global := v8.NewObjectTemplate(iso)
124+
err := global.Set("location", v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
125+
v, err := v8.NewValue(iso, "world")
126+
failIf(t, err)
127+
return v
128+
}))
129+
failIf(t, err)
130+
131+
ctx := v8.NewContext(iso, global)
132+
defer ctx.Close()
133+
134+
v, err := ctx.RunScript(`
135+
new Proxy({
136+
"hello": "unknown"
137+
}, {
138+
get: function () {
139+
return location()
140+
},
141+
})
142+
`, "main.js")
143+
failIf(t, err)
144+
145+
s, err := v8.JSONStringify(ctx, v)
146+
failIf(t, err)
147+
148+
expected := `{"hello":"world"}`
149+
if s != expected {
150+
t.Fatalf("expected %q, got %q", expected, s)
151+
}
152+
}
153+
121154
func BenchmarkContext(b *testing.B) {
122155
b.ReportAllocs()
123156
iso := v8.NewIsolate()

export_test.go

-10
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,6 @@ func (i *Isolate) GetCallback(ref int) FunctionCallback {
1414
return i.getCallback(ref)
1515
}
1616

17-
// Register is exported for testing only.
18-
func (c *Context) Register() {
19-
c.register()
20-
}
21-
22-
// Deregister is exported for testing only.
23-
func (c *Context) Deregister() {
24-
c.deregister()
25-
}
26-
2717
// GetContext is exported for testing only.
2818
var GetContext = getContext
2919

function.go

-4
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ func (fn *Function) Call(recv Valuer, args ...Valuer) (*Value, error) {
2525
}
2626
argptr = (*C.ValuePtr)(unsafe.Pointer(&cArgs[0]))
2727
}
28-
fn.ctx.register()
2928
rtn := C.FunctionCall(fn.ptr, recv.value().ptr, C.int(len(args)), argptr)
30-
fn.ctx.deregister()
3129
return valueResult(fn.ctx, rtn)
3230
}
3331

@@ -41,9 +39,7 @@ func (fn *Function) NewInstance(args ...Valuer) (*Object, error) {
4139
}
4240
argptr = (*C.ValuePtr)(unsafe.Pointer(&cArgs[0]))
4341
}
44-
fn.ctx.register()
4542
rtn := C.FunctionNewInstance(fn.ptr, C.int(len(args)), argptr)
46-
fn.ctx.deregister()
4743
return objectResult(fn.ctx, rtn)
4844
}
4945

promise.go

-9
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,12 @@ func (r *PromiseResolver) GetPromise() *Promise {
6262
// Resolve invokes the Promise resolve state with the given value.
6363
// The Promise state will transition from Pending to Fulfilled.
6464
func (r *PromiseResolver) Resolve(val Valuer) bool {
65-
r.ctx.register()
66-
defer r.ctx.deregister()
6765
return C.PromiseResolverResolve(r.ptr, val.value().ptr) != 0
6866
}
6967

7068
// Reject invokes the Promise reject state with the given value.
7169
// The Promise state will transition from Pending to Rejected.
7270
func (r *PromiseResolver) Reject(err *Value) bool {
73-
r.ctx.register()
74-
defer r.ctx.deregister()
7571
return C.PromiseResolverReject(r.ptr, err.ptr) != 0
7672
}
7773

@@ -98,9 +94,6 @@ func (p *Promise) Result() *Value {
9894
// The default MicrotaskPolicy processes them when the call depth decreases to 0.
9995
// Call (*Context).PerformMicrotaskCheckpoint to trigger it manually.
10096
func (p *Promise) Then(cbs ...FunctionCallback) *Promise {
101-
p.ctx.register()
102-
defer p.ctx.deregister()
103-
10497
var rtn C.RtnValue
10598
switch len(cbs) {
10699
case 1:
@@ -124,8 +117,6 @@ func (p *Promise) Then(cbs ...FunctionCallback) *Promise {
124117
// Catch invokes the given function if the promise is rejected.
125118
// See Then for other details.
126119
func (p *Promise) Catch(cb FunctionCallback) *Promise {
127-
p.ctx.register()
128-
defer p.ctx.deregister()
129120
cbID := p.ctx.iso.registerCallback(cb)
130121
rtn := C.PromiseCatch(p.ptr, C.int(cbID))
131122
obj, err := objectResult(p.ctx, rtn)

0 commit comments

Comments
 (0)