Browse Source

More fixes for yield return decompilation.

pull/734/merge
Daniel Grunwald 8 years ago
parent
commit
7277de6c08
  1. 19
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 2
      ICSharpCode.Decompiler/DecompilerSettings.cs
  3. 22
      ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
  4. 48
      ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
  5. 43
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  6. 13
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  7. 2
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  8. 6
      ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs
  9. 17
      ICSharpCode.Decompiler/Tests/Helpers/Tester.cs
  10. 2
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  11. 8
      ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs
  12. 5
      ICSharpCode.Decompiler/Tests/TestCases/Correctness/Generics.cs
  13. 106
      ICSharpCode.Decompiler/Tests/TestCases/Correctness/YieldReturn.cs
  14. 4
      ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs
  15. 2
      appveyor.yml

19
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -631,6 +631,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -631,6 +631,25 @@ namespace ICSharpCode.Decompiler.CSharp
AddDefinesForConditionalAttributes(function);
var statementBuilder = new StatementBuilder(specializingTypeSystem, decompilationContext, method, function);
entityDecl.AddChild(statementBuilder.ConvertAsBlock(function.Body), Roles.Body);
if (function.IsIterator) {
RemoveAttribute(entityDecl, new TopLevelTypeName("System.Runtime.CompilerServices", "IteratorStateMachineAttribute"));
}
}
void RemoveAttribute(EntityDeclaration entityDecl, FullTypeName attrName)
{
foreach (var section in entityDecl.Attributes) {
foreach (var attr in section.Attributes) {
var symbol = attr.Type.GetSymbol();
if (symbol is ITypeDefinition td && td.FullTypeName == attrName) {
attr.Remove();
}
}
if (section.Attributes.Count == 0) {
section.Remove();
}
}
}
void AddDefinesForConditionalAttributes(ILFunction function)

2
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.Decompiler @@ -57,7 +57,7 @@ namespace ICSharpCode.Decompiler
}
}
bool yieldReturn = false;
bool yieldReturn = true;
/// <summary>
/// Decompile enumerators.

22
ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs

@ -135,18 +135,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -135,18 +135,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
case IfInstruction ifInst:
val = evalContext.Eval(ifInst.Condition).AsBool();
LongSet trueRanges;
LongSet falseRanges;
if (val.Type == SymbolicValueType.StateEquals) {
trueRanges = stateRange.IntersectWith(new LongSet(val.Constant));
falseRanges = stateRange.ExceptWith(new LongSet(val.Constant));
trueRanges = new LongSet(val.Constant);
} else if (val.Type == SymbolicValueType.StateInEquals) {
trueRanges = stateRange.ExceptWith(new LongSet(val.Constant));
falseRanges = stateRange.IntersectWith(new LongSet(val.Constant));
trueRanges = new LongSet(val.Constant).Invert();
} else if (val.Type == SymbolicValueType.StateLessThan) {
// note: val.Constant is of type int, so it can't be equal to long.MinValue,
// which would cause problems.
trueRanges = new LongSet(new LongInterval(long.MinValue, val.Constant));
} else if (val.Type == SymbolicValueType.StateLessEqual) {
trueRanges = new LongSet(LongInterval.Inclusive(long.MinValue, val.Constant));
} else if (val.Type == SymbolicValueType.StateGreaterThan) {
// note: val.Constant is of type int, so the addition can't overflow.
trueRanges = new LongSet(LongInterval.Inclusive(val.Constant + 1L, long.MaxValue));
} else if (val.Type == SymbolicValueType.StateGreaterEqual) {
trueRanges = new LongSet(LongInterval.Inclusive(val.Constant, long.MaxValue));
} else {
goto default;
}
var afterTrue = AssignStateRanges(ifInst.TrueInst, trueRanges);
var afterFalse = AssignStateRanges(ifInst.FalseInst, falseRanges);
var afterTrue = AssignStateRanges(ifInst.TrueInst, stateRange.IntersectWith(trueRanges));
var afterFalse = AssignStateRanges(ifInst.FalseInst, stateRange.ExceptWith(trueRanges));
return afterTrue.UnionWith(afterFalse);
case Branch br:
AddStateRange(br.TargetBlock, stateRange);

48
ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs

