Last active
February 3, 2017 10:08
-
-
Save leculver/2252976329e24a433da4 to your computer and use it in GitHub Desktop.
Proposal for ClrObject implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ClrObject | |
{ | |
const ulong AddressMask = 0x3FFFFFFFFFFFFFFF; | |
const ulong InteriorBit = 0x8000000000000000; | |
const ulong ExactTypeBit = 0x4000000000000000; | |
// bit0: interior | |
// bit1: exact type | |
// remaining bits: the address | |
private ulong _address; | |
private ClrType _type; | |
public ClrObject(ulong address, ClrType type, bool isTypeExact, bool interior = false) | |
{ | |
if (type == null) | |
throw new ArgumentNullException(nameof(type)); | |
_address = address; | |
_type = type; | |
if (isTypeExact) | |
_address |= ExactTypeBit; | |
if (interior) | |
_address |= InteriorBit; | |
} | |
public ulong Address { get { return _address & AddressMask; } } | |
public ClrType Type | |
{ | |
get | |
{ | |
if (!IsTypeExact) | |
{ | |
// The variable 'type' may be null if the runtime is in an inconsistent state. | |
ClrType type = _type.Heap.GetObjectType(Address); | |
if (type != null) | |
_type = type; | |
_address |= ExactTypeBit; | |
} | |
return _type; | |
} | |
} | |
public ClrHeap Heap { get { return _type.Heap; } } | |
public ulong Size { get { return _type.GetSize(Address); } } | |
public bool IsValid { get { return _type is InvalidType; } } | |
public bool IsNull { get { return Address == 0; } } | |
public bool IsString { get { return _type.IsString; } } | |
public bool IsFree { get { return _type.IsFree; } } | |
public bool IsArray { get { return _type.IsArray; } } | |
public bool IsInterior | |
{ | |
get | |
{ | |
return (_address & InteriorBit) == InteriorBit; | |
} | |
} | |
public dynamic Dynamic { get { return null; } } //todo | |
#region Field Readers | |
public ClrObject GetObjectField(string name) | |
{ | |
// Check the type we currently have first. If the field isn't there and we do not | |
// have an exact type, we will resolve the exact type and check again. This should | |
// work correctly the first time in most cases, and resolving/rechecking the exact | |
// type should be the exceptional case. | |
ClrInstanceField field = _type.GetFieldByName(name); | |
if (field == null && !IsTypeExact) | |
field = Type.GetFieldByName(name); | |
if (field == null) | |
throw new InvalidOperationException(string.Format("Field '{0}' not found in type '{1}'.", name, _type.Name)); | |
ulong address = 0; | |
if (!IsNull) | |
{ | |
Debug.Assert(field.Type.IsObjectReference); | |
address = field.GetAddress(Address, IsInterior); | |
Type.Heap.ReadPointer(address, out address); // Assigns 0 on failure. | |
} | |
// The actual object type might be a subclass of field.Type, so isExact=false. | |
return new ClrObject(address, field.Type, isTypeExact: false); | |
} | |
// If this object is null, then GetStructField will successfully return a 0 | |
// address value. That is: obj.GetStructField("foo").IsNull == true. | |
public ClrObject GetStructField(string name) | |
{ | |
ClrInstanceField field = _type.GetFieldByName(name); | |
if (field == null) | |
throw new InvalidOperationException(string.Format("Field '{0}' not found in type '{1}'.", name, _type.Name)); | |
Debug.Assert(field.ElementType == ClrElementType.Struct); | |
ulong address = Address; | |
if (address != 0) | |
address = field.GetAddress(address, IsInterior); | |
return new ClrObject(address, field.Type, isTypeExact: true, interior: true); | |
} | |
// Primitive values will be implemented similar to this. | |
public int GetIntField(string name) | |
{ | |
ClrInstanceField field = _type.GetFieldByName(name); | |
if (field == null) | |
throw new InvalidOperationException(string.Format("Field '{0}' not found in type '{1}'.", name, _type.Name)); | |
Debug.Assert(field.ElementType == ClrElementType.Int32); | |
ulong address = field.GetAddress(Address, IsInterior); | |
// This is not final. I will need to move the "Read" helpers to a better place to avoid this ugly cast. | |
int result; | |
((RuntimeBase)Type.Heap.Runtime).ReadDword(address, out result); | |
return result; | |
} | |
#endregion | |
#region Conversion | |
// Note that only primitive and array converters will go in the class itself. | |
// All "complex" converters (such as AsDateTime, AsGuid, etc) will be in its | |
// own namespace: Microsoft.Diagnostics.Runtime.ObjectConverters | |
// Primitive values will be implemented similar to this. | |
public int AsInt32() | |
{ | |
Debug.Assert(_type.ElementType == ClrElementType.Int32); | |
// TODO: What should the correct behavior be here? | |
if (IsNull) | |
throw new InvalidOperationException("Cannot convert null value to Int32."); | |
ulong address = IsInterior ? Address : Address + (uint)IntPtr.Size; | |
// This is not final. I will need to move the "Read" helpers to a better place to avoid this ugly cast. | |
int result; | |
((RuntimeBase)Type.Heap.Runtime).ReadDword(address, out result); | |
return result; | |
} | |
public ClrObject AsBaseType() | |
{ | |
ClrType baseType = Type.BaseType; | |
if (baseType == null) | |
throw new InvalidOperationException(); | |
ClrObject result = new ClrObject(); | |
result._address = _address; | |
result._type = baseType; | |
return result; | |
} | |
public ClrObjectArray AsObjectArray() | |
{ | |
if (!_type.IsArray || !_type.ComponentType.IsObjectReference) | |
throw new InvalidOperationException("Object is not an object array."); | |
return new ClrObjectArray(Address, _type); | |
} | |
public ClrArray<T> AsPrimitiveArray<T>() where T : struct | |
{ | |
if (!_type.IsArray) | |
throw new InvalidOperationException("Object is not an object array."); | |
return new ClrArray<T>(Address, _type); | |
} | |
public ClrStructArray AsStructArray() | |
{ | |
if (!_type.IsArray || _type.ComponentType.ElementType != ClrElementType.Struct) | |
throw new InvalidOperationException("Object is not an array of structs."); | |
return new ClrStructArray(Address, _type); | |
} | |
#endregion | |
private bool IsTypeExact | |
{ | |
get | |
{ | |
return ((_address & ExactTypeBit) == ExactTypeBit) || !_type.IsObjectReference; | |
} | |
} | |
} | |
// All "complex" ClrObject coverters (such as AsDateTime, AsGuid, etc) will go in its own namespace, | |
// not in the class itself. | |
namespace ObjectConverters | |
{ | |
// All non-primitive types will | |
static class ClrObjectConverters | |
{ | |
public static DateTime AsDateTime(this ClrObject self) | |
{ | |
if (self.Type.Name != "System.DateTime") | |
throw new InvalidOperationException(); | |
return new DateTime(); // TODO: Conversion | |
} | |
} | |
} | |
// These three array types will inherit from a common base, so ignore the copy/paste. | |
class ClrObjectArray | |
{ | |
private ulong _address; | |
private ClrType _type; | |
private int? _length; | |
public ClrObjectArray(ulong address, ClrType type) | |
{ | |
_address = address; | |
_type = type; | |
} | |
public ulong Address { get { return _address; } } | |
public ClrType Type { get { return _type; } } | |
public ClrHeap Heap { get { return _type.Heap; } } | |
public int Length | |
{ | |
get | |
{ | |
if (!_length.HasValue) | |
_length = _type.GetArrayLength(_address); | |
return _length.Value; | |
} | |
} | |
public ClrObject this[int i] | |
{ | |
get | |
{ | |
Debug.Assert(i < Length); | |
if (_address == 0) | |
return new ClrObject(0, _type.ComponentType, isTypeExact: true); | |
ulong address = _type.GetArrayElementAddress(_address, i); | |
_type.Heap.ReadPointer(address, out address); | |
return new ClrObject(address, _type.ComponentType, isTypeExact: false); | |
} | |
} | |
// Also method for enumerating all values. | |
} | |
class ClrStructArray | |
{ | |
private ulong _address; | |
private ClrType _type; | |
private int? _length; | |
public ClrStructArray(ulong address, ClrType type) | |
{ | |
_address = address; | |
_type = type; | |
} | |
public ulong Address { get { return _address; } } | |
public ClrType Type { get { return _type; } } | |
public ClrHeap Heap { get { return _type.Heap; } } | |
public bool IsNull { get { return _address == 0; } } | |
public int Length | |
{ | |
get | |
{ | |
if (!_length.HasValue) | |
_length = _type.GetArrayLength(_address); | |
return _length.Value; | |
} | |
} | |
public ClrObject this[int i] | |
{ | |
get | |
{ | |
Debug.Assert(i < Length); | |
if (_address == 0) | |
return new ClrObject(0, _type.ComponentType, isTypeExact: true); | |
ulong address = _type.GetArrayElementAddress(_address, i); | |
return new ClrObject(address, _type.ComponentType, isTypeExact: true, interior: true); | |
} | |
} | |
// Also method for enumerating all values. | |
} | |
class ClrArray<T> where T : struct | |
{ | |
private ulong _address; | |
private ClrType _type; | |
private int? _length; | |
public ClrArray(ulong address, ClrType type) | |
{ | |
// TODO: Assert that type.ComponentType.ElementType is compatible with T. | |
_address = address; | |
_type = type; | |
} | |
public ulong Address { get { return _address; } } | |
public ClrType Type { get { return _type; } } | |
public ClrHeap Heap { get { return _type.Heap; } } | |
public bool IsNull { get { return _address == 0; } } | |
public int Length | |
{ | |
get | |
{ | |
if (!_length.HasValue) | |
_length = _type.GetArrayLength(_address); | |
return _length.Value; | |
} | |
} | |
public T this[int i] | |
{ | |
get | |
{ | |
Debug.Assert(i < Length); | |
// TODO: Should this throw? | |
if (_address == 0) | |
return default(T); | |
return (T)_type.GetArrayElementValue(_address, i); | |
} | |
} | |
// Also method for enumerating all values. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment