summaryrefslogtreecommitdiff
path: root/src/pkg/reflect
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/reflect')
-rw-r--r--src/pkg/reflect/all_test.go76
-rw-r--r--src/pkg/reflect/type.go134
-rw-r--r--src/pkg/reflect/value.go23
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 {