Skip to content

Commit ec24151

Browse files
committed
Extended the auto-compilation to add module initialisers, if any.
1 parent f7e6db2 commit ec24151

File tree

8 files changed

+279
-3
lines changed

8 files changed

+279
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ code or add scripting support to a native codebase.
3939
* Support for C++ standard library types (work-in-progress)
4040
* Strongly-typed customization APIs and type maps
4141
* Default values of parameters (for target languages which support them)
42+
* Option to automatically add a module initializer with the C# generator
4243

4344
### AST
4445

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
InjectModuleInitializer
3+
4+
Command line program to inject a module initializer into a .NET assembly.
5+
6+
Copyright (C) 2009-2016 Einar Egilsson
7+
http://einaregilsson.com/module-initializers-in-csharp/
8+
9+
This program is licensed under the MIT license: http://opensource.org/licenses/MIT
10+
*/
11+
using System;
12+
13+
namespace EinarEgilsson.Utilities.InjectModuleInitializer
14+
{
15+
internal static class Errors
16+
{
17+
internal static string AssemblyDoesNotExist(string assembly)
18+
{
19+
return String.Format("Assembly '{0}' does not exist", assembly);
20+
}
21+
22+
internal static string NoModuleInitializerTypeFound()
23+
{
24+
return "Found no type named 'ModuleInitializer', this type must exist or the ModuleInitializer parameter must be used";
25+
}
26+
27+
internal static string InvalidFormatForModuleInitializer()
28+
{
29+
return "Invalid format for ModuleInitializer parameter, use Full.Type.Name::MethodName";
30+
}
31+
32+
internal static string TypeNameDoesNotExist(string typeName)
33+
{
34+
return string.Format("No type named '{0}' exists in the given assembly!", typeName);
35+
}
36+
37+
internal static string MethodNameDoesNotExist(string typeName, string methodName)
38+
{
39+
return string.Format("No method named '{0}' exists in the type '{0}'", methodName, typeName);
40+
}
41+
42+
internal static string KeyFileDoesNotExist(string keyfile)
43+
{
44+
return string.Format("The key file'{0}' does not exist", keyfile);
45+
}
46+
47+
internal static string ModuleInitializerMayNotBePrivate()
48+
{
49+
return "Module initializer method may not be private or protected, use public or internal instead";
50+
}
51+
52+
internal static string ModuleInitializerMustBeVoid()
53+
{
54+
return "Module initializer method must have 'void' as return type";
55+
}
56+
57+
internal static string ModuleInitializerMayNotHaveParameters()
58+
{
59+
return "Module initializer method must not have any parameters";
60+
}
61+
62+
internal static string ModuleInitializerMustBeStatic()
63+
{
64+
return "Module initializer method must be static";
65+
}
66+
}
67+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
InjectModuleInitializer
3+
4+
Command line program to inject a module initializer into a .NET assembly.
5+
6+
Copyright (C) 2009-2016 Einar Egilsson
7+
http://einaregilsson.com/module-initializers-in-csharp/
8+
9+
This program is licensed under the MIT license: http://opensource.org/licenses/MIT
10+
*/
11+
using System;
12+
using System.Diagnostics;
13+
using System.IO;
14+
using System.Reflection;
15+
using Mono.Cecil;
16+
using Mono.Cecil.Cil;
17+
using Mono.Cecil.Pdb;
18+
using Mono.Collections.Generic;
19+
using MethodAttributes = Mono.Cecil.MethodAttributes;
20+
21+
namespace EinarEgilsson.Utilities.InjectModuleInitializer
22+
{
23+
public class InjectionException : Exception
24+
{
25+
public InjectionException(string msg) : base(msg) { }
26+
}
27+
28+
internal class Injector
29+
{
30+
public const string DefaultInitializerClassName = "ModuleInitializer";
31+
public const string DefaultInitializerMethodName = "Run";
32+
33+
private AssemblyDefinition Assembly { get; set; }
34+
35+
private string PdbFile(string assemblyFile)
36+
{
37+
Debug.Assert(assemblyFile != null);
38+
string path = Path.ChangeExtension(assemblyFile, ".pdb");
39+
if (File.Exists(path))
40+
{
41+
return path;
42+
}
43+
return null;
44+
}
45+
46+
47+
public void Inject(string assemblyFile, string moduleInitializer=null, string keyfile=null)
48+
{
49+
try
50+
{
51+
if (!File.Exists(assemblyFile))
52+
{
53+
throw new InjectionException(Errors.AssemblyDoesNotExist(assemblyFile));
54+
}
55+
if (keyfile != null && !File.Exists(keyfile))
56+
{
57+
throw new InjectionException(Errors.KeyFileDoesNotExist(keyfile));
58+
}
59+
ReadAssembly(assemblyFile);
60+
MethodReference callee = GetModuleInitializer(moduleInitializer);
61+
InjectInitializer(callee);
62+
63+
WriteAssembly(assemblyFile, keyfile);
64+
}
65+
catch (Exception ex)
66+
{
67+
throw new InjectionException(ex.Message);
68+
}
69+
}
70+
71+
public void InjectInitializer(MethodReference callee)
72+
{
73+
Debug.Assert(Assembly != null);
74+
TypeReference voidRef = Assembly.MainModule.Import(callee.ReturnType);
75+
const MethodAttributes attributes = MethodAttributes.Static
76+
| MethodAttributes.SpecialName
77+
| MethodAttributes.RTSpecialName;
78+
var cctor = new MethodDefinition(".cctor", attributes, voidRef);
79+
ILProcessor il = cctor.Body.GetILProcessor();
80+
il.Append(il.Create(OpCodes.Call, callee));
81+
il.Append(il.Create(OpCodes.Ret));
82+
83+
TypeDefinition moduleClass = Find(Assembly.MainModule.Types, t => t.Name == "<Module>");
84+
moduleClass.Methods.Add(cctor);
85+
86+
Debug.Assert(moduleClass != null, "Found no module class!");
87+
}
88+
89+
public void WriteAssembly(string assemblyFile, string keyfile)
90+
{
91+
Debug.Assert(Assembly != null);
92+
var writeParams = new WriterParameters();
93+
if (PdbFile(assemblyFile) != null)
94+
{
95+
writeParams.WriteSymbols = true;
96+
writeParams.SymbolWriterProvider = new PdbWriterProvider();
97+
}
98+
if (keyfile != null)
99+
{
100+
writeParams.StrongNameKeyPair = new StrongNameKeyPair(File.ReadAllBytes(keyfile));
101+
}
102+
Assembly.Write(assemblyFile, writeParams);
103+
}
104+
105+
public void ReadAssembly(string assemblyFile)
106+
{
107+
Debug.Assert(Assembly == null);
108+
109+
var resolver = new DefaultAssemblyResolver();
110+
resolver.AddSearchDirectory(Path.GetDirectoryName(assemblyFile));
111+
112+
var readParams = new ReaderParameters(ReadingMode.Immediate)
113+
{
114+
AssemblyResolver = resolver
115+
};
116+
117+
if (PdbFile(assemblyFile) != null)
118+
{
119+
readParams.ReadSymbols = true;
120+
readParams.SymbolReaderProvider = new PdbReaderProvider();
121+
}
122+
Assembly = AssemblyDefinition.ReadAssembly(assemblyFile, readParams);
123+
}
124+
125+
public MethodReference GetModuleInitializer(string moduleInitializer = null)
126+
{
127+
Debug.Assert(Assembly != null);
128+
ModuleDefinition module = Assembly.MainModule;
129+
string methodName;
130+
TypeDefinition moduleInitializerClass;
131+
if (string.IsNullOrEmpty(moduleInitializer))
132+
{
133+
methodName = DefaultInitializerMethodName;
134+
moduleInitializerClass = Find(module.Types, t => t.Name == DefaultInitializerClassName);
135+
if (moduleInitializerClass == null)
136+
{
137+
return null;
138+
}
139+
}
140+
else
141+
{
142+
if (!moduleInitializer.Contains("::"))
143+
{
144+
return null;
145+
}
146+
string typeName = moduleInitializer.Substring(0, moduleInitializer.IndexOf("::", StringComparison.Ordinal));
147+
methodName = moduleInitializer.Substring(typeName.Length + 2);
148+
moduleInitializerClass = Find(module.Types, t => t.FullName == typeName);
149+
if (moduleInitializerClass == null)
150+
{
151+
return null;
152+
}
153+
}
154+
155+
MethodDefinition callee = Find(moduleInitializerClass.Methods, m => m.Name == methodName);
156+
if (callee == null)
157+
{
158+
throw new InjectionException(Errors.MethodNameDoesNotExist(moduleInitializerClass.FullName, methodName));
159+
}
160+
if (callee.Parameters.Count > 0)
161+
{
162+
throw new InjectionException(Errors.ModuleInitializerMayNotHaveParameters());
163+
}
164+
if (callee.IsPrivate || callee.IsFamily)
165+
{
166+
throw new InjectionException(Errors.ModuleInitializerMayNotBePrivate());
167+
}
168+
if (!callee.ReturnType.FullName.Equals(typeof(void).FullName)) //Don't compare the types themselves, they might be from different CLR versions.
169+
{
170+
throw new InjectionException(Errors.ModuleInitializerMustBeVoid());
171+
}
172+
if (!callee.IsStatic)
173+
{
174+
throw new InjectionException(Errors.ModuleInitializerMustBeStatic());
175+
}
176+
return callee;
177+
}
178+
179+
//No LINQ, since we want to target 2.0
180+
private static T Find<T>(Collection<T> objects, Predicate<T> condition) where T:class
181+
{
182+
foreach (T obj in objects)
183+
{
184+
if (condition(obj))
185+
{
186+
return obj;
187+
}
188+
}
189+
return null;
190+
}
191+
}
192+
}

deps/Mono.Cecil/Mono.Cecil.Pdb.dll

77.5 KB
Binary file not shown.

deps/Mono.Cecil/Mono.Cecil.dll

264 KB
Binary file not shown.

src/Generator/Driver.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using CppSharp.Types;
1616
using CppSharp.Parser;
1717
using CppSharp.Utils;
18+
using EinarEgilsson.Utilities.InjectModuleInitializer;
1819

1920
namespace CppSharp
2021
{
@@ -439,6 +440,14 @@ public void CompileCode(AST.Module module)
439440
HasCompilationErrors = errors.Count > 0;
440441
if (!HasCompilationErrors)
441442
{
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+
}
442451
Diagnostics.Message("Compilation succeeded.");
443452
var wrapper = Path.Combine(outputDir, assemblyFile);
444453
foreach (var library in module.Libraries)

src/Generator/Utils/Utils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public static class AssemblyHelpers
132132
public static IEnumerable<Type> FindDerivedTypes(this Assembly assembly,
133133
Type baseType)
134134
{
135-
return assembly.GetTypes().Where(baseType.IsAssignableFrom);
135+
return assembly.GetExportedTypes().Where(baseType.IsAssignableFrom);
136136
}
137137
}
138138

src/Generator/premake5.lua

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,23 @@ project "CppSharp.Generator"
55
kind "SharedLib"
66
language "C#"
77

8-
files { "**.cs", "**verbs.txt" }
8+
files { "**.cs", "**verbs.txt", path.join(depsdir, "InjectModuleInitializer", "**.cs") }
99
excludes { "Filter.cs" }
1010

11+
libdirs
12+
{
13+
depsdir .. "/Mono.Cecil"
14+
}
15+
1116
links
1217
{
1318
"System",
1419
"System.Core",
1520
"CppSharp",
1621
"CppSharp.AST",
17-
"CppSharp.Parser"
22+
"CppSharp.Parser",
23+
"Mono.Cecil",
24+
"Mono.Cecil.Pdb"
1825
}
1926

2027
SetupParser()

0 commit comments

Comments
 (0)