-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement support for UnsafeAccessor
in the trimmer
#88268
Merged
+1,345
−2
Merged
Changes from all commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
200b988
First
vitek-karas bc3dac0
Methods
vitek-karas 3983540
Refactoring and true method resolution
vitek-karas aa8b53a
Progress
vitek-karas 27d4674
More tests
vitek-karas 7725027
Tests
vitek-karas 7bede4d
Tests
vitek-karas c83be6f
More
vitek-karas 2e62970
Fixups after a recent fix in CoreCLR implementation
vitek-karas b7e1484
Revert to method group marking
vitek-karas b4fea74
Resolve inhertiance behavior inconsistencies
vitek-karas 95f32b2
Simplify the code
vitek-karas 8f2f52e
Add support for fields
vitek-karas 686528a
Methods on value types
vitek-karas 6f1ee84
Fields on value types
vitek-karas ca883bc
Requires tests
vitek-karas c7aaabc
DAM and UnsafeAccessor tests
vitek-karas 93b53a3
Formatting
vitek-karas 9fd8178
Formatting
vitek-karas e886374
Update src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs
vitek-karas c623196
Linker tests use both XUnit and VSTest format
agocke 0b023e0
Add enablePublishTestResults
agocke 820f14c
Fix API compat - hide the redefined BCL type
vitek-karas 0738762
Exclude the new tests on mono
vitek-karas b70becc
First
vitek-karas cb6f71f
Methods
vitek-karas 6638a05
Refactoring and true method resolution
vitek-karas 75974af
Progress
vitek-karas 6722a03
More tests
vitek-karas 6376210
Tests
vitek-karas 6e439de
Tests
vitek-karas a0b2295
More
vitek-karas 43726d7
Fixups after a recent fix in CoreCLR implementation
vitek-karas 74a5ee5
Revert to method group marking
vitek-karas 47d82d0
Resolve inhertiance behavior inconsistencies
vitek-karas 74d57c0
Simplify the code
vitek-karas 4f945d4
Add support for fields
vitek-karas 13972ca
Methods on value types
vitek-karas 4ffa95d
Fields on value types
vitek-karas e9fcb68
Requires tests
vitek-karas ce9376b
DAM and UnsafeAccessor tests
vitek-karas f2c595a
Formatting
vitek-karas 50a219a
Formatting
vitek-karas 623a2f0
Update src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs
vitek-karas 906ccb9
Linker tests use both XUnit and VSTest format
agocke 7dbca19
Add enablePublishTestResults
agocke 45cd27a
Fix API compat - hide the redefined BCL type
vitek-karas 58741fb
Exclude the new tests on mono
vitek-karas 344037f
Merge branch 'main' into TrimmerUnsafeAccessor
lambdageek ec66190
[mono] uncomment passing tests
lambdageek 22023ed
Merge branch 'TrimmerUnsafeAccessor' of https://github.com/vitek-kara…
vitek-karas 9d5e30f
Fixes after merge with main
vitek-karas b238760
Update src/tools/illink/src/linker/Linker.Steps/UnsafeAccessorMarker.cs
vitek-karas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
src/tools/illink/src/linker/Linker.Steps/UnsafeAccessorMarker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System.Runtime.CompilerServices; | ||
using Mono.Cecil; | ||
|
||
namespace Mono.Linker.Steps | ||
{ | ||
// This class only handles static methods (all the unsafe accessors should be static) | ||
// so there's no problem with forgetting the implicit "this". | ||
#pragma warning disable RS0030 // MethodReference.Parameters is banned | ||
|
||
readonly struct UnsafeAccessorMarker (LinkContext context, MarkStep markStep) | ||
{ | ||
readonly LinkContext _context = context; | ||
readonly MarkStep _markStep = markStep; | ||
|
||
// We don't perform method overload resolution based on list of parameters (or return type) for now | ||
// Mono.Cecil's method resolution is problematic and has bugs. It's also not extensible | ||
// and we would need that to correctly implement the desired behavior around custom modifiers. So for now we decided to not | ||
// duplicate the logic to tweak it and will just mark entire method groups. | ||
|
||
public void ProcessUnsafeAccessorMethod (MethodDefinition method) | ||
{ | ||
if (!method.IsStatic || !method.HasCustomAttributes) | ||
return; | ||
|
||
foreach (CustomAttribute customAttribute in method.CustomAttributes) { | ||
if (customAttribute.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.UnsafeAccessorAttribute") { | ||
if (customAttribute.HasConstructorArguments && customAttribute.ConstructorArguments[0].Value is int kindValue) { | ||
UnsafeAccessorKind kind = (UnsafeAccessorKind) kindValue; | ||
string? name = null; | ||
if (customAttribute.HasProperties) { | ||
foreach (CustomAttributeNamedArgument prop in customAttribute.Properties) { | ||
if (prop.Name == "Name") { | ||
name = prop.Argument.Value as string; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
switch (kind) { | ||
case UnsafeAccessorKind.Constructor: | ||
ProcessConstructorAccessor (method, name); | ||
break; | ||
case UnsafeAccessorKind.StaticMethod: | ||
ProcessMethodAccessor (method, name, isStatic: true); | ||
break; | ||
case UnsafeAccessorKind.Method: | ||
ProcessMethodAccessor (method, name, isStatic: false); | ||
break; | ||
case UnsafeAccessorKind.StaticField: | ||
ProcessFieldAccessor (method, name, isStatic: true); | ||
break; | ||
case UnsafeAccessorKind.Field: | ||
ProcessFieldAccessor (method, name, isStatic: false); | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
// Intentionally only process the first such attribute | ||
// if there's more than one runtime will fail on it anyway. | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
void ProcessConstructorAccessor (MethodDefinition method, string? name) | ||
{ | ||
// A return type is required for a constructor, otherwise | ||
// we don't know the type to construct. | ||
// Types should not be parameterized (that is, by-ref). | ||
// The name is defined by the runtime and should be empty. | ||
if (method.ReturnsVoid () || method.ReturnType.IsByRefOrPointer () || !string.IsNullOrEmpty (name)) | ||
return; | ||
|
||
if (_context.TryResolve (method.ReturnType) is not TypeDefinition targetType) | ||
return; | ||
|
||
foreach (MethodDefinition targetMethod in targetType.Methods) { | ||
if (!targetMethod.IsConstructor || targetMethod.IsStatic) | ||
continue; | ||
|
||
_markStep.MarkMethodVisibleToReflection (targetMethod, new DependencyInfo (DependencyKind.UnsafeAccessorTarget, method), new MessageOrigin (method)); | ||
} | ||
} | ||
|
||
void ProcessMethodAccessor (MethodDefinition method, string? name, bool isStatic) | ||
{ | ||
// Method access requires a target type. | ||
if (method.Parameters.Count == 0) | ||
return; | ||
|
||
if (string.IsNullOrEmpty (name)) | ||
name = method.Name; | ||
|
||
TypeReference targetTypeReference = method.Parameters[0].ParameterType; | ||
if (_context.TryResolve (targetTypeReference) is not TypeDefinition targetType) | ||
return; | ||
|
||
if (!isStatic && targetType.IsValueType && !targetTypeReference.IsByReference) | ||
return; | ||
|
||
foreach (MethodDefinition targetMethod in targetType.Methods) { | ||
if (targetMethod.Name != name || targetMethod.IsStatic != isStatic) | ||
continue; | ||
|
||
_markStep.MarkMethodVisibleToReflection (targetMethod, new DependencyInfo (DependencyKind.UnsafeAccessorTarget, method), new MessageOrigin (method)); | ||
} | ||
} | ||
|
||
void ProcessFieldAccessor (MethodDefinition method, string? name, bool isStatic) | ||
{ | ||
// Field access requires exactly one parameter | ||
if (method.Parameters.Count != 1) | ||
return; | ||
|
||
if (string.IsNullOrEmpty (name)) | ||
name = method.Name; | ||
|
||
if (!method.ReturnType.IsByReference) | ||
return; | ||
|
||
TypeReference targetTypeReference = method.Parameters[0].ParameterType; | ||
if (_context.TryResolve (targetTypeReference) is not TypeDefinition targetType) | ||
return; | ||
|
||
if (!isStatic && targetType.IsValueType && !targetTypeReference.IsByReference) | ||
return; | ||
|
||
foreach (FieldDefinition targetField in targetType.Fields) { | ||
if (targetField.Name != name || targetField.IsStatic != isStatic) | ||
continue; | ||
|
||
_markStep.MarkFieldVisibleToReflection (targetField, new DependencyInfo (DependencyKind.UnsafeAccessorTarget, method), new MessageOrigin (method)); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also test calling a virtual (abstract?) instance method of a base class on a derived object? (ie: unsafe accessor emits a
callvirt
, not acall
)Should we test that looking for a virtual (non-virtual?) instance method defined in a base class but using a derived class as the unsafe accessor target will work?