Skip to content

Commit

Permalink
Merge pull request #239 from 0x0ade/monomod-common
Browse files Browse the repository at this point in the history
Start using MonoMod.Common
  • Loading branch information
pardeike committed Jan 24, 2020
2 parents 888f3f8 + 7b77672 commit 23c2a23
Show file tree
Hide file tree
Showing 22 changed files with 182 additions and 443 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "MonoMod.Common"]
path = MonoMod.Common
url = https://github.com/MonoMod/MonoMod.Common.git
24 changes: 11 additions & 13 deletions Harmony/Extras/FastAccess.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Utils;

namespace HarmonyLib
{
Expand Down Expand Up @@ -48,13 +49,11 @@ public static InstantiationHandler<T> CreateInstantiationHandler<T>()
typeof(T)));
}

var dynamicMethod = new DynamicMethod($"InstantiateObject_{typeof(T).Name}",
MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(T), null, typeof(T),
true);
var dynamicMethod = new DynamicMethodDefinition($"InstantiateObject_{typeof(T).Name}", typeof(T), null);
var generator = dynamicMethod.GetILGenerator();
generator.Emit(OpCodes.Newobj, constructorInfo);
generator.Emit(OpCodes.Ret);
return (InstantiationHandler<T>)dynamicMethod.CreateDelegate(typeof(InstantiationHandler<T>));
return (InstantiationHandler<T>)dynamicMethod.Generate().CreateDelegate(typeof(InstantiationHandler<T>));
}

/// <summary>Creates an getter delegate for a property</summary>
Expand All @@ -73,7 +72,7 @@ public static InstantiationHandler<T> CreateInstantiationHandler<T>()
getGenerator.Emit(OpCodes.Call, getMethodInfo);
getGenerator.Emit(OpCodes.Ret);

return (GetterHandler<T, S>)dynamicGet.CreateDelegate(typeof(GetterHandler<T, S>));
return (GetterHandler<T, S>)dynamicGet.Generate().CreateDelegate(typeof(GetterHandler<T, S>));
}

/// <summary>Creates an getter delegate for a field</summary>
Expand All @@ -91,7 +90,7 @@ public static InstantiationHandler<T> CreateInstantiationHandler<T>()
getGenerator.Emit(OpCodes.Ldfld, fieldInfo);
getGenerator.Emit(OpCodes.Ret);

return (GetterHandler<T, S>)dynamicGet.CreateDelegate(typeof(GetterHandler<T, S>));
return (GetterHandler<T, S>)dynamicGet.Generate().CreateDelegate(typeof(GetterHandler<T, S>));
}

/// <summary>Creates an getter delegate for a field (with a list of possible field names)</summary>
Expand Down Expand Up @@ -133,7 +132,7 @@ public static InstantiationHandler<T> CreateInstantiationHandler<T>()
setGenerator.Emit(OpCodes.Call, setMethodInfo);
setGenerator.Emit(OpCodes.Ret);

return (SetterHandler<T, S>)dynamicSet.CreateDelegate(typeof(SetterHandler<T, S>));
return (SetterHandler<T, S>)dynamicSet.Generate().CreateDelegate(typeof(SetterHandler<T, S>));
}

/// <summary>Creates an setter delegate for a field</summary>
Expand All @@ -152,20 +151,19 @@ public static InstantiationHandler<T> CreateInstantiationHandler<T>()
setGenerator.Emit(OpCodes.Stfld, fieldInfo);
setGenerator.Emit(OpCodes.Ret);

return (SetterHandler<T, S>)dynamicSet.CreateDelegate(typeof(SetterHandler<T, S>));
return (SetterHandler<T, S>)dynamicSet.Generate().CreateDelegate(typeof(SetterHandler<T, S>));
}

//

static DynamicMethod CreateGetDynamicMethod<T, S>(Type type)
static DynamicMethodDefinition CreateGetDynamicMethod<T, S>(Type type)
{
return new DynamicMethod($"DynamicGet_{type.Name}", typeof(S), new Type[] { typeof(T) }, type, true);
return new DynamicMethodDefinition($"DynamicGet_{type.Name}", typeof(S), new Type[] { typeof(T) });
}

