Source file src/pkg/text/template/funcs.go
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. 4 5 package template 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "net/url" 12 "reflect" 13 "strings" 14 "unicode" 15 "unicode/utf8" 16 ) 17 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{} 24 25 var builtins = FuncMap{ 26 "and": and, 27 "call": call, 28 "html": HTMLEscaper, 29 "index": index, 30 "js": JSEscaper, 31 "len": length, 32 "not": not, 33 "or": or, 34 "print": fmt.Sprint, 35 "printf": fmt.Sprintf, 36 "println": fmt.Sprintln, 37 "urlquery": URLQueryEscaper, 38 } 39 40 var builtinFuncs = createValueFuncs(builtins) 41 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) 46 return m 47 } 48 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") 55 } 56 if !goodFunc(v.Type()) { 57 panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) 58 } 59 out[name] = v 60 } 61 } 62 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 { 67 out[name] = fn 68 } 69 } 70 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. 74 switch { 75 case typ.NumOut() == 1: 76 return true 77 case typ.NumOut() == 2 && typ.Out(1) == errorType: 78 return true 79 } 80 return false 81 } 82 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() { 87 return fn, true 88 } 89 } 90 if fn := builtinFuncs[name]; fn.IsValid() { 91 return fn, true 92 } 93 return reflect.Value{}, false 94 } 95 96 // Indexing. 97 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) 105 var isNil bool 106 if v, isNil = indirect(v); isNil { 107 return nil, fmt.Errorf("index of nil pointer") 108 } 109 switch v.Kind() { 110 case reflect.Array, reflect.Slice: 111 var x int64 112 switch index.Kind() { 113 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 114 x = index.Int() 115 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 116 x = int64(index.Uint()) 117 default: 118 return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) 119 } 120 if x < 0 || x >= int64(v.Len()) { 121 return nil, fmt.Errorf("index out of range: %d", x) 122 } 123 v = v.Index(int(x)) 124 case reflect.Map: 125 if !index.Type().AssignableTo(v.Type().Key()) { 126 return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) 127 } 128 if x := v.MapIndex(index); x.IsValid() { 129 v = x 130 } else { 131 v = reflect.Zero(v.Type().Key()) 132 } 133 default: 134 return nil, fmt.Errorf("can't index item of type %s", index.Type()) 135 } 136 } 137 return v.Interface(), nil 138 } 139 140 // Length 141 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)) 145 if isNil { 146 return 0, fmt.Errorf("len of nil pointer") 147 } 148 switch v.Kind() { 149 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: 150 return v.Len(), nil 151 } 152 return 0, fmt.Errorf("len of type %s", v.Type()) 153 } 154 155 // Function invocation 156 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) 161 typ := v.Type() 162 if typ.Kind() != reflect.Func { 163 return nil, fmt.Errorf("non-function of type %s", typ) 164 } 165 if !goodFunc(typ) { 166 return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) 167 } 168 numIn := typ.NumIn() 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) 173 } 174 dddType = typ.In(numIn - 1).Elem() 175 } else { 176 if len(args) != numIn { 177 return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) 178 } 179 } 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 { 186 argType = typ.In(i) 187 } else { 188 argType = dddType 189 } 190 if !value.Type().AssignableTo(argType) { 191 return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType) 192 } 193 argv[i] = reflect.ValueOf(arg) 194 } 195 result := v.Call(argv) 196 if len(result) == 2 { 197 return result[0].Interface(), result[1].Interface().(error) 198 } 199 return result[0].Interface(), nil 200 } 201 202 // Boolean logic. 203 204 func truth(a interface{}) bool { 205 t, _ := isTrue(reflect.ValueOf(a)) 206 return t 207 } 208 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{} { 212 if !truth(arg0) { 213 return arg0 214 } 215 for i := range args { 216 arg0 = args[i] 217 if !truth(arg0) { 218 break 219 } 220 } 221 return arg0 222 } 223 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{} { 227 if truth(arg0) { 228 return arg0 229 } 230 for i := range args { 231 arg0 = args[i] 232 if truth(arg0) { 233 break 234 } 235 } 236 return arg0 237 } 238 239 // not returns the Boolean negation of its argument. 240 func not(arg interface{}) (truth bool) { 241 truth, _ = isTrue(reflect.ValueOf(arg)) 242 return !truth 243 } 244 245 // HTML escaping. 246 247 var ( 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(">") 253 ) 254 255 // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. 256 func HTMLEscape(w io.Writer, b []byte) { 257 last := 0 258 for i, c := range b { 259 var html []byte 260 switch c { 261 case '"': 262 html = htmlQuot 263 case '\'': 264 html = htmlApos 265 case '&': 266 html = htmlAmp 267 case '<': 268 html = htmlLt 269 case '>': 270 html = htmlGt 271 default: 272 continue 273 } 274 w.Write(b[last:i]) 275 w.Write(html) 276 last = i + 1 277 } 278 w.Write(b[last:]) 279 } 280 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 { 285 return s 286 } 287 var b bytes.Buffer 288 HTMLEscape(&b, []byte(s)) 289 return b.String() 290 } 291 292 // HTMLEscaper returns the escaped HTML equivalent of the textual 293 // representation of its arguments. 294 func HTMLEscaper(args ...interface{}) string { 295 ok := false 296 var s string 297 if len(args) == 1 { 298 s, ok = args[0].(string) 299 } 300 if !ok { 301 s = fmt.Sprint(args...) 302 } 303 return HTMLEscapeString(s) 304 } 305 306 // JavaScript escaping. 307 308 var ( 309 jsLowUni = []byte(`\u00`) 310 hex = []byte("0123456789ABCDEF") 311 312 jsBackslash = []byte(`\\`) 313 jsApos = []byte(`\'`) 314 jsQuot = []byte(`\"`) 315 jsLt = []byte(`\x3C`) 316 jsGt = []byte(`\x3E`) 317 ) 318 319 // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. 320 func JSEscape(w io.Writer, b []byte) { 321 last := 0 322 for i := 0; i < len(b); i++ { 323 c := b[i] 324 325 if !jsIsSpecial(rune(c)) { 326 // fast path: nothing to do 327 continue 328 } 329 w.Write(b[last:i]) 330 331 if c < utf8.RuneSelf { 332 // Quotes, slashes and angle brackets get quoted. 333 // Control characters get written as \u00XX. 334 switch c { 335 case '\\': 336 w.Write(jsBackslash) 337 case '\'': 338 w.Write(jsApos) 339 case '"': 340 w.Write(jsQuot) 341 case '<': 342 w.Write(jsLt) 343 case '>': 344 w.Write(jsGt) 345 default: 346 w.Write(jsLowUni) 347 t, b := c>>4, c&0x0f 348 w.Write(hex[t : t+1]) 349 w.Write(hex[b : b+1]) 350 } 351 } else { 352 // Unicode rune. 353 r, size := utf8.DecodeRune(b[i:]) 354 if unicode.IsPrint(r) { 355 w.Write(b[i : i+size]) 356 } else { 357 fmt.Fprintf(w, "\\u%04X", r) 358 } 359 i += size - 1 360 } 361 last = i + 1 362 } 363 w.Write(b[last:]) 364 } 365 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 { 370 return s 371 } 372 var b bytes.Buffer 373 JSEscape(&b, []byte(s)) 374 return b.String() 375 } 376 377 func jsIsSpecial(r rune) bool { 378 switch r { 379 case '\\', '\'', '"', '<', '>': 380 return true 381 } 382 return r < ' ' || utf8.RuneSelf <= r 383 } 384 385 // JSEscaper returns the escaped JavaScript equivalent of the textual 386 // representation of its arguments. 387 func JSEscaper(args ...interface{}) string { 388 ok := false 389 var s string 390 if len(args) == 1 { 391 s, ok = args[0].(string) 392 } 393 if !ok { 394 s = fmt.Sprint(args...) 395 } 396 return JSEscapeString(s) 397 } 398 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 { 402 s, ok := "", false 403 if len(args) == 1 { 404 s, ok = args[0].(string) 405 } 406 if !ok { 407 s = fmt.Sprint(args...) 408 } 409 return url.QueryEscape(s) 410 }