1515
1616namespace DotNetLab ;
1717
18- public class Compiler (
18+ public sealed class Compiler (
1919 ILogger < DecompilerAssemblyResolver > decompilerAssemblyResolverLogger ) : ICompiler
2020{
2121 private const string ToolchainHelpText = """
2222
2323 You can try selecting different Razor toolchain in Settings / Advanced.
2424 """ ;
2525
26- private ( CompilationInput Input , CompiledAssembly Output ) ? lastResult ;
26+ public static readonly string ConfigurationGlobalUsings = """
27+ global using DotNetLab;
28+ global using Microsoft.CodeAnalysis;
29+ global using Microsoft.CodeAnalysis.CSharp;
30+ global using System;
31+ """ ;
2732
2833 /// <summary>
2934 /// Reused for incremental source generation.
3035 /// </summary>
3136 private GeneratorDriver ? generatorDriver ;
3237
38+ internal ( CompilationInput Input , LiveCompilationResult Output ) ? LastResult { get ; private set ; }
39+
3340 public CompiledAssembly Compile (
3441 CompilationInput input ,
3542 ImmutableDictionary < string , ImmutableArray < byte > > ? assemblies ,
3643 ImmutableDictionary < string , ImmutableArray < byte > > ? builtInAssemblies ,
3744 AssemblyLoadContext alc )
3845 {
39- if ( lastResult is { } cached )
46+ if ( LastResult is { } cached )
4047 {
4148 if ( input . Equals ( cached . Input ) )
4249 {
43- return cached . Output ;
50+ return cached . Output . CompiledAssembly ;
4451 }
4552 }
4653
4754 var result = CompileNoCache ( input , assemblies , builtInAssemblies , alc ) ;
48- lastResult = ( input , result ) ;
49- return result ;
55+ LastResult = ( input , result ) ;
56+ return result . CompiledAssembly ;
5057 }
5158
52- private CompiledAssembly CompileNoCache (
59+ private LiveCompilationResult CompileNoCache (
5360 CompilationInput compilationInput ,
5461 ImmutableDictionary < string , ImmutableArray < byte > > ? assemblies ,
5562 ImmutableDictionary < string , ImmutableArray < byte > > ? builtInAssemblies ,
@@ -59,13 +66,15 @@ private CompiledAssembly CompileNoCache(
5966 const string directory = "/" ;
6067
6168 var parseOptions = CreateDefaultParseOptions ( ) ;
69+ CSharpCompilationOptions ? options = null ;
6270
6371 var references = RefAssemblyMetadata . All ;
6472 var referenceInfos = RefAssemblies . All ;
6573
6674 // If we have a configuration, compile and execute it.
6775 Config . Reset ( ) ;
6876 ImmutableArray < Diagnostic > configDiagnostics ;
77+ ImmutableDictionary < string , ImmutableArray < byte > > ? compilerAssembliesUsed = null ;
6978 if ( compilationInput . Configuration is { } configuration )
7079 {
7180 if ( ! executeConfiguration ( configuration , out configDiagnostics ) )
@@ -74,7 +83,7 @@ private CompiledAssembly CompileNoCache(
7483 ImmutableArray < DiagnosticData > configDiagnosticData = configDiagnostics
7584 . Select ( d => d . ToDiagnosticData ( ) )
7685 . ToImmutableArray ( ) ;
77- return new CompiledAssembly (
86+ var configResult = new CompiledAssembly (
7887 Files : ImmutableSortedDictionary < string , CompiledFile > . Empty ,
7988 GlobalOutputs :
8089 [
@@ -93,6 +102,7 @@ private CompiledAssembly CompileNoCache(
93102 {
94103 ConfigDiagnosticCount = configDiagnosticData . Length ,
95104 } ;
105+ return getResult ( configResult ) ;
96106 }
97107
98108 parseOptions = Config . ConfigureCSharpParseOptions ( parseOptions ) ;
@@ -140,7 +150,7 @@ private CompiledAssembly CompileNoCache(
140150
141151 var outputKind = GetDefaultOutputKind ( cSharpSources . Select ( s => s . SyntaxTree ) ) ;
142152
143- var options = CreateDefaultCompilationOptions ( outputKind ) ;
153+ options = CreateDefaultCompilationOptions ( outputKind ) ;
144154
145155 options = Config . ConfigureCSharpCompilationOptions ( options ) ;
146156
@@ -335,7 +345,18 @@ .. string.IsNullOrEmpty(razorDiagnostics)
335345 ConfigDiagnosticCount = configDiagnostics . Count ( filterDiagnostic ) ,
336346 } ;
337347
338- return result ;
348+ return getResult ( result ) ;
349+
350+ LiveCompilationResult getResult ( CompiledAssembly result )
351+ {
352+ return new LiveCompilationResult
353+ {
354+ CompiledAssembly = result ,
355+ CompilerAssemblies = compilerAssembliesUsed ,
356+ CSharpParseOptions = compilerAssembliesUsed != null ? parseOptions : null ,
357+ CSharpCompilationOptions = compilerAssembliesUsed != null ? options : null ,
358+ } ;
359+ }
339360
340361 static bool filterDiagnostic ( Diagnostic d ) => d . Severity != DiagnosticSeverity . Hidden ;
341362
@@ -346,28 +367,22 @@ bool executeConfiguration(string code, out ImmutableArray<Diagnostic> diagnostic
346367 syntaxTrees :
347368 [
348369 CSharpSyntaxTree . ParseText ( code , parseOptions , "Configuration.cs" , Encoding . UTF8 ) ,
349- CSharpSyntaxTree . ParseText ( """
350- global using DotNetLab;
351- global using Microsoft.CodeAnalysis;
352- global using Microsoft.CodeAnalysis.CSharp;
353- global using System;
354- """ , parseOptions , "GlobalUsings.cs" , Encoding . UTF8 )
370+ CSharpSyntaxTree . ParseText ( ConfigurationGlobalUsings , parseOptions , "GlobalUsings.cs" , Encoding . UTF8 )
355371 ] ,
356372 references :
357373 [
358374 ..references ,
359375 ..assemblies ! . Values . Select ( b => MetadataReference . CreateFromImage ( b ) ) ,
360376 ] ,
361- options : CreateDefaultCompilationOptions ( OutputKind . ConsoleApplication )
362- . WithSpecificDiagnosticOptions (
363- [
364- // warning CS1701: Assuming assembly reference 'System.Runtime, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Microsoft.CodeAnalysis.CSharp' matches identity 'System.Runtime, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' of 'System.Runtime', you may need to supply runtime policy
365- KeyValuePair . Create ( "CS1701" , ReportDiagnostic . Suppress ) ,
366- ] ) ) ;
377+ options : CreateConfigurationCompilationOptions ( ) ) ;
367378
368379 var emitStream = getEmitStream ( configCompilation , out diagnostics ) ;
369380
370- if ( emitStream == null )
381+ if ( emitStream != null )
382+ {
383+ compilerAssembliesUsed = assemblies ;
384+ }
385+ else
371386 {
372387 // If compilation fails, it might be because older Roslyn is referenced, re-try with built-in versions.
373388 var configCompilationWithBuiltInReferences = configCompilation . WithReferences (
@@ -379,6 +394,7 @@ bool executeConfiguration(string code, out ImmutableArray<Diagnostic> diagnostic
379394 if ( emitStream != null )
380395 {
381396 diagnostics = diagnosticsWithBuiltInReferences ;
397+ compilerAssembliesUsed = builtInAssemblies ;
382398 }
383399 }
384400
@@ -791,6 +807,16 @@ public static CSharpCompilationOptions CreateDefaultCompilationOptions(OutputKin
791807 nullableContextOptions : NullableContextOptions . Enable ,
792808 concurrentBuild : false ) ;
793809 }
810+
811+ public static CSharpCompilationOptions CreateConfigurationCompilationOptions ( )
812+ {
813+ return CreateDefaultCompilationOptions ( OutputKind . ConsoleApplication )
814+ . WithSpecificDiagnosticOptions (
815+ [
816+ // warning CS1701: Assuming assembly reference 'System.Runtime, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Microsoft.CodeAnalysis.CSharp' matches identity 'System.Runtime, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' of 'System.Runtime', you may need to supply runtime policy
817+ KeyValuePair . Create ( "CS1701" , ReportDiagnostic . Suppress ) ,
818+ ] ) ;
819+ }
794820}
795821
796822public sealed class DecompilerAssemblyResolver ( ILogger < DecompilerAssemblyResolver > logger , ImmutableArray < RefAssembly > references ) : ICSharpCode . Decompiler . Metadata . IAssemblyResolver
@@ -954,3 +980,26 @@ public Result<R> Map<R>(Func<T, R> mapper)
954980 : new Result < R > ( ( ) => mapper ( value ! ) ) ;
955981 }
956982}
983+
984+ /// <summary>
985+ /// Additional data on top of <see cref="CompiledAssembly"/> that are never cached.
986+ /// </summary>
987+ internal sealed class LiveCompilationResult
988+ {
989+ public required CompiledAssembly CompiledAssembly { get ; init ; }
990+
991+ /// <summary>
992+ /// Assemblies used to compile <see cref="CompilationInput.Configuration"/>.
993+ /// </summary>
994+ public required ImmutableDictionary < string , ImmutableArray < byte > > ? CompilerAssemblies { get ; init ; }
995+
996+ /// <summary>
997+ /// Set to <see langword="null"/> if the default options were used.
998+ /// </summary>
999+ public required CSharpParseOptions ? CSharpParseOptions { get ; init ; }
1000+
1001+ /// <summary>
1002+ /// Set to <see langword="null"/> if the default options were used.
1003+ /// </summary>
1004+ public required CSharpCompilationOptions ? CSharpCompilationOptions { get ; init ; }
1005+ }
0 commit comments