gnustep / libobjc2

Objective-C runtime library intended for use with Clang.
http://www.gnustep.org/
MIT License
440 stars 119 forks source link

Selector forwarding is broken on Windows (mingw and msvc) #312

Open qmfrederik opened 3 hours ago

qmfrederik commented 3 hours ago

Selector forwarding from one object to another seems to be broken when using libobjc2 on Windows (see also: https://github.com/gnustep/plugins-themes-WinUXTheme/pull/7#issuecomment-2480839795).

I've opened a PR in libs-base which shows the problem: https://github.com/gnustep/libs-base/pull/466

qmfrederik commented 3 hours ago

@davidchisnall Any idea what's going on here? The simplest program to reproduce the issue is like this:

#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"

@interface GSFakeNSString : NSObject
{
  NSString* _originalItem;
}

- (id) initWithItem: (NSString*)item;
- (NSString*) originalItem;
- (id) target;
- (SEL)action;
- (void) action: (id)sender;
@end

@implementation GSFakeNSString
- (id) initWithItem: (NSString*)item
{
  self = [super init];
  if (self)
  {
    _originalItem = item;
  }
  return self;
}

- (NSString*) originalItem
{
  return _originalItem;
}

- (id)target
{
  return self;
}

- (SEL)action
{
  return @selector(action:);
}

- (id)forwardingTargetForSelector:(SEL)selector
{
  if ([_originalItem respondsToSelector:selector])
    return _originalItem;
  return nil;
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
  SEL selector = [invocation selector];

  // Forward any invocation to the original item if it supports it...
  if ([_originalItem respondsToSelector:selector])
    [invocation invokeWithTarget:_originalItem];
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature *signature = [[_originalItem class] instanceMethodSignatureForSelector:selector];
    if(signature == nil)
    {
        signature = [NSMethodSignature signatureWithObjCTypes:"@^v^c"];
    }
    return(signature);
}

- (void)doesNotRecognizeSelector:(SEL)selector
{
  NSLog(@"%s:selector not recognized: %@", __PRETTY_FUNCTION__, NSStringFromSelector(selector));
}
@end

int main(int argc,char **argv)
{
  START_SET("GSFFIInvocation")

  NSString *string = @"Hello, World!";

  GSFakeNSString *fakeString = [[GSFakeNSString alloc] initWithItem:string];

  NSString *upperCaseString = [string uppercaseString];
  NSString *fakeUpperCaseString = [fakeString uppercaseString];

  PASS_EQUAL(upperCaseString, fakeUpperCaseString, "uppercaseString selector is forwarded from the fake string to the actual NSString object");
  NSLog(@"Upper case string: %@, fake upper case string: %@", upperCaseString, fakeUpperCaseString);

  END_SET("GSFFIInvocation")
  return 0;
}

On Linux this passes, on Windows this gives:

EXCEPTION: NSRangeException in rangeOfCharacterFromSet:options:range:, range { 0, 32759 } extends beyond size (13) (null)

From what I can see, when the NSString uppercaseString method is invoked, the value of self is incorrect (and interestingly, *self seems to hold the correct value).