// Copyright 2016 José Santos // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jet import ( "bytes" "errors" "fmt" "io" "reflect" "runtime" "strconv" "strings" "sync" "github.com/CloudyKit/fastprinter" ) var ( funcType = reflect.TypeOf(Func(nil)) stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() rangerType = reflect.TypeOf((*Ranger)(nil)).Elem() rendererType = reflect.TypeOf((*Renderer)(nil)).Elem() safeWriterType = reflect.TypeOf(SafeWriter(nil)) pool_State = sync.Pool{ New: func() interface{} { return &Runtime{scope: &scope{}, escapeeWriter: new(escapeeWriter)} }, } ) // Renderer is used to detect if a value has its own rendering logic. If the value an action evaluates to implements this // interface, it will not be printed using github.com/CloudyKit/fastprinter, instead, its Render() method will be called // and is responsible for writing the value to the render output. type Renderer interface { Render(*Runtime) } // RendererFunc func implementing interface Renderer type RendererFunc func(*Runtime) func (renderer RendererFunc) Render(r *Runtime) { renderer(r) } type escapeeWriter struct { Writer io.Writer escapee SafeWriter set *Set } func (w *escapeeWriter) Write(b []byte) (int, error) { if w.set.escapee == nil { w.Writer.Write(b) } else { w.set.escapee(w.Writer, b) } return 0, nil } // Runtime this type holds the state of the execution of an template type Runtime struct { *escapeeWriter *scope content func(*Runtime, Expression) translator Translator context reflect.Value } // Context returns the current context value func (r *Runtime) Context() reflect.Value { return r.context } func (st *Runtime) newScope() { st.scope = &scope{parent: st.scope, variables: make(VarMap), blocks: st.blocks} } func (st *Runtime) releaseScope() { st.scope = st.scope.parent } type scope struct { parent *scope variables VarMap blocks map[string]*BlockNode } // YieldBlock yields a block in the current context, will panic if the context is not available func (st *Runtime) YieldBlock(name string, context interface{}) { block, has := st.getBlock(name) if has == false { panic(fmt.Errorf("Block %q was not found!!", name)) } if context != nil { current := st.context st.context = reflect.ValueOf(context) st.executeList(block.List) st.context = current } st.executeList(block.List) } func (st *scope) getBlock(name string) (block *BlockNode, has bool) { block, has = st.blocks[name] for !has && st.parent != nil { st = st.parent block, has = st.blocks[name] } return } func (state *Runtime) setValue(name string, val reflect.Value) error { // try changing existing variable in current or parent scope sc := state.scope for sc != nil { if _, ok := sc.variables[name]; ok { sc.variables[name] = val return nil } sc = sc.parent } return fmt.Errorf("could not assign %q = %v because variable %q is uninitialised", name, val, name) } // LetGlobal sets or initialises a variable in the top-most template scope. func (state *Runtime) LetGlobal(name string, val interface{}) { sc := state.scope // walk up to top-most valid scope for sc.parent != nil && sc.parent.variables != nil { sc = sc.parent } sc.variables[name] = reflect.ValueOf(val) } // Set sets an existing variable in the template scope it lives in. func (state *Runtime) Set(name string, val interface{}) error { return state.setValue(name, reflect.ValueOf(val)) } // Let initialises a variable in the current template scope (possibly shadowing an existing variable of the same name in a parent scope). func (state *Runtime) Let(name string, val interface{}) { state.scope.variables[name] = reflect.ValueOf(val) } // SetOrLet calls Set() (if a variable with the given name is visible from the current scope) or Let() (if there is no variable with the given name in the current or any parent scope). func (state *Runtime) SetOrLet(name string, val interface{}) { _, err := state.resolve(name) if err != nil { state.Let(name, val) } else { state.Set(name, val) } } // Resolve resolves a value from the execution context. func (state *Runtime) resolve(name string) (reflect.Value, error) { if name == "." { return state.context, nil } // try current, then parent variable scopes sc := state.scope for sc != nil { v, ok := sc.variables[name] if ok { return indirectEface(v), nil } sc = sc.parent } // try globals state.set.gmx.RLock() v, ok := state.set.globals[name] state.set.gmx.RUnlock() if ok { return indirectEface(v), nil } // try default variables v, ok = defaultVariables[name] if ok { return indirectEface(v), nil } return reflect.Value{}, fmt.Errorf("identifier %q not available in current (%+v) or parent scope, global, or default variables", name, state.scope.variables) } // Resolve calls resolve() and ignores any errors, meaning it may return a zero reflect.Value. func (state *Runtime) Resolve(name string) reflect.Value { v, _ := state.resolve(name) return v } // Resolve calls resolve() and panics if there is an error. func (state *Runtime) MustResolve(name string) reflect.Value { v, err := state.resolve(name) if err != nil { panic(err) } return v } func (st *Runtime) recover(err *error) { // reset state scope and context just to be safe (they might not be cleared properly if there was a panic while using the state) st.scope = &scope{} st.context = reflect.Value{} pool_State.Put(st) if recovered := recover(); recovered != nil { var ok bool if _, ok = recovered.(runtime.Error); ok { panic(recovered) } *err, ok = recovered.(error) if !ok { panic(recovered) } } } func (st *Runtime) executeSet(left Expression, right reflect.Value) { typ := left.Type() if typ == NodeIdentifier { err := st.setValue(left.(*IdentifierNode).Ident, right) if err != nil { left.error(err) } return } var value reflect.Value var fields []string if typ == NodeChain { chain := left.(*ChainNode) value = st.evalPrimaryExpressionGroup(chain.Node) fields = chain.Field } else { fields = left.(*FieldNode).Ident value = st.context } lef := len(fields) - 1 for i := 0; i < lef; i++ { var err error value, err = resolveIndex(value, reflect.ValueOf(fields[i])) if err != nil { left.errorf("%v", err) } } RESTART: switch value.Kind() { case reflect.Ptr: value = value.Elem() goto RESTART case reflect.Struct: value = value.FieldByName(fields[lef]) if !value.IsValid() { left.errorf("identifier %q is not available in the current scope", fields[lef]) } value.Set(right) case reflect.Map: value.SetMapIndex(reflect.ValueOf(&fields[lef]).Elem(), right) } } func (st *Runtime) executeSetList(set *SetNode) { if set.IndexExprGetLookup { value := st.evalPrimaryExpressionGroup(set.Right[0]) st.executeSet(set.Left[0], value) if value.IsValid() { st.executeSet(set.Left[1], valueBoolTRUE) } else { st.executeSet(set.Left[1], valueBoolFALSE) } } else { for i := 0; i < len(set.Left); i++ { st.executeSet(set.Left[i], st.evalPrimaryExpressionGroup(set.Right[i])) } } } func (st *Runtime) executeLetList(set *SetNode) { if set.IndexExprGetLookup { value := st.evalPrimaryExpressionGroup(set.Right[0]) st.variables[set.Left[0].(*IdentifierNode).Ident] = value if value.IsValid() { st.variables[set.Left[1].(*IdentifierNode).Ident] = valueBoolTRUE } else { st.variables[set.Left[1].(*IdentifierNode).Ident] = valueBoolFALSE } } else { for i := 0; i < len(set.Left); i++ { st.variables[set.Left[i].(*IdentifierNode).Ident] = st.evalPrimaryExpressionGroup(set.Right[i]) } } } func (st *Runtime) executeYieldBlock(block *BlockNode, blockParam, yieldParam *BlockParameterList, expression Expression, content *ListNode) { needNewScope := len(blockParam.List) > 0 || len(yieldParam.List) > 0 if needNewScope { st.newScope() for i := 0; i < len(yieldParam.List); i++ { p := &yieldParam.List[i] if p.Expression == nil { block.errorf("missing name for block parameter '%s'", blockParam.List[i].Identifier) } st.variables[p.Identifier] = st.evalPrimaryExpressionGroup(p.Expression) } for i := 0; i < len(blockParam.List); i++ { p := &blockParam.List[i] if _, found := st.variables[p.Identifier]; !found { if p.Expression == nil { st.variables[p.Identifier] = valueBoolFALSE } else { st.variables[p.Identifier] = st.evalPrimaryExpressionGroup(p.Expression) } } } } mycontent := st.content if content != nil { myscope := st.scope st.content = func(st *Runtime, expression Expression) { outscope := st.scope outcontent := st.content st.scope = myscope st.content = mycontent if expression != nil { context := st.context st.context = st.evalPrimaryExpressionGroup(expression) st.executeList(content) st.context = context } else { st.executeList(content) } st.scope = outscope st.content = outcontent } } if expression != nil { context := st.context st.context = st.evalPrimaryExpressionGroup(expression) st.executeList(block.List) st.context = context } else { st.executeList(block.List) } st.content = mycontent if needNewScope { st.releaseScope() } } func (st *Runtime) executeList(list *ListNode) (returnValue reflect.Value) { inNewScope := false // to use just one scope for multiple actions with variable declarations for i := 0; i < len(list.Nodes); i++ { node := list.Nodes[i] switch node.Type() { case NodeText: node := node.(*TextNode) _, err := st.Writer.Write(node.Text) if err != nil { node.error(err) } case NodeAction: node := node.(*ActionNode) if node.Set != nil { if node.Set.Let { if !inNewScope { st.newScope() inNewScope = true defer st.releaseScope() } st.executeLetList(node.Set) } else { st.executeSetList(node.Set) } } if node.Pipe != nil { v, safeWriter := st.evalPipelineExpression(node.Pipe) if !safeWriter && v.IsValid() { if v.Type().Implements(rendererType) { v.Interface().(Renderer).Render(st) } else { _, err := fastprinter.PrintValue(st.escapeeWriter, v) if err != nil { node.error(err) } } } } case NodeIf: node := node.(*IfNode) var isLet bool if node.Set != nil { if node.Set.Let { isLet = true st.newScope() st.executeLetList(node.Set) } else { st.executeSetList(node.Set) } } if isTrue(st.evalPrimaryExpressionGroup(node.Expression)) { returnValue = st.executeList(node.List) } else if node.ElseList != nil { returnValue = st.executeList(node.ElseList) } if isLet { st.releaseScope() } case NodeRange: node := node.(*RangeNode) var expression reflect.Value isSet := node.Set != nil isLet := false keyVarSlot := 0 valVarSlot := -1 context := st.context if isSet { if len(node.Set.Left) > 1 { valVarSlot = 1 } expression = st.evalPrimaryExpressionGroup(node.Set.Right[0]) if node.Set.Let { isLet = true st.newScope() } } else { expression = st.evalPrimaryExpressionGroup(node.Expression) } ranger, cleanup := getRanger(expression) if !ranger.ProvidesIndex() { if len(node.Set.Left) > 1 { // two-vars assignment with ranger that doesn't provide an index node.error(errors.New("two-var range over ranger that does not provide an index")) } keyVarSlot, valVarSlot = -1, 0 } indexValue, rangeValue, end := ranger.Range() if !end { for !end && !returnValue.IsValid() { if isSet { if isLet { if keyVarSlot >= 0 { st.variables[node.Set.Left[keyVarSlot].String()] = indexValue } if valVarSlot >= 0 { st.variables[node.Set.Left[valVarSlot].String()] = rangeValue } } else { if keyVarSlot >= 0 { st.executeSet(node.Set.Left[keyVarSlot], indexValue) } if valVarSlot >= 0 { st.executeSet(node.Set.Left[valVarSlot], rangeValue) } } } if valVarSlot < 0 { st.context = rangeValue } returnValue = st.executeList(node.List) indexValue, rangeValue, end = ranger.Range() } } else if node.ElseList != nil { returnValue = st.executeList(node.ElseList) } cleanup() st.context = context if isLet { st.releaseScope() } case NodeTry: node := node.(*TryNode) returnValue = st.executeTry(node) case NodeYield: node := node.(*YieldNode) if node.IsContent { if st.content != nil { st.content(st, node.Expression) } } else { block, has := st.getBlock(node.Name) if has == false || block == nil { node.errorf("unresolved block %q!!", node.Name) } st.executeYieldBlock(block, block.Parameters, node.Parameters, node.Expression, node.Content) } case NodeBlock: node := node.(*BlockNode) block, has := st.getBlock(node.Name) if has == false { block = node } st.executeYieldBlock(block, block.Parameters, block.Parameters, block.Expression, block.Content) case NodeInclude: node := node.(*IncludeNode) returnValue = st.executeInclude(node) case NodeReturn: node := node.(*ReturnNode) returnValue = st.evalPrimaryExpressionGroup(node.Value) } } return returnValue } func (st *Runtime) executeTry(try *TryNode) (returnValue reflect.Value) { writer := st.Writer buf := new(bytes.Buffer) defer func() { r := recover() // copy buffered render output to writer only if no panic occured if r == nil { io.Copy(writer, buf) } else { // st.Writer is already set to its original value since the later defer ran first if try.Catch != nil { if try.Catch.Err != nil { st.newScope() st.scope.variables[try.Catch.Err.Ident] = reflect.ValueOf(r) } if try.Catch.List != nil { returnValue = st.executeList(try.Catch.List) } if try.Catch.Err != nil { st.releaseScope() } } } }() st.Writer = buf defer func() { st.Writer = writer }() return st.executeList(try.List) } func (st *Runtime) executeInclude(node *IncludeNode) (returnValue reflect.Value) { var templatePath string name := st.evalPrimaryExpressionGroup(node.Name) if name.Type().Implements(stringerType) { templatePath = name.String() } else if name.Kind() == reflect.String { templatePath = name.String() } else { node.errorf("evaluating name of template to include: unexpected expression type %q", getTypeString(name)) } t, err := st.set.getSiblingTemplate(templatePath, node.TemplatePath) if err != nil { node.error(err) return reflect.Value{} } st.newScope() defer st.releaseScope() st.blocks = t.processedBlocks var context reflect.Value if node.Context != nil { context = st.context defer func() { st.context = context }() st.context = st.evalPrimaryExpressionGroup(node.Context) } Root := t.Root for t.extends != nil { t = t.extends Root = t.Root } return st.executeList(Root) } var ( valueBoolTRUE = reflect.ValueOf(true) valueBoolFALSE = reflect.ValueOf(false) ) func (st *Runtime) evalPrimaryExpressionGroup(node Expression) reflect.Value { switch node.Type() { case NodeAdditiveExpr: return st.evalAdditiveExpression(node.(*AdditiveExprNode)) case NodeMultiplicativeExpr: return st.evalMultiplicativeExpression(node.(*MultiplicativeExprNode)) case NodeComparativeExpr: return st.evalComparativeExpression(node.(*ComparativeExprNode)) case NodeNumericComparativeExpr: return st.evalNumericComparativeExpression(node.(*NumericComparativeExprNode)) case NodeLogicalExpr: return st.evalLogicalExpression(node.(*LogicalExprNode)) case NodeNotExpr: return reflect.ValueOf(!isTrue(st.evalPrimaryExpressionGroup(node.(*NotExprNode).Expr))) case NodeTernaryExpr: node := node.(*TernaryExprNode) if isTrue(st.evalPrimaryExpressionGroup(node.Boolean)) { return st.evalPrimaryExpressionGroup(node.Left) } return st.evalPrimaryExpressionGroup(node.Right) case NodeCallExpr: node := node.(*CallExprNode) baseExpr := st.evalBaseExpressionGroup(node.BaseExpr) if baseExpr.Kind() != reflect.Func { node.errorf("node %q is not func kind %q", node.BaseExpr, baseExpr.Type()) } return st.evalCallExpression(baseExpr, node.Args) case NodeIndexExpr: node := node.(*IndexExprNode) base := st.evalPrimaryExpressionGroup(node.Base) index := st.evalPrimaryExpressionGroup(node.Index) resolved, err := resolveIndex(base, index) if err != nil { node.error(err) } return resolved case NodeSliceExpr: node := node.(*SliceExprNode) baseExpression := st.evalPrimaryExpressionGroup(node.Base) var index, length int if node.Index != nil { indexExpression := st.evalPrimaryExpressionGroup(node.Index) if canNumber(indexExpression.Kind()) { index = int(castInt64(indexExpression)) } else { node.Index.errorf("non numeric value in index expression kind %s", indexExpression.Kind().String()) } } if node.EndIndex != nil { indexExpression := st.evalPrimaryExpressionGroup(node.EndIndex) if canNumber(indexExpression.Kind()) { length = int(castInt64(indexExpression)) } else { node.EndIndex.errorf("non numeric value in index expression kind %s", indexExpression.Kind().String()) } } else { length = baseExpression.Len() } return baseExpression.Slice(index, length) } return st.evalBaseExpressionGroup(node) } // notNil returns false when v.IsValid() == false // or when v's kind can be nil and v.IsNil() == true func notNil(v reflect.Value) bool { if !v.IsValid() { return false } switch v.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return !v.IsNil() default: return true } } func (st *Runtime) isSet(node Node) (ok bool) { defer func() { if r := recover(); r != nil { // something panicked while evaluating node ok = false } }() nodeType := node.Type() switch nodeType { case NodeIndexExpr: node := node.(*IndexExprNode) if !st.isSet(node.Base) || !st.isSet(node.Index) { return false } base := st.evalPrimaryExpressionGroup(node.Base) index := st.evalPrimaryExpressionGroup(node.Index) resolved, err := resolveIndex(base, index) return err == nil && notNil(resolved) case NodeIdentifier: value, err := st.resolve(node.String()) return err == nil && notNil(value) case NodeField: node := node.(*FieldNode) resolved := st.context for i := 0; i < len(node.Ident); i++ { var err error resolved, err = resolveIndex(resolved, reflect.ValueOf(node.Ident[i])) if err != nil || !notNil(resolved) { return false } } case NodeChain: node := node.(*ChainNode) resolved, err := st.evalChainNodeExpression(node) return err == nil && notNil(resolved) default: //todo: maybe work some edge cases if !(nodeType > beginExpressions && nodeType < endExpressions) { node.errorf("unexpected %q node in isset clause", node) } } return true } func (st *Runtime) evalNumericComparativeExpression(node *NumericComparativeExprNode) reflect.Value { left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right) isTrue := false kind := left.Kind() // if the left value is not a float and the right is, we need to promote the left value to a float before the calculation // this is necessary for expressions like 4*1.23 needFloatPromotion := !isFloat(kind) && isFloat(right.Kind()) switch node.Operator.typ { case itemGreat: if isInt(kind) { if needFloatPromotion { isTrue = float64(left.Int()) > right.Float() } else { isTrue = left.Int() > toInt(right) } } else if isFloat(kind) { isTrue = left.Float() > toFloat(right) } else if isUint(kind) { if needFloatPromotion { isTrue = float64(left.Uint()) > right.Float() } else { isTrue = left.Uint() > toUint(right) } } else { node.Left.errorf("a non numeric value in numeric comparative expression") } case itemGreatEquals: if isInt(kind) { if needFloatPromotion { isTrue = float64(left.Int()) >= right.Float() } else { isTrue = left.Int() >= toInt(right) } } else if isFloat(kind) { isTrue = left.Float() >= toFloat(right) } else if isUint(kind) { if needFloatPromotion { isTrue = float64(left.Uint()) >= right.Float() } else { isTrue = left.Uint() >= toUint(right) } } else { node.Left.errorf("a non numeric value in numeric comparative expression") } case itemLess: if isInt(kind) { if needFloatPromotion { isTrue = float64(left.Int()) < right.Float() } else { isTrue = left.Int() < toInt(right) } } else if isFloat(kind) { isTrue = left.Float() < toFloat(right) } else if isUint(kind) { if needFloatPromotion { isTrue = float64(left.Uint()) < right.Float() } else { isTrue = left.Uint() < toUint(right) } } else { node.Left.errorf("a non numeric value in numeric comparative expression") } case itemLessEquals: if isInt(kind) { if needFloatPromotion { isTrue = float64(left.Int()) <= right.Float() } else { isTrue = left.Int() <= toInt(right) } } else if isFloat(kind) { isTrue = left.Float() <= toFloat(right) } else if isUint(kind) { if needFloatPromotion { isTrue = float64(left.Uint()) <= right.Float() } else { isTrue = left.Uint() <= toUint(right) } } else { node.Left.errorf("a non numeric value in numeric comparative expression") } } return reflect.ValueOf(isTrue) } func (st *Runtime) evalLogicalExpression(node *LogicalExprNode) reflect.Value { truthy := isTrue(st.evalPrimaryExpressionGroup(node.Left)) if node.Operator.typ == itemAnd { truthy = truthy && isTrue(st.evalPrimaryExpressionGroup(node.Right)) } else { truthy = truthy || isTrue(st.evalPrimaryExpressionGroup(node.Right)) } return reflect.ValueOf(truthy) } func (st *Runtime) evalComparativeExpression(node *ComparativeExprNode) reflect.Value { left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right) equal := checkEquality(left, right) if node.Operator.typ == itemNotEquals { return reflect.ValueOf(!equal) } return reflect.ValueOf(equal) } func toInt(v reflect.Value) int64 { kind := v.Kind() if isInt(kind) { return v.Int() } else if isFloat(kind) { return int64(v.Float()) } else if isUint(kind) { return int64(v.Uint()) } else if kind == reflect.String { n, e := strconv.ParseInt(v.String(), 10, 0) if e != nil { panic(e) } return n } else if kind == reflect.Bool { if v.Bool() { return 0 } return 1 } panic(fmt.Errorf("type: %q can't be converted to int64", v.Type())) } func toUint(v reflect.Value) uint64 { kind := v.Kind() if isUint(kind) { return v.Uint() } else if isInt(kind) { return uint64(v.Int()) } else if isFloat(kind) { return uint64(v.Float()) } else if kind == reflect.String { n, e := strconv.ParseUint(v.String(), 10, 0) if e != nil { panic(e) } return n } else if kind == reflect.Bool { if v.Bool() { return 0 } return 1 } panic(fmt.Errorf("type: %q can't be converted to uint64", v.Type())) } func toFloat(v reflect.Value) float64 { kind := v.Kind() if isFloat(kind) { return v.Float() } else if isInt(kind) { return float64(v.Int()) } else if isUint(kind) { return float64(v.Uint()) } else if kind == reflect.String { n, e := strconv.ParseFloat(v.String(), 0) if e != nil { panic(e) } return n } else if kind == reflect.Bool { if v.Bool() { return 0 } return 1 } panic(fmt.Errorf("type: %q can't be converted to float64", v.Type())) } func (st *Runtime) evalMultiplicativeExpression(node *MultiplicativeExprNode) reflect.Value { left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right) kind := left.Kind() // if the left value is not a float and the right is, we need to promote the left value to a float before the calculation // this is necessary for expressions like 4*1.23 needFloatPromotion := !isFloat(kind) && isFloat(right.Kind()) switch node.Operator.typ { case itemMul: if isInt(kind) { if needFloatPromotion { // do the promotion and calculates left = reflect.ValueOf(float64(left.Int()) * right.Float()) } else { // do not need float promotion left = reflect.ValueOf(left.Int() * toInt(right)) } } else if isFloat(kind) { left = reflect.ValueOf(left.Float() * toFloat(right)) } else if isUint(kind) { if needFloatPromotion { left = reflect.ValueOf(float64(left.Uint()) * right.Float()) } else { left = reflect.ValueOf(left.Uint() * toUint(right)) } } else { node.Left.errorf("a non numeric value in multiplicative expression") } case itemDiv: if isInt(kind) { if needFloatPromotion { left = reflect.ValueOf(float64(left.Int()) / right.Float()) } else { left = reflect.ValueOf(left.Int() / toInt(right)) } } else if isFloat(kind) { left = reflect.ValueOf(left.Float() / toFloat(right)) } else if isUint(kind) { if needFloatPromotion { left = reflect.ValueOf(float64(left.Uint()) / right.Float()) } else { left = reflect.ValueOf(left.Uint() / toUint(right)) } } else { node.Left.errorf("a non numeric value in multiplicative expression") } case itemMod: if isInt(kind) { left = reflect.ValueOf(left.Int() % toInt(right)) } else if isFloat(kind) { left = reflect.ValueOf(int64(left.Float()) % toInt(right)) } else if isUint(kind) { left = reflect.ValueOf(left.Uint() % toUint(right)) } else { node.Left.errorf("a non numeric value in multiplicative expression") } } return left } func (st *Runtime) evalAdditiveExpression(node *AdditiveExprNode) reflect.Value { isAdditive := node.Operator.typ == itemAdd if node.Left == nil { right := st.evalPrimaryExpressionGroup(node.Right) kind := right.Kind() // todo: optimize if isInt(kind) { if isAdditive { return reflect.ValueOf(+right.Int()) } else { return reflect.ValueOf(-right.Int()) } } else if isUint(kind) { if isAdditive { return right } else { return reflect.ValueOf(-int64(right.Uint())) } } else if isFloat(kind) { if isAdditive { return reflect.ValueOf(+right.Float()) } else { return reflect.ValueOf(-right.Float()) } } node.Left.errorf("additive expression: right side %s (%s) is not a numeric value (no left side)", node.Right, getTypeString(right)) } left, right := st.evalPrimaryExpressionGroup(node.Left), st.evalPrimaryExpressionGroup(node.Right) kind := left.Kind() // if the left value is not a float and the right is, we need to promote the left value to a float before the calculation // this is necessary for expressions like 4+1.23 needFloatPromotion := !isFloat(kind) && kind != reflect.String && isFloat(right.Kind()) if needFloatPromotion { if isInt(kind) { if isAdditive { left = reflect.ValueOf(float64(left.Int()) + right.Float()) } else { left = reflect.ValueOf(float64(left.Int()) - right.Float()) } } else if isUint(kind) { if isAdditive { left = reflect.ValueOf(float64(left.Uint()) + right.Float()) } else { left = reflect.ValueOf(float64(left.Uint()) - right.Float()) } } else { node.Left.errorf("additive expression: left side (%s (%s) needs float promotion but neither int nor uint)", node.Left, getTypeString(left)) } } else { if isInt(kind) { if isAdditive { left = reflect.ValueOf(left.Int() + toInt(right)) } else { left = reflect.ValueOf(left.Int() - toInt(right)) } } else if isFloat(kind) { if isAdditive { left = reflect.ValueOf(left.Float() + toFloat(right)) } else { left = reflect.ValueOf(left.Float() - toFloat(right)) } } else if isUint(kind) { if isAdditive { left = reflect.ValueOf(left.Uint() + toUint(right)) } else { left = reflect.ValueOf(left.Uint() - toUint(right)) } } else if kind == reflect.String { if !isAdditive { node.Right.errorf("minus signal is not allowed with strings") } // converts []byte (and alias types of []byte) to string if right.Kind() == reflect.Slice && right.Type().Elem().Kind() == reflect.Uint8 { right = right.Convert(left.Type()) } left = reflect.ValueOf(left.String() + fmt.Sprint(right)) } else { node.Left.errorf("additive expression: left side %s (%s) is not a numeric value", node.Left, getTypeString(left)) } } return left } func getTypeString(value reflect.Value) string { if value.IsValid() { return value.Type().String() } return "" } func (st *Runtime) evalBaseExpressionGroup(node Node) reflect.Value { switch node.Type() { case NodeNil: return reflect.ValueOf(nil) case NodeBool: if node.(*BoolNode).True { return valueBoolTRUE } return valueBoolFALSE case NodeString: return reflect.ValueOf(&node.(*StringNode).Text).Elem() case NodeIdentifier: resolved, err := st.resolve(node.(*IdentifierNode).Ident) if err != nil { node.error(err) } return resolved case NodeField: node := node.(*FieldNode) resolved := st.context for i := 0; i < len(node.Ident); i++ { field, err := resolveIndex(resolved, reflect.ValueOf(node.Ident[i])) if err != nil { node.errorf("%v", err) } if !field.IsValid() { node.errorf("there is no field or method '%s' in %s (.%s)", node.Ident[i], getTypeString(resolved), strings.Join(node.Ident, ".")) } resolved = field } return resolved case NodeChain: resolved, err := st.evalChainNodeExpression(node.(*ChainNode)) if err != nil { node.error(err) } return resolved case NodeNumber: node := node.(*NumberNode) if node.IsFloat { return reflect.ValueOf(&node.Float64).Elem() } if node.IsInt { return reflect.ValueOf(&node.Int64).Elem() } if node.IsUint { return reflect.ValueOf(&node.Uint64).Elem() } } node.errorf("unexpected node type %s in unary expression evaluating", node) return reflect.Value{} } func (st *Runtime) evalCallExpression(baseExpr reflect.Value, args []Expression, values ...reflect.Value) reflect.Value { if funcType.AssignableTo(baseExpr.Type()) { return baseExpr.Interface().(Func)(Arguments{runtime: st, argExpr: args, argVal: values}) } i := len(args) + len(values) var returns []reflect.Value if i <= 10 { returns = reflect_Call10(i, st, baseExpr, args, values...) } else { returns = reflect_Call(make([]reflect.Value, i, i), st, baseExpr, args, values...) } if len(returns) == 0 { return reflect.Value{} } return returns[0] } func (st *Runtime) evalCommandExpression(node *CommandNode) (reflect.Value, bool) { term := st.evalPrimaryExpressionGroup(node.BaseExpr) if node.Args != nil { if term.Kind() == reflect.Func { if term.Type() == safeWriterType { st.evalSafeWriter(term, node) return reflect.Value{}, true } return st.evalCallExpression(term, node.Args), false } else { node.Args[0].errorf("command %q type %s is not func", node.Args[0], term.Type()) } } return term, false } func (st *Runtime) evalChainNodeExpression(node *ChainNode) (reflect.Value, error) { resolved := st.evalPrimaryExpressionGroup(node.Node) for i := 0; i < len(node.Field); i++ { field, err := resolveIndex(resolved, reflect.ValueOf(node.Field[i])) if err != nil { return reflect.Value{}, err } if !field.IsValid() { if resolved.Kind() == reflect.Map && i == len(node.Field)-1 { // return reflect.Zero(resolved.Type().Elem()), nil return reflect.Value{}, nil } return reflect.Value{}, fmt.Errorf("there is no field or method '%s' in %s (%s)", node.Field[i], getTypeString(resolved), node) } resolved = field } return resolved, nil } type escapeWriter struct { rawWriter io.Writer safeWriter SafeWriter } func (w *escapeWriter) Write(b []byte) (int, error) { w.safeWriter(w.rawWriter, b) return 0, nil } func (st *Runtime) evalSafeWriter(term reflect.Value, node *CommandNode, v ...reflect.Value) { sw := &escapeWriter{rawWriter: st.Writer, safeWriter: term.Interface().(SafeWriter)} for i := 0; i < len(v); i++ { fastprinter.PrintValue(sw, v[i]) } for i := 0; i < len(node.Args); i++ { fastprinter.PrintValue(sw, st.evalPrimaryExpressionGroup(node.Args[i])) } } func (st *Runtime) evalCommandPipeExpression(node *CommandNode, value reflect.Value) (reflect.Value, bool) { term := st.evalPrimaryExpressionGroup(node.BaseExpr) if term.Kind() == reflect.Func { if term.Type() == safeWriterType { st.evalSafeWriter(term, node, value) return reflect.Value{}, true } return st.evalCallExpression(term, node.Args, value), false } else { node.BaseExpr.errorf("pipe command %q type %s is not func", node.BaseExpr, term.Type()) } return term, false } func (st *Runtime) evalPipelineExpression(node *PipeNode) (value reflect.Value, safeWriter bool) { value, safeWriter = st.evalCommandExpression(node.Cmds[0]) for i := 1; i < len(node.Cmds); i++ { if safeWriter { node.Cmds[i].errorf("unexpected command %s, writer command should be the last command", node.Cmds[i]) } value, safeWriter = st.evalCommandPipeExpression(node.Cmds[i], value) } return } func reflect_Call(arguments []reflect.Value, st *Runtime, fn reflect.Value, args []Expression, values ...reflect.Value) []reflect.Value { typ := fn.Type() numIn := typ.NumIn() isVariadic := typ.IsVariadic() if isVariadic { numIn-- } i, j := 0, 0 for ; i < numIn && i < len(values); i++ { in := typ.In(i) term := values[i] if !term.Type().AssignableTo(in) { term = term.Convert(in) } arguments[i] = term } if isVariadic { in := typ.In(numIn).Elem() for ; i < len(values); i++ { term := values[i] if !term.Type().AssignableTo(in) { term = term.Convert(in) } arguments[i] = term } } for ; i < numIn && j < len(args); i, j = i+1, j+1 { in := typ.In(i) term := st.evalPrimaryExpressionGroup(args[j]) if !term.Type().AssignableTo(in) { term = term.Convert(in) } arguments[i] = term } if isVariadic { in := typ.In(numIn).Elem() for ; j < len(args); i, j = i+1, j+1 { term := st.evalPrimaryExpressionGroup(args[j]) if !term.Type().AssignableTo(in) { term = term.Convert(in) } arguments[i] = term } } return fn.Call(arguments[0:i]) } func reflect_Call10(i int, st *Runtime, fn reflect.Value, args []Expression, values ...reflect.Value) []reflect.Value { var arguments [10]reflect.Value return reflect_Call(arguments[0:i], st, fn, args, values...) } func isUint(kind reflect.Kind) bool { return kind >= reflect.Uint && kind <= reflect.Uint64 } func isInt(kind reflect.Kind) bool { return kind >= reflect.Int && kind <= reflect.Int64 } func isFloat(kind reflect.Kind) bool { return kind == reflect.Float32 || kind == reflect.Float64 } // checkEquality of two reflect values in the semantic of the jet runtime func checkEquality(v1, v2 reflect.Value) bool { v1 = indirectInterface(v1) v2 = indirectInterface(v2) if !v1.IsValid() || !v2.IsValid() { return v1.IsValid() == v2.IsValid() } v1Type := v1.Type() v2Type := v2.Type() // fast path if v1Type != v2Type && !v2Type.AssignableTo(v1Type) && !v2Type.ConvertibleTo(v1Type) { return false } kind := v1.Kind() if isInt(kind) { return v1.Int() == toInt(v2) } if isFloat(kind) { return v1.Float() == toFloat(v2) } if isUint(kind) { return v1.Uint() == toUint(v2) } switch kind { case reflect.Bool: return v1.Bool() == isTrue(v2) case reflect.String: return v1.String() == v2.String() case reflect.Array: vlen := v1.Len() if vlen == v2.Len() { return false } for i := 0; i < vlen; i++ { if !checkEquality(v1.Index(i), v2.Index(i)) { return false } } return true case reflect.Slice: if v1.IsNil() != v2.IsNil() { return false } vlen := v1.Len() if vlen != v2.Len() { return false } if v1.CanAddr() && v2.CanAddr() && v1.Pointer() == v2.Pointer() { return true } for i := 0; i < vlen; i++ { if !checkEquality(v1.Index(i), v2.Index(i)) { return false } } return true case reflect.Interface: if v1.IsNil() || v2.IsNil() { return v1.IsNil() == v2.IsNil() } return checkEquality(v1.Elem(), v2.Elem()) case reflect.Ptr: return v1.Pointer() == v2.Pointer() case reflect.Struct: numField := v1.NumField() for i, n := 0, numField; i < n; i++ { if !checkEquality(v1.Field(i), v2.Field(i)) { return false } } return true case reflect.Map: if v1.IsNil() != v2.IsNil() { return false } if v1.Len() != v2.Len() { return false } if v1.Pointer() == v2.Pointer() { return true } for _, k := range v1.MapKeys() { val1 := v1.MapIndex(k) val2 := v2.MapIndex(k) if !val1.IsValid() || !val2.IsValid() || !checkEquality(v1.MapIndex(k), v2.MapIndex(k)) { return false } } return true case reflect.Func: return v1.IsNil() && v2.IsNil() default: // Normal equality suffices return v1.Interface() == v2.Interface() } } func isTrue(v reflect.Value) bool { return v.IsValid() && !v.IsZero() } func canNumber(kind reflect.Kind) bool { return isInt(kind) || isUint(kind) || isFloat(kind) } func castInt64(v reflect.Value) int64 { kind := v.Kind() switch { case isInt(kind): return v.Int() case isUint(kind): return int64(v.Uint()) case isFloat(kind): return int64(v.Float()) } return 0 } var cachedStructsMutex = sync.RWMutex{} var cachedStructsFieldIndex = map[reflect.Type]map[string][]int{} // from text/template's exec.go: // // indirect returns the item at the end of indirection, and a bool to indicate // if it's nil. If the returned bool is true, the returned value's kind will be // either a pointer or interface. func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { if v.IsNil() { return v, true } } return v, false } // indirectInterface returns the concrete value in an interface value, or else v itself. // That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x): // the fact that x was an interface value is forgotten. func indirectInterface(v reflect.Value) reflect.Value { if v.Kind() == reflect.Interface { return v.Elem() } return v } // indirectEface is the same as indirectInterface, but only indirects through v if its type // is the empty interface and its value is not nil. func indirectEface(v reflect.Value) reflect.Value { if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 && !v.IsNil() { return v.Elem() } return v } // mostly copied from text/template's evalField() (exec.go): func resolveIndex(v, index reflect.Value) (reflect.Value, error) { if !v.IsValid() { return reflect.Value{}, fmt.Errorf("there is no field or method '%s' in %s (%s)", index, v, getTypeString(v)) } v, isNil := indirect(v) if v.Kind() == reflect.Interface && isNil { // Calling a method on a nil interface can't work. The // MethodByName method call below would panic. return reflect.Value{}, fmt.Errorf("nil pointer evaluating %s.%s", v.Type(), index) } // Unless it's an interface, need to get to a value of type *T to guarantee // we see all methods of T and *T. if index.Kind() == reflect.String { ptr := v if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Ptr && ptr.CanAddr() { ptr = ptr.Addr() } if method := ptr.MethodByName(index.String()); method.IsValid() { return method, nil } } // It's not a method on v; so now: // - if v is array/slice/string, use index as numeric index // - if v is a struct, use index as field name // - if v is a map, use index as key // - if v is (still) a pointer, indexing will fail but we check for nil to get a useful error switch v.Kind() { case reflect.Array, reflect.Slice, reflect.String: x, err := indexArg(index, v.Len()) if err != nil { return reflect.Value{}, err } return indirectEface(v.Index(x)), nil case reflect.Struct: if index.Kind() != reflect.String { return reflect.Value{}, fmt.Errorf("can't use %s (%s, not string) as field name in struct type %s", index, index.Type(), v.Type()) } tField, ok := v.Type().FieldByName(index.String()) if ok { field := v.FieldByIndex(tField.Index) if tField.PkgPath != "" { // field is unexported return reflect.Value{}, fmt.Errorf("%s is an unexported field of struct type %s", index.String(), v.Type()) } return indirectEface(field), nil } return reflect.Value{}, fmt.Errorf("can't use %s as field name in struct type %s", index, v.Type()) case reflect.Map: // If it's a map, attempt to use the field name as a key. if !index.Type().ConvertibleTo(v.Type().Key()) { return reflect.Value{}, fmt.Errorf("can't use %s (%s) as key for map of type %s", index, index.Type(), v.Type()) } index = index.Convert(v.Type().Key()) // noop in most cases, but not expensive return indirectEface(v.MapIndex(index)), nil case reflect.Ptr: etyp := v.Type().Elem() if etyp.Kind() == reflect.Struct && index.Kind() == reflect.String { if _, ok := etyp.FieldByName(index.String()); !ok { // If there's no such field, say "can't evaluate" // instead of "nil pointer evaluating". break } } if isNil { return reflect.Value{}, fmt.Errorf("nil pointer evaluating %s.%s", v.Type(), index) } } return reflect.Value{}, fmt.Errorf("can't evaluate index %s (%s) in type %s", index, index.Type(), v.Type()) } // from Go's text/template's funcs.go: // // indexArg checks if a reflect.Value can be used as an index, and converts it to int if possible. func indexArg(index reflect.Value, cap int) (int, error) { var x int64 switch index.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: x = index.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: x = int64(index.Uint()) case reflect.Float32, reflect.Float64: x = int64(index.Float()) case reflect.Invalid: return 0, fmt.Errorf("cannot index slice/array/string with nil") default: return 0, fmt.Errorf("cannot index slice/array/string with type %s", index.Type()) } if int(x) < 0 || int(x) >= cap { return 0, fmt.Errorf("index out of range: %d", x) } return int(x), nil } func buildCache(typ reflect.Type, cache map[string][]int, parent []int) { numFields := typ.NumField() max := len(parent) + 1 for i := 0; i < numFields; i++ { index := make([]int, max) copy(index, parent) index[len(parent)] = i field := typ.Field(i) if field.Anonymous { typ := field.Type if typ.Kind() == reflect.Struct { buildCache(typ, cache, index) } } cache[field.Name] = index } }