Skip to content

Commit 21233a6

Browse files
committed
Hid most internal details of the generated code from users.
1 parent ec24151 commit 21233a6

File tree

8 files changed

+110
-81
lines changed

8 files changed

+110
-81
lines changed

src/AST/Module.cs

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,16 @@ namespace CppSharp.AST
44
{
55
public class Module
66
{
7-
public Module()
8-
{
9-
IncludeDirs = new List<string>();
10-
Headers = new List<string>();
11-
LibraryDirs = new List<string>();
12-
Libraries = new List<string>();
13-
Defines = new List<string>();
14-
Undefines = new List<string>();
15-
Units = new List<TranslationUnit>();
16-
CodeFiles = new List<string>();
17-
}
18-
19-
public List<string> IncludeDirs { get; private set; }
20-
public List<string> Headers { get; private set; }
21-
public List<string> LibraryDirs { get; set; }
22-
public List<string> Libraries { get; private set; }
23-
public List<string> Defines { get; set; }
24-
public List<string> Undefines { get; set; }
7+
public List<string> IncludeDirs { get; } = new List<string>();
8+
public List<string> Headers { get; } = new List<string>();
9+
public List<string> LibraryDirs { get; } = new List<string>();
10+
public List<string> Libraries { get; } = new List<string>();
11+
public List<string> Defines { get; } = new List<string>();
12+
public List<string> Undefines { get; } = new List<string>();
2513
public string OutputNamespace { get; set; }
26-
27-
public List<TranslationUnit> Units { get; private set; }
28-
public List<string> CodeFiles { get; private set; }
14+
public List<TranslationUnit> Units { get; } = new List<TranslationUnit>();
15+
public List<string> CodeFiles { get; } = new List<string>();
16+
public List<Module> Dependencies { get; } = new List<Module>();
2917

3018
public string SharedLibraryName
3119
{
@@ -74,6 +62,8 @@ public string TemplatesLibraryName
7462

7563
public string LibraryName { get; set; }
7664

65+
public override string ToString() => LibraryName;
66+
7767
private string sharedLibraryName;
7868
private string inlinesLibraryName;
7969
private string templatesLibraryName;

src/Generator/Driver.cs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,23 @@ public void BuildParseOptions()
223223

224224
public void SortModulesByDependencies()
225225
{
226-
if (Options.Modules.All(m => m.Libraries.Any() || m == Options.SystemModule))
226+
if (!Options.DoAllModulesHaveLibraries())
227+
return;
228+
229+
var sortedModules = Options.Modules.TopologicalSort(m =>
227230
{
228-
var sortedModules = Options.Modules.TopologicalSort(m =>
229-
{
230-
return from library in Context.Symbols.Libraries
231-
where m.Libraries.Contains(library.FileName)
232-
from module in Options.Modules
233-
where library.Dependencies.Intersect(module.Libraries).Any()
234-
select module;
235-
});
236-
Options.Modules.Clear();
237-
Options.Modules.AddRange(sortedModules);
238-
}
231+
var dependencies = (from library in Context.Symbols.Libraries
232+
where m.Libraries.Contains(library.FileName)
233+
from module in Options.Modules
234+
where library.Dependencies.Intersect(module.Libraries).Any()
235+
select module).ToList();
236+
if (m != Options.SystemModule)
237+
m.Dependencies.Add(Options.SystemModule);
238+
m.Dependencies.AddRange(dependencies);
239+
return dependencies;
240+
});
241+
Options.Modules.Clear();
242+
Options.Modules.AddRange(sortedModules);
239243
}
240244

241245
public bool ParseLibraries()
@@ -384,9 +388,7 @@ public void SaveCode(IEnumerable<GeneratorOutput> outputs)
384388

385389
public void CompileCode(AST.Module module)
386390
{
387-
var assemblyFile = Path.Combine(Options.OutputDir,
388-
string.IsNullOrEmpty(module.LibraryName) ?
389-
"out.dll" : module.LibraryName + ".dll");
391+
var assemblyFile = Path.Combine(Options.OutputDir, module.LibraryName + ".dll");
390392

391393
var docFile = Path.ChangeExtension(assemblyFile, ".xml");
392394

@@ -421,7 +423,7 @@ public void CompileCode(AST.Module module)
421423
!compilerParameters.ReferencedAssemblies.Contains(libraryMappings[d]))
422424
.Select(l => libraryMappings[l])).ToArray());
423425

