Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook individual objects #15

Merged
merged 78 commits into from
Jun 15, 2020
Merged

Hook individual objects #15

merged 78 commits into from
Jun 15, 2020

Conversation

steipete
Copy link
Owner

@steipete steipete commented Jun 6, 2020

This enables hooking on a per-object instance base, similar to https://github.com/steipete/Aspects.

The strategy here is:

  1. Create dynamic subclass (with UUID) and set this on the class
  2. Create empty method that simply calls super
  3. Replace IMP on this subclass

This can be made fully reversible, we can unregister the dynamically created subclass; potentially also reuse. Since we - unlike Aspects - don't use _objc_msgForward, no boxing happens, and this approach is fast + safe.

I ran into a few Swift compiler crashers:

TODO:

  • KVO tests
  • Better workaround for the super call
  • deal with _objc_msgSendSuper2_stret for non-arm64

@steipete steipete marked this pull request as draft June 6, 2020 13:34
@steipete steipete marked this pull request as ready for review June 7, 2020 11:34
Comment on lines 131 to 141
Class clazz = object_getClass(obj);
Class superclazz = class_getSuperclass(clazz);
do {
let superclassMethod = class_getInstanceMethod(superclazz, _cmd);
let sameMethods = class_getInstanceMethod(clazz, _cmd) == superclassMethod;
if (!sameMethods && !ITKMethodIsSuperTrampoline(superclassMethod)) {
break;
}
clazz = superclazz;
superclazz = class_getSuperclass(clazz);
}while (1);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not going to work. Let's consider the same situation as in the description above, but now Level3 and Level4 classes override the -sayHello method.
Also, there is no way to determine if Method uses our trampoline or not, since Method can contain user-defined IMP which can call our trampoline IMP.
Seems like the only correct and efficient way to implement this is to use vm_remap() and PC-relative addressing in the same way as this does original imp_implementationWithBlock.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right - this is not a situation the current logic supports. It would be a fun challenge to try how complex a solution with real trampolines work. Happy to take PRs! I add this limitation to the readme for now.

@steipete steipete changed the title Allow to hook individual objects Hook individual objects Jun 15, 2020
@steipete steipete merged commit 9e387e1 into master Jun 15, 2020
@steipete steipete deleted the peter/objecthooks branch June 15, 2020 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants