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