github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/tpl/internal/go_templates/texttemplate/hugo_template.go (about) 1 // Copyright 2019 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package template 15 16 import ( 17 "io" 18 "reflect" 19 20 "github.com/gohugoio/hugo/common/hreflect" 21 22 "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" 23 ) 24 25 /* 26 27 This files contains the Hugo related addons. All the other files in this 28 package is auto generated. 29 30 */ 31 32 // Export it so we can populate Hugo's func map with it, which makes it faster. 33 var GoFuncs = builtinFuncs() 34 35 // Preparer prepares the template before execution. 36 type Preparer interface { 37 Prepare() (*Template, error) 38 } 39 40 // ExecHelper allows some custom eval hooks. 41 type ExecHelper interface { 42 GetFunc(tmpl Preparer, name string) (reflect.Value, bool) 43 GetMethod(tmpl Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value) 44 GetMapValue(tmpl Preparer, receiver, key reflect.Value) (reflect.Value, bool) 45 } 46 47 // Executer executes a given template. 48 type Executer interface { 49 Execute(p Preparer, wr io.Writer, data interface{}) error 50 } 51 52 type executer struct { 53 helper ExecHelper 54 } 55 56 func NewExecuter(helper ExecHelper) Executer { 57 return &executer{helper: helper} 58 } 59 60 func (t *executer) Execute(p Preparer, wr io.Writer, data interface{}) error { 61 tmpl, err := p.Prepare() 62 if err != nil { 63 return err 64 } 65 66 value, ok := data.(reflect.Value) 67 if !ok { 68 value = reflect.ValueOf(data) 69 } 70 71 state := &state{ 72 helper: t.helper, 73 prep: p, 74 tmpl: tmpl, 75 wr: wr, 76 vars: []variable{{"$", value}}, 77 } 78 79 return tmpl.executeWithState(state, value) 80 81 } 82 83 // Prepare returns a template ready for execution. 84 func (t *Template) Prepare() (*Template, error) { 85 return t, nil 86 } 87 88 func (t *Template) executeWithState(state *state, value reflect.Value) (err error) { 89 defer errRecover(&err) 90 if t.Tree == nil || t.Root == nil { 91 state.errorf("%q is an incomplete or empty template", t.Name()) 92 } 93 state.walk(value, t.Root) 94 return 95 } 96 97 // Below are modified structs etc. The changes are marked with "Added for Hugo." 98 99 // state represents the state of an execution. It's not part of the 100 // template so that multiple executions of the same template 101 // can execute in parallel. 102 type state struct { 103 tmpl *Template 104 prep Preparer // Added for Hugo. 105 helper ExecHelper // Added for Hugo. 106 wr io.Writer 107 node parse.Node // current node, for errors 108 vars []variable // push-down stack of variable values. 109 depth int // the height of the stack of executing templates. 110 } 111 112 func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { 113 s.at(node) 114 name := node.Ident 115 116 var function reflect.Value 117 var ok bool 118 if s.helper != nil { 119 // Added for Hugo. 120 function, ok = s.helper.GetFunc(s.prep, name) 121 } 122 123 if !ok { 124 function, ok = findFunction(name, s.tmpl) 125 } 126 127 if !ok { 128 s.errorf("%q is not a defined function", name) 129 } 130 return s.evalCall(dot, function, cmd, name, args, final) 131 } 132 133 // evalField evaluates an expression like (.Field) or (.Field arg1 arg2). 134 // The 'final' argument represents the return value from the preceding 135 // value of the pipeline, if any. 136 func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { 137 if !receiver.IsValid() { 138 if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key. 139 s.errorf("nil data; no entry for key %q", fieldName) 140 } 141 return zero 142 } 143 typ := receiver.Type() 144 receiver, isNil := indirect(receiver) 145 if receiver.Kind() == reflect.Interface && isNil { 146 // Calling a method on a nil interface can't work. The 147 // MethodByName method call below would panic. 148 s.errorf("nil pointer evaluating %s.%s", typ, fieldName) 149 return zero 150 } 151 152 // Unless it's an interface, need to get to a value of type *T to guarantee 153 // we see all methods of T and *T. 154 ptr := receiver 155 if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Ptr && ptr.CanAddr() { 156 ptr = ptr.Addr() 157 } 158 // Added for Hugo. 159 var first reflect.Value 160 var method reflect.Value 161 if s.helper != nil { 162 method, first = s.helper.GetMethod(s.prep, ptr, fieldName) 163 } else { 164 method = ptr.MethodByName(fieldName) 165 } 166 167 if method.IsValid() { 168 if first != zero { 169 return s.evalCall(dot, method, node, fieldName, args, final, first) 170 } 171 172 return s.evalCall(dot, method, node, fieldName, args, final) 173 } 174 175 hasArgs := len(args) > 1 || final != missingVal 176 // It's not a method; must be a field of a struct or an element of a map. 177 switch receiver.Kind() { 178 case reflect.Struct: 179 tField, ok := receiver.Type().FieldByName(fieldName) 180 if ok { 181 field := receiver.FieldByIndex(tField.Index) 182 if tField.PkgPath != "" { // field is unexported 183 s.errorf("%s is an unexported field of struct type %s", fieldName, typ) 184 } 185 // If it's a function, we must call it. 186 if hasArgs { 187 s.errorf("%s has arguments but cannot be invoked as function", fieldName) 188 } 189 return field 190 } 191 case reflect.Map: 192 // If it's a map, attempt to use the field name as a key. 193 nameVal := reflect.ValueOf(fieldName) 194 if nameVal.Type().AssignableTo(receiver.Type().Key()) { 195 if hasArgs { 196 s.errorf("%s is not a method but has arguments", fieldName) 197 } 198 var result reflect.Value 199 if s.helper != nil { 200 // Added for Hugo. 201 result, _ = s.helper.GetMapValue(s.prep, receiver, nameVal) 202 } else { 203 result = receiver.MapIndex(nameVal) 204 } 205 if !result.IsValid() { 206 switch s.tmpl.option.missingKey { 207 case mapInvalid: 208 // Just use the invalid value. 209 case mapZeroValue: 210 result = reflect.Zero(receiver.Type().Elem()) 211 case mapError: 212 s.errorf("map has no entry for key %q", fieldName) 213 } 214 } 215 return result 216 } 217 case reflect.Ptr: 218 etyp := receiver.Type().Elem() 219 if etyp.Kind() == reflect.Struct { 220 if _, ok := etyp.FieldByName(fieldName); !ok { 221 // If there's no such field, say "can't evaluate" 222 // instead of "nil pointer evaluating". 223 break 224 } 225 } 226 if isNil { 227 s.errorf("nil pointer evaluating %s.%s", typ, fieldName) 228 } 229 } 230 s.errorf("can't evaluate field %s in type %s", fieldName, typ) 231 panic("not reached") 232 } 233 234 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so 235 // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] 236 // as the function itself. 237 func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value { 238 if args != nil { 239 args = args[1:] // Zeroth arg is function name/node; not passed to function. 240 } 241 typ := fun.Type() 242 numFirst := len(first) 243 numIn := len(args) + numFirst // // Added for Hugo 244 if final != missingVal { 245 numIn++ 246 } 247 numFixed := len(args) + len(first) 248 if typ.IsVariadic() { 249 numFixed = typ.NumIn() - 1 // last arg is the variadic one. 250 if numIn < numFixed { 251 s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) 252 } 253 } else if numIn != typ.NumIn() { 254 s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn) 255 } 256 if !goodFunc(typ) { 257 // TODO: This could still be a confusing error; maybe goodFunc should provide info. 258 s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) 259 } 260 // Build the arg list. 261 argv := make([]reflect.Value, numIn) 262 // Args must be evaluated. Fixed args first. 263 i := len(first) 264 for ; i < numFixed && i < len(args)+numFirst; i++ { 265 argv[i] = s.evalArg(dot, typ.In(i), args[i-numFirst]) 266 } 267 // Now the ... args. 268 if typ.IsVariadic() { 269 argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. 270 for ; i < len(args)+numFirst; i++ { 271 argv[i] = s.evalArg(dot, argType, args[i-numFirst]) 272 } 273 274 } 275 // Add final value if necessary. 276 if final != missingVal { 277 t := typ.In(typ.NumIn() - 1) 278 if typ.IsVariadic() { 279 if numIn-1 < numFixed { 280 // The added final argument corresponds to a fixed parameter of the function. 281 // Validate against the type of the actual parameter. 282 t = typ.In(numIn - 1) 283 } else { 284 // The added final argument corresponds to the variadic part. 285 // Validate against the type of the elements of the variadic slice. 286 t = t.Elem() 287 } 288 } 289 argv[i] = s.validateType(final, t) 290 } 291 292 // Added for Hugo 293 for i := 0; i < len(first); i++ { 294 argv[i] = s.validateType(first[i], typ.In(i)) 295 } 296 297 v, err := safeCall(fun, argv) 298 // If we have an error that is not nil, stop execution and return that 299 // error to the caller. 300 if err != nil { 301 s.at(node) 302 s.errorf("error calling %s: %v", name, err) 303 } 304 if v.Type() == reflectValueType { 305 v = v.Interface().(reflect.Value) 306 } 307 return v 308 } 309 310 func isTrue(val reflect.Value) (truth, ok bool) { 311 return hreflect.IsTruthfulValue(val), true 312 }