A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Runtime.InteropServices;
6
7namespace osu.Framework.Platform.MacOS.Native
8{
9 internal static class Class
10 {
11 [DllImport(Cocoa.LIB_OBJ_C)]
12 private static extern IntPtr class_replaceMethod(IntPtr classHandle, IntPtr selector, IntPtr method, string types);
13
14 [DllImport(Cocoa.LIB_OBJ_C)]
15 private static extern IntPtr class_getInstanceMethod(IntPtr classHandle, IntPtr selector);
16
17 [DllImport(Cocoa.LIB_OBJ_C)]
18 private static extern void method_exchangeImplementations(IntPtr method1, IntPtr method2);
19
20 [DllImport(Cocoa.LIB_OBJ_C)]
21 private static extern IntPtr objc_getClass(string name);
22
23 public static IntPtr Get(string name)
24 {
25 var id = objc_getClass(name);
26 if (id == IntPtr.Zero)
27 throw new ArgumentException("Unknown class: " + name);
28
29 return id;
30 }
31
32 public static void RegisterMethod(IntPtr handle, Delegate action, string selector, string typeString) =>
33 class_replaceMethod(handle, Selector.Get(selector), Marshal.GetFunctionPointerForDelegate(action), typeString);
34
35 /// <summary>
36 /// Performs method swizzling for a given selector, using a given delegate implementation.
37 /// </summary>
38 /// <remarks>
39 /// This essentially adds a new Objective-C method, then swaps the implementation with an existing one.
40 /// Returns a selector to the newly registered method, which has the original implementation of
41 /// <paramref name="selector"/> before swizzling.
42 /// https://nshipster.com/method-swizzling/
43 /// </remarks>
44 /// <param name="classHandle">The Objective-C class which should have a method swizzled.</param>
45 /// <param name="selector">The selector to swizzle.</param>
46 /// <param name="typeString">The type encoding of the selector.</param>
47 /// <param name="action">The delegate to use as the new implementation.</param>
48 /// <returns>A selector for the newly registered method, containing the old implementation.</returns>
49 public static IntPtr SwizzleMethod(IntPtr classHandle, string selector, string typeString, Delegate action)
50 {
51 var targetSelector = Selector.Get(selector);
52 var targetMethod = class_getInstanceMethod(classHandle, targetSelector);
53 var newMethodImplementation = Marshal.GetFunctionPointerForDelegate(action);
54 var newSelector = Selector.Get($"orig_{selector}");
55 class_replaceMethod(classHandle, newSelector, newMethodImplementation, typeString);
56 var newMethod = class_getInstanceMethod(classHandle, newSelector);
57 method_exchangeImplementations(targetMethod, newMethod);
58 return newSelector;
59 }
60 }
61}