clean up TypeDefinitionConverter code

This commit is contained in:
Perfare 2021-06-26 13:16:12 +08:00
parent 77a0c9c40a
commit d963d71b12
14 changed files with 1104 additions and 62 deletions

View File

@ -34,6 +34,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Mono.Cecil, Version=0.11.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.11.3\lib\net40\Mono.Cecil.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
@ -43,15 +46,6 @@
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Unity.Cecil">
<HintPath>Libraries\Unity.Cecil.dll</HintPath>
</Reference>
<Reference Include="Unity.CecilTools">
<HintPath>Libraries\Unity.CecilTools.dll</HintPath>
</Reference>
<Reference Include="Unity.SerializationLogic">
<HintPath>Libraries\Unity.SerializationLogic.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AssemblyLoader.cs" /> <Compile Include="AssemblyLoader.cs" />
@ -85,6 +79,14 @@
<Compile Include="SpriteHelper.cs" /> <Compile Include="SpriteHelper.cs" />
<Compile Include="Texture2DConverter.cs" /> <Compile Include="Texture2DConverter.cs" />
<Compile Include="Texture2DExtensions.cs" /> <Compile Include="Texture2DExtensions.cs" />
<Compile Include="Unity.CecilTools\CecilUtils.cs" />
<Compile Include="Unity.CecilTools\ElementType.cs" />
<Compile Include="Unity.CecilTools\Extensions\MethodDefinitionExtensions.cs" />
<Compile Include="Unity.CecilTools\Extensions\ResolutionExtensions.cs" />
<Compile Include="Unity.CecilTools\Extensions\TypeDefinitionExtensions.cs" />
<Compile Include="Unity.CecilTools\Extensions\TypeReferenceExtensions.cs" />
<Compile Include="Unity.SerializationLogic\UnityEngineTypePredicates.cs" />
<Compile Include="Unity.SerializationLogic\UnitySerializationLogic.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj"> <ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj">
@ -104,5 +106,8 @@
<Name>Texture2DDecoderWrapper</Name> <Name>Texture2DDecoderWrapper</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -26,22 +26,22 @@ namespace AssetStudio
{ {
var nodes = new List<TypeTreeNode>(); var nodes = new List<TypeTreeNode>();
Stack<TypeReference> baseTypes = new Stack<TypeReference>(); var baseTypes = new Stack<TypeReference>();
TypeReference baseType = TypeDef.BaseType; var lastBaseType = TypeDef.BaseType;
while (!UnitySerializationLogic.IsNonSerialized(baseType)) while (!UnitySerializationLogic.IsNonSerialized(lastBaseType))
{ {
GenericInstanceType genericInstanceType = baseType as GenericInstanceType; var genericInstanceType = lastBaseType as GenericInstanceType;
if (genericInstanceType != null) if (genericInstanceType != null)
{ {
TypeResolver.Add(genericInstanceType); TypeResolver.Add(genericInstanceType);
} }
baseTypes.Push(baseType); baseTypes.Push(lastBaseType);
baseType = baseType.Resolve().BaseType; lastBaseType = lastBaseType.Resolve().BaseType;
} }
while (baseTypes.Count > 0) while (baseTypes.Count > 0)
{ {
TypeReference typeReference = baseTypes.Pop(); var typeReference = baseTypes.Pop();
TypeDefinition typeDefinition = typeReference.Resolve(); var typeDefinition = typeReference.Resolve();
foreach (var fieldDefinition in typeDefinition.Fields.Where(WillUnitySerialize)) foreach (var fieldDefinition in typeDefinition.Fields.Where(WillUnitySerialize))
{ {
if (!IsHiddenByParentClass(baseTypes, fieldDefinition, TypeDef)) if (!IsHiddenByParentClass(baseTypes, fieldDefinition, TypeDef))
@ -50,15 +50,15 @@ namespace AssetStudio
} }
} }
var genericInstanceType2 = typeReference as GenericInstanceType; var genericInstanceType = typeReference as GenericInstanceType;
if (genericInstanceType2 != null) if (genericInstanceType != null)
{ {
TypeResolver.Remove(genericInstanceType2); TypeResolver.Remove(genericInstanceType);
} }
} }
foreach (FieldDefinition fieldDefinition2 in FilteredFields()) foreach (var field in FilteredFields())
{ {
nodes.AddRange(ProcessingFieldRef(fieldDefinition2)); nodes.AddRange(ProcessingFieldRef(field));
} }
return nodes; return nodes;
@ -66,75 +66,60 @@ namespace AssetStudio
private bool WillUnitySerialize(FieldDefinition fieldDefinition) private bool WillUnitySerialize(FieldDefinition fieldDefinition)
{ {
bool result;
try try
{ {
TypeReference typeReference = TypeResolver.Resolve(fieldDefinition.FieldType); var resolvedFieldType = TypeResolver.Resolve(fieldDefinition.FieldType);
if (UnitySerializationLogic.ShouldNotTryToResolve(typeReference)) if (UnitySerializationLogic.ShouldNotTryToResolve(resolvedFieldType))
{ {
result = false; return false;
} }
else if (!UnityEngineTypePredicates.IsUnityEngineObject(resolvedFieldType))
{ {
if (!UnityEngineTypePredicates.IsUnityEngineObject(typeReference)) if (resolvedFieldType.FullName == fieldDefinition.DeclaringType.FullName)
{ {
if (typeReference.FullName == fieldDefinition.DeclaringType.FullName) return false;
{
return false;
}
} }
result = UnitySerializationLogic.WillUnitySerialize(fieldDefinition, TypeResolver);
} }
return UnitySerializationLogic.WillUnitySerialize(fieldDefinition, TypeResolver);
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new Exception(string.Format("Exception while processing {0} {1}, error {2}", fieldDefinition.FieldType.FullName, fieldDefinition.FullName, ex.Message)); throw new Exception(string.Format("Exception while processing {0} {1}, error {2}", fieldDefinition.FieldType.FullName, fieldDefinition.FullName, ex.Message));
} }
return result;
} }
private static bool IsHiddenByParentClass(IEnumerable<TypeReference> parentTypes, FieldDefinition fieldDefinition, TypeDefinition processingType) private static bool IsHiddenByParentClass(IEnumerable<TypeReference> parentTypes, FieldDefinition fieldDefinition, TypeDefinition processingType)
{ {
return processingType.Fields.Any((FieldDefinition f) => f.Name == fieldDefinition.Name) || parentTypes.Any((TypeReference t) => t.Resolve().Fields.Any((FieldDefinition f) => f.Name == fieldDefinition.Name)); return processingType.Fields.Any(f => f.Name == fieldDefinition.Name) || parentTypes.Any(t => t.Resolve().Fields.Any(f => f.Name == fieldDefinition.Name));
} }
private IEnumerable<FieldDefinition> FilteredFields() private IEnumerable<FieldDefinition> FilteredFields()
{ {
foreach (var f in TypeDef.Fields.Where(WillUnitySerialize)) return TypeDef.Fields.Where(WillUnitySerialize).Where(f =>
{ UnitySerializationLogic.IsSupportedCollection(f.FieldType) ||
if (UnitySerializationLogic.IsSupportedCollection(f.FieldType) || !f.FieldType.IsGenericInstance || UnitySerializationLogic.ShouldImplementIDeserializable(f.FieldType.Resolve())) !f.FieldType.IsGenericInstance ||
{ UnitySerializationLogic.ShouldImplementIDeserializable(f.FieldType.Resolve()));
yield return f;
}
}
yield break;
} }
private FieldReference ResolveGenericFieldReference(FieldReference fieldRef) private FieldReference ResolveGenericFieldReference(FieldReference fieldRef)
{ {
FieldReference field = new FieldReference(fieldRef.Name, fieldRef.FieldType, ResolveDeclaringType(fieldRef.DeclaringType)); var field = new FieldReference(fieldRef.Name, fieldRef.FieldType, ResolveDeclaringType(fieldRef.DeclaringType));
return TypeDef.Module.ImportReference(field); return TypeDef.Module.ImportReference(field);
} }
private TypeReference ResolveDeclaringType(TypeReference declaringType) private TypeReference ResolveDeclaringType(TypeReference declaringType)
{ {
TypeDefinition typeDefinition = declaringType.Resolve(); var typeDefinition = declaringType.Resolve();
TypeReference result;
if (typeDefinition == null || !typeDefinition.HasGenericParameters) if (typeDefinition == null || !typeDefinition.HasGenericParameters)
{ {
result = typeDefinition; return typeDefinition;
} }
else var genericInstanceType = new GenericInstanceType(typeDefinition);
foreach (var genericParameter in typeDefinition.GenericParameters)
{ {
GenericInstanceType genericInstanceType = new GenericInstanceType(typeDefinition); genericInstanceType.GenericArguments.Add(genericParameter);
foreach (GenericParameter item in typeDefinition.GenericParameters)
{
genericInstanceType.GenericArguments.Add(item);
}
result = TypeResolver.Resolve(genericInstanceType);
} }
return result; return TypeResolver.Resolve(genericInstanceType);
} }
private List<TypeTreeNode> ProcessingFieldRef(FieldReference fieldDef) private List<TypeTreeNode> ProcessingFieldRef(FieldReference fieldDef)
@ -155,7 +140,6 @@ namespace AssetStudio
private static bool RequiresAlignment(TypeReference typeRef) private static bool RequiresAlignment(TypeReference typeRef)
{ {
bool result;
switch (typeRef.MetadataType) switch (typeRef.MetadataType)
{ {
case MetadataType.Boolean: case MetadataType.Boolean:
@ -164,13 +148,10 @@ namespace AssetStudio
case MetadataType.Byte: case MetadataType.Byte:
case MetadataType.Int16: case MetadataType.Int16:
case MetadataType.UInt16: case MetadataType.UInt16:
result = true; return true;
break;
default: default:
result = (UnitySerializationLogic.IsSupportedCollection(typeRef) && RequiresAlignment(CecilUtils.ElementTypeOfCollection(typeRef))); return UnitySerializationLogic.IsSupportedCollection(typeRef) && RequiresAlignment(CecilUtils.ElementTypeOfCollection(typeRef));
break;
} }
return result;
} }
private static bool IsSystemString(TypeReference typeRef) private static bool IsSystemString(TypeReference typeRef)

