How to pass va_list between method IMPs? (objective c) - objective-c

I'm trying to swizzle a function and call the original implementation with the function args.
the new IMP is of the form:
static id WrapperFunction(id self, SEL _cmd, ...) {
va_list args;
va_start(args, _cmd);
originalImp(self, _cmd, args);
...
}
This is clearly wrong since args now contains _cmd while ... did not.
How can I pass ... to originalImp?

Gcc has: http://gcc.gnu.org/onlinedocs/gcc/Constructing-Calls.html
clang has nothing and you must do assembly to do that (basically if you know the address of originalImp, you just want to "jmp" to it).

Related

Difference between NSLog and NSLogv

Can anyone explain the difference between NSLog and NSLogv? I know NSLog is used to print data in the console. But what is NSLogv?
Suppose you want to write a function similar to NSLog, but which also saves the message to an array in addition to logging it. How would you implement this?
If you write a variadic function void MySpecialLog(NSString *format, ...), someone can call your function just like NSLog — MySpecialLog(#"Hello %#!", name); — but the only way to access the extra arguments beyond format is with a va_list. There's no splat operator in C or Obj-C allowing you to pass them directly to NSLog inside the function.
NSLogv solves this problem by accepting all the additional arguments at once via a va_list. Its signature is void NSLogv(NSString *format, va_list args). You can use it to build your own NSLog wrappers.
Obj-C
void MySpecialLog(NSString *format, ...)
NS_FORMAT_FUNCTION(1, 2)
// The NS_FORMAT_FUNCTION attribute tells the compiler to treat the 1st argument like
// a format string, with values starting from the 2nd argument. This way, you'll
// get the proper warnings if format specifiers and arguments don't match.
{
va_list args;
va_start(args, format);
// Do something slightly more interesting than just passing format & args through...
NSString *newFormat = [#"You've called MySpecialLog()! " stringByAppendingString:format];
NSLogv(newFormat, args);
va_end(args);
}
You can even use the same technique to wrap NSLog with an Obj-C method. (And since -[NSString initWithFormat:] has a similar variant called -initWithFormat:arguments:, you can wrap it too.)
- (void)log:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2)
{
// Similarly to the above, we can pass all the arguments to -initWithFormat:arguments:.
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
// Why not both?
va_start(args, format);
NSLogv(format, args);
va_end(args);
}
Swift
In Swift, you can do this with a variadic function accepting CVarArg...:
func mySpecialLog(_ format: String, _ args: CVarArg...) {
withVaList(args) {
NSLogv("You've called mySpecialLog()! " + format, $0)
}
}
Generally speaking, a suffix of v means that a function takes a va_list as an argument, instead of a variadic argument list.
This is the case for NSLog and NSLogv:
void NSLog(NSString *format, ...);
void NSLogv(NSString *format, va_list args);
This is useful in certain very specific situations where you need to "wrap" a function that takes variadic arguments. If you need it, you'll know. Otherwise, you can safely ignore it.
NSLog is a varadic function, which means it takes a variable number of arguments. But sometimes, programmers will want to implement their own varadic wrapper function which does something else before calling NSLog.
If NSLog was the only function, that wouldn't be possible because you can't pass a set of varadic arguments (aka a va_list) to another varadic function.
That's why NSLogv exists separately from NSLog, which is just a wrapper that accepts a variable number of arguments and passes them to NSLogv.

Is there any way I can tell from what class/method a method is called?

I have a preprocessor macro that I use like this:
#implementation MyClass
- (void)methodA
{
MyLog("Hello, method A!");
}
- (void)methodB
{
MyLog("Hello, method %#!", #"B");
}
#end
And after macro expansion it looks like:
#implementation MyClass
- (void)methodA
{
; NSLog([NSString stringWithFormat:#"%s - %#", __func__, "Hello, method A!"]); ;
}
- (void)methodB
{
; NSLog([NSString stringWithFormat:#"%s - %#", __func__, "Hello, method %#!"], #"B"); ;
}
#end
This will result in these being printed for their respective methods:
-[MyClass methodA] - Hello, method A!
-[MyClass methodB] - Hello, method B!
I want to change this to a set of Objective-C or Swift methods, which I can call the same way and would give me the same result. I don't want to manage an object, so these should be class/static methods. Is it possible to tell that I'm in a particular class/method and only use a particular log prefix while in there?
If not, is there any other way to use methods to mimic the behavior I achieved with the macros?
Generally the Swift tool you want here is #function. For example:
func MyLog(msg: String, function: StaticString = #function) {
print("\(function) - \(msg)")
}
#function, when used as a default parameter value, evaluates to the function that at the calling site. Since it's a default value, you don't have to pass a function parameter.
This is not compatible with ObjC, however. For that, you'll still need macros. If you want to forward along an ObjC macro to Swift, you'd do something like:
#define MYLog(message) [Log log:message function:#(__FUNCTION__)]
You'd then need to write MyLog this way:
struct Log {
static func log(msg: String, function: String = #function) {
// ...
}
}
It has to be in a struct to be accessible to ObjC (or it could be an enum), and you have to make function of type String rather than StaticString because ObjC can't generate StaticString.
Build like this, in Swift, you would call:
Log.log("my message")
and in ObjC you would call
MyLog("my message")
I produce (by macro expansion) a similar result.
In GERuntimeConstants.h, i define
extern void QuietLog(NSString *format, ...);
in GERuntimeConstants.m, i provide QuietLog as :
void QuietLog(NSString *format, ...) {
if (format == nil) {
printf("nil\n");
return;
}
// Get a reference to the arguments that follow the format parameter
va_list argList;
va_start(argList, format);
// Perform format string argument substitution, reinstate %% escapes, then print
NSString *s = [[NSString alloc] initWithFormat:format arguments:argList];
printf("%s\n", [[s stringByReplacingOccurrencesOfString:#"%%" withString:#"%%%%"] UTF8String]);
va_end(argList);
}
in GEMacros, i define
#define __MPLOGWITHFUNCTION(s, ...) \
QuietLog(#"%s : %#",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__])
#define MPLOG(...) __MPLOGWITHFUNCTION(__VA_ARGS__)
I include GEMacros.h in my .pch
anywhere i want to log , i would have a single line of code that looks like the following example :
- (void)cleanupWithError:(NSString *)status message:(NSString *)message {
MPLOG(#"*** Cleaning up , status[%#], message[%#]",status,message);
and this statement translates to this in the console :
-[EHRCall cleanupWithError:message:] : *** Cleaning up , status[INVALID_PARAMETERS], message[No api user with api key [patient01ApiKey]]
So in my code, i use MPLOG as NSLog (always). The definition via _MPLOGWITHFUNCTION is because I have other macros that are tuned to the build. This definition is for logging while working a debug build. For distro MPLOG defines down to ;.

Objective-C swizzled method not receiving arguments

I'm developing an app that needs to perform some swizzling.
I'm swizzling a method -(void)m1:(CMAcceleration)a; with another one that I provide.
-(void)newM(id self, SEL _cmd, ...){
va_list args;
va_start(args, _cmd);
//...
NSInteger returnValue=((NSInteger(*)(id,SEL,...))origImp)(self,_cmd,args);
va_end(args);
}
To swizzle it I use:
origImp=method_setImplementation(method, newImp);
I then call it normally like [ClassInstance m1:a];
The thing is, args seems to be filled with garbage when I expected a structure like {name=type...} as described in here.
I need to pass the arguments to the original implementation after doing some operation like NSLog.
Searching the Internet it seems this is a Simulator problem related but I'm not sure and I have no access to a device to confirm this.
Am I doing something wrong or is there a way to fix this?
You are doing it very wrong.
The method signature should match i.e. -(void)newM:(CMAcceleration)a;
and
Method method = class_getInstanceMethod([SomeClass class],#selector(newM:));
IMP newImp = method_getImplementation(method);
origImp=method_setImplementation(method, newImp);
A different way is make C function
void newM(id self, SEL _cmd, CMAcceleration a) {
}
origImp=method_setImplementation(method, (IMP)newM);

How can I pass variable number of arguments to method2 from method1 that takes a variable number of arguments?

Assume that we have methods:
-(instancetype) initWithElements:(id)firstElement, ... NS_REQUIRES_NIL_TERMINATION;
+(instancetype) objWithElements:(id)firstElement, ... NS_REQUIRES_NIL_TERMINATION;
I understand, how to work with variable number of arguments in -initWithElements:, but I don't understand how to pass variables from -objWithElements: to -initWithElements:.
I mean, I want to write something like:
+(instancetype) objWithElements:(id)firstElement, ... NS_REQUIRES_NIL_TERMINATION {
return [[[self] initWithElements:ELEMENTS] autorelease];
}
Is it even possible?
The only solution for my problem I see is to store arguments in array and use helper method that will init object with given array.
No, in C (and Objective-C), it is not possible to pass down variadic arguments.
The idiomatic solution is to get yourself an initializer that takes a va_list, make that as the designated initializer, and then call it from every other method. From within a variadic method, this would look like:
- (instancetype)initWithVarargs:(id)first, ...
{
va_list args;
va_start(args, first);
id obj = [self initWithFirst:first VAList:args];
va_end(args);
return obj;
}
and here's a designated initializer that takes a va_list argument:
- (id)initWithFirst:(id)first VAList:(va_list)args
{
id obj;
while ((obj = va_arg(args, id)) != nil) {
// do actual stuff
}
// the return self, etc.
}
j
I would create two versions of each method; one which takes variable arguments (...) and another (where the actual implementation is) using va_list:
-(instancetype) initWithElements:(id)firstElement, ... NS_REQUIRES_NIL_TERMINATION;
-(instancetype) initWithElementsImpl:(va_list)va;
+(instancetype) objWithElements:(id)firstElement, ... NS_REQUIRES_NIL_TERMINATION;
+(instancetype) objWithElementsImpl:(va_list)va;
This will allow the va_list version to simply pass that parameter onto the other va_list method with no work at all.
The var args version (...) will use va_start() et al to create the va_list object to pass to the va_list version of the method.

Dynamic method creation in Objective-C

In the book The Pragmatic Programmer, the authors suggest that all method inputs should be validated. This allows problems with a method to be caught early and their sources traced easily.
In my Mac application, I accomplished this by creating an Assert class. This class has several class methods. These methods determine if some precondition is met, and if it is not, then an exception is thrown. A typical assertion might looks something like this:
-(void) setWidth: (int) theWidth {
[Assert integer: width isGreaterThanInteger: 0];
width = theWidth;
}
This works really well, and significantly reduced the amount of time I've spend bug hunting. However, I've noticed lately some of the assertion methods are very useful as predicates. For example, my integer:isGreaterThanInteger:andLessThanInteger: and my stringIsNotEmpty: methods are equally useful. To this end, I created a second class Predicate, which I filled with several of my more useful predicate methods. So I took the logic from the assert methods, and moved it into Predicate, and then rewrote my Assert methods like the following:
if ![Predicate predicateMethod]
throw exception
This has turned into a maintenance nightmare. If I change the name of a method name in Predicate, I must also change it in Assert to stay consistent. If I update the documentation of an Assert method, then I must do the same to a Predicate method.
Ideally, I would like the reconstruct the Assert class so that when any method is called on it, it intercepts the selector. The Predicate class can then be checked to see if it responds to the selector, and if it does, the method is called on Predicatewith the same arguments that were passed into the Assert method. If the Predicate method returns false, then an exception is thrown.
Is there a way to do this in Objective-C?
Thanks.
You could use -forwardingTargetForSelector: to simply forward the method to another object, but if you want advanced behavior (like checking the return value to see if it's false), you may need to use -forwardInvocation:. (However, note that the documentation says this is "much more expensive" than the former option.)
If you're using pure Objective-C, you should see the "Forwarding" discussion here. It basically describes how to do exactly what you want, including example code.
If you're using Cocoa then you might have to use forwardInvocation: instead.
I ended up overriding resolveClassMethod:. While overriding forwardInvocation might have worked (I would have had to figure out some way to override it for the class object), resolveClassMethod: seems like it's the easier and more efficient method. Here's what my final implementation ended up looking like:
#import "Assert.h"
#import "Predicate.h"
#include <objc/objc-runtime.h>
void handlePredicateSelector(id self, SEL _cmd, ...);
#implementation Assert
+(void) failWithMessage: (NSString *) message
{
NSLog(#"%#", message);
[NSException raise:#"ASSERTION FAILURE" format:message];
}
+(void) fail
{
[Assert failWithMessage:#"An unconditional failure has been detected."];
}
+(BOOL) resolveClassMethod: (SEL) selector
{
if ([(id) [Predicate class] respondsToSelector:selector])
{
/*
The meta class fix was taken from here: http://iphonedevelopment.blogspot.com/2008/08/dynamically-adding-class-objects.html
*/
//get the method properties from the Predicate class
Class predicateMetaClass = objc_getMetaClass([[Predicate className] UTF8String]);
Method predicateMethod = class_getClassMethod(predicateMetaClass, selector);
const char *encoding = method_getTypeEncoding(predicateMethod);
Class selfMetaClass = objc_getMetaClass([[self className] UTF8String]);
class_addMethod(selfMetaClass, selector, (IMP) handlePredicateSelector, "B#:?");
return YES;
}
return [super resolveClassMethod:selector];
}
#end
void handlePredicateSelector(id self, SEL _cmd, ...)
{
//get the number of arguments minus the self and _cmd arguments
NSMethodSignature *predicateMethodSignature = [(id) [Predicate class] methodSignatureForSelector:_cmd];
NSUInteger numberOfArguments = [predicateMethodSignature numberOfArguments] - 2;
NSInvocation *predicateInvocation = [NSInvocation invocationWithMethodSignature:predicateMethodSignature];
[predicateInvocation setTarget:[Predicate class]];
[predicateInvocation setSelector:_cmd];
va_list ap;
va_start(ap, _cmd);
for (int i = 0; i < numberOfArguments; i++)
{
void *arg = va_arg(ap, void *);
[predicateInvocation setArgument:&arg atIndex:i+2];
}
va_end(ap);
BOOL returnValue;
[predicateInvocation invoke];
[predicateInvocation getReturnValue:&returnValue];
//determine if the assertion is true
if (!returnValue)
{
[Assert failWithMessage:[NSString stringWithFormat: #"The following assertion failed: %#", NSStringFromSelector(_cmd)]];
}
}
The only thing I couldn't really figure out was how to get the type encoding from the method signature. It didn't seem to affect the output of the methods, but I would like to fix it if I can.