| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "base/mac/scoped_objc_class_swizzler.h" |
| |
| #include <string.h> |
| |
| #include "base/logging.h" |
| |
| namespace base { |
| namespace mac { |
| |
| ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, |
| Class source, |
| SEL selector) |
| : old_selector_impl_(NULL), new_selector_impl_(NULL) { |
| Init(target, source, selector, selector); |
| } |
| |
| ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, |
| SEL original, |
| SEL alternate) |
| : old_selector_impl_(NULL), new_selector_impl_(NULL) { |
| Init(target, target, original, alternate); |
| } |
| |
| ScopedObjCClassSwizzler::~ScopedObjCClassSwizzler() { |
| if (old_selector_impl_ && new_selector_impl_) |
| method_exchangeImplementations(old_selector_impl_, new_selector_impl_); |
| } |
| |
| IMP ScopedObjCClassSwizzler::GetOriginalImplementation() { |
| // Note that while the swizzle is in effect the "new" method is actually |
| // pointing to the original implementation, since they have been swapped. |
| return method_getImplementation(new_selector_impl_); |
| } |
| |
| void ScopedObjCClassSwizzler::Init(Class target, |
| Class source, |
| SEL original, |
| SEL alternate) { |
| old_selector_impl_ = class_getInstanceMethod(target, original); |
| new_selector_impl_ = class_getInstanceMethod(source, alternate); |
| if (!old_selector_impl_ && !new_selector_impl_) { |
| // Try class methods. |
| old_selector_impl_ = class_getClassMethod(target, original); |
| new_selector_impl_ = class_getClassMethod(source, alternate); |
| } |
| |
| DCHECK(old_selector_impl_); |
| DCHECK(new_selector_impl_); |
| if (!old_selector_impl_ || !new_selector_impl_) |
| return; |
| |
| // The argument and return types must match exactly. |
| const char* old_types = method_getTypeEncoding(old_selector_impl_); |
| const char* new_types = method_getTypeEncoding(new_selector_impl_); |
| DCHECK(old_types); |
| DCHECK(new_types); |
| DCHECK_EQ(0, strcmp(old_types, new_types)); |
| if (!old_types || !new_types || strcmp(old_types, new_types)) { |
| old_selector_impl_ = new_selector_impl_ = NULL; |
| return; |
| } |
| |
| method_exchangeImplementations(old_selector_impl_, new_selector_impl_); |
| } |
| |
| } // namespace mac |
| } // namespace base |