From a83c5fa72a01c93c19077fedec6fb5c2fa965915 Mon Sep 17 00:00:00 2001 From: Grant Nelson Date: Tue, 16 Jan 2024 15:06:25 -0700 Subject: [PATCH] Updating natives for go1.19 --- .../natives/src/crypto/elliptic/nistec.go | 81 ++++++++ .../src/crypto/internal/boring/bbig/big.go | 42 ++++ .../src/crypto/internal/nistec/nistec_test.go | 88 +++++++++ .../src/crypto/internal/nistec/wrapper.go | 185 ++++++++++++++++++ compiler/natives/src/go/token/position.go | 26 +++ compiler/natives/src/hash/maphash/maphash.go | 2 +- compiler/natives/src/runtime/runtime.go | 3 +- compiler/natives/src/sync/atomic/atomic.go | 3 + .../natives/src/sync/atomic/atomic_test.go | 53 ++++- compiler/natives/src/sync/sync.go | 13 +- compiler/natives/src/testing/helper_test.go | 4 + .../natives/src/testing/helperfuncs_test.go | 13 ++ 12 files changed, 504 insertions(+), 9 deletions(-) create mode 100644 compiler/natives/src/crypto/elliptic/nistec.go create mode 100644 compiler/natives/src/crypto/internal/boring/bbig/big.go create mode 100644 compiler/natives/src/crypto/internal/nistec/nistec_test.go create mode 100644 compiler/natives/src/crypto/internal/nistec/wrapper.go create mode 100644 compiler/natives/src/go/token/position.go create mode 100644 compiler/natives/src/testing/helperfuncs_test.go diff --git a/compiler/natives/src/crypto/elliptic/nistec.go b/compiler/natives/src/crypto/elliptic/nistec.go new file mode 100644 index 000000000..326c602d5 --- /dev/null +++ b/compiler/natives/src/crypto/elliptic/nistec.go @@ -0,0 +1,81 @@ +//go:build js +// +build js + +package elliptic + +import ( + "crypto/internal/nistec" + "math/big" +) + +// nistPoint uses generics so must be removed for generic-less GopherJS. +// All the following code changes in this file are to make p224, p256, +// p521, and p384 still function correctly without this generic struct. +// +//gopherjs:purge for go1.19 without generics +type nistPoint[T any] interface{} + +// nistCurve replaces the generics with a version using the wrappedPoint +// interface, then update all the method signatures to also use wrappedPoint. +type nistCurve struct { + newPoint func() nistec.WrappedPoint + params *CurveParams +} + +//gopherjs:override-signature +func (curve *nistCurve) Params() *CurveParams + +//gopherjs:override-signature +func (curve *nistCurve) IsOnCurve(x, y *big.Int) bool + +//gopherjs:override-signature +func (curve *nistCurve) pointFromAffine(x, y *big.Int) (p nistec.WrappedPoint, err error) + +//gopherjs:override-signature +func (curve *nistCurve) pointToAffine(p nistec.WrappedPoint) (x, y *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) normalizeScalar(scalar []byte) []byte + +//gopherjs:override-signature +func (curve *nistCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) Unmarshal(data []byte) (x, y *big.Int) + +//gopherjs:override-signature +func (curve *nistCurve) UnmarshalCompressed(data []byte) (x, y *big.Int) + +var p224 = &nistCurve{ + newPoint: nistec.NewP224WrappedPoint, +} + +type p256Curve struct { + nistCurve +} + +var p256 = &p256Curve{ + nistCurve: nistCurve{ + newPoint: nistec.NewP256WrappedPoint, + }, +} + +var p521 = &nistCurve{ + newPoint: nistec.NewP521WrappedPoint, +} + +var p384 = &nistCurve{ + newPoint: nistec.NewP384WrappedPoint, +} diff --git a/compiler/natives/src/crypto/internal/boring/bbig/big.go b/compiler/natives/src/crypto/internal/boring/bbig/big.go new file mode 100644 index 000000000..30ffe1fcd --- /dev/null +++ b/compiler/natives/src/crypto/internal/boring/bbig/big.go @@ -0,0 +1,42 @@ +//go:build js +// +build js + +package bbig + +import ( + "crypto/internal/boring" + "math/big" +) + +func Enc(b *big.Int) boring.BigInt { + if b == nil { + return nil + } + x := b.Bits() + if len(x) == 0 { + return boring.BigInt{} + } + // Replacing original which uses unsafe: + // return unsafe.Slice((*uint)(&x[0]), len(x)) + b2 := make(boring.BigInt, len(x)) + for i, w := range x { + b2[i] = uint(w) + } + return b2 +} + +func Dec(b boring.BigInt) *big.Int { + if b == nil { + return nil + } + if len(b) == 0 { + return new(big.Int) + } + // Replacing original which uses unsafe: + //x := unsafe.Slice((*big.Word)(&b[0]), len(b)) + x := make([]big.Word, len(b)) + for i, w := range b { + x[i] = big.Word(w) + } + return new(big.Int).SetBits(x) +} diff --git a/compiler/natives/src/crypto/internal/nistec/nistec_test.go b/compiler/natives/src/crypto/internal/nistec/nistec_test.go new file mode 100644 index 000000000..f89aaeae0 --- /dev/null +++ b/compiler/natives/src/crypto/internal/nistec/nistec_test.go @@ -0,0 +1,88 @@ +//go:build js +// +build js + +package nistec_test + +import ( + "crypto/elliptic" + "testing" +) + +func TestAllocations(t *testing.T) { + t.Skip("testing.AllocsPerRun not supported in GopherJS") +} + +//gopherjs:purge +type nistPoint[T any] interface{} + +func TestEquivalents(t *testing.T) { + t.Run("P224", func(t *testing.T) { + testEquivalents(t, nistec.NewP224WrappedPoint, nistec.NewP224WrappedGenerator, elliptic.P224()) + }) + t.Run("P256", func(t *testing.T) { + testEquivalents(t, nistec.NewP256WrappedPoint, nistec.NewP256WrappedGenerator, elliptic.P256()) + }) + t.Run("P384", func(t *testing.T) { + testEquivalents(t, nistec.NewP384WrappedPoint, nistec.NewP384WrappedGenerator, elliptic.P384()) + }) + t.Run("P521", func(t *testing.T) { + testEquivalents(t, nistec.NewP521WrappedPoint, nistec.NewP521WrappedGenerator, elliptic.P521()) + }) +} + +//gopherjs:override-signature +func testEquivalents(t *testing.T, newPoint, newGenerator func() WrappedPoint, c elliptic.Curve) {} + +func TestScalarMult(t *testing.T) { + t.Run("P224", func(t *testing.T) { + testScalarMult(t, nistec.NewP224WrappedPoint, nistec.NewP224WrappedGenerator, elliptic.P224()) + }) + t.Run("P256", func(t *testing.T) { + testScalarMult(t, nistec.NewP256WrappedPoint, nistec.NewP256WrappedGenerator, elliptic.P256()) + }) + t.Run("P384", func(t *testing.T) { + testScalarMult(t, nistec.NewP384WrappedPoint, nistec.NewP384WrappedGenerator, elliptic.P384()) + }) + t.Run("P521", func(t *testing.T) { + testScalarMult(t, nistec.NewP521WrappedPoint, nistec.NewP521WrappedGenerator, elliptic.P521()) + }) +} + +//gopherjs:override-signature +func testScalarMult(t *testing.T, newPoint, newGenerator func() WrappedPoint, c elliptic.Curve) + +func BenchmarkScalarMult(b *testing.B) { + b.Run("P224", func(b *testing.B) { + benchmarkScalarMult(b, nistec.NewP224WrappedGenerator(), 28) + }) + b.Run("P256", func(b *testing.B) { + benchmarkScalarMult(b, nistec.NewP256GWrappedenerator(), 32) + }) + b.Run("P384", func(b *testing.B) { + benchmarkScalarMult(b, nistec.NewP384WrappedGenerator(), 48) + }) + b.Run("P521", func(b *testing.B) { + benchmarkScalarMult(b, nistec.NewP521WrappedGenerator(), 66) + }) +} + +//gopherjs:override-signature +func benchmarkScalarMult(b *testing.B, p WrappedPoint, scalarSize int) + +func BenchmarkScalarBaseMult(b *testing.B) { + b.Run("P224", func(b *testing.B) { + benchmarkScalarBaseMult(b, nistec.NewP22Wrapped4Generator(), 28) + }) + b.Run("P256", func(b *testing.B) { + benchmarkScalarBaseMult(b, nistec.NewP256WrappedGenerator(), 32) + }) + b.Run("P384", func(b *testing.B) { + benchmarkScalarBaseMult(b, nistec.NewP384WrappedGenerator(), 48) + }) + b.Run("P521", func(b *testing.B) { + benchmarkScalarBaseMult(b, nistec.NewP521GWrappedenerator(), 66) + }) +} + +//gopherjs:override-signature +func benchmarkScalarBaseMult(b *testing.B, p WrappedPoint, scalarSize int) diff --git a/compiler/natives/src/crypto/internal/nistec/wrapper.go b/compiler/natives/src/crypto/internal/nistec/wrapper.go new file mode 100644 index 000000000..0d6706b52 --- /dev/null +++ b/compiler/natives/src/crypto/internal/nistec/wrapper.go @@ -0,0 +1,185 @@ +//go:build js +// +build js + +package nistec + +type WrappedPoint interface { + Bytes() []byte + SetBytes(b []byte) (WrappedPoint, error) + Add(w1, w2 WrappedPoint) WrappedPoint + Double(w1 WrappedPoint) WrappedPoint + ScalarMult(w1 WrappedPoint, scalar []byte) (WrappedPoint, error) + ScalarBaseMult(scalar []byte) (WrappedPoint, error) +} + +type p224Wrapper struct { + point *P224Point +} + +func wrapP224(point *P224Point) WrappedPoint { + return p224Wrapper{point: point} +} + +func NewP224WrappedPoint() WrappedPoint { + return wrapP224(NewP224Point()) +} + +func NewP224WrappedGenerator() WrappedPoint { + return wrapP224(NewP224Generator()) +} + +func (w p224Wrapper) Bytes() []byte { + return w.point.Bytes() +} + +func (w p224Wrapper) SetBytes(b []byte) (WrappedPoint, error) { + p, err := w.point.SetBytes(b) + return wrapP224(p), err +} + +func (w p224Wrapper) Add(w1, w2 WrappedPoint) WrappedPoint { + return wrapP224(w.point.Add(w1.(p224Wrapper).point, w2.(p224Wrapper).point)) +} + +func (w p224Wrapper) Double(w1 WrappedPoint) WrappedPoint { + return wrapP224(w.point.Double(w1.(p224Wrapper).point)) +} + +func (w p224Wrapper) ScalarMult(w1 WrappedPoint, scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarMult(w1.(p224Wrapper).point, scalar) + return wrapP224(p), err +} + +func (w p224Wrapper) ScalarBaseMult(scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarBaseMult(scalar) + return wrapP224(p), err +} + +type p256Wrapper struct { + point *P256Point +} + +func wrapP256(point *P256Point) WrappedPoint { + return p256Wrapper{point: point} +} + +func NewP256WrappedPoint() WrappedPoint { + return wrapP256(NewP256Point()) +} + +func NewP256WrappedGenerator() WrappedPoint { + return wrapP256(NewP256Generator()) +} + +func (w p256Wrapper) Bytes() []byte { + return w.point.Bytes() +} + +func (w p256Wrapper) SetBytes(b []byte) (WrappedPoint, error) { + p, err := w.point.SetBytes(b) + return wrapP256(p), err +} + +func (w p256Wrapper) Add(w1, w2 WrappedPoint) WrappedPoint { + return wrapP256(w.point.Add(w1.(p256Wrapper).point, w2.(p256Wrapper).point)) +} + +func (w p256Wrapper) Double(w1 WrappedPoint) WrappedPoint { + return wrapP256(w.point.Double(w1.(p256Wrapper).point)) +} + +func (w p256Wrapper) ScalarMult(w1 WrappedPoint, scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarMult(w1.(p256Wrapper).point, scalar) + return wrapP256(p), err +} + +func (w p256Wrapper) ScalarBaseMult(scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarBaseMult(scalar) + return wrapP256(p), err +} + +type p521Wrapper struct { + point *P521Point +} + +func wrapP521(point *P521Point) WrappedPoint { + return p521Wrapper{point: point} +} + +func NewP521WrappedPoint() WrappedPoint { + return wrapP521(NewP521Point()) +} + +func NewP521WrappedGenerator() WrappedPoint { + return wrapP521(NewP521Generator()) +} + +func (w p521Wrapper) Bytes() []byte { + return w.point.Bytes() +} + +func (w p521Wrapper) SetBytes(b []byte) (WrappedPoint, error) { + p, err := w.point.SetBytes(b) + return wrapP521(p), err +} + +func (w p521Wrapper) Add(w1, w2 WrappedPoint) WrappedPoint { + return wrapP521(w.point.Add(w1.(p521Wrapper).point, w2.(p521Wrapper).point)) +} + +func (w p521Wrapper) Double(w1 WrappedPoint) WrappedPoint { + return wrapP521(w.point.Double(w1.(p521Wrapper).point)) +} + +func (w p521Wrapper) ScalarMult(w1 WrappedPoint, scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarMult(w1.(p521Wrapper).point, scalar) + return wrapP521(p), err +} + +func (w p521Wrapper) ScalarBaseMult(scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarBaseMult(scalar) + return wrapP521(p), err +} + +type p384Wrapper struct { + point *P384Point +} + +func wrapP384(point *P384Point) WrappedPoint { + return p384Wrapper{point: point} +} + +func NewP384WrappedPoint() WrappedPoint { + return wrapP384(NewP384Point()) +} + +func NewP384WrappedGenerator() WrappedPoint { + return wrapP384(NewP384Generator()) +} + +func (w p384Wrapper) Bytes() []byte { + return w.point.Bytes() +} + +func (w p384Wrapper) SetBytes(b []byte) (WrappedPoint, error) { + p, err := w.point.SetBytes(b) + return wrapP384(p), err +} + +func (w p384Wrapper) Add(w1, w2 WrappedPoint) WrappedPoint { + return wrapP384(w.point.Add(w1.(p384Wrapper).point, w2.(p384Wrapper).point)) +} + +func (w p384Wrapper) Double(w1 WrappedPoint) WrappedPoint { + return wrapP384(w.point.Double(w1.(p384Wrapper).point)) +} + +func (w p384Wrapper) ScalarMult(w1 WrappedPoint, scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarMult(w1.(p384Wrapper).point, scalar) + return wrapP384(p), err +} + +func (w p384Wrapper) ScalarBaseMult(scalar []byte) (WrappedPoint, error) { + p, err := w.point.ScalarBaseMult(scalar) + return wrapP384(p), err +} diff --git a/compiler/natives/src/go/token/position.go b/compiler/natives/src/go/token/position.go new file mode 100644 index 000000000..c7fabb810 --- /dev/null +++ b/compiler/natives/src/go/token/position.go @@ -0,0 +1,26 @@ +//go:build js +// +build js + +package token + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +type FileSet struct { + mutex sync.RWMutex + base int + files []*File + + // replaced atomic.Pointer[File] for go1.19 without generics + last atomicFilePointer +} + +type atomicFilePointer struct { + v unsafe.Pointer +} + +func (x *atomicFilePointer) Load() *File { return (*File)(atomic.LoadPointer(&x.v)) } +func (x *atomicFilePointer) Store(val *File) { atomic.StorePointer(&x.v, unsafe.Pointer(val)) } diff --git a/compiler/natives/src/hash/maphash/maphash.go b/compiler/natives/src/hash/maphash/maphash.go index 877366f04..dceff2c62 100644 --- a/compiler/natives/src/hash/maphash/maphash.go +++ b/compiler/natives/src/hash/maphash/maphash.go @@ -8,7 +8,7 @@ var hashkey [4]uint32 func init() { for i := range hashkey { - hashkey[i] = runtime_fastrand() + hashkey[i] = uint32(runtime_fastrand64()) } hashkey[0] |= 1 // make sure these numbers are odd hashkey[1] |= 1 diff --git a/compiler/natives/src/runtime/runtime.go b/compiler/natives/src/runtime/runtime.go index 7e76e3ccc..41c60876c 100644 --- a/compiler/natives/src/runtime/runtime.go +++ b/compiler/natives/src/runtime/runtime.go @@ -489,5 +489,6 @@ func throw(s string) { } func nanotime() int64 { - return js.Global.Get("Date").New().Call("getTime").Int64() * int64(1000_000) + const millisecond = 1_000_000 + return js.Global.Get("Date").New().Call("getTime").Int64() * millisecond } diff --git a/compiler/natives/src/sync/atomic/atomic.go b/compiler/natives/src/sync/atomic/atomic.go index ebc98e910..1cbfe65f9 100644 --- a/compiler/natives/src/sync/atomic/atomic.go +++ b/compiler/natives/src/sync/atomic/atomic.go @@ -220,3 +220,6 @@ func sameType(x, y interface{}) bool { // existing and differing for different types. return js.InternalObject(x).Get("constructor") == js.InternalObject(y).Get("constructor") } + +//gopherjs:purge for go1.19 without generics +type Pointer[T any] struct{} diff --git a/compiler/natives/src/sync/atomic/atomic_test.go b/compiler/natives/src/sync/atomic/atomic_test.go index f4450cc67..27ce36df9 100644 --- a/compiler/natives/src/sync/atomic/atomic_test.go +++ b/compiler/natives/src/sync/atomic/atomic_test.go @@ -3,7 +3,51 @@ package atomic_test -import "testing" +import ( + "testing" + "unsafe" +) + +//gopherjs:purge for go1.19 without generics +func testPointers() []unsafe.Pointer {} + +func TestSwapPointer(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestSwapPointerMethod(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestCompareAndSwapPointer(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestCompareAndSwapPointerMethod(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestLoadPointer(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestLoadPointerMethod(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestStorePointer(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +func TestStorePointerMethod(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +//gopherjs:purge for go1.19 without generics +func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) {} + +//gopherjs:purge for go1.19 without generics +func hammerStoreLoadPointerMethod(t *testing.T, paddr unsafe.Pointer) {} func TestHammerStoreLoad(t *testing.T) { t.Skip("use of unsafe") @@ -12,3 +56,10 @@ func TestHammerStoreLoad(t *testing.T) { func TestUnaligned64(t *testing.T) { t.Skip("GopherJS emulates atomics, which makes alignment irrelevant.") } + +func TestNilDeref(t *testing.T) { + t.Skip("GopherJS does not support generics yet.") +} + +//gopherjs:purge for go1.19 without generics +type List struct{} diff --git a/compiler/natives/src/sync/sync.go b/compiler/natives/src/sync/sync.go index 588537751..294b0b109 100644 --- a/compiler/natives/src/sync/sync.go +++ b/compiler/natives/src/sync/sync.go @@ -3,7 +3,11 @@ package sync -import "github.com/gopherjs/gopherjs/js" +import ( + _ "unsafe" // For go:linkname + + "github.com/gopherjs/gopherjs/js" +) var semWaiters = make(map[*uint32][]chan bool) @@ -69,11 +73,8 @@ func runtime_canSpin(i int) bool { return false } -// Copy of time.runtimeNano. -func runtime_nanotime() int64 { - const millisecond = 1000000 - return js.Global.Get("Date").New().Call("getTime").Int64() * millisecond -} +//go:linkname runtime_nanotime runtime.nanotime +func runtime_nanotime() int64 // Implemented in runtime. func throw(s string) { diff --git a/compiler/natives/src/testing/helper_test.go b/compiler/natives/src/testing/helper_test.go index b277fa31f..6815fd651 100644 --- a/compiler/natives/src/testing/helper_test.go +++ b/compiler/natives/src/testing/helper_test.go @@ -2,3 +2,7 @@ // +build js package testing + +func TestTBHelper(t *T) { + t.Skip("GopherJS does not support generics yet.") +} diff --git a/compiler/natives/src/testing/helperfuncs_test.go b/compiler/natives/src/testing/helperfuncs_test.go new file mode 100644 index 000000000..54a1ee737 --- /dev/null +++ b/compiler/natives/src/testing/helperfuncs_test.go @@ -0,0 +1,13 @@ +//go:build js +// +build js + +package testing + +//gopherjs:purge for go1.19 without generics +func genericHelper[G any](t *T, msg string) + +//gopherjs:purge for go1.19 without generics +var genericIntHelper = genericHelper[int] + +//gopherjs:purge for go1.19 without generics (uses genericHelper) +func testHelper(t *T)