|
// 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 Internal.NativeFormat;
using Internal.Runtime.Augments;
using Internal.Runtime.CompilerServices;
using Internal.TypeSystem;
using Internal.TypeSystem.NoMetadata;
namespace Internal.Runtime.TypeLoader
{
public abstract class GenericDictionaryCell
{
internal abstract void Prepare(TypeBuilder builder);
internal abstract IntPtr Create(TypeBuilder builder);
internal virtual unsafe void WriteCellIntoDictionary(TypeBuilder typeBuilder, IntPtr* pDictionary, int slotIndex)
{
pDictionary[slotIndex] = Create(typeBuilder);
}
internal virtual IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult)
{
auxResult = IntPtr.Zero;
return Create(builder);
}
// Helper method for nullable transform. Ideally, we would do the nullable transform upfront before
// the types is build. Unfortunately, there does not seem to be easy way to test for Nullable<> type definition
// without introducing type builder recursion
private static RuntimeTypeHandle GetRuntimeTypeHandleWithNullableTransform(TypeBuilder builder, TypeDesc type)
{
RuntimeTypeHandle th = builder.GetRuntimeTypeHandle(type);
if (RuntimeAugments.IsNullable(th))
th = builder.GetRuntimeTypeHandle(((DefType)type).Instantiation[0]);
return th;
}
private class TypeHandleCell : GenericDictionaryCell
{
internal TypeDesc Type;
internal override void Prepare(TypeBuilder builder)
{
if (Type.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Canonical types do not have EETypes");
builder.RegisterForPreparation(Type);
}
internal override IntPtr Create(TypeBuilder builder)
{
return builder.GetRuntimeTypeHandle(Type).ToIntPtr();
}
}
private class UnwrapNullableTypeCell : GenericDictionaryCell
{
internal DefType Type;
internal override void Prepare(TypeBuilder builder)
{
if (Type.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Canonical types do not have EETypes");
if (Type.IsNullable)
{
Debug.Assert(Type.Instantiation.Length == 1);
builder.RegisterForPreparation(Type.Instantiation[0]);
}
else
builder.RegisterForPreparation(Type);
}
internal override IntPtr Create(TypeBuilder builder)
{
if (Type.IsNullable)
return builder.GetRuntimeTypeHandle(Type.Instantiation[0]).ToIntPtr();
else
return builder.GetRuntimeTypeHandle(Type).ToIntPtr();
}
}
private class InterfaceCallCell : GenericDictionaryCell
{
internal TypeDesc InterfaceType;
internal int Slot;
internal override void Prepare(TypeBuilder builder)
{
if (InterfaceType.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Unable to compute call information for a canonical interface");
builder.RegisterForPreparation(InterfaceType);
}
internal override IntPtr Create(TypeBuilder builder)
{
return RuntimeAugments.NewInterfaceDispatchCell(builder.GetRuntimeTypeHandle(InterfaceType), Slot);
}
}
/// <summary>
/// Used for non-generic static constrained Methods
/// </summary>
private class NonGenericStaticConstrainedMethodCell : GenericDictionaryCell
{
internal TypeDesc ConstraintType;
internal TypeDesc ConstrainedMethodType;
internal int ConstrainedMethodSlot;
internal override void Prepare(TypeBuilder builder)
{
if (ConstraintType.IsCanonicalSubtype(CanonicalFormKind.Any) || ConstrainedMethodType.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Unable to compute call information for a canonical type/method.");
builder.RegisterForPreparation(ConstraintType);
builder.RegisterForPreparation(ConstrainedMethodType);
}
internal override IntPtr Create(TypeBuilder builder)
{
IntPtr result = RuntimeAugments.ResolveStaticDispatchOnType(
builder.GetRuntimeTypeHandle(ConstraintType),
builder.GetRuntimeTypeHandle(ConstrainedMethodType),
ConstrainedMethodSlot,
out RuntimeTypeHandle genericContext);
Debug.Assert(result != IntPtr.Zero);
if (!genericContext.IsNull())
{
result = FunctionPointerOps.GetGenericMethodFunctionPointer(result, genericContext.ToIntPtr());
}
return result;
}
}
/// <summary>
/// Used for non-generic instance constrained Methods
/// </summary>
private class NonGenericInstanceConstrainedMethodCell : GenericDictionaryCell
{
internal TypeDesc ConstraintType;
internal TypeDesc ConstrainedMethodType;
internal int ConstrainedMethodSlot;
internal override void Prepare(TypeBuilder builder)
{
if (ConstraintType.IsCanonicalSubtype(CanonicalFormKind.Any) || ConstrainedMethodType.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Unable to compute call information for a canonical type/method.");
builder.RegisterForPreparation(ConstraintType);
builder.RegisterForPreparation(ConstrainedMethodType);
}
internal override IntPtr Create(TypeBuilder builder)
{
IntPtr result = RuntimeAugments.ResolveDispatchOnType(
builder.GetRuntimeTypeHandle(ConstraintType),
builder.GetRuntimeTypeHandle(ConstrainedMethodType),
ConstrainedMethodSlot);
Debug.Assert(result != IntPtr.Zero);
return result;
}
}
/// <summary>
/// Used for generic static constrained Methods
/// </summary>
private class GenericConstrainedMethodCell : GenericDictionaryCell
{
internal DefType ConstraintType;
internal InstantiatedMethod ConstrainedMethod;
private InstantiatedMethod _resolvedMethod;
internal override void Prepare(TypeBuilder builder)
{
_resolvedMethod = TypeLoaderEnvironment.GVMLookupForSlotWorker(ConstraintType, ConstrainedMethod);
if (_resolvedMethod.CanShareNormalGenericCode())
builder.PrepareMethod(_resolvedMethod);
}
internal override IntPtr Create(TypeBuilder builder)
{
if (_resolvedMethod.CanShareNormalGenericCode())
{
IntPtr methodDictionary = _resolvedMethod.RuntimeMethodDictionary;
return FunctionPointerOps.GetGenericMethodFunctionPointer(_resolvedMethod.FunctionPointer, methodDictionary);
}
else
{
if (!TypeLoaderEnvironment.Instance.TryLookupExactMethodPointer(_resolvedMethod, out nint result))
Environment.FailFast("Unable to find exact method pointer for a resolved GVM.");
return result;
}
}
}
private class StaticDataCell : GenericDictionaryCell
{
internal StaticDataKind DataKind;
internal TypeDesc Type;
internal override void Prepare(TypeBuilder builder)
{
if (Type.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Unable to compute static field locations for a canonical type.");
builder.RegisterForPreparation(Type);
}
internal override IntPtr Create(TypeBuilder builder)
{
RuntimeTypeHandle typeHandle = builder.GetRuntimeTypeHandle(Type);
switch (DataKind)
{
case StaticDataKind.NonGc:
return TypeLoaderEnvironment.Instance.TryGetNonGcStaticFieldData(typeHandle);
case StaticDataKind.Gc:
return TypeLoaderEnvironment.Instance.TryGetGcStaticFieldData(typeHandle);
default:
Debug.Assert(false);
return IntPtr.Zero;
}
}
internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult)
{
auxResult = IntPtr.Zero;
return *(IntPtr*)Create(builder);
}
}
private class ThreadStaticIndexCell : GenericDictionaryCell
{
internal TypeDesc Type;
internal override void Prepare(TypeBuilder builder)
{
if (Type.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Unable to compute static field locations for a canonical type.");
builder.RegisterForPreparation(Type);
}
internal override unsafe IntPtr Create(TypeBuilder builder)
{
return TypeLoaderEnvironment.Instance.TryGetThreadStaticFieldData(builder.GetRuntimeTypeHandle(Type));
}
}
private class MethodDictionaryCell : GenericDictionaryCell
{
internal InstantiatedMethod GenericMethod;
internal override unsafe void Prepare(TypeBuilder builder)
{
if (GenericMethod.IsCanonicalMethod(CanonicalFormKind.Any))
Environment.FailFast("Method dictionaries of canonical methods do not exist");
builder.PrepareMethod(GenericMethod);
}
internal override IntPtr Create(TypeBuilder builder)
{
Debug.Assert(GenericMethod.RuntimeMethodDictionary != IntPtr.Zero);
return GenericMethod.RuntimeMethodDictionary;
}
}
private class FieldLdTokenCell : GenericDictionaryCell
{
internal TypeDesc ContainingType;
internal int FieldHandle;
internal override unsafe void Prepare(TypeBuilder builder)
{
if (ContainingType.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Ldtoken is not permitted for a canonical field");
builder.RegisterForPreparation(ContainingType);
}
internal override unsafe IntPtr Create(TypeBuilder builder)
{
RuntimeFieldHandle handle = TypeLoaderEnvironment.Instance.GetRuntimeFieldHandleForComponents(
builder.GetRuntimeTypeHandle(ContainingType),
FieldHandle);
return *(IntPtr*)&handle;
}
}
private class MethodLdTokenCell : GenericDictionaryCell
{
internal MethodDesc Method;
internal override unsafe void Prepare(TypeBuilder builder)
{
if (Method.IsCanonicalMethod(CanonicalFormKind.Any))
Environment.FailFast("Ldtoken is not permitted for a canonical method");
// Do not use builder.PrepareMethod here. That
// would prepare the dictionary for the method,
// and if the method is abstract, there is no
// dictionary. Also, the dictionary is not necessary
// to create the ldtoken.
builder.RegisterForPreparation(Method.OwningType);
foreach (var type in Method.Instantiation)
builder.RegisterForPreparation(type);
}
internal override unsafe IntPtr Create(TypeBuilder builder)
{
RuntimeTypeHandle[] genericArgHandles = Method.HasInstantiation && !Method.IsMethodDefinition ?
builder.GetRuntimeTypeHandles(Method.Instantiation) : null;
RuntimeMethodHandle handle = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(
builder.GetRuntimeTypeHandle(Method.OwningType),
Method.NameAndSignature.Handle,
genericArgHandles,
Method.AsyncVariant);
return *(IntPtr*)&handle;
}
}
private class AllocateObjectCell : GenericDictionaryCell
{
internal TypeDesc Type;
internal override void Prepare(TypeBuilder builder)
{
if (Type.IsCanonicalSubtype(CanonicalFormKind.Any))
Environment.FailFast("Canonical types cannot be allocated");
builder.RegisterForPreparation(Type);
}
internal override IntPtr Create(TypeBuilder builder)
{
RuntimeTypeHandle th = GetRuntimeTypeHandleWithNullableTransform(builder, Type);
return RuntimeAugments.GetAllocateObjectHelperForType(th);
}
internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult)
{
RuntimeTypeHandle th = GetRuntimeTypeHandleWithNullableTransform(builder, Type);
auxResult = th.ToIntPtr();
return RuntimeAugments.GetAllocateObjectHelperForType(th);
}
}
private class DefaultConstructorCell : GenericDictionaryCell
{
internal TypeDesc Type;
internal override void Prepare(TypeBuilder builder)
{
builder.RegisterForPreparation(Type);
}
internal override IntPtr Create(TypeBuilder builder)
{
IntPtr result = TypeLoaderEnvironment.TryGetDefaultConstructorForType(Type);
if (result != IntPtr.Zero)
{
if (Type.IsValueType)
{
result = TypeLoaderEnvironment.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(result,
builder.GetRuntimeTypeHandle(Type));
}
}
else
{
result = RuntimeAugments.GetFallbackDefaultConstructor();
}
return result;
}
}
private class MethodCell : GenericDictionaryCell
{
internal MethodDesc Method;
internal override unsafe void Prepare(TypeBuilder builder)
{
builder.PrepareMethod(Method);
}
internal override unsafe IntPtr Create(TypeBuilder builder)
{
IntPtr methodDictionary = Method.Instantiation.Length > 0 ?
((InstantiatedMethod)Method).RuntimeMethodDictionary :
builder.GetRuntimeTypeHandle(Method.OwningType).ToIntPtr();
return FunctionPointerOps.GetGenericMethodFunctionPointer(Method.FunctionPointer, methodDictionary);
}
}
internal static unsafe GenericDictionaryCell[] BuildDictionary(TypeBuilder typeBuilder, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser parser)
{
uint parserStartOffset = parser.Offset;
uint count = parser.GetSequenceCount();
Debug.Assert(count > 0);
TypeLoaderLogger.WriteLine("Parsing dictionary layout @ " + parserStartOffset.LowLevelToString() + " (" + count.LowLevelToString() + " entries)");
GenericDictionaryCell[] dictionary = new GenericDictionaryCell[count];
for (uint i = 0; i < count; i++)
{
TypeLoaderLogger.WriteLine(" -> DictionaryCell[" + i.LowLevelToString() + "] = ");
dictionary[i] = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser);
}
for (uint i = 0; i < count; i++)
dictionary[i].Prepare(typeBuilder);
return dictionary;
}
internal static GenericDictionaryCell ParseAndCreateCell(NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, ref NativeParser parser)
{
GenericDictionaryCell cell;
var kind = parser.GetFixupSignatureKind();
switch (kind)
{
case FixupSignatureKind.TypeHandle:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
TypeLoaderLogger.WriteLine("TypeHandle: " + type.ToString());
cell = new TypeHandleCell() { Type = type };
}
break;
case FixupSignatureKind.InterfaceCall:
{
var interfaceType = nativeLayoutInfoLoadContext.GetType(ref parser);
var slot = parser.GetUnsigned();
TypeLoaderLogger.WriteLine("InterfaceCall: " + interfaceType.ToString() + ", slot #" + slot.LowLevelToString());
cell = new InterfaceCallCell() { InterfaceType = interfaceType, Slot = (int)slot };
}
break;
case FixupSignatureKind.MethodDictionary:
{
var genericMethod = nativeLayoutInfoLoadContext.GetMethod(ref parser);
Debug.Assert(genericMethod.Instantiation.Length > 0);
TypeLoaderLogger.WriteLine("MethodDictionary: " + genericMethod.ToString());
cell = new MethodDictionaryCell { GenericMethod = (InstantiatedMethod)genericMethod };
}
break;
case FixupSignatureKind.StaticData:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
StaticDataKind staticDataKind = (StaticDataKind)parser.GetUnsigned();
TypeLoaderLogger.WriteLine("StaticData (" + (staticDataKind == StaticDataKind.Gc ? "Gc" : "NonGc") + ": " + type.ToString());
cell = new StaticDataCell() { DataKind = staticDataKind, Type = type };
}
break;
case FixupSignatureKind.UnwrapNullableType:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
TypeLoaderLogger.WriteLine("UnwrapNullableType of: " + type.ToString());
if (type is DefType)
cell = new UnwrapNullableTypeCell() { Type = (DefType)type };
else
cell = new TypeHandleCell() { Type = type };
}
break;
case FixupSignatureKind.FieldLdToken:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
int handle = (int)parser.GetUnsigned();
TypeLoaderLogger.WriteLine("LdToken on: " + type.ToString() + "." + handle.LowLevelToString());
cell = new FieldLdTokenCell() { FieldHandle = handle, ContainingType = type };
}
break;
case FixupSignatureKind.MethodLdToken:
{
var method = nativeLayoutInfoLoadContext.GetMethod(ref parser);
TypeLoaderLogger.WriteLine("LdToken on: " + method.OwningType.ToString() + "::" + method.NameAndSignature.GetName());
cell = new MethodLdTokenCell
{
Method = method,
};
}
break;
case FixupSignatureKind.AllocateObject:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
TypeLoaderLogger.WriteLine("AllocateObject on: " + type.ToString());
cell = new AllocateObjectCell { Type = type };
}
break;
case FixupSignatureKind.DefaultConstructor:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
TypeLoaderLogger.WriteLine("DefaultConstructor on: " + type.ToString());
cell = new DefaultConstructorCell { Type = type };
}
break;
case FixupSignatureKind.Method:
{
var method = nativeLayoutInfoLoadContext.GetMethod(ref parser);
TypeLoaderLogger.WriteLine("Method: " + method.ToString());
cell = new MethodCell
{
Method = method,
};
}
break;
case FixupSignatureKind.NonGenericStaticConstrainedMethod:
case FixupSignatureKind.NonGenericInstanceConstrainedMethod:
{
var constraintType = nativeLayoutInfoLoadContext.GetType(ref parser);
var constrainedMethodType = nativeLayoutInfoLoadContext.GetType(ref parser);
var constrainedMethodSlot = parser.GetUnsigned();
string kindString = kind == FixupSignatureKind.NonGenericStaticConstrainedMethod ? "NonGenericStaticConstrainedMethod: " : "NonGenericInstanceConstrainedMethod: ";
TypeLoaderLogger.WriteLine(kindString + constraintType.ToString() + " Method " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString());
if (kind == FixupSignatureKind.NonGenericStaticConstrainedMethod)
{
cell = new NonGenericStaticConstrainedMethodCell()
{
ConstraintType = constraintType,
ConstrainedMethodType = constrainedMethodType,
ConstrainedMethodSlot = (int)constrainedMethodSlot
};
}
else
{
cell = new NonGenericInstanceConstrainedMethodCell()
{
ConstraintType = constraintType,
ConstrainedMethodType = constrainedMethodType,
ConstrainedMethodSlot = (int)constrainedMethodSlot
};
}
}
break;
case FixupSignatureKind.GenericConstrainedMethod:
{
TypeDesc constraintType = nativeLayoutInfoLoadContext.GetType(ref parser);
MethodDesc constrainedMethod = nativeLayoutInfoLoadContext.GetMethod(ref parser);
TypeLoaderLogger.WriteLine("GenericConstrainedMethod: " + constraintType.ToString() + " Method " + constrainedMethod.ToString());
cell = new GenericConstrainedMethodCell()
{
ConstraintType = (DefType)constraintType,
ConstrainedMethod = (InstantiatedMethod)constrainedMethod,
};
}
break;
case FixupSignatureKind.ThreadStaticIndex:
{
var type = nativeLayoutInfoLoadContext.GetType(ref parser);
TypeLoaderLogger.WriteLine("ThreadStaticIndex on: " + type.ToString());
cell = new ThreadStaticIndexCell { Type = type };
}
break;
case FixupSignatureKind.NotYetSupported:
TypeLoaderLogger.WriteLine("Valid dictionary entry, but not yet supported by the TypeLoader!");
throw new TypeBuilder.MissingTemplateException();
default:
NativeParser.ThrowBadImageFormatException();
cell = null;
break;
}
return cell;
}
}
}
|