Skip to content

Commit e021fb4

Browse files
committed
C#: Roslyn based stub generation
1 parent 6021d00 commit e021fb4

File tree

8 files changed

+1033
-0
lines changed

8 files changed

+1033
-0
lines changed

csharp/CSharp.sln

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.De
1414
EndProject
1515
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Standalone", "extractor\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj", "{D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}"
1616
EndProject
17+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.StubGenerator", "extractor\Semmle.Extraction.CSharp.StubGenerator\Semmle.Extraction.CSharp.StubGenerator.csproj", "{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}"
18+
EndProject
1719
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Util", "extractor\Semmle.Extraction.CSharp.Util\Semmle.Extraction.CSharp.Util.csproj", "{998A0D4C-8BFC-4513-A28D-4816AFB89882}"
1820
EndProject
1921
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CIL.Driver", "extractor\Semmle.Extraction.CIL.Driver\Semmle.Extraction.CIL.Driver.csproj", "{EFA400B3-C1CE-446F-A4E2-8B44E61EF47C}"
@@ -30,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp", "
3032
EndProject
3133
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp.Tests", "autobuilder\Semmle.Autobuild.CSharp.Tests\Semmle.Autobuild.CSharp.Tests.csproj", "{34256E8F-866A-46C1-800E-3DF69FD1DCB7}"
3234
EndProject
35+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Extraction.CSharp.DependencyStubGenerator", "extractor\Semmle.Extraction.CSharp.DependencyStubGenerator\Semmle.Extraction.CSharp.DependencyStubGenerator.csproj", "{0EDA21A3-ADD8-4C10-B494-58B12B526B76}"
36+
EndProject
3337
Global
3438
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3539
Debug|Any CPU = Debug|Any CPU
@@ -90,6 +94,14 @@ Global
9094
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
9195
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
9296
{B7C9FD47-A78C-4C20-AC29-B0AE638ADE9D}.Release|Any CPU.Build.0 = Release|Any CPU
97+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
98+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Debug|Any CPU.Build.0 = Debug|Any CPU
99+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Release|Any CPU.ActiveCfg = Release|Any CPU
100+
{998A0D4C-8BFC-4513-A28D-4816AFB89882}.Release|Any CPU.Build.0 = Release|Any CPU
101+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
102+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Debug|Any CPU.Build.0 = Debug|Any CPU
103+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Release|Any CPU.ActiveCfg = Release|Any CPU
104+
{0EDA21A3-ADD8-4C10-B494-58B12B526B76}.Release|Any CPU.Build.0 = Release|Any CPU
93105
EndGlobalSection
94106
GlobalSection(SolutionProperties) = preSolution
95107
HideSolutionNode = FALSE
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Semmle.Extraction.CSharp.DependencyFetching;
2+
using Semmle.Extraction.CSharp.StubGenerator;
3+
using Semmle.Util.Logging;
4+
5+
var logger = new ConsoleLogger(Verbosity.Info);
6+
using var dependencyManager = new DependencyManager(".", DependencyOptions.Default, logger);
7+
StubGenerator.GenerateStubs(logger, dependencyManager.ReferenceFiles, "codeql_csharp_stubs");
8+
9+
return 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net7.0</TargetFramework>
6+
<AssemblyName>Semmle.Extraction.CSharp.DependencyStubGenerator</AssemblyName>
7+
<RootNamespace>Semmle.Extraction.CSharp.DependencyStubGenerator</RootNamespace>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<Nullable>enable</Nullable>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
14+
<ProjectReference Include="..\Semmle.Extraction.CSharp.DependencyFetching\Semmle.Extraction.CSharp.DependencyFetching.csproj" />
15+
<ProjectReference Include="..\Semmle.Extraction.CSharp.StubGenerator\Semmle.Extraction.CSharp.StubGenerator.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net7.0</TargetFramework>
4+
<AssemblyName>Semmle.Extraction.CSharp.StubGenerator</AssemblyName>
5+
<RootNamespace>Semmle.Extraction.CSharp.StubGenerator</RootNamespace>
6+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
7+
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
12+
<ProjectReference Include="..\Semmle.Extraction.CSharp.DependencyFetching\Semmle.Extraction.CSharp.DependencyFetching.csproj" />
13+
<ProjectReference Include="..\Semmle.Extraction.CSharp.Util\Semmle.Extraction.CSharp.Util.csproj" />
14+
</ItemGroup>
15+
<ItemGroup>
16+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
17+
</ItemGroup>
18+
</Project>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Semmle.Util;
11+
using Semmle.Util.Logging;
12+
13+
namespace Semmle.Extraction.CSharp.StubGenerator;
14+
15+
public static class StubGenerator
16+
{
17+
/// <summary>
18+
/// Generates stubs for all the provided assembly paths.
19+
/// </summary>
20+
/// <param name="referencesPaths">The paths of the assemblies to generate stubs for.</param>
21+
/// <param name="outputPath">The path in which to store the stubs.</param>
22+
public static void GenerateStubs(ILogger logger, IEnumerable<string> referencesPaths, string outputPath)
23+
{
24+
var stopWatch = new System.Diagnostics.Stopwatch();
25+
stopWatch.Start();
26+
27+
var threads = EnvironmentVariables.GetDefaultNumberOfThreads();
28+
29+
using var references = new BlockingCollection<(MetadataReference Reference, string Path)>();
30+
var referenceResolveTasks = GetResolvedReferenceTasks(referencesPaths, references);
31+
32+
Parallel.Invoke(
33+
new ParallelOptions { MaxDegreeOfParallelism = threads },
34+
referenceResolveTasks.ToArray());
35+
36+
logger.Log(Severity.Info, $"Generating stubs for {references.Count} assemblies.");
37+
38+
var compilation = CSharpCompilation.Create(
39+
"stubgenerator.dll",
40+
null,
41+
references.Select(tuple => tuple.Item1),
42+
new CSharpCompilationOptions(OutputKind.ConsoleApplication, allowUnsafe: true));
43+
44+
var referenceStubTasks = references.Select(@ref => (Action)(() => StubReference(compilation, outputPath, @ref.Reference, @ref.Path)));
45+
Parallel.Invoke(
46+
new ParallelOptions { MaxDegreeOfParallelism = threads },
47+
referenceStubTasks.ToArray());
48+
49+
stopWatch.Stop();
50+
logger.Log(Severity.Info, $"Stub generation took {stopWatch.Elapsed}.");
51+
}
52+
53+
private static IEnumerable<Action> GetResolvedReferenceTasks(IEnumerable<string> referencePaths, BlockingCollection<(MetadataReference, string)> references)
54+
{
55+
return referencePaths.Select<string, Action>(path => () =>
56+
{
57+
var reference = MetadataReference.CreateFromFile(path);
58+
references.Add((reference, path));
59+
});
60+
}
61+
62+
private static void StubReference(CSharpCompilation compilation, string outputPath, MetadataReference reference, string path)
63+
{
64+
if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
65+
{
66+
var logger = new ConsoleLogger(Verbosity.Info);
67+
using var fileStream = new FileStream(FileUtils.NestPaths(logger, outputPath, path.Replace(".dll", ".cs")), FileMode.Create, FileAccess.Write);
68+
using var writer = new StreamWriter(fileStream, new UTF8Encoding(false));
69+
70+
writer.WriteLine("// This file contains auto-generated code.");
71+
writer.WriteLine($"// Generated from `{assembly.Identity}`.");
72+
73+
var visitor = new StubVisitor(assembly, writer);
74+
75+
visitor.StubAttributes(assembly.GetAttributes(), "assembly: ");
76+
77+
foreach (var module in assembly.Modules)
78+
{
79+
module.GlobalNamespace.Accept(new StubVisitor(assembly, writer));
80+
}
81+
}
82+
}
83+
}
84+

0 commit comments

Comments
 (0)