hugo_template.go (12089B)
1 // Copyright 2022 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 "context" 18 "io" 19 "reflect" 20 21 "github.com/gohugoio/hugo/common/hreflect" 22 23 "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" 24 ) 25 26 /* 27 28 This files contains the Hugo related addons. All the other files in this 29 package is auto generated. 30 31 */ 32 33 // Export it so we can populate Hugo's func map with it, which makes it faster. 34 var GoFuncs = builtinFuncs() 35 36 // Preparer prepares the template before execution. 37 type Preparer interface { 38 Prepare() (*Template, error) 39 } 40 41 // ExecHelper allows some custom eval hooks. 42 type ExecHelper interface { 43 Init(ctx context.Context, tmpl Preparer) 44 GetFunc(ctx context.Context, tmpl Preparer, name string) (reflect.Value, reflect.Value, bool) 45 GetMethod(ctx context.Context, tmpl Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value) 46 GetMapValue(ctx context.Context, tmpl Preparer, receiver, key reflect.Value) (reflect.Value, bool) 47 } 48 49 // Executer executes a given template. 50 type Executer interface { 51 ExecuteWithContext(ctx context.Context, p Preparer, wr io.Writer, data any) error 52 } 53 54 type executer struct { 55 helper ExecHelper 56 } 57 58 func NewExecuter(helper ExecHelper) Executer { 59 return &executer{helper: helper} 60 } 61 62 type ( 63 dataContextKeyType string 64 hasLockContextKeyType string 65 ) 66 67 const ( 68 // The data object passed to Execute or ExecuteWithContext gets stored with this key if not already set. 69 DataContextKey = dataContextKeyType("data") 70 // Used in partialCached to signal to nested templates that a lock is already taken. 71 HasLockContextKey = hasLockContextKeyType("hasLock") 72 ) 73 74 // Note: The context is currently not fully implemeted in Hugo. This is a work in progress. 75 func (t *executer) ExecuteWithContext(ctx context.Context, p Preparer, wr io.Writer, data any) error { 76 tmpl, err := p.Prepare() 77 if err != nil { 78 return err 79 } 80 81 if v := ctx.Value(DataContextKey); v == nil { 82 ctx = context.WithValue(ctx, DataContextKey, data) 83 } 84 85 value, ok := data.(reflect.Value) 86 if !ok { 87 value = reflect.ValueOf(data) 88 } 89 90 state := &state{ 91 ctx: ctx, 92 helper: t.helper, 93 prep: p, 94 tmpl: tmpl, 95 wr: wr, 96 vars: []variable{{"$", value}}, 97 } 98 99 t.helper.Init(ctx, p) 100 101 return tmpl.executeWithState(state, value) 102 } 103 104 func (t *executer) Execute(p Preparer, wr io.Writer, data any) error { 105 tmpl, err := p.Prepare() 106 if err != nil { 107 return err 108 } 109 110 value, ok := data.(reflect.Value) 111 if !ok { 112 value = reflect.ValueOf(data) 113 } 114 115 state := &state{ 116 helper: t.helper, 117 prep: p, 118 tmpl: tmpl, 119 wr: wr, 120 vars: []variable{{"$", value}}, 121 } 122 123 return tmpl.executeWithState(state, value) 124 } 125 126 // Prepare returns a template ready for execution. 127 func (t *Template) Prepare() (*Template, error) { 128 return t, nil 129 } 130 131 func (t *Template) executeWithState(state *state, value reflect.Value) (err error) { 132 defer errRecover(&err) 133 if t.Tree == nil || t.Root == nil { 134 state.errorf("%q is an incomplete or empty template", t.Name()) 135 } 136 state.walk(value, t.Root) 137 return 138 } 139 140 // Below are modified structs etc. The changes are marked with "Added for Hugo." 141 142 // state represents the state of an execution. It's not part of the 143 // template so that multiple executions of the same template 144 // can execute in parallel. 145 type state struct { 146 tmpl *Template 147 ctx context.Context // Added for Hugo. The orignal data context. 148 prep Preparer // Added for Hugo. 149 helper ExecHelper // Added for Hugo. 150 wr io.Writer 151 node parse.Node // current node, for errors 152 vars []variable // push-down stack of variable values. 153 depth int // the height of the stack of executing templates. 154 } 155 156 func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { 157 s.at(node) 158 name := node.Ident 159 160 var function reflect.Value 161 // Added for Hugo. 162 var first reflect.Value 163 var ok bool 164 var isBuiltin bool 165 if s.helper != nil { 166 isBuiltin = name == "and" || name == "or" 167 function, first, ok = s.helper.GetFunc(s.ctx, s.prep, name) 168 } 169 170 if !ok { 171 function, isBuiltin, ok = findFunction(name, s.tmpl) 172 } 173 174 if !ok { 175 s.errorf("%q is not a defined function", name) 176 } 177 if first != zero { 178 return s.evalCall(dot, function, isBuiltin, cmd, name, args, final, first) 179 } 180 return s.evalCall(dot, function, isBuiltin, cmd, name, args, final) 181 } 182 183 // evalField evaluates an expression like (.Field) or (.Field arg1 arg2). 184 // The 'final' argument represents the return value from the preceding 185 // value of the pipeline, if any. 186 func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { 187 if !receiver.IsValid() { 188 if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key. 189 s.errorf("nil data; no entry for key %q", fieldName) 190 } 191 return zero 192 } 193 typ := receiver.Type() 194 receiver, isNil := indirect(receiver) 195 if receiver.Kind() == reflect.Interface && isNil { 196 // Calling a method on a nil interface can't work. The 197 // MethodByName method call below would panic. 198 s.errorf("nil pointer evaluating %s.%s", typ, fieldName) 199 return zero 200 } 201 202 // Unless it's an interface, need to get to a value of type *T to guarantee 203 // we see all methods of T and *T. 204 ptr := receiver 205 if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Pointer && ptr.CanAddr() { 206 ptr = ptr.Addr() 207 } 208 209 // Added for Hugo. 210 var first reflect.Value 211 var method reflect.Value 212 if s.helper != nil { 213 method, first = s.helper.GetMethod(s.ctx, s.prep, ptr, fieldName) 214 } else { 215 method = ptr.MethodByName(fieldName) 216 } 217 218 if method.IsValid() { 219 if first != zero { 220 return s.evalCall(dot, method, false, node, fieldName, args, final, first) 221 } 222 223 return s.evalCall(dot, method, false, node, fieldName, args, final) 224 } 225 226 if method := ptr.MethodByName(fieldName); method.IsValid() { 227 return s.evalCall(dot, method, false, node, fieldName, args, final) 228 } 229 hasArgs := len(args) > 1 || final != missingVal 230 // It's not a method; must be a field of a struct or an element of a map. 231 switch receiver.Kind() { 232 case reflect.Struct: 233 tField, ok := receiver.Type().FieldByName(fieldName) 234 if ok { 235 field, err := receiver.FieldByIndexErr(tField.Index) 236 if !tField.IsExported() { 237 s.errorf("%s is an unexported field of struct type %s", fieldName, typ) 238 } 239 if err != nil { 240 s.errorf("%v", err) 241 } 242 // If it's a function, we must call it. 243 if hasArgs { 244 s.errorf("%s has arguments but cannot be invoked as function", fieldName) 245 } 246 return field 247 } 248 case reflect.Map: 249 // If it's a map, attempt to use the field name as a key. 250 nameVal := reflect.ValueOf(fieldName) 251 if nameVal.Type().AssignableTo(receiver.Type().Key()) { 252 if hasArgs { 253 s.errorf("%s is not a method but has arguments", fieldName) 254 } 255 var result reflect.Value 256 if s.helper != nil { 257 // Added for Hugo. 258 result, _ = s.helper.GetMapValue(s.ctx, s.prep, receiver, nameVal) 259 } else { 260 result = receiver.MapIndex(nameVal) 261 } 262 if !result.IsValid() { 263 switch s.tmpl.option.missingKey { 264 case mapInvalid: 265 // Just use the invalid value. 266 case mapZeroValue: 267 result = reflect.Zero(receiver.Type().Elem()) 268 case mapError: 269 s.errorf("map has no entry for key %q", fieldName) 270 } 271 } 272 return result 273 } 274 case reflect.Pointer: 275 etyp := receiver.Type().Elem() 276 if etyp.Kind() == reflect.Struct { 277 if _, ok := etyp.FieldByName(fieldName); !ok { 278 // If there's no such field, say "can't evaluate" 279 // instead of "nil pointer evaluating". 280 break 281 } 282 } 283 if isNil { 284 s.errorf("nil pointer evaluating %s.%s", typ, fieldName) 285 } 286 } 287 s.errorf("can't evaluate field %s in type %s", fieldName, typ) 288 panic("not reached") 289 } 290 291 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so 292 // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] 293 // as the function itself. 294 func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value { 295 if args != nil { 296 args = args[1:] // Zeroth arg is function name/node; not passed to function. 297 } 298 299 typ := fun.Type() 300 numFirst := len(first) 301 numIn := len(args) + numFirst // Added for Hugo 302 if final != missingVal { 303 numIn++ 304 } 305 numFixed := len(args) + len(first) // Adjusted for Hugo 306 if typ.IsVariadic() { 307 numFixed = typ.NumIn() - 1 // last arg is the variadic one. 308 if numIn < numFixed { 309 s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) 310 } 311 } else if numIn != typ.NumIn() { 312 s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn) 313 } 314 if !goodFunc(typ) { 315 // TODO: This could still be a confusing error; maybe goodFunc should provide info. 316 s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) 317 } 318 319 unwrap := func(v reflect.Value) reflect.Value { 320 if v.Type() == reflectValueType { 321 v = v.Interface().(reflect.Value) 322 } 323 return v 324 } 325 326 // Special case for builtin and/or, which short-circuit. 327 if isBuiltin && (name == "and" || name == "or") { 328 argType := typ.In(0) 329 var v reflect.Value 330 for _, arg := range args { 331 v = s.evalArg(dot, argType, arg).Interface().(reflect.Value) 332 if truth(v) == (name == "or") { 333 // This value was already unwrapped 334 // by the .Interface().(reflect.Value). 335 return v 336 } 337 } 338 if final != missingVal { 339 // The last argument to and/or is coming from 340 // the pipeline. We didn't short circuit on an earlier 341 // argument, so we are going to return this one. 342 // We don't have to evaluate final, but we do 343 // have to check its type. Then, since we are 344 // going to return it, we have to unwrap it. 345 v = unwrap(s.validateType(final, argType)) 346 } 347 return v 348 } 349 350 // Build the arg list. 351 argv := make([]reflect.Value, numIn) 352 // Args must be evaluated. Fixed args first. 353 i := len(first) // Adjusted for Hugo. 354 for ; i < numFixed && i < len(args)+numFirst; i++ { // Adjusted for Hugo. 355 argv[i] = s.evalArg(dot, typ.In(i), args[i-numFirst]) // Adjusted for Hugo. 356 } 357 // Now the ... args. 358 if typ.IsVariadic() { 359 argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. 360 for ; i < len(args)+numFirst; i++ { // Adjusted for Hugo. 361 argv[i] = s.evalArg(dot, argType, args[i-numFirst]) // Adjusted for Hugo. 362 } 363 } 364 // Add final value if necessary. 365 if final != missingVal { 366 t := typ.In(typ.NumIn() - 1) 367 if typ.IsVariadic() { 368 if numIn-1 < numFixed { 369 // The added final argument corresponds to a fixed parameter of the function. 370 // Validate against the type of the actual parameter. 371 t = typ.In(numIn - 1) 372 } else { 373 // The added final argument corresponds to the variadic part. 374 // Validate against the type of the elements of the variadic slice. 375 t = t.Elem() 376 } 377 } 378 argv[i] = s.validateType(final, t) 379 } 380 381 // Added for Hugo 382 for i := 0; i < len(first); i++ { 383 argv[i] = s.validateType(first[i], typ.In(i)) 384 } 385 386 v, err := safeCall(fun, argv) 387 // If we have an error that is not nil, stop execution and return that 388 // error to the caller. 389 if err != nil { 390 s.at(node) 391 s.errorf("error calling %s: %w", name, err) 392 } 393 return unwrap(v) 394 } 395 396 func isTrue(val reflect.Value) (truth, ok bool) { 397 return hreflect.IsTruthfulValue(val), true 398 }