Skip to content

[go1.20] Pull master into go1.20 #1308

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
May 25, 2024
1 change: 0 additions & 1 deletion build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *toke
overrides := make(map[string]overrideInfo)
for _, file := range overlayFiles {
augmentOverlayFile(file, overrides)
pruneImports(file)
}
delete(overrides, "init")

Expand Down
4 changes: 1 addition & 3 deletions compiler/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression {
switch basic.Kind() {
case types.Int32, types.Int:
return fc.formatParenExpr("$imul(%e, %e)", e.X, e.Y)
case types.Uint32, types.Uintptr:
case types.Uint32, types.Uint, types.Uintptr:
return fc.formatParenExpr("$imul(%e, %e) >>> 0", e.X, e.Y)
}
return fc.fixNumber(fc.formatExpr("%e * %e", e.X, e.Y), basic)
Expand Down Expand Up @@ -1131,8 +1131,6 @@ func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type
return fc.fixNumber(fc.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t)
}
return fc.fixNumber(fc.formatExpr("%s.$low", fc.translateExpr(expr)), t)
case isFloat(basicExprType):
return fc.formatParenExpr("%e >> 0", expr)
case types.Identical(exprType, types.Typ[types.UnsafePointer]):
return fc.translateExpr(expr)
default:
Expand Down
6 changes: 6 additions & 0 deletions compiler/prelude/jsmapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ var $internalize = (v, t, recv, seen, makeWrapper) => {
case $kindFloat64:
return parseFloat(v);
case $kindArray:
if (v === null || v === undefined) {
$throwRuntimeError("cannot internalize "+v+" as a "+t.string);
}
if (v.length !== t.len) {
$throwRuntimeError("got array with wrong size from JavaScript native");
}
Expand Down Expand Up @@ -331,6 +334,9 @@ var $internalize = (v, t, recv, seen, makeWrapper) => {
return $internalize(v, t.elem, makeWrapper);
}
case $kindSlice:
if (v == null) {
return t.zero();
}
return new t($mapArray(v, e => { return $internalize(e, t.elem, makeWrapper); }));
case $kindString:
v = String(v);
Expand Down
39 changes: 39 additions & 0 deletions tests/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,45 @@ func TestExternalize(t *testing.T) {
}
}

func TestInternalizeSlice(t *testing.T) {
tests := []struct {
name string
init []int
want string
}{
{
name: `nil slice`,
init: []int(nil),
want: `[]int(nil)`,
},
{
name: `empty slice`,
init: []int{},
want: `[]int{}`,
},
{
name: `non-empty slice`,
init: []int{42, 53, 64},
want: `[]int{42, 53, 64}`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := struct {
*js.Object
V []int `js:"V"` // V is externalized
}{Object: js.Global.Get("Object").New()}
b.V = tt.init

result := fmt.Sprintf(`%#v`, b.V) // internalize b.V
if result != tt.want {
t.Errorf(`Unexpected result %q != %q`, result, tt.want)
}
})
}
}

func TestInternalizeExternalizeNull(t *testing.T) {
type S struct {
*js.Object
Expand Down
108 changes: 108 additions & 0 deletions tests/numeric_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package tests

import (
"fmt"
"math/bits"
"math/rand"
"runtime"
"testing"
"testing/quick"

"github.com/gopherjs/gopherjs/js"
)

// naiveMul64 performs 64-bit multiplication without using the multiplication
Expand Down Expand Up @@ -93,3 +97,107 @@ func BenchmarkMul64(b *testing.B) {
}
})
}

