Skip to content

Commit

Permalink
proc: refactor identifier evaluation for range-over-func support (go-…
Browse files Browse the repository at this point in the history
…delve#3738)

Because of how range-over-func is implemented it is difficult to
determine the set of visible local variables during expression
compilation (i.e. it is difficulto to keep the HasLocal function
correct).
This commit moves that logic from expression compilation to expression
evaluation.

Updates go-delve#3733
  • Loading branch information
aarzilli authored Jun 14, 2024
1 parent cce54c0 commit 4b628b8
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 196 deletions.
265 changes: 128 additions & 137 deletions pkg/proc/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,81 +203,6 @@ func (s scopeToEvalLookup) FindTypeExpr(expr ast.Expr) (godwarf.Type, error) {
return s.BinInfo.findTypeExpr(expr)
}

func (scope scopeToEvalLookup) HasLocal(name string) bool {
if scope.Fn == nil {
return false
}

flags := reader.VariablesOnlyVisible
if scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 15) {
flags |= reader.VariablesTrustDeclLine
}

dwarfTree, err := scope.image().getDwarfTree(scope.Fn.offset)
if err != nil {
return false
}

varEntries := reader.Variables(dwarfTree, scope.PC, scope.Line, flags)
for _, entry := range varEntries {
curname, _ := entry.Val(dwarf.AttrName).(string)
if curname == name {
return true
}
if len(curname) > 0 && curname[0] == '&' {
if curname[1:] == name {
return true
}
}
}
return false
}

func (scope scopeToEvalLookup) HasGlobal(pkgName, varName string) bool {
hasGlobalInternal := func(name string) bool {
for _, pkgvar := range scope.BinInfo.packageVars {
if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
return true
}
}
for _, fn := range scope.BinInfo.Functions {
if fn.Name == name || strings.HasSuffix(fn.Name, "/"+name) {
return true
}
}
for _, ctyp := range scope.BinInfo.consts {
for _, cval := range ctyp.values {
if cval.fullName == name || strings.HasSuffix(cval.fullName, "/"+name) {
return true
}
}
}
return false
}

if pkgName == "" {
if scope.Fn == nil {
return false
}
return hasGlobalInternal(scope.Fn.PackageName() + "." + varName)
}

for _, pkgPath := range scope.BinInfo.PackageMap[pkgName] {
if hasGlobalInternal(pkgPath + "." + varName) {
return true
}
}
return hasGlobalInternal(pkgName + "." + varName)
}

func (scope scopeToEvalLookup) LookupRegisterName(name string) (int, bool) {
s := validRegisterName(name)
if s == "" {
return 0, false
}
return scope.BinInfo.Arch.RegisterNameToDwarf(s)
}

func (scope scopeToEvalLookup) HasBuiltin(name string) bool {
return supportedBuiltins[name] != nil
}
Expand Down Expand Up @@ -680,7 +605,20 @@ func (scope *EvalScope) findGlobal(pkgName, varName string) (*Variable, error) {
if err != nil || v != nil {
return v, err
}
return nil, fmt.Errorf("could not find symbol value for %s.%s", pkgName, varName)
return nil, &errCouldNotFindSymbol{fmt.Sprintf("%s.%s", pkgName, varName)}
}

type errCouldNotFindSymbol struct {
name string
}

func (e *errCouldNotFindSymbol) Error() string {
return fmt.Sprintf("could not find symbol %s", e.name)
}

func isSymbolNotFound(e error) bool {
var e2 *errCouldNotFindSymbol
return errors.As(e, &e2)
}

