Upstream itchyny/gojq state of the touched files before 6352bdeffe
This commit is contained in:
1 parent
0a32e14537
commit
5087915266
1 file changed
+13
-34
+13
-34
@@ -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() ||
|
||||||
|
|||||||
Reference in new issue
Block a user