github.com/zeebo/goof@v0.0.0-20230907150950-e9457bc94477/troop_call.go (about) 1 package goof 2 3 import ( 4 "debug/dwarf" 5 "reflect" 6 "sort" 7 "unsafe" 8 9 "github.com/zeebo/errs" 10 ) 11 12 func (t *Troop) addFunctions() error { 13 reader := t.data.Reader() 14 15 for { 16 entry, err := reader.Next() 17 if err != nil { 18 return errs.Wrap(err) 19 } 20 if entry == nil { 21 break 22 } 23 24 if entry.Tag != dwarf.TagSubprogram { 25 continue 26 } 27 28 name, ok := entry.Val(dwarf.AttrName).(string) 29 if !ok { 30 continue 31 } 32 33 pc, ok := entry.Val(dwarf.AttrLowpc).(uint64) 34 if !ok { 35 continue 36 } 37 38 dtypes, err := getFunctionArgTypes(t.data, entry) 39 if err != nil { 40 continue 41 } 42 43 _, err = t.findDwarfTypes(dtypes) 44 if err != nil { 45 continue 46 } 47 48 t.functions[name] = functionCacheEntry{ 49 pc: pc, 50 dtypes: dtypes, 51 } 52 } 53 54 return nil 55 } 56 57 func (t *Troop) Functions() ([]string, error) { 58 if err := t.check(); err != nil { 59 return nil, err 60 } 61 62 out := make([]string, 0, len(t.functions)) 63 for name := range t.functions { 64 out = append(out, name) 65 } 66 sort.Strings(out) 67 return out, nil 68 } 69 70 func (t *Troop) Call(name string, args ...interface{}) ([]interface{}, error) { 71 if err := t.check(); err != nil { 72 return nil, err 73 } 74 75 // and so it begins. find the function, the pc, and the types of the args 76 // and return values. we don't know which is which, but we assume the 77 // caller passed the appropriate things. 78 entry, ok := t.functions[name] 79 if !ok { 80 return nil, errs.New("call %s: unknown or uncallable function", name) 81 } 82 pc, dtypes := entry.pc, entry.dtypes 83 84 // make sure they didn't pass more arguments than total types 85 num_results := len(dtypes) - len(args) 86 if num_results < 0 { 87 return nil, errs.New("call %s: too many args", name) 88 } 89 90 // build the arguments, checking consistency and doing hacks to make it 91 // nice to use. 92 in, in_types, err := t.buildArguments(args, dtypes[:len(args)]) 93 if err != nil { 94 return nil, errs.New("call %s: %v", name, err) 95 } 96 97 // the rest must be the return values, RIGHT? LOL 98 out_types, err := t.findDwarfTypes(dtypes[len(args):]) 99 if err != nil { 100 return nil, errs.New("call %s: %v", name, err) 101 } 102 103 // make it happen 104 fn_typ := reflect.FuncOf(in_types, out_types, false) 105 fn := reflect.ValueOf(makeInterface(dataPtr(fn_typ), unsafe.Pointer(&pc))) 106 return ifaces(fn.Call(in)), nil 107 } 108 109 func (t *Troop) buildArguments(args []interface{}, dtypes []dwarf.Type) ( 110 []reflect.Value, []reflect.Type, error) { 111 112 if len(args) != len(dtypes) { 113 return nil, nil, errs.New("number of arguments does not match") 114 } 115 116 // so I want the Call signatrue to have args passed as ...interface{} 117 // because that makes the api nice: taking a reflect.Value is hard for the 118 // consumer. 119 // 120 // Unfortunately, that means that if you pass an interface type in, they 121 // get down-casted to just interface{}. Doubly unfortunately, the itab 122 // pointer was lost when the value was converted to an interface{} instead 123 // of whatever interface it was in the first place. 124 // 125 // So let's just look and see if we can find the interface type in the 126 // types map based on the dwarf type. If not, shoot. Hopefully that's 127 // rare! Maybe we can expose a CallReflect api that can get around this. 128 // 129 // Heh. 130 131 in_types := make([]reflect.Type, 0, len(args)) 132 in := make([]reflect.Value, 0, len(args)) 133 134 for i, arg := range args { 135 dtyp := dtypes[i] 136 137 val, err := t.constructValue(arg, dtyp) 138 if err != nil { 139 return nil, nil, errs.New("arg %d: %v", i, err) 140 } 141 142 in_types = append(in_types, val.Type()) 143 in = append(in, val) 144 } 145 146 return in, in_types, nil 147 } 148 149 func (t *Troop) constructValue(arg interface{}, dtyp dwarf.Type) ( 150 val reflect.Value, err error) { 151 152 rtyp, err := t.consistentValue(arg, dtyp) 153 if err != nil { 154 return val, err 155 } 156 if arg == nil { 157 return reflect.New(rtyp).Elem(), nil 158 } 159 return reflect.ValueOf(arg).Convert(rtyp), nil 160 } 161 162 func (t *Troop) consistentValue(arg interface{}, dtyp dwarf.Type) ( 163 reflect.Type, error) { 164 165 rtyp, err := t.findDwarfType(dtyp) 166 if err != nil { 167 return nil, err 168 } 169 if arg == nil { 170 if !reflectCanBeNil(rtyp) { 171 return nil, errs.New("passing nil to non-nillable type: %v", 172 rtyp) 173 } 174 return rtyp, nil 175 } 176 if !reflect.TypeOf(arg).ConvertibleTo(rtyp) { 177 return nil, errs.New("cannot convert %v to %T", rtyp, arg) 178 } 179 return rtyp, nil 180 }