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