Bump cel-go to v0.11.2

This commit is contained in:
cici37
2022-05-04 18:32:06 -07:00
parent cb7beb5912
commit a86dd29157
83 changed files with 653 additions and 974 deletions

View File

@@ -44,7 +44,9 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"cel_example_test.go",
"cel_test.go",
"env_test.go",
"io_test.go",
],
data = [
@@ -66,6 +68,7 @@ go_test(
"//test/proto3pb:go_default_library",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@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/structpb:go_default_library",
],
)

View File

@@ -32,9 +32,7 @@ import (
)
// Source interface representing a user-provided expression.
type Source interface {
common.Source
}
type Source = common.Source
// Ast representing the checked or unchecked expression, its source, and related metadata such as
// source position information.
@@ -56,7 +54,7 @@ func (ast *Ast) IsChecked() bool {
return ast.typeMap != nil && len(ast.typeMap) > 0
}
// SourceInfo returns character offset and newling position information about expression elements.
// SourceInfo returns character offset and newline position information about expression elements.
func (ast *Ast) SourceInfo() *exprpb.SourceInfo {
return ast.info
}
@@ -98,6 +96,7 @@ type Env struct {
chk *checker.Env
chkErr error
chkOnce sync.Once
chkOpts []checker.Option
// Program options tied to the environment
progOpts []ProgramOption
@@ -110,8 +109,16 @@ type Env struct {
// See the EnvOption helper functions for the options that can be used to configure the
// environment.
func NewEnv(opts ...EnvOption) (*Env, error) {
stdOpts := append([]EnvOption{StdLib()}, opts...)
return NewCustomEnv(stdOpts...)
// Extend the statically configured standard environment, disabling eager validation to ensure
// the cost of setup for the environment is still just as cheap as it is in v0.11.x and earlier
// releases. The user provided options can easily re-enable the eager validation as they are
// processed after this default option.
stdOpts := append([]EnvOption{EagerlyValidateDeclarations(false)}, opts...)
env, err := getStdEnv()
if err != nil {
return nil, err
}
return env.Extend(stdOpts...)
}
// NewCustomEnv creates a custom program environment which is not automatically configured with the
@@ -152,25 +159,8 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
pe, _ := AstToParsedExpr(ast)
// Construct the internal checker env, erroring if there is an issue adding the declarations.
e.chkOnce.Do(func() {
ce, err := checker.NewEnv(e.Container, e.provider,
checker.HomogeneousAggregateLiterals(
e.HasFeature(featureDisableDynamicAggregateLiterals)),
checker.CrossTypeNumericComparisons(
e.HasFeature(featureCrossTypeNumericComparisons)))
if err != nil {
e.chkErr = err
return
}
err = ce.Add(e.declarations...)
if err != nil {
e.chkErr = err
return
}
e.chk = ce
})
// The once call will ensure that this value is set or nil for all invocations.
if e.chkErr != nil {
err := e.initChecker()
if err != nil {
errs := common.NewErrors(ast.Source())
errs.ReportError(common.NoLocation, e.chkErr.Error())
return nil, NewIssues(errs)
@@ -210,7 +200,7 @@ func (e *Env) Compile(txt string) (*Ast, *Issues) {
// issues discovered during Check.
//
// Note, for parse-only uses of CEL use Parse.
func (e *Env) CompileSource(src common.Source) (*Ast, *Issues) {
func (e *Env) CompileSource(src Source) (*Ast, *Issues) {
ast, iss := e.ParseSource(src)
if iss.Err() != nil {
return nil, iss
@@ -233,11 +223,29 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
if e.chkErr != nil {
return nil, e.chkErr
}
// Copy slices.
decsCopy := make([]*exprpb.Decl, len(e.declarations))
// The type-checker is configured with Declarations. The declarations may either be provided
// as options which have not yet been validated, or may come from a previous checker instance
// whose types have already been validated.
chkOptsCopy := make([]checker.Option, len(e.chkOpts))
copy(chkOptsCopy, e.chkOpts)
// Copy the declarations if needed.
decsCopy := []*exprpb.Decl{}
if e.chk != nil {
// If the type-checker has already been instantiated, then the e.declarations have been
// valdiated within the chk instance.
chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(e.chk))
} else {
// If the type-checker has not been instantiated, ensure the unvalidated declarations are
// provided to the extended Env instance.
decsCopy = make([]*exprpb.Decl, len(e.declarations))
copy(decsCopy, e.declarations)
}
// Copy macros and program options
macsCopy := make([]parser.Macro, len(e.macros))
progOptsCopy := make([]ProgramOption, len(e.progOpts))
copy(decsCopy, e.declarations)
copy(macsCopy, e.macros)
copy(progOptsCopy, e.progOpts)
@@ -281,6 +289,7 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
adapter: adapter,
features: featuresCopy,
provider: provider,
chkOpts: chkOptsCopy,
}
return ext.configure(opts)
}
@@ -294,7 +303,7 @@ func (e *Env) HasFeature(flag int) bool {
// Parse parses the input expression value `txt` to a Ast and/or a set of Issues.
//
// This form of Parse creates a common.Source value for the input `txt` and forwards to the
// This form of Parse creates a Source value for the input `txt` and forwards to the
// ParseSource method.
func (e *Env) Parse(txt string) (*Ast, *Issues) {
src := common.NewTextSource(txt)
@@ -308,7 +317,7 @@ func (e *Env) Parse(txt string) (*Ast, *Issues) {
//
// It is possible to have both non-nil Ast and Issues values returned from this call; however,
// the mere presence of an Ast does not imply that it is valid for use.
func (e *Env) ParseSource(src common.Source) (*Ast, *Issues) {
func (e *Env) ParseSource(src Source) (*Ast, *Issues) {
res, errs := e.prsr.Parse(src)
if len(errs.GetErrors()) > 0 {
return nil, &Issues{errs: errs}
@@ -316,7 +325,7 @@ func (e *Env) ParseSource(src common.Source) (*Ast, *Issues) {
// Manually create the Ast to ensure that the text source information is propagated on
// subsequent calls to Check.
return &Ast{
source: Source(src),
source: src,
expr: res.GetExpr(),
info: res.GetSourceInfo()}, nil
}
@@ -426,6 +435,8 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) {
return nil, err
}
}
// Configure the parser.
prsrOpts := []parser.Option{parser.Macros(e.macros...)}
if e.HasFeature(featureEnableMacroCallTracking) {
prsrOpts = append(prsrOpts, parser.PopulateMacroCalls(true))
@@ -434,9 +445,44 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) {
if err != nil {
return nil, err
}
// The simplest way to eagerly validate declarations on environment creation is to compile
// a dummy program and check for the presence of e.chkErr being non-nil.
if e.HasFeature(featureEagerlyValidateDeclarations) {
err := e.initChecker()
if err != nil {
return nil, err
}
}
return e, nil
}
func (e *Env) initChecker() error {
e.chkOnce.Do(func() {
chkOpts := []checker.Option{}
chkOpts = append(chkOpts, e.chkOpts...)
chkOpts = append(chkOpts,
checker.HomogeneousAggregateLiterals(
e.HasFeature(featureDisableDynamicAggregateLiterals)),
checker.CrossTypeNumericComparisons(
e.HasFeature(featureCrossTypeNumericComparisons)))
ce, err := checker.NewEnv(e.Container, e.provider, chkOpts...)
if err != nil {
e.chkErr = err
return
}
err = ce.Add(e.declarations...)
if err != nil {
e.chkErr = err
return
}
e.chk = ce
})
return e.chkErr
}
// Issues defines methods for inspecting the error details of parse and check calls.
//
// Note: in the future, non-fatal warnings and notices may be inspectable via the Issues struct.
@@ -488,3 +534,17 @@ func (i *Issues) String() string {
}
return i.errs.ToDisplayString()
}
// getStdEnv lazy initializes the CEL standard environment.
func getStdEnv() (*Env, error) {
stdEnvInit.Do(func() {
stdEnv, stdEnvErr = NewCustomEnv(StdLib(), EagerlyValidateDeclarations(true))
})
return stdEnv, stdEnvErr
}
var (
stdEnvInit sync.Once
stdEnv *Env
stdEnvErr error
)

View File

@@ -44,7 +44,7 @@ func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast {
// through future calls.
//
// Prefer CheckedExprToAst if loading expressions from storage.
func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src common.Source) *Ast {
func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src Source) *Ast {
refMap := checkedExpr.GetReferenceMap()
if refMap == nil {
refMap = map[int64]*exprpb.Reference{}
@@ -96,7 +96,7 @@ func ParsedExprToAst(parsedExpr *exprpb.ParsedExpr) *Ast {
// expression, or if you need to separately check a subset of an expression.
//
// Prefer ParsedExprToAst if loading expressions from storage.
func ParsedExprToAstWithSource(parsedExpr *exprpb.ParsedExpr, src common.Source) *Ast {
func ParsedExprToAstWithSource(parsedExpr *exprpb.ParsedExpr, src Source) *Ast {
si := parsedExpr.GetSourceInfo()
if si == nil {
si = &exprpb.SourceInfo{}

View File

@@ -20,14 +20,14 @@ import (
"github.com/google/cel-go/parser"
)
// Library provides a collection of EnvOption and ProgramOption values used to confiugre a CEL
// Library provides a collection of EnvOption and ProgramOption values used to configure a CEL
// environment for a particular use case or with a related set of functionality.
//
// Note, the ProgramOption values provided by a library are expected to be static and not vary
// between calls to Env.Program(). If there is a need for such dynamic configuration, prefer to
// configure these options outside the Library and within the Env.Program() call directly.
type Library interface {
// CompileOptions returns a collection of funcitional options for configuring the Parse / Check
// CompileOptions returns a collection of functional options for configuring the Parse / Check
// environment.
CompileOptions() []EnvOption

View File

@@ -53,6 +53,10 @@ const (
// Enable the use of cross-type numeric comparisons at the type-checker.
featureCrossTypeNumericComparisons
// Enable eager validation of declarations to ensure that Env values created
// with `Extend` inherit a validated list of declarations from the parent Env.
featureEagerlyValidateDeclarations
)
// EnvOption is a functional interface for configuring the environment.
@@ -103,6 +107,18 @@ func Declarations(decls ...*exprpb.Decl) EnvOption {
}
}
// EagerlyValidateDeclarations ensures that any collisions between configured declarations are caught
// at the time of the `NewEnv` call.
//
// Eagerly validating declarations is also useful for bootstrapping a base `cel.Env` value.
// Calls to base `Env.Extend()` will be significantly faster when declarations are eagerly validated
// as declarations will be collision-checked at most once and only incrementally by way of `Extend`
//
// Disabled by default as not all environments are used for type-checking.
func EagerlyValidateDeclarations(enabled bool) EnvOption {
return features(featureEagerlyValidateDeclarations, enabled)
}
// HomogeneousAggregateLiterals option ensures that list and map literal entry types must agree
// during type-checking.
//

View File

@@ -49,7 +49,7 @@ type Program interface {
// to support cancellation and timeouts. This method must be used in conjunction with the
// InterruptCheckFrequency() option for cancellation interrupts to be impact evaluation.
//
// The vars value may eitehr be an `interpreter.Activation` or `map[string]interface{}`.
// The vars value may either be an `interpreter.Activation` or `map[string]interface{}`.
//
// The output contract for `ContextEval` is otherwise identical to the `Eval` method.
ContextEval(context.Context, interface{}) (ref.Val, *EvalDetails, error)