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

2 fix code #5

Merged
merged 58 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
945d0b4
Fix tests
sguldmund Jan 11, 2024
687cf9f
Temporarily output to Console
sguldmund Jan 11, 2024
81df5e1
Add sandbox
sguldmund Jan 11, 2024
79326ca
Add test for verifying that shims are called
sguldmund Jan 11, 2024
b626a9e
WIP: Add missing code
sguldmund Jan 11, 2024
a08bbcf
WIP: More work
sguldmund Jan 11, 2024
2bd8ace
Got static property shimming working again
sguldmund Jan 11, 2024
5a8ffab
#2 Make shims work again (with the exception of constructors)
sguldmund Jan 11, 2024
4881011
WIP: Shimming constructors
sguldmund Jan 11, 2024
f90be12
#2 Shimming instance methods for value types
sguldmund Jan 11, 2024
25e4eae
#2 Shimming instance methods work
sguldmund Jan 11, 2024
50d3a97
#3 Add test for shimming instance method of specific instance
sguldmund Jan 12, 2024
8716736
#2 Constructor shimming works again
sguldmund Jan 12, 2024
b163e86
Add back commented code
sguldmund Jan 12, 2024
5dc77bc
#3 Add support for netstandard2.0
sguldmund Jan 12, 2024
db116b5
#3 Add net6
sguldmund Jan 12, 2024
86077db
#3 Rewrite a few tests to Xunit + FluentAssertions
sguldmund Jan 12, 2024
f1d2803
#4 Rewrite more tests
sguldmund Jan 12, 2024
a075aeb
#4 Rewrite more tests
sguldmund Jan 12, 2024
112111f
#4 Rewrite more tests
sguldmund Jan 12, 2024
4955c89
#3 Add support for netstandard2.1 or greater
sguldmund Jan 12, 2024
ccb773a
#4 Rewrite more tests
sguldmund Jan 12, 2024
bd401d2
#2 Use var everywhere
sguldmund Jan 12, 2024
83d4960
#2 Use built-in reference style
sguldmund Jan 12, 2024
d1049be
#2 Remove unused using statements
sguldmund Jan 12, 2024
7575f83
#2 Use same brace style everywhere
sguldmund Jan 12, 2024
241a2e3
#2 Remove warnings about "possible multiple enumeration"
sguldmund Jan 12, 2024
a504adb
#2 Fix most possible NullReferenceExceptions
sguldmund Jan 12, 2024
a1f4793
#2 Rename members
sguldmund Jan 12, 2024
a7edf00
#2 More cleaning
sguldmund Jan 12, 2024
be98705
#2 Extract forwarding logic to separate method
sguldmund Jan 12, 2024
e614fd9
#2 Silence a few more warnings
sguldmund Jan 12, 2024
1241b05
#2 Silence warnings
sguldmund Jan 12, 2024
61547f5
#2 Use base type where possible
sguldmund Jan 12, 2024
52b0447
#2 Rename DevirtualizeMethod => DeVirtualizeMethod
sguldmund Jan 12, 2024
6122ffc
#2 Expand on checks in StubHelper.IsIntrinsic
sguldmund Jan 12, 2024
3f2e578
#2 Add tests verifying that we can rewrite try-catch blocks as implem…
sguldmund Jan 12, 2024
16e9c60
#2 Add support for net48
sguldmund Jan 12, 2024
c900a33
#2 It seems that it was already supported
sguldmund Jan 12, 2024
264086b
#2 Improve error messages when throwing InvalidShimSignatureException
sguldmund Jan 12, 2024
79d4905
#4 Rewrite last remaining tests
sguldmund Jan 12, 2024
7cba5e4
#2 Add preprocessor directives to Sandbox
sguldmund Jan 12, 2024
86eed30
#2 Fix support for net4.8
sguldmund Jan 12, 2024
01c5aa4
#4 Update testing framework in MethodRewriterTests.cs
sguldmund Jan 12, 2024
aeed745
#4 Update test framework in StubHelperTests.cs
sguldmund Jan 12, 2024
353d16f
#4 Document silencing redundant using statement
sguldmund Jan 12, 2024
3407527
#4 Hopefully root out the last remnants of Microsoft-related test fra…
sguldmund Jan 12, 2024
866cfdd
#4 Finally banish the old testing framework
sguldmund Jan 12, 2024
55a8d06
#8 Locate BakeByteArray method
sguldmund Jan 13, 2024
617bc07
#4 Add test for shimming method in abstract class
sguldmund Jan 13, 2024
7cb5636
#4 Add test for not invoking shim if the method is overriden in a der…
sguldmund Jan 13, 2024
ebcb56f
#8 Add support for .NET 8 + .NET 6
sguldmund Jan 13, 2024
4cc67c5
#10 Yes, we do support shimming methods of sealed classes
sguldmund Jan 13, 2024
7da4ebb
#10 We also support shimming property getter/setter on sealed types
sguldmund Jan 13, 2024
e098e61
#4 Add more tests for abstract methods
sguldmund Jan 13, 2024
8b06e8c
#4 Apply test hierarchy in ShimTests.cs
sguldmund Jan 13, 2024
5505eba
#4 Clean up
sguldmund Jan 13, 2024
ada9c53
#4 Add missing tests for shimming setters on value types
sguldmund Jan 13, 2024
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
15 changes: 15 additions & 0 deletions Pose.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C2484D4C-4
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pose.Tests", "test\Pose.Tests\Pose.Tests.csproj", "{AE06C68C-032F-45CC-888C-54B0B98F860C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sandbox", "src\Sandbox\Sandbox.csproj", "{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -44,13 +46,26 @@ Global
{AE06C68C-032F-45CC-888C-54B0B98F860C}.Release|x64.Build.0 = Release|Any CPU
{AE06C68C-032F-45CC-888C-54B0B98F860C}.Release|x86.ActiveCfg = Release|Any CPU
{AE06C68C-032F-45CC-888C-54B0B98F860C}.Release|x86.Build.0 = Release|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Debug|x64.ActiveCfg = Debug|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Debug|x64.Build.0 = Debug|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Debug|x86.ActiveCfg = Debug|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Debug|x86.Build.0 = Debug|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Release|Any CPU.Build.0 = Release|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Release|x64.ActiveCfg = Release|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Release|x64.Build.0 = Release|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Release|x86.ActiveCfg = Release|Any CPU
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BBC1C0DC-ECA2-48C3-A233-0873428203AF} = {3D859125-47BB-49F0-ACB4-42CEE47FB562}
{AE06C68C-032F-45CC-888C-54B0B98F860C} = {C2484D4C-4BED-46C2-BD61-2508BD8BD3C5}
{46827F5F-E0FD-428B-960C-9EFFFA3D7D9D} = {3D859125-47BB-49F0-ACB4-42CEE47FB562}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FE2B0751-A19A-4621-A703-18607E69D31A}
Expand Down
2 changes: 0 additions & 2 deletions src/Pose/Delegates/ActionRef.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;

