From 6706c0c833a7ce98287c771f3e808f33ee7b29d4 Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Mon, 3 Apr 2023 09:06:31 -0600
Subject: [PATCH 1/7] Initial (rough) support for SharedArrayBuffer

---
 go.mod        |  2 +-
 v8go.cc       | 41 +++++++++++++++++++++++++++++++++++++++++
 v8go.h        |  9 +++++++++
 value.go      | 32 +++++++++++++++++++++++++-------
 value_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 124 insertions(+), 8 deletions(-)

diff --git a/go.mod b/go.mod
index 0bacfa29..4a8709c8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
 module rogchap.com/v8go
 
-go 1.16
+go 1.17
diff --git a/v8go.cc b/v8go.cc
index 86cdc874..f32e3bd0 100644
--- a/v8go.cc
+++ b/v8go.cc
@@ -1670,4 +1670,45 @@ 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();
+}
+
 }
diff --git a/v8go.h b/v8go.h
index b20daca4..57e20cfa 100644
--- a/v8go.h
+++ b/v8go.h
@@ -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>
 
@@ -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
diff --git a/value.go b/value.go
index 61d4deec..7ded7f20 100644
--- a/value.go
+++ b/value.go
@@ -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>")
@@ -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
+}
diff --git a/value_test.go b/value_test.go
index d386a589..04403df5 100644
--- a/value_test.go
+++ b/value_test.go
@@ -7,6 +7,7 @@ package v8go_test
 import (
 	"bytes"
 	"fmt"
+	"log"
 	"math"
 	"math/big"
 	"reflect"
@@ -711,3 +712,50 @@ func TestValueMarshalJSON(t *testing.T) {
 		})
 	}
 }
+
+func TestValueArrayBufferContents(t *testing.T) {
+	t.Parallel()
+	log.Printf("v8.Version(): %#v\n", v8.Version())
+	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()
+	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")
+	}
+}

From ed7e8bbac43bc0f52e1a075e80eba953d9d54f9f Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Mon, 3 Apr 2023 11:35:22 -0600
Subject: [PATCH 2/7] Handle error in test

---
 value_test.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/value_test.go b/value_test.go
index 04403df5..665531a3 100644
--- a/value_test.go
+++ b/value_test.go
@@ -741,6 +741,9 @@ func TestValueArrayBufferContents(t *testing.T) {
 	}
 
 	buf, cleanup, err := val.SharedArrayBufferGetContents()
+	if err != nil {
+		t.Fatalf("error getting array buffer contents: %#v", err)
+	}
 	defer cleanup()
 
 	if len(buf) != 1024 {

From 5df1f4a1174383c57d4127ea95cdfaf45970fe0b Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Tue, 4 Apr 2023 08:41:03 -0600
Subject: [PATCH 3/7] Remove unneeded log

---
 value_test.go | 2 --
 1 file changed, 2 deletions(-)

diff --git a/value_test.go b/value_test.go
index 665531a3..236a8cd3 100644
--- a/value_test.go
+++ b/value_test.go
@@ -7,7 +7,6 @@ package v8go_test
 import (
 	"bytes"
 	"fmt"
-	"log"
 	"math"
 	"math/big"
 	"reflect"
@@ -715,7 +714,6 @@ func TestValueMarshalJSON(t *testing.T) {
 
 func TestValueArrayBufferContents(t *testing.T) {
 	t.Parallel()
-	log.Printf("v8.Version(): %#v\n", v8.Version())
 	iso := v8.NewIsolate()
 	defer iso.Dispose()
 

From 02c9ec07d1f06a9b301675bfc362dda803241363 Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Wed, 5 Apr 2023 12:06:07 -0600
Subject: [PATCH 4/7] Fix lint errors

---
 v8go.cc | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/v8go.cc b/v8go.cc
index f32e3bd0..836bf204 100644
--- a/v8go.cc
+++ b/v8go.cc
@@ -1675,8 +1675,7 @@ void SetFlags(const char* flags) {
 
 struct v8BackingStore {
   v8BackingStore(std::shared_ptr<v8::BackingStore>&& ptr)
-    : backing_store{ptr}
-  {}
+      : backing_store{ptr} {}
   std::shared_ptr<v8::BackingStore> backing_store;
 };
 
@@ -1689,7 +1688,7 @@ BackingStorePtr SharedArrayBufferGetBackingStore(ValuePtr ptr) {
 }
 
 void BackingStoreRelease(BackingStorePtr ptr) {
-  if (ptr == nullptr){
+  if (ptr == nullptr) {
     return;
   }
   ptr->backing_store.reset();
@@ -1697,7 +1696,7 @@ void BackingStoreRelease(BackingStorePtr ptr) {
 }
 
 void* BackingStoreData(BackingStorePtr ptr) {
-  if (ptr == nullptr){
+  if (ptr == nullptr) {
     return nullptr;
   }
 
@@ -1705,10 +1704,9 @@ void* BackingStoreData(BackingStorePtr ptr) {
 }
 
 size_t BackingStoreByteLength(BackingStorePtr ptr) {
-  if (ptr == nullptr){
+  if (ptr == nullptr) {
     return 0;
   }
   return ptr->backing_store->ByteLength();
 }
-
 }

From 17a6ab1f6f09229643c570d22a6c484022217354 Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Mon, 10 Apr 2023 09:37:06 -0600
Subject: [PATCH 5/7] Fix lint?

---
 function_template.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/function_template.go b/function_template.go
index 66f1b8ce..42ff0058 100644
--- a/function_template.go
+++ b/function_template.go
@@ -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)

From 8b555ac69af14d8d36ef004cd2ab5aa9c47bb85c Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Mon, 10 Apr 2023 09:56:02 -0600
Subject: [PATCH 6/7] Add another check so code cov doesn't slip

---
 value_test.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/value_test.go b/value_test.go
index 236a8cd3..5bd439d7 100644
--- a/value_test.go
+++ b/value_test.go
@@ -759,4 +759,14 @@ func TestValueArrayBufferContents(t *testing.T) {
 	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")
+	}
 }

From 06d9d9639d8f017a0ed951967884716405c5c9be Mon Sep 17 00:00:00 2001
From: Jonathan Geddes <jonathan.geddes@reddit.com>
Date: Mon, 10 Apr 2023 10:01:00 -0600
Subject: [PATCH 7/7] Update Changelog

---
 CHANGELOG.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7ba1668..1a366da1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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