/*
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 .
*/
using System;
using System.Diagnostics;
using dnSpy.Debugger.DotNet.Metadata;
namespace dnSpy.Debugger.DotNet.Interpreter {
///
/// IL stack value kind
///
public enum ILValueKind {
///
/// 32-bit integer. 1-byte and 2-byte integers are sign/zero extended to 32 bits. Booleans and chars are zero extended.
///
Int32,
///
/// 64-bit integer
///
Int64,
///
/// 64-bit float (32-bit floats are extended to 64-bit floats)
///
Float,
///
/// Unmanaged pointer or native int
///
NativeInt,
///
/// Managed pointer
///
ByRef,
///
/// Any other reference type or value type
///
Type,
}
///
/// A value that can be stored on the IL stack
///
public abstract class ILValue {
///
/// Gets the stack value kind
///
public abstract ILValueKind Kind { get; }
///
/// true if this is a null value
///
public virtual bool IsNull => false;
///
/// 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.
///
///
public virtual ILValue Clone() => this;
///
/// Gets the type of the value or null if it's unknown, eg. it's a null reference
///
public abstract DmdType? Type { get; }
///
/// Loads an instance field. Returns null if it's not supported.
///
/// Field
///
public virtual ILValue? LoadField(DmdFieldInfo field) => null;
///
/// Stores a value in an instance field. Returns false if it's not supported.
///
/// Field
/// Value
///
public virtual bool StoreField(DmdFieldInfo field, ILValue value) => false;
///
/// Returns the address of an instance field. Returns null if it's not supported.
///
/// Field
///
public virtual ILValue? LoadFieldAddress(DmdFieldInfo field) => null;
///
/// Calls an instance method. The method could be a CLR-generated method, eg. an array Address() method, see .
/// Returns false if it's not supported.
///
/// true if this is a virtual call, false if it's a non-virtual call
/// Method
/// Arguments. The hidden 'this' value isn't included, it's this instance.
/// Updated with the return value. Can be null if the return type is
///
public virtual bool Call(bool isCallvirt, DmdMethodBase method, ILValue[] arguments, out ILValue? returnValue) {
returnValue = null;
return false;
}
///
/// Calls an instance method or returns false on failure
///
/// Method address
/// Method signature
/// Method arguments
/// Return value. It's ignored if the method returns
///
public virtual bool CallIndirect(DmdMethodSignature methodSig, ILValue methodAddress, ILValue[] arguments, out ILValue? returnValue) {
returnValue = null;
return false;
}
///
/// Boxes this instance. Returns null if it's not supported.
///
/// Target type
///
public virtual ILValue? Box(DmdType type) => null;
///
/// Unboxes this instance. Returns null if it's not supported.
///
/// Target type
///
public virtual ILValue? UnboxAny(DmdType type) => null;
///
/// Unboxes this instance. Returns null if it's not supported.
///
/// Target type
///
public virtual ILValue? Unbox(DmdType type) => null;
///
/// Loads an SZ array element. Returns null if it's not supported.
///
/// Type of value to load
/// Array index
/// Optional element type (eg. it's the ldelem instruction)
///
public virtual ILValue? LoadSZArrayElement(LoadValueType loadValueType, long index, DmdType elementType) => null;
///
/// Writes an SZ array element. Returns false if it's not supported.
///
/// Type of value to store
/// Index
/// Value
/// Optional element type (eg. it's the stelem instruction)
///
public virtual bool StoreSZArrayElement(LoadValueType loadValueType, long index, ILValue value, DmdType elementType) => false;
///
/// Loads the address of an SZ array element. Returns null if it's not supported.
///
/// Index
/// Element type
///
public virtual ILValue? LoadSZArrayElementAddress(long index, DmdType elementType) => null;
///
/// Gets the length of an SZ array. Returns false if it's not supported.
///
/// Updated with the length of the array
///
public virtual bool GetSZArrayLength(out long length) {
length = 0;
return false;
}
///
/// Loads a value. Returns null if it's not supported.
///
/// Type
/// Type of value to load
///
public virtual ILValue? LoadIndirect(DmdType type, LoadValueType loadValueType) => null;
///
/// Stores a value. Returns false if it's not supported.
///
/// Type
/// Type of value to store
/// Value
///
public virtual bool StoreIndirect(DmdType type, LoadValueType loadValueType, ILValue value) => false;
///
/// Clears the memory. Returns false if it's not supported.
///
/// Type
///
public virtual bool InitializeObject(DmdType type) => false;
///
/// Copies to this value. Returns false if it's not supported.
///
/// Type
/// Source value
///
public virtual bool CopyObject(DmdType type, ILValue source) => false;
///
/// Initializes memory. Returns false if it's not supported.
///
/// Value to write
/// Size of data
///
public virtual bool InitializeMemory(byte value, long size) => false;
///
/// Copies memory to this value. Returns false if it's not supported.
///
/// Source value
/// Size in bytes
///
public virtual bool CopyMemory(ILValue source, long size) => false;
///
/// Adds a constant to a copy of this value and returns the result. Returns null if it's not supported.
///
/// Opcode kind
/// Value to add
/// Size of a pointer in bytes
///
public virtual ILValue? Add(AddOpCodeKind kind, long value, int pointerSize) => null;
///
/// Subtracts a constant from a copy of this value and returns the result. Returns null if it's not supported.
///
/// Opcode kind
/// Value to subtract
/// Size of a pointer in bytes
///
public virtual ILValue? Sub(SubOpCodeKind kind, long value, int pointerSize) => null;
///
/// Subtracts from a copy of this value and returns the result. Returns null if it's not supported.
///
/// Opcode kind
/// Value to subtract
/// Size of a pointer in bytes
///
public virtual ILValue? Sub(SubOpCodeKind kind, ILValue value, int pointerSize) => null;
///
/// Converts this value to a new value. Returns null if it's not supported.
///
/// Opcode kind
///
public virtual ILValue? Conv(ConvOpCodeKind kind) => null;
}
///
/// Add opcode kind
///
public enum AddOpCodeKind {
///
/// Normal addition
///
Add,
///
/// Signed addition with an overflow check
///
Add_Ovf,
///
/// Unsigned addition with an overflow check
///
Add_Ovf_Un,
}
///
/// Sub opcode kind
///
public enum SubOpCodeKind {
///
/// Normal subtraction
///
Sub,
///
/// Signed subtraction with an overflow check
///
Sub_Ovf,
///
/// Unsigned subtraction with an overflow check
///
Sub_Ovf_Un,
}
///
/// Convert opcode kind
///
public enum ConvOpCodeKind {
///
/// Convert to a
///
Conv_I,
///
/// Convert to a , signed, overflow check
///
Conv_Ovf_I,
///
/// Convert to a , unsigned, overflow check
///
Conv_Ovf_I_Un,
///
/// Convert to a
///
Conv_U,
///
/// Convert to a , signed, overflow check
///
Conv_Ovf_U,
///
/// Convert to a , unsigned, overflow check
///
Conv_Ovf_U_Un,
}
///
/// 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.
///
public class ConstantInt32ILValue : ILValue {
///
/// Always returns
///
public sealed override ILValueKind Kind => ILValueKind.Int32;
///
/// Gets the value
///
public int Value { get; }
///
/// Gets the value as a
///
public uint UnsignedValue => (uint)Value;
internal ulong UnsignedValue64 => (ulong)(long)Value;
///
/// Constructor
///
/// AppDomain
/// Value
public ConstantInt32ILValue(DmdAppDomain appDomain, int value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Int32;
Value = value;
}
///
/// Constructor
///
/// Type, eg.
/// Value
public ConstantInt32ILValue(DmdType type, int value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
Value = value;
}
///
/// Gets the type of the value
///
public override DmdType? Type { get; }
///
/// ToString()
///
///
public override string ToString() => "0x" + Value.ToString("X8");
}
///
/// 64-bit integer
///
public class ConstantInt64ILValue : ILValue {
///
/// Always returns
///
public sealed override ILValueKind Kind => ILValueKind.Int64;
///
/// Gets the value
///
public long Value { get; }
///
/// Gets the value as a
///
public ulong UnsignedValue => (ulong)Value;
///
/// Constructor
///
/// AppDomain
/// Value
public ConstantInt64ILValue(DmdAppDomain appDomain, long value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Int64;
Value = value;
}
///
/// Constructor
///
/// Type, eg.
/// Value
public ConstantInt64ILValue(DmdType type, long value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
Value = value;
}
///
/// Gets the type of the value
///
public override DmdType? Type { get; }
///
/// ToString()
///
///
public override string ToString() => "0x" + Value.ToString("X16");
}
///
/// 64-bit floating point value (32-bit floating point numbers are extended to 64 bits)
///
public class ConstantFloatILValue : ILValue {
///
/// Always returns
///
public sealed override ILValueKind Kind => ILValueKind.Float;
///
/// Gets the value
///
public double Value { get; }
///
/// Constructor
///
/// AppDomain
/// Value
public ConstantFloatILValue(DmdAppDomain appDomain, double value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_Double;
Value = value;
}
///
/// Constructor
///
/// Type, eg.
/// Value
public ConstantFloatILValue(DmdType type, double value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
Value = value;
}
///
/// Gets the type of the value
///
public override DmdType? Type { get; }
///
/// ToString()
///
///
public override string ToString() => Value.ToString();
}
///
/// native integer or unmanaged pointer
///
public abstract class NativeIntILValue : ILValue {
///
/// Always returns
///
public sealed override ILValueKind Kind => ILValueKind.NativeInt;
}
///
/// native integer or unmanaged pointer
///
public class ConstantNativeIntILValue : NativeIntILValue {
readonly long value;
///
/// Gets the value as a
///
public int Value32 => (int)value;
///
/// Gets the value as a
///
public long Value64 => value;
///
/// Gets the value as a
///
public uint UnsignedValue32 => (uint)value;
///
/// Gets the value as a
///
public ulong UnsignedValue64 => (ulong)value;
///
/// Creates a 32-bit native int
///
/// AppDomain
/// Value
///
public static ConstantNativeIntILValue Create32(DmdAppDomain appDomain, int value) => new ConstantNativeIntILValue(appDomain, value);
///
/// Creates a 64-bit native int
///
/// AppDomain
/// Value
///
public static ConstantNativeIntILValue Create64(DmdAppDomain appDomain, long value) => new ConstantNativeIntILValue(appDomain, value);
///
/// Creates a 32-bit native int
///
/// Type, eg.
/// Value
///
public static ConstantNativeIntILValue Create32(DmdType type, int value) => new ConstantNativeIntILValue(type, value);
///
/// Creates a 64-bit native int
///
/// Type, eg.
/// Value
///
public static ConstantNativeIntILValue Create64(DmdType type, long value) => new ConstantNativeIntILValue(type, value);
///
/// Constructor
///
/// AppDomain
/// Value
protected ConstantNativeIntILValue(DmdAppDomain appDomain, int value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_IntPtr;
this.value = value;
}
///
/// Constructor
///
/// AppDomain
/// Value
protected ConstantNativeIntILValue(DmdAppDomain appDomain, long value) {
if (appDomain is null)
throw new ArgumentNullException(nameof(appDomain));
Type = appDomain.System_IntPtr;
this.value = value;
}
///
/// Constructor
///
/// Type, eg.
/// Value
protected ConstantNativeIntILValue(DmdType type, int value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
this.value = value;
}
///
/// Constructor
///
/// Type, eg.
/// Value
protected ConstantNativeIntILValue(DmdType type, long value) {
Type = type ?? throw new ArgumentNullException(nameof(type));
this.value = value;
}
///
/// Gets the type of the value
///
public override DmdType? Type { get; }
///
/// ToString()
///
///
public override string ToString() => "0x" + value.ToString("X");
}
///
/// Function pointer, created by the ldftn/ldvirtftn instructions
///
public sealed class FunctionPointerILValue : NativeIntILValue {
///
/// true if it was created by a ldvirtftn instruction, false it was created by a ldftn instruction
///
public bool IsVirtual => VirtualThisObject is not null;
///
/// Gets the this value if and only if this was created by a ldvirtftn instruction, otherwise it's null
///
public ILValue? VirtualThisObject { get; }
///
/// Gets the method
///
public DmdMethodBase Method { get; }
///
/// Constructor (used by ldftn instruction)
///
/// Method
public FunctionPointerILValue(DmdMethodBase method) {
Method = method ?? throw new ArgumentNullException(nameof(method));
Type = method.AppDomain.System_Void.MakePointerType();
}
///
/// Constructor (used by ldvirtftn instruction)
///
/// Method
/// This object
public FunctionPointerILValue(DmdMethodBase method, ILValue thisValue) {
Method = method;
Type = method.AppDomain.System_Void.MakePointerType();
VirtualThisObject = thisValue;
}
///
/// Gets the type of the value
///
public override DmdType? Type { get; }
}
///
/// Pointer to a block of memory. Used by eg. localloc
///
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;
///
/// Constructor
///
/// AppDomain
/// Size of memory
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;
}
///
/// Adds a constant to a copy of this value and returns the result. Returns null if it's not supported.
///
/// Opcode kind
/// Value to add
/// Size of a pointer in bytes
///
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();
}
}
///
/// Subtracts a constant from a copy of this value and returns the result. Returns null if it's not supported.
///
/// Opcode kind
/// Value to subtract
/// Size of a pointer in bytes
///
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();
}
}
///
/// Loads a value. Returns null if it's not supported.
///
/// Type
/// Type of value to load
///
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;
}
}
///
/// Stores a value. Returns false if it's not supported.
///
/// Type
/// Type of value to store
/// Value
///
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];
}
///
/// Initializes memory or returns false if it's not supported
///
/// Value to write
/// Size of data
///
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;
}
///
/// Gets the type of the value
///
public override DmdType? Type { get; }
}
///
/// Managed pointer
///
public abstract class ByRefILValue : ILValue {
///
/// Always returns
///
public sealed override ILValueKind Kind => ILValueKind.ByRef;
}
///
/// A reference type or a value type
///
public abstract class TypeILValue : ILValue {
///
/// Always returns
///
public sealed override ILValueKind Kind => ILValueKind.Type;
}
///
/// A null reference
///
public class NullObjectRefILValue : TypeILValue {
///
/// Returns true since it's a null value
///
public sealed override bool IsNull => true;
///
/// Constructor
///
public NullObjectRefILValue() { }
///
/// Gets the type of the value
///
public override DmdType? Type => null;
}
}