Browse Source

#1151: Support for ref readonly locals.

pull/1726/head
Siegfried Pammer 6 years ago
parent
commit
9190515d8b
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 11
      ICSharpCode.Decompiler/IL/ILReader.cs
  5. 8
      ICSharpCode.Decompiler/IL/ILVariable.cs
  6. 19
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  7. 57
      ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs
  8. 7
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -105,6 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -105,6 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp
new SwitchOnStringTransform(),
new SwitchOnNullableTransform(),
new SplitVariables(), // split variables once again, because SwitchOnNullableTransform eliminates ldloca
new IntroduceRefReadOnlyModifierOnLocals(),
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
// Even though it's a post-order block-transform as most other transforms,

3
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -447,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -447,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else {
type = context.TypeSystemAstBuilder.ConvertType(v.Type);
}
if (v.ILVariable.IsRefReadOnly && type is ComposedType composedType && composedType.HasRefSpecifier) {
composedType.HasReadOnlySpecifier = true;
}
var vds = new VariableDeclarationStatement(type, v.Name, assignment.Right.Detach());
var init = vds.Variables.Single();
init.AddAnnotation(assignment.Left.GetResolveResult());

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -328,6 +328,7 @@ @@ -328,6 +328,7 @@
<Compile Include="IL\Transforms\HighLevelLoopTransform.cs" />
<Compile Include="IL\Transforms\NamedArgumentTransform.cs" />
<Compile Include="IL\Transforms\IntroduceDynamicTypeOnLocals.cs" />
<Compile Include="IL\Transforms\IntroduceRefReadOnlyModifierOnLocals.cs" />
<Compile Include="IL\Transforms\NullPropagationTransform.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Instructions\StringToInt.cs" />

11
ICSharpCode.Decompiler/IL/ILReader.cs

@ -190,12 +190,15 @@ namespace ICSharpCode.Decompiler.IL @@ -190,12 +190,15 @@ namespace ICSharpCode.Decompiler.IL
// and needs to be converted into a normally usable type.
declaringType = new ParameterizedType(declaringType, declaringType.TypeParameters);
}
parameterVariables[paramIndex++] = CreateILVariable(-1, declaringType, "this");
ILVariable ilVar = CreateILVariable(-1, declaringType, "this");
ilVar.IsRefReadOnly = declaringType.GetDefinition()?.IsReadOnly == true;
parameterVariables[paramIndex++] = ilVar;
}
while (paramIndex < parameterVariables.Length) {
IType type = method.Parameters[paramIndex - offset].Type;
string name = method.Parameters[paramIndex - offset].Name;
parameterVariables[paramIndex] = CreateILVariable(paramIndex - offset, type, name);
IParameter parameter = method.Parameters[paramIndex - offset];
ILVariable ilVar = CreateILVariable(paramIndex - offset, parameter.Type, parameter.Name);
ilVar.IsRefReadOnly = parameter.IsIn;
parameterVariables[paramIndex] = ilVar;
paramIndex++;
}
Debug.Assert(paramIndex == parameterVariables.Length);

8
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -132,6 +132,11 @@ namespace ICSharpCode.Decompiler.IL @@ -132,6 +132,11 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>
/// This variable is either a C# 7 'in' parameter or must be declared as 'ref readonly'.
/// </summary>
public bool IsRefReadOnly { get; internal set; }
/// <summary>
/// The index of the local variable or parameter (depending on Kind)
///
@ -371,6 +376,9 @@ namespace ICSharpCode.Decompiler.IL @@ -371,6 +376,9 @@ namespace ICSharpCode.Decompiler.IL
internal void WriteDefinitionTo(ITextOutput output)
{
if (IsRefReadOnly) {
output.Write("readonly ");
}
switch (Kind) {
case VariableKind.Local:
output.Write("local ");

19
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -329,7 +329,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -329,7 +329,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (inst.OpCode) {
case OpCode.LdLoc:
case OpCode.StLoc:
if (IsReadonlyRefLocal(((IInstructionWithVariableOperand)inst).Variable)) {
if (((IInstructionWithVariableOperand)inst).Variable.IsRefReadOnly) {
return ExpressionClassification.ReadonlyLValue;
} else {
return ExpressionClassification.MutableLValue;
@ -363,7 +363,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -363,7 +363,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private static bool IsReadonlyReference(ILInstruction addr)
internal static bool IsReadonlyReference(ILInstruction addr)
{
switch (addr) {
case LdFlda ldflda:
@ -372,7 +372,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -372,7 +372,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case LdsFlda ldsflda:
return ldsflda.Field.IsReadOnly;
case LdLoc ldloc:
return IsReadonlyRefLocal(ldloc.Variable);
return ldloc.Variable.IsRefReadOnly;
case Call call:
return call.Method.ReturnTypeIsRefReadOnly;
case AddressOf _:
@ -383,19 +383,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -383,19 +383,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private static bool IsReadonlyRefLocal(ILVariable variable)
{
if (variable.Kind == VariableKind.Parameter) {
if (variable.Index == -1) {
// this parameter in readonly struct
return variable.Function.Method?.DeclaringTypeDefinition?.IsReadOnly == true;
} else {
return variable.Function.Parameters[variable.Index.Value].IsIn;
}
}
return false;
}
/// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary>

57
ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
// Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class IntroduceRefReadOnlyModifierOnLocals : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
foreach (var variable in function.Variables) {
if (variable.Type.Kind != TypeKind.ByReference || variable.Kind == VariableKind.Parameter)
continue;
// ref readonly
if (IsUsedAsRefReadonly(variable)) {
variable.IsRefReadOnly = true;
continue;
}
}
}
/// <summary>
/// Infer ref readonly type from usage:
/// An ILVariable should be marked as readonly,
/// if it's a "by-ref-like" type and the initialized value is known to be readonly.
/// </summary>
bool IsUsedAsRefReadonly(ILVariable variable)
{
foreach (var store in variable.StoreInstructions.OfType<StLoc>()) {
if (ILInlining.IsReadonlyReference(store.Value))
return true;
}
return false;
}
}
}

7
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -194,7 +194,6 @@ namespace ICSharpCode.ILSpy @@ -194,7 +194,6 @@ namespace ICSharpCode.ILSpy
case "event":
case "extern":
case "override":
case "readonly":
case "sealed":
case "static":
case "virtual":
@ -203,6 +202,12 @@ namespace ICSharpCode.ILSpy @@ -203,6 +202,12 @@ namespace ICSharpCode.ILSpy
case "partial":
color = modifiersColor;
break;
case "readonly":
if (role == ComposedType.ReadonlyRole)
color = parameterModifierColor;
else
color = modifiersColor;
break;
case "checked":
case "unchecked":
color = checkedKeywordColor;

Loading…
Cancel
Save