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