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; }