Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
1b0d06e
Add support for UnsafeAccessorTypeAttribute
AaronRobinsonMSFT Apr 19, 2025
e1f1d6d
Add lookup from private assembly.
AaronRobinsonMSFT Apr 19, 2025
a933432
Address the collectible assembly issue.
AaronRobinsonMSFT Apr 19, 2025
3848101
Testing.
AaronRobinsonMSFT Apr 19, 2025
925e862
Offline feedback.
AaronRobinsonMSFT Apr 20, 2025
840a54b
Update comment.
AaronRobinsonMSFT Apr 20, 2025
d352abd
Add mono runtime support
AaronRobinsonMSFT Apr 21, 2025
6476d43
Mono
AaronRobinsonMSFT Apr 21, 2025
728c336
Remove unused local.
AaronRobinsonMSFT Apr 21, 2025
6e316b6
Add NativeAOT support
jkoritzinsky Apr 21, 2025
b87d916
Merge branch 'unsafeaccessortype_impl' of https://github.com/AaronRob…
jkoritzinsky Apr 21, 2025
3a84360
Add linker support
jkoritzinsky Apr 21, 2025
e824981
Copy custom modifiers should work on N.
AaronRobinsonMSFT Apr 21, 2025
c6df90b
Add ref return test.
AaronRobinsonMSFT Apr 22, 2025
63bfc45
Additional tests.
AaronRobinsonMSFT Apr 22, 2025
23bb9b8
Remove calling convention mask.
AaronRobinsonMSFT Apr 22, 2025
4b5a47d
Fix windows build break.
AaronRobinsonMSFT Apr 22, 2025
29c5ef8
Merge branch 'unsafeaccessortype_impl' of https://github.com/AaronRob…
jkoritzinsky Apr 22, 2025
c543349
Make SigPointer's Copy functions consistent.
AaronRobinsonMSFT Apr 22, 2025
5774895
Remove duplicate throw helper.
AaronRobinsonMSFT Apr 22, 2025
aab24c9
[CoreCLR] Generic support for UnsafeAccessorType
AaronRobinsonMSFT Apr 22, 2025
51f3651
Add tests.
AaronRobinsonMSFT Apr 22, 2025
d71b5fd
Fix generics support in NativeAOT
jkoritzinsky Apr 22, 2025
a3b8e89
Use canonical vs typical methodtable.
AaronRobinsonMSFT Apr 23, 2025
d0de954
Update format to respect IL syntax for generics ("!N" and "!!N").
AaronRobinsonMSFT Apr 28, 2025
e5d3794
Fix build breaks.
AaronRobinsonMSFT Apr 28, 2025
7097c73
Fix build break on linux
AaronRobinsonMSFT Apr 28, 2025
db756dc
Enable bound generics with private types.
AaronRobinsonMSFT Apr 28, 2025
081e4be
Merge remote-tracking branch 'upstream/main' into unsafeaccessortype_…
AaronRobinsonMSFT Apr 28, 2025
2729f35
Support unbound generics in NAOT.
jkoritzinsky Apr 29, 2025
6a6e616
Fix crossgen usage of updated API
jkoritzinsky Apr 29, 2025
d2dd033
Restructure to avoid having to deal with .NET Standard-targeting ILVe…
jkoritzinsky Apr 29, 2025
baab3ea
Handle byrefs and pointers as type name.
AaronRobinsonMSFT May 1, 2025
fca0b95
Require UnsafeAccessorType to define full signature as a string.
AaronRobinsonMSFT May 3, 2025
5388fc8
Style/nits and added tests
AaronRobinsonMSFT May 3, 2025
1008e98
Adjust ref handling in NAOT and add type check for values (skipping r…
jkoritzinsky May 5, 2025
3f8f22b
Implement byref type validation with a local for NAOT
jkoritzinsky May 5, 2025
02ae9f3
Address type safety issue by passing
AaronRobinsonMSFT May 5, 2025
9b1193e
Use typed enum.
AaronRobinsonMSFT May 5, 2025
0da4049
Const and nits
AaronRobinsonMSFT May 5, 2025
dc97df8
Merge remote-tracking branch 'upstream/main' into unsafeaccessortype_…
AaronRobinsonMSFT May 6, 2025
2e56766
Array support.
AaronRobinsonMSFT May 6, 2025
b46266c
Remove now-incorrect generics handling and fix handling of invalid ge…
jkoritzinsky May 6, 2025
0d855f8
Adjust handling for "ref object" parameters
jkoritzinsky May 6, 2025
b7ce59b
Add missing case
jkoritzinsky May 6, 2025
2a04159
Enforce signature validation explicitly for certain types.
AaronRobinsonMSFT May 7, 2025
8cde6a1
Merge branch 'unsafeaccessortype_impl' of https://github.com/AaronRob…
AaronRobinsonMSFT May 7, 2025
149c159
Update to use unbox.any instead of castclass. The unbox.any
AaronRobinsonMSFT May 7, 2025
3f224dc
Fix mono
AaronRobinsonMSFT May 7, 2025
174f886
CoreCLR nits
AaronRobinsonMSFT May 7, 2025
2eb1b4b
Update NAOT handling
jkoritzinsky May 7, 2025
15e93ee
Merge branch 'unsafeaccessortype_impl' of github.com:AaronRobinsonMSF…
jkoritzinsky May 7, 2025
a7d9c69
PR feedback
jkoritzinsky May 8, 2025
2fb3b8a
[CoreCLR]
AaronRobinsonMSFT May 8, 2025
5fba0a3
Update mono to block ref object on method return.
AaronRobinsonMSFT May 8, 2025
a91035c
Use helper function.
AaronRobinsonMSFT May 8, 2025
93f6f68
Address arrays
AaronRobinsonMSFT May 10, 2025
482f517
Remove unnecessary compare logic.
AaronRobinsonMSFT May 10, 2025
e8ca051
Remove unnecessary changes.
AaronRobinsonMSFT May 10, 2025
4c26d4e
Nits
AaronRobinsonMSFT May 10, 2025
3cfee4c
Remove specific Array branch.
AaronRobinsonMSFT May 10, 2025
27f9eb2
PR feedback
jkoritzinsky May 12, 2025
68f8070
Add UnsafeAccessorType tests
jkoritzinsky May 12, 2025
393cb4a
Handle null ReplacedSignatureElements
jkoritzinsky May 12, 2025
f609f62
Fix NAOT trimming test
jkoritzinsky May 13, 2025
5f3129e
Update API documentation.
AaronRobinsonMSFT May 13, 2025
d516333
Update comments.
AaronRobinsonMSFT May 13, 2025
be08886
Feedback
AaronRobinsonMSFT May 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
Expand All @@ -21,9 +22,12 @@ internal partial struct TypeNameResolver
private bool _extensibleParser;
private bool _requireAssemblyQualifiedName;
private bool _suppressContextualReflectionContext;
private IntPtr _unsafeAccessorMethod;
private Assembly? _requestingAssembly;
private Assembly? _topLevelAssembly;

private bool SupportsUnboundGenerics { get => _unsafeAccessorMethod != IntPtr.Zero; }

[RequiresUnreferencedCode("The type might be removed")]
internal static Type? GetType(
string typeName,
Expand Down Expand Up @@ -128,14 +132,14 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,

// Used by VM
internal static unsafe RuntimeType? GetTypeHelper(char* pTypeName, RuntimeAssembly? requestingAssembly,
bool throwOnError, bool requireAssemblyQualifiedName)
bool throwOnError, bool requireAssemblyQualifiedName, IntPtr unsafeAccessorMethod)
{
ReadOnlySpan<char> typeName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pTypeName);
return GetTypeHelper(typeName, requestingAssembly, throwOnError, requireAssemblyQualifiedName);
return GetTypeHelper(typeName, requestingAssembly, throwOnError, requireAssemblyQualifiedName, unsafeAccessorMethod);
}

