1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "base/mac/scoped_objc_class_swizzler.h" 6 7 #include <string.h> 8 9 #include "base/logging.h" 10 11 namespace base { 12 namespace mac { 13 14 ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, 15 Class source, 16 SEL selector) 17 : old_selector_impl_(NULL), new_selector_impl_(NULL) { 18 Init(target, source, selector, selector); 19 } 20 21 ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, 22 SEL original, 23 SEL alternate) 24 : old_selector_impl_(NULL), new_selector_impl_(NULL) { 25 Init(target, target, original, alternate); 26 } 27 28 ScopedObjCClassSwizzler::~ScopedObjCClassSwizzler() { 29 if (old_selector_impl_ && new_selector_impl_) 30 method_exchangeImplementations(old_selector_impl_, new_selector_impl_); 31 } 32 33 IMP ScopedObjCClassSwizzler::GetOriginalImplementation() { 34 // Note that while the swizzle is in effect the "new" method is actually 35 // pointing to the original implementation, since they have been swapped. 36 return method_getImplementation(new_selector_impl_); 37 } 38 39 void ScopedObjCClassSwizzler::Init(Class target, 40 Class source, 41 SEL original, 42 SEL alternate) { 43 old_selector_impl_ = class_getInstanceMethod(target, original); 44 new_selector_impl_ = class_getInstanceMethod(source, alternate); 45 if (!old_selector_impl_ && !new_selector_impl_) { 46 // Try class methods. 47 old_selector_impl_ = class_getClassMethod(target, original); 48 new_selector_impl_ = class_getClassMethod(source, alternate); 49 } 50 51 DCHECK(old_selector_impl_); 52 DCHECK(new_selector_impl_); 53 if (!old_selector_impl_ || !new_selector_impl_) 54 return; 55 56 // The argument and return types must match exactly. 57 const char* old_types = method_getTypeEncoding(old_selector_impl_); 58 const char* new_types = method_getTypeEncoding(new_selector_impl_); 59 DCHECK(old_types); 60 DCHECK(new_types); 61 DCHECK_EQ(0, strcmp(old_types, new_types)); 62 if (!old_types || !new_types || strcmp(old_types, new_types)) { 63 old_selector_impl_ = new_selector_impl_ = NULL; 64 return; 65 } 66 67 method_exchangeImplementations(old_selector_impl_, new_selector_impl_); 68 } 69 70 } // namespace mac 71 } // namespace base 72