diff --git a/MessageMock.podspec b/MessageMock.podspec new file mode 100644 index 0000000..9922de4 --- /dev/null +++ b/MessageMock.podspec @@ -0,0 +1,29 @@ +Pod::Spec.new do |s| + + s.name = "MessageMock" + + s.version = "1.0" + + s.summary = "Objective-C 方法模拟工具" + + s.description = <<-DESC + Objective-C 方法模拟工具 + DESC + + s.homepage = "https://github.com/indulgeIn" + + s.license = "MIT" + + s.author = { "indulgeIn" => "1106355439@qq.com" } + + s.platform = :ios, "9.0" + + s.source = { :git => "https://github.com/indulgeIn/MessageMock.git", :tag => "#{s.version}" } + + s.requires_arc = true + + s.source_files = "MessageMock/**/*.{h,m,mm,c,cpp,hpp}" + + s.libraries = 'c++.1' + +end diff --git a/MessageMock/Core/mm_argument_callback.cpp b/MessageMock/Core/mm_argument_callback.cpp new file mode 100644 index 0000000..1974a10 --- /dev/null +++ b/MessageMock/Core/mm_argument_callback.cpp @@ -0,0 +1,109 @@ +// +// mm_argument_callback.cpp +// MessageMock +// +// Created by 波儿菜 on 2020/7/10. +// + +#import "mm_argument_callback.h" + +#ifdef __aarch64__ + +#import +#import "mm_define.h" +#import "mm_method_matcher.h" +#import "mm_runtime.h" + +using namespace mm_method_matcher; + +void MMArgumentCallbackIfNeeded(uintptr_t self, uintptr_t _cmd) { +#define XS_SIZE 6 +#define DS_SIZE 8 + uintptr_t xs[XS_SIZE], ds[DS_SIZE]; + __asm volatile + ("mov %0, x2\n" + "mov %1, x3\n" + "mov %2, x4\n" + "mov %3, x5\n" + "mov %4, x6\n" + "mov %5, x7\n" + : "=r"(xs[0]), "=r"(xs[1]), "=r"(xs[2]), "=r"(xs[3]), "=r"(xs[4]), "=r"(xs[5])); + __asm volatile + ("fmov %0, d0\n" + "fmov %1, d1\n" + "fmov %2, d2\n" + "fmov %3, d3\n" + "fmov %4, d4\n" + "fmov %5, d5\n" + "fmov %6, d6\n" + "fmov %7, d7\n" + : "=r"(ds[0]), "=r"(ds[1]), "=r"(ds[2]), "=r"(ds[3]), "=r"(ds[4]), "=r"(ds[5]), "=r"(ds[6]), "=r"(ds[7])); + + ArgumentCallbackMap *callback_map = NULL; + { + MethodMatcherLock lock; + MethodMatcher *matcher = UnsafeGetMethodMatcher(self, _cmd); + if (MM_UNLIKELY(matcher)) { + callback_map = matcher->argument_callback_map; + + ++matcher->using_count; + } + } + if (MM_LIKELY(!callback_map || callback_map->size() <= 0)) { + return; + } + + Method method = MMGetMethod((id)self, (SEL)_cmd); + unsigned int arg_number = method_getNumberOfArguments(method); + + ArgumentCallbackError error = kArgumentCallbackErrorNone; + unsigned int x_idx = 0, d_idx = 0; + for (unsigned int i = 2; i < arg_number; ++i) { + ArgumentCallback callback = callback_map->find(i)->second; + if (!callback) { + continue; + } + + if (error == kArgumentCallbackErrorUnsupported) { + callback(0, error); + continue; + } + + char *type = method_copyArgumentType(method, i); + if (MM_UNLIKELY(!type)) { // 该路径理论上不会走 + callback(0, kArgumentCallbackErrorNoIndex); + continue; + } + + uintptr_t arg = 0; + if (MM_UNLIKELY(MMPtrLengthArgumentTypes().count(*type) > 0)) { + if (*type == 'f' || *type == 'd') { + if (MM_UNLIKELY(d_idx >= DS_SIZE)) { + error = kArgumentCallbackErrorOverD7; + } else { + arg = ds[d_idx++]; + } + } else { + if (MM_UNLIKELY(x_idx >= XS_SIZE)) { + error = kArgumentCallbackErrorOverX7; + } else { + arg = xs[x_idx++]; + } + } + } else { + error = kArgumentCallbackErrorUnsupported; + } + + callback(arg, error); + free(type); + } + +#undef XS_SIZE +#undef DS_SIZE +} + +#else + +void MMArgumentCallbackIfNeeded(uintptr_t self, uintptr_t _cmd) {} + +#endif diff --git a/MessageMock/Core/mm_argument_callback.h b/MessageMock/Core/mm_argument_callback.h new file mode 100644 index 0000000..64aa3ff --- /dev/null +++ b/MessageMock/Core/mm_argument_callback.h @@ -0,0 +1,15 @@ +// +// mm_argument_callback.h +// MessageMock +// +// Created by 波儿菜 on 2020/7/10. +// + +#ifndef mm_argument_callback_h +#define mm_argument_callback_h + +#include + +void MMArgumentCallbackIfNeeded(uintptr_t self, uintptr_t _cmd); + +#endif /* mm_argument_callback_h */ diff --git a/MessageMock/Core/mm_argument_change.cpp b/MessageMock/Core/mm_argument_change.cpp new file mode 100644 index 0000000..6cc4b6e --- /dev/null +++ b/MessageMock/Core/mm_argument_change.cpp @@ -0,0 +1,115 @@ +// +// mm_argument_change.cpp +// MessageMock +// +// Created by 波儿菜 on 2020/7/13. +// + +#import "mm_argument_change.h" + +#ifdef __aarch64__ + +#import +#import "mm_define.h" +#import "mm_runtime.h" +#import "mm_method_matcher.h" + +using namespace mm_method_matcher; + +void MMSetArgumentRegisters(uintptr_t self, uintptr_t _cmd) { + uintptr_t arg_label = 0; + +#define XS_SIZE 6 +#define DS_SIZE 8 + //通用/浮点寄存器值暂存数组 + uintptr_t xs[XS_SIZE] = {0}, ds[DS_SIZE] = {0}, mask = 1; + { + ArgumentList *arguments = NULL; + { + MethodMatcherLock lock; + MethodMatcher *matcher = UnsafeGetMethodMatcher(self, _cmd); + if (MM_UNLIKELY(matcher)) { + arguments = matcher->arguments; + } + } + if (!arguments || arguments->size() <= 0) { + __asm volatile ("b 16f"); + } + + Method method = MMGetMethod((id)self, (SEL)_cmd); + unsigned int arg_number = method_getNumberOfArguments(method); + + std::unordered_map x_register_map, d_register_map; + uintptr_t x_idx = 2, d_idx = 0; + for (unsigned int i = 2; i < arg_number; ++i) { + char *type = method_copyArgumentType(method, i); + if (MM_UNLIKELY(!type || MMPtrLengthArgumentTypes().count(*type) == 0)) { + // Unsupported argument type. + __asm volatile ("b 16f"); + } + if (*type == 'f' || *type == 'd') { + d_register_map[i] = d_idx++; + } else { + x_register_map[i] = x_idx++; + } + free(type); + } + + for (Argument arg : *arguments) { + char *type = method_copyArgumentType(method, arg.idx); + if (MM_UNLIKELY(!type)) { + continue; + } + uintptr_t res_arg = arg.value; + if (arg.object) { + res_arg = (uintptr_t)arg.object; + } + if (*type == 'f' || *type == 'd') { + uintptr_t register_idx = d_register_map[arg.idx]; + if (MM_UNLIKELY(register_idx >= DS_SIZE)) { + continue; + } + arg_label |= (mask << (8 + register_idx)); + ds[register_idx] = res_arg; + } else { + uintptr_t register_idx = x_register_map[arg.idx]; + if (MM_UNLIKELY(register_idx - 2 >= XS_SIZE)) { + continue; + } + arg_label |= (mask << register_idx); + xs[register_idx - 2] = res_arg; + } + free(type); + } + } +#undef XS_SIZE +#undef DS_SIZE + + __asm volatile + ("mov x2, %0\n" + "mov x3, %1\n" + "mov x4, %2\n" + "mov x5, %3\n" + "mov x6, %4\n" + "mov x7, %5\n" + :: "r"(xs[0]), "r"(xs[1]), "r"(xs[2]), "r"(xs[3]), "r"(xs[4]), "r"(xs[5])); + __asm volatile + ("fmov d0, %0\n" + "fmov d1, %1\n" + "fmov d2, %2\n" + "fmov d3, %3\n" + "fmov d4, %4\n" + "fmov d5, %5\n" + "fmov d6, %6\n" + "fmov d7, %7\n" + :: "r"(ds[0]), "r"(ds[1]), "r"(ds[2]), "r"(ds[3]), "r"(ds[4]), "r"(ds[5]), "r"(ds[6]), "r"(ds[7])); + + __asm volatile ("16:"); + __asm volatile ("mov x0, %0" :: "r"(arg_label)); +} + +#else + +void MMSetArgumentRegisters(uintptr_t self, uintptr_t _cmd) {} + +#endif diff --git a/MessageMock/Core/mm_argument_change.h b/MessageMock/Core/mm_argument_change.h new file mode 100644 index 0000000..6902097 --- /dev/null +++ b/MessageMock/Core/mm_argument_change.h @@ -0,0 +1,15 @@ +// +// mm_argument_change.h +// MessageMock +// +// Created by 波儿菜 on 2020/7/13. +// + +#ifndef mm_argument_change_h +#define mm_argument_change_h + +#include + +void MMSetArgumentRegisters(uintptr_t self, uintptr_t _cmd); + +#endif /* mm_argument_change_h */ diff --git a/MessageMock/Core/mm_argument_stack.cpp b/MessageMock/Core/mm_argument_stack.cpp new file mode 100644 index 0000000..48470b4 --- /dev/null +++ b/MessageMock/Core/mm_argument_stack.cpp @@ -0,0 +1,61 @@ +// +// mm_argument_stack.cpp +// MessageMock +// +// Created by 波儿菜 on 2020/7/10. +// + +#import "mm_argument_stack.h" +#import +#import +#import "mm_define.h" + +typedef struct { + MMArgumentItem *items; + int index; + int mask; +} MMArgumentStack; + +static pthread_key_t argument_stack_thread_key; + +static void ArgumentStackThreadRelease(void *ptr) { + MMArgumentStack *stack = (MMArgumentStack *)ptr; + if (MM_LIKELY(stack)) { + if (MM_LIKELY(stack->items)) { + free(stack->items); + } + free(stack); + } +} + +void MMThreadKeyCreate(void) { + pthread_key_create(&argument_stack_thread_key, &ArgumentStackThreadRelease); +} + +static MMArgumentStack *GetArgumentStack() { + MMArgumentStack *stack = (MMArgumentStack *)pthread_getspecific(argument_stack_thread_key); + if (MM_UNLIKELY(!stack)) { + stack = (MMArgumentStack *)malloc(sizeof(MMArgumentStack)); + stack->mask = 128; + stack->index = -1; + stack->items = (MMArgumentItem *)calloc(stack->mask, sizeof(MMArgumentItem)); + pthread_setspecific(argument_stack_thread_key, stack); + } + return stack; +} + +void MMArgumentStackPush(MMArgumentItem item) { + MMArgumentStack *stack = GetArgumentStack(); + int index = ++stack->index; + if (MM_UNLIKELY(index >= stack->mask)) { + stack->mask += 128; + stack->items = (MMArgumentItem *)realloc(stack->items, stack->mask * sizeof(MMArgumentItem)); + } + stack->items[index] = item; +} + +MMArgumentItem MMArgumentStackPop() { + MMArgumentStack *stack = GetArgumentStack(); + MMArgumentItem item = stack->items[stack->index--]; + return item; +} diff --git a/MessageMock/Core/mm_argument_stack.h b/MessageMock/Core/mm_argument_stack.h new file mode 100644 index 0000000..eba34ba --- /dev/null +++ b/MessageMock/Core/mm_argument_stack.h @@ -0,0 +1,25 @@ +// +// mm_argument_stack.h +// MessageMock +// +// Created by 波儿菜 on 2020/7/10. +// + +#ifndef mm_argument_stack_h +#define mm_argument_stack_h + +#include + +typedef struct { + uintptr_t target; + uintptr_t selector; + uintptr_t lr; +} MMArgumentItem; + +void MMThreadKeyCreate(void); + +void MMArgumentStackPush(MMArgumentItem item); + +MMArgumentItem MMArgumentStackPop(void); + +#endif /* mm_argument_stack_h */ diff --git a/MessageMock/Core/mm_define.h b/MessageMock/Core/mm_define.h new file mode 100644 index 0000000..aabfce4 --- /dev/null +++ b/MessageMock/Core/mm_define.h @@ -0,0 +1,17 @@ +// +// mm_define.h +// MessageMock +// +// Created by 波儿菜 on 2020/7/10. +// + +#ifndef mm_define_h +#define mm_define_h + +#define MM_LIKELY(x) (__builtin_expect(!!(x), 1)) +#define MM_UNLIKELY(x) (__builtin_expect(!!(x), 0)) + +#define MM_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define MM_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") + +#endif /* mm_define_h */ diff --git a/MessageMock/Core/mm_hook_objc_msgsend.cpp b/MessageMock/Core/mm_hook_objc_msgsend.cpp new file mode 100644 index 0000000..13b3c39 --- /dev/null +++ b/MessageMock/Core/mm_hook_objc_msgsend.cpp @@ -0,0 +1,302 @@ +// +// mm_hook_objc_msgsend.cpp +// MessageMock +// +// Created by 波儿菜 on 2020/6/27. +// + +#import "mm_hook_objc_msgsend.h" + +#ifdef __aarch64__ + +#import +#import +#import +#import "mm_fishhook.h" +#import "mm_method_matcher.h" +#import "mm_argument_stack.h" +#import "mm_argument_callback.h" +#import "mm_argument_change.h" +#import "mm_runtime.h" + +using namespace mm_method_matcher; + +#pragma mark - Hook Functions + +static id (*origin_objc_msgSend)(id, SEL, ...); + +static void beforeObjcMsgSend(uintptr_t self, uintptr_t _cmd) { + uintptr_t lr = 0; + __asm volatile ("mov %0, x9" : "=r"(lr)); + { // 必须是第一个调用,保证寄存器不会被修改 + MMArgumentCallbackIfNeeded(self, _cmd); + } + bool skip = false; + { + MMArgumentItem item = { + .target = self, + .selector = _cmd, + .lr = lr, + }; + MMArgumentStackPush(item); + { + MethodMatcherLock lock; + MethodMatcher *matcher = UnsafeGetMethodMatcher(self, _cmd); + if (matcher) { + skip = matcher->skip; + } + } + } + { // 必须是最后一个调用,保证寄存器不会被修改 + MMSetArgumentRegisters(self, _cmd); + } + __asm volatile ("mov w1, %w0" :: "r"(skip)); +} + +static void AfterObjcMsgSend() { + uintptr_t lr = 0, x0 = 0, d0 = 0; + __asm volatile ("mov %0, x0\n" + "fmov %1, d0\n" + : "=r"(x0), "=r"(d0)); + uintptr_t res_type = 0, res_value = 0; + { + MMArgumentItem item = MMArgumentStackPop(); + lr = item.lr; + + bool change_return = false; + void *return_object = NULL; + uintptr_t return_value = 0; + ReturnCallback return_callback = NULL; + { + MethodMatcherLock lock; + MethodMatcher *matcher = UnsafeGetMethodMatcher(item.target, item.selector); + if (matcher) { + change_return = matcher->change_return; + return_object = matcher->return_object; + return_value = matcher->return_value; + return_callback = matcher->return_callback; + } + } + + if (change_return || return_callback) { + Method method = MMGetMethod((id)item.target, (SEL)item.selector); + char *type = method_copyReturnType(method); + if (MM_LIKELY(type)) { + if (change_return) { + res_type = (*type == 'f' || *type == 'd') ? 0B10 : 0B1; + res_value = return_object ? (uintptr_t)return_object : return_value; + } + + if (return_callback) { + ReturnCallbackError error = kReturnCallbackErrorNone; + uintptr_t callback_value = 0; + if (MMPtrLengthArgumentTypes().count(*type) == 0) { + error = kReturnCallbackErrorNotX0D0; + } else if (*type == 'v') { + error = kReturnCallbackErrorNoReturn; + } else { + callback_value = res_type > 0 ? res_value : ((*type == 'f' || *type == 'd') ? d0 : x0); + } + return_callback(callback_value, error); + } + + free(type); + } + } + + { // 尝试删除 matcher + MethodMatcherLock lock; + MethodMatcher *matcher = UnsafeGetMethodMatcher(item.target, item.selector); + if (matcher) { + ++matcher->hit_count; + --matcher->using_count; + if (matcher->shouldDelete()) { + UnsafeRemoveMethodMatcher(matcher->target, matcher->selector); + } + } + } + } + __asm volatile + ("mov x0, %0\n" + "mov x1, %1\n" + "mov x2, %2\n" + :: "r"(lr), "r"(res_type), "r"(res_value)); +} + +#define BLR(func) \ +__asm volatile ("stp x8, x9, [sp, #-16]!"); \ +__asm volatile ("mov x12, %0" :: "r"(func)); \ +__asm volatile ("ldp x8, x9, [sp], #16"); \ +__asm volatile ("blr x12"); + +#define SAVE_REGISTERS() \ +__asm volatile ( \ +"stp x8, x9, [sp, #-16]!\n" \ +"stp x6, x7, [sp, #-16]!\n" \ +"stp x4, x5, [sp, #-16]!\n" \ +"stp x2, x3, [sp, #-16]!\n" \ +"stp x0, x1, [sp, #-16]!\n" \ + \ +"stp q8, q9, [sp, #-32]!\n" \ +"stp q6, q7, [sp, #-32]!\n" \ +"stp q4, q5, [sp, #-32]!\n" \ +"stp q2, q3, [sp, #-32]!\n" \ +"stp q0, q1, [sp, #-32]!\n"); + +#define LOAD_REGISTERS() \ +__asm volatile ( \ +"ldp q0, q1, [sp], #32\n" \ +"ldp q2, q3, [sp], #32\n" \ +"ldp q4, q5, [sp], #32\n" \ +"ldp q6, q7, [sp], #32\n" \ +"ldp q8, q9, [sp], #32\n" \ + \ +"ldp x0, x1, [sp], #16\n" \ +"ldp x2, x3, [sp], #16\n" \ +"ldp x4, x5, [sp], #16\n" \ +"ldp x6, x7, [sp], #16\n" \ +"ldp x8, x9, [sp], #16\n"); + +#define MARK_FRAME() \ +__asm volatile( \ +".cfi_def_cfa w29, 16\n" \ +".cfi_offset w30, -8\n" \ +".cfi_offset w29, -16\n"); + +#define RET() __asm volatile ("ret"); + +__attribute__((__naked__)) +static void ObjcMsgSend() { + MARK_FRAME() + SAVE_REGISTERS() + + __asm volatile ("mov x9, lr"); + BLR(&beforeObjcMsgSend) + // 是否跳过原函数调用 + __asm volatile ("cbnz w1, 40f"); + // 是否修改入参 + __asm volatile ("cbz x0, 30f"); + + // 浮点型入参修改 + __asm volatile + ("0:" + "and x1, x0, #0b100000000\n" + "cbz x1, 1f\n" + "str d0, [sp, #0]"); + __asm volatile + ("1:" + "and x1, x0, #0b1000000000\n" + "cbz x1, 2f\n" + "str d1, [sp, #16]"); + __asm volatile + ("2:" + "and x1, x0, #0b10000000000\n" + "cbz x1, 3f\n" + "str d2, [sp, #32]"); + __asm volatile + ("3:" + "and x1, x0, #0b100000000000\n" + "cbz x1, 4f\n" + "str d3, [sp, #48]"); + __asm volatile + ("4:" + "and x1, x0, #0b1000000000000\n" + "cbz x1, 5f\n" + "str d4, [sp, #64]"); + __asm volatile + ("5:" + "and x1, x0, #0b10000000000000\n" + "cbz x1, 6f\n" + "str d5, [sp, #80]"); + __asm volatile + ("6:" + "and x1, x0, #0b100000000000000\n" + "cbz x1, 7f\n" + "str d6, [sp, #96]"); + __asm volatile + ("7:" + "and x1, x0, #0b1000000000000000\n" + "cbz x1, 10f\n" + "str d7, [sp, #112]"); + // 通用型入参修改 + __asm volatile + ("10:" + "and x1, x0, #0b100\n" + "cbz x1, 11f\n" + "str x2, [sp, #176]"); + __asm volatile + ("11:" + "and x1, x0, #0b1000\n" + "cbz x1, 12f\n" + "str x3, [sp, #184]"); + __asm volatile + ("12:" + "and x1, x0, #0b10000\n" + "cbz x1, 13f\n" + "str x4, [sp, #192]"); + __asm volatile + ("13:" + "and x1, x0, #0b100000\n" + "cbz x1, 14f\n" + "str x5, [sp, #200]"); + __asm volatile + ("14:" + "and x1, x0, #0b1000000\n" + "cbz x1, 15f\n" + "str x6, [sp, #208]"); + __asm volatile + ("15:" + "and x1, x0, #0b10000000\n" + "cbz x1, 30f\n" + "str x7, [sp, #216]"); + + __asm volatile ("30:"); + LOAD_REGISTERS() + BLR(origin_objc_msgSend) + SAVE_REGISTERS() + + __asm volatile ("40:"); + BLR(&AfterObjcMsgSend) + // 是否修改返回值 + __asm volatile ("cbz w1, 50f"); + + // 通用型返回值修改 + __asm volatile + ("and x3, x1, #0b1\n" + "cbz x3, 41f\n" + "str x2, [sp, #160]\n"); + // 浮点型返回值修改 + __asm volatile + ("41:" + "and x3, x1, #0b10\n" + "cbz x3, 50f\n" + "str x2, [sp]\n"); + + __asm volatile ("50:"); + __asm volatile ("mov lr, x0"); + + LOAD_REGISTERS() + RET() +} + +#pragma mark - Public + +void MMStartWorking() { + static dispatch_once_t once_token; + dispatch_once(&once_token, ^{ + MMThreadKeyCreate(); + + struct mm_rebinding info = { + .name = "objc_msgSend", + .replacement = (void *)ObjcMsgSend, + .replaced = (void **)&origin_objc_msgSend, + }; + mm_rebind_symbols((struct mm_rebinding[]){info}, 1); + }); +} + +#else + +void MMStartWorking() {} + +#endif diff --git a/MessageMock/Core/mm_hook_objc_msgsend.h b/MessageMock/Core/mm_hook_objc_msgsend.h new file mode 100644 index 0000000..425e936 --- /dev/null +++ b/MessageMock/Core/mm_hook_objc_msgsend.h @@ -0,0 +1,13 @@ +// +// mm_hook_objc_msgsend.h +// MessageMock +// +// Created by 波儿菜 on 2020/6/27. +// + +#ifndef mm_hook_objc_msgsend_h +#define mm_hook_objc_msgsend_h + +void MMStartWorking(void); + +#endif /* mm_hook_objc_msgsend_h */ diff --git a/MessageMock/Core/mm_method_matcher.cpp b/MessageMock/Core/mm_method_matcher.cpp new file mode 100644 index 0000000..571fcbf --- /dev/null +++ b/MessageMock/Core/mm_method_matcher.cpp @@ -0,0 +1,123 @@ +// +// mm_method_matcher.cpp +// MessageMock +// +// Created by 波儿菜 on 2020/6/27. +// + +#import "mm_method_matcher.h" +#import +#import +#import + +namespace mm_method_matcher { + +static pthread_mutex_t *GetMethodMatcherMutex(void) { + static pthread_mutex_t method_matcher_lock; + static dispatch_once_t once_token; + dispatch_once(&once_token, ^{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&method_matcher_lock, &attr); + pthread_mutexattr_destroy(&attr); + }); + return &method_matcher_lock; +} + +typedef std::unordered_map> MethodMatcherMap; + +MethodMatcherMap &GetMethodMatcherMap() { + static MethodMatcherMap *matcher_map = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + matcher_map = new MethodMatcherMap(); + }); + return *matcher_map; +}; + +#pragma mark - MethodMatcherLock Implementation + +MethodMatcherLock::MethodMatcherLock() { + pthread_mutex_lock(GetMethodMatcherMutex()); +} + +MethodMatcherLock::~MethodMatcherLock() { + pthread_mutex_unlock(GetMethodMatcherMutex()); +} + +#pragma mark - MethodMatcher Implementation + +bool MethodMatcher::isValid() { + return 0 != target && 0 != selector && hit_count < limit_hit_count; +} + +bool MethodMatcher::shouldDelete() { + return hit_count >= limit_hit_count && using_count <= 0 && reference <= 0; +} + +MethodMatcher::~MethodMatcher() { + if (arguments) { + for (Argument mark : *arguments) { + if (mark.object) { + CFRelease(mark.object); + } + } + delete arguments; + arguments = NULL; + } + if (return_object) { + CFRelease(return_object); + return_object = NULL; + } +} + +#pragma mark - Matcher Access + +void UnsafeRemoveMethodMatcher(uintptr_t target, uintptr_t selector) { + MethodMatcherMap &map = GetMethodMatcherMap(); + auto iterator = map.find(target); + if (map.end() != iterator) { + std::unordered_map &matcher_map = iterator->second; + auto matcher_iterator = matcher_map.find(selector); + + if (matcher_map.end() != matcher_iterator) { + matcher_map.erase(matcher_iterator); + MethodMatcher *matcher = matcher_iterator->second; + delete matcher; + } + } +} + +MethodMatcher * _Nullable UnsafeGetMethodMatcher(uintptr_t target, uintptr_t selector) { + MethodMatcher *matcher = NULL; + MethodMatcherMap &map = GetMethodMatcherMap(); + auto iterator = map.find(target); + if (map.end() != iterator) { + MethodMatcher *tmp = iterator->second[selector]; + if (tmp && tmp->isValid()) { + matcher = tmp; + } + } + return matcher; +} + +bool AddMethodMatcher(MethodMatcher *matcher) { + if (MM_UNLIKELY(!matcher || !matcher->isValid())) { + return false; + } + uintptr_t target = matcher->target, selector = matcher->selector; + + MethodMatcherLock lock; + MethodMatcher *old_matcher = UnsafeGetMethodMatcher(target, selector); + if (old_matcher && !old_matcher->shouldDelete()) { + return false; // old_matcher 由于数据安全问题无法删除 + } + if (old_matcher != matcher) { + UnsafeRemoveMethodMatcher(target, selector); + GetMethodMatcherMap()[target][selector] = matcher; + } + return true; +} + +} //namespace mm_method_matcher diff --git a/MessageMock/Core/mm_method_matcher.h b/MessageMock/Core/mm_method_matcher.h new file mode 100644 index 0000000..452639c --- /dev/null +++ b/MessageMock/Core/mm_method_matcher.h @@ -0,0 +1,115 @@ +// +// mm_method_matcher.h +// MessageMock +// +// Created by 波儿菜 on 2020/6/27. +// + +#ifndef mm_method_matcher_h +#define mm_method_matcher_h + +#include +#include +#include +#include "mm_define.h" + +MM_ASSUME_NONNULL_BEGIN + +namespace mm_method_matcher { + +typedef struct { + /// 需要改变的参数下标 + unsigned int idx = 0; + /// 需要改变的参数值 + uintptr_t value = 0; + /// 需要改变的参数指针(用于接管 objc 对象引用计数) + void * _Nullable object = NULL; +} Argument; + +typedef std::vector ArgumentList; + +typedef enum { + /// 无错误 + kArgumentCallbackErrorNone, + /// 参数寄存器超过 x0 - x7 + kArgumentCallbackErrorOverX7, + /// 参数寄存器超过 d0 - d7 + kArgumentCallbackErrorOverD7, + /// 参数列表存在不支持的类型 + kArgumentCallbackErrorUnsupported, + /// 没有这个下标的参数 + kArgumentCallbackErrorNoIndex, +} ArgumentCallbackError; + +typedef void(^ArgumentCallback)(uintptr_t arg, ArgumentCallbackError error); + +typedef std::unordered_map ArgumentCallbackMap; + +typedef enum { + /// 无错误 + kReturnCallbackErrorNone, + /// 返回值不是通过 x0/d0 传递 + kReturnCallbackErrorNotX0D0, + /// 没有返回值 + kReturnCallbackErrorNoReturn, +} ReturnCallbackError; + +typedef void(^ReturnCallback)(uintptr_t arg, ReturnCallbackError error); + +/// 方法匹配配置对象 +class MethodMatcher { +public: + /// 是否跳过该 [target selector] 调用 + bool skip = false; + /// 是否改变返回值 + bool change_return = false; + /// 被引用的次数(用于上层代码不期望该内存释放) + long reference = 0; + /// 命中次数 + long hit_count = 0; + /// 限制命中次数 + long limit_hit_count = 1; + /// 正在使用的数量 + long using_count = 0; + /// 目标 id 地址 + uintptr_t target = 0; + /// 目标 SEL 地址 + uintptr_t selector = 0; + /// 需要改变的返回值 + uintptr_t return_value = 0; + /// 需要改变的返回指针(用于接管 objc 对象引用计数) + void * _Nullable return_object = NULL; + /// 需要修改的参数表 + ArgumentList * _Nullable arguments = NULL; + /// 调用时检查参数的函数指针表 + ArgumentCallbackMap * _Nullable argument_callback_map = NULL; + /// 检查返回值的函数指针 + ReturnCallback _Nullable return_callback = NULL; + + /// 该 matcher 是否有效 + bool isValid(); + /// 是否可以删除 + bool shouldDelete(); + + ~MethodMatcher(); +}; + +/// matcher 读写锁 +class MethodMatcherLock { +public: + MethodMatcherLock(); + ~MethodMatcherLock(); +}; + +/// 安全的添加 matcher,返回是否成功 +bool AddMethodMatcher(MethodMatcher *matcher); +/// 不安全的移除 matcher +void UnsafeRemoveMethodMatcher(uintptr_t target, uintptr_t selector); +/// 不安全的获取 matcher +MethodMatcher * _Nullable UnsafeGetMethodMatcher(uintptr_t target, uintptr_t selector); + +} //namespace mm_method_matcher + +MM_ASSUME_NONNULL_END + +#endif /* mm_method_matcher_h */ diff --git a/MessageMock/Core/mm_runtime.cpp b/MessageMock/Core/mm_runtime.cpp new file mode 100644 index 0000000..8440455 --- /dev/null +++ b/MessageMock/Core/mm_runtime.cpp @@ -0,0 +1,31 @@ +// +// mm_runtime.cpp +// MessageMock +// +// Created by 波儿菜 on 2020/7/11. +// + +#import "mm_runtime.h" +#import + +Method MMGetMethod(id self, SEL _cmd) { + Method method = NULL; + if (object_isClass(self)) { + method = class_getClassMethod((Class)self, _cmd); + } else { + method = class_getInstanceMethod(object_getClass(self), _cmd); + } + return method; +} + +std::unordered_set MMPtrLengthArgumentTypes() { + static dispatch_once_t once_token; + static std::unordered_set *types; + dispatch_once(&once_token, ^{ + types = new std::unordered_set( + {'c', 'i', 's', 'l', 'q', 'C', 'I', 'S', 'L', + 'Q', 'f', 'd', 'B', 'v', '*', '@', '#', ':', '^'}); + }); + return *types; +} + diff --git a/MessageMock/Core/mm_runtime.h b/MessageMock/Core/mm_runtime.h new file mode 100644 index 0000000..8a92d00 --- /dev/null +++ b/MessageMock/Core/mm_runtime.h @@ -0,0 +1,20 @@ +// +// mm_runtime.h +// MessageMock +// +// Created by 波儿菜 on 2020/7/11. +// + +#ifndef mm_runtime_h +#define mm_runtime_h + +#include +#include +#include + +Method MMGetMethod(id self, SEL _cmd); + +/// 小于等于指针长度的类型前缀集合 +std::unordered_set MMPtrLengthArgumentTypes(); + +#endif /* mm_runtime_h */ diff --git a/MessageMock/MessageMocker.h b/MessageMock/MessageMocker.h new file mode 100644 index 0000000..b44e3fc --- /dev/null +++ b/MessageMock/MessageMocker.h @@ -0,0 +1,53 @@ +// +// MessageMocker.h +// MessageMock +// +// Created by 波儿菜 on 2020/7/11. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 模拟类,根据 [target selector] 命中目标方法,主要能力: +/// - 修改返回值、参数 +/// - 验证返回值、参数 +/// - 跳过原方法 +/// - 获取方法命中次数 +@interface MessageMocker : NSObject + +/// 遍历构造(target 目标对象 selector 目标方法) +@property (class, nonatomic, copy, readonly) MessageMocker * _Nullable (^build)(id target, SEL selector); + +/// 修改返回值 +@property (nonatomic, copy, readonly) MessageMocker *(^mockReturn)(const char *type, ...); +#define mockReturn(arg) mockReturn(@encode(typeof(arg)), arg, nil) + +/// 修改调用参数 +@property (nonatomic, copy, readonly) MessageMocker *(^mockArgument)(unsigned int idx, const char *type, ...); +#define mockArgument(idx, arg) mockArgument(idx, @encode(typeof(arg)), arg, nil) + +/// 检查返回值,callback 是闭包:^(AnyType arg) {} +@property (nonatomic, copy, readonly) MessageMocker *(^checkReturn)(id callback); + +/// 检查参数,callback 是闭包:^(AnyType arg) {} +@property (nonatomic, copy, readonly) MessageMocker *(^checkArgument)(unsigned int idx, id callback); + +/// 是否跳过该 [target selector] 调用,默认 NO +@property (nonatomic, copy, readonly) MessageMocker *(^skip)(BOOL skip); + +/// 限制命中次数,默认 1(超限则该伪装对象失效) +@property (nonatomic, copy, readonly) MessageMocker *(^limitHitCount)(NSInteger count); + +/// 方法命中次数(并发过高可能获取的值大于预期) +@property (nonatomic, assign, readonly) long hitCount; + +/// 启用(只能启用一次,启用后该对象任何修改无效) +@property (nonatomic, copy, readonly) void(^start)(void); + +- (instancetype)init UNAVAILABLE_ATTRIBUTE; ++ (instancetype)new UNAVAILABLE_ATTRIBUTE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MessageMock/MessageMocker.mm b/MessageMock/MessageMocker.mm new file mode 100644 index 0000000..9441970 --- /dev/null +++ b/MessageMock/MessageMocker.mm @@ -0,0 +1,229 @@ +// +// MessageMocker.m +// MessageMock +// +// Created by 波儿菜 on 2020/7/11. +// + +#import "MessageMocker.h" +#import "mm_method_matcher.h" +#import "mm_hook_objc_msgsend.h" +#import "mm_runtime.h" + +#define MM_CHECK_START if (self->_isStarted) { return self; } + +using namespace mm_method_matcher; + +static void AutoCallback(const char * _Nullable type, uintptr_t arg, id callback) { + switch (*type) { + case 'f': { + float tmp = *((float *)&arg); + ((void(^)(float))callback)(tmp); + } break; + case 'd': { + double tmp = *((double *)&arg); + ((void(^)(double))callback)(tmp); + } break; + default: { + ((void(^)(uintptr_t))callback)(arg); + } break; + } +} + +static BOOL SetNextArgument(const char *type, va_list argList, uintptr_t *nextValue, void **nextObject) { + if (!type || MMPtrLengthArgumentTypes().count(*type) == 0) { + return NO; + } + switch (*type) { + case 'f': { +#pragma clang push +#pragma clang diagnostic ignored "-Wvarargs" + float arg = va_arg(argList, float); +#pragma clang pop + *nextValue = *(uintptr_t *)&arg; + } break; + case 'd': { + double arg = va_arg(argList, double); + *nextValue = *(uintptr_t *)&arg; + } break; + case '@': { + if (*nextObject) { //把之前的对象清理掉 + CFRelease(*nextObject); + } + id arg = va_arg(argList, id); + *nextObject = (__bridge_retained void *)arg; + } break; + default: { + uintptr_t arg = va_arg(argList, uintptr_t); + *nextValue = (uintptr_t)arg; + } break; + } + return YES; +} + +@implementation MessageMocker { + MethodMatcher *_matcher; + BOOL _isStarted; + id _target; //避免 target 先于 mocker 释放 + SEL _selector; +} + +#pragma mark - Life Cycle + +- (nullable instancetype)initWithTarget:(id)target selector:(SEL)selector { + if (!target || !selector) { + return nil; + } + self = [super init]; + if (self) { + _target = target; + _selector = selector; + + _matcher = new MethodMatcher(); + _matcher->target = (uintptr_t)target; + _matcher->selector = (uintptr_t)selector; + ++_matcher->reference; + } + return self; +} + +- (void)dealloc { + MethodMatcherLock lock; + if (_matcher) { + --_matcher->reference; + if (_matcher->shouldDelete()) { + UnsafeRemoveMethodMatcher(_matcher->target, _matcher->selector); + } + } +} + +#pragma mark - Getters + ++ (MessageMocker * _Nullable (^)(id _Nonnull, SEL _Nonnull))build { + return ^MessageMocker *(id target, SEL selector) { + return [[MessageMocker alloc] initWithTarget:target selector:selector]; + }; +} + +- (MessageMocker * _Nonnull (^)(const char * _Nonnull, ...))mockReturn { + return ^MessageMocker *(const char *type, ...) { + MM_CHECK_START + + va_list argList; + va_start(argList, type); + BOOL succeed = SetNextArgument(type, argList, &(self->_matcher->return_value), &(self->_matcher->return_object)); + va_end(argList); + + if (succeed) { + self->_matcher->change_return = true; + } + return self; + }; +} + +- (MessageMocker * _Nonnull (^)(unsigned int, const char * _Nonnull, ...))mockArgument { + return ^MessageMocker *(unsigned int idx, const char *type, ...) { + MM_CHECK_START + + Argument mark; + mark.idx = idx + 2; + + va_list argList; + va_start(argList, type); + BOOL succeed = SetNextArgument(type, argList, &(mark.value), &(mark.object)); + va_end(argList); + + if (succeed) { + if (!self->_matcher->arguments) { + self->_matcher->arguments = new ArgumentList(); + } + self->_matcher->arguments->push_back(mark); + } + return self; + }; +} + +- (MessageMocker * _Nonnull (^)(id _Nonnull))checkReturn { + return ^MessageMocker *(id callback) { + MM_CHECK_START + + Method method = MMGetMethod(self->_target, self->_selector); + char *type = method_copyReturnType(method); + NSString *typeStr = nil; + if (type) { + typeStr = [NSString stringWithUTF8String:type]; + free(type); + } + + self->_matcher->return_callback = ^(uintptr_t arg, ReturnCallbackError error) { + if (error == kReturnCallbackErrorNone && typeStr.length > 0) { + AutoCallback(typeStr.UTF8String, arg, callback); + } + }; + return self; + }; +} + +- (MessageMocker * _Nonnull (^)(unsigned int, id _Nonnull))checkArgument { + return ^MessageMocker *(unsigned int idx, id callback) { + MM_CHECK_START + + unsigned int realIdx = idx + 2; + + Method method = MMGetMethod(self->_target, self->_selector); + char *type = method_copyArgumentType(method, realIdx); + NSString *typeStr = nil; + if (type) { + typeStr = [NSString stringWithUTF8String:type]; + free(type); + } + + ArgumentCallback insideCallback = ^(uintptr_t arg, ArgumentCallbackError error) { + if (error == kArgumentCallbackErrorNone && typeStr.length > 0) { + AutoCallback(typeStr.UTF8String, arg, callback); + } + }; + if (!self->_matcher->argument_callback_map) { + self->_matcher->argument_callback_map = new ArgumentCallbackMap(); + } + self->_matcher->argument_callback_map->insert(std::make_pair(realIdx, insideCallback)); + return self; + }; +} + +- (MessageMocker * _Nonnull (^)(BOOL))skip { + return ^MessageMocker *(BOOL skip) { + MM_CHECK_START + + self->_matcher->skip = skip; + return self; + }; +} + +- (MessageMocker * _Nonnull (^)(NSInteger))limitHitCount { + return ^MessageMocker *(NSInteger count) { + MM_CHECK_START + + self->_matcher->limit_hit_count = count; + return self; + }; +} + +- (long)hitCount { + return self->_matcher->hit_count; +} + +- (void (^)())start { + return ^void() { + if (self->_isStarted) { + return; + } + self->_isStarted = YES; + + AddMethodMatcher(self->_matcher); + MMStartWorking(); + }; +} + +@end + diff --git a/MessageMock/fishhook/mm_fishhook.c b/MessageMock/fishhook/mm_fishhook.c new file mode 100644 index 0000000..72d2a50 --- /dev/null +++ b/MessageMock/fishhook/mm_fishhook.c @@ -0,0 +1,212 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "mm_fishhook.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __LP64__ +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +#ifndef SEG_DATA_CONST +#define SEG_DATA_CONST "__DATA_CONST" +#endif + +struct rebindings_entry { + struct mm_rebinding *rebindings; + size_t rebindings_nel; + struct rebindings_entry *next; +}; + +static struct rebindings_entry *_rebindings_head; + +static int prepend_rebindings(struct rebindings_entry **rebindings_head, + struct mm_rebinding rebindings[], + size_t nel) { + struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); + if (!new_entry) { + return -1; + } + new_entry->rebindings = (struct mm_rebinding *) malloc(sizeof(struct mm_rebinding) * nel); + if (!new_entry->rebindings) { + free(new_entry); + return -1; + } + memcpy(new_entry->rebindings, rebindings, sizeof(struct mm_rebinding) * nel); + new_entry->rebindings_nel = nel; + new_entry->next = *rebindings_head; + *rebindings_head = new_entry; + return 0; +} + +static void perform_rebinding_with_section(struct rebindings_entry *rebindings, + section_t *section, + intptr_t slide, + nlist_t *symtab, + char *strtab, + uint32_t *indirect_symtab) { + uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; + void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); + for (uint i = 0; i < section->size / sizeof(void *); i++) { + uint32_t symtab_index = indirect_symbol_indices[i]; + if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || + symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + continue; + } + uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; + bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; + struct rebindings_entry *cur = rebindings; + while (cur) { + for (uint j = 0; j < cur->rebindings_nel; j++) { + if (symbol_name_longer_than_1 && + strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { + if (cur->rebindings[j].replaced != NULL && + indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { + *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; + } + indirect_symbol_bindings[i] = cur->rebindings[j].replacement; + goto symbol_loop; + } + } + cur = cur->next; + } + symbol_loop:; + } +} + +static void rebind_symbols_for_image(struct rebindings_entry *rebindings, + const struct mach_header *header, + intptr_t slide) { + Dl_info info; + if (dladdr(header, &info) == 0) { + return; + } + + segment_command_t *cur_seg_cmd; + segment_command_t *linkedit_segment = NULL; + struct symtab_command* symtab_cmd = NULL; + struct dysymtab_command* dysymtab_cmd = NULL; + + uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { + linkedit_segment = cur_seg_cmd; + } + } else if (cur_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command*)cur_seg_cmd; + } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; + } + } + + if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || + !dysymtab_cmd->nindirectsyms) { + return; + } + + // Find base symbol/string table addresses + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); + char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); + + // Get indirect symbol table (array of uint32_t indices into symbol table) + uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); + + cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && + strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { + continue; + } + for (uint j = 0; j < cur_seg_cmd->nsects; j++) { + section_t *sect = + (section_t *)(cur + sizeof(segment_command_t)) + j; + if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + } + } + } +} + +static void _rebind_symbols_for_image(const struct mach_header *header, + intptr_t slide) { + rebind_symbols_for_image(_rebindings_head, header, slide); +} + +int mm_rebind_symbols_image(void *header, + intptr_t slide, + struct mm_rebinding rebindings[], + size_t rebindings_nel) { + struct rebindings_entry *rebindings_head = NULL; + int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); + rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); + if (rebindings_head) { + free(rebindings_head->rebindings); + } + free(rebindings_head); + return retval; +} + +int mm_rebind_symbols(struct mm_rebinding rebindings[], size_t rebindings_nel) { + int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); + if (retval < 0) { + return retval; + } + // If this was the first call, register callback for image additions (which is also invoked for + // existing images, otherwise, just run on existing images + if (!_rebindings_head->next) { + _dyld_register_func_for_add_image(_rebind_symbols_for_image); + } else { + uint32_t c = _dyld_image_count(); + for (uint32_t i = 0; i < c; i++) { + _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); + } + } + return retval; +} diff --git a/MessageMock/fishhook/mm_fishhook.h b/MessageMock/fishhook/mm_fishhook.h new file mode 100644 index 0000000..3821076 --- /dev/null +++ b/MessageMock/fishhook/mm_fishhook.h @@ -0,0 +1,68 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef mm_fishhook_h +#define mm_fishhook_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +/* + * A structure representing a particular intended mm_rebinding from a symbol + * name to its replacement + */ +struct mm_rebinding { + const char *name; + void *replacement; + void **replaced; +}; + +/* + * For each mm_rebinding in rebindings, rebinds references to external, indirect + * symbols with the specified name to instead point at replacement for each + * image in the calling process as well as for all future images that are loaded + * by the process. If rebind_functions is called more than once, the symbols to + * rebind are added to the existing list of rebindings, and if a given symbol + * is rebound more than once, the later mm_rebinding will take precedence. + */ +int mm_rebind_symbols(struct mm_rebinding rebindings[], size_t rebindings_nel); + +/* + * Rebinds as above, but only in the specified image. The header should point + * to the mach-o header, the slide should be the slide offset. Others as above. + */ +int mm_rebind_symbols_image(void *header, + intptr_t slide, + struct mm_rebinding rebindings[], + size_t rebindings_nel); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //mm_fishhook_h + diff --git a/MessageMockDemo/MessageMockDemo.xcodeproj/project.pbxproj b/MessageMockDemo/MessageMockDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6610e2b --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcodeproj/project.pbxproj @@ -0,0 +1,583 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 4BBEEA8C24D672E500BF51FC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA8B24D672E500BF51FC /* AppDelegate.m */; }; + 4BBEEA8F24D672E500BF51FC /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA8E24D672E500BF51FC /* SceneDelegate.m */; }; + 4BBEEA9224D672E500BF51FC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9124D672E500BF51FC /* ViewController.m */; }; + 4BBEEA9524D672E500BF51FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9324D672E500BF51FC /* Main.storyboard */; }; + 4BBEEA9724D672E600BF51FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9624D672E600BF51FC /* Assets.xcassets */; }; + 4BBEEA9A24D672E600BF51FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9824D672E600BF51FC /* LaunchScreen.storyboard */; }; + 4BBEEA9D24D672E600BF51FC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9C24D672E600BF51FC /* main.m */; }; + 4BBEEAB624D6758F00BF51FC /* MMReturnCallbackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB124D6758F00BF51FC /* MMReturnCallbackTests.mm */; }; + 4BBEEAB724D6758F00BF51FC /* MMMethodReturnTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB224D6758F00BF51FC /* MMMethodReturnTests.mm */; }; + 4BBEEAB824D6758F00BF51FC /* MessageMockerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB324D6758F00BF51FC /* MessageMockerTests.m */; }; + 4BBEEAB924D6758F00BF51FC /* MMArgumentCallbackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB424D6758F00BF51FC /* MMArgumentCallbackTests.mm */; }; + 4BBEEABA24D6758F00BF51FC /* MMChangeArgumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB524D6758F00BF51FC /* MMChangeArgumentTests.mm */; }; + 4BBEEABE24D6759A00BF51FC /* TestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEABD24D6759A00BF51FC /* TestObject.m */; }; + 788824398C4F1A526B692317 /* Pods_MessageMockDemoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85204ADEF3AF5EED2111AFA2 /* Pods_MessageMockDemoTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 4BBEEAA324D672E600BF51FC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4BBEEA7F24D672E500BF51FC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4BBEEA8624D672E500BF51FC; + remoteInfo = MessageMockDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 262BF739578A3A33058AFD27 /* Pods-MessageMockDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MessageMockDemoTests.release.xcconfig"; path = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.release.xcconfig"; sourceTree = ""; }; + 4BBEEA8724D672E500BF51FC /* MessageMockDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MessageMockDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4BBEEA8A24D672E500BF51FC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 4BBEEA8B24D672E500BF51FC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 4BBEEA8D24D672E500BF51FC /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 4BBEEA8E24D672E500BF51FC /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + 4BBEEA9024D672E500BF51FC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 4BBEEA9124D672E500BF51FC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 4BBEEA9424D672E500BF51FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 4BBEEA9624D672E600BF51FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4BBEEA9924D672E600BF51FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4BBEEA9B24D672E600BF51FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4BBEEA9C24D672E600BF51FC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 4BBEEAA224D672E600BF51FC /* MessageMockDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MessageMockDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 4BBEEAA824D672E600BF51FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4BBEEAB124D6758F00BF51FC /* MMReturnCallbackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMReturnCallbackTests.mm; sourceTree = ""; }; + 4BBEEAB224D6758F00BF51FC /* MMMethodReturnTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMMethodReturnTests.mm; sourceTree = ""; }; + 4BBEEAB324D6758F00BF51FC /* MessageMockerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessageMockerTests.m; sourceTree = ""; }; + 4BBEEAB424D6758F00BF51FC /* MMArgumentCallbackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMArgumentCallbackTests.mm; sourceTree = ""; }; + 4BBEEAB524D6758F00BF51FC /* MMChangeArgumentTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMChangeArgumentTests.mm; sourceTree = ""; }; + 4BBEEABC24D6759A00BF51FC /* TestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestObject.h; sourceTree = ""; }; + 4BBEEABD24D6759A00BF51FC /* TestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestObject.m; sourceTree = ""; }; + 85204ADEF3AF5EED2111AFA2 /* Pods_MessageMockDemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MessageMockDemoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F15F084354D2FA76553B028E /* Pods-MessageMockDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MessageMockDemoTests.debug.xcconfig"; path = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4BBEEA8424D672E500BF51FC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4BBEEA9F24D672E600BF51FC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 788824398C4F1A526B692317 /* Pods_MessageMockDemoTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4BBEEA7E24D672E500BF51FC = { + isa = PBXGroup; + children = ( + 4BBEEA8924D672E500BF51FC /* MessageMockDemo */, + 4BBEEAA524D672E600BF51FC /* MessageMockDemoTests */, + 4BBEEA8824D672E500BF51FC /* Products */, + 88F83CC6A351B0D316702CB8 /* Pods */, + C32F0D5F36833E2214BF6D03 /* Frameworks */, + ); + sourceTree = ""; + }; + 4BBEEA8824D672E500BF51FC /* Products */ = { + isa = PBXGroup; + children = ( + 4BBEEA8724D672E500BF51FC /* MessageMockDemo.app */, + 4BBEEAA224D672E600BF51FC /* MessageMockDemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4BBEEA8924D672E500BF51FC /* MessageMockDemo */ = { + isa = PBXGroup; + children = ( + 4BBEEABB24D6759A00BF51FC /* TestObjects */, + 4BBEEA8A24D672E500BF51FC /* AppDelegate.h */, + 4BBEEA8B24D672E500BF51FC /* AppDelegate.m */, + 4BBEEA8D24D672E500BF51FC /* SceneDelegate.h */, + 4BBEEA8E24D672E500BF51FC /* SceneDelegate.m */, + 4BBEEA9024D672E500BF51FC /* ViewController.h */, + 4BBEEA9124D672E500BF51FC /* ViewController.m */, + 4BBEEA9324D672E500BF51FC /* Main.storyboard */, + 4BBEEA9624D672E600BF51FC /* Assets.xcassets */, + 4BBEEA9824D672E600BF51FC /* LaunchScreen.storyboard */, + 4BBEEA9B24D672E600BF51FC /* Info.plist */, + 4BBEEA9C24D672E600BF51FC /* main.m */, + ); + path = MessageMockDemo; + sourceTree = ""; + }; + 4BBEEAA524D672E600BF51FC /* MessageMockDemoTests */ = { + isa = PBXGroup; + children = ( + 4BBEEAB124D6758F00BF51FC /* MMReturnCallbackTests.mm */, + 4BBEEAB424D6758F00BF51FC /* MMArgumentCallbackTests.mm */, + 4BBEEAB524D6758F00BF51FC /* MMChangeArgumentTests.mm */, + 4BBEEAB224D6758F00BF51FC /* MMMethodReturnTests.mm */, + 4BBEEAB324D6758F00BF51FC /* MessageMockerTests.m */, + 4BBEEAA824D672E600BF51FC /* Info.plist */, + ); + path = MessageMockDemoTests; + sourceTree = ""; + }; + 4BBEEABB24D6759A00BF51FC /* TestObjects */ = { + isa = PBXGroup; + children = ( + 4BBEEABC24D6759A00BF51FC /* TestObject.h */, + 4BBEEABD24D6759A00BF51FC /* TestObject.m */, + ); + path = TestObjects; + sourceTree = ""; + }; + 88F83CC6A351B0D316702CB8 /* Pods */ = { + isa = PBXGroup; + children = ( + F15F084354D2FA76553B028E /* Pods-MessageMockDemoTests.debug.xcconfig */, + 262BF739578A3A33058AFD27 /* Pods-MessageMockDemoTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + C32F0D5F36833E2214BF6D03 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 85204ADEF3AF5EED2111AFA2 /* Pods_MessageMockDemoTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4BBEEA8624D672E500BF51FC /* MessageMockDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4BBEEAAB24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemo" */; + buildPhases = ( + 4BBEEA8324D672E500BF51FC /* Sources */, + 4BBEEA8424D672E500BF51FC /* Frameworks */, + 4BBEEA8524D672E500BF51FC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MessageMockDemo; + productName = MessageMockDemo; + productReference = 4BBEEA8724D672E500BF51FC /* MessageMockDemo.app */; + productType = "com.apple.product-type.application"; + }; + 4BBEEAA124D672E600BF51FC /* MessageMockDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4BBEEAAE24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemoTests" */; + buildPhases = ( + 6BF2D91D93E65C2A7E021BAA /* [CP] Check Pods Manifest.lock */, + 4BBEEA9E24D672E600BF51FC /* Sources */, + 4BBEEA9F24D672E600BF51FC /* Frameworks */, + 4BBEEAA024D672E600BF51FC /* Resources */, + 2D84A30AADD66651E1C588B5 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 4BBEEAA424D672E600BF51FC /* PBXTargetDependency */, + ); + name = MessageMockDemoTests; + productName = MessageMockDemoTests; + productReference = 4BBEEAA224D672E600BF51FC /* MessageMockDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4BBEEA7F24D672E500BF51FC /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1150; + ORGANIZATIONNAME = yangbo; + TargetAttributes = { + 4BBEEA8624D672E500BF51FC = { + CreatedOnToolsVersion = 11.5; + }; + 4BBEEAA124D672E600BF51FC = { + CreatedOnToolsVersion = 11.5; + TestTargetID = 4BBEEA8624D672E500BF51FC; + }; + }; + }; + buildConfigurationList = 4BBEEA8224D672E500BF51FC /* Build configuration list for PBXProject "MessageMockDemo" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4BBEEA7E24D672E500BF51FC; + productRefGroup = 4BBEEA8824D672E500BF51FC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4BBEEA8624D672E500BF51FC /* MessageMockDemo */, + 4BBEEAA124D672E600BF51FC /* MessageMockDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4BBEEA8524D672E500BF51FC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4BBEEA9A24D672E600BF51FC /* LaunchScreen.storyboard in Resources */, + 4BBEEA9724D672E600BF51FC /* Assets.xcassets in Resources */, + 4BBEEA9524D672E500BF51FC /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4BBEEAA024D672E600BF51FC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2D84A30AADD66651E1C588B5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6BF2D91D93E65C2A7E021BAA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-MessageMockDemoTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4BBEEA8324D672E500BF51FC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4BBEEA9224D672E500BF51FC /* ViewController.m in Sources */, + 4BBEEA8C24D672E500BF51FC /* AppDelegate.m in Sources */, + 4BBEEABE24D6759A00BF51FC /* TestObject.m in Sources */, + 4BBEEA9D24D672E600BF51FC /* main.m in Sources */, + 4BBEEA8F24D672E500BF51FC /* SceneDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4BBEEA9E24D672E600BF51FC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4BBEEAB724D6758F00BF51FC /* MMMethodReturnTests.mm in Sources */, + 4BBEEAB924D6758F00BF51FC /* MMArgumentCallbackTests.mm in Sources */, + 4BBEEAB624D6758F00BF51FC /* MMReturnCallbackTests.mm in Sources */, + 4BBEEAB824D6758F00BF51FC /* MessageMockerTests.m in Sources */, + 4BBEEABA24D6758F00BF51FC /* MMChangeArgumentTests.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4BBEEAA424D672E600BF51FC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4BBEEA8624D672E500BF51FC /* MessageMockDemo */; + targetProxy = 4BBEEAA324D672E600BF51FC /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 4BBEEA9324D672E500BF51FC /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4BBEEA9424D672E500BF51FC /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 4BBEEA9824D672E600BF51FC /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4BBEEA9924D672E600BF51FC /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4BBEEAA924D672E600BF51FC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 4BBEEAAA24D672E600BF51FC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4BBEEAAC24D672E600BF51FC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = UU8H9EQ986; + INFOPLIST_FILE = MessageMockDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4BBEEAAD24D672E600BF51FC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = UU8H9EQ986; + INFOPLIST_FILE = MessageMockDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 4BBEEAAF24D672E600BF51FC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F15F084354D2FA76553B028E /* Pods-MessageMockDemoTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = UU8H9EQ986; + INFOPLIST_FILE = MessageMockDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MessageMockDemo.app/MessageMockDemo"; + }; + name = Debug; + }; + 4BBEEAB024D672E600BF51FC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 262BF739578A3A33058AFD27 /* Pods-MessageMockDemoTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = UU8H9EQ986; + INFOPLIST_FILE = MessageMockDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MessageMockDemo.app/MessageMockDemo"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4BBEEA8224D672E500BF51FC /* Build configuration list for PBXProject "MessageMockDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4BBEEAA924D672E600BF51FC /* Debug */, + 4BBEEAAA24D672E600BF51FC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4BBEEAAB24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4BBEEAAC24D672E600BF51FC /* Debug */, + 4BBEEAAD24D672E600BF51FC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4BBEEAAE24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4BBEEAAF24D672E600BF51FC /* Debug */, + 4BBEEAB024D672E600BF51FC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4BBEEA7F24D672E500BF51FC /* Project object */; +} diff --git a/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..4bda33e --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate b/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..a4afea6 Binary files /dev/null and b/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MessageMockDemo/MessageMockDemo.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist b/MessageMockDemo/MessageMockDemo.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..b50fe4c --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + MessageMockDemo.xcscheme_^#shared#^_ + + orderHint + 2 + + + + diff --git a/MessageMockDemo/MessageMockDemo.xcworkspace/contents.xcworkspacedata b/MessageMockDemo/MessageMockDemo.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..af92ba0 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/MessageMockDemo/MessageMockDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MessageMockDemo/MessageMockDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate b/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..ef3fcf9 Binary files /dev/null and b/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..a72d43f --- /dev/null +++ b/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,40 @@ + + + + + + + + + + + + + diff --git a/MessageMockDemo/MessageMockDemo/AppDelegate.h b/MessageMockDemo/MessageMockDemo/AppDelegate.h new file mode 100644 index 0000000..fa0e025 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/AppDelegate.h @@ -0,0 +1,14 @@ +// +// AppDelegate.h +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import + +@interface AppDelegate : UIResponder + + +@end + diff --git a/MessageMockDemo/MessageMockDemo/AppDelegate.m b/MessageMockDemo/MessageMockDemo/AppDelegate.m new file mode 100644 index 0000000..620cb32 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/AppDelegate.m @@ -0,0 +1,40 @@ +// +// AppDelegate.m +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + + +#pragma mark - UISceneSession lifecycle + + +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; +} + + +- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions API_AVAILABLE(ios(13.0)) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. +} + + +@end diff --git a/MessageMockDemo/MessageMockDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/MessageMockDemo/MessageMockDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MessageMockDemo/MessageMockDemo/Assets.xcassets/Contents.json b/MessageMockDemo/MessageMockDemo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MessageMockDemo/MessageMockDemo/Base.lproj/LaunchScreen.storyboard b/MessageMockDemo/MessageMockDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..9d64fdb --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MessageMockDemo/MessageMockDemo/Base.lproj/Main.storyboard b/MessageMockDemo/MessageMockDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..3144d00 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/Base.lproj/Main.storyboard @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MessageMockDemo/MessageMockDemo/Info.plist b/MessageMockDemo/MessageMockDemo/Info.plist new file mode 100644 index 0000000..7b6037c --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/MessageMockDemo/MessageMockDemo/SceneDelegate.h b/MessageMockDemo/MessageMockDemo/SceneDelegate.h new file mode 100644 index 0000000..e42840e --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/SceneDelegate.h @@ -0,0 +1,15 @@ +// +// SceneDelegate.h +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import + +@interface SceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow * window; + +@end + diff --git a/MessageMockDemo/MessageMockDemo/SceneDelegate.m b/MessageMockDemo/MessageMockDemo/SceneDelegate.m new file mode 100644 index 0000000..ad7506e --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/SceneDelegate.m @@ -0,0 +1,57 @@ +// +// SceneDelegate.m +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import "SceneDelegate.h" + +@interface SceneDelegate () + +@end + +@implementation SceneDelegate + + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). +} + + +- (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0)) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). +} + + +- (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. +} + + +- (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). +} + + +- (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. +} + + +- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. +} + + +@end diff --git a/MessageMockDemo/MessageMockDemo/TestObjects/TestObject.h b/MessageMockDemo/MessageMockDemo/TestObjects/TestObject.h new file mode 100644 index 0000000..c7bf63c --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/TestObjects/TestObject.h @@ -0,0 +1,57 @@ +// +// TestObject.h +// MessageMockDemo +// +// Created by 波儿菜 on 2020/6/27. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TestObject : NSObject + ++ (TestObject *)createObj; + ++ (char *)createPointer; + ++ (long)createLong; + ++ (int)createInt; + ++ (int16_t)createInt16; + ++ (int8_t)createInt8; + ++ (double)createDouble; + ++ (float)createFloat; + +/* +参数基本规则 +仅使用寄存器情况: +通用寄存器参数最多 6 个 x2 - x7 +浮点寄存器参数最多 8 个 d0 - d7 (编译器限制不能连续超过6个) +*/ + +@property (nonatomic, copy) NSString *name; +@property (nonatomic, assign) int age; +@property (nonatomic, assign) NSInteger phone; +@property (nonatomic, assign) BOOL isMan; +@property (nonatomic, assign) double height; +@property (nonatomic, assign) float weight; +@property (nonatomic, strong) NSObject *extra; + ++ (TestObject *)createWithName:(NSString *)name + age:(int)age + phone:(NSInteger)phone + isMan:(BOOL)isMan + height:(double)height + weight:(float)weight + extra:(NSObject *)extra; + ++ (TestObject *)defaultArgument; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MessageMockDemo/MessageMockDemo/TestObjects/TestObject.m b/MessageMockDemo/MessageMockDemo/TestObjects/TestObject.m new file mode 100644 index 0000000..ce2b22a --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/TestObjects/TestObject.m @@ -0,0 +1,90 @@ +// +// TestObject.m +// MessageMockDemo +// +// Created by 波儿菜 on 2020/6/27. +// + +#import "TestObject.h" + +@implementation TestObject + +- (void)dealloc { + NSLog(@"dealloc: %@", self); +} + ++ (TestObject *)createObj { + TestObject *obj = [TestObject new]; + return obj; +} + ++ (char *)createPointer { + char *value = (char *)malloc(sizeof(char)); + value = "aaaaa"; + return value; +} + ++ (long)createLong { + return 100; +} + ++ (int)createInt { + return -100; +} + ++ (int16_t)createInt16 { + return -100; +} + ++ (int8_t)createInt8 { + return (int8_t)(1 << 7); +} + ++ (double)createDouble { + return 1000000000000.0001192873912731982; +} + ++ (float)createFloat { + return 97249213.23141234; +} + ++ (TestObject *)createWithName:(NSString *)name age:(int)age phone:(NSInteger)phone isMan:(BOOL)isMan height:(double)height weight:(float)weight extra:(nonnull NSObject *)extra { + TestObject *res = [TestObject new]; + res.name = name; + res.phone = phone; + res.age = age; + res.height = height; + res.weight = weight; + res.isMan = isMan; + res.extra = extra; + return res; +} + ++ (TestObject *)defaultArgument { + TestObject *res = [TestObject new]; + res.name = @"波儿菜"; + res.phone = 66666666666; + res.age = 27; + res.isMan = YES; + res.height = 170; + res.weight = -105; + static NSObject *extra = nil; + if (!extra) { + extra = [NSObject new]; + } + res.extra = extra; + return res; +} + +- (BOOL)isEqual:(TestObject *)object { + return + self.name == object.name + && self.phone == object.phone + && self.age == object.age + && self.height == object.height + && self.weight == object.weight + && self.isMan == object.isMan + && self.extra == object.extra; +} + +@end diff --git a/MessageMockDemo/MessageMockDemo/ViewController.h b/MessageMockDemo/MessageMockDemo/ViewController.h new file mode 100644 index 0000000..3b0d8de --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/ViewController.h @@ -0,0 +1,14 @@ +// +// ViewController.h +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import + +@interface ViewController : UIViewController + + +@end + diff --git a/MessageMockDemo/MessageMockDemo/ViewController.m b/MessageMockDemo/MessageMockDemo/ViewController.m new file mode 100644 index 0000000..be73789 --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/ViewController.m @@ -0,0 +1,22 @@ +// +// ViewController.m +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + + +@end diff --git a/MessageMockDemo/MessageMockDemo/main.m b/MessageMockDemo/MessageMockDemo/main.m new file mode 100644 index 0000000..832972d --- /dev/null +++ b/MessageMockDemo/MessageMockDemo/main.m @@ -0,0 +1,18 @@ +// +// main.m +// MessageMockDemo +// +// Created by 波儿菜 on 2020/8/2. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + NSString * appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/MessageMockDemo/MessageMockDemoTests/Info.plist b/MessageMockDemo/MessageMockDemoTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/MessageMockDemo/MessageMockDemoTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/MessageMockDemo/MessageMockDemoTests/MMArgumentCallbackTests.mm b/MessageMockDemo/MessageMockDemoTests/MMArgumentCallbackTests.mm new file mode 100644 index 0000000..6f70133 --- /dev/null +++ b/MessageMockDemo/MessageMockDemoTests/MMArgumentCallbackTests.mm @@ -0,0 +1,89 @@ +// +// MMArgumentCallbackTests.m +// MessageMockDemoTests +// +// Created by 波儿菜 on 2020/7/4. +// + +#import +#import +#import +#import "TestObject.h" + +using namespace mm_method_matcher; + +@interface MMArgumentCallbackTests : XCTestCase + +@end + +@implementation MMArgumentCallbackTests + +- (void)setUp { + MMStartWorking(); +} + +- (void)tearDown {} + +- (void)testArgumentCallback { + TestObject *argument = [TestObject defaultArgument]; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createWithName:age:phone:isMan:height:weight:extra:)); + matcher->skip = false; + matcher->limit_hit_count = 1; + + ArgumentCallback callBack2 = ^(uintptr_t arg, ArgumentCallbackError error) { + void *argPointer = (void *)arg; + NSString *name = (__bridge NSString *)argPointer; + XCTAssertTrue(name == argument.name); + NSLog(@"\n callBack2 NSString:%@\n", name); + }; + ArgumentCallback callBack3 = ^(uintptr_t arg, ArgumentCallbackError error) { + int age = (int)arg; + XCTAssertTrue(age == argument.age); + NSLog(@"\n callBack3 int:%d\n", age); + }; + ArgumentCallback callBack4 = ^(uintptr_t arg, ArgumentCallbackError error) { + NSInteger phone = (NSInteger)arg; + XCTAssertTrue(phone == argument.phone); + NSLog(@"\n callBack4 NSInteger:%ld\n", phone); + }; + ArgumentCallback callBack5 = ^(uintptr_t arg, ArgumentCallbackError error) { + BOOL isMan = (BOOL)arg; + XCTAssertTrue(isMan == argument.isMan); + NSLog(@"\n callBack5 BOOL:%@\n", @(isMan)); + }; + ArgumentCallback callBack6 = ^(uintptr_t arg, ArgumentCallbackError error) { + double height = *((double *)&arg); + XCTAssertTrue(height == argument.height); + NSLog(@"\n callBack6 double:%lf\n", height); + }; + ArgumentCallback callBack7 = ^(uintptr_t arg, ArgumentCallbackError error) { + float weight = *((float *)&arg); + XCTAssertTrue(weight == argument.weight); + NSLog(@"\n callBack7 float:%f\n", weight); + }; + ArgumentCallback callBack8 = ^(uintptr_t arg, ArgumentCallbackError error) { + void *argPointer = (void *)arg; + NSObject *extra = (__bridge NSObject *)argPointer; + XCTAssertTrue(extra == argument.extra); + NSLog(@"\n callBack8 NSObject:%@\n", extra); + }; + ArgumentCallbackMap *callbackMap = new ArgumentCallbackMap(); + (*callbackMap)[2] = callBack2; + (*callbackMap)[3] = callBack3; + (*callbackMap)[4] = callBack4; + (*callbackMap)[5] = callBack5; + (*callbackMap)[6] = callBack6; + (*callbackMap)[7] = callBack7; + (*callbackMap)[8] = callBack8; + + matcher->argument_callback_map = callbackMap; + + AddMethodMatcher(matcher); + + [TestObject createWithName:argument.name age:argument.age phone:argument.phone isMan:argument.isMan height:argument.height weight:argument.weight extra:argument.extra]; +} + +@end diff --git a/MessageMockDemo/MessageMockDemoTests/MMChangeArgumentTests.mm b/MessageMockDemo/MessageMockDemoTests/MMChangeArgumentTests.mm new file mode 100644 index 0000000..2aafc9d --- /dev/null +++ b/MessageMockDemo/MessageMockDemoTests/MMChangeArgumentTests.mm @@ -0,0 +1,53 @@ +// +// MMChangeArgumentTests.m +// MessageMockDemoTests +// +// Created by 波儿菜 on 2020/7/10. +// + +#import +#import +#import +#import "TestObject.h" + +using namespace mm_method_matcher; + +@interface MMChangeArgumentTests : XCTestCase + +@end + +@implementation MMChangeArgumentTests + +- (void)setUp { + MMStartWorking(); +} + +- (void)tearDown {} + +- (void)testChangeArgument { + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createWithName:age:phone:isMan:height:weight:extra:)); + matcher->skip = false; + matcher->limit_hit_count = 1; + + TestObject *argument = [TestObject defaultArgument]; + Argument mark0 = {.idx = 2, .object = (__bridge_retained void *)argument.name}; + Argument mark1 = {.idx = 3, .value = (uintptr_t)argument.age}; + Argument mark2 = {.idx = 4, .value = (uintptr_t)argument.phone}; + Argument mark3 = {.idx = 5, .value = (uintptr_t)argument.isMan}; + double height = argument.height; + Argument mark4 = {.idx = 6, .value = *(uintptr_t *)&height}; + float weight = argument.weight; + Argument mark5 = {.idx = 7, .value = *(uintptr_t *)&weight}; + Argument mark6 = {.idx = 8, .object = (__bridge_retained void *)argument.extra}; + matcher->arguments = new ArgumentList({mark0, mark1, mark2, mark3, mark4, mark5, mark6}); + + AddMethodMatcher(matcher); + + TestObject *res = [TestObject createWithName:@"小芳" age:20 phone:55555555555 isMan:NO height:165 weight:100 extra:[NSObject new]]; + + XCTAssertTrue([res isEqual:argument]); +} + +@end diff --git a/MessageMockDemo/MessageMockDemoTests/MMMethodReturnTests.mm b/MessageMockDemo/MessageMockDemoTests/MMMethodReturnTests.mm new file mode 100644 index 0000000..15abee5 --- /dev/null +++ b/MessageMockDemo/MessageMockDemoTests/MMMethodReturnTests.mm @@ -0,0 +1,216 @@ +// +// MMMethodReturnTests.m +// MessageMockDemoTests +// +// Created by 波儿菜 on 2020/6/28. +// + +#import +#import +#import +#import "TestObject.h" + +using namespace mm_method_matcher; + +@interface MMMethodReturnTests : XCTestCase + +@end + +@implementation MMMethodReturnTests + +- (void)setUp { + MMStartWorking(); +} + +- (void)tearDown {} + +#pragma mark - 指针返回值改为其它指针 + +- (void)testChangeReturnPointer { + TestObject *returnValue = [TestObject createObj]; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createObj)); + matcher->skip = false; + matcher->change_return = true; + matcher->return_object = (__bridge_retained void *)returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + TestObject *value = [TestObject createObj]; + XCTAssertTrue(returnValue == value); + + TestObject *value1 = [TestObject createObj]; + XCTAssertFalse(returnValue == value1); +} + +#pragma mark - 指针返回值改为 C 指针 + +- (void)testChangeReturnCPointer { + char *returnValue = [TestObject createPointer]; + returnValue = (char *)"dddddddd"; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createPointer)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = (uintptr_t)returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + char *value = [TestObject createPointer]; + XCTAssertTrue(returnValue == value); + + char *value1 = [TestObject createPointer]; + XCTAssertFalse(returnValue == value1); +} + +#pragma mark - 指针返回值改为 nil + +- (void)testChangeReturnPointerToNil { + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createObj)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = (uintptr_t)nil; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + TestObject *value = [TestObject createObj]; + XCTAssertTrue(nil == value); + + TestObject *value1 = [TestObject createObj]; + XCTAssertFalse(nil == value1); +} + +#pragma mark - 整形返回值修改 + +- (void)testChangeReturnLong { + long returnValue = [TestObject createLong] + 10086; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createLong)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + long value = [TestObject createLong]; + XCTAssertTrue(returnValue == value); + + long value1 = [TestObject createLong]; + XCTAssertFalse(returnValue == value1); +} + +- (void)testChangeReturnInt { + int returnValue = [TestObject createInt] - 20000; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createInt)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + int value = [TestObject createInt]; + XCTAssertTrue(returnValue == value); + + int value1 = [TestObject createInt]; + XCTAssertFalse(returnValue == value1); +} + +- (void)testChangeReturnInt16 { + int returnValue = [TestObject createInt16] - 200; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createInt16)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + int value = [TestObject createInt16]; + XCTAssertTrue(returnValue == value); + + int value1 = [TestObject createInt16]; + XCTAssertFalse(returnValue == value1); +} + +- (void)testChangeReturnInt8 { + int returnValue = [TestObject createInt8] + 10; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createInt8)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + int value = [TestObject createInt8]; + XCTAssertTrue(returnValue == value); + + int value1 = [TestObject createInt8]; + XCTAssertFalse(returnValue == value1); +} + +#pragma mark - 浮点型返回值修改 + +- (void)testChangeReturnDouble { + double returnValue = [TestObject createDouble] - 1000000000000.000002394713984; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createDouble)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = *(uintptr_t *)&returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + double value = [TestObject createDouble]; + XCTAssertTrue(returnValue == value); + + double value1 = [TestObject createDouble]; + XCTAssertFalse(returnValue == value1); +} + +- (void)testChangeReturnFloat { + float returnValue = [TestObject createFloat] - 2000000000000.000002394713984; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createFloat)); + matcher->skip = true; + matcher->change_return = true; + matcher->return_value = *(uintptr_t *)&returnValue; + matcher->limit_hit_count = 1; + + AddMethodMatcher(matcher); + + float value = [TestObject createFloat]; + XCTAssertTrue(returnValue == value); + + float value1 = [TestObject createFloat]; + XCTAssertFalse(returnValue == value1); +} + +@end diff --git a/MessageMockDemo/MessageMockDemoTests/MMReturnCallbackTests.mm b/MessageMockDemo/MessageMockDemoTests/MMReturnCallbackTests.mm new file mode 100644 index 0000000..ea6e4d4 --- /dev/null +++ b/MessageMockDemo/MessageMockDemoTests/MMReturnCallbackTests.mm @@ -0,0 +1,93 @@ +// +// MMReturnCallbackTests.m +// MessageMockDemoTests +// +// Created by 波儿菜 on 2020/7/18. +// + +#import +#import +#import +#import "TestObject.h" + +using namespace mm_method_matcher; + +@interface MMReturnCallbackTests : XCTestCase + +@end + +@implementation MMReturnCallbackTests + +- (void)setUp { + MMStartWorking(); +} + +- (void)tearDown {} + +- (void)testReturnObjectCallback { + __block TestObject *checkObj = nil; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createObj)); + matcher->skip = false; + + ReturnCallback returnCallback = ^(uintptr_t arg, ReturnCallbackError error) { + void *argPointer = (void *)arg; + checkObj = (__bridge TestObject *)argPointer; + }; + matcher->return_callback = returnCallback; + + AddMethodMatcher(matcher); + + TestObject *returnObj = [TestObject createObj]; + + XCTAssertTrue(returnObj == checkObj); +} + +- (void)testReturnFloatCallback { + __block float value = 0; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createFloat)); + matcher->skip = false; + + ReturnCallback returnCallback = ^(uintptr_t arg, ReturnCallbackError error) { + float tmp = *(float *)&arg; + value = tmp; + }; + matcher->return_callback = returnCallback; + + AddMethodMatcher(matcher); + + float res = [TestObject createFloat]; + + XCTAssertTrue(value == res); +} + +- (void)testChangeReturnObjectCallback { + TestObject *changeObj = [TestObject new]; + + MethodMatcher *matcher = new MethodMatcher(); + matcher->target = (uintptr_t)TestObject.self; + matcher->selector = (uintptr_t)(void *)(@selector(createObj)); + matcher->skip = false; + matcher->change_return = true; + matcher->return_object = (__bridge_retained void *)changeObj; + + ReturnCallback returnCallback = ^(uintptr_t arg, ReturnCallbackError error) { + void *argPointer = (void *)arg; + TestObject *tmp = (__bridge TestObject *)argPointer; + XCTAssertTrue(changeObj == tmp); + }; + matcher->return_callback = returnCallback; + + AddMethodMatcher(matcher); + + TestObject *returnObj = [TestObject createObj]; + + XCTAssertTrue(returnObj == changeObj); +} + +@end diff --git a/MessageMockDemo/MessageMockDemoTests/MessageMockerTests.m b/MessageMockDemo/MessageMockDemoTests/MessageMockerTests.m new file mode 100644 index 0000000..9206935 --- /dev/null +++ b/MessageMockDemo/MessageMockDemoTests/MessageMockerTests.m @@ -0,0 +1,216 @@ +// +// MessageMockerTests.m +// MessageMockDemoTests +// +// Created by 波儿菜 on 2020/7/12. +// + +#import +#import +#import "TestObject.h" + +@interface MessageMockerTests : XCTestCase + +@end + +@implementation MessageMockerTests + +- (void)setUp {} + +- (void)tearDown {} + +- (void)testChangeReturnFloat { + float value = 0.384189347192384789; + + MessageMocker.build(NSObject.self, @selector(new)).mockReturn(nil).skip(YES).start(); + + MessageMocker.build(TestObject.self, @selector(createFloat)).mockReturn(value).start(); + + XCTAssertTrue(value == [TestObject createFloat]); + XCTAssertTrue(value != [TestObject createFloat]); +} + +- (void)testChangeReturnDouble { + double value = 0.48591734913287498; + + MessageMocker.build(TestObject.self, @selector(createDouble)).mockReturn(value).start(); + + XCTAssertTrue(value == [TestObject createDouble]); + XCTAssertTrue(value != [TestObject createDouble]); +} + +- (void)testChangeReturnObject { + NSObject *value = [NSObject new]; + + MessageMocker.build(TestObject.self, @selector(createObj)).mockReturn(value).start(); + + XCTAssertTrue(value == [TestObject createObj]); + XCTAssertTrue(value != [TestObject createObj]); +} + +- (void)testChangeReturnObject1 { + NSObject *value = [NSObject new]; + + MessageMocker.build(NSNotificationCenter.self, @selector(defaultCenter)).mockReturn(value).start(); + + XCTAssertTrue(value == [NSNotificationCenter defaultCenter]); + XCTAssertTrue(value != [NSNotificationCenter defaultCenter]); + + MessageMocker.build([NSUserDefaults standardUserDefaults], @selector(objectForKey:)).mockReturn(value).start(); + + XCTAssertTrue(value == [[NSUserDefaults standardUserDefaults] objectForKey:@"AnyKey"]); + XCTAssertTrue(value != [[NSUserDefaults standardUserDefaults] objectForKey:@"AnyKey"]); +} + +- (void)testChangeArgument { + TestObject *value = [TestObject defaultArgument]; + + MessageMocker.build(TestObject.self, @selector(createWithName:age:phone:isMan:height:weight:extra:)) + .mockArgument(0, value.name) + .mockArgument(1, value.age) + .mockArgument(2, value.phone) + .mockArgument(3, value.isMan) + .mockArgument(4, value.height) + .mockArgument(5, value.weight) + .mockArgument(6, value.extra) + .start(); + + TestObject *res = [TestObject createWithName:@"小芳" age:20 phone:55555555555 isMan:NO height:165 weight:100 extra:[NSObject new]]; + + XCTAssertTrue([res isEqual:value]); +} + +- (void)testChangeArgument1 { + UIView *view = [UIView new]; + NSInteger value = 999; + + MessageMocker.build(view, @selector(setTag:)).mockArgument(0, value).start(); + + view.tag = 666; + XCTAssertTrue(view.tag == value); +} + +- (void)testCheckReturnObject { + __block id value = nil; + + MessageMocker.build(TestObject.self, @selector(createObj)) + .checkReturn(^(TestObject *arg) { + value = arg; + }) + .start(); + + id res = [TestObject createObj]; + XCTAssertTrue(value == res); +} + +- (void)testCheckReturnInt { + __block int value = 0; + + MessageMocker.build(TestObject.self, @selector(createInt)) + .checkReturn(^(int arg) { + value = arg; + }) + .start(); + + int res = [TestObject createInt]; + XCTAssertTrue(value == res); +} + +- (void)testCheckReturnFloat { + __block float value = 0; + + MessageMocker.build(TestObject.self, @selector(createFloat)) + .checkReturn(^(float arg) { + value = arg; + }) + .start(); + + float res = [TestObject createFloat]; + XCTAssertTrue(value == res); +} + +- (void)testCheckReturnLong { + __block unsigned long value = 0; + + NSObject *obj = [NSObject new]; + + MessageMocker.build(obj, @selector(hash)) + .checkReturn(^(unsigned long arg) { + value = arg; + }) + .start(); + + unsigned long res = [obj hash]; + XCTAssertTrue(value == res); +} + +- (void)testCheckArgument { + TestObject *argument = [TestObject defaultArgument]; + + MessageMocker.build(TestObject.self, @selector(createWithName:age:phone:isMan:height:weight:extra:)) + .checkArgument(0, ^(NSString *arg) { + XCTAssertTrue(arg == argument.name); + }) + .checkArgument(1, ^(int arg) { + XCTAssertTrue(arg == argument.age); + }) + .checkArgument(2, ^(NSInteger arg) { + XCTAssertTrue(arg == argument.phone); + }) + .checkArgument(3, ^(BOOL arg) { + XCTAssertTrue(arg == argument.isMan); + }) + .checkArgument(4, ^(double arg) { + XCTAssertTrue(arg == argument.height); + }) + .checkArgument(5, ^(float arg) { + XCTAssertTrue(arg == argument.weight); + }) + .checkArgument(6, ^(id arg) { + XCTAssertTrue(arg == argument.extra); + }) + .start(); + + [TestObject createWithName:argument.name age:argument.age phone:argument.phone isMan:argument.isMan height:argument.height weight:argument.weight extra:argument.extra]; +} + +- (void)testCheckArgument1 { + Class anyCls = NSString.self; + + MessageMocker.build(NSObject.self, @selector(isSubclassOfClass:)) + .checkArgument(0, ^(Class cls){ + XCTAssertTrue(anyCls == cls); + }) + .start(); + + [NSObject isSubclassOfClass:anyCls]; +} + +- (void)testSkip { + UIView *view = [UIView new]; + NSInteger tag = 666; + view.tag = tag; + + MessageMocker.build(view, @selector(setTag:)).skip(YES).start(); + + view.tag = 999; + XCTAssertTrue(view.tag = tag); +} + +- (void)testHitCount { + NSObject *value = [NSObject new]; + + MessageMocker *mocker = MessageMocker.build(TestObject.self, @selector(createObj)).mockReturn(value).limitHitCount(3); + mocker.start(); + + XCTAssertTrue(value == [TestObject createObj]); + XCTAssertTrue(mocker.hitCount == 1); + XCTAssertTrue(value == [TestObject createObj]); + XCTAssertTrue(mocker.hitCount == 2); + XCTAssertTrue(value == [TestObject createObj]); + XCTAssertTrue(mocker.hitCount == 3); + + XCTAssertTrue(value != [TestObject createObj]); +} + +@end diff --git a/MessageMockDemo/Podfile b/MessageMockDemo/Podfile new file mode 100644 index 0000000..b5099e3 --- /dev/null +++ b/MessageMockDemo/Podfile @@ -0,0 +1,8 @@ +platform:ios, '9.0' + +target ‘MessageMockDemoTests’ do +use_frameworks! + +pod 'MessageMock', :path => '../', :configurations => ['Debug'] + +end diff --git a/MessageMockDemo/Podfile.lock b/MessageMockDemo/Podfile.lock new file mode 100644 index 0000000..b623eb0 --- /dev/null +++ b/MessageMockDemo/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - MessageMock (1.0) + +DEPENDENCIES: + - MessageMock (from `../`) + +EXTERNAL SOURCES: + MessageMock: + :path: "../" + +SPEC CHECKSUMS: + MessageMock: 17efd42346cf9d04792dc4e06791908b9ca0fd3e + +PODFILE CHECKSUM: d82d697e487c7dfc1ca5bd1d4efd186eff3368c2 + +COCOAPODS: 1.8.4 diff --git a/MessageMockDemo/Pods/Local Podspecs/MessageMock.podspec.json b/MessageMockDemo/Pods/Local Podspecs/MessageMock.podspec.json new file mode 100644 index 0000000..903892a --- /dev/null +++ b/MessageMockDemo/Pods/Local Podspecs/MessageMock.podspec.json @@ -0,0 +1,21 @@ +{ + "name": "MessageMock", + "version": "1.0", + "summary": "Objective-C 方法模拟工具", + "description": "Objective-C 方法模拟工具", + "homepage": "https://github.com/indulgeIn/MessageMock", + "license": "MIT", + "authors": { + "indulgeIn": "1106355439@qq.com" + }, + "platforms": { + "ios": "9.0" + }, + "source": { + "git": "https://github.com/indulgeIn/MessageMock.git", + "tag": "1.0" + }, + "requires_arc": true, + "source_files": "MessageMock/**/*.{h,m,mm,c,cpp,hpp}", + "libraries": "c++.1" +} diff --git a/MessageMockDemo/Pods/Manifest.lock b/MessageMockDemo/Pods/Manifest.lock new file mode 100644 index 0000000..b623eb0 --- /dev/null +++ b/MessageMockDemo/Pods/Manifest.lock @@ -0,0 +1,16 @@ +PODS: + - MessageMock (1.0) + +DEPENDENCIES: + - MessageMock (from `../`) + +EXTERNAL SOURCES: + MessageMock: + :path: "../" + +SPEC CHECKSUMS: + MessageMock: 17efd42346cf9d04792dc4e06791908b9ca0fd3e + +PODFILE CHECKSUM: d82d697e487c7dfc1ca5bd1d4efd186eff3368c2 + +COCOAPODS: 1.8.4 diff --git a/MessageMockDemo/Pods/Pods.xcodeproj/project.pbxproj b/MessageMockDemo/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7e06c3e --- /dev/null +++ b/MessageMockDemo/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,696 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 006A5202DA8D63735802BD2DD2C81B80 /* MessageMock-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF3921DC37497A1D856E9B0CC4BD2E4 /* MessageMock-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1FB88F3A82E998FD8F31858CC74B9A07 /* Pods-MessageMockDemoTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 45E0B4DDD172B91873020A2B30AA8D1A /* Pods-MessageMockDemoTests-dummy.m */; }; + 21D7E7B9291AC10E2621008046B218B2 /* mm_argument_change.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44CAFB591123437B7177E491FE9DE8E5 /* mm_argument_change.cpp */; }; + 28B7C014DF07364EFEB74DDE0185C692 /* mm_argument_change.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CB91277397839001E6C2AE08958132A /* mm_argument_change.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 383AA9C531C51F74F200DE0A3FBD54C6 /* MessageMocker.mm in Sources */ = {isa = PBXBuildFile; fileRef = C3230046D05486195C0F952243562BF5 /* MessageMocker.mm */; }; + 3BD9F144AA4E0A613A923F5DA7B0107E /* mm_argument_stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE8ED299CC65AD093896ACBF67B2A8C6 /* mm_argument_stack.cpp */; }; + 50CD9E644C6614610A92E0F772E0E8BF /* mm_argument_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = A2BEFE37965F08F8C9AD669F09ED9A87 /* mm_argument_callback.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6BBAFF22B26B96CF328F9E8D4BE798F3 /* mm_define.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F1354228863C85F4C5EB6F2A9E6E8B0 /* mm_define.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 76B3018681383CF81C8BB87BADE929DB /* mm_fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = E2AE909801B176A8E29B55A197FF03B6 /* mm_fishhook.c */; }; + 7792573CA56A65C99F012C4C9D505574 /* mm_fishhook.h in Headers */ = {isa = PBXBuildFile; fileRef = A32D253029562D93A04B0DC43B5E4A63 /* mm_fishhook.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7C111FF3CA1D631663095E374C733C19 /* mm_runtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 081F7669A053E41E9FC46B57774EF931 /* mm_runtime.cpp */; }; + A882763C285C889F0B1896C4B591DD2B /* Pods-MessageMockDemoTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F0318F1F0A0C63F1CB8B965F29A35FF /* Pods-MessageMockDemoTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B485BDE7ED3F98853804F0D534738C06 /* mm_runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = E943472018DAF2DA0358B50252166BEF /* mm_runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B9A5715005DE3F27192EB4C66E8A82D8 /* mm_argument_stack.h in Headers */ = {isa = PBXBuildFile; fileRef = 72959443356585CF350E4AF0F7BC6E84 /* mm_argument_stack.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C209FE0D8DCF71D006231BC1BFC3CA11 /* MessageMocker.h in Headers */ = {isa = PBXBuildFile; fileRef = ACAD3467482F46A240385688B20E0026 /* MessageMocker.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CB6D436365207738A6F4532ACDB22876 /* mm_argument_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA69A9FDF41912A7E86D0B18145F9E9D /* mm_argument_callback.cpp */; }; + D9492F1EDA0B5C385E97D61E6F30F2FA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; }; + E432AAC418E66927B7DB578D9341E6E6 /* mm_hook_objc_msgsend.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F03BE7D90077EF143676DA820F2E1AA /* mm_hook_objc_msgsend.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E56356525F1D64A28CEFA0C98F2D7DFC /* mm_method_matcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ECD0CE6EC95ACB1948E5255DA921AB9 /* mm_method_matcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB8FEDCA9E91E53D5E59024CD8965E5F /* MessageMock-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 72E87D0112CC0C80183153D0D6A849DF /* MessageMock-dummy.m */; }; + F057FE746667651224C4405544DBBB5F /* mm_hook_objc_msgsend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62606F77B15DBFB5A276B250B813E8F4 /* mm_hook_objc_msgsend.cpp */; }; + F4428DDD1438A3BEB19D98099F53E0ED /* mm_method_matcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76AC65AF62D34E922BCD3085A9675878 /* mm_method_matcher.cpp */; }; + F67036B46B58ACDF88076B5B2287F87C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C7A485C171BA07567E26569795CDB8A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0DC72AA7DFB4148188F5D3D63C80F621; + remoteInfo = MessageMock; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 081F7669A053E41E9FC46B57774EF931 /* mm_runtime.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_runtime.cpp; sourceTree = ""; }; + 0A870BDF92896BDE64683AF4F71CB96A /* Pods-MessageMockDemoTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-MessageMockDemoTests.modulemap"; sourceTree = ""; }; + 0E4F714EC593ECF5453250160A393349 /* MessageMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MessageMock.framework; path = MessageMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0FF3921DC37497A1D856E9B0CC4BD2E4 /* MessageMock-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MessageMock-umbrella.h"; sourceTree = ""; }; + 193C1AC6FDC933128082A5C8DFC49A8D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; path = README.md; sourceTree = ""; }; + 1ECD0CE6EC95ACB1948E5255DA921AB9 /* mm_method_matcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_method_matcher.h; sourceTree = ""; }; + 2F1354228863C85F4C5EB6F2A9E6E8B0 /* mm_define.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_define.h; sourceTree = ""; }; + 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 37D7595A2AE54D14872185009BC4F5F2 /* Pods-MessageMockDemoTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MessageMockDemoTests-Info.plist"; sourceTree = ""; }; + 44CAFB591123437B7177E491FE9DE8E5 /* mm_argument_change.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_argument_change.cpp; sourceTree = ""; }; + 45E0B4DDD172B91873020A2B30AA8D1A /* Pods-MessageMockDemoTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-MessageMockDemoTests-dummy.m"; sourceTree = ""; }; + 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MessageMock.xcconfig; sourceTree = ""; }; + 4AF519972B919C142AE9B773E45291A8 /* MessageMock.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = MessageMock.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 5CB91277397839001E6C2AE08958132A /* mm_argument_change.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_argument_change.h; sourceTree = ""; }; + 62606F77B15DBFB5A276B250B813E8F4 /* mm_hook_objc_msgsend.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_hook_objc_msgsend.cpp; sourceTree = ""; }; + 67769E141D99397F27D9AAC2A28E02E7 /* Pods-MessageMockDemoTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-MessageMockDemoTests-acknowledgements.markdown"; sourceTree = ""; }; + 72959443356585CF350E4AF0F7BC6E84 /* mm_argument_stack.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_argument_stack.h; sourceTree = ""; }; + 72E87D0112CC0C80183153D0D6A849DF /* MessageMock-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MessageMock-dummy.m"; sourceTree = ""; }; + 76AC65AF62D34E922BCD3085A9675878 /* mm_method_matcher.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_method_matcher.cpp; sourceTree = ""; }; + 7F0318F1F0A0C63F1CB8B965F29A35FF /* Pods-MessageMockDemoTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-MessageMockDemoTests-umbrella.h"; sourceTree = ""; }; + 7F03BE7D90077EF143676DA820F2E1AA /* mm_hook_objc_msgsend.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_hook_objc_msgsend.h; sourceTree = ""; }; + 8B4163FC65ED75060468B05BE08BD5B3 /* Pods-MessageMockDemoTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-MessageMockDemoTests-frameworks.sh"; sourceTree = ""; }; + 9612836D2E24DD4A4F2BFF3225820F30 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9DB5937B7D6FE9575700E937D57595D2 /* MessageMock-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MessageMock-prefix.pch"; sourceTree = ""; }; + A2BEFE37965F08F8C9AD669F09ED9A87 /* mm_argument_callback.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_argument_callback.h; sourceTree = ""; }; + A32D253029562D93A04B0DC43B5E4A63 /* mm_fishhook.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_fishhook.h; sourceTree = ""; }; + ACAD3467482F46A240385688B20E0026 /* MessageMocker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MessageMocker.h; path = MessageMock/MessageMocker.h; sourceTree = ""; }; + B27452342D018A7B6017C418581E288A /* Pods_MessageMockDemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_MessageMockDemoTests.framework; path = "Pods-MessageMockDemoTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + BC7258C913072156F5364BF28592EEA9 /* MessageMock.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MessageMock.modulemap; sourceTree = ""; }; + BE1E37FD9AEA6EE788FF0AB458D51D86 /* MessageMock-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MessageMock-Info.plist"; sourceTree = ""; }; + C3230046D05486195C0F952243562BF5 /* MessageMocker.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = MessageMocker.mm; path = MessageMock/MessageMocker.mm; sourceTree = ""; }; + C499D9FA0F99944F924A98A0B832DD0F /* Pods-MessageMockDemoTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MessageMockDemoTests-acknowledgements.plist"; sourceTree = ""; }; + E2AE909801B176A8E29B55A197FF03B6 /* mm_fishhook.c */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_fishhook.c; sourceTree = ""; }; + E943472018DAF2DA0358B50252166BEF /* mm_runtime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_runtime.h; sourceTree = ""; }; + EA69A9FDF41912A7E86D0B18145F9E9D /* mm_argument_callback.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_argument_callback.cpp; sourceTree = ""; }; + EE119006FF2E3126B33DE859B44E3EEA /* Pods-MessageMockDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MessageMockDemoTests.release.xcconfig"; sourceTree = ""; }; + F6D8B468295743E6B9837C8A54608760 /* Pods-MessageMockDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MessageMockDemoTests.debug.xcconfig"; sourceTree = ""; }; + FE8ED299CC65AD093896ACBF67B2A8C6 /* mm_argument_stack.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_argument_stack.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2BE8084F2AD80591444CC51C67272690 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F67036B46B58ACDF88076B5B2287F87C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6BD51F928FDF1CF116AB117FD1807F49 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D9492F1EDA0B5C385E97D61E6F30F2FA /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0AC9AE219A0277325E677B52AEE0BAD8 /* MessageMock */ = { + isa = PBXGroup; + children = ( + ACAD3467482F46A240385688B20E0026 /* MessageMocker.h */, + C3230046D05486195C0F952243562BF5 /* MessageMocker.mm */, + 222A2AAF822822BF920B4B481684ADCC /* Core */, + 712574555A87162701DB9E904A3175C7 /* fishhook */, + 206DE43C7B0E3DF04C44E5F983882E1E /* Pod */, + AD4B3B939834FAC653B8D67ABCF1682D /* Support Files */, + ); + name = MessageMock; + path = ../..; + sourceTree = ""; + }; + 206DE43C7B0E3DF04C44E5F983882E1E /* Pod */ = { + isa = PBXGroup; + children = ( + 9612836D2E24DD4A4F2BFF3225820F30 /* LICENSE */, + 4AF519972B919C142AE9B773E45291A8 /* MessageMock.podspec */, + 193C1AC6FDC933128082A5C8DFC49A8D /* README.md */, + ); + name = Pod; + sourceTree = ""; + }; + 222A2AAF822822BF920B4B481684ADCC /* Core */ = { + isa = PBXGroup; + children = ( + EA69A9FDF41912A7E86D0B18145F9E9D /* mm_argument_callback.cpp */, + A2BEFE37965F08F8C9AD669F09ED9A87 /* mm_argument_callback.h */, + 44CAFB591123437B7177E491FE9DE8E5 /* mm_argument_change.cpp */, + 5CB91277397839001E6C2AE08958132A /* mm_argument_change.h */, + FE8ED299CC65AD093896ACBF67B2A8C6 /* mm_argument_stack.cpp */, + 72959443356585CF350E4AF0F7BC6E84 /* mm_argument_stack.h */, + 2F1354228863C85F4C5EB6F2A9E6E8B0 /* mm_define.h */, + 62606F77B15DBFB5A276B250B813E8F4 /* mm_hook_objc_msgsend.cpp */, + 7F03BE7D90077EF143676DA820F2E1AA /* mm_hook_objc_msgsend.h */, + 76AC65AF62D34E922BCD3085A9675878 /* mm_method_matcher.cpp */, + 1ECD0CE6EC95ACB1948E5255DA921AB9 /* mm_method_matcher.h */, + 081F7669A053E41E9FC46B57774EF931 /* mm_runtime.cpp */, + E943472018DAF2DA0358B50252166BEF /* mm_runtime.h */, + ); + name = Core; + path = MessageMock/Core; + sourceTree = ""; + }; + 45295D8DEF41133486C8B14ED69034BF /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + AACE813D43FF64ECEC52E32A00A3BE2B /* Pods-MessageMockDemoTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 655736BB2CE9AE1D504E8D0CF146FEE1 /* Products */ = { + isa = PBXGroup; + children = ( + 0E4F714EC593ECF5453250160A393349 /* MessageMock.framework */, + B27452342D018A7B6017C418581E288A /* Pods_MessageMockDemoTests.framework */, + ); + name = Products; + sourceTree = ""; + }; + 712574555A87162701DB9E904A3175C7 /* fishhook */ = { + isa = PBXGroup; + children = ( + E2AE909801B176A8E29B55A197FF03B6 /* mm_fishhook.c */, + A32D253029562D93A04B0DC43B5E4A63 /* mm_fishhook.h */, + ); + name = fishhook; + path = MessageMock/fishhook; + sourceTree = ""; + }; + 9B238F32725C2DC2FABEB92E9EA93C2A /* Development Pods */ = { + isa = PBXGroup; + children = ( + 0AC9AE219A0277325E677B52AEE0BAD8 /* MessageMock */, + ); + name = "Development Pods"; + sourceTree = ""; + }; + AACE813D43FF64ECEC52E32A00A3BE2B /* Pods-MessageMockDemoTests */ = { + isa = PBXGroup; + children = ( + 0A870BDF92896BDE64683AF4F71CB96A /* Pods-MessageMockDemoTests.modulemap */, + 67769E141D99397F27D9AAC2A28E02E7 /* Pods-MessageMockDemoTests-acknowledgements.markdown */, + C499D9FA0F99944F924A98A0B832DD0F /* Pods-MessageMockDemoTests-acknowledgements.plist */, + 45E0B4DDD172B91873020A2B30AA8D1A /* Pods-MessageMockDemoTests-dummy.m */, + 8B4163FC65ED75060468B05BE08BD5B3 /* Pods-MessageMockDemoTests-frameworks.sh */, + 37D7595A2AE54D14872185009BC4F5F2 /* Pods-MessageMockDemoTests-Info.plist */, + 7F0318F1F0A0C63F1CB8B965F29A35FF /* Pods-MessageMockDemoTests-umbrella.h */, + F6D8B468295743E6B9837C8A54608760 /* Pods-MessageMockDemoTests.debug.xcconfig */, + EE119006FF2E3126B33DE859B44E3EEA /* Pods-MessageMockDemoTests.release.xcconfig */, + ); + name = "Pods-MessageMockDemoTests"; + path = "Target Support Files/Pods-MessageMockDemoTests"; + sourceTree = ""; + }; + AD4B3B939834FAC653B8D67ABCF1682D /* Support Files */ = { + isa = PBXGroup; + children = ( + BC7258C913072156F5364BF28592EEA9 /* MessageMock.modulemap */, + 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */, + 72E87D0112CC0C80183153D0D6A849DF /* MessageMock-dummy.m */, + BE1E37FD9AEA6EE788FF0AB458D51D86 /* MessageMock-Info.plist */, + 9DB5937B7D6FE9575700E937D57595D2 /* MessageMock-prefix.pch */, + 0FF3921DC37497A1D856E9B0CC4BD2E4 /* MessageMock-umbrella.h */, + ); + name = "Support Files"; + path = "MessageMockDemo/Pods/Target Support Files/MessageMock"; + sourceTree = ""; + }; + C0834CEBB1379A84116EF29F93051C60 /* iOS */ = { + isa = PBXGroup; + children = ( + 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + 9B238F32725C2DC2FABEB92E9EA93C2A /* Development Pods */, + D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, + 655736BB2CE9AE1D504E8D0CF146FEE1 /* Products */, + 45295D8DEF41133486C8B14ED69034BF /* Targets Support Files */, + ); + sourceTree = ""; + }; + D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C0834CEBB1379A84116EF29F93051C60 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0E1D2749020AB6F89831EF95F4EB5BB3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 006A5202DA8D63735802BD2DD2C81B80 /* MessageMock-umbrella.h in Headers */, + C209FE0D8DCF71D006231BC1BFC3CA11 /* MessageMocker.h in Headers */, + 50CD9E644C6614610A92E0F772E0E8BF /* mm_argument_callback.h in Headers */, + 28B7C014DF07364EFEB74DDE0185C692 /* mm_argument_change.h in Headers */, + B9A5715005DE3F27192EB4C66E8A82D8 /* mm_argument_stack.h in Headers */, + 6BBAFF22B26B96CF328F9E8D4BE798F3 /* mm_define.h in Headers */, + 7792573CA56A65C99F012C4C9D505574 /* mm_fishhook.h in Headers */, + E432AAC418E66927B7DB578D9341E6E6 /* mm_hook_objc_msgsend.h in Headers */, + E56356525F1D64A28CEFA0C98F2D7DFC /* mm_method_matcher.h in Headers */, + B485BDE7ED3F98853804F0D534738C06 /* mm_runtime.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D34163734298E55BFE9B7EF75955F309 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A882763C285C889F0B1896C4B591DD2B /* Pods-MessageMockDemoTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0DC72AA7DFB4148188F5D3D63C80F621 /* MessageMock */ = { + isa = PBXNativeTarget; + buildConfigurationList = 891E52FC4734C81DEC7CEC07FE8C53CA /* Build configuration list for PBXNativeTarget "MessageMock" */; + buildPhases = ( + 0E1D2749020AB6F89831EF95F4EB5BB3 /* Headers */, + ADD6DFC5D55455FA5B9B31A63D5D6E24 /* Sources */, + 6BD51F928FDF1CF116AB117FD1807F49 /* Frameworks */, + 5EBDBF80CD9F1B56E09246F86B058121 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MessageMock; + productName = MessageMock; + productReference = 0E4F714EC593ECF5453250160A393349 /* MessageMock.framework */; + productType = "com.apple.product-type.framework"; + }; + 8751FC505D38D6521F525192EDA257C7 /* Pods-MessageMockDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C6B905825BE92CBC95730E2C030DF486 /* Build configuration list for PBXNativeTarget "Pods-MessageMockDemoTests" */; + buildPhases = ( + D34163734298E55BFE9B7EF75955F309 /* Headers */, + EE334356D54A910A60783FFC83E44E60 /* Sources */, + 2BE8084F2AD80591444CC51C67272690 /* Frameworks */, + 6AD4DCBCE56FAB083CB85A15933951B2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9B04BB158ECD3F467E3E2A4208167A14 /* PBXTargetDependency */, + ); + name = "Pods-MessageMockDemoTests"; + productName = "Pods-MessageMockDemoTests"; + productReference = B27452342D018A7B6017C418581E288A /* Pods_MessageMockDemoTests.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = 655736BB2CE9AE1D504E8D0CF146FEE1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0DC72AA7DFB4148188F5D3D63C80F621 /* MessageMock */, + 8751FC505D38D6521F525192EDA257C7 /* Pods-MessageMockDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5EBDBF80CD9F1B56E09246F86B058121 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6AD4DCBCE56FAB083CB85A15933951B2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + ADD6DFC5D55455FA5B9B31A63D5D6E24 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EB8FEDCA9E91E53D5E59024CD8965E5F /* MessageMock-dummy.m in Sources */, + 383AA9C531C51F74F200DE0A3FBD54C6 /* MessageMocker.mm in Sources */, + CB6D436365207738A6F4532ACDB22876 /* mm_argument_callback.cpp in Sources */, + 21D7E7B9291AC10E2621008046B218B2 /* mm_argument_change.cpp in Sources */, + 3BD9F144AA4E0A613A923F5DA7B0107E /* mm_argument_stack.cpp in Sources */, + 76B3018681383CF81C8BB87BADE929DB /* mm_fishhook.c in Sources */, + F057FE746667651224C4405544DBBB5F /* mm_hook_objc_msgsend.cpp in Sources */, + F4428DDD1438A3BEB19D98099F53E0ED /* mm_method_matcher.cpp in Sources */, + 7C111FF3CA1D631663095E374C733C19 /* mm_runtime.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EE334356D54A910A60783FFC83E44E60 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1FB88F3A82E998FD8F31858CC74B9A07 /* Pods-MessageMockDemoTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 9B04BB158ECD3F467E3E2A4208167A14 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MessageMock; + target = 0DC72AA7DFB4148188F5D3D63C80F621 /* MessageMock */; + targetProxy = C7A485C171BA07567E26569795CDB8A0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 014E9B349A99F0CE39806D8C6CF705E9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MessageMock/MessageMock-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MessageMock/MessageMock-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MessageMock/MessageMock.modulemap"; + PRODUCT_MODULE_NAME = MessageMock; + PRODUCT_NAME = MessageMock; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 16A38ADD9D4879B101D3C7757F0E49F7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MessageMock/MessageMock-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MessageMock/MessageMock-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MessageMock/MessageMock.modulemap"; + PRODUCT_MODULE_NAME = MessageMock; + PRODUCT_NAME = MessageMock; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 4F0AFF0E33B3BC1AD41EC25765D7AAFE /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EE119006FF2E3126B33DE859B44E3EEA /* Pods-MessageMockDemoTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 5BE66E32DBC615D507AE7B4757774964 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F6D8B468295743E6B9837C8A54608760 /* Pods-MessageMockDemoTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 8F17DC3A99F99FBAD606CE6963886315 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + 916E0404255105F480DC4950B7625F7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 916E0404255105F480DC4950B7625F7A /* Debug */, + 8F17DC3A99F99FBAD606CE6963886315 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 891E52FC4734C81DEC7CEC07FE8C53CA /* Build configuration list for PBXNativeTarget "MessageMock" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 16A38ADD9D4879B101D3C7757F0E49F7 /* Debug */, + 014E9B349A99F0CE39806D8C6CF705E9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C6B905825BE92CBC95730E2C030DF486 /* Build configuration list for PBXNativeTarget "Pods-MessageMockDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5BE66E32DBC615D507AE7B4757774964 /* Debug */, + 4F0AFF0E33B3BC1AD41EC25765D7AAFE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/MessageMock.xcscheme b/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/MessageMock.xcscheme new file mode 100644 index 0000000..039884f --- /dev/null +++ b/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/MessageMock.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/Pods-MessageMockDemoTests.xcscheme b/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/Pods-MessageMockDemoTests.xcscheme new file mode 100644 index 0000000..9ea9959 --- /dev/null +++ b/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/Pods-MessageMockDemoTests.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist b/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..3bc0a48 --- /dev/null +++ b/MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,25 @@ + + + + + SchemeUserState + + MessageMock.xcscheme + + isShown + + orderHint + 0 + + Pods-MessageMockDemoTests.xcscheme + + isShown + + orderHint + 1 + + + SuppressBuildableAutocreation + + + diff --git a/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-Info.plist b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-dummy.m b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-dummy.m new file mode 100644 index 0000000..76c60b8 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MessageMock : NSObject +@end +@implementation PodsDummy_MessageMock +@end diff --git a/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-prefix.pch b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-umbrella.h b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-umbrella.h new file mode 100644 index 0000000..47d9505 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-umbrella.h @@ -0,0 +1,25 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "mm_argument_callback.h" +#import "mm_argument_change.h" +#import "mm_argument_stack.h" +#import "mm_define.h" +#import "mm_hook_objc_msgsend.h" +#import "mm_method_matcher.h" +#import "mm_runtime.h" +#import "mm_fishhook.h" +#import "MessageMocker.h" + +FOUNDATION_EXPORT double MessageMockVersionNumber; +FOUNDATION_EXPORT const unsigned char MessageMockVersionString[]; + diff --git a/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.modulemap b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.modulemap new file mode 100644 index 0000000..cef9fc5 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.modulemap @@ -0,0 +1,6 @@ +framework module MessageMock { + umbrella header "MessageMock-umbrella.h" + + export * + module * { export * } +} diff --git a/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.xcconfig b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.xcconfig new file mode 100644 index 0000000..7ac44b4 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MessageMock +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -l"c++.1" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.markdown b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.markdown new file mode 100644 index 0000000..e5254ec --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.markdown @@ -0,0 +1,28 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## MessageMock + +MIT License + +Copyright (c) 2020 波儿菜 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.plist b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.plist new file mode 100644 index 0000000..84bebfa --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.plist @@ -0,0 +1,60 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + MIT License + +Copyright (c) 2020 波儿菜 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + License + MIT + Title + MessageMock + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-dummy.m b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-dummy.m new file mode 100644 index 0000000..5a28e6b --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_MessageMockDemoTests : NSObject +@end +@implementation PodsDummy_Pods_MessageMockDemoTests +@end diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-input-files.xcfilelist b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 0000000..97c6622 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh +${BUILT_PRODUCTS_DIR}/MessageMock/MessageMock.framework \ No newline at end of file diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-output-files.xcfilelist b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 0000000..c9ff89b --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageMock.framework \ No newline at end of file diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-input-files.xcfilelist b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-input-files.xcfilelist new file mode 100644 index 0000000..1f037f7 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-input-files.xcfilelist @@ -0,0 +1 @@ +${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh \ No newline at end of file diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-output-files.xcfilelist b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-output-files.xcfilelist new file mode 100644 index 0000000..e69de29 diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh new file mode 100755 index 0000000..d850f45 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh @@ -0,0 +1,168 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/MessageMock/MessageMock.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-umbrella.h b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-umbrella.h new file mode 100644 index 0000000..5123348 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_MessageMockDemoTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_MessageMockDemoTestsVersionString[]; + diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.debug.xcconfig b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.debug.xcconfig new file mode 100644 index 0000000..aaaf61a --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.debug.xcconfig @@ -0,0 +1,10 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MessageMock" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MessageMock/MessageMock.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -l"c++.1" -framework "MessageMock" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap new file mode 100644 index 0000000..9056312 --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_MessageMockDemoTests { + umbrella header "Pods-MessageMockDemoTests-umbrella.h" + + export * + module * { export * } +} diff --git a/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.release.xcconfig b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.release.xcconfig new file mode 100644 index 0000000..b52088a --- /dev/null +++ b/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.release.xcconfig @@ -0,0 +1,6 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/README.md b/README.md index 1523edd..95b2c91 100644 --- a/README.md +++ b/README.md @@ -1 +1,122 @@ -# MessageMock \ No newline at end of file + +# MessageMock + +#### 优雅的模拟 Objective-C 方法,对参数、返回值进行任意修改和检查。 + +#### 原理分析文章:[MessageMock : 优雅的模拟 Objective-C 方法](https://juejin.im/post/6856324772303273992) + + +# 特性 + +通过任意`[target selector]`调用命中目标方法。 + +**功能:** +- 修改目标方法返回值、参数 +- 验证目标方法返回值、参数 +- 跳过目标方法调用 +- 获取目标方法命中次数 + +**TODO** +- 支持 x86 +- 支持大于指针长度的返回值修改 +- 支持大于指针长度的参数修改 + + +# 安装 + +## CocoaPods + +TODO + +## 手动导入 + +1. 把 MessageMock 源码文件夹整个拖入工程。 +2. 导入文件`#import "MessageMocker.h"` + + +# 基本用法 + +通过`MessageMocker.h`的接口进行链式配置(参考`MessageMockerTests.m`的单元测试代码)。 + +### 修改返回值 + +``` + NSObject *value = [NSObject new]; + + MessageMocker.build(NSNotificationCenter.self, @selector(defaultCenter)).mockReturn(value).start(); + + XCTAssertTrue(value == [NSNotificationCenter defaultCenter]); +``` + +### 修改参数 +``` + UIView *view = [UIView new]; + NSInteger value = 999; + + MessageMocker.build(view, @selector(setTag:)).mockArgument(0, value).start(); + + view.tag = 666; + XCTAssertTrue(view.tag == value); +``` + +### 检查返回值 + +``` + __block unsigned long value = 0; + + NSObject *obj = [NSObject new]; + + MessageMocker.build(obj, @selector(hash)) + .checkReturn(^(unsigned long arg) { + value = arg; + }) + .start(); + + unsigned long res = [obj hash]; + XCTAssertTrue(value == res); +``` + +### 检查参数 + +``` + Class anyCls = NSString.self; + + MessageMocker.build(NSObject.self, @selector(isSubclassOfClass:)) + .checkArgument(0, ^(Class cls){ + XCTAssertTrue(anyCls == cls); + }) + .start(); + + [NSObject isSubclassOfClass:anyCls]; +``` + +### 跳过方法调用 + +``` + UIView *view = [UIView new]; + NSInteger tag = 666; + view.tag = tag; + + MessageMocker.build(view, @selector(setTag:)).skip(YES).start(); + + view.tag = 999; + XCTAssertTrue(view.tag = tag); +``` + +### 设置/读取命中次数 + +``` + NSObject *value = [NSObject new]; + + MessageMocker *mocker = MessageMocker.build(TestObject.self, @selector(createObj)) + .mockReturn(value) + .limitHitCount(2); + mocker.start(); + + XCTAssertTrue(value == [TestObject createObj]); + XCTAssertTrue(mocker.hitCount == 1); + XCTAssertTrue(value == [TestObject createObj]); + XCTAssertTrue(mocker.hitCount == 2); + + XCTAssertTrue(value != [TestObject createObj]); +```