internal static unsafe RuntimeType? GetTypeHelper(ReadOnlySpan<char> typeName, RuntimeAssembly? requestingAssembly,
bool throwOnError, bool requireAssemblyQualifiedName)
bool throwOnError, bool requireAssemblyQualifiedName, IntPtr unsafeAccessorMethod = 0)
{
// Compat: Empty name throws TypeLoadException instead of
// the natural ArgumentException
Expand All @@ -158,6 +162,7 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
_throwOnError = throwOnError,
_suppressContextualReflectionContext = true,
_requireAssemblyQualifiedName = requireAssemblyQualifiedName,
_unsafeAccessorMethod = unsafeAccessorMethod,
}.Resolve(parsed);

if (type != null)
Expand Down Expand Up @@ -186,6 +191,9 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
return assembly;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "UnsafeAccessors_ResolveGenericParamToTypeHandle")]
private static partial IntPtr ResolveGenericParamToTypeHandle(IntPtr unsafeAccessorMethod, [MarshalAs(UnmanagedType.Bool)] bool isMethodParam, uint paramIndex);

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "TypeNameResolver.GetType is marked as RequiresUnreferencedCode.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
Expand Down Expand Up @@ -228,6 +236,42 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
{
if (assembly is null)
{
if (SupportsUnboundGenerics
&& !string.IsNullOrEmpty(escapedTypeName)
&& escapedTypeName[0] == '!')
{
Debug.Assert(_throwOnError); // Unbound generic support currently always throws.

// Parse the type as an unbound generic parameter. Following the common VAR/MVAR IL syntax:
// !<Number> - Represents a zero-based index into the type's generic parameters.
// !!<Number> - Represents a zero-based index into the method's generic parameters.

// Confirm we have at least one more character
if (escapedTypeName.Length == 1)
{
throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveType, escapedTypeName), typeName: escapedTypeName);
}

// At this point we expect either another '!' and then a number or a number.
bool isMethodParam = escapedTypeName[1] == '!';
ReadOnlySpan<char> toParse = isMethodParam
? escapedTypeName.AsSpan(2) // Skip over "!!"
: escapedTypeName.AsSpan(1); // Skip over "!"
if (!uint.TryParse(toParse, NumberStyles.None, null, out uint paramIndex))
{
throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveType, escapedTypeName), typeName: escapedTypeName);
}

