diff options
Diffstat (limited to 'src/reflect')
-rw-r--r-- | src/reflect/all_test.go | 214 | ||||
-rw-r--r-- | src/reflect/deepequal.go | 12 | ||||
-rw-r--r-- | src/reflect/type.go | 82 | ||||
-rw-r--r-- | src/reflect/value.go | 33 |
4 files changed, 281 insertions, 60 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index abdfe41908..be15362aae 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -2405,8 +2405,14 @@ func TestVariadicMethodValue(t *testing.T) { points := []Point{{20, 21}, {22, 23}, {24, 25}} want := int64(p.TotalDist(points[0], points[1], points[2])) + // Variadic method of type. + tfunc := TypeOf((func(Point, ...Point) int)(nil)) + if tt := TypeOf(p).Method(4).Type; tt != tfunc { + t.Errorf("Variadic Method Type from TypeOf is %s; want %s", tt, tfunc) + } + // Curried method of value. - tfunc := TypeOf((func(...Point) int)(nil)) + tfunc = TypeOf((func(...Point) int)(nil)) v := ValueOf(p).Method(4) if tt := v.Type(); tt != tfunc { t.Errorf("Variadic Method Type is %s; want %s", tt, tfunc) @@ -2989,6 +2995,14 @@ func TestUnexportedMethods(t *testing.T) { if got := typ.NumMethod(); got != 0 { t.Errorf("NumMethod=%d, want 0 satisfied methods", got) } + + var i unexpI + if got := TypeOf(&i).Elem().NumMethod(); got != 0 { + t.Errorf("NumMethod=%d, want 0 satisfied methods", got) + } + if got := ValueOf(&i).Elem().NumMethod(); got != 0 { + t.Errorf("NumMethod=%d, want 0 satisfied methods", got) + } } type InnerInt struct { @@ -3642,21 +3656,21 @@ func TestCallPanic(t *testing.T) { v := ValueOf(T{i, i, i, i, T2{i, i}, i, i, T2{i, i}}) badCall(func() { call(v.Field(0).Method(0)) }) // .t0.W badCall(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W - badCall(func() { call(v.Field(0).Method(1)) }) // .t0.w + badMethod(func() { call(v.Field(0).Method(1)) }) // .t0.w badMethod(func() { call(v.Field(0).Elem().Method(2)) }) // .t0.w ok(func() { call(v.Field(1).Method(0)) }) // .T1.Y ok(func() { call(v.Field(1).Elem().Method(0)) }) // .T1.Y - badCall(func() { call(v.Field(1).Method(1)) }) // .T1.y + badMethod(func() { call(v.Field(1).Method(1)) }) // .T1.y badMethod(func() { call(v.Field(1).Elem().Method(2)) }) // .T1.y ok(func() { call(v.Field(2).Method(0)) }) // .NamedT0.W ok(func() { call(v.Field(2).Elem().Method(0)) }) // .NamedT0.W - badCall(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w + badMethod(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w badMethod(func() { call(v.Field(2).Elem().Method(2)) }) // .NamedT0.w ok(func() { call(v.Field(3).Method(0)) }) // .NamedT1.Y ok(func() { call(v.Field(3).Elem().Method(0)) }) // .NamedT1.Y - badCall(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y + badMethod(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y badMethod(func() { call(v.Field(3).Elem().Method(3)) }) // .NamedT1.y ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y @@ -3666,7 +3680,7 @@ func TestCallPanic(t *testing.T) { badCall(func() { call(v.Field(5).Method(0)) }) // .namedT0.W badCall(func() { call(v.Field(5).Elem().Method(0)) }) // .namedT0.W - badCall(func() { call(v.Field(5).Method(1)) }) // .namedT0.w + badMethod(func() { call(v.Field(5).Method(1)) }) // .namedT0.w badMethod(func() { call(v.Field(5).Elem().Method(2)) }) // .namedT0.w badCall(func() { call(v.Field(6).Method(0)) }) // .namedT1.Y @@ -4259,24 +4273,6 @@ var gFloat32 float32 func TestConvertNaNs(t *testing.T) { const snan uint32 = 0x7f800001 - - // Test to see if a store followed by a load of a signaling NaN - // maintains the signaling bit. The only platform known to fail - // this test is 386,GO386=387. The real test below will always fail - // if the platform can't even store+load a float without mucking - // with the bits. - gFloat32 = math.Float32frombits(snan) - runtime.Gosched() // make sure we don't optimize the store/load away - r := math.Float32bits(gFloat32) - if r != snan { - // This should only happen on 386,GO386=387. We have no way to - // test for 387, so we just make sure we're at least on 386. - if runtime.GOARCH != "386" { - t.Errorf("store/load of sNaN not faithful") - } - t.Skip("skipping test, float store+load not faithful") - } - type myFloat32 float32 x := V(myFloat32(math.Float32frombits(snan))) y := x.Convert(TypeOf(float32(0))) @@ -7177,6 +7173,176 @@ func TestMapIterDelete1(t *testing.T) { } } +func TestStructTagLookup(t *testing.T) { + var tests = []struct { + tag StructTag + key string + expectedValue string + expectedOK bool + }{ + { + tag: `json:"json_value_1"`, + key: "json", + expectedValue: "json_value_1", + expectedOK: true, + }, + { + tag: `json:"json_value_2" xml:"xml_value_2"`, + key: "json", + expectedValue: "json_value_2", + expectedOK: true, + }, + { + tag: `json:"json_value_3" xml:"xml_value_3"`, + key: "xml", + expectedValue: "xml_value_3", + expectedOK: true, + }, + { + tag: `bson json:"shared_value_4"`, + key: "json", + expectedValue: "shared_value_4", + expectedOK: true, + }, + { + tag: `bson json:"shared_value_5"`, + key: "bson", + expectedValue: "shared_value_5", + expectedOK: true, + }, + { + tag: `json bson xml form:"field_1,omitempty" other:"value_1"`, + key: "xml", + expectedValue: "field_1,omitempty", + expectedOK: true, + }, + { + tag: `json bson xml form:"field_2,omitempty" other:"value_2"`, + key: "form", + expectedValue: "field_2,omitempty", + expectedOK: true, + }, + { + tag: `json bson xml form:"field_3,omitempty" other:"value_3"`, + key: "other", + expectedValue: "value_3", + expectedOK: true, + }, + { + tag: `json bson xml form:"field_4" other:"value_4"`, + key: "json", + expectedValue: "field_4", + expectedOK: true, + }, + { + tag: `json bson xml form:"field_5" other:"value_5"`, + key: "non_existing", + expectedValue: "", + expectedOK: false, + }, + { + tag: `json "json_6"`, + key: "json", + expectedValue: "", + expectedOK: false, + }, + { + tag: `json:"json_7" bson "bson_7"`, + key: "json", + expectedValue: "json_7", + expectedOK: true, + }, + { + tag: `json:"json_8" xml "xml_8"`, + key: "xml", + expectedValue: "", + expectedOK: false, + }, + { + tag: `json bson xml form "form_9" other:"value_9"`, + key: "bson", + expectedValue: "", + expectedOK: false, + }, + { + tag: `json bson xml form "form_10" other:"value_10"`, + key: "other", + expectedValue: "", + expectedOK: false, + }, + { + tag: `json bson xml form:"form_11" other "value_11"`, + key: "json", + expectedValue: "form_11", + expectedOK: true, + }, + { + tag: `tag1`, + key: "tag1", + expectedValue: "", + expectedOK: false, + }, + { + tag: `tag2 :"hello_2"`, + key: "tag2", + expectedValue: "", + expectedOK: false, + }, + { + tag: `tag3: "hello_3"`, + key: "tag3", + expectedValue: "", + expectedOK: false, + }, + { + tag: "json\x7fbson: \"hello_4\"", + key: "json", + expectedValue: "", + expectedOK: false, + }, + { + tag: "json\x7fbson: \"hello_5\"", + key: "bson", + expectedValue: "", + expectedOK: false, + }, + { + tag: "json bson:\x7f\"hello_6\"", + key: "json", + expectedValue: "", + expectedOK: false, + }, + { + tag: "json bson:\x7f\"hello_7\"", + key: "bson", + expectedValue: "", + expectedOK: false, + }, + { + tag: "json\x09bson:\"hello_8\"", + key: "json", + expectedValue: "", + expectedOK: false, + }, + { + tag: "a\x7fb json:\"val\"", + key: "json", + expectedValue: "", + expectedOK: false, + }, + } + + for _, test := range tests { + v, ok := test.tag.Lookup(test.key) + if v != test.expectedValue { + t.Errorf("struct tag lookup failed, got %s, want %s", v, test.expectedValue) + } + if ok != test.expectedOK { + t.Errorf("struct tag lookup failed, got %t, want %t", ok, test.expectedOK) + } + } +} + // iterateToString returns the set of elements // returned by an iterator in readable form. func iterateToString(it *MapIter) string { diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go index be66464129..d951d8d999 100644 --- a/src/reflect/deepequal.go +++ b/src/reflect/deepequal.go @@ -35,7 +35,17 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { // and it's safe and valid to get Value's internal pointer. hard := func(v1, v2 Value) bool { switch v1.Kind() { - case Map, Slice, Ptr, Interface: + case Ptr: + if v1.typ.ptrdata == 0 { + // go:notinheap pointers can't be cyclic. + // At least, all of our current uses of go:notinheap have + // that property. The runtime ones aren't cyclic (and we don't use + // DeepEqual on them anyway), and the cgo-generated ones are + // all empty structs. + return false + } + fallthrough + case Map, Slice, Interface: // Nil pointers cannot be cyclic. Avoid putting them in the visited map. return !v1.IsNil() && !v2.IsNil() } diff --git a/src/reflect/type.go b/src/reflect/type.go index 44c96fea82..0b34ca0c94 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -386,10 +386,14 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { rtype - pkgPath name // import path - methods []imethod // sorted by hash + pkgPath name // import path + expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded. } +// methods returns t's full method set, both exported and non-exported. +func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] } +func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 } + // mapType represents a map type. type mapType struct { rtype @@ -1049,25 +1053,22 @@ func (d ChanDir) String() string { // Method returns the i'th method in the type's method set. func (t *interfaceType) Method(i int) (m Method) { - if i < 0 || i >= len(t.methods) { - return + if i < 0 || i >= len(t.expMethods) { + panic("reflect: Method index out of range") } - p := &t.methods[i] + p := &t.expMethods[i] pname := t.nameOff(p.name) m.Name = pname.name() if !pname.isExported() { - m.PkgPath = pname.pkgPath() - if m.PkgPath == "" { - m.PkgPath = t.pkgPath.name() - } + panic("reflect: unexported method: " + pname.name()) } m.Type = toType(t.typeOff(p.typ)) m.Index = i return } -// NumMethod returns the number of interface methods in the type's method set. -func (t *interfaceType) NumMethod() int { return len(t.methods) } +// NumMethod returns the number of exported interface methods in the type's method set. +func (t *interfaceType) NumMethod() int { return len(t.expMethods) } // MethodByName method with the given name in the type's method set. func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { @@ -1075,8 +1076,8 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { return } var p *imethod - for i := range t.methods { - p = &t.methods[i] + for i := range t.expMethods { + p = &t.expMethods[i] if t.nameOff(p.name).name() == name { return t.Method(i), true } @@ -1130,6 +1131,9 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) { // When modifying this code, also update the validateStructTag code // in cmd/vet/structtag.go. + // keyFound indicates that such key on the left side has already been found. + var keyFound bool + for tag != "" { // Skip leading space. i := 0 @@ -1149,11 +1153,29 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) { for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { i++ } - if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + if i == 0 || i+1 >= len(tag) || tag[i] < ' ' || tag[i] == 0x7f { break } name := string(tag[:i]) - tag = tag[i+1:] + tag = tag[i:] + + // If we found a space char here - assume that we have a tag with + // multiple keys. + if tag[0] == ' ' { + if name == key { + keyFound = true + } + continue + } + + // Spaces were filtered above so we assume that here we have + // only valid tag value started with `:"`. + if tag[0] != ':' || tag[1] != '"' { + break + } + + // Remove the colon leaving tag at the start of the quoted string. + tag = tag[1:] // Scan quoted string to find value. i = 1 @@ -1169,7 +1191,7 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) { qvalue := string(tag[:i+1]) tag = tag[i+1:] - if key == name { + if key == name || keyFound { value, err := strconv.Unquote(qvalue) if err != nil { break @@ -1464,9 +1486,10 @@ func implements(T, V *rtype) bool { return false } t := (*interfaceType)(unsafe.Pointer(T)) - if len(t.methods) == 0 { + if t.isEmpty() { return true } + tmethods := t.methods() // The same algorithm applies in both cases, but the // method tables for an interface type and a concrete type @@ -1483,10 +1506,11 @@ func implements(T, V *rtype) bool { if V.Kind() == Interface { v := (*interfaceType)(unsafe.Pointer(V)) i := 0 - for j := 0; j < len(v.methods); j++ { - tm := &t.methods[i] + vmethods := v.methods() + for j := 0; j < len(vmethods); j++ { + tm := &tmethods[i] tmName := t.nameOff(tm.name) - vm := &v.methods[j] + vm := &vmethods[j] vmName := V.nameOff(vm.name) if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if !tmName.isExported() { @@ -1502,7 +1526,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(t.methods) { + if i++; i >= len(tmethods) { return true } } @@ -1517,7 +1541,7 @@ func implements(T, V *rtype) bool { i := 0 vmethods := v.methods() for j := 0; j < int(v.mcount); j++ { - tm := &t.methods[i] + tm := &tmethods[i] tmName := t.nameOff(tm.name) vm := vmethods[j] vmName := V.nameOff(vm.name) @@ -1535,7 +1559,7 @@ func implements(T, V *rtype) bool { continue } } - if i++; i >= len(t.methods) { + if i++; i >= len(tmethods) { return true } } @@ -1637,7 +1661,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { case Interface: t := (*interfaceType)(unsafe.Pointer(T)) v := (*interfaceType)(unsafe.Pointer(V)) - if len(t.methods) == 0 && len(v.methods) == 0 { + if t.isEmpty() && v.isEmpty() { return true } // Might have the same methods but still @@ -2421,7 +2445,7 @@ func StructOf(fields []StructField) Type { switch f.typ.Kind() { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) - for im, m := range ift.methods { + for im, m := range ift.methods() { if ift.nameOff(m.name).pkgPath() != "" { // TODO(sbinet). Issue 15924. panic("reflect: embedded interface with unexported method(s) not implemented") @@ -3128,3 +3152,11 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { } } } + +func isEmptyIface(rt *rtype) bool { + if rt.Kind() != Interface { + return false + } + tt := (*interfaceType)(unsafe.Pointer(rt)) + return len(tt.methods()) == 0 +} diff --git a/src/reflect/value.go b/src/reflect/value.go index a14131e1f8..24eab6a2c6 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -90,6 +90,7 @@ func (f flag) ro() flag { // pointer returns the underlying pointer represented by v. // v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer +// if v.Kind() == Ptr, the base type must not be go:notinheap. func (v Value) pointer() unsafe.Pointer { if v.typ.size != ptrSize || !v.typ.pointers() { panic("can't call pointer on a non-pointer Value") @@ -635,10 +636,11 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *fu i := methodIndex if v.typ.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(v.typ)) - if uint(i) >= uint(len(tt.methods)) { + ttmethods := tt.methods() + if uint(i) >= uint(len(ttmethods)) { panic("reflect: internal error: invalid method index") } - m := &tt.methods[i] + m := &ttmethods[i] if !tt.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } @@ -812,7 +814,7 @@ func (v Value) Elem() Value { switch k { case Interface: var eface interface{} - if v.typ.NumMethod() == 0 { + if isEmptyIface(v.typ) { eface = *(*interface{})(v.ptr) } else { eface = (interface{})(*(*interface { @@ -1033,7 +1035,7 @@ func valueInterface(v Value, safe bool) interface{} { // Special case: return the element inside the interface. // Empty interface has one layout, all interfaces with // methods have a second layout. - if v.NumMethod() == 0 { + if isEmptyIface(v.typ) { return *(*interface{})(v.ptr) } return *(*interface { @@ -1452,7 +1454,16 @@ func (v Value) Pointer() uintptr { // TODO: deprecate k := v.kind() switch k { - case Chan, Map, Ptr, UnsafePointer: + case Ptr: + if v.typ.ptrdata == 0 { + // Handle pointers to go:notinheap types directly, + // so we never materialize such pointers as an + // unsafe.Pointer. (Such pointers are always indirect.) + // See issue 42076. + return *(*uintptr)(v.ptr) + } + fallthrough + case Chan, Map, UnsafePointer: return uintptr(v.pointer()) case Func: if v.flag&flagMethod != 0 { @@ -1908,10 +1919,11 @@ func (v Value) Type() Type { if v.typ.Kind() == Interface { // Method on interface. tt := (*interfaceType)(unsafe.Pointer(v.typ)) - if uint(i) >= uint(len(tt.methods)) { + ttmethods := tt.methods() + if uint(i) >= uint(len(ttmethods)) { panic("reflect: internal error: invalid method index") } - m := &tt.methods[i] + m := &ttmethods[i] return v.typ.typeOff(m.typ) } // Method on concrete type. @@ -2429,7 +2441,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, nil, flag(Interface)} } x := valueInterface(v, false) - if dst.NumMethod() == 0 { + if isEmptyIface(dst) { *(*interface{})(target) = x } else { ifaceE2I(dst, x, target) @@ -2718,10 +2730,11 @@ func cvtDirect(v Value, typ Type) Value { func cvtT2I(v Value, typ Type) Value { target := unsafe_New(typ.common()) x := valueInterface(v, false) - if typ.NumMethod() == 0 { + rt := typ.(*rtype) + if isEmptyIface(rt) { *(*interface{})(target) = x } else { - ifaceE2I(typ.(*rtype), x, target) + ifaceE2I(rt, x, target) } return Value{typ.common(), target, v.flag.ro() | flagIndir | flag(Interface)} } |