Single Fetcher.Fetch on the Guard that holds DenyPrefixes. skipTlsVerification is ignored. No 502. keys add writes the secret only to --out. Release binary has no -test-allow-prefix.
444 lines
9.3 KiB
Go
444 lines
9.3 KiB
Go
package gojq
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"reflect"
|
|
"sort"
|
|
)
|
|
|
|
func (env *env) execute(bc *Code, v any, vars ...any) Iter {
|
|
env.codes = bc.codes
|
|
env.codeinfos = bc.codeinfos
|
|
env.push(v)
|
|
for i := len(vars) - 1; i >= 0; i-- {
|
|
env.push(vars[i])
|
|
}
|
|
env.debugCodes()
|
|
return env
|
|
}
|
|
|
|
func (env *env) Next() (any, bool) {
|
|
var err error
|
|
pc, callpc, index := env.pc, len(env.codes)-1, -1
|
|
backtrack, hasCtx := env.backtrack, env.ctx != context.Background()
|
|
defer func() { env.pc, env.backtrack = pc, true }()
|
|
loop:
|
|
for ; pc < len(env.codes); pc++ {
|
|
env.debugState(pc, backtrack)
|
|
code := env.codes[pc]
|
|
if hasCtx {
|
|
select {
|
|
case <-env.ctx.Done():
|
|
pc, env.forks = len(env.codes), nil
|
|
return env.ctx.Err(), true
|
|
default:
|
|
}
|
|
}
|
|
switch code.op {
|
|
case opnop:
|
|
// nop
|
|
case oppush:
|
|
env.push(code.v)
|
|
case oppop:
|
|
env.pop()
|
|
case opdup:
|
|
v := env.pop()
|
|
env.push(v)
|
|
env.push(v)
|
|
case opconst:
|
|
env.pop()
|
|
env.push(code.v)
|
|
case opload:
|
|
env.push(env.values[env.index(code.v.([2]int))])
|
|
case opstore:
|
|
env.values[env.index(code.v.([2]int))] = env.pop()
|
|
case opobject:
|
|
if backtrack {
|
|
break loop
|
|
}
|
|
n := code.v.(int)
|
|
m := make(map[string]any, n)
|
|
for range n {
|
|
v, k := env.pop(), env.pop()
|
|
s, ok := k.(string)
|
|
if !ok {
|
|
err = &objectKeyNotStringError{k}
|
|
break loop
|
|
}
|
|
if _, ok := m[s]; !ok {
|
|
m[s] = v
|
|
}
|
|
}
|
|
env.push(m)
|
|
case opappend:
|
|
i := env.index(code.v.([2]int))
|
|
env.values[i] = append(env.values[i].([]any), env.pop())
|
|
case opfork:
|
|
if backtrack {
|
|
if err != nil {
|
|
break loop
|
|
}
|
|
pc, backtrack = code.v.(int), false
|
|
goto loop
|
|
}
|
|
env.pushfork(pc)
|
|
case opforktrybegin:
|
|
if backtrack {
|
|
if err == nil {
|
|
break loop
|
|
}
|
|
switch e := err.(type) {
|
|
case *tryEndError:
|
|
err = e.err
|
|
break loop
|
|
case *breakError, *HaltError:
|
|
break loop
|
|
case ValueError:
|
|
env.pop()
|
|
env.push(e.Value())
|
|
default:
|
|
env.pop()
|
|
env.push(err.Error())
|
|
}
|
|
pc, backtrack, err = code.v.(int), false, nil
|
|
goto loop
|
|
}
|
|
env.pushfork(pc)
|
|
case opforktryend:
|
|
if backtrack {
|
|
if err != nil {
|
|
err = &tryEndError{err}
|
|
}
|
|
break loop
|
|
}
|
|
env.pushfork(pc)
|
|
case opforkalt:
|
|
if backtrack {
|
|
if err == nil {
|
|
break loop
|
|
}
|
|
pc, backtrack, err = code.v.(int), false, nil
|
|
goto loop
|
|
}
|
|
env.pushfork(pc)
|
|
case opforklabel:
|
|
if backtrack {
|
|
label := env.pop()
|
|
if e, ok := err.(*breakError); ok && e.v == label {
|
|
err = nil
|
|
}
|
|
break loop
|
|
}
|
|
env.push(env.label)
|
|
env.pushfork(pc)
|
|
env.pop()
|
|
env.values[env.index(code.v.([2]int))] = env.label
|
|
env.label++
|
|
case opbacktrack:
|
|
break loop
|
|
case opjump:
|
|
pc = code.v.(int)
|
|
goto loop
|
|
case opjumpifnot:
|
|
if v := env.pop(); v == nil || v == false {
|
|
pc = code.v.(int)
|
|
goto loop
|
|
}
|
|
case opindex, opindexarray:
|
|
if backtrack {
|
|
break loop
|
|
}
|
|
p, v := code.v, env.pop()
|
|
if code.op == opindexarray && v != nil {
|
|
if _, ok := v.([]any); !ok {
|
|
err = &expectedArrayError{v}
|
|
break loop
|
|
}
|
|
}
|
|
w := funcIndex2(nil, v, p)
|
|
if e, ok := w.(error); ok {
|
|
err = e
|
|
break loop
|
|
}
|
|
env.push(w)
|
|
if !env.paths.empty() && env.expdepth == 0 {
|
|
if !env.pathIntact(v) {
|
|
err = &invalidPathError{v}
|
|
break loop
|
|
}
|
|
env.paths.push(pathValue{path: p, value: w})
|
|
}
|
|
case opcall:
|
|
if backtrack {
|
|
break loop
|
|
}
|
|
switch v := code.v.(type) {
|
|
case int:
|
|
pc, callpc, index = v, pc, env.scopes.index
|
|
goto loop
|
|
case [3]any:
|
|
argcnt := v[1].(int)
|
|
x, args := env.pop(), env.args[:argcnt]
|
|
for i := range argcnt {
|
|
args[i] = env.pop()
|
|
}
|
|
w := v[0].(func(any, []any) any)(x, args)
|
|
if e, ok := w.(error); ok {
|
|
err = e
|
|
break loop
|
|
}
|
|
env.push(w)
|
|
if !env.paths.empty() && env.expdepth == 0 {
|
|
switch v[2].(string) {
|
|
case "_index":
|
|
if x = args[0]; !env.pathIntact(x) {
|
|
err = &invalidPathError{x}
|
|
break loop
|
|
}
|
|
env.paths.push(pathValue{path: args[1], value: w})
|
|
case "_slice":
|
|
if x = args[0]; !env.pathIntact(x) {
|
|
err = &invalidPathError{x}
|
|
break loop
|
|
}
|
|
env.paths.push(pathValue{
|
|
path: map[string]any{"start": args[2], "end": args[1]},
|
|
value: w,
|
|
})
|
|
case "getpath":
|
|
if !env.pathIntact(x) {
|
|
err = &invalidPathError{x}
|
|
break loop
|
|
}
|
|
for _, p := range args[0].([]any) {
|
|
env.paths.push(pathValue{path: p, value: w})
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
panic(v)
|
|
}
|
|
case opcallrec:
|
|
pc, callpc, index = code.v.(int), -1, env.scopes.index
|
|
goto loop
|
|
case oppushpc:
|
|
env.push([2]int{code.v.(int), env.scopes.index})
|
|
case opcallpc:
|
|
xs := env.pop().([2]int)
|
|
pc, callpc, index = xs[0], pc, xs[1]
|
|
goto loop
|
|
case opscope:
|
|
xs := code.v.([3]int)
|
|
var saveindex, outerindex int
|
|
if index == env.scopes.index {
|
|
if callpc >= 0 {
|
|
saveindex = index
|
|
} else {
|
|
callpc, saveindex = env.popscope()
|
|
}
|
|
} else {
|
|
saveindex = env.scopes.index
|
|
}
|
|
if outerindex = index; outerindex >= 0 {
|
|
if s := env.scopes.data[outerindex].value; s.id == xs[0] {
|
|
outerindex = s.outerindex
|
|
}
|
|
}
|
|
env.scopes.push(scope{xs[0], env.offset, callpc, saveindex, outerindex})
|
|
env.offset += xs[1]
|
|
if env.offset > len(env.values) {
|
|
vs := make([]any, env.offset*2)
|
|
copy(vs, env.values)
|
|
env.values = vs
|
|
}
|
|
case opret:
|
|
if backtrack {
|
|
break loop
|
|
}
|
|
pc, env.scopes.index = env.popscope()
|
|
if env.scopes.empty() {
|
|
return env.pop(), true
|
|
}
|
|
case opiter:
|
|
if err != nil {
|
|
break loop
|
|
}
|
|
backtrack = false
|
|
var xs []pathValue
|
|
switch v := env.pop().(type) {
|
|
case []pathValue:
|
|
xs = v
|
|
case []any:
|
|
if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) {
|
|
err = &invalidPathIterError{v}
|
|
break loop
|
|
}
|
|
if len(v) == 0 {
|
|
break loop
|
|
}
|
|
xs = make([]pathValue, len(v))
|
|
for i, v := range v {
|
|
xs[i] = pathValue{path: i, value: v}
|
|
}
|
|
case map[string]any:
|
|
if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) {
|
|
err = &invalidPathIterError{v}
|
|
break loop
|
|
}
|
|
if len(v) == 0 {
|
|
break loop
|
|
}
|
|
xs = make([]pathValue, len(v))
|
|
var i int
|
|
for k, v := range v {
|
|
xs[i] = pathValue{path: k, value: v}
|
|
i++
|
|
}
|
|
sort.Slice(xs, func(i, j int) bool {
|
|
return xs[i].path.(string) < xs[j].path.(string)
|
|
})
|
|
case Iter:
|
|
if w, ok := v.Next(); ok {
|
|
env.push(v)
|
|
env.pushfork(pc)
|
|
env.pop()
|
|
if e, ok := w.(error); ok {
|
|
err = e
|
|
break loop
|
|
}
|
|
env.push(w)
|
|
continue
|
|
}
|
|
break loop
|
|
default:
|
|
err = &iteratorError{v}
|
|
env.push(emptyIter{})
|
|
break loop
|
|
}
|
|
if len(xs) > 1 {
|
|
env.push(xs[1:])
|
|
env.pushfork(pc)
|
|
env.pop()
|
|
}
|
|
env.push(xs[0].value)
|
|
if !env.paths.empty() && env.expdepth == 0 {
|
|
env.paths.push(xs[0])
|
|
}
|
|
case opexpbegin:
|
|
env.expdepth++
|
|
case opexpend:
|
|
env.expdepth--
|
|
case oppathbegin:
|
|
env.paths.push(env.expdepth)
|
|
env.paths.push(pathValue{value: env.stack.top()})
|
|
env.expdepth = 0
|
|
case oppathend:
|
|
if backtrack {
|
|
break loop
|
|
}
|
|
env.pop()
|
|
if v := env.pop(); !env.pathIntact(v) {
|
|
err = &invalidPathError{v}
|
|
break loop
|
|
}
|
|
env.push(env.poppaths())
|
|
env.expdepth = env.paths.pop().(int)
|
|
default:
|
|
panic(code.op)
|
|
}
|
|
}
|
|
if len(env.forks) > 0 {
|
|
pc, backtrack = env.popfork(), true
|
|
goto loop
|
|
}
|
|
if err != nil {
|
|
return err, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func (env *env) push(v any) {
|
|
env.stack.push(v)
|
|
}
|
|
|
|
func (env *env) pop() any {
|
|
return env.stack.pop()
|
|
}
|
|
|
|
func (env *env) popscope() (int, int) {
|
|
free := env.scopes.index > env.scopes.limit
|
|
s := env.scopes.pop()
|
|
if free {
|
|
env.offset = s.offset
|
|
}
|
|
return s.pc, s.saveindex
|
|
}
|
|
|
|
func (env *env) pushfork(pc int) {
|
|
f := fork{pc: pc, offset: env.offset, expdepth: env.expdepth}
|
|
f.stackindex, f.stacklimit = env.stack.save()
|
|
f.scopeindex, f.scopelimit = env.scopes.save()
|
|
f.pathindex, f.pathlimit = env.paths.save()
|
|
env.forks = append(env.forks, f)
|
|
env.debugForks(pc, ">>>")
|
|
}
|
|
|
|
func (env *env) popfork() int {
|
|
f := env.forks[len(env.forks)-1]
|
|
env.debugForks(f.pc, "<<<")
|
|
env.forks, env.offset, env.expdepth =
|
|
env.forks[:len(env.forks)-1], f.offset, f.expdepth
|
|
env.stack.restore(f.stackindex, f.stacklimit)
|
|
env.scopes.restore(f.scopeindex, f.scopelimit)
|
|
env.paths.restore(f.pathindex, f.pathlimit)
|
|
return f.pc
|
|
}
|
|
|
|
func (env *env) index(v [2]int) int {
|
|
for id, i := v[0], env.scopes.index; i >= 0; {
|
|
s := env.scopes.data[i].value
|
|
if s.id == id {
|
|
return s.offset + v[1]
|
|
}
|
|
i = s.outerindex
|
|
}
|
|
panic("env.index")
|
|
}
|
|
|
|
type pathValue struct {
|
|
path, value any
|
|
}
|
|
|
|
func (env *env) pathIntact(v any) bool {
|
|
w := env.paths.top().(pathValue).value
|
|
switch v := v.(type) {
|
|
case []any, map[string]any:
|
|
switch w.(type) {
|
|
case []any, map[string]any:
|
|
v, w := reflect.ValueOf(v), reflect.ValueOf(w)
|
|
return v.Pointer() == w.Pointer() && v.Len() == w.Len()
|
|
}
|
|
case float64:
|
|
if w, ok := w.(float64); ok {
|
|
return v == w || math.IsNaN(v) && math.IsNaN(w)
|
|
}
|
|
}
|
|
return v == w
|
|
}
|
|
|
|
func (env *env) poppaths() []any {
|
|
xs := []any{}
|
|
for {
|
|
p := env.paths.pop().(pathValue)
|
|
if p.path == nil {
|
|
break
|
|
}
|
|
xs = append(xs, p.path)
|
|
}
|
|
for i, j := 0, len(xs)-1; i < j; i, j = i+1, j-1 {
|
|
xs[i], xs[j] = xs[j], xs[i]
|
|
}
|
|
return xs
|
|
}
|