1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 // FuncMap is the type of the map defining the mapping from names to functions.
19 // Each function must have either a single return value, or two return values of
20 // which the second has type error. In that case, if the second (error)
21 // argument evaluates to non-nil during execution, execution terminates and
22 // Execute returns that error.
23 type FuncMap map[string]interface{}
25 var builtins = FuncMap{
35 "printf": fmt.Sprintf,
36 "println": fmt.Sprintln,
37 "urlquery": URLQueryEscaper,
40 var builtinFuncs = createValueFuncs(builtins)
42 // createValueFuncs turns a FuncMap into a map[string]reflect.Value
43 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
44 m := make(map[string]reflect.Value)
45 addValueFuncs(m, funcMap)
49 // addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
50 func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
51 for name, fn := range in {
52 v := reflect.ValueOf(fn)
53 if v.Kind() != reflect.Func {
54 panic("value for " + name + " not a function")
56 if !goodFunc(v.Type()) {
57 panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
63 // addFuncs adds to values the functions in funcs. It does no checking of the input -
64 // call addValueFuncs first.
65 func addFuncs(out, in FuncMap) {
66 for name, fn := range in {
71 // goodFunc checks that the function or method has the right result signature.
72 func goodFunc(typ reflect.Type) bool {
73 // We allow functions with 1 result or 2 results where the second is an error.
75 case typ.NumOut() == 1:
77 case typ.NumOut() == 2 && typ.Out(1) == errorType:
83 // findFunction looks for a function in the template, and global map.
84 func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
85 if tmpl != nil && tmpl.common != nil {
86 if fn := tmpl.execFuncs[name]; fn.IsValid() {
90 if fn := builtinFuncs[name]; fn.IsValid() {
93 return reflect.Value{}, false
98 // index returns the result of indexing its first argument by the following
99 // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
100 // indexed item must be a map, slice, or array.
101 func index(item interface{}, indices ...interface{}) (interface{}, error) {
102 v := reflect.ValueOf(item)
103 for _, i := range indices {
104 index := reflect.ValueOf(i)
106 if v, isNil = indirect(v); isNil {
107 return nil, fmt.Errorf("index of nil pointer")
110 case reflect.Array, reflect.Slice:
112 switch index.Kind() {
113 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
115 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
116 x = int64(index.Uint())
118 return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
120 if x < 0 || x >= int64(v.Len()) {
121 return nil, fmt.Errorf("index out of range: %d", x)
125 if !index.Type().AssignableTo(v.Type().Key()) {
126 return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
128 if x := v.MapIndex(index); x.IsValid() {
131 v = reflect.Zero(v.Type().Key())
134 return nil, fmt.Errorf("can't index item of type %s", index.Type())
137 return v.Interface(), nil
142 // length returns the length of the item, with an error if it has no defined length.
143 func length(item interface{}) (int, error) {
144 v, isNil := indirect(reflect.ValueOf(item))
146 return 0, fmt.Errorf("len of nil pointer")
149 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
152 return 0, fmt.Errorf("len of type %s", v.Type())
155 // Function invocation
157 // call returns the result of evaluating the the first argument as a function.
158 // The function must return 1 result, or 2 results, the second of which is an error.
159 func call(fn interface{}, args ...interface{}) (interface{}, error) {
160 v := reflect.ValueOf(fn)
162 if typ.Kind() != reflect.Func {
163 return nil, fmt.Errorf("non-function of type %s", typ)
166 return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
169 var dddType reflect.Type
170 if typ.IsVariadic() {
171 if len(args) < numIn-1 {
172 return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
174 dddType = typ.In(numIn - 1).Elem()
176 if len(args) != numIn {
177 return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
180 argv := make([]reflect.Value, len(args))
181 for i, arg := range args {
182 value := reflect.ValueOf(arg)
183 // Compute the expected type. Clumsy because of variadics.
184 var argType reflect.Type
185 if !typ.IsVariadic() || i < numIn-1 {
190 if !value.Type().AssignableTo(argType) {
191 return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
193 argv[i] = reflect.ValueOf(arg)
195 result := v.Call(argv)
196 if len(result) == 2 {
197 return result[0].Interface(), result[1].Interface().(error)
199 return result[0].Interface(), nil
204 func truth(a interface{}) bool {
205 t, _ := isTrue(reflect.ValueOf(a))
209 // and computes the Boolean AND of its arguments, returning
210 // the first false argument it encounters, or the last argument.
211 func and(arg0 interface{}, args ...interface{}) interface{} {
215 for i := range args {
224 // or computes the Boolean OR of its arguments, returning
225 // the first true argument it encounters, or the last argument.
226 func or(arg0 interface{}, args ...interface{}) interface{} {
230 for i := range args {
239 // not returns the Boolean negation of its argument.
240 func not(arg interface{}) (truth bool) {
241 truth, _ = isTrue(reflect.ValueOf(arg))
248 htmlQuot = []byte(""") // shorter than """
249 htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5
250 htmlAmp = []byte("&")
251 htmlLt = []byte("<")
252 htmlGt = []byte(">")
255 // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
256 func HTMLEscape(w io.Writer, b []byte) {
258 for i, c := range b {
281 // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
282 func HTMLEscapeString(s string) string {
283 // Avoid allocation if we can.
284 if strings.IndexAny(s, `'"&<>`) < 0 {
288 HTMLEscape(&b, []byte(s))
292 // HTMLEscaper returns the escaped HTML equivalent of the textual
293 // representation of its arguments.
294 func HTMLEscaper(args ...interface{}) string {
298 s, ok = args[0].(string)
301 s = fmt.Sprint(args...)
303 return HTMLEscapeString(s)
306 // JavaScript escaping.
309 jsLowUni = []byte(`\u00`)
310 hex = []byte("0123456789ABCDEF")
312 jsBackslash = []byte(`\\`)
313 jsApos = []byte(`\'`)
314 jsQuot = []byte(`\"`)
315 jsLt = []byte(`\x3C`)
316 jsGt = []byte(`\x3E`)
319 // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
320 func JSEscape(w io.Writer, b []byte) {
322 for i := 0; i < len(b); i++ {
325 if !jsIsSpecial(rune(c)) {
326 // fast path: nothing to do
331 if c < utf8.RuneSelf {
332 // Quotes, slashes and angle brackets get quoted.
333 // Control characters get written as \u00XX.
348 w.Write(hex[t : t+1])
349 w.Write(hex[b : b+1])
353 r, size := utf8.DecodeRune(b[i:])
354 if unicode.IsPrint(r) {
355 w.Write(b[i : i+size])
357 fmt.Fprintf(w, "\\u%04X", r)
366 // JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
367 func JSEscapeString(s string) string {
368 // Avoid allocation if we can.
369 if strings.IndexFunc(s, jsIsSpecial) < 0 {
373 JSEscape(&b, []byte(s))
377 func jsIsSpecial(r rune) bool {
379 case '\\', '\'', '"', '<', '>':
382 return r < ' ' || utf8.RuneSelf <= r
385 // JSEscaper returns the escaped JavaScript equivalent of the textual
386 // representation of its arguments.
387 func JSEscaper(args ...interface{}) string {
391 s, ok = args[0].(string)
394 s = fmt.Sprint(args...)
396 return JSEscapeString(s)
399 // URLQueryEscaper returns the escaped value of the textual representation of
400 // its arguments in a form suitable for embedding in a URL query.
401 func URLQueryEscaper(args ...interface{}) string {
404 s, ok = args[0].(string)
407 s = fmt.Sprint(args...)
409 return url.QueryEscape(s)