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