static DynamicMethod CreateSetDynamicMethod<T, S>(Type type)
static DynamicMethodDefinition CreateSetDynamicMethod<T, S>(Type type)
{
return new DynamicMethod($"DynamicSet_{type.Name}", typeof(void), new Type[] { typeof(T), typeof(S) }, type,
true);
return new DynamicMethodDefinition($"DynamicSet_{type.Name}", typeof(void), new Type[] { typeof(T), typeof(S) });
}
}
}
21 changes: 6 additions & 15 deletions Harmony/Extras/MethodInvoker.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using MonoMod.Utils;
using System;
using System.Reflection;
using System.Reflection.Emit;
Expand All @@ -15,21 +16,12 @@ namespace HarmonyLib
/// <summary>A helper class to invoke method with delegates</summary>
public class MethodInvoker
{
/// <summary>Creates a fast invocation handler from a method and a module</summary>
/// <param name="methodInfo">The method to invoke</param>
/// <param name="module">The module context</param>
/// <returns>The fast invocation handler</returns>
public static FastInvokeHandler GetHandler(MethodInfo methodInfo, Module module)
{
return defaultInstance.Handler(methodInfo, module);
}

/// <summary>Creates a fast invocation handler from a method and its declaring type's module</summary>
/// <summary>Creates a fast invocation handler from a method</summary>
/// <param name="methodInfo">The method to invoke</param>
/// <returns>The fast invocation handler</returns>
public static FastInvokeHandler GetHandler(MethodInfo methodInfo)
{
return defaultInstance.Handler(methodInfo, methodInfo.DeclaringType.Module);
return defaultInstance.Handler(methodInfo);
}

static readonly MethodInvoker defaultInstance = new MethodInvoker();
Expand Down Expand Up @@ -71,11 +63,10 @@ public MethodInvoker(bool directBoxValueAccess = false)

/// <summary>Creates a fast invocation handler from a method and a module</summary>
/// <param name="methodInfo">The method to invoke</param>
/// <param name="module">The module context</param>
/// <returns>The fast invocation handler</returns>
public FastInvokeHandler Handler(MethodInfo methodInfo, Module module)
public FastInvokeHandler Handler(MethodInfo methodInfo)
{
var dynamicMethod = new DynamicMethod($"FastInvoke_{methodInfo.Name}_{(directBoxValueAccess ? "direct" : "indirect")}", typeof(object), new Type[] { typeof(object), typeof(object[]) }, module, true);
var dynamicMethod = new DynamicMethodDefinition($"FastInvoke_{methodInfo.Name}_{(directBoxValueAccess ? "direct" : "indirect")}", typeof(object), new Type[] { typeof(object), typeof(object[]) });
var il = dynamicMethod.GetILGenerator();

if (!methodInfo.IsStatic)
Expand Down Expand Up @@ -166,7 +157,7 @@ public FastInvokeHandler Handler(MethodInfo methodInfo, Module module)

Emit(il, OpCodes.Ret);

var invoder = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
var invoder = (FastInvokeHandler)dynamicMethod.Generate().CreateDelegate(typeof(FastInvokeHandler));
return invoder;
}

Expand Down
62 changes: 62 additions & 0 deletions Harmony/Harmony.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@
<PublicSign>true</PublicSign>
</PropertyGroup>

<Choose>
<When Condition="$(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp'))">
<PropertyGroup>
<IsCoreOrStandard>true</IsCoreOrStandard>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<IsCoreOrStandard>false</IsCoreOrStandard>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
</Otherwise>
</Choose>

<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<DefineConstants>NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
Expand Down Expand Up @@ -81,4 +95,52 @@
</Reference>
</ItemGroup>

<ItemGroup>
<!-- Import MonoMod.Common source and dependencies. -->
<Compile Include="..\MonoMod.Common\Shared\**\*.cs" />
<Compile Include="..\MonoMod.Common\Utils\**\*.cs" />
<Compile Include="..\MonoMod.Common\RuntimeDetour\**\*.cs" />
<!-- Cecil 0.11 drops support for .NET Framework 3.5 while introducing breaking API changes. -->
<PackageReference Include="Mono.Cecil" Version="0.10" Condition="'$(TargetFramework)'=='net35'">
<PrivateAssets Condition="!$(IsCoreOrStandard)">all</PrivateAssets>
</PackageReference>
<PackageReference Include="Mono.Cecil" Version="0.11" Condition="'$(TargetFramework)'!='net35'">
<PrivateAssets Condition="!$(IsCoreOrStandard)">all</PrivateAssets>
</PackageReference>
<PackageReference Include="ILRepack.MSBuild.Task" Version="2.0.13" PrivateAssets="all" />
</ItemGroup>

<PropertyGroup>
<DefineConstants>MONOMOD_INTERNAL;$(DefineConstants)</DefineConstants>
<!-- .NET Core doesn't define NETSTANDARD, needed for MonoMod.Common. -->
<DefineConstants Condition="$(IsCoreOrStandard)">NETSTANDARD;$(DefineConstants)</DefineConstants>
<!-- Cecil 0.11 drops support for .NET Framework 3.5 while introducing breaking API changes. -->
<DefineConstants Condition="'$(TargetFramework)'=='net35'">CECIL0_10;$(DefineConstants)</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)'!='net35'">CECIL0_11;$(DefineConstants)</DefineConstants>
</PropertyGroup>

