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")
|
panic("reflect: " + op + " of method on nil interface value")
|
||||||
}
|
}
|
||||||
rcvrtype = iface.itab.typ
|
rcvrtype = iface.itab.typ
|
||||||
fn = unsafe.Pointer(&iface.itab.fun[i])
|
fn = unsafe.Pointer(iface.itab.fun[i])
|
||||||
t = (*funcType)(unsafe.Pointer(m.Typ_))
|
t = (*funcType)(unsafe.Pointer(m.Typ_))
|
||||||
} else {
|
} else {
|
||||||
rcvrtype = v.typ()
|
rcvrtype = v.typ()
|
||||||
@@ -3058,6 +3058,5 @@ func MakeMapWithSize(typ Type, n int) Value {
|
|||||||
return Value{t, m, flag(Map)}
|
return Value{t, m, flag(Map)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer) {
|
//go:linkname ifaceE2I github.com/goplus/llgo/runtime/internal/runtime.IfaceE2I
|
||||||
panic("todo: reflect.ifaceE2I")
|
func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer)
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/runtime/abi"
|
||||||
|
)
|
||||||
|
|
||||||
// A boundsError represents an indexing or slicing operation gone wrong.
|
// A boundsError represents an indexing or slicing operation gone wrong.
|
||||||
type boundsError struct {
|
type boundsError struct {
|
||||||
x int64
|
x int64
|
||||||
@@ -114,3 +120,54 @@ func itoa(buf []byte, val uint64) []byte {
|
|||||||
func PanicSliceConvert(x int, y int) {
|
func PanicSliceConvert(x int, y int) {
|
||||||
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsConvert})
|
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"
|
"github.com/goplus/llgo/runtime/abi"
|
||||||
c "github.com/goplus/llgo/runtime/internal/clite"
|
c "github.com/goplus/llgo/runtime/internal/clite"
|
||||||
|
"github.com/goplus/llgo/runtime/internal/clite/pthread/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type eface struct {
|
type eface struct {
|
||||||
@@ -306,11 +307,50 @@ func Interface(pkgPath string, methods []Imethod) *InterfaceType {
|
|||||||
return ret
|
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.
|
// NewItab returns a new itab.
|
||||||
func NewItab(inter *InterfaceType, typ *Type) *Itab {
|
func NewItab(inter *InterfaceType, typ *Type) *Itab {
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if i := findItab(inter, typ); i != nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
n := len(inter.Methods)
|
n := len(inter.Methods)
|
||||||
size := itabHdrSize + uintptr(n)*pointerSize
|
size := itabHdrSize + uintptr(n)*pointerSize
|
||||||
ptr := AllocU(size)
|
ptr := AllocU(size)
|
||||||
@@ -335,6 +375,9 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab {
|
|||||||
*c.Advance(data, i) = uintptr(fn)
|
*c.Advance(data, i) = uintptr(fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ret.fun[0] != 0 {
|
||||||
|
addItab(ret)
|
||||||
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,4 +675,41 @@ func shallowHashTuple(tuple []*Type) uint32 {
|
|||||||
return hash
|
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