Skip to content

Commit

Permalink
Merge pull request #362 from lbmaian/createinstance
Browse files Browse the repository at this point in the history
AccessTools.CreateInstance improvements
  • Loading branch information
pardeike authored Nov 16, 2020
2 parents ea77024 + 66d7811 commit e79874b
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 7 deletions.
13 changes: 9 additions & 4 deletions Harmony/Public/CodeInstruction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
Expand All @@ -11,21 +11,26 @@ namespace HarmonyLib
public class CodeInstruction
{
/// <summary>The opcode</summary>
///
///
public OpCode opcode;

/// <summary>The operand</summary>
///
public object operand;

/// <summary>All labels defined on this instruction</summary>
///
///
public List<Label> labels = new List<Label>();

/// <summary>All exception block boundaries defined on this instruction</summary>
///
///
public List<ExceptionBlock> blocks = new List<ExceptionBlock>();

// Internal parameterless constructor that AccessTools.CreateInstance can use, ensuring that labels/blocks are initialized.
internal CodeInstruction()
{
}

/// <summary>Creates a new CodeInstruction with a given opcode and optional operand</summary>
/// <param name="opcode">The opcode</param>
/// <param name="operand">The operand</param>
Expand Down
20 changes: 17 additions & 3 deletions Harmony/Tools/AccessTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1692,12 +1692,26 @@ public static object CreateInstance(Type type)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
var ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[0], null);
var ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, binder: null,
CallingConventions.Any, new Type[0], modifiers: null);
if (ctor is object)
return Activator.CreateInstance(type);
return ctor.Invoke(null);
return FormatterServices.GetUninitializedObject(type);
}

/// <summary>Creates an (possibly uninitialized) instance of a given type</summary>
/// <typeparam name="T">The class/type</typeparam>
/// <returns>The new instance</returns>
///
public static T CreateInstance<T>()
{
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;
}

/// <summary>
/// A cache for the <see cref="ICollection{T}.Add"/> or similar Add methods for different types.
/// </summary>
Expand All @@ -1706,7 +1720,7 @@ public static object CreateInstance(Type type)
static readonly ReaderWriterLockSlim addHandlerCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

/// <summary>Makes a deep copy of any object</summary>
/// <typeparam name="T">The type of the instance that should be created</typeparam>
/// <typeparam name="T">The type of the instance that should be created; for legacy reasons, this must be a class or interface</typeparam>
/// <param name="source">The original object</param>
/// <returns>A copy of the original object but of type T</returns>
///
Expand Down
37 changes: 37 additions & 0 deletions HarmonyTests/Tools/Assets/AccessToolsClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions HarmonyTests/Tools/TestAccessTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AccessToolsCreateInstance.NoConstructor>().constructorCalled);
Assert.IsFalse(AccessTools.CreateInstance<AccessToolsCreateInstance.OnlyNonParameterlessConstructor>().constructorCalled);
Assert.IsTrue(AccessTools.CreateInstance<AccessToolsCreateInstance.PublicParameterlessConstructor>().constructorCalled);
Assert.IsTrue(AccessTools.CreateInstance<AccessToolsCreateInstance.InternalParameterlessConstructor>().constructorCalled);
var instruction = AccessTools.CreateInstance<CodeInstruction>();
Assert.NotNull(instruction.labels);
Assert.NotNull(instruction.blocks);
}

[Test]
public void Test_AccessTools_TypeExtension_Description()
{
Expand Down

0 comments on commit e79874b

Please sign in to comment.