Skip to content

Various standard library and compiler fixes to support Go 1.18 #1120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 10, 2022
Merged
2 changes: 2 additions & 0 deletions .std_test_pkg_exclusions
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
embed/internal/embedtest
encoding/xml
go/build
go/internal/srcimporter
go/types
internal/abi
internal/intern
internal/syscall/windows
internal/syscall/windows/registry
internal/syscall/windows/sysdll
Expand Down
18 changes: 9 additions & 9 deletions compiler/gopherjspkg/fs_vfsdata.go

Large diffs are not rendered by default.

90 changes: 64 additions & 26 deletions compiler/natives/fs_vfsdata.go

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions compiler/natives/src/internal/intern/intern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//go:build js

package intern

var (
eth0 = &Value{cmpVal: "eth0"}
eth1 = &Value{cmpVal: "eth1"}
)

func get(k key) *Value {
// Interning implementation in this package unavoidably relies upon
// runtime.SetFinalizer(), which GopherJS doesn't support (at least until it
// is considered safe to use the WeakMap API). Without working finalizers
// using this package would create memory leaks.
//
// Considering that this package is supposed to serve as an optimization tool,
// it is better to make it explicitly unusable and work around it at the call
// sites.

// net/netip tests use intern API with a few fixed values. It is easier to
// special-case them here than to override the entire test set.
if k.isString && k.s == "eth0" {
return eth0
} else if k.isString && k.s == "eth1" {
return eth1
}

panic("internal/intern is not supported by GopherJS")
}
7 changes: 6 additions & 1 deletion compiler/natives/src/math/math.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ var math = js.Global.Get("Math")
var _zero float64 = 0
var posInf = 1 / _zero
var negInf = -1 / _zero
var nan = 0 / _zero

// Usually, NaN can be obtained in JavaScript with `0 / 0` operation. However,
// in V8, `0 / _zero` yields a bitwise-different value of NaN compared to the
// default NaN or `0 / 0`. Unfortunately, Go compiler forbids division by zero,
// so we have to get this value from prelude.
var nan = js.Global.Get("$NaN").Float()

func Acos(x float64) float64 {
return math.Call("acos", x).Float()
Expand Down
2 changes: 1 addition & 1 deletion compiler/natives/src/net/http/http_wasm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ func init() {
// TODO(nevkontakte): We could test our real implementations if we mock out
// browser APIs and redirect them to the fake networking stack, but this is
// not easy.
useFakeNetwork = true
jsFetchMissing = true
DefaultTransport = &Transport{}
}
20 changes: 20 additions & 0 deletions compiler/natives/src/net/netip/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//go:build js

package netip

import (
"fmt"
"internal/intern"
)

//gopherjs:prune-original
func MkAddr(u Uint128, z any) Addr {
switch z := z.(type) {
case *intern.Value:
return Addr{u, z.Get().(string)}
case string:
return Addr{u, z}
default:
panic(fmt.Errorf("unexpected type %T of the z argument"))
}
}
39 changes: 39 additions & 0 deletions compiler/natives/src/net/netip/netip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//go:build js

package netip

type Addr struct {
addr uint128
// Unlike the upstream, we store the string directly instead of trying to
// use internal/intern package for optimization.
z string
}

var (
// Sentinel values for different zones. \x00 character makes it unlikely for
// the sentinel value to clash with any real-life IPv6 zone index.
z0 = ""
z4 = "\x00ipv4"
z6noz = "\x00ipv6noz"
)

//gopherjs:prune-original
func (ip Addr) Zone() string {
if ip.z == z4 || ip.z == z6noz {
return ""
}
return ip.z
}

//gopherjs:prune-original
func (ip Addr) WithZone(zone string) Addr {
if !ip.Is6() {
return ip
}
if zone == "" {
ip.z = z6noz
return ip
}
ip.z = zone
return ip
}
19 changes: 19 additions & 0 deletions compiler/natives/src/reflect/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,25 @@ func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) {
js.InternalObject(m).Delete(k)
}

