Reflection isn't able to find Final methods
Archives Forums/BlitzMax Bug Reports/Reflection isn't able to find Final methods
| ||
Uncomment either of the Final declarations in the code below:Import BRL.Reflection SuperStrict Type a Abstract Method foo() End Method End Type Type b Extends a 'Final Method bar() 'Final End Method End Type Local obj:b= New b Local id:TTypeId = TTypeId.ForObject(obj) For Local m:TMethod = EachIn id.EnumMethods() Print m.name() + ": " + m._index Next ...and you'll find that the indexing is all messed up. The app will crash if it tries to invoke this. My guess is that this is somehow related to Max emitting direct function references instead of pointer calls for Final methods, but this surely shouldn't also be applied to the debug information that reflection queries to get offsets? Reflection doesn't care whether the method in question is being optimized elsewhere in the code... it needs a pointer either way. (How has this gone unnoticed for so long? Does nobody use Final types?) |
| ||
Here's the details of b with and without Final.. Non-Final _12: dd 2 dd _13 dd 6 dd _8 dd _9 dd 16 dd 6 dd _10 dd _9 dd 20 dd 6 dd _14 dd _9 dd 52 dd 0 align 4 and with Final _12: dd 2 dd _13 dd 6 dd _8 dd _9 dd _bb_b_New dd 6 dd _10 dd _9 dd _bb_b_Delete dd 6 dd _14 dd _9 dd _bb_b_bar dd 0 align 4 Although interestingly, Invoke() in TMethod does do a check on _index to determine if it is a valid index or a reference : Method Invoke:Object( obj:Object,args:Object[] ) If _index<65536 Return _Call( bbRefMethodPtr( obj,_index ),_typeId,obj,args,_argTypes ) EndIf Return _Call( Byte Ptr(_index),_typeId,obj,args,_argTypes ) End Method So it appears it is all by design. Since _index is an internal variable, you wouldn't be using it anyway :-) Where is it crashing for you? Works fine for me when I invoke the method using : m.Invoke(obj, Null) |
| ||
Using this minor modification (so it actually tries): I get this output: New: 16 Delete: 20 foo: 48 New: 16 Delete: 20 bar: 52 in bar ...which changes to this if I uncomment Final: New: 16 Delete: 20 foo: 48 New: 10573 Delete: 10615 bar: 10652 (...boom) In debug mode the offsets are in the 20k-range instead, to much the same effect. No other settings I have found affect this noticeably. This is on a clean install of BlitzMax on OSX. I didn't test Invoke on my Windows box yesterday (only the offset printing, not having noticed that Invoke does actually do a Final check; wasn't paying enough attention), and it actually works fine on that machine after all. Linux doesn't have this problem either (hah). I guess I'll install it again on OSX and see if it persists in case I somehow screwed up the installation. EDIT: behaviour persists on a completely fresh install. Unzip -> build modules -> paste -> run -> boom. |
| ||
Yes, it appears to be a mistaken assumption that a function pointer will never have an address below 0x10000, which on OS X appears to be a possibility. |
| ||
GAH I nearly found a fix for this... The -sectalign option to lld allows one to specify the alignment of a section, so I added this line after line 280 of bmk_util.bmx: cmd:+" -Wl,-sectalign,__TEXT,__text,0x10000" ...reasoning that since it can't place code at 0, a minimum alignment of 0x10000 will mean that all code is emitted above that point. This is what it says in response: ld: argument for -sectalign must be less than or equal to 0x8000 Using the maximum value of 0x8000 does move the code, but not enough - it puts it at around 35k for the above example. |
| ||
A fix would be to do away with indexes altogether :-) |
| ||
Indeed. But that requires changing the compiler to emit consistent class descriptions (unless there's some reason why the Final optimization needs to apply anywhere other than at actual method call sites?). I have no idea what I was thinking with -sectalign actually... modifying bmk only is no better than changing BRL.Mod or swapping out the compiler as far as compatibility with other existing users goes. They've either patched their installation or they haven't, so that wasn't really any kind of useful solution. |
| ||
unless there's some reason why the Final optimization needs to apply anywhere other than at actual method call sites? I've no idea why it's done differently for Final, since, as you say, it's up to the compiler to decide what you can extend or override. I don't see why it can't be indexes for Final types either. |
| ||
A fix for this is to change _index<65536 to _index<4096 in reflection.bmx According to 'man ld', this will be safe because 32 bit apps have a 4K 'page zero' which will always cause an error when read. This can be changed via ld options, but it seems like a good default anyway. This does mean vftbl indices have to be <4K, but this allows for up to 1000-ish methods which is quite a lot! I probably did it this way so final methods called be called directly without going via the vftbl, ie: if the compiler knows that the static type contains a reference to a final method, it doesn't have to use indirection to call the method. In the case of final methods that don't override anything, there may not even be a vftbl slot 'allocated' for them, although it probably would have been cleaner to do so anyway. |