File: Internal\Runtime\CompilerServices\OpenMethodResolver.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.Collections.Generic;
using System.Runtime;
using System.Runtime.InteropServices;

using Internal.Runtime.Augments;

namespace Internal.Runtime.CompilerServices
{
    // This structure is used to resolve a instance method given an object instance. To use this type
    // 1) New up an instance using one of the constructors below.
    // 2) Use the ToIntPtr() method to get the interned instance of this type. This will permanently allocate
    //    a block of memory that can be used to represent a virtual method resolution. This memory is interned
    //    so that repeated allocation of the same resolver will not leak.
    // 3) Use the ResolveMethod function to do the virtual lookup. This function takes advantage of
    //    a lockless cache so the resolution is very fast for repeated lookups.
    public unsafe struct OpenMethodResolver : IEquatable<OpenMethodResolver>
    {
        // Lazy initialized to point to the type loader method when the first `GVMResolve` resolver is created
        private static delegate*<object, RuntimeMethodHandle, nint> s_lazyGvmLookupForSlot;

        public const short DispatchResolve = 0;
        public const short GVMResolve = 1;
        public const short OpenNonVirtualResolve = 2;
        public const short OpenNonVirtualResolveLookthruUnboxing = 3;

        private readonly short _resolveType;
        private readonly GCHandle _readerGCHandle;
        private readonly int _handle;
        private readonly IntPtr _methodHandleOrSlotOrCodePointer;
        private readonly IntPtr _nonVirtualOpenInvokeCodePointer;
        private readonly MethodTable* _declaringType;

        public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, int slot, GCHandle readerGCHandle, int handle)
        {
            _resolveType = DispatchResolve;
            _declaringType = declaringTypeOfSlot.ToMethodTable();
            _methodHandleOrSlotOrCodePointer = new IntPtr(slot);
            _handle = handle;
            _readerGCHandle = readerGCHandle;
            _nonVirtualOpenInvokeCodePointer = IntPtr.Zero;
        }