@ -60,7 +60,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -60,7 +60,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// bool: State != Constant
/// </summary>
StateInEquals
StateInEquals,
/// <summary>
/// bool: State &lt; Constant
/// </summary>
StateLessThan,
/// <summary>
/// bool: State &lt;= Constant
/// </summary>
StateLessEqual,
/// <summary>
/// bool: State &gt; Constant
/// </summary>
StateGreaterThan,
/// <summary>
/// bool: State &gt;= Constant
/// </summary>
StateGreaterEqual
}
struct SymbolicValue
@ -143,16 +159,34 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -143,16 +159,34 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return new SymbolicValue(SymbolicValueType.StateEquals, unchecked(right.Constant - left.Constant));
else if (comp.Kind == ComparisonKind.Inequality)
return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant));
else if (comp.Kind == ComparisonKind.LessThan && left.Constant == 0)
return new SymbolicValue(SymbolicValueType.StateLessThan, right.Constant);
else if (comp.Kind == ComparisonKind.LessThanOrEqual && left.Constant == 0)
return new SymbolicValue(SymbolicValueType.StateLessEqual, right.Constant);
else if (comp.Kind == ComparisonKind.GreaterThan && left.Constant == 0)
return new SymbolicValue(SymbolicValueType.StateGreaterThan, right.Constant);
else if (comp.Kind == ComparisonKind.GreaterThanOrEqual && left.Constant == 0)
return new SymbolicValue(SymbolicValueType.StateGreaterEqual, right.Constant);
else
return Failed;
} else if (inst is LogicNot logicNot) {
SymbolicValue val = Eval(logicNot.Argument).AsBool();
if (val.Type == SymbolicValueType.StateEquals)
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant);
else if (val.Type == SymbolicValueType.StateInEquals)
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant);
else
return Failed;
switch (val.Type) {
case SymbolicValueType.StateEquals:
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant);
case SymbolicValueType.StateInEquals:
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant);
case SymbolicValueType.StateLessThan:
return new SymbolicValue(SymbolicValueType.StateGreaterEqual, val.Constant);
case SymbolicValueType.StateLessEqual:
return new SymbolicValue(SymbolicValueType.StateGreaterThan, val.Constant);
case SymbolicValueType.StateGreaterThan:
return new SymbolicValue(SymbolicValueType.StateLessEqual, val.Constant);
case SymbolicValueType.StateGreaterEqual:
return new SymbolicValue(SymbolicValueType.StateLessThan, val.Constant);
default:
return Failed;
}
} else {
return Failed;
}

