mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
* export the checker and ensure no methods or fields are exported * make Checker reusable * add PatchAndCheck method to Checker, and reuse it in visitors * avoid unnecessary map and consolidate structs * make all Nature methods use pointer-receiver * added cache for type and nature information * optimize type and nature checks * more aggressively cache some partial results and add cheap checks * cache reused values * reduce copying in Nature by breaking down optional fields * enforce use of cache * improve ergonomics of FromType and NatureOf * improve struct method fetching * make cache private to Nature * first iteration improving struct reflect caching * cleaned up struct fields data * improve caching of functions data * simplify checker code * simplify and remove dead code * add checker benchmarks * address PR comments and minor improvements * rename from SetCache to Bind * make the benchmarks more fair * fix benchmark option not being applied * move Cache to be argument instead of field * merge FuncData into Optional * rename Optional to TypeData * simplify struct logic * cache builtin numeric types --------- Co-authored-by: Anton Medvedev <[email protected]>
127 lines
2.8 KiB
Go
127 lines
2.8 KiB
Go
package checker
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/expr-lang/expr/ast"
|
|
. "github.com/expr-lang/expr/checker/nature"
|
|
"github.com/expr-lang/expr/vm"
|
|
)
|
|
|
|
func FieldIndex(c *Cache, env Nature, node ast.Node) (bool, []int, string) {
|
|
switch n := node.(type) {
|
|
case *ast.IdentifierNode:
|
|
if idx, ok := env.FieldIndex(c, n.Value); ok {
|
|
return true, idx, n.Value
|
|
}
|
|
case *ast.MemberNode:
|
|
base := n.Node.Nature()
|
|
base = base.Deref(c)
|
|
if base.Kind == reflect.Struct {
|
|
if prop, ok := n.Property.(*ast.StringNode); ok {
|
|
if idx, ok := base.FieldIndex(c, prop.Value); ok {
|
|
return true, idx, prop.Value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false, nil, ""
|
|
}
|
|
|
|
func MethodIndex(c *Cache, env Nature, node ast.Node) (bool, int, string) {
|
|
switch n := node.(type) {
|
|
case *ast.IdentifierNode:
|
|
if env.Kind == reflect.Struct {
|
|
if m, ok := env.Get(c, n.Value); ok && m.TypeData != nil {
|
|
return m.Method, m.MethodIndex, n.Value
|
|
}
|
|
}
|
|
case *ast.MemberNode:
|
|
if name, ok := n.Property.(*ast.StringNode); ok {
|
|
base := n.Node.Type()
|
|
if base != nil && base.Kind() != reflect.Interface {
|
|
if m, ok := base.MethodByName(name.Value); ok {
|
|
return true, m.Index, name.Value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false, 0, ""
|
|
}
|
|
|
|
func TypedFuncIndex(fn reflect.Type, method bool) (int, bool) {
|
|
if fn == nil {
|
|
return 0, false
|
|
}
|
|
if fn.Kind() != reflect.Func {
|
|
return 0, false
|
|
}
|
|
// OnCallTyped doesn't work for functions with variadic arguments.
|
|
if fn.IsVariadic() {
|
|
return 0, false
|
|
}
|
|
// OnCallTyped doesn't work named function, like `type MyFunc func() int`.
|
|
if fn.PkgPath() != "" { // If PkgPath() is not empty, it means that function is named.
|
|
return 0, false
|
|
}
|
|
|
|
fnNumIn := fn.NumIn()
|
|
fnInOffset := 0
|
|
if method {
|
|
fnNumIn--
|
|
fnInOffset = 1
|
|
}
|
|
|
|
funcTypes:
|
|
for i := range vm.FuncTypes {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
typed := reflect.ValueOf(vm.FuncTypes[i]).Elem().Type()
|
|
if typed.Kind() != reflect.Func {
|
|
continue
|
|
}
|
|
if typed.NumOut() != fn.NumOut() {
|
|
continue
|
|
}
|
|
for j := 0; j < typed.NumOut(); j++ {
|
|
if typed.Out(j) != fn.Out(j) {
|
|
continue funcTypes
|
|
}
|
|
}
|
|
if typed.NumIn() != fnNumIn {
|
|
continue
|
|
}
|
|
for j := 0; j < typed.NumIn(); j++ {
|
|
if typed.In(j) != fn.In(j+fnInOffset) {
|
|
continue funcTypes
|
|
}
|
|
}
|
|
return i, true
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func IsFastFunc(fn reflect.Type, method bool) bool {
|
|
if fn == nil {
|
|
return false
|
|
}
|
|
if fn.Kind() != reflect.Func {
|
|
return false
|
|
}
|
|
numIn := 1
|
|
if method {
|
|
numIn = 2
|
|
}
|
|
if fn.IsVariadic() &&
|
|
fn.NumIn() == numIn &&
|
|
fn.NumOut() == 1 &&
|
|
fn.Out(0).Kind() == reflect.Interface {
|
|
rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods
|
|
if rest != nil && rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|