Skip to content

Commit b6118cd

Browse files
authored
optimize post processor by avoiding unnecessary reading of reference assemblies. (#8)
1 parent c0ffc1c commit b6118cd

File tree

5 files changed

+100
-85
lines changed

5 files changed

+100
-85
lines changed

Assets/Sample/Test.unity

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,6 @@ MonoBehaviour:
257257
references:
258258
version: 1
259259
00000000:
260-
type: {class: ILibInterface`1<System_Int32>/LibGenericObject, ns: <GenericSerializeReference>, asm: Assembly-CSharp}
260+
type: {class: ILibInterface`1<System_Int32>/LibIntObject, ns: <GenericSerializeReference>, asm: Assembly-CSharp}
261261
00000001:
262262
type: {class: ILibInterface`1<System_Single>/LibGenericObject_2, ns: <GenericSerializeReference>, asm: Assembly-CSharp}

Assets/Sample/TestSO.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace GenericSerializeReference.Sample
55
[CreateAssetMenu(fileName = "TestSO", menuName = "TestSO", order = 0)]
66
public class TestSO : ScriptableObject
77
{
8-
[GenericSerializeReference]
8+
[GenericSerializeReference(mode: GenerateMode.Embed)]
99
public MultipleGeneric.IInterface<int, float> IntFloat { get; set; }
1010
}
1111
}

Assets/__.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
namespace GenericSerializeReference.Library.CodeGen
1+
namespace GenericSerializeReference
22
{
3-
internal static class ___ {}
3+
// just make sure there has `AssemblyCSharp.dll`
4+
static class ___ {}
45
}

Packages/generic-serialize-reference/Editor/GenericSerializeReferencePostProcessor.cs

Lines changed: 94 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -28,104 +28,106 @@ public override bool WillProcess(ICompiledAssembly compiledAssembly)
2828

2929
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
3030
{
31-
var logger = new ILPostProcessorLogger(new List<DiagnosticMessage>());
3231
using var resolver = new PostProcessorAssemblyResolver(compiledAssembly.References);
3332
using var assembly = compiledAssembly.LoadAssembly(resolver);
34-
var referenceAssemblies = compiledAssembly.LoadLibraryAssemblies(resolver).ToArray();
35-
try
33+
var logger = assembly.CreateLogger();
34+
logger.Info($"process GenericSerializeReference on {assembly.Name.Name}({string.Join(",", compiledAssembly.References.Where(r => r.StartsWith("Library")))})");
35+
var modified = Process(compiledAssembly, assembly, resolver, logger);
36+
if (!modified) return new ILPostProcessResult(null, logger.Messages);
37+
38+
var pe = new MemoryStream();
39+
var pdb = new MemoryStream();
40+
var writerParameters = new WriterParameters
3641
{
37-
var loggerAttributes = assembly.GetAttributesOf<GenericSerializeReferenceLoggerAttribute>();
38-
if (loggerAttributes.Any()) logger.LogLevel = (LogLevel)loggerAttributes.First().ConstructorArguments[0].Value;
39-
logger.Info($"process GenericSerializeReference on {assembly.Name.Name}({string.Join(",", referenceAssemblies.Select(r => r.Name.Name))})");
40-
var allTypes = referenceAssemblies.Append(assembly)
41-
.Where(asm => !asm.Name.Name.StartsWith("Unity.")
42-
&& !asm.Name.Name.StartsWith("UnityEditor.")
43-
&& !asm.Name.Name.StartsWith("UnityEngine.")
44-
)
45-
.SelectMany(asm => asm.MainModule.GetAllTypes())
46-
;
47-
logger.Debug($"all types: {string.Join(", ", allTypes.Select(t => t.Name))}");
48-
var typeTree = new TypeTree(allTypes);
49-
logger.Debug($"tree: {typeTree}");
50-
var modified = Process(assembly.MainModule, typeTree, logger);
51-
if (!modified) return new ILPostProcessResult(null, logger.Messages);
42+
SymbolWriterProvider = new PortablePdbWriterProvider(), SymbolStream = pdb, WriteSymbols = true
43+
};
44+
assembly.Write(pe, writerParameters);
45+
// assembly.Write();
46+
var inMemoryAssembly = new InMemoryAssembly(pe.ToArray(), pdb.ToArray());
47+
return new ILPostProcessResult(inMemoryAssembly, logger.Messages);
48+
}
5249

53-
var pe = new MemoryStream();
54-
var pdb = new MemoryStream();
55-
var writerParameters = new WriterParameters
56-
{
57-
SymbolWriterProvider = new PortablePdbWriterProvider(), SymbolStream = pdb, WriteSymbols = true
58-
};
59-
assembly.Write(pe, writerParameters);
60-
// assembly.Write();
61-
var inMemoryAssembly = new InMemoryAssembly(pe.ToArray(), pdb.ToArray());
62-
return new ILPostProcessResult(inMemoryAssembly, logger.Messages);
50+
private bool Process(ICompiledAssembly compiledAssembly, AssemblyDefinition assembly, PostProcessorAssemblyResolver resolver, ILPostProcessorLogger logger)
51+
{
52+
var module = assembly.MainModule;
53+
IReadOnlyList<AssemblyDefinition> referenceAssemblies = Array.Empty<AssemblyDefinition>();
54+
TypeTree typeTree = null;
55+
56+
try
57+
{
58+
return ProcessProperties();
6359
}
6460
finally
6561
{
66-
foreach (var reference in referenceAssemblies) reference.Dispose();
62+
foreach (var @ref in referenceAssemblies) @ref.Dispose();
6763
}
68-
}
6964

70-
private bool Process(ModuleDefinition module, TypeTree typeTree, ILPostProcessorLogger logger)
71-
{
72-
var modified = false;
73-
foreach (var (type, property, attribute) in
74-
from type in module.GetAllTypes()
75-
where type.IsClass && !type.IsAbstract
76-
from property in type.Properties.ToArray() // able to change `Properties` during looping
77-
from attribute in property.GetAttributesOf<GenericSerializeReferenceAttribute>()
78-
select (type, property, attribute)
79-
)
65+
bool ProcessProperties()
8066
{
81-
if (property.GetMethod == null)
67+
var modified = false;
68+
foreach (var (type, property, attribute) in
69+
from type in module.GetAllTypes()
70+
where type.IsClass && !type.IsAbstract
71+
from property in type.Properties.ToArray() // able to change `Properties` during looping
72+
from attribute in property.GetAttributesOf<GenericSerializeReferenceAttribute>()
73+
select (type, property, attribute)
74+
)
8275
{
83-
logger.Warning($"Cannot process on property {property} without getter");
84-
continue;
85-
}
76+
if (property.GetMethod == null)
77+
{
78+
logger.Warning($"Cannot process on property {property} without getter");
79+
continue;
80+
}
8681

87-
if (!property.PropertyType.IsGenericInstance)
88-
{
89-
logger.Warning($"Cannot process on property {property} with non-generic type {property.PropertyType.Name}");
90-
continue;
91-
}
82+
if (!property.PropertyType.IsGenericInstance)
83+
{
84+
logger.Warning($"Cannot process on property {property} with non-generic type {property.PropertyType.Name}");
85+
continue;
86+
}
9287

93-
TypeReference baseInterface;
94-
var mode = (GenerateMode)attribute.ConstructorArguments[1].Value;
95-
if (mode == GenerateMode.Embed)
96-
{
97-
var wrapperName = $"<{property.Name}>__generic_serialize_reference";
98-
var wrapper = property.DeclaringType.CreateNestedStaticPrivateClass(wrapperName);
99-
baseInterface = CreateInterface(wrapper);
100-
CreateDerivedClasses(property, wrapper, baseInterface);
101-
}
102-
else
103-
{
104-
baseInterface = module.ImportReference(typeof(IBase));
105-
}
88+
TypeReference baseInterface;
89+
var mode = (GenerateMode)attribute.ConstructorArguments[1].Value;
90+
if (mode == GenerateMode.Embed)
91+
{
92+
var wrapperName = $"<{property.Name}>__generic_serialize_reference";
93+
var wrapper = property.DeclaringType.CreateNestedStaticPrivateClass(wrapperName);
94+
baseInterface = CreateInterface(wrapper);
95+
CreateDerivedClasses(property, wrapper, baseInterface);
96+
}
97+
else
98+
{
99+
baseInterface = module.ImportReference(typeof(IBase));
100+
}
106101

107-
logger.Info($"generate nested class with interface {baseInterface.FullName}");
108-
var fieldNamePrefix = (string)attribute.ConstructorArguments[0].Value;
109-
GenerateField(module, property, baseInterface, fieldNamePrefix);
102+
logger.Info($"generate nested class with interface {baseInterface.FullName}");
103+
var fieldNamePrefix = (string)attribute.ConstructorArguments[0].Value;
104+
GenerateField(module, property, baseInterface, fieldNamePrefix);
110105

111-
modified = true;
106+
modified = true;
107+
}
108+
return modified;
112109
}
113-
return modified;
114110

115-
TypeDefinition CreateInterface(TypeDefinition wrapper, string interfaceName = "IBase")
111+
void CreateTypeTree()
116112
{
117-
// .class interface nested public abstract auto ansi
118-
var interfaceAttributes = TypeAttributes.Class |
119-
TypeAttributes.Interface |
120-
TypeAttributes.NestedPublic |
121-
TypeAttributes.Abstract;
122-
var baseInterface = new TypeDefinition("", interfaceName, interfaceAttributes);
123-
wrapper.NestedTypes.Add(baseInterface);
124-
return baseInterface;
113+
if (typeTree != null) return;
114+
115+
referenceAssemblies = compiledAssembly.LoadLibraryAssemblies(resolver).ToArray();
116+
var allTypes = referenceAssemblies.Append(assembly)
117+
.Where(asm => !asm.Name.Name.StartsWith("Unity.")
118+
&& !asm.Name.Name.StartsWith("UnityEditor.")
119+
&& !asm.Name.Name.StartsWith("UnityEngine.")
120+
)
121+
.SelectMany(asm => asm.MainModule.GetAllTypes())
122+
;
123+
logger.Debug($"all types: {string.Join(", ", allTypes.Select(t => t.Name))}");
124+
typeTree = new TypeTree(allTypes);
125+
logger.Debug($"tree: {typeTree}");
125126
}
126127

127128
void CreateDerivedClasses(PropertyDefinition property, TypeDefinition wrapper, TypeReference baseInterface)
128129
{
130+
if (typeTree == null) CreateTypeTree();
129131
logger.Debug($"get derived {property.PropertyType.Module} {property.PropertyType} {property.PropertyType.Resolve()}");
130132
foreach (var derived in typeTree.GetOrCreateAllDerivedReference(property.PropertyType))
131133
{
@@ -151,7 +153,19 @@ void CreateDerivedClasses(PropertyDefinition property, TypeDefinition wrapper, T
151153
}
152154
}
153155

154-
internal static void GenerateField(
156+
private TypeDefinition CreateInterface(TypeDefinition wrapper, string interfaceName = "IBase")
157+
{
158+
// .class interface nested public abstract auto ansi
159+
var interfaceAttributes = TypeAttributes.Class |
160+
TypeAttributes.Interface |
161+
TypeAttributes.NestedPublic |
162+
TypeAttributes.Abstract;
163+
var baseInterface = new TypeDefinition("", interfaceName, interfaceAttributes);
164+
wrapper.NestedTypes.Add(baseInterface);
165+
return baseInterface;
166+
}
167+
168+
private static void GenerateField(
155169
ModuleDefinition module,
156170
PropertyDefinition property,
157171
TypeReference fieldType,
@@ -162,7 +176,7 @@ internal static void GenerateField(
162176
InjectSetter(property, serializedField);
163177
}
164178

165-
internal static FieldDefinition CreateSerializeReferenceField(
179+
private static FieldDefinition CreateSerializeReferenceField(
166180
ModuleDefinition module,
167181
PropertyDefinition property,
168182
TypeReference @interface,
@@ -183,7 +197,7 @@ internal static FieldDefinition CreateSerializeReferenceField(
183197
return serializedField;
184198
}
185199

186-
internal static void InjectGetter(PropertyDefinition property, FieldDefinition serializedField)
200+
private static void InjectGetter(PropertyDefinition property, FieldDefinition serializedField)
187201
{
188202
// --------add--------
189203
// IL_0000: ldarg.0 // this
@@ -206,7 +220,7 @@ internal static void InjectGetter(PropertyDefinition property, FieldDefinition s
206220
instructions.Insert(4, Instruction.Create(OpCodes.Pop));
207221
}
208222

209-
internal static void InjectSetter(PropertyDefinition property, FieldDefinition serializedField)
223+
private static void InjectSetter(PropertyDefinition property, FieldDefinition serializedField)
210224
{
211225
//IL_0000: ldarg.0 // this
212226
//IL_0001: ldarg.1 // 'value'

Packages/generic-serialize-reference/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "com.quabug.generic-serialize-reference",
33
"description": "Automatically alter generic field of SerializeReference into its non-generic form",
4-
"version": "1.2.0",
4+
"version": "1.2.1",
55
"unity": "2020.2",
66
"displayName": "GenericSerializeReference",
77
"samples": [

0 commit comments

Comments
 (0)