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  }