View File

@ -0,0 +1,65 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Unity.CecilTools.Extensions;
namespace Unity.CecilTools
{
public static class CecilUtils
{
public static MethodDefinition FindInTypeExplicitImplementationFor(MethodDefinition interfaceMethod, TypeDefinition typeDefinition)
{
return typeDefinition.Methods.SingleOrDefault(m => m.Overrides.Any(o => o.CheckedResolve().SameAs(interfaceMethod)));
}
public static IEnumerable<TypeDefinition> AllInterfacesImplementedBy(TypeDefinition typeDefinition)
{
return TypeAndBaseTypesOf(typeDefinition).SelectMany(t => t.Interfaces).Select(i => i.InterfaceType.CheckedResolve()).Distinct();
}
public static IEnumerable<TypeDefinition> TypeAndBaseTypesOf(TypeReference typeReference)
{
while (typeReference != null)
{
var typeDefinition = typeReference.CheckedResolve();
yield return typeDefinition;
typeReference = typeDefinition.BaseType;
}
}
public static IEnumerable<TypeDefinition> BaseTypesOf(TypeReference typeReference)
{
return TypeAndBaseTypesOf(typeReference).Skip(1);
}
public static bool IsGenericList(TypeReference type)
{
return type.Name == "List`1" && type.SafeNamespace() == "System.Collections.Generic";
}
public static bool IsGenericDictionary(TypeReference type)
{
if (type is GenericInstanceType)
type = ((GenericInstanceType)type).ElementType;
return type.Name == "Dictionary`2" && type.SafeNamespace() == "System.Collections.Generic";
}
public static TypeReference ElementTypeOfCollection(TypeReference type)
{
var at = type as ArrayType;
if (at != null)
return at.ElementType;
if (IsGenericList(type))
return ((GenericInstanceType)type).GenericArguments.Single();
throw new ArgumentException();
}
}
}

