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  }