-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
reflect: the docs of StructOf should mention embedding types with too many method will panic. #25402
Comments
btw, gccgo also panics for this example, but with a different reason
|
Why do gccgo and gc output different results? Isn't this a standard package specified problem? |
I haven't looked at this case, but the reflect package is closely tied to the compiler, since the compiler generates the data structures that it uses, and there are significant differences in the implementation of the reflect package for gc and gccgo. |
@ianlancetaylor - the number of Exported methods in time.Time is 38 and it looks from the code Lines 2589 to 2595 in 1174ad3
that only 32 methods are supported; that's the reason for the panic. Let me know if this issue needs to be fixed; happy to help!. |
Yes, this should be fixed, possibly in the documentation, but it would be better to fix the code. |
I was wrong; the copy(t.m[:], methods) copies the methods' details on to the memory after Lines 642 to 647 in 1174ad3
The code Lines 2566 to 2596 in 1174ad3
should be replaced with a dynamic version of
typ := &structType{}
ut := &uncommonType{}
I will post an update soon on the fix for this. |
There are two places from where the []methods field gets populated -
My first solution was to have a struct type structTypeDynamic struct {
structType
u uncommonType
m []method
} and do default:
t := new(structTypeDynamic)
typ = &t.structType
ut = &t.u
t.m = make([]method, len(methods))
copy(t.m, methods)
} This would also require a change in return (*(*[]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.xcount > 0")))[:t.xcount:t.xcount] The above code assumes the underlying array is a slice. However, The simplest fix for this bug is to introduce case len(methods) <= 64:
t := new(structTypeFixed64)
typ = &t.structType
ut = &t.u
copy(t.m[:], methods) Only problem with this approach is, we might still end up with a @ianlancetaylor let me know if the solution looks ok. I will send a PR for the same. |
If you're willing to do a little work, I think the correct approach is for |
@ianlancetaylor - I can allocate space for the right number of methods using this approach or even by using |
@ianlancetaylor one other thing I can do to fix this. Add a flag argument to Lines 2569 to 2596 in 1174ad3
to switch {
case len(methods) == 0:
t := new(structTypeUncommon)
typ = &t.structType
ut = &t.u
default:
t := new(structTypeDynamic)
typ = &t.structType
ut = &t.u
t.m = make([]method, len(methods))
copy(t.m, methods)
} and set In the methods It would look like: methodPtr := add(unsafe.Pointer(t), uintptr(t.moff), "t.xcount > 0")
if t.methodStorageSlice == true {
methods = (*[1 << 16]method)(methodPtr)[:t.xcount:t.xcount]
} else {
methods = (*(*[]method)(methodPtr))[:t.xcount:t.xcount]
}
return methods The same has to be replicated in I would have liked a cleaner approach than this to fix the issue. But this is the only thing I could think of. Please let me know! |
@jamdagni86 Sorry, I didn't notice your comment of 22 days ago, and when I read it now I don't understand it. I'm suggesting that instead of using the existing |
@ianlancetaylor - I think I figured out a way to fix this bug. Lines 2278 to 2299 in 1174ad3
All the 3 fields need to be allocated sequentially because of the way they are accessed in certain places, like:
Lines 704 to 710 in 1174ad3
Also, uncommonType.methods and uncommonType.exportedMethods get the method backing array by adding offset to the base address of itself.Lines 642 to 647 in 1174ad3
I cannot use StructOf to call itself to create enough space for structTypeFixedNN because it will need to have structType and uncommonType fields (which both have methods).
However, we could allocate enough space for the entire struct by using Let me know your thoughts! switch {
case len(methods) == 0:
t := new(structTypeUncommon)
typ = &t.structType
ut = &t.u
default:
szTyp := int(unsafe.Sizeof(structType{}))
szUt := int(unsafe.Sizeof(uncommonType{}))
szMethods := int(unsafe.Sizeof(methods[0]))
szTotal := szTyp + szUt + int(len(methods)*szMethods)
structArr := New(ArrayOf(szTotal, TypeOf(byte(0))))
typ = (*structType)(unsafe.Pointer(structArr.Pointer()))
ut = (*uncommonType)(unsafe.Pointer(structArr.Pointer() + uintptr(szTyp)))
mb := structArr.Elem().Slice(szTyp+szUt, szTotal).Interface().([]byte)
for i, m := range methods {
copy(mb[i*szMethods:], (*(*[1 << 16]byte)(unsafe.Pointer(&m)))[:szMethods])
}
} |
Before discussing your suggestion, can you explain why you don't want to use my suggestion? The advantage of the approach that I suggested above is that we allocate memory with the correct type, which simplifies the interface to the garbage collector. |
As I mentioned in my earlier comment, I can not call Please let me know if I'm missing something :-) |
They have a fixed number of methods. Can we predefine that specific type? |
Yeah. I was wrong about infinite recursion though. This worked without infinite recursion, not sure why. switch {
case len(methods) == 0:
t := new(structTypeUncommon)
typ = &t.structType
ut = &t.u
default:
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)(unsafe.Pointer(tt.Pointer()))
ut = (*uncommonType)(unsafe.Pointer(tt.Pointer() + unsafe.Sizeof(*typ)))
copy(tt.Elem().FieldByName("M").Slice(0, len(methods)).Interface().([]method), methods)
} |
… embedded field with more than 32 mehtods is present. Structs with embedded fields having methods are represented by structTypeFixed4 ... structTypeFixed32. Any embedded field with more than 32 mehtods used to panic as that was the default action. Solution: StructOf calls calls itself recursively to create structTypeFixedN dynamically by allocating enough space for N methods present in the embedded type. Fixes golang#25402
Change https://golang.org/cl/128479 mentions this issue: |
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?go version go1.10.2 linux/amd64
Does this issue reproduce with the latest release?
yes
What did you do?
What did you expect to see?
not panic, or document it.
What did you see instead?
panic
The text was updated successfully, but these errors were encountered: