Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions internal/abi/type_llgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,28 +348,14 @@ func (t *UncommonType) Methods() []Method {
if t.Mcount == 0 {
return nil
}
methodsPtr := addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0")
methods := make([]Method, t.Mcount)
for i := 0; i < int(t.Mcount); i++ {
elemPtr := addChecked(methodsPtr, uintptr(i)*unsafe.Sizeof(Method{}), "accessing method element")
elem := (*Method)(elemPtr)
methods[i] = *elem
}
return methods
return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount]
}

func (t *UncommonType) ExportedMethods() []Method {
if t.Xcount == 0 {
return nil
}
mthdsPtr := addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0")
mthds := make([]Method, t.Xcount)
for i := 0; i < int(t.Xcount); i++ {
elemPtr := addChecked(mthdsPtr, uintptr(i)*unsafe.Sizeof(Method{}), "accessing method element")
elem := (*Method)(elemPtr)
mthds[i] = *elem
}
return mthds
return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注释掉的旧实现(原 Methods()ExportedMethods() 循环拷贝版本)应当删除。新的切片转换实现已经正确,保留注释代码只会增加干扰。

}

// Imethod represents a method on an interface type
Expand Down
1 change: 1 addition & 0 deletions methodof.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func resizeMethod(typ reflect.Type, mcount int, xcount int) error {
return fmt.Errorf("too many methods of %v", typ)
}
ut.Xcount = uint16(xcount)
ut.Mcount = uint16(mcount)
return nil
}

Expand Down
4 changes: 4 additions & 0 deletions reflectx.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,7 @@ func MethodByName(typ reflect.Type, name string) (m reflect.Method, ok bool) {
m, ok = rtypeMethodByNameX(totype(typ), name)
return
}

func MethodX(typ reflect.Type, i int) reflect.Method {
return rtypeMethodX(totype(typ), i)
}
4 changes: 0 additions & 4 deletions rtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,6 @@ func DumpType(w io.Writer, typ reflect.Type) {
}
}

func MethodX(typ reflect.Type, i int) reflect.Method {
return rtypeMethodX(totype(typ), i)
}

func rtypeMethodX(t *rtype, i int) (m reflect.Method) {
if reflect.Kind(t.Kind()) == reflect.Interface {
return toType(t).Method(i)
Expand Down
252 changes: 226 additions & 26 deletions rtype_llgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strconv"
"strings"
"unsafe"

"github.com/goplus/reflectx/internal/abi"
)

func NamedTypeOf(pkgpath string, name string, from reflect.Type) reflect.Type {
Expand Down Expand Up @@ -146,6 +148,9 @@ func closureOf(ftyp *funcType) *rtype
//go:linkname toFuncType reflect.toFuncType
func toFuncType(ftyp *structType) *funcType

//go:linkname makeFunc reflect.makeFunc
func makeFunc(typ reflect.Type, method bool, fn func(args []reflect.Value) (results []reflect.Value)) reflect.Value

func rtypeMethodX(t *rtype, i int) (m reflect.Method) {
if reflect.Kind(t.Kind()) == reflect.Interface {
return toType(t).Method(i)
Expand Down Expand Up @@ -181,7 +186,6 @@ func rtypeMethodX(t *rtype, i int) (m reflect.Method) {

func newType(pkg string, name string, styp reflect.Type, mcount int, xcount int) (*rtype, []method) {
var rt *rtype
var fnoff uint32
var tt reflect.Value
ort := totype(styp)
skind := styp.Kind()
Expand Down Expand Up @@ -246,13 +250,9 @@ func newType(pkg string, name string, styp reflect.Type, mcount int, xcount int)
st.Elem = ost.Elem
st.Dir = ost.Dir
case reflect.Func:
numIn := styp.NumIn()
numOut := styp.NumOut()
narg := numIn + numOut
tt = reflect.New(reflect.StructOf([]reflect.StructField{
{Name: "S", Type: reflect.TypeOf(funcType{})},
{Name: "U", Type: reflect.TypeOf(uncommonType{})},
{Name: "N", Type: reflect.ArrayOf(narg, reflect.TypeOf((*rtype)(nil)))},
{Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))},
}))
st := (*funcType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr()))
Expand All @@ -275,6 +275,7 @@ func newType(pkg string, name string, styp reflect.Type, mcount int, xcount int)
{Name: "M", Type: reflect.ArrayOf(mcount, reflect.TypeOf(method{}))},
}))
}

