github.com/zeebo/goof@v0.0.0-20230907150950-e9457bc94477/troop_types.go (about)

     1  package goof
     2  
     3  import (
     4  	"debug/dwarf"
     5  	"fmt"
     6  	"reflect"
     7  	"sort"
     8  	"strings"
     9  	"unsafe"
    10  
    11  	"github.com/zeebo/errs"
    12  )
    13  
    14  //go:linkname typelinks reflect.typelinks
    15  func typelinks() (sections []unsafe.Pointer, offset [][]int32)
    16  
    17  func (t *Troop) addTypes() error {
    18  	sections, offset := typelinks()
    19  	for i, offs := range offset {
    20  		section := sections[i]
    21  		for _, off := range offs {
    22  			ptr := unsafe.Pointer(uintptr(section) + uintptr(off))
    23  			typ := reflect.TypeOf(makeInterface(ptr, nil))
    24  			t.addType(typ)
    25  		}
    26  	}
    27  
    28  	// special cased types
    29  	t.types["*void"] = unsafePointerType
    30  	t.types["**void"] = reflect.PtrTo(unsafePointerType)
    31  
    32  	return nil
    33  }
    34  
    35  func (t *Troop) addType(typ reflect.Type) {
    36  	name := dwarfName(typ)
    37  	if _, ok := t.types[name]; ok {
    38  		return
    39  	}
    40  	t.types[name] = typ
    41  
    42  	defer func() {
    43  		if r := recover(); r != nil {
    44  			t.failures[name] = fmt.Errorf("failed to add type %q (%v): %v", name, typ, r)
    45  		}
    46  	}()
    47  
    48  	switch typ.Kind() {
    49  	case reflect.Ptr, reflect.Chan, reflect.Slice, reflect.Array:
    50  		t.addType(typ.Elem())
    51  
    52  	case reflect.Map:
    53  		t.addType(typ.Key())
    54  		t.addType(typ.Elem())
    55  
    56  	case reflect.Func:
    57  		for i := 0; i < typ.NumIn(); i++ {
    58  			t.addType(typ.In(i))
    59  		}
    60  		for i := 0; i < typ.NumOut(); i++ {
    61  			t.addType(typ.Out(i))
    62  		}
    63  
    64  	case reflect.Struct:
    65  		for i := 0; i < typ.NumField(); i++ {
    66  			t.addType(typ.Field(i).Type)
    67  		}
    68  		for i := 0; i < typ.NumMethod(); i++ {
    69  			t.addType(typ.Method(i).Type)
    70  		}
    71  
    72  	case reflect.Interface:
    73  		for i := 0; i < typ.NumMethod(); i++ {
    74  			t.addType(typ.Method(i).Type)
    75  		}
    76  	}
    77  }
    78  
    79  func (t *Troop) Types() ([]reflect.Type, error) {
    80  	if err := t.check(); err != nil {
    81  		return nil, err
    82  	}
    83  	out := make([]reflect.Type, 0, len(t.types))
    84  	for _, typ := range t.types {
    85  		out = append(out, typ)
    86  	}
    87  	sort.Sort(typesByString(out))
    88  	return out, nil
    89  }
    90  
    91  func (t *Troop) Type(name string) (reflect.Type, error) {
    92  	if err := t.check(); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	entry, ok := t.types[name]
    97  	if !ok {
    98  		return nil, errs.New("type %s: unknown type", name)
    99  	}
   100  
   101  	return entry, nil
   102  }
   103  
   104  func (t *Troop) findDwarfTypes(dtypes []dwarf.Type) ([]reflect.Type, error) {
   105  	out_types := make([]reflect.Type, 0, len(dtypes))
   106  	for _, dtyp := range dtypes {
   107  		typ, err := t.findDwarfType(dtyp)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		out_types = append(out_types, typ)
   112  	}
   113  	return out_types, nil
   114  }
   115  
   116  func (t *Troop) findDwarfType(dtyp dwarf.Type) (reflect.Type, error) {
   117  	// TODO(jeff): we can synthesize some of these dwarf types at runtime,
   118  	// but hopefully we got enough of them when we loaded up all of the
   119  	// types. The problematic types are: 1. named types, 2. recursive types.
   120  	var dname string
   121  	switch dtyp := dtyp.(type) {
   122  	case *dwarf.StructType:
   123  		if dtyp.StructName != "" {
   124  			dname = dtyp.StructName
   125  		} else {
   126  			dname = dtyp.Defn()
   127  		}
   128  	default:
   129  		dname = dtyp.String()
   130  	}
   131  
   132  	// heh this is super hacky, but what isn't!?
   133  	if strings.HasPrefix(dname, "*struct ") &&
   134  		!strings.HasPrefix(dname, "*struct {") {
   135  
   136  		dname = "*" + dname[len("*struct "):]
   137  	}
   138  
   139  	typ, ok := t.types[dname]
   140  	if !ok {
   141  		return nil, errs.New("dwarf type %q unknown", dname)
   142  	}
   143  	return typ, nil
   144  }