Browse Source

Hid most internal details of the generated code from users.

pull/732/head
Dimitar Dobrev 9 years ago
parent
commit
21233a6622
  1. 32
      src/AST/Module.cs
  2. 42
      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
{ {
public class Module public class Module
{ {
public Module() public List<string> IncludeDirs { get; } = new List<string>();
{ public List<string> Headers { get; } = new List<string>();
IncludeDirs = new List<string>(); public List<string> LibraryDirs { get; } = new List<string>();
Headers = new List<string>(); public List<string> Libraries { get; } = new List<string>();
LibraryDirs = new List<string>(); public List<string> Defines { get; } = new List<string>();
Libraries = new List<string>(); public List<string> Undefines { get; } = 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 string OutputNamespace { get; set; } public string OutputNamespace { get; set; }
public List<TranslationUnit> Units { get; } = new List<TranslationUnit>();
public List<TranslationUnit> Units { get; private set; } public List<string> CodeFiles { get; } = new List<string>();
public List<string> CodeFiles { get; private set; } public List<Module> Dependencies { get; } = new List<Module>();
public string SharedLibraryName public string SharedLibraryName
{ {
@ -74,6 +62,8 @@ namespace CppSharp.AST
public string LibraryName { get; set; } public string LibraryName { get; set; }
public override string ToString() => LibraryName;
private string sharedLibraryName; private string sharedLibraryName;
private string inlinesLibraryName; private string inlinesLibraryName;
private string templatesLibraryName; private string templatesLibraryName;

42
src/Generator/Driver.cs

@ -223,20 +223,24 @@ namespace CppSharp
public void SortModulesByDependencies() 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 var dependencies = (from library in Context.Symbols.Libraries
where m.Libraries.Contains(library.FileName) where m.Libraries.Contains(library.FileName)
from module in Options.Modules from module in Options.Modules
where library.Dependencies.Intersect(module.Libraries).Any() where library.Dependencies.Intersect(module.Libraries).Any()
select module; select module).ToList();
if (m != Options.SystemModule)
m.Dependencies.Add(Options.SystemModule);
m.Dependencies.AddRange(dependencies);
return dependencies;
}); });
Options.Modules.Clear(); Options.Modules.Clear();
Options.Modules.AddRange(sortedModules); Options.Modules.AddRange(sortedModules);
} }
}
public bool ParseLibraries() public bool ParseLibraries()
{ {
@ -384,9 +388,7 @@ namespace CppSharp
public void CompileCode(AST.Module module) public void CompileCode(AST.Module module)
{ {
var assemblyFile = Path.Combine(Options.OutputDir, var assemblyFile = Path.Combine(Options.OutputDir, module.LibraryName + ".dll");
string.IsNullOrEmpty(module.LibraryName) ?
"out.dll" : module.LibraryName + ".dll");
var docFile = Path.ChangeExtension(assemblyFile, ".xml"); var docFile = Path.ChangeExtension(assemblyFile, ".xml");
@ -421,7 +423,7 @@ namespace CppSharp
!compilerParameters.ReferencedAssemblies.Contains(libraryMappings[d])) !compilerParameters.ReferencedAssemblies.Contains(libraryMappings[d]))
.Select(l => libraryMappings[l])).ToArray()); .Select(l => libraryMappings[l])).ToArray());
Diagnostics.Message("Compiling {0}...", module.LibraryName); Diagnostics.Message($"Compiling {module.LibraryName}...");
CompilerResults compilerResults; CompilerResults compilerResults;
using (var codeProvider = new CSharpCodeProvider( using (var codeProvider = new CSharpCodeProvider(
new Dictionary<string, string> { new Dictionary<string, string> {
@ -439,6 +441,23 @@ namespace CppSharp
HasCompilationErrors = errors.Count > 0; HasCompilationErrors = errors.Count > 0;
if (!HasCompilationErrors) if (!HasCompilationErrors)
{
InjectModuleInitializer(assemblyFile);
Diagnostics.Message("Compilation succeeded.");
var wrapper = Path.Combine(outputDir, assemblyFile);
foreach (var library in module.Libraries)
libraryMappings[library] = wrapper;
}
}
/// <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(); var injector = new Injector();
injector.ReadAssembly(assemblyFile); injector.ReadAssembly(assemblyFile);
@ -448,11 +467,6 @@ namespace CppSharp
injector.InjectInitializer(moduleInitializer); injector.InjectInitializer(moduleInitializer);
injector.WriteAssembly(assemblyFile, null); injector.WriteAssembly(assemblyFile, null);
} }
Diagnostics.Message("Compilation succeeded.");
var wrapper = Path.Combine(outputDir, assemblyFile);
foreach (var library in module.Libraries)
libraryMappings[library] = wrapper;
}
} }
public void AddTranslationUnitPass(TranslationUnitPass pass) public void AddTranslationUnitPass(TranslationUnitPass pass)

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

