Skip to content

Commit

Permalink
Implement NewValueExternal and ValueToExternal for sharing Golang str…
Browse files Browse the repository at this point in the history
…ucts; add external value management in ExternalStore
  • Loading branch information
trheyi committed Nov 4, 2024
1 parent 4e8371c commit 83af34e
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 0 deletions.
101 changes: 101 additions & 0 deletions external.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package v8go

// #include <stdlib.h>
// #include "v8go.h"
import "C"
import (
"errors"
"sync"
"unsafe"
)

// ExternalStore is a store for external values.
type ExternalStore struct {
counter uintptr
store sync.Map
lock sync.Mutex
}

var externals = NewExternalStore()

// NewExternalStore creates a new ExternalStore.
func NewExternalStore() *ExternalStore {
return &ExternalStore{
counter: 0,
store: sync.Map{},
lock: sync.Mutex{},
}
}

// Get returns a value from the store.
func (s *ExternalStore) Get(key uintptr) (interface{}, bool) {
return s.store.Load(key)
}

// Add adds a value to the store. It returns a key that can be used to retrieve the value.
func (s *ExternalStore) Add(value interface{}) uintptr {
s.lock.Lock()
defer s.lock.Unlock()
s.counter++
s.store.Store(s.counter, value)
return s.counter
}

// Remove removes a value from the store.
func (s *ExternalStore) Remove(key uintptr) {
s.store.Delete(key)
}

// ExternalCount returns the number of external values in the store.
func ExternalCount() int {
count := 0
externals.store.Range(func(_, _ interface{}) bool {
count++
return true
})
return count
}

// NewExternal creates a new external value.
func NewExternal(iso *Isolate, val interface{}) (*Value, error) {
ptr := externals.Add(val)
rtnVal := &Value{
ptr: C.NewValueExternal(iso.ptr, unsafe.Pointer(ptr)),
}
return rtnVal, nil
}

// External returns the external value.
// then an error is returned. Use `value.Object()` to do the JS equivalent of `Object(value)`.
func (v *Value) External() (interface{}, error) {
if !v.IsYaoExternal() {
return nil, errors.New("v8go: value is not an External")
}

rtnValue := C.ValueToExternal(v.ptr)
if rtnValue == 0 {
return nil, errors.New("v8go: failed to get external value")
}

value, ok := externals.Get(uintptr(rtnValue))
if !ok {
return nil, errors.New("v8go: failed to get external value, not found")
}

return value, nil
}

// IsYaoExternal returns true if the value is an external value.
func (v *Value) IsYaoExternal() bool {
return C.ValueIsExternal(v.ptr) != 0
}

// ReleaseExternal releases the external value.
func (v *Value) ReleaseExternal() {
if v.IsYaoExternal() {
rtnValue := C.ValueToExternal(v.ptr)
if rtnValue != 0 {
externals.Remove(uintptr(rtnValue))
}
}
}
45 changes: 45 additions & 0 deletions external_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package v8go_test

import (
"testing"

v8 "rogchap.com/v8go"
)

func TestExternal(t *testing.T) {
t.Parallel()

ctx := v8.NewContext()
defer ctx.Isolate().Dispose()
defer ctx.Close()

goValue := map[string]interface{}{"foo": "bar"}
val, err := v8.NewExternal(ctx.Isolate(), goValue)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if !val.IsYaoExternal() {
t.Errorf("expected value to be of type External")
}

resValue, err := val.External()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

v, ok := resValue.(map[string]interface{})
if !ok {
t.Errorf("expected value to be of type map[string]interface{}")
}

if v["foo"] != "bar" {
t.Errorf("expected value to be 'bar', got %v", v["foo"])
}

// Release the external value
val.Release()
if v8.ExternalCount() != 0 {
t.Errorf("expected external count to be 0, got %v", v8.ExternalCount())
}
}
21 changes: 21 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,18 @@ ValuePtr NewValueBigIntFromUnsigned(IsolatePtr iso, uint64_t v) {
return tracked_value(ctx, val);
}

ValuePtr NewValueExternal(IsolatePtr iso, void* v) {
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);
m_value* val = new m_value;
val->id = 0;
val->iso = iso;
val->ctx = ctx;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(
iso, External::New(iso, v));
return tracked_value(ctx, val);
}


RtnValue NewValueBigIntFromWords(IsolatePtr iso,
int sign_bit,
int word_count,
Expand Down Expand Up @@ -1040,6 +1052,15 @@ int64_t ValueToInteger(ValuePtr ptr) {
return value->IntegerValue(local_ctx).ToChecked();
}

uint64_t ValueToExternal(ValuePtr ptr) {
LOCAL_VALUE(ptr);
if (value->IsExternal()) {
Local<External> external = value.As<External>();
return reinterpret_cast<uint64_t>(external->Value());
}
return 0;
}

double ValueToNumber(ValuePtr ptr) {
LOCAL_VALUE(ptr);
return value->NumberValue(local_ctx).ToChecked();
Expand Down
3 changes: 3 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ extern RtnValue NewValueBigIntFromWords(IsolatePtr iso_ptr,
int sign_bit,
int word_count,
const uint64_t* words);
extern ValuePtr NewValueExternal(IsolatePtr iso_ptr, void* v); // Yao App Engine: Implement NewValueExternal to Share Golang Structs

void ValueRelease(ValuePtr ptr);
extern RtnString ValueToString(ValuePtr ptr);
const uint32_t* ValueToArrayIndex(ValuePtr ptr);
Expand All @@ -233,6 +235,7 @@ int64_t ValueToInteger(ValuePtr ptr);
double ValueToNumber(ValuePtr ptr);
RtnString ValueToDetailString(ValuePtr ptr);
uint32_t ValueToUint32(ValuePtr ptr);
uint64_t ValueToExternal(ValuePtr ptr); // Yao App Engine: Implement ValueToExternal to Share Golang Structs
extern ValueBigInt ValueToBigInt(ValuePtr ptr);
extern RtnValue ValueToObject(ValuePtr ptr);
int ValueSameValue(ValuePtr ptr, ValuePtr otherPtr);
Expand Down
5 changes: 5 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,11 @@ func (v *Value) IsProxy() bool {

// Release this value. Using the value after calling this function will result in undefined behavior.
func (v *Value) Release() {

// Yao: Before releasing the value, we need to release the external object
// if it is an external object.
v.ReleaseExternal()

C.ValueRelease(v.ptr)
}

Expand Down

0 comments on commit 83af34e

Please sign in to comment.