Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shared array buffers #378

Merged
merged 7 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.10.0] - 2023-04-10

### Changed
- Required Go version changed to 1.17 (needed for SharedArrayBuffer support)

### Added
- Support for getting the underlying data (as a `[]byte`) from a SharedArrayBuffer

### Fixed
- Upgrade to V8 11.1.277.13


## [v0.9.0] - 2023-03-30

### Fixed
Expand Down
1 change: 1 addition & 0 deletions function_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func (tmpl *FunctionTemplate) GetFunction(ctx *Context) *Function {

// Note that ideally `thisAndArgs` would be split into two separate arguments, but they were combined
// to workaround an ERROR_COMMITMENT_LIMIT error on windows that was detected in CI.
//
//export goFunctionCallback
func goFunctionCallback(ctxref int, cbref int, thisAndArgs *C.ValuePtr, argsCount int) C.ValuePtr {
ctx := getContext(ctxref)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module rogchap.com/v8go

go 1.16
go 1.17
39 changes: 39 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1670,4 +1670,43 @@ const char* Version() {
void SetFlags(const char* flags) {
V8::SetFlagsFromString(flags);
}

/********** SharedArrayBuffer & BackingStore ***********/

struct v8BackingStore {
v8BackingStore(std::shared_ptr<v8::BackingStore>&& ptr)
: backing_store{ptr} {}
std::shared_ptr<v8::BackingStore> backing_store;
};

BackingStorePtr SharedArrayBufferGetBackingStore(ValuePtr ptr) {
LOCAL_VALUE(ptr);
auto buffer = Local<SharedArrayBuffer>::Cast(value);
auto backing_store = buffer->GetBackingStore();
auto proxy = new v8BackingStore(std::move(backing_store));
return proxy;
}

void BackingStoreRelease(BackingStorePtr ptr) {
if (ptr == nullptr) {
return;
}
ptr->backing_store.reset();
delete ptr;
}

void* BackingStoreData(BackingStorePtr ptr) {
if (ptr == nullptr) {
return nullptr;
}

return ptr->backing_store->Data();
}

size_t BackingStoreByteLength(BackingStorePtr ptr) {
if (ptr == nullptr) {
return 0;
}
return ptr->backing_store->ByteLength();
}
}
9 changes: 9 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ typedef struct v8ScriptCompilerCachedData v8ScriptCompilerCachedData;
typedef const v8ScriptCompilerCachedData* ScriptCompilerCachedDataPtr;
#endif

// Opaque to both C and C++
typedef struct v8BackingStore v8BackingStore;
typedef v8BackingStore* BackingStorePtr;

#include <stddef.h>
#include <stdint.h>

Expand Down Expand Up @@ -307,6 +311,11 @@ ValuePtr FunctionSourceMapUrl(ValuePtr ptr);
const char* Version();
extern void SetFlags(const char* flags);

extern BackingStorePtr SharedArrayBufferGetBackingStore(ValuePtr ptr);
extern void BackingStoreRelease(BackingStorePtr ptr);
extern void* BackingStoreData(BackingStorePtr ptr);
extern size_t BackingStoreByteLength(BackingStorePtr ptr);

#ifdef __cplusplus
} // extern "C"
#endif
Expand Down
32 changes: 25 additions & 7 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ func Null(iso *Isolate) *Value {
}

// NewValue will create a primitive value. Supported values types to create are:
// string -> V8::String
// int32 -> V8::Integer
// uint32 -> V8::Integer
// int64 -> V8::BigInt
// uint64 -> V8::BigInt
// bool -> V8::Boolean
// *big.Int -> V8::BigInt
//
// string -> V8::String
// int32 -> V8::Integer
// uint32 -> V8::Integer
// int64 -> V8::BigInt
// uint64 -> V8::BigInt
// bool -> V8::Boolean
// *big.Int -> V8::BigInt
func NewValue(iso *Isolate, val interface{}) (*Value, error) {
if iso == nil {
return nil, errors.New("v8go: failed to create new Value: Isolate cannot be <nil>")
Expand Down Expand Up @@ -580,3 +581,20 @@ func (v *Value) MarshalJSON() ([]byte, error) {
}
return []byte(jsonStr), nil
}

func (v *Value) SharedArrayBufferGetContents() ([]byte, func(), error) {
if !v.IsSharedArrayBuffer() {
return nil, nil, errors.New("v8go: value is not a SharedArrayBuffer")
}

backingStore := C.SharedArrayBufferGetBackingStore(v.ptr)
release := func() {
C.BackingStoreRelease(backingStore)
}

byte_ptr := (*byte)(unsafe.Pointer(C.BackingStoreData(backingStore)))
byte_size := C.BackingStoreByteLength(backingStore)
byte_slice := unsafe.Slice(byte_ptr, byte_size)

return byte_slice, release, nil
}
59 changes: 59 additions & 0 deletions value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -711,3 +711,62 @@ func TestValueMarshalJSON(t *testing.T) {
})
}
}

func TestValueArrayBufferContents(t *testing.T) {
t.Parallel()
iso := v8.NewIsolate()
defer iso.Dispose()

ctx := v8.NewContext(iso)
defer ctx.Close()

val, err := ctx.RunScript(`
(()=>{
let buf = new SharedArrayBuffer(1024);
let arr = new Int8Array(buf);
arr[0] = 42;
arr[1] = 52;
return buf;
})();
`, "test.js")

if err != nil {
t.Fatalf("failed to run script: %v", err)
}

if !val.IsSharedArrayBuffer() {
t.Fatalf("expected SharedArrayBuffer value")
}

buf, cleanup, err := val.SharedArrayBufferGetContents()
if err != nil {
t.Fatalf("error getting array buffer contents: %#v", err)
}
defer cleanup()

if len(buf) != 1024 {
t.Fatalf("expected len(buf) to be 1024")
}

if buf[0] != 42 {
t.Fatalf("expected buf[0] to be 42")
}

if buf[1] != 52 {
t.Fatalf("expected buf[1] to be 52")
}

if buf[3] != 0 {
t.Fatalf("expected buf[1] to be 0")
}

// ensure there's an error if we call the method on something that isn't a SharedArrayBuffer
val, err = ctx.RunScript("7", "test2.js")
if err != nil {
t.Fatalf("error running trivial script")
}
_, _, err = val.SharedArrayBufferGetContents()
if err == nil {
t.Fatalf("Expected an error trying call SharedArrayBufferGetContents on value of incorrect type")
}
}