Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AccessTools.CreateInstance improvements #362

Merged
merged 2 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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