Skip to content

Commit 83af34e

Browse files
committed
Implement NewValueExternal and ValueToExternal for sharing Golang structs; add external value management in ExternalStore
1 parent 4e8371c commit 83af34e

File tree

5 files changed

+175
-0
lines changed

5 files changed

+175
-0
lines changed

external.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package v8go
2+
3+
// #include <stdlib.h>
4+
// #include "v8go.h"
5+
import "C"
6+
import (
7+
"errors"
8+
"sync"
9+
"unsafe"
10+
)
11+
12+
// ExternalStore is a store for external values.
13+
type ExternalStore struct {
14+
counter uintptr
15+
store sync.Map
16+
lock sync.Mutex
17+
}
18+
19+
var externals = NewExternalStore()
20+
21+
// NewExternalStore creates a new ExternalStore.
22+
func NewExternalStore() *ExternalStore {
23+
return &ExternalStore{
24+
counter: 0,
25+
store: sync.Map{},
26+
lock: sync.Mutex{},
27+
}
28+
}
29+
30+
// Get returns a value from the store.
31+
func (s *ExternalStore) Get(key uintptr) (interface{}, bool) {
32+
return s.store.Load(key)
33+
}
34+
35+
// Add adds a value to the store. It returns a key that can be used to retrieve the value.
36+
func (s *ExternalStore) Add(value interface{}) uintptr {
37+
s.lock.Lock()
38+
defer s.lock.Unlock()
39+
s.counter++
40+
s.store.Store(s.counter, value)
41+
return s.counter
42+
}
43+
44+
// Remove removes a value from the store.
45+
func (s *ExternalStore) Remove(key uintptr) {
46+
s.store.Delete(key)
47+
}
48+
49+
// ExternalCount returns the number of external values in the store.
50+
func ExternalCount() int {
51+
count := 0
52+
externals.store.Range(func(_, _ interface{}) bool {
53+
count++
54+
return true
55+
})
56+
return count
57+
}
58+
59+
// NewExternal creates a new external value.
60+
func NewExternal(iso *Isolate, val interface{}) (*Value, error) {
61+
ptr := externals.Add(val)
62+
rtnVal := &Value{
63+
ptr: C.NewValueExternal(iso.ptr, unsafe.Pointer(ptr)),
64+
}
65+
return rtnVal, nil
66+
}
67+
68+
// External returns the external value.
69+
// then an error is returned. Use `value.Object()` to do the JS equivalent of `Object(value)`.
70+
func (v *Value) External() (interface{}, error) {
71+
if !v.IsYaoExternal() {
72+
return nil, errors.New("v8go: value is not an External")
73+
}
74+
75+
rtnValue := C.ValueToExternal(v.ptr)
76+
if rtnValue == 0 {
77+
return nil, errors.New("v8go: failed to get external value")
78+
}
79+
80+
value, ok := externals.Get(uintptr(rtnValue))
81+
if !ok {
82+
return nil, errors.New("v8go: failed to get external value, not found")
83+
}
84+
85+
return value, nil
86+
}
87+
88+
// IsYaoExternal returns true if the value is an external value.
89+
func (v *Value) IsYaoExternal() bool {
90+
return C.ValueIsExternal(v.ptr) != 0
91+
}
92+
93+
// ReleaseExternal releases the external value.
94+
func (v *Value) ReleaseExternal() {
95+
if v.IsYaoExternal() {
96+
rtnValue := C.ValueToExternal(v.ptr)
97+
if rtnValue != 0 {
98+
externals.Remove(uintptr(rtnValue))
99+
}
100+
}
101+
}

