added iOS source code
[wl-app.git] / iOS / Pods / GoogleUtilities / GoogleUtilities / MethodSwizzler / GULSwizzler.m
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #import "Private/GULSwizzler.h"
16
17 #import <objc/runtime.h>
18
19 #import <GoogleUtilities/GULLogger.h>
20 #import "../Common/GULLoggerCodes.h"
21
22 #ifdef GUL_UNSWIZZLING_ENABLED
23 #import <GoogleUtilities/GULSwizzlingCache.h>
24 // We need a private method for an assert.
25 #import <GoogleUtilities/GULSwizzlingCache_Private.h>
26 #endif
27
28 static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilites/MethodSwizzler]";
29
30 dispatch_queue_t GetGULSwizzlingQueue() {
31   static dispatch_queue_t queue;
32   static dispatch_once_t onceToken;
33   dispatch_once(&onceToken, ^{
34     queue = dispatch_queue_create("com.google.GULSwizzler", DISPATCH_QUEUE_SERIAL);
35   });
36   return queue;
37 }
38
39 @implementation GULSwizzler
40
41 + (void)swizzleClass:(Class)aClass
42             selector:(SEL)selector
43      isClassSelector:(BOOL)isClassSelector
44            withBlock:(nullable id)block {
45   dispatch_sync(GetGULSwizzlingQueue(), ^{
46     NSAssert(selector, @"The selector cannot be NULL");
47     NSAssert(aClass, @"The class cannot be Nil");
48     Class resolvedClass = aClass;
49     Method method = nil;
50     if (isClassSelector) {
51       method = class_getClassMethod(aClass, selector);
52       resolvedClass = object_getClass(aClass);
53     } else {
54       method = class_getInstanceMethod(aClass, selector);
55     }
56     NSAssert(method, @"You're attempting to swizzle a method that doesn't exist. (%@, %@)",
57              NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
58     IMP newImp = imp_implementationWithBlock(block);
59
60 #ifdef GUL_UNSWIZZLING_ENABLED
61     IMP currentImp = class_getMethodImplementation(resolvedClass, selector);
62     [[GULSwizzlingCache sharedInstance] cacheCurrentIMP:currentImp
63                                               forNewIMP:newImp
64                                                forClass:resolvedClass
65                                            withSelector:selector];
66 #endif
67
68     const char *typeEncoding = method_getTypeEncoding(method);
69     __unused IMP originalImpOfClass =
70         class_replaceMethod(resolvedClass, selector, newImp, typeEncoding);
71
72 #ifdef GUL_UNSWIZZLING_ENABLED
73     // If !originalImpOfClass, then the IMP came from a superclass.
74     if (originalImpOfClass) {
75       if (originalImpOfClass !=
76           [[GULSwizzlingCache sharedInstance] originalIMPOfCurrentIMP:currentImp]) {
77         GULLogWarning(kGULLoggerSwizzler, NO,
78                       [NSString stringWithFormat:@"I-SWZ%06ld",
79                                                  (long)kGULSwizzlerMessageCodeMethodSwizzling000],
80                       @"Swizzling class: %@ SEL:%@ after it has been previously been swizzled.",
81                       NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
82       }
83     }
84 #endif
85   });
86 }
87
88 + (void)unswizzleClass:(Class)aClass selector:(SEL)selector isClassSelector:(BOOL)isClassSelector {
89 #ifdef GUL_UNSWIZZLING_ENABLED
90   dispatch_sync(GetGULSwizzlingQueue(), ^{
91     NSAssert(aClass != nil && selector != nil, @"You cannot unswizzle a nil class or selector.");
92     Method method = nil;
93     Class resolvedClass = aClass;
94     if (isClassSelector) {
95       resolvedClass = object_getClass(aClass);
96       method = class_getClassMethod(aClass, selector);
97     } else {
98       method = class_getInstanceMethod(aClass, selector);
99     }
100     NSAssert(method, @"Couldn't find the method you're unswizzling in the runtime.");
101     IMP originalImp =
102         [[GULSwizzlingCache sharedInstance] cachedIMPForClass:resolvedClass withSelector:selector];
103     NSAssert(originalImp, @"This class/selector combination hasn't been swizzled");
104     IMP currentImp = method_setImplementation(method, originalImp);
105     BOOL didRemoveBlock = imp_removeBlock(currentImp);
106     NSAssert(didRemoveBlock, @"Wasn't able to remove the block of a swizzled IMP.");
107     [[GULSwizzlingCache sharedInstance] clearCacheForSwizzledIMP:currentImp
108                                                         selector:selector
109                                                           aClass:resolvedClass];
110   });
111 #else
112   NSAssert(NO, @"Unswizzling is disabled.");
113 #endif
114 }
115
116 + (nullable IMP)originalImplementationForClass:(Class)aClass
117                                       selector:(SEL)selector
118                                isClassSelector:(BOOL)isClassSelector {
119 #ifdef GUL_UNSWIZZLING_ENABLED
120   __block IMP originalImp = nil;
121   dispatch_sync(GetGULSwizzlingQueue(), ^{
122     Class resolvedClass = isClassSelector ? object_getClass(aClass) : aClass;
123     originalImp =
124         [[GULSwizzlingCache sharedInstance] cachedIMPForClass:resolvedClass withSelector:selector];
125     NSAssert(originalImp, @"The IMP for this class/selector combo doesn't exist (%@, %@).",
126              NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
127   });
128   return originalImp;
129 #else
130   NSAssert(NO, @"Unswizzling is disabled and the original IMP is not cached.");
131   return nil;
132 #endif
133 }
134
135 + (nullable IMP)currentImplementationForClass:(Class)aClass
136                                      selector:(SEL)selector
137                               isClassSelector:(BOOL)isClassSelector {
138   NSAssert(selector, @"The selector cannot be NULL");
139   NSAssert(aClass, @"The class cannot be Nil");
140   if (selector == NULL || aClass == nil) {
141     return nil;
142   }
143   __block IMP currentIMP = nil;
144   dispatch_sync(GetGULSwizzlingQueue(), ^{
145     Method method = nil;
146     if (isClassSelector) {
147       method = class_getClassMethod(aClass, selector);
148     } else {
149       method = class_getInstanceMethod(aClass, selector);
150     }
151     NSAssert(method, @"The Method for this class/selector combo doesn't exist (%@, %@).",
152              NSStringFromClass(aClass), NSStringFromSelector(selector));
153     if (method == nil) {
154       return;
155     }
156     currentIMP = method_getImplementation(method);
157     NSAssert(currentIMP, @"The IMP for this class/selector combo doesn't exist (%@, %@).",
158              NSStringFromClass(aClass), NSStringFromSelector(selector));
159   });
160   return currentIMP;
161 }
162
163 + (BOOL)selector:(SEL)selector existsInClass:(Class)aClass isClassSelector:(BOOL)isClassSelector {
164   Method method = isClassSelector ? class_getClassMethod(aClass, selector)
165                                   : class_getInstanceMethod(aClass, selector);
166   return method != nil;
167 }
168
169 + (NSArray<id> *)ivarObjectsForObject:(id)object {
170   NSMutableArray *array = [NSMutableArray array];
171   unsigned int count;
172   Ivar *vars = class_copyIvarList([object class], &count);
173   for (NSUInteger i = 0; i < count; i++) {
174     const char *typeEncoding = ivar_getTypeEncoding(vars[i]);
175     // Check to see if the ivar is an object.
176     if (strncmp(typeEncoding, "@", 1) == 0) {
177       id ivarObject = object_getIvar(object, vars[i]);
178       [array addObject:ivarObject];
179     }
180   }
181   free(vars);
182   return array;
183 }
184
185 @end