// TODO(nevkonatkte): The following three "faststr" implementations are meant to
// perform better for the common case of string-keyed maps (see upstream:
// https://github.com/golang/go/commit/23832ba2e2fb396cda1dacf3e8afcb38ec36dcba)
// However, the stubs below will perform the same or worse because of the extra
// string-to-pointer conversion. Not sure how to fix this without significant
// code duplication, however.

func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) {
return mapaccess(t, m, unsafe.Pointer(&key))
}

func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) {
mapassign(t, m, unsafe.Pointer(&key), val)
}

func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) {
mapdelete(t, m, unsafe.Pointer(&key))
}

type hiter struct {
t Type
m *js.Object // Underlying map object.
Expand Down
7 changes: 7 additions & 0 deletions compiler/natives/src/strings/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,10 @@ func (b *Builder) copyCheck() {
panic("strings: illegal use of non-zero Builder copied by value")
}
}

func Clone(s string) string {
// Since in the JavaScript runtime we don't have access the the string's
// baking memory, we let the engine's garbage collector deal with substring
// memory overheads and simply return the string as-is.
return s
}
4 changes: 4 additions & 0 deletions compiler/natives/src/strings/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ func TestBuilderGrow(t *testing.T) {
func TestCompareStrings(t *testing.T) {
t.Skip("unsafeString not supported in GopherJS")
}

func TestClone(t *testing.T) {
t.Skip("conversion to reflect.StringHeader is not supported in GopherJS")
}
5 changes: 3 additions & 2 deletions compiler/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor
return
}
// Some other unexpected panic, catch the stack trace and return as an error.
err = bailout(e)
err = bailout(fmt.Errorf("unexpected compiler panic while building package %q: %v", importPath, e))
}()

// Files must be in the same order to get reproducible JS
Expand Down Expand Up @@ -645,7 +645,8 @@ func (fc *funcContext) initArgs(ty types.Type) string {
}
return fmt.Sprintf(`"%s", [%s]`, pkgPath, strings.Join(fields, ", "))
default:
panic("invalid type")
err := bailout(fmt.Errorf("%v has unexpected type %T", ty, ty))
panic(err)
}
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/prelude/prelude.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const Prelude = prelude + numeric + types + goroutines + jsmapping

const prelude = `Error.stackTraceLimit = Infinity;

var $NaN = NaN;
var $global, $module;
if (typeof window !== "undefined") { /* web page */
$global = window;
Expand Down Expand Up @@ -201,7 +202,7 @@ var $sliceToGoArray = function(slice, arrayPtrType) {
return arrayPtrType.nil; // Nil slice converts to nil array pointer.
}
if (slice.$array.constructor !== Array) {
return slice.$array.subarray(slice.$offset, slice.$offset + slice.$length);
return slice.$array.subarray(slice.$offset, slice.$offset + arrayType.len);
}
if (slice.$offset == 0 && slice.$length == slice.$capacity && slice.$length == arrayType.len) {
return slice.$array;
Expand Down
2 changes: 1 addition & 1 deletion compiler/prelude/prelude_min.go

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion tests/slice_to_array_ptr_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package tests

import "testing"
import (
"runtime"
"testing"
)

// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
func TestSliceToArrayPointerConversion(t *testing.T) {
Expand Down Expand Up @@ -75,6 +78,25 @@ func TestSliceToArrayPointerConversion(t *testing.T) {
t.Error("u0 should not be nil")
}
})

t.Run("SliceToShorterArray", func(t *testing.T) {
s[0] = 'x'
s[1] = 'y'
s4 := (*[1]byte)(s[:])
if got := s4[0]; got != 'x' {
t.Errorf("Got s0[0] = %q, want 'x'", got)
}
if got := len(s4); got != 1 {
t.Errorf("Got len(s0) = %d, want 1.", got)
}

// Verify that the backing array size has been reduced to match the Go
// type. If not, a "source too large" runtime exception will be thrown
// upon the copy attempt.
s5 := [1]byte{}
s5 = *s4
runtime.KeepAlive(s5)
})
})

t.Run("String", func(t *testing.T) {
Expand Down