Skip to content

Commit c85d7e5

Browse files
committed
C#: Add functionality to detect overlay mode and integrate in extraction context.
1 parent ccefd88 commit c85d7e5

File tree

7 files changed

+153
-7
lines changed

7 files changed

+153
-7
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,20 @@ public class Analyser : IDisposable
4141

4242
public IPathCache PathCache { get; }
4343

44+
public IOverlayInfo OverlayInfo { get; }
45+
4446
protected Analyser(
4547
IProgressMonitor pm,
4648
ILogger logger,
4749
PathTransformer pathTransformer,
4850
IPathCache pathCache,
51+
IOverlayInfo overlayInfo,
4952
bool addAssemblyTrapPrefix)
5053
{
5154
Logger = logger;
5255
PathTransformer = pathTransformer;
5356
PathCache = pathCache;
57+
OverlayInfo = overlayInfo;
5458
this.addAssemblyTrapPrefix = addAssemblyTrapPrefix;
5559
this.progressMonitor = pm;
5660

@@ -158,7 +162,7 @@ private void DoAnalyseReferenceAssembly(PortableExecutableReference r)
158162

159163
if (compilation.GetAssemblyOrModuleSymbol(r) is IAssemblySymbol assembly)
160164
{
161-
var cx = new Context(ExtractionContext, compilation, trapWriter, new AssemblyScope(assembly, assemblyPath), addAssemblyTrapPrefix);
165+
var cx = new Context(ExtractionContext, compilation, trapWriter, new AssemblyScope(assembly, assemblyPath), OverlayInfo, addAssemblyTrapPrefix);
162166

163167
foreach (var module in assembly.Modules)
164168
{
@@ -195,7 +199,7 @@ private void DoExtractTree(SyntaxTree tree)
195199
var currentTaskId = IncrementTaskCount();
196200
ReportProgressTaskStarted(currentTaskId, sourcePath);
197201

198-
var cx = new Context(ExtractionContext, compilation, trapWriter, new SourceScope(tree), addAssemblyTrapPrefix);
202+
var cx = new Context(ExtractionContext, compilation, trapWriter, new SourceScope(tree), OverlayInfo, addAssemblyTrapPrefix);
199203
// Ensure that the file itself is populated in case the source file is totally empty
200204
var root = tree.GetRoot();
201205
Entities.File.Create(cx, root.SyntaxTree.FilePath);
@@ -234,7 +238,7 @@ private void DoAnalyseCompilation()
234238
var assembly = compilation.Assembly;
235239
var trapWriter = transformedAssemblyPath.CreateTrapWriter(Logger, options.TrapCompression, discardDuplicates: false);
236240
compilationTrapFile = trapWriter; // Dispose later
237-
var cx = new Context(ExtractionContext, compilation, trapWriter, new AssemblyScope(assembly, assemblyPath), addAssemblyTrapPrefix);
241+
var cx = new Context(ExtractionContext, compilation, trapWriter, new AssemblyScope(assembly, assemblyPath), OverlayInfo, addAssemblyTrapPrefix);
238242

239243
compilationEntity = Entities.Compilation.Create(cx);
240244

csharp/extractor/Semmle.Extraction.CSharp/Extractor/BinaryLogAnalyser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp
88
public class BinaryLogAnalyser : Analyser
99
{
1010
public BinaryLogAnalyser(IProgressMonitor pm, ILogger logger, PathTransformer pathTransformer, IPathCache pathCache, bool addAssemblyTrapPrefix)
11-
: base(pm, logger, pathTransformer, pathCache, addAssemblyTrapPrefix)
11+
: base(pm, logger, pathTransformer, pathCache, new TrivialOverlayInfo(), addAssemblyTrapPrefix)
1212
{
1313
}
1414

csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ public class Context
2929
/// </summary>
3030
public bool ShouldAddAssemblyTrapPrefix { get; }
3131

32+
/// <summary>
33+
/// Holds if trap only should be created for types and member signatures (and not for expressions and statements).
34+
/// This is the case for all unchanged files, when running in overlay mode.
35+
/// </summary>
36+
public bool OnlyScaffold { get; }
37+
3238
public IList<object> TrapStackSuffix { get; } = new List<object>();
3339

3440
private int GetNewId() => TrapWriter.IdCounter++;
@@ -523,13 +529,14 @@ internal void CacheLambdaParameterSymbol(IParameterSymbol param, SyntaxNode synt
523529

524530
internal CommentProcessor CommentGenerator { get; } = new CommentProcessor();
525531

526-
public Context(ExtractionContext extractionContext, Compilation c, TrapWriter trapWriter, IExtractionScope scope, bool shouldAddAssemblyTrapPrefix = false)
532+
public Context(ExtractionContext extractionContext, Compilation c, TrapWriter trapWriter, IExtractionScope scope, IOverlayInfo overlayInfo, bool shouldAddAssemblyTrapPrefix = false)
527533
{
528534
ExtractionContext = extractionContext;
529535
TrapWriter = trapWriter;
530536
ShouldAddAssemblyTrapPrefix = shouldAddAssemblyTrapPrefix;
531537
Compilation = c;
532538
this.scope = scope;
539+
OnlyScaffold = overlayInfo.IsOverlayMode && scope is SourceScope ss && overlayInfo.OnlyMakeScaffold(ss.SourceTree.FilePath);
533540
}
534541

535542
public bool FromSource => scope is SourceScope;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text.Json;
5+
using Semmle.Util;
6+
using Semmle.Util.Logging;
7+
8+
namespace Semmle.Extraction.CSharp
9+
{
10+
public interface IOverlayInfo
11+
{
12+
/// <summary>
13+
/// True, if the extractor is running in overlay mode.
14+
/// </summary>
15+
bool IsOverlayMode { get; }
16+
17+
/// <summary>
18+
/// Returns true, if the given file is not in the set of changed files.
19+
/// </summary>
20+
/// <param name="filePath">A source file path</param>
21+
bool OnlyMakeScaffold(string filePath);
22+
}
23+
24+
25+
/// <summary>
26+
/// An instance of this class is used when overlay is not enabled.
27+
/// </summary>
28+
public class TrivialOverlayInfo : IOverlayInfo
29+
{
30+
public TrivialOverlayInfo() { }
31+
32+
public bool IsOverlayMode { get; } = false;
33+
34+
public bool OnlyMakeScaffold(string filePath) => false;
35+
}
36+
37+
/// <summary>
38+
/// An instance of this class is used for detecting
39+
/// (1) Whether overlay is enabled.
40+
/// (2) Fetch the changed files that should be fully extracted as a part
41+
/// of the overlay extraction.
42+
/// </summary>
43+
public class OverlayInfo : IOverlayInfo
44+
{
45+
private readonly ILogger logger;
46+
private readonly HashSet<string> changedFiles;
47+
48+
public OverlayInfo(ILogger logger, string json)
49+
{
50+
this.logger = logger;
51+
changedFiles = ParseJson(json);
52+
}
53+
54+
public bool IsOverlayMode { get; } = true;
55+
56+
public bool OnlyMakeScaffold(string filePath) => !changedFiles.Contains(filePath);
57+
58+
/// <summary>
59+
/// Private type only used to parse overlay changes JSON files.
60+
///
61+
/// The content of such a file has the format
62+
/// {
63+
/// "changes": [
64+
/// "app/controllers/about_controller.xyz",
65+
/// "app/models/about.xyz"
66+
/// ]
67+
/// }
68+
/// </summary>
69+
public record ChangedFiles
70+
{
71+
public string[]? Changes { get; set; }
72+
}
73+
74+
private HashSet<string> ParseJson(string json)
75+
{
76+
try
77+
{
78+
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
79+
var changedFiles = JsonSerializer.Deserialize<ChangedFiles>(json, options);
80+
return changedFiles?.Changes is string[] changes
81+
? new HashSet<string>(changes)
82+
: [];
83+
}
84+
catch (JsonException)
85+
{
86+
logger.LogError("Overlay: Unable to parse the JSON content from the overlay changes file");
87+
return [];
88+
}
89+
}
90+
}
91+
92+
public static class OverlayInfoFactory
93+
{
94+
/// <summary>
95+
/// The returned object is used to decide, whether
96+
/// (1) The extractor is running in overlay mode.
97+
/// (2) Which files to only extract scaffolds for (unchanged files)
98+
/// </summary>
99+
/// <param name="mode">The extraction mode</param>
100+
/// <param name="logger">A logger</param>
101+
/// <returns>An overlay information object.</returns>
102+
public static IOverlayInfo Make(ILogger logger)
103+
{
104+
if (EnvironmentVariables.GetOverlayChangesFilePath() is string path)
105+
{
106+
try
107+
{
108+
var json = File.ReadAllText(path);
109+
return new OverlayInfo(logger, json);
110+
}
111+
catch
112+
{
113+
logger.LogError("Overlay: Unexpected error while reading the overlay changes file.");
114+
}
115+
116+
}
117+
118+
return new TrivialOverlayInfo();
119+
}
120+
}
121+
}

csharp/extractor/Semmle.Extraction.CSharp/Extractor/StandaloneAnalyser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Semmle.Extraction.CSharp
99
public class StandaloneAnalyser : Analyser
1010
{
1111
public StandaloneAnalyser(IProgressMonitor pm, ILogger logger, PathTransformer pathTransformer, IPathCache pathCache, bool addAssemblyTrapPrefix)
12-
: base(pm, logger, pathTransformer, pathCache, addAssemblyTrapPrefix)
12+
: base(pm, logger, pathTransformer, pathCache, OverlayInfoFactory.Make(logger), addAssemblyTrapPrefix)
1313
{
1414
}
1515

csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class TracingAnalyser : Analyser
1414
private bool init;
1515

1616
public TracingAnalyser(IProgressMonitor pm, ILogger logger, PathTransformer pathTransformer, IPathCache pathCache, bool addAssemblyTrapPrefix)
17-
: base(pm, logger, pathTransformer, pathCache, addAssemblyTrapPrefix)
17+
: base(pm, logger, pathTransformer, pathCache, new TrivialOverlayInfo(), addAssemblyTrapPrefix)
1818
{
1919
}
2020

csharp/extractor/Semmle.Util/EnvironmentVariables.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,19 @@ public static IEnumerable<string> GetURLs(string name)
5353
{
5454
return Environment.GetEnvironmentVariable(name)?.Split(" ", StringSplitOptions.RemoveEmptyEntries) ?? [];
5555
}
56+
57+
/// <summary>
58+
/// Used to
59+
/// (1) Detect whether the extractor should run in overlay mode.
60+
/// (2) Returns the path to the file containing a list of changed files
61+
/// in JSON format.
62+
///
63+
/// The environment variable is only set in case the extraction is supposed to be
64+
/// performed in overlay mode. Furthermore, this only applies to buildless extraction.
65+
/// </summary>
66+
public static string? GetOverlayChangesFilePath()
67+
{
68+
return Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OVERLAY_CHANGES");
69+
}
5670
}
5771
}

0 commit comments

Comments
 (0)