diff --git a/src/text/template/exec.go b/src/text/template/exec.go index 8e5ad93ca6..5a6e454ec6 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -171,13 +171,19 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) // execution stops, but partial results may already have been written to // the output writer. // A template may be executed safely in parallel. +// +// If data is a reflect.Value, the template applies to the concrete +// value that the reflect.Value holds, as in fmt.Print. func (t *Template) Execute(wr io.Writer, data interface{}) error { return t.execute(wr, data) } func (t *Template) execute(wr io.Writer, data interface{}) (err error) { defer errRecover(&err) - value := reflect.ValueOf(data) + value, ok := data.(reflect.Value) + if !ok { + value = reflect.ValueOf(data) + } state := &state{ tmpl: t, wr: wr, @@ -596,8 +602,9 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, } var ( - errorType = reflect.TypeOf((*error)(nil)).Elem() - fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() + errorType = reflect.TypeOf((*error)(nil)).Elem() + fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() + reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem() ) // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so @@ -661,7 +668,11 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a s.at(node) s.errorf("error calling %s: %s", name, result[1].Interface().(error)) } - return result[0] + v := result[0] + if v.Type() == reflectValueType { + v = v.Interface().(reflect.Value) + } + return v } // canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. @@ -669,6 +680,8 @@ func canBeNil(typ reflect.Type) bool { switch typ.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return true + case reflect.Struct: + return typ == reflectValueType } return false } @@ -682,6 +695,9 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu } s.errorf("invalid value; expected %s", typ) } + if typ == reflectValueType && value.Type() != typ { + return reflect.ValueOf(value) + } if typ != nil && !value.Type().AssignableTo(typ) { if value.Kind() == reflect.Interface && !value.IsNil() { value = value.Elem() @@ -743,6 +759,10 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle if typ.NumMethod() == 0 { return s.evalEmptyInterface(dot, n) } + case reflect.Struct: + if typ == reflectValueType { + return reflect.ValueOf(s.evalEmptyInterface(dot, n)) + } case reflect.String: return s.evalString(typ, n) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index 3ef065edcf..7092961850 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -1310,3 +1310,26 @@ func TestMaxExecDepth(t *testing.T) { t.Errorf("got error %q; want %q", got, want) } } + +func TestAddrOfIndex(t *testing.T) { + // golang.org/issue/14916. + // Before index worked on reflect.Values, the .String could not be + // found on the (incorrectly unaddressable) V value, + // in contrast to range, which worked fine. + // Also testing that passing a reflect.Value to tmpl.Execute works. + texts := []string{ + `{{range .}}{{.String}}{{end}}`, + `{{with index . 0}}{{.String}}{{end}}`, + } + for _, text := range texts { + tmpl := Must(New("tmpl").Parse(text)) + var buf bytes.Buffer + err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}})) + if err != nil { + t.Fatal("%s: Execute: %v", text, err) + } + if buf.String() != "<1>" { + t.Fatalf("%s: template output = %q, want %q", text, buf, "<1>") + } + } +} diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go index cd0b82b243..8d8bc059f0 100644 --- a/src/text/template/funcs.go +++ b/src/text/template/funcs.go @@ -21,6 +21,12 @@ import ( // which the second has type error. In that case, if the second (error) // return value evaluates to non-nil during execution, execution terminates and // Execute returns that error. +// +// When template execution invokes a function with an argument list, that list +// must be assignable to the function's parameter types. Functions meant to +// apply to arguments of arbitrary type can use parameters of type interface{} or +// of type reflect.Value. Similarly, functions meant to return a result of arbitrary +// type can return interface{} or reflect.Value. type FuncMap map[string]interface{} var builtins = FuncMap{ @@ -144,16 +150,15 @@ func prepareArg(value reflect.Value, argType reflect.Type) (reflect.Value, error // index returns the result of indexing its first argument by the following // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each // indexed item must be a map, slice, or array. -func index(item interface{}, indices ...interface{}) (interface{}, error) { - v := reflect.ValueOf(item) +func index(item reflect.Value, indices ...reflect.Value) (reflect.Value, error) { + v := item if !v.IsValid() { - return nil, fmt.Errorf("index of untyped nil") + return reflect.Value{}, fmt.Errorf("index of untyped nil") } - for _, i := range indices { - index := reflect.ValueOf(i) + for _, index := range indices { var isNil bool if v, isNil = indirect(v); isNil { - return nil, fmt.Errorf("index of nil pointer") + return reflect.Value{}, fmt.Errorf("index of nil pointer") } switch v.Kind() { case reflect.Array, reflect.Slice, reflect.String: @@ -164,18 +169,18 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: x = int64(index.Uint()) case reflect.Invalid: - return nil, fmt.Errorf("cannot index slice/array with nil") + return reflect.Value{}, fmt.Errorf("cannot index slice/array with nil") default: - return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) + return reflect.Value{}, fmt.Errorf("cannot index slice/array with type %s", index.Type()) } if x < 0 || x >= int64(v.Len()) { - return nil, fmt.Errorf("index out of range: %d", x) + return reflect.Value{}, fmt.Errorf("index out of range: %d", x) } v = v.Index(int(x)) case reflect.Map: index, err := prepareArg(index, v.Type().Key()) if err != nil { - return nil, err + return reflect.Value{}, err } if x := v.MapIndex(index); x.IsValid() { v = x @@ -186,10 +191,10 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) { // the loop holds invariant: v.IsValid() panic("unreachable") default: - return nil, fmt.Errorf("can't index item of type %s", v.Type()) + return reflect.Value{}, fmt.Errorf("can't index item of type %s", v.Type()) } } - return v.Interface(), nil + return v, nil } // Length @@ -215,33 +220,32 @@ func length(item interface{}) (int, error) { // call returns the result of evaluating the first argument as a function. // The function must return 1 result, or 2 results, the second of which is an error. -func call(fn interface{}, args ...interface{}) (interface{}, error) { - v := reflect.ValueOf(fn) +func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) { + v := fn if !v.IsValid() { - return nil, fmt.Errorf("call of nil") + return reflect.Value{}, fmt.Errorf("call of nil") } typ := v.Type() if typ.Kind() != reflect.Func { - return nil, fmt.Errorf("non-function of type %s", typ) + return reflect.Value{}, fmt.Errorf("non-function of type %s", typ) } if !goodFunc(typ) { - return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) + return reflect.Value{}, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) } numIn := typ.NumIn() var dddType reflect.Type if typ.IsVariadic() { if len(args) < numIn-1 { - return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) + return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) } dddType = typ.In(numIn - 1).Elem() } else { if len(args) != numIn { - return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) + return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) } } argv := make([]reflect.Value, len(args)) - for i, arg := range args { - value := reflect.ValueOf(arg) + for i, value := range args { // Compute the expected type. Clumsy because of variadics. var argType reflect.Type if !typ.IsVariadic() || i < numIn-1 { @@ -252,26 +256,26 @@ func call(fn interface{}, args ...interface{}) (interface{}, error) { var err error if argv[i], err = prepareArg(value, argType); err != nil { - return nil, fmt.Errorf("arg %d: %s", i, err) + return reflect.Value{}, fmt.Errorf("arg %d: %s", i, err) } } result := v.Call(argv) if len(result) == 2 && !result[1].IsNil() { - return result[0].Interface(), result[1].Interface().(error) + return result[0], result[1].Interface().(error) } - return result[0].Interface(), nil + return result[0], nil } // Boolean logic. -func truth(a interface{}) bool { - t, _ := IsTrue(a) +func truth(arg reflect.Value) bool { + t, _ := isTrue(arg) return t } // and computes the Boolean AND of its arguments, returning // the first false argument it encounters, or the last argument. -func and(arg0 interface{}, args ...interface{}) interface{} { +func and(arg0 reflect.Value, args ...reflect.Value) reflect.Value { if !truth(arg0) { return arg0 } @@ -286,7 +290,7 @@ func and(arg0 interface{}, args ...interface{}) interface{} { // or computes the Boolean OR of its arguments, returning // the first true argument it encounters, or the last argument. -func or(arg0 interface{}, args ...interface{}) interface{} { +func or(arg0 reflect.Value, args ...reflect.Value) reflect.Value { if truth(arg0) { return arg0 } @@ -300,7 +304,7 @@ func or(arg0 interface{}, args ...interface{}) interface{} { } // not returns the Boolean negation of its argument. -func not(arg interface{}) bool { +func not(arg reflect.Value) bool { return !truth(arg) } @@ -345,8 +349,8 @@ func basicKind(v reflect.Value) (kind, error) { } // eq evaluates the comparison a == b || a == c || ... -func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) +func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { + v1 := arg1 k1, err := basicKind(v1) if err != nil { return false, err @@ -354,8 +358,7 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { if len(arg2) == 0 { return false, errNoComparison } - for _, arg := range arg2 { - v2 := reflect.ValueOf(arg) + for _, v2 := range arg2 { k2, err := basicKind(v2) if err != nil { return false, err @@ -397,20 +400,18 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { } // ne evaluates the comparison a != b. -func ne(arg1, arg2 interface{}) (bool, error) { +func ne(arg1, arg2 reflect.Value) (bool, error) { // != is the inverse of ==. equal, err := eq(arg1, arg2) return !equal, err } // lt evaluates the comparison a < b. -func lt(arg1, arg2 interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) +func lt(v1, v2 reflect.Value) (bool, error) { k1, err := basicKind(v1) if err != nil { return false, err } - v2 := reflect.ValueOf(arg2) k2, err := basicKind(v2) if err != nil { return false, err @@ -446,7 +447,7 @@ func lt(arg1, arg2 interface{}) (bool, error) { } // le evaluates the comparison <= b. -func le(arg1, arg2 interface{}) (bool, error) { +func le(arg1, arg2 reflect.Value) (bool, error) { // <= is < or ==. lessThan, err := lt(arg1, arg2) if lessThan || err != nil { @@ -456,7 +457,7 @@ func le(arg1, arg2 interface{}) (bool, error) { } // gt evaluates the comparison a > b. -func gt(arg1, arg2 interface{}) (bool, error) { +func gt(arg1, arg2 reflect.Value) (bool, error) { // > is the inverse of <=. lessOrEqual, err := le(arg1, arg2) if err != nil { @@ -466,7 +467,7 @@ func gt(arg1, arg2 interface{}) (bool, error) { } // ge evaluates the comparison a >= b. -func ge(arg1, arg2 interface{}) (bool, error) { +func ge(arg1, arg2 reflect.Value) (bool, error) { // >= is the inverse of <. lessThan, err := lt(arg1, arg2) if err != nil {