diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-08-03 16:54:30 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-08-03 16:54:30 +0200 |
commit | 28592ee1ea1f5cdffcf85472f9de0285d928cf12 (patch) | |
tree | 32944e18b23f7fe4a0818a694aa2a6dfb1835463 /src/pkg/reflect | |
parent | e836bee4716dc0d4d913537ad3ad1925a7ac32d0 (diff) | |
download | golang-upstream/59.tar.gz |
Imported Upstream version 59upstream/59
Diffstat (limited to 'src/pkg/reflect')
-rw-r--r-- | src/pkg/reflect/all_test.go | 76 | ||||
-rw-r--r-- | src/pkg/reflect/type.go | 134 | ||||
-rw-r--r-- | src/pkg/reflect/value.go | 23 |
3 files changed, 209 insertions, 24 deletions
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index c83a9b75f..34d74b37a 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -127,17 +127,17 @@ var typeTests = []pair{ }, {struct { x struct { - a int8 "hi there" + a int8 `reflect:"hi there"` } }{}, - `struct { a int8 "hi there" }`, + `struct { a int8 "reflect:\"hi there\"" }`, }, {struct { x struct { - a int8 "hi \x00there\t\n\"\\" + a int8 `reflect:"hi \x00there\t\n\"\\"` } }{}, - `struct { a int8 "hi \x00there\t\n\"\\" }`, + `struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`, }, {struct { x struct { @@ -423,7 +423,7 @@ func TestAll(t *testing.T) { // make sure tag strings are not part of element type typ = TypeOf(struct { - d []uint32 "TAG" + d []uint32 `reflect:"TAG"` }{}).Field(0).Type testType(t, 14, typ, "[]uint32") } @@ -1050,6 +1050,12 @@ type Point struct { x, y int } +// This will be index 0. +func (p Point) AnotherMethod(scale int) int { + return -1 +} + +// This will be index 1. func (p Point) Dist(scale int) int { // println("Point.Dist", p.x, p.y, scale) return p.x*p.x*scale + p.y*p.y*scale @@ -1058,26 +1064,52 @@ func (p Point) Dist(scale int) int { func TestMethod(t *testing.T) { // Non-curried method of type. p := Point{3, 4} - i := TypeOf(p).Method(0).Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int() + i := TypeOf(p).Method(1).Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Type Method returned %d; want 250", i) } - i = TypeOf(&p).Method(0).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int() + m, ok := TypeOf(p).MethodByName("Dist") + 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 = 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) } + 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) + } + // Curried method of value. - i = ValueOf(p).Method(0).Call([]Value{ValueOf(10)})[0].Int() + i = ValueOf(p).Method(1).Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value Method returned %d; want 250", i) } + i = ValueOf(p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Value MethodByName returned %d; want 250", i) + } // Curried method of pointer. - i = ValueOf(&p).Method(0).Call([]Value{ValueOf(10)})[0].Int() + i = ValueOf(&p).Method(1).Call([]Value{ValueOf(10)})[0].Int() if i != 250 { - t.Errorf("Value Method returned %d; want 250", i) + t.Errorf("Pointer Value Method returned %d; want 250", i) + } + i = ValueOf(&p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Pointer Value MethodByName returned %d; want 250", i) } // Curried method of interface value. @@ -1094,6 +1126,10 @@ func TestMethod(t *testing.T) { if i != 250 { t.Errorf("Interface Method returned %d; want 250", i) } + i = pv.MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Interface MethodByName returned %d; want 250", i) + } } func TestInterfaceSet(t *testing.T) { @@ -1508,3 +1544,23 @@ func TestVariadic(t *testing.T) { t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world") } } + +var tagGetTests = []struct { + Tag StructTag + Key string + Value string +}{ + {`protobuf:"PB(1,2)"`, `protobuf`, `PB(1,2)`}, + {`protobuf:"PB(1,2)"`, `foo`, ``}, + {`protobuf:"PB(1,2)"`, `rotobuf`, ``}, + {`protobuf:"PB(1,2)" json:"name"`, `json`, `name`}, + {`protobuf:"PB(1,2)" json:"name"`, `protobuf`, `PB(1,2)`}, +} + +func TestTagGet(t *testing.T) { + for _, tt := range tagGetTests { + if v := tt.Tag.Get(tt.Key); v != tt.Value { + t.Errorf("StructTag(%#q).Get(%#q) = %#q, want %#q", tt.Tag, tt.Key, v, tt.Value) + } + } +} diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index aef6370db..a120da732 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -47,6 +47,16 @@ type Type interface { // method signature, without a receiver, and the Func field is nil. Method(int) Method + // MethodByName returns the method with that name in the type's + // method set and a boolean indicating if the method was found. + // + // For a non-interface type T or *T, the returned Method's Type and Func + // fields describe a function whose first argument is the receiver. + // + // For an interface type, the returned Method's Type field gives the + // method signature, without a receiver, and the Func field is nil. + MethodByName(string) (Method, bool) + // NumMethod returns the number of methods in the type's method set. NumMethod() int @@ -264,7 +274,7 @@ const ( // arrayType represents a fixed array type. type arrayType struct { - commonType "array" + commonType `reflect:"array"` elem *runtime.Type slice *runtime.Type len uintptr @@ -272,14 +282,14 @@ type arrayType struct { // chanType represents a channel type. type chanType struct { - commonType "chan" + commonType `reflect:"chan"` elem *runtime.Type dir uintptr } // funcType represents a function type. type funcType struct { - commonType "func" + commonType `reflect:"func"` dotdotdot bool in []*runtime.Type out []*runtime.Type @@ -294,26 +304,26 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { - commonType "interface" + commonType `reflect:"interface"` methods []imethod } // mapType represents a map type. type mapType struct { - commonType "map" + commonType `reflect:"map"` key *runtime.Type elem *runtime.Type } // ptrType represents a pointer type. type ptrType struct { - commonType "ptr" + commonType `reflect:"ptr"` elem *runtime.Type } // sliceType represents a slice type. type sliceType struct { - commonType "slice" + commonType `reflect:"slice"` elem *runtime.Type } @@ -328,7 +338,7 @@ type structField struct { // structType represents a struct type. type structType struct { - commonType "struct" + commonType `reflect:"struct"` fields []structField } @@ -344,6 +354,7 @@ type Method struct { Name string Type Type Func Value + Index int } // High bit says whether type has @@ -451,6 +462,7 @@ func (t *uncommonType) Method(i int) (m Method) { m.Type = toType(p.typ) fn := p.tfn m.Func = valueFromIword(flag, m.Type, iword(fn)) + m.Index = i return } @@ -461,6 +473,20 @@ func (t *uncommonType) NumMethod() int { return len(t.methods) } +func (t *uncommonType) MethodByName(name string) (m Method, ok bool) { + if t == nil { + return + } + var p *method + for i := range t.methods { + p = &t.methods[i] + if p.name != nil && *p.name == name { + return t.Method(i), true + } + } + return +} + // TODO(rsc): 6g supplies these, but they are not // as efficient as they could be: they have commonType // as the receiver instead of *commonType. @@ -480,6 +506,14 @@ func (t *commonType) Method(i int) (m Method) { return t.uncommonType.Method(i) } +func (t *commonType) MethodByName(name string) (m Method, ok bool) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.MethodByName(name) + } + return t.uncommonType.MethodByName(name) +} + func (t *commonType) PkgPath() string { return t.uncommonType.PkgPath() } @@ -636,22 +670,98 @@ func (t *interfaceType) Method(i int) (m Method) { m.PkgPath = *p.pkgPath } m.Type = toType(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) } +// MethodByName method with the given name in the type's method set. +func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { + if t == nil { + return + } + var p *imethod + for i := range t.methods { + p = &t.methods[i] + if *p.name == name { + return t.Method(i), true + } + } + return +} + type StructField struct { PkgPath string // empty for uppercase Name Name string Type Type - Tag string + Tag StructTag Offset uintptr Index []int Anonymous bool } +// A StructTag is the tag string in a struct field. +// +// By convention, tag strings are a concatenation of +// optionally space-separated key:"value" pairs. +// Each key is a non-empty string consisting of non-control +// characters other than space (U+0020 ' '), quote (U+0022 '"'), +// and colon (U+003A ':'). Each value is quoted using U+0022 '"' +// characters and Go string literal syntax. +type StructTag string + +// Get returns the value associated with key in the tag string. +// If there is no such key in the tag, Get returns the empty string. +// If the tag does not have the conventional format, the value +// returned by Get is unspecified, +func (tag StructTag) Get(key string) string { + for tag != "" { + // skip leading space + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // scan to colon. + // a space or a quote is a syntax error + i = 0 + for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + i++ + } + if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // scan quoted string to find value + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if key == name { + value, _ := strconv.Unquote(qvalue) + return value + } + } + return "" +} + // Field returns the i'th struct field. func (t *structType) Field(i int) (f StructField) { if i < 0 || i >= len(t.fields) { @@ -673,7 +783,7 @@ func (t *structType) Field(i int) (f StructField) { f.PkgPath = *p.pkgPath } if p.tag != nil { - f.Tag = *p.tag + f.Tag = StructTag(*p.tag) } f.Offset = p.offset f.Index = []int{i} @@ -827,7 +937,7 @@ func (t *commonType) runtimeType() *runtime.Type { i runtime.Type ct commonType } - return (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - uintptr(unsafe.Offsetof(rt.ct)))) + return (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - unsafe.Offsetof(rt.ct))) } // PtrTo returns the pointer type with element t. @@ -888,7 +998,7 @@ func PtrTo(t Type) Type { p.uncommonType = nil p.ptrToThis = nil - p.elem = (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - uintptr(unsafe.Offsetof(rt.ptrType)))) + p.elem = (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - unsafe.Offsetof(rt.ptrType))) ptrMap.m[ct] = p ptrMap.Unlock() diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 3abe13e04..bfeb3267c 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -11,7 +11,7 @@ import ( "unsafe" ) -const ptrSize = uintptr(unsafe.Sizeof((*byte)(nil))) +const ptrSize = unsafe.Sizeof((*byte)(nil)) const cannotSet = "cannot set value obtained from unexported struct field" // TODO: This will have to go away when @@ -933,7 +933,7 @@ func (v Value) Kind() Kind { } // Len returns v's length. -// It panics if v's Kind is not Array, Chan, Map, or Slice. +// It panics if v's Kind is not Array, Chan, Map, Slice, or String. func (v Value) Len() int { iv := v.internal() switch iv.kind { @@ -945,6 +945,8 @@ func (v Value) Len() int { return int(maplen(iv.word)) case Slice: return (*SliceHeader)(iv.addr).Len + case String: + return (*StringHeader)(iv.addr).Len } panic(&ValueError{"reflect.Value.Len", iv.kind}) } @@ -1023,6 +1025,23 @@ func (v Value) Method(i int) Value { return Value{v.Internal, i + 1} } +// MethodByName returns a function value corresponding to the method +// of v with the given name. +// The arguments to a Call on the returned function should not include +// a receiver; the returned function will always use v as the receiver. +// It returns the zero Value if no method was found. +func (v Value) MethodByName(name string) Value { + iv := v.internal() + if iv.kind == Invalid { + panic(&ValueError{"reflect.Value.MethodByName", Invalid}) + } + m, ok := iv.typ.MethodByName(name) + if ok { + return Value{v.Internal, m.Index + 1} + } + return Value{} +} + // NumField returns the number of fields in the struct v. // It panics if v's Kind is not Struct. func (v Value) NumField() int { |