internal/runtime: impl ifaceE2I
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
|
||||
@@ -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: ""})
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user