func (scope *EvalScope) findGlobalInternal(name string) (*Variable, error) {
Expand Down Expand Up @@ -967,83 +905,44 @@ func (stack *evalStack) executeOp() {
stack.push(newConstant(op.Value, scope.Mem))

case *evalop.PushLocal:
var vars []*Variable
var err error
if op.Frame != 0 {
frameScope, err2 := ConvertEvalScope(scope.target, -1, int(op.Frame), 0)
if err2 != nil {
stack.err = err2
return
}
vars, err = frameScope.Locals(0)
} else {
vars, err = scope.Locals(0)
}
if err != nil {
stack.err = err
return
}
found := false
for i := range vars {
if vars[i].Name == op.Name && vars[i].Flags&VariableShadowed == 0 {
stack.push(vars[i])
found = true
break
}
}
found := stack.pushLocal(scope, op.Name, op.Frame)
if !found {
stack.err = fmt.Errorf("could not find symbol value for %s", op.Name)
}

case *evalop.PushNil:
stack.push(nilVariable)

case *evalop.PushRegister:
reg := scope.Regs.Reg(uint64(op.Regnum))
if reg == nil {
stack.err = fmt.Errorf("could not find symbol value for %s", op.Regname)
case *evalop.PushPackageVarOrSelect:
v, err := scope.findGlobal(op.Name, op.Sel)
if err != nil && !isSymbolNotFound(err) {
stack.err = err
return
}
reg.FillBytes()

var typ godwarf.Type
if len(reg.Bytes) <= 8 {
typ = godwarf.FakeBasicType("uint", 64)
if v != nil {
stack.push(v)
} else {
var err error
typ, err = scope.BinInfo.findType("string")
if err != nil {
stack.err = err
if op.NameIsString {
stack.err = fmt.Errorf("%q (type string) is not a struct", op.Name)
return
}
}
found := stack.pushIdent(scope, op.Name)
if stack.err != nil {
return
}
if found {
scope.evalStructSelector(&evalop.Select{Name: op.Sel}, stack)
} else {
stack.err = fmt.Errorf("could not find symbol value for %s", op.Name)
}

v := newVariable(op.Regname, 0, typ, scope.BinInfo, scope.Mem)
if v.Kind == reflect.String {
v.Len = int64(len(reg.Bytes) * 2)
v.Base = fakeAddressUnresolv
}
v.Addr = fakeAddressUnresolv
v.Flags = VariableCPURegister
v.reg = reg
stack.push(v)

case *evalop.PushPackageVar:
pkgName := op.PkgName
replaceName := false
if pkgName == "" {
replaceName = true
pkgName = scope.Fn.PackageName()
}
v, err := scope.findGlobal(pkgName, op.Name)
if err != nil {
stack.err = err
return
}
if replaceName {
v.Name = op.Name
case *evalop.PushIdent:
found := stack.pushIdent(scope, op.Name)
if !found {
stack.err = fmt.Errorf("could not find symbol value for %s", op.Name)
}
stack.push(v)

case *evalop.PushLen:
v := stack.peek()
Expand Down Expand Up @@ -1134,6 +1033,98 @@ func (stack *evalStack) executeOp() {
stack.opidx++
}

func (stack *evalStack) pushLocal(scope *EvalScope, name string, frame int64) (found bool) {
var vars []*Variable
var err error
if frame != 0 {
frameScope, err2 := ConvertEvalScope(scope.target, -1, int(frame), 0)
if err2 != nil {
stack.err = err2
return
}
vars, err = frameScope.Locals(0)
} else {
vars, err = scope.Locals(0)
}
if err != nil {
stack.err = err
return
}
found = false
for i := range vars {
if vars[i].Name == name && vars[i].Flags&VariableShadowed == 0 {
stack.push(vars[i])
found = true
break
}
}
return found
}

func (stack *evalStack) pushIdent(scope *EvalScope, name string) (found bool) {
found = stack.pushLocal(scope, name, 0)
if found || stack.err != nil {
return found
}
v, err := scope.findGlobal(scope.Fn.PackageName(), name)
if err != nil && !isSymbolNotFound(err) {
stack.err = err
return false
}
if v != nil {
v.Name = name
stack.push(v)
return true
}

switch name {
case "true", "false":
stack.push(newConstant(constant.MakeBool(name == "true"), scope.Mem))
return true
case "nil":
stack.push(nilVariable)
return true
}

regname := validRegisterName(name)
if regname == "" {
return false
}
regnum, ok := scope.BinInfo.Arch.RegisterNameToDwarf(regname)
if !ok {
return false
}

reg := scope.Regs.Reg(uint64(regnum))
if reg == nil {
return
}
reg.FillBytes()

var typ godwarf.Type
if len(reg.Bytes) <= 8 {
typ = godwarf.FakeBasicType("uint", 64)
} else {
var err error
typ, err = scope.BinInfo.findType("string")
if err != nil {
stack.err = err
return false
}
}

v = newVariable(regname, 0, typ, scope.BinInfo, scope.Mem)
if v.Kind == reflect.String {
v.Len = int64(len(reg.Bytes) * 2)
v.Base = fakeAddressUnresolv
}
v.Addr = fakeAddressUnresolv
v.Flags = VariableCPURegister
v.reg = reg
stack.push(v)
return true
}

func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
ops, err := evalop.CompileAST(scopeToEvalLookup{scope}, t)
if err != nil {
Expand Down
47 changes: 6 additions & 41 deletions pkg/proc/evalop/evalcompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ type compileCtx struct {

type evalLookup interface {
FindTypeExpr(ast.Expr) (godwarf.Type, error)
HasLocal(string) bool
HasGlobal(string, string) bool
HasBuiltin(string) bool
LookupRegisterName(string) (int, bool)
}

// CompileAST compiles the expression t into a list of instructions.
Expand Down Expand Up @@ -221,15 +218,8 @@ func (ctx *compileCtx) compileAST(t ast.Expr) error {
case x.Name == "runtime" && node.Sel.Name == "threadid":
ctx.pushOp(&PushThreadID{})

case ctx.HasLocal(x.Name):
ctx.pushOp(&PushLocal{Name: x.Name})
ctx.pushOp(&Select{node.Sel.Name})

case ctx.HasGlobal(x.Name, node.Sel.Name):
ctx.pushOp(&PushPackageVar{x.Name, node.Sel.Name})

default:
return ctx.compileUnary(node.X, &Select{node.Sel.Name})
ctx.pushOp(&PushPackageVarOrSelect{Name: x.Name, Sel: node.Sel.Name})
}

case *ast.CallExpr:
Expand Down Expand Up @@ -258,11 +248,7 @@ func (ctx *compileCtx) compileAST(t ast.Expr) error {
if err != nil {
return err
}
if ctx.HasGlobal(s, node.Sel.Name) {
ctx.pushOp(&PushPackageVar{s, node.Sel.Name})
return nil
}
return ctx.compileUnary(node.X, &Select{node.Sel.Name})
ctx.pushOp(&PushPackageVarOrSelect{Name: s, Sel: node.Sel.Name, NameIsString: true})

default:
return ctx.compileUnary(node.X, &Select{node.Sel.Name})
Expand Down Expand Up @@ -367,13 +353,10 @@ func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr) error {
}
return ctx.compileFunctionCall(node)
case *ast.Ident:
if ctx.HasBuiltin(n.Name) {
return ctx.compileFunctionCall(node)
}
if ctx.HasGlobal("", n.Name) || ctx.HasLocal(n.Name) {
return ctx.compileFunctionCall(node)
if typ, _ := ctx.FindTypeExpr(n); typ != nil {
return ctx.compileTypeCast(node, fmt.Errorf("could not find symbol value for %s", n.Name))
}
return ctx.compileTypeCast(node, fmt.Errorf("could not find symbol value for %s", n.Name))
return ctx.compileFunctionCall(node)
case *ast.IndexExpr:
// Ambiguous, could be a parametric type
switch n.X.(type) {
Expand Down Expand Up @@ -438,25 +421,7 @@ func (ctx *compileCtx) compileBuiltinCall(builtin string, args []ast.Expr) error
}

func (ctx *compileCtx) compileIdent(node *ast.Ident) error {
switch {
case ctx.HasLocal(node.Name):
ctx.pushOp(&PushLocal{Name: node.Name})
case ctx.HasGlobal("", node.Name):
ctx.pushOp(&PushPackageVar{"", node.Name})
case node.Name == "true" || node.Name == "false":
ctx.pushOp(&PushConst{constant.MakeBool(node.Name == "true")})
case node.Name == "nil":
ctx.pushOp(&PushNil{})
default:
found := false
if regnum, ok := ctx.LookupRegisterName(node.Name); ok {
ctx.pushOp(&PushRegister{regnum, node.Name})
found = true
}
if !found {
return fmt.Errorf("could not find symbol value for %s", node.Name)
}
}
ctx.pushOp(&PushIdent{node.Name})
return nil
}

Expand Down
Loading

0 comments on commit 4b628b8

Please sign in to comment.