diff --git a/.github/workflows/build-frontends.yml b/.github/workflows/build-frontends.yml index 2ebe5fd40..d9abeabe5 100644 --- a/.github/workflows/build-frontends.yml +++ b/.github/workflows/build-frontends.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [ master, release/** ] +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/build-ilspy.yml b/.github/workflows/build-ilspy.yml index 10e5d8e9d..d596d8fcb 100644 --- a/.github/workflows/build-ilspy.yml +++ b/.github/workflows/build-ilspy.yml @@ -6,8 +6,13 @@ on: pull_request: branches: [ master, release/** ] +permissions: + contents: read + jobs: Build: + permissions: + packages: write # for dotnet nuget push runs-on: windows-2022 strategy: fail-fast: false @@ -30,12 +35,15 @@ jobs: with: dotnet-version: '8.0.x' dotnet-quality: 'ga' - + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/.dotnet + DOTNET_ROOT: ${{ runner.temp }}/.dotnet + - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - name: Install dotnet-format - run: dotnet tool install -g dotnet-format --version "8.0.453106" --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json + run: dotnet tool install -g dotnet-format --version "8.3.546805" --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json - name: Get Version id: version @@ -150,7 +158,20 @@ jobs: if: github.ref == 'refs/heads/master' && matrix.configuration == 'release' run: | dotnet nuget push "ICSharpCode.ILSpyX\bin\Release\ICSharpCode.ILSpyX*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }} - + + - name: Upload BamlDecompiler NuGet release build artifacts + if: matrix.configuration == 'release' + uses: actions/upload-artifact@v4 + with: + name: ICSharpCode.BamlDecompiler NuGet Package (${{ matrix.configuration }}) + path: ICSharpCode.BamlDecompiler\bin\Release\ICSharpCode.BamlDecompiler*.nupkg + if-no-files-found: error + + - name: Publish DecomBamlDecompilerpiler NuGet + if: github.ref == 'refs/heads/master' && matrix.configuration == 'release' + run: | + dotnet nuget push "ICSharpCode.BamlDecompiler\bin\Release\ICSharpCode.BamlDecompiler*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/${{ github.repository_owner }} + - name: Upload zip binaries build artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1886a05af..f7cd2250c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -6,8 +6,15 @@ on: pull_request: branches: [ master, release/** ] +permissions: + contents: read + jobs: analyze: + permissions: + actions: read # for github/codeql-action/init to get workflow details + security-events: write # for github/codeql-action/analyze to upload SARIF results + name: Analyze runs-on: ubuntu-latest diff --git a/.github/workflows/generate-bom.yml b/.github/workflows/generate-bom.yml index 5cfafea63..44cf80453 100644 --- a/.github/workflows/generate-bom.yml +++ b/.github/workflows/generate-bom.yml @@ -3,6 +3,9 @@ name: Generate BOM on: workflow_dispatch: +permissions: + contents: read + jobs: build: diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 3bc404b70..4ef571260 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -4,8 +4,13 @@ on: schedule: - cron: '0 0 * * *' +permissions: + contents: read + jobs: lock: + permissions: + issues: write # for dessant/lock-threads to lock issues runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v5.0.1 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..1ba02149a --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,47 @@ +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection (disabled) + # branch_protection_rule: + workflow_dispatch: + + # schedule ("Maintained") and push are disabled atm + # schedule: + # - cron: '25 1 * * 2' + # push: + # branches: [ "master" ] + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + security-events: write # Needed to upload the results to code-scanning dashboard. + id-token: write # Needed to publish results and get a badge (see publish_results below). + + steps: + - name: "Checkout code" + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + - name: "Upload artifact" + uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/BuildTools/pre-commit b/BuildTools/pre-commit index eee675c87..1067d5581 100644 --- a/BuildTools/pre-commit +++ b/BuildTools/pre-commit @@ -5,7 +5,7 @@ set -eu -DOTNET_FORMAT_VERSION=8.0.453106 +DOTNET_FORMAT_VERSION=8.3.546805 DOTNET_PATH="$LOCALAPPDATA/ICSharpCode/ILSpy/dotnet-format-$DOTNET_FORMAT_VERSION" if [ ! -d "$DOTNET_PATH" ]; then echo "Downloading dotnet-format $DOTNET_FORMAT_VERSION..." diff --git a/Directory.Packages.props b/Directory.Packages.props index 054032035..5c5711fea 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,19 +5,19 @@ - + - - + + - + - - + + @@ -25,32 +25,35 @@ - - - + + + - + - + - + - - - - - - - + + + + + + + - - - + + + + + + - + \ No newline at end of file diff --git a/ICSharpCode.BamlDecompiler/ICSharpCode.BamlDecompiler.csproj b/ICSharpCode.BamlDecompiler/ICSharpCode.BamlDecompiler.csproj index cd022af4b..d054a6512 100644 --- a/ICSharpCode.BamlDecompiler/ICSharpCode.BamlDecompiler.csproj +++ b/ICSharpCode.BamlDecompiler/ICSharpCode.BamlDecompiler.csproj @@ -41,6 +41,12 @@ + + + true + true + + diff --git a/ICSharpCode.BamlDecompiler/packages.lock.json b/ICSharpCode.BamlDecompiler/packages.lock.json new file mode 100644 index 000000000..2fe6f64a0 --- /dev/null +++ b/ICSharpCode.BamlDecompiler/packages.lock.json @@ -0,0 +1,44 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "TomsToolbox.Composition.Analyzer": { + "type": "Direct", + "requested": "[2.21.0, )", + "resolved": "2.21.0", + "contentHash": "vJx9hxAzjni34slGz78ewqYP9Ylk8dJszfEUK1TF5cflVKMBO3ORSFd0FtICTwJtE8munvZrMrcLWwXt5bIcEA==" + }, + "icsharpcode.decompiler": { + "type": "Project", + "dependencies": { + "System.Collections.Immutable": "[6.0.0, )", + "System.Reflection.Metadata": "[6.0.0, )" + } + }, + "System.Collections.Immutable": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Reflection.Metadata": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "6.0.0", + "contentHash": "sffDOcex1C3HO5kDolOYcWXTwRpZY/LvJujM6SMjn63fWMJWchYAAmkoAJXlbpZ5yf4d+KMgxd+LeETa4gD9sQ==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "CentralTransitive", + "requested": "[6.1.0, )", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index 2b877d110..a0901f255 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -409,7 +409,7 @@ namespace ICSharpCode.Decompiler.Tests options |= CompilerOptions.UseTestRunner; string testFileName = testName + ".cs"; string testOutputFileName = TestsAssemblyOutput.GetFilePath(TestCasePath, testName, Tester.GetSuffix(options) + ".exe"); - CompilerResults outputFile = null, decompiledOutputFile = null; + Helpers.CompilerResults outputFile = null, decompiledOutputFile = null; try { @@ -453,7 +453,7 @@ namespace ICSharpCode.Decompiler.Tests options |= CompilerOptions.UseTestRunner; string testFileName = testName + ".vb"; string testOutputFileName = TestsAssemblyOutput.GetFilePath(TestCasePath, testName, Tester.GetSuffix(options) + ".exe"); - CompilerResults outputFile = null, decompiledOutputFile = null; + Helpers.CompilerResults outputFile = null, decompiledOutputFile = null; try { @@ -477,7 +477,7 @@ namespace ICSharpCode.Decompiler.Tests async Task RunIL(string testFileName, CompilerOptions options = CompilerOptions.UseDebug, AssemblerOptions asmOptions = AssemblerOptions.None) { string outputFile = null; - CompilerResults decompiledOutputFile = null; + Helpers.CompilerResults decompiledOutputFile = null; bool optionsForce32Bit = options.HasFlag(CompilerOptions.Force32Bit); bool asmOptionsForce32Bit = asmOptions.HasFlag(AssemblerOptions.Force32Bit); diff --git a/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs b/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs index df78e9bc1..1a9557c77 100644 --- a/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs +++ b/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs @@ -19,6 +19,7 @@ using System; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.Metadata; using NUnit.Framework; @@ -119,5 +120,14 @@ namespace ICSharpCode.Decompiler.Tests // Assert Assert.That(targetFramework.Moniker, Is.EqualTo(expectedMoniker)); } + + [TestCase(".NETCoreApp, Version=v5.0", TargetFrameworkIdentifier.NET, "5.0.0")] + [TestCase(".NETCoreApp, Version=v10.0", TargetFrameworkIdentifier.NET, "10.0.0")] + public void VerifyUniversalAssemblyResolverParseTargetFramework(string targetFramework, TargetFrameworkIdentifier identifier, string version) + { + var (id, v) = UniversalAssemblyResolver.ParseTargetFramework(targetFramework); + Assert.That(id, Is.EqualTo(identifier)); + Assert.That(v.ToString(3), Is.EqualTo(version)); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 856c02028..f5647187f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -67,6 +67,76 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public class ImplicitInt + { + private readonly int s; + + public ImplicitInt(int s) + { + this.s = s; + } + + public static implicit operator int(ImplicitInt v) + { + return v.s; + } + } + + public class ImplicitConversionConflictWithLong + { + private readonly int s; + + public ImplicitConversionConflictWithLong(int s) + { + this.s = s; + } + + public static implicit operator int(ImplicitConversionConflictWithLong v) + { + return v.s; + } + + public static implicit operator long(ImplicitConversionConflictWithLong v) + { + return v.s; + } + } + + public class ImplicitConversionConflictWithString + { + private readonly int s; + + public ImplicitConversionConflictWithString(int s) + { + this.s = s; + } + + public static implicit operator int(ImplicitConversionConflictWithString v) + { + return v.s; + } + + public static implicit operator string(ImplicitConversionConflictWithString v) + { + return string.Empty; + } + } + + public class ExplicitInt + { + private readonly int s; + + public ExplicitInt(int s) + { + this.s = s; + } + + public static explicit operator int(ExplicitInt v) + { + return v.s; + } + } + public enum State { False, @@ -310,6 +380,118 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public static void SwitchOverExplicitInt(ExplicitInt i) + { + switch ((int)i) + { + case 0: + Console.WriteLine("zero"); + break; + case 5: + Console.WriteLine("five"); + break; + case 10: + Console.WriteLine("ten"); + break; + case 15: + Console.WriteLine("fifteen"); + break; + case 20: + Console.WriteLine("twenty"); + break; + case 25: + Console.WriteLine("twenty-five"); + break; + case 30: + Console.WriteLine("thirty"); + break; + } + } + + public static void SwitchOverImplicitInt(ImplicitInt i) + { + switch (i) + { + case 0: + Console.WriteLine("zero"); + break; + case 5: + Console.WriteLine("five"); + break; + case 10: + Console.WriteLine("ten"); + break; + case 15: + Console.WriteLine("fifteen"); + break; + case 20: + Console.WriteLine("twenty"); + break; + case 25: + Console.WriteLine("twenty-five"); + break; + case 30: + Console.WriteLine("thirty"); + break; + } + } + + public static void SwitchOverImplicitIntConflictLong(ImplicitConversionConflictWithLong i) + { + switch ((int)i) + { + case 0: + Console.WriteLine("zero"); + break; + case 5: + Console.WriteLine("five"); + break; + case 10: + Console.WriteLine("ten"); + break; + case 15: + Console.WriteLine("fifteen"); + break; + case 20: + Console.WriteLine("twenty"); + break; + case 25: + Console.WriteLine("twenty-five"); + break; + case 30: + Console.WriteLine("thirty"); + break; + } + } + + public static void SwitchOverImplicitIntConflictString(ImplicitConversionConflictWithString i) + { + switch ((string)i) + { + case "0": + Console.WriteLine("zero"); + break; + case "5": + Console.WriteLine("five"); + break; + case "10": + Console.WriteLine("ten"); + break; + case "15": + Console.WriteLine("fifteen"); + break; + case "20": + Console.WriteLine("twenty"); + break; + case "25": + Console.WriteLine("twenty-five"); + break; + case "30": + Console.WriteLine("thirty"); + break; + } + } + // SwitchDetection.UseCSharpSwitch requires more complex heuristic to identify this when compiled with Roslyn public static void CompactSwitchOverInt(int i) { @@ -421,9 +603,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static string SwitchOverImplicitString(ImplicitString s) { - // we emit an explicit cast, because the rules used by the C# compiler are counter-intuitive: - // The C# compiler does *not* take the type of the switch labels into account at all. - switch ((string)s) + switch (s) { case "First case": return "Text1"; diff --git a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs index 620424ca8..bc60f38c9 100644 --- a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs @@ -146,7 +146,7 @@ namespace ICSharpCode.Decompiler.Tests if (!File.Exists(ilFile)) { // re-create .il file if necessary - CompilerResults output = null; + Helpers.CompilerResults output = null; try { output = await Tester.CompileCSharp(csFile, cscOptions).ConfigureAwait(false); diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 520619787..7147785de 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -521,7 +521,7 @@ namespace ICSharpCode.Decompiler.CSharp file.DetectTargetFrameworkId(), file.DetectRuntimePack(), settings.LoadInMemory ? PEStreamOptions.PrefetchMetadata : PEStreamOptions.Default, settings.ApplyWindowsRuntimeProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None); - return new DecompilerTypeSystem(file, resolver); + return new DecompilerTypeSystem(file, resolver, settings); } static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 635d32871..2b32f842b 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3900,31 +3900,94 @@ namespace ICSharpCode.Decompiler.CSharp } } - protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context) + internal (TranslatedExpression, IType, StringToInt) TranslateSwitchValue(SwitchInstruction inst, bool isExpressionContext) { TranslatedExpression value; IType type; + // prepare expression and expected type if (inst.Value is StringToInt strToInt) { - value = Translate(strToInt.Argument) - .ConvertTo( - strToInt.ExpectedType, - this, - allowImplicitConversion: false // switch-expression does not support implicit conversions - ); - type = compilation.FindType(KnownTypeCode.String); + value = Translate(strToInt.Argument); + type = strToInt.ExpectedType ?? compilation.FindType(KnownTypeCode.String); } else { strToInt = null; value = Translate(inst.Value); - if (inst.Type != null) + type = inst.Type ?? value.Type; + } + + // find and unwrap the input type + IType inputType = value.Type; + if (value.Expression is CastExpression && value.ResolveResult is ConversionResolveResult crr) + { + inputType = crr.Input.Type; + } + inputType = NullableType.GetUnderlyingType(inputType).GetEnumUnderlyingType(); + + // check input/underlying type for compatibility + bool allowImplicitConversion; + if (IsCompatibleWithSwitch(inputType) || (strToInt != null && inputType.Equals(type))) + { + allowImplicitConversion = !isExpressionContext; + } + else + { + var applicableImplicitConversionOperators = inputType.GetMethods(IsCompatibleImplicitConversionOperator).ToArray(); + switch (applicableImplicitConversionOperators.Length) { - value = value.ConvertTo(inst.Type, this, allowImplicitConversion: true); + case 0: + allowImplicitConversion = !isExpressionContext; + break; + case 1: + allowImplicitConversion = !isExpressionContext; + // TODO validate + break; + default: + allowImplicitConversion = false; + break; } - type = value.Type; } + value = value.ConvertTo(type, this, allowImplicitConversion: allowImplicitConversion); + + var caseType = strToInt != null + ? compilation.FindType(KnownTypeCode.String) + : type; + + return (value, caseType, strToInt); + + static bool IsCompatibleWithSwitch(IType type) + { + return type.IsKnownType(KnownTypeCode.SByte) + || type.IsKnownType(KnownTypeCode.Byte) + || type.IsKnownType(KnownTypeCode.Int16) + || type.IsKnownType(KnownTypeCode.UInt16) + || type.IsKnownType(KnownTypeCode.Int32) + || type.IsKnownType(KnownTypeCode.UInt32) + || type.IsKnownType(KnownTypeCode.Int64) + || type.IsKnownType(KnownTypeCode.UInt64) + || type.IsKnownType(KnownTypeCode.Char) + || type.IsKnownType(KnownTypeCode.String); + } + + bool IsCompatibleImplicitConversionOperator(IMethod operatorMethod) + { + if (!operatorMethod.IsOperator) + return false; + if (operatorMethod.Name != "op_Implicit") + return false; + if (operatorMethod.Parameters.Count != 1) + return false; + return IsCompatibleWithSwitch(operatorMethod.ReturnType); + } + } + + protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context) + { + // switch-expression does not support implicit conversions + var (value, type, strToInt) = TranslateSwitchValue(inst, true); + IL.SwitchSection defaultSection = inst.GetDefaultSection(); SwitchExpression switchExpr = new SwitchExpression(); switchExpr.Expression = value; diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 4dfc5f658..094a51355 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -210,30 +210,7 @@ namespace ICSharpCode.Decompiler.CSharp var oldCaseLabelMapping = caseLabelMapping; caseLabelMapping = new Dictionary(); - TranslatedExpression value; - IType type; - if (inst.Value is StringToInt strToInt) - { - value = exprBuilder.Translate(strToInt.Argument) - .ConvertTo( - strToInt.ExpectedType, - exprBuilder, - // switch statement does support implicit conversions in general, however, the rules are - // not very intuitive and in order to prevent bugs, we emit an explicit cast. - allowImplicitConversion: false - ); - type = exprBuilder.compilation.FindType(KnownTypeCode.String); - } - else - { - strToInt = null; - value = exprBuilder.Translate(inst.Value); - if (inst.Type != null) - { - value = value.ConvertTo(inst.Type, exprBuilder, allowImplicitConversion: true); - } - type = value.Type; - } + var (value, type, strToInt) = exprBuilder.TranslateSwitchValue(inst, false); IL.SwitchSection defaultSection = inst.GetDefaultSection(); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 1abe4653e..d45e054ca 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -48,6 +48,12 @@ + + + + true + true + True diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index e254d243b..8302038b8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -1070,10 +1070,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms stringValues.Add((null, nullValueCaseBlock)); } // In newer Roslyn versions (>=3.7) the null check appears in the default case, not prior to the switch. - if (!stringValues.Any(pair => pair.Value == null) && IsNullCheckInDefaultBlock(ref exitOrDefaultBlock, switchValueLoad.Variable, out nullValueCaseBlock)) + ILInstruction exitOrDefault = exitOrDefaultBlock; + if (!stringValues.Any(pair => pair.Value == null) && IsNullCheckInDefaultBlock(ref exitOrDefault, switchValueLoad.Variable, out nullValueCaseBlock)) { stringValues.Add((null, nullValueCaseBlock)); } + exitOrDefaultBlock = (Block)exitOrDefault; context.Step(nameof(MatchRoslynSwitchOnString), switchValueLoad); if (exitOrDefaultBlock != null) @@ -1176,7 +1178,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!instructions[i + 1].MatchBranch(out var nextBlock)) return false; - if (!exitBlockJump.MatchBranch(out nullCase)) + if (!exitBlockJump.MatchBranch(out nullCase) && !exitBlockJump.MatchLeave(out _)) return false; // if (comp(ldloc switchValueVar == ldnull)) br ... // br switchOnLengthBlock @@ -1202,7 +1204,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms switchValueVar = null; // will be extracted in MatchSwitchOnLengthBlock switchOnLengthBlockStartOffset = i; } - Block defaultCase = null; + ILInstruction defaultCase = null; if (!MatchSwitchOnLengthBlock(ref switchValueVar, switchOnLengthBlock, switchOnLengthBlockStartOffset, out var blocksByLength)) return false; List<(string, ILInstruction)> stringValues = new(); @@ -1216,41 +1218,51 @@ namespace ICSharpCode.Decompiler.IL.Transforms else { int length = (int)b.Length.Intervals[0].Start; - if (MatchSwitchOnCharBlock(b.TargetBlock, length, switchValueVar, out var mapping)) + switch (b.TargetBlock) { - foreach (var item in mapping) - { - if (!stringValues.Any(x => x.Item1 == item.StringValue)) + case Leave leave: + break; + case Block targetBlock: + if (MatchSwitchOnCharBlock(targetBlock, length, switchValueVar, out var mapping)) + { + foreach (var item in mapping) + { + if (!stringValues.Any(x => x.Item1 == item.StringValue)) + { + stringValues.Add(item); + } + else + { + return false; + } + } + } + else if (MatchRoslynCaseBlockHead(targetBlock, switchValueVar, out var bodyOrLeave, out var exit, out string stringValue, out _)) + { + if (exit != defaultCase) + return false; + if (!stringValues.Any(x => x.Item1 == stringValue)) + { + stringValues.Add((stringValue, bodyOrLeave)); + } + else + { + return false; + } + } + else if (length == 0) { - stringValues.Add(item); + stringValues.Add(("", b.TargetBlock)); } else { return false; } - } - } - else if (MatchRoslynCaseBlockHead(b.TargetBlock, switchValueVar, out var bodyOrLeave, out var exit, out string stringValue, out _)) - { - if (exit != defaultCase) + break; + default: return false; - if (!stringValues.Any(x => x.Item1 == stringValue)) - { - stringValues.Add((stringValue, bodyOrLeave)); - } - else - { - return false; - } - } - else if (length == 0) - { - stringValues.Add(("", b.TargetBlock)); - } - else - { - return false; } + } } @@ -1278,7 +1290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } var newSwitch = new SwitchInstruction(new StringToInt(new LdLoc(switchValueVar), values, switchValueVar.Type)); newSwitch.Sections.AddRange(sections); - newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = new Branch(defaultCase) }); + newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultCase is Block b2 ? new Branch(b2) : defaultCase }); newSwitch.AddILRange(instructions[i]); if (nullCase != null) { @@ -1399,7 +1411,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return results?.Count > 0; } - bool MatchSwitchOnLengthBlock(ref ILVariable switchValueVar, Block switchOnLengthBlock, int startOffset, out List<(LongSet Length, Block TargetBlock)> blocks) + bool MatchSwitchOnLengthBlock(ref ILVariable switchValueVar, Block switchOnLengthBlock, int startOffset, out List<(LongSet Length, ILInstruction TargetBlock)> blocks) { blocks = null; SwitchInstruction @switch; @@ -1482,17 +1494,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (section.HasNullLabel) return false; - if (!section.Body.MatchBranch(out var target)) + if (!section.Body.MatchBranch(out var target) && !section.Body.MatchLeave(out _)) return false; + ILInstruction targetInst = target ?? section.Body; if (section.Labels.Count() != 1) { - defaultCase ??= target; - if (defaultCase != target) + defaultCase ??= targetInst; + if (defaultCase != targetInst) return false; } else { - blocks.Add((section.Labels, target)); + blocks.Add((section.Labels, targetInst)); } } return true; @@ -1506,10 +1519,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// br newDefaultBlock /// } /// - private bool IsNullCheckInDefaultBlock(ref Block exitOrDefaultBlock, ILVariable switchVar, out Block nullValueCaseBlock) + private bool IsNullCheckInDefaultBlock(ref ILInstruction exitOrDefault, ILVariable switchVar, out Block nullValueCaseBlock) { nullValueCaseBlock = null; - if (exitOrDefaultBlock == null) + if (exitOrDefault is not Block exitOrDefaultBlock) return false; if (!exitOrDefaultBlock.Instructions[0].MatchIfInstruction(out var condition, out var thenBranch)) return false; @@ -1523,7 +1536,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (elseBlock.Parent != exitOrDefaultBlock.Parent) return false; - exitOrDefaultBlock = elseBlock; + exitOrDefault = elseBlock; return true; } diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 90ba8724a..ffd6d700d 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -189,15 +189,16 @@ namespace ICSharpCode.Decompiler.Metadata switch (pair[0].Trim().ToUpperInvariant()) { case "VERSION": - var versionString = pair[1].TrimStart('v', ' ', '\t'); - if (identifier == TargetFrameworkIdentifier.NETCoreApp || - identifier == TargetFrameworkIdentifier.NETStandard) - { - if (versionString.Length == 3) - versionString += ".0"; - } + var versionString = pair[1].TrimStart('v', 'V', ' ', '\t'); + if (!Version.TryParse(versionString, out version)) + { version = null; + } + else + { + version = new Version(version.Major, version.Minor, version.Build < 0 ? 0 : version.Build); + } // .NET 5 or greater still use ".NETCOREAPP" as TargetFrameworkAttribute value... if (version?.Major >= 5 && identifier == TargetFrameworkIdentifier.NETCoreApp) identifier = TargetFrameworkIdentifier.NET; diff --git a/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs b/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs index 947188ef1..8810b7e16 100644 --- a/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs +++ b/ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs @@ -4,7 +4,7 @@ public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "preview3"; + public const string VersionName = "RC"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; public const string FullVersionWithShortCommitHash = FullVersion + "+$INSERTSHORTCOMMITHASH$"; diff --git a/ICSharpCode.Decompiler/packages.lock.json b/ICSharpCode.Decompiler/packages.lock.json new file mode 100644 index 000000000..c6e34ae99 --- /dev/null +++ b/ICSharpCode.Decompiler/packages.lock.json @@ -0,0 +1,91 @@ +{ + "version": 1, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Collections.Immutable": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Reflection.Metadata": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "sffDOcex1C3HO5kDolOYcWXTwRpZY/LvJujM6SMjn63fWMJWchYAAmkoAJXlbpZ5yf4d+KMgxd+LeETa4gD9sQ==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "TunnelVisionLabs.ReferenceAssemblyAnnotator": { + "type": "Direct", + "requested": "[1.0.0-alpha.160, )", + "resolved": "1.0.0-alpha.160", + "contentHash": "ktxB8PGoPpIaYKjLk/+P94Fi2Qw2E1Dw7atBQRrKnHA57sk8WwmkI4RJmg6s5ph4k1RIaaAZMus05ah/AikEkA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj index ca25d2eef..aba229ad0 100644 --- a/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj +++ b/ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj @@ -31,6 +31,12 @@ + + + + true + true + false diff --git a/ICSharpCode.ILSpyCmd/packages.lock.json b/ICSharpCode.ILSpyCmd/packages.lock.json new file mode 100644 index 000000000..0ffdf6bd2 --- /dev/null +++ b/ICSharpCode.ILSpyCmd/packages.lock.json @@ -0,0 +1,453 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "McMaster.Extensions.Hosting.CommandLine": { + "type": "Direct", + "requested": "[4.1.1, )", + "resolved": "4.1.1", + "contentHash": "+a37L3hHZC2KG1sbwdzTGlUWIJWYQv/9I4dLnrC0OVusR/665hkewjlz1jiAKa8jYbve4GTSZsRCoVXcSFFrdA==", + "dependencies": { + "McMaster.Extensions.CommandLineUtils": "4.1.1", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.Hosting": { + "type": "Direct", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "bP9EEkHBEfjgYiG8nUaXqMk/ujwJrffOkNPP7onpRMO8R+OUSESSP4xHkCAXgYZ1COP2Q9lXlU5gkMFh20gRuw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.Configuration.CommandLine": "8.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.Configuration.Json": "8.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "8.0.1", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Configuration": "8.0.1", + "Microsoft.Extensions.Logging.Console": "8.0.1", + "Microsoft.Extensions.Logging.Debug": "8.0.1", + "Microsoft.Extensions.Logging.EventLog": "8.0.1", + "Microsoft.Extensions.Logging.EventSource": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "NuGet.Protocol": { + "type": "Direct", + "requested": "[6.12.1, )", + "resolved": "6.12.1", + "contentHash": "VBN7OtG/Y9Rnj1WT3G8X88ZHu5Pq+yzca5Z6OI/FWXcENVAQkUl0ml6Cv8ghOqYyiuvnObGDV9oWLD/bIuVtDw==", + "dependencies": { + "NuGet.Packaging": "6.12.1" + } + }, + "TomsToolbox.Composition.Analyzer": { + "type": "Direct", + "requested": "[2.21.0, )", + "resolved": "2.21.0", + "contentHash": "vJx9hxAzjni34slGz78ewqYP9Ylk8dJszfEUK1TF5cflVKMBO3ORSFd0FtICTwJtE8munvZrMrcLWwXt5bIcEA==" + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "NZuZMz3Q8Z780nKX3ifV1fE7lS+6pynDHK71OfU4OZ1ItgvDOhyOC7E6z+JMZrAj63zRpwbdldYFk499t3+1dQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "7tYqdPPpAK+3jO9d5LTuCK2VxrEdf85Ol4trUr6ds4jclBecadWZ/RyPCbNjfbN5iGTfUnD/h65TOQuqQv2c+A==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Json": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "doVPCUUCY7c6LhBsEfiy3W1bvS7Mi6LkfQMS8nlC22jZWNxBv8VO8bdfeyvpYFst6Kxqk7HBC6lytmEoBssvSQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "uzcg/5U2eLyn5LIKlERkdSxw6VPC1yydnOSQiRRWGBGN3kphq3iL4emORzrojScDmxRhv49gp5BI8U3Dz7y4iA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Configuration": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "B8hqNuYudC2RB+L/DI33uO4rf5by41fZVdcVL2oZj0UyoAZqnwTwYHp1KafoH4nkl1/23piNeybFFASaV2HkFg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "ZD1m4GXoxcZeDJIq8qePKj+QAWeQNO/OG8skvrOG8RQfxLp9MAKRoliTc27xanoNUzeqvX5HhS/I7c0BvwAYUg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "System.Diagnostics.EventLog": "8.0.1" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "YMXMAla6B6sEf/SnfZYTty633Ool3AH7KOw2LOaaEqwSo2piK4f7HMtzyc3CNiipDnq1fsUSuG5Oc7ZzpVy8WQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NuGet.Common": { + "type": "Transitive", + "resolved": "6.12.1", + "contentHash": "nk8nTdhQl4x2VaAQUvefI7DDYAuBDlE+OZZRffm50Qx5dUAEq8wkc5JIqrN2lTEohObHPI/SXyG2UFdMQkrdyg==", + "dependencies": { + "NuGet.Frameworks": "6.12.1" + } + }, + "NuGet.Configuration": { + "type": "Transitive", + "resolved": "6.12.1", + "contentHash": "IRwlY1379ZgJ0oEJvjD+lDuOhJ5S1fsU5n/bEC5/i0+N9bo2WIMDAdaQ/qIdyK/gMJ/YWS+++GSX6rN7luqEvg==", + "dependencies": { + "NuGet.Common": "6.12.1", + "System.Security.Cryptography.ProtectedData": "4.4.0" + } + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.12.1", + "contentHash": "kPaRD5RJC0ByUg+yGX6bDz5XHMI7OYmQwP8kbtef+vZ+csj/VDb5Bwas4ChxwhoAbI8lEvwP5/3aViQPpgNBow==" + }, + "NuGet.Packaging": { + "type": "Transitive", + "resolved": "6.12.1", + "contentHash": "6s5NO3VNX6fIx6GwuWZtIsal9W1xkelYd3Vg2KUAg1zGqnKC3wB5IZlombvVGVGcwyl/A+iDvpUwSvgeDoB3wA==", + "dependencies": { + "Newtonsoft.Json": "13.0.3", + "NuGet.Configuration": "6.12.1", + "NuGet.Versioning": "6.12.1", + "System.Formats.Asn1": "8.0.1", + "System.Security.Cryptography.Pkcs": "6.0.4" + } + }, + "NuGet.Versioning": { + "type": "Transitive", + "resolved": "6.12.1", + "contentHash": "fJ6rFYANDnohFsdpaY79FvrJxI6murmoOxXz6nZlf819F48+IBKMnAIg3oIBRtZq5y498ObMtKnro5IitvizUg==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==" + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "XqKba7Mm/koKSjKMfW82olQdmfbI5yqeoLV/tidRp7fbh5rmHAQ5raDI/7SU0swTzv+jgqtUGkzmFxuUg0it1A==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "LGbXi1oUJ9QgCNGXRO9ndzBL/GZgANcsURpMhNR8uO+rca47SZmciS3RSQUvlQRwK3QHZSHNOXzoMUASKA+Anw==", + "dependencies": { + "System.Formats.Asn1": "6.0.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==" + }, + "icsharpcode.decompiler": { + "type": "Project", + "dependencies": { + "System.Collections.Immutable": "[6.0.0, )", + "System.Reflection.Metadata": "[6.0.0, )" + } + }, + "icsharpcode.ilspyx": { + "type": "Project", + "dependencies": { + "ICSharpCode.Decompiler": "[8.0.0-noversion, )", + "K4os.Compression.LZ4": "[1.3.8, )", + "Mono.Cecil": "[0.11.6, )", + "System.Composition.AttributedModel": "[9.0.0, )", + "System.Reflection.Metadata": "[9.0.0, )", + "System.Runtime.CompilerServices.Unsafe": "[6.1.0, )" + } + }, + "K4os.Compression.LZ4": { + "type": "CentralTransitive", + "requested": "[1.3.8, )", + "resolved": "1.3.8", + "contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g==" + }, + "McMaster.Extensions.CommandLineUtils": { + "type": "CentralTransitive", + "requested": "[4.1.1, )", + "resolved": "4.1.1", + "contentHash": "zxgDY+G5yVq2q8sVB3Z275Qkxed1jC95nwAfnlSyoG4l5Nicvd4+ke1jXusEZEfyuErlAgXCKS937c13FmZWBg==", + "dependencies": { + "System.ComponentModel.Annotations": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "CentralTransitive", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "CentralTransitive", + "requested": "[8.0.2, )", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Mono.Cecil": { + "type": "CentralTransitive", + "requested": "[0.11.6, )", + "resolved": "0.11.6", + "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA==" + }, + "System.Collections.Immutable": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" + }, + "System.Composition.AttributedModel": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "iri00l/zIX9g4lHMY+Nz0qV1n40+jFYAmgsaiNn16xvt2RDwlqByNG4wgblagnDYxm3YSQQ0jLlC/7Xlk9CzyA==" + }, + "System.Reflection.Metadata": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==", + "dependencies": { + "System.Collections.Immutable": "9.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "CentralTransitive", + "requested": "[6.1.0, )", + "resolved": "6.1.0", + "contentHash": "5o/HZxx6RVqYlhKSq8/zronDkALJZUT2Vz0hx43f0gwe8mwlM0y2nYlqdBwLMzr262Bwvpikeb/yEwkAa5PADg==" + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj b/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj index 76a7b3504..4272c40aa 100644 --- a/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj +++ b/ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj @@ -48,6 +48,12 @@ + + + true + true + + diff --git a/ICSharpCode.ILSpyX/packages.lock.json b/ICSharpCode.ILSpyX/packages.lock.json new file mode 100644 index 000000000..37a460ef0 --- /dev/null +++ b/ICSharpCode.ILSpyX/packages.lock.json @@ -0,0 +1,79 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "K4os.Compression.LZ4": { + "type": "Direct", + "requested": "[1.3.8, )", + "resolved": "1.3.8", + "contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Mono.Cecil": { + "type": "Direct", + "requested": "[0.11.6, )", + "resolved": "0.11.6", + "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA==" + }, + "System.Composition.AttributedModel": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "iri00l/zIX9g4lHMY+Nz0qV1n40+jFYAmgsaiNn16xvt2RDwlqByNG4wgblagnDYxm3YSQQ0jLlC/7Xlk9CzyA==" + }, + "System.Reflection.Metadata": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==", + "dependencies": { + "System.Collections.Immutable": "9.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Direct", + "requested": "[6.1.0, )", + "resolved": "6.1.0", + "contentHash": "5o/HZxx6RVqYlhKSq8/zronDkALJZUT2Vz0hx43f0gwe8mwlM0y2nYlqdBwLMzr262Bwvpikeb/yEwkAa5PADg==" + }, + "TomsToolbox.Composition.Analyzer": { + "type": "Direct", + "requested": "[2.21.0, )", + "resolved": "2.21.0", + "contentHash": "vJx9hxAzjni34slGz78ewqYP9Ylk8dJszfEUK1TF5cflVKMBO3ORSFd0FtICTwJtE8munvZrMrcLWwXt5bIcEA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "icsharpcode.decompiler": { + "type": "Project", + "dependencies": { + "System.Collections.Immutable": "[6.0.0, )", + "System.Reflection.Metadata": "[6.0.0, )" + } + }, + "System.Collections.Immutable": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" + } + } + } +} \ No newline at end of file diff --git a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj index 83404ae2f..c5d27caea 100644 --- a/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj +++ b/ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj @@ -149,7 +149,7 @@ - + Always true diff --git a/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template b/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template index be1488335..55e6649e6 100644 --- a/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template +++ b/ILSpy.AddIn.VS2022/source.extension.vsixmanifest.template @@ -5,7 +5,7 @@ ILSpy 2022 Integrates the ILSpy decompiler into Visual Studio. https://ilspy.net - license.txt + LICENSE ILSpy-Large.ico ILSpy;IL;decompile;decompiler;decompilation;C#;CSharp;.NET;Productivity;Open Source;Free diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index 9b41766fd..bdcf4d021 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -148,7 +148,7 @@ - + Always true diff --git a/ILSpy.AddIn/source.extension.vsixmanifest.template b/ILSpy.AddIn/source.extension.vsixmanifest.template index b87e1d1dc..c3d0d0d60 100644 --- a/ILSpy.AddIn/source.extension.vsixmanifest.template +++ b/ILSpy.AddIn/source.extension.vsixmanifest.template @@ -5,7 +5,7 @@ ILSpy Integrates the ILSpy decompiler into Visual Studio. https://ilspy.net - license.txt + LICENSE ILSpy-Large.ico diff --git a/ILSpy.Installer/ILSpy.Installer.csproj b/ILSpy.Installer/ILSpy.Installer.csproj index 6edfaab49..266020a76 100644 --- a/ILSpy.Installer/ILSpy.Installer.csproj +++ b/ILSpy.Installer/ILSpy.Installer.csproj @@ -15,7 +15,7 @@ - + diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index 26a3dc0df..de2620bd7 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -30,6 +30,7 @@ using System.Runtime.CompilerServices; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; @@ -96,6 +97,10 @@ namespace ICSharpCode.ILSpy.ReadyToRun public void WriteReference(IMember member, string text, bool isDefinition = false) { } + + public void WriteReference(MetadataFile metadata, Handle handle, string text, string protocol = "decompile", bool isDefinition = false) + { + } } #endif diff --git a/ILSpy/Commands/CreateDiagramContextMenuEntry.cs b/ILSpy/Commands/CreateDiagramContextMenuEntry.cs new file mode 100644 index 000000000..edbbe14a3 --- /dev/null +++ b/ILSpy/Commands/CreateDiagramContextMenuEntry.cs @@ -0,0 +1,124 @@ +// Copyright (c) 2024 Christoph Wille for the SharpDevelop Team +// +// 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.Composition; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.Docking; +using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpyX.MermaidDiagrammer; + +using Microsoft.Win32; + +namespace ICSharpCode.ILSpy.TextView +{ + [ExportContextMenuEntry(Header = nameof(Resources._CreateDiagram), Category = nameof(Resources.Save), Icon = "Images/Save")] + [Shared] + sealed class CreateDiagramContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry + { + public void Execute(TextViewContext context) + { + var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly; + if (assembly == null) + return; + + var selectedPath = SelectDestinationFolder(); + if (string.IsNullOrEmpty(selectedPath)) + return; + + dockWorkspace.RunWithCancellation(ct => Task.Factory.StartNew(() => { + AvalonEditTextOutput output = new() { + EnableHyperlinks = true + }; + Stopwatch stopwatch = Stopwatch.StartNew(); + try + { + var command = new GenerateHtmlDiagrammer { + Assembly = assembly.FileName, + OutputFolder = selectedPath + }; + + command.Run(); + } + catch (OperationCanceledException) + { + output.WriteLine(); + output.WriteLine(Resources.GenerationWasCancelled); + throw; + } + stopwatch.Stop(); + output.WriteLine(Resources.GenerationCompleteInSeconds, stopwatch.Elapsed.TotalSeconds.ToString("F1")); + output.WriteLine(); + output.WriteLine("Learn more: " + "https://github.com/icsharpcode/ILSpy/wiki/Diagramming#tips-for-using-the-html-diagrammer"); + output.WriteLine(); + + var diagramHtml = Path.Combine(selectedPath, "index.html"); + output.AddButton(null, Resources.OpenExplorer, delegate { Process.Start("explorer", "/select,\"" + diagramHtml + "\""); }); + output.WriteLine(); + return output; + }, ct), Properties.Resources.CreatingDiagram).Then(dockWorkspace.ShowText).HandleExceptions(); + + return; + } + + public bool IsEnabled(TextViewContext context) => true; + + public bool IsVisible(TextViewContext context) + { + return context.SelectedTreeNodes?.Length == 1 + && context.SelectedTreeNodes?.FirstOrDefault() is AssemblyTreeNode tn + && tn.LoadedAssembly.IsLoadedAsValidAssembly; + } + + static string SelectDestinationFolder() + { + OpenFolderDialog dialog = new(); + dialog.Multiselect = false; + dialog.Title = "Select target folder"; + + if (dialog.ShowDialog() != true) + { + return null; + } + + string selectedPath = Path.GetDirectoryName(dialog.FolderName); + bool directoryNotEmpty; + try + { + directoryNotEmpty = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } + catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) + { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Target directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + return null; + } + + return dialog.FolderName; + } + } +} diff --git a/ILSpy/Controls/CustomDialog.cs b/ILSpy/Controls/CustomDialog.cs index 994bedffc..63ca6bef9 100644 --- a/ILSpy/Controls/CustomDialog.cs +++ b/ILSpy/Controls/CustomDialog.cs @@ -51,9 +51,8 @@ namespace ICSharpCode.ILSpy.Controls using (Graphics g = this.CreateGraphics()) { - Rectangle screen = Screen.PrimaryScreen.WorkingArea; - SizeF size = g.MeasureString(message, label.Font, screen.Width - 20); - Size clientSize = size.ToSize(); + SizeF size = TextRenderer.MeasureText(message, label.Font, default, TextFormatFlags.NoPrefix); + Size clientSize = new Size((int)Math.Ceiling(size.Width * 96 / g.DpiX) + DockPadding.Left + DockPadding.Right, (int)Math.Ceiling(size.Height * 96 / g.DpiY) + DockPadding.Top + DockPadding.Bottom); Button[] buttons = new Button[buttonLabels.Length]; int[] positions = new int[buttonLabels.Length]; int pos = 0; @@ -65,8 +64,8 @@ namespace ICSharpCode.ILSpy.Controls string buttonLabel = buttonLabels[i]; newButton.Text = buttonLabel; newButton.Click += new EventHandler(ButtonClick); - SizeF buttonSize = g.MeasureString(buttonLabel, newButton.Font); - newButton.Width = Math.Max(newButton.Width, ((int)Math.Ceiling(buttonSize.Width / 8.0) + 1) * 8); + SizeF buttonSize = TextRenderer.MeasureText(buttonLabel, newButton.Font); + newButton.Width = Math.Max(newButton.Width, ((int)Math.Ceiling(buttonSize.Width * 96 / g.DpiX / 8.0) + 1) * 8); positions[i] = pos; buttons[i] = newButton; pos += newButton.Width + 4; @@ -87,7 +86,7 @@ namespace ICSharpCode.ILSpy.Controls { clientSize.Width = pos; } - clientSize.Height += panel.Height + 6; + clientSize.Height += panel.Height; this.ClientSize = clientSize; int start = (clientSize.Width - pos) / 2; for (int i = 0; i < buttons.Length; i++) diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index d92b92847..2fe0d0fc2 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -25,6 +25,7 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using System.Windows.Data; using System.Windows.Threading; using AvalonDock; @@ -50,6 +51,7 @@ namespace ICSharpCode.ILSpy.Docking private readonly IExportProvider exportProvider; private readonly ObservableCollection tabPages = []; + private ReadOnlyCollection toolPanes; readonly SessionSettings sessionSettings; @@ -123,7 +125,7 @@ namespace ICSharpCode.ILSpy.Docking public ReadOnlyObservableCollection TabPages { get; } - public ReadOnlyCollection ToolPanes => exportProvider + public ReadOnlyCollection ToolPanes => toolPanes ??= exportProvider .GetExportedValues("ToolPane") .OrderBy(item => item.Title) .ToArray() @@ -194,6 +196,9 @@ namespace ICSharpCode.ILSpy.Docking { serializer.LayoutSerializationCallback -= LayoutSerializationCallback; } + + DockingManager.SetBinding(DockingManager.AnchorablesSourceProperty, new Binding(nameof(ToolPanes))); + DockingManager.SetBinding(DockingManager.DocumentsSourceProperty, new Binding(nameof(TabPages))); } void LayoutSerializationCallback(object sender, LayoutSerializationCallbackEventArgs e) @@ -226,6 +231,11 @@ namespace ICSharpCode.ILSpy.Docking return ActiveTabPage.ShowTextViewAsync(textView => textView.RunWithCancellation(taskCreation)); } + public Task RunWithCancellation(Func> taskCreation, string progressTitle) + { + return ActiveTabPage.ShowTextViewAsync(textView => textView.RunWithCancellation(taskCreation, progressTitle)); + } + internal void ShowNodes(AvalonEditTextOutput output, TreeNodes.ILSpyTreeNode[] nodes, IHighlightingDefinition highlighting) { ActiveTabPage.ShowTextView(textView => textView.ShowNodes(output, nodes, highlighting)); diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 6bd46530f..ba2e33f5b 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -58,7 +58,7 @@ - + license.txt @@ -93,6 +93,11 @@ + + + + + diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 99c5ee08f..a60f6c990 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -67,8 +67,6 @@ diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index ca8b55151..01628056b 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -114,6 +114,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Create _Diagram.... + /// + public static string _CreateDiagram { + get { + return ResourceManager.GetString("_CreateDiagram", resourceCulture); + } + } + /// /// Looks up a localized string similar to _File. /// @@ -567,6 +576,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Creating diagram.... + /// + public static string CreatingDiagram { + get { + return ResourceManager.GetString("CreatingDiagram", resourceCulture); + } + } + /// /// Looks up a localized string similar to Culture. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index b853b0641..bcb79fae7 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -210,6 +210,9 @@ Are you sure you want to continue? Create + + Creating diagram... + Culture @@ -1036,6 +1039,9 @@ Do you want to continue? _Collapse all tree nodes + + Create _Diagram... + _File diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 2e65074d1..4e748fd2a 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -595,14 +595,14 @@ namespace ICSharpCode.ILSpy.TextView /// the task. /// If another task is started before the previous task finishes running, the previous task is cancelled. /// - public Task RunWithCancellation(Func> taskCreation) + public Task RunWithCancellation(Func> taskCreation, string? progressTitle = null) { if (waitAdorner.Visibility != Visibility.Visible) { waitAdorner.Visibility = Visibility.Visible; // Work around a WPF bug by setting IsIndeterminate only while the progress bar is visible. // https://github.com/icsharpcode/ILSpy/issues/593 - progressTitle.Text = Properties.Resources.Decompiling; + this.progressTitle.Text = progressTitle == null ? Properties.Resources.Decompiling : progressTitle; progressBar.IsIndeterminate = true; progressText.Text = null; progressText.Visibility = Visibility.Collapsed; diff --git a/doc/license.txt b/LICENSE similarity index 92% rename from doc/license.txt rename to LICENSE index 52c20debd..22e1b4257 100644 --- a/doc/license.txt +++ b/LICENSE @@ -1,6 +1,6 @@ MIT license -Copyright (c) 2011-2023 AlphaSierraPapa for the ILSpy team +Copyright (c) 2011-2025 AlphaSierraPapa for the ILSpy team 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 diff --git a/NuGet.config b/NuGet.config index 5209a02bf..ccdac39a4 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,7 +4,7 @@ - + @@ -12,7 +12,7 @@ - + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..1ba498f29 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +Supported are the latest released version as well as the latest preview/rc version. + +## Reporting a Vulnerability + +Security issues and bugs should be reported privately to christoph.wille AT gmail.com. Please note that +we cannot guarantee a response time (*) although we will strive to get back to you within one business day. If you +do not hear back from Chris within a week, alternatively try to contact siegfriedpammer AT gmail.com. + +Please do not open issues for anything you think might have a security implication. + +(*) We are an OSS project entirely run by volunteers and sometimes lifetm will mean longer response times. diff --git a/doc/ILSpyAboutPage.txt b/doc/ILSpyAboutPage.txt index 0b5610aea..cf181a904 100644 --- a/doc/ILSpyAboutPage.txt +++ b/doc/ILSpyAboutPage.txt @@ -3,7 +3,7 @@ ILSpy is the open-source .NET assembly browser and decompiler. Website: https://ilspy.net/ Found a bug? https://github.com/icsharpcode/ILSpy/issues/new/choose -Copyright 2011-2023 AlphaSierraPapa for the ILSpy team +Copyright 2011-2025 AlphaSierraPapa for the ILSpy team Current and past contributors: https://github.com/icsharpcode/ILSpy/graphs/contributors ILSpy is distributed under the MIT License. diff --git a/doc/ILSpyAboutPage_zh_Hans.txt b/doc/ILSpyAboutPage_zh_Hans.txt index ebaf5676d..3723702f8 100644 --- a/doc/ILSpyAboutPage_zh_Hans.txt +++ b/doc/ILSpyAboutPage_zh_Hans.txt @@ -4,7 +4,7 @@ ILSpy 是开源的 .NET 程序集浏览器和反编译器。 反馈BUG: https://github.com/icsharpcode/ILSpy/issues/new/choose 中文翻译反馈:https://github.com/maikebing/ILSpy/issues -Copyright 2011-2023 AlphaSierraPapa for the ILSpy team +Copyright 2011-2025 AlphaSierraPapa for the ILSpy team 当前和过去的贡献者: https://github.com/icsharpcode/ILSpy/graphs/contributors ILSpy 基于 MIT 许可证发行。 diff --git a/doc/copyright.txt b/doc/copyright.txt index de4935037..979e262b9 100644 --- a/doc/copyright.txt +++ b/doc/copyright.txt @@ -1,4 +1,4 @@ -Copyright 2011-2023 for the ILSpy team +Copyright 2011-2025 for the ILSpy team by AlphaSierraPapa, Christoph Wille