Skip to content

Commit 0dc0416

Browse files
committed
Added InjectedAttribute to show source and an optional comment for each injected field.
1 parent 95bcde7 commit 0dc0416

File tree

4 files changed

+232
-167
lines changed

4 files changed

+232
-167
lines changed

ModTekSimpleInjector/Injector.cs

Lines changed: 0 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Text.RegularExpressions;
44
using System.Xml.Serialization;
55
using Mono.Cecil;
6-
using FieldAttributes = Mono.Cecil.FieldAttributes;
76

87
namespace ModTekSimpleInjector;
98

@@ -76,170 +75,4 @@ private static void ProcessAdditions(string sourceFile, IAssemblyResolver resolv
7675
}
7776
}
7877
}
79-
80-
private class SimpleInjection
81-
{
82-
private readonly TypeDefinition typeDefinition;
83-
private readonly ModuleDefinition moduleDefinition;
84-
private readonly CustomAttribute customAttribute;
85-
86-
public SimpleInjection(
87-
string sourceFile,
88-
IAssemblyResolver resolver,
89-
Addition addition
90-
) {
91-
Console.WriteLine($"Processing {addition}");
92-
var assemblyName = new AssemblyNameReference(addition.InAssembly, null);
93-
94-
var assemblyDefinition = resolver.Resolve(assemblyName)
95-
?? throw new ArgumentException($"Unable to resolve assembly {addition.InAssembly}");
96-
typeDefinition = assemblyDefinition.MainModule.GetType(addition.ToType)
97-
?? throw new ArgumentException($"Unable to resolve type {addition.ToType} in assembly {addition.InAssembly}");
98-
moduleDefinition = assemblyDefinition.MainModule;
99-
100-
customAttribute = CreateCustomAttribute(sourceFile);
101-
}
102-
103-
internal void InjectField(AddField fieldAddition)
104-
{
105-
var fieldType = ResolveType(fieldAddition.OfType);
106-
var fieldTypeReference = moduleDefinition.ImportReference(fieldType);
107-
108-
var field = new FieldDefinition(fieldAddition.Name, fieldAddition.Attributes, fieldTypeReference);
109-
//field.CustomAttributes.Add(customAttribute); // TODO can't reference non-existing attributes
110-
typeDefinition.Fields.Add(field);
111-
}
112-
113-
internal void InjectEnumConstant(AddEnumConstant enumConstant)
114-
{
115-
const FieldAttributes EnumFieldAttributes =
116-
FieldAttributes.Static
117-
| FieldAttributes.Literal
118-
| FieldAttributes.Public
119-
| FieldAttributes.HasDefault;
120-
var constantValue = enumConstant.Value;
121-
var field = new FieldDefinition(enumConstant.Name, EnumFieldAttributes, typeDefinition)
122-
{
123-
Constant = constantValue
124-
};
125-
//field.CustomAttributes.Add(customAttribute); // TODO can't reference non-existing attributes
126-
typeDefinition.Fields.Add(field);
127-
}
128-
129-
private CustomAttribute CreateCustomAttribute(string source)
130-
{
131-
var attributeType = typeof(SourceAttribute);
132-
var attributeConstructor = moduleDefinition.ImportReference(attributeType.GetConstructor([typeof(string)]));
133-
var attribute = new CustomAttribute(attributeConstructor);
134-
var attributeArgument = new CustomAttributeArgument(moduleDefinition.ImportReference(typeof(string)), source);
135-
attribute.ConstructorArguments.Add(attributeArgument);
136-
return attribute;
137-
}
138-
}
139-
140-
private static Type ResolveType(string typeName)
141-
{
142-
var isArray = typeName.EndsWith("[]");
143-
if (isArray)
144-
{
145-
typeName = typeName[..^2];
146-
}
147-
148-
var genericArgumentsRegex = new Regex("^(.+?)<(.+)>$");
149-
var genericArgumentsMatch = genericArgumentsRegex.Match(typeName);
150-
Type[] genericArgumentsTypes;
151-
if (genericArgumentsMatch.Success)
152-
{
153-
var genericArgumentsString = genericArgumentsMatch.Groups[2].Value;
154-
var genericArgumentsStrings = genericArgumentsString.Split(',');
155-
typeName = genericArgumentsMatch.Groups[1].Value;
156-
typeName += "`" + genericArgumentsStrings.Length;
157-
genericArgumentsTypes = new Type[genericArgumentsStrings.Length];
158-
for (var i = 0; i < genericArgumentsTypes.Length; i++)
159-
{
160-
genericArgumentsTypes[i] = ResolveType(genericArgumentsStrings[i]);
161-
}
162-
}
163-
else
164-
{
165-
genericArgumentsTypes = null;
166-
}
167-
168-
// we only support mscorlib classes for now
169-
var fieldType = typeof(int).Assembly.GetType(typeName);
170-
171-
if (genericArgumentsTypes != null)
172-
{
173-
fieldType = fieldType.MakeGenericType(genericArgumentsTypes);
174-
}
175-
if (isArray)
176-
{
177-
fieldType = fieldType.MakeArrayType();
178-
}
179-
return fieldType;
180-
}
181-
}
182-
183-
[AttributeUsage(AttributeTargets.Field)]
184-
public class SourceAttribute : Attribute
185-
{
186-
public SourceAttribute(string source)
187-
{
188-
}
189-
}
190-
191-
// XmlSerializer requires the following classes to be public
192-
193-
[XmlRoot(ElementName = "ModTekSimpleInjector")]
194-
public class Additions
195-
{
196-
[XmlElement(ElementName = "AddField")]
197-
public AddField[] AddField = [];
198-
[XmlElement(ElementName = "AddEnumConstant")]
199-
public AddEnumConstant[] AddEnumConstant = [];
200-
}
201-
202-
public abstract class Addition
203-
{
204-
[XmlAttribute("InAssembly")]
205-
public string InAssembly;
206-
[XmlAttribute("ToType")]
207-
public string ToType;
208-
209-
public override string ToString()
210-
{
211-
return $"{this.GetType().Name}:{InAssembly}:{ToType}";
212-
}
213-
}
214-
215-
[XmlType("AddField")]
216-
[XmlRoot(ElementName = "AddField")]
217-
public class AddField : Addition
218-
{
219-
[XmlAttribute("Name")]
220-
public string Name;
221-
[XmlAttribute("OfType")]
222-
public string OfType;
223-
[XmlAttribute("Attributes")]
224-
public FieldAttributes Attributes = FieldAttributes.Private;
225-
226-
public override string ToString()
227-
{
228-
return $"{base.ToString()}:{Name}:{OfType}:{Attributes}";
229-
}
230-
}
231-
232-
[XmlType("AddEnumConstant")]
233-
[XmlRoot(ElementName = "AddEnumConstant")]
234-
public class AddEnumConstant : Addition
235-
{
236-
[XmlAttribute("Name")]
237-
public string Name;
238-
[XmlAttribute("Value")]
239-
public int Value;
240-
241-
public override string ToString()
242-
{
243-
return $"{base.ToString()}:{Name}:{Value}";
244-
}
24578
}

