proc: allow accessing captured variable as if they were struct fields (#3866)

If 'a' is a captured variable in a function pointer called 'f' let
'f.a' evaluate to its value.
This commit is contained in:
Alessandro Arzilli 2024-12-02 20:20:51 +01:00 committed by GitHub
parent 9af09688ea
commit 7b9a379e59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 42 additions and 19 deletions

@ -5479,28 +5479,30 @@ func TestReadClosure(t *testing.T) {
accV := evalVariable(p, t, "acc")
t.Log(api.ConvertVar(accV).MultilineString("", ""))
if len(accV.Children) != 2 {
t.Error("wrong number of children")
} else {
found := 0
for j := range accV.Children {
v := &accV.Children[j]
switch v.Name {
case "scale":
found++
if val, _ := constant.Int64Val(v.Value); val != 3 {
t.Error("wrong value for scale")
}
case "a":
found++
if val, _ := constant.Int64Val(v.Value); val != avalues[i] {
t.Errorf("wrong value for a: %d", val)
}
t.Fatal("wrong number of children")
}
found := 0
for j := range accV.Children {
v := &accV.Children[j]
switch v.Name {
case "scale":
found++
if val, _ := constant.Int64Val(v.Value); val != 3 {
t.Error("wrong value for scale")
}
case "a":
found++
if val, _ := constant.Int64Val(v.Value); val != avalues[i] {
t.Errorf("wrong value for a: %d", val)
}
}
if found != 2 {
t.Error("wrong captured variables")
}
}
if found != 2 {
t.Error("wrong captured variables")
}
assertVariable(t, evalVariable(p, t, "acc.scale"), varTest{name: "acc.scale", preserveName: true, value: "3", varType: "int"})
assertVariable(t, evalVariable(p, t, "acc.a"), varTest{name: "acc.a", preserveName: true, value: fmt.Sprintf("%d", avalues[i]), varType: "int"})
}
})
}

@ -1101,6 +1101,7 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
}
return nil, fmt.Errorf("%s has no member %s", vname, memberName)
}
closure := false
switch v.Kind {
case reflect.Chan:
v = v.clone()
@ -1110,6 +1111,19 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
if len(v.Children) > 0 {
v = &v.Children[0]
}
case reflect.Func:
v.loadFunctionPtr(0, LoadConfig{MaxVariableRecurse: -1})
if v.Unreadable != nil {
return nil, v.Unreadable
}
if v.closureAddr != 0 {
fn := v.bi.PCToFunc(v.Base)
if fn != nil {
cst := fn.extra(v.bi).closureStructType
v = v.newVariable(v.Name, v.closureAddr, cst, v.mem)
closure = true
}
}
}
queue := []*Variable{v}
@ -1136,6 +1150,13 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
if field.Name == memberName {
return structVar.toField(field)
}
if len(queue) == 0 && field.Name == "&"+memberName && closure {
f, err := structVar.toField(field)
if err != nil {
return nil, err
}
return f.maybeDereference(), nil
}
isEmbeddedStructMember :=
field.Embedded ||
(field.Type.Common().Name == field.Name) ||