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
// This file implements the static analysis deciding whether the accumulated
// value of a reduce expression may be updated in place; see
// compiler#compileReduce for how the result is used, and the comment on
// 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.
// Static analysis deciding whether the accumulated value of a reduce
// expression may be updated in place; see compiler#compileReduce. The checks
// are conservative: failing them only keeps the previous copying behavior.
// inPlaceUpdate reports whether the query is a single assignment applied to
// the input value, and returns its operands. Such an assignment receives the
// 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.
// the input value, and returns its operands.
func (e *Query) inPlaceUpdate() (l, r *Query, op Operator, ok bool) {
if e.Term != nil || len(e.FuncDefs) > 0 {
return nil, nil, 0, false
}
switch e.Op {
case OpModify:
// The update function is applied to the values found at the target
// 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.
// _modify releases the allocator before calling the update function.
return e.Left, e.Right, e.Op, true
case OpAssign:
// An assignment to a constant path is compiled to a setpath call,
// which does not use an allocator; see compiler#compileQueryUpdate.
// A constant path compiles to setpath, which uses no allocator.
if e.Left.toIndices(nil) != nil {
return nil, nil, 0, false
}
fallthrough
case OpUpdateAdd, OpUpdateSub, OpUpdateMul,
OpUpdateDiv, OpUpdateMod, OpUpdateAlt:
// The right-hand side is evaluated against the input value, so it must
// not observe it; the assigned value would otherwise be able to alias
// the value it is assigned into. It must not yield multiple values
// either, because the assignment then runs once per value, each time
// against an input value the previous run already modified.
// The right-hand side must neither observe the input value, which it
// could otherwise alias, nor yield multiple values, which would run
// the assignment against an already modified input.
if !e.Right.readsInput() && e.Right.singleValued() {
return e.Left, e.Right, e.Op, true
}
@@ -59,9 +43,7 @@ func (e *Query) readsInput() bool {
}
switch e.Op {
case OpPipe:
// The right-hand side is evaluated against the output of the
// left-hand side, unless the query binds variables, in which case it
// is evaluated against the input value as well.
// Binding variables also exposes the input to the right-hand side.
if len(e.Patterns) > 0 {
return e.Left.readsInput() || e.Right.readsInput()
}
@@ -118,8 +100,7 @@ func (e *Term) readsInput() bool {
case TermTypeUnary:
return e.Unary.Term.readsInput()
case TermTypeFunc:
// Only a variable reference is known not to read the input value; any
// function may be redefined, so its name tells us nothing.
// Any function may be redefined, so only $x is known not to read it.
return e.Func.Name[0] != '$' || len(e.Func.Args) > 0
case TermTypeQuery:
return e.Query.readsInput()
@@ -160,8 +141,7 @@ func (e *Term) singleValued() bool {
}
func (e *Suffix) readsInput() bool {
// Both iterating and the optional operator work on the value of the term,
// while an index is evaluated against the input value.
// Only an index is evaluated against the input value.
return e.Index != nil && e.Index.readsInput()
}
@@ -205,8 +185,7 @@ func (e *String) singleValued() bool {
func (e *ObjectKeyVal) readsInput() bool {
if e.Val == nil {
// The shorthand {foo} and {"foo"} read the input value, while {$foo}
// does not.
// {foo} reads the input value, {$foo} does not.
return e.Key == "" || e.Key[0] != '$'
}
if e.KeyString != nil && e.KeyString.readsInput() ||