|  | // 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 |