<Target Name="ILRepack" AfterTargets="PostBuildEvent" Condition="!$(IsCoreOrStandard)">
<!--
Mono.Cecil ships as a separate set of .dlls, which makes bundling Harmony more difficult.
Instead of leaving them as is, merge them into 0Harmony.dll.
-->
<PropertyGroup>
<WorkingDirectory>$(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework)</WorkingDirectory>
</PropertyGroup>
<ItemGroup>
<InputAssemblies Include="Mono.Cecil.dll" />
<InputAssemblies Include="Mono.Cecil.Mdb.dll" />
<InputAssemblies Include="Mono.Cecil.Pdb.dll" />
<InputAssemblies Include="Mono.Cecil.Rocks.dll" />
<InternalizeExcludeAssemblies Include="Harmony" />
</ItemGroup>

<ILRepack OutputType="$(OutputType)" MainAssembly="$(AssemblyName).dll" OutputAssembly="$(AssemblyName).dll" InputAssemblies="@(InputAssemblies)" InternalizeExcludeAssemblies="@(InternalizeExcludeAssemblies)" KeyFile="..\keys\Harmony.Pair.snk" WorkingDirectory="$(WorkingDirectory)" />

</Target>

<Target Name="DeleteCecil" AfterTargets="Build">
<Delete Files=".\**\Mono.Cecil.*" />
</Target>

</Project>
106 changes: 9 additions & 97 deletions Harmony/Internal/DynamicTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using MonoMod.Utils;