Debug.Assert(_unsafeAccessorMethod != IntPtr.Zero);
IntPtr typeHandle = ResolveGenericParamToTypeHandle(_unsafeAccessorMethod, isMethodParam, paramIndex);
if (typeHandle == IntPtr.Zero)
{
throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveType, escapedTypeName), typeName: escapedTypeName);
}

return RuntimeTypeHandle.GetRuntimeTypeFromHandle(typeHandle);
}

if (_requireAssemblyQualifiedName)
{
if (_throwOnError)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/dlls/mscorrc/mscorrc.rc
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ BEGIN
BFA_BAD_FIELD_TOKEN "Field token out of range."
BFA_INVALID_FIELD_ACC_FLAGS "Invalid Field Access Flags."
BFA_INVALID_UNSAFEACCESSOR "Invalid usage of UnsafeAccessorAttribute."
BFA_INVALID_UNSAFEACCESSORTYPE "Invalid usage of UnsafeAccessorTypeAttribute."
BFA_INVALID_UNSAFEACCESSORTYPE_VALUETYPE "ValueTypes are not supported with UnsafeAccessorTypeAttribute."
BFA_FIELD_LITERAL_AND_INIT "Field is Literal and InitOnly."
BFA_NONSTATIC_GLOBAL_FIELD "Non-Static Global Field."
BFA_INSTANCE_FIELD_IN_INT "Instance Field in an Interface."
Expand Down
9 changes: 5 additions & 4 deletions src/coreclr/dlls/mscorrc/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,11 @@
#define BFA_BAD_TYPEREF_TOKEN 0x2046
#define BFA_BAD_CLASS_INT_CA_FORMAT 0x2048
#define BFA_BAD_COMPLUS_SIG 0x2049
#define BFA_BAD_ELEM_IN_SIZEOF 0x204b
#define BFA_IJW_IN_COLLECTIBLE_ALC 0x204c
#define BFA_INVALID_UNSAFEACCESSOR 0x204d

#define BFA_BAD_ELEM_IN_SIZEOF 0x204a
#define BFA_IJW_IN_COLLECTIBLE_ALC 0x204b
#define BFA_INVALID_UNSAFEACCESSOR 0x204c
#define BFA_INVALID_UNSAFEACCESSORTYPE 0x204d
#define BFA_INVALID_UNSAFEACCESSORTYPE_VALUETYPE 0x204e
#define IDS_CLASSLOAD_INTERFACE_NO_ACCESS 0x204f

#define BFA_BAD_CA_HEADER 0x2050
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static class CustomAttributeTypeNameParser
/// This is the inverse of what <see cref="CustomAttributeTypeNameFormatter"/> does.
/// </summary>
public static TypeDesc GetTypeByCustomAttributeTypeName(this ModuleDesc module, string name, bool throwIfNotFound = true,
Func<ModuleDesc, string, MetadataType> canonResolver = null)
Func<ModuleDesc, string, TypeDesc> canonGenericResolver = null)
{
if (!TypeName.TryParse(name.AsSpan(), out TypeName parsed, s_typeNameParseOptions))
ThrowHelper.ThrowTypeLoadException(name, module);
Expand All @@ -31,7 +31,7 @@ public static TypeDesc GetTypeByCustomAttributeTypeName(this ModuleDesc module,
_context = module.Context,
_module = module,
_throwIfNotFound = throwIfNotFound,
_canonResolver = canonResolver
_canonGenericResolver = canonGenericResolver
}.Resolve(parsed);
}

