internal/runtime: impl ifaceE2I

This commit is contained in:
visualfc
2024-12-17 12:42:30 +08:00
parent 281d29a100
commit d81c5e750d
3 changed files with 140 additions and 4 deletions

View File

@@ -2609,7 +2609,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *abi.Type, t
panic("reflect: " + op + " of method on nil interface value")
}
rcvrtype = iface.itab.typ
fn = unsafe.Pointer(&iface.itab.fun[i])
fn = unsafe.Pointer(iface.itab.fun[i])
t = (*funcType)(unsafe.Pointer(m.Typ_))
} else {
rcvrtype = v.typ()
@@ -3058,6 +3058,5 @@ func MakeMapWithSize(typ Type, n int) Value {
return Value{t, m, flag(Map)}
}
func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer) {
panic("todo: reflect.ifaceE2I")
}
//go:linkname ifaceE2I github.com/goplus/llgo/runtime/internal/runtime.IfaceE2I
func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer)

View File

@@ -4,6 +4,12 @@
package runtime
import (
"unsafe"
"github.com/goplus/llgo/runtime/abi"
)
// A boundsError represents an indexing or slicing operation gone wrong.
type boundsError struct {
x int64
@@ -114,3 +120,54 @@ func itoa(buf []byte, val uint64) []byte {
func PanicSliceConvert(x int, y int) {
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsConvert})
}
// A TypeAssertionError explains a failed type assertion.
type TypeAssertionError struct {
_interface *_type
concrete *_type
asserted *_type
missingMethod string // one method needed by Interface, missing from Concrete
}
func (*TypeAssertionError) RuntimeError() {}
func (e *TypeAssertionError) Error() string {
inter := "interface"
if e._interface != nil {
inter = e._interface.String()
}
as := e.asserted.String()
if e.concrete == nil {
return "interface conversion: " + inter + " is nil, not " + as
}
cs := e.concrete.String()
if e.missingMethod == "" {
msg := "interface conversion: " + inter + " is " + cs + ", not " + as
if cs == as {
// provide slightly clearer error message
if pkgpath(e.concrete) != pkgpath(e.asserted) {
msg += " (types from different packages)"
} else {
msg += " (types from different scopes)"
}
}
return msg
}
return "interface conversion: " + cs + " is not " + as +
": missing method " + e.missingMethod
}
func pkgpath(t *_type) string {
if u := t.Uncommon(); u != nil {
return u.PkgPath_
}
switch t.Kind() {
case abi.Struct:
st := (*structtype)(unsafe.Pointer(t))
return st.PkgPath_
case abi.Interface:
it := (*interfacetype)(unsafe.Pointer(t))
return it.PkgPath_
}
return ""
}

View File

@@ -21,6 +21,7 @@ import (
"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 {
@@ -306,11 +307,50 @@ func Interface(pkgPath string, methods []Imethod) *InterfaceType {
return ret
}
var itabTable struct {
mutex
entries []*Itab
}
type mutex sync.Mutex
func (m *mutex) Lock() {
if *(*c.Long)(unsafe.Pointer(m)) == 0 {
(*sync.Mutex)(m).Init(nil)
}
(*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)
@@ -335,6 +375,9 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab {
*c.Advance(data, i) = uintptr(fn)
}
}
if ret.fun[0] != 0 {
addItab(ret)
}
return ret
}
@@ -632,4 +675,41 @@ func shallowHashTuple(tuple []*Type) uint32 {
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: ""})
}
// -----------------------------------------------------------------------------