mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
rebase: update kubernetes to 1.28.0 in main
updating kubernetes to 1.28.0 in the main repo. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
committed by
mergify[bot]
parent
b2fdc269c3
commit
ff3e84ad67
7
vendor/github.com/google/cel-go/interpreter/BUILD.bazel
generated
vendored
7
vendor/github.com/google/cel-go/interpreter/BUILD.bazel
generated
vendored
@ -11,10 +11,10 @@ go_library(
|
||||
"activation.go",
|
||||
"attribute_patterns.go",
|
||||
"attributes.go",
|
||||
"coster.go",
|
||||
"decorators.go",
|
||||
"dispatcher.go",
|
||||
"evalstate.go",
|
||||
"formatting.go",
|
||||
"interpretable.go",
|
||||
"interpreter.go",
|
||||
"optimizations.go",
|
||||
@ -32,7 +32,7 @@ go_library(
|
||||
"//common/types/ref:go_default_library",
|
||||
"//common/types/traits:go_default_library",
|
||||
"//interpreter/functions:go_default_library",
|
||||
"@org_golang_google_genproto//googleapis/api/expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/durationpb:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/structpb:go_default_library",
|
||||
@ -49,6 +49,7 @@ go_test(
|
||||
"attributes_test.go",
|
||||
"interpreter_test.go",
|
||||
"prune_test.go",
|
||||
"runtimecost_test.go",
|
||||
],
|
||||
embed = [
|
||||
":go_default_library",
|
||||
@ -65,7 +66,7 @@ go_test(
|
||||
"//test:go_default_library",
|
||||
"//test/proto2pb:go_default_library",
|
||||
"//test/proto3pb:go_default_library",
|
||||
"@org_golang_google_genproto//googleapis/api/expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/anypb:go_default_library",
|
||||
],
|
||||
|
30
vendor/github.com/google/cel-go/interpreter/activation.go
generated
vendored
30
vendor/github.com/google/cel-go/interpreter/activation.go
generated
vendored
@ -28,7 +28,7 @@ import (
|
||||
type Activation interface {
|
||||
// ResolveName returns a value from the activation by qualified name, or false if the name
|
||||
// could not be found.
|
||||
ResolveName(name string) (interface{}, bool)
|
||||
ResolveName(name string) (any, bool)
|
||||
|
||||
// Parent returns the parent of the current activation, may be nil.
|
||||
// If non-nil, the parent will be searched during resolve calls.
|
||||
@ -43,23 +43,23 @@ func EmptyActivation() Activation {
|
||||
// emptyActivation is a variable-free activation.
|
||||
type emptyActivation struct{}
|
||||
|
||||
func (emptyActivation) ResolveName(string) (interface{}, bool) { return nil, false }
|
||||
func (emptyActivation) Parent() Activation { return nil }
|
||||
func (emptyActivation) ResolveName(string) (any, bool) { return nil, false }
|
||||
func (emptyActivation) Parent() Activation { return nil }
|
||||
|
||||
// NewActivation returns an activation based on a map-based binding where the map keys are
|
||||
// expected to be qualified names used with ResolveName calls.
|
||||
//
|
||||
// The input `bindings` may either be of type `Activation` or `map[string]interface{}`.
|
||||
// The input `bindings` may either be of type `Activation` or `map[string]any`.
|
||||
//
|
||||
// Lazy bindings may be supplied within the map-based input in either of the following forms:
|
||||
// - func() interface{}
|
||||
// - func() any
|
||||
// - func() ref.Val
|
||||
//
|
||||
// The output of the lazy binding will overwrite the variable reference in the internal map.
|
||||
//
|
||||
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
|
||||
// the ref.TypeAdapter configured in the environment.
|
||||
func NewActivation(bindings interface{}) (Activation, error) {
|
||||
func NewActivation(bindings any) (Activation, error) {
|
||||
if bindings == nil {
|
||||
return nil, errors.New("bindings must be non-nil")
|
||||
}
|
||||
@ -67,7 +67,7 @@ func NewActivation(bindings interface{}) (Activation, error) {
|
||||
if isActivation {
|
||||
return a, nil
|
||||
}
|
||||
m, isMap := bindings.(map[string]interface{})
|
||||
m, isMap := bindings.(map[string]any)
|
||||
if !isMap {
|
||||
return nil, fmt.Errorf(
|
||||
"activation input must be an activation or map[string]interface: got %T",
|
||||
@ -81,7 +81,7 @@ func NewActivation(bindings interface{}) (Activation, error) {
|
||||
// Named bindings may lazily supply values by providing a function which accepts no arguments and
|
||||
// produces an interface value.
|
||||
type mapActivation struct {
|
||||
bindings map[string]interface{}
|
||||
bindings map[string]any
|
||||
}
|
||||
|
||||
// Parent implements the Activation interface method.
|
||||
@ -90,7 +90,7 @@ func (a *mapActivation) Parent() Activation {
|
||||
}
|
||||
|
||||
// ResolveName implements the Activation interface method.
|
||||
func (a *mapActivation) ResolveName(name string) (interface{}, bool) {
|
||||
func (a *mapActivation) ResolveName(name string) (any, bool) {
|
||||
obj, found := a.bindings[name]
|
||||
if !found {
|
||||
return nil, false
|
||||
@ -100,7 +100,7 @@ func (a *mapActivation) ResolveName(name string) (interface{}, bool) {
|
||||
obj = fn()
|
||||
a.bindings[name] = obj
|
||||
}
|
||||
fnRaw, isLazy := obj.(func() interface{})
|
||||
fnRaw, isLazy := obj.(func() any)
|
||||
if isLazy {
|
||||
obj = fnRaw()
|
||||
a.bindings[name] = obj
|
||||
@ -121,7 +121,7 @@ func (a *hierarchicalActivation) Parent() Activation {
|
||||
}
|
||||
|
||||
// ResolveName implements the Activation interface method.
|
||||
func (a *hierarchicalActivation) ResolveName(name string) (interface{}, bool) {
|
||||
func (a *hierarchicalActivation) ResolveName(name string) (any, bool) {
|
||||
if object, found := a.child.ResolveName(name); found {
|
||||
return object, found
|
||||
}
|
||||
@ -138,8 +138,8 @@ func NewHierarchicalActivation(parent Activation, child Activation) Activation {
|
||||
// representing field and index operations that should result in a 'types.Unknown' result.
|
||||
//
|
||||
// The `bindings` value may be any value type supported by the interpreter.NewActivation call,
|
||||
// but is typically either an existing Activation or map[string]interface{}.
|
||||
func NewPartialActivation(bindings interface{},
|
||||
// but is typically either an existing Activation or map[string]any.
|
||||
func NewPartialActivation(bindings any,
|
||||
unknowns ...*AttributePattern) (PartialActivation, error) {
|
||||
a, err := NewActivation(bindings)
|
||||
if err != nil {
|
||||
@ -184,7 +184,7 @@ func (v *varActivation) Parent() Activation {
|
||||
}
|
||||
|
||||
// ResolveName implements the Activation interface method.
|
||||
func (v *varActivation) ResolveName(name string) (interface{}, bool) {
|
||||
func (v *varActivation) ResolveName(name string) (any, bool) {
|
||||
if name == v.name {
|
||||
return v.val, true
|
||||
}
|
||||
@ -194,7 +194,7 @@ func (v *varActivation) ResolveName(name string) (interface{}, bool) {
|
||||
var (
|
||||
// pool of var activations to reduce allocations during folds.
|
||||
varActivationPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
return &varActivation{}
|
||||
},
|
||||
}
|
||||
|
77
vendor/github.com/google/cel-go/interpreter/attribute_patterns.go
generated
vendored
77
vendor/github.com/google/cel-go/interpreter/attribute_patterns.go
generated
vendored
@ -15,8 +15,6 @@
|
||||
package interpreter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/cel-go/common/containers"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
@ -36,9 +34,9 @@ import (
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// 1. ns.myvar["complex-value"]
|
||||
// 2. ns.myvar["complex-value"][0]
|
||||
// 3. ns.myvar["complex-value"].*.name
|
||||
// 1. ns.myvar["complex-value"]
|
||||
// 2. ns.myvar["complex-value"][0]
|
||||
// 3. ns.myvar["complex-value"].*.name
|
||||
//
|
||||
// The first example is simple: match an attribute where the variable is 'ns.myvar' with a
|
||||
// field access on 'complex-value'. The second example expands the match to indicate that only
|
||||
@ -108,7 +106,7 @@ func (apat *AttributePattern) QualifierPatterns() []*AttributeQualifierPattern {
|
||||
// AttributeQualifierPattern holds a wildcard or valued qualifier pattern.
|
||||
type AttributeQualifierPattern struct {
|
||||
wildcard bool
|
||||
value interface{}
|
||||
value any
|
||||
}
|
||||
|
||||
// Matches returns true if the qualifier pattern is a wildcard, or the Qualifier implements the
|
||||
@ -134,44 +132,44 @@ func (qpat *AttributeQualifierPattern) Matches(q Qualifier) bool {
|
||||
type qualifierValueEquator interface {
|
||||
// QualifierValueEquals returns true if the input value is equal to the value held in the
|
||||
// Qualifier.
|
||||
QualifierValueEquals(value interface{}) bool
|
||||
QualifierValueEquals(value any) bool
|
||||
}
|
||||
|
||||
// QualifierValueEquals implementation for boolean qualifiers.
|
||||
func (q *boolQualifier) QualifierValueEquals(value interface{}) bool {
|
||||
func (q *boolQualifier) QualifierValueEquals(value any) bool {
|
||||
bval, ok := value.(bool)
|
||||
return ok && q.value == bval
|
||||
}
|
||||
|
||||
// QualifierValueEquals implementation for field qualifiers.
|
||||
func (q *fieldQualifier) QualifierValueEquals(value interface{}) bool {
|
||||
func (q *fieldQualifier) QualifierValueEquals(value any) bool {
|
||||
sval, ok := value.(string)
|
||||
return ok && q.Name == sval
|
||||
}
|
||||
|
||||
// QualifierValueEquals implementation for string qualifiers.
|
||||
func (q *stringQualifier) QualifierValueEquals(value interface{}) bool {
|
||||
func (q *stringQualifier) QualifierValueEquals(value any) bool {
|
||||
sval, ok := value.(string)
|
||||
return ok && q.value == sval
|
||||
}
|
||||
|
||||
// QualifierValueEquals implementation for int qualifiers.
|
||||
func (q *intQualifier) QualifierValueEquals(value interface{}) bool {
|
||||
func (q *intQualifier) QualifierValueEquals(value any) bool {
|
||||
return numericValueEquals(value, q.celValue)
|
||||
}
|
||||
|
||||
// QualifierValueEquals implementation for uint qualifiers.
|
||||
func (q *uintQualifier) QualifierValueEquals(value interface{}) bool {
|
||||
func (q *uintQualifier) QualifierValueEquals(value any) bool {
|
||||
return numericValueEquals(value, q.celValue)
|
||||
}
|
||||
|
||||
// QualifierValueEquals implementation for double qualifiers.
|
||||
func (q *doubleQualifier) QualifierValueEquals(value interface{}) bool {
|
||||
func (q *doubleQualifier) QualifierValueEquals(value any) bool {
|
||||
return numericValueEquals(value, q.celValue)
|
||||
}
|
||||
|
||||
// numericValueEquals uses CEL equality to determine whether two number values are
|
||||
func numericValueEquals(value interface{}, celValue ref.Val) bool {
|
||||
func numericValueEquals(value any, celValue ref.Val) bool {
|
||||
val := types.DefaultTypeAdapter.NativeToValue(value)
|
||||
return celValue.Equal(val) == types.True
|
||||
}
|
||||
@ -272,13 +270,9 @@ func (fac *partialAttributeFactory) matchesUnknownPatterns(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unk, isUnk := val.(types.Unknown)
|
||||
if isUnk {
|
||||
return unk, nil
|
||||
}
|
||||
// If this resolution behavior ever changes, new implementations of the
|
||||
// qualifierValueEquator may be required to handle proper resolution.
|
||||
qual, err = fac.NewQualifier(nil, qual.ID(), val)
|
||||
qual, err = fac.NewQualifier(nil, qual.ID(), val, attr.IsOptional())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -338,24 +332,10 @@ func (m *attributeMatcher) AddQualifier(qual Qualifier) (Attribute, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Resolve is an implementation of the Attribute interface method which uses the
|
||||
// attributeMatcher TryResolve implementation rather than the embedded NamespacedAttribute
|
||||
// Resolve implementation.
|
||||
func (m *attributeMatcher) Resolve(vars Activation) (interface{}, error) {
|
||||
obj, found, err := m.TryResolve(vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("no such attribute: %v", m.NamespacedAttribute)
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// TryResolve is an implementation of the NamespacedAttribute interface method which tests
|
||||
// Resolve is an implementation of the NamespacedAttribute interface method which tests
|
||||
// for matching unknown attribute patterns and returns types.Unknown if present. Otherwise,
|
||||
// the standard Resolve logic applies.
|
||||
func (m *attributeMatcher) TryResolve(vars Activation) (interface{}, bool, error) {
|
||||
func (m *attributeMatcher) Resolve(vars Activation) (any, error) {
|
||||
id := m.NamespacedAttribute.ID()
|
||||
// Bug in how partial activation is resolved, should search parents as well.
|
||||
partial, isPartial := toPartialActivation(vars)
|
||||
@ -366,30 +346,23 @@ func (m *attributeMatcher) TryResolve(vars Activation) (interface{}, bool, error
|
||||
m.CandidateVariableNames(),
|
||||
m.qualifiers)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
return nil, err
|
||||
}
|
||||
if unk != nil {
|
||||
return unk, true, nil
|
||||
return unk, nil
|
||||
}
|
||||
}
|
||||
return m.NamespacedAttribute.TryResolve(vars)
|
||||
return m.NamespacedAttribute.Resolve(vars)
|
||||
}
|
||||
|
||||
// Qualify is an implementation of the Qualifier interface method.
|
||||
func (m *attributeMatcher) Qualify(vars Activation, obj interface{}) (interface{}, error) {
|
||||
val, err := m.Resolve(vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unk, isUnk := val.(types.Unknown)
|
||||
if isUnk {
|
||||
return unk, nil
|
||||
}
|
||||
qual, err := m.fac.NewQualifier(nil, m.ID(), val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return qual.Qualify(vars, obj)
|
||||
func (m *attributeMatcher) Qualify(vars Activation, obj any) (any, error) {
|
||||
return attrQualify(m.fac, vars, obj, m)
|
||||
}
|
||||
|
||||
// QualifyIfPresent is an implementation of the Qualifier interface method.
|
||||
func (m *attributeMatcher) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
|
||||
return attrQualifyIfPresent(m.fac, vars, obj, m, presenceOnly)
|
||||
}
|
||||
|
||||
func toPartialActivation(vars Activation) (PartialActivation, bool) {
|
||||
|
1098
vendor/github.com/google/cel-go/interpreter/attributes.go
generated
vendored
1098
vendor/github.com/google/cel-go/interpreter/attributes.go
generated
vendored
File diff suppressed because it is too large
Load Diff
35
vendor/github.com/google/cel-go/interpreter/coster.go
generated
vendored
35
vendor/github.com/google/cel-go/interpreter/coster.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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 interpreter
|
||||
|
||||
import "math"
|
||||
|
||||
// TODO: remove Coster.
|
||||
|
||||
// Coster calculates the heuristic cost incurred during evaluation.
|
||||
// Deprecated: Please migrate cel.EstimateCost, it supports length estimates for input data and cost estimates for
|
||||
// extension functions.
|
||||
type Coster interface {
|
||||
Cost() (min, max int64)
|
||||
}
|
||||
|
||||
// estimateCost returns the heuristic cost interval for the program.
|
||||
func estimateCost(i interface{}) (min, max int64) {
|
||||
c, ok := i.(Coster)
|
||||
if !ok {
|
||||
return 0, math.MaxInt64
|
||||
}
|
||||
return c.Cost()
|
||||
}
|
11
vendor/github.com/google/cel-go/interpreter/decorators.go
generated
vendored
11
vendor/github.com/google/cel-go/interpreter/decorators.go
generated
vendored
@ -29,7 +29,7 @@ type InterpretableDecorator func(Interpretable) (Interpretable, error)
|
||||
func decObserveEval(observer EvalObserver) InterpretableDecorator {
|
||||
return func(i Interpretable) (Interpretable, error) {
|
||||
switch inst := i.(type) {
|
||||
case *evalWatch, *evalWatchAttr, *evalWatchConst:
|
||||
case *evalWatch, *evalWatchAttr, *evalWatchConst, *evalWatchConstructor:
|
||||
// these instruction are already watching, return straight-away.
|
||||
return i, nil
|
||||
case InterpretableAttribute:
|
||||
@ -42,6 +42,11 @@ func decObserveEval(observer EvalObserver) InterpretableDecorator {
|
||||
InterpretableConst: inst,
|
||||
observer: observer,
|
||||
}, nil
|
||||
case InterpretableConstructor:
|
||||
return &evalWatchConstructor{
|
||||
constructor: inst,
|
||||
observer: observer,
|
||||
}, nil
|
||||
default:
|
||||
return &evalWatch{
|
||||
Interpretable: i,
|
||||
@ -224,8 +229,8 @@ func maybeOptimizeSetMembership(i Interpretable, inlist InterpretableCall) (Inte
|
||||
valueSet := make(map[ref.Val]ref.Val)
|
||||
for it.HasNext() == types.True {
|
||||
elem := it.Next()
|
||||
if !types.IsPrimitiveType(elem) {
|
||||
// Note, non-primitive type are not yet supported.
|
||||
if !types.IsPrimitiveType(elem) || elem.Type() == types.BytesType {
|
||||
// Note, non-primitive type are not yet supported, and []byte isn't hashable.
|
||||
return i, nil
|
||||
}
|
||||
valueSet[elem] = types.True
|
||||
|
383
vendor/github.com/google/cel-go/interpreter/formatting.go
generated
vendored
Normal file
383
vendor/github.com/google/cel-go/interpreter/formatting.go
generated
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// 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 interpreter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
)
|
||||
|
||||
type typeVerifier func(int64, ...*types.TypeValue) (bool, error)
|
||||
|
||||
// InterpolateFormattedString checks the syntax and cardinality of any string.format calls present in the expression and reports
|
||||
// any errors at compile time.
|
||||
func InterpolateFormattedString(verifier typeVerifier) InterpretableDecorator {
|
||||
return func(inter Interpretable) (Interpretable, error) {
|
||||
call, ok := inter.(InterpretableCall)
|
||||
if !ok {
|
||||
return inter, nil
|
||||
}
|
||||
if call.OverloadID() != "string_format" {
|
||||
return inter, nil
|
||||
}
|
||||
args := call.Args()
|
||||
if len(args) != 2 {
|
||||
return nil, fmt.Errorf("wrong number of arguments to string.format (expected 2, got %d)", len(args))
|
||||
}
|
||||
fmtStrInter, ok := args[0].(InterpretableConst)
|
||||
if !ok {
|
||||
return inter, nil
|
||||
}
|
||||
var fmtArgsInter InterpretableConstructor
|
||||
fmtArgsInter, ok = args[1].(InterpretableConstructor)
|
||||
if !ok {
|
||||
return inter, nil
|
||||
}
|
||||
if fmtArgsInter.Type() != types.ListType {
|
||||
// don't necessarily return an error since the list may be DynType
|
||||
return inter, nil
|
||||
}
|
||||
formatStr := fmtStrInter.Value().Value().(string)
|
||||
initVals := fmtArgsInter.InitVals()
|
||||
|
||||
formatCheck := &formatCheck{
|
||||
args: initVals,
|
||||
verifier: verifier,
|
||||
}
|
||||
// use a placeholder locale, since locale doesn't affect syntax
|
||||
_, err := ParseFormatString(formatStr, formatCheck, formatCheck, "en_US")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seenArgs := formatCheck.argsRequested
|
||||
if len(initVals) > seenArgs {
|
||||
return nil, fmt.Errorf("too many arguments supplied to string.format (expected %d, got %d)", seenArgs, len(initVals))
|
||||
}
|
||||
return inter, nil
|
||||
}
|
||||
}
|
||||
|
||||
type formatCheck struct {
|
||||
args []Interpretable
|
||||
argsRequested int
|
||||
curArgIndex int64
|
||||
enableCheckArgTypes bool
|
||||
verifier typeVerifier
|
||||
}
|
||||
|
||||
func (c *formatCheck) String(arg ref.Val, locale string) (string, error) {
|
||||
valid, err := verifyString(c.args[c.curArgIndex], c.verifier)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("string clause can only be used on strings, bools, bytes, ints, doubles, maps, lists, types, durations, and timestamps")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c *formatCheck) Decimal(arg ref.Val, locale string) (string, error) {
|
||||
id := c.args[c.curArgIndex].ID()
|
||||
valid, err := c.verifier(id, types.IntType, types.UintType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("integer clause can only be used on integers")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c *formatCheck) Fixed(precision *int) func(ref.Val, string) (string, error) {
|
||||
return func(arg ref.Val, locale string) (string, error) {
|
||||
id := c.args[c.curArgIndex].ID()
|
||||
// we allow StringType since "NaN", "Infinity", and "-Infinity" are also valid values
|
||||
valid, err := c.verifier(id, types.DoubleType, types.StringType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("fixed-point clause can only be used on doubles")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *formatCheck) Scientific(precision *int) func(ref.Val, string) (string, error) {
|
||||
return func(arg ref.Val, locale string) (string, error) {
|
||||
id := c.args[c.curArgIndex].ID()
|
||||
valid, err := c.verifier(id, types.DoubleType, types.StringType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("scientific clause can only be used on doubles")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *formatCheck) Binary(arg ref.Val, locale string) (string, error) {
|
||||
id := c.args[c.curArgIndex].ID()
|
||||
valid, err := c.verifier(id, types.IntType, types.UintType, types.BoolType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("only integers and bools can be formatted as binary")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c *formatCheck) Hex(useUpper bool) func(ref.Val, string) (string, error) {
|
||||
return func(arg ref.Val, locale string) (string, error) {
|
||||
id := c.args[c.curArgIndex].ID()
|
||||
valid, err := c.verifier(id, types.IntType, types.UintType, types.StringType, types.BytesType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("only integers, byte buffers, and strings can be formatted as hex")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *formatCheck) Octal(arg ref.Val, locale string) (string, error) {
|
||||
id := c.args[c.curArgIndex].ID()
|
||||
valid, err := c.verifier(id, types.IntType, types.UintType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
return "", errors.New("octal clause can only be used on integers")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c *formatCheck) Arg(index int64) (ref.Val, error) {
|
||||
c.argsRequested++
|
||||
c.curArgIndex = index
|
||||
// return a dummy value - this is immediately passed to back to us
|
||||
// through one of the FormatCallback functions, so anything will do
|
||||
return types.Int(0), nil
|
||||
}
|
||||
|
||||
func (c *formatCheck) ArgSize() int64 {
|
||||
return int64(len(c.args))
|
||||
}
|
||||
|
||||
func verifyString(sub Interpretable, verifier typeVerifier) (bool, error) {
|
||||
subVerified, err := verifier(sub.ID(),
|
||||
types.ListType, types.MapType, types.IntType, types.UintType, types.DoubleType,
|
||||
types.BoolType, types.StringType, types.TimestampType, types.BytesType, types.DurationType, types.TypeType, types.NullType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !subVerified {
|
||||
return false, nil
|
||||
}
|
||||
con, ok := sub.(InterpretableConstructor)
|
||||
if ok {
|
||||
members := con.InitVals()
|
||||
for _, m := range members {
|
||||
// recursively verify if we're dealing with a list/map
|
||||
verified, err := verifyString(m, verifier)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !verified {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
}
|
||||
|
||||
// FormatStringInterpolator is an interface that allows user-defined behavior
|
||||
// for formatting clause implementations, as well as argument retrieval.
|
||||
// Each function is expected to support the appropriate types as laid out in
|
||||
// the string.format documentation, and to return an error if given an inappropriate type.
|
||||
type FormatStringInterpolator interface {
|
||||
// String takes a ref.Val and a string representing the current locale identifier
|
||||
// and returns the Val formatted as a string, or an error if one occurred.
|
||||
String(ref.Val, string) (string, error)
|
||||
|
||||
// Decimal takes a ref.Val and a string representing the current locale identifier
|
||||
// and returns the Val formatted as a decimal integer, or an error if one occurred.
|
||||
Decimal(ref.Val, string) (string, error)
|
||||
|
||||
// Fixed takes an int pointer representing precision (or nil if none was given) and
|
||||
// returns a function operating in a similar manner to String and Decimal, taking a
|
||||
// ref.Val and locale and returning the appropriate string. A closure is returned
|
||||
// so precision can be set without needing an additional function call/configuration.
|
||||
Fixed(*int) func(ref.Val, string) (string, error)
|
||||
|
||||
// Scientific functions identically to Fixed, except the string returned from the closure
|
||||
// is expected to be in scientific notation.
|
||||
Scientific(*int) func(ref.Val, string) (string, error)
|
||||
|
||||
// Binary takes a ref.Val and a string representing the current locale identifier
|
||||
// and returns the Val formatted as a binary integer, or an error if one occurred.
|
||||
Binary(ref.Val, string) (string, error)
|
||||
|
||||
// Hex takes a boolean that, if true, indicates the hex string output by the returned
|
||||
// closure should use uppercase letters for A-F.
|
||||
Hex(bool) func(ref.Val, string) (string, error)
|
||||
|
||||
// Octal takes a ref.Val and a string representing the current locale identifier and
|
||||
// returns the Val formatted in octal, or an error if one occurred.
|
||||
Octal(ref.Val, string) (string, error)
|
||||
}
|
||||
|
||||
// FormatList is an interface that allows user-defined list-like datatypes to be used
|
||||
// for formatting clause implementations.
|
||||
type FormatList interface {
|
||||
// Arg returns the ref.Val at the given index, or an error if one occurred.
|
||||
Arg(int64) (ref.Val, error)
|
||||
// ArgSize returns the length of the argument list.
|
||||
ArgSize() int64
|
||||
}
|
||||
|
||||
type clauseImpl func(ref.Val, string) (string, error)
|
||||
|
||||
// ParseFormatString formats a string according to the string.format syntax, taking the clause implementations
|
||||
// from the provided FormatCallback and the args from the given FormatList.
|
||||
func ParseFormatString(formatStr string, callback FormatStringInterpolator, list FormatList, locale string) (string, error) {
|
||||
i := 0
|
||||
argIndex := 0
|
||||
var builtStr strings.Builder
|
||||
for i < len(formatStr) {
|
||||
if formatStr[i] == '%' {
|
||||
if i+1 < len(formatStr) && formatStr[i+1] == '%' {
|
||||
err := builtStr.WriteByte('%')
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing format string: %w", err)
|
||||
}
|
||||
i += 2
|
||||
continue
|
||||
} else {
|
||||
argAny, err := list.Arg(int64(argIndex))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if i+1 >= len(formatStr) {
|
||||
return "", errors.New("unexpected end of string")
|
||||
}
|
||||
if int64(argIndex) >= list.ArgSize() {
|
||||
return "", fmt.Errorf("index %d out of range", argIndex)
|
||||
}
|
||||
numRead, val, refErr := parseAndFormatClause(formatStr[i:], argAny, callback, list, locale)
|
||||
if refErr != nil {
|
||||
return "", refErr
|
||||
}
|
||||
_, err = builtStr.WriteString(val)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing format string: %w", err)
|
||||
}
|
||||
i += numRead
|
||||
argIndex++
|
||||
}
|
||||
} else {
|
||||
err := builtStr.WriteByte(formatStr[i])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error writing format string: %w", err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return builtStr.String(), nil
|
||||
}
|
||||
|
||||
// parseAndFormatClause parses the format clause at the start of the given string with val, and returns
|
||||
// how many characters were consumed and the substituted string form of val, or an error if one occurred.
|
||||
func parseAndFormatClause(formatStr string, val ref.Val, callback FormatStringInterpolator, list FormatList, locale string) (int, string, error) {
|
||||
i := 1
|
||||
read, formatter, err := parseFormattingClause(formatStr[i:], callback)
|
||||
i += read
|
||||
if err != nil {
|
||||
return -1, "", fmt.Errorf("could not parse formatting clause: %s", err)
|
||||
}
|
||||
|
||||
valStr, err := formatter(val, locale)
|
||||
if err != nil {
|
||||
return -1, "", fmt.Errorf("error during formatting: %s", err)
|
||||
}
|
||||
return i, valStr, nil
|
||||
}
|
||||
|
||||
func parseFormattingClause(formatStr string, callback FormatStringInterpolator) (int, clauseImpl, error) {
|
||||
i := 0
|
||||
read, precision, err := parsePrecision(formatStr[i:])
|
||||
i += read
|
||||
if err != nil {
|
||||
return -1, nil, fmt.Errorf("error while parsing precision: %w", err)
|
||||
}
|
||||
r := rune(formatStr[i])
|
||||
i++
|
||||
switch r {
|
||||
case 's':
|
||||
return i, callback.String, nil
|
||||
case 'd':
|
||||
return i, callback.Decimal, nil
|
||||
case 'f':
|
||||
return i, callback.Fixed(precision), nil
|
||||
case 'e':
|
||||
return i, callback.Scientific(precision), nil
|
||||
case 'b':
|
||||
return i, callback.Binary, nil
|
||||
case 'x', 'X':
|
||||
return i, callback.Hex(unicode.IsUpper(r)), nil
|
||||
case 'o':
|
||||
return i, callback.Octal, nil
|
||||
default:
|
||||
return -1, nil, fmt.Errorf("unrecognized formatting clause \"%c\"", r)
|
||||
}
|
||||
}
|
||||
|
||||
func parsePrecision(formatStr string) (int, *int, error) {
|
||||
i := 0
|
||||
if formatStr[i] != '.' {
|
||||
return i, nil, nil
|
||||
}
|
||||
i++
|
||||
var buffer strings.Builder
|
||||
for {
|
||||
if i >= len(formatStr) {
|
||||
return -1, nil, errors.New("could not find end of precision specifier")
|
||||
}
|
||||
if !isASCIIDigit(rune(formatStr[i])) {
|
||||
break
|
||||
}
|
||||
buffer.WriteByte(formatStr[i])
|
||||
i++
|
||||
}
|
||||
precision, err := strconv.Atoi(buffer.String())
|
||||
if err != nil {
|
||||
return -1, nil, fmt.Errorf("error while converting precision to integer: %w", err)
|
||||
}
|
||||
return i, &precision, nil
|
||||
}
|
||||
|
||||
func isASCIIDigit(r rune) bool {
|
||||
return r <= unicode.MaxASCII && unicode.IsDigit(r)
|
||||
}
|
2
vendor/github.com/google/cel-go/interpreter/functions/functions.go
generated
vendored
2
vendor/github.com/google/cel-go/interpreter/functions/functions.go
generated
vendored
@ -58,5 +58,5 @@ type UnaryOp func(value ref.Val) ref.Val
|
||||
type BinaryOp func(lhs ref.Val, rhs ref.Val) ref.Val
|
||||
|
||||
// FunctionOp is a function with accepts zero or more arguments and produces
|
||||
// an value (as interface{}) or error as a result.
|
||||
// a value or error as a result.
|
||||
type FunctionOp func(values ...ref.Val) ref.Val
|
||||
|
473
vendor/github.com/google/cel-go/interpreter/interpretable.go
generated
vendored
473
vendor/github.com/google/cel-go/interpreter/interpretable.go
generated
vendored
@ -15,7 +15,7 @@
|
||||
package interpreter
|
||||
|
||||
import (
|
||||
"math"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/overloads"
|
||||
@ -64,10 +64,18 @@ type InterpretableAttribute interface {
|
||||
|
||||
// Qualify replicates the Attribute.Qualify method to permit extension and interception
|
||||
// of object qualification.
|
||||
Qualify(vars Activation, obj interface{}) (interface{}, error)
|
||||
Qualify(vars Activation, obj any) (any, error)
|
||||
|
||||
// QualifyIfPresent qualifies the object if the qualifier is declared or defined on the object.
|
||||
// The 'presenceOnly' flag indicates that the value is not necessary, just a boolean status as
|
||||
// to whether the qualifier is present.
|
||||
QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error)
|
||||
|
||||
// IsOptional indicates whether the resulting value is an optional type.
|
||||
IsOptional() bool
|
||||
|
||||
// Resolve returns the value of the Attribute given the current Activation.
|
||||
Resolve(Activation) (interface{}, error)
|
||||
Resolve(Activation) (any, error)
|
||||
}
|
||||
|
||||
// InterpretableCall interface for inspecting Interpretable instructions related to function calls.
|
||||
@ -103,10 +111,8 @@ type InterpretableConstructor interface {
|
||||
// Core Interpretable implementations used during the program planning phase.
|
||||
|
||||
type evalTestOnly struct {
|
||||
id int64
|
||||
op Interpretable
|
||||
field types.String
|
||||
fieldType *ref.FieldType
|
||||
id int64
|
||||
InterpretableAttribute
|
||||
}
|
||||
|
||||
// ID implements the Interpretable interface method.
|
||||
@ -116,44 +122,55 @@ func (test *evalTestOnly) ID() int64 {
|
||||
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (test *evalTestOnly) Eval(ctx Activation) ref.Val {
|
||||
// Handle field selection on a proto in the most efficient way possible.
|
||||
if test.fieldType != nil {
|
||||
opAttr, ok := test.op.(InterpretableAttribute)
|
||||
if ok {
|
||||
opVal, err := opAttr.Resolve(ctx)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
refVal, ok := opVal.(ref.Val)
|
||||
if ok {
|
||||
opVal = refVal.Value()
|
||||
}
|
||||
if test.fieldType.IsSet(opVal) {
|
||||
return types.True
|
||||
}
|
||||
return types.False
|
||||
}
|
||||
val, err := test.Resolve(ctx)
|
||||
// Return an error if the resolve step fails
|
||||
if err != nil {
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
|
||||
obj := test.op.Eval(ctx)
|
||||
tester, ok := obj.(traits.FieldTester)
|
||||
if ok {
|
||||
return tester.IsSet(test.field)
|
||||
if optVal, isOpt := val.(*types.Optional); isOpt {
|
||||
return types.Bool(optVal.HasValue())
|
||||
}
|
||||
container, ok := obj.(traits.Container)
|
||||
if ok {
|
||||
return container.Contains(test.field)
|
||||
}
|
||||
return types.ValOrErr(obj, "invalid type for field selection.")
|
||||
return test.Adapter().NativeToValue(val)
|
||||
}
|
||||
|
||||
// Cost provides the heuristic cost of a `has(field)` macro. The cost has at least 1 for determining
|
||||
// if the field exists, apart from the cost of accessing the field.
|
||||
func (test *evalTestOnly) Cost() (min, max int64) {
|
||||
min, max = estimateCost(test.op)
|
||||
min++
|
||||
max++
|
||||
return
|
||||
// AddQualifier appends a qualifier that will always and only perform a presence test.
|
||||
func (test *evalTestOnly) AddQualifier(q Qualifier) (Attribute, error) {
|
||||
cq, ok := q.(ConstantQualifier)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("test only expressions must have constant qualifiers: %v", q)
|
||||
}
|
||||
return test.InterpretableAttribute.AddQualifier(&testOnlyQualifier{ConstantQualifier: cq})
|
||||
}
|
||||
|
||||
type testOnlyQualifier struct {
|
||||
ConstantQualifier
|
||||
}
|
||||
|
||||
// Qualify determines whether the test-only qualifier is present on the input object.
|
||||
func (q *testOnlyQualifier) Qualify(vars Activation, obj any) (any, error) {
|
||||
out, present, err := q.ConstantQualifier.QualifyIfPresent(vars, obj, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unk, isUnk := out.(types.Unknown); isUnk {
|
||||
return unk, nil
|
||||
}
|
||||
if opt, isOpt := out.(types.Optional); isOpt {
|
||||
return opt.HasValue(), nil
|
||||
}
|
||||
return present, nil
|
||||
}
|
||||
|
||||
// QualifyIfPresent returns whether the target field in the test-only expression is present.
|
||||
func (q *testOnlyQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
|
||||
// Only ever test for presence.
|
||||
return q.ConstantQualifier.QualifyIfPresent(vars, obj, true)
|
||||
}
|
||||
|
||||
// QualifierValueEquals determines whether the test-only constant qualifier equals the input value.
|
||||
func (q *testOnlyQualifier) QualifierValueEquals(value any) bool {
|
||||
// The input qualifier will always be of type string
|
||||
return q.ConstantQualifier.Value().Value() == value
|
||||
}
|
||||
|
||||
// NewConstValue creates a new constant valued Interpretable.
|
||||
@ -179,11 +196,6 @@ func (cons *evalConst) Eval(ctx Activation) ref.Val {
|
||||
return cons.val
|
||||
}
|
||||
|
||||
// Cost returns zero for a constant valued Interpretable.
|
||||
func (cons *evalConst) Cost() (min, max int64) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Value implements the InterpretableConst interface method.
|
||||
func (cons *evalConst) Value() ref.Val {
|
||||
return cons.val
|
||||
@ -233,12 +245,6 @@ func (or *evalOr) Eval(ctx Activation) ref.Val {
|
||||
return types.ValOrErr(rVal, "no such overload")
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method. The minimum possible cost incurs when the left-hand
|
||||
// side expr is sufficient in determining the evaluation result.
|
||||
func (or *evalOr) Cost() (min, max int64) {
|
||||
return calShortCircuitBinaryOpsCost(or.lhs, or.rhs)
|
||||
}
|
||||
|
||||
type evalAnd struct {
|
||||
id int64
|
||||
lhs Interpretable
|
||||
@ -283,18 +289,6 @@ func (and *evalAnd) Eval(ctx Activation) ref.Val {
|
||||
return types.ValOrErr(rVal, "no such overload")
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method. The minimum possible cost incurs when the left-hand
|
||||
// side expr is sufficient in determining the evaluation result.
|
||||
func (and *evalAnd) Cost() (min, max int64) {
|
||||
return calShortCircuitBinaryOpsCost(and.lhs, and.rhs)
|
||||
}
|
||||
|
||||
func calShortCircuitBinaryOpsCost(lhs, rhs Interpretable) (min, max int64) {
|
||||
lMin, lMax := estimateCost(lhs)
|
||||
_, rMax := estimateCost(rhs)
|
||||
return lMin, lMax + rMax + 1
|
||||
}
|
||||
|
||||
type evalEq struct {
|
||||
id int64
|
||||
lhs Interpretable
|
||||
@ -319,11 +313,6 @@ func (eq *evalEq) Eval(ctx Activation) ref.Val {
|
||||
return types.Equal(lVal, rVal)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (eq *evalEq) Cost() (min, max int64) {
|
||||
return calExhaustiveBinaryOpsCost(eq.lhs, eq.rhs)
|
||||
}
|
||||
|
||||
// Function implements the InterpretableCall interface method.
|
||||
func (*evalEq) Function() string {
|
||||
return operators.Equals
|
||||
@ -363,11 +352,6 @@ func (ne *evalNe) Eval(ctx Activation) ref.Val {
|
||||
return types.Bool(types.Equal(lVal, rVal) != types.True)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (ne *evalNe) Cost() (min, max int64) {
|
||||
return calExhaustiveBinaryOpsCost(ne.lhs, ne.rhs)
|
||||
}
|
||||
|
||||
// Function implements the InterpretableCall interface method.
|
||||
func (*evalNe) Function() string {
|
||||
return operators.NotEquals
|
||||
@ -400,11 +384,6 @@ func (zero *evalZeroArity) Eval(ctx Activation) ref.Val {
|
||||
return zero.impl()
|
||||
}
|
||||
|
||||
// Cost returns 1 representing the heuristic cost of the function.
|
||||
func (zero *evalZeroArity) Cost() (min, max int64) {
|
||||
return 1, 1
|
||||
}
|
||||
|
||||
// Function implements the InterpretableCall interface method.
|
||||
func (zero *evalZeroArity) Function() string {
|
||||
return zero.function
|
||||
@ -456,14 +435,6 @@ func (un *evalUnary) Eval(ctx Activation) ref.Val {
|
||||
return types.NewErr("no such overload: %s", un.function)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (un *evalUnary) Cost() (min, max int64) {
|
||||
min, max = estimateCost(un.arg)
|
||||
min++ // add cost for function
|
||||
max++
|
||||
return
|
||||
}
|
||||
|
||||
// Function implements the InterpretableCall interface method.
|
||||
func (un *evalUnary) Function() string {
|
||||
return un.function
|
||||
@ -522,11 +493,6 @@ func (bin *evalBinary) Eval(ctx Activation) ref.Val {
|
||||
return types.NewErr("no such overload: %s", bin.function)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (bin *evalBinary) Cost() (min, max int64) {
|
||||
return calExhaustiveBinaryOpsCost(bin.lhs, bin.rhs)
|
||||
}
|
||||
|
||||
// Function implements the InterpretableCall interface method.
|
||||
func (bin *evalBinary) Function() string {
|
||||
return bin.function
|
||||
@ -593,14 +559,6 @@ func (fn *evalVarArgs) Eval(ctx Activation) ref.Val {
|
||||
return types.NewErr("no such overload: %s", fn.function)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (fn *evalVarArgs) Cost() (min, max int64) {
|
||||
min, max = sumOfCost(fn.args)
|
||||
min++ // add cost for function
|
||||
max++
|
||||
return
|
||||
}
|
||||
|
||||
// Function implements the InterpretableCall interface method.
|
||||
func (fn *evalVarArgs) Function() string {
|
||||
return fn.function
|
||||
@ -617,9 +575,11 @@ func (fn *evalVarArgs) Args() []Interpretable {
|
||||
}
|
||||
|
||||
type evalList struct {
|
||||
id int64
|
||||
elems []Interpretable
|
||||
adapter ref.TypeAdapter
|
||||
id int64
|
||||
elems []Interpretable
|
||||
optionals []bool
|
||||
hasOptionals bool
|
||||
adapter ref.TypeAdapter
|
||||
}
|
||||
|
||||
// ID implements the Interpretable interface method.
|
||||
@ -629,14 +589,24 @@ func (l *evalList) ID() int64 {
|
||||
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (l *evalList) Eval(ctx Activation) ref.Val {
|
||||
elemVals := make([]ref.Val, len(l.elems))
|
||||
elemVals := make([]ref.Val, 0, len(l.elems))
|
||||
// If any argument is unknown or error early terminate.
|
||||
for i, elem := range l.elems {
|
||||
elemVal := elem.Eval(ctx)
|
||||
if types.IsUnknownOrError(elemVal) {
|
||||
return elemVal
|
||||
}
|
||||
elemVals[i] = elemVal
|
||||
if l.hasOptionals && l.optionals[i] {
|
||||
optVal, ok := elemVal.(*types.Optional)
|
||||
if !ok {
|
||||
return invalidOptionalElementInit(elemVal)
|
||||
}
|
||||
if !optVal.HasValue() {
|
||||
continue
|
||||
}
|
||||
elemVal = optVal.GetValue()
|
||||
}
|
||||
elemVals = append(elemVals, elemVal)
|
||||
}
|
||||
return l.adapter.NativeToValue(elemVals)
|
||||
}
|
||||
@ -649,16 +619,13 @@ func (l *evalList) Type() ref.Type {
|
||||
return types.ListType
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (l *evalList) Cost() (min, max int64) {
|
||||
return sumOfCost(l.elems)
|
||||
}
|
||||
|
||||
type evalMap struct {
|
||||
id int64
|
||||
keys []Interpretable
|
||||
vals []Interpretable
|
||||
adapter ref.TypeAdapter
|
||||
id int64
|
||||
keys []Interpretable
|
||||
vals []Interpretable
|
||||
optionals []bool
|
||||
hasOptionals bool
|
||||
adapter ref.TypeAdapter
|
||||
}
|
||||
|
||||
// ID implements the Interpretable interface method.
|
||||
@ -679,6 +646,17 @@ func (m *evalMap) Eval(ctx Activation) ref.Val {
|
||||
if types.IsUnknownOrError(valVal) {
|
||||
return valVal
|
||||
}
|
||||
if m.hasOptionals && m.optionals[i] {
|
||||
optVal, ok := valVal.(*types.Optional)
|
||||
if !ok {
|
||||
return invalidOptionalEntryInit(keyVal, valVal)
|
||||
}
|
||||
if !optVal.HasValue() {
|
||||
delete(entries, keyVal)
|
||||
continue
|
||||
}
|
||||
valVal = optVal.GetValue()
|
||||
}
|
||||
entries[keyVal] = valVal
|
||||
}
|
||||
return m.adapter.NativeToValue(entries)
|
||||
@ -704,19 +682,14 @@ func (m *evalMap) Type() ref.Type {
|
||||
return types.MapType
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (m *evalMap) Cost() (min, max int64) {
|
||||
kMin, kMax := sumOfCost(m.keys)
|
||||
vMin, vMax := sumOfCost(m.vals)
|
||||
return kMin + vMin, kMax + vMax
|
||||
}
|
||||
|
||||
type evalObj struct {
|
||||
id int64
|
||||
typeName string
|
||||
fields []string
|
||||
vals []Interpretable
|
||||
provider ref.TypeProvider
|
||||
id int64
|
||||
typeName string
|
||||
fields []string
|
||||
vals []Interpretable
|
||||
optionals []bool
|
||||
hasOptionals bool
|
||||
provider ref.TypeProvider
|
||||
}
|
||||
|
||||
// ID implements the Interpretable interface method.
|
||||
@ -733,6 +706,17 @@ func (o *evalObj) Eval(ctx Activation) ref.Val {
|
||||
if types.IsUnknownOrError(val) {
|
||||
return val
|
||||
}
|
||||
if o.hasOptionals && o.optionals[i] {
|
||||
optVal, ok := val.(*types.Optional)
|
||||
if !ok {
|
||||
return invalidOptionalEntryInit(field, val)
|
||||
}
|
||||
if !optVal.HasValue() {
|
||||
delete(fieldVals, field)
|
||||
continue
|
||||
}
|
||||
val = optVal.GetValue()
|
||||
}
|
||||
fieldVals[field] = val
|
||||
}
|
||||
return o.provider.NewValue(o.typeName, fieldVals)
|
||||
@ -746,21 +730,6 @@ func (o *evalObj) Type() ref.Type {
|
||||
return types.NewObjectTypeValue(o.typeName)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (o *evalObj) Cost() (min, max int64) {
|
||||
return sumOfCost(o.vals)
|
||||
}
|
||||
|
||||
func sumOfCost(interps []Interpretable) (min, max int64) {
|
||||
min, max = 0, 0
|
||||
for _, in := range interps {
|
||||
minT, maxT := estimateCost(in)
|
||||
min += minT
|
||||
max += maxT
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type evalFold struct {
|
||||
id int64
|
||||
accuVar string
|
||||
@ -842,38 +811,6 @@ func (fold *evalFold) Eval(ctx Activation) ref.Val {
|
||||
return res
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (fold *evalFold) Cost() (min, max int64) {
|
||||
// Compute the cost for evaluating iterRange.
|
||||
iMin, iMax := estimateCost(fold.iterRange)
|
||||
|
||||
// Compute the size of iterRange. If the size depends on the input, return the maximum possible
|
||||
// cost range.
|
||||
foldRange := fold.iterRange.Eval(EmptyActivation())
|
||||
if !foldRange.Type().HasTrait(traits.IterableType) {
|
||||
return 0, math.MaxInt64
|
||||
}
|
||||
var rangeCnt int64
|
||||
it := foldRange.(traits.Iterable).Iterator()
|
||||
for it.HasNext() == types.True {
|
||||
it.Next()
|
||||
rangeCnt++
|
||||
}
|
||||
aMin, aMax := estimateCost(fold.accu)
|
||||
cMin, cMax := estimateCost(fold.cond)
|
||||
sMin, sMax := estimateCost(fold.step)
|
||||
rMin, rMax := estimateCost(fold.result)
|
||||
if fold.exhaustive {
|
||||
cMin = cMin * rangeCnt
|
||||
sMin = sMin * rangeCnt
|
||||
}
|
||||
|
||||
// The cond and step costs are multiplied by size(iterRange). The minimum possible cost incurs
|
||||
// when the evaluation result can be determined by the first iteration.
|
||||
return iMin + aMin + cMin + sMin + rMin,
|
||||
iMax + aMax + cMax*rangeCnt + sMax*rangeCnt + rMax
|
||||
}
|
||||
|
||||
// Optional Interpretable implementations that specialize, subsume, or extend the core evaluation
|
||||
// plan via decorators.
|
||||
|
||||
@ -893,17 +830,15 @@ func (e *evalSetMembership) ID() int64 {
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (e *evalSetMembership) Eval(ctx Activation) ref.Val {
|
||||
val := e.arg.Eval(ctx)
|
||||
if types.IsUnknownOrError(val) {
|
||||
return val
|
||||
}
|
||||
if ret, found := e.valueSet[val]; found {
|
||||
return ret
|
||||
}
|
||||
return types.False
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (e *evalSetMembership) Cost() (min, max int64) {
|
||||
return estimateCost(e.arg)
|
||||
}
|
||||
|
||||
// evalWatch is an Interpretable implementation that wraps the execution of a given
|
||||
// expression so that it may observe the computed value and send it to an observer.
|
||||
type evalWatch struct {
|
||||
@ -918,15 +853,10 @@ func (e *evalWatch) Eval(ctx Activation) ref.Val {
|
||||
return val
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (e *evalWatch) Cost() (min, max int64) {
|
||||
return estimateCost(e.Interpretable)
|
||||
}
|
||||
|
||||
// evalWatchAttr describes a watcher of an instAttr Interpretable.
|
||||
// evalWatchAttr describes a watcher of an InterpretableAttribute Interpretable.
|
||||
//
|
||||
// Since the watcher may be selected against at a later stage in program planning, the watcher
|
||||
// must implement the instAttr interface by proxy.
|
||||
// must implement the InterpretableAttribute interface by proxy.
|
||||
type evalWatchAttr struct {
|
||||
InterpretableAttribute
|
||||
observer EvalObserver
|
||||
@ -953,11 +883,6 @@ func (e *evalWatchAttr) AddQualifier(q Qualifier) (Attribute, error) {
|
||||
return e, err
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (e *evalWatchAttr) Cost() (min, max int64) {
|
||||
return estimateCost(e.InterpretableAttribute)
|
||||
}
|
||||
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (e *evalWatchAttr) Eval(vars Activation) ref.Val {
|
||||
val := e.InterpretableAttribute.Eval(vars)
|
||||
@ -973,17 +898,12 @@ type evalWatchConstQual struct {
|
||||
adapter ref.TypeAdapter
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (e *evalWatchConstQual) Cost() (min, max int64) {
|
||||
return estimateCost(e.ConstantQualifier)
|
||||
}
|
||||
|
||||
// Qualify observes the qualification of a object via a constant boolean, int, string, or uint.
|
||||
func (e *evalWatchConstQual) Qualify(vars Activation, obj interface{}) (interface{}, error) {
|
||||
func (e *evalWatchConstQual) Qualify(vars Activation, obj any) (any, error) {
|
||||
out, err := e.ConstantQualifier.Qualify(vars, obj)
|
||||
var val ref.Val
|
||||
if err != nil {
|
||||
val = types.NewErr(err.Error())
|
||||
val = types.WrapErr(err)
|
||||
} else {
|
||||
val = e.adapter.NativeToValue(out)
|
||||
}
|
||||
@ -991,8 +911,25 @@ func (e *evalWatchConstQual) Qualify(vars Activation, obj interface{}) (interfac
|
||||
return out, err
|
||||
}
|
||||
|
||||
// QualifyIfPresent conditionally qualifies the variable and only records a value if one is present.
|
||||
func (e *evalWatchConstQual) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
|
||||
out, present, err := e.ConstantQualifier.QualifyIfPresent(vars, obj, presenceOnly)
|
||||
var val ref.Val
|
||||
if err != nil {
|
||||
val = types.WrapErr(err)
|
||||
} else if out != nil {
|
||||
val = e.adapter.NativeToValue(out)
|
||||
} else if presenceOnly {
|
||||
val = types.Bool(present)
|
||||
}
|
||||
if present || presenceOnly {
|
||||
e.observer(e.ID(), e.ConstantQualifier, val)
|
||||
}
|
||||
return out, present, err
|
||||
}
|
||||
|
||||
// QualifierValueEquals tests whether the incoming value is equal to the qualifying constant.
|
||||
func (e *evalWatchConstQual) QualifierValueEquals(value interface{}) bool {
|
||||
func (e *evalWatchConstQual) QualifierValueEquals(value any) bool {
|
||||
qve, ok := e.ConstantQualifier.(qualifierValueEquator)
|
||||
return ok && qve.QualifierValueEquals(value)
|
||||
}
|
||||
@ -1004,17 +941,12 @@ type evalWatchQual struct {
|
||||
adapter ref.TypeAdapter
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (e *evalWatchQual) Cost() (min, max int64) {
|
||||
return estimateCost(e.Qualifier)
|
||||
}
|
||||
|
||||
// Qualify observes the qualification of a object via a value computed at runtime.
|
||||
func (e *evalWatchQual) Qualify(vars Activation, obj interface{}) (interface{}, error) {
|
||||
func (e *evalWatchQual) Qualify(vars Activation, obj any) (any, error) {
|
||||
out, err := e.Qualifier.Qualify(vars, obj)
|
||||
var val ref.Val
|
||||
if err != nil {
|
||||
val = types.NewErr(err.Error())
|
||||
val = types.WrapErr(err)
|
||||
} else {
|
||||
val = e.adapter.NativeToValue(out)
|
||||
}
|
||||
@ -1022,6 +954,23 @@ func (e *evalWatchQual) Qualify(vars Activation, obj interface{}) (interface{},
|
||||
return out, err
|
||||
}
|
||||
|
||||
// QualifyIfPresent conditionally qualifies the variable and only records a value if one is present.
|
||||
func (e *evalWatchQual) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
|
||||
out, present, err := e.Qualifier.QualifyIfPresent(vars, obj, presenceOnly)
|
||||
var val ref.Val
|
||||
if err != nil {
|
||||
val = types.WrapErr(err)
|
||||
} else if out != nil {
|
||||
val = e.adapter.NativeToValue(out)
|
||||
} else if presenceOnly {
|
||||
val = types.Bool(present)
|
||||
}
|
||||
if present || presenceOnly {
|
||||
e.observer(e.ID(), e.Qualifier, val)
|
||||
}
|
||||
return out, present, err
|
||||
}
|
||||
|
||||
// evalWatchConst describes a watcher of an instConst Interpretable.
|
||||
type evalWatchConst struct {
|
||||
InterpretableConst
|
||||
@ -1035,11 +984,6 @@ func (e *evalWatchConst) Eval(vars Activation) ref.Val {
|
||||
return val
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (e *evalWatchConst) Cost() (min, max int64) {
|
||||
return estimateCost(e.InterpretableConst)
|
||||
}
|
||||
|
||||
// evalExhaustiveOr is just like evalOr, but does not short-circuit argument evaluation.
|
||||
type evalExhaustiveOr struct {
|
||||
id int64
|
||||
@ -1078,12 +1022,7 @@ func (or *evalExhaustiveOr) Eval(ctx Activation) ref.Val {
|
||||
if types.IsError(lVal) {
|
||||
return lVal
|
||||
}
|
||||
return types.ValOrErr(rVal, "no such overload")
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (or *evalExhaustiveOr) Cost() (min, max int64) {
|
||||
return calExhaustiveBinaryOpsCost(or.lhs, or.rhs)
|
||||
return types.MaybeNoSuchOverloadErr(rVal)
|
||||
}
|
||||
|
||||
// evalExhaustiveAnd is just like evalAnd, but does not short-circuit argument evaluation.
|
||||
@ -1124,18 +1063,7 @@ func (and *evalExhaustiveAnd) Eval(ctx Activation) ref.Val {
|
||||
if types.IsError(lVal) {
|
||||
return lVal
|
||||
}
|
||||
return types.ValOrErr(rVal, "no such overload")
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (and *evalExhaustiveAnd) Cost() (min, max int64) {
|
||||
return calExhaustiveBinaryOpsCost(and.lhs, and.rhs)
|
||||
}
|
||||
|
||||
func calExhaustiveBinaryOpsCost(lhs, rhs Interpretable) (min, max int64) {
|
||||
lMin, lMax := estimateCost(lhs)
|
||||
rMin, rMax := estimateCost(rhs)
|
||||
return lMin + rMin + 1, lMax + rMax + 1
|
||||
return types.MaybeNoSuchOverloadErr(rVal)
|
||||
}
|
||||
|
||||
// evalExhaustiveConditional is like evalConditional, but does not short-circuit argument
|
||||
@ -1154,77 +1082,114 @@ func (cond *evalExhaustiveConditional) ID() int64 {
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (cond *evalExhaustiveConditional) Eval(ctx Activation) ref.Val {
|
||||
cVal := cond.attr.expr.Eval(ctx)
|
||||
tVal, err := cond.attr.truthy.Resolve(ctx)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
fVal, err := cond.attr.falsy.Resolve(ctx)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
tVal, tErr := cond.attr.truthy.Resolve(ctx)
|
||||
fVal, fErr := cond.attr.falsy.Resolve(ctx)
|
||||
cBool, ok := cVal.(types.Bool)
|
||||
if !ok {
|
||||
return types.ValOrErr(cVal, "no such overload")
|
||||
}
|
||||
if cBool {
|
||||
if tErr != nil {
|
||||
return types.WrapErr(tErr)
|
||||
}
|
||||
return cond.adapter.NativeToValue(tVal)
|
||||
}
|
||||
if fErr != nil {
|
||||
return types.WrapErr(fErr)
|
||||
}
|
||||
return cond.adapter.NativeToValue(fVal)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (cond *evalExhaustiveConditional) Cost() (min, max int64) {
|
||||
return cond.attr.Cost()
|
||||
}
|
||||
|
||||
// evalAttr evaluates an Attribute value.
|
||||
type evalAttr struct {
|
||||
adapter ref.TypeAdapter
|
||||
attr Attribute
|
||||
adapter ref.TypeAdapter
|
||||
attr Attribute
|
||||
optional bool
|
||||
}
|
||||
|
||||
var _ InterpretableAttribute = &evalAttr{}
|
||||
|
||||
// ID of the attribute instruction.
|
||||
func (a *evalAttr) ID() int64 {
|
||||
return a.attr.ID()
|
||||
}
|
||||
|
||||
// AddQualifier implements the instAttr interface method.
|
||||
// AddQualifier implements the InterpretableAttribute interface method.
|
||||
func (a *evalAttr) AddQualifier(qual Qualifier) (Attribute, error) {
|
||||
attr, err := a.attr.AddQualifier(qual)
|
||||
a.attr = attr
|
||||
return attr, err
|
||||
}
|
||||
|
||||
// Attr implements the instAttr interface method.
|
||||
// Attr implements the InterpretableAttribute interface method.
|
||||
func (a *evalAttr) Attr() Attribute {
|
||||
return a.attr
|
||||
}
|
||||
|
||||
// Adapter implements the instAttr interface method.
|
||||
// Adapter implements the InterpretableAttribute interface method.
|
||||
func (a *evalAttr) Adapter() ref.TypeAdapter {
|
||||
return a.adapter
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (a *evalAttr) Cost() (min, max int64) {
|
||||
return estimateCost(a.attr)
|
||||
}
|
||||
|
||||
// Eval implements the Interpretable interface method.
|
||||
func (a *evalAttr) Eval(ctx Activation) ref.Val {
|
||||
v, err := a.attr.Resolve(ctx)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
return types.WrapErr(err)
|
||||
}
|
||||
return a.adapter.NativeToValue(v)
|
||||
}
|
||||
|
||||
// Qualify proxies to the Attribute's Qualify method.
|
||||
func (a *evalAttr) Qualify(ctx Activation, obj interface{}) (interface{}, error) {
|
||||
func (a *evalAttr) Qualify(ctx Activation, obj any) (any, error) {
|
||||
return a.attr.Qualify(ctx, obj)
|
||||
}
|
||||
|
||||
// QualifyIfPresent proxies to the Attribute's QualifyIfPresent method.
|
||||
func (a *evalAttr) QualifyIfPresent(ctx Activation, obj any, presenceOnly bool) (any, bool, error) {
|
||||
return a.attr.QualifyIfPresent(ctx, obj, presenceOnly)
|
||||
}
|
||||
|
||||
func (a *evalAttr) IsOptional() bool {
|
||||
return a.optional
|
||||
}
|
||||
|
||||
// Resolve proxies to the Attribute's Resolve method.
|
||||
func (a *evalAttr) Resolve(ctx Activation) (interface{}, error) {
|
||||
func (a *evalAttr) Resolve(ctx Activation) (any, error) {
|
||||
return a.attr.Resolve(ctx)
|
||||
}
|
||||
|
||||
type evalWatchConstructor struct {
|
||||
constructor InterpretableConstructor
|
||||
observer EvalObserver
|
||||
}
|
||||
|
||||
// InitVals implements the InterpretableConstructor InitVals function.
|
||||
func (c *evalWatchConstructor) InitVals() []Interpretable {
|
||||
return c.constructor.InitVals()
|
||||
}
|
||||
|
||||
// Type implements the InterpretableConstructor Type function.
|
||||
func (c *evalWatchConstructor) Type() ref.Type {
|
||||
return c.constructor.Type()
|
||||
}
|
||||
|
||||
// ID implements the Interpretable ID function.
|
||||
func (c *evalWatchConstructor) ID() int64 {
|
||||
return c.constructor.ID()
|
||||
}
|
||||
|
||||
// Eval implements the Interpretable Eval function.
|
||||
func (c *evalWatchConstructor) Eval(ctx Activation) ref.Val {
|
||||
val := c.constructor.Eval(ctx)
|
||||
c.observer(c.ID(), c.constructor, val)
|
||||
return val
|
||||
}
|
||||
|
||||
func invalidOptionalEntryInit(field any, value ref.Val) ref.Val {
|
||||
return types.NewErr("cannot initialize optional entry '%v' from non-optional value %v", field, value)
|
||||
}
|
||||
|
||||
func invalidOptionalElementInit(value ref.Val) ref.Val {
|
||||
return types.NewErr("cannot initialize optional list element from non-optional value %v", value)
|
||||
}
|
||||
|
12
vendor/github.com/google/cel-go/interpreter/interpreter.go
generated
vendored
12
vendor/github.com/google/cel-go/interpreter/interpreter.go
generated
vendored
@ -29,19 +29,17 @@ import (
|
||||
type Interpreter interface {
|
||||
// NewInterpretable creates an Interpretable from a checked expression and an
|
||||
// optional list of InterpretableDecorator values.
|
||||
NewInterpretable(checked *exprpb.CheckedExpr,
|
||||
decorators ...InterpretableDecorator) (Interpretable, error)
|
||||
NewInterpretable(checked *exprpb.CheckedExpr, decorators ...InterpretableDecorator) (Interpretable, error)
|
||||
|
||||
// NewUncheckedInterpretable returns an Interpretable from a parsed expression
|
||||
// and an optional list of InterpretableDecorator values.
|
||||
NewUncheckedInterpretable(expr *exprpb.Expr,
|
||||
decorators ...InterpretableDecorator) (Interpretable, error)
|
||||
NewUncheckedInterpretable(expr *exprpb.Expr, decorators ...InterpretableDecorator) (Interpretable, error)
|
||||
}
|
||||
|
||||
// EvalObserver is a functional interface that accepts an expression id and an observed value.
|
||||
// The id identifies the expression that was evaluated, the programStep is the Interpretable or Qualifier that
|
||||
// was evaluated and value is the result of the evaluation.
|
||||
type EvalObserver func(id int64, programStep interface{}, value ref.Val)
|
||||
type EvalObserver func(id int64, programStep any, value ref.Val)
|
||||
|
||||
// Observe constructs a decorator that calls all the provided observers in order after evaluating each Interpretable
|
||||
// or Qualifier during program evaluation.
|
||||
@ -49,7 +47,7 @@ func Observe(observers ...EvalObserver) InterpretableDecorator {
|
||||
if len(observers) == 1 {
|
||||
return decObserveEval(observers[0])
|
||||
}
|
||||
observeFn := func(id int64, programStep interface{}, val ref.Val) {
|
||||
observeFn := func(id int64, programStep any, val ref.Val) {
|
||||
for _, observer := range observers {
|
||||
observer(id, programStep, val)
|
||||
}
|
||||
@ -96,7 +94,7 @@ func TrackState(state EvalState) InterpretableDecorator {
|
||||
// This decorator is not thread-safe, and the EvalState must be reset between Eval()
|
||||
// calls.
|
||||
func EvalStateObserver(state EvalState) EvalObserver {
|
||||
return func(id int64, programStep interface{}, val ref.Val) {
|
||||
return func(id int64, programStep any, val ref.Val) {
|
||||
state.SetValue(id, val)
|
||||
}
|
||||
}
|
||||
|
178
vendor/github.com/google/cel-go/interpreter/planner.go
generated
vendored
178
vendor/github.com/google/cel-go/interpreter/planner.go
generated
vendored
@ -20,7 +20,6 @@ import (
|
||||
|
||||
"github.com/google/cel-go/common/containers"
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter/functions"
|
||||
|
||||
@ -189,16 +188,7 @@ func (p *planner) planSelect(expr *exprpb.Expr) (Interpretable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine the field type if this is a proto message type.
|
||||
var fieldType *ref.FieldType
|
||||
opType := p.typeMap[sel.GetOperand().GetId()]
|
||||
if opType.GetMessageType() != "" {
|
||||
ft, found := p.provider.FindFieldType(opType.GetMessageType(), sel.GetField())
|
||||
if found && ft.IsSet != nil && ft.GetFrom != nil {
|
||||
fieldType = ft
|
||||
}
|
||||
}
|
||||
|
||||
// If the Select was marked TestOnly, this is a presence test.
|
||||
//
|
||||
@ -211,37 +201,31 @@ func (p *planner) planSelect(expr *exprpb.Expr) (Interpretable, error) {
|
||||
// If a string named 'a.b.c' is declared in the environment and referenced within `has(a.b.c)`,
|
||||
// it is not clear whether has should error or follow the convention defined for structured
|
||||
// values.
|
||||
if sel.TestOnly {
|
||||
// Return the test only eval expression.
|
||||
return &evalTestOnly{
|
||||
id: expr.GetId(),
|
||||
field: types.String(sel.GetField()),
|
||||
fieldType: fieldType,
|
||||
op: op,
|
||||
}, nil
|
||||
}
|
||||
// Build a qualifier.
|
||||
qual, err := p.attrFactory.NewQualifier(
|
||||
opType, expr.GetId(), sel.GetField())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Lastly, create a field selection Interpretable.
|
||||
|
||||
// Establish the attribute reference.
|
||||
attr, isAttr := op.(InterpretableAttribute)
|
||||
if isAttr {
|
||||
_, err = attr.AddQualifier(qual)
|
||||
return attr, err
|
||||
if !isAttr {
|
||||
attr, err = p.relativeAttr(op.ID(), op, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
relAttr, err := p.relativeAttr(op.ID(), op)
|
||||
// Build a qualifier for the attribute.
|
||||
qual, err := p.attrFactory.NewQualifier(opType, expr.GetId(), sel.GetField(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = relAttr.AddQualifier(qual)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Modify the attribute to be test-only.
|
||||
if sel.GetTestOnly() {
|
||||
attr = &evalTestOnly{
|
||||
id: expr.GetId(),
|
||||
InterpretableAttribute: attr,
|
||||
}
|
||||
}
|
||||
return relAttr, nil
|
||||
// Append the qualifier on the attribute.
|
||||
_, err = attr.AddQualifier(qual)
|
||||
return attr, err
|
||||
}
|
||||
|
||||
// planCall creates a callable Interpretable while specializing for common functions and invocation
|
||||
@ -286,7 +270,9 @@ func (p *planner) planCall(expr *exprpb.Expr) (Interpretable, error) {
|
||||
case operators.NotEquals:
|
||||
return p.planCallNotEqual(expr, args)
|
||||
case operators.Index:
|
||||
return p.planCallIndex(expr, args)
|
||||
return p.planCallIndex(expr, args, false)
|
||||
case operators.OptSelect, operators.OptIndex:
|
||||
return p.planCallIndex(expr, args, true)
|
||||
}
|
||||
|
||||
// Otherwise, generate Interpretable calls specialized by argument count.
|
||||
@ -423,8 +409,7 @@ func (p *planner) planCallVarArgs(expr *exprpb.Expr,
|
||||
}
|
||||
|
||||
// planCallEqual generates an equals (==) Interpretable.
|
||||
func (p *planner) planCallEqual(expr *exprpb.Expr,
|
||||
args []Interpretable) (Interpretable, error) {
|
||||
func (p *planner) planCallEqual(expr *exprpb.Expr, args []Interpretable) (Interpretable, error) {
|
||||
return &evalEq{
|
||||
id: expr.GetId(),
|
||||
lhs: args[0],
|
||||
@ -433,8 +418,7 @@ func (p *planner) planCallEqual(expr *exprpb.Expr,
|
||||
}
|
||||
|
||||
// planCallNotEqual generates a not equals (!=) Interpretable.
|
||||
func (p *planner) planCallNotEqual(expr *exprpb.Expr,
|
||||
args []Interpretable) (Interpretable, error) {
|
||||
func (p *planner) planCallNotEqual(expr *exprpb.Expr, args []Interpretable) (Interpretable, error) {
|
||||
return &evalNe{
|
||||
id: expr.GetId(),
|
||||
lhs: args[0],
|
||||
@ -443,8 +427,7 @@ func (p *planner) planCallNotEqual(expr *exprpb.Expr,
|
||||
}
|
||||
|
||||
// planCallLogicalAnd generates a logical and (&&) Interpretable.
|
||||
func (p *planner) planCallLogicalAnd(expr *exprpb.Expr,
|
||||
args []Interpretable) (Interpretable, error) {
|
||||
func (p *planner) planCallLogicalAnd(expr *exprpb.Expr, args []Interpretable) (Interpretable, error) {
|
||||
return &evalAnd{
|
||||
id: expr.GetId(),
|
||||
lhs: args[0],
|
||||
@ -453,8 +436,7 @@ func (p *planner) planCallLogicalAnd(expr *exprpb.Expr,
|
||||
}
|
||||
|
||||
// planCallLogicalOr generates a logical or (||) Interpretable.
|
||||
func (p *planner) planCallLogicalOr(expr *exprpb.Expr,
|
||||
args []Interpretable) (Interpretable, error) {
|
||||
func (p *planner) planCallLogicalOr(expr *exprpb.Expr, args []Interpretable) (Interpretable, error) {
|
||||
return &evalOr{
|
||||
id: expr.GetId(),
|
||||
lhs: args[0],
|
||||
@ -463,10 +445,8 @@ func (p *planner) planCallLogicalOr(expr *exprpb.Expr,
|
||||
}
|
||||
|
||||
// planCallConditional generates a conditional / ternary (c ? t : f) Interpretable.
|
||||
func (p *planner) planCallConditional(expr *exprpb.Expr,
|
||||
args []Interpretable) (Interpretable, error) {
|
||||
func (p *planner) planCallConditional(expr *exprpb.Expr, args []Interpretable) (Interpretable, error) {
|
||||
cond := args[0]
|
||||
|
||||
t := args[1]
|
||||
var tAttr Attribute
|
||||
truthyAttr, isTruthyAttr := t.(InterpretableAttribute)
|
||||
@ -493,48 +473,54 @@ func (p *planner) planCallConditional(expr *exprpb.Expr,
|
||||
|
||||
// planCallIndex either extends an attribute with the argument to the index operation, or creates
|
||||
// a relative attribute based on the return of a function call or operation.
|
||||
func (p *planner) planCallIndex(expr *exprpb.Expr,
|
||||
args []Interpretable) (Interpretable, error) {
|
||||
func (p *planner) planCallIndex(expr *exprpb.Expr, args []Interpretable, optional bool) (Interpretable, error) {
|
||||
op := args[0]
|
||||
ind := args[1]
|
||||
opAttr, err := p.relativeAttr(op.ID(), op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opType := p.typeMap[expr.GetCallExpr().GetTarget().GetId()]
|
||||
indConst, isIndConst := ind.(InterpretableConst)
|
||||
if isIndConst {
|
||||
qual, err := p.attrFactory.NewQualifier(
|
||||
opType, expr.GetId(), indConst.Value())
|
||||
|
||||
// Establish the attribute reference.
|
||||
var err error
|
||||
attr, isAttr := op.(InterpretableAttribute)
|
||||
if !isAttr {
|
||||
attr, err = p.relativeAttr(op.ID(), op, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = opAttr.AddQualifier(qual)
|
||||
return opAttr, err
|
||||
}
|
||||
indAttr, isIndAttr := ind.(InterpretableAttribute)
|
||||
if isIndAttr {
|
||||
qual, err := p.attrFactory.NewQualifier(
|
||||
opType, expr.GetId(), indAttr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = opAttr.AddQualifier(qual)
|
||||
return opAttr, err
|
||||
|
||||
// Construct the qualifier type.
|
||||
var qual Qualifier
|
||||
switch ind := ind.(type) {
|
||||
case InterpretableConst:
|
||||
qual, err = p.attrFactory.NewQualifier(opType, expr.GetId(), ind.Value(), optional)
|
||||
case InterpretableAttribute:
|
||||
qual, err = p.attrFactory.NewQualifier(opType, expr.GetId(), ind, optional)
|
||||
default:
|
||||
qual, err = p.relativeAttr(expr.GetId(), ind, optional)
|
||||
}
|
||||
indQual, err := p.relativeAttr(expr.GetId(), ind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = opAttr.AddQualifier(indQual)
|
||||
return opAttr, err
|
||||
|
||||
// Add the qualifier to the attribute
|
||||
_, err = attr.AddQualifier(qual)
|
||||
return attr, err
|
||||
}
|
||||
|
||||
// planCreateList generates a list construction Interpretable.
|
||||
func (p *planner) planCreateList(expr *exprpb.Expr) (Interpretable, error) {
|
||||
list := expr.GetListExpr()
|
||||
elems := make([]Interpretable, len(list.GetElements()))
|
||||
for i, elem := range list.GetElements() {
|
||||
optionalIndices := list.GetOptionalIndices()
|
||||
elements := list.GetElements()
|
||||
optionals := make([]bool, len(elements))
|
||||
for _, index := range optionalIndices {
|
||||
if index < 0 || index >= int32(len(elements)) {
|
||||
return nil, fmt.Errorf("optional index %d out of element bounds [0, %d]", index, len(elements))
|
||||
}
|
||||
optionals[index] = true
|
||||
}
|
||||
elems := make([]Interpretable, len(elements))
|
||||
for i, elem := range elements {
|
||||
elemVal, err := p.Plan(elem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -542,9 +528,11 @@ func (p *planner) planCreateList(expr *exprpb.Expr) (Interpretable, error) {
|
||||
elems[i] = elemVal
|
||||
}
|
||||
return &evalList{
|
||||
id: expr.GetId(),
|
||||
elems: elems,
|
||||
adapter: p.adapter,
|
||||
id: expr.GetId(),
|
||||
elems: elems,
|
||||
optionals: optionals,
|
||||
hasOptionals: len(optionals) != 0,
|
||||
adapter: p.adapter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -555,6 +543,7 @@ func (p *planner) planCreateStruct(expr *exprpb.Expr) (Interpretable, error) {
|
||||
return p.planCreateObj(expr)
|
||||
}
|
||||
entries := str.GetEntries()
|
||||
optionals := make([]bool, len(entries))
|
||||
keys := make([]Interpretable, len(entries))
|
||||
vals := make([]Interpretable, len(entries))
|
||||
for i, entry := range entries {
|
||||
@ -569,23 +558,27 @@ func (p *planner) planCreateStruct(expr *exprpb.Expr) (Interpretable, error) {
|
||||
return nil, err
|
||||
}
|
||||
vals[i] = valVal
|
||||
optionals[i] = entry.GetOptionalEntry()
|
||||
}
|
||||
return &evalMap{
|
||||
id: expr.GetId(),
|
||||
keys: keys,
|
||||
vals: vals,
|
||||
adapter: p.adapter,
|
||||
id: expr.GetId(),
|
||||
keys: keys,
|
||||
vals: vals,
|
||||
optionals: optionals,
|
||||
hasOptionals: len(optionals) != 0,
|
||||
adapter: p.adapter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// planCreateObj generates an object construction Interpretable.
|
||||
func (p *planner) planCreateObj(expr *exprpb.Expr) (Interpretable, error) {
|
||||
obj := expr.GetStructExpr()
|
||||
typeName, defined := p.resolveTypeName(obj.MessageName)
|
||||
typeName, defined := p.resolveTypeName(obj.GetMessageName())
|
||||
if !defined {
|
||||
return nil, fmt.Errorf("unknown type: %s", typeName)
|
||||
return nil, fmt.Errorf("unknown type: %s", obj.GetMessageName())
|
||||
}
|
||||
entries := obj.GetEntries()
|
||||
optionals := make([]bool, len(entries))
|
||||
fields := make([]string, len(entries))
|
||||
vals := make([]Interpretable, len(entries))
|
||||
for i, entry := range entries {
|
||||
@ -595,13 +588,16 @@ func (p *planner) planCreateObj(expr *exprpb.Expr) (Interpretable, error) {
|
||||
return nil, err
|
||||
}
|
||||
vals[i] = val
|
||||
optionals[i] = entry.GetOptionalEntry()
|
||||
}
|
||||
return &evalObj{
|
||||
id: expr.GetId(),
|
||||
typeName: typeName,
|
||||
fields: fields,
|
||||
vals: vals,
|
||||
provider: p.provider,
|
||||
id: expr.GetId(),
|
||||
typeName: typeName,
|
||||
fields: fields,
|
||||
vals: vals,
|
||||
optionals: optionals,
|
||||
hasOptionals: len(optionals) != 0,
|
||||
provider: p.provider,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -753,14 +749,18 @@ func (p *planner) resolveFunction(expr *exprpb.Expr) (*exprpb.Expr, string, stri
|
||||
return target, fnName, ""
|
||||
}
|
||||
|
||||
func (p *planner) relativeAttr(id int64, eval Interpretable) (InterpretableAttribute, error) {
|
||||
// relativeAttr indicates that the attribute in this case acts as a qualifier and as such needs to
|
||||
// be observed to ensure that it's evaluation value is properly recorded for state tracking.
|
||||
func (p *planner) relativeAttr(id int64, eval Interpretable, opt bool) (InterpretableAttribute, error) {
|
||||
eAttr, ok := eval.(InterpretableAttribute)
|
||||
if !ok {
|
||||
eAttr = &evalAttr{
|
||||
adapter: p.adapter,
|
||||
attr: p.attrFactory.RelativeAttribute(id, eval),
|
||||
adapter: p.adapter,
|
||||
attr: p.attrFactory.RelativeAttribute(id, eval),
|
||||
optional: opt,
|
||||
}
|
||||
}
|
||||
// This looks like it should either decorate the new evalAttr node, or early return the InterpretableAttribute
|
||||
decAttr, err := p.decorate(eAttr, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
381
vendor/github.com/google/cel-go/interpreter/prune.go
generated
vendored
381
vendor/github.com/google/cel-go/interpreter/prune.go
generated
vendored
@ -16,6 +16,7 @@ package interpreter
|
||||
|
||||
import (
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/overloads"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
@ -26,6 +27,7 @@ import (
|
||||
|
||||
type astPruner struct {
|
||||
expr *exprpb.Expr
|
||||
macroCalls map[int64]*exprpb.Expr
|
||||
state EvalState
|
||||
nextExprID int64
|
||||
}
|
||||
@ -65,13 +67,22 @@ type astPruner struct {
|
||||
// compiled and constant folded expressions, but is not willing to constant
|
||||
// fold(and thus cache results of) some external calls, then they can prepare
|
||||
// the overloads accordingly.
|
||||
func PruneAst(expr *exprpb.Expr, state EvalState) *exprpb.Expr {
|
||||
func PruneAst(expr *exprpb.Expr, macroCalls map[int64]*exprpb.Expr, state EvalState) *exprpb.ParsedExpr {
|
||||
pruneState := NewEvalState()
|
||||
for _, id := range state.IDs() {
|
||||
v, _ := state.Value(id)
|
||||
pruneState.SetValue(id, v)
|
||||
}
|
||||
pruner := &astPruner{
|
||||
expr: expr,
|
||||
state: state,
|
||||
nextExprID: 1}
|
||||
newExpr, _ := pruner.prune(expr)
|
||||
return newExpr
|
||||
macroCalls: macroCalls,
|
||||
state: pruneState,
|
||||
nextExprID: getMaxID(expr)}
|
||||
newExpr, _ := pruner.maybePrune(expr)
|
||||
return &exprpb.ParsedExpr{
|
||||
Expr: newExpr,
|
||||
SourceInfo: &exprpb.SourceInfo{MacroCalls: pruner.macroCalls},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *astPruner) createLiteral(id int64, val *exprpb.Constant) *exprpb.Expr {
|
||||
@ -84,28 +95,50 @@ func (p *astPruner) createLiteral(id int64, val *exprpb.Constant) *exprpb.Expr {
|
||||
}
|
||||
|
||||
func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (*exprpb.Expr, bool) {
|
||||
switch val.Type() {
|
||||
case types.BoolType:
|
||||
switch v := val.(type) {
|
||||
case types.Bool:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_BoolValue{BoolValue: val.Value().(bool)}}), true
|
||||
case types.IntType:
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_BoolValue{BoolValue: bool(v)}}), true
|
||||
case types.Bytes:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_Int64Value{Int64Value: val.Value().(int64)}}), true
|
||||
case types.UintType:
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_BytesValue{BytesValue: []byte(v)}}), true
|
||||
case types.Double:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_Uint64Value{Uint64Value: val.Value().(uint64)}}), true
|
||||
case types.StringType:
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_DoubleValue{DoubleValue: float64(v)}}), true
|
||||
case types.Duration:
|
||||
p.state.SetValue(id, val)
|
||||
durationString := string(v.ConvertToType(types.StringType).(types.String))
|
||||
return &exprpb.Expr{
|
||||
Id: id,
|
||||
ExprKind: &exprpb.Expr_CallExpr{
|
||||
CallExpr: &exprpb.Expr_Call{
|
||||
Function: overloads.TypeConvertDuration,
|
||||
Args: []*exprpb.Expr{
|
||||
p.createLiteral(p.nextID(),
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_StringValue{StringValue: durationString}}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, true
|
||||
case types.Int:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_StringValue{StringValue: val.Value().(string)}}), true
|
||||
case types.DoubleType:
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_Int64Value{Int64Value: int64(v)}}), true
|
||||
case types.Uint:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_DoubleValue{DoubleValue: val.Value().(float64)}}), true
|
||||
case types.BytesType:
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_Uint64Value{Uint64Value: uint64(v)}}), true
|
||||
case types.String:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_BytesValue{BytesValue: val.Value().([]byte)}}), true
|
||||
case types.NullType:
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_StringValue{StringValue: string(v)}}), true
|
||||
case types.Null:
|
||||
p.state.SetValue(id, val)
|
||||
return p.createLiteral(id,
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_NullValue{NullValue: val.Value().(structpb.NullValue)}}), true
|
||||
&exprpb.Constant{ConstantKind: &exprpb.Constant_NullValue{NullValue: v.Value().(structpb.NullValue)}}), true
|
||||
}
|
||||
|
||||
// Attempt to build a list literal.
|
||||
@ -123,6 +156,7 @@ func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (*exprpb.Expr, boo
|
||||
}
|
||||
elemExprs[i] = elemExpr
|
||||
}
|
||||
p.state.SetValue(id, val)
|
||||
return &exprpb.Expr{
|
||||
Id: id,
|
||||
ExprKind: &exprpb.Expr_ListExpr{
|
||||
@ -162,6 +196,7 @@ func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (*exprpb.Expr, boo
|
||||
entries[i] = entry
|
||||
i++
|
||||
}
|
||||
p.state.SetValue(id, val)
|
||||
return &exprpb.Expr{
|
||||
Id: id,
|
||||
ExprKind: &exprpb.Expr_StructExpr{
|
||||
@ -177,70 +212,147 @@ func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (*exprpb.Expr, boo
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneAndOr(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
if !p.existsWithUnknownValue(node.GetId()) {
|
||||
func (p *astPruner) maybePruneOptional(elem *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
elemVal, found := p.value(elem.GetId())
|
||||
if found && elemVal.Type() == types.OptionalType {
|
||||
opt := elemVal.(*types.Optional)
|
||||
if !opt.HasValue() {
|
||||
return nil, true
|
||||
}
|
||||
if newElem, pruned := p.maybeCreateLiteral(elem.GetId(), opt.GetValue()); pruned {
|
||||
return newElem, true
|
||||
}
|
||||
}
|
||||
return elem, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneIn(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
// elem in list
|
||||
call := node.GetCallExpr()
|
||||
val, exists := p.maybeValue(call.GetArgs()[1].GetId())
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
if sz, ok := val.(traits.Sizer); ok && sz.Size() == types.IntZero {
|
||||
return p.maybeCreateLiteral(node.GetId(), types.False)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneLogicalNot(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
call := node.GetCallExpr()
|
||||
arg := call.GetArgs()[0]
|
||||
val, exists := p.maybeValue(arg.GetId())
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
if b, ok := val.(types.Bool); ok {
|
||||
return p.maybeCreateLiteral(node.GetId(), !b)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneOr(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
call := node.GetCallExpr()
|
||||
// We know result is unknown, so we have at least one unknown arg
|
||||
// and if one side is a known value, we know we can ignore it.
|
||||
if p.existsWithKnownValue(call.Args[0].GetId()) {
|
||||
return call.Args[1], true
|
||||
if v, exists := p.maybeValue(call.GetArgs()[0].GetId()); exists {
|
||||
if v == types.True {
|
||||
return p.maybeCreateLiteral(node.GetId(), types.True)
|
||||
}
|
||||
return call.GetArgs()[1], true
|
||||
}
|
||||
if p.existsWithKnownValue(call.Args[1].GetId()) {
|
||||
return call.Args[0], true
|
||||
if v, exists := p.maybeValue(call.GetArgs()[1].GetId()); exists {
|
||||
if v == types.True {
|
||||
return p.maybeCreateLiteral(node.GetId(), types.True)
|
||||
}
|
||||
return call.GetArgs()[0], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneAnd(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
call := node.GetCallExpr()
|
||||
// We know result is unknown, so we have at least one unknown arg
|
||||
// and if one side is a known value, we know we can ignore it.
|
||||
if v, exists := p.maybeValue(call.GetArgs()[0].GetId()); exists {
|
||||
if v == types.False {
|
||||
return p.maybeCreateLiteral(node.GetId(), types.False)
|
||||
}
|
||||
return call.GetArgs()[1], true
|
||||
}
|
||||
if v, exists := p.maybeValue(call.GetArgs()[1].GetId()); exists {
|
||||
if v == types.False {
|
||||
return p.maybeCreateLiteral(node.GetId(), types.False)
|
||||
}
|
||||
return call.GetArgs()[0], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneConditional(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
if !p.existsWithUnknownValue(node.GetId()) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
call := node.GetCallExpr()
|
||||
condVal, condValueExists := p.value(call.Args[0].GetId())
|
||||
if !condValueExists || types.IsUnknownOrError(condVal) {
|
||||
cond, exists := p.maybeValue(call.GetArgs()[0].GetId())
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if condVal.Value().(bool) {
|
||||
return call.Args[1], true
|
||||
if cond.Value().(bool) {
|
||||
return call.GetArgs()[1], true
|
||||
}
|
||||
return call.Args[2], true
|
||||
return call.GetArgs()[2], true
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePruneFunction(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
if _, exists := p.value(node.GetId()); !exists {
|
||||
return nil, false
|
||||
}
|
||||
call := node.GetCallExpr()
|
||||
if call.Function == operators.LogicalOr || call.Function == operators.LogicalAnd {
|
||||
return p.maybePruneAndOr(node)
|
||||
if call.Function == operators.LogicalOr {
|
||||
return p.maybePruneOr(node)
|
||||
}
|
||||
if call.Function == operators.LogicalAnd {
|
||||
return p.maybePruneAnd(node)
|
||||
}
|
||||
if call.Function == operators.Conditional {
|
||||
return p.maybePruneConditional(node)
|
||||
}
|
||||
|
||||
if call.Function == operators.In {
|
||||
return p.maybePruneIn(node)
|
||||
}
|
||||
if call.Function == operators.LogicalNot {
|
||||
return p.maybePruneLogicalNot(node)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *astPruner) maybePrune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
return p.prune(node)
|
||||
}
|
||||
|
||||
func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
if node == nil {
|
||||
return node, false
|
||||
}
|
||||
val, valueExists := p.value(node.GetId())
|
||||
if valueExists && !types.IsUnknownOrError(val) {
|
||||
val, valueExists := p.maybeValue(node.GetId())
|
||||
if valueExists {
|
||||
if newNode, ok := p.maybeCreateLiteral(node.GetId(), val); ok {
|
||||
delete(p.macroCalls, node.GetId())
|
||||
return newNode, true
|
||||
}
|
||||
}
|
||||
if macro, found := p.macroCalls[node.GetId()]; found {
|
||||
// prune the expression in terms of the macro call instead of the expanded form.
|
||||
if newMacro, pruned := p.prune(macro); pruned {
|
||||
p.macroCalls[node.GetId()] = newMacro
|
||||
}
|
||||
}
|
||||
|
||||
// We have either an unknown/error value, or something we don't want to
|
||||
// transform, or expression was not evaluated. If possible, drill down
|
||||
// more.
|
||||
|
||||
switch node.GetExprKind().(type) {
|
||||
case *exprpb.Expr_SelectExpr:
|
||||
if operand, pruned := p.prune(node.GetSelectExpr().GetOperand()); pruned {
|
||||
if operand, pruned := p.maybePrune(node.GetSelectExpr().GetOperand()); pruned {
|
||||
return &exprpb.Expr{
|
||||
Id: node.GetId(),
|
||||
ExprKind: &exprpb.Expr_SelectExpr{
|
||||
@ -253,10 +365,6 @@ func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
}, true
|
||||
}
|
||||
case *exprpb.Expr_CallExpr:
|
||||
if newExpr, pruned := p.maybePruneFunction(node); pruned {
|
||||
newExpr, _ = p.prune(newExpr)
|
||||
return newExpr, true
|
||||
}
|
||||
var prunedCall bool
|
||||
call := node.GetCallExpr()
|
||||
args := call.GetArgs()
|
||||
@ -268,40 +376,75 @@ func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
}
|
||||
for i, arg := range args {
|
||||
newArgs[i] = arg
|
||||
if newArg, prunedArg := p.prune(arg); prunedArg {
|
||||
if newArg, prunedArg := p.maybePrune(arg); prunedArg {
|
||||
prunedCall = true
|
||||
newArgs[i] = newArg
|
||||
}
|
||||
}
|
||||
if newTarget, prunedTarget := p.prune(call.GetTarget()); prunedTarget {
|
||||
if newTarget, prunedTarget := p.maybePrune(call.GetTarget()); prunedTarget {
|
||||
prunedCall = true
|
||||
newCall.Target = newTarget
|
||||
}
|
||||
newNode := &exprpb.Expr{
|
||||
Id: node.GetId(),
|
||||
ExprKind: &exprpb.Expr_CallExpr{
|
||||
CallExpr: newCall,
|
||||
},
|
||||
}
|
||||
if newExpr, pruned := p.maybePruneFunction(newNode); pruned {
|
||||
newExpr, _ = p.maybePrune(newExpr)
|
||||
return newExpr, true
|
||||
}
|
||||
if prunedCall {
|
||||
return &exprpb.Expr{
|
||||
Id: node.GetId(),
|
||||
ExprKind: &exprpb.Expr_CallExpr{
|
||||
CallExpr: newCall,
|
||||
},
|
||||
}, true
|
||||
return newNode, true
|
||||
}
|
||||
case *exprpb.Expr_ListExpr:
|
||||
elems := node.GetListExpr().GetElements()
|
||||
newElems := make([]*exprpb.Expr, len(elems))
|
||||
optIndices := node.GetListExpr().GetOptionalIndices()
|
||||
optIndexMap := map[int32]bool{}
|
||||
for _, i := range optIndices {
|
||||
optIndexMap[i] = true
|
||||
}
|
||||
newOptIndexMap := make(map[int32]bool, len(optIndexMap))
|
||||
newElems := make([]*exprpb.Expr, 0, len(elems))
|
||||
var prunedList bool
|
||||
|
||||
prunedIdx := 0
|
||||
for i, elem := range elems {
|
||||
newElems[i] = elem
|
||||
if newElem, prunedElem := p.prune(elem); prunedElem {
|
||||
newElems[i] = newElem
|
||||
prunedList = true
|
||||
_, isOpt := optIndexMap[int32(i)]
|
||||
if isOpt {
|
||||
newElem, pruned := p.maybePruneOptional(elem)
|
||||
if pruned {
|
||||
prunedList = true
|
||||
if newElem != nil {
|
||||
newElems = append(newElems, newElem)
|
||||
prunedIdx++
|
||||
}
|
||||
continue
|
||||
}
|
||||
newOptIndexMap[int32(prunedIdx)] = true
|
||||
}
|
||||
if newElem, prunedElem := p.maybePrune(elem); prunedElem {
|
||||
newElems = append(newElems, newElem)
|
||||
prunedList = true
|
||||
} else {
|
||||
newElems = append(newElems, elem)
|
||||
}
|
||||
prunedIdx++
|
||||
}
|
||||
optIndices = make([]int32, len(newOptIndexMap))
|
||||
idx := 0
|
||||
for i := range newOptIndexMap {
|
||||
optIndices[idx] = i
|
||||
idx++
|
||||
}
|
||||
if prunedList {
|
||||
return &exprpb.Expr{
|
||||
Id: node.GetId(),
|
||||
ExprKind: &exprpb.Expr_ListExpr{
|
||||
ListExpr: &exprpb.Expr_CreateList{
|
||||
Elements: newElems,
|
||||
Elements: newElems,
|
||||
OptionalIndices: optIndices,
|
||||
},
|
||||
},
|
||||
}, true
|
||||
@ -313,8 +456,8 @@ func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
newEntries := make([]*exprpb.Expr_CreateStruct_Entry, len(entries))
|
||||
for i, entry := range entries {
|
||||
newEntries[i] = entry
|
||||
newKey, prunedKey := p.prune(entry.GetMapKey())
|
||||
newValue, prunedValue := p.prune(entry.GetValue())
|
||||
newKey, prunedKey := p.maybePrune(entry.GetMapKey())
|
||||
newValue, prunedValue := p.maybePrune(entry.GetValue())
|
||||
if !prunedKey && !prunedValue {
|
||||
continue
|
||||
}
|
||||
@ -331,6 +474,7 @@ func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
MapKey: newKey,
|
||||
}
|
||||
}
|
||||
newEntry.OptionalEntry = entry.GetOptionalEntry()
|
||||
newEntries[i] = newEntry
|
||||
}
|
||||
if prunedStruct {
|
||||
@ -344,27 +488,6 @@ func (p *astPruner) prune(node *exprpb.Expr) (*exprpb.Expr, bool) {
|
||||
},
|
||||
}, true
|
||||
}
|
||||
case *exprpb.Expr_ComprehensionExpr:
|
||||
compre := node.GetComprehensionExpr()
|
||||
// Only the range of the comprehension is pruned since the state tracking only records
|
||||
// the last iteration of the comprehension and not each step in the evaluation which
|
||||
// means that the any residuals computed in between might be inaccurate.
|
||||
if newRange, pruned := p.prune(compre.GetIterRange()); pruned {
|
||||
return &exprpb.Expr{
|
||||
Id: node.GetId(),
|
||||
ExprKind: &exprpb.Expr_ComprehensionExpr{
|
||||
ComprehensionExpr: &exprpb.Expr_Comprehension{
|
||||
IterVar: compre.GetIterVar(),
|
||||
IterRange: newRange,
|
||||
AccuVar: compre.GetAccuVar(),
|
||||
AccuInit: compre.GetAccuInit(),
|
||||
LoopCondition: compre.GetLoopCondition(),
|
||||
LoopStep: compre.GetLoopStep(),
|
||||
Result: compre.GetResult(),
|
||||
},
|
||||
},
|
||||
}, true
|
||||
}
|
||||
}
|
||||
return node, false
|
||||
}
|
||||
@ -374,24 +497,82 @@ func (p *astPruner) value(id int64) (ref.Val, bool) {
|
||||
return val, (found && val != nil)
|
||||
}
|
||||
|
||||
func (p *astPruner) existsWithUnknownValue(id int64) bool {
|
||||
val, valueExists := p.value(id)
|
||||
return valueExists && types.IsUnknown(val)
|
||||
}
|
||||
|
||||
func (p *astPruner) existsWithKnownValue(id int64) bool {
|
||||
val, valueExists := p.value(id)
|
||||
return valueExists && !types.IsUnknown(val)
|
||||
func (p *astPruner) maybeValue(id int64) (ref.Val, bool) {
|
||||
val, found := p.value(id)
|
||||
if !found || types.IsUnknownOrError(val) {
|
||||
return nil, false
|
||||
}
|
||||
return val, true
|
||||
}
|
||||
|
||||
func (p *astPruner) nextID() int64 {
|
||||
for {
|
||||
_, found := p.state.Value(p.nextExprID)
|
||||
if !found {
|
||||
next := p.nextExprID
|
||||
p.nextExprID++
|
||||
return next
|
||||
}
|
||||
p.nextExprID++
|
||||
next := p.nextExprID
|
||||
p.nextExprID++
|
||||
return next
|
||||
}
|
||||
|
||||
type astVisitor struct {
|
||||
// visitEntry is called on every expr node, including those within a map/struct entry.
|
||||
visitExpr func(expr *exprpb.Expr)
|
||||
// visitEntry is called before entering the key, value of a map/struct entry.
|
||||
visitEntry func(entry *exprpb.Expr_CreateStruct_Entry)
|
||||
}
|
||||
|
||||
func getMaxID(expr *exprpb.Expr) int64 {
|
||||
maxID := int64(1)
|
||||
visit(expr, maxIDVisitor(&maxID))
|
||||
return maxID
|
||||
}
|
||||
|
||||
func maxIDVisitor(maxID *int64) astVisitor {
|
||||
return astVisitor{
|
||||
visitExpr: func(e *exprpb.Expr) {
|
||||
if e.GetId() >= *maxID {
|
||||
*maxID = e.GetId() + 1
|
||||
}
|
||||
},
|
||||
visitEntry: func(e *exprpb.Expr_CreateStruct_Entry) {
|
||||
if e.GetId() >= *maxID {
|
||||
*maxID = e.GetId() + 1
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func visit(expr *exprpb.Expr, visitor astVisitor) {
|
||||
exprs := []*exprpb.Expr{expr}
|
||||
for len(exprs) != 0 {
|
||||
e := exprs[0]
|
||||
visitor.visitExpr(e)
|
||||
exprs = exprs[1:]
|
||||
switch e.GetExprKind().(type) {
|
||||
case *exprpb.Expr_SelectExpr:
|
||||
exprs = append(exprs, e.GetSelectExpr().GetOperand())
|
||||
case *exprpb.Expr_CallExpr:
|
||||
call := e.GetCallExpr()
|
||||
if call.GetTarget() != nil {
|
||||
exprs = append(exprs, call.GetTarget())
|
||||
}
|
||||
exprs = append(exprs, call.GetArgs()...)
|
||||
case *exprpb.Expr_ComprehensionExpr:
|
||||
compre := e.GetComprehensionExpr()
|
||||
exprs = append(exprs,
|
||||
compre.GetIterRange(),
|
||||
compre.GetAccuInit(),
|
||||
compre.GetLoopCondition(),
|
||||
compre.GetLoopStep(),
|
||||
compre.GetResult())
|
||||
case *exprpb.Expr_ListExpr:
|
||||
list := e.GetListExpr()
|
||||
exprs = append(exprs, list.GetElements()...)
|
||||
case *exprpb.Expr_StructExpr:
|
||||
for _, entry := range e.GetStructExpr().GetEntries() {
|
||||
visitor.visitEntry(entry)
|
||||
if entry.GetMapKey() != nil {
|
||||
exprs = append(exprs, entry.GetMapKey())
|
||||
}
|
||||
exprs = append(exprs, entry.GetValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
vendor/github.com/google/cel-go/interpreter/runtimecost.go
generated
vendored
58
vendor/github.com/google/cel-go/interpreter/runtimecost.go
generated
vendored
@ -36,7 +36,7 @@ type ActualCostEstimator interface {
|
||||
|
||||
// CostObserver provides an observer that tracks runtime cost.
|
||||
func CostObserver(tracker *CostTracker) EvalObserver {
|
||||
observer := func(id int64, programStep interface{}, val ref.Val) {
|
||||
observer := func(id int64, programStep any, val ref.Val) {
|
||||
switch t := programStep.(type) {
|
||||
case ConstantQualifier:
|
||||
// TODO: Push identifiers on to the stack before observing constant qualifiers that apply to them
|
||||
@ -53,6 +53,11 @@ func CostObserver(tracker *CostTracker) EvalObserver {
|
||||
tracker.stack.drop(t.Attr().ID())
|
||||
tracker.cost += common.SelectAndIdentCost
|
||||
}
|
||||
if !tracker.presenceTestHasCost {
|
||||
if _, isTestOnly := programStep.(*evalTestOnly); isTestOnly {
|
||||
tracker.cost -= common.SelectAndIdentCost
|
||||
}
|
||||
}
|
||||
case *evalExhaustiveConditional:
|
||||
// Ternary has no direct cost. All cost is from the conditional and the true/false branch expressions.
|
||||
tracker.stack.drop(t.attr.falsy.ID(), t.attr.truthy.ID(), t.attr.expr.ID())
|
||||
@ -95,21 +100,58 @@ func CostObserver(tracker *CostTracker) EvalObserver {
|
||||
return observer
|
||||
}
|
||||
|
||||
// CostTracker represents the information needed for tacking runtime cost
|
||||
// CostTrackerOption configures the behavior of CostTracker objects.
|
||||
type CostTrackerOption func(*CostTracker) error
|
||||
|
||||
// CostTrackerLimit sets the runtime limit on the evaluation cost during execution and will terminate the expression
|
||||
// evaluation if the limit is exceeded.
|
||||
func CostTrackerLimit(limit uint64) CostTrackerOption {
|
||||
return func(tracker *CostTracker) error {
|
||||
tracker.Limit = &limit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// PresenceTestHasCost determines whether presence testing has a cost of one or zero.
|
||||
// Defaults to presence test has a cost of one.
|
||||
func PresenceTestHasCost(hasCost bool) CostTrackerOption {
|
||||
return func(tracker *CostTracker) error {
|
||||
tracker.presenceTestHasCost = hasCost
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewCostTracker creates a new CostTracker with a given estimator and a set of functional CostTrackerOption values.
|
||||
func NewCostTracker(estimator ActualCostEstimator, opts ...CostTrackerOption) (*CostTracker, error) {
|
||||
tracker := &CostTracker{
|
||||
Estimator: estimator,
|
||||
presenceTestHasCost: true,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
err := opt(tracker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return tracker, nil
|
||||
}
|
||||
|
||||
// CostTracker represents the information needed for tracking runtime cost.
|
||||
type CostTracker struct {
|
||||
Estimator ActualCostEstimator
|
||||
Limit *uint64
|
||||
Estimator ActualCostEstimator
|
||||
Limit *uint64
|
||||
presenceTestHasCost bool
|
||||
|
||||
cost uint64
|
||||
stack refValStack
|
||||
}
|
||||
|
||||
// ActualCost returns the runtime cost
|
||||
func (c CostTracker) ActualCost() uint64 {
|
||||
func (c *CostTracker) ActualCost() uint64 {
|
||||
return c.cost
|
||||
}
|
||||
|
||||
func (c CostTracker) costCall(call InterpretableCall, argValues []ref.Val, result ref.Val) uint64 {
|
||||
func (c *CostTracker) costCall(call InterpretableCall, argValues []ref.Val, result ref.Val) uint64 {
|
||||
var cost uint64
|
||||
if c.Estimator != nil {
|
||||
callCost := c.Estimator.CallCost(call.Function(), call.OverloadID(), argValues, result)
|
||||
@ -122,7 +164,7 @@ func (c CostTracker) costCall(call InterpretableCall, argValues []ref.Val, resul
|
||||
// if user has their own implementation of ActualCostEstimator, make sure to cover the mapping between overloadId and cost calculation
|
||||
switch call.OverloadID() {
|
||||
// O(n) functions
|
||||
case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString:
|
||||
case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString:
|
||||
cost += uint64(math.Ceil(float64(c.actualSize(argValues[0])) * common.StringTraversalCostFactor))
|
||||
case overloads.InList:
|
||||
// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
|
||||
@ -179,7 +221,7 @@ func (c CostTracker) costCall(call InterpretableCall, argValues []ref.Val, resul
|
||||
}
|
||||
|
||||
// actualSize returns the size of value
|
||||
func (c CostTracker) actualSize(value ref.Val) uint64 {
|
||||
func (c *CostTracker) actualSize(value ref.Val) uint64 {
|
||||
if sz, ok := value.(traits.Sizer); ok {
|
||||
return uint64(sz.Size().(types.Int))
|
||||
}
|
||||
|
Reference in New Issue
Block a user