rt = (*rtype)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr()))
rt.Size_ = ort.Size_
rt.TFlag = ort.TFlag | tflagUncommon
Expand All @@ -291,39 +292,101 @@ func newType(pkg string, name string, styp reflect.Type, mcount int, xcount int)
ut.Moff = uint32(unsafe.Sizeof(uncommonType{}))
if skind == reflect.Interface {
return rt, nil
} else if skind == reflect.Func {
ut.Moff += fnoff
//return rt, tt.Elem().Field(3).Slice(0, mcount).Interface().([]method)
data := toWord(tt.Elem().Field(3).Slice(0, mcount).Interface())
return rt, *(*[]method)(data)
}
//return rt, tt.Elem().Field(2).Slice(0, mcount).Interface().([]method)
data := toWord(tt.Elem().Field(2).Slice(0, mcount).Interface())
return rt, *(*[]method)(data)
}

func toWord(i interface{}) unsafe.Pointer {
return (*emptyInterface)(unsafe.Pointer(&i)).word
}
return rt, tt.Elem().Field(2).Slice(0, mcount).Interface().([]method)
}

func (ctx *Context) Reset() {
ctx.nAllocateError = 0
ctx.embedLookupCache = make(map[reflect.Type]reflect.Type)
ctx.structLookupCache = make(map[string][]reflect.Type)
ctx.interfceLookupCache = make(map[string]reflect.Type)
ctx.methodIndexList = make(map[int][]int)
ctx.fnHasImethod = nil
}

func resetAll() {
globalMethodCache = make(map[int]*ifnValue)
}

func newMethodSet(styp reflect.Type, maxmfunc, maxpfunc int) reflect.Type {
// rt, _ := newType("", "", styp, maxmfunc, 0)
// prt, _ := newType("", "", PtrTo(styp), maxpfunc, 0)
// rt.PtrToThis = resolveReflectType(prt)
// (*ptrType)(unsafe.Pointer(prt)).Elem = rt
// setTypeName(rt, styp.PkgPath(), styp.Name())
// prt.Uncommon().PkgPath = resolveReflectName(newName(styp.PkgPath(), "", false))
// return toType(rt)
panic("TODO newMethodSet")
rt, _ := newType("", "", styp, maxmfunc, 0)
prt, _ := newType("", "", reflect.PtrTo(styp), maxpfunc, 0)
rt.PtrToThis_ = prt
(*ptrType)(unsafe.Pointer(prt)).Elem = rt
setTypeName(rt, styp.PkgPath(), styp.Name())
prt.Uncommon().PkgPath_ = styp.PkgPath()
return toType(rt)
}

