Files
llgo/runtime/internal/runtime/z_face.go
xgopilot 79bf753c0e fix(ssa): use empty PkgPath for anonymous interfaces
Based on meeting discussion, this commit reverts the previous approach
of extracting PkgPath from interface methods. Instead, anonymous
interfaces now use an empty PkgPath string, and the runtime's methods()
function handles empty PkgPath by returning all methods.

Changes:
- Reverted unsafeInterface() to remove originType parameter
- Set anonymous interface PkgPath to empty string in abitype.go:202
- Added early return in methods() in z_face.go for empty PkgPath
- All call sites of unsafeInterface() updated to pass rawIntf directly

This approach is cleaner because anonymous interfaces don't belong to
any package, and the compiler already guarantees safety for cross-package
private method access.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-30 07:33:20 +00:00

719 lines
17 KiB
Go

/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
import (
"unsafe"
"github.com/goplus/llgo/runtime/abi"
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/pthread/sync"
)
type eface struct {
_type *_type
data unsafe.Pointer
}
type iface struct {
tab *itab
data unsafe.Pointer
}
type interfacetype = abi.InterfaceType
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs.
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
// -----------------------------------------------------------------------------
type (
Eface = eface
Iface = iface
Itab = itab
)
type Imethod = abi.Imethod
type Method = abi.Method
type FuncType = abi.FuncType
type InterfaceType = abi.InterfaceType
// ToEface converts an iface to an eface.
func ToEface(i Iface) Eface {
return Eface{i.tab._type, i.data}
}
// -----------------------------------------------------------------------------
const (
typeHdrSize = unsafe.Sizeof(abi.Type{})
arrayTypeHdrSize = unsafe.Sizeof(abi.ArrayType{})
chanTypeHdrSize = unsafe.Sizeof(abi.ChanType{})
funcTypeHdrSize = unsafe.Sizeof(abi.FuncType{})
interfaceTypeHdrSize = unsafe.Sizeof(abi.InterfaceType{})
mapTypeHdrSize = unsafe.Sizeof(abi.MapType{})
ptrTypeHdrSize = unsafe.Sizeof(abi.PtrType{})
sliceTypeHdrSize = unsafe.Sizeof(abi.SliceType{})
structTypeHdrSize = unsafe.Sizeof(abi.StructType{})
uncommonTypeHdrSize = unsafe.Sizeof(abi.UncommonType{})
methodSize = unsafe.Sizeof(abi.Method{})
pointerSize = unsafe.Sizeof(uintptr(0))
itabHdrSize = unsafe.Sizeof(itab{}) - pointerSize
)
func hdrSizeOf(kind abi.Kind) uintptr {
switch kind {
case abi.Array:
return arrayTypeHdrSize
case abi.Chan:
return chanTypeHdrSize
case abi.Func:
return funcTypeHdrSize
case abi.Interface:
return interfaceTypeHdrSize
case abi.Map:
return mapTypeHdrSize
case abi.Pointer:
return ptrTypeHdrSize
case abi.Slice:
return sliceTypeHdrSize
case abi.Struct:
return structTypeHdrSize
default:
return typeHdrSize
}
}
// NewNamed returns an uninitialized named type.
func NewNamed(pkgPath string, name string, kind abi.Kind, size uintptr, methods, ptrMethods int) *Type {
if pkgPath != "" {
name = pkgName(pkgPath) + "." + name
}
if t := rtypeList.findNamed(pkgPath, name); t != nil {
return t
}
ret := allocUncommonType(kind, size, methods, abi.TFlagUninited|abi.TFlagNamed|abi.TFlagUncommon, pkgPath)
ret.Str_ = name
ret.Hash = 9157 + hashString(pkgPath) + hashString(name)
if ptrMethods == 0 {
ret.PtrToThis_ = newPointer(ret)
} else {
ret.PtrToThis_ = allocUncommonType(abi.Pointer, pointerSize, ptrMethods, abi.TFlagUncommon, pkgPath)
setPointer((*abi.PtrType)(unsafe.Pointer(ret.PtrToThis_)), ret)
}
rtypeList.addType(ret)
return ret
}
func lastSlash(s string) int {
i := len(s) - 1
for i >= 0 && s[i] != '/' {
i--
}
return i
}
func pkgName(path string) string {
i := lastSlash(path)
return path[i+1:]
}
// InitNamed initializes an uninitialized named type.
func InitNamed(ret *Type, underlying *Type, methods, ptrMethods []Method) {
// skip initialized
if ret.TFlag&abi.TFlagUninited == 0 {
return
}
setUnderlying(ret, underlying)
if len(methods) > 0 {
setUncommon(ret, methods)
}
if len(ptrMethods) > 0 {
setUncommon(ret.PtrToThis_, ptrMethods)
}
}
func allocUncommonType(kind abi.Kind, size uintptr, methods int, tflag abi.TFlag, pkgPath string) *Type {
baseSize := hdrSizeOf(kind)
allocSize := baseSize + uncommonTypeHdrSize + uintptr(methods)*methodSize
ret := (*Type)(AllocU(allocSize))
ret.Size_ = size
ret.Kind_ = uint8(kind)
ret.TFlag = tflag
uncommon := (*abi.UncommonType)(c.Advance(unsafe.Pointer(ret), int(baseSize)))
uncommon.PkgPath_ = pkgPath
uncommon.Moff = uint32(uncommonTypeHdrSize)
return ret
}
func setUnderlying(ret *Type, underlying *Type) {
str := ret.Str_
ptr := ret.PtrToThis_
baseSize := hdrSizeOf(ret.Kind())
c.Memcpy(unsafe.Pointer(ret), unsafe.Pointer(underlying), baseSize)
ret.Str_ = str
ret.PtrToThis_ = ptr
ret.TFlag = underlying.TFlag | abi.TFlagNamed | abi.TFlagUncommon
}
func setUncommon(ret *Type, methods []Method) {
ptr := unsafe.Pointer(ret)
baseSize := hdrSizeOf(ret.Kind())
n := len(methods)
xcount := uint16(0)
for _, m := range methods {
if !m.Exported() {
break
}
xcount++
}
uncommon := (*abi.UncommonType)(c.Advance(ptr, int(baseSize)))
uncommon.Mcount = uint16(n)
uncommon.Xcount = xcount
uncommon.Moff = uint32(uncommonTypeHdrSize)
extraOff := int(baseSize + uncommonTypeHdrSize)
data := (*abi.Method)(c.Advance(ptr, extraOff))
copy(unsafe.Slice(data, n), methods)
}
// Func returns a function type.
func Func(in, out []*Type, variadic bool) *FuncType {
if t := rtypeList.findFunc(in, out, variadic); t != nil {
return t
}
ret := &FuncType{
Type: Type{
Size_: 2 * unsafe.Sizeof(uintptr(0)),
PtrBytes: 2 * pointerSize,
Align_: uint8(pointerAlign),
FieldAlign_: uint8(pointerAlign),
Kind_: uint8(abi.Func),
},
In: in,
Out: out,
}
var hash uint32 = 9091
if variadic {
hash *= 8863
ret.TFlag |= abi.TFlagVariadic
}
hash += 3*hashTuple(in) + 5*hashTuple(out)
ret.Hash = hash
ret.Str_ = funcStr(ret)
rtypeList.addType(&ret.Type)
return ret
}
// NewNamedInterface returns an interface type.
// Don't call NewNamed for named interface type.
func NewNamedInterface(pkgPath, name string) *InterfaceType {
if pkgPath != "" {
name = pkgName(pkgPath) + "." + name
}
if t := rtypeList.findNamed(pkgPath, name); t != nil {
return t.InterfaceType()
}
ret := &struct {
abi.InterfaceType
u abi.UncommonType
}{
abi.InterfaceType{
Type: Type{
Size_: unsafe.Sizeof(eface{}),
PtrBytes: 2 * pointerSize,
Hash: 9157 + hashString(pkgPath) + hashString(name),
Align_: uint8(pointerAlign),
FieldAlign_: uint8(pointerAlign),
Kind_: uint8(abi.Interface),
Str_: name,
TFlag: abi.TFlagNamed | abi.TFlagUncommon,
},
PkgPath_: pkgPath,
},
abi.UncommonType{
PkgPath_: pkgPath,
},
}
rtypeList.addType(&ret.Type)
return &ret.InterfaceType
}
func InitNamedInterface(ret *InterfaceType, methods []Imethod) {
ret.Methods = methods
if len(methods) == 0 {
ret.Equal = nilinterequal
} else {
ret.Equal = interequal
}
}
func Interface(pkgPath string, methods []Imethod) *InterfaceType {
if t := rtypeList.findInterface(pkgPath, methods); t != nil {
return t
}
ret := &abi.InterfaceType{
Type: Type{
Size_: unsafe.Sizeof(eface{}),
PtrBytes: 2 * pointerSize,
Align_: uint8(pointerAlign),
FieldAlign_: uint8(pointerAlign),
Kind_: uint8(abi.Interface),
},
PkgPath_: pkgPath,
Methods: methods,
}
if len(methods) == 0 {
ret.Equal = nilinterequal
} else {
ret.Equal = interequal
}
ret.Str_ = interfaceStr(ret)
var hash uint32 = 9103
// Hash methods.
for _, m := range methods {
// Use shallow hash on method signature to
// avoid anonymous interface cycles.
hash += 3*hashString(m.Name()) + 5*shallowHash(&m.Typ_.Type)
}
ret.Hash = hash
rtypeList.addType(&ret.Type)
return ret
}
var itabTable struct {
mutex
entries []*Itab
}
func init() {
(*sync.Mutex)(&itabTable.mutex).Init(nil)
}
type mutex sync.Mutex
func (m *mutex) Lock() {
(*sync.Mutex)(m).Lock()
}
func (m *mutex) Unlock() {
(*sync.Mutex)(m).Unlock()
}
func findItab(inter *InterfaceType, typ *Type) *Itab {
itabTable.Lock()
for _, i := range itabTable.entries {
if i.inter == inter && i._type == typ {
itabTable.Unlock()
return i
}
}
itabTable.Unlock()
return nil
}
func addItab(i *Itab) {
itabTable.Lock()
itabTable.entries = append(itabTable.entries, i)
itabTable.Unlock()
}
// NewItab returns a new itab.
func NewItab(inter *InterfaceType, typ *Type) *Itab {
if typ == nil {
return nil
}
if i := findItab(inter, typ); i != nil {
return i
}
n := len(inter.Methods)
size := itabHdrSize + uintptr(n)*pointerSize
ptr := AllocU(size)
ret := (*itab)(ptr)
ret.inter = inter
ret._type = typ
ret.hash = typ.Hash
u := typ.Uncommon()
if u == nil {
ret.fun[0] = 0
} else {
data := (*uintptr)(c.Advance(ptr, int(itabHdrSize)))
mthds := methods(u, inter.PkgPath_)
for i, m := range inter.Methods {
fn := findMethod(mthds, m)
if fn == nil {
ret.fun[0] = 0
break
}
*c.Advance(data, i) = uintptr(fn)
}
}
if ret.fun[0] != 0 {
addItab(ret)
}
return ret
}
func findMethod(mthds []abi.Method, im abi.Imethod) abi.Text {
imName := im.Name_
for _, m := range mthds {
mName := m.Name_
if mName >= imName {
if mName == imName && m.Mtyp_ == im.Typ_ {
return m.Ifn_
}
break
}
}
return nil
}
func methods(u *abi.UncommonType, from string) []abi.Method {
if from == "" {
return u.Methods()
}
if u.PkgPath_ == from {
return u.Methods()
}
return u.ExportedMethods()
}
func IfaceType(i iface) *abi.Type {
if i.tab == nil {
return nil
}
return i.tab._type
}
func IfacePtrData(i iface) unsafe.Pointer {
if i.tab == nil {
panic(errorString("invalid memory address or nil pointer dereference").Error())
}
switch i.tab._type.Kind() {
case abi.Bool, abi.Int, abi.Int8, abi.Int16, abi.Int32, abi.Int64,
abi.Uint, abi.Uint8, abi.Uint16, abi.Uint32, abi.Uint64, abi.Uintptr,
abi.Float32, abi.Float64, abi.Array, abi.Struct:
if isDirectIface(i.tab._type) {
return unsafe.Pointer(&i.data)
}
}
return i.data
}
// Implements reports whether the type V implements the interface type T.
func Implements(T, V *abi.Type) bool {
if V == nil {
return false
}
if T.Kind() != abi.Interface {
return false
}
t := (*abi.InterfaceType)(unsafe.Pointer(T))
if len(t.Methods) == 0 {
return true
}
// The same algorithm applies in both cases, but the
// method tables for an interface type and a concrete type
// are different, so the code is duplicated.
// In both cases the algorithm is a linear scan over the two
// lists - T's methods and V's methods - simultaneously.
// Since method tables are stored in a unique sorted order
// (alphabetical, with no duplicate method names), the scan
// through V's methods must hit a match for each of T's
// methods along the way, or else V does not implement T.
// This lets us run the scan in overall linear time instead of
// the quadratic time a naive search would require.
// See also ../runtime/iface.go.
if V.Kind() == abi.Interface {
v := (*abi.InterfaceType)(unsafe.Pointer(V))
i := 0
for j := 0; j < len(v.Methods); j++ {
tm := &t.Methods[i]
vm := &v.Methods[j]
if vm.Name_ == tm.Name_ && vm.Typ_ == tm.Typ_ {
if i++; i >= len(t.Methods) {
return true
}
}
}
return false
}
v := V.Uncommon()
if v == nil {
return false
}
i := 0
vmethods := v.Methods()
for j := 0; j < int(v.Mcount); j++ {
tm := &t.Methods[i]
vm := vmethods[j]
if vm.Name_ == tm.Name_ && vm.Mtyp_ == tm.Typ_ {
if i++; i >= len(t.Methods) {
return true
}
}
}
return false
}
func EfaceEqual(v, u eface) bool {
if v._type == nil || u._type == nil {
return v._type == u._type
}
if v._type != u._type {
return false
}
if isDirectIface(v._type) {
return v.data == u.data
}
if equal := v._type.Equal; equal != nil {
return equal(v.data, u.data)
}
panic(errorString("comparing uncomparable type " + v._type.String()).Error())
}
func (v eface) Kind() abi.Kind {
if v._type == nil {
return abi.Invalid
}
return v._type.Kind()
}
func (v eface) Elem() eface {
switch v.Kind() {
case abi.Interface:
var i any
tt := (*abi.InterfaceType)(unsafe.Pointer(v._type))
if len(tt.Methods) == 0 {
i = *(*any)(v.data)
} else {
i = (any)(*(*interface {
M()
})(v.data))
}
return *(*eface)(unsafe.Pointer(&i))
case abi.Pointer:
ptr := v.data
if isDirectIface(v._type) {
ptr = *(*unsafe.Pointer)(ptr)
}
if ptr == nil {
return eface{}
}
return eface{v._type.Elem(), ptr}
}
panic("invalid eface elem")
}
func SetDirectIface(t *abi.Type) {
t.Kind_ |= abi.KindDirectIface
}
func isDirectIface(t *_type) bool {
return t.Kind_&abi.KindDirectIface != 0
}
func interfaceStr(ft *abi.InterfaceType) string {
repr := make([]byte, 0, 64)
repr = append(repr, "interface {"...)
for i, t := range ft.Methods {
if i > 0 {
repr = append(repr, ';')
}
repr = append(repr, ' ')
repr = append(repr, t.Name_...)
repr = append(repr, t.Typ_.String()[4:]...)
}
if len(ft.Methods) > 0 {
repr = append(repr, ' ')
}
repr = append(repr, '}')
return string(repr)
}
func funcStr(ft *abi.FuncType) string {
repr := make([]byte, 0, 64)
repr = append(repr, "func("...)
for i, t := range ft.In {
if i > 0 {
repr = append(repr, ", "...)
}
if ft.Variadic() && i == len(ft.In)-1 {
repr = append(repr, "..."...)
repr = append(repr, (*abi.SliceType)(unsafe.Pointer(t)).Elem.String()...)
} else {
repr = append(repr, t.String()...)
}
}
repr = append(repr, ')')
out := ft.Out
if len(out) == 1 {
repr = append(repr, ' ')
} else if len(out) > 1 {
repr = append(repr, " ("...)
}
for i, t := range out {
if i > 0 {
repr = append(repr, ", "...)
}
repr = append(repr, t.String()...)
}
if len(out) > 1 {
repr = append(repr, ')')
}
return string(repr)
}
func hashTuple(tuple []*Type) uint32 {
// See go/types.identicalTypes for rationale.
n := len(tuple)
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 3 * tuple[i].Hash
}
return hash
}
// shallowHash computes a hash of t without looking at any of its
// element Types, to avoid potential anonymous cycles in the types of
// interface methods.
//
// When an unnamed non-empty interface type appears anywhere among the
// arguments or results of an interface method, there is a potential
// for endless recursion. Consider:
//
// type X interface { m() []*interface { X } }
//
// The problem is that the Methods of the interface in m's result type
// include m itself; there is no mention of the named type X that
// might help us break the cycle.
// (See comment in go/types.identical, case *Interface, for more.)
func shallowHash(t *abi.Type) uint32 {
// t is the type of an interface method (Signature),
// its params or results (Tuples), or their immediate
// elements (mostly Slice, Pointer, Basic, Named),
// so there's no need to optimize anything else.
if t.HasName() {
return 9157 + hashString(t.Uncommon().PkgPath_) + hashString(t.Str_)
}
switch t.Kind() {
case abi.Bool, abi.Int, abi.Int8, abi.Int16, abi.Int32, abi.Int64,
abi.Uint, abi.Uint8, abi.Uint16, abi.Uint32, abi.Uint64, abi.Uintptr,
abi.Float32, abi.Float64, abi.Complex64, abi.Complex128, abi.String, abi.UnsafePointer:
return 45212177 * uint32(t.Kind())
case abi.Func:
t := t.FuncType()
var hash uint32 = 604171
if t.Variadic() {
hash *= 971767
}
// The Signature/Tuple recursion is always finite
// and invariably shallow.
return hash + 1062599*shallowHashTuple(t.In) + 1282529*shallowHashTuple(t.Out)
case abi.Array:
return 1524181 + 2*uint32(t.ArrayType().Len)
case abi.Slice:
return 2690201
case abi.Struct:
return 3326489
case abi.Pointer:
return 4393139
case abi.Interface:
return 2124679 // no recursion here
case abi.Map:
return 9109
case abi.Chan:
return 9127
}
panic("shallowHash:" + t.String())
}
func shallowHashTuple(tuple []*Type) uint32 {
n := len(tuple)
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 53471161 * shallowHash(tuple[i])
}
return hash
}
func assertE2I(inter *interfacetype, t *_type) *itab {
if t == nil {
// explicit conversions require non-nil interface value.
panic(&TypeAssertionError{nil, nil, &inter.Type, ""})
}
return getitab(inter, t, false)
}
func IfaceE2I(inter *interfacetype, e eface, dst *iface) {
*dst = iface{assertE2I(inter, e._type), e.data}
}
func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
if len(inter.Methods) == 0 {
panic("internal error - misuse of itab")
}
// easy case
if typ.TFlag&abi.TFlagUncommon == 0 {
if canfail {
return nil
}
name := inter.Methods[0].Name()
panic(&TypeAssertionError{nil, typ, &inter.Type, name})
}
m := NewItab(inter, typ)
if m.fun[0] != 0 {
return m
}
if canfail {
return nil
}
panic(&TypeAssertionError{concrete: typ, asserted: &inter.Type, missingMethod: ""})
}
// -----------------------------------------------------------------------------