Browse Source

Add instructions for modeling the "?." operator in the ILAst.

pull/1066/head
Daniel Grunwald 7 years ago
parent
commit
a8a852b742
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 121
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs
  4. 11
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  5. 12
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
  6. 2
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  7. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  8. 12
      ICSharpCode.Decompiler/IL/InstructionFlags.cs
  9. 140
      ICSharpCode.Decompiler/IL/Instructions.cs
  10. 10
      ICSharpCode.Decompiler/IL/Instructions.tt
  11. 9
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  12. 62
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  13. 5
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  14. 2
      doc/copyright.txt

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -65,6 +65,7 @@ @@ -65,6 +65,7 @@
<Compile Include="TestCases\ILPretty\Issue982.cs" />
<Compile Include="TestCases\Pretty\CS72_PrivateProtected.cs" />
<Compile Include="TestCases\Pretty\ExpressionTrees.cs" />
<Compile Include="TestCases\Pretty\NullPropagation.cs" />
<Compile Include="TestCases\Pretty\VariableNaming.cs" />
<Compile Include="TestCases\Pretty\VariableNamingWithoutSymbols.cs" />
<None Include="TestCases\ILPretty\Issue959.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -230,6 +230,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -230,6 +230,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test, Ignore]
public void NullPropagation([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{
var ilFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(cscOptions) + ".il";

121
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
class NullPropagation
{
class MyClass
{
public int IntVal;
public MyClass Field;
public MyClass Property { get; set; }
public MyClass Method(int arg) { return null; }
public MyClass this[int index] {
get { return null; }
}
public void Done()
{
}
}
struct MyStruct
{
public int IntVal;
public MyClass Field;
public MyStruct? Property1 { get { return null; } }
public MyStruct Property2 { get { return default; } }
public MyStruct? Method1(int arg) { return null; }
public MyStruct Method2(int arg) { return default; }
public MyStruct? this[int index] {
get { return null; }
}
public void Done()
{
}
}
int GetInt()
{
return 9;
}
string GetString()
{
return null;
}
MyClass GetMyClass()
{
return null;
}
MyStruct? GetMyStruct()
{
return null;
}
string Substring()
{
return GetString()?.Substring(GetInt());
}
void CallDone()
{
GetMyClass()?.Done();
GetMyClass()?.Field?.Done();
GetMyClass()?.Field.Done();
GetMyClass()?.Property?.Done();
GetMyClass()?.Property.Done();
GetMyClass()?.Method(GetInt())?.Done();
GetMyClass()?.Method(GetInt()).Done();
GetMyClass()?[GetInt()]?.Done();
GetMyClass()?[GetInt()].Done();
}
void CallDoneStruct()
{
GetMyStruct()?.Done();
GetMyStruct()?.Field?.Done();
GetMyStruct()?.Field.Done();
GetMyStruct()?.Property1?.Done();
GetMyStruct()?.Property2.Done();
GetMyStruct()?.Method1(GetInt())?.Done();
GetMyStruct()?.Method2(GetInt()).Done();
GetMyStruct()?[GetInt()]?.Done();
}
void RequiredParentheses()
{
(GetMyClass()?.Field).Done();
(GetMyClass()?.Method(GetInt())).Done();
(GetMyStruct()?.Property2)?.Done();
}
int? SumOfChains()
{
return GetMyClass()?.IntVal
+ GetMyClass()?.Field.IntVal
+ GetMyClass()?.Field?.IntVal
+ GetMyClass()?.Property.IntVal
+ GetMyClass()?.Property?.IntVal
+ GetMyClass()?.Method(GetInt()).IntVal
+ GetMyClass()?.Method(GetInt())?.IntVal
+ GetMyClass()?[GetInt()].IntVal
+ GetMyClass()?[GetInt()]?.IntVal;
}
int? SumOfChainsStruct()
{
return GetMyStruct()?.IntVal
+ GetMyStruct()?.Field.IntVal
+ GetMyStruct()?.Field?.IntVal
+ GetMyStruct()?.Property2.IntVal
+ GetMyStruct()?.Property1?.IntVal
+ GetMyStruct()?.Method2(GetInt()).IntVal
+ GetMyStruct()?.Method1(GetInt())?.IntVal
+ GetMyStruct()?[GetInt()]?.IntVal;
}
}
}

11
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1049,16 +1049,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1049,16 +1049,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType);
if (opType == UnaryOperatorType.Await) {
WriteKeyword(opSymbol);
} else if (!(opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement)) {
} else if (!IsPostfixOperator(opType)) {
WriteToken(opSymbol);
}
unaryOperatorExpression.Expression.AcceptVisitor(this);
if (opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement) {
if (IsPostfixOperator(opType)) {
WriteToken(opSymbol);
}
EndNode(unaryOperatorExpression);
}
static bool IsPostfixOperator(UnaryOperatorType op)
{
return op == UnaryOperatorType.PostIncrement
|| op == UnaryOperatorType.PostDecrement
|| op == UnaryOperatorType.NullConditional;
}
public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression)
{
StartNode(uncheckedExpression);

12
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs

@ -43,6 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -43,6 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole DereferenceRole = new TokenRole ("*");
public readonly static TokenRole AddressOfRole = new TokenRole ("&");
public readonly static TokenRole AwaitRole = new TokenRole ("await");
public readonly static TokenRole NullConditionalRole = new TokenRole ("?");
public UnaryOperatorExpression()
{
@ -63,9 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -63,9 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
get { return GetChildByRole (GetOperatorRole (Operator)); }
}
static Expression NoUnaryExpressionError = new ErrorExpression ("No unary expression");
public Expression Expression {
get { return GetChildByRole (Roles.Expression) ?? NoUnaryExpressionError; }
get { return GetChildByRole (Roles.Expression); }
set { SetChildByRole (Roles.Expression, value); }
}
@ -114,6 +114,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -114,6 +114,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return AddressOfRole;
case UnaryOperatorType.Await:
return AwaitRole;
case UnaryOperatorType.NullConditional:
return NullConditionalRole;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
}
@ -176,6 +178,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -176,6 +178,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <summary>Get address (&a)</summary>
AddressOf,
/// <summary>C# 5.0 await</summary>
Await
Await,
/// <summary>C# 6 null-conditional operator.
/// Occurs as target of member reference or indexer expressions
/// to indicate <c>?.</c> or <c>?[]</c></summary>
NullConditional
}
}

