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  }