Browse Source

Hid most internal details of the generated code from users.

pull/732/head
Dimitar Dobrev 8 years ago
parent
commit
21233a6622
  1. 32
      src/AST/Module.cs
  2. 62
      src/Generator/Driver.cs
  3. 25
      src/Generator/Generators/CSharp/CSharpSources.cs
  4. 24
      src/Generator/Options.cs
  5. 13
      src/Generator/Passes/CheckIgnoredDecls.cs
  6. 3
      src/Generator/Passes/DelegatesPass.cs
  7. 21
      tests/CSharp/CSharp.Tests.cs
  8. 11
      tests/NamespacesDerived/NamespacesDerived.cs

32
src/AST/Module.cs

@ -4,28 +4,16 @@ namespace CppSharp.AST @@ -4,28 +4,16 @@ namespace CppSharp.AST
{
public class Module
{
public Module()
{
IncludeDirs = new List<string>();
Headers = new List<string>();
LibraryDirs = new List<string>();
Libraries = new List<string>();
Defines = new List<string>();
Undefines = new List<string>();
Units = new List<TranslationUnit>();
CodeFiles = new List<string>();
}
public List<string> IncludeDirs { get; private set; }
public List<string> Headers { get; private set; }
public List<string> LibraryDirs { get; set; }
public List<string> Libraries { get; private set; }
public List<string> Defines { get; set; }
public List<string> Undefines { get; set; }
public List<string> IncludeDirs { get; } = new List<string>();
public List<string> Headers { get; } = new List<string>();
public List<string> LibraryDirs { get; } = new List<string>();
public List<string> Libraries { get; } = new List<string>();
public List<string> Defines { get; } = new List<string>();
public List<string> Undefines { get; } = new List<string>();
public string OutputNamespace { get; set; }
public List<TranslationUnit> Units { get; private set; }
public List<string> CodeFiles { get; private set; }
public List<TranslationUnit> Units { get; } = new List<TranslationUnit>();
public List<string> CodeFiles { get; } = new List<string>();
public List<Module> Dependencies { get; } = new List<Module>();
public string SharedLibraryName
{
@ -74,6 +62,8 @@ namespace CppSharp.AST @@ -74,6 +62,8 @@ namespace CppSharp.AST
public string LibraryName { get; set; }
public override string ToString() => LibraryName;
private string sharedLibraryName;
private string inlinesLibraryName;
private string templatesLibraryName;

62
src/Generator/Driver.cs

@ -223,19 +223,23 @@ namespace CppSharp @@ -223,19 +223,23 @@ namespace CppSharp
public void SortModulesByDependencies()
{
if (Options.Modules.All(m => m.Libraries.Any() || m == Options.SystemModule))
if (!Options.DoAllModulesHaveLibraries())
return;
var sortedModules = Options.Modules.TopologicalSort(m =>
{
var sortedModules = Options.Modules.TopologicalSort(m =>
{
return from library in Context.Symbols.Libraries
where m.Libraries.Contains(library.FileName)
from module in Options.Modules
where library.Dependencies.Intersect(module.Libraries).Any()
select module;
});
Options.Modules.Clear();
Options.Modules.AddRange(sortedModules);
}
var dependencies = (from library in Context.Symbols.Libraries
where m.Libraries.Contains(library.FileName)
from module in Options.Modules
where library.Dependencies.Intersect(module.Libraries).Any()
select module).ToList();
if (m != Options.SystemModule)
m.Dependencies.Add(Options.SystemModule);
m.Dependencies.AddRange(dependencies);
return dependencies;
});
Options.Modules.Clear();
Options.Modules.AddRange(sortedModules);
}
public bool ParseLibraries()
@ -384,9 +388,7 @@ namespace CppSharp @@ -384,9 +388,7 @@ namespace CppSharp
public void CompileCode(AST.Module module)
{
var assemblyFile = Path.Combine(Options.OutputDir,
string.IsNullOrEmpty(module.LibraryName) ?
"out.dll" : module.LibraryName + ".dll");
var assemblyFile = Path.Combine(Options.OutputDir, module.LibraryName + ".dll");
var docFile = Path.ChangeExtension(assemblyFile, ".xml");
@ -421,7 +423,7 @@ namespace CppSharp @@ -421,7 +423,7 @@ namespace CppSharp
!compilerParameters.ReferencedAssemblies.Contains(libraryMappings[d]))
.Select(l => libraryMappings[l])).ToArray());
Diagnostics.Message("Compiling {0}...", module.LibraryName);
Diagnostics.Message($"Compiling {module.LibraryName}...");
CompilerResults compilerResults;
using (var codeProvider = new CSharpCodeProvider(
new Dictionary<string, string> {
@ -440,14 +442,7 @@ namespace CppSharp @@ -440,14 +442,7 @@ namespace CppSharp
HasCompilationErrors = errors.Count > 0;
if (!HasCompilationErrors)
{
var injector = new Injector();
injector.ReadAssembly(assemblyFile);
var moduleInitializer = injector.GetModuleInitializer();
if (moduleInitializer != null)
{
injector.InjectInitializer(moduleInitializer);
injector.WriteAssembly(assemblyFile, null);
}
InjectModuleInitializer(assemblyFile);
Diagnostics.Message("Compilation succeeded.");
var wrapper = Path.Combine(outputDir, assemblyFile);
foreach (var library in module.Libraries)
@ -455,6 +450,25 @@ namespace CppSharp @@ -455,6 +450,25 @@ namespace CppSharp
}
}
/// <summary>
/// Injects a module initializer, if any, i.e. code executed right after an
/// assembly is loaded and before any other code in it.
/// <para>The run-time supports it but C# does not so we inject it in the
/// compiled assembly by manually adding IL instructions.</para>
/// </summary>
/// <param name="assemblyFile">The assembly to inject a module initializer to.</param>
private static void InjectModuleInitializer(string assemblyFile)
{
var injector = new Injector();
injector.ReadAssembly(assemblyFile);
var moduleInitializer = injector.GetModuleInitializer();
if (moduleInitializer != null)
{
injector.InjectInitializer(moduleInitializer);
injector.WriteAssembly(assemblyFile, null);
}
}
public void AddTranslationUnitPass(TranslationUnitPass pass)
{
Context.TranslationUnitPasses.AddPass(pass);

25
src/Generator/Generators/CSharp/CSharpSources.cs

@ -157,6 +157,8 @@ namespace CppSharp.Generators.CSharp @@ -157,6 +157,8 @@ namespace CppSharp.Generators.CSharp
WriteLine("using System;");
WriteLine("using System.Runtime.InteropServices;");
WriteLine("using System.Security;");
if (Context.Options.DoAllModulesHaveLibraries())
WriteLine("using System.Runtime.CompilerServices;");
foreach (var customUsingStatement in Options.DependentNameSpaces)
{
WriteLine(string.Format("using {0};", customUsingStatement));
@ -165,6 +167,21 @@ namespace CppSharp.Generators.CSharp @@ -165,6 +167,21 @@ namespace CppSharp.Generators.CSharp
var module = TranslationUnits.Count == 0 ?
Context.Options.SystemModule : TranslationUnit.Module;
var hasInternalsVisibleTo = false;
if (Context.Options.DoAllModulesHaveLibraries())
{
foreach (var library in from m in Options.Modules
where m.Dependencies.Contains(module)
select m.LibraryName)
{
WriteLine($"[assembly:InternalsVisibleTo(\"{library}\")]");
if (!hasInternalsVisibleTo)
hasInternalsVisibleTo = true;
}
}
if (hasInternalsVisibleTo)
NewLine();
if (!string.IsNullOrEmpty(module.OutputNamespace))
{
PushBlock(CSharpBlockKind.Namespace);
@ -471,7 +488,7 @@ namespace CppSharp.Generators.CSharp @@ -471,7 +488,7 @@ namespace CppSharp.Generators.CSharp
{
WriteLine("private {0}.{1} {2};", @class.Name, Helpers.InternalStruct,
Helpers.InstanceField);
WriteLine("public {0}.{1} {2} {{ get {{ return {3}; }} }}", @class.Name,
WriteLine("internal {0}.{1} {2} {{ get {{ return {3}; }} }}", @class.Name,
Helpers.InternalStruct, Helpers.InstanceIdentifier, Helpers.InstanceField);
}
else
@ -488,7 +505,7 @@ namespace CppSharp.Generators.CSharp @@ -488,7 +505,7 @@ namespace CppSharp.Generators.CSharp
// use interfaces if any - derived types with a secondary base this class must be compatible with the map
var @interface = @class.Namespace.Classes.Find(c => c.OriginalClass == @class);
WriteLine(
"public static readonly System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}> NativeToManagedMap = new System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}>();",
"internal static readonly System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}> NativeToManagedMap = new System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}>();",
@interface != null ? @interface.Name : @class.Name);
WriteLine("protected void*[] __OriginalVTables;");
}
@ -2008,7 +2025,7 @@ namespace CppSharp.Generators.CSharp @@ -2008,7 +2025,7 @@ namespace CppSharp.Generators.CSharp
if (!@class.IsAbstractImpl)
{
PushBlock(CSharpBlockKind.Method);
WriteLine("public static {0}{1} {2}(global::System.IntPtr native, bool skipVTables = false)",
WriteLine("internal static {0}{1} {2}(global::System.IntPtr native, bool skipVTables = false)",
@class.NeedsBase && !@class.BaseClass.IsInterface ? "new " : string.Empty,
@class.Name, Helpers.CreateInstanceIdentifier);
WriteStartBraceIndent();
@ -2080,7 +2097,7 @@ namespace CppSharp.Generators.CSharp @@ -2080,7 +2097,7 @@ namespace CppSharp.Generators.CSharp
if (!@class.IsAbstractImpl)
{
PushBlock(CSharpBlockKind.Method);
WriteLine("public static {0} {1}({0}.{2}{3} native, bool skipVTables = false)",
WriteLine("internal static {0} {1}({0}.{2}{3} native, bool skipVTables = false)",
className, Helpers.CreateInstanceIdentifier,
Helpers.InternalStruct, internalSuffix);
WriteStartBraceIndent();

24
src/Generator/Options.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using CppSharp.AST;
using CppSharp.Generators;
@ -39,8 +40,8 @@ namespace CppSharp @@ -39,8 +40,8 @@ namespace CppSharp
/// </summary>
public bool DryRun;
public Module SystemModule { get; private set; }
public List<Module> Modules { get; private set; }
public Module SystemModule { get; }
public List<Module> Modules { get; }
public Module MainModule
{
@ -53,11 +54,11 @@ namespace CppSharp @@ -53,11 +54,11 @@ namespace CppSharp
}
// Parser options
public List<string> Headers { get { return MainModule.Headers; } }
public List<string> Headers => MainModule.Headers;
public bool IgnoreParseWarnings;
// Library options
public List<string> Libraries { get { return MainModule.Libraries; } }
public List<string> Libraries => MainModule.Libraries;
public bool CheckSymbols;
public string SharedLibraryName
@ -133,15 +134,9 @@ namespace CppSharp @@ -133,15 +134,9 @@ namespace CppSharp
set { MainModule.TemplatesLibraryName = value; }
}
public bool IsCSharpGenerator
{
get { return GeneratorKind == GeneratorKind.CSharp; }
}
public bool IsCSharpGenerator => GeneratorKind == GeneratorKind.CSharp;
public bool IsCLIGenerator
{
get { return GeneratorKind == GeneratorKind.CLI; }
}
public bool IsCLIGenerator => GeneratorKind == GeneratorKind.CLI;
public readonly List<string> DependentNameSpaces = new List<string>();
public bool MarshalCharAsManagedChar { get; set; }
@ -156,7 +151,10 @@ namespace CppSharp @@ -156,7 +151,10 @@ namespace CppSharp
/// <summary>
/// C# end only: force patching of the virtual entries of the functions in this list.
/// </summary>
public HashSet<string> ExplicitlyPatchedVirtualFunctions { get; private set; }
public HashSet<string> ExplicitlyPatchedVirtualFunctions { get; }
public bool DoAllModulesHaveLibraries() =>
Modules.All(m => m == SystemModule || m.Libraries.Count > 0);
}
public class InvalidOptionException : Exception

13
src/Generator/Passes/CheckIgnoredDecls.cs

@ -347,7 +347,7 @@ namespace CppSharp.Passes @@ -347,7 +347,7 @@ namespace CppSharp.Passes
return true;
}
if (Options.Modules.All(m => m == Options.SystemModule || m.Libraries.Count > 0) &&
if (Options.DoAllModulesHaveLibraries() &&
module != Options.SystemModule && IsTypeExternal(module, type))
{
msg = "external";
@ -410,16 +410,7 @@ namespace CppSharp.Passes @@ -410,16 +410,7 @@ namespace CppSharp.Passes
if (declaration.Namespace == null || declaration.TranslationUnit.Module == null)
return false;
// Check if there’s another module which wraps libraries with dependencies on
// the ones in the current module.
if (declaration.TranslationUnit.Module.Libraries.Any(l =>
Context.Symbols.Libraries.First(
lib => lib.FileName == l).Dependencies.Any(
d => module != declaration.TranslationUnit.Module &&
module.Libraries.Contains(d))))
return true;
return false;
return declaration.TranslationUnit.Module.Dependencies.Contains(module);
}
private bool IsTypeIgnored(Type type)

3
src/Generator/Passes/DelegatesPass.cs

@ -122,7 +122,8 @@ namespace CppSharp.Passes @@ -122,7 +122,8 @@ namespace CppSharp.Passes
ReturnType = method.ReturnType
}))),
Namespace = namespaceDelegates,
IsSynthetized = true
IsSynthetized = true,
Access = AccessSpecifier.Private
};
Generator.CurrentOutputNamespace = module.OutputNamespace;

21
tests/CSharp/CSharp.Tests.cs

@ -320,17 +320,23 @@ public unsafe class CSharpTests : GeneratorTestFixture @@ -320,17 +320,23 @@ public unsafe class CSharpTests : GeneratorTestFixture
{
IntPtr native1;
IntPtr native2;
var hasVirtualDtor1Map = (IDictionary<IntPtr, HasVirtualDtor1>) typeof(
HasVirtualDtor1).GetField("NativeToManagedMap",
BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
var hasVirtualDtor2Map = (IDictionary<IntPtr, HasVirtualDtor2>) typeof(
HasVirtualDtor2).GetField("NativeToManagedMap",
BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
using (var testNativeToManagedMap = new TestNativeToManagedMap())
{
var hasVirtualDtor2 = testNativeToManagedMap.HasVirtualDtor2;
native2 = hasVirtualDtor2.__Instance;
native1 = hasVirtualDtor2.HasVirtualDtor1.__Instance;
Assert.IsTrue(HasVirtualDtor2.NativeToManagedMap.ContainsKey(native2));
Assert.IsTrue(HasVirtualDtor1.NativeToManagedMap.ContainsKey(native1));
Assert.IsTrue(hasVirtualDtor1Map.ContainsKey(native1));
Assert.IsTrue(hasVirtualDtor2Map.ContainsKey(native2));
Assert.AreSame(hasVirtualDtor2, testNativeToManagedMap.HasVirtualDtor2);
}
Assert.IsFalse(HasVirtualDtor2.NativeToManagedMap.ContainsKey(native2));
Assert.IsFalse(HasVirtualDtor1.NativeToManagedMap.ContainsKey(native1));
Assert.IsFalse(hasVirtualDtor1Map.ContainsKey(native1));
Assert.IsFalse(hasVirtualDtor2Map.ContainsKey(native2));
}
[Test]
@ -338,12 +344,15 @@ public unsafe class CSharpTests : GeneratorTestFixture @@ -338,12 +344,15 @@ public unsafe class CSharpTests : GeneratorTestFixture
{
using (var testNativeToManagedMap = new TestNativeToManagedMap())
{
var quxMap = (IDictionary<IntPtr, IQux>) typeof(
Qux).GetField("NativeToManagedMap",
BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
var bar = new Bar();
testNativeToManagedMap.PropertyWithNoVirtualDtor = bar;
Assert.AreSame(bar, testNativeToManagedMap.PropertyWithNoVirtualDtor);
Assert.IsTrue(Qux.NativeToManagedMap.ContainsKey(bar.__Instance));
Assert.IsTrue(quxMap.ContainsKey(bar.__Instance));
bar.Dispose();
Assert.IsFalse(Qux.NativeToManagedMap.ContainsKey(bar.__Instance));
Assert.IsFalse(quxMap.ContainsKey(bar.__Instance));
}
}

11
tests/NamespacesDerived/NamespacesDerived.cs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
using System;
using System.IO;
using CppSharp.AST;
using CppSharp.Generators;
@ -37,12 +38,20 @@ namespace CppSharp.Tests @@ -37,12 +38,20 @@ namespace CppSharp.Tests
new CaseRenamePass(
RenameTargets.Function | RenameTargets.Method | RenameTargets.Property | RenameTargets.Delegate | RenameTargets.Variable,
RenameCasePattern.UpperCamelCase).VisitASTContext(driver.Context.ASTContext);
driver.Generator.OnUnitGenerated += o =>
{
var firstBlock = o.Templates[0].RootBlock.Blocks[1];
firstBlock.Text.StringBuilder.Append($@"using System.Runtime.CompilerServices;{
Environment.NewLine}{
Environment.NewLine}[assembly:InternalsVisibleTo(""NamespacesDerived.CSharp"")]{
Environment.NewLine}");
};
}
}
public class NamespacesDerived
{
public static void Main(string[] args)
{
ConsoleDriver.Run(new NamespacesDerivedTests(GeneratorKind.CSharp));

Loading…
Cancel
Save