github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/reflect/value_test.go (about)

     1  package reflect_test
     2  
     3  import (
     4  	"encoding/base64"
     5  	. "reflect"
     6  	"sort"
     7  	"strings"
     8  	"testing"
     9  )
    10  
    11  func TestTinyIndirectPointers(t *testing.T) {
    12  	var m = map[string]int{}
    13  	m["x"] = 1
    14  
    15  	var a = &m
    16  
    17  	if ValueOf(a).Elem().Len() != 1 {
    18  		t.Errorf("bad map length via reflect")
    19  	}
    20  
    21  	var b struct {
    22  		Decoded *[3]byte
    23  	}
    24  
    25  	v1 := New(TypeOf(b.Decoded).Elem())
    26  
    27  	var bb [3]byte
    28  	bb[0] = 0xaa
    29  
    30  	v1.Elem().Set(ValueOf(bb))
    31  
    32  	if v1.Elem().Index(0).Uint() != 0xaa {
    33  		t.Errorf("bad indirect array index via reflect")
    34  	}
    35  }
    36  
    37  func TestTinyMap(t *testing.T) {
    38  
    39  	m := make(map[string]int)
    40  
    41  	mtyp := TypeOf(m)
    42  
    43  	if got, want := mtyp.Key().Kind().String(), "string"; got != want {
    44  		t.Errorf("m.Type().Key().String()=%q, want %q", got, want)
    45  	}
    46  
    47  	if got, want := mtyp.Elem().Kind().String(), "int"; got != want {
    48  		t.Errorf("m.Elem().String()=%q, want %q", got, want)
    49  	}
    50  
    51  	m["foo"] = 2
    52  
    53  	mref := ValueOf(m)
    54  	two := mref.MapIndex(ValueOf("foo"))
    55  
    56  	if got, want := two.Interface().(int), 2; got != want {
    57  		t.Errorf("MapIndex(`foo`)=%v, want %v", got, want)
    58  	}
    59  
    60  	m["bar"] = 3
    61  	m["baz"] = 4
    62  	m["qux"] = 5
    63  
    64  	it := mref.MapRange()
    65  
    66  	var gotKeys []string
    67  	for it.Next() {
    68  		k := it.Key()
    69  		v := it.Value()
    70  
    71  		kstr := k.Interface().(string)
    72  		vint := v.Interface().(int)
    73  
    74  		gotKeys = append(gotKeys, kstr)
    75  
    76  		if m[kstr] != vint {
    77  			t.Errorf("m[%v]=%v, want %v", kstr, vint, m[kstr])
    78  		}
    79  	}
    80  	var wantKeys []string
    81  	for k := range m {
    82  		wantKeys = append(wantKeys, k)
    83  	}
    84  	sort.Strings(gotKeys)
    85  	sort.Strings(wantKeys)
    86  
    87  	if !equal(gotKeys, wantKeys) {
    88  		t.Errorf("MapRange return unexpected keys: got %v, want %v", gotKeys, wantKeys)
    89  	}
    90  
    91  	refMapKeys := mref.MapKeys()
    92  	gotKeys = gotKeys[:0]
    93  	for _, v := range refMapKeys {
    94  		gotKeys = append(gotKeys, v.Interface().(string))
    95  	}
    96  
    97  	sort.Strings(gotKeys)
    98  	if !equal(gotKeys, wantKeys) {
    99  		t.Errorf("MapKeys return unexpected keys: got %v, want %v", gotKeys, wantKeys)
   100  	}
   101  
   102  	mref.SetMapIndex(ValueOf("bar"), Value{})
   103  	if _, ok := m["bar"]; ok {
   104  		t.Errorf("SetMapIndex failed to delete `bar`")
   105  	}
   106  
   107  	mref.SetMapIndex(ValueOf("baz"), ValueOf(6))
   108  	if got, want := m["baz"], 6; got != want {
   109  		t.Errorf("SetMapIndex(bar, 6) got %v, want %v", got, want)
   110  	}
   111  
   112  	m2ref := MakeMap(mref.Type())
   113  	m2ref.SetMapIndex(ValueOf("foo"), ValueOf(2))
   114  
   115  	m2 := m2ref.Interface().(map[string]int)
   116  
   117  	if m2["foo"] != 2 {
   118  		t.Errorf("MakeMap failed to create map")
   119  	}
   120  
   121  	type stringint struct {
   122  		s string
   123  		i int
   124  	}
   125  
   126  	simap := make(map[stringint]int)
   127  
   128  	refsimap := MakeMap(TypeOf(simap))
   129  
   130  	refsimap.SetMapIndex(ValueOf(stringint{"hello", 4}), ValueOf(6))
   131  
   132  	six := refsimap.MapIndex(ValueOf(stringint{"hello", 4}))
   133  
   134  	if six.Interface().(int) != 6 {
   135  		t.Errorf("m[hello, 4]=%v, want 6", six)
   136  	}
   137  
   138  	keys := refsimap.MapKeys()
   139  	if len(keys) != 1 {
   140  		t.Errorf("refsimap: MapKeys()=%v, want 1", len(keys))
   141  	}
   142  	if keys[0].Type() != TypeOf(stringint{}) {
   143  		t.Errorf("keys[0] has wrong type: %v", keys[0].Type().String())
   144  	}
   145  
   146  	sikey := keys[0].Interface().(stringint)
   147  
   148  	if sikey != (stringint{"hello", 4}) {
   149  		t.Errorf("sikey has unexpected value: %#v", sikey)
   150  	}
   151  
   152  	// make sure we can look up interface keys with reflection
   153  	type unmarshalerText struct {
   154  		A, B string
   155  	}
   156  
   157  	ut := make(map[unmarshalerText]bool)
   158  
   159  	refut := ValueOf(ut)
   160  
   161  	// put in a key with the compiler
   162  	ut[unmarshalerText{"x", "y"}] = true
   163  
   164  	// make sure we can get it out
   165  	v2 := refut.MapIndex(ValueOf(unmarshalerText{"x", "y"}))
   166  	if !v2.IsValid() || !v2.Bool() {
   167  		t.Errorf("Failed to look up map struct key with refection")
   168  	}
   169  
   170  	// put in a key with reflection
   171  	refut.SetMapIndex(ValueOf(unmarshalerText{"y", "z"}), ValueOf(true))
   172  
   173  	// make sure we can get it out with the compiler
   174  	if !ut[unmarshalerText{"y", "z"}] {
   175  		t.Errorf("Failed to look up up reflect-set map key with compiler")
   176  	}
   177  
   178  	utKeys := refut.MapKeys()
   179  
   180  	// make sure keys extracted via reflection have the correct type
   181  	if _, ok := utKeys[0].Interface().(unmarshalerText); !ok {
   182  		t.Errorf("Map keys via MapKeys() have wrong type: %v", utKeys[0].Type().String())
   183  	}
   184  
   185  	// and via iteration
   186  
   187  	utIter := refut.MapRange()
   188  	utIter.Next()
   189  	utIterKey := utIter.Key()
   190  
   191  	if _, ok := utIterKey.Interface().(unmarshalerText); !ok {
   192  		t.Errorf("Map keys via MapIter() have wrong type: %v", utIterKey.Type().String())
   193  	}
   194  
   195  	{
   196  		m := map[any]any{1: 2}
   197  		rv := ValueOf(m)
   198  		iter := rv.MapRange()
   199  
   200  		iter.Next()
   201  		k := iter.Key()
   202  		if k.Kind().String() != "interface" {
   203  			t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String())
   204  		}
   205  
   206  		keys := rv.MapKeys()
   207  		if k := keys[0]; k.Kind().String() != "interface" {
   208  			t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String())
   209  		}
   210  
   211  	}
   212  }
   213  
   214  // For an interface type, it returns the number of exported and unexported methods.
   215  
   216  type counter interface {
   217  	count() int
   218  }
   219  
   220  type count struct {
   221  	i int
   222  }
   223  
   224  func (c *count) count() int {
   225  	return c.i
   226  }
   227  
   228  func TestMapInterfaceKeys(t *testing.T) {
   229  	m := make(map[interface{}]int)
   230  	for i := 0; i < 20; i++ {
   231  		c := &count{i}
   232  		m[c] = i
   233  	}
   234  
   235  	for k, v := range m {
   236  		if got := m[k]; got != v {
   237  			t.Error("lookup failure got", got, "want", v)
   238  		}
   239  	}
   240  
   241  	refm := ValueOf(m)
   242  
   243  	keys := refm.MapKeys()
   244  	if len(keys) != 20 {
   245  		t.Error("failed to look up 20 keys")
   246  	}
   247  	for _, k := range keys {
   248  		c := k.Interface().(*count)
   249  		e := refm.MapIndex(k)
   250  		if e.Interface().(int) != c.i {
   251  			t.Error("reflect lookup failure:", c.i)
   252  		}
   253  	}
   254  
   255  	it := refm.MapRange()
   256  	var totalKeys int
   257  	for it.Next() {
   258  		totalKeys++
   259  		k := it.Key()
   260  		v := it.Value().Interface().(int)
   261  
   262  		c := k.Interface().(*count)
   263  		if v != c.i || refm.MapIndex(k).Interface().(int) != c.i {
   264  			t.Error("reflect iter lookup failure:", c.i)
   265  		}
   266  	}
   267  
   268  	if totalKeys != 20 {
   269  		t.Errorf("failed to get 20 keys")
   270  	}
   271  }
   272  
   273  func TestMapInterfaceElem(t *testing.T) {
   274  	m := make(map[string]interface{})
   275  	refm := ValueOf(m)
   276  
   277  	four := ValueOf(4)
   278  	hello := ValueOf("hello")
   279  
   280  	refm.SetMapIndex(hello, four)
   281  
   282  	if v := refm.MapIndex(hello).Interface().(int); v != 4 {
   283  		t.Errorf("failed to get value assigned to interface via MapIndex")
   284  	}
   285  
   286  	if v := m["hello"].(int); v != 4 {
   287  		t.Errorf("failed to get value assigned to interface via direct lookup")
   288  	}
   289  }
   290  
   291  func TestTinySlice(t *testing.T) {
   292  	s := []int{0, 10, 20}
   293  	refs := ValueOf(s)
   294  
   295  	for i := 3; i < 10; i++ {
   296  		refs = Append(refs, ValueOf(i*10))
   297  	}
   298  
   299  	s = refs.Interface().([]int)
   300  
   301  	for i := 0; i < 10; i++ {
   302  		if s[i] != i*10 {
   303  			t.Errorf("s[%d]=%d, want %d", i, s[i], i*10)
   304  		}
   305  	}
   306  
   307  	s28 := s[2:8]
   308  	s28ref := refs.Slice(2, 8)
   309  
   310  	if len(s28) != s28ref.Len() || cap(s28) != s28ref.Cap() {
   311  		t.Errorf("Slice: len(s28)=%d s28ref.Len()=%d cap(s28)=%d s28ref.Cap()=%d\n", len(s28), s28ref.Len(), cap(s28), s28ref.Cap())
   312  	}
   313  
   314  	for i, got := range s28 {
   315  		want := int(s28ref.Index(i).Int())
   316  		if got != want {
   317  			t.Errorf("s28[%d]=%d, want %d", i, got, want)
   318  		}
   319  	}
   320  
   321  	s268 := s[2:6:8]
   322  	s268ref := refs.Slice3(2, 6, 8)
   323  
   324  	if len(s268) != s268ref.Len() || cap(s268) != s268ref.Cap() {
   325  		t.Errorf("Slice3: len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s268), s268ref.Len(), cap(s268), s268ref.Cap())
   326  	}
   327  
   328  	for i, got := range s268 {
   329  		want := int(s268ref.Index(i).Int())
   330  		if got != want {
   331  			t.Errorf("s268[%d]=%d, want %d", i, got, want)
   332  		}
   333  	}
   334  
   335  	// should be equivalent to s28 now, except for the capacity which doesn't change
   336  
   337  	s268ref = ValueOf(&s268).Elem()
   338  	s268ref.SetLen(6)
   339  	if len(s28) != s268ref.Len() || cap(s268) != s268ref.Cap() {
   340  		t.Errorf("SetLen: len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s28), s268ref.Len(), cap(s268), s268ref.Cap())
   341  	}
   342  
   343  	for i, got := range s28 {
   344  		want := int(s268ref.Index(i).Int())
   345  		if got != want {
   346  			t.Errorf("s28[%d]=%d, want %d", i, got, want)
   347  		}
   348  	}
   349  
   350  	refs = MakeSlice(TypeOf(s), 5, 10)
   351  	s = refs.Interface().([]int)
   352  
   353  	if len(s) != refs.Len() || cap(s) != refs.Cap() {
   354  		t.Errorf("len(s)=%v refs.Len()=%v cap(s)=%v refs.Cap()=%v", len(s), refs.Len(), cap(s), refs.Cap())
   355  	}
   356  }
   357  
   358  func TestTinyBytes(t *testing.T) {
   359  	s := []byte("abcde")
   360  	refs := ValueOf(s)
   361  
   362  	s2 := refs.Bytes()
   363  
   364  	if !equal(s, s2) {
   365  		t.Errorf("Failed to get Bytes(): %v != %v", s, s2)
   366  	}
   367  
   368  	Copy(refs, ValueOf("12345"))
   369  
   370  	if string(s) != "12345" {
   371  		t.Errorf("Copy()=%q, want `12345`", string(s))
   372  	}
   373  
   374  	// test small arrays that fit in a pointer
   375  	a := [3]byte{10, 20, 30}
   376  	v := ValueOf(&a)
   377  	vslice := v.Elem().Bytes()
   378  	if len(vslice) != 3 || cap(vslice) != 3 {
   379  		t.Errorf("len(vslice)=%v, cap(vslice)=%v", len(vslice), cap(vslice))
   380  	}
   381  
   382  	for i, got := range vslice {
   383  		if want := (byte(i) + 1) * 10; got != want {
   384  			t.Errorf("vslice[%d]=%d, want %d", i, got, want)
   385  		}
   386  	}
   387  }
   388  
   389  func TestTinyNamedTypes(t *testing.T) {
   390  	type namedString string
   391  
   392  	named := namedString("foo")
   393  	if got, want := TypeOf(named).Name(), "namedString"; got != want {
   394  		t.Errorf("TypeOf.Name()=%v, want %v", got, want)
   395  	}
   396  
   397  	if got, want := TypeOf(named).String(), "reflect_test.namedString"; got != want {
   398  		t.Errorf("TypeOf.String()=%v, want %v", got, want)
   399  	}
   400  
   401  	errorType := TypeOf((*error)(nil)).Elem()
   402  	if s := errorType.String(); s != "error" {
   403  		t.Errorf("error type = %v, want error", s)
   404  	}
   405  
   406  	m := make(map[[4]uint16]string)
   407  
   408  	if got, want := TypeOf(m).String(), "map[[4]uint16]string"; got != want {
   409  		t.Errorf("Type.String()=%v, want %v", got, want)
   410  	}
   411  
   412  	s := struct {
   413  		a int8
   414  		b int8
   415  		c int8
   416  		d int8
   417  		e int8
   418  		f int32
   419  	}{}
   420  
   421  	if got, want := TypeOf(s).String(), "struct { a int8; b int8; c int8; d int8; e int8; f int32 }"; got != want {
   422  		t.Errorf("Type.String()=%v, want %v", got, want)
   423  	}
   424  
   425  	if got, want := ValueOf(m).String(), "<map[[4]uint16]string Value>"; got != want {
   426  		t.Errorf("Value.String()=%v, want %v", got, want)
   427  	}
   428  
   429  	if got, want := TypeOf(base64.Encoding{}).String(), "base64.Encoding"; got != want {
   430  		t.Errorf("Type.String(base64.Encoding{})=%v, want %v", got, want)
   431  	}
   432  
   433  	type Repository struct {
   434  		RoleName *string `json:"role_name,omitempty"`
   435  	}
   436  
   437  	var repo *Repository
   438  	v := ValueOf(&repo).Elem()
   439  	n := New(v.Type().Elem())
   440  	v.Set(n)
   441  }
   442  
   443  func TestTinyStruct(t *testing.T) {
   444  	type barStruct struct {
   445  		QuxString string
   446  		BazInt    int
   447  	}
   448  
   449  	type foobar struct {
   450  		Foo string `foo:"struct tag"`
   451  		Bar barStruct
   452  	}
   453  
   454  	var fb foobar
   455  	fb.Bar.QuxString = "qux"
   456  
   457  	reffb := TypeOf(fb)
   458  
   459  	q := reffb.FieldByIndex([]int{1, 0})
   460  	if want := "QuxString"; q.Name != want {
   461  		t.Errorf("FieldByIndex=%v, want %v", q.Name, want)
   462  	}
   463  
   464  	var ok bool
   465  	q, ok = reffb.FieldByName("Foo")
   466  	if q.Name != "Foo" || !ok {
   467  		t.Errorf("FieldByName(Foo)=%v,%v, want Foo, true", q.Name, ok)
   468  	}
   469  
   470  	if got, want := q.Tag, `foo:"struct tag"`; string(got) != want {
   471  		t.Errorf("StrucTag for Foo=%v, want %v", got, want)
   472  	}
   473  
   474  	q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "bar" })
   475  	if q.Name != "Bar" || !ok {
   476  		t.Errorf("FieldByNameFunc(bar)=%v,%v, want Bar, true", q.Name, ok)
   477  	}
   478  
   479  	q, ok = reffb.FieldByName("Snorble")
   480  	if q.Name != "" || ok {
   481  		t.Errorf("FieldByName(Snorble)=%v,%v, want ``, false", q.Name, ok)
   482  	}
   483  
   484  	q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "snorble" })
   485  	if q.Name != "" || ok {
   486  		t.Errorf("FieldByName(snorble)=%v,%v, want ``, false", q.Name, ok)
   487  	}
   488  }
   489  
   490  func TestTinyZero(t *testing.T) {
   491  	s := "hello, world"
   492  	var sptr *string = &s
   493  	v := ValueOf(&sptr).Elem()
   494  	v.Set(Zero(v.Type()))
   495  
   496  	sptr = v.Interface().(*string)
   497  
   498  	if sptr != nil {
   499  		t.Errorf("failed to set a nil string pointer")
   500  	}
   501  
   502  	sl := []int{1, 2, 3}
   503  	v = ValueOf(&sl).Elem()
   504  	v.Set(Zero(v.Type()))
   505  	sl = v.Interface().([]int)
   506  
   507  	if sl != nil {
   508  		t.Errorf("failed to set a nil slice")
   509  	}
   510  }
   511  
   512  func addrDecode(body interface{}) {
   513  	vbody := ValueOf(body)
   514  	ptr := vbody.Elem()
   515  	pptr := ptr.Addr()
   516  	addrSetInt(pptr.Interface())
   517  }
   518  
   519  func addrSetInt(intf interface{}) {
   520  	ptr := intf.(*uint64)
   521  	*ptr = 112358
   522  }
   523  
   524  func TestTinyAddr(t *testing.T) {
   525  	var n uint64
   526  	addrDecode(&n)
   527  	if n != 112358 {
   528  		t.Errorf("Failed to set t=112358, got %v", n)
   529  	}
   530  
   531  	v := ValueOf(&n)
   532  	if got, want := v.Elem().Addr().CanAddr(), false; got != want {
   533  		t.Errorf("Elem.Addr.CanAddr=%v, want %v", got, want)
   534  	}
   535  }
   536  
   537  func TestTinyNilType(t *testing.T) {
   538  	var a any = nil
   539  	typ := TypeOf(a)
   540  	if typ != nil {
   541  		t.Errorf("Type of any{nil} is not nil")
   542  	}
   543  }
   544  
   545  func TestTinySetBytes(t *testing.T) {
   546  	var b []byte
   547  	refb := ValueOf(&b).Elem()
   548  	s := []byte("hello")
   549  	refb.SetBytes(s)
   550  	s[0] = 'b'
   551  
   552  	refbSlice := refb.Interface().([]byte)
   553  
   554  	if len(refbSlice) != len(s) || b[0] != s[0] || refbSlice[0] != s[0] {
   555  		t.Errorf("SetBytes(): reflection slice mismatch")
   556  	}
   557  }
   558  
   559  type methodStruct struct {
   560  	i int
   561  }
   562  
   563  func (m methodStruct) ValueMethod1() int {
   564  	return m.i
   565  }
   566  
   567  func (m methodStruct) valueMethod2() int {
   568  	return m.i
   569  }
   570  
   571  func (m *methodStruct) PointerMethod1() int {
   572  	return m.i
   573  }
   574  
   575  func (m *methodStruct) PointerMethod2() int {
   576  	return m.i
   577  }
   578  
   579  func (m *methodStruct) pointerMethod3() int {
   580  	return m.i
   581  }
   582  
   583  func TestTinyNumMethods(t *testing.T) {
   584  	refptrt := TypeOf(&methodStruct{})
   585  	if got, want := refptrt.NumMethod(), 1+2; got != want {
   586  		t.Errorf("Pointer Methods=%v, want %v", got, want)
   587  	}
   588  
   589  	reft := refptrt.Elem()
   590  	if got, want := reft.NumMethod(), 1; got != want {
   591  		t.Errorf("Value Methods=%v, want %v", got, want)
   592  	}
   593  }
   594  
   595  func TestAssignableTo(t *testing.T) {
   596  	var a any
   597  	refa := ValueOf(&a).Elem()
   598  	refa.Set(ValueOf(4))
   599  	if got, want := refa.Interface().(int), 4; got != want {
   600  		t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
   601  	}
   602  }
   603  
   604  func TestConvert(t *testing.T) {
   605  	v := ValueOf(int64(3))
   606  	c := v.Convert(TypeOf(byte(0)))
   607  
   608  	if c.Type().Kind() != Uint8 || c.Uint() != 3 {
   609  		t.Errorf("Convert(uint64 -> byte failed: kind=%v, value=%d", c.Type().Kind().String(), c.Uint())
   610  	}
   611  
   612  	v = ValueOf("hello")
   613  	c = v.Convert(TypeOf([]byte("")))
   614  
   615  	if c.Type().Kind() != Slice || c.Type().Elem().Kind() != Uint8 && c.Len() != 5 && string(c.Bytes()) != "hello" {
   616  		t.Errorf("Convert(string -> []byte")
   617  	}
   618  
   619  	type namedString string
   620  
   621  	c = v.Convert(TypeOf(namedString("")))
   622  	if c.Type().Kind() != String || c.Type().Name() != "namedString" {
   623  		t.Errorf("Convert(string -> namedString")
   624  	}
   625  }
   626  
   627  func TestIssue4040(t *testing.T) {
   628  	var value interface{} = uint16(0)
   629  
   630  	// get the pointer to the interface value
   631  	inPtr := ValueOf(&value)
   632  
   633  	// dereference to get the actual value (an interface)
   634  	inElem := inPtr.Elem()
   635  
   636  	// create a new value of the same concrete type
   637  	uint16Type := TypeOf(uint16(0))
   638  	newUint16Value := New(uint16Type).Elem()
   639  	newUint16Value.Set(ValueOf(uint16(13)))
   640  
   641  	// set the new value to the interface
   642  	inElem.Set(newUint16Value)
   643  
   644  	if value.(uint16) != 13 {
   645  		t.Errorf("Failed to set interface value from uint16")
   646  	}
   647  }
   648  
   649  func equal[T comparable](a, b []T) bool {
   650  	if len(a) != len(b) {
   651  		return false
   652  	}
   653  
   654  	for i, aa := range a {
   655  		if b[i] != aa {
   656  			return false
   657  		}
   658  	}
   659  	return true
   660  }