Files
kyle 9444a35322 p1: /v2/scrape, /v1/fetch, auth, CLI, contract tests
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.
2026-09-22 21:54:01 -07:00

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
}