Skip to content

Commit

Permalink
ruleguard/quasigo: pass args via ValueStack (#377)
Browse files Browse the repository at this point in the history
It makes it easier to implement quasigo funcs call
later on, so Go->quasigo and quasigo->quasigo can use
the same calling convention.

Note that args can be bound once (until env.Stack.Reset),
which is useful in plugins-like context when you pass
the same argument list to all user functions.
  • Loading branch information
quasilyte authored Feb 13, 2022
1 parent 6aa060f commit dbd4b2c
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 77 deletions.
2 changes: 1 addition & 1 deletion ruleguard/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func makeCustomVarFilter(src, varname string, fn *quasigo.Func) filterFunc {
// We should probably catch the panic here, print trace and return "false"
// from the filter (or even propagate that panic to let it crash).
params.varname = varname
result := quasigo.Call(params.env, fn, params)
result := quasigo.Call(params.env, fn)
if result.Value().(bool) {
return filterSuccess
}
Expand Down
45 changes: 36 additions & 9 deletions ruleguard/quasigo/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ type compiler struct {
locals map[string]int
constantsPool map[interface{}]int
intConstantsPool map[int]int
params map[string]int

params map[string]int
intParams map[string]int

code []byte
constants []interface{}
Expand Down Expand Up @@ -89,20 +91,31 @@ func (cl *compiler) compileFunc(fn *ast.FuncDecl) *Func {
panic(cl.errorUnsupportedType(fn.Name, cl.retType, "function result"))
}

dbg := funcDebugInfo{
paramNames: make([]string, cl.fnType.Params().Len()),
}

cl.params = make(map[string]int, cl.fnType.Params().Len())
cl.intParams = make(map[string]int, cl.fnType.Params().Len())
for i := 0; i < cl.fnType.Params().Len(); i++ {
p := cl.fnType.Params().At(i)
paramName := p.Name()
paramType := p.Type()
cl.params[paramName] = i
dbg.paramNames[i] = paramName
if !cl.isSupportedType(paramType) {
panic(cl.errorUnsupportedType(fn.Name, paramType, paramName+" param"))
}
if typeIsInt(paramType) {
cl.intParams[paramName] = len(cl.intParams)
} else {
cl.params[paramName] = len(cl.params)
}
}

dbg := funcDebugInfo{
paramNames: make([]string, len(cl.params)),
intParamNames: make([]string, len(cl.intParams)),
}
for paramName, i := range cl.params {
dbg.paramNames[i] = paramName
}
for paramName, i := range cl.intParams {
dbg.intParamNames[i] = paramName
}

cl.compileStmt(fn.Body)
Expand Down Expand Up @@ -298,10 +311,20 @@ func (cl *compiler) compileAssignStmt(assign *ast.AssignStmt) {
}
}

func (cl *compiler) isParamName(varname string) bool {
if _, ok := cl.params[varname]; ok {
return true
}
if _, ok := cl.intParams[varname]; ok {
return true
}
return false
}

func (cl *compiler) getLocal(v ast.Expr, varname string) int {
id, ok := cl.locals[varname]
if !ok {
if _, ok := cl.params[varname]; ok {
if cl.isParamName(varname) {
panic(cl.errorf(v, "can't assign to %s, params are readonly", varname))
}
panic(cl.errorf(v, "%s is not a writeable local variable", varname))
Expand Down Expand Up @@ -645,7 +668,11 @@ func (cl *compiler) compileIdent(ident *ast.Ident) {
return
}
if paramIndex, ok := cl.params[ident.String()]; ok {
cl.emit8(pickOp(typeIsInt(tv.Type), opPushIntParam, opPushParam), paramIndex)
cl.emit8(opPushParam, paramIndex)
return
}
if paramIndex, ok := cl.intParams[ident.String()]; ok {
cl.emit8(opPushIntParam, paramIndex)
return
}
if localIndex, ok := cl.locals[ident.String()]; ok {
Expand Down
30 changes: 15 additions & 15 deletions ruleguard/quasigo/compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestCompile(t *testing.T) {
},

`return b`: {
` PushParam 2 # b`,
` PushParam 1 # b`,
` ReturnTop`,
},

Expand Down Expand Up @@ -55,7 +55,7 @@ func TestCompile(t *testing.T) {
` EqInt`,
` Dup`,
` JumpFalse 8 # L0`,
` PushParam 1 # s`,
` PushParam 0 # s`,
` PushConst 0 # value="foo"`,
` EqString`,
`L0:`,
Expand All @@ -81,7 +81,7 @@ func TestCompile(t *testing.T) {
},

`if b { return 1 }; return 0`: {
` PushParam 2 # b`,
` PushParam 1 # b`,
` JumpFalse 6 # L0`,
` PushIntConst 0 # value=1`,
` ReturnIntTop`,
Expand All @@ -91,7 +91,7 @@ func TestCompile(t *testing.T) {
},

`if b { return 1 } else { return 0 }`: {
` PushParam 2 # b`,
` PushParam 1 # b`,
` JumpFalse 6 # L0`,
` PushIntConst 0 # value=1`,
` ReturnIntTop`,
Expand All @@ -103,7 +103,7 @@ func TestCompile(t *testing.T) {
`x := 0; if b { x = 5 } else { x = 50 }; return x`: {
` PushIntConst 0 # value=0`,
` SetIntLocal 0 # x`,
` PushParam 2 # b`,
` PushParam 1 # b`,
` JumpFalse 10 # L0`,
` PushIntConst 1 # value=5`,
` SetIntLocal 0 # x`,
Expand All @@ -124,7 +124,7 @@ func TestCompile(t *testing.T) {
` PushConst 0 # value="a"`,
` ReturnTop`,
`L0:`,
` PushParam 2 # b`,
` PushParam 1 # b`,
` JumpFalse 6 # L1`,
` PushConst 1 # value="b"`,
` ReturnTop`,
Expand All @@ -134,58 +134,58 @@ func TestCompile(t *testing.T) {
},

`return eface == nil`: {
` PushParam 3 # eface`,
` PushParam 2 # eface`,
` IsNil`,
` ReturnTop`,
},

`return nil == eface`: {
` PushParam 3 # eface`,
` PushParam 2 # eface`,
` IsNil`,
` ReturnTop`,
},

`return eface != nil`: {
` PushParam 3 # eface`,
` PushParam 2 # eface`,
` IsNotNil`,
` ReturnTop`,
},

`return nil != eface`: {
` PushParam 3 # eface`,
` PushParam 2 # eface`,
` IsNotNil`,
` ReturnTop`,
},

`return s[:]`: {
` PushParam 1 # s`,
` PushParam 0 # s`,
` ReturnTop`,
},

`return s[1:]`: {
` PushParam 1 # s`,
` PushParam 0 # s`,
` PushIntConst 0 # value=1`,
` StringSliceFrom`,
` ReturnTop`,
},

`return s[:1]`: {
` PushParam 1 # s`,
` PushParam 0 # s`,
` PushIntConst 0 # value=1`,
` StringSliceTo`,
` ReturnTop`,
},

`return s[1:2]`: {
` PushParam 1 # s`,
` PushParam 0 # s`,
` PushIntConst 0 # value=1`,
` PushIntConst 1 # value=2`,
` StringSlice`,
` ReturnTop`,
},

`return len(s) >= 0`: {
` PushParam 1 # s`,
` PushParam 0 # s`,
` StringLen`,
` PushIntConst 0 # value=0`,
` GtEqInt`,
Expand Down
5 changes: 3 additions & 2 deletions ruleguard/quasigo/debug_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ type debugInfo struct {
}

type funcDebugInfo struct {
paramNames []string
localNames []string
paramNames []string
intParamNames []string
localNames []string
}

func newDebugInfo() *debugInfo {
Expand Down
6 changes: 5 additions & 1 deletion ruleguard/quasigo/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ func disasm(env *Env, fn *Func) string {
id := decode16(code, pc+1)
arg = id
comment = env.nativeFuncs[id].name
case opPushParam, opPushIntParam:
case opPushParam:
index := int(code[pc+1])
arg = index
comment = dbg.paramNames[index]
case opPushIntParam:
index := int(code[pc+1])
arg = index
comment = dbg.intParamNames[index]
case opSetLocal, opSetIntLocal, opPushLocal, opPushIntLocal, opIncLocal, opDecLocal:
index := int(code[pc+1])
arg = index
Expand Down
12 changes: 6 additions & 6 deletions ruleguard/quasigo/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,22 @@ func (s *ValueStack) dup() { s.objects = append(s.objects, s.objects[len(s.objec
// Identical to s.Pop() without using the result.
func (s *ValueStack) discard() { s.objects = s.objects[:len(s.objects)-1] }

func eval(env *EvalEnv, fn *Func, args []interface{}) CallResult {
func eval(env *EvalEnv, fn *Func, top, intTop int) CallResult {
pc := 0
code := fn.code
stack := env.stack
stack := &env.Stack
var locals [maxFuncLocals]interface{}
var intLocals [maxFuncLocals]int

for {
switch op := opcode(code[pc]); op {
case opPushParam:
index := code[pc+1]
stack.Push(args[index])
index := int(code[pc+1])
stack.Push(stack.objects[top+index])
pc += 2
case opPushIntParam:
index := code[pc+1]
stack.PushInt(args[index].(int))
index := int(code[pc+1])
stack.PushInt(stack.ints[intTop+index])
pc += 2

case opPushLocal:
Expand Down
Loading

0 comments on commit dbd4b2c

Please sign in to comment.