|
// 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.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Internal.Runtime;
using Internal.Runtime.Augments;
using Internal.Runtime.CompilerServices;
namespace System.Runtime
{
// Initialize the cache eagerly to avoid null checks.
[EagerStaticClassConstruction]
internal static class TypeLoaderExports
{
//
// Generic lookup cache
//
#if DEBUG
// use smaller numbers to hit resizing/preempting logic in debug
private const int InitialCacheSize = 8; // MUST BE A POWER OF TWO
private const int MaximumCacheSize = 512;
#else
private const int InitialCacheSize = 128; // MUST BE A POWER OF TWO
private const int MaximumCacheSize = 128 * 1024;
#endif // DEBUG
private static GenericCache<Key, Value> s_cache =
new GenericCache<Key, Value>(InitialCacheSize, MaximumCacheSize);
private struct Key : IEquatable<Key>
{
public IntPtr _context;
public IntPtr _signature;
public Key(nint context, nint signature)
{
_context = context;
_signature = signature;
}
public bool Equals(Key other)
{
return _context == other._context && _signature == other._signature;
}
public override int GetHashCode()
{
// pointers will likely match and cancel out in the upper bits
// we will rotate context by 16 bit to keep more varying bits in the hash
IntPtr context = (IntPtr)BitOperations.RotateLeft((nuint)_context, 16);
return (context ^ _signature).GetHashCode();
}
public override bool Equals(object obj)
{
return obj is Key && Equals((Key)obj);
}
}
private struct Value
{
public IntPtr _result;
public IntPtr _auxResult;
public Value(IntPtr result, IntPtr auxResult)
{
_result = result;
_auxResult = auxResult;
}
}
private static Value LookupOrAdd(IntPtr context, IntPtr signature)
{
if (!TryGetFromCache(context, signature, out var v))
{
v = CacheMiss(context, signature);
}
return v;
}
public static IntPtr GenericLookup(IntPtr context, IntPtr signature)
{
if (!TryGetFromCache(context, signature, out var v))
{
v = CacheMiss(context, signature);
}
return v._result;
}
public static unsafe IntPtr GVMLookupForSlot(object obj, RuntimeMethodHandle slot)
{
if (TryGetFromCache((IntPtr)obj.GetMethodTable(), RuntimeMethodHandle.ToIntPtr(slot), out var v))
return v._result;
return GVMLookupForSlotSlow(obj, slot);
}
private static unsafe IntPtr GVMLookupForSlotSlow(object obj, RuntimeMethodHandle slot)
{
Value v = CacheMiss((IntPtr)obj.GetMethodTable(), RuntimeMethodHandle.ToIntPtr(slot),
(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult)
=> RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget(new RuntimeTypeHandle((MethodTable*)context), *(RuntimeMethodHandle*)&signature));
return v._result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe IntPtr OpenInstanceMethodLookup(IntPtr openResolver, object obj)
{
if (!TryGetFromCache((IntPtr)obj.GetMethodTable(), openResolver, out var v))
{
v = CacheMiss((IntPtr)obj.GetMethodTable(), openResolver,
(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult)
=> Internal.Runtime.CompilerServices.OpenMethodResolver.ResolveMethodWorker(signature, contextObject),
obj);
}
return v._result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryGetFromCache(IntPtr context, IntPtr signature, out Value entry)
{
Key k = new Key(context, signature);
return s_cache.TryGet(k, out entry);
}
private static Value CacheMiss(IntPtr ctx, IntPtr sig)
{
return CacheMiss(ctx, sig,
(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) =>
RuntimeAugments.TypeLoaderCallbacks.GenericLookupFromContextAndSignature(context, signature, out auxResult)
);
}
private static unsafe Value CacheMiss(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject = null)
{
//
// Call into the type loader to compute the target
//
IntPtr auxResult = default;
IntPtr result = factory(context, signature, contextObject, ref auxResult);
Key k = new Key(context, signature);
Value v = new Value(result, auxResult);
s_cache.TrySet(k, v);
return v;
}
}
internal delegate IntPtr RuntimeObjectFactory(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult);
internal static unsafe class RawCalliHelper
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CallDefaultStructConstructor(System.IntPtr pfn, ref byte data)
{
// Manually expand call of the instance method fat pointer. We cannot use a regular static C# function
// pointer call since it would not work for shared generic instance method.
if (FunctionPointerOps.IsGenericMethodPointer(pfn))
{
GenericMethodDescriptor* gmd = FunctionPointerOps.ConvertToGenericDescriptor(pfn);
((delegate*<ref byte, IntPtr, void>)gmd->MethodFunctionPointer)(ref data, gmd->InstantiationArgument);
}
else
{
((delegate*<ref byte, void>)pfn)(ref data);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Call<T>(System.IntPtr pfn, IntPtr arg)
=> ((delegate*<IntPtr, T>)pfn)(arg);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Call(System.IntPtr pfn, object arg)
=> ((delegate*<object, void>)pfn)(arg);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref byte Call(IntPtr pfn, void* arg1, ref byte arg2, ref byte arg3, void* arg4)
=> ref ((delegate*<void*, ref byte, ref byte, void*, ref byte>)pfn)(arg1, ref arg2, ref arg3, arg4);
}
}
|