2
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -271,7 +271,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -271,7 +271,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// <summary>
/// Derived classes may add to this set of flags to ensure they don't forget to override an interesting method.
/// </summary>
protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable;
protected sealed override void Default(ILInstruction inst)
{

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -286,6 +286,7 @@ @@ -286,6 +286,7 @@
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\ILAstWritingOptions.cs" />
<Compile Include="IL\Instructions\LdFlda.cs" />
<Compile Include="IL\Instructions\NullableInstructions.cs" />
<Compile Include="IL\Instructions\StLoc.cs" />
<Compile Include="IL\SequencePoint.cs" />
<Compile Include="IL\Instructions\CallIndirect.cs" />

12
ICSharpCode.Decompiler/IL/InstructionFlags.cs

@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// The instruction may read from local variables.
/// </summary>
MayReadLocals = 0x10,
MayReadLocals = 0x10,
/// <summary>
/// The instruction may write to local variables.
/// </summary>
@ -51,17 +51,21 @@ namespace ICSharpCode.Decompiler.IL @@ -51,17 +51,21 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
MayThrow = 0x100,
/// <summary>
/// The instruction may exit with a branch or return.
/// The instruction may exit with a branch or leave.
/// </summary>
MayBranch = 0x200,
/// <summary>
/// The instruction may jump to the closest containing <c>nullable.rewrap</c> instruction.
/// </summary>
MayUnwrapNull = 0x400,
/// <summary>
/// The instruction performs unconditional control flow, so that its endpoint is unreachable.
/// </summary>
/// <remarks>
/// If EndPointUnreachable is set, either MayThrow or MayBranch should also be set
/// (unless the instruction represents an infinite loop).
/// </remarks>
EndPointUnreachable = 0x400,
EndPointUnreachable = 0x800,
/// <summary>
/// The instruction contains some kind of internal control flow.
/// </summary>
@ -72,6 +76,6 @@ namespace ICSharpCode.Decompiler.IL @@ -72,6 +76,6 @@ namespace ICSharpCode.Decompiler.IL
/// Note that branch instructions don't have this flag set, because their control flow is not internal
/// (and they don't have any unusual argument evaluation rules).
/// </remarks>
ControlFlow = 0x800,
ControlFlow = 0x1000,
}
}