View File

@ -0,0 +1,21 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using Mono.Cecil;
namespace Unity.CecilTools
{
static public class ElementType
{
public static TypeReference For(TypeReference byRefType)
{
var refType = byRefType as TypeSpecification;
if (refType != null)
return refType.ElementType;
throw new ArgumentException(string.Format("TypeReference isn't a TypeSpecification {0} ", byRefType));
}
}
}

View File

@ -0,0 +1,50 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
static class MethodDefinitionExtensions
{
public static bool SameAs(this MethodDefinition self, MethodDefinition other)
{
// FIXME: should be able to compare MethodDefinition references directly
return self.FullName == other.FullName;
}
public static string PropertyName(this MethodDefinition self)
{
return self.Name.Substring(4);
}
public static bool IsConversionOperator(this MethodDefinition method)
{
if (!method.IsSpecialName)
return false;
return method.Name == "op_Implicit" || method.Name == "op_Explicit";
}
public static bool IsSimpleSetter(this MethodDefinition original)
{
return original.IsSetter && original.Parameters.Count == 1;
}
public static bool IsSimpleGetter(this MethodDefinition original)
{
return original.IsGetter && original.Parameters.Count == 0;
}
public static bool IsSimplePropertyAccessor(this MethodDefinition method)
{
return method.IsSimpleGetter() || method.IsSimpleSetter();
}
public static bool IsDefaultConstructor(MethodDefinition m)
{
return m.IsConstructor && !m.IsStatic && m.Parameters.Count == 0;
}
}
}

View File

@ -0,0 +1,36 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
public static class ResolutionExtensions
{
public static TypeDefinition CheckedResolve(this TypeReference type)
{
return Resolve(type, reference => reference.Resolve());
}
public static MethodDefinition CheckedResolve(this MethodReference method)
{
return Resolve(method, reference => reference.Resolve());
}
private static TDefinition Resolve<TReference, TDefinition>(TReference reference, Func<TReference, TDefinition> resolve)
where TReference : MemberReference
where TDefinition : class, IMemberDefinition
{
if (reference.Module == null)
throw new ResolutionException(reference);
var definition = resolve(reference);
if (definition == null)
throw new ResolutionException(reference);
return definition;
}
}
}

View File

