File: Contracts\Object_1.cs
Web Access
Project: src\src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal readonly struct Object_1 : IObject
{
    private readonly Target _target;
    private readonly ulong _methodTableOffset;
    private readonly byte _objectToMethodTableUnmask;
    private readonly TargetPointer _stringMethodTable;
    private readonly uint _syncBlockIsHashOrSyncBlockIndex;
    private readonly uint _syncBlockIsHashCode;
    private readonly uint _syncBlockHashCodeMask;
    private readonly uint _syncBlockIndexMask;

    internal Object_1(Target target)
    {
        _target = target;
        _methodTableOffset = (ulong)target.GetTypeInfo(DataType.Object).Fields["m_pMethTab"].Offset;
        _objectToMethodTableUnmask = target.ReadGlobal<byte>(Constants.Globals.ObjectToMethodTableUnmask);
        _stringMethodTable = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.StringMethodTable));
        _syncBlockIsHashOrSyncBlockIndex = target.ReadGlobal<uint>(Constants.Globals.SyncBlockIsHashOrSyncBlockIndex);
        _syncBlockIsHashCode = target.ReadGlobal<uint>(Constants.Globals.SyncBlockIsHashCode);
        _syncBlockHashCodeMask = target.ReadGlobal<uint>(Constants.Globals.SyncBlockHashCodeMask);
        _syncBlockIndexMask = target.ReadGlobal<uint>(Constants.Globals.SyncBlockIndexMask);
    }

    public TargetPointer GetMethodTableAddress(TargetPointer address)
    {
        TargetPointer mt = _target.ReadPointer(address + _methodTableOffset);
        return mt.Value & (ulong)~_objectToMethodTableUnmask;
    }

    string IObject.GetStringValue(TargetPointer address)
    {
        TargetPointer mt = GetMethodTableAddress(address);
        if (mt == TargetPointer.Null)
            throw new ArgumentException("Address represents a set-free object");
        if (mt != _stringMethodTable)
            throw new ArgumentException("Address does not represent a string object", nameof(address));

        Data.String str = _target.ProcessedData.GetOrAdd<Data.String>(address);
        if (str.StringLength == 0)
            return string.Empty;

        Span<byte> span = stackalloc byte[(int)str.StringLength * sizeof(char)];
        _target.ReadBuffer(str.FirstChar, span);
        return new string(MemoryMarshal.Cast<byte, char>(span));
    }

    public TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds)
    {
        TargetPointer mt = GetMethodTableAddress(address);
        if (mt == TargetPointer.Null)
            throw new ArgumentException("Address represents a set-free object");
        Contracts.IRuntimeTypeSystem typeSystemContract = _target.Contracts.RuntimeTypeSystem;
        TypeHandle typeHandle = typeSystemContract.GetTypeHandle(mt);
        uint rank;
        if (!typeSystemContract.IsArray(typeHandle, out rank))
            throw new ArgumentException("Address does not represent an array object", nameof(address));

        Data.Array array = _target.ProcessedData.GetOrAdd<Data.Array>(address);
        count = array.NumComponents;

        Target.TypeInfo arrayTypeInfo = _target.GetTypeInfo(DataType.Array);
        CorElementType corType = typeSystemContract.GetSignatureCorElementType(typeHandle);
        Debug.Assert(corType is CorElementType.Array or CorElementType.SzArray);
        if (corType == CorElementType.Array)
        {
            // Multi-dimensional - has bounds as part of the array object
            // The object is allocated with:
            //   << fields that are part of the array type info >>
            //   int32_t bounds[rank];
            //   int32_t lowerBounds[rank];
            boundsStart = address + (ulong)arrayTypeInfo.Size!;
            lowerBounds = boundsStart + (rank * sizeof(int));
        }
        else
        {
            // Single-dimensional, zero-based - doesn't have bounds
            boundsStart = address + (ulong)arrayTypeInfo.Fields[Constants.FieldNames.Array.NumComponents].Offset;
            lowerBounds = _target.ReadGlobalPointer(Constants.Globals.ArrayBoundsZero);
        }

        // Sync block is before `this` pointer, so substract the object header size
        ulong dataOffset = typeSystemContract.GetBaseSize(typeHandle) - _target.GetTypeInfo(DataType.ObjectHeader).Size!.Value;
        return address + dataOffset;
    }

    public bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw, out TargetPointer ccf)
    {
        rcw = TargetPointer.Null;
        ccw = TargetPointer.Null;
        ccf = TargetPointer.Null;

        TargetPointer syncBlockPtr = GetSyncBlockAddress(address);
        if (syncBlockPtr == TargetPointer.Null)
            return false;

        return _target.Contracts.SyncBlock.GetBuiltInComData(syncBlockPtr, out rcw, out ccw, out ccf);
    }

    int IObject.TryGetHashCode(TargetPointer address)
    {
        ulong objectHeaderSize = _target.GetTypeInfo(DataType.ObjectHeader).Size!.Value;
        Data.ObjectHeader header = _target.ProcessedData.GetOrAdd<Data.ObjectHeader>(address - objectHeaderSize);
        uint syncBlockValue = header.SyncBlockValue;

        if ((syncBlockValue & _syncBlockIsHashOrSyncBlockIndex) != 0)
        {
            if ((syncBlockValue & _syncBlockIsHashCode) != 0)
            {
                return (int)(syncBlockValue & _syncBlockHashCodeMask);
            }
            else
            {
                TargetPointer syncBlockPtr = GetSyncBlockAddress(address);
                if (syncBlockPtr != TargetPointer.Null)
                {
                    Data.SyncBlock syncBlock = _target.ProcessedData.GetOrAdd<Data.SyncBlock>(syncBlockPtr);
                    return (int)syncBlock.HashCode;
                }
            }
        }

        return 0;
    }

    public TargetPointer GetSyncBlockAddress(TargetPointer address)
    {
        ulong objectHeaderSize = _target.GetTypeInfo(DataType.ObjectHeader).Size!.Value;
        Data.ObjectHeader header = _target.ProcessedData.GetOrAdd<Data.ObjectHeader>(address - objectHeaderSize);
        uint syncBlockValue = header.SyncBlockValue;

        // Check if the sync block value represents a sync block index (not a hash code)
        if ((syncBlockValue & (_syncBlockIsHashCode | _syncBlockIsHashOrSyncBlockIndex))
                != _syncBlockIsHashOrSyncBlockIndex)
            return TargetPointer.Null;

        uint index = syncBlockValue & _syncBlockIndexMask;
        return _target.Contracts.SyncBlock.GetSyncBlock(index);
    }
}