140
ICSharpCode.Decompiler/IL/Instructions.cs

@ -102,6 +102,13 @@ namespace ICSharpCode.Decompiler.IL @@ -102,6 +102,13 @@ namespace ICSharpCode.Decompiler.IL
ThreeValuedLogicAnd,
/// <summary>Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.</summary>
ThreeValuedLogicOr,
/// <summary>The input operand must be either a nullable value type or a reference type.
/// If the input is non-null, evaluates to the (unwrapped) input.
/// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.</summary>
NullableUnwrap,
/// <summary>Serves as jump target for the nullable.unwrap instruction.
/// If the input evaluates normally, evaluates to the input value, wrapped in Nullable<T> if the input is a value type.If a nullable.unwrap encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap evaluates to a null.</summary>
NullableRewrap,
/// <summary>Loads a constant string.</summary>
LdStr,
/// <summary>Loads a constant 32-bit integer.</summary>
@ -2510,6 +2517,91 @@ namespace ICSharpCode.Decompiler.IL @@ -2510,6 +2517,91 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>The input operand must be either a nullable value type or a reference type.
/// If the input is non-null, evaluates to the (unwrapped) input.
/// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.</summary>
public sealed partial class NullableUnwrap : UnaryInstruction
{
public NullableUnwrap(ILInstruction argument, IType type) : base(OpCode.NullableUnwrap, argument)
{
this.type = type;
}
IType type;
/// <summary>Returns the type operand.</summary>
public IType Type {
get { return type; }
set { type = value; InvalidateFlags(); }
}
public override StackType ResultType { get { return type.GetStackType(); } }
protected override InstructionFlags ComputeFlags()
{
return base.ComputeFlags() | InstructionFlags.MayUnwrapNull;
}
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayUnwrapNull;
}
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
output.Write('(');
Argument.WriteTo(output, options);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitNullableUnwrap(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitNullableUnwrap(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitNullableUnwrap(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as NullableUnwrap;
return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Serves as jump target for the nullable.unwrap instruction.
/// If the input evaluates normally, evaluates to the input value, wrapped in Nullable<T> if the input is a value type.If a nullable.unwrap encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap evaluates to a null.</summary>
public sealed partial class NullableRewrap : UnaryInstruction
{
public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument)
{
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitNullableRewrap(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitNullableRewrap(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitNullableRewrap(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as NullableRewrap;
return o != null && this.Argument.PerformMatch(o.Argument, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Loads a constant string.</summary>
public sealed partial class LdStr : SimpleInstruction
@ -5131,6 +5223,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5131,6 +5223,14 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitNullableUnwrap(NullableUnwrap inst)
{
Default(inst);
}
protected internal virtual void VisitNullableRewrap(NullableRewrap inst)
{
Default(inst);
}
protected internal virtual void VisitLdStr(LdStr inst)
{
Default(inst);
@ -5441,6 +5541,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5441,6 +5541,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst)
{
return Default(inst);
}
protected internal virtual T VisitNullableRewrap(NullableRewrap inst)
{
return Default(inst);
}
protected internal virtual T VisitLdStr(LdStr inst)
{
return Default(inst);
@ -5751,6 +5859,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5751,6 +5859,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitNullableRewrap(NullableRewrap inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitLdStr(LdStr inst, C context)
{
return Default(inst, context);
@ -5948,6 +6064,8 @@ namespace ICSharpCode.Decompiler.IL @@ -5948,6 +6064,8 @@ namespace ICSharpCode.Decompiler.IL
"addressof",
"3vl.logic.and",
"3vl.logic.or",
"nullable.unwrap",
"nullable.rewrap",
"ldstr",
"ldc.i4",
"ldc.i8",
@ -6163,6 +6281,28 @@ namespace ICSharpCode.Decompiler.IL @@ -6163,6 +6281,28 @@ namespace ICSharpCode.Decompiler.IL
right = default(ILInstruction);
return false;
}
public bool MatchNullableUnwrap(out ILInstruction argument, out IType type)
{
var inst = this as NullableUnwrap;
if (inst != null) {
argument = inst.Argument;
type = inst.Type;
return true;
}
argument = default(ILInstruction);
type = default(IType);
return false;
}
public bool MatchNullableRewrap(out ILInstruction argument)
{
var inst = this as NullableRewrap;
if (inst != null) {
argument = inst.Argument;
return true;
}
argument = default(ILInstruction);
return false;
}
public bool MatchLdStr(out string value)
{
var inst = this as LdStr;

10
ICSharpCode.Decompiler/IL/Instructions.tt

@ -160,6 +160,16 @@ @@ -160,6 +160,16 @@
CustomClassName("ThreeValuedLogicAnd"), Binary, ResultType("O")),
new OpCode("3vl.logic.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.",
CustomClassName("ThreeValuedLogicOr"), Binary, ResultType("O")),
new OpCode("nullable.unwrap", "The input operand must be either a nullable value type or a reference type." + Environment.NewLine
+ "If the input is non-null, evaluates to the (unwrapped) input." + Environment.NewLine
+ "If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.",
Unary, HasTypeOperand, ResultType("type.GetStackType()"),
HasFlag("InstructionFlags.MayUnwrapNull")),
new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine
+ "If the input evaluates normally, evaluates to the input value, wrapped in Nullable<T> if the input is a value type."
+ "If a nullable.unwrap encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,"
+ "the nullable.rewrap evaluates to a null.",
Unary, CustomComputeFlags),
new OpCode("ldstr", "Loads a constant string.",
CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")),

9
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -56,12 +56,17 @@ namespace ICSharpCode.Decompiler.IL @@ -56,12 +56,17 @@ namespace ICSharpCode.Decompiler.IL
{
base.CheckInvariant(phase);
Debug.Assert(condition.ResultType == StackType.I4);
Debug.Assert(trueInst.ResultType == falseInst.ResultType);
Debug.Assert(trueInst.ResultType == falseInst.ResultType
|| trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)
|| falseInst.HasDirectFlag(InstructionFlags.EndPointUnreachable));
}
public override StackType ResultType {
get {
return trueInst.ResultType;
if (trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable))
return falseInst.ResultType;
else
return trueInst.ResultType;
}
}

62
ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
// Copyright (c) 2018 Daniel Grunwald
//
// 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.Diagnostics;
using System.Linq;
namespace ICSharpCode.Decompiler.IL
{
partial class NullableUnwrap
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(Argument.ResultType == StackType.O, "nullable.unwrap expects nullable type as input");
Debug.Assert(Ancestors.Any(a => a is NullableRewrap));
}
}
partial class NullableRewrap
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(Argument.HasFlag(InstructionFlags.MayUnwrapNull));
}
public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow;
protected override InstructionFlags ComputeFlags()
{
// Convert MayUnwrapNull flag to ControlFlow flag.
// Also, remove EndpointUnreachable flag, because the end-point is reachable through
// the implicit nullable.unwrap branch.
const InstructionFlags flagsToRemove = InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable;
return (Argument.Flags & ~flagsToRemove) | InstructionFlags.ControlFlow;
}
public override StackType ResultType {
get {
if (Argument.ResultType == StackType.Void)
return StackType.Void;
else
return StackType.O;
}
}
}
}

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

@ -354,6 +354,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -354,6 +354,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst) ?? false;
// If FindLoadInNext() returns null, we still can't continue searching
// because we can't inline over the remainder of the blockcontainer.
} else if (expr is NullableRewrap) {
// Inlining into nullable.rewrap is OK unless the expression being inlined
// contains a nullable.wrap that isn't being re-wrapped within the expression being inlined.
if (expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull))
return false;
}
foreach (var child in expr.Children) {
if (!child.SlotInfo.CanInlineInto)

2
doc/copyright.txt

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
Copyright 2011-2014 for the SharpDevelop team
Copyright 2011-2018 for the SharpDevelop team
by
AlphaSierraPapa, Christoph Wille

Loading…
Cancel
Save