1
0
mirror of https://github.com/golang/go synced 2024-11-22 02:54:39 -07:00

template: allow accesses only to exported fields and methods

R=rsc, gri
CC=golang-dev
https://golang.org/cl/3890042
This commit is contained in:
Rob Pike 2011-01-11 15:47:45 -08:00
parent ac1c09458c
commit 02e88019f2
2 changed files with 125 additions and 110 deletions

View File

@ -73,6 +73,8 @@ import (
"os" "os"
"reflect" "reflect"
"strings" "strings"
"unicode"
"utf8"
) )
// Errors returned during parsing and execution. Users may extract the information and reformat // Errors returned during parsing and execution. Users may extract the information and reformat
@ -198,6 +200,12 @@ func (t *Template) parseError(err string, args ...interface{}) {
panic(&Error{t.linenum, fmt.Sprintf(err, args...)}) panic(&Error{t.linenum, fmt.Sprintf(err, args...)})
} }
// Is this an exported - upper case - name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// -- Lexical analysis // -- Lexical analysis
// Is c a white space character? // Is c a white space character?
@ -596,6 +604,9 @@ func lookup(v reflect.Value, name string) reflect.Value {
m := typ.Method(i) m := typ.Method(i)
mtyp := m.Type mtyp := m.Type
if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 {
if !isExported(name) {
return nil
}
return v.Method(i).Call(nil)[0] return v.Method(i).Call(nil)[0]
} }
} }
@ -606,6 +617,9 @@ func lookup(v reflect.Value, name string) reflect.Value {
case *reflect.InterfaceValue: case *reflect.InterfaceValue:
v = av.Elem() v = av.Elem()
case *reflect.StructValue: case *reflect.StructValue:
if !isExported(name) {
return nil
}
return av.FieldByName(name) return av.FieldByName(name)
case *reflect.MapValue: case *reflect.MapValue:
return av.Elem(reflect.NewValue(name)) return av.Elem(reflect.NewValue(name))

View File

@ -20,40 +20,40 @@ type Test struct {
} }
type T struct { type T struct {
item string Item string
value string Value string
} }
type U struct { type U struct {
mp map[string]int Mp map[string]int
} }
type S struct { type S struct {
header string Header string
integer int Integer int
raw string Raw string
innerT T InnerT T
innerPointerT *T InnerPointerT *T
data []T Data []T
pdata []*T Pdata []*T
empty []*T Empty []*T
emptystring string Emptystring string
null []*T Null []*T
vec *vector.Vector Vec *vector.Vector
true bool True bool
false bool False bool
mp map[string]string Mp map[string]string
json interface{} JSON interface{}
innermap U Innermap U
stringmap map[string]string Stringmap map[string]string
bytes []byte Bytes []byte
iface interface{} Iface interface{}
ifaceptr interface{} Ifaceptr interface{}
} }
func (s *S) pointerMethod() string { return "ptrmethod!" } func (s *S) PointerMethod() string { return "ptrmethod!" }
func (s S) valueMethod() string { return "valmethod!" } func (s S) ValueMethod() string { return "valmethod!" }
var t1 = T{"ItemNumber1", "ValueNumber1"} var t1 = T{"ItemNumber1", "ValueNumber1"}
var t2 = T{"ItemNumber2", "ValueNumber2"} var t2 = T{"ItemNumber2", "ValueNumber2"}
@ -112,48 +112,48 @@ var tests = []*Test{
// Variables at top level // Variables at top level
&Test{ &Test{
in: "{header}={integer}\n", in: "{Header}={Integer}\n",
out: "Header=77\n", out: "Header=77\n",
}, },
// Method at top level // Method at top level
&Test{ &Test{
in: "ptrmethod={pointerMethod}\n", in: "ptrmethod={PointerMethod}\n",
out: "ptrmethod=ptrmethod!\n", out: "ptrmethod=ptrmethod!\n",
}, },
&Test{ &Test{
in: "valmethod={valueMethod}\n", in: "valmethod={ValueMethod}\n",
out: "valmethod=valmethod!\n", out: "valmethod=valmethod!\n",
}, },
// Section // Section
&Test{ &Test{
in: "{.section data }\n" + in: "{.section Data }\n" +
"some text for the section\n" + "some text for the section\n" +
"{.end}\n", "{.end}\n",
out: "some text for the section\n", out: "some text for the section\n",
}, },
&Test{ &Test{
in: "{.section data }\n" + in: "{.section Data }\n" +
"{header}={integer}\n" + "{Header}={Integer}\n" +
"{.end}\n", "{.end}\n",
out: "Header=77\n", out: "Header=77\n",
}, },
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{header}={integer}\n" + "{Header}={Integer}\n" +
"{.end}\n", "{.end}\n",
out: "Header=77\n", out: "Header=77\n",
}, },
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"data present\n" + "data present\n" +
"{.or}\n" + "{.or}\n" +
"data not present\n" + "data not present\n" +
@ -162,7 +162,7 @@ var tests = []*Test{
out: "data present\n", out: "data present\n",
}, },
&Test{ &Test{
in: "{.section empty }\n" + in: "{.section Empty }\n" +
"data present\n" + "data present\n" +
"{.or}\n" + "{.or}\n" +
"data not present\n" + "data not present\n" +
@ -171,7 +171,7 @@ var tests = []*Test{
out: "data not present\n", out: "data not present\n",
}, },
&Test{ &Test{
in: "{.section null }\n" + in: "{.section Null }\n" +
"data present\n" + "data present\n" +
"{.or}\n" + "{.or}\n" +
"data not present\n" + "data not present\n" +
@ -180,10 +180,10 @@ var tests = []*Test{
out: "data not present\n", out: "data not present\n",
}, },
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{header}={integer}\n" + "{Header}={Integer}\n" +
"{.section @ }\n" + "{.section @ }\n" +
"{header}={integer}\n" + "{Header}={Integer}\n" +
"{.end}\n" + "{.end}\n" +
"{.end}\n", "{.end}\n",
@ -192,22 +192,23 @@ var tests = []*Test{
}, },
&Test{ &Test{
in: "{.section data}{.end} {header}\n", in: "{.section Data}{.end} {Header}\n",
out: " Header\n", out: " Header\n",
}, },
&Test{ &Test{
in: "{.section integer}{@}{.end}", in: "{.section Integer}{@}{.end}",
out: "77", out: "77",
}, },
// Repeated // Repeated
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{.repeated section @ }\n" + "{.repeated section @ }\n" +
"{item}={value}\n" + "{Item}={Value}\n" +
"{.end}\n" + "{.end}\n" +
"{.end}\n", "{.end}\n",
@ -215,9 +216,9 @@ var tests = []*Test{
"ItemNumber2=ValueNumber2\n", "ItemNumber2=ValueNumber2\n",
}, },
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{.repeated section @ }\n" + "{.repeated section @ }\n" +
"{item}={value}\n" + "{Item}={Value}\n" +
"{.or}\n" + "{.or}\n" +
"this should not appear\n" + "this should not appear\n" +
"{.end}\n" + "{.end}\n" +
@ -228,8 +229,8 @@ var tests = []*Test{
}, },
&Test{ &Test{
in: "{.section @ }\n" + in: "{.section @ }\n" +
"{.repeated section empty }\n" + "{.repeated section Empty }\n" +
"{item}={value}\n" + "{Item}={Value}\n" +
"{.or}\n" + "{.or}\n" +
"this should appear: empty field\n" + "this should appear: empty field\n" +
"{.end}\n" + "{.end}\n" +
@ -238,8 +239,8 @@ var tests = []*Test{
out: "this should appear: empty field\n", out: "this should appear: empty field\n",
}, },
&Test{ &Test{
in: "{.repeated section pdata }\n" + in: "{.repeated section Pdata }\n" +
"{item}\n" + "{Item}\n" +
"{.alternates with}\n" + "{.alternates with}\n" +
"is\nover\nmultiple\nlines\n" + "is\nover\nmultiple\nlines\n" +
"{.end}\n", "{.end}\n",
@ -249,8 +250,8 @@ var tests = []*Test{
"ItemNumber2\n", "ItemNumber2\n",
}, },
&Test{ &Test{
in: "{.repeated section pdata }\n" + in: "{.repeated section Pdata }\n" +
"{item}\n" + "{Item}\n" +
"{.alternates with}\n" + "{.alternates with}\n" +
"is\nover\nmultiple\nlines\n" + "is\nover\nmultiple\nlines\n" +
" {.end}\n", " {.end}\n",
@ -260,9 +261,9 @@ var tests = []*Test{
"ItemNumber2\n", "ItemNumber2\n",
}, },
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{.repeated section @ }\n" + "{.repeated section @ }\n" +
"{item}={value}\n" + "{Item}={Value}\n" +
"{.alternates with}DIVIDER\n" + "{.alternates with}DIVIDER\n" +
"{.or}\n" + "{.or}\n" +
"this should not appear\n" + "this should not appear\n" +
@ -274,7 +275,7 @@ var tests = []*Test{
"ItemNumber2=ValueNumber2\n", "ItemNumber2=ValueNumber2\n",
}, },
&Test{ &Test{
in: "{.repeated section vec }\n" + in: "{.repeated section Vec }\n" +
"{@}\n" + "{@}\n" +
"{.end}\n", "{.end}\n",
@ -283,28 +284,28 @@ var tests = []*Test{
}, },
// Same but with a space before {.end}: was a bug. // Same but with a space before {.end}: was a bug.
&Test{ &Test{
in: "{.repeated section vec }\n" + in: "{.repeated section Vec }\n" +
"{@} {.end}\n", "{@} {.end}\n",
out: "elt1 elt2 \n", out: "elt1 elt2 \n",
}, },
&Test{ &Test{
in: "{.repeated section integer}{.end}", in: "{.repeated section Integer}{.end}",
err: "line 1: .repeated: cannot repeat integer (type int)", err: "line 1: .repeated: cannot repeat Integer (type int)",
}, },
// Nested names // Nested names
&Test{ &Test{
in: "{.section @ }\n" + in: "{.section @ }\n" +
"{innerT.item}={innerT.value}\n" + "{InnerT.Item}={InnerT.Value}\n" +
"{.end}", "{.end}",
out: "ItemNumber1=ValueNumber1\n", out: "ItemNumber1=ValueNumber1\n",
}, },
&Test{ &Test{
in: "{.section @ }\n" + in: "{.section @ }\n" +
"{innerT.item}={.section innerT}{.section value}{@}{.end}{.end}\n" + "{InnerT.Item}={.section InnerT}{.section Value}{@}{.end}{.end}\n" +
"{.end}", "{.end}",
out: "ItemNumber1=ValueNumber1\n", out: "ItemNumber1=ValueNumber1\n",
@ -313,9 +314,9 @@ var tests = []*Test{
// Formatters // Formatters
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{header|uppercase}={integer|+1}\n" + "{Header|uppercase}={Integer|+1}\n" +
"{header|html}={integer|str}\n" + "{Header|html}={Integer|str}\n" +
"{.end}\n", "{.end}\n",
out: "HEADER=78\n" + out: "HEADER=78\n" +
@ -323,10 +324,10 @@ var tests = []*Test{
}, },
&Test{ &Test{
in: "{.section pdata }\n" + in: "{.section Pdata }\n" +
"{header|uppercase}={integer header|multiword}\n" + "{Header|uppercase}={Integer Header|multiword}\n" +
"{header|html}={header integer|multiword}\n" + "{Header|html}={Header Integer|multiword}\n" +
"{header|html}={header integer}\n" + "{Header|html}={Header Integer}\n" +
"{.end}\n", "{.end}\n",
out: "HEADER=<77><Header>\n" + out: "HEADER=<77><Header>\n" +
@ -335,29 +336,29 @@ var tests = []*Test{
}, },
&Test{ &Test{
in: "{raw}\n" + in: "{Raw}\n" +
"{raw|html}\n", "{Raw|html}\n",
out: "&<>!@ #$%^\n" + out: "&<>!@ #$%^\n" +
"&amp;&lt;&gt;!@ #$%^\n", "&amp;&lt;&gt;!@ #$%^\n",
}, },
&Test{ &Test{
in: "{.section emptystring}emptystring{.end}\n" + in: "{.section Emptystring}emptystring{.end}\n" +
"{.section header}header{.end}\n", "{.section Header}header{.end}\n",
out: "\nheader\n", out: "\nheader\n",
}, },
&Test{ &Test{
in: "{.section true}1{.or}2{.end}\n" + in: "{.section True}1{.or}2{.end}\n" +
"{.section false}3{.or}4{.end}\n", "{.section False}3{.or}4{.end}\n",
out: "1\n4\n", out: "1\n4\n",
}, },
&Test{ &Test{
in: "{bytes}", in: "{Bytes}",
out: "hello", out: "hello",
}, },
@ -365,32 +366,32 @@ var tests = []*Test{
// Maps // Maps
&Test{ &Test{
in: "{mp.mapkey}\n", in: "{Mp.mapkey}\n",
out: "Ahoy!\n", out: "Ahoy!\n",
}, },
&Test{ &Test{
in: "{innermap.mp.innerkey}\n", in: "{Innermap.Mp.innerkey}\n",
out: "55\n", out: "55\n",
}, },
&Test{ &Test{
in: "{.section innermap}{.section mp}{innerkey}{.end}{.end}\n", in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n",
out: "55\n", out: "55\n",
}, },
&Test{ &Test{
in: "{.section json}{.repeated section maps}{a}{b}{.end}{.end}\n", in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n",
out: "1234\n", out: "1234\n",
}, },
&Test{ &Test{
in: "{stringmap.stringkey1}\n", in: "{Stringmap.stringkey1}\n",
out: "stringresult\n", out: "stringresult\n",
}, },
&Test{ &Test{
in: "{.repeated section stringmap}\n" + in: "{.repeated section Stringmap}\n" +
"{@}\n" + "{@}\n" +
"{.end}", "{.end}",
@ -398,7 +399,7 @@ var tests = []*Test{
"stringresult\n", "stringresult\n",
}, },
&Test{ &Test{
in: "{.repeated section stringmap}\n" + in: "{.repeated section Stringmap}\n" +
"\t{@}\n" + "\t{@}\n" +
"{.end}", "{.end}",
@ -409,22 +410,22 @@ var tests = []*Test{
// Interface values // Interface values
&Test{ &Test{
in: "{iface}", in: "{Iface}",
out: "[1 2 3]", out: "[1 2 3]",
}, },
&Test{ &Test{
in: "{.repeated section iface}{@}{.alternates with} {.end}", in: "{.repeated section Iface}{@}{.alternates with} {.end}",
out: "1 2 3", out: "1 2 3",
}, },
&Test{ &Test{
in: "{.section iface}{@}{.end}", in: "{.section Iface}{@}{.end}",
out: "[1 2 3]", out: "[1 2 3]",
}, },
&Test{ &Test{
in: "{.section ifaceptr}{item} {value}{.end}", in: "{.section Ifaceptr}{Item} {Value}{.end}",
out: "Item Value", out: "Item Value",
}, },
@ -457,30 +458,30 @@ func TestAll(t *testing.T) {
func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) {
s := new(S) s := new(S)
// initialized by hand for clarity. // initialized by hand for clarity.
s.header = "Header" s.Header = "Header"
s.integer = 77 s.Integer = 77
s.raw = "&<>!@ #$%^" s.Raw = "&<>!@ #$%^"
s.innerT = t1 s.InnerT = t1
s.data = []T{t1, t2} s.Data = []T{t1, t2}
s.pdata = []*T{&t1, &t2} s.Pdata = []*T{&t1, &t2}
s.empty = []*T{} s.Empty = []*T{}
s.null = nil s.Null = nil
s.vec = new(vector.Vector) s.Vec = new(vector.Vector)
s.vec.Push("elt1") s.Vec.Push("elt1")
s.vec.Push("elt2") s.Vec.Push("elt2")
s.true = true s.True = true
s.false = false s.False = false
s.mp = make(map[string]string) s.Mp = make(map[string]string)
s.mp["mapkey"] = "Ahoy!" s.Mp["mapkey"] = "Ahoy!"
json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.json) json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON)
s.innermap.mp = make(map[string]int) s.Innermap.Mp = make(map[string]int)
s.innermap.mp["innerkey"] = 55 s.Innermap.Mp["innerkey"] = 55
s.stringmap = make(map[string]string) s.Stringmap = make(map[string]string)
s.stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
s.stringmap["stringkey2"] = "stringresult" s.Stringmap["stringkey2"] = "stringresult"
s.bytes = []byte("hello") s.Bytes = []byte("hello")
s.iface = []int{1, 2, 3} s.Iface = []int{1, 2, 3}
s.ifaceptr = &T{"Item", "Value"} s.Ifaceptr = &T{"Item", "Value"}
var buf bytes.Buffer var buf bytes.Buffer
for _, test := range tests { for _, test := range tests {
@ -606,10 +607,10 @@ func TestCustomDelims(t *testing.T) {
func TestVarIndirection(t *testing.T) { func TestVarIndirection(t *testing.T) {
s := new(S) s := new(S)
// initialized by hand for clarity. // initialized by hand for clarity.
s.innerPointerT = &t1 s.InnerPointerT = &t1
var buf bytes.Buffer var buf bytes.Buffer
input := "{.section @}{innerPointerT}{.end}" input := "{.section @}{InnerPointerT}{.end}"
tmpl, err := Parse(input, nil) tmpl, err := Parse(input, nil)
if err != nil { if err != nil {
t.Fatal("unexpected parse error:", err) t.Fatal("unexpected parse error:", err)