github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/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 interface{}) 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 interface{}) 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 interface{}) 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 if s.helper != nil { 165 function, first, ok = s.helper.GetFunc(s.ctx, s.prep, name) 166 } 167 168 if !ok { 169 function, ok = findFunction(name, s.tmpl) 170 } 171 172 if !ok { 173 s.errorf("%q is not a defined function", name) 174 } 175 if first != zero { 176 return s.evalCall(dot, function, cmd, name, args, final, first) 177 } 178 return s.evalCall(dot, function, cmd, name, args, final) 179 } 180 181 // evalField evaluates an expression like (.Field) or (.Field arg1 arg2). 182 // The 'final' argument represents the return value from the preceding 183 // value of the pipeline, if any. 184 func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { 185 if !receiver.IsValid() { 186 if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key. 187 s.errorf("nil data; no entry for key %q", fieldName) 188 } 189 return zero 190 } 191 typ := receiver.Type() 192 receiver, isNil := indirect(receiver) 193 if receiver.Kind() == reflect.Interface && isNil { 194 // Calling a method on a nil interface can't work. The 195 // MethodByName method call below would panic. 196 s.errorf("nil pointer evaluating %s.%s", typ, fieldName) 197 return zero 198 } 199 200 // Unless it's an interface, need to get to a value of type *T to guarantee 201 // we see all methods of T and *T. 202 ptr := receiver 203 if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Ptr && ptr.CanAddr() { 204 ptr = ptr.Addr() 205 } 206 // Added for Hugo. 207 var first reflect.Value 208 var method reflect.Value 209 if s.helper != nil { 210 method, first = s.helper.GetMethod(s.ctx, s.prep, ptr, fieldName) 211 } else { 212 method = ptr.MethodByName(fieldName) 213 } 214 215 if method.IsValid() { 216 if first != zero { 217 return s.evalCall(dot, method, node, fieldName, args, final, first) 218 } 219 220 return s.evalCall(dot, method, node, fieldName, args, final) 221 } 222 223 hasArgs := len(args) > 1 || final != missingVal 224 // It's not a method; must be a field of a struct or an element of a map. 225 switch receiver.Kind() { 226 case reflect.Struct: 227 tField, ok := receiver.Type().FieldByName(fieldName) 228 if ok { 229 field := receiver.FieldByIndex(tField.Index) 230 if tField.PkgPath != "" { // field is unexported 231 s.errorf("%s is an unexported field of struct type %s", fieldName, typ) 232 } 233 // If it's a function, we must call it. 234 if hasArgs { 235 s.errorf("%s has arguments but cannot be invoked as function", fieldName) 236 } 237 return field 238 } 239 case reflect.Map: 240 // If it's a map, attempt to use the field name as a key. 241 nameVal := reflect.ValueOf(fieldName) 242 if nameVal.Type().AssignableTo(receiver.Type().Key()) { 243 if hasArgs { 244 s.errorf("%s is not a method but has arguments", fieldName) 245 } 246 var result reflect.Value 247 if s.helper != nil { 248 // Added for Hugo. 249 result, _ = s.helper.GetMapValue(s.ctx, s.prep, receiver, nameVal) 250 } else { 251 result = receiver.MapIndex(nameVal) 252 } 253 if !result.IsValid() { 254 switch s.tmpl.option.missingKey { 255 case mapInvalid: 256 // Just use the invalid value. 257 case mapZeroValue: 258 result = reflect.Zero(receiver.Type().Elem()) 259 case mapError: 260 s.errorf("map has no entry for key %q", fieldName) 261 } 262 } 263 return result 264 } 265 case reflect.Ptr: 266 etyp := receiver.Type().Elem() 267 if etyp.Kind() == reflect.Struct { 268 if _, ok := etyp.FieldByName(fieldName); !ok { 269 // If there's no such field, say "can't evaluate" 270 // instead of "nil pointer evaluating". 271 break 272 } 273 } 274 if isNil { 275 s.errorf("nil pointer evaluating %s.%s", typ, fieldName) 276 } 277 } 278 s.errorf("can't evaluate field %s in type %s", fieldName, typ) 279 panic("not reached") 280 } 281 282 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so 283 // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] 284 // as the function itself. 285 func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value { 286 if args != nil { 287 args = args[1:] // Zeroth arg is function name/node; not passed to function. 288 } 289 typ := fun.Type() 290 numFirst := len(first) 291 numIn := len(args) + numFirst // // Added for Hugo 292 if final != missingVal { 293 numIn++ 294 } 295 numFixed := len(args) + len(first) 296 if typ.IsVariadic() { 297 numFixed = typ.NumIn() - 1 // last arg is the variadic one. 298 if numIn < numFixed { 299 s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) 300 } 301 } else if numIn != typ.NumIn() { 302 s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn) 303 } 304 if !goodFunc(typ) { 305 // TODO: This could still be a confusing error; maybe goodFunc should provide info. 306 s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) 307 } 308 // Build the arg list. 309 argv := make([]reflect.Value, numIn) 310 // Args must be evaluated. Fixed args first. 311 i := len(first) 312 for ; i < numFixed && i < len(args)+numFirst; i++ { 313 argv[i] = s.evalArg(dot, typ.In(i), args[i-numFirst]) 314 } 315 // Now the ... args. 316 if typ.IsVariadic() { 317 argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. 318 for ; i < len(args)+numFirst; i++ { 319 argv[i] = s.evalArg(dot, argType, args[i-numFirst]) 320 } 321 322 } 323 // Add final value if necessary. 324 if final != missingVal { 325 t := typ.In(typ.NumIn() - 1) 326 if typ.IsVariadic() { 327 if numIn-1 < numFixed { 328 // The added final argument corresponds to a fixed parameter of the function. 329 // Validate against the type of the actual parameter. 330 t = typ.In(numIn - 1) 331 } else { 332 // The added final argument corresponds to the variadic part. 333 // Validate against the type of the elements of the variadic slice. 334 t = t.Elem() 335 } 336 } 337 argv[i] = s.validateType(final, t) 338 } 339 340 // Added for Hugo 341 for i := 0; i < len(first); i++ { 342 argv[i] = s.validateType(first[i], typ.In(i)) 343 } 344 345 v, err := safeCall(fun, argv) 346 // If we have an error that is not nil, stop execution and return that 347 // error to the caller. 348 if err != nil { 349 s.at(node) 350 s.errorf("error calling %s: %v", name, err) 351 } 352 if v.Type() == reflectValueType { 353 v = v.Interface().(reflect.Value) 354 } 355 return v 356 } 357 358 func isTrue(val reflect.Value) (truth, ok bool) { 359 return hreflect.IsTruthfulValue(val), true 360 }