2

I'm to create an object (whatever it is) satisfying certain interface dynamically. Current reflect.StructOf creates structs but without adding methods. So I read the source code and tried to add a function in reflect package and I got the following error,

unexpected fault address 0xc0000b8120
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x2 addr=0xc0000b8120 pc=0xc0000b8120]

Here is the file I added into the reflect package,

package reflect

import (
    "sort"
    "unsafe"
)

type InterfaceMethod struct {
    Name string
    Func Value
}

func InterfaceOf(funcs []InterfaceMethod) Value {
    if len(funcs) == 0 {
        return New(TypeOf("Interface {}"))
    }

    var (
        repr     = []byte("struct { /* non-invertible */ }")
        hash     = fnv1(0, []byte("struct { /* non-invertible */ }")...)
        size     uintptr
        typalign uint8
        methods  []method
    )

    // add sorted methods here
    {
        for idx := range funcs {
            if funcs[idx].Func.Type().Kind() != Func {
                panic("funcs contains non-func")
            }
            c := funcs[idx].Name[0]
            if !('A' <= c && c <= 'Z') {
                panic("funcs contains unexported func")
            }
        }

        sort.Slice(funcs, func(i, j int) bool {
            return funcs[i].Name < funcs[j].Name
        })

        for idx := range funcs {
            ft := funcs[idx].Func.Type().common()
            ft.uncommon()

            ifn := funcs[idx].Func
            methods = append(methods, method{
                name: resolveReflectName(newName(funcs[idx].Name, "", true)),
                mtyp: resolveReflectType(ft),
                ifn:  resolveReflectText(unsafe.Pointer(&ifn)),
                tfn:  resolveReflectText(unsafe.Pointer(&ifn)),
            })
        }
    }

    var typ *structType
    var ut *uncommonType

    {
        // A *rtype representing a struct is followed directly in memory by an
        // array of method objects representing the methods attached to the
        // struct. To get the same layout for a run time generated type, we
        // need an array directly following the uncommonType memory.
        // A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.
        tt := New(StructOf([]StructField{
            {Name: "S", Type: TypeOf(structType{})},
            {Name: "U", Type: TypeOf(uncommonType{})},
            {Name: "M", Type: ArrayOf(len(methods), TypeOf(methods[0]))},
        }))

        typ = (*structType)(tt.Elem().Field(0).Addr().UnsafePointer())
        ut = (*uncommonType)(tt.Elem().Field(1).Addr().UnsafePointer())

        copy(tt.Elem().Field(2).Slice(0, len(methods)).Interface().([]method), methods)
    }

    ut.mcount = uint16(len(methods))
    ut.xcount = ut.mcount // we only have exported methods
    ut.moff = uint32(unsafe.Sizeof(uncommonType{}))
    str := string(repr)

    // Round the size up to be a multiple of the alignment.
    size = align(size, uintptr(typalign))

    // Make the struct type.
    var istruct any = struct{}{}
    prototype := *(**structType)(unsafe.Pointer(&istruct))
    *typ = *prototype

    typ.str = resolveReflectName(newName(str, "", false))
    typ.tflag = 0 // TODO: set tflagRegularMemory
    typ.hash = hash
    typ.size = size
    typ.ptrdata = typeptrdata(typ.common())
    typ.align = typalign
    typ.fieldAlign = typalign
    typ.ptrToThis = 0
    typ.tflag |= tflagUncommon

    {
        typ.kind &^= kindGCProg
        bv := new(bitVector)
        addTypeBits(bv, 0, typ.common())
        if len(bv.data) > 0 {
            typ.gcdata = &bv.data[0]
        }
    }

    typ.equal = nil

    typ.kind &^= kindDirectIface

    return Zero(&typ.rtype)
}

Here is the testing file.

package main

import (
    "fmt"
    "reflect"
    "testing"
)

func TestInterfaceOf(t *testing.T) {
    type AliceInterface interface {
        BobMethod() (error, string)
    }
    Alice := reflect.TypeOf((*AliceInterface)(nil)).Elem()
    Bob := Alice.Method(0)

    Carol := reflect.MakeFunc(Bob.Type, func(args []reflect.Value) []reflect.Value {
        fmt.Println("[wow]")
        nilError := reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())
        return []reflect.Value{nilError, reflect.ValueOf("haha")}
    })

    Dan := reflect.InterfaceOf([]reflect.InterfaceMethod{
        {
            Name: "BobMethod",
            Func: Carol,
        },
    })

    Dan.Method(0).Call([]reflect.Value{}) // panic

    dan := Dan.Interface().(AliceInterface)
    dan.BobMethod() // also panic

}
march1993
  • 133
  • 1
  • 7
  • it is possible to smoke the caller to implement such calls `Dan.Method(0).Call([]reflect.Value{})`. But this is not possible, afaiu it, to define a method on a type at runtime is prohibited: `dan := Dan.Interface().(AliceInterface)` see also https://github.com/golang/go/issues/20189 –  May 22 '22 at 17:09
  • Acctually it's not the same problem. The link you gave focuses on adding methods to an existing struct, but my problem is to create an object(whatever it is) satisfying certian interface. – march1993 May 23 '22 at 04:17
  • It is the same error in https://github.com/golang/go/issues/38783 – crvv May 24 '22 at 11:50

0 Answers0