43
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -139,12 +139,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -139,12 +139,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(newBody);
// Copy-propagate temporaries holding a copy of 'this'.
// This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables.
foreach (var stloc in function.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context);
}
context.Step("Translate fields to local accesses", function);
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
@ -299,7 +293,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -299,7 +293,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (method == null || !method.HasBody)
throw new SymbolicAnalysisFailedException();
var il = new ILReader(context.TypeSystem).ReadIL(method.Body, context.CancellationToken);
var typeSystem = context.TypeSystem;
var sdtp = typeSystem as SpecializingDecompilerTypeSystem;
if (sdtp != null) {
typeSystem = new SpecializingDecompilerTypeSystem(
sdtp.Context,
new TypeParameterSubstitution(
(sdtp.Substitution.ClassTypeArguments ?? EmptyList<IType>.Instance)
.Concat(sdtp.Substitution.MethodTypeArguments ?? EmptyList<IType>.Instance).ToArray(),
EmptyList<IType>.Instance
)
);
}
var il = new ILReader(typeSystem).ReadIL(method.Body, context.CancellationToken);
il.RunTransforms(CSharpDecompiler.EarlyILTransforms(), new ILTransformContext {
Settings = context.Settings,
CancellationToken = context.CancellationToken,
@ -401,6 +407,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -401,6 +407,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
ILFunction moveNextFunction = CreateILAst(moveNextMethod);
// Copy-propagate temporaries holding a copy of 'this'.
// This is necessary because the old (pre-Roslyn) C# compiler likes to store 'this' in temporary variables.
context.Stepper.StartGroup("AnalyzeMoveNext");
foreach (var stloc in moveNextFunction.Descendants.OfType<StLoc>().Where(s => s.Variable.IsSingleDefinition && s.Value.MatchLdThis()).ToList()) {
CopyPropagation.Propagate(stloc, context);
}
context.Stepper.EndGroup();
var body = (BlockContainer)moveNextFunction.Body;
if (body.Blocks.Count == 1 && body.Blocks[0].Instructions.Count == 1 && body.Blocks[0].Instructions[0] is TryFault tryFault) {
body = (BlockContainer)tryFault.TryBlock;
@ -420,7 +434,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -420,7 +434,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Note: body may contain try-catch or try-finally statements that have nested block containers,
// but those cannot contain any yield statements.
// So for reconstructing the control flow, we only need at the blocks directly within body.
// So for reconstructing the control flow, we only consider the blocks directly within body.
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorMoveNext, stateField);
rangeAnalysis.AssignStateRanges(body, LongSet.Universe);
@ -702,8 +716,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -702,8 +716,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Propagate newState to successor blocks
foreach (var branch in block.Descendants.OfType<Branch>()) {
if (branch.TargetBlock.Parent == newBody) {
Debug.Assert(blockState[branch.TargetBlock.ChildIndex] == newState || blockState[branch.TargetBlock.ChildIndex] == 0);
blockState[branch.TargetBlock.ChildIndex] = newState;
int stateAfterBranch = newState;
if (Block.GetPredecessor(branch) is Call call
&& call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis()
&& call.Method.Name == "System.IDisposable.Dispose") {
// pre-roslyn compiles "yield break;" into "Dispose(); goto return_false;",
// so convert the dispose call into a state transition to the final state
stateAfterBranch = -1;
call.ReplaceWith(new Nop() { Comment = "Dispose call" });
}
Debug.Assert(blockState[branch.TargetBlock.ChildIndex] == stateAfterBranch || blockState[branch.TargetBlock.ChildIndex] == 0);
blockState[branch.TargetBlock.ChildIndex] = stateAfterBranch;
}
}
}

13
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -221,6 +221,19 @@ namespace ICSharpCode.Decompiler.IL @@ -221,6 +221,19 @@ namespace ICSharpCode.Decompiler.IL
context.Stepper.EndGroup();
}
}
/// <summary>
/// Gets the predecessor of the given instruction.
/// Returns null if inst.Parent is not a block.
/// </summary>
public static ILInstruction GetPredecessor(ILInstruction inst)
{
if (inst.Parent is Block block && inst.ChildIndex > 0) {
return block.Instructions[inst.ChildIndex - 1];
} else {
return null;
}
}
}
public enum BlockType {

2
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -59,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL @@ -59,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL
base.CheckInvariant(phase);
}
protected void CloneVariables()
void CloneVariables()
{
throw new NotImplementedException();
}

6
ICSharpCode.Decompiler/Tests/CorrectnessTestRunner.cs

