Commit 0584eb2e authored by Andrew Gerrand's avatar Andrew Gerrand

[release-branch.r58] reflect: disallow Interface method on Value obtained via unexported name

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
parent adfa87c5
......@@ -76,7 +76,6 @@ DIRS=\
encoding/hex\
encoding/pem\
exec\
exp/datafmt\
exp/eval\
exp/gui\
exp/gui/x11\
......
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../../../Make.inc
TARG=exp/datafmt
GOFILES=\
datafmt.go\
parser.go\
include ../../../Make.pkg
This diff is collapsed.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package datafmt
import (
"fmt"
"testing"
"go/token"
)
var fset = token.NewFileSet()
func parse(t *testing.T, form string, fmap FormatterMap) Format {
f, err := Parse(fset, "", []byte(form), fmap)
if err != nil {
t.Errorf("Parse(%s): %v", form, err)
return nil
}
return f
}
func verify(t *testing.T, f Format, expected string, args ...interface{}) {
if f == nil {
return // allow other tests to run
}
result := f.Sprint(args...)
if result != expected {
t.Errorf(
"result : `%s`\nexpected: `%s`\n\n",
result, expected)
}
}
func formatter(s *State, value interface{}, rule_name string) bool {
switch rule_name {
case "/":
fmt.Fprintf(s, "%d %d %d", s.Pos().Line, s.LinePos().Column, s.Pos().Column)
return true
case "blank":
s.Write([]byte{' '})
return true
case "int":
if value.(int)&1 == 0 {
fmt.Fprint(s, "even ")
} else {
fmt.Fprint(s, "odd ")
}
return true
case "nil":
return false
case "testing.T":
s.Write([]byte("testing.T"))
return true
}
panic("unreachable")
return false
}
func TestCustomFormatters(t *testing.T) {
fmap0 := FormatterMap{"/": formatter}
fmap1 := FormatterMap{"int": formatter, "blank": formatter, "nil": formatter}
fmap2 := FormatterMap{"testing.T": formatter}
f := parse(t, `int=`, fmap0)
verify(t, f, ``, 1, 2, 3)
f = parse(t, `int="#"`, nil)
verify(t, f, `###`, 1, 2, 3)
f = parse(t, `int="#";string="%s"`, fmap0)
verify(t, f, "#1 0 1#1 0 7#1 0 13\n2 0 0foo2 0 8\n", 1, 2, 3, "\n", "foo", "\n")
f = parse(t, ``, fmap1)
verify(t, f, `even odd even odd `, 0, 1, 2, 3)
f = parse(t, `/ =@:blank; float64="#"`, fmap1)
verify(t, f, `# # #`, 0.0, 1.0, 2.0)
f = parse(t, `float64=@:nil`, fmap1)
verify(t, f, ``, 0.0, 1.0, 2.0)
f = parse(t, `testing "testing"; ptr=*`, fmap2)
verify(t, f, `testing.T`, t)
// TODO needs more tests
}
// ----------------------------------------------------------------------------
// Formatting of basic and simple composite types
func check(t *testing.T, form, expected string, args ...interface{}) {
f := parse(t, form, nil)
if f == nil {
return // allow other tests to run
}
result := f.Sprint(args...)
if result != expected {
t.Errorf(
"format : %s\nresult : `%s`\nexpected: `%s`\n\n",
form, result, expected)
}
}
func TestBasicTypes(t *testing.T) {
check(t, ``, ``)
check(t, `bool=":%v"`, `:true:false`, true, false)
check(t, `int="%b %d %o 0x%x"`, `101010 42 52 0x2a`, 42)
check(t, `int="%"`, `%`, 42)
check(t, `int="%%"`, `%`, 42)
check(t, `int="**%%**"`, `**%**`, 42)
check(t, `int="%%%%%%"`, `%%%`, 42)
check(t, `int="%%%d%%"`, `%42%`, 42)
const i = -42
const is = `-42`
check(t, `int ="%d"`, is, i)
check(t, `int8 ="%d"`, is, int8(i))
check(t, `int16="%d"`, is, int16(i))
check(t, `int32="%d"`, is, int32(i))
check(t, `int64="%d"`, is, int64(i))
const u = 42
const us = `42`
check(t, `uint ="%d"`, us, uint(u))
check(t, `uint8 ="%d"`, us, uint8(u))
check(t, `uint16="%d"`, us, uint16(u))
check(t, `uint32="%d"`, us, uint32(u))
check(t, `uint64="%d"`, us, uint64(u))
const f = 3.141592
const fs = `3.141592`
check(t, `float64="%g"`, fs, f)
check(t, `float32="%g"`, fs, float32(f))
check(t, `float64="%g"`, fs, float64(f))
}
func TestArrayTypes(t *testing.T) {
var a0 [10]int
check(t, `array="array";`, `array`, a0)
a1 := [...]int{1, 2, 3}
check(t, `array="array";`, `array`, a1)
check(t, `array={*}; int="%d";`, `123`, a1)
check(t, `array={* / ", "}; int="%d";`, `1, 2, 3`, a1)
check(t, `array={* / *}; int="%d";`, `12233`, a1)
a2 := []interface{}{42, "foo", 3.14}
check(t, `array={* / ", "}; interface=*; string="bar"; default="%v";`, `42, bar, 3.14`, a2)
}
func TestChanTypes(t *testing.T) {
var c0 chan int
check(t, `chan="chan"`, `chan`, c0)
c1 := make(chan int)
go func() { c1 <- 42 }()
check(t, `chan="chan"`, `chan`, c1)
// check(t, `chan=*`, `42`, c1); // reflection support for chans incomplete
}
func TestFuncTypes(t *testing.T) {
var f0 func() int
check(t, `func="func"`, `func`, f0)
f1 := func() int { return 42 }
check(t, `func="func"`, `func`, f1)
// check(t, `func=*`, `42`, f1); // reflection support for funcs incomplete
}
func TestMapTypes(t *testing.T) {
var m0 map[string]int
check(t, `map="map"`, `map`, m0)
m1 := map[string]int{}
check(t, `map="map"`, `map`, m1)
// check(t, `map=*`, ``, m1); // reflection support for maps incomplete
}
func TestPointerTypes(t *testing.T) {
var p0 *int
check(t, `ptr="ptr"`, `ptr`, p0)
check(t, `ptr=*`, ``, p0)
check(t, `ptr=*|"nil"`, `nil`, p0)
x := 99991
p1 := &x
check(t, `ptr="ptr"`, `ptr`, p1)
check(t, `ptr=*; int="%d"`, `99991`, p1)
}
func TestDefaultRule(t *testing.T) {
check(t, `default="%v"`, `42foo3.14`, 42, "foo", 3.14)
check(t, `default="%v"; int="%x"`, `abcdef`, 10, 11, 12, 13, 14, 15)
check(t, `default="%v"; int="%x"`, `ab**ef`, 10, 11, "**", 14, 15)
check(t, `default="%x"; int=@:default`, `abcdef`, 10, 11, 12, 13, 14, 15)
}
func TestGlobalSeparatorRule(t *testing.T) {
check(t, `int="%d"; / ="-"`, `1-2-3-4`, 1, 2, 3, 4)
check(t, `int="%x%x"; / ="*"`, `aa*aa`, 10, 10)
}
// ----------------------------------------------------------------------------
// Formatting of a struct
type T1 struct {
a int
}
const F1 = `datafmt "datafmt";` +
`int = "%d";` +
`datafmt.T1 = "<" a ">";`
func TestStruct1(t *testing.T) { check(t, F1, "<42>", T1{42}) }
// ----------------------------------------------------------------------------
// Formatting of a struct with an optional field (ptr)
type T2 struct {
s string
p *T1
}
const F2a = F1 +
`string = "%s";` +
`ptr = *;` +
`datafmt.T2 = s ["-" p "-"];`
const F2b = F1 +
`string = "%s";` +
`ptr = *;` +
`datafmt.T2 = s ("-" p "-" | "empty");`
func TestStruct2(t *testing.T) {
check(t, F2a, "foo", T2{"foo", nil})
check(t, F2a, "bar-<17>-", T2{"bar", &T1{17}})
check(t, F2b, "fooempty", T2{"foo", nil})
}
// ----------------------------------------------------------------------------
// Formatting of a struct with a repetitive field (slice)
type T3 struct {
s string
a []int
}
const F3a = `datafmt "datafmt";` +
`default = "%v";` +
`array = *;` +
`datafmt.T3 = s {" " a a / ","};`
const F3b = `datafmt "datafmt";` +
`int = "%d";` +
`string = "%s";` +
`array = *;` +
`nil = ;` +
`empty = *:nil;` +
`datafmt.T3 = s [a:empty ": " {a / "-"}]`
func TestStruct3(t *testing.T) {
check(t, F3a, "foo", T3{"foo", nil})
check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}})
check(t, F3b, "bar", T3{"bar", nil})
check(t, F3b, "bal: 2-3-5", T3{"bal", []int{2, 3, 5}})
}
// ----------------------------------------------------------------------------
// Formatting of a struct with alternative field
type T4 struct {
x *int
a []int
}
const F4a = `datafmt "datafmt";` +
`int = "%d";` +
`ptr = *;` +
`array = *;` +
`nil = ;` +
`empty = *:nil;` +
`datafmt.T4 = "<" (x:empty x | "-") ">" `
const F4b = `datafmt "datafmt";` +
`int = "%d";` +
`ptr = *;` +
`array = *;` +
`nil = ;` +
`empty = *:nil;` +
`datafmt.T4 = "<" (a:empty {a / ", "} | "-") ">" `
func TestStruct4(t *testing.T) {
x := 7
check(t, F4a, "<->", T4{nil, nil})
check(t, F4a, "<7>", T4{&x, nil})
check(t, F4b, "<->", T4{nil, nil})
check(t, F4b, "<2, 3, 7>", T4{nil, []int{2, 3, 7}})
}
// ----------------------------------------------------------------------------
// Formatting a struct (documentation example)
type Point struct {
name string
x, y int
}
const FPoint = `datafmt "datafmt";` +
`int = "%d";` +
`hexInt = "0x%x";` +
`string = "---%s---";` +
`datafmt.Point = name "{" x ", " y:hexInt "}";`
func TestStructPoint(t *testing.T) {
p := Point{"foo", 3, 15}
check(t, FPoint, "---foo---{3, 0xf}", p)
}
// ----------------------------------------------------------------------------
// Formatting a slice (documentation example)
const FSlice = `int = "%b";` +
`array = { * / ", " }`
func TestSlice(t *testing.T) { check(t, FSlice, "10, 11, 101, 111", []int{2, 3, 5, 7}) }
// TODO add more tests
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package datafmt
import (
"container/vector"
"go/scanner"
"go/token"
"os"
"strconv"
"strings"
)
// ----------------------------------------------------------------------------
// Parsing
type parser struct {
scanner.ErrorVector
scanner scanner.Scanner
file *token.File
pos token.Pos // token position
tok token.Token // one token look-ahead
lit string // token literal
packs map[string]string // PackageName -> ImportPath
rules map[string]expr // RuleName -> Expression
}
func (p *parser) next() {
p.pos, p.tok, p.lit = p.scanner.Scan()
switch p.tok {
case token.CHAN, token.FUNC, token.INTERFACE, token.MAP, token.STRUCT:
// Go keywords for composite types are type names
// returned by reflect. Accept them as identifiers.
p.tok = token.IDENT // p.lit is already set correctly
}
}
func (p *parser) init(fset *token.FileSet, filename string, src []byte) {
p.ErrorVector.Reset()
p.file = fset.AddFile(filename, fset.Base(), len(src))
p.scanner.Init(p.file, src, p, scanner.AllowIllegalChars) // return '@' as token.ILLEGAL w/o error message
p.next() // initializes pos, tok, lit
p.packs = make(map[string]string)
p.rules = make(map[string]expr)
}
func (p *parser) error(pos token.Pos, msg string) {
p.Error(p.file.Position(pos), msg)
}
func (p *parser) errorExpected(pos token.Pos, msg string) {
msg = "expected " + msg
if pos == p.pos {
// the error happened at the current position;
// make the error message more specific
msg += ", found '" + p.tok.String() + "'"
if p.tok.IsLiteral() {
msg += " " + p.lit
}
}
p.error(pos, msg)
}
func (p *parser) expect(tok token.Token) token.Pos {
pos := p.pos
if p.tok != tok {
p.errorExpected(pos, "'"+tok.String()+"'")
}
p.next() // make progress in any case
return pos
}
func (p *parser) parseIdentifier() string {
name := p.lit
p.expect(token.IDENT)
return name
}
func (p *parser) parseTypeName() (string, bool) {
pos := p.pos
name, isIdent := p.parseIdentifier(), true
if p.tok == token.PERIOD {
// got a package name, lookup package
if importPath, found := p.packs[name]; found {
name = importPath
} else {
p.error(pos, "package not declared: "+name)
}
p.next()
name, isIdent = name+"."+p.parseIdentifier(), false
}
return name, isIdent
}
// Parses a rule name and returns it. If the rule name is
// a package-qualified type name, the package name is resolved.
// The 2nd result value is true iff the rule name consists of a
// single identifier only (and thus could be a package name).
//
func (p *parser) parseRuleName() (string, bool) {
name, isIdent := "", false
switch p.tok {
case token.IDENT:
name, isIdent = p.parseTypeName()
case token.DEFAULT:
name = "default"
p.next()
case token.QUO:
name = "/"
p.next()
default:
p.errorExpected(p.pos, "rule name")
p.next() // make progress in any case
}
return name, isIdent
}
func (p *parser) parseString() string {
s := ""
if p.tok == token.STRING {
s, _ = strconv.Unquote(p.lit)
// Unquote may fail with an error, but only if the scanner found
// an illegal string in the first place. In this case the error
// has already been reported.
p.next()
return s
} else {
p.expect(token.STRING)
}
return s
}
func (p *parser) parseLiteral() literal {
s := []byte(p.parseString())
// A string literal may contain %-format specifiers. To simplify
// and speed up printing of the literal, split it into segments
// that start with "%" possibly followed by a last segment that
// starts with some other character.
var list vector.Vector
i0 := 0
for i := 0; i < len(s); i++ {
if s[i] == '%' && i+1 < len(s) {
// the next segment starts with a % format
if i0 < i {
// the current segment is not empty, split it off
list.Push(s[i0:i])
i0 = i
}
i++ // skip %; let loop skip over char after %
}
}
// the final segment may start with any character
// (it is empty iff the string is empty)
list.Push(s[i0:])
// convert list into a literal
lit := make(literal, list.Len())
for i := 0; i < list.Len(); i++ {
lit[i] = list.At(i).([]byte)
}
return lit
}
func (p *parser) parseField() expr {
var fname string
switch p.tok {
case token.ILLEGAL:
if p.lit != "@" {
return nil
}
fname = "@"
p.next()
case token.MUL:
fname = "*"
p.next()
case token.IDENT:
fname = p.parseIdentifier()
default:
return nil
}
var ruleName string
if p.tok == token.COLON {
p.next()
ruleName, _ = p.parseRuleName()
}
return &field{fname, ruleName}
}
func (p *parser) parseOperand() (x expr) {
switch p.tok {
case token.STRING:
x = p.parseLiteral()
case token.LPAREN:
p.next()
x = p.parseExpression()
if p.tok == token.SHR {
p.next()
x = &group{x, p.parseExpression()}
}
p.expect(token.RPAREN)
case token.LBRACK:
p.next()
x = &option{p.parseExpression()}
p.expect(token.RBRACK)
case token.LBRACE:
p.next()
x = p.parseExpression()
var div expr
if p.tok == token.QUO {
p.next()
div = p.parseExpression()
}
x = &repetition{x, div}
p.expect(token.RBRACE)
default:
x = p.parseField() // may be nil
}
return x
}
func (p *parser) parseSequence() expr {
var list vector.Vector
for x := p.parseOperand(); x != nil; x = p.parseOperand() {
list.Push(x)
}
// no need for a sequence if list.Len() < 2
switch list.Len() {
case 0:
return nil
case 1:
return list.At(0).(expr)
}
// convert list into a sequence
seq := make(sequence, list.Len())
for i := 0; i < list.Len(); i++ {
seq[i] = list.At(i).(expr)
}
return seq
}
func (p *parser) parseExpression() expr {
var list vector.Vector
for {
x := p.parseSequence()
if x != nil {
list.Push(x)
}
if p.tok != token.OR {
break
}
p.next()
}
// no need for an alternatives if list.Len() < 2
switch list.Len() {
case 0:
return nil
case 1:
return list.At(0).(expr)
}
// convert list into a alternatives
alt := make(alternatives, list.Len())
for i := 0; i < list.Len(); i++ {
alt[i] = list.At(i).(expr)
}
return alt
}
func (p *parser) parseFormat() {
for p.tok != token.EOF {
pos := p.pos
name, isIdent := p.parseRuleName()
switch p.tok {
case token.STRING:
// package declaration
importPath := p.parseString()
// add package declaration
if !isIdent {
p.error(pos, "illegal package name: "+name)
} else if _, found := p.packs[name]; !found {
p.packs[name] = importPath
} else {
p.error(pos, "package already declared: "+name)
}
case token.ASSIGN:
// format rule
p.next()
x := p.parseExpression()
// add rule
if _, found := p.rules[name]; !found {
p.rules[name] = x
} else {
p.error(pos, "format rule already declared: "+name)
}
default:
p.errorExpected(p.pos, "package declaration or format rule")
p.next() // make progress in any case
}
if p.tok == token.SEMICOLON {
p.next()
} else {
break
}
}
p.expect(token.EOF)
}
func remap(p *parser, name string) string {
i := strings.Index(name, ".")
if i >= 0 {
packageName, suffix := name[0:i], name[i:]
// lookup package
if importPath, found := p.packs[packageName]; found {
name = importPath + suffix
} else {
var invalidPos token.Position
p.Error(invalidPos, "package not declared: "+packageName)
}
}
return name
}
// Parse parses a set of format productions from source src. Custom
// formatters may be provided via a map of formatter functions. If
// there are no errors, the result is a Format and the error is nil.
// Otherwise the format is nil and a non-empty ErrorList is returned.
//
func Parse(fset *token.FileSet, filename string, src []byte, fmap FormatterMap) (Format, os.Error) {
// parse source
var p parser
p.init(fset, filename, src)
p.parseFormat()
// add custom formatters, if any
for name, form := range fmap {
name = remap(&p, name)
if _, found := p.rules[name]; !found {
p.rules[name] = &custom{name, form}
} else {
var invalidPos token.Position
p.Error(invalidPos, "formatter already declared: "+name)
}
}
return p.rules, p.GetError(scanner.NoMultiples)
}
......@@ -62,7 +62,7 @@ type I int
func (i I) String() string { return Sprintf("<%d>", int(i)) }
type B struct {
i I
I I
j int
}
......@@ -84,8 +84,8 @@ func (g G) GoString() string {
}
type S struct {
f F // a struct field that Formats
g G // a struct field that GoStrings
F F // a struct field that Formats
G G // a struct field that GoStrings
}
// A type with a String method with pointer receiver for testing %p
......@@ -322,8 +322,8 @@ var fmttests = []struct {
{"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`},
// +v on structs with Stringable items
{"%+v", B{1, 2}, `{i:<1> j:2}`},
{"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`},
{"%+v", B{1, 2}, `{I:<1> j:2}`},
{"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`},
// q on Stringable items
{"%s", I(23), `<23>`},
......@@ -339,7 +339,7 @@ var fmttests = []struct {
{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"},
{"%#v", 1000000000, "1000000000"},
{"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`},
{"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`},
{"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{I:1, j:2}, "b":fmt_test.B{I:3, j:4}}`},
{"%#v", []string{"a", "b"}, `[]string{"a", "b"}`},
// slices with other formats
......@@ -374,11 +374,11 @@ var fmttests = []struct {
// Formatter
{"%x", F(1), "<x=F(1)>"},
{"%x", G(2), "2"},
{"%+v", S{F(4), G(5)}, "{f:<v=F(4)> g:5}"},
{"%+v", S{F(4), G(5)}, "{F:<v=F(4)> G:5}"},
// GoStringer
{"%#v", G(6), "GoString(6)"},
{"%#v", S{F(7), G(8)}, "fmt_test.S{f:<v=F(7)>, g:GoString(8)}"},
{"%#v", S{F(7), G(8)}, "fmt_test.S{F:<v=F(7)>, G:GoString(8)}"},
// %T
{"%T", (4 - 3i), "complex128"},
......
This diff is collapsed.
......@@ -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")
}
}
......@@ -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")
......
......@@ -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 {
......
......@@ -12,20 +12,20 @@ package main
import "reflect"
type T struct {
f float32
g float32
F float32
G float32
s string
t string
S string
T string
u uint32
v uint32
U uint32
V uint32
w uint32
x uint32
W uint32
X uint32
y uint32
z uint32
Y uint32
Z uint32
}
func add(s, t string) string {
......@@ -40,16 +40,16 @@ func assert(b bool) {
func main() {
var x T
x.f = 1.0
x.g = x.f
x.s = add("abc", "def")
x.t = add("abc", "def")
x.u = 1
x.v = 2
x.w = 1 << 28
x.x = 2 << 28
x.y = 0x12345678
x.z = x.y
x.F = 1.0
x.G = x.F
x.S = add("abc", "def")
x.T = add("abc", "def")
x.U = 1
x.V = 2
x.W = 1 << 28
x.X = 2 << 28
x.Y = 0x12345678
x.Z = x.Y
// check mem and string
v := reflect.ValueOf(x)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment