Upgrade dependent version: github.com/open-policy-agent/opa (#5315)
Upgrade dependent version: github.com/open-policy-agent/opa v0.18.0 -> v0.45.0 Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
345
vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go
generated
vendored
345
vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package copypropagation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
@@ -30,6 +31,19 @@ type CopyPropagator struct {
|
||||
livevars ast.VarSet // vars that must be preserved in the resulting query
|
||||
sorted []ast.Var // sorted copy of vars to ensure deterministic result
|
||||
ensureNonEmptyBody bool
|
||||
compiler *ast.Compiler
|
||||
localvargen *localVarGenerator
|
||||
}
|
||||
|
||||
type localVarGenerator struct {
|
||||
next int
|
||||
}
|
||||
|
||||
func (l *localVarGenerator) Generate() ast.Var {
|
||||
result := ast.Var(fmt.Sprintf("__localcp%d__", l.next))
|
||||
l.next++
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
// New returns a new CopyPropagator that optimizes queries while preserving vars
|
||||
@@ -45,7 +59,7 @@ func New(livevars ast.VarSet) *CopyPropagator {
|
||||
return sorted[i].Compare(sorted[j]) < 0
|
||||
})
|
||||
|
||||
return &CopyPropagator{livevars: livevars, sorted: sorted}
|
||||
return &CopyPropagator{livevars: livevars, sorted: sorted, localvargen: &localVarGenerator{}}
|
||||
}
|
||||
|
||||
// WithEnsureNonEmptyBody configures p to ensure that results are always non-empty.
|
||||
@@ -54,8 +68,17 @@ func (p *CopyPropagator) WithEnsureNonEmptyBody(yes bool) *CopyPropagator {
|
||||
return p
|
||||
}
|
||||
|
||||
// WithCompiler configures the compiler to read from while processing the query. This
|
||||
// should be the same compiler used to compile the original policy.
|
||||
func (p *CopyPropagator) WithCompiler(c *ast.Compiler) *CopyPropagator {
|
||||
p.compiler = c
|
||||
return p
|
||||
}
|
||||
|
||||
// Apply executes the copy propagation optimization and returns a new query.
|
||||
func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
func (p *CopyPropagator) Apply(query ast.Body) ast.Body {
|
||||
|
||||
result := ast.NewBody()
|
||||
|
||||
uf, ok := makeDisjointSets(p.livevars, query)
|
||||
if !ok {
|
||||
@@ -63,14 +86,15 @@ func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
}
|
||||
|
||||
// Compute set of vars that appear in the head of refs in the query. If a var
|
||||
// is dereferenced, we cannot plug it with a constant value so the constant on
|
||||
// the union-find root must be unset (e.g., [1][0] is not legal.)
|
||||
// is dereferenced, we can plug it with a constant value, but it is not always
|
||||
// optimal to do so.
|
||||
// TODO: Improve the algorithm for when we should plug constants/calls/etc
|
||||
headvars := ast.NewVarSet()
|
||||
ast.WalkRefs(query, func(x ast.Ref) bool {
|
||||
if v, ok := x[0].Value.(ast.Var); ok {
|
||||
if root, ok := uf.Find(v); ok {
|
||||
root.constant = nil
|
||||
headvars.Add(root.key)
|
||||
headvars.Add(root.key.(ast.Var))
|
||||
} else {
|
||||
headvars.Add(v)
|
||||
}
|
||||
@@ -78,21 +102,21 @@ func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
return false
|
||||
})
|
||||
|
||||
bindings := map[ast.Var]*binding{}
|
||||
removedEqs := ast.NewValueMap()
|
||||
|
||||
for _, expr := range query {
|
||||
|
||||
pctx := &plugContext{
|
||||
bindings: bindings,
|
||||
uf: uf,
|
||||
negated: expr.Negated,
|
||||
headvars: headvars,
|
||||
removedEqs: removedEqs,
|
||||
uf: uf,
|
||||
negated: expr.Negated,
|
||||
headvars: headvars,
|
||||
}
|
||||
|
||||
if expr, keep := p.plugBindings(pctx, expr); keep {
|
||||
if p.updateBindings(pctx, expr) {
|
||||
result.Append(expr)
|
||||
}
|
||||
expr = p.plugBindings(pctx, expr)
|
||||
|
||||
if p.updateBindings(pctx, expr) {
|
||||
result.Append(expr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +128,7 @@ func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
// from being added to the result. For example:
|
||||
//
|
||||
// - Given the following result: <empty>
|
||||
// - Given the following bindings: x/input.x and y/input
|
||||
// - Given the following removed equalities: "x = input.x" and "y = input"
|
||||
// - Given the following liveset: {x}
|
||||
//
|
||||
// If this step were to run AFTER the following step, the output would be:
|
||||
@@ -116,8 +140,8 @@ func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
if root, ok := uf.Find(v); ok {
|
||||
if root.constant != nil {
|
||||
result.Append(ast.Equality.Expr(ast.NewTerm(v), root.constant))
|
||||
} else if b, ok := bindings[root.key]; ok {
|
||||
result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(b.v)))
|
||||
} else if b := removedEqs.Get(root.key); b != nil {
|
||||
result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(b)))
|
||||
} else if root.key != v {
|
||||
result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(root.key)))
|
||||
}
|
||||
@@ -125,17 +149,46 @@ func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
}
|
||||
|
||||
// Run post-processing step on query to ensure that all killed exprs are
|
||||
// accounted for. If an expr is killed but the binding is never used, the query
|
||||
// must still include the expr. For example, given the query 'input.x = a' and
|
||||
// an empty livevar set, the result must include the ref input.x otherwise the
|
||||
// query could be satisfied without input.x being defined. When exprs are
|
||||
// killed we initialize the binding counter to zero and then increment it each
|
||||
// time the binding is substituted. if the binding was never substituted it
|
||||
// means the binding value must be added back into the query.
|
||||
for _, b := range sortbindings(bindings) {
|
||||
if !b.containedIn(result) {
|
||||
result.Append(ast.Equality.Expr(ast.NewTerm(b.k), ast.NewTerm(b.v)))
|
||||
// accounted for. There are several cases we look for:
|
||||
//
|
||||
// * If an expr is killed but the binding is never used, the query
|
||||
// must still include the expr. For example, given the query 'input.x = a' and
|
||||
// an empty livevar set, the result must include the ref input.x otherwise the
|
||||
// query could be satisfied without input.x being defined.
|
||||
//
|
||||
// * If an expr is killed that provided safety to vars which are not
|
||||
// otherwise being made safe by the current result.
|
||||
//
|
||||
// For any of these cases we re-add the removed equality expression
|
||||
// to the current result.
|
||||
|
||||
// Invariant: Live vars are bound (above) and reserved vars are implicitly ground.
|
||||
safe := ast.ReservedVars.Copy()
|
||||
safe.Update(p.livevars)
|
||||
safe.Update(ast.OutputVarsFromBody(p.compiler, result, safe))
|
||||
unsafe := result.Vars(ast.SafetyCheckVisitorParams).Diff(safe)
|
||||
|
||||
for _, b := range sortbindings(removedEqs) {
|
||||
removedEq := ast.Equality.Expr(ast.NewTerm(b.k), ast.NewTerm(b.v))
|
||||
|
||||
providesSafety := false
|
||||
outputVars := ast.OutputVarsFromExpr(p.compiler, removedEq, safe)
|
||||
diff := unsafe.Diff(outputVars)
|
||||
if len(diff) < len(unsafe) {
|
||||
unsafe = diff
|
||||
providesSafety = true
|
||||
}
|
||||
|
||||
if providesSafety || !containedIn(b.v, result) {
|
||||
result.Append(removedEq)
|
||||
safe.Update(outputVars)
|
||||
}
|
||||
}
|
||||
|
||||
if len(unsafe) > 0 {
|
||||
// NOTE(tsandall): This should be impossible but if it does occur, throw
|
||||
// away the result rather than generating unsafe output.
|
||||
return query
|
||||
}
|
||||
|
||||
if p.ensureNonEmptyBody && len(result) == 0 {
|
||||
@@ -147,19 +200,7 @@ func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) {
|
||||
|
||||
// plugBindings applies the binding list and union-find to x. This process
|
||||
// removes as many variables as possible.
|
||||
func (p *CopyPropagator) plugBindings(pctx *plugContext, expr *ast.Expr) (*ast.Expr, bool) {
|
||||
|
||||
// Kill single term expressions that are in the binding list. They will be
|
||||
// re-added during post-processing if needed.
|
||||
if term, ok := expr.Terms.(*ast.Term); ok {
|
||||
if v, ok := term.Value.(ast.Var); ok {
|
||||
if root, ok := pctx.uf.Find(v); ok {
|
||||
if _, ok := pctx.bindings[root.key]; ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (p *CopyPropagator) plugBindings(pctx *plugContext, expr *ast.Expr) *ast.Expr {
|
||||
|
||||
xform := bindingPlugTransform{
|
||||
pctx: pctx,
|
||||
@@ -175,7 +216,7 @@ func (p *CopyPropagator) plugBindings(pctx *plugContext, expr *ast.Expr) (*ast.E
|
||||
if expr, ok := x.(*ast.Expr); !ok || err != nil {
|
||||
panic("unreachable")
|
||||
} else {
|
||||
return expr, true
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,31 +235,40 @@ func (t bindingPlugTransform) Transform(x interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) (result ast.Value) {
|
||||
func (t bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) ast.Value {
|
||||
|
||||
result = v
|
||||
var result ast.Value = v
|
||||
|
||||
// Apply union-find to remove redundant variables from input.
|
||||
if root, ok := pctx.uf.Find(v); ok {
|
||||
root, ok := pctx.uf.Find(v)
|
||||
if ok {
|
||||
result = root.Value()
|
||||
}
|
||||
|
||||
// Apply binding list to substitute remaining vars.
|
||||
if v, ok := result.(ast.Var); ok {
|
||||
if b, ok := pctx.bindings[v]; ok {
|
||||
if !pctx.negated || b.v.IsGround() {
|
||||
result = b.v
|
||||
}
|
||||
}
|
||||
v, ok = result.(ast.Var)
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
b := pctx.removedEqs.Get(v)
|
||||
if b == nil {
|
||||
return result
|
||||
}
|
||||
if pctx.negated && !b.IsGround() {
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
if r, ok := b.(ast.Ref); ok && r.OutputVars().Contains(v) {
|
||||
return result
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (t bindingPlugTransform) plugBindingsRef(pctx *plugContext, v ast.Ref) ast.Ref {
|
||||
|
||||
// Apply union-find to remove redundant variables from input.
|
||||
if root, ok := pctx.uf.Find(v[0].Value.(ast.Var)); ok {
|
||||
if root, ok := pctx.uf.Find(v[0].Value); ok {
|
||||
v[0].Value = root.Value()
|
||||
}
|
||||
|
||||
@@ -226,11 +276,16 @@ func (t bindingPlugTransform) plugBindingsRef(pctx *plugContext, v ast.Ref) ast.
|
||||
|
||||
// Refs require special handling. If the head of the ref was killed, then
|
||||
// the rest of the ref must be concatenated with the new base.
|
||||
//
|
||||
// Invariant: ref heads can only be replaced by refs (not calls).
|
||||
if b, ok := pctx.bindings[v[0].Value.(ast.Var)]; ok {
|
||||
if !pctx.negated || b.v.IsGround() {
|
||||
result = b.v.(ast.Ref).Concat(v[1:])
|
||||
if b := pctx.removedEqs.Get(v[0].Value); b != nil {
|
||||
if !pctx.negated || b.IsGround() {
|
||||
var base ast.Ref
|
||||
switch x := b.(type) {
|
||||
case ast.Ref:
|
||||
base = x
|
||||
default:
|
||||
base = ast.Ref{ast.NewTerm(x)}
|
||||
}
|
||||
result = base.Concat(v[1:])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,32 +295,54 @@ func (t bindingPlugTransform) plugBindingsRef(pctx *plugContext, v ast.Ref) ast.
|
||||
// updateBindings returns false if the expression can be killed. If the
|
||||
// expression is killed, the binding list is updated to map a var to value.
|
||||
func (p *CopyPropagator) updateBindings(pctx *plugContext, expr *ast.Expr) bool {
|
||||
if pctx.negated || len(expr.With) > 0 {
|
||||
switch {
|
||||
case pctx.negated || len(expr.With) > 0:
|
||||
return true
|
||||
}
|
||||
if expr.IsEquality() {
|
||||
|
||||
case expr.IsEquality():
|
||||
a, b := expr.Operand(0), expr.Operand(1)
|
||||
if a.Equal(b) {
|
||||
if p.livevarRef(a) {
|
||||
pctx.removedEqs.Put(p.localvargen.Generate(), a.Value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
k, v, keep := p.updateBindingsEq(a, b)
|
||||
if !keep {
|
||||
if v != nil {
|
||||
pctx.bindings[k] = newbinding(k, v)
|
||||
pctx.removedEqs.Put(k, v)
|
||||
}
|
||||
return false
|
||||
}
|
||||
} else if expr.IsCall() {
|
||||
|
||||
case expr.IsCall():
|
||||
terms := expr.Terms.([]*ast.Term)
|
||||
output := terms[len(terms)-1]
|
||||
if k, ok := output.Value.(ast.Var); ok && !p.livevars.Contains(k) && !pctx.headvars.Contains(k) {
|
||||
pctx.bindings[k] = newbinding(k, ast.CallTerm(terms[:len(terms)-1]...).Value)
|
||||
return false
|
||||
if p.compiler.GetArity(expr.Operator()) == len(terms)-2 { // with captured output
|
||||
output := terms[len(terms)-1]
|
||||
if k, ok := output.Value.(ast.Var); ok && !p.livevars.Contains(k) && !pctx.headvars.Contains(k) {
|
||||
pctx.removedEqs.Put(k, ast.CallTerm(terms[:len(terms)-1]...).Value)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return !isNoop(expr)
|
||||
}
|
||||
|
||||
func (p *CopyPropagator) livevarRef(a *ast.Term) bool {
|
||||
ref, ok := a.Value.(ast.Ref)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, v := range p.sorted {
|
||||
if ref[0].Value.Compare(v) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *CopyPropagator) updateBindingsEq(a, b *ast.Term) (ast.Var, ast.Value, bool) {
|
||||
k, v, keep := p.updateBindingsEqAsymmetric(a, b)
|
||||
if !keep {
|
||||
@@ -289,26 +366,21 @@ func (p *CopyPropagator) updateBindingsEqAsymmetric(a, b *ast.Term) (ast.Var, as
|
||||
}
|
||||
|
||||
type plugContext struct {
|
||||
bindings map[ast.Var]*binding
|
||||
uf *unionFind
|
||||
headvars ast.VarSet
|
||||
negated bool
|
||||
removedEqs *ast.ValueMap
|
||||
uf *unionFind
|
||||
headvars ast.VarSet
|
||||
negated bool
|
||||
}
|
||||
|
||||
type binding struct {
|
||||
k ast.Var
|
||||
v ast.Value
|
||||
k, v ast.Value
|
||||
}
|
||||
|
||||
func newbinding(k ast.Var, v ast.Value) *binding {
|
||||
return &binding{k: k, v: v}
|
||||
}
|
||||
|
||||
func (b *binding) containedIn(query ast.Body) bool {
|
||||
func containedIn(value ast.Value, x interface{}) bool {
|
||||
var stop bool
|
||||
switch v := b.v.(type) {
|
||||
switch v := value.(type) {
|
||||
case ast.Ref:
|
||||
ast.WalkRefs(query, func(other ast.Ref) bool {
|
||||
ast.WalkRefs(x, func(other ast.Ref) bool {
|
||||
if stop || other.HasPrefix(v) {
|
||||
stop = true
|
||||
return stop
|
||||
@@ -316,7 +388,7 @@ func (b *binding) containedIn(query ast.Body) bool {
|
||||
return false
|
||||
})
|
||||
default:
|
||||
ast.WalkTerms(query, func(other *ast.Term) bool {
|
||||
ast.WalkTerms(x, func(other *ast.Term) bool {
|
||||
if stop || other.Value.Compare(v) == 0 {
|
||||
stop = true
|
||||
return stop
|
||||
@@ -327,23 +399,18 @@ func (b *binding) containedIn(query ast.Body) bool {
|
||||
return stop
|
||||
}
|
||||
|
||||
func sortbindings(bindings map[ast.Var]*binding) []*binding {
|
||||
sorted := make([]*binding, 0, len(bindings))
|
||||
for _, b := range bindings {
|
||||
sorted = append(sorted, b)
|
||||
}
|
||||
func sortbindings(bindings *ast.ValueMap) []*binding {
|
||||
sorted := make([]*binding, 0, bindings.Len())
|
||||
bindings.Iter(func(k ast.Value, v ast.Value) bool {
|
||||
sorted = append(sorted, &binding{k, v})
|
||||
return false
|
||||
})
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i].k.Compare(sorted[j].k) < 0
|
||||
return sorted[i].k.Compare(sorted[j].k) > 0
|
||||
})
|
||||
return sorted
|
||||
}
|
||||
|
||||
type unionFind struct {
|
||||
roots map[ast.Var]*unionFindRoot
|
||||
parents map[ast.Var]ast.Var
|
||||
rank rankFunc
|
||||
}
|
||||
|
||||
// makeDisjointSets builds the union-find structure for the query. The structure
|
||||
// is built by processing all of the equality exprs in the query. Sets represent
|
||||
// vars that must be equal to each other. In addition to vars, each set can have
|
||||
@@ -352,7 +419,7 @@ type unionFind struct {
|
||||
// false.
|
||||
func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) {
|
||||
uf := newUnionFind(func(r1, r2 *unionFindRoot) (*unionFindRoot, *unionFindRoot) {
|
||||
if livevars.Contains(r1.key) {
|
||||
if v, ok := r1.key.(ast.Var); ok && livevars.Contains(v) {
|
||||
return r1, r2
|
||||
}
|
||||
return r2, r1
|
||||
@@ -362,17 +429,21 @@ func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) {
|
||||
a, b := expr.Operand(0), expr.Operand(1)
|
||||
varA, ok1 := a.Value.(ast.Var)
|
||||
varB, ok2 := b.Value.(ast.Var)
|
||||
if ok1 && ok2 {
|
||||
|
||||
switch {
|
||||
case ok1 && ok2:
|
||||
if _, ok := uf.Merge(varA, varB); !ok {
|
||||
return nil, false
|
||||
}
|
||||
} else if ok1 && ast.IsConstant(b.Value) {
|
||||
|
||||
case ok1 && ast.IsConstant(b.Value):
|
||||
root := uf.MakeSet(varA)
|
||||
if root.constant != nil && !root.constant.Equal(b) {
|
||||
return nil, false
|
||||
}
|
||||
root.constant = b
|
||||
} else if ok2 && ast.IsConstant(a.Value) {
|
||||
|
||||
case ok2 && ast.IsConstant(a.Value):
|
||||
root := uf.MakeSet(varB)
|
||||
if root.constant != nil && !root.constant.Equal(a) {
|
||||
return nil, false
|
||||
@@ -385,89 +456,9 @@ func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) {
|
||||
return uf, true
|
||||
}
|
||||
|
||||
type rankFunc func(*unionFindRoot, *unionFindRoot) (*unionFindRoot, *unionFindRoot)
|
||||
|
||||
func newUnionFind(rank rankFunc) *unionFind {
|
||||
return &unionFind{
|
||||
roots: map[ast.Var]*unionFindRoot{},
|
||||
parents: map[ast.Var]ast.Var{},
|
||||
rank: rank,
|
||||
}
|
||||
}
|
||||
|
||||
func (uf *unionFind) MakeSet(v ast.Var) *unionFindRoot {
|
||||
|
||||
root, ok := uf.Find(v)
|
||||
if ok {
|
||||
return root
|
||||
}
|
||||
|
||||
root = newUnionFindRoot(v)
|
||||
uf.parents[v] = v
|
||||
uf.roots[v] = root
|
||||
return uf.roots[v]
|
||||
}
|
||||
|
||||
func (uf *unionFind) Find(v ast.Var) (*unionFindRoot, bool) {
|
||||
|
||||
parent, ok := uf.parents[v]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if parent == v {
|
||||
return uf.roots[v], true
|
||||
}
|
||||
|
||||
return uf.Find(parent)
|
||||
}
|
||||
|
||||
func (uf *unionFind) Merge(a, b ast.Var) (*unionFindRoot, bool) {
|
||||
|
||||
r1 := uf.MakeSet(a)
|
||||
r2 := uf.MakeSet(b)
|
||||
|
||||
if r1 != r2 {
|
||||
|
||||
r1, r2 = uf.rank(r1, r2)
|
||||
|
||||
uf.parents[r2.key] = r1.key
|
||||
delete(uf.roots, r2.key)
|
||||
|
||||
// Sets can have at most one constant value associated with them. When
|
||||
// unioning, we must preserve this invariant. If a set has two constants,
|
||||
// there will be no way to prove the query.
|
||||
if r1.constant != nil && r2.constant != nil && !r1.constant.Equal(r2.constant) {
|
||||
return nil, false
|
||||
} else if r1.constant == nil {
|
||||
r1.constant = r2.constant
|
||||
}
|
||||
}
|
||||
|
||||
return r1, true
|
||||
}
|
||||
|
||||
type unionFindRoot struct {
|
||||
key ast.Var
|
||||
constant *ast.Term
|
||||
}
|
||||
|
||||
func newUnionFindRoot(key ast.Var) *unionFindRoot {
|
||||
return &unionFindRoot{
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *unionFindRoot) Value() ast.Value {
|
||||
if r.constant != nil {
|
||||
return r.constant.Value
|
||||
}
|
||||
return r.key
|
||||
}
|
||||
|
||||
func isNoop(expr *ast.Expr) bool {
|
||||
|
||||
if !expr.IsCall() {
|
||||
if !expr.IsCall() && !expr.IsEvery() {
|
||||
term := expr.Terms.(*ast.Term)
|
||||
if !ast.IsConstant(term.Value) {
|
||||
return false
|
||||
|
||||
135
vendor/github.com/open-policy-agent/opa/topdown/copypropagation/unionfind.go
generated
vendored
Normal file
135
vendor/github.com/open-policy-agent/opa/topdown/copypropagation/unionfind.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2020 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package copypropagation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
)
|
||||
|
||||
type rankFunc func(*unionFindRoot, *unionFindRoot) (*unionFindRoot, *unionFindRoot)
|
||||
|
||||
type unionFind struct {
|
||||
roots *util.HashMap
|
||||
parents *ast.ValueMap
|
||||
rank rankFunc
|
||||
}
|
||||
|
||||
func newUnionFind(rank rankFunc) *unionFind {
|
||||
return &unionFind{
|
||||
roots: util.NewHashMap(func(a util.T, b util.T) bool {
|
||||
return a.(ast.Value).Compare(b.(ast.Value)) == 0
|
||||
}, func(v util.T) int {
|
||||
return v.(ast.Value).Hash()
|
||||
}),
|
||||
parents: ast.NewValueMap(),
|
||||
rank: rank,
|
||||
}
|
||||
}
|
||||
|
||||
func (uf *unionFind) MakeSet(v ast.Value) *unionFindRoot {
|
||||
|
||||
root, ok := uf.Find(v)
|
||||
if ok {
|
||||
return root
|
||||
}
|
||||
|
||||
root = newUnionFindRoot(v)
|
||||
uf.parents.Put(v, v)
|
||||
uf.roots.Put(v, root)
|
||||
return root
|
||||
}
|
||||
|
||||
func (uf *unionFind) Find(v ast.Value) (*unionFindRoot, bool) {
|
||||
|
||||
parent := uf.parents.Get(v)
|
||||
if parent == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if parent.Compare(v) == 0 {
|
||||
r, ok := uf.roots.Get(v)
|
||||
return r.(*unionFindRoot), ok
|
||||
}
|
||||
|
||||
return uf.Find(parent)
|
||||
}
|
||||
|
||||
func (uf *unionFind) Merge(a, b ast.Value) (*unionFindRoot, bool) {
|
||||
|
||||
r1 := uf.MakeSet(a)
|
||||
r2 := uf.MakeSet(b)
|
||||
|
||||
if r1 != r2 {
|
||||
|
||||
r1, r2 = uf.rank(r1, r2)
|
||||
|
||||
uf.parents.Put(r2.key, r1.key)
|
||||
uf.roots.Delete(r2.key)
|
||||
|
||||
// Sets can have at most one constant value associated with them. When
|
||||
// unioning, we must preserve this invariant. If a set has two constants,
|
||||
// there will be no way to prove the query.
|
||||
if r1.constant != nil && r2.constant != nil && !r1.constant.Equal(r2.constant) {
|
||||
return nil, false
|
||||
} else if r1.constant == nil {
|
||||
r1.constant = r2.constant
|
||||
}
|
||||
}
|
||||
|
||||
return r1, true
|
||||
}
|
||||
|
||||
func (uf *unionFind) String() string {
|
||||
o := struct {
|
||||
Roots map[string]interface{}
|
||||
Parents map[string]ast.Value
|
||||
}{
|
||||
map[string]interface{}{},
|
||||
map[string]ast.Value{},
|
||||
}
|
||||
|
||||
uf.roots.Iter(func(k util.T, v util.T) bool {
|
||||
o.Roots[k.(ast.Value).String()] = struct {
|
||||
Constant *ast.Term
|
||||
Key ast.Value
|
||||
}{
|
||||
v.(*unionFindRoot).constant,
|
||||
v.(*unionFindRoot).key,
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
uf.parents.Iter(func(k ast.Value, v ast.Value) bool {
|
||||
o.Parents[k.String()] = v
|
||||
return true
|
||||
})
|
||||
|
||||
return string(util.MustMarshalJSON(o))
|
||||
}
|
||||
|
||||
type unionFindRoot struct {
|
||||
key ast.Value
|
||||
constant *ast.Term
|
||||
}
|
||||
|
||||
func newUnionFindRoot(key ast.Value) *unionFindRoot {
|
||||
return &unionFindRoot{
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *unionFindRoot) Value() ast.Value {
|
||||
if r.constant != nil {
|
||||
return r.constant.Value
|
||||
}
|
||||
return r.key
|
||||
}
|
||||
|
||||
func (r *unionFindRoot) String() string {
|
||||
return fmt.Sprintf("{key: %s, constant: %s", r.key, r.constant)
|
||||
}
|
||||
Reference in New Issue
Block a user