File: Internal\Runtime\CompilerHelpers\RuntimeInteropData.cs
Web Access
Project: src\src\runtime\src\coreclr\nativeaot\System.Private.CoreLib\src\System.Private.CoreLib.csproj (System.Private.CoreLib)
// 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;

using Internal.NativeFormat;
using Internal.Runtime.Augments;
using Internal.Runtime.TypeLoader;

namespace Internal.Runtime.CompilerHelpers
{
    internal static class RuntimeInteropData
    {
        public static uint GetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName)
        {
            if (TryGetStructData(structureTypeHandle, out _, out NativeParser entryParser))
            {
                uint mask = entryParser.GetUnsigned();
                if ((mask & InteropDataConstants.HasInvalidLayout) != 0)
                {
                    throw new ArgumentException(SR.Format(SR.Arg_CannotMarshal, Type.GetTypeFromHandle(structureTypeHandle)), nameof(structureTypeHandle));
                }

                if ((mask & InteropDataConstants.HasMarshallers) != 0)
                {
                    // skip the first 4 IntPtrs(3 stubs and size)
                    entryParser.SkipInteger();
                    entryParser.SkipInteger();
                    entryParser.SkipInteger();
                    entryParser.SkipInteger();
                }

                uint fieldCount = mask >> InteropDataConstants.FieldCountShift;
                for (uint index = 0; index < fieldCount; index++)
                {
                    string name = entryParser.GetString();
                    uint offset = entryParser.GetUnsigned();
                    if (name == fieldName)
                    {
                        return offset;
                    }
                }

                throw new ArgumentException(SR.Format(SR.Argument_OffsetOfFieldNotFound, Type.GetTypeFromHandle(structureTypeHandle)), nameof(fieldName));
            }

            throw new NotSupportedException(SR.Format(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structureTypeHandle)!));
        }

        public static unsafe int GetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle)
        {
            MethodTable* structureMT = structureTypeHandle.ToMethodTable();
            if (TryGetStructData(structureTypeHandle, out _, out NativeParser entryParser))
            {
                uint mask = entryParser.GetUnsigned();
                if ((mask & InteropDataConstants.HasInvalidLayout) != 0)
                {
                    throw new ArgumentException(SR.Format(SR.Arg_CannotMarshal, Type.GetTypeFromHandle(structureTypeHandle)), nameof(structureTypeHandle));
                }

                if ((mask & InteropDataConstants.HasMarshallers) != 0)
                {
                    return (int)entryParser.GetUnsigned();
                }

                // We expect a blittable value type at this point
                Debug.Assert(!structureMT->ContainsGCPointers);
                Debug.Assert(structureMT->IsValueType);
                return (int)structureMT->ValueTypeSize;
            }

            // No interop data.
            // If the type is an interface or a generic type, the reason is likely that.
            Type structureType = Type.GetTypeFromHandle(structureTypeHandle)!;
            if (structureMT->IsInterface || structureMT->IsGeneric)
            {
                throw new ArgumentException(SR.Format(SR.Arg_CannotMarshal, structureType));
            }

            // Otherwise assume we miss interop data for the type.
            throw new NotSupportedException(SR.Format(SR.StructMarshalling_MissingInteropData, structureType));
        }

        public static IntPtr GetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle)
        {
            GetMarshallersForStruct(structureTypeHandle, out _, out IntPtr unmarshal, out _);
            return unmarshal;
        }

        public static IntPtr GetStructMarshalStub(RuntimeTypeHandle structureTypeHandle)
        {
            GetMarshallersForStruct(structureTypeHandle, out IntPtr marshal, out _, out _);
            return marshal;
        }

        public static IntPtr GetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle)
        {
            GetMarshallersForStruct(structureTypeHandle, out _, out _, out IntPtr destroyStub);
            return destroyStub;
        }

        public static IntPtr GetForwardDelegateCreationStub(RuntimeTypeHandle delegateTypeHandle)
        {
            GetMarshallersForDelegate(delegateTypeHandle, out _, out _, out IntPtr delegateCreationStub);
            if (delegateCreationStub == IntPtr.Zero)
                throw new NotSupportedException(SR.Format(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle)));
            return delegateCreationStub;
        }

        public static IntPtr GetDelegateMarshallingStub(RuntimeTypeHandle delegateTypeHandle, bool openStaticDelegate)
        {
            GetMarshallersForDelegate(delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out _);
            IntPtr pStub = openStaticDelegate ? openStub : closedStub;
            if (pStub == IntPtr.Zero)
                throw new NotSupportedException(SR.Format(SR.DelegateMarshalling_MissingInteropData, Type.GetTypeFromHandle(delegateTypeHandle)));
            return pStub;
        }

        private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader)
        {
            byte* pBlob;
            uint cbBlob;

            if (RuntimeImports.RhFindBlob(module, (uint)blob, &pBlob, &cbBlob))
            {
                reader = new NativeReader(pBlob, cbBlob);
                return true;
            }

            reader = default(NativeReader);
            return false;
        }

        private static unsafe bool GetMarshallersForDelegate(RuntimeTypeHandle delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out IntPtr delegateCreationStub)
        {
            int delegateHashcode = delegateTypeHandle.GetHashCode();
            openStub = IntPtr.Zero;
            closedStub = IntPtr.Zero;
            delegateCreationStub = IntPtr.Zero;

            foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules())
            {
                NativeReader delegateMapReader;
                if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.DelegateMarshallingStubMap, out delegateMapReader))
                {
                    NativeParser delegateMapParser = new NativeParser(delegateMapReader, 0);
                    NativeHashtable delegateHashtable = new NativeHashtable(delegateMapParser);

                    ExternalReferencesTable externalReferences = default(ExternalReferencesTable);
                    externalReferences.InitializeCommonFixupsTable(module);

                    var lookup = delegateHashtable.Lookup(delegateHashcode);
                    NativeParser entryParser;
                    while (!(entryParser = lookup.GetNext()).IsNull)
                    {
                        RuntimeTypeHandle foundDelegateType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
                        if (foundDelegateType.Equals(delegateTypeHandle))
                        {
                            openStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned());
                            closedStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned());
                            delegateCreationStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned());
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        private static unsafe bool TryGetStructData(RuntimeTypeHandle structTypeHandle, out ExternalReferencesTable externalReferences, out NativeParser entryParser)
        {
            int structHashcode = structTypeHandle.GetHashCode();
            externalReferences = default(ExternalReferencesTable);
            entryParser = default(NativeParser);
            foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules())
            {
                NativeReader structMapReader;
                if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.StructMarshallingStubMap, out structMapReader))
                {
                    NativeParser structMapParser = new NativeParser(structMapReader, 0);
                    NativeHashtable structHashtable = new NativeHashtable(structMapParser);

                    externalReferences.InitializeCommonFixupsTable(module);

                    var lookup = structHashtable.Lookup(structHashcode);
                    while (!(entryParser = lookup.GetNext()).IsNull)
                    {
                        RuntimeTypeHandle foundStructType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
                        if (foundStructType.Equals(structTypeHandle))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        private static unsafe void GetMarshallersForStruct(RuntimeTypeHandle structTypeHandle, out IntPtr marshalStub, out IntPtr unmarshalStub, out IntPtr destroyStub)
        {
            marshalStub = IntPtr.Zero;
            unmarshalStub = IntPtr.Zero;
            destroyStub = IntPtr.Zero;

            if (TryGetStructData(structTypeHandle, out ExternalReferencesTable externalReferences, out NativeParser entryParser))
            {
                uint mask = entryParser.GetUnsigned();
                if ((mask & InteropDataConstants.HasInvalidLayout) != 0)
                {
                    throw new ArgumentException(SR.Format(SR.Arg_CannotMarshal, Type.GetTypeFromHandle(structTypeHandle)));
                }

                if ((mask & InteropDataConstants.HasMarshallers) != 0)
                {
                    entryParser.GetUnsigned(); // skip size
                    marshalStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned());
                    unmarshalStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned());
                    destroyStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned());
                }
            }
            else
            {
                throw new NotSupportedException(SR.Format(SR.StructMarshalling_MissingInteropData, Type.GetTypeFromHandle(structTypeHandle)));
            }
        }
    }
}