// resolveNameOff resolves a name offset from a base pointer.
// The (*rtype).nameOff method is a convenience wrapper for this function.
// Implemented in the runtime package.
+//
+//go:noescape
func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer
// resolveTypeOff resolves an *rtype offset from a base type.
// The (*rtype).typeOff method is a convenience wrapper for this function.
// Implemented in the runtime package.
+//
+//go:noescape
func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
func (t rtype) nameOff(off nameOff) abi.Name {
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i any) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
- return toType(eface.typ)
+ // Noescape so this doesn't make i to escape. See the comment
+ // at Value.typ for why this is safe.
+ return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
}
func (t rtype) Implements(u Type) bool {
// Using == on two Values does not compare the underlying values
// they represent.
type Value struct {
- // typ holds the type of the value represented by a Value.
- typ *abi.Type
+ // typ_ holds the type of the value represented by a Value.
+ // Access using the typ method to avoid escape of v.
+ typ_ *abi.Type
// Pointer-valued data or, if flagIndir is set, pointer to data.
// Valid when either flagIndir is set or typ.pointers() is true.
return 0
}
+func (v Value) typ() *abi.Type {
+ // Types are either static (for compiler-created types) or
+ // heap-allocated but always reachable (for reflection-created
+ // types, held in the central map). So there is no need to
+ // escape types. noescape here help avoid unnecessary escape
+ // of v.
+ return (*abi.Type)(noescape(unsafe.Pointer(v.typ_)))
+}
+
// pointer returns the underlying pointer represented by v.
// v.Kind() must be Pointer, Map, Chan, Func, or UnsafePointer
func (v Value) pointer() unsafe.Pointer {
- if v.typ.Size() != goarch.PtrSize || !v.typ.Pointers() {
+ if v.typ().Size() != goarch.PtrSize || !v.typ().Pointers() {
panic("can't call pointer on a non-pointer Value")
}
if v.flag&flagIndir != 0 {
// packEface converts v to the empty interface.
func packEface(v Value) any {
- t := v.typ
+ t := v.typ()
var i any
e := (*emptyInterface)(unsafe.Pointer(&i))
// First, fill in the data portion of the interface.
switch k {
case abi.Interface:
var eface any
- if v.typ.NumMethod() == 0 {
+ if v.typ().NumMethod() == 0 {
eface = *(*any)(v.ptr)
} else {
eface = (any)(*(*interface {
if ptr == nil {
return Value{}
}
- tt := (*ptrType)(unsafe.Pointer(v.typ))
+ tt := (*ptrType)(unsafe.Pointer(v.typ()))
typ := tt.Elem
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
}
// implemented in runtime:
+
+//go:noescape
func chanlen(unsafe.Pointer) int
+
+//go:noescape
func maplen(unsafe.Pointer) int
// Len returns v's length.
k := v.kind()
switch k {
case abi.Array:
- tt := (*arrayType)(unsafe.Pointer(v.typ))
+ tt := (*arrayType)(unsafe.Pointer(v.typ()))
return int(tt.Len)
case abi.Chan:
return chanlen(v.pointer())
// NumMethod returns the number of exported methods in the value's method set.
func (v Value) numMethod() int {
- if v.typ == nil {
+ if v.typ() == nil {
panic(&ValueError{"reflectlite.Value.NumMethod", abi.Invalid})
}
- return v.typ.NumMethod()
+ return v.typ().NumMethod()
}
// Set assigns x to the value v.
if v.kind() == abi.Interface {
target = v.ptr
}
- x = x.assignTo("reflectlite.Set", v.typ, target)
+ x = x.assignTo("reflectlite.Set", v.typ(), target)
if x.flag&flagIndir != 0 {
- typedmemmove(v.typ, v.ptr, x.ptr)
+ typedmemmove(v.typ(), v.ptr, x.ptr)
} else {
*(*unsafe.Pointer)(v.ptr) = x.ptr
}
panic(&ValueError{"reflectlite.Value.Type", abi.Invalid})
}
// Method values not supported.
- return toRType(v.typ)
+ return toRType(v.typ())
}
/*
*/
// implemented in package runtime
+
+//go:noescape
func unsafe_New(*abi.Type) unsafe.Pointer
// ValueOf returns a new Value initialized to the concrete value
if i == nil {
return Value{}
}
-
- // TODO: Maybe allow contents of a Value to live on the stack.
- // For now we make the contents always escape to the heap. It
- // makes life easier in a few places (see chanrecv/mapassign
- // comment below).
- escapes(i)
-
return unpackEface(i)
}
// }
switch {
- case directlyAssignable(dst, v.typ):
+ case directlyAssignable(dst, v.typ()):
// Overwrite type so that they match.
// Same memory layout, so no harm done.
fl := v.flag&(flagAddr|flagIndir) | v.flag.ro()
fl |= flag(dst.Kind())
return Value{dst, v.ptr, fl}
- case implements(dst, v.typ):
+ case implements(dst, v.typ()):
if target == nil {
target = unsafe_New(dst)
}
}
// Failed.
- panic(context + ": value of type " + toRType(v.typ).String() + " is not assignable to type " + toRType(dst).String())
+ panic(context + ": value of type " + toRType(v.typ()).String() + " is not assignable to type " + toRType(dst).String())
}
// arrayAt returns the i-th element of p,
b bool
x any
}
+
+//go:nosplit
+func noescape(p unsafe.Pointer) unsafe.Pointer {
+ x := uintptr(p)
+ return unsafe.Pointer(x ^ 0)
+}