diff --git a/BuildTools/appveyor-ilspycmd-install.ps1 b/BuildTools/appveyor-ilspycmd-install.ps1 index 41691695a..0d549c7a7 100644 --- a/BuildTools/appveyor-ilspycmd-install.ps1 +++ b/BuildTools/appveyor-ilspycmd-install.ps1 @@ -3,6 +3,9 @@ $ErrorActionPreference = "Stop" $baseCommit = "e17b4bfedf4dc747b105396224cd726bdca500ad"; $baseCommitRev = 1; +# make sure this matches artifacts-only branches list in appveyor.yml! +$masterBranches = '^(master|release/.+)$'; + $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; $versionParts = @{}; @@ -18,10 +21,10 @@ if ($versionName -ne "null") { } else { $versionName = ""; } -if ($env:APPVEYOR_REPO_BRANCH -ne 'master') { - $branch = "-$env:APPVEYOR_REPO_BRANCH"; -} else { +if ($env:APPVEYOR_REPO_BRANCH -match $masterBranches) { $branch = ""; +} else { + $branch = "-$env:APPVEYOR_REPO_BRANCH"; } $revision = [Int32]::Parse((git rev-list --count "$baseCommit..HEAD")) + $baseCommitRev; diff --git a/BuildTools/appveyor-install.ps1 b/BuildTools/appveyor-install.ps1 index 0b922deb8..a69ff0732 100644 --- a/BuildTools/appveyor-install.ps1 +++ b/BuildTools/appveyor-install.ps1 @@ -3,8 +3,8 @@ $ErrorActionPreference = "Stop" $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; -# make sure this list matches artifacts-only branches list in appveyor.yml! -$masterBranches = @("master", "5.0.x"); +# make sure this matches artifacts-only branches list in appveyor.yml! +$masterBranches = '^(master|release/.+)$'; $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; @@ -21,7 +21,7 @@ if ($versionName -ne "null") { } else { $versionName = ""; } -if ($masterBranches -contains $env:APPVEYOR_REPO_BRANCH) { +if ($env:APPVEYOR_REPO_BRANCH -match $masterBranches) { $branch = ""; } else { $branch = "-$env:APPVEYOR_REPO_BRANCH"; diff --git a/BuildTools/pipelines-install.ps1 b/BuildTools/pipelines-install.ps1 index 4e3dda35e..7e73f3c60 100644 --- a/BuildTools/pipelines-install.ps1 +++ b/BuildTools/pipelines-install.ps1 @@ -3,8 +3,8 @@ $ErrorActionPreference = "Stop" $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; -# make sure this list matches artifacts-only branches list in azure-pipelines.yml! -$masterBranches = @("master", "5.0.x"); +# make sure this matches artifacts-only branches list in appveyor.yml! +$masterBranches = '^refs/heads/(master|release/.+)$'; $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; @@ -21,7 +21,8 @@ if ($versionName -ne "null") { } else { $versionName = ""; } -if ($masterBranches -contains $env:BUILD_SOURCEBRANCHNAME) { + +if ($env:BUILD_SOURCEBRANCH -match $masterBranches) { $branch = ""; } else { $branch = "-$env:BUILD_SOURCEBRANCHNAME"; diff --git a/BuildTools/update-assemblyinfo.ps1 b/BuildTools/update-assemblyinfo.ps1 index dbfa5f911..f7fa079b1 100644 --- a/BuildTools/update-assemblyinfo.ps1 +++ b/BuildTools/update-assemblyinfo.ps1 @@ -3,8 +3,8 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; $baseCommitRev = 1; -# make sure this list matches artifacts-only branches list in appveyor.yml! -$masterBranches = @("master", "5.0.x"); +# make sure this matches artifacts-only branches list in appveyor.yml! +$masterBranches = '^(master|release/.+)$'; $globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs"; @@ -112,7 +112,7 @@ try { $branchName = gitBranch; $gitCommitHash = gitCommitHash; - if ($masterBranches -contains $branchName) { + if ($branchName -match $masterBranches) { $postfixBranchName = ""; } else { $postfixBranchName = "-$branchName"; diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 7ae4cf43b..8a6a6d33f 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -65,6 +65,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index 54ce7b5cb..7826d0af2 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -146,6 +146,12 @@ namespace ICSharpCode.Decompiler.Tests Run(settings: new DecompilerSettings { SwitchExpressions = false }); } + [Test] + public void UnknownTypes() + { + Run(); + } + [Test] public void Issue1145() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.cs new file mode 100644 index 000000000..c77ae5e14 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.cs @@ -0,0 +1,12 @@ +internal class UnknownTypes +{ + private readonly IInterface memberField; + + public override bool CanExecute(CallbackQuery message) + { + return ((IInterface)(object)memberField).Execute(new SomeClass { + ChatId = StaticClass.GetChatId(message), + MessageId = StaticClass.GetMessageId(message) + }); + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.il new file mode 100644 index 000000000..69c743fa4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/UnknownTypes.il @@ -0,0 +1,59 @@ +#define CORE_ASSEMBLY "System.Runtime" + +.assembly extern CORE_ASSEMBLY +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 4:0:0:0 +} +.assembly extern UnknownAssembly +{ + .publickeytoken = (01 02 03 04 05 06 07 08 ) + .ver 1:0:0:0 +} + +.class private auto ansi beforefieldinit UnknownTypes + extends [System.Private.CoreLib]System.Object +{ + .field private initonly class [UnknownAssembly]IInterface memberField + + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x206e + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [CORE_ASSEMBLY]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method UnknownTypes::.ctor + + .method public hidebysig virtual + instance bool CanExecute ( + class [UnknownAssembly]CallbackQuery message + ) cil managed + { + // Method begins at RVA 0x4dbc + // Code size 44 (0x2c) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ldfld class [UnknownAssembly]IInterface UnknownTypes::memberField + IL_0006: newobj instance void [UnknownAssembly]SomeClass::.ctor() + IL_000b: dup + IL_000d: ldarg.1 + IL_000e: call int64 [UnknownAssembly]StaticClass::GetChatId(class [UnknownAssembly]CallbackQuery) + IL_0013: stfld int64 [UnknownAssembly]SomeClass::ChatId + IL_0018: dup + IL_001a: ldarg.1 + IL_001b: call int32 [UnknownAssembly]StaticClass::GetMessageId(class [UnknownAssembly]CallbackQuery) + IL_0020: conv.i8 + IL_0021: stfld int64 [UnknownAssembly]SomeClass::MessageId + IL_0026: callvirt instance !1 class [UnknownAssembly]IInterface`2::Execute(!0) + IL_002b: ret + } // end of method CanExecute + + +} // end of class UnknownTypes diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 8b798edb1..10c422d3a 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -286,8 +286,8 @@ namespace ICSharpCode.Decompiler.CSharp var methodSemantics = module.MethodSemanticsLookup.GetSemantics(methodHandle).Item2; if (methodSemantics != 0 && methodSemantics != System.Reflection.MethodSemanticsAttributes.Other) return true; - if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, methodHandle)) - return settings.LocalFunctions; + if (settings.LocalFunctions && LocalFunctionDecompiler.IsLocalFunctionMethod(module, methodHandle)) + return true; if (settings.AnonymousMethods && methodHandle.HasGeneratedName(metadata) && methodHandle.IsCompilerGenerated(metadata)) return true; if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedMainMethod(module, methodHandle)) @@ -299,8 +299,8 @@ namespace ICSharpCode.Decompiler.CSharp name = metadata.GetString(type.Name); if (!type.GetDeclaringType().IsNil) { - if (LocalFunctionDecompiler.IsLocalFunctionDisplayClass(module, typeHandle)) - return settings.LocalFunctions; + if (settings.LocalFunctions && LocalFunctionDecompiler.IsLocalFunctionDisplayClass(module, typeHandle)) + return true; if (settings.AnonymousMethods && IsClosureType(type, metadata)) return true; if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(typeHandle, metadata)) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c49d167f4..2b28c643d 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -168,16 +168,19 @@ namespace ICSharpCode.Decompiler.CSharp } else if (inst is ILiftableInstruction liftable && liftable.IsLifted) { - Debug.Assert(NullableType.IsNullable(cexpr.Type)); - IType underlying = NullableType.GetUnderlyingType(cexpr.Type); - if (liftable.UnderlyingResultType.IsIntegerType()) + if (liftable.UnderlyingResultType != StackType.Unknown) { - Debug.Assert(underlying.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); - Debug.Assert(underlying.GetSign() != Sign.None, "Must have a sign specified for zero/sign-extension"); - } - else - { - Debug.Assert(underlying.GetStackType() == liftable.UnderlyingResultType); + Debug.Assert(NullableType.IsNullable(cexpr.Type)); + IType underlying = NullableType.GetUnderlyingType(cexpr.Type); + if (liftable.UnderlyingResultType.IsIntegerType()) + { + Debug.Assert(underlying.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); + Debug.Assert(underlying.GetSign() != Sign.None, "Must have a sign specified for zero/sign-extension"); + } + else + { + Debug.Assert(underlying.GetStackType() == liftable.UnderlyingResultType); + } } } else if (inst.ResultType == StackType.Ref) diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 41900b0e6..170c4d2db 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -502,7 +502,8 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref)); } if (this.ResolveResult.IsCompileTimeConstant && this.ResolveResult.ConstantValue != null - && NullableType.IsNullable(targetType) && !utype.Equals(targetUType)) + && NullableType.IsNullable(targetType) && !utype.Equals(targetUType) + && targetUType.GetStackType().IsIntegerType()) { // Casts like `(uint?)-1` are only valid in an explicitly unchecked context, but we // don't have logic to ensure such a context (usually we emit into an implicitly unchecked context). diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 0a889de45..cbfe23e79 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1312,7 +1312,8 @@ namespace ICSharpCode.Decompiler.IL return PopPointer(); default: // field in unresolved type - if (PeekStackType() == StackType.O) + var stackType = PeekStackType(); + if (stackType == StackType.O || stackType == StackType.Unknown) return Pop(); else return PopPointer(); diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 0fffa2a28..be3114ace 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -213,6 +213,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms var values = new List<(string, ILInstruction)>(); var uniqueValues = new HashSet(); int numberOfUniqueMatchesWithCurrentVariable = 0; + HashSet caseBlocks = new HashSet(); + caseBlocks.Add((Block)instructions[i].Parent); bool AddSwitchSection(string value, ILInstruction inst) { @@ -308,6 +310,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!AddSwitchSection(value, block)) return false; } + caseBlocks.Add(currentCaseBlock); currentCaseBlock = nextCaseBlock as Block; } while (currentCaseBlock != null); @@ -316,7 +319,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; context.Step(nameof(SimplifyCascadingIfStatements), instructions[i]); // if the switchValueVar is used in other places as well, do not eliminate the store. - if (switchValueVar.LoadCount > numberOfUniqueMatchesWithCurrentVariable) + if (switchValueVar.LoadCount > numberOfUniqueMatchesWithCurrentVariable || !ValidateUsesOfSwitchValueVariable(switchValueVar, caseBlocks)) { keepAssignmentBefore = true; removeExtraLoad = false; // prevent loads from being deleted after detecting that @@ -355,6 +358,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + private bool ValidateUsesOfSwitchValueVariable(ILVariable switchValueVar, HashSet caseBlocks) + { + foreach (var use in switchValueVar.LoadInstructions) + { + bool isValid = false; + foreach (var caseBlock in caseBlocks) + { + if (use.IsDescendantOf(caseBlock)) + isValid = true; + } + if (!isValid) + return false; + } + + return true; + } + bool SimplifyCSharp1CascadingIfStatements(InstructionCollection instructions, ref int i) { if (i < 1) diff --git a/ILSpy/Properties/app.config.template b/ILSpy/Properties/app.config.template index 1bde4b5e0..7360911a3 100644 --- a/ILSpy/Properties/app.config.template +++ b/ILSpy/Properties/app.config.template @@ -14,7 +14,7 @@ - + diff --git a/appveyor.yml b/appveyor.yml index 7bc77bb58..8b9b0dd07 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -45,7 +45,7 @@ for: - branches: only: - master - - 5.0.x + - /release\/*/ artifacts: - path: ILSpy_binaries.zip name: ILSpy %APPVEYOR_REPO_BRANCH% %ILSPY_VERSION_NUMBER% binaries diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e45b45d2d..e087c1aea 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,10 +1,10 @@ trigger: - master -- 5.0.x +- release/* pr: - master -- 5.0.x +- release/* variables: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true @@ -52,7 +52,7 @@ jobs: inputs: command: 'custom' custom: 'tool' - arguments: 'install dotnet-format --tool-path BuildTools' + arguments: 'install dotnet-format --tool-path $(Agent.ToolsDirectory)/dotnet-format' - script: pwsh .\BuildTools\pipelines-install.ps1 displayName: Install @@ -94,7 +94,7 @@ jobs: - script: python BuildTools\tidy.py displayName: Tab check - - script: .\BuildTools\dotnet-format --check --verbosity diagnostic ILSpy.sln + - script: $(Agent.ToolsDirectory)\dotnet-format\dotnet-format --check --verbosity diagnostic ILSpy.sln displayName: dotnet-format check - task: CopyFiles@2