github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/type.go (about)

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/goobj"
     5  	"cmd/objfile/obj"
     6  	"cmd/objfile/objabi"
     7  	"fmt"
     8  	"reflect"
     9  	"runtime"
    10  	"strings"
    11  	"unsafe"
    12  )
    13  
    14  type tflag uint8
    15  
    16  // See runtime/type.go _typePair
    17  type _typePair struct {
    18  	t1 *_type
    19  	t2 *_type
    20  }
    21  
    22  // See reflect/value.go emptyInterface
    23  type emptyInterface struct {
    24  	_type *_type
    25  	data  unsafe.Pointer
    26  }
    27  
    28  type nonEmptyInterface struct {
    29  	// see ../runtime/iface.go:/Itab
    30  	itab *itab
    31  	word unsafe.Pointer
    32  }
    33  
    34  func efaceOf(ep *interface{}) *emptyInterface {
    35  	return (*emptyInterface)(unsafe.Pointer(ep))
    36  }
    37  
    38  // See reflect/value.go sliceHeader
    39  type sliceHeader struct {
    40  	Data uintptr
    41  	Len  int
    42  	Cap  int
    43  }
    44  
    45  // Method on non-interface type
    46  type method struct {
    47  	name nameOff // name of method
    48  	mtyp typeOff // method type (without receiver)
    49  	ifn  textOff // fn used in interface call (one-word receiver)
    50  	tfn  textOff // fn used for normal method call
    51  }
    52  
    53  type imethod struct {
    54  	name nameOff
    55  	ityp typeOff
    56  }
    57  
    58  type interfacetype struct {
    59  	typ     _type
    60  	pkgpath name
    61  	mhdr    []imethod
    62  }
    63  
    64  type name struct {
    65  	bytes *byte
    66  }
    67  
    68  func (t *_type) uncommon() *uncommonType    { return _uncommon(t) }
    69  func (t *_type) nameOff(off nameOff) name   { return _nameOff(t, off) }
    70  func (t *_type) typeOff(off typeOff) *_type { return _typeOff(t, off) }
    71  func (n name) name() string                 { return _name(n) }
    72  func (n name) pkgPath() string              { return _pkgPath(n) }
    73  func (n name) isExported() bool             { return _isExported(n) }
    74  func (t *uncommonType) methods() []method   { return _methods(t) }
    75  func (t *_type) Kind() reflect.Kind         { return _Kind(t) }
    76  func (t *_type) Elem() *_type               { return fromRType(_Elem(t)) }
    77  
    78  // This replaces local package names with import paths, including where the package name doesn't match the last part of the import path e.g.
    79  //
    80  //	import "github.com/org/somepackage/v4" + somepackage.SomeStruct
    81  //	 =>  github.com/org/somepackage/v4.SomeStruct
    82  func resolveFullyQualifiedSymbolName(t *_type) string {
    83  	typ := AsRType(t)
    84  	// go.shape is a special builtin package whose name shouldn't be escaped
    85  	pkgPath := unescapeGoShapePkg(objabi.PathToPrefix(t.PkgPath()))
    86  
    87  	name := typ.Name()
    88  	if name == "" {
    89  		name = nameFromTypeString(t)
    90  	}
    91  	var maybeStar string
    92  	if typ.String()[0] == '*' {
    93  		maybeStar = "*"
    94  	}
    95  	if pkgPath != "" && name != "" {
    96  		if strings.HasPrefix(pkgPath, "go.shape") { // go.shape Name()s don't necessarily match the String()
    97  			return typ.String()
    98  		}
    99  		return maybeStar + pkgPath + "." + name
   100  	}
   101  	switch t.Kind() {
   102  	case reflect.Ptr:
   103  		return "*" + resolveFullyQualifiedSymbolName(fromRType(typ.Elem()))
   104  	case reflect.Struct:
   105  		if typ.NumField() == 0 {
   106  			return typ.String()
   107  		}
   108  		fields := make([]string, typ.NumField())
   109  		for i := 0; i < typ.NumField(); i++ {
   110  			fieldName := typ.Field(i).Name + " "
   111  			if typ.Field(i).Anonymous {
   112  				fieldName = ""
   113  			}
   114  			fieldPkgPath := ""
   115  			if typ.Field(i).PkgPath != "" && !typ.Field(i).Anonymous {
   116  				fieldPkgPath = unescapeGoShapePkg(objabi.PathToPrefix(typ.Field(i).PkgPath)) + "."
   117  			}
   118  			fieldStructTag := ""
   119  			if typ.Field(i).Tag != "" {
   120  				fieldStructTag = fmt.Sprintf(" %q", string(typ.Field(i).Tag))
   121  			}
   122  			fields[i] = fmt.Sprintf("%s%s%s%s", fieldPkgPath, fieldName, resolveFullyQualifiedSymbolName(fromRType(typ.Field(i).Type)), fieldStructTag)
   123  		}
   124  		return fmt.Sprintf("struct { %s }", strings.Join(fields, "; "))
   125  	case reflect.Map:
   126  		return fmt.Sprintf("map[%s]%s", resolveFullyQualifiedSymbolName(fromRType(typ.Key())), resolveFullyQualifiedSymbolName(fromRType(typ.Elem())))
   127  	case reflect.Chan:
   128  		switch reflect.ChanDir(typ.ChanDir()) {
   129  		case reflect.BothDir:
   130  			return fmt.Sprintf("chan %s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem())))
   131  		case reflect.RecvDir:
   132  			return fmt.Sprintf("<-chan %s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem())))
   133  		case reflect.SendDir:
   134  			return fmt.Sprintf("chan<- %s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem())))
   135  		}
   136  	case reflect.Slice:
   137  		return fmt.Sprintf("[]%s", resolveFullyQualifiedSymbolName(fromRType(typ.Elem())))
   138  	case reflect.Array:
   139  		return fmt.Sprintf("[%d]%s", typ.Len(), resolveFullyQualifiedSymbolName(fromRType(typ.Elem())))
   140  	case reflect.Func:
   141  		ins := make([]string, typ.NumIn())
   142  		outs := make([]string, typ.NumOut())
   143  		for i := 0; i < typ.NumIn(); i++ {
   144  			ins[i] = resolveFullyQualifiedSymbolName(fromRType(typ.In(i)))
   145  			if i == typ.NumIn()-1 && typ.IsVariadic() {
   146  				ins[i] = "..." + resolveFullyQualifiedSymbolName(fromRType(typ.In(i).Elem()))
   147  			}
   148  		}
   149  		for i := 0; i < typ.NumOut(); i++ {
   150  			outs[i] = resolveFullyQualifiedSymbolName(fromRType(typ.Out(i)))
   151  		}
   152  		funcName := "func(" + strings.Join(ins, ", ") + ")"
   153  		if len(outs) > 0 {
   154  			funcName += " "
   155  		}
   156  		if len(outs) > 1 {
   157  			funcName += "("
   158  		}
   159  		funcName += strings.Join(outs, ", ")
   160  		if len(outs) > 1 {
   161  			funcName += ")"
   162  		}
   163  		return funcName
   164  	case reflect.Interface:
   165  		if goobj.BuiltinIdx(TypePrefix+typ.String(), int(obj.ABI0)) != -1 {
   166  			// must be a builtin,
   167  			return typ.String()
   168  		}
   169  		if typ.NumMethod() == 0 {
   170  			return typ.String()
   171  		}
   172  		methods := make([]string, typ.NumMethod())
   173  		ifaceT := (*interfacetype)(unsafe.Pointer(t))
   174  
   175  		for i := 0; i < typ.NumMethod(); i++ {
   176  			methodType := _typeOff(t, ifaceT.mhdr[i].ityp)
   177  			methodName := _nameOff(t, ifaceT.mhdr[i].name).name()
   178  			methods[i] = fmt.Sprintf("%s(%s", methodName, strings.TrimPrefix(resolveFullyQualifiedSymbolName(methodType), "func("))
   179  		}
   180  		reflect.TypeOf(0)
   181  		return fmt.Sprintf("interface { %s }", strings.Join(methods, "; "))
   182  	default:
   183  		if goobj.BuiltinIdx(TypePrefix+typ.String(), int(obj.ABI0)) != -1 {
   184  			// must be a builtin,
   185  			return typ.String()
   186  		}
   187  		switch typ.String() {
   188  		case "int", "uint", "struct {}", "interface {}":
   189  			return typ.String()
   190  		}
   191  		panic("unexpected builtin type: " + typ.String())
   192  	}
   193  	return ""
   194  }
   195  
   196  func nameFromTypeString(t *_type) string {
   197  	typ := AsRType(t)
   198  	s := typ.String()
   199  	i := len(s) - 1
   200  	sqBrackets := 0
   201  	for i >= 0 && (s[i] != '.' || sqBrackets != 0) {
   202  		switch s[i] {
   203  		case ']':
   204  			sqBrackets++
   205  		case '[':
   206  			sqBrackets--
   207  		}
   208  		i--
   209  	}
   210  	return s[i+1:]
   211  }
   212  
   213  func fullyQualifiedMethodName(t *_type, method method) string {
   214  	methodName := t.nameOff(method.name).name()
   215  
   216  	// t.PkgPath() will always return a path, whereas AsRType(t).PkgPath() will only return if flag TNamed is set
   217  	pkgPath := unescapeGoShapePkg(objabi.PathToPrefix(t.PkgPath()))
   218  	name := nameFromTypeString(t)
   219  	if name == "" {
   220  		panic("method on type with no name")
   221  	}
   222  	if t.Kind() == reflect.Pointer {
   223  		return pkgPath + ".(*" + name + ")." + methodName
   224  	}
   225  	return pkgPath + "." + name + "." + methodName
   226  
   227  }
   228  
   229  func unescapeGoShapePkg(pkgPath string) string {
   230  	if pkgPath == "go%2eshape" {
   231  		return "go.shape"
   232  	}
   233  	return pkgPath
   234  }
   235  
   236  func symbolIsVariant(name string) (string, bool) {
   237  	const dot = "·"
   238  	const noAlgPrefix = TypePrefix + "noalg."
   239  	if strings.HasPrefix(name, TypePrefix+"struct {") || strings.HasPrefix(name, TypePrefix+"*struct {") {
   240  		// Anonymous structs might embed variant types, so these will need parsing first
   241  		ptr := false
   242  		if strings.HasPrefix(name, TypePrefix+"*struct {") {
   243  			ptr = true
   244  		}
   245  		fieldsStr := strings.TrimPrefix(name, TypePrefix+"struct { ")
   246  		fieldsStr = strings.TrimPrefix(name, TypePrefix+"*struct { ")
   247  		fieldsStr = strings.TrimSuffix(fieldsStr, " }")
   248  		fields := strings.Split(fieldsStr, "; ")
   249  		isVariant := false
   250  		for j, field := range fields {
   251  			var typeName string
   252  			var typeNameIndex int
   253  			fieldTypeTag := strings.SplitN(field, " ", 3)
   254  			// could be anonymous, or tagless, or both - we want to operate on the type
   255  			switch len(fieldTypeTag) {
   256  			case 1:
   257  				// Anonymous, tagless - just a type
   258  				typeName = fieldTypeTag[0]
   259  			case 2:
   260  				// could be a name + type, or type + tag
   261  				if strings.HasPrefix(fieldTypeTag[1], "\"") || strings.HasPrefix(fieldTypeTag[1], "`") {
   262  					// type + tag
   263  					typeName = fieldTypeTag[0]
   264  				} else {
   265  					// name + type
   266  					typeName = fieldTypeTag[1]
   267  					typeNameIndex = 1
   268  				}
   269  			case 3:
   270  				// Name + type + tag
   271  				typeName = fieldTypeTag[1]
   272  				typeNameIndex = 1
   273  			}
   274  			i := len(typeName)
   275  			for i > 0 && typeName[i-1] >= '0' && typeName[i-1] <= '9' {
   276  				i--
   277  			}
   278  			if i >= len(dot) && typeName[i-len(dot):i] == dot {
   279  				isVariant = true
   280  				fieldTypeTag[typeNameIndex] = typeName[:i-len(dot)]
   281  				fields[j] = strings.Join(fieldTypeTag, " ")
   282  			}
   283  		}
   284  		if isVariant {
   285  			if ptr {
   286  				return TypePrefix + "*struct { " + strings.Join(fields, "; ") + " }", true
   287  			}
   288  			return TypePrefix + "struct { " + strings.Join(fields, "; ") + " }", true
   289  
   290  		}
   291  		return "", false
   292  	} else {
   293  		// need to double check for function scoped types which get a ·N suffix added, and also type.noalg.* variants
   294  		i := len(name)
   295  		for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
   296  			i--
   297  		}
   298  		if i >= len(dot) && name[i-len(dot):i] == dot {
   299  			return name[:i-len(dot)], true
   300  		} else if strings.HasPrefix(name, noAlgPrefix) {
   301  			return TypePrefix + strings.TrimPrefix(name, noAlgPrefix), true
   302  		}
   303  		return "", false
   304  	}
   305  }
   306  
   307  func funcPkgPath(funcName string) string {
   308  	funcName = strings.TrimPrefix(funcName, TypeDoubleDotPrefix+"eq.")
   309  	// Anonymous struct methods can't have a package
   310  	if strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"struct {") || strings.HasPrefix(funcName, "go"+ObjSymbolSeparator+"(*struct {") {
   311  		return ""
   312  	}
   313  	lastSlash := strings.LastIndexByte(funcName, '/')
   314  	if lastSlash == -1 {
   315  		lastSlash = 0
   316  	}
   317  	// Generic dictionaries
   318  	firstDict := strings.Index(funcName, "..dict")
   319  	if firstDict > 0 {
   320  		return funcName[:firstDict]
   321  	} else {
   322  		// Methods on structs embedding structs from other packages look funny, e.g.:
   323  		// regexp.(*onePassInst).regexp/syntax.op
   324  		firstBracket := strings.LastIndex(funcName, ".(")
   325  		if firstBracket > 0 && lastSlash > firstBracket {
   326  			lastSlash = firstBracket
   327  		}
   328  	}
   329  	dot := lastSlash
   330  	for ; dot < len(funcName) && funcName[dot] != '.' && funcName[dot] != '(' && funcName[dot] != '['; dot++ {
   331  	}
   332  	pkgPath := funcName[:dot]
   333  	return strings.TrimPrefix(strings.TrimPrefix(pkgPath, TypePrefix+".eq."), "[...]")
   334  }
   335  
   336  func (t *_type) PkgPath() string {
   337  	ut := t.uncommon()
   338  	if ut == nil {
   339  		return EmptyString
   340  	}
   341  	return t.nameOff(ut.pkgpath).name()
   342  }
   343  
   344  func RegTypes(symPtr map[string]uintptr, interfaces ...interface{}) {
   345  	for _, inter := range interfaces {
   346  		v := reflect.ValueOf(inter)
   347  		regType(symPtr, v)
   348  		if v.Kind() == reflect.Ptr {
   349  			regType(symPtr, v.Elem())
   350  		}
   351  	}
   352  }
   353  
   354  func regType(symPtr map[string]uintptr, v reflect.Value) {
   355  	inter := v.Interface()
   356  	if v.Kind() == reflect.Func && getFunctionPtr(inter) != 0 {
   357  		symPtr[runtime.FuncForPC(v.Pointer()).Name()] = getFunctionPtr(inter)
   358  	} else {
   359  		header := (*emptyInterface)(unsafe.Pointer(&inter))
   360  		t := header._type
   361  		registerType(t, symPtr, map[string]struct{}{})
   362  	}
   363  }
   364  
   365  func buildModuleTypeHash(module *moduledata, typeHash map[uint32][]*_type) {
   366  	for _, tl := range module.typelinks {
   367  		var t *_type
   368  		t = (*_type)(adduintptr(module.types, int(tl)))
   369  		registerTypeHash(t, typeHash)
   370  	}
   371  }
   372  
   373  func registerTypeHash(t *_type, typeHash map[uint32][]*_type) {
   374  	if t.Kind() == reflect.Invalid {
   375  		panic("Unexpected invalid kind during registration!")
   376  	}
   377  
   378  	tlist := typeHash[t.hash]
   379  	for _, tcur := range tlist {
   380  		if tcur == t {
   381  			return
   382  		}
   383  	}
   384  	typeHash[t.hash] = append(tlist, t)
   385  
   386  	switch t.Kind() {
   387  	case reflect.Ptr, reflect.Chan, reflect.Array, reflect.Slice:
   388  		// Indirect pointers + elems
   389  		element := t.Elem()
   390  		registerTypeHash(element, typeHash)
   391  	case reflect.Func:
   392  		typ := AsType(t)
   393  		for i := 0; i < typ.NumIn(); i++ {
   394  			registerTypeHash(toType(typ.In(i)), typeHash)
   395  		}
   396  		for i := 0; i < typ.NumOut(); i++ {
   397  			registerTypeHash(toType(typ.Out(i)), typeHash)
   398  		}
   399  	case reflect.Struct:
   400  		typ := AsType(t)
   401  		for i := 0; i < typ.NumField(); i++ {
   402  			registerTypeHash(toType(typ.Field(i).Type), typeHash)
   403  		}
   404  	case reflect.Map:
   405  		mt := (*mapType)(unsafe.Pointer(t))
   406  		registerTypeHash(mt.key, typeHash)
   407  		registerTypeHash(mt.elem, typeHash)
   408  	case reflect.Bool, reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64, reflect.Int32, reflect.Uint32, reflect.Int16, reflect.Uint16, reflect.Int8, reflect.Uint8, reflect.Float64, reflect.Float32, reflect.String, reflect.UnsafePointer, reflect.Uintptr, reflect.Complex64, reflect.Complex128, reflect.Interface:
   409  		// Already added above
   410  	default:
   411  		panic(fmt.Sprintf("registerTypeHash found unexpected type (kind %s): ", t.Kind()))
   412  	}
   413  }