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  }