hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

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 }