namespace Pose.Delegates
{
public delegate void ActionRef<T>(ref T arg);
Expand Down
2 changes: 0 additions & 2 deletions src/Pose/Delegates/FuncRef.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;

namespace Pose.Delegates
{
public delegate TResult FuncRef<T1, TResult>(ref T1 arg1);
Expand Down
2 changes: 1 addition & 1 deletion src/Pose/Exceptions/InvalidShimSignatureException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace Pose.Exceptions
[System.Serializable]
internal class InvalidShimSignatureException : System.Exception
{
public InvalidShimSignatureException() : base() { }
public InvalidShimSignatureException() { }
public InvalidShimSignatureException(string message) : base(message) { }
public InvalidShimSignatureException(string message, System.Exception inner) : base(message, inner) { }
protected InvalidShimSignatureException(
Expand Down
17 changes: 17 additions & 0 deletions src/Pose/Exceptions/MethodRewriteException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Runtime.Serialization;

namespace Pose.Exceptions
{
[Serializable]
public class MethodRewriteException : Exception
{
public MethodRewriteException() { }

protected MethodRewriteException(SerializationInfo info, StreamingContext context) : base(info, context) { }

public MethodRewriteException(string message) : base(message) { }

public MethodRewriteException(string message, Exception innerException) : base(message, innerException) { }
}
}
6 changes: 5 additions & 1 deletion src/Pose/Extensions/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ namespace Pose.Extensions
{
internal static class DictionaryExtensions
{
public static bool TryAdd<T, U>(this Dictionary<T, U> dictionary, T key, U value)
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
#if NETSTANDARD2_0 || NET48
try
{
dictionary.Add(key, value);
Expand All @@ -15,6 +16,9 @@ public static bool TryAdd<T, U>(this Dictionary<T, U> dictionary, T key, U value
{
return false;
}
#else
return dictionary.TryAdd(key, value);
#endif
}
}
}
14 changes: 12 additions & 2 deletions src/Pose/Extensions/ILGeneratorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Reflection;
using System.Reflection.Emit;

Expand All @@ -7,9 +8,18 @@ internal static class ILGeneratorExtensions
{
public static byte[] GetILBytes(this ILGenerator ilGenerator)
{
var bakeByteArray = typeof(ILGenerator).GetMethod("BakeByteArray", BindingFlags.Instance | BindingFlags.NonPublic);
byte[] ilBytes = (byte[])bakeByteArray.Invoke(ilGenerator, null);
#if NET8_0_OR_GREATER
var runtimeILGeneratorType = Type.GetType("System.Reflection.Emit.RuntimeILGenerator") ?? throw new Exception("Cannot find type System.Reflection.Emit.RuntimeILGenerator");
var bakeByteArray = runtimeILGeneratorType.GetMethod("BakeByteArray", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new Exception($"Cannot get method BakeByteArray from type {nameof(ILGenerator)}");
var ilBytes = (byte[])bakeByteArray.Invoke(ilGenerator, null);
return ilBytes;
#else
var bakeByteArray = typeof(ILGenerator).GetMethod("BakeByteArray", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new Exception($"Cannot get method BakeByteArray from type {nameof(ILGenerator)}");
var ilBytes = (byte[])bakeByteArray.Invoke(ilGenerator, null);
return ilBytes;
#endif
}
}
}
13 changes: 12 additions & 1 deletion src/Pose/Extensions/MethodBaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@ internal static class MethodBaseExtensions
{
public static bool InCoreLibrary(this MethodBase methodBase)
{
return methodBase.DeclaringType.Assembly == typeof(Exception).Assembly;
if (methodBase == null) throw new ArgumentNullException(nameof(methodBase));

var declaringType = methodBase.DeclaringType ?? throw new Exception($"Method {methodBase.Name} does not have a {nameof(MethodBase.DeclaringType)}");

return declaringType.Assembly == typeof(Exception).Assembly;
}

public static bool IsForValueType(this MethodBase methodBase)
{
if (methodBase == null) throw new ArgumentNullException(nameof(methodBase));

return methodBase.DeclaringType?.IsSubclassOf(typeof(ValueType)) ?? throw new Exception($"Method {methodBase.Name} does not have a {nameof(MethodBase.DeclaringType)}");
}

public static bool IsOverride(this MethodBase methodBase)
Expand Down
74 changes: 40 additions & 34 deletions src/Pose/Helpers/ShimHelper.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using Pose.Exceptions;
using Pose.Extensions;

namespace Pose.Helpers
{
internal static class ShimHelper
{
public static MethodBase GetMethodFromExpression(Expression expression, bool setter, out Object instanceOrType)
public static MethodBase GetMethodFromExpression(Expression expression, bool setter, out object instanceOrType)
{
switch (expression.NodeType)
{
case ExpressionType.MemberAccess:
{
MemberExpression memberExpression = expression as MemberExpression;
MemberInfo memberInfo = memberExpression.Member;
var memberExpression = expression as MemberExpression ?? throw new Exception($"Cannot cast expression to {nameof(MemberExpression)}");
var memberInfo = memberExpression.Member;
if (memberInfo.MemberType == MemberTypes.Property)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
var propertyInfo = memberInfo as PropertyInfo ?? throw new Exception($"Cannot cast {nameof(memberInfo)} to {nameof(PropertyInfo)}");
instanceOrType = GetObjectInstanceOrType(memberExpression.Expression);
return setter ? propertyInfo.GetSetMethod() : propertyInfo.GetGetMethod();
}
else
{
throw new NotImplementedException("Unsupported expression");
}
}
case ExpressionType.Call:
MethodCallExpression methodCallExpression = expression as MethodCallExpression;
var methodCallExpression = expression as MethodCallExpression ?? throw new Exception($"Cannot cast expression to {nameof(MethodCallExpression)}");
instanceOrType = GetObjectInstanceOrType(methodCallExpression.Object);
return methodCallExpression.Method;
case ExpressionType.New:
NewExpression newExpression = expression as NewExpression;
var newExpression = expression as NewExpression ?? throw new Exception($"Cannot cast expression to {nameof(NewExpression)}");
instanceOrType = null;
return newExpression.Constructor;
default:
Expand All @@ -43,40 +43,45 @@ public static MethodBase GetMethodFromExpression(Expression expression, bool set

public static void ValidateReplacementMethodSignature(MethodBase original, MethodInfo replacement, Type type, bool setter)
{
bool isValueType = original.DeclaringType.IsValueType;
bool isStatic = original.IsStatic;
bool isConstructor = original.IsConstructor;
bool isStaticOrConstructor = isStatic || isConstructor;
if (original == null) throw new ArgumentNullException(nameof(original));
if (replacement == null) throw new ArgumentNullException(nameof(replacement));

var isValueType = original.DeclaringType?.IsValueType ?? throw new Exception($"Method {original.Name} does not have a {nameof(MethodBase.DeclaringType)}");
var isStatic = original.IsStatic;
var isConstructor = original.IsConstructor;
var isStaticOrConstructor = isStatic || isConstructor;

Type vaildReturnType = isConstructor ? original.DeclaringType : (original as MethodInfo).ReturnType;
vaildReturnType = setter ? typeof(void) : vaildReturnType;
Type shimReturnType = replacement.ReturnType;
var validReturnType = isConstructor ? original.DeclaringType : (original as MethodInfo).ReturnType;
validReturnType = setter ? typeof(void) : validReturnType;
var shimReturnType = replacement.ReturnType;

Type validOwningType = type;
Type shimOwningType = isStaticOrConstructor
var validOwningType = type;
var shimOwningType = isStaticOrConstructor
? validOwningType : replacement.GetParameters().Select(p => p.ParameterType).FirstOrDefault();

var validParameterTypes = original.GetParameters().Select(p => p.ParameterType);
var validParameterTypes = original.GetParameters().Select(p => p.ParameterType).ToArray();
var shimParameterTypes = replacement.GetParameters()
.Select(p => p.ParameterType)
.Skip(isStaticOrConstructor ? 0 : 1);
.Skip(isStaticOrConstructor ? 0 : 1)
.ToArray();

if (vaildReturnType != shimReturnType)
throw new InvalidShimSignatureException("Mismatched return types");
if (validReturnType != shimReturnType)
throw new InvalidShimSignatureException($"Mismatched return types. Expected {validReturnType}. Got {shimReturnType}");

if (!isStaticOrConstructor)
{
if (isValueType && !shimOwningType.IsByRef)
throw new InvalidShimSignatureException("ValueType instances must be passed by ref");
}

if ((isValueType && !isStaticOrConstructor ? validOwningType.MakeByRefType() : validOwningType) != shimOwningType)
throw new InvalidShimSignatureException("Mismatched instance types");
var expectedType = (isValueType && !isStaticOrConstructor ? validOwningType.MakeByRefType() : validOwningType);
if (expectedType != shimOwningType)
throw new InvalidShimSignatureException($"Mismatched instance types. Expected {expectedType.FullName}. Got {shimOwningType.FullName}");

if (validParameterTypes.Count() != shimParameterTypes.Count())
throw new InvalidShimSignatureException("Parameters count do not match");
if (validParameterTypes.Length != shimParameterTypes.Length)
throw new InvalidShimSignatureException($"Parameters count do not match. Expected {validParameterTypes.Length}. Got {shimParameterTypes.Length}");

for (int i = 0; i < validParameterTypes.Count(); i++)
for (var i = 0; i < validParameterTypes.Length; i++)
{
if (validParameterTypes.ElementAt(i) != shimParameterTypes.ElementAt(i))
throw new InvalidShimSignatureException($"Parameter types at {i} do not match");
Expand All @@ -90,27 +95,28 @@ public static object GetObjectInstanceOrType(Expression expression)
{
case ExpressionType.MemberAccess:
{
MemberExpression memberExpression = expression as MemberExpression;
ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;
var memberExpression = expression as MemberExpression ?? throw new Exception($"Cannot cast expression to {nameof(MemberExpression)}");
var constantExpression = memberExpression.Expression as ConstantExpression;

if (memberExpression.Member.MemberType == MemberTypes.Field)
{
FieldInfo fieldInfo = (memberExpression.Member as FieldInfo);
var obj = fieldInfo.IsStatic ? null : constantExpression.Value;
var fieldInfo = memberExpression.Member as FieldInfo ?? throw new Exception($"Cannot cast {nameof(MemberExpression.Member)} to {nameof(FieldInfo)}");
var obj = fieldInfo.IsStatic ? null : constantExpression?.Value;
instanceOrType = fieldInfo.GetValue(obj);
}
else if (memberExpression.Member.MemberType == MemberTypes.Property)
{
PropertyInfo propertyInfo = (memberExpression.Member as PropertyInfo);
var obj = propertyInfo.GetMethod.IsStatic ? null : constantExpression.Value;
var propertyInfo = memberExpression.Member as PropertyInfo ?? throw new Exception($"Cannot cast {nameof(MemberExpression.Member)} to {nameof(PropertyInfo)}");
var obj = propertyInfo.GetMethod.IsStatic ? null : constantExpression?.Value;
instanceOrType = propertyInfo.GetValue(obj);
}
EnsureInstanceNotValueType(instanceOrType);
break;
}
case ExpressionType.Call:
{
MethodCallExpression methodCallExpression = expression as MethodCallExpression;
MethodInfo methodInfo = methodCallExpression.Method;
var methodCallExpression = expression as MethodCallExpression ?? throw new Exception($"Cannot cast expression to {nameof(MethodCallExpression)}");
var methodInfo = methodCallExpression.Method;
instanceOrType = methodInfo.GetGenericArguments().FirstOrDefault();
break;
}
Expand Down
Loading
Loading