Skip to content

Commit

Permalink
Added generics support to SimpleInjector.
Browse files Browse the repository at this point in the history
  • Loading branch information
CptMoore committed Jan 27, 2025
1 parent 39d63af commit dc995bc
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
38 changes: 35 additions & 3 deletions ModTekSimpleInjector/Injector.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using Mono.Cecil;
using FieldAttributes = Mono.Cecil.FieldAttributes;
Expand Down Expand Up @@ -33,14 +34,19 @@ public static void Inject(IAssemblyResolver resolver)
var files = Directory.GetFiles(baseDirectory, "ModTekSimpleInjector.*.xml");
Array.Sort(files);
var serializer = new XmlSerializer(typeof(Additions));
// find a valid class name character in front of "<" and assume its part of the ofType expression
// in XML < is invalid in attribute values, Rider does not care though
var greaterThanFix = new Regex(@"(?<=[\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Nl}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Cf}])<");
foreach (var file in files)
{
if (file.EndsWith("ModTekSimpleInjector.Example.xml"))
{
continue;
}
Console.WriteLine($"Processing additions in file {file}");
using var reader = new StreamReader(file);
var xml = File.ReadAllText(file);
var sanitized = greaterThanFix.Replace(xml, "&lt;");
using var reader = new StringReader(sanitized);
var additions = (Additions)serializer.Deserialize(reader);
ProcessAdditions(resolver, additions);
}
Expand Down Expand Up @@ -104,15 +110,41 @@ private static void ProcessAddEnumConstant(TypeDefinition type, AddEnumConstant
type.Fields.Add(field);
}

private static Type ResolveType(string additionType)
private static Type ResolveType(string typeName)
{
var typeName = additionType;
var isArray = typeName.EndsWith("[]");
if (isArray)
{
typeName = typeName[..^2];
}

var genericArgumentsRegex = new Regex("^(.+?)<(.+)>$");
var genericArgumentsMatch = genericArgumentsRegex.Match(typeName);
Type[] genericArgumentsTypes;
if (genericArgumentsMatch.Success)
{
var genericArgumentsString = genericArgumentsMatch.Groups[2].Value;
var genericArgumentsStrings = genericArgumentsString.Split(',');
typeName = genericArgumentsMatch.Groups[1].Value;
typeName += "`" + genericArgumentsStrings.Length;
genericArgumentsTypes = new Type[genericArgumentsStrings.Length];
for (var i = 0; i < genericArgumentsTypes.Length; i++)
{
genericArgumentsTypes[i] = ResolveType(genericArgumentsStrings[i]);
}
}
else
{
genericArgumentsTypes = null;
}

// we only support mscorlib classes for now
var fieldType = typeof(int).Assembly.GetType(typeName);

if (genericArgumentsTypes != null)
{
fieldType = fieldType.MakeGenericType(genericArgumentsTypes);
}
if (isArray)
{
fieldType = fieldType.MakeArrayType();
Expand Down
15 changes: 10 additions & 5 deletions ModTekSimpleInjector/ModTekSimpleInjector.Example.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,22 @@ Do not rely on fields added by other mods, unless explicitly advised to do so.
Fields can be of any types found in mscorlib.
Keywords (e.g. byte) are not supported, use the types full name (e.g. System.Byte).
For custom types use `System.Object` and `Unsafe.As<object, MyType>(ref addedField)` for high performance access.
A `[]` suffix means that the type should be an array of the specified type.
A `[]` suffix means that the specified type should be an array.
-->
<AddField ToType="HBS.Logging.Logger/LogImpl" InAssembly="Assembly-CSharp"
Name="nameAsBytes2" OfType="System.Byte[]" />
Name="exampleField" OfType="System.String" />

<!-- Generics are supported. No whitespaces allowed. -->
<AddField ToType="HBS.Logging.Logger" InAssembly="Assembly-CSharp"
Name="exampleField2" OfType="System.Nullable<System.Int32>" />

<!-- AddField and Attributes
Added fields are private by default, using other attributes is **strongly** discouraged.
Publicizers allow to efficiently access private fields anyway.
Publicizers allow to efficiently access private fields.
Static fields can be defined in a mod and do not require to be injected.
-->
<AddField ToType="HBS.Logging.Logger/LogImpl" InAssembly="Assembly-CSharp"
Name="secondaryName" OfType="System.String" Attributes="Public" />
Name="exampleField3" OfType="System.String" Attributes="Public" />

<!-- AddEnumConstant
AddEnumConstant adds an enum constant to an existing enum type.
Expand All @@ -38,5 +43,5 @@ Do not rely on fields added by other mods, unless explicitly advised to do so.
Enums are just ints and one can convert to and from enums using ints, other mods might already be using an int value you are targeting.
-->
<AddEnumConstant ToType="HBS.Logging.LogLevel" InAssembly="Assembly-CSharp"
Name="Trace" Value="200" />
Name="exampleEnumConstant" Value="100" />
</ModTekSimpleInjector>

0 comments on commit dc995bc

Please sign in to comment.