diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
index 042ca5bc8d..adc8afa716 100644
--- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
@@ -971,7 +971,11 @@ enum CallTransformation
RequireTarget = 1,
RequireTypeArguments = 2,
NoOptionalArgumentAllowed = 4,
- All = 7
+ ///
+ /// Add calls to AsRefReadOnly for in parameters that did not have an explicit DirectionExpression yet.
+ ///
+ EnforceExplicitIn = 8,
+ All = 0xf,
}
private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
@@ -1122,6 +1126,11 @@ private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetai
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
}
+ else if ((allowedTransforms & CallTransformation.EnforceExplicitIn) != 0)
+ {
+ EnforceExplicitIn(argumentList.Arguments, argumentList.ExpectedParameters);
+ allowedTransforms &= ~CallTransformation.EnforceExplicitIn;
+ }
else
{
break;
@@ -1141,6 +1150,32 @@ private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetai
return transform;
}
+ private void EnforceExplicitIn(TranslatedExpression[] arguments, IParameter[] expectedParameters)
+ {
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ if (expectedParameters[i].ReferenceKind != ReferenceKind.In)
+ continue;
+ if (arguments[i].Expression is DirectionExpression)
+ continue;
+
+ arguments[i] = WrapInAsRefReadOnly(arguments[i]);
+ expressionBuilder.statementBuilder.EmitAsRefReadOnly = true;
+ }
+ }
+
+ private TranslatedExpression WrapInAsRefReadOnly(TranslatedExpression arg)
+ {
+ return new DirectionExpression(
+ FieldDirection.In,
+ new InvocationExpression {
+ Target = new IdentifierExpression("ILSpyHelper_AsRefReadOnly"),
+ Arguments = { arg.Expression }
+ }
+ ).WithRR(new ByReferenceResolveResult(arg.Type, ReferenceKind.In))
+ .WithoutILInstruction();
+ }
+
private bool IsPossibleExtensionMethodCallOnNull(IMethod method, IList arguments)
{
return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression;
diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index daa2cbe7fc..4dfc5f6583 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -47,6 +47,8 @@ sealed class StatementBuilder : ILVisitor
internal IType currentResultType;
internal bool currentIsIterator;
+ internal bool EmitAsRefReadOnly;
+
public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext,
ILFunction currentFunction, DecompilerSettings settings, DecompileRun decompileRun,
CancellationToken cancellationToken)
@@ -1367,6 +1369,32 @@ BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop)
{
var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop);
DeclareLocalFunctions(currentFunction, container, blockStatement);
+ if (currentFunction.Body == container)
+ {
+ if (EmitAsRefReadOnly)
+ {
+ var methodDecl = new MethodDeclaration();
+ if (settings.StaticLocalFunctions)
+ {
+ methodDecl.Modifiers = Modifiers.Static;
+ }
+
+ methodDecl.ReturnType = new ComposedType() { HasReadOnlySpecifier = true, HasRefSpecifier = true, BaseType = new SimpleType("T") };
+ methodDecl.Name = "ILSpyHelper_AsRefReadOnly";
+ methodDecl.TypeParameters.Add(new TypeParameterDeclaration("T"));
+ methodDecl.Parameters.Add(new ParameterDeclaration { ParameterModifier = ReferenceKind.In, Type = new SimpleType("T"), Name = "temp" });
+
+ methodDecl.Body = new BlockStatement();
+ methodDecl.Body.AddChild(new Comment(
+ "ILSpy generated this function to help ensure overload resolution can pick the overload using 'in'"),
+ Roles.Comment);
+ methodDecl.Body.Add(new ReturnStatement(new DirectionExpression(FieldDirection.Ref, new IdentifierExpression("temp"))));
+
+ blockStatement.Statements.Add(
+ new LocalFunctionDeclarationStatement(methodDecl)
+ );
+ }
+ }
return blockStatement;
}