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.
This commit is contained in:
+443
@@ -0,0 +1,443 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user