Files
llgo/ssa/abitype.go
xgopilot b62dafbc3a fix(ssa): use abi.PathOf() for correct package path in interface metadata
Fixes os.ReadDir segfault caused by package path mismatch between llgo
overlay packages and canonical standard library paths.

The issue occurred in: os.ReadDir → sort.Slice → reflectlite.Swapper
where internal/reflectlite.Type interface has private methods common()
and uncommon(). Using mPkg.Path() returned the overlay path
'github.com/goplus/llgo/runtime/internal/lib/internal/reflectlite'
instead of the canonical 'internal/reflectlite', causing runtime to
only fill public methods and leave private method pointers NULL.

Changed to use abi.PathOf() which returns the correct canonical package
path for matching, ensuring all interface methods (both public and
private) are properly filled in the interface table.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 11:32:38 +00:00

519 lines
14 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 ssa
import (
"go/token"
"go/types"
"unsafe"
"github.com/goplus/llgo/ssa/abi"
"github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil"
)
// -----------------------------------------------------------------------------
// abiBasic returns the abi type of the specified basic kind.
func (b Builder) abiBasic(t *types.Basic) func() Expr {
/*
TODO(xsw):
return b.abiExtern(abi.BasicName(t))
*/
return func() Expr {
kind := int(abi.BasicKind(t))
dk, _, _ := abi.DataKindOf(t, 0, b.Prog.is32Bits)
switch dk {
case abi.Integer, abi.BitCast, abi.Pointer:
const kindDirectIface = 1 << 5
kind |= kindDirectIface
}
return b.InlineCall(b.Pkg.rtFunc("Basic"), b.Prog.Val(kind))
}
}
/*
func (b Builder) abiExtern(name string) Expr {
g := b.Pkg.NewVarFrom(name, b.Prog.AbiTypePtrPtr())
return b.Load(g.Expr)
}
*/
func (b Builder) abiTypeOf(t types.Type) func() Expr {
switch t := t.(type) {
case *types.Basic:
return b.abiBasic(t)
case *types.Pointer:
return b.abiPointerOf(t)
case *types.Struct:
return b.abiStructOf(t)
case *types.Named:
if _, ok := t.Underlying().(*types.Interface); ok {
return b.abiNamedInterfaceOf(t)
}
return b.abiNamedOf(t)
case *types.Interface:
return b.abiInterfaceOf(t)
case *types.Signature:
return b.abiFuncOf(t)
case *types.Slice:
return b.abiSliceOf(t)
case *types.Array:
return b.abiArrayOf(t)
case *types.Chan:
return b.abiChanOf(t)
case *types.Map:
return b.abiMapOf(t)
case *types.Alias:
return b.abiTypeOf(types.Unalias(t))
}
panic("todo")
}
func (b Builder) abiTupleOf(t *types.Tuple) func() Expr {
n := t.Len()
tuple := make([]func() Expr, n)
for i := 0; i < n; i++ {
tuple[i] = b.abiTypeOf(t.At(i).Type())
}
return func() Expr {
prog := b.Prog
tSlice := prog.Slice(prog.AbiTypePtr())
elts := make([]Expr, n)
for i := 0; i < n; i++ {
elts[i] = tuple[i]()
}
return b.SliceLit(tSlice, elts...)
}
}
// func Func(in, out []*Type, variadic bool)
func (b Builder) abiFuncOf(sig *types.Signature) func() Expr {
params := b.abiTupleOf(sig.Params())
results := b.abiTupleOf(sig.Results())
return func() Expr {
prog := b.Prog
pkg := b.Pkg
fn := pkg.rtFunc("Func")
variadic := prog.Val(sig.Variadic())
return b.Call(fn, params(), results(), variadic)
}
}
// Imethod{name string, typ *FuncType}
func (b Builder) abiImethodOf(mName string, typ Expr) Expr {
prog := b.Prog
name := b.Str(mName)
return b.aggregateValue(prog.rtType("Imethod"), name.impl, typ.impl)
}
func (b Builder) abiMethods(t *types.Named) (ret, pret int) {
methods := typeutil.IntuitiveMethodSet(t, nil)
pret = len(methods)
for _, m := range methods {
if _, ok := m.Recv().(*types.Pointer); ok {
continue
}
ret++
}
return
}
// Method{name string, typ *FuncType, ifn, tfn abi.Text}
func (b Builder) abiMethodOf(mPkg *types.Package, mName string, mSig *types.Signature /*, bg Background = InGo */) (mthd, ptrMthd Expr) {
prog := b.Prog
name := b.Str(mName).impl
if !token.IsExported(mName) {
name = b.Str(abi.FullName(mPkg, mName)).impl
}
abiSigGo := types.NewSignatureType(nil, nil, nil, mSig.Params(), mSig.Results(), mSig.Variadic())
abiSig := prog.FuncDecl(abiSigGo, InGo).raw.Type
abiTyp := b.abiType(abiSig)
abiTypImpl := abiTyp.impl
recv := mSig.Recv()
recvType := recv.Type()
if _, ok := recvType.(*types.Pointer); ok {
ptrMthd, _ = b.abiMthd(mPkg, mName, mSig, name, abiTypImpl, llvm.Value{})
return
}
ptrRecv := types.NewVar(0, nil, "", types.NewPointer(recvType))
ptrSig := types.NewSignatureType(ptrRecv, nil, nil, mSig.Params(), mSig.Results(), mSig.Variadic())
ptrMthd, ifn := b.abiMthd(mPkg, mName, ptrSig, name, abiTypImpl, llvm.Value{})
mthd, _ = b.abiMthd(mPkg, mName, mSig, name, abiTypImpl, ifn)
return
}
func (b Builder) abiMthd(mPkg *types.Package, mName string, mSig *types.Signature, name, abiTyp, ifn llvm.Value) (ret Expr, tfn llvm.Value) {
fullName := FuncName(mPkg, mName, mSig.Recv(), false)
if mSig.TypeParams().Len() > 0 || mSig.RecvTypeParams().Len() > 0 {
if !b.Pkg.Prog.FuncCompiled(fullName) {
return
}
}
if b.Pkg.fnlink != nil {
fullName = b.Pkg.fnlink(fullName)
}
tfn = b.Pkg.NewFunc(fullName, mSig, InGo).impl // TODO(xsw): use rawType to speed up
if ifn.IsNil() {
ifn = tfn
}
ret = b.aggregateValue(b.Prog.rtType("Method"), name, abiTyp, ifn, tfn)
return
}
// func Interface(pkgPath, name string, methods []abi.Imethod)
func (b Builder) abiInterfaceOf(t *types.Interface) func() Expr {
n := t.NumMethods()
typs := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
typs[i] = b.abiType(m.Type())
}
return func() Expr {
prog := b.Prog
methods := make([]Expr, n)
pkgPath := ""
for i := 0; i < n; i++ {
m := t.Method(i)
mName := m.Name()
if !token.IsExported(mName) {
if pkgPath == "" {
if mPkg := m.Pkg(); mPkg != nil {
pkgPath = abi.PathOf(mPkg)
}
}
mName = abi.FullName(m.Pkg(), mName)
}
methods[i] = b.abiImethodOf(mName, typs[i])
}
pkg := b.Pkg
if pkgPath == "" {
pkgPath = pkg.Path()
}
fn := pkg.rtFunc("Interface")
tSlice := lastParamType(prog, fn)
methodSlice := b.SliceLit(tSlice, methods...)
return b.Call(fn, b.Str(pkgPath), methodSlice)
}
}
func (b Builder) abiInitNamedInterface(ret Expr, t *types.Interface) func() Expr {
n := t.NumMethods()
typs := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
typs[i] = b.abiType(m.Type())
}
return func() Expr {
prog := b.Prog
methods := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
mName := m.Name()
if !token.IsExported(mName) {
mName = abi.FullName(m.Pkg(), mName)
}
methods[i] = b.abiImethodOf(mName, typs[i])
}
fn := b.Pkg.rtFunc("InitNamedInterface")
tSlice := lastParamType(prog, fn)
methodSlice := b.SliceLit(tSlice, methods...)
return b.Call(fn, ret, methodSlice)
}
}
// func NewNamed(kind abi.Kind, methods, ptrMethods int)
func (b Builder) abiNamedOf(t *types.Named) func() Expr {
expr := b.abiNamed(t)
return func() Expr {
return expr
}
}
func (b Builder) abiNamed(t *types.Named) Expr {
pkg := b.Pkg
tunder := t.Underlying()
kind := int(abi.UnderlyingKind(tunder))
size := b.sizeof(tunder)
numMethods, numPtrMethods := b.abiMethods(t)
newNamed := pkg.rtFunc("NewNamed")
obj := t.Obj()
expr := b.Call(newNamed, b.Str(abi.PathOf(obj.Pkg())), b.Str(abi.NamedName(t)), b.Prog.Val(kind), b.Prog.IntVal(uint64(size), b.Prog.Uintptr()), b.Prog.Val(numMethods), b.Prog.Val(numPtrMethods))
return expr
}
func (b Builder) abiNamedInterfaceOf(t *types.Named) func() Expr {
obj := t.Obj()
fn := b.Pkg.rtFunc("NewNamedInterface")
expr := b.Call(fn, b.Str(abi.PathOf(obj.Pkg())), b.Str(obj.Name()))
return func() Expr {
return expr
}
}
func (b Builder) sizeof(t types.Type) int64 {
sizes := (*goProgram)(unsafe.Pointer(b.Prog))
return sizes.Sizeof(t)
}
// func InitNamed(ret *Type, pkgPath, name string, underlying *Type, methods, ptrMethods []Method)
func (b Builder) abiInitNamed(ret Expr, t *types.Named) func() Expr {
under := b.abiType(t.Underlying())
return func() Expr {
pkg := b.Pkg
prog := b.Prog
var initNamed = pkg.rtFunc("InitNamed")
var tSlice = lastParamType(prog, initNamed)
mset := typeutil.IntuitiveMethodSet(t, nil)
n := len(mset)
var methods, ptrMethods Expr
if n == 0 {
methods = prog.Zero(tSlice)
ptrMethods = methods
} else {
var mthds []Expr
var ptrMthds = make([]Expr, 0, n)
for i := 0; i < n; i++ {
var mPkg *types.Package
var mSig *types.Signature
m := mset[i]
obj := m.Obj()
mName := obj.Name()
if token.IsExported(mName) {
mPkg = t.Obj().Pkg()
mSig = m.Type().(*types.Signature)
} else {
mPkg = obj.Pkg()
mSig = obj.Type().(*types.Signature)
}
mthd, ptrMthd := b.abiMethodOf(mPkg, mName, mSig)
if !mthd.IsNil() {
mthds = append(mthds, mthd)
}
if !ptrMthd.IsNil() {
ptrMthds = append(ptrMthds, ptrMthd)
}
}
if len(mthds) > 0 {
methods = b.SliceLit(tSlice, mthds...)
} else {
methods = prog.Zero(tSlice)
}
if len(ptrMthds) > 0 {
ptrMethods = b.SliceLit(tSlice, ptrMthds...)
} else {
ptrMethods = prog.Zero(tSlice)
}
}
return b.Call(initNamed, ret, under, methods, ptrMethods)
}
}
func (b Builder) abiPointerOf(t *types.Pointer) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
return b.Call(b.Pkg.rtFunc("PointerTo"), elem())
}
}
func (b Builder) abiSliceOf(t *types.Slice) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
return b.Call(b.Pkg.rtFunc("SliceOf"), elem())
}
}
func (b Builder) abiArrayOf(t *types.Array) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
n := b.Prog.IntVal(uint64(t.Len()), b.Prog.Uintptr())
return b.Call(b.Pkg.rtFunc("ArrayOf"), n, elem())
}
}
func (b Builder) abiChanOf(t *types.Chan) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
dir, s := abi.ChanDir(t.Dir())
return b.Call(b.Pkg.rtFunc("ChanOf"), b.Prog.IntVal(uint64(dir), b.Prog.Int()), b.Str(s), elem())
}
}
func (b Builder) abiMapOf(t *types.Map) func() Expr {
key := b.abiTypeOf(t.Key())
elem := b.abiTypeOf(t.Elem())
sizes := (*goProgram)(unsafe.Pointer(b.Prog))
bucket := b.abiTypeOf(abi.MapBucketType(t, sizes))
flags := abi.MapTypeFlags(t, sizes)
return func() Expr {
return b.Call(b.Pkg.rtFunc("MapOf"), key(), elem(), bucket(), b.Prog.Val(flags))
}
}
// func StructField(name string, typ *abi.Type, off uintptr, tag string, embedded bool)
// func Struct(pkgPath string, size uintptr, fields []abi.StructField)
func (b Builder) abiStructOf(t *types.Struct) func() Expr {
n := t.NumFields()
typs := make([]func() Expr, n)
for i := 0; i < n; i++ {
f := t.Field(i)
typs[i] = b.abiTypeOf(f.Type())
}
return func() Expr {
pkg := b.Pkg
prog := b.Prog
flds := make([]Expr, n)
strucAbi := pkg.rtFunc("Struct")
sfAbi := pkg.rtFunc("StructField")
tStruc := prog.rawType(t)
for i := 0; i < n; i++ {
f := t.Field(i)
off := uintptr(prog.OffsetOf(tStruc, i))
name := b.Str(f.Name())
tag := b.Str(t.Tag(i))
embedded := prog.Val(f.Embedded())
flds[i] = b.Call(sfAbi, name, typs[i](), prog.Val(off), tag, embedded)
}
pkgPath := b.Str(pkg.Path())
tSlice := lastParamType(prog, strucAbi)
fldSlice := b.SliceLit(tSlice, flds...)
size := prog.IntVal(prog.SizeOf(tStruc), prog.Uintptr())
return b.Call(strucAbi, pkgPath, size, fldSlice)
}
}
func lastParamType(prog Program, fn Expr) Type {
params := fn.raw.Type.(*types.Signature).Params()
return prog.rawType(params.At(params.Len() - 1).Type())
}
// -----------------------------------------------------------------------------
func (p Package) patchType(t types.Type) types.Type {
switch t := t.(type) {
case *types.Pointer:
return types.NewPointer(p.patchType(t.Elem()))
}
return p.patch(t)
}
func (p Package) abiTypeInit(g Global, t types.Type, pub bool) {
b := p.afterBuilder()
if p.patch != nil {
t = p.patchType(t)
}
tabi := b.abiTypeOf(t)
expr := g.Expr
var eq Expr
var blks []BasicBlock
if pub {
eq = b.BinOp(token.EQL, b.Load(expr), b.Prog.Nil(expr.Type))
blks = b.Func.MakeBlocks(2)
b.If(eq, blks[0], blks[1])
b.SetBlockEx(blks[0], AtEnd, false)
}
vexpr := tabi()
prog := p.Prog
if kind, _, _ := abi.DataKindOf(t, 0, prog.is32Bits); kind == abi.Pointer {
b.InlineCall(b.Pkg.rtFunc("SetDirectIface"), vexpr)
}
b.Store(expr, vexpr)
if pub {
b.Jump(blks[1])
b.SetBlockEx(blks[1], AtEnd, false)
b.blk.last = blks[1].last
}
if t, ok := t.(*types.Named); ok {
if iface, ok := t.Underlying().(*types.Interface); ok {
tabi = b.abiInitNamedInterface(vexpr, iface)
} else {
tabi = b.abiInitNamed(vexpr, t)
}
if pub {
blks = b.Func.MakeBlocks(2)
b.If(eq, blks[0], blks[1])
b.SetBlockEx(blks[0], AtEnd, false)
}
tabi()
if pub {
b.Jump(blks[1])
b.SetBlockEx(blks[1], AtEnd, false)
b.blk.last = blks[1].last
}
}
}
// abiType returns the abi type of the specified type.
func (b Builder) abiType(t types.Type) Expr {
switch t := t.(type) {
case *types.Pointer:
b.checkAbi(t.Elem())
case *types.Array:
b.checkAbi(t.Elem())
case *types.Map:
b.checkAbi(t.Key())
b.checkAbi(t.Elem())
case *types.Slice:
b.checkAbi(t.Elem())
case *types.Chan:
b.checkAbi(t.Elem())
case *types.Struct:
for i := 0; i < t.NumFields(); i++ {
b.checkAbi(t.Field(i).Type())
}
case *types.Interface:
for i := 0; i < t.NumMethods(); i++ {
b.checkAbi(t.Method(i).Type())
}
case *types.Signature:
for i := 0; i < t.Params().Len(); i++ {
b.checkAbi(t.Params().At(i).Type())
}
for i := 0; i < t.Results().Len(); i++ {
b.checkAbi(t.Results().At(i).Type())
}
}
g := b.loadType(t)
return b.Load(g.Expr)
}
func (b Builder) checkAbi(t types.Type) {
if b.Pkg.chkabi[t] {
return
}
b.abiType(t)
}
func (b Builder) loadType(t types.Type) Global {
b.Pkg.chkabi[t] = true
pkg := b.Pkg
name, pub := pkg.abi.TypeName(t)
g := pkg.VarOf(name)
if g == nil {
prog := b.Prog
g = pkg.doNewVar(name, prog.AbiTypePtrPtr())
g.InitNil()
g.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
pkg.abiTypeInit(g, t, pub)
}
return g
}
// -----------------------------------------------------------------------------