ModTekSimpleInjector/ModTekSimpleInjector.Example.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ Do not rely on fields added by other mods, unless explicitly advised to do so.
4848
<AddField ToType="HBS.Logging.Logger" InAssembly="Assembly-CSharp"
4949
Name="exampleField5" OfType="System.Char[]" />
5050

51+
<!-- Comments can be added that appear via attributes to the field -->
52+
<AddField ToType="HBS.Logging.Logger" InAssembly="Assembly-CSharp"
53+
Name="exampleField6" OfType="System.SByte" Attributes="Public"
54+
Comment="This field is public for the json de-/serializer, do not use!" />
55+
5156
<!--
5257
AddEnumConstant adds an enum constant to an existing enum type.
5358
Multiple enum constants with the same value can be added, they should behave like aliases. This is usually unwanted.
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
using Mono.Cecil;
5+
using Mono.Cecil.Rocks;
6+
7+
namespace ModTekSimpleInjector;
8+
9+
internal class SimpleInjection
10+
{
11+
private readonly TypeDefinition _typeDefinition;
12+
private readonly ModuleDefinition _moduleDefinition;
13+
private readonly CustomAttribute _customAttribute;
14+
15+
public SimpleInjection(
16+
string sourceFile,
17+
IAssemblyResolver resolver,
18+
Addition addition
19+
) {
20+
Console.WriteLine($"Processing {addition}");
21+
var assemblyName = new AssemblyNameReference(addition.InAssembly, null);
22+
23+
var assemblyDefinition = resolver.Resolve(assemblyName)
24+
?? throw new ArgumentException($"Unable to resolve assembly {addition.InAssembly}");
25+
_typeDefinition = assemblyDefinition.MainModule.GetType(addition.ToType)
26+
?? throw new ArgumentException($"Unable to resolve type {addition.ToType} in assembly {addition.InAssembly}");
27+
_moduleDefinition = assemblyDefinition.MainModule;
28+
29+
_customAttribute = CreateCustomAttribute(_moduleDefinition,"ModTekSimpleInjector", "InjectedAttribute", [
30+
new ParameterInfo<string>("source", sourceFile),
31+
new ParameterInfo<string>("comment", addition.Comment)
32+
]);
33+
}
34+
35+
internal void InjectField(AddField fieldAddition)
36+
{
37+
var fieldType = ResolveType(fieldAddition.OfType);
38+
var fieldTypeReference = _moduleDefinition.ImportReference(fieldType);
39+
var fieldDefinition = new FieldDefinition(fieldAddition.Name, fieldAddition.Attributes, fieldTypeReference);
40+
fieldDefinition.CustomAttributes.Add(_customAttribute);
41+
_typeDefinition.Fields.Add(fieldDefinition);
42+
}
43+
44+
internal void InjectEnumConstant(AddEnumConstant enumConstant)
45+
{
46+
const FieldAttributes EnumFieldAttributes =
47+
FieldAttributes.Static
48+
| FieldAttributes.Literal
49+
| FieldAttributes.Public
50+
| FieldAttributes.HasDefault;
51+
var fieldDefinition = new FieldDefinition(enumConstant.Name, EnumFieldAttributes, _typeDefinition)
52+
{
53+
Constant = enumConstant.Value
54+
};
55+
fieldDefinition.CustomAttributes.Add(_customAttribute);
56+
_typeDefinition.Fields.Add(fieldDefinition);
57+
}
58+
59+
private static CustomAttribute CreateCustomAttribute(
60+
ModuleDefinition moduleDefinition,
61+
string @namespace,
62+
string name,
63+
IParameterInfo[] parameters
64+
) {
65+
var attributeTypeDefinition = moduleDefinition.GetType(@namespace, name);
66+
if (attributeTypeDefinition == null)
67+
{
68+
var baseType = moduleDefinition.ImportReference(typeof(Attribute));
69+
attributeTypeDefinition = new TypeDefinition(
70+
@namespace,
71+
name,
72+
TypeAttributes.NotPublic | TypeAttributes.Sealed,
73+
baseType
74+
);
75+
const MethodAttributes CtorAttributes =
76+
MethodAttributes.Public
77+
| MethodAttributes.HideBySig
78+
| MethodAttributes.SpecialName
79+
| MethodAttributes.RTSpecialName;
80+
var methodDefinition = new MethodDefinition(".ctor", CtorAttributes, moduleDefinition.TypeSystem.Void);
81+
// if not wanting CustomAttributeNamedArgument, one can use:
82+
// foreach (var parameter in parameters)
83+
// {
84+
// var parameterDefinition = new ParameterDefinition(
85+
// parameter.name,
86+
// ParameterAttributes.None,
87+
// moduleDefinition.ImportReference(parameter.type)
88+
// );
89+
// methodDefinition.Parameters.Add(parameterDefinition);
90+
// }
91+
attributeTypeDefinition.Methods.Add(methodDefinition);
92+
moduleDefinition.Types.Add(attributeTypeDefinition);
93+
}
94+
var attributeConstructor = attributeTypeDefinition.GetConstructors().First();
95+
var attribute = new CustomAttribute(attributeConstructor);
96+
foreach (var parameter in parameters)
97+
{
98+
if (parameter.Value is null)
99+
{
100+
continue;
101+
}
102+
var attributeArgument = new CustomAttributeArgument(
103+
moduleDefinition.ImportReference(parameter.Type),
104+
parameter.Value
105+
);
106+
var attributeNamedArgument = new CustomAttributeNamedArgument(parameter.Name, attributeArgument);
107+
attribute.Fields.Add(attributeNamedArgument);
108+
}
109+
return attribute;
110+
}
111+
private record ParameterInfo<T>(string Name, T value) : IParameterInfo
112+
{
113+
public Type Type => typeof(T);
114+
public object Value => value;
115+
}
116+
private interface IParameterInfo
117+
{
118+
string Name { get; }
119+
object Value { get; }
120+
Type Type { get; }
121+
}
122+
123+
private static Type ResolveType(string typeName)
124+
{
125+
var isArray = typeName.EndsWith("[]");
126+
if (isArray)
127+
{
128+
typeName = typeName[..^2];
129+
}
130+
131+
var genericArgumentsRegex = new Regex("^(.+?)<(.+)>$");
132+
var genericArgumentsMatch = genericArgumentsRegex.Match(typeName);
133+
Type[] genericArgumentsTypes;
134+
if (genericArgumentsMatch.Success)
135+
{
136+
var genericArgumentsString = genericArgumentsMatch.Groups[2].Value;
137+
var genericArgumentsStrings = genericArgumentsString.Split(',');
138+
typeName = genericArgumentsMatch.Groups[1].Value;
139+
typeName += "`" + genericArgumentsStrings.Length;
140+
genericArgumentsTypes = new Type[genericArgumentsStrings.Length];
141+
for (var i = 0; i < genericArgumentsTypes.Length; i++)
142+
{
143+
genericArgumentsTypes[i] = ResolveType(genericArgumentsStrings[i]);
144+
}
145+
}
146+
else
147+
{
148+
genericArgumentsTypes = null;
149+
}
150+
151+
// TODO fix msbuild and unity mono supporting different classes in mscorlib!
152+
// we only support mscorlib classes for now
153+
var fieldType = typeof(int).Assembly.GetType(typeName);
154+
155+
if (genericArgumentsTypes != null)
156+
{
157+
fieldType = fieldType.MakeGenericType(genericArgumentsTypes);
158+
}
159+
if (isArray)
160+
{
161+
fieldType = fieldType.MakeArrayType();
162+
}
163+
return fieldType;
164+
}
165+
}

0 commit comments

Comments
 (0)