|
|
@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
// Thus, we have to ensure we're operating on an r-value.
|
|
|
|
// Thus, we have to ensure we're operating on an r-value.
|
|
|
|
// Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
|
|
|
|
// Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
|
|
|
|
// of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
|
|
|
|
// of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
|
|
|
|
if (IsUsedAsThisPointerInCall(loadInst, out var method, out var constrainedTo) || IsPassedToInParameter(loadInst, out method)) |
|
|
|
if (IsUsedAsThisPointerInCall(loadInst, out var method, out var constrainedTo)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (options.HasFlag(InliningOptions.Aggressive)) |
|
|
|
if (options.HasFlag(InliningOptions.Aggressive)) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -326,6 +326,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
throw new InvalidOperationException("invalid expression classification"); |
|
|
|
throw new InvalidOperationException("invalid expression classification"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
else if (IsPassedToReadOnlySpanOfCharCtor(loadInst)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Always inlining is possible here, because it's an 'in' or 'ref readonly' parameter
|
|
|
|
|
|
|
|
// and the C# compiler allows calling it with an rvalue, even though that might produce
|
|
|
|
|
|
|
|
// a warning. Note that we don't need to check the expression classification, because
|
|
|
|
|
|
|
|
// expressionBuilder.VisitAddressOf will handle creating the copy for us.
|
|
|
|
|
|
|
|
// This is necessary, because there are compiler-generated uses of this ctor when
|
|
|
|
|
|
|
|
// concatenating a string to a char and our following transforms assume the char is
|
|
|
|
|
|
|
|
// already inlined.
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (IsPassedToInParameter(loadInst)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (options.HasFlag(InliningOptions.Aggressive)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Inlining might be required in ctor initializers (see #2714).
|
|
|
|
|
|
|
|
// expressionBuilder.VisitAddressOf will handle creating the copy for us.
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (ClassifyExpression(inlinedExpression)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
case ExpressionClassification.RValue: |
|
|
|
|
|
|
|
// For rvalues passed to in parameters, the C# compiler generates a temporary.
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
case ExpressionClassification.MutableLValue: |
|
|
|
|
|
|
|
case ExpressionClassification.ReadonlyLValue: |
|
|
|
|
|
|
|
// For lvalues passed to in parameters, the C# compiler never generates temporaries.
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
throw new InvalidOperationException("invalid expression classification"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
else if (IsUsedAsThisPointerInFieldRead(loadInst)) |
|
|
|
else if (IsUsedAsThisPointerInFieldRead(loadInst)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// mcs generated temporaries for field reads on rvalues (#1555)
|
|
|
|
// mcs generated temporaries for field reads on rvalues (#1555)
|
|
|
@ -415,17 +448,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return inst != ldloca && inst.Parent is LdObj; |
|
|
|
return inst != ldloca && inst.Parent is LdObj; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static bool IsPassedToInParameter(LdLoca ldloca, out IMethod method) |
|
|
|
static bool IsPassedToInParameter(LdLoca ldloca) |
|
|
|
{ |
|
|
|
{ |
|
|
|
method = null; |
|
|
|
|
|
|
|
if (ldloca.Parent is not CallInstruction call) |
|
|
|
if (ldloca.Parent is not CallInstruction call) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
method = call.Method; |
|
|
|
|
|
|
|
return call.GetParameter(ldloca.ChildIndex)?.ReferenceKind is ReferenceKind.In; |
|
|
|
return call.GetParameter(ldloca.ChildIndex)?.ReferenceKind is ReferenceKind.In; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool IsPassedToReadOnlySpanOfCharCtor(LdLoca ldloca) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (ldloca.Parent is not NewObj call) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return IsReadOnlySpanCharCtor(call.Method); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal static bool IsReadOnlySpanCharCtor(IMethod method) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return method.IsConstructor |
|
|
|
|
|
|
|
&& method.Parameters.Count == 1 |
|
|
|
|
|
|
|
&& method.DeclaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) |
|
|
|
|
|
|
|
&& method.DeclaringType.TypeArguments[0].IsKnownType(KnownTypeCode.Char) |
|
|
|
|
|
|
|
&& method.Parameters[0].Type is ByReferenceType brt |
|
|
|
|
|
|
|
&& brt.ElementType.IsKnownType(KnownTypeCode.Char); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets whether the instruction, when converted into C#, turns into an l-value that can
|
|
|
|
/// Gets whether the instruction, when converted into C#, turns into an l-value that can
|
|
|
|
/// be used to mutate a value-type.
|
|
|
|
/// be used to mutate a value-type.
|
|
|
|