@ -157,6 +157,8 @@ namespace CppSharp.Generators.CSharp
WriteLine("using System;"); WriteLine("using System;");
WriteLine("using System.Runtime.InteropServices;"); WriteLine("using System.Runtime.InteropServices;");
WriteLine("using System.Security;"); WriteLine("using System.Security;");
if (Context.Options.DoAllModulesHaveLibraries())
WriteLine("using System.Runtime.CompilerServices;");
foreach (var customUsingStatement in Options.DependentNameSpaces) foreach (var customUsingStatement in Options.DependentNameSpaces)
{ {
WriteLine(string.Format("using {0};", customUsingStatement)); WriteLine(string.Format("using {0};", customUsingStatement));
@ -165,6 +167,21 @@ namespace CppSharp.Generators.CSharp
var module = TranslationUnits.Count == 0 ? var module = TranslationUnits.Count == 0 ?
Context.Options.SystemModule : TranslationUnit.Module; 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)) if (!string.IsNullOrEmpty(module.OutputNamespace))
{ {
PushBlock(CSharpBlockKind.Namespace); PushBlock(CSharpBlockKind.Namespace);
@ -471,7 +488,7 @@ namespace CppSharp.Generators.CSharp
{ {
WriteLine("private {0}.{1} {2};", @class.Name, Helpers.InternalStruct, WriteLine("private {0}.{1} {2};", @class.Name, Helpers.InternalStruct,
Helpers.InstanceField); 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); Helpers.InternalStruct, Helpers.InstanceIdentifier, Helpers.InstanceField);
} }
else else
@ -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 // 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); var @interface = @class.Namespace.Classes.Find(c => c.OriginalClass == @class);
WriteLine( 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); @interface != null ? @interface.Name : @class.Name);
WriteLine("protected void*[] __OriginalVTables;"); WriteLine("protected void*[] __OriginalVTables;");
} }
@ -2008,7 +2025,7 @@ namespace CppSharp.Generators.CSharp
if (!@class.IsAbstractImpl) if (!@class.IsAbstractImpl)
{ {
PushBlock(CSharpBlockKind.Method); 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.NeedsBase && !@class.BaseClass.IsInterface ? "new " : string.Empty,
@class.Name, Helpers.CreateInstanceIdentifier); @class.Name, Helpers.CreateInstanceIdentifier);
WriteStartBraceIndent(); WriteStartBraceIndent();
@ -2080,7 +2097,7 @@ namespace CppSharp.Generators.CSharp
if (!@class.IsAbstractImpl) if (!@class.IsAbstractImpl)
{ {
PushBlock(CSharpBlockKind.Method); 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, className, Helpers.CreateInstanceIdentifier,
Helpers.InternalStruct, internalSuffix); Helpers.InternalStruct, internalSuffix);
WriteStartBraceIndent(); WriteStartBraceIndent();

24
src/Generator/Options.cs

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

13
src/Generator/Passes/CheckIgnoredDecls.cs

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

3
src/Generator/Passes/DelegatesPass.cs

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

21
tests/CSharp/CSharp.Tests.cs

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

11
tests/NamespacesDerived/NamespacesDerived.cs

@ -1,3 +1,4 @@
using System;
using System.IO; using System.IO;
using CppSharp.AST; using CppSharp.AST;
using CppSharp.Generators; using CppSharp.Generators;
@ -37,12 +38,20 @@ namespace CppSharp.Tests
new CaseRenamePass( new CaseRenamePass(
RenameTargets.Function | RenameTargets.Method | RenameTargets.Property | RenameTargets.Delegate | RenameTargets.Variable, RenameTargets.Function | RenameTargets.Method | RenameTargets.Property | RenameTargets.Delegate | RenameTargets.Variable,
RenameCasePattern.UpperCamelCase).VisitASTContext(driver.Context.ASTContext); 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 class NamespacesDerived
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
ConsoleDriver.Run(new NamespacesDerivedTests(GeneratorKind.CSharp)); ConsoleDriver.Run(new NamespacesDerivedTests(GeneratorKind.CSharp));

Loading…
Cancel
Save