@ -0,0 +1,47 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
public static class TypeDefinitionExtensions
{
public static bool IsSubclassOf(this TypeDefinition type, string baseTypeName)
{
var baseType = type.BaseType;
if (baseType == null)
return false;
if (baseType.FullName == baseTypeName)
return true;
var baseTypeDef = baseType.Resolve();
if (baseTypeDef == null)
return false;
return IsSubclassOf(baseTypeDef, baseTypeName);
}
public static bool IsSubclassOf(this TypeDefinition type, params string[] baseTypeNames)
{
var baseType = type.BaseType;
if (baseType == null)
return false;
for (int i = 0; i < baseTypeNames.Length; i++)
if (baseType.FullName == baseTypeNames[i])
return true;
var baseTypeDef = baseType.Resolve();
if (baseTypeDef == null)
return false;
return IsSubclassOf(baseTypeDef, baseTypeNames);
}
}
}

View File

@ -0,0 +1,53 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
public static class TypeReferenceExtensions
{
public static string SafeNamespace(this TypeReference type)
{
if (type.IsGenericInstance)
return ((GenericInstanceType)type).ElementType.SafeNamespace();
if (type.IsNested)
return type.DeclaringType.SafeNamespace();
return type.Namespace;
}
public static bool IsAssignableTo(this TypeReference typeRef, string typeName)
{
try
{
if (typeRef.IsGenericInstance)
return ElementType.For(typeRef).IsAssignableTo(typeName);
if (typeRef.FullName == typeName)
return true;
return typeRef.CheckedResolve().IsSubclassOf(typeName);
}
catch (AssemblyResolutionException) // If we can't resolve our typeref or one of its base types,
{ // let's assume it is not assignable to our target type
return false;
}
}
public static bool IsEnum(this TypeReference type)
{
return type.IsValueType && !type.IsPrimitive && type.CheckedResolve().IsEnum;
}
public static bool IsStruct(this TypeReference type)
{
return type.IsValueType && !type.IsPrimitive && !type.IsEnum() && !IsSystemDecimal(type);
}
private static bool IsSystemDecimal(TypeReference type)
{
return type.FullName == "System.Decimal";
}
}
}

View File

@ -0,0 +1,168 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System.Collections.Generic;
using Unity.CecilTools.Extensions;
using Mono.Cecil;
namespace Unity.SerializationLogic
{
public class UnityEngineTypePredicates
{
private static readonly HashSet<string> TypesThatShouldHaveHadSerializableAttribute = new HashSet<string>
{
"Vector3",
"Vector2",
"Vector4",
"Rect",
"RectInt",
"Quaternion",
"Matrix4x4",
"Color",
"Color32",
"LayerMask",
"Bounds",
"BoundsInt",
"Vector3Int",
"Vector2Int",
};
private const string Gradient = "UnityEngine.Gradient";
private const string GUIStyle = "UnityEngine.GUIStyle";
private const string RectOffset = "UnityEngine.RectOffset";
protected const string UnityEngineObject = "UnityEngine.Object";
public const string MonoBehaviour = "UnityEngine.MonoBehaviour";
public const string ScriptableObject = "UnityEngine.ScriptableObject";
protected const string Matrix4x4 = "UnityEngine.Matrix4x4";
protected const string Color32 = "UnityEngine.Color32";
private const string SerializeFieldAttribute = "UnityEngine.SerializeField";
private const string SerializeReferenceAttribute = "UnityEngine.SerializeReference";
private static string[] serializableClasses = new[]
{
"UnityEngine.AnimationCurve",
"UnityEngine.Gradient",
"UnityEngine.GUIStyle",
"UnityEngine.RectOffset"
};
private static string[] serializableStructs = new[]
{
// NOTE: assumes all types here are NOT interfaces
"UnityEngine.Color32",
"UnityEngine.Matrix4x4",
"UnityEngine.Rendering.SphericalHarmonicsL2",
"UnityEngine.PropertyName",
};
public static bool IsMonoBehaviour(TypeReference type)
{
return IsMonoBehaviour(type.CheckedResolve());
}
private static bool IsMonoBehaviour(TypeDefinition typeDefinition)
{
return typeDefinition.IsSubclassOf(MonoBehaviour);
}
public static bool IsScriptableObject(TypeReference type)
{
return IsScriptableObject(type.CheckedResolve());
}
private static bool IsScriptableObject(TypeDefinition temp)
{
return temp.IsSubclassOf(ScriptableObject);
}
public static bool IsColor32(TypeReference type)
{
return type.IsAssignableTo(Color32);
}
//Do NOT remove these, cil2as still depends on these in 4.x
public static bool IsMatrix4x4(TypeReference type)
{
return type.IsAssignableTo(Matrix4x4);
}
public static bool IsGradient(TypeReference type)
{
return type.IsAssignableTo(Gradient);
}
public static bool IsGUIStyle(TypeReference type)
{
return type.IsAssignableTo(GUIStyle);
}
public static bool IsRectOffset(TypeReference type)
{
return type.IsAssignableTo(RectOffset);
}
public static bool IsSerializableUnityClass(TypeReference type)
{
foreach (var unityClasses in serializableClasses)
{
if (type.IsAssignableTo(unityClasses))
return true;
}
return false;
}
public static bool IsSerializableUnityStruct(TypeReference type)
{
foreach (var unityStruct in serializableStructs)
{
// NOTE: structs cannot inherit from structs, and can only inherit from interfaces
// since we know all types in serializableStructs are not interfaces,
// we can just do a direct comparison.
if (type.FullName == unityStruct)
return true;
}
if (type.FullName.IndexOf("UnityEngine.LazyLoadReference`1") == 0)
return true;
return false;
}
public static bool IsUnityEngineObject(TypeReference type)
{
//todo: somehow solve this elegantly. CheckedResolve() drops the [] of a type.
if (type.IsArray)
return false;
if (type.FullName == UnityEngineObject)
return true;
var typeDefinition = type.Resolve();
if (typeDefinition == null)
return false;
return typeDefinition.IsSubclassOf(UnityEngineObject);
}
public static bool ShouldHaveHadSerializableAttribute(TypeReference type)
{
return IsUnityEngineValueType(type);
}
public static bool IsUnityEngineValueType(TypeReference type)
{
return type.SafeNamespace() == "UnityEngine" && TypesThatShouldHaveHadSerializableAttribute.Contains(type.Name);
}
public static bool IsSerializeFieldAttribute(TypeReference attributeType)
{
return attributeType.FullName == SerializeFieldAttribute;
}
public static bool IsSerializeReferenceAttribute(TypeReference attributeType)
{
return attributeType.FullName == SerializeReferenceAttribute;
}
}
}