        public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle)
        {
            _resolveType = GVMResolve;
            _methodHandleOrSlotOrCodePointer = *(IntPtr*)&gvmSlot;
            _declaringType = declaringTypeOfSlot.ToMethodTable();
            _handle = handle;
            _readerGCHandle = readerGCHandle;
            _nonVirtualOpenInvokeCodePointer = IntPtr.Zero;

            if (s_lazyGvmLookupForSlot == null)
                s_lazyGvmLookupForSlot = &TypeLoaderExports.GVMLookupForSlot;
        }

        public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle, short resolveType)
        {
            _resolveType = resolveType;
            _methodHandleOrSlotOrCodePointer = codePointer;
            _declaringType = declaringType.ToMethodTable();
            _handle = handle;
            _readerGCHandle = readerGCHandle;
            if (resolveType == OpenNonVirtualResolve)
                _nonVirtualOpenInvokeCodePointer = codePointer;
            else if (resolveType == OpenNonVirtualResolveLookthruUnboxing)
                _nonVirtualOpenInvokeCodePointer = RuntimeAugments.TypeLoaderCallbacks.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(codePointer, declaringType);
            else
                throw new NotSupportedException();
        }

        public short ResolverType
        {
            get
            {
                return _resolveType;
            }
        }

        public RuntimeTypeHandle DeclaringType
        {
            get
            {
                return new RuntimeTypeHandle(_declaringType);
            }
        }

        public RuntimeMethodHandle GVMMethodHandle
        {
            get
            {
                IntPtr localIntPtr = _methodHandleOrSlotOrCodePointer;
                IntPtr* pMethodHandle = &localIntPtr;
                return *(RuntimeMethodHandle*)pMethodHandle;
            }
        }

        public bool IsOpenNonVirtualResolve
        {
            get
            {
                switch (_resolveType)
                {
                    case OpenNonVirtualResolve:
                    case OpenNonVirtualResolveLookthruUnboxing:
                        return true;
                    default:
                        return false;
                }
            }
        }

        public IntPtr CodePointer
        {
            get
            {
                return _methodHandleOrSlotOrCodePointer;
            }
        }

        public object Reader
        {
            get
            {
                return _readerGCHandle.Target;
            }
        }

        public int Handle
        {
            get
            {
                return _handle;
            }
        }

        private IntPtr ResolveMethod(object thisObject)
        {
            if (_resolveType == DispatchResolve)
            {
                return RuntimeImports.RhResolveDispatch(thisObject, _declaringType, (ushort)_methodHandleOrSlotOrCodePointer);
            }
            else if (_resolveType == GVMResolve)
            {
                return s_lazyGvmLookupForSlot(thisObject, GVMMethodHandle);
            }
            else
            {
                throw new NotSupportedException(); // Should never happen, in this case, the dispatch should be resolved in the other ResolveMethod function
            }
        }

        internal static IntPtr ResolveMethodWorker(IntPtr resolver, object thisObject)
        {
            return ((OpenMethodResolver*)resolver)->ResolveMethod(thisObject);
        }

        public static IntPtr ResolveMethod(IntPtr resolver, object thisObject)
        {
            IntPtr nonVirtualOpenInvokeCodePointer = ((OpenMethodResolver*)resolver)->_nonVirtualOpenInvokeCodePointer;
            if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero)
                return nonVirtualOpenInvokeCodePointer;

            return TypeLoaderExports.OpenInstanceMethodLookup(resolver, thisObject);
        }

        public static IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisType)
        {
            OpenMethodResolver* resolver = ((OpenMethodResolver*)resolverPtr);
            IntPtr nonVirtualOpenInvokeCodePointer = resolver->_nonVirtualOpenInvokeCodePointer;
            if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero)
                return nonVirtualOpenInvokeCodePointer;

            return RuntimeImports.RhResolveDispatchOnType(thisType.ToMethodTable(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer);
        }

        private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int hashCode4)
        {
            int length = 4;

            int hash1 = 0x449b3ad6;
            int hash2 = (length << 3) + 0x55399219;

            hash1 = (hash1 + int.RotateLeft(hash1, 5)) ^ hashCode1;
            hash2 = (hash2 + int.RotateLeft(hash2, 5)) ^ hashCode2;
            hash1 = (hash1 + int.RotateLeft(hash1, 5)) ^ hashCode3;
            hash2 = (hash2 + int.RotateLeft(hash2, 5)) ^ hashCode4;

            hash1 += int.RotateLeft(hash1, 8);
            hash2 += int.RotateLeft(hash2, 8);

            return hash1 ^ hash2;
        }

        public override int GetHashCode()
        {
            return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType == null ? 0 : (int)_declaringType->HashCode);
        }

        public bool Equals(OpenMethodResolver other)
        {
            if (other._resolveType != _resolveType)
                return false;

            if (other._handle != _handle)
                return false;

            if (other._methodHandleOrSlotOrCodePointer != _methodHandleOrSlotOrCodePointer)
                return false;

            return other._declaringType == _declaringType;
        }

        public override bool Equals(object? obj)
        {
            if (!(obj is OpenMethodResolver))
            {
                return false;
            }

            return ((OpenMethodResolver)obj).Equals(this);
        }

        private static LowLevelDictionary<OpenMethodResolver, IntPtr> s_internedResolverHash = new LowLevelDictionary<OpenMethodResolver, IntPtr>();

        public IntPtr ToIntPtr()
        {
            lock (s_internedResolverHash)
            {
                IntPtr returnValue;
                if (s_internedResolverHash.TryGetValue(this, out returnValue))
                    return returnValue;
                returnValue = (IntPtr)NativeMemory.Alloc((nuint)sizeof(OpenMethodResolver));
                *((OpenMethodResolver*)returnValue) = this;
                s_internedResolverHash.Add(this, returnValue);
                return returnValue;
            }
        }
    }
}