VMProtect/runtime/VMProtect.Runtime/Tests/UnitTestProject/MsilToVmTestCompiler.cs

101 lines
4.5 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.ExceptionServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestProject
{
public abstract class MsilToVmTestCompiler
{
public enum LongEnum : long { Eval1 = 1, Evalm1 = -1 }
public enum ULongEnum : ulong { Evalm1 = 0xFFFFFFFFFFFFFFFF }
public enum ByteEnum : byte {}
public enum SByteEnum : sbyte { }
public enum IntEnum /*: int*/ { }
public enum UIntEnum : uint { }
public enum ShortEnum : short { }
public enum UShortEnum : ushort { }
public static byte[] GetIlBytes(DynamicMethod dynamicMethod)
{
// ReSharper disable once PossibleNullReferenceException
var resolver = typeof(DynamicMethod).GetField("m_resolver", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dynamicMethod);
if (resolver == null) throw new ArgumentException("The dynamic method's IL has not been finalized.");
// ReSharper disable once PossibleNullReferenceException
return (byte[])resolver.GetType().GetField("m_code", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(resolver);
}
public abstract Stream CreateVmStream(Type rt, ParameterInfo[] pi, Type[] locals, byte[] ilBytes);
public enum InvokeTestCombineError
{
NoError,
NoErrorByStdEx, // текущий стандарт ECMA-335 оставляет некоторые сценарии как "неопределенное поведение", но их можно определить
VmOtherExceptionExpected,
VmOtherTypeExpected,
VmOtherValueExpected,
Cnt
};
// dyn/vm обязаны что-то возвратить, либо выбросить исключение
// (в тестах не должно быть методов, возвращающих void, null или исключение в качестве retVal)
[HandleProcessCorruptedStateExceptions]
public InvokeTestCombineError InvokeTestCombine(DynamicMethod dyn, object[] parameters, out object dynRet, out object vmRet, out string err)
{
Type exceptionType = null;
err = "";
try
{
dynRet = dyn.Invoke(null, parameters);
Assert.IsNotNull(dynRet);
Assert.IsInstanceOfType(dynRet, dyn.ReturnType);
}
catch (Exception e)
{
Assert.IsInstanceOfType(e, typeof(TargetInvocationException));
Assert.IsNotNull(e.InnerException);
exceptionType = e.InnerException.GetType();
dynRet = e.InnerException;
}
var vmStream = CreateVmStream(dyn.ReturnType, dyn.GetBaseDefinition().GetParameters(),
//FIXME dyn.GetMethodBody().LocalVariables.Select(o => o.LocalType).ToArray(),
new Type[] {},
GetIlBytes(dyn));
try
{
vmRet = Invoke(parameters, vmStream);
if(exceptionType != null)
{
err = $"VmOtherExceptionExpected: {exceptionType.FullName}, actual: {vmRet}";
return InvokeTestCombineError.VmOtherExceptionExpected;
}
if (vmRet == null || vmRet.GetType() != dyn.ReturnType)
{
err = $"VmOtherTypeExpected: {dyn.ReturnType.FullName}, actual: {((vmRet != null) ? vmRet.GetType().FullName : "null")}";
return InvokeTestCombineError.VmOtherTypeExpected;
}
}
catch (Exception e)
{
vmRet = e;
if (e.GetType() != exceptionType)
{
err = $"VmOtherExceptionExpected: {((exceptionType != null) ? exceptionType.FullName : dynRet)}, actual: {e.GetType().FullName}";
return InvokeTestCombineError.VmOtherExceptionExpected;
}
return InvokeTestCombineError.NoError;
}
if (dynRet.Equals(vmRet))
{
return InvokeTestCombineError.NoError;
}
err = $"VmOtherValueExpected: {dynRet}, actual: {vmRet}";
return InvokeTestCombineError.VmOtherValueExpected;
}
public abstract object Invoke(object[] parameters, Stream vmStream);
}
}