theos / logos

Preprocessor that simplifies Objective-C hooking.
https://theos.dev/docs/logos
Other
206 stars 34 forks source link

%Property breaks depending on asterisk position #23

Closed thomasfinch closed 6 years ago

thomasfinch commented 7 years ago

The %property declaration breaks depending on the position of the asterisk in the declaration, ex. %property (copy) NSObject* myObj doesn't work but %property (copy) NSObject *myObj does.

What are the steps to reproduce this issue?

  1. Save the following as test.xm:
    
    %hook UIView

%property (copy) NSObject* myObj;

%end

2. Run `perl $THEOS/bin/logos.pl test.xm` to get the logos output.
3. Change test.xm so the property declaration reads `%property (copy) NSObject *myObj`, save the file
4. Run the perl command again to get different logos output.

What happens?
-------------
The property declaration is left unprocessed in the output file.

What were you expecting to happen?
----------------------------------
The property declaration should be preprocessed into associated object calls, but isn't if the asterisk isn't directly adjacent to the object name (`NSObject * myObj` also fails).

Any logs, error output, etc?
----------------------------
Output of the first perl command:
```objc
#line 1 "/Users/thomasfinch/Downloads/test.xm"

#include <substrate.h>
#if defined(__clang__)
#if __has_feature(objc_arc)
#define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained
#define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed))
#define _LOGOS_SELF_CONST const
#define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained))
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif

@class UIView; 

#line 1 "/Users/thomasfinch/Downloads/test.xm"

%property (copy) NSObject* myObj;

#line 6 "/Users/thomasfinch/Downloads/test.xm"

Output of the second perl command (after change):

#line 1 "/Users/thomasfinch/Downloads/test.xm"

#include <substrate.h>
#if defined(__clang__)
#if __has_feature(objc_arc)
#define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained
#define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed))
#define _LOGOS_SELF_CONST const
#define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained))
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif

@class UIView; 

#line 1 "/Users/thomasfinch/Downloads/test.xm"

static char _logos_property_key$_ungrouped$UIView$myObj;__attribute__((used)) static NSObject * _logos_method$_ungrouped$UIView$myObj$(UIView* __unused self, SEL __unused _cmd){ return objc_getAssociatedObject(self, &_logos_property_key$_ungrouped$UIView$myObj); }__attribute__((used)) static void _logos_method$_ungrouped$UIView$setMyObj$(UIView* __unused self, SEL __unused _cmd, NSObject * arg){ objc_setAssociatedObject(self, &_logos_property_key$_ungrouped$UIView$myObj, arg, OBJC_ASSOCIATION_COPY); }

static __attribute__((constructor)) void _logosLocalInit() {
{Class _logos_class$_ungrouped$UIView = objc_getClass("UIView"); { class_addMethod(_logos_class$_ungrouped$UIView, @selector(myObj), (IMP)&_logos_method$_ungrouped$UIView$myObj$, [[NSString stringWithFormat:@"%s@:", @encode(NSObject *)] UTF8String]);class_addMethod(_logos_class$_ungrouped$UIView, @selector(setMyObj:), (IMP)&_logos_method$_ungrouped$UIView$setMyObj$, [[NSString stringWithFormat:@"v@:%s", @encode(NSObject *)] UTF8String]);} } }
#line 6 "/Users/thomasfinch/Downloads/test.xm"

Any other comments?

This happens regardless of the class being hooked, the object type, or the property type.

What versions of software are you using?

Operating System: Mac OS 10.12.3

Toolchain Version: Not quite sure what this means, but using theos commit e94545975dda0c669bbcbed1eba0d7382ba49389 (current master head), clang 800.0.42.1

SDK Version: iOS 10.2 SDK, Xcode 8.2.1

uroboro commented 7 years ago

Hopefully it's as easy as fixing the regex filtering for %properties here https://github.com/theos/theos/blob/master/bin/logos.pl#L566: /\G%property\s*(?:\((\s*\w+\s*(?:,\s*(?:\w|\=|:)+\s*)*)\))?\s*((?:\w+\s+\**)+)(\w+)\s*;/gc, ((?:\w+\s+\**)+)(\w+) being the main part to change. It might also be beneficial to support more than just \w+ named properties.

This might be a suitable replacement for that part ([a-zA-Z_$][\w$]*(?:\s*[a-zA-Z_$][\w$]*)*(?:\s*\*+)?)\s*(\b[a-zA-Z_$][\w$]*+) such that the resulting regex would be /\G%property\s*(?:\((\s*\w+\s*(?:,\s*(?:\w|\=|:)+\s*)*)\))?\s*([a-zA-Z_$][\w$]*(?:\s*[a-zA-Z_$][\w$]*)*(?:\s*\*+)*)\s*(\b[a-zA-Z_$][\w$]*+)\s*;/gc. It's awkwardly longer because \w matches [a-zA-Z0-9_] but you can't start symbols with a number so I used [a-zA-Z_$] as first character and [\w$] for the following ($ is a valid character in symbols).

These should match:

%property (retain, getter=string2) NSString *string;
%property NSString *string;
%property NSNumber* number;
%property NSSet * set2;
%property BOOL bool;
%property struct dirent _dir;
%property char **list;
%property char2 **list2;
%property char_3 **list_3;
%property char$4 **list$4;
%property char5$ * *list5$;

While these shouldn't match:

%property 2 broken;
%property 2NSString* string;
%property NSNumber* 2number;
%property 2NSSet* 2set;

To compare, current version vs this version