diff --git a/src/Common/src/Internal/Runtime/EEType.Constants.cs b/src/Common/src/Internal/Runtime/EEType.Constants.cs index 7aef1aebf08..fcd23ca0906 100644 --- a/src/Common/src/Internal/Runtime/EEType.Constants.cs +++ b/src/Common/src/Internal/Runtime/EEType.Constants.cs @@ -189,6 +189,11 @@ internal enum EETypeRareFlags : int /// This EEType is an abstract class (but not an interface). /// IsAbstractClassFlag = 0x00004000, + + /// + /// This EEType is for a Byref-like class (TypedReference, Span<T>,...) + /// + IsByRefLikeFlag = 0x00008000, } internal enum EETypeField diff --git a/src/Common/src/Internal/Runtime/EEType.cs b/src/Common/src/Internal/Runtime/EEType.cs index e2b760bfd97..94f21f943fe 100644 --- a/src/Common/src/Internal/Runtime/EEType.cs +++ b/src/Common/src/Internal/Runtime/EEType.cs @@ -507,6 +507,14 @@ internal bool IsAbstract } } + internal bool IsByRefLike + { + get + { + return (RareFlags & EETypeRareFlags.IsByRefLikeFlag) != 0; + } + } + internal bool IsDynamicType { get diff --git a/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs b/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs index d5f530471c5..5ef722b6d06 100644 --- a/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs +++ b/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs @@ -49,6 +49,16 @@ private class FieldLayoutFlags /// True if information about the shape of value type has been computed. /// public const int ComputedValueTypeShapeCharacteristics = 0x40; + + /// + /// True if has been computed. + /// + public const int ComputedIsByRefLike = 0x80; + + /// + /// True if this is a byref-like type. + /// + public const int IsByRefLike = 0x100; } private class StaticBlockInfo @@ -275,6 +285,22 @@ public DefType HfaElementType } } + /// + /// Gets a value indicating whether this is a byref-like type + /// (a TypedReference, Span<T>, etc.). + /// + public bool IsByRefLike + { + get + { + if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedIsByRefLike)) + { + ComputeIsByRefLike(); + } + return _fieldLayoutFlags.HasFlags(FieldLayoutFlags.IsByRefLike); + } + } + private void ComputeValueTypeShapeCharacteristics() { _valueTypeShapeCharacteristics = this.Context.GetLayoutAlgorithmForType(this).ComputeValueTypeShapeCharacteristics(this); @@ -360,6 +386,21 @@ public void ComputeTypeContainsGCPointers() _fieldLayoutFlags.AddFlags(flagsToAdd); } + + public void ComputeIsByRefLike() + { + if (_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedIsByRefLike)) + return; + + int flagsToAdd = FieldLayoutFlags.ComputedIsByRefLike; + + if (this.Context.GetLayoutAlgorithmForType(this).ComputeIsByRefLike(this)) + { + flagsToAdd |= FieldLayoutFlags.IsByRefLike; + } + + _fieldLayoutFlags.AddFlags(flagsToAdd); + } } } diff --git a/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs index 6cd253aa8a9..80babbd66c2 100644 --- a/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs +++ b/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs @@ -43,6 +43,11 @@ public abstract class FieldLayoutAlgorithm /// the element type of the homogenous float aggregate. This will either be System.Double or System.Float. /// public abstract DefType ComputeHomogeneousFloatAggregateElementType(DefType type); + + /// + /// Compute whether '' is a ByRef-like value type (TypedReference, Span<T>, etc.). + /// + public abstract bool ComputeIsByRefLike(DefType type); } /// diff --git a/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 490134275fa..b9ca36f40f8 100644 --- a/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -27,8 +27,25 @@ public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defTyp // Count the number of instance fields in advance for convenience int numInstanceFields = 0; foreach (var field in type.GetFields()) - if (!field.IsStatic) - numInstanceFields++; + { + if (field.IsStatic) + continue; + + TypeDesc fieldType = field.FieldType; + + // ByRef instance fields are not allowed. + if (fieldType.IsByRef) + throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + + // ByRef-like instance fields on reference types are not allowed. + if (fieldType.IsValueType && !type.IsValueType) + { + if (((DefType)fieldType).IsByRefLike) + throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + } + + numInstanceFields++; + } if (type.IsModuleType) { @@ -123,11 +140,6 @@ out instanceByteSizeAndAlignment return result; } - // Verify that no ByRef types present in this type's fields - foreach (var field in type.GetFields()) - if (field.FieldType.IsByRef) - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); - // If the type has layout, read its packing and size info // If the type has explicit layout, also read the field offset info if (type.IsExplicitLayout || type.IsSequentialLayout) @@ -198,12 +210,18 @@ public unsafe override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefTyp if (!field.IsStatic || field.HasRva || field.IsLiteral) continue; + TypeDesc fieldType = field.FieldType; + if (fieldType.IsByRef || (fieldType.IsValueType && ((DefType)fieldType).IsByRefLike)) + { + throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + } + StaticsBlock* block = field.IsThreadStatic ? &result.ThreadStatics : field.HasGCStaticBase ? &result.GcStatics : &result.NonGcStatics; - SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, type.Context.Target.DefaultPackingSize); + SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, type.Context.Target.DefaultPackingSize); block->Size = LayoutInt.AlignUp(block->Size, sizeAndAlignment.Alignment); result.Offsets[index] = new FieldAndOffset(field, block->Size); @@ -240,7 +258,7 @@ public override bool ComputeContainsGCPointers(DefType type) break; } } - else if (fieldType.IsGCPointer || fieldType.IsByRef) + else if (fieldType.IsGCPointer) { someFieldContainsPointers = true; break; @@ -403,7 +421,7 @@ private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, result.Alignment = fieldType.Context.Target.LayoutPointerSize; } } - else if (fieldType.IsByRef || fieldType.IsArray) + else if (fieldType.IsArray) { // This could use InstanceFieldSize/Alignment (and those results should match what's here) // but, its more efficient to just assume pointer size instead of fulling processing @@ -601,6 +619,34 @@ public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type } } + public override bool ComputeIsByRefLike(DefType type) + { + // Reference types can never be ByRef-like. + if (!type.IsValueType) + return false; + + if (type.IsByReferenceOfT) + return true; + + foreach (FieldDesc field in type.GetFields()) + { + if (field.IsStatic) + continue; + + TypeDesc fieldType = field.FieldType; + if (fieldType.IsValueType && !fieldType.IsPrimitive) + { + DefType fieldDefType = (DefType)fieldType; + if (fieldDefType.IsByRefLike) + { + return true; + } + } + } + + return false; + } + private struct SizeAndAlignment { public LayoutInt Size; diff --git a/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs b/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs index ed60c69f8fb..3c5278b0822 100644 --- a/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs +++ b/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs @@ -40,6 +40,9 @@ public abstract partial class MetadataTypeSystemContext : TypeSystemContext "RuntimeFieldHandle", "Exception", + + "TypedReference", + "ByReference`1", }; private MetadataType[] _wellKnownTypes; diff --git a/src/Common/src/TypeSystem/Common/TypeDesc.cs b/src/Common/src/TypeSystem/Common/TypeDesc.cs index 7b57a65b049..ab7333d3e6d 100644 --- a/src/Common/src/TypeSystem/Common/TypeDesc.cs +++ b/src/Common/src/TypeSystem/Common/TypeDesc.cs @@ -115,6 +115,8 @@ internal void SetWellKnownType(WellKnownType wellKnownType) case WellKnownType.RuntimeTypeHandle: case WellKnownType.RuntimeMethodHandle: case WellKnownType.RuntimeFieldHandle: + case WellKnownType.TypedReference: + case WellKnownType.ByReferenceOfT: flags = TypeFlags.ValueType; break; @@ -265,6 +267,18 @@ public bool IsNullable } } + /// + /// Gets a value indicating whether this is a generic definition, or + /// an instance of System.ByReference`1. + /// + public bool IsByReferenceOfT + { + get + { + return this.GetTypeDefinition().IsWellKnownType(WellKnownType.ByReferenceOfT); + } + } + /// /// Gets a value indicating whether this is an array type (). /// Note this will return true for both multidimensional array types and vector types. diff --git a/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs index aad04e31d1e..0c001845473 100644 --- a/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs +++ b/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs @@ -19,7 +19,12 @@ public class UniversalCanonLayoutAlgorithm : FieldLayoutAlgorithm public override bool ComputeContainsGCPointers(DefType type) { // This should never be called - throw new ArgumentException(); + throw new NotSupportedException(); + } + + public override bool ComputeIsByRefLike(DefType type) + { + return false; } public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type) diff --git a/src/Common/src/TypeSystem/Common/WellKnownType.cs b/src/Common/src/TypeSystem/Common/WellKnownType.cs index 767ed125a23..3cd9988b863 100644 --- a/src/Common/src/TypeSystem/Common/WellKnownType.cs +++ b/src/Common/src/TypeSystem/Common/WellKnownType.cs @@ -41,5 +41,8 @@ public enum WellKnownType RuntimeFieldHandle, Exception, + + TypedReference, + ByReferenceOfT, } } diff --git a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs index 3700411febc..ed8b83801c6 100644 --- a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -109,7 +109,7 @@ private TypeDesc ParseType(SignatureTypeCode typeCode) return _module.Context.GetInstantiatedType(metadataTypeDef, new Instantiation(instance)); } case SignatureTypeCode.TypedReference: - throw new PlatformNotSupportedException("TypedReference not supported in .NET Core"); + return GetWellKnownType(WellKnownType.TypedReference); case SignatureTypeCode.FunctionPointer: return _module.Context.GetFunctionPointerType(ParseMethodSignature()); default: diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 9e4b49f884e..12a81660890 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -272,6 +272,10 @@ public static bool CreationAllowed(TypeDesc type) if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) return false; + // Byref-like types have interior pointers and cannot be heap allocated. + if (type.IsValueType && ((DefType)type).IsByRefLike) + return false; + break; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs index 4f42bc6b812..6a7d3060896 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs @@ -522,6 +522,8 @@ void ComputeRareFlags(NodeFactory factory) { uint flags = 0; + MetadataType metadataType = _type as MetadataType; + if (_type.IsNullable) { flags |= (uint)EETypeRareFlags.IsNullableFlag; @@ -537,7 +539,7 @@ void ComputeRareFlags(NodeFactory factory) flags |= (uint)EETypeRareFlags.RequiresAlign8Flag; } - if (_type.IsDefType && ((DefType)_type).IsHfa) + if (metadataType != null && metadataType.IsHfa) { flags |= (uint)EETypeRareFlags.IsHFAFlag; } @@ -551,11 +553,16 @@ void ComputeRareFlags(NodeFactory factory) } } - if ((_type is MetadataType) && !_type.IsInterface && ((MetadataType)_type).IsAbstract) + if (metadataType != null && !_type.IsInterface && metadataType.IsAbstract) { flags |= (uint)EETypeRareFlags.IsAbstractClassFlag; } + if (metadataType != null && metadataType.IsByRefLike) + { + flags |= (uint)EETypeRareFlags.IsByRefLikeFlag; + } + if (flags != 0) { _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, flags); @@ -699,7 +706,11 @@ public static void CheckCanGenerateEEType(NodeFactory factory, TypeDesc type) foreach (TypeDesc typeArg in defType.Instantiation) { // ByRefs, pointers, function pointers, and System.Void are never valid instantiation arguments - if (typeArg.IsByRef || typeArg.IsPointer || typeArg.IsFunctionPointer || typeArg.IsVoid) + if (typeArg.IsByRef + || typeArg.IsPointer + || typeArg.IsFunctionPointer + || typeArg.IsVoid + || (typeArg.IsValueType && ((DefType)typeArg).IsByRefLike)) { throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } @@ -739,6 +750,12 @@ public static void CheckCanGenerateEEType(NodeFactory factory, TypeDesc type) { throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadRankTooLarge, type); } + + if ((parameterType.IsDefType) && ((DefType)parameterType).IsByRefLike) + { + // Arrays of byref-like types are not allowed + throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + } } // Validate we're not constructing a type over a ByRef diff --git a/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs b/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs index d0551428d2c..59b675487fb 100644 --- a/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs +++ b/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs @@ -178,6 +178,13 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, methodDesc = context.SystemModule.GetKnownType("System.Runtime", "TypeLoaderExports").GetKnownMethod("GVMLookupForSlot", null); break; + case ReadyToRunHelper.TypeHandleToRuntimeType: + methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "TypeHandleToRuntimeTypeMaybeNull"); + break; + case ReadyToRunHelper.GetRefAny: + methodDesc = context.GetHelperEntryPoint("TypedReferenceHelpers", "GetRefAny"); + break; + default: throw new NotImplementedException(id.ToString()); } diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs index b8795fd136a..75c6c3d82a1 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs @@ -192,7 +192,10 @@ public bool IsReflectionInvokable (MethodDesc method) { var signature = method.Signature; + // ---------------------------------------------------------------- // TODO: support for methods returning pointer types - https://github.com/dotnet/corert/issues/2113 + // ---------------------------------------------------------------- + if (signature.ReturnType.IsPointer) return false; @@ -200,7 +203,10 @@ public bool IsReflectionInvokable (MethodDesc method) if (signature[i].IsByRef && ((ByRefType)signature[i]).ParameterType.IsPointer) return false; + // ---------------------------------------------------------------- // TODO: function pointer types are odd: https://github.com/dotnet/corert/issues/1929 + // ---------------------------------------------------------------- + if (signature.ReturnType.IsFunctionPointer) return false; @@ -208,11 +214,31 @@ public bool IsReflectionInvokable (MethodDesc method) if (signature[i].IsFunctionPointer) return false; + // ---------------------------------------------------------------- // Methods with ByRef returns can't be reflection invoked + // ---------------------------------------------------------------- + if (signature.ReturnType.IsByRef) return false; + // ---------------------------------------------------------------- + // Methods that return ByRef-like types or take them by reference can't be reflection invoked + // ---------------------------------------------------------------- + + if (signature.ReturnType.IsDefType && ((DefType)signature.ReturnType).IsByRefLike) + return false; + + for (int i = 0; i < signature.Length; i++) + { + ByRefType paramType = signature[i] as ByRefType; + if (paramType != null && paramType.ParameterType.IsDefType && ((DefType)paramType.ParameterType).IsByRefLike) + return false; + } + + // ---------------------------------------------------------------- // Delegate construction is only allowed through specific IL sequences + // ---------------------------------------------------------------- + if (method.OwningType.IsDelegate && method.IsConstructor) return false; diff --git a/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs b/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs index 17ecac5a35d..462255ace1f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs +++ b/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs @@ -115,5 +115,9 @@ public enum ReadyToRunHelper // GVM lookup helper GVMLookupForSlot = 0x100, + + // TypedReference + TypeHandleToRuntimeType = 0x110, + GetRefAny = 0x111, } } diff --git a/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs b/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs index b6bea6761e6..c89e50ead6a 100644 --- a/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs +++ b/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/Platform.cs @@ -69,6 +69,10 @@ public class Type public sealed class ParamArrayAttribute : Attribute { } + + public struct TypedReference { } + + public struct ByReference { } } namespace System.Collections diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs index 827f9f47896..912d7e778c1 100644 --- a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs @@ -146,3 +146,31 @@ public class ClassBoolDoubleBool } } +namespace IsByRefLike +{ + public struct ByRefLikeStruct + { + ByReference ByRef; + } + + public struct ComposedStruct + { + ByRefLikeStruct ByRefLike; + } + + public struct NotByRefLike + { + int X; + } + + public class Invalid + { + ByReference ByRef; + } + + public class ComposedInvalid + { + ByRefLikeStruct ByRefLike; + } +} + diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs index 7f2a8e25859..b15af240e12 100644 --- a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. #pragma warning disable 649 +#pragma warning disable 169 namespace System { @@ -65,6 +66,14 @@ public class ThreadStaticAttribute : Attribute { } public class Array : Array, System.Collections.Generic.IList { } public class Exception { } + + public struct TypedReference + { + private readonly ByReference _value; + private readonly RuntimeTypeHandle _typeHandle; + } + + public struct ByReference { } } namespace System.Collections diff --git a/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs b/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs index 67d7f82fbd8..ef65ceb5d73 100644 --- a/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs +++ b/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs @@ -299,5 +299,48 @@ public void TestTypeContainsGCPointers() type = _testModule.GetType("ContainsGCPointers", "ClassHasArrayOfClassType"); Assert.True(type.ContainsGCPointers); } + + [Fact] + public void TestByRefLikeTypes() + { + { + DefType type = _context.GetWellKnownType(WellKnownType.TypedReference); + Assert.True(type.IsByRefLike); + } + + { + DefType type = _context.GetWellKnownType(WellKnownType.ByReferenceOfT); + Assert.True(type.IsByRefLike); + } + + { + DefType type = _testModule.GetType("IsByRefLike", "ByRefLikeStruct"); + Assert.True(type.IsByRefLike); + } + + { + DefType type = _testModule.GetType("IsByRefLike", "ComposedStruct"); + Assert.True(type.IsByRefLike); + } + + { + DefType type = _testModule.GetType("IsByRefLike", "NotByRefLike"); + Assert.False(type.IsByRefLike); + } + } + + [Fact] + public void TestInvalidByRefLikeTypes() + { + { + DefType type = _testModule.GetType("IsByRefLike", "Invalid"); + Assert.Throws(() => type.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields)); + } + + { + DefType type = _testModule.GetType("IsByRefLike", "ComposedInvalid"); + Assert.Throws(() => type.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields)); + } + } } } diff --git a/src/JitInterface/src/CorInfoImpl.Intrinsics.cs b/src/JitInterface/src/CorInfoImpl.Intrinsics.cs index e58c87db5bd..7e8b2e309ee 100644 --- a/src/JitInterface/src/CorInfoImpl.Intrinsics.cs +++ b/src/JitInterface/src/CorInfoImpl.Intrinsics.cs @@ -122,8 +122,8 @@ static IntrinsicHashtable InitializeIntrinsicHashtable() table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_MemoryBarrier, "MemoryBarrier", "System.Threading", "Interlocked"); // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetCurrentManagedThread, "GetCurrentThreadNative", "System", "Thread"); // not in .NET Core // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetManagedThreadId, "get_ManagedThreadId", "System", "Thread"); // not in .NET Core - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor, ".ctor", "System", "ByReference`1"); // not handled - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value, "get_Value", "System", "ByReference`1"); // not handled + table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor, ".ctor", "System", "ByReference`1"); // not handled + table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value, "get_Value", "System", "ByReference`1"); // not handled // If this assert fails, make sure to add the new intrinsics to the table above and update the expected count below. Debug.Assert((int)CorInfoIntrinsics.CORINFO_INTRINSIC_Count == 47); @@ -196,6 +196,8 @@ private CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_STRUCT_* ftn, ref bool p case CorInfoIntrinsics.CORINFO_INTRINSIC_RTH_GetValueInternal: case CorInfoIntrinsics.CORINFO_INTRINSIC_InitializeArray: + case CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor: + case CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value: pMustExpand = true; break; diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index b0795638fc1..7e7dae7da1a 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -1051,8 +1051,11 @@ private uint getClassAttribsInternal(TypeDesc type) { result |= CorInfoFlag.CORINFO_FLG_VALUECLASS; + if (metadataType.IsByRefLike) + result |= CorInfoFlag.CORINFO_FLG_CONTAINS_STACK_PTR; + // The CLR has more complicated rules around CUSTOMLAYOUT, but this will do. - if (metadataType.IsExplicitLayout) + if (metadataType.IsExplicitLayout || metadataType.IsWellKnownType(WellKnownType.TypedReference)) result |= CorInfoFlag.CORINFO_FLG_CUSTOMLAYOUT; // TODO @@ -1128,6 +1131,12 @@ private int GatherClassGCLayout(TypeDesc type, byte* gcPtrs) { int result = 0; + if (type.IsByReferenceOfT) + { + *gcPtrs = (byte)CorInfoGCType.TYPE_GC_BYREF; + return 1; + } + foreach (var field in type.GetFields()) { if (field.IsStatic) @@ -1138,7 +1147,8 @@ private int GatherClassGCLayout(TypeDesc type, byte* gcPtrs) var fieldType = field.FieldType; if (fieldType.IsValueType) { - if (!((DefType)fieldType).ContainsGCPointers) + var fieldDefType = (DefType)fieldType; + if (!fieldDefType.ContainsGCPointers && !fieldDefType.IsByRefLike) continue; gcType = CorInfoGCType.TYPE_GC_OTHER; @@ -1198,7 +1208,7 @@ private uint getClassGClayout(CORINFO_CLASS_STRUCT_* cls, byte* gcPtrs) for (int i = 0; i < ptrsCount; i++) gcPtrs[i] = (byte)CorInfoGCType.TYPE_GC_NONE; - if (type.ContainsGCPointers) + if (type.ContainsGCPointers || type.IsByRefLike) { result = (uint)GatherClassGCLayout(type, gcPtrs); } @@ -1277,6 +1287,11 @@ private CorInfoHelpFunc getBoxHelper(CORINFO_CLASS_STRUCT_* cls) { var type = HandleToObject(cls); + // we shouldn't allow boxing of types that contains stack pointers + // csc and vbc already disallow it. + if (type.IsValueType && ((DefType)type).IsByRefLike) + throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramSpecific, MethodBeingCompiled); + return type.IsNullable ? CorInfoHelpFunc.CORINFO_HELP_BOX_NULLABLE : CorInfoHelpFunc.CORINFO_HELP_BOX; } @@ -1496,7 +1511,7 @@ private void classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_STRUCT_* cls) return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Object)); case CorInfoClassId.CLASSID_TYPED_BYREF: - throw new TypeSystemException.TypeLoadException("System", "TypedReference", _compilation.TypeSystemContext.SystemModule); + return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.TypedReference)); case CorInfoClassId.CLASSID_TYPE_HANDLE: return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.RuntimeTypeHandle)); @@ -1616,8 +1631,19 @@ private CorInfoIsAccessAllowedResult canAccessClass(ref CORINFO_RESOLVED_TOKEN p private CorInfoType getFieldType(CORINFO_FIELD_STRUCT_* field, ref CORINFO_CLASS_STRUCT_* structType, CORINFO_CLASS_STRUCT_* memberParent) { - var fieldDesc = HandleToObject(field); - return asCorInfoType(fieldDesc.FieldType, out structType); + FieldDesc fieldDesc = HandleToObject(field); + TypeDesc fieldType = fieldDesc.FieldType; + CorInfoType type = asCorInfoType(fieldType, out structType); + + Debug.Assert(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.ByReferenceOfT).GetKnownField("_value").FieldType.Category == TypeFlags.IntPtr); + if (type == CorInfoType.CORINFO_TYPE_NATIVEINT && fieldDesc.OwningType.IsByReferenceOfT) + { + Debug.Assert(structType == null); + Debug.Assert(fieldDesc.Offset.AsInt == 0); + type = CorInfoType.CORINFO_TYPE_BYREF; + } + + return type; } private uint getFieldOffset(CORINFO_FIELD_STRUCT_* field) @@ -2270,6 +2296,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_GVMLOOKUP_FOR_SLOT: id = ReadyToRunHelper.GVMLookupForSlot; break; + case CorInfoHelpFunc.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL: id = ReadyToRunHelper.TypeHandleToRuntimeType; break; + case CorInfoHelpFunc.CORINFO_HELP_GETREFANY: id = ReadyToRunHelper.GetRefAny; break; + default: throw new NotImplementedException(ftnNum.ToString()); } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 12bc670ac7e..a03b3c39ce4 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -30,6 +30,11 @@ using Volatile = System.Threading.Volatile; +#if !CORERT +// Temporary workaround until NUTC stops crashing +using TypedReference = System.Reflection.TypedReference; +#endif + namespace Internal.Runtime.Augments { public enum CanonTypeKind diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/TypedReferenceHelpers.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/TypedReferenceHelpers.cs new file mode 100644 index 00000000000..ab6d2729234 --- /dev/null +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/TypedReferenceHelpers.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to implement TypedReference-related instructions. + /// + internal static class TypedReferenceHelpers + { + public unsafe static RuntimeTypeHandle TypeHandleToRuntimeTypeMaybeNull(RuntimeTypeHandle typeHandle) + { + return typeHandle; + } + + public unsafe static ref byte GetRefAny(RuntimeTypeHandle type, TypedReference typedRef) + { + if (!TypedReference.RawTargetTypeToken(typedRef).Equals(type)) + { + throw new InvalidCastException(); + } + + return ref typedRef.Value; + } + } +} diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index bbc0a0005e5..495f96a60d0 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -1215,6 +1215,9 @@ Arrays of System.Void are not supported. + + Cannot create boxed ByRef-like values. + WaitAll for multiple handles on a STA thread is not supported. diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index cf27e65d720..b08cb84e1cd 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -101,6 +101,7 @@ + diff --git a/src/System.Private.CoreLib/src/System/ByReference.cs b/src/System.Private.CoreLib/src/System/ByReference.cs index d0129c2ee7c..2644a8a5525 100644 --- a/src/System.Private.CoreLib/src/System/ByReference.cs +++ b/src/System.Private.CoreLib/src/System/ByReference.cs @@ -10,6 +10,7 @@ namespace System // around lack of first class support for byref fields in C# and IL. The JIT and // type loader have special handling for it that turns it into a thin wrapper around ref T. [StackOnly] + [System.Runtime.CompilerServices.DependencyReductionRoot] // TODO: put this in System.Private.ILToolchain contract instead internal struct ByReference { // CS0169: The private field '{blah}' is never used diff --git a/src/System.Private.CoreLib/src/System/EETypePtr.cs b/src/System.Private.CoreLib/src/System/EETypePtr.cs index 239a1689471..3a1866ebf66 100644 --- a/src/System.Private.CoreLib/src/System/EETypePtr.cs +++ b/src/System.Private.CoreLib/src/System/EETypePtr.cs @@ -248,6 +248,14 @@ internal bool IsAbstract } } + internal bool IsByRefLike + { + get + { + return _value->IsByRefLike; + } + } + internal bool IsNullable { get diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionRootAttribute.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionRootAttribute.cs index 8f2cb272c8f..417cdb8417c 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionRootAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionRootAttribute.cs @@ -6,7 +6,7 @@ namespace System.Runtime.CompilerServices { // When applied to a type this custom attribute will cause the type to become a new dependency // reduction root. - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public class DependencyReductionRootAttribute : Attribute { } diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 5d113ea328e..cf2b29ef426 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -271,6 +271,11 @@ public static object GetUninitializedObject(Type type) throw new MemberAccessException(SR.Acc_CreateAbst); } + if (eeTypePtr.IsByRefLike) + { + throw new NotSupportedException(SR.NotSupported_ByRefLike); + } + if (eeTypePtr.IsNullable) { return GetUninitializedObject(ReflectionCoreNonPortable.GetRuntimeTypeForEEType(eeTypePtr.NullableType)); diff --git a/src/System.Private.CoreLib/src/System/TypedReference.cs b/src/System.Private.CoreLib/src/System/TypedReference.cs index 603113a5270..3f52e840cfd 100644 --- a/src/System.Private.CoreLib/src/System/TypedReference.cs +++ b/src/System.Private.CoreLib/src/System/TypedReference.cs @@ -11,21 +11,41 @@ using Internal.Runtime.Augments; using Internal.Reflection.Augments; +#if CORERT +namespace System +#else +// Add a fake TypedReference to keep Project X running with CoreRT's type system that needs this now. +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + [System.Runtime.CompilerServices.DependencyReductionRoot] // TODO: proper fix is to put this in the ILToolchain contract + internal struct TypedReference + { + } +} + namespace System.Reflection //@TODO: Intentionally placing TypedReference in the wrong namespace to work around NUTC's inability to handle ELEMENT_TYPE_TYPEDBYREF. +#endif { [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] public struct TypedReference { // Do not change the ordering of these fields. The JIT has a dependency on this layout. +#if CORERT + private readonly ByReference _value; +#else private readonly ByReferenceOfByte _value; +#endif private readonly RuntimeTypeHandle _typeHandle; private TypedReference(object target, int offset, RuntimeTypeHandle typeHandle) { - //@todo: Once ByReference is fixed, uncomment the following line and delete the one after it. - //_value = new ByReference(ref Unsafe.Add(ref target.GetRawData(), offset)); +#if CORERT + _value = new ByReference(ref Unsafe.Add(ref target.GetRawData(), offset)); +#else _value = new ByReferenceOfByte(target, offset); +#endif _typeHandle = typeHandle; } @@ -46,6 +66,11 @@ public static RuntimeTypeHandle TargetTypeToken(TypedReference value) return value._typeHandle; } + internal static RuntimeTypeHandle RawTargetTypeToken(TypedReference value) + { + return value._typeHandle; + } + public static object ToObject(TypedReference value) { RuntimeTypeHandle typeHandle = value._typeHandle; diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index c79ee408e22..a91b50cc092 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -171,6 +171,7 @@ internal unsafe class EETypeCreator ushort runtimeInterfacesLength = 0; bool isGenericEETypeDef = false; bool isAbstractClass; + bool isByRefLike; #if EETYPE_TYPE_MANAGER IntPtr typeManager = IntPtr.Zero; #endif @@ -194,6 +195,7 @@ internal unsafe class EETypeCreator isArray = pTemplateEEType->IsArray; isGeneric = pTemplateEEType->IsGeneric; isAbstractClass = pTemplateEEType->IsAbstract && !pTemplateEEType->IsInterface; + isByRefLike = pTemplateEEType->IsByRefLike; #if EETYPE_TYPE_MANAGER typeManager = pTemplateEEType->PointerToTypeManager; #endif @@ -214,6 +216,7 @@ internal unsafe class EETypeCreator isGeneric = false; isGenericEETypeDef = true; isAbstractClass = false; + isByRefLike = false; componentSize = checked((ushort)state.TypeBeingBuilt.Instantiation.Length); baseSize = 0; } @@ -230,6 +233,8 @@ internal unsafe class EETypeCreator && ((MetadataType)state.TypeBeingBuilt).IsAbstract && !state.TypeBeingBuilt.IsInterface; + isByRefLike = (state.TypeBeingBuilt is DefType) && ((DefType)state.TypeBeingBuilt).IsByRefLike; + if (state.TypeBeingBuilt.HasVariance) { state.GenericVarianceFlags = new int[state.TypeBeingBuilt.Instantiation.Length]; @@ -328,6 +333,11 @@ internal unsafe class EETypeCreator else rareFlags &= ~(uint)EETypeRareFlags.IsAbstractClassFlag; + if (isByRefLike) + rareFlags |= (uint)EETypeRareFlags.IsByRefLikeFlag; + else + rareFlags &= ~(uint)EETypeRareFlags.IsByRefLikeFlag; + rareFlags |= (uint)EETypeRareFlags.HasDynamicModuleFlag; optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags); diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs index 39f65ed78cd..77ca83760e4 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs @@ -37,6 +37,23 @@ public unsafe override bool ComputeContainsGCPointers(DefType type) } } + public unsafe override bool ComputeIsByRefLike(DefType type) + { + if (type.IsTemplateCanonical()) + { + return type.ComputeTemplate().RuntimeTypeHandle.ToEETypePtr()->IsByRefLike; + } + else + { + if (type.RetrieveRuntimeTypeHandleIfPossible()) + { + return type.RuntimeTypeHandle.ToEETypePtr()->IsByRefLike; + } + + throw new NotImplementedException(); + } + } + public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType type, InstanceLayoutKind layoutKind) { if (!type.IsTemplateUniversal() && (layoutKind == InstanceLayoutKind.TypeOnly)) diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs index 973b1aaaa86..63b4bad5a94 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs @@ -23,6 +23,11 @@ public unsafe override bool ComputeContainsGCPointers(DefType type) return type.RuntimeTypeHandle.ToEETypePtr()->HasGCPointers; } + public unsafe override bool ComputeIsByRefLike(DefType type) + { + return type.RuntimeTypeHandle.ToEETypePtr()->IsByRefLike; + } + /// /// Reads the minimal information about type layout encoded in the /// EEType. That doesn't include field information.