View File

@ -0,0 +1,612 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Collections.Generic;
using Unity.CecilTools;
using Unity.CecilTools.Extensions;
namespace Unity.SerializationLogic
{
internal class GenericInstanceHolder
{
public int Count;
public IGenericInstance GenericInstance;
}
public class TypeResolver
{
private readonly IGenericInstance _typeDefinitionContext;
private readonly IGenericInstance _methodDefinitionContext;
private readonly Dictionary<string, GenericInstanceHolder> _context = new Dictionary<string, GenericInstanceHolder>();
public TypeResolver()
{
}
public TypeResolver(IGenericInstance typeDefinitionContext)
{
_typeDefinitionContext = typeDefinitionContext;
}
public TypeResolver(GenericInstanceMethod methodDefinitionContext)
{
_methodDefinitionContext = methodDefinitionContext;
}
public TypeResolver(IGenericInstance typeDefinitionContext, IGenericInstance methodDefinitionContext)
{
_typeDefinitionContext = typeDefinitionContext;
_methodDefinitionContext = methodDefinitionContext;
}
public void Add(GenericInstanceType genericInstanceType)
{
Add(ElementTypeFor(genericInstanceType).FullName, genericInstanceType);
}
public void Remove(GenericInstanceType genericInstanceType)
{
Remove(genericInstanceType.ElementType.FullName, genericInstanceType);
}
public void Add(GenericInstanceMethod genericInstanceMethod)
{
Add(ElementTypeFor(genericInstanceMethod).FullName, genericInstanceMethod);
}
private static MemberReference ElementTypeFor(TypeSpecification genericInstanceType)
{
return genericInstanceType.ElementType;
}
private static MemberReference ElementTypeFor(MethodSpecification genericInstanceMethod)
{
return genericInstanceMethod.ElementMethod;
}
public void Remove(GenericInstanceMethod genericInstanceMethod)
{
Remove(genericInstanceMethod.ElementMethod.FullName, genericInstanceMethod);
}
public TypeReference Resolve(TypeReference typeReference)
{
var genericParameter = typeReference as GenericParameter;
if (genericParameter != null)
{
var resolved = ResolveGenericParameter(genericParameter);
if (genericParameter == resolved) // Resolving failed, return what we have.
return resolved;
return Resolve(resolved);
}
var arrayType = typeReference as ArrayType;
if (arrayType != null)
return new ArrayType(Resolve(arrayType.ElementType), arrayType.Rank);
var pointerType = typeReference as PointerType;
if (pointerType != null)
return new PointerType(Resolve(pointerType.ElementType));
var byReferenceType = typeReference as ByReferenceType;
if (byReferenceType != null)
return new ByReferenceType(Resolve(byReferenceType.ElementType));
var genericInstanceType = typeReference as GenericInstanceType;
if (genericInstanceType != null)
{
var newGenericInstanceType = new GenericInstanceType(Resolve(genericInstanceType.ElementType));
foreach (var genericArgument in genericInstanceType.GenericArguments)
newGenericInstanceType.GenericArguments.Add(Resolve(genericArgument));
return newGenericInstanceType;
}
var pinnedType = typeReference as PinnedType;
if (pinnedType != null)
return new PinnedType(Resolve(pinnedType.ElementType));
var reqModifierType = typeReference as RequiredModifierType;
if (reqModifierType != null)
return Resolve(reqModifierType.ElementType);
var optModifierType = typeReference as OptionalModifierType;
if (optModifierType != null)
return new OptionalModifierType(Resolve(optModifierType.ModifierType), Resolve(optModifierType.ElementType));
var sentinelType = typeReference as SentinelType;
if (sentinelType != null)
return new SentinelType(Resolve(sentinelType.ElementType));
var funcPtrType = typeReference as FunctionPointerType;
if (funcPtrType != null)
throw new NotSupportedException("Function pointer types are not supported by the SerializationWeaver");
if (typeReference is TypeSpecification)
throw new NotSupportedException();
return typeReference;
}
private TypeReference ResolveGenericParameter(GenericParameter genericParameter)
{
if (genericParameter.Owner == null)
throw new NotSupportedException();
var memberReference = genericParameter.Owner as MemberReference;
if (memberReference == null)
throw new NotSupportedException();
var key = memberReference.FullName;
if (!_context.ContainsKey(key))
{
if (genericParameter.Type == GenericParameterType.Type)
{
if (_typeDefinitionContext != null)
return _typeDefinitionContext.GenericArguments[genericParameter.Position];
return genericParameter;
}
if (_methodDefinitionContext != null)
return _methodDefinitionContext.GenericArguments[genericParameter.Position];
return genericParameter;
}
return GenericArgumentAt(key, genericParameter.Position);
}
private TypeReference GenericArgumentAt(string key, int position)
{
return _context[key].GenericInstance.GenericArguments[position];
}
private void Add(string key, IGenericInstance value)
{
GenericInstanceHolder oldValue;
if (_context.TryGetValue(key, out oldValue))
{
var memberReference = value as MemberReference;
if (memberReference == null)
throw new NotSupportedException();
var storedValue = (MemberReference)oldValue.GenericInstance;
if (storedValue.FullName != memberReference.FullName)
throw new ArgumentException("Duplicate key!", "key");
oldValue.Count++;
return;
}
_context.Add(key, new GenericInstanceHolder { Count = 1, GenericInstance = value });
}
private void Remove(string key, IGenericInstance value)
{
GenericInstanceHolder oldValue;
if (_context.TryGetValue(key, out oldValue))
{
var memberReference = value as MemberReference;
if (memberReference == null)
throw new NotSupportedException();
var storedValue = (MemberReference)oldValue.GenericInstance;
if (storedValue.FullName != memberReference.FullName)
throw new ArgumentException("Invalid value!", "value");
oldValue.Count--;
if (oldValue.Count == 0)
_context.Remove(key);
return;
}
throw new ArgumentException("Invalid key!", "key");
}
}
public static class UnitySerializationLogic
{
public static bool WillUnitySerialize(FieldDefinition fieldDefinition)
{
return WillUnitySerialize(fieldDefinition, new TypeResolver(null));
}
public static bool WillUnitySerialize(FieldDefinition fieldDefinition, TypeResolver typeResolver)
{
if (fieldDefinition == null)
return false;
//skip static, const and NotSerialized fields before even checking the type
if (fieldDefinition.IsStatic || IsConst(fieldDefinition) || fieldDefinition.IsNotSerialized || fieldDefinition.IsInitOnly)
return false;
// The field must have correct visibility/decoration to be serialized.
if (!fieldDefinition.IsPublic &&
!ShouldHaveHadAllFieldsPublic(fieldDefinition) &&
!HasSerializeFieldAttribute(fieldDefinition) &&
!HasSerializeReferenceAttribute(fieldDefinition))
return false;
// Don't try to resolve types that come from Windows assembly,
// as serialization weaver will fail to resolve that (due to it being in platform specific SDKs)
if (ShouldNotTryToResolve(fieldDefinition.FieldType))
return false;
if (IsFixedBuffer(fieldDefinition))
return true;
// Resolving types is more complex and slower than checking their names or attributes,
// thus keep those checks below
var typeReference = typeResolver.Resolve(fieldDefinition.FieldType);
//the type of the field must be serializable in the first place.
if (typeReference.MetadataType == MetadataType.String)
return true;
if (typeReference.IsValueType)
return IsValueTypeSerializable(typeReference);
if (typeReference is ArrayType || CecilUtils.IsGenericList(typeReference))
{
if (!HasSerializeReferenceAttribute(fieldDefinition))
return IsSupportedCollection(typeReference);
}
if (!IsReferenceTypeSerializable(typeReference) && !HasSerializeReferenceAttribute(fieldDefinition))
return false;
if (IsDelegate(typeReference))
return false;
return true;
}
private static bool IsDelegate(TypeReference typeReference)
{
return typeReference.IsAssignableTo("System.Delegate");
}
public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition)
{
return ShouldFieldBePPtrRemapped(fieldDefinition, new TypeResolver(null));
}
public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition, TypeResolver typeResolver)
{
if (!WillUnitySerialize(fieldDefinition, typeResolver))
return false;
return CanTypeContainUnityEngineObjectReference(typeResolver.Resolve(fieldDefinition.FieldType));
}
private static bool CanTypeContainUnityEngineObjectReference(TypeReference typeReference)
{
if (IsUnityEngineObject(typeReference))
return true;
if (typeReference.IsEnum())
return false;
if (IsSerializablePrimitive(typeReference))
return false;
if (IsSupportedCollection(typeReference))
return CanTypeContainUnityEngineObjectReference(CecilUtils.ElementTypeOfCollection(typeReference));
var definition = typeReference.Resolve();
if (definition == null)
return false;
return HasFieldsThatCanContainUnityEngineObjectReferences(definition, new TypeResolver(typeReference as GenericInstanceType));
}
private static bool HasFieldsThatCanContainUnityEngineObjectReferences(TypeDefinition definition, TypeResolver typeResolver)
{
return AllFieldsFor(definition, typeResolver).Where(kv => kv.Value.Resolve(kv.Key.FieldType).Resolve() != definition).Any(kv => CanFieldContainUnityEngineObjectReference(definition, kv.Key, kv.Value));
}
private static IEnumerable<KeyValuePair<FieldDefinition, TypeResolver>> AllFieldsFor(TypeDefinition definition, TypeResolver typeResolver)
{
var baseType = definition.BaseType;
if (baseType != null)
{
var genericBaseInstanceType = baseType as GenericInstanceType;
if (genericBaseInstanceType != null)
typeResolver.Add(genericBaseInstanceType);
foreach (var kv in AllFieldsFor(baseType.Resolve(), typeResolver))
yield return kv;
if (genericBaseInstanceType != null)
typeResolver.Remove(genericBaseInstanceType);
}
foreach (var fieldDefinition in definition.Fields)
yield return new KeyValuePair<FieldDefinition, TypeResolver>(fieldDefinition, typeResolver);
}
private static bool CanFieldContainUnityEngineObjectReference(TypeReference typeReference, FieldDefinition t, TypeResolver typeResolver)
{
if (typeResolver.Resolve(t.FieldType) == typeReference)
return false;
if (!WillUnitySerialize(t, typeResolver))
return false;
if (UnityEngineTypePredicates.IsUnityEngineValueType(typeReference))
return false;
return true;
}
private static bool IsConst(FieldDefinition fieldDefinition)
{
return fieldDefinition.IsLiteral && !fieldDefinition.IsInitOnly;
}
public static bool HasSerializeFieldAttribute(FieldDefinition field)
{
//return FieldAttributes(field).Any(UnityEngineTypePredicates.IsSerializeFieldAttribute);
foreach (var attribute in FieldAttributes(field))
if (UnityEngineTypePredicates.IsSerializeFieldAttribute(attribute))
return true;
return false;
}
public static bool HasSerializeReferenceAttribute(FieldDefinition field)
{
foreach (var attribute in FieldAttributes(field))
if (UnityEngineTypePredicates.IsSerializeReferenceAttribute(attribute))
return true;
return false;
}
private static IEnumerable<TypeReference> FieldAttributes(FieldDefinition field)
{
return field.CustomAttributes.Select(_ => _.AttributeType);
}
public static bool ShouldNotTryToResolve(TypeReference typeReference)
{
var typeReferenceScopeName = typeReference.Scope.Name;
if (typeReferenceScopeName == "Windows")
{
return true;
}
if (typeReferenceScopeName == "mscorlib")
{
var resolved = typeReference.Resolve();
return resolved == null;
}
try
{ // This will throw an exception if typereference thinks it's referencing a .dll,
// but actually there's .winmd file in the current directory. RRW will fix this
// at a later step, so we will not try to resolve this type. This is OK, as any
// type defined in a winmd cannot be serialized.
typeReference.Resolve();
}
catch
{
return true;
}
return false;
}
private static bool IsFieldTypeSerializable(TypeReference typeReference, FieldDefinition fieldDefinition)
{
return IsTypeSerializable(typeReference) || IsSupportedCollection(typeReference) || IsFixedBuffer(fieldDefinition);
}
private static bool IsValueTypeSerializable(TypeReference typeReference)
{
if (typeReference.IsPrimitive)
return IsSerializablePrimitive(typeReference);
return UnityEngineTypePredicates.IsSerializableUnityStruct(typeReference) ||
typeReference.IsEnum() ||
ShouldImplementIDeserializable(typeReference);
}
private static bool IsReferenceTypeSerializable(TypeReference typeReference)
{
if (typeReference.MetadataType == MetadataType.String)
return IsSerializablePrimitive(typeReference);
if (IsGenericDictionary(typeReference))
return false;
if (IsUnityEngineObject(typeReference) ||
ShouldImplementIDeserializable(typeReference) ||
UnityEngineTypePredicates.IsSerializableUnityClass(typeReference))
return true;
return false;
}
private static bool IsTypeSerializable(TypeReference typeReference)
{
if (typeReference.MetadataType == MetadataType.String)
return true;
if (typeReference.IsValueType)
return IsValueTypeSerializable(typeReference);
return IsReferenceTypeSerializable(typeReference);
}
private static bool IsGenericDictionary(TypeReference typeReference)
{
var current = typeReference;
if (current != null)
{
if (CecilUtils.IsGenericDictionary(current))
return true;
}
return false;
}
public static bool IsFixedBuffer(FieldDefinition fieldDefinition)
{
return GetFixedBufferAttribute(fieldDefinition) != null;
}
public static CustomAttribute GetFixedBufferAttribute(FieldDefinition fieldDefinition)
{
if (!fieldDefinition.HasCustomAttributes)
return null;
return fieldDefinition.CustomAttributes.SingleOrDefault(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.FixedBufferAttribute");
}
public static int GetFixedBufferLength(FieldDefinition fieldDefinition)
{
var fixedBufferAttribute = GetFixedBufferAttribute(fieldDefinition);
if (fixedBufferAttribute == null)
throw new ArgumentException(string.Format("Field '{0}' is not a fixed buffer field.", fieldDefinition.FullName));
var size = (Int32)fixedBufferAttribute.ConstructorArguments[1].Value;
return size;
}
public static int PrimitiveTypeSize(TypeReference type)
{
switch (type.MetadataType)
{
case MetadataType.Boolean:
case MetadataType.Byte:
case MetadataType.SByte:
return 1;
case MetadataType.Char:
case MetadataType.Int16:
case MetadataType.UInt16:
return 2;
case MetadataType.Int32:
case MetadataType.UInt32:
case MetadataType.Single:
return 4;
case MetadataType.Int64:
case MetadataType.UInt64:
case MetadataType.Double:
return 8;
default:
throw new ArgumentException(string.Format("Unsupported {0}", type.MetadataType));
}
}
private static bool IsSerializablePrimitive(TypeReference typeReference)
{
switch (typeReference.MetadataType)
{
case MetadataType.SByte:
case MetadataType.Byte:
case MetadataType.Char:
case MetadataType.Int16:
case MetadataType.UInt16:
case MetadataType.Int64:
case MetadataType.UInt64:
case MetadataType.Int32:
case MetadataType.UInt32:
case MetadataType.Single:
case MetadataType.Double:
case MetadataType.Boolean:
case MetadataType.String:
return true;
}
return false;
}
public static bool IsSupportedCollection(TypeReference typeReference)
{
if (!(typeReference is ArrayType || CecilUtils.IsGenericList(typeReference)))
return false;
// We don't support arrays like byte[,] etc
if (typeReference.IsArray && ((ArrayType)typeReference).Rank > 1)
return false;
return IsTypeSerializable(CecilUtils.ElementTypeOfCollection(typeReference));
}
private static bool ShouldHaveHadAllFieldsPublic(FieldDefinition field)
{
return UnityEngineTypePredicates.IsUnityEngineValueType(field.DeclaringType);
}
private static bool IsUnityEngineObject(TypeReference typeReference)
{
return UnityEngineTypePredicates.IsUnityEngineObject(typeReference);
}
public static bool IsNonSerialized(TypeReference typeDeclaration)
{
if (typeDeclaration == null)
return true;
if (typeDeclaration.HasGenericParameters)
return true;
if (typeDeclaration.MetadataType == MetadataType.Object)
return true;
var fullName = typeDeclaration.FullName;
if (fullName.StartsWith("System.")) //can this be done better?
return true;
if (typeDeclaration.IsArray)
return true;
if (typeDeclaration.FullName == UnityEngineTypePredicates.MonoBehaviour)
return true;
if (typeDeclaration.FullName == UnityEngineTypePredicates.ScriptableObject)
return true;
if (typeDeclaration.IsEnum())
return true;
return false;
}
public static bool ShouldImplementIDeserializable(TypeReference typeDeclaration)
{
if (typeDeclaration.FullName == "UnityEngine.ExposedReference`1")
return true;
if (IsNonSerialized(typeDeclaration))
return false;
try
{
if (UnityEngineTypePredicates.ShouldHaveHadSerializableAttribute(typeDeclaration))
return true;
var resolvedTypeDeclaration = typeDeclaration.CheckedResolve();
if (resolvedTypeDeclaration.IsValueType)
{
return resolvedTypeDeclaration.IsSerializable && !resolvedTypeDeclaration.CustomAttributes.Any(a => a.AttributeType.FullName.Contains("System.Runtime.CompilerServices.CompilerGenerated"));
}
else
{
return (resolvedTypeDeclaration.IsSerializable && !resolvedTypeDeclaration.CustomAttributes.Any(a => a.AttributeType.FullName.Contains("System.Runtime.CompilerServices.CompilerGenerated"))) ||
resolvedTypeDeclaration.IsSubclassOf(UnityEngineTypePredicates.MonoBehaviour, UnityEngineTypePredicates.ScriptableObject);
}
}
catch (Exception)
{
return false;
}
}
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mono.Cecil" version="0.11.3" targetFramework="net472" />
</packages>