@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void YieldReturn([ValueSource("defaultOptions")] CompilerOptions options)
{
RunCS(options: options);
}
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug)
{
string testFileName = testName + ".cs";

17
ICSharpCode.Decompiler/Tests/Helpers/Tester.cs

@ -143,8 +143,14 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -143,8 +143,14 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
sourceFileNames.Add(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(sourceFileName), match.Groups[1].Value)));
}
var preprocessorSymbols = new List<string>();
if (flags.HasFlag(CompilerOptions.UseDebug)) {
preprocessorSymbols.Add("DEBUG");
}
if (flags.HasFlag(CompilerOptions.UseRoslyn)) {
var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), path: f));
var parseOptions = new CSharpParseOptions(preprocessorSymbols: preprocessorSymbols.ToArray());
var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f));
var compilation = CSharpCompilation.Create(Path.GetFileNameWithoutExtension(sourceFileName),
syntaxTrees, defaultReferences.Value,
new CSharpCompilationOptions(
@ -166,10 +172,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -166,10 +172,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
}
return results;
} else {
preprocessorSymbols.Add("LEGACY_CSC");
var provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
CompilerParameters options = new CompilerParameters();
options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library);
options.CompilerOptions = "/unsafe /o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-") + (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : "") + (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : "");
options.CompilerOptions = "/unsafe /o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-");
options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : "");
options.CompilerOptions += (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : "");
if (preprocessorSymbols.Count > 0) {
options.CompilerOptions += " /d:" + string.Join(";", preprocessorSymbols);
}
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromFile(options, sourceFileNames.ToArray());
if (results.Errors.Cast<CompilerError>().Any(e => !e.IsWarning)) {

2
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -147,7 +147,7 @@ @@ -147,7 +147,7 @@
<Compile Include="TestCases\Correctness\ValueTypeCall.cs" />
<Compile Include="CorrectnessTestRunner.cs" />
<Compile Include="TestCases\Pretty\Loops.cs" />
<Compile Include="TestCases\Pretty\YieldReturnTest.cs" />
<Compile Include="TestCases\Correctness\YieldReturn.cs" />
<Compile Include="TestCases\Pretty\CompoundAssignmentTest.cs" />
<Compile Include="TestCases\Pretty\ExceptionHandling.cs" />
<Compile Include="TestCases\Pretty\HelloWorld.cs" />

8
ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs

@ -102,13 +102,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -102,13 +102,7 @@ namespace ICSharpCode.Decompiler.Tests
{
Run(cscOptions: cscOptions);
}
[Test, Ignore("Not implemented")]
public void YieldReturnTest([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test, Ignore("Not fully working yet")]
public void Loops([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{

5
ICSharpCode.Decompiler/Tests/TestCases/Correctness/Generics.cs

@ -52,6 +52,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -52,6 +52,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
return (T)(BaseClass)d;
}
public T New<T>() where T : new()
{
return new T();
}
}
class GenericClass<T>

106
ICSharpCode.Decompiler/Tests/TestCases/Pretty/YieldReturnTest.cs → ICSharpCode.Decompiler/Tests/TestCases/Correctness/YieldReturn.cs

@ -19,10 +19,60 @@ @@ -19,10 +19,60 @@
using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
public class YieldReturnTest
{
static void Main()
{
Print("SimpleYieldReturn", SimpleYieldReturn().GetEnumerator());
Print("SimpleYieldReturnEnumerator", SimpleYieldReturnEnumerator());
Print("YieldReturnParameters",
new YieldReturnTest { fieldOnThis = 1 }.YieldReturnParameters(2).GetEnumerator());
Print("YieldReturnParametersEnumerator",
new YieldReturnTest { fieldOnThis = 1 }.YieldReturnParametersEnumerator(2));
Print("YieldReturnInLoop", YieldReturnInLoop().GetEnumerator());
Print("YieldReturnWithTryFinally", YieldReturnWithTryFinally().GetEnumerator());
Print("YieldReturnInLock1", YieldReturnInLock1(new object()).GetEnumerator());
Print("YieldReturnInLock2", YieldReturnInLock2(new object()).GetEnumerator());
Print("YieldReturnWithNestedTryFinally(false)", YieldReturnWithNestedTryFinally(false).GetEnumerator());
Print("YieldReturnWithNestedTryFinally(true)", YieldReturnWithNestedTryFinally(true).GetEnumerator());
Print("YieldReturnWithTwoNonNestedFinallyBlocks", YieldReturnWithTwoNonNestedFinallyBlocks(SimpleYieldReturn()).GetEnumerator());
// TODO: check anon methods
Print("GetEvenNumbers", GetEvenNumbers(3).GetEnumerator());
Print("YieldChars", YieldChars.GetEnumerator());
Print("ExceptionHandling", ExceptionHandling().GetEnumerator());
Print("YieldBreakInCatch", YieldBreakInCatch().GetEnumerator());
Print("YieldBreakInCatchInTryFinally", YieldBreakInCatchInTryFinally().GetEnumerator());
Print("YieldBreakInTryCatchInTryFinally", YieldBreakInTryCatchInTryFinally().GetEnumerator());
Print("YieldBreakInTryFinallyInTryFinally(false)", YieldBreakInTryFinallyInTryFinally(false).GetEnumerator());
Print("YieldBreakInTryFinallyInTryFinally(true)", YieldBreakInTryFinallyInTryFinally(true).GetEnumerator());
try {
Print("UnconditionalThrowInTryFinally()", UnconditionalThrowInTryFinally().GetEnumerator());
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
Print("NestedTryFinallyStartingOnSamePosition", NestedTryFinallyStartingOnSamePosition().GetEnumerator());
Print("TryFinallyWithTwoExitPoints(false)", TryFinallyWithTwoExitPoints(false).GetEnumerator());
Print("TryFinallyWithTwoExitPoints(true)", TryFinallyWithTwoExitPoints(true).GetEnumerator());
#if !LEGACY_CSC
Print("YieldBreakInNestedTryFinally()", YieldBreakInNestedTryFinally().GetEnumerator());
Print("TryFinallyWithTwoExitPointsInNestedTry(false)", TryFinallyWithTwoExitPointsInNestedTry(false).GetEnumerator());
Print("TryFinallyWithTwoExitPointsInNestedTry(true)", TryFinallyWithTwoExitPointsInNestedTry(true).GetEnumerator());
Print("TryFinallyWithTwoExitPointsInNestedCatch(false)", TryFinallyWithTwoExitPointsInNestedCatch(false).GetEnumerator());
Print("TryFinallyWithTwoExitPointsInNestedCatch(true)", TryFinallyWithTwoExitPointsInNestedCatch(true).GetEnumerator());
#endif
Print("GenericYield<int>()", GenericYield<int>().GetEnumerator());
}
static void Print<T>(string name, IEnumerator<T> enumerator)
{
Console.WriteLine(name + ": Test start");
while (enumerator.MoveNext()) {
Console.WriteLine(name + ": " + enumerator.Current);
}
}
int fieldOnThis;
public static IEnumerable<string> SimpleYieldReturn()
@ -98,8 +148,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -98,8 +148,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("Within inner try - 1");
yield return "Within inner try";
Console.WriteLine("Within inner try - 2");
if (breakInMiddle)
if (breakInMiddle) {
Console.WriteLine("Breaking...");
yield break;
}
Console.WriteLine("End of inner try - 1");
yield return "End of inner try";
Console.WriteLine("End of inner try - 2");
@ -162,11 +214,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -162,11 +214,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public static IEnumerable<char> YieldChars()
public static IEnumerable<char> YieldChars
{
yield return 'a';
yield return 'b';
yield return 'c';
get {
yield return 'a';
yield return 'b';
yield return 'c';
}
}
@ -202,7 +256,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -202,7 +256,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
yield return 1;
}
public static IEnumerable<int> YieldBreakInCatchInTryFinally()
{
try {
@ -211,6 +265,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -211,6 +265,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("In Try");
} catch {
// yield return is not allowed in catch, but yield break is
// Note that pre-roslyn, this code triggers a compiler bug:
// If the finally block throws an exception, it ends up getting
// called a second time.
yield break;
}
yield return 1;
@ -225,7 +282,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -225,7 +282,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
yield return 0;
try {
Console.WriteLine("In Try");
yield break;
yield break; // same compiler bug as in YieldBreakInCatchInTryFinally
} catch {
Console.WriteLine("Catch");
}
@ -242,7 +299,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -242,7 +299,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
try {
Console.WriteLine("In Try");
if (b)
yield break;
yield break; // same compiler bug as in YieldBreakInCatchInTryFinally
} finally {
Console.WriteLine("Inner Finally");
}
@ -281,7 +338,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -281,7 +338,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static IEnumerable<int> TryFinallyWithTwoExitPoints(bool b)
{
// The first user IL instruction is already in 2 nested try blocks.
// Uses goto for multiple non-exceptional exits out of try-finally.
try {
if (b) {
yield return 1;
@ -300,9 +357,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -300,9 +357,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("Exit2");
}
#if !LEGACY_CSC
public static IEnumerable<int> YieldBreakInNestedTryFinally()
{
try {
yield return 1;
try {
// Compiler bug: pre-Roslyn, the finally blocks will execute in the wrong order
yield break;
} finally {
Console.WriteLine("Inner Finally");
}
} finally {
Console.WriteLine("Outer Finally");
}
}
// Legacy csc has a compiler bug with this type of code:
// If the goto statements triggers a finally block, and the finally block throws an exception,
// that exception gets caught by the catch block.
public static IEnumerable<int> TryFinallyWithTwoExitPointsInNestedTry(bool b)
{
// The first user IL instruction is already in 2 nested try blocks.
try {
yield return 1;
try {
@ -345,5 +420,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -345,5 +420,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
exit2:
Console.WriteLine("Exit2");
}
#endif
public static IEnumerable<T> GenericYield<T>() where T : new()
{
T val = new T();
for (int i = 0; i < 3; i++) {
yield return val;
}
}
}
}

4
ICSharpCode.Decompiler/TypeSystem/SpecializingDecompilerTypeSystem.cs

@ -47,6 +47,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -47,6 +47,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
get { return context.Compilation; }
}
public TypeParameterSubstitution Substitution {
get { return substitution; }
}
public IType Resolve(Mono.Cecil.TypeReference typeReference)
{
return context.Resolve(typeReference).AcceptVisitor(substitution);

2
appveyor.yml

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
version: 1.0.{build}
configuration: Debug
platform: Any CPU
image: Visual Studio 2017 RC
image: Visual Studio 2017
install:
- git submodule update --init --recursive
- git clone https://github.com/icsharpcode/ILSpy-tests

Loading…
Cancel
Save