2021-09-20 18:20:01 +02:00

1079 lines
32 KiB
C#

/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics;
using dnSpy.Debugger.DotNet.Metadata;
namespace dnSpy.Debugger.DotNet.Interpreter {
/// <summary>
/// IL stack value kind
/// </summary>
public enum ILValueKind {
/// <summary>
/// 32-bit integer. 1-byte and 2-byte integers are sign/zero extended to 32 bits. Booleans and chars are zero extended.
/// </summary>
Int32,
/// <summary>
/// 64-bit integer
/// </summary>
Int64,
/// <summary>
/// 64-bit float (32-bit floats are extended to 64-bit floats)
/// </summary>
Float,
/// <summary>
/// Unmanaged pointer or native int
/// </summary>
NativeInt,
/// <summary>
/// Managed pointer
/// </summary>
ByRef,
/// <summary>
/// Any other reference type or value type
/// </summary>
Type,
}
/// <summary>
/// A value that can be stored on the IL stack
/// </summary>
public abstract class ILValue {
/// <summary>
/// Gets the stack value kind
/// </summary>
public abstract ILValueKind Kind { get; }
/// <summary>
/// true if this is a null value
/// </summary>
public virtual bool IsNull => false;
/// <summary>
/// Makes a copy of this instance so the new instance can be pushed onto the stack. The default implementation
/// returns itself. Only mutable value types need to override this method.
/// </summary>
/// <returns></returns>
public virtual ILValue Clone() => this;
/// <summary>
/// Gets the type of the value or null if it's unknown, eg. it's a null reference
/// </summary>
public abstract DmdType? Type { get; }
/// <summary>
/// Loads an instance field. Returns null if it's not supported.
/// </summary>
/// <param name="field">Field</param>
/// <returns></returns>
public virtual ILValue? LoadField(DmdFieldInfo field) => null;
/// <summary>
/// Stores a value in an instance field. Returns false if it's not supported.
/// </summary>
/// <param name="field">Field</param>
/// <param name="value">Value</param>
/// <returns></returns>
public virtual bool StoreField(DmdFieldInfo field, ILValue value) => false;
/// <summary>
/// Returns the address of an instance field. Returns null if it's not supported.
/// </summary>
/// <param name="field">Field</param>
/// <returns></returns>
public virtual ILValue? LoadFieldAddress(DmdFieldInfo field) => null;
/// <summary>
/// Calls an instance method. The method could be a CLR-generated method, eg. an array Address() method, see <see cref="DmdSpecialMethodKind"/>.
/// Returns false if it's not supported.
/// </summary>
/// <param name="isCallvirt">true if this is a virtual call, false if it's a non-virtual call</param>
/// <param name="method">Method</param>
/// <param name="arguments">Arguments. The hidden 'this' value isn't included, it's this instance.</param>
/// <param name="returnValue">Updated with the return value. Can be null if the return type is <see cref="void"/></param>
/// <returns></returns>
public virtual bool Call(bool isCallvirt, DmdMethodBase method, ILValue[] arguments, out ILValue? returnValue) {
returnValue = null;
return false;
}
/// <summary>
/// Calls an instance method or returns false on failure
/// </summary>
/// <param name="methodAddress">Method address</param>
/// <param name="methodSig">Method signature</param>
/// <param name="arguments">Method arguments</param>
/// <param name="returnValue">Return value. It's ignored if the method returns <see cref="void"/></param>
/// <returns></returns>
public virtual bool CallIndirect(DmdMethodSignature methodSig, ILValue methodAddress, ILValue[] arguments, out ILValue? returnValue) {
returnValue = null;
return false;
}
/// <summary>
/// Boxes this instance. Returns null if it's not supported.
/// </summary>
/// <param name="type">Target type</param>
/// <returns></returns>
public virtual ILValue? Box(DmdType type) => null;
/// <summary>
/// Unboxes this instance. Returns null if it's not supported.
/// </summary>
/// <param name="type">Target type</param>
/// <returns></returns>
public virtual ILValue? UnboxAny(DmdType type) => null;
/// <summary>
/// Unboxes this instance. Returns null if it's not supported.
/// </summary>
/// <param name="type">Target type</param>
/// <returns></returns>
public virtual ILValue? Unbox(DmdType type) => null;
/// <summary>
/// Loads an SZ array element. Returns null if it's not supported.
/// </summary>
/// <param name="loadValueType">Type of value to load</param>
/// <param name="index">Array index</param>
/// <param name="elementType">Optional element type (eg. it's the ldelem instruction)</param>
/// <returns></returns>
public virtual ILValue? LoadSZArrayElement(LoadValueType loadValueType, long index, DmdType elementType) => null;
/// <summary>
/// Writes an SZ array element. Returns false if it's not supported.
/// </summary>
/// <param name="loadValueType">Type of value to store</param>
/// <param name="index">Index</param>
/// <param name="value">Value</param>
/// <param name="elementType">Optional element type (eg. it's the stelem instruction)</param>
/// <returns></returns>
public virtual bool StoreSZArrayElement(LoadValueType loadValueType, long index, ILValue value, DmdType elementType) => false;
/// <summary>
/// Loads the address of an SZ array element. Returns null if it's not supported.
/// </summary>
/// <param name="index">Index</param>
/// <param name="elementType">Element type</param>
/// <returns></returns>
public virtual ILValue? LoadSZArrayElementAddress(long index, DmdType elementType) => null;
/// <summary>
/// Gets the length of an SZ array. Returns false if it's not supported.
/// </summary>
/// <param name="length">Updated with the length of the array</param>
/// <returns></returns>
public virtual bool GetSZArrayLength(out long length) {
length = 0;
return false;
}
/// <summary>
/// Loads a value. Returns null if it's not supported.
/// </summary>
/// <param name="type">Type</param>
/// <param name="loadValueType">Type of value to load</param>
/// <returns></returns>
public virtual ILValue? LoadIndirect(DmdType type, LoadValueType loadValueType) => null;
/// <summary>
/// Stores a value. Returns false if it's not supported.
/// </summary>
/// <param name="type">Type</param>
/// <param name="loadValueType">Type of value to store</param>
/// <param name="value">Value</param>
/// <returns></returns>
public virtual bool StoreIndirect(DmdType type, LoadValueType loadValueType, ILValue value) => false;
/// <summary>
/// Clears the memory. Returns false if it's not supported.
/// </summary>
/// <param name="type">Type</param>
/// <returns></returns>
public virtual bool InitializeObject(DmdType type) => false;
/// <summary>
/// Copies <paramref name="source"/> to this value. Returns false if it's not supported.
/// </summary>
/// <param name="type">Type</param>
/// <param name="source">Source value</param>
/// <returns></returns>
public virtual bool CopyObject(DmdType type, ILValue source) => false;
/// <summary>
/// Initializes memory. Returns false if it's not supported.
/// </summary>
/// <param name="value">Value to write</param>
/// <param name="size">Size of data</param>
/// <returns></returns>
public virtual bool InitializeMemory(byte value, long size) => false;
/// <summary>
/// Copies memory to this value. Returns false if it's not supported.
/// </summary>
/// <param name="source">Source value</param>
/// <param name="size">Size in bytes</param>
/// <returns></returns>
public virtual bool CopyMemory(ILValue source, long size) => false;
/// <summary>
/// Adds a constant to a copy of this value and returns the result. Returns null if it's not supported.
/// </summary>
/// <param name="kind">Opcode kind</param>
/// <param name="value">Value to add</param>
/// <param name="pointerSize">Size of a pointer in bytes</param>
/// <returns></returns>
public virtual ILValue? Add(AddOpCodeKind kind, long value, int pointerSize) => null;
/// <summary>
/// Subtracts a constant from a copy of this value and returns the result. Returns null if it's not supported.
/// </summary>
/// <param name="kind">Opcode kind</param>
/// <param name="value">Value to subtract</param>
/// <param name="pointerSize">Size of a pointer in bytes</param>
/// <returns></returns>
public virtual ILValue? Sub(SubOpCodeKind kind, long value, int pointerSize) => null;
/// <summary>
/// Subtracts <paramref name="value"/> from a copy of this value and returns the result. Returns null if it's not supported.
/// </summary>
/// <param name="kind">Opcode kind</param>
/// <param name="value">Value to subtract</param>
/// <param name="pointerSize">Size of a pointer in bytes</param>
/// <returns></returns>
public virtual ILValue? Sub(SubOpCodeKind kind, ILValue value, int pointerSize) => null;
/// <summary>
/// Converts this value to a new value. Returns null if it's not supported.
/// </summary>
/// <param name="kind">Opcode kind</param>
/// <returns></returns>
public virtual ILValue? Conv(ConvOpCodeKind kind) => null;
}
/// <summary>
/// Add opcode kind
/// </summary>
public enum AddOpCodeKind {
/// <summary>
/// Normal addition
/// </summary>
Add,
/// <summary>
/// Signed addition with an overflow check
/// </summary>
Add_Ovf,
/// <summary>
/// Unsigned addition with an overflow check
/// </summary>
Add_Ovf_Un,
}
/// <summary>
/// Sub opcode kind
/// </summary>
public enum SubOpCodeKind {
/// <summary>
/// Normal subtraction
/// </summary>
Sub,
/// <summary>
/// Signed subtraction with an overflow check
/// </summary>
Sub_Ovf,
/// <summary>
/// Unsigned subtraction with an overflow check
/// </summary>
Sub_Ovf_Un,
}
/// <summary>
/// Convert opcode kind
/// </summary>
public enum ConvOpCodeKind {
/// <summary>
/// Convert to a <see cref="IntPtr"/>
/// </summary>
Conv_I,
/// <summary>
/// Convert to a <see cref="IntPtr"/>, signed, overflow check
/// </summary>
Conv_Ovf_I,
/// <summary>
/// Convert to a <see cref="IntPtr"/>, unsigned, overflow check
/// </summary>
Conv_Ovf_I_Un,
/// <summary>
/// Convert to a <see cref="UIntPtr"/>
/// </summary>
Conv_U,
/// <summary>
/// Convert to a <see cref="UIntPtr"/>, signed, overflow check
/// </summary>
Conv_Ovf_U,
/// <summary>
/// Convert to a <see cref="UIntPtr"/>, unsigned, overflow check
/// </summary>
Conv_Ovf_U_Un,
}
/// <summary>
/// 32-bit integer. 1-byte, 2-byte and 4-byte integer values, booleans, and chars use this class.
/// Smaller values are sign or zero extended.
/// </summary>
public class ConstantInt32ILValue : ILValue {
/// <summary>
/// Always returns <see cref="ILValueKind.Int32"/>
/// </summary>
public sealed override ILValueKind Kind => ILValueKind.Int32;
/// <summary>
/// Gets the value
/// </summary>
public int Value { get; }
/// <summary>
/// Gets the value as a <see cref="uint"/>
/// </summary>
public uint UnsignedValue => (uint)Value;
internal ulong UnsignedValue64 => (ulong)(long)Value;
/// <summary>
/// Constructor
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
public ConstantInt32ILValue(DmdAppDomain appDomain, int value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Int32;
Value = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">Type, eg. <see cref="int"/></param>
/// <param name="value">Value</param>
public ConstantInt32ILValue(DmdType type, int value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
Value = value;
}
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type { get; }
/// <summary>
/// ToString()
/// </summary>
/// <returns></returns>
public override string ToString() => "0x" + Value.ToString("X8");
}
/// <summary>
/// 64-bit integer
/// </summary>
public class ConstantInt64ILValue : ILValue {
/// <summary>
/// Always returns <see cref="ILValueKind.Int64"/>
/// </summary>
public sealed override ILValueKind Kind => ILValueKind.Int64;
/// <summary>
/// Gets the value
/// </summary>
public long Value { get; }
/// <summary>
/// Gets the value as a <see cref="ulong"/>
/// </summary>
public ulong UnsignedValue => (ulong)Value;
/// <summary>
/// Constructor
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
public ConstantInt64ILValue(DmdAppDomain appDomain, long value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Int64;
Value = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">Type, eg. <see cref="long"/></param>
/// <param name="value">Value</param>
public ConstantInt64ILValue(DmdType type, long value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
Value = value;
}
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type { get; }
/// <summary>
/// ToString()
/// </summary>
/// <returns></returns>
public override string ToString() => "0x" + Value.ToString("X16");
}
/// <summary>
/// 64-bit floating point value (32-bit floating point numbers are extended to 64 bits)
/// </summary>
public class ConstantFloatILValue : ILValue {
/// <summary>
/// Always returns <see cref="ILValueKind.Float"/>
/// </summary>
public sealed override ILValueKind Kind => ILValueKind.Float;
/// <summary>
/// Gets the value
/// </summary>
public double Value { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
public ConstantFloatILValue(DmdAppDomain appDomain, double value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Double;
Value = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">Type, eg. <see cref="double"/></param>
/// <param name="value">Value</param>
public ConstantFloatILValue(DmdType type, double value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
Value = value;
}
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type { get; }
/// <summary>
/// ToString()
/// </summary>
/// <returns></returns>
public override string ToString() => Value.ToString();
}
/// <summary>
/// native integer or unmanaged pointer
/// </summary>
public abstract class NativeIntILValue : ILValue {
/// <summary>
/// Always returns <see cref="ILValueKind.NativeInt"/>
/// </summary>
public sealed override ILValueKind Kind => ILValueKind.NativeInt;
}
/// <summary>
/// native integer or unmanaged pointer
/// </summary>
public class ConstantNativeIntILValue : NativeIntILValue {
readonly long value;
/// <summary>
/// Gets the value as a <see cref="int"/>
/// </summary>
public int Value32 => (int)value;
/// <summary>
/// Gets the value as a <see cref="long"/>
/// </summary>
public long Value64 => value;
/// <summary>
/// Gets the value as a <see cref="uint"/>
/// </summary>
public uint UnsignedValue32 => (uint)value;
/// <summary>
/// Gets the value as a <see cref="ulong"/>
/// </summary>
public ulong UnsignedValue64 => (ulong)value;
/// <summary>
/// Creates a 32-bit native int
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
/// <returns></returns>
public static ConstantNativeIntILValue Create32(DmdAppDomain appDomain, int value) => new ConstantNativeIntILValue(appDomain, value);
/// <summary>
/// Creates a 64-bit native int
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
/// <returns></returns>
public static ConstantNativeIntILValue Create64(DmdAppDomain appDomain, long value) => new ConstantNativeIntILValue(appDomain, value);
/// <summary>
/// Creates a 32-bit native int
/// </summary>
/// <param name="type">Type, eg. <see cref="IntPtr"/></param>
/// <param name="value">Value</param>
/// <returns></returns>
public static ConstantNativeIntILValue Create32(DmdType type, int value) => new ConstantNativeIntILValue(type, value);
/// <summary>
/// Creates a 64-bit native int
/// </summary>
/// <param name="type">Type, eg. <see cref="IntPtr"/></param>
/// <param name="value">Value</param>
/// <returns></returns>
public static ConstantNativeIntILValue Create64(DmdType type, long value) => new ConstantNativeIntILValue(type, value);
/// <summary>
/// Constructor
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
protected ConstantNativeIntILValue(DmdAppDomain appDomain, int value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_IntPtr;
this.value = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="value">Value</param>
protected ConstantNativeIntILValue(DmdAppDomain appDomain, long value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_IntPtr;
this.value = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">Type, eg. <see cref="IntPtr"/></param>
/// <param name="value">Value</param>
protected ConstantNativeIntILValue(DmdType type, int value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
this.value = value;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="type">Type, eg. <see cref="IntPtr"/></param>
/// <param name="value">Value</param>
protected ConstantNativeIntILValue(DmdType type, long value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
this.value = value;
}
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type { get; }
/// <summary>
/// ToString()
/// </summary>
/// <returns></returns>
public override string ToString() => "0x" + value.ToString("X");
}
/// <summary>
/// Function pointer, created by the ldftn/ldvirtftn instructions
/// </summary>
public sealed class FunctionPointerILValue : NativeIntILValue {
/// <summary>
/// true if it was created by a ldvirtftn instruction, false it was created by a ldftn instruction
/// </summary>
public bool IsVirtual => VirtualThisObject is not null;
/// <summary>
/// Gets the this value if and only if this was created by a ldvirtftn instruction, otherwise it's null
/// </summary>
public ILValue? VirtualThisObject { get; }
/// <summary>
/// Gets the method
/// </summary>
public DmdMethodBase Method { get; }
/// <summary>
/// Constructor (used by ldftn instruction)
/// </summary>
/// <param name="method">Method</param>
public FunctionPointerILValue(DmdMethodBase method) {
Method = method ?? throw new ArgumentNullException(nameof(method));
Type = method.AppDomain.System_Void.MakePointerType();
}
/// <summary>
/// Constructor (used by ldvirtftn instruction)
/// </summary>
/// <param name="method">Method</param>
/// <param name="thisValue">This object</param>
public FunctionPointerILValue(DmdMethodBase method, ILValue thisValue) {
Method = method;
Type = method.AppDomain.System_Void.MakePointerType();
VirtualThisObject = thisValue;
}
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type { get; }
}
/// <summary>
/// Pointer to a block of memory. Used by eg. localloc
/// </summary>
public sealed class NativeMemoryILValue : NativeIntILValue {
readonly byte[] data;
long offset;
int Offset32 => (int)offset;
long Offset64 => offset;
uint UnsignedOffset32 => (uint)offset;
ulong UnsignedOffset64 => (ulong)offset;
/// <summary>
/// Constructor
/// </summary>
/// <param name="appDomain">AppDomain</param>
/// <param name="size">Size of memory</param>
public NativeMemoryILValue(DmdAppDomain appDomain, int size) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Void.MakePointerType();
data = new byte[size];
}
NativeMemoryILValue(byte[] data, long offset) {
this.data = data;
this.offset = offset;
}
/// <summary>
/// Adds a constant to a copy of this value and returns the result. Returns null if it's not supported.
/// </summary>
/// <param name="kind">Opcode kind</param>
/// <param name="value">Value to add</param>
/// <param name="pointerSize">Size of a pointer in bytes</param>
/// <returns></returns>
public override ILValue? Add(AddOpCodeKind kind, long value, int pointerSize) {
if (value == 0)
return this;
switch (kind) {
case AddOpCodeKind.Add:
if (pointerSize == 4)
return new NativeMemoryILValue(data, Offset32 + (int)value);
return new NativeMemoryILValue(data, Offset64 + value);
case AddOpCodeKind.Add_Ovf:
if (pointerSize == 4) {
int value2 = (int)value;
return new NativeMemoryILValue(data, checked(Offset32 + value2));
}
return new NativeMemoryILValue(data, checked(Offset64 + value));
case AddOpCodeKind.Add_Ovf_Un:
if (pointerSize == 4) {
uint value2 = (uint)value;
return new NativeMemoryILValue(data, (int)checked(UnsignedOffset32 + value2));
}
else {
ulong value2 = (ulong)value;
return new NativeMemoryILValue(data, (long)checked(UnsignedOffset64 + value2));
}
default:
throw new InvalidOperationException();
}
}
/// <summary>
/// Subtracts a constant from a copy of this value and returns the result. Returns null if it's not supported.
/// </summary>
/// <param name="kind">Opcode kind</param>
/// <param name="value">Value to subtract</param>
/// <param name="pointerSize">Size of a pointer in bytes</param>
/// <returns></returns>
public override ILValue? Sub(SubOpCodeKind kind, long value, int pointerSize) {
if (value == 0)
return this;
switch (kind) {
case SubOpCodeKind.Sub:
if (pointerSize == 4)
return new NativeMemoryILValue(data, Offset32 - (int)value);
return new NativeMemoryILValue(data, Offset64 - value);
case SubOpCodeKind.Sub_Ovf:
if (pointerSize == 4) {
int value2 = (int)value;
return new NativeMemoryILValue(data, checked(Offset32 - value2));
}
return new NativeMemoryILValue(data, checked(Offset64 - value));
case SubOpCodeKind.Sub_Ovf_Un:
if (pointerSize == 4) {
uint value2 = (uint)value;
return new NativeMemoryILValue(data, (int)checked(UnsignedOffset32 - value2));
}
else {
ulong value2 = (ulong)value;
return new NativeMemoryILValue(data, (long)checked(UnsignedOffset64 - value2));
}
default:
throw new InvalidOperationException();
}
}
/// <summary>
/// Loads a value. Returns null if it's not supported.
/// </summary>
/// <param name="type">Type</param>
/// <param name="loadValueType">Type of value to load</param>
/// <returns></returns>
public override ILValue? LoadIndirect(DmdType type, LoadValueType loadValueType) {
int pointerSize = type.AppDomain.Runtime.PointerSize;
switch (loadValueType) {
case LoadValueType.I:
if (pointerSize == 4) {
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return null;
return ConstantNativeIntILValue.Create32(type.AppDomain, BitConverter.ToInt32(data, (int)offset));
}
else {
Debug.Assert(pointerSize == 8);
if (offset + 8 - 1 < offset || (ulong)offset + 8 - 1 >= (ulong)data.Length)
return null;
return ConstantNativeIntILValue.Create64(type.AppDomain, BitConverter.ToInt64(data, (int)offset));
}
case LoadValueType.I1:
if ((ulong)offset >= (ulong)data.Length)
return null;
return new ConstantInt32ILValue(type.AppDomain.System_SByte, (sbyte)data[(int)offset]);
case LoadValueType.I2:
if (offset + 2 - 1 < offset || (ulong)offset + 2 - 1 >= (ulong)data.Length)
return null;
return new ConstantInt32ILValue(type.AppDomain.System_Int16, BitConverter.ToInt16(data, (int)offset));
case LoadValueType.I4:
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return null;
return new ConstantInt32ILValue(type.AppDomain.System_Int32, BitConverter.ToInt32(data, (int)offset));
case LoadValueType.I8:
if (offset + 8 - 1 < offset || (ulong)offset + 8 - 1 >= (ulong)data.Length)
return null;
return new ConstantInt64ILValue(type.AppDomain.System_Int64, BitConverter.ToInt64(data, (int)offset));
case LoadValueType.R4:
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return null;
return new ConstantFloatILValue(type.AppDomain.System_Single, BitConverter.ToSingle(data, (int)offset));
case LoadValueType.R8:
if (offset + 8 - 1 < offset || (ulong)offset + 8 - 1 >= (ulong)data.Length)
return null;
return new ConstantFloatILValue(type.AppDomain.System_Double, BitConverter.ToDouble(data, (int)offset));
case LoadValueType.Ref:
return null;
case LoadValueType.U1:
if ((ulong)offset >= (ulong)data.Length)
return null;
return new ConstantInt32ILValue(type.AppDomain.System_Byte, data[(int)offset]);
case LoadValueType.U2:
if (offset + 2 - 1 < offset || (ulong)offset + 2 - 1 >= (ulong)data.Length)
return null;
return new ConstantInt32ILValue(type.AppDomain.System_UInt16, BitConverter.ToUInt16(data, (int)offset));
case LoadValueType.U4:
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return null;
return new ConstantInt32ILValue(type.AppDomain.System_UInt32, BitConverter.ToInt32(data, (int)offset));
default:
return null;
}
}
/// <summary>
/// Stores a value. Returns false if it's not supported.
/// </summary>
/// <param name="type">Type</param>
/// <param name="loadValueType">Type of value to store</param>
/// <param name="value">Value</param>
/// <returns></returns>
public override bool StoreIndirect(DmdType type, LoadValueType loadValueType, ILValue value) {
int pointerSize = type.AppDomain.Runtime.PointerSize;
long v;
double d;
switch (loadValueType) {
case LoadValueType.I:
if (pointerSize == 4) {
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, pointerSize, out v))
return false;
WriteInt32(data, (int)offset, v);
return true;
}
else {
Debug.Assert(pointerSize == 8);
if (offset + 8 - 1 < offset || (ulong)offset + 8 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, pointerSize, out v))
return false;
WriteInt64(data, (int)offset, v);
return true;
}
case LoadValueType.I1:
case LoadValueType.U1:
if ((ulong)offset >= (ulong)data.Length)
return false;
if (!GetValue(value, pointerSize, out v))
return false;
WriteInt8(data, (int)offset, v);
return true;
case LoadValueType.I2:
case LoadValueType.U2:
if (offset + 2 - 1 < offset || (ulong)offset + 2 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, pointerSize, out v))
return false;
WriteInt16(data, (int)offset, v);
return true;
case LoadValueType.I4:
case LoadValueType.U4:
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, pointerSize, out v))
return false;
WriteInt32(data, (int)offset, v);
return true;
case LoadValueType.I8:
if (offset + 8 - 1 < offset || (ulong)offset + 8 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, pointerSize, out v))
return false;
WriteInt64(data, (int)offset, v);
return true;
case LoadValueType.R4:
if (offset + 4 - 1 < offset || (ulong)offset + 4 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, out d))
return false;
WriteSingle(data, (int)offset, (float)d);
return true;
case LoadValueType.R8:
if (offset + 8 - 1 < offset || (ulong)offset + 8 - 1 >= (ulong)data.Length)
return false;
if (!GetValue(value, out d))
return false;
WriteDouble(data, (int)offset, d);
return true;
case LoadValueType.Ref:
return false;
default:
return false;
}
}
static bool GetValue(ILValue value, int pointerSize, out long result) {
if (value is ConstantInt32ILValue c32) {
result = c32.Value;
return true;
}
else if (value is ConstantInt64ILValue c64) {
result = c64.Value;
return true;
}
else if (value is ConstantNativeIntILValue cni) {
result = pointerSize == 4 ? cni.Value32 : cni.Value64;
return true;
}
result = 0;
return false;
}
static bool GetValue(ILValue value, out double result) {
if (value is ConstantFloatILValue f) {
result = f.Value;
return true;
}
result = 0;
return false;
}
static void WriteInt8(byte[] data, int offset, long value) {
data[offset] = (byte)value;
}
static void WriteInt16(byte[] data, int offset, long value) {
data[offset++] = (byte)value;
data[offset] = (byte)(value >> 8);
}
static void WriteInt32(byte[] data, int offset, long value) {
data[offset++] = (byte)value;
data[offset++] = (byte)(value >> 8);
data[offset++] = (byte)(value >> 16);
data[offset] = (byte)(value >> 24);
}
static void WriteInt64(byte[] data, int offset, long value) {
data[offset++] = (byte)value;
data[offset++] = (byte)(value >> 8);
data[offset++] = (byte)(value >> 16);
data[offset++] = (byte)(value >> 24);
data[offset++] = (byte)(value >> 32);
data[offset++] = (byte)(value >> 40);
data[offset++] = (byte)(value >> 48);
data[offset] = (byte)(value >> 56);
}
static void WriteSingle(byte[] data, int offset, float value) {
var b = BitConverter.GetBytes((float)value);
for (int i = 0; i < b.Length; i++)
data[offset + i] = b[i];
}
static void WriteDouble(byte[] data, int offset, double value) {
var b = BitConverter.GetBytes(value);
for (int i = 0; i < b.Length; i++)
data[offset + i] = b[i];
}
/// <summary>
/// Initializes memory or returns false if it's not supported
/// </summary>
/// <param name="value">Value to write</param>
/// <param name="size">Size of data</param>
/// <returns></returns>
public override bool InitializeMemory(byte value, long size) {
if (offset + size < offset || (ulong)(offset + size) > (ulong)data.Length)
return false;
int size2 = (int)size;
int o = (int)offset;
var d = data;
for (int i = 0; i < size2; i++)
d[i + o] = value;
return true;
}
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type { get; }
}
/// <summary>
/// Managed pointer
/// </summary>
public abstract class ByRefILValue : ILValue {
/// <summary>
/// Always returns <see cref="ILValueKind.ByRef"/>
/// </summary>
public sealed override ILValueKind Kind => ILValueKind.ByRef;
}
/// <summary>
/// A reference type or a value type
/// </summary>
public abstract class TypeILValue : ILValue {
/// <summary>
/// Always returns <see cref="ILValueKind.Type"/>
/// </summary>
public sealed override ILValueKind Kind => ILValueKind.Type;
}
/// <summary>
/// A null reference
/// </summary>
public class NullObjectRefILValue : TypeILValue {
/// <summary>
/// Returns true since it's a null value
/// </summary>
public sealed override bool IsNull => true;
/// <summary>
/// Constructor
/// </summary>
public NullObjectRefILValue() { }
/// <summary>
/// Gets the type of the value
/// </summary>
public override DmdType? Type => null;
}
}