From 2d4757a73af8dfa40ecddbfd170a3739a77c87e2 Mon Sep 17 00:00:00 2001 From: lbmaian Date: Mon, 16 Nov 2020 11:35:23 -0800 Subject: [PATCH 1/2] Add AccessTools.CreateInstance() and unit tests that demonstrate CreateInstance only calls public parameterless constructors --- Harmony/Tools/AccessTools.cs | 15 +++++++- HarmonyTests/Tools/Assets/AccessToolsClass.cs | 37 +++++++++++++++++++ HarmonyTests/Tools/TestAccessTools.cs | 12 ++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/Harmony/Tools/AccessTools.cs b/Harmony/Tools/AccessTools.cs index ba3fe8b4..de72c246 100644 --- a/Harmony/Tools/AccessTools.cs +++ b/Harmony/Tools/AccessTools.cs @@ -1698,6 +1698,19 @@ public static object CreateInstance(Type type) return FormatterServices.GetUninitializedObject(type); } + /// Creates an (possibly uninitialized) instance of a given type + /// The class/type + /// The new instance + /// + public static T CreateInstance() + { + var instance = CreateInstance(typeof(T)); + // Not using `as` operator since it only works with reference types. + if (instance is T typedInstance) + return typedInstance; + return default; + } + /// /// A cache for the or similar Add methods for different types. /// @@ -1706,7 +1719,7 @@ public static object CreateInstance(Type type) static readonly ReaderWriterLockSlim addHandlerCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); /// Makes a deep copy of any object - /// The type of the instance that should be created + /// The type of the instance that should be created; for legacy reasons, this must be a class or interface /// The original object /// A copy of the original object but of type T /// diff --git a/HarmonyTests/Tools/Assets/AccessToolsClass.cs b/HarmonyTests/Tools/Assets/AccessToolsClass.cs index 1f0e12ed..2a71b457 100644 --- a/HarmonyTests/Tools/Assets/AccessToolsClass.cs +++ b/HarmonyTests/Tools/Assets/AccessToolsClass.cs @@ -168,6 +168,43 @@ public string Method1() } #pragma warning restore CS0169, CS0414, IDE0044, IDE0051, IDE0052 + public static class AccessToolsCreateInstance + { + // Has default public parameterless constructor. + public class NoConstructor + { + public bool constructorCalled = true; + } + + // Does NOT have a default public parameterless constructor (or any parameterless constructor for that matter). + public class OnlyNonParameterlessConstructor + { + public bool constructorCalled = true; + + public OnlyNonParameterlessConstructor(int _) + { + } + } + + public class PublicParameterlessConstructor + { + public bool constructorCalled = true; + + public PublicParameterlessConstructor() + { + } + } + + public class InternalParameterlessConstructor + { + public bool constructorCalled = true; + + internal InternalParameterlessConstructor() + { + } + } + } + public static class AccessToolsMethodDelegate { public interface IInterface diff --git a/HarmonyTests/Tools/TestAccessTools.cs b/HarmonyTests/Tools/TestAccessTools.cs index bda283ee..4412785c 100644 --- a/HarmonyTests/Tools/TestAccessTools.cs +++ b/HarmonyTests/Tools/TestAccessTools.cs @@ -359,6 +359,18 @@ public void Test_AccessTools_GetDefaultValue() Assert.AreEqual(null, AccessTools.GetDefaultValue(typeof(void))); } + [Test] + public void Test_AccessTools_CreateInstance() + { + Assert.IsTrue(AccessTools.CreateInstance().constructorCalled); + Assert.IsFalse(AccessTools.CreateInstance().constructorCalled); + Assert.IsTrue(AccessTools.CreateInstance().constructorCalled); + Assert.IsFalse(AccessTools.CreateInstance().constructorCalled); + var instruction = AccessTools.CreateInstance(); + Assert.Null(instruction.labels); + Assert.Null(instruction.blocks); + } + [Test] public void Test_AccessTools_TypeExtension_Description() { From 66d7811b81456c8c7c2b64014f6e04e73dd8a1a1 Mon Sep 17 00:00:00 2001 From: lbmaian Date: Mon, 16 Nov 2020 11:50:35 -0800 Subject: [PATCH 2/2] AccessTools.CreateInstance can now use non-public parameterless constructors, define internal CodeInstruction parameterless constructor AccessTools.CreateInstance also now invokes the ConstructorInfo rather than using Activator.CreateInstance; the former should be faster. --- Harmony/Public/CodeInstruction.cs | 13 +++++++++---- Harmony/Tools/AccessTools.cs | 5 +++-- HarmonyTests/Tools/TestAccessTools.cs | 6 +++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Harmony/Public/CodeInstruction.cs b/Harmony/Public/CodeInstruction.cs index e826b156..e9d7dc50 100644 --- a/Harmony/Public/CodeInstruction.cs +++ b/Harmony/Public/CodeInstruction.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -11,7 +11,7 @@ namespace HarmonyLib public class CodeInstruction { /// The opcode - /// + /// public OpCode opcode; /// The operand @@ -19,13 +19,18 @@ public class CodeInstruction public object operand; /// All labels defined on this instruction - /// + /// public List