1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71

base / apple / scoped_objc_class_swizzler.mm [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "base/apple/scoped_objc_class_swizzler.h"

#include <string.h>

#include "base/check_op.h"

namespace base::apple {

ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target,
                                                 Class source,
                                                 SEL selector)
    : old_selector_impl_(nullptr), new_selector_impl_(nullptr) {
  Init(target, source, selector, selector);
}

ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target,
                                                 SEL original,
                                                 SEL alternate)
    : old_selector_impl_(nullptr), new_selector_impl_(nullptr) {
  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() const {
  // 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_ = nullptr;
    return;
  }

  method_exchangeImplementations(old_selector_impl_, new_selector_impl_);
}

}  // namespace base::apple