Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 23 additions & 10 deletions src/linker/Linker.Dataflow/MethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ protected virtual void Scan (MethodBody methodBody, ref InterproceduralState int

case Code.Stfld:
case Code.Stsfld:
ScanStfld (operation, currentStack, thisMethod, methodBody, ref interproceduralState);
ScanStfld (operation, currentStack, thisMethod, methodBody, locals, ref interproceduralState);
break;

case Code.Cpobj:
Expand All @@ -558,7 +558,7 @@ protected virtual void Scan (MethodBody methodBody, ref InterproceduralState int
case Code.Stind_R8:
case Code.Stind_Ref:
case Code.Stobj:
ScanIndirectStore (operation, currentStack, methodBody, locals, curBasicBlock);
ScanIndirectStore (operation, currentStack, methodBody, locals, curBasicBlock, ref interproceduralState);
break;

case Code.Initobj:
Expand Down Expand Up @@ -863,12 +863,13 @@ private void ScanIndirectStore (
Stack<StackSlot> currentStack,
MethodBody methodBody,
LocalVariableStore locals,
int curBasicBlock)
int curBasicBlock,
ref InterproceduralState ipState)
{
StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset);
StackSlot destination = PopUnknown (currentStack, 1, methodBody, operation.Offset);

StoreInReference (destination.Value, valueToStore.Value, methodBody.Method, operation, locals, curBasicBlock);
StoreInReference (destination.Value, valueToStore.Value, methodBody.Method, operation, locals, curBasicBlock, ref ipState);
}

