Source file src/runtime/type.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Runtime type representation.
     6  
     7  package runtime
     8  
     9  import (
    10  	"internal/abi"
    11  	"unsafe"
    12  )
    13  
    14  type nameOff = abi.NameOff
    15  type typeOff = abi.TypeOff
    16  type textOff = abi.TextOff
    17  
    18  type _type = abi.Type
    19  
    20  // rtype is a wrapper that allows us to define additional methods.
    21  type rtype struct {
    22  	*abi.Type // embedding is okay here (unlike reflect) because none of this is public
    23  }
    24  
    25  func (t rtype) string() string {
    26  	s := t.nameOff(t.Str).Name()
    27  	if t.TFlag&abi.TFlagExtraStar != 0 {
    28  		return s[1:]
    29  	}
    30  	return s
    31  }
    32  
    33  func (t rtype) uncommon() *uncommontype {
    34  	return t.Uncommon()
    35  }
    36  
    37  func (t rtype) name() string {
    38  	if t.TFlag&abi.TFlagNamed == 0 {
    39  		return ""
    40  	}
    41  	s := t.string()
    42  	i := len(s) - 1
    43  	sqBrackets := 0
    44  	for i >= 0 && (s[i] != '.' || sqBrackets != 0) {
    45  		switch s[i] {
    46  		case ']':
    47  			sqBrackets++
    48  		case '[':
    49  			sqBrackets--
    50  		}
    51  		i--
    52  	}
    53  	return s[i+1:]
    54  }
    55  
    56  // pkgpath returns the path of the package where t was defined, if
    57  // available. This is not the same as the reflect package's PkgPath
    58  // method, in that it returns the package path for struct and interface
    59  // types, not just named types.
    60  func (t rtype) pkgpath() string {
    61  	if u := t.uncommon(); u != nil {
    62  		return t.nameOff(u.PkgPath).Name()
    63  	}
    64  	switch t.Kind_ & abi.KindMask {
    65  	case abi.Struct:
    66  		st := (*structtype)(unsafe.Pointer(t.Type))
    67  		return st.PkgPath.Name()
    68  	case abi.Interface:
    69  		it := (*interfacetype)(unsafe.Pointer(t.Type))
    70  		return it.PkgPath.Name()
    71  	}
    72  	return ""
    73  }
    74  
    75  // reflectOffs holds type offsets defined at run time by the reflect package.
    76  //
    77  // When a type is defined at run time, its *rtype data lives on the heap.
    78  // There are a wide range of possible addresses the heap may use, that
    79  // may not be representable as a 32-bit offset. Moreover the GC may
    80  // one day start moving heap memory, in which case there is no stable
    81  // offset that can be defined.
    82  //
    83  // To provide stable offsets, we add pin *rtype objects in a global map
    84  // and treat the offset as an identifier. We use negative offsets that
    85  // do not overlap with any compile-time module offsets.
    86  //
    87  // Entries are created by reflect.addReflectOff.
    88  var reflectOffs struct {
    89  	lock mutex
    90  	next int32
    91  	m    map[int32]unsafe.Pointer
    92  	minv map[unsafe.Pointer]int32
    93  }
    94  
    95  func reflectOffsLock() {
    96  	lock(&reflectOffs.lock)
    97  	if raceenabled {
    98  		raceacquire(unsafe.Pointer(&reflectOffs.lock))
    99  	}
   100  }
   101  
   102  func reflectOffsUnlock() {
   103  	if raceenabled {
   104  		racerelease(unsafe.Pointer(&reflectOffs.lock))
   105  	}
   106  	unlock(&reflectOffs.lock)
   107  }
   108  
   109  // resolveNameOff should be an internal detail,
   110  // but widely used packages access it using linkname.
   111  // Notable members of the hall of shame include:
   112  //   - github.com/cloudwego/frugal
   113  //
   114  // Do not remove or change the type signature.
   115  // See go.dev/issue/67401.
   116  //
   117  //go:linkname resolveNameOff
   118  func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
   119  	if off == 0 {
   120  		return name{}
   121  	}
   122  	base := uintptr(ptrInModule)
   123  	for md := &firstmoduledata; md != nil; md = md.next {
   124  		if base >= md.types && base < md.etypes {
   125  			res := md.types + uintptr(off)
   126  			if res > md.etypes {
   127  				println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
   128  				throw("runtime: name offset out of range")
   129  			}
   130  			return name{Bytes: (*byte)(unsafe.Pointer(res))}
   131  		}
   132  	}
   133  
   134  	// No module found. see if it is a run time name.
   135  	reflectOffsLock()
   136  	res, found := reflectOffs.m[int32(off)]
   137  	reflectOffsUnlock()
   138  	if !found {
   139  		println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
   140  		for next := &firstmoduledata; next != nil; next = next.next {
   141  			println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   142  		}
   143  		throw("runtime: name offset base pointer out of range")
   144  	}
   145  	return name{Bytes: (*byte)(res)}
   146  }
   147  
   148  func (t rtype) nameOff(off nameOff) name {
   149  	return resolveNameOff(unsafe.Pointer(t.Type), off)
   150  }
   151  
   152  // resolveTypeOff should be an internal detail,
   153  // but widely used packages access it using linkname.
   154  // Notable members of the hall of shame include:
   155  //   - github.com/cloudwego/frugal
   156  //
   157  // Do not remove or change the type signature.
   158  // See go.dev/issue/67401.
   159  //
   160  //go:linkname resolveTypeOff
   161  func resolveTypeOff(ptrInModule unsafe.Pointer, off typeOff) *_type {
   162  	if off == 0 || off == -1 {
   163  		// -1 is the sentinel value for unreachable code.
   164  		// See cmd/link/internal/ld/data.go:relocsym.
   165  		return nil
   166  	}
   167  	base := uintptr(ptrInModule)
   168  	var md *moduledata
   169  	for next := &firstmoduledata; next != nil; next = next.next {
   170  		if base >= next.types && base < next.etypes {
   171  			md = next
   172  			break
   173  		}
   174  	}
   175  	if md == nil {
   176  		reflectOffsLock()
   177  		res := reflectOffs.m[int32(off)]
   178  		reflectOffsUnlock()
   179  		if res == nil {
   180  			println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
   181  			for next := &firstmoduledata; next != nil; next = next.next {
   182  				println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   183  			}
   184  			throw("runtime: type offset base pointer out of range")
   185  		}
   186  		return (*_type)(res)
   187  	}
   188  	if t := md.typemap[off]; t != nil {
   189  		return t
   190  	}
   191  	res := md.types + uintptr(off)
   192  	if res > md.etypes {
   193  		println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
   194  		throw("runtime: type offset out of range")
   195  	}
   196  	return (*_type)(unsafe.Pointer(res))
   197  }
   198  
   199  func (t rtype) typeOff(off typeOff) *_type {
   200  	return resolveTypeOff(unsafe.Pointer(t.Type), off)
   201  }
   202  
   203  func (t rtype) textOff(off textOff) unsafe.Pointer {
   204  	if off == -1 {
   205  		// -1 is the sentinel value for unreachable code.
   206  		// See cmd/link/internal/ld/data.go:relocsym.
   207  		return unsafe.Pointer(abi.FuncPCABIInternal(unreachableMethod))
   208  	}
   209  	base := uintptr(unsafe.Pointer(t.Type))
   210  	var md *moduledata
   211  	for next := &firstmoduledata; next != nil; next = next.next {
   212  		if base >= next.types && base < next.etypes {
   213  			md = next
   214  			break
   215  		}
   216  	}
   217  	if md == nil {
   218  		reflectOffsLock()
   219  		res := reflectOffs.m[int32(off)]
   220  		reflectOffsUnlock()
   221  		if res == nil {
   222  			println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
   223  			for next := &firstmoduledata; next != nil; next = next.next {
   224  				println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   225  			}
   226  			throw("runtime: text offset base pointer out of range")
   227  		}
   228  		return res
   229  	}
   230  	res := md.textAddr(uint32(off))
   231  	return unsafe.Pointer(res)
   232  }
   233  
   234  type uncommontype = abi.UncommonType
   235  
   236  type interfacetype = abi.InterfaceType
   237  
   238  type maptype = abi.MapType
   239  
   240  type arraytype = abi.ArrayType
   241  
   242  type chantype = abi.ChanType
   243  
   244  type slicetype = abi.SliceType
   245  
   246  type functype = abi.FuncType
   247  
   248  type ptrtype = abi.PtrType
   249  
   250  type name = abi.Name
   251  
   252  type structtype = abi.StructType
   253  
   254  func pkgPath(n name) string {
   255  	if n.Bytes == nil || *n.Data(0)&(1<<2) == 0 {
   256  		return ""
   257  	}
   258  	i, l := n.ReadVarint(1)
   259  	off := 1 + i + l
   260  	if *n.Data(0)&(1<<1) != 0 {
   261  		i2, l2 := n.ReadVarint(off)
   262  		off += i2 + l2
   263  	}
   264  	var nameOff nameOff
   265  	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.Data(off)))[:])
   266  	pkgPathName := resolveNameOff(unsafe.Pointer(n.Bytes), nameOff)
   267  	return pkgPathName.Name()
   268  }
   269  
   270  // typelinksinit scans the types from extra modules and builds the
   271  // moduledata typemap used to de-duplicate type pointers.
   272  func typelinksinit() {
   273  	if firstmoduledata.next == nil {
   274  		return
   275  	}
   276  	typehash := make(map[uint32][]*_type, len(firstmoduledata.typelinks))
   277  
   278  	modules := activeModules()
   279  	prev := modules[0]
   280  	for _, md := range modules[1:] {
   281  		// Collect types from the previous module into typehash.
   282  	collect:
   283  		for _, tl := range prev.typelinks {
   284  			var t *_type
   285  			if prev.typemap == nil {
   286  				t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl)))
   287  			} else {
   288  				t = prev.typemap[typeOff(tl)]
   289  			}
   290  			// Add to typehash if not seen before.
   291  			tlist := typehash[t.Hash]
   292  			for _, tcur := range tlist {
   293  				if tcur == t {
   294  					continue collect
   295  				}
   296  			}
   297  			typehash[t.Hash] = append(tlist, t)
   298  		}
   299  
   300  		if md.typemap == nil {
   301  			// If any of this module's typelinks match a type from a
   302  			// prior module, prefer that prior type by adding the offset
   303  			// to this module's typemap.
   304  			tm := make(map[typeOff]*_type, len(md.typelinks))
   305  			pinnedTypemaps = append(pinnedTypemaps, tm)
   306  			md.typemap = tm
   307  			for _, tl := range md.typelinks {
   308  				t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
   309  				for _, candidate := range typehash[t.Hash] {
   310  					seen := map[_typePair]struct{}{}
   311  					if typesEqual(t, candidate, seen) {
   312  						t = candidate
   313  						break
   314  					}
   315  				}
   316  				md.typemap[typeOff(tl)] = t
   317  			}
   318  		}
   319  
   320  		prev = md
   321  	}
   322  }
   323  
   324  type _typePair struct {
   325  	t1 *_type
   326  	t2 *_type
   327  }
   328  
   329  func toRType(t *abi.Type) rtype {
   330  	return rtype{t}
   331  }
   332  
   333  // typesEqual reports whether two types are equal.
   334  //
   335  // Everywhere in the runtime and reflect packages, it is assumed that
   336  // there is exactly one *_type per Go type, so that pointer equality
   337  // can be used to test if types are equal. There is one place that
   338  // breaks this assumption: buildmode=shared. In this case a type can
   339  // appear as two different pieces of memory. This is hidden from the
   340  // runtime and reflect package by the per-module typemap built in
   341  // typelinksinit. It uses typesEqual to map types from later modules
   342  // back into earlier ones.
   343  //
   344  // Only typelinksinit needs this function.
   345  func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool {
   346  	tp := _typePair{t, v}
   347  	if _, ok := seen[tp]; ok {
   348  		return true
   349  	}
   350  
   351  	// mark these types as seen, and thus equivalent which prevents an infinite loop if
   352  	// the two types are identical, but recursively defined and loaded from
   353  	// different modules
   354  	seen[tp] = struct{}{}
   355  
   356  	if t == v {
   357  		return true
   358  	}
   359  	kind := t.Kind_ & abi.KindMask
   360  	if kind != v.Kind_&abi.KindMask {
   361  		return false
   362  	}
   363  	rt, rv := toRType(t), toRType(v)
   364  	if rt.string() != rv.string() {
   365  		return false
   366  	}
   367  	ut := t.Uncommon()
   368  	uv := v.Uncommon()
   369  	if ut != nil || uv != nil {
   370  		if ut == nil || uv == nil {
   371  			return false
   372  		}
   373  		pkgpatht := rt.nameOff(ut.PkgPath).Name()
   374  		pkgpathv := rv.nameOff(uv.PkgPath).Name()
   375  		if pkgpatht != pkgpathv {
   376  			return false
   377  		}
   378  	}
   379  	if abi.Bool <= kind && kind <= abi.Complex128 {
   380  		return true
   381  	}
   382  	switch kind {
   383  	case abi.String, abi.UnsafePointer:
   384  		return true
   385  	case abi.Array:
   386  		at := (*arraytype)(unsafe.Pointer(t))
   387  		av := (*arraytype)(unsafe.Pointer(v))
   388  		return typesEqual(at.Elem, av.Elem, seen) && at.Len == av.Len
   389  	case abi.Chan:
   390  		ct := (*chantype)(unsafe.Pointer(t))
   391  		cv := (*chantype)(unsafe.Pointer(v))
   392  		return ct.Dir == cv.Dir && typesEqual(ct.Elem, cv.Elem, seen)
   393  	case abi.Func:
   394  		ft := (*functype)(unsafe.Pointer(t))
   395  		fv := (*functype)(unsafe.Pointer(v))
   396  		if ft.OutCount != fv.OutCount || ft.InCount != fv.InCount {
   397  			return false
   398  		}
   399  		tin, vin := ft.InSlice(), fv.InSlice()
   400  		for i := 0; i < len(tin); i++ {
   401  			if !typesEqual(tin[i], vin[i], seen) {
   402  				return false
   403  			}
   404  		}
   405  		tout, vout := ft.OutSlice(), fv.OutSlice()
   406  		for i := 0; i < len(tout); i++ {
   407  			if !typesEqual(tout[i], vout[i], seen) {
   408  				return false
   409  			}
   410  		}
   411  		return true
   412  	case abi.Interface:
   413  		it := (*interfacetype)(unsafe.Pointer(t))
   414  		iv := (*interfacetype)(unsafe.Pointer(v))
   415  		if it.PkgPath.Name() != iv.PkgPath.Name() {
   416  			return false
   417  		}
   418  		if len(it.Methods) != len(iv.Methods) {
   419  			return false
   420  		}
   421  		for i := range it.Methods {
   422  			tm := &it.Methods[i]
   423  			vm := &iv.Methods[i]
   424  			// Note the mhdr array can be relocated from
   425  			// another module. See #17724.
   426  			tname := resolveNameOff(unsafe.Pointer(tm), tm.Name)
   427  			vname := resolveNameOff(unsafe.Pointer(vm), vm.Name)
   428  			if tname.Name() != vname.Name() {
   429  				return false
   430  			}
   431  			if pkgPath(tname) != pkgPath(vname) {
   432  				return false
   433  			}
   434  			tityp := resolveTypeOff(unsafe.Pointer(tm), tm.Typ)
   435  			vityp := resolveTypeOff(unsafe.Pointer(vm), vm.Typ)
   436  			if !typesEqual(tityp, vityp, seen) {
   437  				return false
   438  			}
   439  		}
   440  		return true
   441  	case abi.Map:
   442  		mt := (*maptype)(unsafe.Pointer(t))
   443  		mv := (*maptype)(unsafe.Pointer(v))
   444  		return typesEqual(mt.Key, mv.Key, seen) && typesEqual(mt.Elem, mv.Elem, seen)
   445  	case abi.Pointer:
   446  		pt := (*ptrtype)(unsafe.Pointer(t))
   447  		pv := (*ptrtype)(unsafe.Pointer(v))
   448  		return typesEqual(pt.Elem, pv.Elem, seen)
   449  	case abi.Slice:
   450  		st := (*slicetype)(unsafe.Pointer(t))
   451  		sv := (*slicetype)(unsafe.Pointer(v))
   452  		return typesEqual(st.Elem, sv.Elem, seen)
   453  	case abi.Struct:
   454  		st := (*structtype)(unsafe.Pointer(t))
   455  		sv := (*structtype)(unsafe.Pointer(v))
   456  		if len(st.Fields) != len(sv.Fields) {
   457  			return false
   458  		}
   459  		if st.PkgPath.Name() != sv.PkgPath.Name() {
   460  			return false
   461  		}
   462  		for i := range st.Fields {
   463  			tf := &st.Fields[i]
   464  			vf := &sv.Fields[i]
   465  			if tf.Name.Name() != vf.Name.Name() {
   466  				return false
   467  			}
   468  			if !typesEqual(tf.Typ, vf.Typ, seen) {
   469  				return false
   470  			}
   471  			if tf.Name.Tag() != vf.Name.Tag() {
   472  				return false
   473  			}
   474  			if tf.Offset != vf.Offset {
   475  				return false
   476  			}
   477  			if tf.Name.IsEmbedded() != vf.Name.IsEmbedded() {
   478  				return false
   479  			}
   480  		}
   481  		return true
   482  	default:
   483  		println("runtime: impossible type kind", kind)
   484  		throw("runtime: impossible type kind")
   485  		return false
   486  	}
   487  }
   488  

View as plain text