Expand Down Expand Up @@ -91,7 +91,7 @@ private struct TypeNameResolver
internal TypeSystemContext _context;
internal ModuleDesc _module;
internal bool _throwIfNotFound;
internal Func<ModuleDesc, string, MetadataType> _canonResolver;
internal Func<ModuleDesc, string, TypeDesc> _canonGenericResolver;

internal List<ModuleDesc> _referencedModules;

Expand Down Expand Up @@ -136,30 +136,30 @@ private TypeDesc GetSimpleType(TypeName typeName)
}

ModuleDesc module = _module;
if (topLevelTypeName.AssemblyName != null)
if (topLevelTypeName.AssemblyName is not null)
{
module = _context.ResolveAssembly(typeName.AssemblyName, throwIfNotFound: _throwIfNotFound);
if (module == null)
return null;
}

if (module != null)
if (module is not null)
{
TypeDesc type = GetSimpleTypeFromModule(typeName, module);
if (type != null)
if (type is not null)
{
_referencedModules?.Add(module);
return type;
}
}

// If it didn't resolve and wasn't assembly-qualified, we also try core library
if (topLevelTypeName.AssemblyName == null)
{
// If it didn't resolve and wasn't assembly-qualified, we also try core library
if (module != _context.SystemModule)
{
TypeDesc type = GetSimpleTypeFromModule(typeName, _context.SystemModule);
if (type != null)
if (type is not null)
{
_referencedModules?.Add(_context.SystemModule);
return type;
Expand All @@ -184,9 +184,9 @@ private TypeDesc GetSimpleTypeFromModule(TypeName typeName, ModuleDesc module)

string fullName = TypeNameHelpers.Unescape(typeName.FullName);

if (_canonResolver != null)
if (_canonGenericResolver != null)
{
MetadataType canonType = _canonResolver(module, fullName);
TypeDesc canonType = _canonGenericResolver(module, fullName);
if (canonType != null)
return canonType;
}
Expand Down
Loading
Loading