/// <summary>
Expand All @@ -879,7 +880,7 @@ private void ScanIndirectStore (
/// <param name="method">The method body that contains the operation causing the store</param>
/// <param name="operation">The instruction causing the store</param>
/// <exception cref="LinkerFatalErrorException">Throws if <paramref name="target"/> is not a valid target for an indirect store.</exception>
protected void StoreInReference (MultiValue target, MultiValue source, MethodDefinition method, Instruction operation, LocalVariableStore locals, int curBasicBlock)
protected void StoreInReference (MultiValue target, MultiValue source, MethodDefinition method, Instruction operation, LocalVariableStore locals, int curBasicBlock, ref InterproceduralState ipState)
{
foreach (var value in target) {
switch (value) {
Expand All @@ -902,6 +903,9 @@ when GetMethodParameterValue (parameterReference.MethodDefinition, parameterRefe
// Ref returns don't have special ReferenceValue values, so assume if the target here is a MethodReturnValue then it must be a ref return value
HandleStoreMethodReturnValue (method, methodReturnValue, operation, source);
break;
case FieldValue fieldValue:
HandleStoreField (method, fieldValue, operation, DereferenceValue (source, locals, ref ipState));
break;
case IValueWithStaticType valueWithStaticType:
if (valueWithStaticType.StaticType is not null && _context.Annotations.FlowAnnotations.IsTypeInterestingForDataflow (valueWithStaticType.StaticType))
throw new LinkerFatalErrorException (MessageContainer.CreateErrorMessage (
Expand Down Expand Up @@ -971,6 +975,7 @@ private void ScanStfld (
Stack<StackSlot> currentStack,
MethodDefinition thisMethod,
MethodBody methodBody,
LocalVariableStore locals,
ref InterproceduralState interproceduralState)
{
StackSlot valueToStoreSlot = PopUnknown (currentStack, 1, methodBody, operation.Offset);
Expand All @@ -990,7 +995,10 @@ private void ScanStfld (
if (value is not FieldValue fieldValue)
continue;

HandleStoreField (thisMethod, fieldValue, operation, valueToStoreSlot.Value);
// Incomplete handling of ref fields -- if we're storing a reference to a value, pretend it's just the value
MultiValue valueToStore = DereferenceValue (valueToStoreSlot.Value, locals, ref interproceduralState);

HandleStoreField (thisMethod, fieldValue, operation, valueToStore);
}
}
}
Expand Down Expand Up @@ -1034,7 +1042,7 @@ private ValueNodeList PopCallArguments (
return methodParams;
}

internal MultiValue DereferenceValue (MultiValue maybeReferenceValue, Dictionary<VariableDefinition, ValueBasicBlockPair> locals, ref InterproceduralState interproceduralState)
internal MultiValue DereferenceValue (MultiValue maybeReferenceValue, LocalVariableStore locals, ref InterproceduralState interproceduralState)
{
MultiValue dereferencedValue = MultiValueLattice.Top;
foreach (var value in maybeReferenceValue) {
Expand All @@ -1059,6 +1067,10 @@ internal MultiValue DereferenceValue (MultiValue maybeReferenceValue, Dictionary
break;
case ReferenceValue referenceValue:
throw new NotImplementedException ($"Unhandled dereference of ReferenceValue of type {referenceValue.GetType ().FullName}");
// Incomplete handling for ref values
case FieldValue fieldValue:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this case? I think it's the same as the default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's probably not necessary, but I thought it might be nice to have the case just to point out that it's not fully / correctly implemented. I can remove it if you feel like it's just noise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I am fine with it as-is.

dereferencedValue = MultiValue.Meet (dereferencedValue, fieldValue);
break;
default:
dereferencedValue = MultiValue.Meet (dereferencedValue, value);
break;
Expand All @@ -1076,7 +1088,8 @@ protected void AssignRefAndOutParameters (
ValueNodeList methodArguments,
Instruction operation,
LocalVariableStore locals,
int curBasicBlock)
int curBasicBlock,
ref InterproceduralState ipState)
{
MethodDefinition? calledMethodDefinition = _context.Resolve (calledMethod);
bool methodIsResolved = calledMethodDefinition is not null;
Expand All @@ -1088,7 +1101,7 @@ protected void AssignRefAndOutParameters (
SingleValue newByRefValue = methodIsResolved
? _context.Annotations.FlowAnnotations.GetMethodParameterValue (calledMethodDefinition!, parameterIndex)
: UnknownValue.Instance;
StoreInReference (methodArguments[ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock);
StoreInReference (methodArguments[ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState);
}
}

Expand Down Expand Up @@ -1135,7 +1148,7 @@ private void HandleCall (
if (isNewObj || !calledMethod.ReturnsVoid ())
currentStack.Push (new StackSlot (methodReturnValue));

AssignRefAndOutParameters (callingMethodBody, calledMethod, methodArguments, operation, locals, curBasicBlock);
AssignRefAndOutParameters (callingMethodBody, calledMethod, methodArguments, operation, locals, curBasicBlock, ref interproceduralState);

foreach (var param in methodArguments) {
foreach (var v in param) {
Expand Down
43 changes: 19 additions & 24 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3503,18 +3503,8 @@ bool CheckRequiresReflectionMethodBodyScanner (MethodBody body)
foreach (Instruction instruction in body.Instructions) {
switch (instruction.OpCode.OperandType) {
case OperandType.InlineField:
switch (instruction.OpCode.Code) {
case Code.Stfld:
case Code.Stsfld:
case Code.Ldflda:
case Code.Ldsflda:
if (ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand))
return true;
break;

default:
break;
}
if (InstructionRequiresReflectionMethodBodyScannerForFieldAccess (instruction))
return true;
break;

case OperandType.InlineMethod:
Expand Down Expand Up @@ -3595,22 +3585,27 @@ void MarkInterfacesNeededByBodyStack (MethodBody body)
MarkInterfaceImplementation (implementation, new MessageOrigin (type));
}

bool InstructionRequiresReflectionMethodBodyScannerForFieldAccess (Instruction instruction)
=> instruction.OpCode.Code switch {
// Field stores (Storing value to annotated field must be checked)
Code.Stfld or
Code.Stsfld or
// Field address loads (as those can be used to store values to annotated field and thus must be checked)
Code.Ldflda or
Code.Ldsflda
=> ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand),
// For ref fields, ldfld loads an address which can be used to store values to annotated fields
Code.Ldfld or Code.Ldsfld when ((FieldReference) instruction.Operand).FieldType.IsByRefOrPointer ()
=> ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand),
// Other field operations are not interesting as they don't need to be checked
_ => false
};

protected virtual void MarkInstruction (Instruction instruction, MethodDefinition method, ref bool requiresReflectionMethodBodyScanner)
{
switch (instruction.OpCode.OperandType) {
case OperandType.InlineField:
switch (instruction.OpCode.Code) {
case Code.Stfld: // Field stores (Storing value to annotated field must be checked)
case Code.Stsfld:
case Code.Ldflda: // Field address loads (as those can be used to store values to annotated field and thus must be checked)
case Code.Ldsflda:
requiresReflectionMethodBodyScanner |=
ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand);
break;

default: // Other field operations are not interesting as they don't need to be checked
break;
}
requiresReflectionMethodBodyScanner |= InstructionRequiresReflectionMethodBodyScannerForFieldAccess (instruction);

ScopeStack.UpdateCurrentScopeInstructionOffset (instruction.Offset);
MarkField ((FieldReference) instruction.Operand, new DependencyInfo (DependencyKind.FieldAccess, method), ScopeStack.CurrentScope.Origin);
Expand Down
6 changes: 6 additions & 0 deletions test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ public Task PropertyDataFlow ()
return RunTest (nameof (PropertyDataFlow));
}

[Fact]
public Task RefFieldDataFlow ()
{
return RunTest (nameof (RefFieldDataFlow));
}

[Fact (Skip = "https://github.com/dotnet/linker/issues/2273")]
public Task SuppressWarningWithLinkAttributes ()
{
Expand Down
41 changes: 41 additions & 0 deletions test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,52 @@ ref struct R
[Kept]
public ref int UnusedRefField;

[Kept]
public ref ReferencedType UnusedClass;

[Kept]
public ref ReferencedStruct UnusedStruct;

[Kept]
public ReferencedRefStruct UnusedRefStruct;

[Kept]
int UnusedField;

[Kept]
int UsedField;
}

[Kept]
struct ReferencedStruct
{
[Kept]
int UnusedField;

[Kept]
int UnusedField2;
}

[Kept]
[KeptAttributeAttribute (typeof (IsByRefLikeAttribute))]
[KeptAttributeAttribute (typeof (CompilerFeatureRequiredAttribute))]
[KeptAttributeAttribute (typeof (ObsoleteAttribute))]
ref struct ReferencedRefStruct
{
[Kept]
public ref int UnusedRefField;

[Kept]
public ref ReferencedType UnusedClass;

[Kept]
int UnusedField;
}

[Kept]
class ReferencedType
{
int field;
}
}
}
Loading