Skip to content

Commit cee5f84

Browse files
authored
shared array buffers (#378)
* Initial support for SharedArrayBuffer
1 parent 0e40e6e commit cee5f84

File tree

7 files changed

+146
-8
lines changed

7 files changed

+146
-8
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [v0.10.0] - 2023-04-10
8+
9+
### Changed
10+
- Required Go version changed to 1.17 (needed for SharedArrayBuffer support)
11+
12+
### Added
13+
- Support for getting the underlying data (as a `[]byte`) from a SharedArrayBuffer
14+
15+
### Fixed
16+
- Upgrade to V8 11.1.277.13
17+
18+
719
## [v0.9.0] - 2023-03-30
820

921
### Fixed

function_template.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func (tmpl *FunctionTemplate) GetFunction(ctx *Context) *Function {
8383

8484
// Note that ideally `thisAndArgs` would be split into two separate arguments, but they were combined
8585
// to workaround an ERROR_COMMITMENT_LIMIT error on windows that was detected in CI.
86+
//
8687
//export goFunctionCallback
8788
func goFunctionCallback(ctxref int, cbref int, thisAndArgs *C.ValuePtr, argsCount int) C.ValuePtr {
8889
ctx := getContext(ctxref)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module rogchap.com/v8go
22

3-
go 1.16
3+
go 1.17

v8go.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,4 +1670,43 @@ const char* Version() {
16701670
void SetFlags(const char* flags) {
16711671
V8::SetFlagsFromString(flags);
16721672
}
1673+
1674+
/********** SharedArrayBuffer & BackingStore ***********/
1675+
1676+
struct v8BackingStore {
1677+
v8BackingStore(std::shared_ptr<v8::BackingStore>&& ptr)
1678+
: backing_store{ptr} {}
1679+
std::shared_ptr<v8::BackingStore> backing_store;
1680+
};
1681+
1682+
BackingStorePtr SharedArrayBufferGetBackingStore(ValuePtr ptr) {
1683+
LOCAL_VALUE(ptr);
1684+
auto buffer = Local<SharedArrayBuffer>::Cast(value);
1685+
auto backing_store = buffer->GetBackingStore();
1686+
auto proxy = new v8BackingStore(std::move(backing_store));
1687+
return proxy;
1688+
}
1689+
1690+
void BackingStoreRelease(BackingStorePtr ptr) {
1691+
if (ptr == nullptr) {
1692+
return;
1693+
}
1694+
ptr->backing_store.reset();
1695+
delete ptr;
1696+
}
1697+
1698+
void* BackingStoreData(BackingStorePtr ptr) {
1699+
if (ptr == nullptr) {
1700+
return nullptr;
1701+
}
1702+
1703+
return ptr->backing_store->Data();
1704+
}
1705+
1706+
size_t BackingStoreByteLength(BackingStorePtr ptr) {
1707+
if (ptr == nullptr) {
1708+
return 0;
1709+
}
1710+
return ptr->backing_store->ByteLength();
1711+
}
16731712
}

v8go.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ typedef struct v8ScriptCompilerCachedData v8ScriptCompilerCachedData;
3535
typedef const v8ScriptCompilerCachedData* ScriptCompilerCachedDataPtr;
3636
#endif
3737

38+
// Opaque to both C and C++
39+
typedef struct v8BackingStore v8BackingStore;
40+
typedef v8BackingStore* BackingStorePtr;
41+
3842
#include <stddef.h>
3943
#include <stdint.h>
4044

@@ -307,6 +311,11 @@ ValuePtr FunctionSourceMapUrl(ValuePtr ptr);
307311
const char* Version();
308312
extern void SetFlags(const char* flags);
309313

314+
extern BackingStorePtr SharedArrayBufferGetBackingStore(ValuePtr ptr);
315+
extern void BackingStoreRelease(BackingStorePtr ptr);
316+
extern void* BackingStoreData(BackingStorePtr ptr);
317+
extern size_t BackingStoreByteLength(BackingStorePtr ptr);
318+
310319
#ifdef __cplusplus
311320
} // extern "C"
312321
#endif

value.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ func Null(iso *Isolate) *Value {
5454
}
5555

5656
// NewValue will create a primitive value. Supported values types to create are:
57-
// string -> V8::String
58-
// int32 -> V8::Integer
59-
// uint32 -> V8::Integer
60-
// int64 -> V8::BigInt
61-
// uint64 -> V8::BigInt
62-
// bool -> V8::Boolean
63-
// *big.Int -> V8::BigInt
57+
//
58+
// string -> V8::String
59+
// int32 -> V8::Integer
60+
// uint32 -> V8::Integer
61+
// int64 -> V8::BigInt
62+
// uint64 -> V8::BigInt
63+
// bool -> V8::Boolean
64+
// *big.Int -> V8::BigInt
6465
func NewValue(iso *Isolate, val interface{}) (*Value, error) {
6566
if iso == nil {
6667
return nil, errors.New("v8go: failed to create new Value: Isolate cannot be <nil>")
@@ -580,3 +581,20 @@ func (v *Value) MarshalJSON() ([]byte, error) {
580581
}
581582
return []byte(jsonStr), nil
582583
}
584+
585+
func (v *Value) SharedArrayBufferGetContents() ([]byte, func(), error) {
586+
if !v.IsSharedArrayBuffer() {
587+
return nil, nil, errors.New("v8go: value is not a SharedArrayBuffer")
588+
}
589+
590+
backingStore := C.SharedArrayBufferGetBackingStore(v.ptr)
591+
release := func() {
592+
C.BackingStoreRelease(backingStore)
593+
}
594+
595+
byte_ptr := (*byte)(unsafe.Pointer(C.BackingStoreData(backingStore)))
596+
byte_size := C.BackingStoreByteLength(backingStore)
597+
byte_slice := unsafe.Slice(byte_ptr, byte_size)
598+
599+
return byte_slice, release, nil
600+
}

value_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,3 +711,62 @@ func TestValueMarshalJSON(t *testing.T) {
711711
})
712712
}
713713
}
714+
715+
func TestValueArrayBufferContents(t *testing.T) {
716+
t.Parallel()
717+
iso := v8.NewIsolate()
718+
defer iso.Dispose()
719+
720+
ctx := v8.NewContext(iso)
721+
defer ctx.Close()
722+
723+
val, err := ctx.RunScript(`
724+
(()=>{
725+
let buf = new SharedArrayBuffer(1024);
726+
let arr = new Int8Array(buf);
727+
arr[0] = 42;
728+
arr[1] = 52;
729+
return buf;
730+
})();
731+
`, "test.js")
732+
733+
if err != nil {
734+
t.Fatalf("failed to run script: %v", err)
735+
}
736+
737+
if !val.IsSharedArrayBuffer() {
738+
t.Fatalf("expected SharedArrayBuffer value")
739+
}
740+
741+
buf, cleanup, err := val.SharedArrayBufferGetContents()
742+
if err != nil {
743+
t.Fatalf("error getting array buffer contents: %#v", err)
744+
}
745+
defer cleanup()
746+
747+
if len(buf) != 1024 {
748+
t.Fatalf("expected len(buf) to be 1024")
749+
}
750+
751+
if buf[0] != 42 {
752+
t.Fatalf("expected buf[0] to be 42")
753+
}
754+
755+
if buf[1] != 52 {
756+
t.Fatalf("expected buf[1] to be 52")
757+
}
758+
759+
if buf[3] != 0 {
760+
t.Fatalf("expected buf[1] to be 0")
761+
}
762+
763+
// ensure there's an error if we call the method on something that isn't a SharedArrayBuffer
764+
val, err = ctx.RunScript("7", "test2.js")
765+
if err != nil {
766+
t.Fatalf("error running trivial script")
767+
}
768+
_, _, err = val.SharedArrayBufferGetContents()
769+
if err == nil {
770+
t.Fatalf("Expected an error trying call SharedArrayBufferGetContents on value of incorrect type")
771+
}
772+
}

0 commit comments

Comments
 (0)