424-
Diagnostics.Message("Compiling {0}...", module.LibraryName);
426+
Diagnostics.Message($"Compiling {module.LibraryName}...");
425427
CompilerResults compilerResults;
426428
using (var codeProvider = new CSharpCodeProvider(
427429
new Dictionary<string, string> {
@@ -440,21 +442,33 @@ public void CompileCode(AST.Module module)
440442
HasCompilationErrors = errors.Count > 0;
441443
if (!HasCompilationErrors)
442444
{
443-
var injector = new Injector();
444-
injector.ReadAssembly(assemblyFile);
445-
var moduleInitializer = injector.GetModuleInitializer();
446-
if (moduleInitializer != null)
447-
{
448-
injector.InjectInitializer(moduleInitializer);
449-
injector.WriteAssembly(assemblyFile, null);
450-
}
445+
InjectModuleInitializer(assemblyFile);
451446
Diagnostics.Message("Compilation succeeded.");
452447
var wrapper = Path.Combine(outputDir, assemblyFile);
453448
foreach (var library in module.Libraries)
454449
libraryMappings[library] = wrapper;
455450
}
456451
}
457452

453+
/// <summary>
454+
/// Injects a module initializer, if any, i.e. code executed right after an
455+
/// assembly is loaded and before any other code in it.
456+
/// <para>The run-time supports it but C# does not so we inject it in the
457+
/// compiled assembly by manually adding IL instructions.</para>
458+
/// </summary>
459+
/// <param name="assemblyFile">The assembly to inject a module initializer to.</param>
460+
private static void InjectModuleInitializer(string assemblyFile)
461+
{
462+
var injector = new Injector();
463+
injector.ReadAssembly(assemblyFile);
464+
var moduleInitializer = injector.GetModuleInitializer();
465+
if (moduleInitializer != null)
466+
{
467+
injector.InjectInitializer(moduleInitializer);
468+
injector.WriteAssembly(assemblyFile, null);
469+
}
470+
}
471+
458472
public void AddTranslationUnitPass(TranslationUnitPass pass)
459473
{
460474
Context.TranslationUnitPasses.AddPass(pass);

src/Generator/Generators/CSharp/CSharpSources.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ public override void Process()
157157
WriteLine("using System;");
158158
WriteLine("using System.Runtime.InteropServices;");
159159
WriteLine("using System.Security;");
160+
if (Context.Options.DoAllModulesHaveLibraries())
161+
WriteLine("using System.Runtime.CompilerServices;");
160162
foreach (var customUsingStatement in Options.DependentNameSpaces)
161163
{
162164
WriteLine(string.Format("using {0};", customUsingStatement));
@@ -165,6 +167,21 @@ public override void Process()
165167

166168
var module = TranslationUnits.Count == 0 ?
167169
Context.Options.SystemModule : TranslationUnit.Module;
170+
var hasInternalsVisibleTo = false;
171+
if (Context.Options.DoAllModulesHaveLibraries())
172+
{
173+
foreach (var library in from m in Options.Modules
174+
where m.Dependencies.Contains(module)
175+
select m.LibraryName)
176+
{
177+
WriteLine($"[assembly:InternalsVisibleTo(\"{library}\")]");
178+
if (!hasInternalsVisibleTo)
179+
hasInternalsVisibleTo = true;
180+
}
181+
}
182+
if (hasInternalsVisibleTo)
183+
NewLine();
184+
168185
if (!string.IsNullOrEmpty(module.OutputNamespace))
169186
{
170187
PushBlock(CSharpBlockKind.Namespace);
@@ -471,7 +488,7 @@ public void GenerateClass(Class @class)
471488
{
472489
WriteLine("private {0}.{1} {2};", @class.Name, Helpers.InternalStruct,
473490
Helpers.InstanceField);
474-
WriteLine("public {0}.{1} {2} {{ get {{ return {3}; }} }}", @class.Name,
491+
WriteLine("internal {0}.{1} {2} {{ get {{ return {3}; }} }}", @class.Name,
475492
Helpers.InternalStruct, Helpers.InstanceIdentifier, Helpers.InstanceField);
476493
}
477494
else
@@ -488,7 +505,7 @@ public void GenerateClass(Class @class)
488505
// use interfaces if any - derived types with a secondary base this class must be compatible with the map
489506
var @interface = @class.Namespace.Classes.Find(c => c.OriginalClass == @class);
490507
WriteLine(
491-
"public static readonly System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}> NativeToManagedMap = new System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}>();",
508+
"internal static readonly System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}> NativeToManagedMap = new System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}>();",
492509
@interface != null ? @interface.Name : @class.Name);
493510
WriteLine("protected void*[] __OriginalVTables;");
494511
}
@@ -2008,7 +2025,7 @@ private void GenerateNativeConstructor(Class @class)
20082025
if (!@class.IsAbstractImpl)
20092026
{
20102027
PushBlock(CSharpBlockKind.Method);
2011-
WriteLine("public static {0}{1} {2}(global::System.IntPtr native, bool skipVTables = false)",
2028+
WriteLine("internal static {0}{1} {2}(global::System.IntPtr native, bool skipVTables = false)",
20122029
@class.NeedsBase && !@class.BaseClass.IsInterface ? "new " : string.Empty,
20132030
@class.Name, Helpers.CreateInstanceIdentifier);
20142031
WriteStartBraceIndent();
@@ -2080,7 +2097,7 @@ private void GenerateNativeConstructorByValue(Class @class, string className, st
20802097
if (!@class.IsAbstractImpl)
20812098
{
20822099
PushBlock(CSharpBlockKind.Method);
2083-
WriteLine("public static {0} {1}({0}.{2}{3} native, bool skipVTables = false)",
2100+
WriteLine("internal static {0} {1}({0}.{2}{3} native, bool skipVTables = false)",
20842101
className, Helpers.CreateInstanceIdentifier,
20852102
Helpers.InternalStruct, internalSuffix);
20862103
WriteStartBraceIndent();

src/Generator/Options.cs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Text;
56
using CppSharp.AST;
67
using CppSharp.Generators;
@@ -39,8 +40,8 @@ public DriverOptions()
3940
/// </summary>
4041
public bool DryRun;
4142

42-
public Module SystemModule { get; private set; }
43-
public List<Module> Modules { get; private set; }
43+
public Module SystemModule { get; }
44+
public List<Module> Modules { get; }
4445

4546
public Module MainModule
4647
{
@@ -53,11 +54,11 @@ public Module MainModule
5354
}
5455

5556
// Parser options
56-
public List<string> Headers { get { return MainModule.Headers; } }
57+
public List<string> Headers => MainModule.Headers;
5758
public bool IgnoreParseWarnings;
5859

5960
// Library options
60-
public List<string> Libraries { get { return MainModule.Libraries; } }
61+
public List<string> Libraries => MainModule.Libraries;
6162
public bool CheckSymbols;
6263

6364
public string SharedLibraryName
@@ -133,15 +134,9 @@ public string TemplatesLibraryName
133134
set { MainModule.TemplatesLibraryName = value; }
134135
}
135136

136-
public bool IsCSharpGenerator
137-
{
138-
get { return GeneratorKind == GeneratorKind.CSharp; }
139-
}
137+
public bool IsCSharpGenerator => GeneratorKind == GeneratorKind.CSharp;
140138

141-
public bool IsCLIGenerator
142-
{
143-
get { return GeneratorKind == GeneratorKind.CLI; }
144-
}
139+
public bool IsCLIGenerator => GeneratorKind == GeneratorKind.CLI;
145140

146141
public readonly List<string> DependentNameSpaces = new List<string>();
147142
public bool MarshalCharAsManagedChar { get; set; }
@@ -156,7 +151,10 @@ public bool IsCLIGenerator
156151
/// <summary>
157152
/// C# end only: force patching of the virtual entries of the functions in this list.
158153
/// </summary>
159-
public HashSet<string> ExplicitlyPatchedVirtualFunctions { get; private set; }
154+
public HashSet<string> ExplicitlyPatchedVirtualFunctions { get; }
155+
156+
public bool DoAllModulesHaveLibraries() =>
157+
Modules.All(m => m == SystemModule || m.Libraries.Count > 0);
160158
}
161159

162160
public class InvalidOptionException : Exception

src/Generator/Passes/CheckIgnoredDecls.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ private bool HasInvalidType(Type type, Module module, out string msg)
347347
return true;
348348
}
349349

350-
if (Options.Modules.All(m => m == Options.SystemModule || m.Libraries.Count > 0) &&
350+
if (Options.DoAllModulesHaveLibraries() &&
351351
module != Options.SystemModule && IsTypeExternal(module, type))
352352
{
353353
msg = "external";
@@ -410,16 +410,7 @@ private bool IsTypeExternal(Module module, Type type)
410410
if (declaration.Namespace == null || declaration.TranslationUnit.Module == null)
411411
return false;
412412

413-
// Check if there’s another module which wraps libraries with dependencies on
414-
// the ones in the current module.
415-
if (declaration.TranslationUnit.Module.Libraries.Any(l =>
416-
Context.Symbols.Libraries.First(
417-
lib => lib.FileName == l).Dependencies.Any(
418-
d => module != declaration.TranslationUnit.Module &&
419-
module.Libraries.Contains(d))))
420-
return true;
421-
422-
return false;
413+
return declaration.TranslationUnit.Module.Dependencies.Contains(module);
423414
}
424415

425416
private bool IsTypeIgnored(Type type)

src/Generator/Passes/DelegatesPass.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ public override bool VisitMethodDecl(Method method)
122122
ReturnType = method.ReturnType
123123
}))),
124124
Namespace = namespaceDelegates,
125-
IsSynthetized = true
125+
IsSynthetized = true,
126+
Access = AccessSpecifier.Private
126127
};
127128

128129
Generator.CurrentOutputNamespace = module.OutputNamespace;

tests/CSharp/CSharp.Tests.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,30 +320,39 @@ public void TestNativeToManagedMapWithForeignObjects()
320320
{
321321
IntPtr native1;
322322
IntPtr native2;
323+
var hasVirtualDtor1Map = (IDictionary<IntPtr, HasVirtualDtor1>) typeof(
324+
HasVirtualDtor1).GetField("NativeToManagedMap",
325+
BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
326+
var hasVirtualDtor2Map = (IDictionary<IntPtr, HasVirtualDtor2>) typeof(
327+
HasVirtualDtor2).GetField("NativeToManagedMap",
328+
BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
323329
using (var testNativeToManagedMap = new TestNativeToManagedMap())
324330
{
325331
var hasVirtualDtor2 = testNativeToManagedMap.HasVirtualDtor2;
326332
native2 = hasVirtualDtor2.__Instance;
327333
native1 = hasVirtualDtor2.HasVirtualDtor1.__Instance;
328-
Assert.IsTrue(HasVirtualDtor2.NativeToManagedMap.ContainsKey(native2));
329-
Assert.IsTrue(HasVirtualDtor1.NativeToManagedMap.ContainsKey(native1));
334+
Assert.IsTrue(hasVirtualDtor1Map.ContainsKey(native1));
335+
Assert.IsTrue(hasVirtualDtor2Map.ContainsKey(native2));
330336
Assert.AreSame(hasVirtualDtor2, testNativeToManagedMap.HasVirtualDtor2);
331337
}
332-
Assert.IsFalse(HasVirtualDtor2.NativeToManagedMap.ContainsKey(native2));
333-
Assert.IsFalse(HasVirtualDtor1.NativeToManagedMap.ContainsKey(native1));
338+
Assert.IsFalse(hasVirtualDtor1Map.ContainsKey(native1));
339+
Assert.IsFalse(hasVirtualDtor2Map.ContainsKey(native2));
334340
}
335341

336342
[Test]
337343
public void TestNativeToManagedMapWithOwnObjects()
338344
{
339345
using (var testNativeToManagedMap = new TestNativeToManagedMap())
340346
{
347+
var quxMap = (IDictionary<IntPtr, IQux>) typeof(
348+
Qux).GetField("NativeToManagedMap",
349+
BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
341350
var bar = new Bar();
342351
testNativeToManagedMap.PropertyWithNoVirtualDtor = bar;
343352
Assert.AreSame(bar, testNativeToManagedMap.PropertyWithNoVirtualDtor);
344-
Assert.IsTrue(Qux.NativeToManagedMap.ContainsKey(bar.__Instance));
353+
Assert.IsTrue(quxMap.ContainsKey(bar.__Instance));
345354
bar.Dispose();
346-
Assert.IsFalse(Qux.NativeToManagedMap.ContainsKey(bar.__Instance));
355+
Assert.IsFalse(quxMap.ContainsKey(bar.__Instance));
347356
}
348357
}
349358

tests/NamespacesDerived/NamespacesDerived.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.IO;
23
using CppSharp.AST;
34
using CppSharp.Generators;
@@ -37,12 +38,20 @@ public override void Postprocess(Driver driver, ASTContext ctx)
3738
new CaseRenamePass(
3839
RenameTargets.Function | RenameTargets.Method | RenameTargets.Property | RenameTargets.Delegate | RenameTargets.Variable,
3940
RenameCasePattern.UpperCamelCase).VisitASTContext(driver.Context.ASTContext);
41+
42+
driver.Generator.OnUnitGenerated += o =>
43+
{
44+
var firstBlock = o.Templates[0].RootBlock.Blocks[1];
45+
firstBlock.Text.StringBuilder.Append($@"using System.Runtime.CompilerServices;{
46+
Environment.NewLine}{
47+
Environment.NewLine}[assembly:InternalsVisibleTo(""NamespacesDerived.CSharp"")]{
48+
Environment.NewLine}");
49+
};
4050
}
4151
}
4252

4353
public class NamespacesDerived
4454
{
45-
4655
public static void Main(string[] args)
4756
{
4857
ConsoleDriver.Run(new NamespacesDerivedTests(GeneratorKind.CSharp));

0 commit comments

Comments
 (0)