ceph-csi/e2e/vendor/github.com/google/cel-go/parser/helper.go
Niels de Vos f87d06ed85 build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These
packages are less important to update, as the they can not influence the
final executable that is part of the Ceph-CSI container-image.

By moving these dependencies out of the main Ceph-CSI go.mod, it is
easier to identify if a reported CVE affects Ceph-CSI, or only the
testing (like most of the Kubernetes CVEs).

Signed-off-by: Niels de Vos <ndevos@ibm.com>
2025-03-04 17:43:49 +01:00

511 lines
17 KiB
Go

// Copyright 2018 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 parser
import (
"sync"
antlr "github.com/antlr4-go/antlr/v4"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
type parserHelper struct {
exprFactory ast.ExprFactory
source common.Source
sourceInfo *ast.SourceInfo
nextID int64
}
func newParserHelper(source common.Source, fac ast.ExprFactory) *parserHelper {
return &parserHelper{
exprFactory: fac,
source: source,
sourceInfo: ast.NewSourceInfo(source),
nextID: 1,
}
}
func (p *parserHelper) getSourceInfo() *ast.SourceInfo {
return p.sourceInfo
}
func (p *parserHelper) newLiteral(ctx any, value ref.Val) ast.Expr {
return p.exprFactory.NewLiteral(p.newID(ctx), value)
}
func (p *parserHelper) newLiteralBool(ctx any, value bool) ast.Expr {
return p.newLiteral(ctx, types.Bool(value))
}
func (p *parserHelper) newLiteralString(ctx any, value string) ast.Expr {
return p.newLiteral(ctx, types.String(value))
}
func (p *parserHelper) newLiteralBytes(ctx any, value []byte) ast.Expr {
return p.newLiteral(ctx, types.Bytes(value))
}
func (p *parserHelper) newLiteralInt(ctx any, value int64) ast.Expr {
return p.newLiteral(ctx, types.Int(value))
}
func (p *parserHelper) newLiteralUint(ctx any, value uint64) ast.Expr {
return p.newLiteral(ctx, types.Uint(value))
}
func (p *parserHelper) newLiteralDouble(ctx any, value float64) ast.Expr {
return p.newLiteral(ctx, types.Double(value))
}
func (p *parserHelper) newIdent(ctx any, name string) ast.Expr {
return p.exprFactory.NewIdent(p.newID(ctx), name)
}
func (p *parserHelper) newSelect(ctx any, operand ast.Expr, field string) ast.Expr {
return p.exprFactory.NewSelect(p.newID(ctx), operand, field)
}
func (p *parserHelper) newPresenceTest(ctx any, operand ast.Expr, field string) ast.Expr {
return p.exprFactory.NewPresenceTest(p.newID(ctx), operand, field)
}
func (p *parserHelper) newGlobalCall(ctx any, function string, args ...ast.Expr) ast.Expr {
return p.exprFactory.NewCall(p.newID(ctx), function, args...)
}
func (p *parserHelper) newReceiverCall(ctx any, function string, target ast.Expr, args ...ast.Expr) ast.Expr {
return p.exprFactory.NewMemberCall(p.newID(ctx), function, target, args...)
}
func (p *parserHelper) newList(ctx any, elements []ast.Expr, optionals ...int32) ast.Expr {
return p.exprFactory.NewList(p.newID(ctx), elements, optionals)
}
func (p *parserHelper) newMap(ctx any, entries ...ast.EntryExpr) ast.Expr {
return p.exprFactory.NewMap(p.newID(ctx), entries)
}
func (p *parserHelper) newMapEntry(entryID int64, key ast.Expr, value ast.Expr, optional bool) ast.EntryExpr {
return p.exprFactory.NewMapEntry(entryID, key, value, optional)
}
func (p *parserHelper) newObject(ctx any, typeName string, fields ...ast.EntryExpr) ast.Expr {
return p.exprFactory.NewStruct(p.newID(ctx), typeName, fields)
}
func (p *parserHelper) newObjectField(fieldID int64, field string, value ast.Expr, optional bool) ast.EntryExpr {
return p.exprFactory.NewStructField(fieldID, field, value, optional)
}
func (p *parserHelper) newComprehension(ctx any,
iterRange ast.Expr,
iterVar,
accuVar string,
accuInit ast.Expr,
condition ast.Expr,
step ast.Expr,
result ast.Expr) ast.Expr {
return p.exprFactory.NewComprehension(
p.newID(ctx), iterRange, iterVar, accuVar, accuInit, condition, step, result)
}
func (p *parserHelper) newComprehensionTwoVar(ctx any,
iterRange ast.Expr,
iterVar, iterVar2,
accuVar string,
accuInit ast.Expr,
condition ast.Expr,
step ast.Expr,
result ast.Expr) ast.Expr {
return p.exprFactory.NewComprehensionTwoVar(
p.newID(ctx), iterRange, iterVar, iterVar2, accuVar, accuInit, condition, step, result)
}
func (p *parserHelper) newID(ctx any) int64 {
if id, isID := ctx.(int64); isID {
return id
}
return p.id(ctx)
}
func (p *parserHelper) newExpr(ctx any) ast.Expr {
return p.exprFactory.NewUnspecifiedExpr(p.newID(ctx))
}
func (p *parserHelper) id(ctx any) int64 {
var offset ast.OffsetRange
switch c := ctx.(type) {
case antlr.ParserRuleContext:
start := c.GetStart()
offset.Start = p.sourceInfo.ComputeOffset(int32(start.GetLine()), int32(start.GetColumn()))
offset.Stop = offset.Start + int32(len(c.GetText()))
case antlr.Token:
offset.Start = p.sourceInfo.ComputeOffset(int32(c.GetLine()), int32(c.GetColumn()))
offset.Stop = offset.Start + int32(len(c.GetText()))
case common.Location:
offset.Start = p.sourceInfo.ComputeOffset(int32(c.Line()), int32(c.Column()))
offset.Stop = offset.Start
case ast.OffsetRange:
offset = c
default:
// This should only happen if the ctx is nil
return -1
}
id := p.nextID
p.sourceInfo.SetOffsetRange(id, offset)
p.nextID++
return id
}
func (p *parserHelper) deleteID(id int64) {
p.sourceInfo.ClearOffsetRange(id)
if id == p.nextID-1 {
p.nextID--
}
}
func (p *parserHelper) getLocation(id int64) common.Location {
return p.sourceInfo.GetStartLocation(id)
}
func (p *parserHelper) getLocationByOffset(offset int32) common.Location {
return p.getSourceInfo().GetLocationByOffset(offset)
}
// buildMacroCallArg iterates the expression and returns a new expression
// where all macros have been replaced by their IDs in MacroCalls
func (p *parserHelper) buildMacroCallArg(expr ast.Expr) ast.Expr {
if _, found := p.sourceInfo.GetMacroCall(expr.ID()); found {
return p.exprFactory.NewUnspecifiedExpr(expr.ID())
}
switch expr.Kind() {
case ast.CallKind:
// Iterate the AST from `expr` recursively looking for macros. Because we are at most
// starting from the top level macro, this recursion is bounded by the size of the AST. This
// means that the depth check on the AST during parsing will catch recursion overflows
// before we get to here.
call := expr.AsCall()
macroArgs := make([]ast.Expr, len(call.Args()))
for index, arg := range call.Args() {
macroArgs[index] = p.buildMacroCallArg(arg)
}
if !call.IsMemberFunction() {
return p.exprFactory.NewCall(expr.ID(), call.FunctionName(), macroArgs...)
}
macroTarget := p.buildMacroCallArg(call.Target())
return p.exprFactory.NewMemberCall(expr.ID(), call.FunctionName(), macroTarget, macroArgs...)
case ast.ListKind:
list := expr.AsList()
macroListArgs := make([]ast.Expr, list.Size())
for i, elem := range list.Elements() {
macroListArgs[i] = p.buildMacroCallArg(elem)
}
return p.exprFactory.NewList(expr.ID(), macroListArgs, list.OptionalIndices())
}
return expr
}
// addMacroCall adds the macro the the MacroCalls map in source info. If a macro has args/subargs/target
// that are macros, their ID will be stored instead for later self-lookups.
func (p *parserHelper) addMacroCall(exprID int64, function string, target ast.Expr, args ...ast.Expr) {
macroArgs := make([]ast.Expr, len(args))
for index, arg := range args {
macroArgs[index] = p.buildMacroCallArg(arg)
}
if target == nil {
p.sourceInfo.SetMacroCall(exprID, p.exprFactory.NewCall(0, function, macroArgs...))
return
}
macroTarget := target
if _, found := p.sourceInfo.GetMacroCall(target.ID()); found {
macroTarget = p.exprFactory.NewUnspecifiedExpr(target.ID())
} else {
macroTarget = p.buildMacroCallArg(target)
}
p.sourceInfo.SetMacroCall(exprID, p.exprFactory.NewMemberCall(0, function, macroTarget, macroArgs...))
}
// logicManager compacts logical trees into a more efficient structure which is semantically
// equivalent with how the logic graph is constructed by the ANTLR parser.
//
// The purpose of the logicManager is to ensure a compact serialization format for the logical &&, ||
// operators which have a tendency to create long DAGs which are skewed in one direction. Since the
// operators are commutative re-ordering the terms *must not* affect the evaluation result.
//
// The logic manager will either render the terms to N-chained && / || operators as a single logical
// call with N-terms, or will rebalance the tree. Rebalancing the terms is a safe, if somewhat
// controversial choice as it alters the traditional order of execution assumptions present in most
// expressions.
type logicManager struct {
exprFactory ast.ExprFactory
function string
terms []ast.Expr
ops []int64
variadicASTs bool
}
// newVariadicLogicManager creates a logic manager instance bound to a specific function and its first term.
func newVariadicLogicManager(fac ast.ExprFactory, function string, term ast.Expr) *logicManager {
return &logicManager{
exprFactory: fac,
function: function,
terms: []ast.Expr{term},
ops: []int64{},
variadicASTs: true,
}
}
// newBalancingLogicManager creates a logic manager instance bound to a specific function and its first term.
func newBalancingLogicManager(fac ast.ExprFactory, function string, term ast.Expr) *logicManager {
return &logicManager{
exprFactory: fac,
function: function,
terms: []ast.Expr{term},
ops: []int64{},
variadicASTs: false,
}
}
// addTerm adds an operation identifier and term to the set of terms to be balanced.
func (l *logicManager) addTerm(op int64, term ast.Expr) {
l.terms = append(l.terms, term)
l.ops = append(l.ops, op)
}
// toExpr renders the logic graph into an Expr value, either balancing a tree of logical
// operations or creating a variadic representation of the logical operator.
func (l *logicManager) toExpr() ast.Expr {
if len(l.terms) == 1 {
return l.terms[0]
}
if l.variadicASTs {
return l.exprFactory.NewCall(l.ops[0], l.function, l.terms...)
}
return l.balancedTree(0, len(l.ops)-1)
}
// balancedTree recursively balances the terms provided to a commutative operator.
func (l *logicManager) balancedTree(lo, hi int) ast.Expr {
mid := (lo + hi + 1) / 2
var left ast.Expr
if mid == lo {
left = l.terms[mid]
} else {
left = l.balancedTree(lo, mid-1)
}
var right ast.Expr
if mid == hi {
right = l.terms[mid+1]
} else {
right = l.balancedTree(mid+1, hi)
}
return l.exprFactory.NewCall(l.ops[mid], l.function, left, right)
}
type exprHelper struct {
*parserHelper
id int64
}
func (e *exprHelper) nextMacroID() int64 {
return e.parserHelper.id(e.parserHelper.getLocation(e.id))
}
// Copy implements the ExprHelper interface method by producing a copy of the input Expr value
// with a fresh set of numeric identifiers the Expr and all its descendants.
func (e *exprHelper) Copy(expr ast.Expr) ast.Expr {
offsetRange, _ := e.parserHelper.sourceInfo.GetOffsetRange(expr.ID())
copyID := e.parserHelper.newID(offsetRange)
switch expr.Kind() {
case ast.LiteralKind:
return e.exprFactory.NewLiteral(copyID, expr.AsLiteral())
case ast.IdentKind:
return e.exprFactory.NewIdent(copyID, expr.AsIdent())
case ast.SelectKind:
sel := expr.AsSelect()
op := e.Copy(sel.Operand())
if sel.IsTestOnly() {
return e.exprFactory.NewPresenceTest(copyID, op, sel.FieldName())
}
return e.exprFactory.NewSelect(copyID, op, sel.FieldName())
case ast.CallKind:
call := expr.AsCall()
args := call.Args()
argsCopy := make([]ast.Expr, len(args))
for i, arg := range args {
argsCopy[i] = e.Copy(arg)
}
if !call.IsMemberFunction() {
return e.exprFactory.NewCall(copyID, call.FunctionName(), argsCopy...)
}
return e.exprFactory.NewMemberCall(copyID, call.FunctionName(), e.Copy(call.Target()), argsCopy...)
case ast.ListKind:
list := expr.AsList()
elems := list.Elements()
elemsCopy := make([]ast.Expr, len(elems))
for i, elem := range elems {
elemsCopy[i] = e.Copy(elem)
}
return e.exprFactory.NewList(copyID, elemsCopy, list.OptionalIndices())
case ast.MapKind:
m := expr.AsMap()
entries := m.Entries()
entriesCopy := make([]ast.EntryExpr, len(entries))
for i, en := range entries {
entry := en.AsMapEntry()
entryID := e.nextMacroID()
entriesCopy[i] = e.exprFactory.NewMapEntry(entryID,
e.Copy(entry.Key()), e.Copy(entry.Value()), entry.IsOptional())
}
return e.exprFactory.NewMap(copyID, entriesCopy)
case ast.StructKind:
s := expr.AsStruct()
fields := s.Fields()
fieldsCopy := make([]ast.EntryExpr, len(fields))
for i, f := range fields {
field := f.AsStructField()
fieldID := e.nextMacroID()
fieldsCopy[i] = e.exprFactory.NewStructField(fieldID,
field.Name(), e.Copy(field.Value()), field.IsOptional())
}
return e.exprFactory.NewStruct(copyID, s.TypeName(), fieldsCopy)
case ast.ComprehensionKind:
compre := expr.AsComprehension()
iterRange := e.Copy(compre.IterRange())
accuInit := e.Copy(compre.AccuInit())
cond := e.Copy(compre.LoopCondition())
step := e.Copy(compre.LoopStep())
result := e.Copy(compre.Result())
// All comprehensions can be represented by the two-variable comprehension since the
// differentiation between one and two-variable is whether the iterVar2 value is non-empty.
return e.exprFactory.NewComprehensionTwoVar(copyID,
iterRange, compre.IterVar(), compre.IterVar2(), compre.AccuVar(), accuInit, cond, step, result)
}
return e.exprFactory.NewUnspecifiedExpr(copyID)
}
// NewLiteral implements the ExprHelper interface method.
func (e *exprHelper) NewLiteral(value ref.Val) ast.Expr {
return e.exprFactory.NewLiteral(e.nextMacroID(), value)
}
// NewList implements the ExprHelper interface method.
func (e *exprHelper) NewList(elems ...ast.Expr) ast.Expr {
return e.exprFactory.NewList(e.nextMacroID(), elems, []int32{})
}
// NewMap implements the ExprHelper interface method.
func (e *exprHelper) NewMap(entries ...ast.EntryExpr) ast.Expr {
return e.exprFactory.NewMap(e.nextMacroID(), entries)
}
// NewMapEntry implements the ExprHelper interface method.
func (e *exprHelper) NewMapEntry(key ast.Expr, val ast.Expr, optional bool) ast.EntryExpr {
return e.exprFactory.NewMapEntry(e.nextMacroID(), key, val, optional)
}
// NewStruct implements the ExprHelper interface method.
func (e *exprHelper) NewStruct(typeName string, fieldInits ...ast.EntryExpr) ast.Expr {
return e.exprFactory.NewStruct(e.nextMacroID(), typeName, fieldInits)
}
// NewStructField implements the ExprHelper interface method.
func (e *exprHelper) NewStructField(field string, init ast.Expr, optional bool) ast.EntryExpr {
return e.exprFactory.NewStructField(e.nextMacroID(), field, init, optional)
}
// NewComprehension implements the ExprHelper interface method.
func (e *exprHelper) NewComprehension(
iterRange ast.Expr,
iterVar string,
accuVar string,
accuInit ast.Expr,
condition ast.Expr,
step ast.Expr,
result ast.Expr) ast.Expr {
return e.exprFactory.NewComprehension(
e.nextMacroID(), iterRange, iterVar, accuVar, accuInit, condition, step, result)
}
// NewComprehensionTwoVar implements the ExprHelper interface method.
func (e *exprHelper) NewComprehensionTwoVar(
iterRange ast.Expr,
iterVar,
iterVar2,
accuVar string,
accuInit,
condition,
step,
result ast.Expr) ast.Expr {
return e.exprFactory.NewComprehensionTwoVar(
e.nextMacroID(), iterRange, iterVar, iterVar2, accuVar, accuInit, condition, step, result)
}
// NewIdent implements the ExprHelper interface method.
func (e *exprHelper) NewIdent(name string) ast.Expr {
return e.exprFactory.NewIdent(e.nextMacroID(), name)
}
// NewAccuIdent implements the ExprHelper interface method.
func (e *exprHelper) NewAccuIdent() ast.Expr {
return e.exprFactory.NewAccuIdent(e.nextMacroID())
}
// NewGlobalCall implements the ExprHelper interface method.
func (e *exprHelper) NewCall(function string, args ...ast.Expr) ast.Expr {
return e.exprFactory.NewCall(e.nextMacroID(), function, args...)
}
// NewMemberCall implements the ExprHelper interface method.
func (e *exprHelper) NewMemberCall(function string, target ast.Expr, args ...ast.Expr) ast.Expr {
return e.exprFactory.NewMemberCall(e.nextMacroID(), function, target, args...)
}
// NewPresenceTest implements the ExprHelper interface method.
func (e *exprHelper) NewPresenceTest(operand ast.Expr, field string) ast.Expr {
return e.exprFactory.NewPresenceTest(e.nextMacroID(), operand, field)
}
// NewSelect implements the ExprHelper interface method.
func (e *exprHelper) NewSelect(operand ast.Expr, field string) ast.Expr {
return e.exprFactory.NewSelect(e.nextMacroID(), operand, field)
}
// OffsetLocation implements the ExprHelper interface method.
func (e *exprHelper) OffsetLocation(exprID int64) common.Location {
return e.parserHelper.sourceInfo.GetStartLocation(exprID)
}
// NewError associates an error message with a given expression id, populating the source offset location of the error if possible.
func (e *exprHelper) NewError(exprID int64, message string) *common.Error {
return common.NewError(exprID, message, e.OffsetLocation(exprID))
}
var (
// Thread-safe pool of ExprHelper values to minimize alloc overhead of ExprHelper creations.
exprHelperPool = &sync.Pool{
New: func() any {
return &exprHelper{}
},
}
)