aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/reflect
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2011-10-18 14:06:11 +1100
committerAndrew Gerrand <adg@golang.org>2011-10-18 14:06:11 +1100
commit0584eb2e7779d5bf699702d06acb686cd08bddd2 (patch)
tree291e3db413833a248a5afbb4cb023e23ca78593e /src/pkg/reflect
parentadfa87c5d754252f2bf428b38560de3d630dbe9d (diff)
downloadgo-release.r58.2.tar.xz
[release-branch.r58] reflect: disallow Interface method on Value obtained via unexported namerelease.r58.2
Also remove exp/datafmt that depends on the broken reflect behavior. ««« CL 5267049 / eeca0d4a91a3 reflect: disallow Interface method on Value obtained via unexported name Had been allowing it for use by fmt, but it is too hard to lock down. Fix other packages not to depend on it. R=r, r CC=golang-dev https://golang.org/cl/5266054 »»» R=golang-dev, dsymonds CC=golang-dev https://golang.org/cl/5297042
Diffstat (limited to 'src/pkg/reflect')
-rw-r--r--src/pkg/reflect/all_test.go115
-rw-r--r--src/pkg/reflect/deepequal.go2
-rw-r--r--src/pkg/reflect/value.go31
3 files changed, 106 insertions, 42 deletions
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index c83a9b75f6..7a72ef8518 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -855,13 +855,13 @@ func TestIsNil(t *testing.T) {
func TestInterfaceExtraction(t *testing.T) {
var s struct {
- w io.Writer
+ W io.Writer
}
- s.w = os.Stdout
+ s.W = os.Stdout
v := Indirect(ValueOf(&s)).Field(0).Interface()
- if v != s.w.(interface{}) {
- t.Error("Interface() on interface: ", v, s.w)
+ if v != s.W.(interface{}) {
+ t.Error("Interface() on interface: ", v, s.W)
}
}
@@ -1156,18 +1156,18 @@ type D2 struct {
}
type S0 struct {
- a, b, c int
+ A, B, C int
D1
D2
}
type S1 struct {
- b int
+ B int
S0
}
type S2 struct {
- a int
+ A int
*S1
}
@@ -1182,36 +1182,36 @@ type S1y struct {
type S3 struct {
S1x
S2
- d, e int
+ D, E int
*S1y
}
type S4 struct {
*S4
- a int
+ A int
}
var fieldTests = []FTest{
{struct{}{}, "", nil, 0},
- {struct{}{}, "foo", nil, 0},
- {S0{a: 'a'}, "a", []int{0}, 'a'},
- {S0{}, "d", nil, 0},
- {S1{S0: S0{a: 'a'}}, "a", []int{1, 0}, 'a'},
- {S1{b: 'b'}, "b", []int{0}, 'b'},
+ {struct{}{}, "Foo", nil, 0},
+ {S0{A: 'a'}, "A", []int{0}, 'a'},
+ {S0{}, "D", nil, 0},
+ {S1{S0: S0{A: 'a'}}, "A", []int{1, 0}, 'a'},
+ {S1{B: 'b'}, "B", []int{0}, 'b'},
{S1{}, "S0", []int{1}, 0},
- {S1{S0: S0{c: 'c'}}, "c", []int{1, 2}, 'c'},
- {S2{a: 'a'}, "a", []int{0}, 'a'},
+ {S1{S0: S0{C: 'c'}}, "C", []int{1, 2}, 'c'},
+ {S2{A: 'a'}, "A", []int{0}, 'a'},
{S2{}, "S1", []int{1}, 0},
- {S2{S1: &S1{b: 'b'}}, "b", []int{1, 0}, 'b'},
- {S2{S1: &S1{S0: S0{c: 'c'}}}, "c", []int{1, 1, 2}, 'c'},
- {S2{}, "d", nil, 0},
+ {S2{S1: &S1{B: 'b'}}, "B", []int{1, 0}, 'b'},
+ {S2{S1: &S1{S0: S0{C: 'c'}}}, "C", []int{1, 1, 2}, 'c'},
+ {S2{}, "D", nil, 0},
{S3{}, "S1", nil, 0},
- {S3{S2: S2{a: 'a'}}, "a", []int{1, 0}, 'a'},
- {S3{}, "b", nil, 0},
- {S3{d: 'd'}, "d", []int{2}, 0},
- {S3{e: 'e'}, "e", []int{3}, 'e'},
- {S4{a: 'a'}, "a", []int{1}, 'a'},
- {S4{}, "b", nil, 0},
+ {S3{S2: S2{A: 'a'}}, "A", []int{1, 0}, 'a'},
+ {S3{}, "B", nil, 0},
+ {S3{D: 'd'}, "D", []int{2}, 0},
+ {S3{E: 'e'}, "E", []int{3}, 'e'},
+ {S4{A: 'a'}, "A", []int{1}, 'a'},
+ {S4{}, "B", nil, 0},
}
func TestFieldByIndex(t *testing.T) {
@@ -1508,3 +1508,68 @@ func TestVariadic(t *testing.T) {
t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world")
}
}
+
+type Private struct {
+ x int
+ y **int
+}
+
+func (p *Private) m() {
+}
+
+type Public struct {
+ X int
+ Y **int
+}
+
+func (p *Public) M() {
+}
+
+func TestUnexported(t *testing.T) {
+ var pub Public
+ v := ValueOf(&pub)
+ isValid(v.Elem().Field(0))
+ isValid(v.Elem().Field(1))
+ isValid(v.Elem().FieldByName("X"))
+ isValid(v.Elem().FieldByName("Y"))
+ isValid(v.Type().Method(0).Func)
+ isNonNil(v.Elem().Field(0).Interface())
+ isNonNil(v.Elem().Field(1).Interface())
+ isNonNil(v.Elem().FieldByName("X").Interface())
+ isNonNil(v.Elem().FieldByName("Y").Interface())
+ isNonNil(v.Type().Method(0).Func.Interface())
+
+ var priv Private
+ v = ValueOf(&priv)
+ isValid(v.Elem().Field(0))
+ isValid(v.Elem().Field(1))
+ isValid(v.Elem().FieldByName("x"))
+ isValid(v.Elem().FieldByName("y"))
+ isValid(v.Type().Method(0).Func)
+ shouldPanic(func() { v.Elem().Field(0).Interface() })
+ shouldPanic(func() { v.Elem().Field(1).Interface() })
+ shouldPanic(func() { v.Elem().FieldByName("x").Interface() })
+ shouldPanic(func() { v.Elem().FieldByName("y").Interface() })
+ shouldPanic(func() { v.Type().Method(0).Func.Interface() })
+}
+
+func shouldPanic(f func()) {
+ defer func() {
+ if recover() == nil {
+ panic("did not panic")
+ }
+ }()
+ f()
+}
+
+func isNonNil(x interface{}) {
+ if x == nil {
+ panic("nil interface")
+ }
+}
+
+func isValid(v Value) {
+ if !v.IsValid() {
+ panic("zero Value")
+ }
+}
diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go
index a483135b01..63c28fe202 100644
--- a/src/pkg/reflect/deepequal.go
+++ b/src/pkg/reflect/deepequal.go
@@ -104,7 +104,7 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
return true
default:
// Normal equality suffices
- return v1.Interface() == v2.Interface()
+ return valueInterface(v1, false) == valueInterface(v2, false)
}
panic("Not reached")
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 3abe13e04d..9ece67f5fc 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -841,14 +841,7 @@ func (v Value) CanInterface() bool {
if iv.kind == Invalid {
panic(&ValueError{"reflect.Value.CanInterface", iv.kind})
}
- // TODO(rsc): Check flagRO too. Decide what to do about asking for
- // interface for a value obtained via an unexported field.
- // If the field were of a known type, say chan int or *sync.Mutex,
- // the caller could interfere with the data after getting the
- // interface. But fmt.Print depends on being able to look.
- // Now that reflect is more efficient the special cases in fmt
- // might be less important.
- return v.InternalMethod == 0
+ return v.InternalMethod == 0 && iv.flag&flagRO == 0
}
// Interface returns v's value as an interface{}.
@@ -856,19 +849,25 @@ func (v Value) CanInterface() bool {
// (as opposed to Type.Method), Interface cannot return an
// interface value, so it panics.
func (v Value) Interface() interface{} {
- return v.internal().Interface()
+ return valueInterface(v, true)
}
-func (iv internalValue) Interface() interface{} {
+func valueInterface(v Value, safe bool) interface{} {
+ iv := v.internal()
+ return iv.valueInterface(safe)
+}
+
+func (iv internalValue) valueInterface(safe bool) interface{} {
if iv.method {
panic("reflect.Value.Interface: cannot create interface value for method with bound receiver")
}
- /*
- if v.flag()&noExport != 0 {
- panic("reflect.Value.Interface: cannot return value obtained from unexported struct field")
- }
- */
+ if safe && iv.flag&flagRO != 0 {
+ // Do not allow access to unexported values via Interface,
+ // because they might be pointers that should not be
+ // writable or methods or function that should not be callable.
+ panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
+ }
if iv.kind == Interface {
// Special case: return the element inside the interface.
// Won't recurse further because an interface cannot contain an interface.
@@ -1669,7 +1668,7 @@ func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv interna
if addr == nil {
addr = unsafe.Pointer(new(interface{}))
}
- x := iv.Interface()
+ x := iv.valueInterface(false)
if dst.NumMethod() == 0 {
*(*interface{})(addr) = x
} else {