101 lines
4.5 KiB
C#
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);
|
|
}
|
|
}
|