From 468dc6e922e6602549c24c4b44a6872acb324f66 Mon Sep 17 00:00:00 2001 From: kgame Date: Tue, 28 Jul 2026 13:27:57 +0800 Subject: [PATCH] Fix no-reflection Console.Title crash (#213) --- src/bflat.Tests/BflatCompilationResult.cs | 5 +++-- src/bflat.Tests/UnitTest1.cs | 24 ++++++++++++++++++++++- src/bflat/BuildCommand.cs | 2 +- src/bflat/SpanMetadataBlockingPolicy.cs | 17 ++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/bflat/SpanMetadataBlockingPolicy.cs diff --git a/src/bflat.Tests/BflatCompilationResult.cs b/src/bflat.Tests/BflatCompilationResult.cs index 80c0663..359d2d7 100644 --- a/src/bflat.Tests/BflatCompilationResult.cs +++ b/src/bflat.Tests/BflatCompilationResult.cs @@ -22,13 +22,14 @@ public void Run(string expectedOutput = null) } string stdOut = p.StandardOutput.ReadToEnd(); + string stdErr = p.StandardError.ReadToEnd(); + Assert.True(p.ExitCode == 0, $"Non-zero exit code: {p.ExitCode}\nStdout:\n{stdOut}\nStderr:\n{stdErr}"); + if (expectedOutput != null) { Assert.Equal(expectedOutput, stdOut); } - - Assert.Equal(0, p.ExitCode); } } diff --git a/src/bflat.Tests/UnitTest1.cs b/src/bflat.Tests/UnitTest1.cs index 0abbe27..1c87d58 100644 --- a/src/bflat.Tests/UnitTest1.cs +++ b/src/bflat.Tests/UnitTest1.cs @@ -10,4 +10,26 @@ public void Test1() { new BflatCompilation().Build("System.Console.WriteLine(\"Hello\");").Run("Hello" + Environment.NewLine); } -} \ No newline at end of file + + [Fact] + public void ConsoleTitleAndEnvironmentExitWorkWithReflectionDisabled() + { + new BflatCompilation().Build(""" + using System; + + public static class Program + { + public static void Main() => FailFast("Done", 0); + + private static void FailFast(string msg, int code) + { + Console.WriteLine(msg); + _ = Console.Title; + Environment.Exit(code); + } + } + """, "--no-reflection --no-stacktrace-data --no-globalization --no-exception-messages --optimize-space --no-debug-info") + .Run("Done" + Environment.NewLine); + } + +} diff --git a/src/bflat/BuildCommand.cs b/src/bflat/BuildCommand.cs index 0ff3ca9..e5ab20a 100644 --- a/src/bflat/BuildCommand.cs +++ b/src/bflat/BuildCommand.cs @@ -586,7 +586,7 @@ public override int Handle(ParseResult result) } else { - mdBlockingPolicy = new FullyBlockedMetadataBlockingPolicy(); + mdBlockingPolicy = new SpanMetadataBlockingPolicy(); resBlockingPolicy = new FullyBlockedManifestResourceBlockingPolicy(); } DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); diff --git a/src/bflat/SpanMetadataBlockingPolicy.cs b/src/bflat/SpanMetadataBlockingPolicy.cs new file mode 100644 index 0000000..2c481d6 --- /dev/null +++ b/src/bflat/SpanMetadataBlockingPolicy.cs @@ -0,0 +1,17 @@ +using ILCompiler; +using Internal.TypeSystem; + +// .NET 10 Console.Title formats its result through Span.ToString(). +// Keep only the metadata needed for that virtual call when reflection is disabled. +internal sealed class SpanMetadataBlockingPolicy : FullyBlockedMetadataBlockingPolicy +{ + public override bool IsBlocked(MetadataType type) => !IsSpanType(type) && base.IsBlocked(type); + + public override bool IsBlocked(MethodDesc method) => !(IsSpanType(method.OwningType) && method.Name == "ToString") && base.IsBlocked(method); + + public override bool IsBlocked(FieldDesc field) => base.IsBlocked(field); + + private static bool IsSpanType(TypeDesc type) => type is MetadataType metadataType + && metadataType.Namespace == "System" + && metadataType.Name == "Span`1"; +}