diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2014-07-19 08:53:52 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2014-07-19 08:53:52 +0000 |
commit | 00d86ac99f5dd6afa5bbd7c38ffe1c585edd2387 (patch) | |
tree | b988e32ea14a3dc1b4718b1fdfa47bab087ae96c /libgo/go/reflect | |
parent | bcf2fc6ee0a7edbe7de4299f28b66527c07bb0a2 (diff) | |
download | gcc-00d86ac99f5dd6afa5bbd7c38ffe1c585edd2387.zip gcc-00d86ac99f5dd6afa5bbd7c38ffe1c585edd2387.tar.gz gcc-00d86ac99f5dd6afa5bbd7c38ffe1c585edd2387.tar.bz2 |
libgo: Update to Go 1.3 release.
From-SVN: r212837
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r-- | libgo/go/reflect/all_test.go | 147 | ||||
-rw-r--r-- | libgo/go/reflect/export_test.go | 1 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc.go | 14 | ||||
-rw-r--r-- | libgo/go/reflect/type.go | 12 | ||||
-rw-r--r-- | libgo/go/reflect/value.go | 45 |
5 files changed, 192 insertions, 27 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 6c015ad..799bbea 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -15,6 +15,7 @@ import ( . "reflect" "runtime" "sort" + "strings" "sync" "testing" "time" @@ -972,6 +973,31 @@ func TestMap(t *testing.T) { } } +func TestNilMap(t *testing.T) { + var m map[string]int + mv := ValueOf(m) + keys := mv.MapKeys() + if len(keys) != 0 { + t.Errorf(">0 keys for nil map: %v", keys) + } + + // Check that value for missing key is zero. + x := mv.MapIndex(ValueOf("hello")) + if x.Kind() != Invalid { + t.Errorf("m.MapIndex(\"hello\") for nil map = %v, want Invalid Value", x) + } + + // Check big value too. + var mbig map[string][10 << 20]byte + x = ValueOf(mbig).MapIndex(ValueOf("hello")) + if x.Kind() != Invalid { + t.Errorf("mbig.MapIndex(\"hello\") for nil map = %v, want Invalid Value", x) + } + + // Test that deletes from a nil map succeed. + mv.SetMapIndex(ValueOf("hi"), Value{}) +} + func TestChan(t *testing.T) { for loop := 0; loop < 2; loop++ { var c chan int @@ -1523,6 +1549,23 @@ func TestMakeFuncInterface(t *testing.T) { } } +func TestMakeFuncVariadic(t *testing.T) { + // Test that variadic arguments are packed into a slice and passed as last arg + fn := func(_ int, is ...int) []int { return nil } + fv := MakeFunc(TypeOf(fn), func(in []Value) []Value { return in[1:2] }) + ValueOf(&fn).Elem().Set(fv) + + r := fv.Call([]Value{ValueOf(1), ValueOf(2), ValueOf(3)})[0].Interface().([]int) + if r[0] != 2 || r[1] != 3 { + t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1]) + } + + r = fv.CallSlice([]Value{ValueOf(1), ValueOf([]int{2, 3})})[0].Interface().([]int) + if r[0] != 2 || r[1] != 3 { + t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1]) + } +} + type Point struct { x, y int } @@ -3723,3 +3766,107 @@ func TestBigZero(t *testing.T) { } } } + +func TestFieldByIndexNil(t *testing.T) { + type P struct { + F int + } + type T struct { + *P + } + v := ValueOf(T{}) + + v.FieldByName("P") // should be fine + + defer func() { + if err := recover(); err == nil { + t.Fatalf("no error") + } else if !strings.Contains(fmt.Sprint(err), "nil pointer to embedded struct") { + t.Fatalf(`err=%q, wanted error containing "nil pointer to embedded struct"`, err) + } + }() + v.FieldByName("F") // should panic + + t.Fatalf("did not panic") +} + +// Given +// type Outer struct { +// *Inner +// ... +// } +// the compiler generates the implementation of (*Outer).M dispatching to the embedded Inner. +// The implementation is logically: +// func (p *Outer) M() { +// (p.Inner).M() +// } +// but since the only change here is the replacement of one pointer receiver with another, +// the actual generated code overwrites the original receiver with the p.Inner pointer and +// then jumps to the M method expecting the *Inner receiver. +// +// During reflect.Value.Call, we create an argument frame and the associated data structures +// to describe it to the garbage collector, populate the frame, call reflect.call to +// run a function call using that frame, and then copy the results back out of the frame. +// The reflect.call function does a memmove of the frame structure onto the +// stack (to set up the inputs), runs the call, and the memmoves the stack back to +// the frame structure (to preserve the outputs). +// +// Originally reflect.call did not distinguish inputs from outputs: both memmoves +// were for the full stack frame. However, in the case where the called function was +// one of these wrappers, the rewritten receiver is almost certainly a different type +// than the original receiver. This is not a problem on the stack, where we use the +// program counter to determine the type information and understand that +// during (*Outer).M the receiver is an *Outer while during (*Inner).M the receiver in the same +// memory word is now an *Inner. But in the statically typed argument frame created +// by reflect, the receiver is always an *Outer. Copying the modified receiver pointer +// off the stack into the frame will store an *Inner there, and then if a garbage collection +// happens to scan that argument frame before it is discarded, it will scan the *Inner +// memory as if it were an *Outer. If the two have different memory layouts, the +// collection will intepret the memory incorrectly. +// +// One such possible incorrect interpretation is to treat two arbitrary memory words +// (Inner.P1 and Inner.P2 below) as an interface (Outer.R below). Because interpreting +// an interface requires dereferencing the itab word, the misinterpretation will try to +// deference Inner.P1, causing a crash during garbage collection. +// +// This came up in a real program in issue 7725. + +type Outer struct { + *Inner + R io.Reader +} + +type Inner struct { + X *Outer + P1 uintptr + P2 uintptr +} + +func (pi *Inner) M() { + // Clear references to pi so that the only way the + // garbage collection will find the pointer is in the + // argument frame, typed as a *Outer. + pi.X.Inner = nil + + // Set up an interface value that will cause a crash. + // P1 = 1 is a non-zero, so the interface looks non-nil. + // P2 = pi ensures that the data word points into the + // allocated heap; if not the collection skips the interface + // value as irrelevant, without dereferencing P1. + pi.P1 = 1 + pi.P2 = uintptr(unsafe.Pointer(pi)) +} + +func TestCallMethodJump(t *testing.T) { + // In reflect.Value.Call, trigger a garbage collection after reflect.call + // returns but before the args frame has been discarded. + // This is a little clumsy but makes the failure repeatable. + *CallGC = true + + p := &Outer{Inner: new(Inner)} + p.Inner.X = p + ValueOf(p).Method(0).Call(nil) + + // Stop garbage collecting during reflect.call. + *CallGC = false +} diff --git a/libgo/go/reflect/export_test.go b/libgo/go/reflect/export_test.go index cd8cf2c..0778ad3 100644 --- a/libgo/go/reflect/export_test.go +++ b/libgo/go/reflect/export_test.go @@ -16,3 +16,4 @@ func IsRO(v Value) bool { } var ArrayOf = arrayOf +var CallGC = &callGC diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index b248743..a46e1d8 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -113,7 +113,7 @@ func makeMethodValue(op string, v Value) Value { // 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. - t, _ := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift) + _, t, _ := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift) fv := &makeFuncImpl{ code: code, @@ -167,17 +167,9 @@ func (c *makeFuncImpl) call(in []Value) []Value { if c.method == -1 { return c.fn(in) } else if c.method == -2 { - if c.typ.IsVariadic() { - return c.rcvr.CallSlice(in) - } else { - return c.rcvr.Call(in) - } + return c.rcvr.Call(in) } else { m := c.rcvr.Method(c.method) - if c.typ.IsVariadic() { - return m.CallSlice(in) - } else { - return m.Call(in) - } + return m.Call(in) } } diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index f8e2c59..74cf294 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -16,6 +16,7 @@ package reflect import ( + "runtime" "strconv" "sync" "unsafe" @@ -1519,6 +1520,13 @@ func MapOf(key, elem Type) Type { mt.uncommonType = nil mt.ptrToThis = nil mt.zero = unsafe.Pointer(&make([]byte, mt.size)[0]) + // mt.gc = unsafe.Pointer(&ptrGC{ + // width: unsafe.Sizeof(uintptr(0)), + // op: _GC_PTR, + // off: 0, + // elemgc: mt.hmap.gc, + // end: _GC_END, + // }) // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues // fail when mt.gc is wrong. @@ -1551,6 +1559,10 @@ func bucketOf(ktyp, etyp *rtype) *rtype { gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow offset += ptrsize + if runtime.GOARCH == "amd64p32" { + offset += 4 + } + // keys if ktyp.kind&kindNoPointers == 0 { gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, ktyp.size) diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index 64081b9..cac0833 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -425,6 +425,8 @@ func (v Value) CallSlice(in []Value) []Value { return v.call("CallSlice", in) } +var callGC bool // for testing; see TestCallMethodJump + var makeFuncStubFn = makeFuncStub var makeFuncStubCode = **(**uintptr)(unsafe.Pointer(&makeFuncStubFn)) @@ -437,9 +439,8 @@ func (v Value) call(op string, in []Value) []Value { rcvrtype *rtype ) if v.flag&flagMethod != 0 { - rcvrtype = t rcvr = v - t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) + rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) } else if v.flag&flagIndir != 0 { fn = *(*unsafe.Pointer)(v.ptr) } else { @@ -450,17 +451,6 @@ func (v Value) call(op string, in []Value) []Value { panic("reflect.Value.Call: call of nil function") } - // If target is makeFuncStub, short circuit the unpack onto stack / - // pack back into []Value for the args and return values. Just do the - // call directly. - // We need to do this here because otherwise we have a situation where - // reflect.callXX calls makeFuncStub, neither of which knows the - // layout of the args. That's bad for precise gc & stack copying. - x := (*makeFuncImpl)(fn) - if x.code == makeFuncStubCode { - return x.call(in) - } - isSlice := op == "CallSlice" n := t.NumIn() if isSlice { @@ -518,6 +508,17 @@ func (v Value) call(op string, in []Value) []Value { } nout := t.NumOut() + // If target is makeFuncStub, short circuit the unpack onto stack / + // pack back into []Value for the args and return values. Just do the + // call directly. + // We need to do this here because otherwise we have a situation where + // reflect.callXX calls makeFuncStub, neither of which knows the + // layout of the args. That's bad for precise gc & stack copying. + x := (*makeFuncImpl)(fn) + if x.code == makeFuncStubCode { + return x.call(in) + } + if v.flag&flagMethod != 0 { nin++ } @@ -575,6 +576,11 @@ func (v Value) call(op string, in []Value) []Value { call(t, fn, v.flag&flagMethod != 0, firstPointer, pp, pr) + // For testing; see TestCallMethodJump. + if callGC { + runtime.GC() + } + return ret } @@ -582,9 +588,10 @@ func (v Value) call(op string, in []Value) []Value { // 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. +// The return value rcvrtype gives the method's actual receiver type. // The return value t gives the method type signature (without the receiver). // The return value fn is a pointer to the method code. -func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Pointer) { +func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn unsafe.Pointer) { i := methodIndex if v.typ.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(v.typ)) @@ -599,9 +606,11 @@ func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Po if iface.itab == nil { panic("reflect: " + op + " of method on nil interface value") } + rcvrtype = iface.itab.typ fn = unsafe.Pointer(&iface.itab.fun[i]) t = m.typ } else { + rcvrtype = v.typ ut := v.typ.uncommon() if ut == nil || i < 0 || i >= len(ut.methods) { panic("reflect: internal error: invalid method index") @@ -786,7 +795,10 @@ func (v Value) FieldByIndex(index []int) Value { v.mustBe(Struct) for i, x := range index { if i > 0 { - if v.Kind() == Ptr && v.Elem().Kind() == Struct { + if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct { + if v.IsNil() { + panic("reflect: indirection through nil pointer to embedded struct") + } v = v.Elem() } } @@ -1516,6 +1528,7 @@ func (v Value) SetCap(n int) { // SetMapIndex sets the value associated with key in the map v to val. // It panics if v's Kind is not Map. // If val is the zero Value, SetMapIndex deletes the key from the map. +// Otherwise if v holds a nil map, SetMapIndex will panic. // As in Go, key's value must be assignable to the map's key type, // and val's value must be assignable to the map's value type. func (v Value) SetMapIndex(key, val Value) { @@ -2198,7 +2211,7 @@ func Zero(typ Type) Value { } // New returns a Value representing a pointer to a new zero value -// for the specified type. That is, the returned Value's Type is PtrTo(t). +// for the specified type. That is, the returned Value's Type is PtrTo(typ). func New(typ Type) Value { if typ == nil { panic("reflect: New(nil)") |