2018-12-17 20:52:26 -08:00
|
|
|
package gotypes
|
|
|
|
|
|
|
|
|
|
import (
|
2022-03-27 15:31:26 -07:00
|
|
|
"errors"
|
2019-04-13 18:50:51 -04:00
|
|
|
"go/token"
|
2018-12-17 20:52:26 -08:00
|
|
|
"go/types"
|
2019-01-27 19:22:21 -08:00
|
|
|
"strings"
|
2018-12-17 20:52:26 -08:00
|
|
|
"testing"
|
2019-01-27 19:22:21 -08:00
|
|
|
|
2026-03-06 20:14:02 +00:00
|
|
|
"sources.truenas.cloud/code/avo/operand"
|
|
|
|
|
"sources.truenas.cloud/code/avo/reg"
|
2018-12-17 20:52:26 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestBasicKindsArePrimitive(t *testing.T) {
|
|
|
|
|
kinds := []types.BasicKind{
|
|
|
|
|
types.Bool,
|
|
|
|
|
types.Int,
|
|
|
|
|
types.Int8,
|
|
|
|
|
types.Int16,
|
|
|
|
|
types.Int32,
|
|
|
|
|
types.Int64,
|
|
|
|
|
types.Uint,
|
|
|
|
|
types.Uint8,
|
|
|
|
|
types.Uint16,
|
|
|
|
|
types.Uint32,
|
|
|
|
|
types.Uint64,
|
|
|
|
|
types.Uintptr,
|
|
|
|
|
types.Float32,
|
|
|
|
|
types.Float64,
|
|
|
|
|
}
|
|
|
|
|
for _, k := range kinds {
|
|
|
|
|
AssertPrimitive(t, types.Typ[k])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPointersArePrimitive(t *testing.T) {
|
|
|
|
|
typ := types.NewPointer(types.Typ[types.Uint32])
|
|
|
|
|
AssertPrimitive(t, typ)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AssertPrimitive(t *testing.T, typ types.Type) {
|
2022-03-27 15:31:26 -07:00
|
|
|
t.Helper()
|
2019-01-27 19:22:21 -08:00
|
|
|
c := NewComponent(typ, operand.NewParamAddr("primitive", 0))
|
2018-12-17 20:52:26 -08:00
|
|
|
if _, err := c.Resolve(); err != nil {
|
|
|
|
|
t.Errorf("expected type %s to be primitive: got error '%s'", typ, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-01-27 19:22:21 -08:00
|
|
|
|
|
|
|
|
func TestComponentErrors(t *testing.T) {
|
|
|
|
|
comp := NewComponent(types.Typ[types.Uint32], operand.Mem{})
|
|
|
|
|
cases := []struct {
|
|
|
|
|
Component Component
|
|
|
|
|
ErrorSubstring string
|
|
|
|
|
}{
|
|
|
|
|
{comp.Base(), "only slices and strings"},
|
|
|
|
|
{comp.Len(), "only slices and strings"},
|
|
|
|
|
{comp.Cap(), "only slices have"},
|
|
|
|
|
{comp.Real(), "only complex"},
|
|
|
|
|
{comp.Imag(), "only complex"},
|
|
|
|
|
{comp.Index(42), "not array type"},
|
|
|
|
|
{comp.Field("a"), "not struct type"},
|
|
|
|
|
{comp.Dereference(reg.R12), "not pointer type"},
|
|
|
|
|
}
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
_, err := c.Component.Resolve()
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error")
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(err.Error(), c.ErrorSubstring) {
|
|
|
|
|
t.Fatalf("error message %q; expected substring %q", err.Error(), c.ErrorSubstring)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestComponentErrorChaining(t *testing.T) {
|
|
|
|
|
// Build a component with an error.
|
|
|
|
|
comp := NewComponent(types.Typ[types.Uint32], operand.Mem{}).Index(3)
|
|
|
|
|
_, expect := comp.Resolve()
|
|
|
|
|
if expect == nil {
|
|
|
|
|
t.Fatal("expected error")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Confirm that the error is preserved through chaining operations.
|
|
|
|
|
cases := []Component{
|
|
|
|
|
comp.Dereference(reg.R13),
|
|
|
|
|
comp.Base(),
|
|
|
|
|
comp.Len(),
|
|
|
|
|
comp.Cap(),
|
|
|
|
|
comp.Real(),
|
|
|
|
|
comp.Imag(),
|
|
|
|
|
comp.Index(42),
|
|
|
|
|
comp.Field("field"),
|
|
|
|
|
}
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
_, err := c.Resolve()
|
2022-03-27 15:31:26 -07:00
|
|
|
if !errors.Is(err, expect) {
|
2019-01-27 19:22:21 -08:00
|
|
|
t.Fatal("chaining should preserve error")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-13 18:50:51 -04:00
|
|
|
|
|
|
|
|
func TestComponentDeconstruction(t *testing.T) {
|
|
|
|
|
cases := []struct {
|
|
|
|
|
Name string
|
|
|
|
|
Type types.Type
|
|
|
|
|
Chain func(Component) Component
|
|
|
|
|
Param string
|
|
|
|
|
Offset int
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
Name: "slice_base",
|
|
|
|
|
Type: types.NewSlice(types.Typ[types.Uint64]),
|
|
|
|
|
Chain: func(c Component) Component { return c.Base() },
|
|
|
|
|
Param: "base",
|
|
|
|
|
Offset: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "slice_len",
|
|
|
|
|
Type: types.NewSlice(types.Typ[types.Uint64]),
|
|
|
|
|
Chain: func(c Component) Component { return c.Len() },
|
|
|
|
|
Param: "len",
|
|
|
|
|
Offset: 8,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "slice_cap",
|
|
|
|
|
Type: types.NewSlice(types.Typ[types.Uint64]),
|
|
|
|
|
Chain: func(c Component) Component { return c.Cap() },
|
|
|
|
|
Param: "cap",
|
|
|
|
|
Offset: 16,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "string_base",
|
|
|
|
|
Type: types.Typ[types.String],
|
|
|
|
|
Chain: func(c Component) Component { return c.Base() },
|
|
|
|
|
Param: "base",
|
|
|
|
|
Offset: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "slice_len",
|
|
|
|
|
Type: types.Typ[types.String],
|
|
|
|
|
Chain: func(c Component) Component { return c.Len() },
|
|
|
|
|
Param: "len",
|
|
|
|
|
Offset: 8,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "complex64_real",
|
|
|
|
|
Type: types.Typ[types.Complex64],
|
|
|
|
|
Chain: func(c Component) Component { return c.Real() },
|
|
|
|
|
Param: "real",
|
|
|
|
|
Offset: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "complex64_imag",
|
|
|
|
|
Type: types.Typ[types.Complex64],
|
|
|
|
|
Chain: func(c Component) Component { return c.Imag() },
|
|
|
|
|
Param: "imag",
|
|
|
|
|
Offset: 4,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "complex128_real",
|
|
|
|
|
Type: types.Typ[types.Complex128],
|
|
|
|
|
Chain: func(c Component) Component { return c.Real() },
|
|
|
|
|
Param: "real",
|
|
|
|
|
Offset: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "complex128_imag",
|
|
|
|
|
Type: types.Typ[types.Complex128],
|
|
|
|
|
Chain: func(c Component) Component { return c.Imag() },
|
|
|
|
|
Param: "imag",
|
|
|
|
|
Offset: 8,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "array",
|
|
|
|
|
Type: types.NewArray(types.Typ[types.Uint32], 7),
|
|
|
|
|
Chain: func(c Component) Component { return c.Index(3) },
|
|
|
|
|
Param: "3",
|
|
|
|
|
Offset: 12,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "struct",
|
|
|
|
|
Type: types.NewStruct([]*types.Var{
|
|
|
|
|
types.NewField(token.NoPos, nil, "Byte", types.Typ[types.Byte], false),
|
|
|
|
|
types.NewField(token.NoPos, nil, "Uint64", types.Typ[types.Uint64], false),
|
|
|
|
|
}, nil),
|
|
|
|
|
Chain: func(c Component) Component { return c.Field("Uint64") },
|
|
|
|
|
Param: "Uint64",
|
|
|
|
|
Offset: 8,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For every test case, generate the same case but when the type is wrapped in
|
|
|
|
|
// a named type.
|
|
|
|
|
n := len(cases)
|
2024-12-22 19:01:48 -05:00
|
|
|
for i := range n {
|
2019-04-13 18:50:51 -04:00
|
|
|
wrapped := cases[i]
|
|
|
|
|
wrapped.Name += "_wrapped"
|
|
|
|
|
wrapped.Type = types.NewNamed(
|
|
|
|
|
types.NewTypeName(token.NoPos, nil, "wrapped", nil),
|
|
|
|
|
wrapped.Type,
|
|
|
|
|
nil,
|
|
|
|
|
)
|
|
|
|
|
cases = append(cases, wrapped)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
t.Run(c.Name, func(t *testing.T) {
|
|
|
|
|
t.Log(c.Type)
|
|
|
|
|
base := operand.NewParamAddr("test", 0)
|
|
|
|
|
comp := NewComponent(c.Type, base)
|
|
|
|
|
comp = c.Chain(comp)
|
|
|
|
|
|
|
|
|
|
b, err := comp.Resolve()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expectname := "test_" + c.Param
|
|
|
|
|
if b.Addr.Symbol.Name != expectname {
|
|
|
|
|
t.Errorf("parameter name %q; expected %q", b.Addr.Symbol.Name, expectname)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if b.Addr.Disp != c.Offset {
|
|
|
|
|
t.Errorf("offset %d; expected %d", b.Addr.Disp, c.Offset)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|