Upstream itchyny/gojq state of the touched files before 6352bdeffe

This commit is contained in:
upstream committed 2026-09-14 07:37:56 +02:00
1 parent 0a32e14537
commit 5087915266
1 file changed
+13 -34
+13 -34
View File
@@ -1,46 +1,30 @@
package gojq package gojq
// This file implements the static analysis deciding whether the accumulated // Static analysis deciding whether the accumulated value of a reduce
// value of a reduce expression may be updated in place; see // expression may be updated in place; see compiler#compileReduce. The checks
// compiler#compileReduce for how the result is used, and the comment on // are conservative: failing them only keeps the previous copying behavior.
// compiler#compileAssign for why an allocator makes assignments cheap.
//
// Reusing an allocator across the iterations of a reduce expression is only
// correct while the accumulated value, and the values the allocator created
// inside it, are reachable through the accumulated value alone. The checks
// below are deliberately conservative; failing them only means the assignment
// keeps copying the accumulated value, which is the previous behavior.
// inPlaceUpdate reports whether the query is a single assignment applied to // inPlaceUpdate reports whether the query is a single assignment applied to
// the input value, and returns its operands. Such an assignment receives the // the input value, and returns its operands.
// accumulated value of the enclosing reduce expression, and passes it on as
// the next accumulated value, without any other part of the query observing
// it in between.
func (e *Query) inPlaceUpdate() (l, r *Query, op Operator, ok bool) { func (e *Query) inPlaceUpdate() (l, r *Query, op Operator, ok bool) {
if e.Term != nil || len(e.FuncDefs) > 0 { if e.Term != nil || len(e.FuncDefs) > 0 {
return nil, nil, 0, false return nil, nil, 0, false
} }
switch e.Op { switch e.Op {
case OpModify: case OpModify:
// The update function is applied to the values found at the target // _modify releases the allocator before calling the update function.
// paths, so it never observes the input value itself. When a target
// path points to a value the allocator created, _modify releases the
// allocator before calling the update function.
return e.Left, e.Right, e.Op, true return e.Left, e.Right, e.Op, true
case OpAssign: case OpAssign:
// An assignment to a constant path is compiled to a setpath call, // A constant path compiles to setpath, which uses no allocator.
// which does not use an allocator; see compiler#compileQueryUpdate.
if e.Left.toIndices(nil) != nil { if e.Left.toIndices(nil) != nil {
return nil, nil, 0, false return nil, nil, 0, false
} }
fallthrough fallthrough
case OpUpdateAdd, OpUpdateSub, OpUpdateMul, case OpUpdateAdd, OpUpdateSub, OpUpdateMul,
OpUpdateDiv, OpUpdateMod, OpUpdateAlt: OpUpdateDiv, OpUpdateMod, OpUpdateAlt:
// The right-hand side is evaluated against the input value, so it must // The right-hand side must neither observe the input value, which it
// not observe it; the assigned value would otherwise be able to alias // could otherwise alias, nor yield multiple values, which would run
// the value it is assigned into. It must not yield multiple values // the assignment against an already modified input.
// either, because the assignment then runs once per value, each time
// against an input value the previous run already modified.
if !e.Right.readsInput() && e.Right.singleValued() { if !e.Right.readsInput() && e.Right.singleValued() {
return e.Left, e.Right, e.Op, true return e.Left, e.Right, e.Op, true
} }
@@ -59,9 +43,7 @@ func (e *Query) readsInput() bool {
} }
switch e.Op { switch e.Op {
case OpPipe: case OpPipe:
// The right-hand side is evaluated against the output of the // Binding variables also exposes the input to the right-hand side.
// left-hand side, unless the query binds variables, in which case it
// is evaluated against the input value as well.
if len(e.Patterns) > 0 { if len(e.Patterns) > 0 {
return e.Left.readsInput() || e.Right.readsInput() return e.Left.readsInput() || e.Right.readsInput()
} }
@@ -118,8 +100,7 @@ func (e *Term) readsInput() bool {
case TermTypeUnary: case TermTypeUnary:
return e.Unary.Term.readsInput() return e.Unary.Term.readsInput()
case TermTypeFunc: case TermTypeFunc:
// Only a variable reference is known not to read the input value; any // Any function may be redefined, so only $x is known not to read it.
// function may be redefined, so its name tells us nothing.
return e.Func.Name[0] != '$' || len(e.Func.Args) > 0 return e.Func.Name[0] != '$' || len(e.Func.Args) > 0
case TermTypeQuery: case TermTypeQuery:
return e.Query.readsInput() return e.Query.readsInput()
@@ -160,8 +141,7 @@ func (e *Term) singleValued() bool {
} }
func (e *Suffix) readsInput() bool { func (e *Suffix) readsInput() bool {
// Both iterating and the optional operator work on the value of the term, // Only an index is evaluated against the input value.
// while an index is evaluated against the input value.
return e.Index != nil && e.Index.readsInput() return e.Index != nil && e.Index.readsInput()
} }
@@ -205,8 +185,7 @@ func (e *String) singleValued() bool {
func (e *ObjectKeyVal) readsInput() bool { func (e *ObjectKeyVal) readsInput() bool {
if e.Val == nil { if e.Val == nil {
// The shorthand {foo} and {"foo"} read the input value, while {$foo} // {foo} reads the input value, {$foo} does not.
// does not.
return e.Key == "" || e.Key[0] != '$' return e.Key == "" || e.Key[0] != '$'
} }
if e.KeyString != nil && e.KeyString.readsInput() || if e.KeyString != nil && e.KeyString.readsInput() ||