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
|
||||
|
||||
// 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() ||
|
||||
|
||||
Reference in new issue
Block a user