build(deps): bump github.com/open-policy-agent/opa from 1.10.1 to 1.11.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.10.1 to 1.11.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.10.1...v1.11.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
5ed944dfc3
commit
82c82f8ae2
+232
-190
@@ -32,7 +32,7 @@ const (
|
||||
opaWasmABIMinorVersionVar = "opa_wasm_abi_minor_version"
|
||||
)
|
||||
|
||||
// nolint: deadcode,varcheck
|
||||
// nolint: varcheck
|
||||
const (
|
||||
opaTypeNull int32 = iota + 1
|
||||
opaTypeBoolean
|
||||
@@ -414,7 +414,7 @@ func (c *Compiler) initModule() error {
|
||||
},
|
||||
},
|
||||
},
|
||||
Init: bytes.Repeat([]byte{0}, int(heapBase-offset)),
|
||||
Init: make([]byte, int(heapBase-offset)),
|
||||
})
|
||||
|
||||
return nil
|
||||
@@ -1058,9 +1058,11 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err
|
||||
},
|
||||
})
|
||||
case *ir.AssignIntStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs, instruction.I64Const{Value: stmt.Value})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueNumberSetInt)})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.Target)},
|
||||
instruction.I64Const{Value: stmt.Value},
|
||||
instruction.Call{Index: c.function(opaValueNumberSetInt)},
|
||||
)
|
||||
case *ir.ScanStmt:
|
||||
if err := c.compileScan(stmt, &instrs); err != nil {
|
||||
return nil, err
|
||||
@@ -1073,12 +1075,14 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err
|
||||
}
|
||||
case *ir.DotStmt:
|
||||
if loc, ok := stmt.Source.Value.(ir.Local); ok {
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(loc)})
|
||||
instrs = append(instrs, c.instrRead(stmt.Key))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueGet)})
|
||||
instrs = append(instrs, instruction.TeeLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs, instruction.I32Eqz{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(loc)},
|
||||
c.instrRead(stmt.Key),
|
||||
instruction.Call{Index: c.function(opaValueGet)},
|
||||
instruction.TeeLocal{Index: c.local(stmt.Target)},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
} else {
|
||||
// Booleans and string sources would lead to the BrIf (since opa_value_get
|
||||
// on them returns 0), so let's skip trying that.
|
||||
@@ -1086,97 +1090,131 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err
|
||||
break
|
||||
}
|
||||
case *ir.LenStmt:
|
||||
instrs = append(instrs, c.instrRead(stmt.Source))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueLength)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaNumberSize)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
c.instrRead(stmt.Source),
|
||||
instruction.Call{Index: c.function(opaValueLength)},
|
||||
instruction.Call{Index: c.function(opaNumberSize)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.EqualStmt:
|
||||
instrs = append(instrs, c.instrRead(stmt.A))
|
||||
instrs = append(instrs, c.instrRead(stmt.B))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
c.instrRead(stmt.A),
|
||||
c.instrRead(stmt.B),
|
||||
instruction.Call{Index: c.function(opaValueCompare)},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
case *ir.NotEqualStmt:
|
||||
instrs = append(instrs, c.instrRead(stmt.A))
|
||||
instrs = append(instrs, c.instrRead(stmt.B))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)})
|
||||
instrs = append(instrs, instruction.I32Eqz{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
c.instrRead(stmt.A),
|
||||
c.instrRead(stmt.B),
|
||||
instruction.Call{Index: c.function(opaValueCompare)},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
case *ir.MakeNullStmt:
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaNull)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.Call{Index: c.function(opaNull)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.MakeNumberIntStmt:
|
||||
instrs = append(instrs, instruction.I64Const{Value: stmt.Value})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaNumberInt)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.I64Const{Value: stmt.Value},
|
||||
instruction.Call{Index: c.function(opaNumberInt)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.MakeNumberRefStmt:
|
||||
instrs = append(instrs, instruction.I32Const{Value: c.stringAddr(stmt.Index)})
|
||||
instrs = append(instrs, instruction.I32Const{Value: int32(len(c.policy.Static.Strings[stmt.Index].Value))})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaNumberRef)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.I32Const{Value: c.stringAddr(stmt.Index)},
|
||||
instruction.I32Const{Value: int32(len(c.policy.Static.Strings[stmt.Index].Value))},
|
||||
instruction.Call{Index: c.function(opaNumberRef)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.MakeArrayStmt:
|
||||
instrs = append(instrs, instruction.I32Const{Value: stmt.Capacity})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaArrayWithCap)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.I32Const{Value: stmt.Capacity},
|
||||
instruction.Call{Index: c.function(opaArrayWithCap)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.MakeObjectStmt:
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaObject)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.Call{Index: c.function(opaObject)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.MakeSetStmt:
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaSet)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.Call{Index: c.function(opaSet)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.IsArrayStmt:
|
||||
if loc, ok := stmt.Source.Value.(ir.Local); ok {
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(loc)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)})
|
||||
instrs = append(instrs, instruction.I32Const{Value: opaTypeArray})
|
||||
instrs = append(instrs, instruction.I32Ne{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(loc)},
|
||||
instruction.Call{Index: c.function(opaValueType)},
|
||||
instruction.I32Const{Value: opaTypeArray},
|
||||
instruction.I32Ne{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
} else {
|
||||
instrs = append(instrs, instruction.Br{Index: 0})
|
||||
break
|
||||
}
|
||||
case *ir.IsObjectStmt:
|
||||
if loc, ok := stmt.Source.Value.(ir.Local); ok {
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(loc)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)})
|
||||
instrs = append(instrs, instruction.I32Const{Value: opaTypeObject})
|
||||
instrs = append(instrs, instruction.I32Ne{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(loc)},
|
||||
instruction.Call{Index: c.function(opaValueType)},
|
||||
instruction.I32Const{Value: opaTypeObject},
|
||||
instruction.I32Ne{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
} else {
|
||||
instrs = append(instrs, instruction.Br{Index: 0})
|
||||
break
|
||||
}
|
||||
case *ir.IsSetStmt:
|
||||
if loc, ok := stmt.Source.Value.(ir.Local); ok {
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(loc)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)})
|
||||
instrs = append(instrs, instruction.I32Const{Value: opaTypeSet})
|
||||
instrs = append(instrs, instruction.I32Ne{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(loc)},
|
||||
instruction.Call{Index: c.function(opaValueType)},
|
||||
instruction.I32Const{Value: opaTypeSet},
|
||||
instruction.I32Ne{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
} else {
|
||||
instrs = append(instrs, instruction.Br{Index: 0})
|
||||
break
|
||||
}
|
||||
case *ir.IsUndefinedStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)})
|
||||
instrs = append(instrs, instruction.I32Const{Value: 0})
|
||||
instrs = append(instrs, instruction.I32Ne{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.Source)},
|
||||
instruction.I32Const{Value: 0},
|
||||
instruction.I32Ne{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
case *ir.ResetLocalStmt:
|
||||
instrs = append(instrs, instruction.I32Const{Value: 0})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.I32Const{Value: 0},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.IsDefinedStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)})
|
||||
instrs = append(instrs, instruction.I32Eqz{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.Source)},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
case *ir.ArrayAppendStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Array)})
|
||||
instrs = append(instrs, c.instrRead(stmt.Value))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaArrayAppend)})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.Array)},
|
||||
c.instrRead(stmt.Value),
|
||||
instruction.Call{Index: c.function(opaArrayAppend)},
|
||||
)
|
||||
case *ir.ObjectInsertStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Object)})
|
||||
instrs = append(instrs, c.instrRead(stmt.Key))
|
||||
instrs = append(instrs, c.instrRead(stmt.Value))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.Object)},
|
||||
c.instrRead(stmt.Key),
|
||||
c.instrRead(stmt.Value),
|
||||
instruction.Call{Index: c.function(opaObjectInsert)},
|
||||
)
|
||||
case *ir.ObjectInsertOnceStmt:
|
||||
tmp := c.genLocal()
|
||||
instrs = append(instrs, instruction.Block{
|
||||
@@ -1203,14 +1241,18 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err
|
||||
},
|
||||
})
|
||||
case *ir.ObjectMergeStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueMerge)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.A)},
|
||||
instruction.GetLocal{Index: c.local(stmt.B)},
|
||||
instruction.Call{Index: c.function(opaValueMerge)},
|
||||
instruction.SetLocal{Index: c.local(stmt.Target)},
|
||||
)
|
||||
case *ir.SetAddStmt:
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Set)})
|
||||
instrs = append(instrs, c.instrRead(stmt.Value))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaSetAdd)})
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(stmt.Set)},
|
||||
c.instrRead(stmt.Value),
|
||||
instruction.Call{Index: c.function(opaSetAdd)},
|
||||
)
|
||||
default:
|
||||
var buf bytes.Buffer
|
||||
err := ir.Pretty(&buf, stmt)
|
||||
@@ -1226,8 +1268,7 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err
|
||||
|
||||
func (c *Compiler) compileScan(scan *ir.ScanStmt, result *[]instruction.Instruction) error {
|
||||
var instrs = *result
|
||||
instrs = append(instrs, instruction.I32Const{Value: 0})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Key)})
|
||||
instrs = append(instrs, instruction.I32Const{Value: 0}, instruction.SetLocal{Index: c.local(scan.Key)})
|
||||
body, err := c.compileScanBlock(scan)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1242,23 +1283,21 @@ func (c *Compiler) compileScan(scan *ir.ScanStmt, result *[]instruction.Instruct
|
||||
}
|
||||
|
||||
func (c *Compiler) compileScanBlock(scan *ir.ScanStmt) ([]instruction.Instruction, error) {
|
||||
var instrs []instruction.Instruction
|
||||
|
||||
// Execute iterator.
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Source)})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueIter)})
|
||||
|
||||
// Check for emptiness.
|
||||
instrs = append(instrs, instruction.TeeLocal{Index: c.local(scan.Key)})
|
||||
instrs = append(instrs, instruction.I32Eqz{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 1})
|
||||
|
||||
// Load value.
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Source)})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaValueGet)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Value)})
|
||||
instrs := []instruction.Instruction{
|
||||
// Execute iterator.
|
||||
instruction.GetLocal{Index: c.local(scan.Source)},
|
||||
instruction.GetLocal{Index: c.local(scan.Key)},
|
||||
instruction.Call{Index: c.function(opaValueIter)},
|
||||
// Check for emptiness.
|
||||
instruction.TeeLocal{Index: c.local(scan.Key)},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 1},
|
||||
// Load value.
|
||||
instruction.GetLocal{Index: c.local(scan.Source)},
|
||||
instruction.GetLocal{Index: c.local(scan.Key)},
|
||||
instruction.Call{Index: c.function(opaValueGet)},
|
||||
instruction.SetLocal{Index: c.local(scan.Value)},
|
||||
}
|
||||
|
||||
// Loop body.
|
||||
nested, err := c.compileBlock(scan.Block)
|
||||
@@ -1278,8 +1317,7 @@ func (c *Compiler) compileNot(not *ir.NotStmt, result *[]instruction.Instruction
|
||||
|
||||
// generate and initialize condition variable
|
||||
cond := c.genLocal()
|
||||
instrs = append(instrs, instruction.I32Const{Value: 1})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: cond})
|
||||
instrs = append(instrs, instruction.I32Const{Value: 1}, instruction.SetLocal{Index: cond})
|
||||
|
||||
nested, err := c.compileBlock(not.Block)
|
||||
if err != nil {
|
||||
@@ -1287,14 +1325,15 @@ func (c *Compiler) compileNot(not *ir.NotStmt, result *[]instruction.Instruction
|
||||
}
|
||||
|
||||
// unset condition variable if end of block is reached
|
||||
nested = append(nested, instruction.I32Const{Value: 0})
|
||||
nested = append(nested, instruction.SetLocal{Index: cond})
|
||||
instrs = append(instrs, instruction.Block{Instrs: nested})
|
||||
|
||||
// break out of block if condition variable was unset
|
||||
instrs = append(instrs, instruction.GetLocal{Index: cond})
|
||||
instrs = append(instrs, instruction.I32Eqz{})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
instrs = append(instrs, instruction.Block{Instrs: append(nested,
|
||||
instruction.I32Const{Value: 0},
|
||||
instruction.SetLocal{Index: cond},
|
||||
)},
|
||||
// break out of block if condition variable was unset
|
||||
instruction.GetLocal{Index: cond},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
|
||||
*result = instrs
|
||||
return nil
|
||||
@@ -1304,34 +1343,36 @@ func (c *Compiler) compileWithStmt(with *ir.WithStmt, result *[]instruction.Inst
|
||||
|
||||
var instrs = *result
|
||||
save := c.genLocal()
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaMemoizePush)})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(with.Local)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: save})
|
||||
instrs = append(instrs,
|
||||
instruction.Call{Index: c.function(opaMemoizePush)},
|
||||
instruction.GetLocal{Index: c.local(with.Local)},
|
||||
instruction.SetLocal{Index: save},
|
||||
)
|
||||
|
||||
if len(with.Path) == 0 {
|
||||
instrs = append(instrs, c.instrRead(with.Value))
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(with.Local)})
|
||||
instrs = append(instrs, c.instrRead(with.Value), instruction.SetLocal{Index: c.local(with.Local)})
|
||||
} else {
|
||||
instrs = c.compileUpsert(with.Local, with.Path, with.Value, with.Location, instrs)
|
||||
}
|
||||
|
||||
undefined := c.genLocal()
|
||||
instrs = append(instrs, instruction.I32Const{Value: 1})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: undefined})
|
||||
instrs = append(instrs, instruction.I32Const{Value: 1}, instruction.SetLocal{Index: undefined})
|
||||
|
||||
nested, err := c.compileBlock(with.Block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nested = append(nested, instruction.I32Const{Value: 0})
|
||||
nested = append(nested, instruction.SetLocal{Index: undefined})
|
||||
instrs = append(instrs, instruction.Block{Instrs: nested})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: save})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: c.local(with.Local)})
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaMemoizePop)})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: undefined})
|
||||
instrs = append(instrs, instruction.BrIf{Index: 0})
|
||||
nested = append(nested, instruction.I32Const{Value: 0}, instruction.SetLocal{Index: undefined})
|
||||
|
||||
instrs = append(instrs,
|
||||
instruction.Block{Instrs: nested},
|
||||
instruction.GetLocal{Index: save},
|
||||
instruction.SetLocal{Index: c.local(with.Local)},
|
||||
instruction.Call{Index: c.function(opaMemoizePop)},
|
||||
instruction.GetLocal{Index: undefined},
|
||||
instruction.BrIf{Index: 0},
|
||||
)
|
||||
|
||||
*result = instrs
|
||||
|
||||
@@ -1339,37 +1380,38 @@ func (c *Compiler) compileWithStmt(with *ir.WithStmt, result *[]instruction.Inst
|
||||
}
|
||||
|
||||
func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Operand, _ ir.Location, instrs []instruction.Instruction) []instruction.Instruction {
|
||||
|
||||
lcopy := c.genLocal() // holds copy of local
|
||||
instrs = append(instrs, instruction.GetLocal{Index: c.local(local)})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: lcopy})
|
||||
|
||||
// Shallow copy the local if defined otherwise initialize to an empty object.
|
||||
instrs = append(instrs, instruction.Block{
|
||||
Instrs: []instruction.Instruction{
|
||||
instruction.Block{Instrs: []instruction.Instruction{
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 0},
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.Call{Index: c.function(opaValueShallowCopy)},
|
||||
instrs = append(instrs,
|
||||
instruction.GetLocal{Index: c.local(local)},
|
||||
instruction.SetLocal{Index: lcopy},
|
||||
// Shallow copy the local if defined otherwise initialize to an empty object.
|
||||
instruction.Block{
|
||||
Instrs: []instruction.Instruction{
|
||||
instruction.Block{Instrs: []instruction.Instruction{
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: 0},
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.Call{Index: c.function(opaValueShallowCopy)},
|
||||
instruction.TeeLocal{Index: lcopy},
|
||||
instruction.SetLocal{Index: c.local(local)},
|
||||
instruction.Br{Index: 1},
|
||||
}},
|
||||
instruction.Call{Index: c.function(opaObject)},
|
||||
instruction.TeeLocal{Index: lcopy},
|
||||
instruction.SetLocal{Index: c.local(local)},
|
||||
instruction.Br{Index: 1},
|
||||
}},
|
||||
instruction.Call{Index: c.function(opaObject)},
|
||||
instruction.TeeLocal{Index: lcopy},
|
||||
instruction.SetLocal{Index: c.local(local)},
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// Initialize the locals that specify the path of the upsert operation.
|
||||
lpath := make(map[int]uint32, len(path))
|
||||
|
||||
for i := range path {
|
||||
lpath[i] = c.genLocal()
|
||||
instrs = append(instrs, instruction.I32Const{Value: c.opaStringAddr(path[i])})
|
||||
instrs = append(instrs, instruction.SetLocal{Index: lpath[i]})
|
||||
instrs = append(instrs,
|
||||
instruction.I32Const{Value: c.opaStringAddr(path[i])},
|
||||
instruction.SetLocal{Index: lpath[i]},
|
||||
)
|
||||
}
|
||||
|
||||
// Generate a block that traverses the path of the upsert operation,
|
||||
@@ -1379,36 +1421,34 @@ func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Operand, _
|
||||
ltemp := c.genLocal()
|
||||
|
||||
for i := range len(path) - 1 {
|
||||
|
||||
// Lookup the next part of the path.
|
||||
inner = append(inner, instruction.GetLocal{Index: lcopy})
|
||||
inner = append(inner, instruction.GetLocal{Index: lpath[i]})
|
||||
inner = append(inner, instruction.Call{Index: c.function(opaValueGet)})
|
||||
inner = append(inner, instruction.SetLocal{Index: ltemp})
|
||||
|
||||
// If the next node is missing, break.
|
||||
inner = append(inner, instruction.GetLocal{Index: ltemp})
|
||||
inner = append(inner, instruction.I32Eqz{})
|
||||
inner = append(inner, instruction.BrIf{Index: uint32(i)})
|
||||
|
||||
// If the next node is not an object, break.
|
||||
inner = append(inner, instruction.GetLocal{Index: ltemp})
|
||||
inner = append(inner, instruction.Call{Index: c.function(opaValueType)})
|
||||
inner = append(inner, instruction.I32Const{Value: opaTypeObject})
|
||||
inner = append(inner, instruction.I32Ne{})
|
||||
inner = append(inner, instruction.BrIf{Index: uint32(i)})
|
||||
|
||||
// Otherwise, shallow copy the next node node and insert into the copy
|
||||
// before continuing.
|
||||
inner = append(inner, instruction.GetLocal{Index: ltemp})
|
||||
inner = append(inner, instruction.Call{Index: c.function(opaValueShallowCopy)})
|
||||
inner = append(inner, instruction.SetLocal{Index: ltemp})
|
||||
inner = append(inner, instruction.GetLocal{Index: lcopy})
|
||||
inner = append(inner, instruction.GetLocal{Index: lpath[i]})
|
||||
inner = append(inner, instruction.GetLocal{Index: ltemp})
|
||||
inner = append(inner, instruction.Call{Index: c.function(opaObjectInsert)})
|
||||
inner = append(inner, instruction.GetLocal{Index: ltemp})
|
||||
inner = append(inner, instruction.SetLocal{Index: lcopy})
|
||||
inner = append(inner,
|
||||
// Lookup the next part of the path.
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.GetLocal{Index: lpath[i]},
|
||||
instruction.Call{Index: c.function(opaValueGet)},
|
||||
instruction.SetLocal{Index: ltemp},
|
||||
// If the next node is missing, break.
|
||||
instruction.GetLocal{Index: ltemp},
|
||||
instruction.I32Eqz{},
|
||||
instruction.BrIf{Index: uint32(i)},
|
||||
// If the next node is not an object, break.
|
||||
instruction.GetLocal{Index: ltemp},
|
||||
instruction.Call{Index: c.function(opaValueType)},
|
||||
instruction.I32Const{Value: opaTypeObject},
|
||||
instruction.I32Ne{},
|
||||
instruction.BrIf{Index: uint32(i)},
|
||||
// Otherwise, shallow copy the next node node and insert into the copy
|
||||
// before continuing.
|
||||
instruction.GetLocal{Index: ltemp},
|
||||
instruction.Call{Index: c.function(opaValueShallowCopy)},
|
||||
instruction.SetLocal{Index: ltemp},
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.GetLocal{Index: lpath[i]},
|
||||
instruction.GetLocal{Index: ltemp},
|
||||
instruction.Call{Index: c.function(opaObjectInsert)},
|
||||
instruction.GetLocal{Index: ltemp},
|
||||
instruction.SetLocal{Index: lcopy},
|
||||
)
|
||||
}
|
||||
|
||||
inner = append(inner, instruction.Br{Index: uint32(len(path) - 1)})
|
||||
@@ -1418,27 +1458,29 @@ func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Operand, _
|
||||
lval := c.genLocal()
|
||||
|
||||
for i := range len(path) - 1 {
|
||||
block = append(block, instruction.Block{Instrs: inner})
|
||||
block = append(block, instruction.Call{Index: c.function(opaObject)})
|
||||
block = append(block, instruction.SetLocal{Index: lval})
|
||||
block = append(block, instruction.GetLocal{Index: lcopy})
|
||||
block = append(block, instruction.GetLocal{Index: lpath[i]})
|
||||
block = append(block, instruction.GetLocal{Index: lval})
|
||||
block = append(block, instruction.Call{Index: c.function(opaObjectInsert)})
|
||||
block = append(block, instruction.GetLocal{Index: lval})
|
||||
block = append(block, instruction.SetLocal{Index: lcopy})
|
||||
block = append(block,
|
||||
instruction.Block{Instrs: inner},
|
||||
instruction.Call{Index: c.function(opaObject)},
|
||||
instruction.SetLocal{Index: lval},
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.GetLocal{Index: lpath[i]},
|
||||
instruction.GetLocal{Index: lval},
|
||||
instruction.Call{Index: c.function(opaObjectInsert)},
|
||||
instruction.GetLocal{Index: lval},
|
||||
instruction.SetLocal{Index: lcopy},
|
||||
)
|
||||
inner = block
|
||||
block = nil
|
||||
}
|
||||
|
||||
// Finish by inserting the statement's value into the shallow copied node.
|
||||
instrs = append(instrs, instruction.Block{Instrs: inner})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: lcopy})
|
||||
instrs = append(instrs, instruction.GetLocal{Index: lpath[len(path)-1]})
|
||||
instrs = append(instrs, c.instrRead(value))
|
||||
instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)})
|
||||
|
||||
return instrs
|
||||
return append(instrs,
|
||||
instruction.Block{Instrs: inner},
|
||||
instruction.GetLocal{Index: lcopy},
|
||||
instruction.GetLocal{Index: lpath[len(path)-1]},
|
||||
c.instrRead(value),
|
||||
instruction.Call{Index: c.function(opaObjectInsert)},
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Compiler) compileCallDynamicStmt(stmt *ir.CallDynamicStmt, result *[]instruction.Instruction) error {
|
||||
|
||||
+53
-20
@@ -4,39 +4,72 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MustWriteTarGz write the list of file names and content
|
||||
// into a tarball.
|
||||
func MustWriteTarGz(files [][2]string) *bytes.Buffer {
|
||||
var buf bytes.Buffer
|
||||
gw := gzip.NewWriter(&buf)
|
||||
defer gw.Close()
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
for _, file := range files {
|
||||
if err := WriteFile(tw, file[0], []byte(file[1])); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return &buf
|
||||
type TarGzWriter struct {
|
||||
*tar.Writer
|
||||
|
||||
gw *gzip.Writer
|
||||
}
|
||||
|
||||
// WriteFile adds a file header with content to the given tar writer
|
||||
func WriteFile(tw *tar.Writer, path string, bs []byte) error {
|
||||
func NewTarGzWriter(w io.Writer) *TarGzWriter {
|
||||
gw := gzip.NewWriter(w)
|
||||
tw := tar.NewWriter(gw)
|
||||
|
||||
return &TarGzWriter{
|
||||
Writer: tw,
|
||||
gw: gw,
|
||||
}
|
||||
}
|
||||
|
||||
func (tgw *TarGzWriter) WriteFile(path string, bs []byte) (err error) {
|
||||
hdr := &tar.Header{
|
||||
Name: "/" + strings.TrimLeft(path, "/"),
|
||||
Name: path,
|
||||
Mode: 0600,
|
||||
Typeflag: tar.TypeReg,
|
||||
Size: int64(len(bs)),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
if err = tgw.WriteHeader(hdr); err == nil {
|
||||
_, err = tgw.Write(bs)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (tgw *TarGzWriter) WriteJSONFile(path string, v any) error {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := json.NewEncoder(buf).Encode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := tw.Write(bs)
|
||||
return err
|
||||
return tgw.WriteFile(path, buf.Bytes())
|
||||
}
|
||||
|
||||
func (tgw *TarGzWriter) Close() error {
|
||||
return errors.Join(tgw.Writer.Close(), tgw.gw.Close())
|
||||
}
|
||||
|
||||
// MustWriteTarGz writes the list of file names and content into a tarball.
|
||||
// Paths are prefixed with "/".
|
||||
func MustWriteTarGz(files [][2]string) *bytes.Buffer {
|
||||
buf := &bytes.Buffer{}
|
||||
tgw := NewTarGzWriter(buf)
|
||||
defer tgw.Close()
|
||||
|
||||
for _, file := range files {
|
||||
if !strings.HasPrefix(file[0], "/") {
|
||||
file[0] = "/" + file[0]
|
||||
}
|
||||
|
||||
if err := tgw.WriteFile(file[0], []byte(file[1])); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
//
|
||||
// created 26-02-2013
|
||||
|
||||
// nolint: deadcode,unused,varcheck // Package in development (2021).
|
||||
// nolint:unused,varcheck // Package in development (2021).
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
|
||||
+2
@@ -158,6 +158,8 @@ func SignV4(headers map[string][]string, method string, theURL *url.URL, body []
|
||||
// include the values for the signed headers
|
||||
orderedKeys := util.KeysSorted(headersToSign)
|
||||
for _, k := range orderedKeys {
|
||||
// TODO: fix later
|
||||
//nolint:perfsprint
|
||||
canonicalReq += k + ":" + strings.Join(headersToSign[k], ",") + "\n"
|
||||
}
|
||||
canonicalReq += "\n" // linefeed to terminate headers
|
||||
|
||||
+2
-2
@@ -7,16 +7,16 @@ package ref
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/open-policy-agent/opa/v1/ast"
|
||||
"github.com/open-policy-agent/opa/v1/storage"
|
||||
"github.com/open-policy-agent/opa/v1/util"
|
||||
)
|
||||
|
||||
// ParseDataPath returns a ref from the slash separated path s rooted at data.
|
||||
// All path segments are treated as identifier strings.
|
||||
func ParseDataPath(s string) (ast.Ref, error) {
|
||||
path, ok := storage.ParsePath("/" + strings.TrimPrefix(s, "/"))
|
||||
path, ok := storage.ParsePath(util.WithPrefix(s, "/"))
|
||||
if !ok {
|
||||
return nil, errors.New("invalid path")
|
||||
}
|
||||
|
||||
+6
-10
@@ -81,8 +81,6 @@ type GHResponse struct {
|
||||
|
||||
// New returns an instance of the Reporter
|
||||
func New(opts Options) (Reporter, error) {
|
||||
r := GHVersionCollector{}
|
||||
|
||||
url := cmp.Or(os.Getenv("OPA_TELEMETRY_SERVICE_URL"), ExternalServiceURL)
|
||||
|
||||
restConfig := fmt.Appendf(nil, `{
|
||||
@@ -93,7 +91,7 @@ func New(opts Options) (Reporter, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.client = client
|
||||
r := GHVersionCollector{client: client}
|
||||
|
||||
// heap_usage_bytes is always present, so register it unconditionally
|
||||
r.RegisterGatherer("heap_usage_bytes", readRuntimeMemStats)
|
||||
@@ -135,19 +133,17 @@ func createDataResponse(ghResp GHResponse) (*DataResponse, error) {
|
||||
return nil, errors.New("server response does not contain tag_name")
|
||||
}
|
||||
|
||||
v := strings.TrimPrefix(version.Version, "v")
|
||||
sv, err := semver.NewVersion(v)
|
||||
sv, err := semver.Parse(version.Version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse current version %q: %w", v, err)
|
||||
return nil, fmt.Errorf("failed to parse current version %q: %w", version.Version, err)
|
||||
}
|
||||
|
||||
latestV := strings.TrimPrefix(ghResp.TagName, "v")
|
||||
latestSV, err := semver.NewVersion(latestV)
|
||||
latestSV, err := semver.Parse(ghResp.TagName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse latest version %q: %w", latestV, err)
|
||||
return nil, fmt.Errorf("failed to parse latest version %q: %w", ghResp.TagName, err)
|
||||
}
|
||||
|
||||
isLatest := sv.Compare(*latestSV) >= 0
|
||||
isLatest := sv.Compare(latestSV) >= 0
|
||||
|
||||
// Note: alternatively, we could look through the assets in the GH API response to find a matching asset,
|
||||
// and use its URL. However, this is not guaranteed to be more robust, and wouldn't use the 'openpolicyagent.org' domain.
|
||||
|
||||
+2
-5
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/open-policy-agent/opa/v1/loader"
|
||||
"github.com/open-policy-agent/opa/v1/metrics"
|
||||
"github.com/open-policy-agent/opa/v1/storage"
|
||||
"github.com/open-policy-agent/opa/v1/util"
|
||||
)
|
||||
|
||||
// InsertAndCompileOptions contains the input for the operation.
|
||||
@@ -246,13 +247,9 @@ func WalkPaths(paths []string, filter loader.Filter, asBundle bool) (*WalkPathsR
|
||||
cleanedPath = fp
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(cleanedPath, "/") {
|
||||
cleanedPath = "/" + cleanedPath
|
||||
}
|
||||
|
||||
result.FileDescriptors = append(result.FileDescriptors, &Descriptor{
|
||||
Root: path,
|
||||
Path: cleanedPath,
|
||||
Path: util.WithPrefix(cleanedPath, "/"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+194
-197
@@ -14,237 +14,234 @@
|
||||
|
||||
// Semantic Versions http://semver.org
|
||||
|
||||
// Package semver has been vendored from:
|
||||
// This file was originally vendored from:
|
||||
// https://github.com/coreos/go-semver/tree/e214231b295a8ea9479f11b70b35d5acf3556d9b/semver
|
||||
// A number of the original functions of the package have been removed since
|
||||
// they are not required for our built-ins.
|
||||
// There isn't a single line left from the original source today, but being generous about
|
||||
// attribution won't hurt.
|
||||
package semver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/open-policy-agent/opa/v1/util"
|
||||
)
|
||||
|
||||
// reMetaIdentifier matches pre-release and metadata identifiers against the spec requirements
|
||||
var reMetaIdentifier = regexp.MustCompile(`^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$`)
|
||||
|
||||
// Version represents a parsed SemVer
|
||||
type Version struct {
|
||||
Major int64
|
||||
Minor int64
|
||||
Patch int64
|
||||
PreRelease PreRelease
|
||||
Metadata string
|
||||
PreRelease string `json:"PreRelease,omitempty"`
|
||||
Metadata string `json:"Metadata,omitempty"`
|
||||
}
|
||||
|
||||
// PreRelease represents a pre-release suffix string
|
||||
type PreRelease string
|
||||
// Parse constructs new semver Version from version string.
|
||||
func Parse(version string) (v Version, err error) {
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
|
||||
func splitOff(input *string, delim string) (val string) {
|
||||
parts := strings.SplitN(*input, delim, 2)
|
||||
|
||||
if len(parts) == 2 {
|
||||
*input = parts[0]
|
||||
val = parts[1]
|
||||
version, v.Metadata = cut(version, '+')
|
||||
if v.Metadata != "" && !reMetaIdentifier.MatchString(v.Metadata) {
|
||||
return v, fmt.Errorf("invalid metadata identifier: %s", v.Metadata)
|
||||
}
|
||||
|
||||
return val
|
||||
version, v.PreRelease = cut(version, '-')
|
||||
if v.PreRelease != "" && !reMetaIdentifier.MatchString(v.PreRelease) {
|
||||
return v, fmt.Errorf("invalid pre-release identifier: %s", v.PreRelease)
|
||||
}
|
||||
|
||||
if strings.Count(version, ".") != 2 {
|
||||
return v, fmt.Errorf("%s should contain major, minor, and patch versions", version)
|
||||
}
|
||||
|
||||
major, after := cut(version, '.')
|
||||
if v.Major, err = strconv.ParseInt(major, 10, 64); err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
minor, after := cut(after, '.')
|
||||
if v.Minor, err = strconv.ParseInt(minor, 10, 64); err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
if v.Patch, err = strconv.ParseInt(after, 10, 64); err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// NewVersion constructs new SemVers from strings
|
||||
func NewVersion(version string) (*Version, error) {
|
||||
v := Version{}
|
||||
|
||||
if err := v.Set(version); err != nil {
|
||||
return nil, err
|
||||
// MustParse is like Parse but panics if the version string is invalid instead of returning an error.
|
||||
func MustParse(version string) Version {
|
||||
v, err := Parse(version)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &v, nil
|
||||
return v
|
||||
}
|
||||
|
||||
// Set parses and updates v from the given version string. Implements flag.Value
|
||||
func (v *Version) Set(version string) error {
|
||||
metadata := splitOff(&version, "+")
|
||||
preRelease := PreRelease(splitOff(&version, "-"))
|
||||
dotParts := strings.SplitN(version, ".", 3)
|
||||
|
||||
if len(dotParts) != 3 {
|
||||
return fmt.Errorf("%s is not in dotted-tri format", version)
|
||||
}
|
||||
|
||||
if err := validateIdentifier(string(preRelease)); err != nil {
|
||||
return fmt.Errorf("failed to validate pre-release: %v", err)
|
||||
}
|
||||
|
||||
if err := validateIdentifier(metadata); err != nil {
|
||||
return fmt.Errorf("failed to validate metadata: %v", err)
|
||||
}
|
||||
|
||||
parsed := make([]int64, 3)
|
||||
|
||||
for i, v := range dotParts[:3] {
|
||||
val, err := strconv.ParseInt(v, 10, 64)
|
||||
parsed[i] = val
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
v.Metadata = metadata
|
||||
v.PreRelease = preRelease
|
||||
v.Major = parsed[0]
|
||||
v.Minor = parsed[1]
|
||||
v.Patch = parsed[2]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buffer, "%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
|
||||
if v.PreRelease != "" {
|
||||
fmt.Fprintf(&buffer, "-%s", v.PreRelease)
|
||||
}
|
||||
|
||||
if v.Metadata != "" {
|
||||
fmt.Fprintf(&buffer, "+%s", v.Metadata)
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Compare tests if v is less than, equal to, or greater than versionB,
|
||||
// returning -1, 0, or +1 respectively.
|
||||
func (v Version) Compare(versionB Version) int {
|
||||
if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return preReleaseCompare(v, versionB)
|
||||
}
|
||||
|
||||
// Slice converts the comparable parts of the semver into a slice of integers.
|
||||
func (v Version) Slice() []int64 {
|
||||
return []int64{v.Major, v.Minor, v.Patch}
|
||||
}
|
||||
|
||||
// Slice splits the pre-release suffix string
|
||||
func (p PreRelease) Slice() []string {
|
||||
preRelease := string(p)
|
||||
return strings.Split(preRelease, ".")
|
||||
}
|
||||
|
||||
func preReleaseCompare(versionA Version, versionB Version) int {
|
||||
a := versionA.PreRelease
|
||||
b := versionB.PreRelease
|
||||
|
||||
/* Handle the case where if two versions are otherwise equal it is the
|
||||
* one without a PreRelease that is greater */
|
||||
if len(a) == 0 && (len(b) > 0) {
|
||||
return 1
|
||||
} else if len(b) == 0 && (len(a) > 0) {
|
||||
return -1
|
||||
}
|
||||
|
||||
// If there is a prerelease, check and compare each part.
|
||||
return recursivePreReleaseCompare(a.Slice(), b.Slice())
|
||||
}
|
||||
|
||||
func recursiveCompare(versionA []int64, versionB []int64) int {
|
||||
if len(versionA) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
a := versionA[0]
|
||||
b := versionB[0]
|
||||
|
||||
if a > b {
|
||||
return 1
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
|
||||
return recursiveCompare(versionA[1:], versionB[1:])
|
||||
}
|
||||
|
||||
func recursivePreReleaseCompare(versionA []string, versionB []string) int {
|
||||
// A larger set of pre-release fields has a higher precedence than a smaller set,
|
||||
// if all of the preceding identifiers are equal.
|
||||
if len(versionA) == 0 {
|
||||
if len(versionB) > 0 {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
} else if len(versionB) == 0 {
|
||||
// We're longer than versionB so return 1.
|
||||
return 1
|
||||
}
|
||||
|
||||
a := versionA[0]
|
||||
b := versionB[0]
|
||||
|
||||
aInt := false
|
||||
bInt := false
|
||||
|
||||
aI, err := strconv.Atoi(versionA[0])
|
||||
if err == nil {
|
||||
aInt = true
|
||||
}
|
||||
|
||||
bI, err := strconv.Atoi(versionB[0])
|
||||
if err == nil {
|
||||
bInt = true
|
||||
}
|
||||
|
||||
// Numeric identifiers always have lower precedence than non-numeric identifiers.
|
||||
if aInt && !bInt {
|
||||
return -1
|
||||
} else if !aInt && bInt {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Handle Integer Comparison
|
||||
if aInt && bInt {
|
||||
if aI > bI {
|
||||
return 1
|
||||
} else if aI < bI {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// Handle String Comparison
|
||||
if a > b {
|
||||
return 1
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
|
||||
return recursivePreReleaseCompare(versionA[1:], versionB[1:])
|
||||
}
|
||||
|
||||
// validateIdentifier makes sure the provided identifier satisfies semver spec
|
||||
func validateIdentifier(id string) error {
|
||||
if id != "" && !reIdentifier.MatchString(id) {
|
||||
return fmt.Errorf("%s is not a valid semver identifier", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reIdentifier is a regular expression used to check that pre-release and metadata
|
||||
// identifiers satisfy the spec requirements
|
||||
var reIdentifier = regexp.MustCompile(`^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$`)
|
||||
|
||||
// Compare compares two semver strings.
|
||||
func Compare(a, b string) int {
|
||||
aV, err := NewVersion(strings.TrimPrefix(a, "v"))
|
||||
aV, err := Parse(a)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
bV, err := NewVersion(strings.TrimPrefix(b, "v"))
|
||||
bV, err := Parse(b)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
return aV.Compare(*bV)
|
||||
return aV.Compare(bV)
|
||||
}
|
||||
|
||||
// AppendText appends the textual representation of the version to b and returns the extended buffer.
|
||||
// This method conforms to the encoding.TextAppender interface, and is useful for serializing the Version
|
||||
// without allocating, provided the caller has pre-allocated sufficient space in b.
|
||||
func (v Version) AppendText(b []byte) ([]byte, error) {
|
||||
if b == nil {
|
||||
b = make([]byte, 0, length(v))
|
||||
}
|
||||
|
||||
b = append(strconv.AppendInt(b, v.Major, 10), '.')
|
||||
b = append(strconv.AppendInt(b, v.Minor, 10), '.')
|
||||
b = strconv.AppendInt(b, v.Patch, 10)
|
||||
|
||||
if v.PreRelease != "" {
|
||||
b = append(append(b, '-'), v.PreRelease...)
|
||||
}
|
||||
if v.Metadata != "" {
|
||||
b = append(append(b, '+'), v.Metadata...)
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String returns the string representation of the version.
|
||||
func (v Version) String() string {
|
||||
bs := make([]byte, 0, length(v))
|
||||
bs, _ = v.AppendText(bs)
|
||||
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
// Compare tests if v is less than, equal to, or greater than other, returning -1, 0, or +1 respectively.
|
||||
// Comparison is based on the SemVer specification (https://semver.org/#spec-item-11).
|
||||
func (v Version) Compare(other Version) int {
|
||||
if v.Major > other.Major {
|
||||
return 1
|
||||
} else if v.Major < other.Major {
|
||||
return -1
|
||||
}
|
||||
|
||||
if v.Minor > other.Minor {
|
||||
return 1
|
||||
} else if v.Minor < other.Minor {
|
||||
return -1
|
||||
}
|
||||
|
||||
if v.Patch > other.Patch {
|
||||
return 1
|
||||
} else if v.Patch < other.Patch {
|
||||
return -1
|
||||
}
|
||||
|
||||
if v.PreRelease == other.PreRelease {
|
||||
return 0
|
||||
}
|
||||
|
||||
// if two versions are otherwise equal it is the one without a pre-release that is greater
|
||||
if v.PreRelease == "" && other.PreRelease != "" {
|
||||
return 1
|
||||
}
|
||||
if other.PreRelease == "" && v.PreRelease != "" {
|
||||
return -1
|
||||
}
|
||||
|
||||
a, afterA := cut(v.PreRelease, '.')
|
||||
b, afterB := cut(other.PreRelease, '.')
|
||||
|
||||
for {
|
||||
if a == "" && b != "" {
|
||||
return -1
|
||||
}
|
||||
if a != "" && b == "" {
|
||||
return 1
|
||||
}
|
||||
|
||||
aIsInt := isAllDecimals(a)
|
||||
bIsInt := isAllDecimals(b)
|
||||
|
||||
// numeric identifiers have lower precedence than non-numeric
|
||||
if aIsInt && !bIsInt {
|
||||
return -1
|
||||
} else if !aIsInt && bIsInt {
|
||||
return 1
|
||||
}
|
||||
|
||||
if aIsInt && bIsInt {
|
||||
aInt, _ := strconv.Atoi(a)
|
||||
bInt, _ := strconv.Atoi(b)
|
||||
|
||||
if aInt > bInt {
|
||||
return 1
|
||||
} else if aInt < bInt {
|
||||
return -1
|
||||
}
|
||||
} else {
|
||||
// string comparison
|
||||
if a > b {
|
||||
return 1
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// a larger set of pre-release fields has a higher precedence than a
|
||||
// smaller set, if all of the preceding identifiers are equal.
|
||||
if afterA != "" && afterB == "" {
|
||||
return 1
|
||||
} else if afterA == "" && afterB != "" {
|
||||
return -1
|
||||
}
|
||||
|
||||
a, afterA = cut(afterA, '.')
|
||||
b, afterB = cut(afterB, '.')
|
||||
}
|
||||
}
|
||||
|
||||
func isAllDecimals(s string) bool {
|
||||
for _, r := range s {
|
||||
if r < '0' || r > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return s != ""
|
||||
}
|
||||
|
||||
// length allows calculating the length of the version for pre-allocation.
|
||||
func length(v Version) int {
|
||||
n := util.NumDigitsInt64(v.Major) + util.NumDigitsInt64(v.Minor) + util.NumDigitsInt64(v.Patch) + 2
|
||||
if v.PreRelease != "" {
|
||||
n += len(v.PreRelease) + 1
|
||||
}
|
||||
if v.Metadata != "" {
|
||||
n += len(v.Metadata) + 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// cut is a *slightly* faster version of strings.Cut only accepting
|
||||
// single byte separators, and skipping the boolean return value.
|
||||
func cut(s string, sep byte) (before, after string) {
|
||||
if i := strings.IndexByte(s, sep); i >= 0 {
|
||||
return s[:i], s[i+1:]
|
||||
}
|
||||
return s, ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user