diff options
Diffstat (limited to 'src/pkg/reflect')
-rw-r--r-- | src/pkg/reflect/all_test.go | 489 | ||||
-rw-r--r-- | src/pkg/reflect/asm_386.s | 14 | ||||
-rw-r--r-- | src/pkg/reflect/asm_amd64.s | 14 | ||||
-rw-r--r-- | src/pkg/reflect/asm_arm.s | 14 | ||||
-rw-r--r-- | src/pkg/reflect/deepequal.go | 2 | ||||
-rw-r--r-- | src/pkg/reflect/makefunc.go | 57 | ||||
-rw-r--r-- | src/pkg/reflect/tostring_test.go | 1 | ||||
-rw-r--r-- | src/pkg/reflect/type.go | 175 | ||||
-rw-r--r-- | src/pkg/reflect/value.go | 244 |
9 files changed, 872 insertions, 138 deletions
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index 6f006db18..56cb315ad 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -13,6 +13,8 @@ import ( "math/rand" "os" . "reflect" + "runtime" + "sort" "sync" "testing" "time" @@ -1457,7 +1459,7 @@ func (p Point) AnotherMethod(scale int) int { // This will be index 1. func (p Point) Dist(scale int) int { - // println("Point.Dist", p.x, p.y, scale) + //println("Point.Dist", p.x, p.y, scale) return p.x*p.x*scale + p.y*p.y*scale } @@ -1473,23 +1475,23 @@ func TestMethod(t *testing.T) { if !ok { t.Fatalf("method by name failed") } - m.Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Type MethodByName returned %d; want 250", i) + i = m.Func.Call([]Value{ValueOf(p), ValueOf(11)})[0].Int() + if i != 275 { + t.Errorf("Type MethodByName returned %d; want 275", i) } - i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Pointer Type Method returned %d; want 250", i) + i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(12)})[0].Int() + if i != 300 { + t.Errorf("Pointer Type Method returned %d; want 300", i) } m, ok = TypeOf(&p).MethodByName("Dist") if !ok { t.Fatalf("ptr method by name failed") } - i = m.Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Pointer Type MethodByName returned %d; want 250", i) + i = m.Func.Call([]Value{ValueOf(&p), ValueOf(13)})[0].Int() + if i != 325 { + t.Errorf("Pointer Type MethodByName returned %d; want 325", i) } // Curried method of value. @@ -1498,7 +1500,74 @@ func TestMethod(t *testing.T) { if tt := v.Type(); tt != tfunc { t.Errorf("Value Method Type is %s; want %s", tt, tfunc) } - i = v.Call([]Value{ValueOf(10)})[0].Int() + i = v.Call([]Value{ValueOf(14)})[0].Int() + if i != 350 { + t.Errorf("Value Method returned %d; want 350", i) + } + v = ValueOf(p).MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(15)})[0].Int() + if i != 375 { + t.Errorf("Value MethodByName returned %d; want 375", i) + } + + // Curried method of pointer. + v = ValueOf(&p).Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(16)})[0].Int() + if i != 400 { + t.Errorf("Pointer Value Method returned %d; want 400", i) + } + v = ValueOf(&p).MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(17)})[0].Int() + if i != 425 { + t.Errorf("Pointer Value MethodByName returned %d; want 425", i) + } + + // Curried method of interface value. + // Have to wrap interface value in a struct to get at it. + // Passing it to ValueOf directly would + // access the underlying Point, not the interface. + var x interface { + Dist(int) int + } = p + pv := ValueOf(&x).Elem() + v = pv.Method(0) + if tt := v.Type(); tt != tfunc { + t.Errorf("Interface Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(18)})[0].Int() + if i != 450 { + t.Errorf("Interface Method returned %d; want 450", i) + } + v = pv.MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(19)})[0].Int() + if i != 475 { + t.Errorf("Interface MethodByName returned %d; want 475", i) + } +} + +func TestMethodValue(t *testing.T) { + p := Point{3, 4} + var i int64 + + // Curried method of value. + tfunc := TypeOf((func(int) int)(nil)) + v := ValueOf(p).Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Value Method Type is %s; want %s", tt, tfunc) + } + i = ValueOf(v.Interface()).Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value Method returned %d; want 250", i) } @@ -1506,9 +1575,9 @@ func TestMethod(t *testing.T) { if tt := v.Type(); tt != tfunc { t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc) } - i = v.Call([]Value{ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Value MethodByName returned %d; want 250", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(11)})[0].Int() + if i != 275 { + t.Errorf("Value MethodByName returned %d; want 275", i) } // Curried method of pointer. @@ -1516,17 +1585,17 @@ func TestMethod(t *testing.T) { if tt := v.Type(); tt != tfunc { t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc) } - i = v.Call([]Value{ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Pointer Value Method returned %d; want 250", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(12)})[0].Int() + if i != 300 { + t.Errorf("Pointer Value Method returned %d; want 300", i) } v = ValueOf(&p).MethodByName("Dist") if tt := v.Type(); tt != tfunc { t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc) } - i = v.Call([]Value{ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Pointer Value MethodByName returned %d; want 250", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(13)})[0].Int() + if i != 325 { + t.Errorf("Pointer Value MethodByName returned %d; want 325", i) } // Curried method of interface value. @@ -1543,20 +1612,203 @@ func TestMethod(t *testing.T) { if tt := v.Type(); tt != tfunc { t.Errorf("Interface Method Type is %s; want %s", tt, tfunc) } - i = v.Call([]Value{ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Interface Method returned %d; want 250", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int() + if i != 350 { + t.Errorf("Interface Method returned %d; want 350", i) } v = pv.MethodByName("Dist") if tt := v.Type(); tt != tfunc { t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc) } - i = v.Call([]Value{ValueOf(10)})[0].Int() - if i != 250 { - t.Errorf("Interface MethodByName returned %d; want 250", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int() + if i != 375 { + t.Errorf("Interface MethodByName returned %d; want 375", i) } } +// Reflect version of $GOROOT/test/method5.go + +// Concrete types implementing M method. +// Smaller than a word, word-sized, larger than a word. +// Value and pointer receivers. + +type Tinter interface { + M(int, byte) (byte, int) +} + +type Tsmallv byte + +func (v Tsmallv) M(x int, b byte) (byte, int) { return b, x + int(v) } + +type Tsmallp byte + +func (p *Tsmallp) M(x int, b byte) (byte, int) { return b, x + int(*p) } + +type Twordv uintptr + +func (v Twordv) M(x int, b byte) (byte, int) { return b, x + int(v) } + +type Twordp uintptr + +func (p *Twordp) M(x int, b byte) (byte, int) { return b, x + int(*p) } + +type Tbigv [2]uintptr + +func (v Tbigv) M(x int, b byte) (byte, int) { return b, x + int(v[0]) + int(v[1]) } + +type Tbigp [2]uintptr + +func (p *Tbigp) M(x int, b byte) (byte, int) { return b, x + int(p[0]) + int(p[1]) } + +// Again, with an unexported method. + +type tsmallv byte + +func (v tsmallv) m(x int, b byte) (byte, int) { return b, x + int(v) } + +type tsmallp byte + +func (p *tsmallp) m(x int, b byte) (byte, int) { return b, x + int(*p) } + +type twordv uintptr + +func (v twordv) m(x int, b byte) (byte, int) { return b, x + int(v) } + +type twordp uintptr + +func (p *twordp) m(x int, b byte) (byte, int) { return b, x + int(*p) } + +type tbigv [2]uintptr + +func (v tbigv) m(x int, b byte) (byte, int) { return b, x + int(v[0]) + int(v[1]) } + +type tbigp [2]uintptr + +func (p *tbigp) m(x int, b byte) (byte, int) { return b, x + int(p[0]) + int(p[1]) } + +type tinter interface { + m(int, byte) (byte, int) +} + +// Embedding via pointer. + +type Tm1 struct { + Tm2 +} + +type Tm2 struct { + *Tm3 +} + +type Tm3 struct { + *Tm4 +} + +type Tm4 struct { +} + +func (t4 Tm4) M(x int, b byte) (byte, int) { return b, x + 40 } + +func TestMethod5(t *testing.T) { + CheckF := func(name string, f func(int, byte) (byte, int), inc int) { + b, x := f(1000, 99) + if b != 99 || x != 1000+inc { + t.Errorf("%s(1000, 99) = %v, %v, want 99, %v", name, b, x, 1000+inc) + } + } + + CheckV := func(name string, i Value, inc int) { + bx := i.Method(0).Call([]Value{ValueOf(1000), ValueOf(byte(99))}) + b := bx[0].Interface() + x := bx[1].Interface() + if b != byte(99) || x != 1000+inc { + t.Errorf("direct %s.M(1000, 99) = %v, %v, want 99, %v", name, b, x, 1000+inc) + } + + CheckF(name+".M", i.Method(0).Interface().(func(int, byte) (byte, int)), inc) + } + + var TinterType = TypeOf(new(Tinter)).Elem() + var tinterType = TypeOf(new(tinter)).Elem() + + CheckI := func(name string, i interface{}, inc int) { + v := ValueOf(i) + CheckV(name, v, inc) + CheckV("(i="+name+")", v.Convert(TinterType), inc) + } + + sv := Tsmallv(1) + CheckI("sv", sv, 1) + CheckI("&sv", &sv, 1) + + sp := Tsmallp(2) + CheckI("&sp", &sp, 2) + + wv := Twordv(3) + CheckI("wv", wv, 3) + CheckI("&wv", &wv, 3) + + wp := Twordp(4) + CheckI("&wp", &wp, 4) + + bv := Tbigv([2]uintptr{5, 6}) + CheckI("bv", bv, 11) + CheckI("&bv", &bv, 11) + + bp := Tbigp([2]uintptr{7, 8}) + CheckI("&bp", &bp, 15) + + t4 := Tm4{} + t3 := Tm3{&t4} + t2 := Tm2{&t3} + t1 := Tm1{t2} + CheckI("t4", t4, 40) + CheckI("&t4", &t4, 40) + CheckI("t3", t3, 40) + CheckI("&t3", &t3, 40) + CheckI("t2", t2, 40) + CheckI("&t2", &t2, 40) + CheckI("t1", t1, 40) + CheckI("&t1", &t1, 40) + + methodShouldPanic := func(name string, i interface{}) { + v := ValueOf(i) + m := v.Method(0) + shouldPanic(func() { m.Call([]Value{ValueOf(1000), ValueOf(byte(99))}) }) + shouldPanic(func() { m.Interface() }) + + v = v.Convert(tinterType) + m = v.Method(0) + shouldPanic(func() { m.Call([]Value{ValueOf(1000), ValueOf(byte(99))}) }) + shouldPanic(func() { m.Interface() }) + } + + _sv := tsmallv(1) + methodShouldPanic("_sv", _sv) + methodShouldPanic("&_sv", &_sv) + + _sp := tsmallp(2) + methodShouldPanic("&_sp", &_sp) + + _wv := twordv(3) + methodShouldPanic("_wv", _wv) + methodShouldPanic("&_wv", &_wv) + + _wp := twordp(4) + methodShouldPanic("&_wp", &_wp) + + _bv := tbigv([2]uintptr{5, 6}) + methodShouldPanic("_bv", _bv) + methodShouldPanic("&_bv", &_bv) + + _bp := tbigp([2]uintptr{7, 8}) + methodShouldPanic("&_bp", &_bp) + + var tnil Tinter + vnil := ValueOf(&tnil).Elem() + shouldPanic(func() { vnil.Method(0) }) +} + func TestInterfaceSet(t *testing.T) { p := &Point{3, 4} @@ -1948,6 +2200,30 @@ func TestPtrTo(t *testing.T) { } } +func TestPtrToGC(t *testing.T) { + type T *uintptr + tt := TypeOf(T(nil)) + pt := PtrTo(tt) + const n = 100 + var x []interface{} + for i := 0; i < n; i++ { + v := New(pt) + p := new(*uintptr) + *p = new(uintptr) + **p = uintptr(i) + v.Elem().Set(ValueOf(p).Convert(pt)) + x = append(x, v.Interface()) + } + runtime.GC() + + for i, xi := range x { + k := ValueOf(xi).Elem().Elem().Elem().Interface().(uintptr) + if k != uintptr(i) { + t.Errorf("lost x[%d] = %d, want %d", i, k, i) + } + } +} + func TestAddr(t *testing.T) { var p struct { X, Y int @@ -2011,6 +2287,9 @@ func TestAddr(t *testing.T) { } func noAlloc(t *testing.T, n int, f func(int)) { + if runtime.GOMAXPROCS(0) > 1 { + t.Skip("skipping; GOMAXPROCS>1") + } i := -1 allocs := testing.AllocsPerRun(n, func() { f(i) @@ -2737,8 +3016,10 @@ func TestSliceOf(t *testing.T) { type T int st := SliceOf(TypeOf(T(1))) v := MakeSlice(st, 10, 10) + runtime.GC() for i := 0; i < v.Len(); i++ { v.Index(i).Set(ValueOf(T(i))) + runtime.GC() } s := fmt.Sprint(v.Interface()) want := "[0 1 2 3 4 5 6 7 8 9]" @@ -2751,13 +3032,44 @@ func TestSliceOf(t *testing.T) { checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{}) } +func TestSliceOfGC(t *testing.T) { + type T *uintptr + tt := TypeOf(T(nil)) + st := SliceOf(tt) + const n = 100 + var x []interface{} + for i := 0; i < n; i++ { + v := MakeSlice(st, n, n) + for j := 0; j < v.Len(); j++ { + p := new(uintptr) + *p = uintptr(i*n + j) + v.Index(j).Set(ValueOf(p).Convert(tt)) + } + x = append(x, v.Interface()) + } + runtime.GC() + + for i, xi := range x { + v := ValueOf(xi) + for j := 0; j < v.Len(); j++ { + k := v.Index(j).Elem().Interface() + if k != uintptr(i*n+j) { + t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j) + } + } + } +} + func TestChanOf(t *testing.T) { // check construction and use of type not in binary type T string ct := ChanOf(BothDir, TypeOf(T(""))) v := MakeChan(ct, 2) + runtime.GC() v.Send(ValueOf(T("hello"))) + runtime.GC() v.Send(ValueOf(T("world"))) + runtime.GC() sv1, _ := v.Recv() sv2, _ := v.Recv() @@ -2772,13 +3084,63 @@ func TestChanOf(t *testing.T) { checkSameType(t, Zero(ChanOf(BothDir, TypeOf(T1(1)))).Interface(), (chan T1)(nil)) } +func TestChanOfGC(t *testing.T) { + done := make(chan bool, 1) + go func() { + select { + case <-done: + case <-time.After(5 * time.Second): + panic("deadlock in TestChanOfGC") + } + }() + + defer func() { + done <- true + }() + + type T *uintptr + tt := TypeOf(T(nil)) + ct := ChanOf(BothDir, tt) + + // NOTE: The garbage collector handles allocated channels specially, + // so we have to save pointers to channels in x; the pointer code will + // use the gc info in the newly constructed chan type. + const n = 100 + var x []interface{} + for i := 0; i < n; i++ { + v := MakeChan(ct, n) + for j := 0; j < n; j++ { + p := new(uintptr) + *p = uintptr(i*n + j) + v.Send(ValueOf(p).Convert(tt)) + } + pv := New(ct) + pv.Elem().Set(v) + x = append(x, pv.Interface()) + } + runtime.GC() + + for i, xi := range x { + v := ValueOf(xi).Elem() + for j := 0; j < n; j++ { + pv, _ := v.Recv() + k := pv.Elem().Interface() + if k != uintptr(i*n+j) { + t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j) + } + } + } +} + func TestMapOf(t *testing.T) { // check construction and use of type not in binary type K string type V float64 v := MakeMap(MapOf(TypeOf(K("")), TypeOf(V(0)))) + runtime.GC() v.SetMapIndex(ValueOf(K("a")), ValueOf(V(1))) + runtime.GC() s := fmt.Sprint(v.Interface()) want := "map[a:1]" @@ -2788,6 +3150,81 @@ func TestMapOf(t *testing.T) { // check that type already in binary is found checkSameType(t, Zero(MapOf(TypeOf(V(0)), TypeOf(K("")))).Interface(), map[V]K(nil)) + + // check that invalid key type panics + shouldPanic(func() { MapOf(TypeOf((func())(nil)), TypeOf(false)) }) +} + +func TestMapOfGCKeys(t *testing.T) { + type T *uintptr + tt := TypeOf(T(nil)) + mt := MapOf(tt, TypeOf(false)) + + // NOTE: The garbage collector handles allocated maps specially, + // so we have to save pointers to maps in x; the pointer code will + // use the gc info in the newly constructed map type. + const n = 100 + var x []interface{} + for i := 0; i < n; i++ { + v := MakeMap(mt) + for j := 0; j < n; j++ { + p := new(uintptr) + *p = uintptr(i*n + j) + v.SetMapIndex(ValueOf(p).Convert(tt), ValueOf(true)) + } + pv := New(mt) + pv.Elem().Set(v) + x = append(x, pv.Interface()) + } + runtime.GC() + + for i, xi := range x { + v := ValueOf(xi).Elem() + var out []int + for _, kv := range v.MapKeys() { + out = append(out, int(kv.Elem().Interface().(uintptr))) + } + sort.Ints(out) + for j, k := range out { + if k != i*n+j { + t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j) + } + } + } +} + +func TestMapOfGCValues(t *testing.T) { + type T *uintptr + tt := TypeOf(T(nil)) + mt := MapOf(TypeOf(1), tt) + + // NOTE: The garbage collector handles allocated maps specially, + // so we have to save pointers to maps in x; the pointer code will + // use the gc info in the newly constructed map type. + const n = 100 + var x []interface{} + for i := 0; i < n; i++ { + v := MakeMap(mt) + for j := 0; j < n; j++ { + p := new(uintptr) + *p = uintptr(i*n + j) + v.SetMapIndex(ValueOf(j), ValueOf(p).Convert(tt)) + } + pv := New(mt) + pv.Elem().Set(v) + x = append(x, pv.Interface()) + } + runtime.GC() + + for i, xi := range x { + v := ValueOf(xi).Elem() + for j := 0; j < n; j++ { + k := v.MapIndex(ValueOf(j)).Elem().Interface().(uintptr) + if k != uintptr(i*n+j) { + t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j) + } + } + } } type B1 struct { diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s index 27d3fa21d..bbd068d98 100644 --- a/src/pkg/reflect/asm_386.s +++ b/src/pkg/reflect/asm_386.s @@ -3,11 +3,21 @@ // license that can be found in the LICENSE file. // makeFuncStub is the code half of the function returned by MakeFunc. -// See the comment on the declaration of makeFuncStub in value.go +// See the comment on the declaration of makeFuncStub in makefunc.go // for more details. TEXT ·makeFuncStub(SB),7,$8 MOVL DX, 0(SP) - LEAL arg+0(FP), CX + LEAL argframe+0(FP), CX MOVL CX, 4(SP) CALL ·callReflect(SB) RET + +// methodValueCall is the code half of the function returned by makeMethodValue. +// See the comment on the declaration of methodValueCall in makefunc.go +// for more details. +TEXT ·methodValueCall(SB),7,$8 + MOVL DX, 0(SP) + LEAL argframe+0(FP), CX + MOVL CX, 4(SP) + CALL ·callMethod(SB) + RET diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s index d51d982a9..2e7fce55d 100644 --- a/src/pkg/reflect/asm_amd64.s +++ b/src/pkg/reflect/asm_amd64.s @@ -3,11 +3,21 @@ // license that can be found in the LICENSE file. // makeFuncStub is the code half of the function returned by MakeFunc. -// See the comment on the declaration of makeFuncStub in value.go +// See the comment on the declaration of makeFuncStub in makefunc.go // for more details. TEXT ·makeFuncStub(SB),7,$16 MOVQ DX, 0(SP) - LEAQ arg+0(FP), CX + LEAQ argframe+0(FP), CX MOVQ CX, 8(SP) CALL ·callReflect(SB) RET + +// methodValueCall is the code half of the function returned by makeMethodValue. +// See the comment on the declaration of methodValueCall in makefunc.go +// for more details. +TEXT ·methodValueCall(SB),7,$16 + MOVQ DX, 0(SP) + LEAQ argframe+0(FP), CX + MOVQ CX, 8(SP) + CALL ·callMethod(SB) + RET diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s index db487f8a5..fb1dddebe 100644 --- a/src/pkg/reflect/asm_arm.s +++ b/src/pkg/reflect/asm_arm.s @@ -3,11 +3,21 @@ // license that can be found in the LICENSE file. // makeFuncStub is jumped to by the code generated by MakeFunc. -// See the comment on the declaration of makeFuncStub in value.go +// See the comment on the declaration of makeFuncStub in makefunc.go // for more details. TEXT ·makeFuncStub(SB),7,$8 MOVW R7, 4(R13) - MOVW $arg+0(FP), R1 + MOVW $argframe+0(FP), R1 MOVW R1, 8(R13) BL ·callReflect(SB) RET + +// methodValueCall is the code half of the function returned by makeMethodValue. +// See the comment on the declaration of methodValueCall in makefunc.go +// for more details. +TEXT ·methodValueCall(SB),7,$8 + MOVW R7, 4(R13) + MOVW $argframe+0(FP), R1 + MOVW R1, 8(R13) + BL ·callMethod(SB) + RET diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go index db047963e..915afed4c 100644 --- a/src/pkg/reflect/deepequal.go +++ b/src/pkg/reflect/deepequal.go @@ -118,8 +118,6 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool // Normal equality suffices return valueInterface(v1, false) == valueInterface(v2, false) } - - panic("Not reached") } // DeepEqual tests for deep equality. It uses normal == equality where diff --git a/src/pkg/reflect/makefunc.go b/src/pkg/reflect/makefunc.go index 024f938f1..ccdd683a0 100644 --- a/src/pkg/reflect/makefunc.go +++ b/src/pkg/reflect/makefunc.go @@ -48,8 +48,8 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { t := typ.common() ftyp := (*funcType)(unsafe.Pointer(t)) - // indirect Go func value (dummy) to obtain - // actual code address. (A Go func is a pointer + // Indirect Go func value (dummy) to obtain + // actual code address. (A Go func value is a pointer // to a C function pointer. http://golang.org/s/go11func.) dummy := makeFuncStub code := **(**uintptr)(unsafe.Pointer(&dummy)) @@ -65,3 +65,56 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { // where ctxt is the context register and frame is a pointer to the first // word in the passed-in argument frame. func makeFuncStub() + +type methodValue struct { + fn uintptr + method int + rcvr Value +} + +// makeMethodValue converts v from the rcvr+method index representation +// of a method value to an actual method func value, which is +// basically the receiver value with a special bit set, into a true +// func value - a value holding an actual func. The output is +// semantically equivalent to the input as far as the user of package +// reflect can tell, but the true func representation can be handled +// by code like Convert and Interface and Assign. +func makeMethodValue(op string, v Value) Value { + if v.flag&flagMethod == 0 { + panic("reflect: internal error: invalid use of makePartialFunc") + } + + // Ignoring the flagMethod bit, v describes the receiver, not the method type. + fl := v.flag & (flagRO | flagAddr | flagIndir) + fl |= flag(v.typ.Kind()) << flagKindShift + rcvr := Value{v.typ, v.val, fl} + + // v.Type returns the actual type of the method value. + funcType := v.Type().(*rtype) + + // Indirect Go func value (dummy) to obtain + // actual code address. (A Go func value is a pointer + // to a C function pointer. http://golang.org/s/go11func.) + dummy := methodValueCall + code := **(**uintptr)(unsafe.Pointer(&dummy)) + + fv := &methodValue{ + fn: code, + method: int(v.flag) >> flagMethodShift, + rcvr: rcvr, + } + + // Cause panic if method is not appropriate. + // The panic would still happen during the call if we omit this, + // but we want Interface() and other operations to fail early. + methodReceiver(op, fv.rcvr, fv.method) + + return Value{funcType, unsafe.Pointer(fv), v.flag&flagRO | flag(Func)<<flagKindShift} +} + +// methodValueCall is an assembly function that is the code half of +// the function returned from makeMethodValue. It expects a *methodValue +// as its context register, and its job is to invoke callMethod(ctxt, frame) +// where ctxt is the context register and frame is a pointer to the first +// word in the passed-in argument frame. +func methodValueCall() diff --git a/src/pkg/reflect/tostring_test.go b/src/pkg/reflect/tostring_test.go index 7486a9bfc..e416fd84d 100644 --- a/src/pkg/reflect/tostring_test.go +++ b/src/pkg/reflect/tostring_test.go @@ -92,5 +92,4 @@ func valueToString(val Value) string { default: panic("valueToString: can't print type " + typ.String()) } - return "valueToString: can't happen" } diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index 94a7521a7..b513fee90 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -233,17 +233,17 @@ const ( // with a unique tag like `reflect:"array"` or `reflect:"ptr"` // so that code cannot convert from, say, *arrayType to *ptrType. type rtype struct { - size uintptr // size in bytes - hash uint32 // hash of type; avoids computation in hash tables - _ uint8 // unused/padding - align uint8 // alignment of variable with this type - fieldAlign uint8 // alignment of struct field with this type - kind uint8 // enumeration for C - alg *uintptr // algorithm table (../runtime/runtime.h:/Alg) - gc uintptr // garbage collection data - string *string // string form; unnecessary but undeniably useful - *uncommonType // (relatively) uncommon fields - ptrToThis *rtype // type for pointer to this type, if used in binary or has methods + size uintptr // size in bytes + hash uint32 // hash of type; avoids computation in hash tables + _ uint8 // unused/padding + align uint8 // alignment of variable with this type + fieldAlign uint8 // alignment of struct field with this type + kind uint8 // enumeration for C + alg *uintptr // algorithm table (../runtime/runtime.h:/Alg) + gc unsafe.Pointer // garbage collection data + string *string // string form; unnecessary but undeniably useful + *uncommonType // (relatively) uncommon fields + ptrToThis *rtype // type for pointer to this type, if used in binary or has methods } // Method on non-interface type @@ -345,6 +345,25 @@ type structType struct { fields []structField // sorted by offset } +// NOTE: These are copied from ../runtime/mgc0.h. +// They must be kept in sync. +const ( + _GC_END = iota + _GC_PTR + _GC_APTR + _GC_ARRAY_START + _GC_ARRAY_NEXT + _GC_CALL + _GC_MAP_PTR + _GC_CHAN_PTR + _GC_STRING + _GC_EFACE + _GC_IFACE + _GC_SLICE + _GC_REGION + _GC_NUM_INSTR +) + /* * The compiler knows the exact layout of all the data structures above. * The compiler does not know about the data structures and methods below. @@ -368,7 +387,10 @@ type Method struct { // High bit says whether type has // embedded pointers,to help garbage collector. -const kindMask = 0x7f +const ( + kindMask = 0x7f + kindNoPointers = 0x80 +) func (k Kind) String() string { if int(k) < len(kindNames) { @@ -978,6 +1000,32 @@ var ptrMap struct { m map[*rtype]*ptrType } +// garbage collection bytecode program for pointer to memory without pointers. +// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. +type ptrDataGC struct { + width uintptr // sizeof(ptr) + op uintptr // _GC_APTR + off uintptr // 0 + end uintptr // _GC_END +} + +var ptrDataGCProg = ptrDataGC{ + width: unsafe.Sizeof((*byte)(nil)), + op: _GC_APTR, + off: 0, + end: _GC_END, +} + +// garbage collection bytecode program for pointer to memory with pointers. +// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. +type ptrGC struct { + width uintptr // sizeof(ptr) + op uintptr // _GC_PTR + off uintptr // 0 + elemgc unsafe.Pointer // element gc type + end uintptr // _GC_END +} + // PtrTo returns the pointer type with element t. // For example, if t represents type Foo, PtrTo(t) represents *Foo. func PtrTo(t Type) Type { @@ -1034,6 +1082,20 @@ func (t *rtype) ptrTo() *rtype { p.ptrToThis = nil p.elem = t + if t.kind&kindNoPointers != 0 { + p.gc = unsafe.Pointer(&ptrDataGCProg) + } else { + p.gc = unsafe.Pointer(&ptrGC{ + width: p.size, + op: _GC_PTR, + off: 0, + elemgc: t.gc, + end: _GC_END, + }) + } + // INCORRECT. Uncomment to check that TestPtrToGC fails when p.gc is wrong. + //p.gc = unsafe.Pointer(&badGC{width: p.size, end: _GC_END}) + ptrMap.m[t] = p ptrMap.Unlock() return &p.rtype @@ -1246,7 +1308,7 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { } // typelinks is implemented in package runtime. -// It retuns a slice of all the 'typelink' information in the binary, +// It returns a slice of all the 'typelink' information in the binary, // which is to say a slice of known types, sorted by string. // Note that strings are not unique identifiers for types: // there can be more than one with a given string. @@ -1338,6 +1400,21 @@ func cachePut(k cacheKey, t *rtype) Type { return t } +// garbage collection bytecode program for chan or map. +// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. +type chanMapGC struct { + width uintptr // sizeof(map) + op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR + off uintptr // 0 + typ *rtype // map type + end uintptr // _GC_END +} + +type badGC struct { + width uintptr + end uintptr +} + // ChanOf returns the channel type with the given direction and element type. // For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int. // @@ -1390,20 +1467,35 @@ func ChanOf(dir ChanDir, t Type) Type { ch.uncommonType = nil ch.ptrToThis = nil + ch.gc = unsafe.Pointer(&chanMapGC{ + width: ch.size, + op: _GC_CHAN_PTR, + off: 0, + typ: &ch.rtype, + end: _GC_END, + }) + + // INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong. + //ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END}) + return cachePut(ckey, &ch.rtype) } +func ismapkey(*rtype) bool // implemented in runtime + // MapOf returns the map type with the given key and element types. // For example, if k represents int and e represents string, // MapOf(k, e) represents map[int]string. // // If the key type is not a valid map key type (that is, if it does -// not implement Go's == operator), MapOf panics. TODO(rsc). +// not implement Go's == operator), MapOf panics. func MapOf(key, elem Type) Type { ktyp := key.(*rtype) etyp := elem.(*rtype) - // TODO: Check for invalid key types. + if !ismapkey(ktyp) { + panic("reflect.MapOf: invalid key type " + ktyp.String()) + } // Look in cache. ckey := cacheKey{Map, ktyp, etyp, 0} @@ -1432,9 +1524,47 @@ func MapOf(key, elem Type) Type { mt.uncommonType = nil mt.ptrToThis = nil + mt.gc = unsafe.Pointer(&chanMapGC{ + width: mt.size, + op: _GC_MAP_PTR, + off: 0, + typ: &mt.rtype, + end: _GC_END, + }) + + // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues + // fail when mt.gc is wrong. + //mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END}) + return cachePut(ckey, &mt.rtype) } +// garbage collection bytecode program for slice of non-zero-length values. +// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. +type sliceGC struct { + width uintptr // sizeof(slice) + op uintptr // _GC_SLICE + off uintptr // 0 + elemgc unsafe.Pointer // element gc program + end uintptr // _GC_END +} + +// garbage collection bytecode program for slice of zero-length values. +// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. +type sliceEmptyGC struct { + width uintptr // sizeof(slice) + op uintptr // _GC_APTR + off uintptr // 0 + end uintptr // _GC_END +} + +var sliceEmptyGCProg = sliceEmptyGC{ + width: unsafe.Sizeof([]byte(nil)), + op: _GC_APTR, + off: 0, + end: _GC_END, +} + // SliceOf returns the slice type with element type t. // For example, if t represents int, SliceOf(t) represents []int. func SliceOf(t Type) Type { @@ -1466,6 +1596,21 @@ func SliceOf(t Type) Type { slice.uncommonType = nil slice.ptrToThis = nil + if typ.size == 0 { + slice.gc = unsafe.Pointer(&sliceEmptyGCProg) + } else { + slice.gc = unsafe.Pointer(&sliceGC{ + width: slice.size, + op: _GC_SLICE, + off: 0, + elemgc: typ.gc, + end: _GC_END, + }) + } + + // INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong. + //slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END}) + return cachePut(ckey, &slice.rtype) } diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index c87812c46..80aa85723 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -249,7 +249,7 @@ func (f flag) mustBeExported() { panic(&ValueError{methodName(), 0}) } if f&flagRO != 0 { - panic(methodName() + " using value obtained using unexported field") + panic("reflect: " + methodName() + " using value obtained using unexported field") } } @@ -262,10 +262,10 @@ func (f flag) mustBeAssignable() { } // Assignable if addressable and not read-only. if f&flagRO != 0 { - panic(methodName() + " using value obtained using unexported field") + panic("reflect: " + methodName() + " using value obtained using unexported field") } if f&flagAddr == 0 { - panic(methodName() + " using unaddressable value") + panic("reflect: " + methodName() + " using unaddressable value") } } @@ -358,7 +358,7 @@ func (v Value) CallSlice(in []Value) []Value { return v.call("CallSlice", in) } -func (v Value) call(method string, in []Value) []Value { +func (v Value) call(op string, in []Value) []Value { // Get function pointer, type. t := v.typ var ( @@ -366,36 +366,7 @@ func (v Value) call(method string, in []Value) []Value { rcvr iword ) if v.flag&flagMethod != 0 { - i := int(v.flag) >> flagMethodShift - if v.typ.Kind() == Interface { - tt := (*interfaceType)(unsafe.Pointer(v.typ)) - if i < 0 || i >= len(tt.methods) { - panic("reflect: broken Value") - } - m := &tt.methods[i] - if m.pkgPath != nil { - panic(method + " of unexported method") - } - t = m.typ - iface := (*nonEmptyInterface)(v.val) - if iface.itab == nil { - panic(method + " of method on nil interface value") - } - fn = unsafe.Pointer(&iface.itab.fun[i]) - rcvr = iface.word - } else { - ut := v.typ.uncommon() - if ut == nil || i < 0 || i >= len(ut.methods) { - panic("reflect: broken Value") - } - m := &ut.methods[i] - if m.pkgPath != nil { - panic(method + " of unexported method") - } - fn = unsafe.Pointer(&m.ifn) - t = m.mtyp - rcvr = v.iword() - } + t, fn, rcvr = methodReceiver(op, v, int(v.flag)>>flagMethodShift) } else if v.flag&flagIndir != 0 { fn = *(*unsafe.Pointer)(v.val) } else { @@ -406,7 +377,7 @@ func (v Value) call(method string, in []Value) []Value { panic("reflect.Value.Call: call of nil function") } - isSlice := method == "CallSlice" + isSlice := op == "CallSlice" n := t.NumIn() if isSlice { if !t.IsVariadic() { @@ -431,12 +402,12 @@ func (v Value) call(method string, in []Value) []Value { } for _, x := range in { if x.Kind() == Invalid { - panic("reflect: " + method + " using zero Value argument") + panic("reflect: " + op + " using zero Value argument") } } for i := 0; i < n; i++ { if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { - panic("reflect: " + method + " using " + xt.String() + " as type " + targ.String()) + panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) } } if !isSlice && t.IsVariadic() { @@ -447,7 +418,7 @@ func (v Value) call(method string, in []Value) []Value { for i := 0; i < m; i++ { x := in[n+i] if xt := x.Type(); !xt.AssignableTo(elem) { - panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + method) + panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) } slice.Index(i).Set(x) } @@ -467,40 +438,11 @@ func (v Value) call(method string, in []Value) []Value { // This computation is 5g/6g/8g-dependent // and probably wrong for gccgo, but so // is most of this function. - size := uintptr(0) - if v.flag&flagMethod != 0 { - // extra word for receiver interface word - size += ptrSize - } - for i := 0; i < nin; i++ { - tv := t.In(i) - a := uintptr(tv.Align()) - size = (size + a - 1) &^ (a - 1) - size += tv.Size() - } - size = (size + ptrSize - 1) &^ (ptrSize - 1) - for i := 0; i < nout; i++ { - tv := t.Out(i) - a := uintptr(tv.Align()) - size = (size + a - 1) &^ (a - 1) - size += tv.Size() - } - - // size must be > 0 in order for &args[0] to be valid. - // the argument copying is going to round it up to - // a multiple of ptrSize anyway, so make it ptrSize to begin with. - if size < ptrSize { - size = ptrSize - } - - // round to pointer size - size = (size + ptrSize - 1) &^ (ptrSize - 1) + size, _, _, _ := frameSize(t, v.flag&flagMethod != 0) // Copy into args. // - // TODO(rsc): revisit when reference counting happens. - // The values are holding up the in references for us, - // but something must be done for the out references. + // TODO(rsc): This will need to be updated for any new garbage collector. // For now make everything look like a pointer by allocating // a []unsafe.Pointer. args := make([]unsafe.Pointer, size/ptrSize) @@ -616,6 +558,119 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) { } } +// methodReceiver returns information about the receiver +// described by v. The Value v may or may not have the +// flagMethod bit set, so the kind cached in v.flag should +// not be used. +func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Pointer, rcvr iword) { + i := methodIndex + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: internal error: invalid method index") + } + m := &tt.methods[i] + if m.pkgPath != nil { + panic("reflect: " + op + " of unexported method") + } + t = m.typ + iface := (*nonEmptyInterface)(v.val) + if iface.itab == nil { + panic("reflect: " + op + " of method on nil interface value") + } + fn = unsafe.Pointer(&iface.itab.fun[i]) + rcvr = iface.word + } else { + ut := v.typ.uncommon() + if ut == nil || i < 0 || i >= len(ut.methods) { + panic("reflect: internal error: invalid method index") + } + m := &ut.methods[i] + if m.pkgPath != nil { + panic("reflect: " + op + " of unexported method") + } + fn = unsafe.Pointer(&m.ifn) + t = m.mtyp + rcvr = v.iword() + } + return +} + +// align returns the result of rounding x up to a multiple of n. +// n must be a power of two. +func align(x, n uintptr) uintptr { + return (x + n - 1) &^ (n - 1) +} + +// frameSize returns the sizes of the argument and result frame +// for a function of the given type. The rcvr bool specifies whether +// a one-word receiver should be included in the total. +func frameSize(t *rtype, rcvr bool) (total, in, outOffset, out uintptr) { + if rcvr { + // extra word for receiver interface word + total += ptrSize + } + + nin := t.NumIn() + in = -total + for i := 0; i < nin; i++ { + tv := t.In(i) + total = align(total, uintptr(tv.Align())) + total += tv.Size() + } + in += total + total = align(total, ptrSize) + nout := t.NumOut() + outOffset = total + out = -total + for i := 0; i < nout; i++ { + tv := t.Out(i) + total = align(total, uintptr(tv.Align())) + total += tv.Size() + } + out += total + + // total must be > 0 in order for &args[0] to be valid. + // the argument copying is going to round it up to + // a multiple of ptrSize anyway, so make it ptrSize to begin with. + if total < ptrSize { + total = ptrSize + } + + // round to pointer + total = align(total, ptrSize) + + return +} + +// callMethod is the call implementation used by a function returned +// by makeMethodValue (used by v.Method(i).Interface()). +// It is a streamlined version of the usual reflect call: the caller has +// already laid out the argument frame for us, so we don't have +// to deal with individual Values for each argument. +// It is in this file so that it can be next to the two similar functions above. +// The remainder of the makeMethodValue implementation is in makefunc.go. +func callMethod(ctxt *methodValue, frame unsafe.Pointer) { + t, fn, rcvr := methodReceiver("call", ctxt.rcvr, ctxt.method) + total, in, outOffset, out := frameSize(t, true) + + // Copy into args. + // + // TODO(rsc): This will need to be updated for any new garbage collector. + // For now make everything look like a pointer by allocating + // a []unsafe.Pointer. + args := make([]unsafe.Pointer, total/ptrSize) + args[0] = unsafe.Pointer(rcvr) + base := unsafe.Pointer(&args[0]) + memmove(unsafe.Pointer(uintptr(base)+ptrSize), frame, in) + + // Call. + call(fn, unsafe.Pointer(&args[0]), uint32(total)) + + // Copy return values. + memmove(unsafe.Pointer(uintptr(frame)+outOffset-ptrSize), unsafe.Pointer(uintptr(base)+outOffset), out) +} + // funcName returns the name of f, for use in error messages. func funcName(f func([]Value) []Value) string { pc := *(*uintptr)(unsafe.Pointer(&f)) @@ -902,7 +957,7 @@ func (v Value) CanInterface() bool { if v.flag == 0 { panic(&ValueError{"reflect.Value.CanInterface", Invalid}) } - return v.flag&(flagMethod|flagRO) == 0 + return v.flag&flagRO == 0 } // Interface returns v's current value as an interface{}. @@ -921,16 +976,15 @@ func valueInterface(v Value, safe bool) interface{} { if v.flag == 0 { panic(&ValueError{"reflect.Value.Interface", 0}) } - if v.flag&flagMethod != 0 { - panic("reflect.Value.Interface: cannot create interface value for method with bound receiver") - } - if safe && v.flag&flagRO != 0 { // Do not allow access to unexported values via Interface, // because they might be pointers that should not be // writable or methods or function that should not be callable. panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") } + if v.flag&flagMethod != 0 { + v = makeMethodValue("Interface", v) + } k := v.kind() if k == Interface { @@ -981,7 +1035,7 @@ func (v Value) IsNil() bool { switch k { case Chan, Func, Map, Ptr: if v.flag&flagMethod != 0 { - panic("reflect: IsNil of method Value") + return false } ptr := v.val if v.flag&flagIndir != 0 { @@ -1100,7 +1154,7 @@ func (v Value) MapKeys() []Value { // Method returns a function value corresponding to v's i'th method. // The arguments to a Call on the returned function should not include // a receiver; the returned function will always use v as the receiver. -// Method panics if i is out of range. +// Method panics if i is out of range or if v is a nil interface value. func (v Value) Method(i int) Value { if v.typ == nil { panic(&ValueError{"reflect.Value.Method", Invalid}) @@ -1108,7 +1162,10 @@ func (v Value) Method(i int) Value { if v.flag&flagMethod != 0 || i < 0 || i >= v.typ.NumMethod() { panic("reflect: Method index out of range") } - fl := v.flag & (flagRO | flagAddr | flagIndir) + if v.typ.Kind() == Interface && v.IsNil() { + panic("reflect: Method on nil interface value") + } + fl := v.flag & (flagRO | flagIndir) fl |= flag(Func) << flagKindShift fl |= flag(i)<<flagMethodShift | flagMethod return Value{v.typ, v.val, fl} @@ -1232,7 +1289,14 @@ func (v Value) Pointer() uintptr { return uintptr(p) case Func: if v.flag&flagMethod != 0 { - panic("reflect.Value.Pointer of method Value") + // As the doc comment says, the returned pointer is an + // underlying code pointer but not necessarily enough to + // identify a single function uniquely. All method expressions + // created via reflect have the same underlying code pointer, + // so their Pointers are equal. The function used here must + // match the one used in makeMethodValue. + f := methodValueCall + return **(**uintptr)(unsafe.Pointer(&f)) } p := v.val if v.flag&flagIndir != 0 { @@ -1267,7 +1331,7 @@ func (v Value) Recv() (x Value, ok bool) { func (v Value) recv(nb bool) (val Value, ok bool) { tt := (*chanType)(unsafe.Pointer(v.typ)) if ChanDir(tt.dir)&RecvDir == 0 { - panic("recv on send-only channel") + panic("reflect: recv on send-only channel") } word, selected, ok := chanrecv(v.typ, v.iword(), nb) if selected { @@ -1295,7 +1359,7 @@ func (v Value) Send(x Value) { func (v Value) send(x Value, nb bool) (selected bool) { tt := (*chanType)(unsafe.Pointer(v.typ)) if ChanDir(tt.dir)&SendDir == 0 { - panic("send on recv-only channel") + panic("reflect: send on recv-only channel") } x.mustBeExported() x = x.assignTo("reflect.Value.Send", tt.elem, nil) @@ -1578,7 +1642,7 @@ func (v Value) Type() Type { // Method on interface. tt := (*interfaceType)(unsafe.Pointer(v.typ)) if i < 0 || i >= len(tt.methods) { - panic("reflect: broken Value") + panic("reflect: internal error: invalid method index") } m := &tt.methods[i] return m.typ @@ -1586,7 +1650,7 @@ func (v Value) Type() Type { // Method on concrete type. ut := v.typ.uncommon() if ut == nil || i < 0 || i >= len(ut.methods) { - panic("reflect: broken Value") + panic("reflect: internal error: invalid method index") } m := &ut.methods[i] return m.mtyp @@ -1635,14 +1699,22 @@ func (v Value) UnsafeAddr() uintptr { } // StringHeader is the runtime representation of a string. -// It cannot be used safely or portably. +// It cannot be used safely or portably and its representation may +// change in a later release. +// Moreover, the Data field is not sufficient to guarantee the data +// it references will not be garbage collected, so programs must keep +// a separate, correctly typed pointer to the underlying data. type StringHeader struct { Data uintptr Len int } // SliceHeader is the runtime representation of a slice. -// It cannot be used safely or portably. +// It cannot be used safely or portably and its representation may +// change in a later release. +// Moreover, the Data field is not sufficient to guarantee the data +// it references will not be garbage collected, so programs must keep +// a separate, correctly typed pointer to the underlying data. type SliceHeader struct { Data uintptr Len int @@ -2030,7 +2102,7 @@ func NewAt(typ Type, p unsafe.Pointer) Value { // For a conversion to an interface type, target is a suggested scratch space to use. func (v Value) assignTo(context string, dst *rtype, target *interface{}) Value { if v.flag&flagMethod != 0 { - panic(context + ": cannot assign method value to type " + dst.String()) + v = makeMethodValue(context, v) } switch { @@ -2064,7 +2136,7 @@ func (v Value) assignTo(context string, dst *rtype, target *interface{}) Value { // of the value v to type t, Convert panics. func (v Value) Convert(t Type) Value { if v.flag&flagMethod != 0 { - panic("reflect.Value.Convert: cannot convert method values") + v = makeMethodValue("Convert", v) } op := convertOp(t.common(), v.typ) if op == nil { |