From 50879152664d740b67f9dcdd19463bcb164ea54a Mon Sep 17 00:00:00 2001 From: upstream Date: Mon, 14 Sep 2026 07:37:56 +0200 Subject: [PATCH] Upstream itchyny/gojq state of the touched files before 6352bdeffe --- inplace.go | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/inplace.go b/inplace.go index 6a401e7..442babb 100644 --- a/inplace.go +++ b/inplace.go @@ -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() ||