1// Copyright 2014 The Chromium Authors 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/apple/scoped_objc_class_swizzler.h" 6 7#include <string.h> 8 9#include "base/check_op.h" 10 11namespace base::apple { 12 13ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, 14 Class source, 15 SEL selector) 16 : old_selector_impl_(nullptr), new_selector_impl_(nullptr) { 17 Init(target, source, selector, selector); 18} 19 20ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, 21 SEL original, 22 SEL alternate) 23 : old_selector_impl_(nullptr), new_selector_impl_(nullptr) { 24 Init(target, target, original, alternate); 25} 26 27ScopedObjCClassSwizzler::~ScopedObjCClassSwizzler() { 28 if (old_selector_impl_ && new_selector_impl_) { 29 method_exchangeImplementations(old_selector_impl_, new_selector_impl_); 30 } 31} 32 33IMP ScopedObjCClassSwizzler::GetOriginalImplementation() const { 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 39void 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 57 // The argument and return types must match exactly. 58 const char* old_types = method_getTypeEncoding(old_selector_impl_); 59 const char* new_types = method_getTypeEncoding(new_selector_impl_); 60 DCHECK(old_types); 61 DCHECK(new_types); 62 DCHECK_EQ(0, strcmp(old_types, new_types)); 63 if (!old_types || !new_types || strcmp(old_types, new_types)) { 64 old_selector_impl_ = new_selector_impl_ = nullptr; 65 return; 66 } 67 68 method_exchangeImplementations(old_selector_impl_, new_selector_impl_); 69} 70 71} // namespace base::apple 72