mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
A member that decompiled to a very large amount of text could hang or exhaust memory on the UI thread with no escape. Cap display output at five million characters and, on overflow, show a message offering to display anyway (at an extended limit) or save to disk. Also bridge three Display options that were persisted but never reached the decompiler -- brace folding, debug-symbol info, and the indentation string -- so toggling them once again affects the generated source. Assisted-by: Claude:claude-opus-4-8:Claude Codepull/3755/head
5 changed files with 293 additions and 8 deletions
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa 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 AwesomeAssertions; |
||||
|
||||
using ICSharpCode.Decompiler; |
||||
|
||||
using ILSpy.Options; |
||||
using ILSpy.TextView; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.ILSpy.Tests.TextView; |
||||
|
||||
/// <summary>
|
||||
/// The Display options that affect generated source (brace folding, debug info, indentation, and the
|
||||
/// fold-expansion flags) must be bridged into the decompiler settings used for a decompile. WPF wired
|
||||
/// these in DecompilationOptions; the Avalonia port previously only bridged the two expand flags, so
|
||||
/// FoldBraces / ShowDebugInfo / indentation were silently dead.
|
||||
/// </summary>
|
||||
[TestFixture] |
||||
public class DisplaySettingsBridgeTests |
||||
{ |
||||
[Test] |
||||
public void Brace_Folding_And_Debug_Info_Are_Bridged() |
||||
{ |
||||
var display = new DisplaySettings { FoldBraces = true, ShowDebugInfo = true }; |
||||
var settings = new DecompilerSettings { FoldBraces = false, ShowDebugInfo = false }; |
||||
|
||||
DecompilerTabPageModel.ApplyDisplaySettings(settings, display); |
||||
|
||||
settings.FoldBraces.Should().BeTrue(); |
||||
settings.ShowDebugInfo.Should().BeTrue(); |
||||
} |
||||
|
||||
[Test] |
||||
public void Indentation_Uses_Spaces_When_Tabs_Are_Off() |
||||
{ |
||||
var display = new DisplaySettings { IndentationUseTabs = false, IndentationSize = 2 }; |
||||
var settings = new DecompilerSettings(); |
||||
|
||||
DecompilerTabPageModel.ApplyDisplaySettings(settings, display); |
||||
|
||||
settings.CSharpFormattingOptions.IndentationString.Should().Be(" ", "two spaces"); |
||||
} |
||||
|
||||
[Test] |
||||
public void Indentation_Uses_Tabs_When_Enabled() |
||||
{ |
||||
var display = new DisplaySettings { IndentationUseTabs = true, IndentationSize = 4, IndentationTabSize = 4 }; |
||||
var settings = new DecompilerSettings(); |
||||
|
||||
DecompilerTabPageModel.ApplyDisplaySettings(settings, display); |
||||
|
||||
settings.CSharpFormattingOptions.IndentationString.Should().Be("\t", "one tab"); |
||||
} |
||||
|
||||
[Test] |
||||
public void Expand_Flags_Still_Bridged() |
||||
{ |
||||
var display = new DisplaySettings { ExpandUsingDeclarations = true, ExpandMemberDefinitions = true }; |
||||
var settings = new DecompilerSettings { ExpandUsingDeclarations = false, ExpandMemberDefinitions = false }; |
||||
|
||||
DecompilerTabPageModel.ApplyDisplaySettings(settings, display); |
||||
|
||||
settings.ExpandUsingDeclarations.Should().BeTrue(); |
||||
settings.ExpandMemberDefinitions.Should().BeTrue(); |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa 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 AwesomeAssertions; |
||||
|
||||
using ILSpy.TextView; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.ILSpy.Tests.TextView; |
||||
|
||||
/// <summary>
|
||||
/// The decompiler text output stops once it exceeds its LengthLimit, so a runaway decompile (a huge
|
||||
/// type/namespace) can't hang or OOM the UI thread. (Regression: the Avalonia port had no limit.)
|
||||
/// </summary>
|
||||
[TestFixture] |
||||
public class OutputLengthLimitTests |
||||
{ |
||||
[Test] |
||||
public void Write_Throws_Once_The_Length_Limit_Is_Exceeded() |
||||
{ |
||||
var output = new AvaloniaEditTextOutput { LengthLimit = 10 }; |
||||
|
||||
var act = () => { |
||||
for (int i = 0; i < 100; i++) |
||||
output.Write("0123456789"); |
||||
}; |
||||
|
||||
act.Should().Throw<OutputLengthExceededException>(); |
||||
} |
||||
|
||||
[Test] |
||||
public void Write_Does_Not_Throw_Below_The_Limit() |
||||
{ |
||||
var output = new AvaloniaEditTextOutput { LengthLimit = 1000 }; |
||||
|
||||
var act = () => { |
||||
output.Write("short output"); |
||||
output.WriteLine(); |
||||
}; |
||||
|
||||
act.Should().NotThrow(); |
||||
} |
||||
|
||||
[Test] |
||||
public void Default_Limit_Is_Unlimited() |
||||
{ |
||||
var output = new AvaloniaEditTextOutput(); |
||||
output.LengthLimit.Should().Be(int.MaxValue); |
||||
} |
||||
} |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa 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; |
||||
|
||||
namespace ILSpy.TextView |
||||
{ |
||||
/// <summary>
|
||||
/// Thrown by <see cref="AvaloniaEditTextOutput"/> once its accumulated text exceeds
|
||||
/// <see cref="AvaloniaEditTextOutput.LengthLimit"/>, so a runaway decompile (a huge type or
|
||||
/// namespace) is stopped before it can hang or exhaust memory on the UI thread.
|
||||
/// </summary>
|
||||
public sealed class OutputLengthExceededException : Exception |
||||
{ |
||||
} |
||||
} |
||||
Loading…
Reference in new issue