I wanted to raise this here because this one here got rejected.
https://tracker.xojo.com/xojoinc/xojo/-/issues/81684#note_618968
REALGetPropValueIntegerand and REALSetPropValueInteger absolutely have to do same as what setting the property in the IDE does.
A plugin will have zero chance to know if someone has subclassed a class and shadowed a property.
For us plugin authors to go and poke around in for loops to see if property is Shadowed makes no sense. And even less sense if we don’t even know if the property is shadowed or not (which will be the case in most cases)
Now to make things even worse then the proposed solution to look up properties with the REALGetClassProperty and REALGetClassProperty is just nuts, not only is it turtle slow to do and we of course wont know in advance if property is shadowned or not, but its also the most unstable API that Xojo has ever made in the Plugin API, this was reported back then and tests today show its still just as unstable as it was before.
if(scrollViewRef != NULL && REALObjectIsA((REALobject)control, scrollViewRef))
{
RBInteger propCount = REALCountClassProperties((REALobject)control);
NSLog(@"Checkpoint 1");
for(uint32_t i = 0; i < propCount; ++i)
{
void* getter;
void* setter;
long param;
REALstring declaration;
if(REALGetClassProperty(control, i, &getter, &setter, ¶m, &declaration))
{
REALstring rs = REALBuildStringWithEncoding("Left as Integer", 15, kREALTextEncodingUTF8);
if(REALCompareStrings(rs, declaration) == 0)
{
//NSLog(@"Checkpoint 3 -- index %ld", (long)i); // Having NSLog here with a parameter is just crash
}
REALUnlockString(rs); // Unlocking this one will sometimes cause crash.
//REALUnlockString(declaration); // Unlocking this one is just crash
}
}
}
Almost anything you do around it will result in crash. Hinting that its clobbering memory.
2 Likes
The scrollable area is implemented in Xojo code and shadows a lot of properties with computed ones.
The SDK functions look for properties and see the properties defined in C++ code.
I’d love Xojo to properly handle overwriting properties to improve this.
I also use REALCountClassProperties for my plugins to find the second property with a name as a workaround.
1 Like
Just looping through them and doing nothing at all crashes almost every single time on Arm.
I am just gona tell my users I cannot support this at all.
I don’t know if its crashing because its control instance or because this API just has never been fixed at all.
I tried this:
void testPlugin(REALobject control)
{
RBInteger propCount = REALCountClassProperties((REALobject)control);
DebugMessage("propCount", propCount);
REALstring rs = REALBuildStringWithEncoding("Left As Int64", 13, kREALTextEncodingUTF8);
DebugMessage("rs", rs);
for(RBInteger i = 0; i < propCount; ++i)
{
DebugMessage("i", i);
void* getter = nil;
void* setter = nil;
long param = 0;
REALstring declaration = nil;
if(REALGetClassProperty(control, i, &getter, &setter, ¶m, &declaration))
{
rbText t(declaration);
DebugMessage("declaration", t.GetUTF8());
if(REALCompareStrings(rs, declaration) == 0)
{
//NSLog(@"Checkpoint 3 -- index %ld", (long)i); // Having NSLog here with a parameter is just crash
DebugMessage("match");
}
}
}
REALUnlockString(rs); // Unlocking this one will sometimes cause crash.
}
I can run this 10 times and get no crash.
The output is like this:
propCount 88
rs 0x72c42d480: Left As Int64
i 0
declaration Handle As Ptr
i 1
declaration Index As Int64
i 2
declaration Name As String
i 3
declaration Window As DesktopWindow
i 4
declaration _mImp As Object
i 5
declaration _mIndex As Int64
i 6
declaration _mInitialParent As String
i 7
declaration _mName As String
i 8
declaration _mPanelIndex As Int64
i 9
declaration _mWindow As DesktopWindow
i 10
declaration _WindowOrContainer As DesktopWindow
i 11
declaration Active As Boolean
i 12
declaration AllowAutoDeactivate As Boolean
i 13
declaration AllowTabStop As Boolean
i 14
declaration Enabled As Boolean
i 15
declaration Height As Int64
i 16
declaration InitialParent As String
i 17
declaration Left As Int64
match
i 18
declaration LockBottom As Boolean
i 19
declaration LockLeft As Boolean
i 20
declaration LockRight As Boolean
i 21
declaration LockTop As Boolean
i 22
declaration MouseCursor As MouseCursor
i 23
declaration PanelIndex As Int64
i 24
declaration Parent As Object
i 25
declaration TabIndex As Int64
i 26
declaration TabPanelIndex As Int64
i 27
declaration TabStop As Boolean
i 28
declaration Tooltip As String
i 29
declaration Top As Int64
i 30
declaration Transparent As Boolean
i 31
declaration Visible As Boolean
i 32
declaration Width As Int64
i 33
declaration _gridCell As WeakRef
i 34
declaration AllowAutoDeactivate As Boolean
i 35
declaration Container As DesktopContainer
i 36
declaration Enabled As Boolean
i 37
declaration Handle As Ptr
i 38
declaration Height As Int64
i 39
declaration Left As Int64
match
i 40
declaration LockBottom As Boolean
i 41
declaration LockLeft As Boolean
i 42
declaration LockRight As Boolean
i 43
declaration LockTop As Boolean
i 44
declaration mAllowAutoDeactivate As Boolean
i 45
declaration mBacking As DesktopContainer
i 46
declaration mClip As DesktopContainer
i 47
declaration mContent As DesktopContainer
i 48
declaration mContentMacOS As _macOSDesktopScrollableArea
i 49
declaration mDeferAllowAutoDeactivate As Boolean
i 50
declaration mDeferContent As DesktopContainer
i 51
declaration mDeferEnabled As Boolean
i 52
declaration mDeferHeight As Int64
i 53
declaration mDeferLeft As Int64
i 54
declaration mDeferLockBottom As Boolean
i 55
declaration mDeferLockLeft As Boolean
i 56
declaration mDeferLockRight As Boolean
i 57
declaration mDeferLockTop As Boolean
i 58
declaration mDeferTop As Int64
i 59
declaration mDeferTransparent As Boolean
i 60
declaration mDeferVisible As Boolean
i 61
declaration mDeferWidth As Int64
i 62
declaration mEnabled As Boolean
i 63
declaration mHeight As Int64
i 64
declaration mHorizontalScrollbar As DesktopScrollbar
i 65
declaration mLastHeight As Int64
i 66
declaration mLastWidth As Int64
i 67
declaration mLeft As Int64
i 68
declaration mLineStep As Int64
i 69
declaration mLockBottom As Boolean
i 70
declaration mLockLeft As Boolean
i 71
declaration mLockRight As Boolean
i 72
declaration mLockTop As Boolean
i 73
declaration mNeedH As Boolean
i 74
declaration mNeedV As Boolean
i 75
declaration mScrollX As Int64
i 76
declaration mScrollY As Int64
i 77
declaration mTop As Int64
i 78
declaration mTransparent As Boolean
i 79
declaration mVerticalScrollbar As DesktopScrollbar
i 80
declaration mVisible As Boolean
i 81
declaration mWidth As Int64
i 82
declaration needH As Boolean
i 83
declaration needV As Boolean
i 84
declaration Top As Int64
i 85
declaration Transparent As Boolean
i 86
declaration Visible As Boolean
i 87
I also noticed, that it’s “Left as Int64” and a match with “integer” doesn’t work.
Thanks for the test.
I have made some progress and my findings are if not wanting to crash.
- Do not run it from MouseMove event (thats where all the crashes came, presumably thought that happens because its ran hundrreds of times then ?).
2. Do not unlock the declaration.
3. Do not treat the declaration as vallid REALstring, as in using REALStringCompare will start clobbering there.
So yea.....just peeking in the bytes and using strcmp and not doing it from MouseEvent.
All of those findings are not good though all of them point to tiny clobbering maybe, which then just crashes if you do it from place like MouseMove.
You really need people to somehow cache this.
Like pass scrollable area earlier in to lookup getter/setter.
Then in mouse move event (of your window splitter control?) you can call them.
I will cache this if possible in the Add Function.
But am a bit nervous if its unstable like this then even if just calling it once it might crash for someone.
Or not thats not gona work sadly.
If storing the setter and the param for example in the Open event. (and having tested it to work to use it from the Open event)… then the stored one wont work later.
I am not sure why it would not though.
When I cache a pointer to a method, I also store a copy of the object pointer and compare that later before the call. I may even go further and store the pointer to the class definition, just in case memory was reused.
1 Like
I got storage to work now, it was the 2nd property that was overriding the pointer.
What a drama for little thing !