theos/logos

Dynamic class name in %init(<class>=<expr>) and %hook <class> to assume type

Opened this issue · 6 comments

TLDR

Specifying the class like %init([<class>=<expr>, …]);, results in the hooked object (self) being left without type to the compiler. I understand that not assuming any class is the most sane thing to do, but I was wondering if it would be possible to automatically set the object to a known type or to one that conforms to a protocol in order to avoid casting.

Background

I have a few hooks for iOS 11 and 12 (pre 12.2) that hooks the class MediaControlsPanelViewController in a few of my tweaks. In iOS 12.2 however, this class was renamed to MRPlatterViewController. All properties and methods seems to be the same. Thus, I would like to reuse the code within the hook, like:

%ctor {
    ...

    Class c;
    if (running iOS 12.2)
        c = %c(MRPlatterViewController);
    else
        c = %c(MediaControlsPanelViewController);
    %init(MediaControlsPanelViewController = c);
}

This works fine, but inside the actual hooked code, the compiler does no longer know which type self has.

I realize that it's possible to do something like this:

@protocol PanelViewController<NSObject>
@property (nonatomic, retain) MediaControlsParentContainerView *parentContainerView;
...
@end

@interface MediaControlsPanelViewController : UIViewController <PanelViewController>
@end

@interface MRPlatterViewController : UIViewController <PanelViewController>
@end

and then in the code cast it to an object that conforms to that protocol:

%hook MediaControlsPanelViewController

- (void)viewDidLoad {
    %orig;

    UIViewController<PanelViewController> *controller = (UIViewController<PanelViewController> *)self;

    controller.parentContainerView...
}
...

instead of using self. I was wondering if it would be possible to automatically set the object to a known type or to one that conforms to a protocol in order to avoid doing this casting. Ideally this would be done in the %ctor once instead of once in every hooked method.

Elaborating on this:
For example if f we take the code:

%hook ClassToHook
-(void)hookedMethod {}
%end

%ctor {
	%init(ClassToHook = objc_getClass("aClass"));
}

Logos will currently generate the function definition:

static void _logos_method$_ungrouped$ClassToHook$hookedMethod(_LOGOS_SELF_TYPE_NORMAL id _LOGOS_SELF_CONST __unused self, SEL __unused _cmd) {}

and declarations:

@class ClassToHook;
static void (*_logos_orig$_ungrouped$ClassToHook$hookedMethod)(_LOGOS_SELF_TYPE_NORMAL id _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$ClassToHook$hookedMethod(_LOGOS_SELF_TYPE_NORMAL id _LOGOS_SELF_CONST, SEL);

(notice how it still generates the @class, but is unused). Logos should instead generate the definition:

static void _logos_method$_ungrouped$ClassToHook$hookedMethod(_LOGOS_SELF_TYPE_NORMAL ClassToHook* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd) {}

and declarations:

@class ClassToHook;
static void (*_logos_orig$_ungrouped$ClassToHook$hookedMethod)(_LOGOS_SELF_TYPE_NORMAL ClassToHook* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$ClassToHook$hookedMethod(_LOGOS_SELF_TYPE_NORMAL ClassToHook* _LOGOS_SELF_CONST, SEL);

As would be generated if the class was hooked normally, without being passed into %init.

@Nosskirneh How exactly is logos supposed to know which class it's supposed to be at compile-time?

@NSExceptional my examples show the output I believe that logos should generate to resolve this issue, and that this is indeed a bug by the unused @class forward declaration. Normally when hooking with %hook UIView for example, logos will declare the self variable as _LOGOS_SELF_TYPE_NORMAL UIView* _LOGOS_SELF_CONST. However when hooking labels defined in %init, logos declares self as _LOGOS_SELF_TYPE_NORMAL id _LOGOS_SELF_CONST, instead of declaring it as a ClassToHook*, leaving the @class ClassToHook; unused.

Ah, my bad. I understand now.

This is really biting me in the ass right now. @uroboro do you know where exactly I might go to resolve this in Logos? I've been digging around for an hour and can't find it

I think I've found it, in bin/lib/Logos/Class.pm:

sub type {
	my $self = shift;
	if(@_) { $self->{TYPE} = shift; }
	# return $self->{TYPE} if $self->{TYPE};
	return $self->{NAME}." *";
}

Commenting out the 2nd-to-last line as above does the trick. @uroboro If this is the only thing type is used for (and it as AFAICT) then can you just commit this change?