namespace HarmonyLib
{
/// <summary>Creating dynamic methods</summary>
internal static class DynamicTools
{
internal static DynamicMethod CreateDynamicMethod(MethodBase original, string suffix)
internal static DynamicMethodDefinition CreateDynamicMethod(MethodBase original, string suffix)
{
if (original == null) throw new ArgumentNullException(nameof(original));
var patchName = original.Name + suffix;
Expand All @@ -30,25 +31,21 @@ internal static DynamicMethod CreateDynamicMethod(MethodBase original, string su
parameterTypes.Insert(0, typeof(IntPtr));
var returnType = firstArgIsReturnBuffer ? typeof(void) : AccessTools.GetReturnedType(original);

// DynamicMethod does not support byref return types
if (returnType == null || returnType.IsByRef)
return null;

var method = new DynamicMethod(
var method = new DynamicMethodDefinition(
patchName,
MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard,
returnType,
parameterTypes.ToArray(),
original.DeclaringType,
true
parameterTypes.ToArray()
);

#if NETSTANDARD2_0 || NETCOREAPP2_0
#else
var offset = (original.IsStatic ? 0 : 1) + (firstArgIsReturnBuffer ? 1 : 0);
for (var i = 0; i < parameters.Length; i++)
_ = method.DefineParameter(i + offset, parameters[i].Attributes, parameters[i].Name);
{
var param = method.Definition.Parameters[i + offset];
param.Attributes = (Mono.Cecil.ParameterAttributes)parameters[i].Attributes;
param.Name = parameters[i].Name;
}
#endif

return method;
Expand Down Expand Up @@ -97,90 +94,5 @@ internal static LocalBuilder DeclareLocalVariable(ILGenerator generator, Type ty
}
return null;
}

internal static void PrepareDynamicMethod(DynamicMethod method)
{
var nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
var nonPublicStatic = BindingFlags.NonPublic | BindingFlags.Static;

// on mono, just call 'CreateDynMethod'
//
var m_CreateDynMethod = method.GetType().GetMethod("CreateDynMethod", nonPublicInstance);
if (m_CreateDynMethod != null)
{
var h_CreateDynMethod = MethodInvoker.GetHandler(m_CreateDynMethod);
_ = h_CreateDynMethod(method, new object[0]);
return;
}

// on all .NET Core versions, call 'RuntimeHelpers._CompileMethod' but with a different parameter:
//
var m__CompileMethod = typeof(RuntimeHelpers).GetMethod("_CompileMethod", nonPublicStatic);
var h__CompileMethod = MethodInvoker.GetHandler(m__CompileMethod);

var m_GetMethodDescriptor = method.GetType().GetMethod("GetMethodDescriptor", nonPublicInstance);
var h_GetMethodDescriptor = MethodInvoker.GetHandler(m_GetMethodDescriptor);
var handle = (RuntimeMethodHandle)h_GetMethodDescriptor(method, new object[0]);

// 1) RuntimeHelpers._CompileMethod(handle.GetMethodInfo())
//
object runtimeMethodInfo = null;
var f_m_value = handle.GetType().GetField("m_value", nonPublicInstance);
if (f_m_value != null)
runtimeMethodInfo = f_m_value.GetValue(handle);
else
{
var f_Value = handle.GetType().GetField("Value", nonPublicInstance);
if (f_Value != null)
runtimeMethodInfo = f_Value.GetValue(handle);
else
{
var m_GetMethodInfo = handle.GetType().GetMethod("GetMethodInfo", nonPublicInstance);
if (m_GetMethodInfo != null)
{
var h_GetMethodInfo = MethodInvoker.GetHandler(m_GetMethodInfo);
runtimeMethodInfo = h_GetMethodInfo(handle, new object[0]);
}
}
}
if (runtimeMethodInfo != null)
{
// Core 3.1 fails for certain methods in the try statement below
// The following extraction is wrong and stands here as a reminder that
// we need to fix that runtimeMethodInfo sometimes is a RuntimeMethodInfoStub
//
//f_m_value = runtimeMethodInfo.GetType().GetField("m_value");
//if (f_m_value != null)
// runtimeMethodInfo = f_m_value.GetValue(runtimeMethodInfo);

try
{
// this can throw BadImageFormatException "An attempt was made to load a program with an incorrect format"
_ = h__CompileMethod(null, new object[] { runtimeMethodInfo });
return;
}
#pragma warning disable RECS0022
catch
#pragma warning restore RECS0022
{
}
}

// 2) RuntimeHelpers._CompileMethod(handle.Value)
//
if (m__CompileMethod.GetParameters()[0].ParameterType.IsAssignableFrom(handle.Value.GetType()))
{
_ = h__CompileMethod(null, new object[] { handle.Value });
return;
}

// 3) RuntimeHelpers._CompileMethod(handle)
//
if (m__CompileMethod.GetParameters()[0].ParameterType.IsAssignableFrom(handle.GetType()))
{
_ = h__CompileMethod(null, new object[] { handle });
return;
}
}
}
}

0 comments on commit 23c2a23

Please sign in to comment.