When converting concrete type pointers to interfaces with private methods across packages, the interface metadata's PkgPath was incorrectly set to the current compilation package instead of the interface definition package. This caused the runtime to only fill exported methods in the itab, leaving private method slots as NULL (0x0), which led to segmentation faults when calling these private methods. The fix extracts the package path from the interface's private methods (if any) instead of using the current package path. This ensures the runtime receives the correct package path for the visibility check. Fixes #1370 Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: luoliwoshang <luoliwoshang@users.noreply.github.com>
519 lines
14 KiB
Go
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 = mPkg.Path()
|
|
}
|
|
}
|
|
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
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|