From d1eb76f68b77e904454d34f54080dd46d715541f Mon Sep 17 00:00:00 2001 From: Grant Nelson Date: Thu, 25 Apr 2024 12:46:21 -0600 Subject: [PATCH 1/2] Added built-in support for unsafe.SliceData --- compiler/expressions.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/expressions.go b/compiler/expressions.go index 2768e3d2a..dc766967f 100644 --- a/compiler/expressions.go +++ b/compiler/expressions.go @@ -1058,6 +1058,12 @@ func (fc *funcContext) translateBuiltin(name string, sig *types.Signature, args case "Offsetof": sel, _ := fc.selectionOf(astutil.RemoveParens(args[0]).(*ast.SelectorExpr)) return fc.formatExpr("%d", typesutil.OffsetOf(sizes32, sel)) + case "SliceData": + // SliceData returns nil if the slice is nil, otherwise returns a pointer to the first index of the array, &s[0]. + // If the slice is empty (cap == 0), it returns an "unspecified memory address" or in our case, a pointer to the empty array. + t := fc.typeOf(args[0]).Underlying().(*types.Slice) + elemPtrType := types.NewPointer(t.Elem()) + return fc.formatExpr("(%1e === %2s.nil) ? %3s.nil : $indexPtr(%1e.$array, %1e.$offset, %3s)", args[0], fc.typeName(t), fc.typeName(elemPtrType)) default: panic(fmt.Sprintf("Unhandled builtin: %s\n", name)) } From a19f8ed345cd266b41ddc0a44a2f194361a957ae Mon Sep 17 00:00:00 2001 From: Grant Nelson Date: Thu, 2 May 2024 12:38:12 -0600 Subject: [PATCH 2/2] Adding unsafe.SliceData --- compiler/expressions.go | 5 +---- compiler/prelude/prelude.js | 7 +++++++ tests/js_test.go | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/compiler/expressions.go b/compiler/expressions.go index dc766967f..622101997 100644 --- a/compiler/expressions.go +++ b/compiler/expressions.go @@ -1059,11 +1059,8 @@ func (fc *funcContext) translateBuiltin(name string, sig *types.Signature, args sel, _ := fc.selectionOf(astutil.RemoveParens(args[0]).(*ast.SelectorExpr)) return fc.formatExpr("%d", typesutil.OffsetOf(sizes32, sel)) case "SliceData": - // SliceData returns nil if the slice is nil, otherwise returns a pointer to the first index of the array, &s[0]. - // If the slice is empty (cap == 0), it returns an "unspecified memory address" or in our case, a pointer to the empty array. t := fc.typeOf(args[0]).Underlying().(*types.Slice) - elemPtrType := types.NewPointer(t.Elem()) - return fc.formatExpr("(%1e === %2s.nil) ? %3s.nil : $indexPtr(%1e.$array, %1e.$offset, %3s)", args[0], fc.typeName(t), fc.typeName(elemPtrType)) + return fc.formatExpr(`$sliceData(%e, %s)`, args[0], fc.typeName(t)) default: panic(fmt.Sprintf("Unhandled builtin: %s\n", name)) } diff --git a/compiler/prelude/prelude.js b/compiler/prelude/prelude.js index d35de6b01..0f6b9cb80 100644 --- a/compiler/prelude/prelude.js +++ b/compiler/prelude/prelude.js @@ -569,3 +569,10 @@ var $instanceOf = (x, y) => { var $typeOf = x => { return typeof (x); }; + +var $sliceData = (slice, typ) => { + if (slice === typ.nil) { + return $ptrType(typ.elem).nil; + } + return $indexPtr(slice.$array, slice.$offset, typ.elem); +}; diff --git a/tests/js_test.go b/tests/js_test.go index 2ce43865f..ebebeca2a 100644 --- a/tests/js_test.go +++ b/tests/js_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/google/go-cmp/cmp" "github.com/gopherjs/gopherjs/js" @@ -936,3 +937,41 @@ func TestStructWithNonIdentifierJSTag(t *testing.T) { t.Errorf("value via js.Object.Get gave %q, want %q", got, want) } } + +func TestSliceData(t *testing.T) { + var ( + s0 = []int(nil) + s1 = []int{} + s2 = []int{1, 2, 3} + s3 = s2[1:] + s4 = []int{4, 5, 6} + + sd0 = unsafe.SliceData(s0) + sd1 = unsafe.SliceData(s1) + sd2 = unsafe.SliceData(s2) + sd3 = unsafe.SliceData(s3) + sd4 = unsafe.SliceData(s4) + ) + + if sd0 != nil { + t.Errorf("slice data for nil slice was not nil") + } + if sd1 == nil { + t.Errorf("slice data for empty slice was nil") + } + if sd2 == nil { + t.Errorf("slice data for non-empty slice was nil") + } + if sd3 == nil { + t.Errorf("slice data for sub-slice was nil") + } + if sd1 == sd2 { + t.Errorf("slice data for empty and non-empty slices were the same") + } + if sd2 == sd3 { + t.Errorf("slice data for slice and sub-slice were the same") + } + if sd2 == sd4 { + t.Errorf("slice data for different slices were the same") + } +}