external_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package v8go_test
2+
3+
import (
4+
"testing"
5+
6+
v8 "rogchap.com/v8go"
7+
)
8+
9+
func TestExternal(t *testing.T) {
10+
t.Parallel()
11+
12+
ctx := v8.NewContext()
13+
defer ctx.Isolate().Dispose()
14+
defer ctx.Close()
15+
16+
goValue := map[string]interface{}{"foo": "bar"}
17+
val, err := v8.NewExternal(ctx.Isolate(), goValue)
18+
if err != nil {
19+
t.Fatalf("unexpected error: %v", err)
20+
}
21+
22+
if !val.IsYaoExternal() {
23+
t.Errorf("expected value to be of type External")
24+
}
25+
26+
resValue, err := val.External()
27+
if err != nil {
28+
t.Fatalf("unexpected error: %v", err)
29+
}
30+
31+
v, ok := resValue.(map[string]interface{})
32+
if !ok {
33+
t.Errorf("expected value to be of type map[string]interface{}")
34+
}
35+
36+
if v["foo"] != "bar" {
37+
t.Errorf("expected value to be 'bar', got %v", v["foo"])
38+
}
39+
40+
// Release the external value
41+
val.Release()
42+
if v8.ExternalCount() != 0 {
43+
t.Errorf("expected external count to be 0, got %v", v8.ExternalCount())
44+
}
45+
}

v8go.cc

+21
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,18 @@ ValuePtr NewValueBigIntFromUnsigned(IsolatePtr iso, uint64_t v) {
989989
return tracked_value(ctx, val);
990990
}
991991

992+
ValuePtr NewValueExternal(IsolatePtr iso, void* v) {
993+
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);
994+
m_value* val = new m_value;
995+
val->id = 0;
996+
val->iso = iso;
997+
val->ctx = ctx;
998+
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(
999+
iso, External::New(iso, v));
1000+
return tracked_value(ctx, val);
1001+
}
1002+
1003+
9921004
RtnValue NewValueBigIntFromWords(IsolatePtr iso,
9931005
int sign_bit,
9941006
int word_count,
@@ -1040,6 +1052,15 @@ int64_t ValueToInteger(ValuePtr ptr) {
10401052
return value->IntegerValue(local_ctx).ToChecked();
10411053
}
10421054

1055+
uint64_t ValueToExternal(ValuePtr ptr) {
1056+
LOCAL_VALUE(ptr);
1057+
if (value->IsExternal()) {
1058+
Local<External> external = value.As<External>();
1059+
return reinterpret_cast<uint64_t>(external->Value());
1060+
}
1061+
return 0;
1062+
}
1063+
10431064
double ValueToNumber(ValuePtr ptr) {
10441065
LOCAL_VALUE(ptr);
10451066
return value->NumberValue(local_ctx).ToChecked();

v8go.h

+3
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ extern RtnValue NewValueBigIntFromWords(IsolatePtr iso_ptr,
224224
int sign_bit,
225225
int word_count,
226226
const uint64_t* words);
227+
extern ValuePtr NewValueExternal(IsolatePtr iso_ptr, void* v); // Yao App Engine: Implement NewValueExternal to Share Golang Structs
228+
227229
void ValueRelease(ValuePtr ptr);
228230
extern RtnString ValueToString(ValuePtr ptr);
229231
const uint32_t* ValueToArrayIndex(ValuePtr ptr);
@@ -233,6 +235,7 @@ int64_t ValueToInteger(ValuePtr ptr);
233235
double ValueToNumber(ValuePtr ptr);
234236
RtnString ValueToDetailString(ValuePtr ptr);
235237
uint32_t ValueToUint32(ValuePtr ptr);
238+
uint64_t ValueToExternal(ValuePtr ptr); // Yao App Engine: Implement ValueToExternal to Share Golang Structs
236239
extern ValueBigInt ValueToBigInt(ValuePtr ptr);
237240
extern RtnValue ValueToObject(ValuePtr ptr);
238241
int ValueSameValue(ValuePtr ptr, ValuePtr otherPtr);

value.go

+5
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,11 @@ func (v *Value) IsProxy() bool {
534534

535535
// Release this value. Using the value after calling this function will result in undefined behavior.
536536
func (v *Value) Release() {
537+
538+
// Yao: Before releasing the value, we need to release the external object
539+
// if it is an external object.
540+
v.ReleaseExternal()
541+
537542
C.ValueRelease(v.ptr)
538543
}
539544

0 commit comments

Comments
 (0)