func resizeMethod(typ reflect.Type, mcount int, xcount int) error {
rt := totype(typ)
ut := rt.Uncommon()
if ut == nil {
return fmt.Errorf("not found uncommonType of %v", typ)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resizeMethod 只更新 ut.Xcount,不更新 ut.Mcount。若实际方法数少于分配时的 maxmfunc(即 ut.Mcount),运行时遍历 ut.Methods() 会读取未初始化的 method 槽。应在此同时更新 ut.Mcount = uint16(mcount)

if uint16(mcount) > ut.Mcount {
return fmt.Errorf("too many methods of %v", typ)
}
Comment on lines +328 to +330

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Using uint16(mcount) > ut.Mcount can lead to an integer overflow bypass if mcount is greater than 65535 (e.g., 65536 casts to 0, which is less than ut.Mcount). Since mcount is an int and always non-negative, comparing mcount > int(ut.Mcount) is safer and prevents any overflow issues.

\tif mcount > int(ut.Mcount) {\n\t\treturn fmt.Errorf(\"too many methods of %v\", typ)\n\t}

ut.Xcount = uint16(xcount)
ut.Mcount = uint16(mcount)
return nil
}

type textOff = abi.Text

var globalMethodCache = make(map[int]*ifnValue)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The globalMethodCache map is defined and read from, but it is never populated. This means the cache lookup will always miss. If caching is intended, please implement the logic to populate the cache. Note that if setMethodSet can be called concurrently, you must ensure thread-safety (e.g., by using a sync.RWMutex or sync.Map).


type ifnValue struct {
method method
pmethod method
}

func createMethod(typ reflect.Type, ptyp reflect.Type, m Method, index int) (mtyp *abi.Type, tfn, ptfn reflect.Value, mfn, pmfn reflect.Value) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The index parameter is never used within the createMethod function. It should be removed from the function signature to simplify the code.

Suggested change
func createMethod(typ reflect.Type, ptyp reflect.Type, m Method, index int) (mtyp *abi.Type, tfn, ptfn reflect.Value, mfn, pmfn reflect.Value) {
func createMethod(typ reflect.Type, ptyp reflect.Type, m Method) (mtyp *abi.Type, tfn, ptfn reflect.Value, mfn, pmfn reflect.Value) {

var in []reflect.Type
var out []reflect.Type
var ntyp reflect.Type
in, out, ntyp, _, _ = parserMethodType(m.Type, nil)
mtyp = totype(ntyp)
var ftyp reflect.Type
if m.Pointer {
ftyp = reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, m.Type.IsVariadic())
} else {
ftyp = reflect.FuncOf(append([]reflect.Type{typ}, in...), out, m.Type.IsVariadic())
}

if m.Pointer {
ptfn = makeFunc(ftyp, false, m.Func)
pmfn = makeFunc(ftyp, true, m.Func)
} else {
tfn = makeFunc(ftyp, false, m.Func)
ftyp = reflect.FuncOf(append([]reflect.Type{ptyp}, in...), out, m.Type.IsVariadic())
ptfn = makeFunc(ftyp, false, func(args []reflect.Value) []reflect.Value {
args[0] = args[0].Elem()
return m.Func(args)
})
mfn = makeFunc(ftyp, true, func(args []reflect.Value) []reflect.Value {
args[0] = args[0].Elem()
return m.Func(args)
})
pmfn = mfn
}
return
}

func (ctx *Context) hasImethod(typ reflect.Type, method Method) bool {
if ctx.fnHasImethod != nil {
return ctx.fnHasImethod(typ, method)
}
return true
}

//go:linkname gcEnable C.GC_enable
func gcEnable()

//go:linkname gcDisable C.GC_disable
func gcDisable()

func (ctx *Context) setMethodSet(typ reflect.Type, methods []Method, sortMethods bool) error {
if sortMethods {
sort.Slice(methods, func(i, j int) bool {
Expand All @@ -334,6 +397,74 @@ func (ctx *Context) setMethodSet(typ reflect.Type, methods []Method, sortMethods
return n < 0
})
}
var mcount, pcount int
var xcount, pxcount int
pcount = len(methods)
for _, m := range methods {
isexport := methodIsExported(m.Name)
if isexport {
pxcount++
}
if !m.Pointer {
if isexport {
xcount++
}
mcount++
}
}
ptyp := PtrTo(typ)
if err := resizeMethod(typ, mcount, xcount); err != nil {
return err
}
if err := resizeMethod(ptyp, pcount, pxcount); err != nil {
return err
}
rt := totype(typ)
prt := totype(ptyp)

ms := rtypeMethods(rt)
pms := rtypeMethods(prt)

gcDisable()
defer gcEnable()

var index int
for i, m := range methods {
if m.FuncId > 0 {
if pv, ok := globalMethodCache[m.FuncId]; ok {
pms[i] = pv.pmethod
if !m.Pointer {
ms[index] = pv.method
index++
}
continue
}
}
var mname string
if !methodIsExported(m.Name) {
mname = m.PkgPath + "." + m.Name
} else {
mname = m.Name
}
mtyp, tfn, ptfn, mfn, pmfn := createMethod(typ, ptyp, m, index)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove the unused index argument from the createMethod call.

Suggested change
mtyp, tfn, ptfn, mfn, pmfn := createMethod(typ, ptyp, m, index)
\t\tmtyp, tfn, ptfn, mfn, pmfn := createMethod(typ, ptyp, m)

pms[i].Name_ = mname
pms[i].Mtyp_ = mtyp.FuncType()
pms[i].Tfn_ = textOff(ptfn.UnsafePointer())
pms[i].Ifn_ = textOff(pmfn.UnsafePointer())
if m.FuncId > 0 {
globalMethodCache[m.FuncId] = &ifnValue{pmethod: pms[i]}
}
if !m.Pointer {
ms[index].Name_ = mname
ms[index].Mtyp_ = mtyp.FuncType()
ms[index].Tfn_ = textOff(tfn.UnsafePointer())
ms[index].Ifn_ = textOff(mfn.UnsafePointer())
if m.FuncId > 0 {
globalMethodCache[m.FuncId].method = ms[index]
}
index++
}
}
return nil
}

Expand Down Expand Up @@ -398,3 +529,72 @@ func (ctx *Context) newInterface(methods []reflect.Method) reflect.Type {
ctx.interfceLookupCache[str] = typ
return typ
}

func SetUnderlying(typ reflect.Type, styp reflect.Type) {
rt := totype(typ)
ort := totype(styp)
switch styp.Kind() {
case reflect.Struct:
st := (*structType)(unsafe.Pointer(rt))
ost := (*structType)(unsafe.Pointer(ort))
st.Fields = ost.Fields
case reflect.Ptr:
st := (*ptrType)(unsafe.Pointer(rt))
ost := (*ptrType)(unsafe.Pointer(ort))
st.Elem = ost.Elem
case reflect.Slice:
st := (*sliceType)(unsafe.Pointer(rt))
ost := (*sliceType)(unsafe.Pointer(ort))
st.Elem = ost.Elem
case reflect.Array:
st := (*arrayType)(unsafe.Pointer(rt))
ost := (*arrayType)(unsafe.Pointer(ort))
st.Elem = ost.Elem
st.Slice = ost.Slice
st.Len = ost.Len
case reflect.Chan:
st := (*chanType)(unsafe.Pointer(rt))
ost := (*chanType)(unsafe.Pointer(ort))
st.Elem = ost.Elem
st.Dir = ost.Dir
case reflect.Interface:
st := (*interfaceType)(unsafe.Pointer(rt))
ost := (*interfaceType)(unsafe.Pointer(ort))
st.Methods = ost.Methods
case reflect.Map:
st := (*mapType)(unsafe.Pointer(rt))
ost := (*mapType)(unsafe.Pointer(ort))
cloneMap(st, ost)
case reflect.Func:
st := (*funcType)(unsafe.Pointer(rt))
ost := (*funcType)(unsafe.Pointer(ort))
st.In = ost.In
st.Out = ost.Out
}
rt.Size_ = ort.Size_
rt.TFlag |= tflagUncommon | tflagExtraStar | tflagNamed
rt.Kind_ = ort.Kind_
rt.Align_ = ort.Align_
rt.FieldAlign_ = ort.FieldAlign_
rt.GCData = ort.GCData
rt.PtrBytes = ort.PtrBytes
rt.Equal = ort.Equal
//rt.Str = resolveReflectName(rtype_nameOff(ort, ort.Str))
if isRegularMemory(typ) {
rt.TFlag |= tflagRegularMemory
}
}

// icall stat
func IcallStat() (capacity int, allocate int, aviable int) {
return
}

// icall global cached
func IcallCached() int {
return 0
}

func (ctx *Context) IcallAlloc() int {
return 0
}