From 525b182acf252fa3759477adacfc385c0dd2faf0 Mon Sep 17 00:00:00 2001 From: quabug Date: Mon, 5 Jul 2021 00:52:38 +0800 Subject: [PATCH] able to generate code into `AssemblyCSharp.dll` which is useful for library design (#7) --- .gitignore | 2 + .../GenericSerializeReference.Sample.asmdef | 4 +- Assets/{_.meta => Sample/Library.meta} | 3 +- ...icSerializeReference.Sample.Library.asmdef | 17 + ...ializeReference.Sample.Library.asmdef.meta | 7 + Assets/Sample/Library/Implement.meta | 8 + ...eReference.Sample.Library.Implement.asmdef | 16 + ...rence.Sample.Library.Implement.asmdef.meta | 7 + Assets/Sample/Library/Implement/Implement.cs | 4 + .../Library/Implement/Implement.cs.meta | 3 + Assets/Sample/Library/Interface.meta | 8 + ...eReference.Sample.Library.Interface.asmdef | 14 + ...rence.Sample.Library.Interface.asmdef.meta | 7 + Assets/Sample/Library/Interface/Interface.cs | 1 + .../Library/Interface/Interface.cs.meta | 3 + Assets/Sample/Library/LibBehavior.cs | 19 ++ Assets/Sample/Library/LibBehavior.cs.meta | 11 + Assets/Sample/MyMonoBehavior.cs | 8 +- Assets/Sample/Test.unity | 35 +- Assets/Sample/TestMonoBehavior.cs | 14 +- Assets/_ | 0 Assets/__.cs | 4 + Assets/__.cs.meta | 11 + .../Editor/AssemblyCSharpPostProcessor.cs | 147 +++++++++ .../AssemblyCSharpPostProcessor.cs.meta | 3 + .../Editor/AssemblyInfo.cs | 3 +- .../Editor/Extensions.cs | 111 ++++++- ...cSerializeReferenceFieldAttributeDrawer.cs | 271 +++++++--------- .../GenericSerializeReferencePostProcessor.cs | 305 +++++++----------- .../Editor/PostProcessorAssemblyResolver.cs | 4 +- .../Editor/TypeTree.cs | 46 ++- .../GenericSerializeReferenceAttribute.cs | 8 +- ...rializeReferenceGeneratedFieldAttribute.cs | 7 +- .../Runtime/IBase.cs | 4 + .../Runtime/IBase.cs.meta | 3 + .../GenericSerializeReference.Tests.asmdef | 2 +- .../Tests~/Test.cs | 24 -- .../Tests~/Test.cs.meta | 3 - .../Tests~/TestTypeTree.cs | 32 +- .../generic-serialize-reference/package.json | 6 +- Packages/manifest.json | 6 +- Packages/packages-lock.json | 25 +- ProjectSettings/EditorBuildSettings.asset | 5 +- ProjectSettings/ProjectSettings.asset | 8 +- ProjectSettings/ProjectVersion.txt | 4 +- ProjectSettings/UnityConnectSettings.asset | 1 + 46 files changed, 796 insertions(+), 438 deletions(-) rename Assets/{_.meta => Sample/Library.meta} (67%) create mode 100644 Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef create mode 100644 Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef.meta create mode 100644 Assets/Sample/Library/Implement.meta create mode 100644 Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef create mode 100644 Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef.meta create mode 100644 Assets/Sample/Library/Implement/Implement.cs create mode 100644 Assets/Sample/Library/Implement/Implement.cs.meta create mode 100644 Assets/Sample/Library/Interface.meta create mode 100644 Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef create mode 100644 Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef.meta create mode 100644 Assets/Sample/Library/Interface/Interface.cs create mode 100644 Assets/Sample/Library/Interface/Interface.cs.meta create mode 100644 Assets/Sample/Library/LibBehavior.cs create mode 100644 Assets/Sample/Library/LibBehavior.cs.meta delete mode 100644 Assets/_ create mode 100644 Assets/__.cs create mode 100644 Assets/__.cs.meta create mode 100644 Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs create mode 100644 Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs.meta create mode 100644 Packages/generic-serialize-reference/Runtime/IBase.cs create mode 100644 Packages/generic-serialize-reference/Runtime/IBase.cs.meta delete mode 100644 Packages/generic-serialize-reference/Tests~/Test.cs delete mode 100644 Packages/generic-serialize-reference/Tests~/Test.cs.meta diff --git a/.gitignore b/.gitignore index 9a9ec55..c580ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,5 @@ Assets/Plugins/Animancer/Examples.meta # OS .DS_Store .vsconfig +Packages/generic-serialize-reference/Test +Packages/generic-serialize-reference/Test.meta diff --git a/Assets/Sample/GenericSerializeReference.Sample.asmdef b/Assets/Sample/GenericSerializeReference.Sample.asmdef index f3ab22d..a727630 100644 --- a/Assets/Sample/GenericSerializeReference.Sample.asmdef +++ b/Assets/Sample/GenericSerializeReference.Sample.asmdef @@ -3,14 +3,14 @@ "rootNamespace": "", "references": [ "GUID:e68bcd447d2cc4be2b6d31311e6ca7e9", - "GUID:7db2c3edd9cbf4a0c84e314cca3ddf42" + "GUID:36fd0010d4685b4408721d3172136183" ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [], - "autoReferenced": false, + "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false diff --git a/Assets/_.meta b/Assets/Sample/Library.meta similarity index 67% rename from Assets/_.meta rename to Assets/Sample/Library.meta index 52ed60a..f40e97a 100644 --- a/Assets/_.meta +++ b/Assets/Sample/Library.meta @@ -1,5 +1,6 @@ fileFormatVersion: 2 -guid: 91abd8f746c824d8eb9cdc0f40251dbc +guid: 1365f989be438cc4582240be1ec876d7 +folderAsset: yes DefaultImporter: externalObjects: {} userData: diff --git a/Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef b/Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef new file mode 100644 index 0000000..8ae996e --- /dev/null +++ b/Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef @@ -0,0 +1,17 @@ +{ + "name": "GenericSerializeReference.Sample.Library", + "rootNamespace": "", + "references": [ + "GUID:e68bcd447d2cc4be2b6d31311e6ca7e9", + "GUID:bb7127c6ea4789d41b77cdef13d93652" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef.meta b/Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef.meta new file mode 100644 index 0000000..49ad1d0 --- /dev/null +++ b/Assets/Sample/Library/GenericSerializeReference.Sample.Library.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b5701f8ff0277214388e4ff5f836be88 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Library/Implement.meta b/Assets/Sample/Library/Implement.meta new file mode 100644 index 0000000..3a6f91b --- /dev/null +++ b/Assets/Sample/Library/Implement.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d9e8371a213f7444cae03c97a9ba85e3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef b/Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef new file mode 100644 index 0000000..4c8784d --- /dev/null +++ b/Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef @@ -0,0 +1,16 @@ +{ + "name": "GenericSerializeReference.Sample.Library.Implement", + "rootNamespace": "", + "references": [ + "GUID:bb7127c6ea4789d41b77cdef13d93652" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef.meta b/Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef.meta new file mode 100644 index 0000000..fa348ee --- /dev/null +++ b/Assets/Sample/Library/Implement/GenericSerializeReference.Sample.Library.Implement.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 36fd0010d4685b4408721d3172136183 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Library/Implement/Implement.cs b/Assets/Sample/Library/Implement/Implement.cs new file mode 100644 index 0000000..b545a2d --- /dev/null +++ b/Assets/Sample/Library/Implement/Implement.cs @@ -0,0 +1,4 @@ +public class LibIntObject : ILibInterface {} +public class LibGenericObject : ILibInterface {} +public class LibGenericObject_2 : ILibInterface {} +public class LibFloatObject : ILibInterface {} diff --git a/Assets/Sample/Library/Implement/Implement.cs.meta b/Assets/Sample/Library/Implement/Implement.cs.meta new file mode 100644 index 0000000..0c188bf --- /dev/null +++ b/Assets/Sample/Library/Implement/Implement.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2bbe89902cff4f4d9d5cda7b54fe71eb +timeCreated: 1624884126 \ No newline at end of file diff --git a/Assets/Sample/Library/Interface.meta b/Assets/Sample/Library/Interface.meta new file mode 100644 index 0000000..f5cb4b6 --- /dev/null +++ b/Assets/Sample/Library/Interface.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bdae992b0433a4146a6a29cc1d4368f0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef b/Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef new file mode 100644 index 0000000..3903225 --- /dev/null +++ b/Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef @@ -0,0 +1,14 @@ +{ + "name": "GenericSerializeReference.Sample.Library.Interface", + "rootNamespace": "", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef.meta b/Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef.meta new file mode 100644 index 0000000..d4922d2 --- /dev/null +++ b/Assets/Sample/Library/Interface/GenericSerializeReference.Sample.Library.Interface.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bb7127c6ea4789d41b77cdef13d93652 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Library/Interface/Interface.cs b/Assets/Sample/Library/Interface/Interface.cs new file mode 100644 index 0000000..943fd81 --- /dev/null +++ b/Assets/Sample/Library/Interface/Interface.cs @@ -0,0 +1 @@ +public interface ILibInterface {} diff --git a/Assets/Sample/Library/Interface/Interface.cs.meta b/Assets/Sample/Library/Interface/Interface.cs.meta new file mode 100644 index 0000000..9dba9f5 --- /dev/null +++ b/Assets/Sample/Library/Interface/Interface.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9d48cc3c31964df1902d2fd8d233ee35 +timeCreated: 1624884110 \ No newline at end of file diff --git a/Assets/Sample/Library/LibBehavior.cs b/Assets/Sample/Library/LibBehavior.cs new file mode 100644 index 0000000..ac957d5 --- /dev/null +++ b/Assets/Sample/Library/LibBehavior.cs @@ -0,0 +1,19 @@ +using System; +using GenericSerializeReference; +using UnityEngine; + +[assembly: GenericSerializeReferenceLoggerAttribute(LogLevel.Debug)] +public class LibBehavior : MonoBehaviour +{ + [GenericSerializeReference(mode: GenerateMode.AssemblyCSharp)] + public ILibInterface Int { get; set; } + + [GenericSerializeReference(mode: GenerateMode.AssemblyCSharp)] + public ILibInterface Float { get; set; } + + private void Awake() + { + Debug.Log($"{nameof(LibBehavior)}.{nameof(Int)} is {Int.GetType()}"); + Debug.Log($"{nameof(LibBehavior)}.{nameof(Float)} is {Float.GetType()}"); + } +} diff --git a/Assets/Sample/Library/LibBehavior.cs.meta b/Assets/Sample/Library/LibBehavior.cs.meta new file mode 100644 index 0000000..fba8bd7 --- /dev/null +++ b/Assets/Sample/Library/LibBehavior.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66e141599402ff44f8de6aef9ccd07f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/MyMonoBehavior.cs b/Assets/Sample/MyMonoBehavior.cs index ac55895..1c735ae 100644 --- a/Assets/Sample/MyMonoBehavior.cs +++ b/Assets/Sample/MyMonoBehavior.cs @@ -1,3 +1,4 @@ +using System; using GenericSerializeReference; using UnityEngine; @@ -7,10 +8,15 @@ public class MyGenericObject : IMyInterface {} public struct StructWillNotShow : IMyInterface {} public class MyMonoBehavior : MonoBehaviour { - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public IMyInterface Value { get; set; } // // [GenericSerializeReference("_serialized")] // public IMyInterface Foo { get; set; } // private int __Foo; + + private void Awake() + { + Debug.Log($"{name}.{nameof(Value)} is {Value.GetType()}"); + } } \ No newline at end of file diff --git a/Assets/Sample/Test.unity b/Assets/Sample/Test.unity index fc49b1b..23d792e 100644 --- a/Assets/Sample/Test.unity +++ b/Assets/Sample/Test.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_IndirectSpecularColor: {r: 0.3731193, g: 0.38073996, b: 0.35872698, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -134,6 +134,7 @@ GameObject: - component: {fileID: 366887100} - component: {fileID: 366887099} - component: {fileID: 366887101} + - component: {fileID: 366887102} m_Layer: 0 m_Name: GameObject m_TagString: Untagged @@ -174,12 +175,11 @@ MonoBehaviour: data: V: 0 00000001: - type: {class: TestMonoBehavior/__generic_serialize_reference/SubObject, ns: GenericSerializeReference.Sample, asm: GenericSerializeReference.Sample} + type: {class: TestMonoBehavior/__generic_serialize_reference/PartialObject, ns: GenericSerializeReference.Sample, asm: GenericSerializeReference.Sample} data: ValueT: 0 ValueU: 0 - SubValueT: [] - SubValueU: + ValueDouble: 0 00000002: type: {class: TestMonoBehavior/__generic_serialize_reference/PartialObject, ns: GenericSerializeReference.Sample, asm: GenericSerializeReference.Sample} data: @@ -191,10 +191,9 @@ MonoBehaviour: data: Value: 0 00000004: - type: {class: TestMonoBehavior/__generic_serialize_reference/SubObject, ns: GenericSerializeReference.Sample, asm: GenericSerializeReference.Sample} + type: {class: TestMonoBehavior/__generic_serialize_reference/DoubleObject, ns: GenericSerializeReference.Sample, asm: GenericSerializeReference.Sample} data: Value: 0 - SubValue: [] 00000005: type: {class: TestMonoBehavior/__generic_serialize_reference/SubObject, ns: GenericSerializeReference.Sample, asm: GenericSerializeReference.Sample} data: @@ -235,11 +234,29 @@ MonoBehaviour: m_EditorClassIdentifier: __Value: id: 0 - _serializedFoo: + references: + version: 1 + 00000000: + type: {class: MyMonoBehavior/__generic_serialize_reference/MyGenericObject, ns: , asm: GenericSerializeReference.Sample} +--- !u!114 &366887102 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 366887098} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 66e141599402ff44f8de6aef9ccd07f2, type: 3} + m_Name: + m_EditorClassIdentifier: + __Int: + id: 0 + __Float: id: 1 references: version: 1 00000000: - type: {class: MyMonoBehavior/__generic_serialize_reference/MyIntObject, ns: , asm: GenericSerializeReference.Sample} + type: {class: ILibInterface`1/LibGenericObject, ns: , asm: Assembly-CSharp} 00000001: - type: {class: MyMonoBehavior/__generic_serialize_reference/MyIntObject, ns: , asm: GenericSerializeReference.Sample} + type: {class: ILibInterface`1/LibGenericObject_2, ns: , asm: Assembly-CSharp} diff --git a/Assets/Sample/TestMonoBehavior.cs b/Assets/Sample/TestMonoBehavior.cs index 9c68d0f..633adeb 100644 --- a/Assets/Sample/TestMonoBehavior.cs +++ b/Assets/Sample/TestMonoBehavior.cs @@ -14,25 +14,25 @@ public class Object : MultipleGeneric.IInterface public int V; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public MultipleGeneric.IInterface IntFloat { get; set; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public MultipleGeneric.IInterface FloatInt { get; set; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public MultipleGeneric.IInterface IntInt { get; set; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public SingleGeneric.IInterface Int { get; set; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public SingleGeneric.IInterface Double { get; set; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public SingleGeneric.Object IntObject { get; set; } - [GenericSerializeReference] + [GenericSerializeReference(mode: GenerateMode.Embed)] public MultipleGeneric.Object IntIntObject { get; set; } private void Awake() diff --git a/Assets/_ b/Assets/_ deleted file mode 100644 index e69de29..0000000 diff --git a/Assets/__.cs b/Assets/__.cs new file mode 100644 index 0000000..0334868 --- /dev/null +++ b/Assets/__.cs @@ -0,0 +1,4 @@ +namespace GenericSerializeReference.Library.CodeGen +{ + internal static class ___ {} +} diff --git a/Assets/__.cs.meta b/Assets/__.cs.meta new file mode 100644 index 0000000..270fe6e --- /dev/null +++ b/Assets/__.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5e51a09192c3854da4df1e7d5e7ac96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs b/Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs new file mode 100644 index 0000000..01b068c --- /dev/null +++ b/Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using JetBrains.Annotations; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using Unity.CompilationPipeline.Common.Diagnostics; +using Unity.CompilationPipeline.Common.ILPostProcessing; +using UnityEngine; + +namespace GenericSerializeReference +{ + public class AssemblyCSharpPostProcessor : ILPostProcessor + { + public override ILPostProcessor GetInstance() + { + return this; + } + + public override bool WillProcess(ICompiledAssembly compiledAssembly) + { + return compiledAssembly.Name == "Assembly-CSharp"; + } + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) + { + var logger = new ILPostProcessorLogger(new List()); + using var resolver = new PostProcessorAssemblyResolver(compiledAssembly.References); + using var assembly = compiledAssembly.LoadAssembly(resolver); + var referenceAssemblies = compiledAssembly.LoadLibraryAssemblies(resolver).ToArray(); + + try + { + var allTypes = referenceAssemblies.Append(assembly) + .Where(asm => !asm.Name.Name.StartsWith("Unity.") && + !asm.Name.Name.StartsWith("UnityEditor.") && + !asm.Name.Name.StartsWith("UnityEngine.") + ) + .SelectMany(asm => asm.MainModule.GetAllTypes()) + .ToArray() + ; + var modified = Process(assembly.MainModule, allTypes, logger); + if (!modified) return new ILPostProcessResult(null, logger.Messages); + + var pe = new MemoryStream(); + var pdb = new MemoryStream(); + var writerParameters = new WriterParameters + { + SymbolWriterProvider = new PortablePdbWriterProvider(), SymbolStream = pdb, WriteSymbols = true + }; + assembly.Write(pe, writerParameters); + return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), logger.Messages); + } + finally + { + foreach (var reference in referenceAssemblies) reference.Dispose(); + } + } + + private bool Process(ModuleDefinition module, IReadOnlyList types, ILPostProcessorLogger logger) + { + var modified = false; + var typeTree = new TypeTree(types); + var wrappers = new Dictionary(); + foreach (var (type, property, attribute) in + from type in types + where type.IsClass && !type.IsAbstract + from property in type.Properties + where property.PropertyType.IsGenericInstance + from attribute in property.GetAttributesOf() + where IsAssemblyCSharpMode(attribute) + select (type, property, attribute) + ) + { + var baseInterface = module.ImportReference(typeof(IBase)); + var baseGeneric = property.PropertyType; + + if (!wrappers.TryGetValue(baseGeneric, out var wrapper)) + { + wrapper = new TypeDefinition( + "" + baseGeneric.Namespace, + baseGeneric.FullName.Replace('.', '_'), + TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.Public | TypeAttributes.BeforeFieldInit + ); + wrappers[baseGeneric] = wrapper; + + foreach (var derived in typeTree + .GetOrCreateAllDerivedReference(baseGeneric) + .Select(module.ImportReference)) + { + var generated = CreateDerived(wrapper, derived, baseInterface); + if (generated != null) + { + wrapper.NestedTypes.Add(generated); + modified = true; + } + } + } + module.Types.Add(wrapper); + + modified = true; + } + return modified; + + TypeDefinition CreateDerived(TypeDefinition wrapper, TypeReference derived, TypeReference baseInterface) + { + var genericArguments = derived.IsGenericInstance + ? ((GenericInstanceType) derived).GenericArguments + : (IEnumerable)Array.Empty() + ; + if (genericArguments.All(arg => !arg.IsGenericParameter)) + { + var className = derived.Name.Split('`')[0]; + if (wrapper.NestedTypes.Any(t => t.Name == className)) + className = derived.Resolve().NameWithOuterClasses(); + // TODO: should handle if the className is still the same with any of existing type. + if (wrapper.NestedTypes.Any(t => t.Name == className)) + logger.Warning($"Overwrite type with same name {className}"); + var classAttributes = TypeAttributes.Class | TypeAttributes.NestedPublic | TypeAttributes.BeforeFieldInit; + var generated = new TypeDefinition("", className, classAttributes); + generated.BaseType = derived.HasGenericParameters ? derived.MakeGenericInstanceType(genericArguments.ToArray()) : derived; + var ctor = module.ImportReference(derived.Resolve().GetConstructors().First(c => !c.HasParameters)).Resolve(); + var ctorCall = new MethodReference(ctor.Name, module.ImportReference(ctor.ReturnType)) + { + DeclaringType = generated.BaseType, + HasThis = ctor.HasThis, + ExplicitThis = ctor.ExplicitThis, + CallingConvention = ctor.CallingConvention, + }; + generated.AddEmptyCtor(ctorCall); + var interfaceImplementation = new InterfaceImplementation(baseInterface); + generated.Interfaces.Add(interfaceImplementation); + return generated; + } + return null; + } + } + + static bool IsAssemblyCSharpMode(CustomAttribute attribute) + { + var mode = (GenerateMode) attribute.ConstructorArguments[1].Value; + return mode == GenerateMode.AssemblyCSharp; + } + } +} \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs.meta b/Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs.meta new file mode 100644 index 0000000..b3f4eb6 --- /dev/null +++ b/Packages/generic-serialize-reference/Editor/AssemblyCSharpPostProcessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 96eeecf641524af79225003122a4fddd +timeCreated: 1625371957 \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Editor/AssemblyInfo.cs b/Packages/generic-serialize-reference/Editor/AssemblyInfo.cs index 7926c74..4a83c9a 100644 --- a/Packages/generic-serialize-reference/Editor/AssemblyInfo.cs +++ b/Packages/generic-serialize-reference/Editor/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("GenericSerializeReference.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("GenericSerializeReference.Tests")] +[assembly: InternalsVisibleTo("Unity.GenericSerializeReference.Library.CodeGen")] diff --git a/Packages/generic-serialize-reference/Editor/Extensions.cs b/Packages/generic-serialize-reference/Editor/Extensions.cs index 777d6a9..65dd1e8 100644 --- a/Packages/generic-serialize-reference/Editor/Extensions.cs +++ b/Packages/generic-serialize-reference/Editor/Extensions.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; +using JetBrains.Annotations; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; +using Unity.CompilationPipeline.Common.Diagnostics; +using Unity.CompilationPipeline.Common.ILPostProcessing; namespace GenericSerializeReference { @@ -34,14 +38,39 @@ public static string ToReadableName(this Type type) } } - internal static class CecilExtension + internal static class PostProcessorExtension { - public static TypeDefinition ToTypeDefinition(this ModuleDefinition module) => - module.ToTypeDefinition(typeof(T)); + public static AssemblyDefinition LoadAssembly(this ICompiledAssembly compiledAssembly, IAssemblyResolver resolver) + { + var symbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData.ToArray()); + var readerParameters = new ReaderParameters + { + SymbolStream = symbolStream, + SymbolReaderProvider = new PortablePdbReaderProvider(), + AssemblyResolver = resolver, + ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(), + ReadingMode = ReadingMode.Immediate, + }; + var peStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PeData.ToArray()); + return AssemblyDefinition.ReadAssembly(peStream, readerParameters); + } - public static TypeDefinition ToTypeDefinition(this ModuleDefinition module, Type type) => - module.ImportReference(type).Resolve(); + public static IEnumerable LoadLibraryAssemblies(this ICompiledAssembly compiledAssembly, PostProcessorAssemblyResolver resolver) + { + return compiledAssembly.References.Where(name => name.StartsWith("Library")).Select(resolver.Resolve); + } + public static ILPostProcessorLogger CreateLogger(this AssemblyDefinition assembly) + { + var logger = new ILPostProcessorLogger(new List()); + var loggerAttributes = assembly.GetAttributesOf(); + if (loggerAttributes.Any()) logger.LogLevel = (LogLevel)loggerAttributes.First().ConstructorArguments[0].Value; + return logger; + } + } + + internal static class CecilExtension + { public static string ToReadableName(this TypeReference type) { if (!type.IsGenericInstance) return type.Name; @@ -160,5 +189,77 @@ public static IEnumerable GetSelfAndAllDeclaringTypes(this TypeD type = type.DeclaringType; } } + + public static CustomAttribute AddCustomAttribute( + this ICustomAttributeProvider attributeProvider + , ModuleDefinition module + , params Type[] constructorTypes + ) where T : Attribute + { + var attribute = new CustomAttribute(module.ImportReference(typeof(T).GetConstructor(constructorTypes))); + attributeProvider.CustomAttributes.Add(attribute); + return attribute; + } + + public static TypeDefinition GenerateDerivedClass(this TypeReference baseType, IEnumerable genericArguments, string className, ModuleDefinition module = null) + { + // .class nested public auto ansi beforefieldinit + // Object + // extends class [GenericSerializeReference.Tests]GenericSerializeReference.Tests.MultipleGeneric/Object`2 + // implements GenericSerializeReference.Tests.TestMonoBehavior/IBase + // { + + // .method public hidebysig specialname rtspecialname instance void + // .ctor() cil managed + // { + // .maxstack 8 + + // IL_0000: ldarg.0 // this + // IL_0001: call instance void class [GenericSerializeReference.Tests]GenericSerializeReference.Tests.MultipleGeneric/Object`2::.ctor() + // IL_0006: nop + // IL_0007: ret + + // } // end of method Object::.ctor + // } // end of class Object + module ??= baseType.Module; + var classAttributes = TypeAttributes.Class | TypeAttributes.NestedPublic | TypeAttributes.BeforeFieldInit; + var type = new TypeDefinition("", className, classAttributes); + type.BaseType = baseType.HasGenericParameters ? baseType.MakeGenericInstanceType(genericArguments.ToArray()) : baseType; + var ctor = module.ImportReference(baseType.Resolve().GetConstructors().First(c => !c.HasParameters)).Resolve(); + var ctorCall = new MethodReference(ctor.Name, module.ImportReference(ctor.ReturnType)) + { + DeclaringType = type.BaseType, + HasThis = ctor.HasThis, + ExplicitThis = ctor.ExplicitThis, + CallingConvention = ctor.CallingConvention, + }; + type.AddEmptyCtor(ctorCall); + return type; + } + + public static TypeDefinition CreateNestedStaticPrivateClass(this TypeDefinition type, string name) + { + // .class nested private abstract sealed auto ansi beforefieldinit + // <$PropertyName>__generic_serialize_reference + // extends [mscorlib]System.Object + var typeAttributes = TypeAttributes.Class | + TypeAttributes.Sealed | + TypeAttributes.Abstract | + TypeAttributes.NestedPrivate | + TypeAttributes.BeforeFieldInit; + var nestedType = new TypeDefinition("", name, typeAttributes); + nestedType.BaseType = type.Module.ImportReference(typeof(object)); + type.NestedTypes.Add(nestedType); + return nestedType; + } + + public static IEnumerable GetAttributesOf([NotNull] this ICustomAttributeProvider provider) where T : Attribute + { + return provider.HasCustomAttributes ? + provider.CustomAttributes.Where(IsAttributeOf) : + Enumerable.Empty(); + + static bool IsAttributeOf(CustomAttribute attribute) => attribute.AttributeType.FullName == typeof(T).FullName; + } } } \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Editor/GenericSerializeReferenceFieldAttributeDrawer.cs b/Packages/generic-serialize-reference/Editor/GenericSerializeReferenceFieldAttributeDrawer.cs index eb45702..845abe2 100644 --- a/Packages/generic-serialize-reference/Editor/GenericSerializeReferenceFieldAttributeDrawer.cs +++ b/Packages/generic-serialize-reference/Editor/GenericSerializeReferenceFieldAttributeDrawer.cs @@ -47,108 +47,155 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten var labelPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(labelPosition, label); - DrawSelectionButtonForManagedReference(property, position); + DrawSelectionButtonForManagedReference(); EditorGUI.PropertyField(position, property, GUIContent.none, true); EditorGUI.EndProperty(); - } - private void DrawSelectionButtonForManagedReference(SerializedProperty property, Rect position, IEnumerable> filters = null) - { - var backgroundColor = new Color(0.1f, 0.55f, 0.9f, 1f); + void DrawSelectionButtonForManagedReference() + { + var backgroundColor = new Color(0.1f, 0.55f, 0.9f, 1f); - var buttonPosition = position; - buttonPosition.x += EditorGUIUtility.labelWidth + 1 * EditorGUIUtility.standardVerticalSpacing; - buttonPosition.width = position.width - EditorGUIUtility.labelWidth - 1 * EditorGUIUtility.standardVerticalSpacing; - buttonPosition.height = EditorGUIUtility.singleLineHeight; + var buttonPosition = position; + buttonPosition.x += EditorGUIUtility.labelWidth + 1 * EditorGUIUtility.standardVerticalSpacing; + buttonPosition.width = position.width - EditorGUIUtility.labelWidth - 1 * EditorGUIUtility.standardVerticalSpacing; + buttonPosition.height = EditorGUIUtility.singleLineHeight; - var storedIndent = EditorGUI.indentLevel; - EditorGUI.indentLevel = 0; - var storedColor = GUI.backgroundColor; - GUI.backgroundColor = backgroundColor; + var storedIndent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + var storedColor = GUI.backgroundColor; + GUI.backgroundColor = backgroundColor; - var names = GetSplitNamesFromTypename(property.managedReferenceFullTypename); - var className = string.IsNullOrEmpty(names.ClassName) ? "Null (Assign)" : names.ClassName; - className = className.Split('/').Last(); - var assemblyName = names.AssemblyName; - if (GUI.Button(buttonPosition, new GUIContent(className, className + " ( "+ assemblyName +" )" ))) - ShowContextMenuForManagedReference(property, filters); + var names = GetSplitNamesFromTypename(property.managedReferenceFullTypename); + var className = string.IsNullOrEmpty(names.ClassName) ? "Null (Assign)" : names.ClassName; + className = className.Split('/').Last(); + var assemblyName = names.AssemblyName; + if (GUI.Button(buttonPosition, new GUIContent(className, className + " ( "+ assemblyName +" )" ))) + ShowContextMenuForManagedReference(); - GUI.backgroundColor = storedColor; - EditorGUI.indentLevel = storedIndent; - } + GUI.backgroundColor = storedColor; + EditorGUI.indentLevel = storedIndent; + } - private (string AssemblyName, string ClassName) GetSplitNamesFromTypename(string typename) - { - if (string.IsNullOrEmpty(typename)) - return ("",""); + (string AssemblyName, string ClassName) GetSplitNamesFromTypename(string typename) + { + if (string.IsNullOrEmpty(typename)) + return ("",""); - var typeSplitString = typename.Split(char.Parse(" ")); - var typeClassName = typeSplitString[1]; - var typeAssemblyName = typeSplitString[0]; - return (typeAssemblyName, typeClassName); - } + var typeSplitString = typename.Split(char.Parse(" ")); + var typeClassName = typeSplitString[1]; + var typeAssemblyName = typeSplitString[0]; + return (typeAssemblyName, typeClassName); + } - private void ShowContextMenuForManagedReference(SerializedProperty property, IEnumerable> filters = null) - { - var context = new GenericMenu(); - FillContextMenu(filters, context, property); - context.ShowAsContext(); - } + void ShowContextMenuForManagedReference() + { + var context = new GenericMenu(); + FillContextMenu(context); + context.ShowAsContext(); + } - private void FillContextMenu(IEnumerable> enumerableFilters, GenericMenu contextMenu, SerializedProperty property) - { - var filters = new List>(); - if (enumerableFilters != null) filters.AddRange(enumerableFilters); + void FillContextMenu(GenericMenu contextMenu) + { + // Adds "Make Null" menu command + contextMenu.AddItem(new GUIContent("Null"), false, SetManagedReferenceToNull()); - // Adds "Make Null" menu command - contextMenu.AddItem(new GUIContent("Null"), false, SetManagedReferenceToNull(property)); + // Collects appropriate types + var appropriateTypes = GetAppropriateTypesForAssigningToManagedReference(); - // Collects appropriate types - var appropriateTypes = GetAppropriateTypesForAssigningToManagedReference(property, filters); + // Adds appropriate types to menu + foreach (var appropriateType in appropriateTypes) + AddItemToContextMenu(appropriateType, contextMenu); + } - // Adds appropriate types to menu - foreach (var appropriateType in appropriateTypes) - AddItemToContextMenu(appropriateType, contextMenu, property); - } + GenericMenu.MenuFunction SetManagedReferenceToNull() + { + return () => + { + property.serializedObject.Update(); + property.managedReferenceValue = null; + property.serializedObject.ApplyModifiedProperties(); + }; + } - private GenericMenu.MenuFunction SetManagedReferenceToNull(SerializedProperty serializedProperty) - { - return () => + void AddItemToContextMenu(Type type, GenericMenu genericMenuContext) + { + // it must have a BaseType + var assemblyName = type.BaseType.Assembly.ToString().Split('(', ',')[0]; + var entryName = type.BaseType.ToReadableName() + " ( " + assemblyName + " )"; + genericMenuContext.AddItem(new GUIContent(entryName), false, AssignNewInstanceCommand, new GenericMenuParameterForAssignInstanceCommand(type, property)); + } + + void AssignNewInstanceCommand(object objectGenericMenuParameter ) { + var parameter = (GenericMenuParameterForAssignInstanceCommand) objectGenericMenuParameter; + var type = parameter.Type; + var property = parameter.Property; + AssignNewInstanceOfTypeToManagedReference(property, type); + } + + object AssignNewInstanceOfTypeToManagedReference(SerializedProperty serializedProperty, Type type) + { + var instance = Activator.CreateInstance(type); + serializedProperty.serializedObject.Update(); - serializedProperty.managedReferenceValue = null; + serializedProperty.managedReferenceValue = instance; serializedProperty.serializedObject.ApplyModifiedProperties(); - }; - } - private void AddItemToContextMenu(Type type, GenericMenu genericMenuContext, SerializedProperty property) - { - // it must have a BaseType - var assemblyName = type.BaseType.Assembly.ToString().Split('(', ',')[0]; - var entryName = type.BaseType.ToReadableName() + " ( " + assemblyName + " )"; - genericMenuContext.AddItem(new GUIContent(entryName), false, AssignNewInstanceCommand, new GenericMenuParameterForAssignInstanceCommand(type, property)); - } + return instance; + } - private void AssignNewInstanceCommand(object objectGenericMenuParameter ) - { - var parameter = (GenericMenuParameterForAssignInstanceCommand) objectGenericMenuParameter; - var type = parameter.Type; - var property = parameter.Property; - AssignNewInstanceOfTypeToManagedReference(property, type); - } + IEnumerable GetAppropriateTypesForAssigningToManagedReference() + { + var fieldType = GetManagedReferenceFieldType(); + return GetAppropriateTypesForAssigningToManagedReferenceOfField(fieldType); + } - private object AssignNewInstanceOfTypeToManagedReference(SerializedProperty serializedProperty, Type type) - { - var instance = Activator.CreateInstance(type); + // Gets real type of managed reference + Type GetManagedReferenceFieldType() + { + var realPropertyType = GetRealTypeFromTypename(property.managedReferenceFieldTypename); + if (realPropertyType != null) + return realPropertyType; - serializedProperty.serializedObject.Update(); - serializedProperty.managedReferenceValue = instance; - serializedProperty.serializedObject.ApplyModifiedProperties(); + Debug.LogError($"Can not get field type of managed reference : {property.managedReferenceFieldTypename}"); + return null; + } - return instance; + // Gets real type of managed reference's field typeName + Type GetRealTypeFromTypename(string stringType) + { + var names = GetSplitNamesFromTypename(stringType); + var realType = Type.GetType($"{names.ClassName}, {names.AssemblyName}"); + return realType; + } + + IEnumerable GetAppropriateTypesForAssigningToManagedReferenceOfField(Type fieldType) + { + var appropriateTypes = new List(); + + var propertyType = ((GenericSerializeReferenceGeneratedFieldAttribute) this.attribute).PropertyType; + // Get and filter all appropriate types + var derivedTypes = TypeCache.GetTypesDerivedFrom(fieldType).Intersect(TypeCache.GetTypesDerivedFrom(propertyType)); + foreach (var type in derivedTypes) + { + // Skips unity engine Objects (because they are not serialized by SerializeReference) + if (type.IsSubclassOf(typeof(UnityEngine.Object))) + continue; + // Skip abstract classes because they should not be instantiated + if (type.IsAbstract) + continue; + // Skip types that has no public empty constructors (activator can not create them) + if (type.IsClass && type.GetConstructor(Type.EmptyTypes) == null) // Structs still can be created (strangely) + continue; + + appropriateTypes.Add(type); + } + + return appropriateTypes; + } } private readonly struct GenericMenuParameterForAssignInstanceCommand @@ -162,77 +209,5 @@ public GenericMenuParameterForAssignInstanceCommand(Type type, SerializedPropert public readonly SerializedProperty Property; public readonly Type Type; } - - private IEnumerable GetAppropriateTypesForAssigningToManagedReference(SerializedProperty property, List> filters = null) - { - var fieldType = GetManagedReferenceFieldType(property); - return GetAppropriateTypesForAssigningToManagedReference(fieldType, filters); - } - - /// Gets real type of managed reference - private Type GetManagedReferenceFieldType(SerializedProperty property) - { - var realPropertyType = GetRealTypeFromTypename(property.managedReferenceFieldTypename); - if (realPropertyType != null) - return realPropertyType; - - Debug.LogError($"Can not get field type of managed reference : {property.managedReferenceFieldTypename}"); - return null; - } - - /// Gets real type of managed reference's field typeName - private Type GetRealTypeFromTypename(string stringType) - { - var names = GetSplitNamesFromTypename(stringType); - var realType = Type.GetType($"{names.ClassName}, {names.AssemblyName}"); - return realType; - } - - private IEnumerable GetAppropriateTypesForAssigningToManagedReference(Type fieldType, List> filters = null) - { - var appropriateTypes = new List(); - - // Get and filter all appropriate types - var derivedTypes = TypeCache.GetTypesDerivedFrom(fieldType); - foreach (var type in derivedTypes) - { - // Skips unity engine Objects (because they are not serialized by SerializeReference) - if (type.IsSubclassOf(typeof(UnityEngine.Object))) - continue; - // Skip abstract classes because they should not be instantiated - if (type.IsAbstract) - continue; - // Skip types that has no public empty constructors (activator can not create them) - if (type.IsClass && type.GetConstructor(Type.EmptyTypes) == null) // Structs still can be created (strangely) - continue; - // Filter types by provided filters if there is ones - if (filters != null && filters.All(f => f == null || f.Invoke(type)) == false) - continue; - - appropriateTypes.Add(type); - } - - return appropriateTypes; - } - // - // private IEnumerable> GetAllBuiltInTypeRestrictions(FieldInfo fieldInfo) - // { - // var result = new List>(); - // - // var attributeObjects = fieldInfo.GetCustomAttributes(false); - // foreach (var attributeObject in attributeObjects) - // { - // switch (attributeObject) - // { - // case SerializeReferenceUIRestrictionIncludeTypes includeTypes: - // result.Add(SerializeReferenceTypeRestrictionFilters.TypeIsSubclassOrEqualOrHasInterface(includeTypes.Types)); - // continue; - // case SerializeReferenceUIRestrictionExcludeTypes excludeTypes: - // result.Add(SerializeReferenceTypeRestrictionFilters.TypeIsNotSubclassOrEqualOrHasInterface(excludeTypes.Types)); - // continue; - // } - // } - // return result; - // } } } \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Editor/GenericSerializeReferencePostProcessor.cs b/Packages/generic-serialize-reference/Editor/GenericSerializeReferencePostProcessor.cs index 6a08553..e5fad54 100644 --- a/Packages/generic-serialize-reference/Editor/GenericSerializeReferencePostProcessor.cs +++ b/Packages/generic-serialize-reference/Editor/GenericSerializeReferencePostProcessor.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using JetBrains.Annotations; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; @@ -30,12 +29,14 @@ public override bool WillProcess(ICompiledAssembly compiledAssembly) public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) { var logger = new ILPostProcessorLogger(new List()); - var (assembly, referenceAssemblies) = LoadAssemblyDefinition(compiledAssembly, name => name.StartsWith("Library/ScriptAssemblies")); - var loggerAttributes = GetAttributesOf(assembly); - if (loggerAttributes.Any()) logger.LogLevel = (LogLevel)loggerAttributes.First().ConstructorArguments[0].Value; - logger.Info($"process GenericSerializeReference on {assembly.Name.Name}({string.Join(",", referenceAssemblies.Select(r => r.Name.Name))})"); + using var resolver = new PostProcessorAssemblyResolver(compiledAssembly.References); + using var assembly = compiledAssembly.LoadAssembly(resolver); + var referenceAssemblies = compiledAssembly.LoadLibraryAssemblies(resolver).ToArray(); try { + var loggerAttributes = assembly.GetAttributesOf(); + if (loggerAttributes.Any()) logger.LogLevel = (LogLevel)loggerAttributes.First().ConstructorArguments[0].Value; + logger.Info($"process GenericSerializeReference on {assembly.Name.Name}({string.Join(",", referenceAssemblies.Select(r => r.Name.Name))})"); var allTypes = referenceAssemblies.Append(assembly) .Where(asm => !asm.Name.Name.StartsWith("Unity.") && !asm.Name.Name.StartsWith("UnityEditor.") @@ -56,11 +57,12 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) SymbolWriterProvider = new PortablePdbWriterProvider(), SymbolStream = pdb, WriteSymbols = true }; assembly.Write(pe, writerParameters); - return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), logger.Messages); + // assembly.Write(); + var inMemoryAssembly = new InMemoryAssembly(pe.ToArray(), pdb.ToArray()); + return new ILPostProcessResult(inMemoryAssembly, logger.Messages); } finally { - assembly.Dispose(); foreach (var reference in referenceAssemblies) reference.Dispose(); } } @@ -72,8 +74,7 @@ private bool Process(ModuleDefinition module, TypeTree typeTree, ILPostProcessor from type in module.GetAllTypes() where type.IsClass && !type.IsAbstract from property in type.Properties.ToArray() // able to change `Properties` during looping - where property.PropertyType.IsGenericInstance - from attribute in GetAttributesOf(property) + from attribute in property.GetAttributesOf() select (type, property, attribute) ) { @@ -83,221 +84,147 @@ from attribute in GetAttributesOf(property) continue; } - var serializedFieldInterface = CreateWrapperClass(property); - logger.Info($"generate nested class with interface {serializedFieldInterface.FullName}"); - var fieldNamePrefix = (string)attribute.ConstructorArguments[0].Value; - var serializedField = CreateSerializeReferenceField(property, serializedFieldInterface, fieldNamePrefix); - InjectGetter(property, serializedField); - InjectSetter(property, serializedField); - modified = true; - } - return modified; - - void InjectGetter(PropertyDefinition property, FieldDefinition serializedField) - { - // --------add-------- - // IL_0000: ldarg.0 // this - // IL_0001: ldfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::_Value - // IL_0006: dup - // IL_0007: brtrue.s IL_0010 - // IL_0009: pop - // --------add-------- - - // IL_000a: ldarg.0 // this - // IL_000b: ldfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::k__BackingField - // IL_0010: ret - if (property.GetMethod == null) return; - var instructions = property.GetMethod.Body.Instructions; - var ret = instructions.Last(i => i.OpCode == OpCodes.Ret); - instructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0)); - instructions.Insert(1, Instruction.Create(OpCodes.Ldfld, serializedField)); - instructions.Insert(2, Instruction.Create(OpCodes.Dup)); - instructions.Insert(3, Instruction.Create(OpCodes.Brtrue_S, ret)); - instructions.Insert(4, Instruction.Create(OpCodes.Pop)); - } + if (!property.PropertyType.IsGenericInstance) + { + logger.Warning($"Cannot process on property {property} with non-generic type {property.PropertyType.Name}"); + continue; + } - void InjectSetter(PropertyDefinition property, FieldDefinition serializedField) - { - //IL_0000: ldarg.0 // this - //IL_0001: ldarg.1 // 'value' - //IL_0002: stfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::'k__BackingField' - // before ret - // -------add------- - // IL_0008: ldarg.0 // this - // IL_0009: ldnull - // IL_000a: stfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::_Value - // -------add------- - //IL_0007: ret - if (property.SetMethod == null) return; - var instructions = property.SetMethod.Body.Instructions; - var retIndex = instructions.FindLastIndexOf(i => i.OpCode == OpCodes.Ret); - instructions.Insert(retIndex + 0, Instruction.Create(OpCodes.Ldarg_0)); - instructions.Insert(retIndex + 1, Instruction.Create(OpCodes.Ldnull)); - instructions.Insert(retIndex + 2, Instruction.Create(OpCodes.Stfld, serializedField)); - } + TypeReference baseInterface; + var mode = (GenerateMode)attribute.ConstructorArguments[1].Value; + if (mode == GenerateMode.Embed) + { + var wrapperName = $"<{property.Name}>__generic_serialize_reference"; + var wrapper = property.DeclaringType.CreateNestedStaticPrivateClass(wrapperName); + baseInterface = CreateInterface(wrapper); + CreateDerivedClasses(property, wrapper, baseInterface); + } + else + { + baseInterface = module.ImportReference(typeof(IBase)); + } - FieldDefinition CreateSerializeReferenceField(PropertyDefinition property, TypeReference @interface, string namePrefix) - { - //.field private class GenericSerializeReference.Tests.TestMonoBehavior/__generic_serialize_reference_GenericInterface__/IBase _GenericInterface - // .custom instance void [UnityEngine.CoreModule]UnityEngine.SerializeReference::.ctor() - // = (01 00 00 00 ) - var serializedField = new FieldDefinition( - $"{namePrefix}{property.Name}" - , FieldAttributes.Private - , @interface - ); - serializedField.CustomAttributes.Add(CreateCustomAttribute()); - serializedField.CustomAttributes.Add(CreateCustomAttribute()); - // var backingField = property.DeclaringType.Fields.First(field => field.Name == $"<{property.Name}>k__BackingField"); - // foreach (var customAttribute in backingField.CustomAttributes) - // serializedField.CustomAttributes.Add(customAttribute); - property.DeclaringType.Fields.Add(serializedField); - logger.Debug($"add field into {property.DeclaringType.FullName}"); - return serializedField; - } + logger.Info($"generate nested class with interface {baseInterface.FullName}"); + var fieldNamePrefix = (string)attribute.ConstructorArguments[0].Value; + GenerateField(module, property, baseInterface, fieldNamePrefix); - CustomAttribute CreateCustomAttribute(params Type[] constructorTypes) where T : Attribute - { - return new CustomAttribute(module.ImportReference(typeof(T).GetConstructor(constructorTypes))); + modified = true; } + return modified; - TypeDefinition/*interface*/ CreateWrapperClass(PropertyDefinition property) + TypeDefinition CreateInterface(TypeDefinition wrapper, string interfaceName = "IBase") { - // .class nested public abstract sealed auto ansi beforefieldinit - // <$PropertyName>__generic_serialize_reference - // extends [mscorlib]System.Object - var typeAttributes = TypeAttributes.Class | - TypeAttributes.Sealed | - TypeAttributes.Abstract | - TypeAttributes.NestedPrivate | - TypeAttributes.BeforeFieldInit; - var wrapper = new TypeDefinition("", $"<{property.Name}>__generic_serialize_reference", typeAttributes); - wrapper.BaseType = property.Module.ImportReference(typeof(System.Object)); - property.DeclaringType.NestedTypes.Add(wrapper); - // .class interface nested public abstract auto ansi var interfaceAttributes = TypeAttributes.Class | TypeAttributes.Interface | TypeAttributes.NestedPublic | TypeAttributes.Abstract; - var baseInterface = new TypeDefinition("", "IBase", interfaceAttributes); + var baseInterface = new TypeDefinition("", interfaceName, interfaceAttributes); wrapper.NestedTypes.Add(baseInterface); - - logger.Debug($"get derived {property.PropertyType.Module} {property.PropertyType} {property.PropertyType.Resolve()}"); - var propertyTypeDefinition = property.PropertyType.Resolve(); - var derivedTypes = typeTree.GetDirectDerived(propertyTypeDefinition); - // TODO: should avoid recursive process with side effect? - // return an enumerable of type reference with info to generate? - foreach (var derivedDef in derivedTypes) RecursiveProcess(derivedDef, property.PropertyType); return baseInterface; + } - void RecursiveProcess(TypeDefinition type, TypeReference baseType) + void CreateDerivedClasses(PropertyDefinition property, TypeDefinition wrapper, TypeReference baseInterface) + { + logger.Debug($"get derived {property.PropertyType.Module} {property.PropertyType} {property.PropertyType.Resolve()}"); + foreach (var derived in typeTree.GetOrCreateAllDerivedReference(property.PropertyType)) { - logger.Info($"create {type}({type.Module.Name}) : {baseType.ToReadableName()}"); - if (!type.IsPublicOrNestedPublic()) return; - var baseCtor = type.GetConstructors().FirstOrDefault(ctor => !ctor.HasParameters); - if (baseCtor == null) return; - - var typeReference = module.ImportReference(type); - IReadOnlyList genericArguments = Array.Empty(); - try - { - genericArguments = type.ResolveGenericArguments(baseType); - } - catch (Exception ex) - { - logger.Debug($"cannot resolve {typeReference.ToReadableName()} : {baseType.ToReadableName()}: {ex}"); - return; - } + var genericArguments = derived.IsGenericInstance + ? ((GenericInstanceType) derived).GenericArguments + : (IEnumerable)Array.Empty() + ; - logger.Debug($"resolve generic arguments: {string.Join(",", genericArguments.Select(a => a.Name))}"); if (genericArguments.All(arg => !arg.IsGenericParameter)) { - - var className = typeReference.Name.Split('`')[0]; + var className = derived.Name.Split('`')[0]; if (wrapper.NestedTypes.Any(t => t.Name == className)) - className = typeReference.Resolve().NameWithOuterClasses(); + className = derived.Resolve().NameWithOuterClasses(); // TODO: should handle if the className is still the same with any of existing type. if (wrapper.NestedTypes.Any(t => t.Name == className)) logger.Warning($"Overwrite type with same name {className}"); - var generated = GenerateDerivedClass(typeReference, genericArguments, className); - logger.Debug($"generate {generated.ToReadableName()} : {baseType.ToReadableName()}"); + var generated = derived.GenerateDerivedClass(genericArguments, className); + logger.Debug($"generate {generated.ToReadableName()}"); generated.Interfaces.Add(new InterfaceImplementation(baseInterface)); wrapper.NestedTypes.Add(generated); } - - baseType = type.HasGenericParameters - ? (TypeReference) type.MakeGenericInstanceType(genericArguments.ToArray()) - : type - ; - logger.Debug($"baseType = {baseType}"); - var derivedTypes = typeTree.GetDirectDerived(type); - foreach (var derivedDef in derivedTypes) RecursiveProcess(derivedDef, baseType); } } + } - TypeDefinition GenerateDerivedClass(TypeReference baseType, IReadOnlyList genericArguments, string className) - { - // .class nested public auto ansi beforefieldinit - // Object - // extends class [GenericSerializeReference.Tests]GenericSerializeReference.Tests.MultipleGeneric/Object`2 - // implements GenericSerializeReference.Tests.TestMonoBehavior/IBase - // { - - // .method public hidebysig specialname rtspecialname instance void - // .ctor() cil managed - // { - // .maxstack 8 - - // IL_0000: ldarg.0 // this - // IL_0001: call instance void class [GenericSerializeReference.Tests]GenericSerializeReference.Tests.MultipleGeneric/Object`2::.ctor() - // IL_0006: nop - // IL_0007: ret - - // } // end of method Object::.ctor - // } // end of class Object - var classAttributes = TypeAttributes.Class | TypeAttributes.NestedPublic | TypeAttributes.BeforeFieldInit; - var type = new TypeDefinition("", className, classAttributes); - type.BaseType = baseType.HasGenericParameters ? baseType.MakeGenericInstanceType(genericArguments.ToArray()) : baseType; - var ctor = baseType.Resolve().GetConstructors().First(c => !c.HasParameters); - var ctorCall = new MethodReference(ctor.Name, module.ImportReference(ctor.ReturnType)) - { - DeclaringType = type.BaseType, - HasThis = ctor.HasThis, - ExplicitThis = ctor.ExplicitThis, - CallingConvention = ctor.CallingConvention, - }; - type.AddEmptyCtor(ctorCall); - return type; - } + internal static void GenerateField( + ModuleDefinition module, + PropertyDefinition property, + TypeReference fieldType, + string fieldNamePrefix) + { + var serializedField = CreateSerializeReferenceField(module, property, fieldType, fieldNamePrefix); + InjectGetter(property, serializedField); + InjectSetter(property, serializedField); } - private static (AssemblyDefinition compiled, AssemblyDefinition[] references) - LoadAssemblyDefinition(ICompiledAssembly compiledAssembly, Func referencePredicate) + internal static FieldDefinition CreateSerializeReferenceField( + ModuleDefinition module, + PropertyDefinition property, + TypeReference @interface, + string namePrefix) { - var resolver = new PostProcessorAssemblyResolver(compiledAssembly.References); - var symbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData.ToArray()); - var readerParameters = new ReaderParameters - { - SymbolStream = symbolStream, - SymbolReaderProvider = new PortablePdbReaderProvider(), - AssemblyResolver = resolver, - ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(), - ReadingMode = ReadingMode.Immediate, - }; - var peStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PeData.ToArray()); - var assembly = AssemblyDefinition.ReadAssembly(peStream, readerParameters); - var referenceAssemblies = compiledAssembly.References.Where(referencePredicate).Select(resolver.Resolve).ToArray(); - return (assembly, referenceAssemblies); + //.field private class GenericSerializeReference.Tests.TestMonoBehavior/__generic_serialize_reference_GenericInterface__/IBase _GenericInterface + // .custom instance void [UnityEngine.CoreModule]UnityEngine.SerializeReference::.ctor() + // = (01 00 00 00 ) + var serializedField = new FieldDefinition( + $"{namePrefix}{property.Name}" + , FieldAttributes.Private + , @interface + ); + serializedField.AddCustomAttribute(module); + var attribute = serializedField.AddCustomAttribute(module, typeof(Type)); + attribute.ConstructorArguments.Add(new CustomAttributeArgument(module.ImportReference(typeof(Type)), property.PropertyType)); + property.DeclaringType.Fields.Add(serializedField); + return serializedField; } - private IEnumerable GetAttributesOf([NotNull] ICustomAttributeProvider provider) where T : Attribute + internal static void InjectGetter(PropertyDefinition property, FieldDefinition serializedField) { - return provider.HasCustomAttributes ? - provider.CustomAttributes.Where(IsAttributeOf) : - Enumerable.Empty(); + // --------add-------- + // IL_0000: ldarg.0 // this + // IL_0001: ldfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::_Value + // IL_0006: dup + // IL_0007: brtrue.s IL_0010 + // IL_0009: pop + // --------add-------- + + // IL_000a: ldarg.0 // this + // IL_000b: ldfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::k__BackingField + // IL_0010: ret + if (property.GetMethod == null) return; + var instructions = property.GetMethod.Body.Instructions; + var ret = instructions.Last(i => i.OpCode == OpCodes.Ret); + instructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0)); + instructions.Insert(1, Instruction.Create(OpCodes.Ldfld, serializedField)); + instructions.Insert(2, Instruction.Create(OpCodes.Dup)); + instructions.Insert(3, Instruction.Create(OpCodes.Brtrue_S, ret)); + instructions.Insert(4, Instruction.Create(OpCodes.Pop)); + } - static bool IsAttributeOf(CustomAttribute attribute) => attribute.AttributeType.FullName == typeof(T).FullName; + internal static void InjectSetter(PropertyDefinition property, FieldDefinition serializedField) + { + //IL_0000: ldarg.0 // this + //IL_0001: ldarg.1 // 'value' + //IL_0002: stfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::'k__BackingField' + // before ret + // -------add------- + // IL_0008: ldarg.0 // this + // IL_0009: ldnull + // IL_000a: stfld class GenericSerializeReference.Tests.SingleGeneric/IInterface`1 GenericSerializeReference.Tests.A::_Value + // -------add------- + //IL_0007: ret + if (property.SetMethod == null) return; + var instructions = property.SetMethod.Body.Instructions; + var retIndex = instructions.FindLastIndexOf(i => i.OpCode == OpCodes.Ret); + instructions.Insert(retIndex + 0, Instruction.Create(OpCodes.Ldarg_0)); + instructions.Insert(retIndex + 1, Instruction.Create(OpCodes.Ldnull)); + instructions.Insert(retIndex + 2, Instruction.Create(OpCodes.Stfld, serializedField)); } + } } \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Editor/PostProcessorAssemblyResolver.cs b/Packages/generic-serialize-reference/Editor/PostProcessorAssemblyResolver.cs index 8400d1c..18fef6d 100644 --- a/Packages/generic-serialize-reference/Editor/PostProcessorAssemblyResolver.cs +++ b/Packages/generic-serialize-reference/Editor/PostProcessorAssemblyResolver.cs @@ -14,9 +14,9 @@ internal class PostProcessorAssemblyResolver : IAssemblyResolver private readonly IDictionary _cache = new Dictionary(); private readonly IReadOnlyList _references; - public PostProcessorAssemblyResolver([NotNull] IReadOnlyList references) + public PostProcessorAssemblyResolver([NotNull] IEnumerable references) { - _references = references; + _references = references.ToArray(); } public void Dispose() diff --git a/Packages/generic-serialize-reference/Editor/TypeTree.cs b/Packages/generic-serialize-reference/Editor/TypeTree.cs index 2f31ec3..fceeeb1 100644 --- a/Packages/generic-serialize-reference/Editor/TypeTree.cs +++ b/Packages/generic-serialize-reference/Editor/TypeTree.cs @@ -4,6 +4,7 @@ using System.Text; using JetBrains.Annotations; using Mono.Cecil; +using Mono.Cecil.Rocks; namespace GenericSerializeReference { @@ -113,12 +114,12 @@ public TypeTree([NotNull] IEnumerable sourceTypes) /// /// Get all derived class type of . - /// Ignore generic type argument if is a generic class with certain type argument. + /// Ignore generic type argument if is a generic class with certain type of arguments. /// /// /// Any type of classes derived from directly or indirectly. /// - public IEnumerable GetAllDerived(TypeDefinition baseType) + public IEnumerable GetAllDerivedDefinition(TypeDefinition baseType) { _typeTreeNodeMap.TryGetValue(new TypeKey(baseType), out var node); if (node == null) throw new ArgumentException($"{baseType} is not part of this tree"); @@ -142,13 +143,52 @@ select type /// /// Any type of classes derived from directly. /// - public IEnumerable GetDirectDerived(TypeDefinition baseType) + public IEnumerable GetDirectDerivedDefinition(TypeDefinition baseType) { _typeTreeNodeMap.TryGetValue(new TypeKey(baseType), out var node); if (node == null) throw new ArgumentException($"{baseType} is not part of this tree"); return node.Subs.Select(sub => sub.Type); } + /// + /// Get all derived class type of . + /// Will make new TypeReference if derived type is generic. + /// + /// + /// including public only classes or also including private ones. + /// Any type of classes derived from directly or indirectly. + public IEnumerable GetOrCreateAllDerivedReference(TypeReference rootType, bool publicOnly = true) + { + return GetDirectDerivedDefinition(rootType.Resolve()).SelectMany(t => RecursiveProcess(t, rootType)); + + IEnumerable RecursiveProcess(TypeDefinition type, TypeReference baseType) + { + if (publicOnly && !type.IsPublicOrNestedPublic()) return Enumerable.Empty(); + var baseCtor = type.GetConstructors().FirstOrDefault(ctor => !ctor.HasParameters); + if (baseCtor == null) return Enumerable.Empty(); + + IReadOnlyList genericArguments = Array.Empty(); + try + { + genericArguments = type.ResolveGenericArguments(baseType); + } + catch + { + // logger.Debug($"cannot resolve {typeReference.ToReadableName()} : {baseType.ToReadableName()}: {ex}"); + return Enumerable.Empty(); + } + + baseType = type.HasGenericParameters + ? (TypeReference) type.MakeGenericInstanceType(genericArguments.ToArray()) + : type + ; + + return baseType.Yield().Concat( + GetDirectDerivedDefinition(type).SelectMany(t => RecursiveProcess(t, baseType)) + ); + } + } + public override string ToString() { var builder = new StringBuilder(); diff --git a/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceAttribute.cs b/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceAttribute.cs index bf66999..520a402 100644 --- a/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceAttribute.cs +++ b/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceAttribute.cs @@ -2,11 +2,13 @@ namespace GenericSerializeReference { + public enum GenerateMode { Embed, AssemblyCSharp } + [AttributeUsage(AttributeTargets.Property)] public class GenericSerializeReferenceAttribute : Attribute { - public string SerializedFieldPrefix { get; } - public GenericSerializeReferenceAttribute(string serializedFieldPrefix = "__") => - SerializedFieldPrefix = serializedFieldPrefix; + public const int FIELD_PREFIX_INDEX = 0; + public const int MODE_PREFIX = 1; + public GenericSerializeReferenceAttribute(string serializedFieldPrefix = "__", GenerateMode mode = GenerateMode.AssemblyCSharp) {} } } \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceGeneratedFieldAttribute.cs b/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceGeneratedFieldAttribute.cs index e70312b..3af2317 100644 --- a/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceGeneratedFieldAttribute.cs +++ b/Packages/generic-serialize-reference/Runtime/GenericSerializeReferenceGeneratedFieldAttribute.cs @@ -3,9 +3,14 @@ using UnityEngine; [assembly: InternalsVisibleTo("Unity."+nameof(GenericSerializeReference)+".CodeGen")] +[assembly: InternalsVisibleTo("GenericSerializeReference.Library.Editor")] namespace GenericSerializeReference { [AttributeUsage(AttributeTargets.Field)] - internal class GenericSerializeReferenceGeneratedFieldAttribute : PropertyAttribute {} + internal class GenericSerializeReferenceGeneratedFieldAttribute : PropertyAttribute + { + public Type PropertyType { get; } + public GenericSerializeReferenceGeneratedFieldAttribute(Type propertyType) => PropertyType = propertyType; + } } diff --git a/Packages/generic-serialize-reference/Runtime/IBase.cs b/Packages/generic-serialize-reference/Runtime/IBase.cs new file mode 100644 index 0000000..a5f7410 --- /dev/null +++ b/Packages/generic-serialize-reference/Runtime/IBase.cs @@ -0,0 +1,4 @@ +namespace GenericSerializeReference +{ + public interface IBase {} +} \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Runtime/IBase.cs.meta b/Packages/generic-serialize-reference/Runtime/IBase.cs.meta new file mode 100644 index 0000000..6b09ab5 --- /dev/null +++ b/Packages/generic-serialize-reference/Runtime/IBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 17e90c5379eb45488acdb0e64b044392 +timeCreated: 1625411010 \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Tests~/GenericSerializeReference.Tests.asmdef b/Packages/generic-serialize-reference/Tests~/GenericSerializeReference.Tests.asmdef index 4597da1..56960fc 100644 --- a/Packages/generic-serialize-reference/Tests~/GenericSerializeReference.Tests.asmdef +++ b/Packages/generic-serialize-reference/Tests~/GenericSerializeReference.Tests.asmdef @@ -15,8 +15,8 @@ "precompiledReferences": [ "nunit.framework.dll", "Mono.Cecil.dll", - "Mono.Cecil.Pdb.dll", "Mono.Cecil.Mdb.dll", + "Mono.Cecil.Pdb.dll", "Mono.Cecil.Rocks.dll" ], "autoReferenced": false, diff --git a/Packages/generic-serialize-reference/Tests~/Test.cs b/Packages/generic-serialize-reference/Tests~/Test.cs deleted file mode 100644 index 3cf8ac8..0000000 --- a/Packages/generic-serialize-reference/Tests~/Test.cs +++ /dev/null @@ -1,24 +0,0 @@ -using NUnit.Framework; - -namespace GenericSerializeReference.Tests -{ - public class A - { - [GenericSerializeReference] public SingleGeneric.IInterface Value { get; set; } - } - - public class Test - { - interface IBase {} - class MultipleGenericObject : MultipleGeneric.Object, IBase {} - - [Test] - public void t() - { - IBase value = null; - // var value = (new MultipleGenericObject()); - var obj = (MultipleGeneric.Object)value; - // var value = (MultipleGenericObject) obj; - } - } -} \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Tests~/Test.cs.meta b/Packages/generic-serialize-reference/Tests~/Test.cs.meta deleted file mode 100644 index 0d96857..0000000 --- a/Packages/generic-serialize-reference/Tests~/Test.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: cb6442db98ce4eee95659bc9970a637b -timeCreated: 1615127797 \ No newline at end of file diff --git a/Packages/generic-serialize-reference/Tests~/TestTypeTree.cs b/Packages/generic-serialize-reference/Tests~/TestTypeTree.cs index 8e5230b..e48985a 100644 --- a/Packages/generic-serialize-reference/Tests~/TestTypeTree.cs +++ b/Packages/generic-serialize-reference/Tests~/TestTypeTree.cs @@ -8,7 +8,6 @@ namespace GenericSerializeReference.Tests public class TestTypeTree : CecilTestBase { private TypeTree _tree; - private ModuleDefinition _module; interface IGeneric {} class TInt : IGeneric {} @@ -20,20 +19,25 @@ class TIntSub : TInt {} protected override void OnSetUp() { - _module = _assemblyDefinition.MainModule; _tree = new TypeTree(_module.GetTypes()); } [Test] public void should_get_derived_from_generic_interface() { - CheckDerived(typeof(IGeneric<,>), typeof(TInt<>), typeof(IntU<>), typeof(IntInt), typeof(TFloat<>), typeof(AnotherTFloat<>), typeof(TIntSub)); + CheckDerivedIgnoreGenericParameters(typeof(IGeneric<,>), typeof(TInt<>), typeof(IntU<>), typeof(IntInt), typeof(TFloat<>), typeof(AnotherTFloat<>), typeof(TIntSub)); } [Test] - public void should_get_derived_from_generic_interface_without_type_constraint() + public void should_get_derived_from_generic_interface_by_ignoring_generic_parameters() { - CheckDerived(typeof(IGeneric), typeof(TInt), typeof(IntU), typeof(IntInt), typeof(TFloat), typeof(AnotherTFloat), typeof(TIntSub)); + CheckDerivedIgnoreGenericParameters(typeof(IGeneric), typeof(TInt), typeof(IntU), typeof(IntInt), typeof(TFloat), typeof(AnotherTFloat), typeof(TIntSub)); + } + + [Test] + public void should_get_derived_from_concrete_generic_interface() + { + CheckDerived(typeof(IGeneric), typeof(TInt), typeof(IntU), typeof(IntInt), typeof(TIntSub)); } interface I {} @@ -118,18 +122,22 @@ public void should_get_derived_from_interface() void CheckDerived(Type @base, params Type[] types) { - var tokens = _tree - .GetAllDerived(_module.ToTypeDefinition(@base)) - .Select(type => type.MetadataToken) + var derivedTypes = _tree + .GetOrCreateAllDerivedReference(_module.ImportReference(@base), publicOnly: false) + .Select(type => type.FullName) .ToArray() ; - IsTokensContains(tokens, types); + Assert.That(derivedTypes, Is.EquivalentTo(types.Select(type => _module.ImportReference(type).FullName))); } - void IsTokensContains(MetadataToken[] tokens, params Type[] types) + void CheckDerivedIgnoreGenericParameters(Type @base, params Type[] types) { - Assert.AreEqual(types.Length, tokens.Length); - foreach (var type in types) Assert.Contains(_module.ToTypeDefinition(type).MetadataToken, tokens); + var tokens = _tree + .GetAllDerivedDefinition(_module.ImportReference(@base).Resolve()) + .Select(type => type.MetadataToken) + .ToArray() + ; + Assert.That(tokens, Is.EquivalentTo(types.Select(type => _module.ImportReference(type).Resolve().MetadataToken))); } } } \ No newline at end of file diff --git a/Packages/generic-serialize-reference/package.json b/Packages/generic-serialize-reference/package.json index 8fa3ba2..1829e1c 100644 --- a/Packages/generic-serialize-reference/package.json +++ b/Packages/generic-serialize-reference/package.json @@ -1,7 +1,7 @@ { "name": "com.quabug.generic-serialize-reference", "description": "Automatically alter generic field of SerializeReference into its non-generic form", - "version": "1.1.1", + "version": "1.2.0", "unity": "2020.2", "displayName": "GenericSerializeReference", "samples": [ @@ -12,7 +12,7 @@ } ], "dependencies": { - "com.unity.nuget.mono-cecil": "0.1.6-preview.2" + "com.unity.nuget.mono-cecil": "1.10.1" }, "type": "library" -} +} \ No newline at end of file diff --git a/Packages/manifest.json b/Packages/manifest.json index 7299c19..62d2220 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -1,9 +1,9 @@ { "dependencies": { - "com.unity.ide.rider": "3.0.5", - "com.unity.ide.visualstudio": "2.0.7", + "com.unity.ide.rider": "3.0.7", + "com.unity.ide.visualstudio": "2.0.9", "com.unity.ide.vscode": "1.2.3", - "com.unity.test-framework": "1.1.24" + "com.unity.test-framework": "1.1.27" }, "scopedRegistries": [ { diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index fadb29f..17bae5a 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -5,7 +5,7 @@ "depth": 0, "source": "embedded", "dependencies": { - "com.unity.nuget.mono-cecil": "0.1.6-preview.2" + "com.unity.nuget.mono-cecil": "1.10.1" } }, "com.unity.ext.nunit": { @@ -16,14 +16,16 @@ "url": "https://packages.unity.com" }, "com.unity.ide.rider": { - "version": "3.0.5", + "version": "3.0.7", "depth": 0, "source": "registry", - "dependencies": {}, + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { - "version": "2.0.7", + "version": "2.0.9", "depth": 0, "source": "registry", "dependencies": { @@ -39,16 +41,14 @@ "url": "https://packages.unity.com" }, "com.unity.nuget.mono-cecil": { - "version": "0.1.6-preview.2", + "version": "1.10.1", "depth": 1, "source": "registry", - "dependencies": { - "nuget.mono-cecil": "0.1.6-preview" - }, + "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.test-framework": { - "version": "1.1.24", + "version": "1.1.27", "depth": 0, "source": "registry", "dependencies": { @@ -58,13 +58,6 @@ }, "url": "https://packages.unity.com" }, - "nuget.mono-cecil": { - "version": "0.1.6-preview", - "depth": 2, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, "com.unity.modules.imgui": { "version": "1.0.0", "depth": 1, diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset index 0147887..cad91f3 100644 --- a/ProjectSettings/EditorBuildSettings.asset +++ b/ProjectSettings/EditorBuildSettings.asset @@ -4,5 +4,8 @@ EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 - m_Scenes: [] + m_Scenes: + - enabled: 1 + path: Assets/Sample/Test.unity + guid: 0a484f69d999d4c6997ce7aa86edc14d m_configObjects: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 9c1f134..8a9eb39 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -93,7 +93,7 @@ PlayerSettings: xboxEnableFitness: 0 visibleInBackground: 1 allowFullscreenSwitch: 1 - fullscreenMode: 1 + fullscreenMode: 0 xboxSpeechDB: 0 xboxEnableHeadOrientation: 0 xboxEnableGuest: 0 @@ -371,6 +371,7 @@ PlayerSettings: switchTitleNames_12: switchTitleNames_13: switchTitleNames_14: + switchTitleNames_15: switchPublisherNames_0: switchPublisherNames_1: switchPublisherNames_2: @@ -386,6 +387,7 @@ PlayerSettings: switchPublisherNames_12: switchPublisherNames_13: switchPublisherNames_14: + switchPublisherNames_15: switchIcons_0: {fileID: 0} switchIcons_1: {fileID: 0} switchIcons_2: {fileID: 0} @@ -401,6 +403,7 @@ PlayerSettings: switchIcons_12: {fileID: 0} switchIcons_13: {fileID: 0} switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} switchSmallIcons_0: {fileID: 0} switchSmallIcons_1: {fileID: 0} switchSmallIcons_2: {fileID: 0} @@ -416,6 +419,7 @@ PlayerSettings: switchSmallIcons_12: {fileID: 0} switchSmallIcons_13: {fileID: 0} switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} switchManualHTML: switchAccessibleURLs: switchLegalInformation: @@ -479,6 +483,8 @@ PlayerSettings: switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0 + switchUseMicroSleepForYield: 1 + switchMicroSleepForYieldTime: 25 ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 4db0bd0..79c71bc 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2020.3.0f1 -m_EditorVersionWithRevision: 2020.3.0f1 (c7b5465681fb) +m_EditorVersion: 2020.3.12f1 +m_EditorVersionWithRevision: 2020.3.12f1 (b3b2c6512326) diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset index fa0b146..6125b30 100644 --- a/ProjectSettings/UnityConnectSettings.asset +++ b/ProjectSettings/UnityConnectSettings.asset @@ -9,6 +9,7 @@ UnityConnectSettings: m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com m_TestInitMode: 0 CrashReportingSettings: m_EventUrl: https://perf-events.cloud.unity3d.com