diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-11-06 19:49:01 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-11-06 19:49:01 +0000 |
commit | f038dae646bac2b31be98ab592c0e5206d2d96f5 (patch) | |
tree | 39530b071991b2326f881b2a30a2d82d6c133fd6 /libgo/go/reflect | |
parent | f20f261304993444741e0f0a14d3147e591bc660 (diff) | |
download | gcc-f038dae646bac2b31be98ab592c0e5206d2d96f5.tar.gz |
libgo: Update to October 24 version of master library.
From-SVN: r204466
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r-- | libgo/go/reflect/all_test.go | 176 | ||||
-rw-r--r-- | libgo/go/reflect/deepequal.go | 39 | ||||
-rw-r--r-- | libgo/go/reflect/example_test.go | 14 | ||||
-rw-r--r-- | libgo/go/reflect/makefunc.go | 2 | ||||
-rw-r--r-- | libgo/go/reflect/type.go | 131 | ||||
-rw-r--r-- | libgo/go/reflect/value.go | 134 |
6 files changed, 404 insertions, 92 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 526f09bb2ca..6ab02f7d854 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -169,16 +169,20 @@ var typeTests = []pair{ } var valueTests = []pair{ + {new(int), "132"}, {new(int8), "8"}, {new(int16), "16"}, {new(int32), "32"}, {new(int64), "64"}, + {new(uint), "132"}, {new(uint8), "8"}, {new(uint16), "16"}, {new(uint32), "32"}, {new(uint64), "64"}, {new(float32), "256.25"}, {new(float64), "512.125"}, + {new(complex64), "532.125+10i"}, + {new(complex128), "564.25+1i"}, {new(string), "stringy cheese"}, {new(bool), "true"}, {new(*int8), "*int8(0)"}, @@ -944,7 +948,7 @@ func TestMap(t *testing.T) { newm := newmap.Interface().(map[string]int) if len(newm) != len(m) { - t.Errorf("length after copy: newm=%d, m=%d", newm, m) + t.Errorf("length after copy: newm=%d, m=%d", len(newm), len(m)) } for k, v := range newm { @@ -1630,6 +1634,25 @@ func TestMethodValue(t *testing.T) { t.Errorf("Pointer Value MethodByName returned %d; want 325", i) } + // Curried method of pointer to pointer. + pp := &p + v = ValueOf(&pp).Elem().Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Pointer Value Method Type is %s; want %s", tt, tfunc) + } + i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int() + if i != 350 { + t.Errorf("Pointer Pointer Value Method returned %d; want 350", i) + } + v = ValueOf(&pp).Elem().MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Pointer Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int() + if i != 375 { + t.Errorf("Pointer Pointer Value MethodByName returned %d; want 375", i) + } + // Curried method of interface value. // Have to wrap interface value in a struct to get at it. // Passing it to ValueOf directly would @@ -1644,17 +1667,17 @@ func TestMethodValue(t *testing.T) { if tt := v.Type(); tt != tfunc { t.Errorf("Interface Method Type is %s; want %s", tt, tfunc) } - i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int() - if i != 350 { - t.Errorf("Interface Method returned %d; want 350", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(16)})[0].Int() + if i != 400 { + t.Errorf("Interface Method returned %d; want 400", i) } v = pv.MethodByName("Dist") if tt := v.Type(); tt != tfunc { t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc) } - i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int() - if i != 375 { - t.Errorf("Interface MethodByName returned %d; want 375", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(17)})[0].Int() + if i != 425 { + t.Errorf("Interface MethodByName returned %d; want 425", i) } } @@ -2330,6 +2353,9 @@ func TestAddr(t *testing.T) { /* gccgo does do allocations here. func noAlloc(t *testing.T, n int, f func(int)) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -2413,6 +2439,74 @@ func TestSlice(t *testing.T) { } } +func TestSlice3(t *testing.T) { + xs := []int{1, 2, 3, 4, 5, 6, 7, 8} + v := ValueOf(xs).Slice3(3, 5, 7).Interface().([]int) + if len(v) != 2 { + t.Errorf("len(xs.Slice3(3, 5, 7)) = %d", len(v)) + } + if cap(v) != 4 { + t.Errorf("cap(xs.Slice3(3, 5, 7)) = %d", cap(v)) + } + if !DeepEqual(v[0:4], xs[3:7:7]) { + t.Errorf("xs.Slice3(3, 5, 7)[0:4] = %v", v[0:4]) + } + rv := ValueOf(&xs).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 1) }) + shouldPanic(func() { rv.Slice3(1, 1, 11) }) + shouldPanic(func() { rv.Slice3(2, 2, 1) }) + + xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} + v = ValueOf(&xa).Elem().Slice3(2, 5, 6).Interface().([]int) + if len(v) != 3 { + t.Errorf("len(xa.Slice(2, 5, 6)) = %d", len(v)) + } + if cap(v) != 4 { + t.Errorf("cap(xa.Slice(2, 5, 6)) = %d", cap(v)) + } + if !DeepEqual(v[0:4], xa[2:6:6]) { + t.Errorf("xs.Slice(2, 5, 6)[0:4] = %v", v[0:4]) + } + rv = ValueOf(&xa).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 1) }) + shouldPanic(func() { rv.Slice3(1, 1, 11) }) + shouldPanic(func() { rv.Slice3(2, 2, 1) }) + + s := "hello world" + rv = ValueOf(&s).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 3) }) +} + +func TestSetLenCap(t *testing.T) { + xs := []int{1, 2, 3, 4, 5, 6, 7, 8} + xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} + + vs := ValueOf(&xs).Elem() + shouldPanic(func() { vs.SetLen(10) }) + shouldPanic(func() { vs.SetCap(10) }) + shouldPanic(func() { vs.SetLen(-1) }) + shouldPanic(func() { vs.SetCap(-1) }) + shouldPanic(func() { vs.SetCap(6) }) // smaller than len + vs.SetLen(5) + if len(xs) != 5 || cap(xs) != 8 { + t.Errorf("after SetLen(5), len, cap = %d, %d, want 5, 8", len(xs), cap(xs)) + } + vs.SetCap(6) + if len(xs) != 5 || cap(xs) != 6 { + t.Errorf("after SetCap(6), len, cap = %d, %d, want 5, 6", len(xs), cap(xs)) + } + vs.SetCap(5) + if len(xs) != 5 || cap(xs) != 5 { + t.Errorf("after SetCap(5), len, cap = %d, %d, want 5, 5", len(xs), cap(xs)) + } + shouldPanic(func() { vs.SetCap(4) }) // smaller than len + shouldPanic(func() { vs.SetLen(6) }) // bigger than cap + + va := ValueOf(&xa).Elem() + shouldPanic(func() { va.SetLen(8) }) + shouldPanic(func() { va.SetCap(8) }) +} + func TestVariadic(t *testing.T) { var b bytes.Buffer V := ValueOf @@ -2958,17 +3052,28 @@ func TestConvert(t *testing.T) { all[t2] = true canConvert[[2]Type{t1, t2}] = true + // vout1 represents the in value converted to the in type. v1 := tt.in vout1 := v1.Convert(t1) out1 := vout1.Interface() if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) { - t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t1, out1, out1, tt.in.Interface(), tt.in.Interface()) + t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t1, out1, tt.in.Interface()) + } + + // vout2 represents the in value converted to the out type. + vout2 := v1.Convert(t2) + out2 := vout2.Interface() + if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) { + t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out2, tt.out.Interface()) } - vout := v1.Convert(t2) - out := vout.Interface() - if vout.Type() != tt.out.Type() || !DeepEqual(out, tt.out.Interface()) { - t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t2, out, out, tt.out.Interface(), tt.out.Interface()) + // vout3 represents a new value of the out type, set to vout2. This makes + // sure the converted value vout2 is really usable as a regular value. + vout3 := New(t2).Elem() + vout3.Set(vout2) + out3 := vout3.Interface() + if vout3.Type() != tt.out.Type() || !DeepEqual(out3, tt.out.Interface()) { + t.Errorf("Set(ValueOf(%T(%[1]v)).Convert(%s)) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out3, tt.out.Interface()) } if IsRO(v1) { @@ -2977,8 +3082,11 @@ func TestConvert(t *testing.T) { if IsRO(vout1) { t.Errorf("self-conversion output %v is RO, should not be", vout1) } - if IsRO(vout) { - t.Errorf("conversion output %v is RO, should not be", vout) + if IsRO(vout2) { + t.Errorf("conversion output %v is RO, should not be", vout2) + } + if IsRO(vout3) { + t.Errorf("set(conversion output) %v is RO, should not be", vout3) } if !IsRO(MakeRO(v1).Convert(t1)) { t.Errorf("RO self-conversion output %v is not RO, should be", v1) @@ -3405,6 +3513,46 @@ func BenchmarkFieldByName3(b *testing.B) { } } +type S struct { + i1 int64 + i2 int64 +} + +func BenchmarkInterfaceBig(b *testing.B) { + v := ValueOf(S{}) + for i := 0; i < b.N; i++ { + v.Interface() + } + b.StopTimer() +} + +func TestAllocsInterfaceBig(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } + v := ValueOf(S{}) + if allocs := testing.AllocsPerRun(100, func() { v.Interface() }); allocs > 0 { + t.Error("allocs:", allocs) + } +} + +func BenchmarkInterfaceSmall(b *testing.B) { + v := ValueOf(int64(0)) + for i := 0; i < b.N; i++ { + v.Interface() + } +} + +func TestAllocsInterfaceSmall(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } + v := ValueOf(int64(0)) + if allocs := testing.AllocsPerRun(100, func() { v.Interface() }); allocs > 0 { + t.Error("allocs:", allocs) + } +} + // An exhaustive is a mechanism for writing exhaustive or stochastic tests. // The basic usage is: // diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go index 915afed4cda..e3bf3dcac0c 100644 --- a/libgo/go/reflect/deepequal.go +++ b/libgo/go/reflect/deepequal.go @@ -9,18 +9,17 @@ package reflect // During deepValueEqual, must keep track of checks that are // in progress. The comparison algorithm assumes that all // checks in progress are true when it reencounters them. -// Visited are stored in a map indexed by 17 * a1 + a2; +// Visited comparisons are stored in a map indexed by visit. type visit struct { - a1 uintptr - a2 uintptr - typ Type - next *visit + a1 uintptr + a2 uintptr + typ Type } // Tests for deep equality using reflected types. The map argument tracks // comparisons that have already been seen, which allows short circuiting on // recursive types. -func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool) { +func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool { if !v1.IsValid() || !v2.IsValid() { return v1.IsValid() == v2.IsValid() } @@ -29,8 +28,15 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } // if depth > 10 { panic("deepValueEqual") } // for debugging + hard := func(k Kind) bool { + switch k { + case Array, Map, Slice, Struct: + return true + } + return false + } - if v1.CanAddr() && v2.CanAddr() { + if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) { addr1 := v1.UnsafeAddr() addr2 := v2.UnsafeAddr() if addr1 > addr2 { @@ -44,17 +50,14 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } // ... or already seen - h := 17*addr1 + addr2 - seen := visited[h] typ := v1.Type() - for p := seen; p != nil; p = p.next { - if p.a1 == addr1 && p.a2 == addr2 && p.typ == typ { - return true - } + v := visit{addr1, addr2, typ} + if visited[v] { + return true } // Remember for later. - visited[h] = &visit{addr1, addr2, typ, seen} + visited[v] = true } switch v1.Kind() { @@ -75,6 +78,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool if v1.Len() != v2.Len() { return false } + if v1.Pointer() == v2.Pointer() { + return true + } for i := 0; i < v1.Len(); i++ { if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) { return false @@ -102,6 +108,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool if v1.Len() != v2.Len() { return false } + if v1.Pointer() == v2.Pointer() { + return true + } for _, k := range v1.MapKeys() { if !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) { return false @@ -135,5 +144,5 @@ func DeepEqual(a1, a2 interface{}) bool { if v1.Type() != v2.Type() { return false } - return deepValueEqual(v1, v2, make(map[uintptr]*visit), 0) + return deepValueEqual(v1, v2, make(map[visit]bool), 0) } diff --git a/libgo/go/reflect/example_test.go b/libgo/go/reflect/example_test.go index 62455c00ad9..cca28eeece8 100644 --- a/libgo/go/reflect/example_test.go +++ b/libgo/go/reflect/example_test.go @@ -50,3 +50,17 @@ func ExampleMakeFunc() { // 1 0 // 3.14 2.72 } + +func ExampleStructTag() { + type S struct { + F string `species:"gopher" color:"blue"` + } + + s := S{} + st := reflect.TypeOf(s) + field := st.Field(0) + fmt.Println(field.Tag.Get("color"), field.Tag.Get("species")) + + // Output: + // blue gopher +} diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index 3e8085bec65..505c543a082 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -23,7 +23,7 @@ type makeFuncImpl struct { // that wraps the function fn. When called, that new function // does the following: // -// - converts its arguments to a list of Values args. +// - converts its arguments to a slice of Values. // - runs results := fn(args). // - returns the results as a slice of Values, one per formal result. // diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index d084f38eba7..aaac2c3487f 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -191,6 +191,14 @@ type Type interface { uncommon() *uncommonType } +// BUG(rsc): FieldByName and related functions consider struct field names to be equal +// if the names are equal, even if they are unexported names originating +// in different packages. The practical effect of this is that the result of +// t.FieldByName("x") is not well defined if the struct type t contains +// multiple fields named x (embedded from different packages). +// FieldByName may return one of the fields named x or may report that there are none. +// See golang.org/issue/4876 for more details. + /* * These data structures are known to the compiler (../../cmd/gc/reflect.c). * A few are known to ../runtime/type.go to convey to debuggers. @@ -320,6 +328,8 @@ type mapType struct { rtype `reflect:"map"` key *rtype // map key type elem *rtype // map element (value) type + // bucket *rtype // internal bucket structure + // hmap *rtype // internal map header } // ptrType represents a pointer type. @@ -358,7 +368,6 @@ const ( _GC_ARRAY_START _GC_ARRAY_NEXT _GC_CALL - _GC_MAP_PTR _GC_CHAN_PTR _GC_STRING _GC_EFACE @@ -1382,11 +1391,11 @@ func cachePut(k cacheKey, t *rtype) Type { return t } -// garbage collection bytecode program for chan or map. +// garbage collection bytecode program for chan. // See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. -type chanMapGC struct { +type chanGC struct { width uintptr // sizeof(map) - op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR + op uintptr // _GC_CHAN_PTR off uintptr // 0 typ *rtype // map type end uintptr // _GC_END @@ -1500,6 +1509,8 @@ func MapOf(key, elem Type) Type { mt.key = ktyp mt.elem = etyp + // mt.bucket = bucketOf(ktyp, etyp) + // mt.hmap = hMapOf(mt.bucket) mt.uncommonType = nil mt.ptrToThis = nil @@ -1510,6 +1521,118 @@ func MapOf(key, elem Type) Type { return cachePut(ckey, &mt.rtype) } +// Make sure these routines stay in sync with ../../pkg/runtime/hashmap.c! +// These types exist only for GC, so we only fill out GC relevant info. +// Currently, that's just size and the GC program. We also fill in string +// for possible debugging use. +const ( + _BUCKETSIZE = 8 + _MAXKEYSIZE = 128 + _MAXVALSIZE = 128 +) + +func bucketOf(ktyp, etyp *rtype) *rtype { + if ktyp.size > _MAXKEYSIZE { + ktyp = PtrTo(ktyp).(*rtype) + } + if etyp.size > _MAXVALSIZE { + etyp = PtrTo(etyp).(*rtype) + } + ptrsize := unsafe.Sizeof(uintptr(0)) + + gc := make([]uintptr, 1) // first entry is size, filled in at the end + offset := _BUCKETSIZE * unsafe.Sizeof(uint8(0)) // topbits + gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow + offset += ptrsize + + // keys + if ktyp.kind&kindNoPointers == 0 { + gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, ktyp.size) + gc = appendGCProgram(gc, ktyp) + gc = append(gc, _GC_ARRAY_NEXT) + } + offset += _BUCKETSIZE * ktyp.size + + // values + if etyp.kind&kindNoPointers == 0 { + gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, etyp.size) + gc = appendGCProgram(gc, etyp) + gc = append(gc, _GC_ARRAY_NEXT) + } + offset += _BUCKETSIZE * etyp.size + + gc = append(gc, _GC_END) + gc[0] = offset + gc[3] = uintptr(unsafe.Pointer(&gc[0])) // set self pointer + + b := new(rtype) + b.size = offset + // b.gc = unsafe.Pointer(&gc[0]) + s := "bucket(" + *ktyp.string + "," + *etyp.string + ")" + b.string = &s + return b +} + +// Take the GC program for "t" and append it to the GC program "gc". +func appendGCProgram(gc []uintptr, t *rtype) []uintptr { + // p := t.gc + var p unsafe.Pointer + p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size +loop: + for { + var argcnt int + switch *(*uintptr)(p) { + case _GC_END: + // Note: _GC_END not included in append + break loop + case _GC_ARRAY_NEXT: + argcnt = 0 + case _GC_APTR, _GC_STRING, _GC_EFACE, _GC_IFACE: + argcnt = 1 + case _GC_PTR, _GC_CALL, _GC_CHAN_PTR, _GC_SLICE: + argcnt = 2 + case _GC_ARRAY_START, _GC_REGION: + argcnt = 3 + default: + panic("unknown GC program op for " + *t.string + ": " + strconv.FormatUint(*(*uint64)(p), 10)) + } + for i := 0; i < argcnt+1; i++ { + gc = append(gc, *(*uintptr)(p)) + p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) + } + } + return gc +} +func hMapOf(bucket *rtype) *rtype { + ptrsize := unsafe.Sizeof(uintptr(0)) + + // make gc program & compute hmap size + gc := make([]uintptr, 1) // first entry is size, filled in at the end + offset := unsafe.Sizeof(uint(0)) // count + offset += unsafe.Sizeof(uint32(0)) // flags + offset += unsafe.Sizeof(uint32(0)) // hash0 + offset += unsafe.Sizeof(uint8(0)) // B + offset += unsafe.Sizeof(uint8(0)) // keysize + offset += unsafe.Sizeof(uint8(0)) // valuesize + offset = (offset + 1) / 2 * 2 + offset += unsafe.Sizeof(uint16(0)) // bucketsize + offset = (offset + ptrsize - 1) / ptrsize * ptrsize + // gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // buckets + offset += ptrsize + // gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // oldbuckets + offset += ptrsize + offset += ptrsize // nevacuate + gc = append(gc, _GC_END) + gc[0] = offset + + h := new(rtype) + h.size = offset + // h.gc = unsafe.Pointer(&gc[0]) + s := "hmap(" + *bucket.string + ")" + h.string = &s + return h +} + // garbage collection bytecode program for slice of non-zero-length values. // See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. type sliceGC struct { diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index b199f70888c..216ee3f1ca2 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -561,47 +561,6 @@ 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 -} - // funcName returns the name of f, for use in error messages. func funcName(f func([]Value) []Value) string { pc := *(*uintptr)(unsafe.Pointer(&f)) @@ -894,10 +853,7 @@ func (v Value) CanInterface() bool { // Interface returns v's current value as an interface{}. // It is equivalent to: // var i interface{} = (v's underlying value) -// If v is a method obtained by invoking Value.Method -// (as opposed to Type.Method), Interface cannot return an -// interface value, so it panics. -// It also panics if the Value was obtained by accessing +// It panics if the Value was obtained by accessing // unexported struct fields. func (v Value) Interface() (i interface{}) { return valueInterface(v, true) @@ -935,7 +891,8 @@ func valueInterface(v Value, safe bool) interface{} { eface.typ = toType(v.typ).common() eface.word = v.iword() - if v.flag&flagIndir != 0 && v.kind() != Ptr && v.kind() != UnsafePointer { + // Don't need to allocate if v is not addressable or fits in one word. + if v.flag&flagAddr != 0 && v.kind() != Ptr && v.kind() != UnsafePointer { // eface.word is a pointer to the actual data, // which might be changed. We need to return // a pointer to unchanging data, so make a copy. @@ -1411,6 +1368,19 @@ func (v Value) SetLen(n int) { s.Len = n } +// SetCap sets v's capacity to n. +// It panics if v's Kind is not Slice or if n is smaller than the length or +// greater than the capacity of the slice. +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := (*SliceHeader)(v.val) + if n < int(s.Len) || n > int(s.Cap) { + panic("reflect: slice capacity out of range in SetCap") + } + s.Cap = n +} + // 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. @@ -1467,17 +1437,18 @@ func (v Value) SetString(x string) { *(*string)(v.val) = x } -// Slice returns a slice of v. -// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array. -func (v Value) Slice(beg, end int) Value { +// Slice returns v[i:j]. +// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array, +// or if the indexes are out of bounds. +func (v Value) Slice(i, j int) Value { var ( cap int typ *sliceType base unsafe.Pointer ) - switch k := v.kind(); k { + switch kind := v.kind(); kind { default: - panic(&ValueError{"reflect.Value.Slice", k}) + panic(&ValueError{"reflect.Value.Slice", kind}) case Array: if v.flag&flagAddr == 0 { @@ -1496,17 +1467,17 @@ func (v Value) Slice(beg, end int) Value { case String: s := (*StringHeader)(v.val) - if beg < 0 || end < beg || end > s.Len { + if i < 0 || j < i || j > s.Len { panic("reflect.Value.Slice: string slice index out of bounds") } var x string val := (*StringHeader)(unsafe.Pointer(&x)) - val.Data = s.Data + uintptr(beg) - val.Len = end - beg + val.Data = s.Data + uintptr(i) + val.Len = j - i return Value{v.typ, unsafe.Pointer(&x), v.flag} } - if beg < 0 || end < beg || end > cap { + if i < 0 || j < i || j > cap { panic("reflect.Value.Slice: slice index out of bounds") } @@ -1515,9 +1486,56 @@ func (v Value) Slice(beg, end int) Value { // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size() - s.Len = end - beg - s.Cap = cap - beg + s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() + s.Len = j - i + s.Cap = cap - i + + fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift + return Value{typ.common(), unsafe.Pointer(&x), fl} +} + +// Slice3 is the 3-index form of the slice operation: it returns v[i:j:k]. +// It panics if v's Kind is not Array or Slice, or if v is an unaddressable array, +// or if the indexes are out of bounds. +func (v Value) Slice3(i, j, k int) Value { + var ( + cap int + typ *sliceType + base unsafe.Pointer + ) + switch kind := v.kind(); kind { + default: + panic(&ValueError{"reflect.Value.Slice3", kind}) + + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = (*sliceType)(unsafe.Pointer(tt.slice)) + base = v.val + + case Slice: + typ = (*sliceType)(unsafe.Pointer(v.typ)) + s := (*SliceHeader)(v.val) + base = unsafe.Pointer(s.Data) + cap = s.Cap + } + + if i < 0 || j < i || k < j || k > cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + // Declare slice so that the garbage collector + // can see the base pointer in it. + var x []unsafe.Pointer + + // Reinterpret as *SliceHeader to edit. + s := (*SliceHeader)(unsafe.Pointer(&x)) + s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() + s.Len = j - i + s.Cap = k - i fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift return Value{typ.common(), unsafe.Pointer(&x), fl} |