diff --git a/compiler/natives/src/encoding/gob/gob.go b/compiler/natives/src/encoding/gob/gob.go new file mode 100644 index 000000000..f21315171 --- /dev/null +++ b/compiler/natives/src/encoding/gob/gob.go @@ -0,0 +1,40 @@ +//go:build js +// +build js + +package gob + +import ( + "reflect" + "sync" +) + +type typeInfo struct { + id typeId + encInit sync.Mutex + + // temporarily replacement of atomic.Pointer[encEngine] for go1.20 without generics. + encoder atomicEncEnginePointer + wire *wireType +} + +type atomicEncEnginePointer struct { + v *encEngine +} + +func (x *atomicEncEnginePointer) Load() *encEngine { return x.v } +func (x *atomicEncEnginePointer) Store(val *encEngine) { x.v = val } + +// temporarily replacement of growSlice[E any] for go1.20 without generics. +func growSlice(v reflect.Value, ps any, length int) { + vps := reflect.ValueOf(ps) + vs := vps.Elem() + zero := reflect.Zero(vs.Elem().Type()) + vs.Set(reflect.Append(vs, zero)) + cp := vs.Cap() + if cp > length { + cp = length + } + vs.Set(vs.Slice(0, cp)) + v.Set(vs) + vps.Set(vs.Addr()) +} diff --git a/compiler/natives/src/go/token/position.go b/compiler/natives/src/go/token/position.go index 6a1ee0c15..436c48380 100644 --- a/compiler/natives/src/go/token/position.go +++ b/compiler/natives/src/go/token/position.go @@ -20,3 +20,11 @@ type atomicFilePointer struct { func (x *atomicFilePointer) Load() *File { return x.v } func (x *atomicFilePointer) Store(val *File) { x.v = val } + +func (x *atomicFilePointer) CompareAndSwap(old, new *File) bool { + if x.v == old { + x.v = new + return true + } + return false +} diff --git a/compiler/natives/src/internal/coverage/slicereader/slicereader.go b/compiler/natives/src/internal/coverage/slicereader/slicereader.go new file mode 100644 index 000000000..71409c5b9 --- /dev/null +++ b/compiler/natives/src/internal/coverage/slicereader/slicereader.go @@ -0,0 +1,12 @@ +//go:build js +// +build js + +package slicereader + +func toString(b []byte) string { + if len(b) == 0 { + return `` + } + // Overwritten to avoid `unsafe.String` + return string(b) +} diff --git a/compiler/natives/src/internal/godebug/godebug.go b/compiler/natives/src/internal/godebug/godebug.go new file mode 100644 index 000000000..a303bed67 --- /dev/null +++ b/compiler/natives/src/internal/godebug/godebug.go @@ -0,0 +1,95 @@ +//go:build js +// +build js + +package godebug + +import ( + "sync" +) + +type Setting struct { + name string + once sync.Once + + // temporarily replacement of atomic.Pointer[string] for go1.20 without generics. + value *atomicStringPointer +} + +type atomicStringPointer struct { + v *string +} + +func (x *atomicStringPointer) Load() *string { return x.v } +func (x *atomicStringPointer) Store(val *string) { x.v = val } + +func (s *Setting) Value() string { + s.once.Do(func() { + v, ok := cache.Load(s.name) + if !ok { + // temporarily replacement of atomic.Pointer[string] for go1.20 without generics. + p := new(atomicStringPointer) + p.Store(&empty) + v, _ = cache.LoadOrStore(s.name, p) + } + // temporarily replacement of atomic.Pointer[string] for go1.20 without generics. + s.value = v.(*atomicStringPointer) + }) + return *s.value.Load() +} + +// setUpdate is provided by package runtime. +// It calls update(def, env), where def is the default GODEBUG setting +// and env is the current value of the $GODEBUG environment variable. +// After that first call, the runtime calls update(def, env) +// again each time the environment variable changes +// (due to use of os.Setenv, for example). +// +// GOPHERJS: Currently we don't inject a proxy into process.env to watch +// for changes via syscall.runtimeSetenv and syscall.runtimeUnsetenv. +// We may want to look into this in the future. +func setUpdate(update func(string, string)) {} + +func update(def, env string) { + updateMu.Lock() + defer updateMu.Unlock() + + did := make(map[string]bool) + parse(did, env) + parse(did, def) + + cache.Range(func(name, v any) bool { + if !did[name.(string)] { + // temporarily replacement of atomic.Pointer[string] for go1.20 without generics. + v.(*atomicStringPointer).Store(&empty) + } + return true + }) +} + +func parse(did map[string]bool, s string) { + end := len(s) + eq := -1 + for i := end - 1; i >= -1; i-- { + if i == -1 || s[i] == ',' { + if eq >= 0 { + name, value := s[i+1:eq], s[eq+1:end] + if !did[name] { + did[name] = true + v, ok := cache.Load(name) + if !ok { + // temporarily replacement of atomic.Pointer[string] for go1.20 without generics. + p := new(atomicStringPointer) + p.Store(&empty) + v, _ = cache.LoadOrStore(name, p) + } + // temporarily replacement of atomic.Pointer[string] for go1.20 without generics. + v.(*atomicStringPointer).Store(&value) + } + } + eq = -1 + end = i + } else if s[i] == '=' { + eq = i + } + } +} diff --git a/compiler/natives/src/math/rand/rand.go b/compiler/natives/src/math/rand/rand.go new file mode 100644 index 000000000..0dfb1b279 --- /dev/null +++ b/compiler/natives/src/math/rand/rand.go @@ -0,0 +1,9 @@ +//go:build js +// +build js + +package rand + +import _ "unsafe" + +//go:linkname fastrand64 runtime.fastrand64 +func fastrand64() uint64 diff --git a/compiler/natives/src/net/http/client_test.go b/compiler/natives/src/net/http/client_test.go index 302b800df..b3739fc09 100644 --- a/compiler/natives/src/net/http/client_test.go +++ b/compiler/natives/src/net/http/client_test.go @@ -6,14 +6,14 @@ import ( "testing" ) -func testClientTimeout(t *testing.T, h2 bool) { +func testClientTimeout(t *testing.T, mode testMode) { // The original test expects Client.Timeout error to be returned, but under // GopherJS an "i/o timeout" error is frequently returned. Otherwise the test // seems to be working correctly. t.Skip("Flaky test under GopherJS.") } -func testClientTimeout_Headers(t *testing.T, h2 bool) { +func testClientTimeout_Headers(t *testing.T, mode testMode) { // The original test expects Client.Timeout error to be returned, but under // GopherJS an "i/o timeout" error is frequently returned. Otherwise the test // seems to be working correctly. diff --git a/compiler/natives/src/net/http/clientserver_test.go b/compiler/natives/src/net/http/clientserver_test.go index 35b44dd4d..39f1a2d73 100644 --- a/compiler/natives/src/net/http/clientserver_test.go +++ b/compiler/natives/src/net/http/clientserver_test.go @@ -7,10 +7,10 @@ import ( "testing" ) -func testTransportGCRequest(t *testing.T, h2, body bool) { +func testTransportGCRequest(t *testing.T, mode testMode, body bool) { t.Skip("The test relies on runtime.SetFinalizer(), which is not supported by GopherJS.") } -func testWriteHeaderAfterWrite(t *testing.T, h2, hijack bool) { +func testWriteHeaderAfterWrite(t *testing.T, mode testMode, hijack bool) { t.Skip("GopherJS source maps don't preserve original function names in stack traces, which this test relied on.") } diff --git a/compiler/natives/src/net/http/http.go b/compiler/natives/src/net/http/http.go index 8fd607c4d..f82c0363c 100644 --- a/compiler/natives/src/net/http/http.go +++ b/compiler/natives/src/net/http/http.go @@ -6,10 +6,15 @@ package http import ( "bufio" "bytes" + "context" + "crypto/tls" "errors" "io" + "net" "net/textproto" "strconv" + "sync" + "sync/atomic" "github.com/gopherjs/gopherjs/js" ) @@ -113,3 +118,30 @@ func (t *XHRTransport) CancelRequest(req *Request) { xhr.Call("abort") } } + +type conn struct { + server *Server + cancelCtx context.CancelFunc + rwc net.Conn + remoteAddr string + tlsState *tls.ConnectionState + werr error + r *connReader + bufr *bufio.Reader + bufw *bufio.Writer + lastMethod string + + // temporarily replacement of `atomic.Pointer[response]` for go1.20 without generics. + curReq atomicResponsePointer + + curState atomic.Uint64 + mu sync.Mutex + hijackedv bool +} + +type atomicResponsePointer struct { + v *response +} + +func (x *atomicResponsePointer) Load() *response { return x.v } +func (x *atomicResponsePointer) Store(val *response) { x.v = val } diff --git a/compiler/natives/src/strings/strings_test.go b/compiler/natives/src/strings/strings_test.go index fb9a4a57a..3b0775e63 100644 --- a/compiler/natives/src/strings/strings_test.go +++ b/compiler/natives/src/strings/strings_test.go @@ -18,5 +18,9 @@ func TestCompareStrings(t *testing.T) { } func TestClone(t *testing.T) { - t.Skip("conversion to reflect.StringHeader is not supported in GopherJS") + t.Skip("conversion to unsafe.StringData is not supported in GopherJS") +} + +func TestMap(t *testing.T) { + t.Skip("identity test uses unsafe.StringData is not supported in GopherJS") } diff --git a/compiler/natives/src/sync/atomic/atomic.go b/compiler/natives/src/sync/atomic/atomic.go index 1cbfe65f9..d993f3b80 100644 --- a/compiler/natives/src/sync/atomic/atomic.go +++ b/compiler/natives/src/sync/atomic/atomic.go @@ -221,5 +221,19 @@ func sameType(x, y interface{}) bool { return js.InternalObject(x).Get("constructor") == js.InternalObject(y).Get("constructor") } -//gopherjs:purge for go1.19 without generics +// Override pointer so that the type check in the source code is satisfied +// but remove the fields and methods for go1.20 without generics. +// See https://cs.opensource.google/go/go/+/refs/tags/go1.20.14:src/sync/atomic/type.go;l=40 type Pointer[T any] struct{} + +//gopherjs:purge for go1.20 without generics +func (x *Pointer[T]) Load() *T + +//gopherjs:purge for go1.20 without generics +func (x *Pointer[T]) Store(val *T) + +//gopherjs:purge for go1.20 without generics +func (x *Pointer[T]) Swap(new *T) (old *T) + +//gopherjs:purge for go1.20 without generics +func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) diff --git a/compiler/natives/src/syscall/syscall_js_wasm.go b/compiler/natives/src/syscall/syscall_js_wasm.go index 5bcbdeed4..7ee92342e 100644 --- a/compiler/natives/src/syscall/syscall_js_wasm.go +++ b/compiler/natives/src/syscall/syscall_js_wasm.go @@ -22,6 +22,14 @@ func runtime_envs() []string { return envs } +func runtimeSetenv(k, v string) { + setenv_c(k, v) +} + +func runtimeUnsetenv(k string) { + unsetenv_c(k) +} + func setenv_c(k, v string) { process := js.Global().Get("process") if process.IsUndefined() { diff --git a/compiler/natives/src/time/export_test.go b/compiler/natives/src/time/export_test.go new file mode 100644 index 000000000..edcbb9324 --- /dev/null +++ b/compiler/natives/src/time/export_test.go @@ -0,0 +1,7 @@ +//go:build js +// +build js + +package time + +// replaced for go1.20 temporarily without generics. +var ParseRFC3339 = parseRFC3339