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 }