func TestIssue733(t *testing.T) {
if runtime.GOOS != "js" {
t.Skip("test uses GopherJS-specific features")
}

t.Run("sign", func(t *testing.T) {
f := float64(-1)
i := uint32(f)
underlying := js.InternalObject(i).Float() // Get the raw JS number behind i.
if want := float64(4294967295); underlying != want {
t.Errorf("Got: uint32(float64(%v)) = %v. Want: %v.", f, underlying, want)
}
})
t.Run("truncation", func(t *testing.T) {
f := float64(300)
i := uint8(f)
underlying := js.InternalObject(i).Float() // Get the raw JS number behind i.
if want := float64(44); underlying != want {
t.Errorf("Got: uint32(float64(%v)) = %v. Want: %v.", f, underlying, want)
}
})
}

// Test_32BitEnvironment tests that GopherJS behaves correctly
// as a 32-bit environment for integers. To simulate a 32 bit environment
// we have to use `$imul` instead of `*` to get the correct result.
func Test_32BitEnvironment(t *testing.T) {
if bits.UintSize != 32 {
t.Skip(`test is only relevant for 32-bit environment`)
}

tests := []struct {
x, y, exp uint64
}{
{
x: 65535, // x = 2^16 - 1
y: 65535, // same as x
exp: 4294836225, // x² works since it doesn't overflow 32 bits.
},
{
x: 134217729, // x = 2^27 + 1, x < 2^32 and x > sqrt(2^53), so x² overflows 53 bits.
y: 134217729, // same as x
exp: 268435457, // x² mod 2^32 = (2^27 + 1)² mod 2^32 = (2^54 + 2^28 + 1) mod 2^32 = 2^28 + 1
// In pure JS, `x * x >>> 0`, would result in 268,435,456 because it lost the least significant bit
// prior to being truncated, where in a real 32 bit environment, it would be 268,435,457 since
// the rollover removed the most significant bit and doesn't affect the least significant bit.
},
{
x: 4294967295, // x = 2^32 - 1 another case where x² overflows 53 bits causing a loss of precision.
y: 4294967295, // same as x
exp: 1, // x² mod 2^32 = (2^32 - 1)² mod 2^32 = (2^64 - 2^33 + 1) mod 2^32 = 1
// In pure JS, `x * x >>> 0`, would result in 0 because it lost the least significant bits.
},
{
x: 4294967295, // x = 2^32 - 1
y: 3221225473, // y = 2^31 + 2^30 + 1
exp: 1073741823, // 2^32 - 1.
// In pure JS, `x * y >>> 0`, would result in 1,073,741,824.
},
{
x: 4294967295, // x = 2^32 - 1
y: 134217729, // y = 2^27 + 1
exp: 4160749567, // In pure JS, `x * y >>> 0`, would result in 4,160,749,568.
},
}

for i, test := range tests {
t.Run(fmt.Sprintf(`#%d/uint32`, i), func(t *testing.T) {
x, y, exp := uint32(test.x), uint32(test.y), uint32(test.exp)
if got := x * y; got != exp {
t.Errorf("got: %d\nwant: %d.", got, exp)
}
})

t.Run(fmt.Sprintf(`#%d/uintptr`, i), func(t *testing.T) {
x, y, exp := uintptr(test.x), uintptr(test.y), uintptr(test.exp)
if got := x * y; got != exp {
t.Errorf("got: %d\nwant: %d.", got, exp)
}
})

t.Run(fmt.Sprintf(`#%d/uint`, i), func(t *testing.T) {
x, y, exp := uint(test.x), uint(test.y), uint(test.exp)
if got := x * y; got != exp {
t.Errorf("got: %d\nwant: %d.", got, exp)
}
})

t.Run(fmt.Sprintf(`#%d/int32`, i), func(t *testing.T) {
x, y, exp := int32(test.x), int32(test.y), int32(test.exp)
if got := x * y; got != exp {
t.Errorf("got: %d\nwant: %d.", got, exp)
}
})

t.Run(fmt.Sprintf(`#%d/int`, i), func(t *testing.T) {
x, y, exp := int(test.x), int(test.y), int(test.exp)
if got := x * y; got != exp {
t.Errorf("got: %d\nwant: %d.", got, exp)
}
})
}
}
Loading