using System.Collections; using System.Collections.Generic; using System; using System.Linq; using System.Reflection; using System.ComponentModel; using UnityEngine; using ES3Types; namespace ES3Internal { public static class ES3Reflection { public const string memberFieldPrefix = "m_"; public const string componentTagFieldName = "tag"; public const string componentNameFieldName = "name"; public static readonly string[] excludedPropertyNames = new string[]{"runInEditMode", "useGUILayout", "hideFlags"}; public static readonly Type serializableAttributeType = typeof(System.SerializableAttribute); public static readonly Type serializeFieldAttributeType = typeof(SerializeField); public static readonly Type obsoleteAttributeType = typeof(System.ObsoleteAttribute); public static readonly Type nonSerializedAttributeType = typeof(System.NonSerializedAttribute); public static readonly Type es3SerializableAttributeType = typeof(ES3Serializable); public static readonly Type es3NonSerializableAttributeType = typeof(ES3NonSerializable); public static Type[] EmptyTypes = new Type[0]; private static Assembly[] _assemblies = null; private static Assembly[] Assemblies { get { if(_assemblies == null) { var assemblyNames = new ES3Settings().assemblyNames; var assemblyList = new List(); for(int i=0; i GetSerializableFields(Type type, List serializableFields=null, bool safe=true, string[] memberNames=null, BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) { if (type == null) return new List(); var fields = type.GetFields(bindings); if(serializableFields == null) serializableFields = new List(); foreach(var field in fields) { var fieldName = field.Name; // If a members array was provided as a parameter, only include the field if it's in the array. if(memberNames != null) if(!memberNames.Contains(fieldName)) continue; var fieldType = field.FieldType; if (AttributeIsDefined(field, es3SerializableAttributeType)) { serializableFields.Add(field); continue; } if (AttributeIsDefined(field, es3NonSerializableAttributeType)) continue; if (safe) { // If the field is private, only serialize it if it's explicitly marked as serializable. if(!field.IsPublic && !AttributeIsDefined(field, serializeFieldAttributeType)) continue; } // Exclude const or readonly fields. if (field.IsLiteral || field.IsInitOnly) continue; // Don't store fields whose type is the same as the class the field is housed in unless it's stored by reference (to prevent cyclic references) if (fieldType == type && !IsAssignableFrom(typeof(UnityEngine.Object), fieldType)) continue; // If property is marked as obsolete or non-serialized, don't serialize it. if(AttributeIsDefined(field, nonSerializedAttributeType) || AttributeIsDefined(field, obsoleteAttributeType)) continue; if(!TypeIsSerializable(field.FieldType)) continue; // Don't serialize member fields. if(safe && fieldName.StartsWith(memberFieldPrefix)) continue; serializableFields.Add(field); } var baseType = BaseType(type); if (baseType != null && baseType != typeof(System.Object) && baseType != typeof(UnityEngine.Object)) GetSerializableFields(BaseType(type), serializableFields, safe, memberNames); return serializableFields; } public static List GetSerializableProperties(Type type, List serializableProperties=null, bool safe=true, string[] memberNames=null, BindingFlags bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) { bool isComponent = IsAssignableFrom(typeof(UnityEngine.Component), type); // Only get private properties if we're not getting properties safely. if(!safe) bindings = bindings | BindingFlags.NonPublic; var properties = type.GetProperties(bindings); if(serializableProperties == null) serializableProperties = new List(); foreach(var p in properties) { if (AttributeIsDefined(p, es3SerializableAttributeType)) { serializableProperties.Add(p); continue; } if (AttributeIsDefined(p, es3NonSerializableAttributeType)) continue; var propertyName = p.Name; if (excludedPropertyNames.Contains(propertyName)) continue; // If a members array was provided as a parameter, only include the property if it's in the array. if(memberNames != null) if(!memberNames.Contains(propertyName)) continue; if(safe) { // If safe serialization is enabled, only get properties which are explicitly marked as serializable. if(!AttributeIsDefined(p, serializeFieldAttributeType) && !AttributeIsDefined(p, es3SerializableAttributeType)) continue; } var propertyType = p.PropertyType; // Don't store properties whose type is the same as the class the property is housed in unless it's stored by reference (to prevent cyclic references) if(propertyType == type && !IsAssignableFrom(typeof(UnityEngine.Object), propertyType)) continue; if(!p.CanRead || !p.CanWrite) continue; // Only support properties with indexing if they're an array. if(p.GetIndexParameters().Length != 0 && !propertyType.IsArray) continue; // Check that the type of the property is one which we can serialize. // Also check whether an ES3Type exists for it. if(!TypeIsSerializable(propertyType)) continue; // Ignore certain properties on components. if(isComponent) { // Ignore properties which are accessors for GameObject fields. if(propertyName == componentTagFieldName || propertyName == componentNameFieldName) continue; } // If property is marked as obsolete or non-serialized, don't serialize it. if(AttributeIsDefined(p, obsoleteAttributeType) || AttributeIsDefined(p, nonSerializedAttributeType)) continue; // Don't serialize member propertes as these are usually unsafe in Unity classes. if(safe && propertyName.StartsWith(memberFieldPrefix)) continue; serializableProperties.Add(p); } var baseType = BaseType(type); if (baseType != null && baseType != typeof(System.Object)) GetSerializableProperties(baseType, serializableProperties, safe, memberNames); return serializableProperties; } public static bool TypeIsSerializable(Type type) { if(type == null) return false; if (AttributeIsDefined(type, es3NonSerializableAttributeType)) return false; if(IsPrimitive(type) || IsValueType(type) || IsAssignableFrom(typeof(UnityEngine.Component), type) || IsAssignableFrom(typeof(UnityEngine.ScriptableObject), type)) return true; var es3Type = ES3TypeMgr.GetOrCreateES3Type(type, false); if(es3Type != null && !es3Type.isUnsupported) return true; if(TypeIsArray(type)) { if(TypeIsSerializable(type.GetElementType())) return true; return false; } var genericArgs = type.GetGenericArguments(); for(int i=0; i(), safe, memberNames); var propertyInfos = GetSerializableProperties(type, new List(), safe, memberNames); var reflectedFields = new ES3ReflectedMember[fieldInfos.Count + propertyInfos.Count]; for(int i=0; i GetInstances() { var instances = new List(); foreach (var assembly in Assemblies) foreach (var type in assembly.GetTypes()) if (IsAssignableFrom (typeof(T), type) && ES3Reflection.HasParameterlessConstructor (type) && !ES3Reflection.IsAbstract (type)) instances.Add ((T)Activator.CreateInstance(type)); return instances; } public static IList GetDerivedTypes(Type derivedType) { return ( from assembly in Assemblies from type in assembly.GetTypes() where IsAssignableFrom(derivedType, type) select type ).ToList(); } public static bool IsAssignableFrom(Type a, Type b) { return a.IsAssignableFrom(b); } public static Type GetGenericTypeDefinition(Type type) { return type.GetGenericTypeDefinition(); } public static Type[] GetGenericArguments(Type type) { return type.GetGenericArguments(); } public static int GetArrayRank(Type type) { return type.GetArrayRank(); } public static string GetAssemblyQualifiedName(Type type) { return type.AssemblyQualifiedName; } public static ES3ReflectedMethod GetMethod(Type type, string methodName, Type[] genericParameters, Type[] parameterTypes) { return new ES3ReflectedMethod(type, methodName, genericParameters, parameterTypes); } public static bool TypeIsArray(Type type) { return type.IsArray; } public static Type GetElementType(Type type) { return type.GetElementType(); } #if NETFX_CORE public static bool IsAbstract(Type type) { return type.GetTypeInfo().IsAbstract; } public static bool IsInterface(Type type) { return type.GetTypeInfo().IsInterface; } public static bool IsGenericType(Type type) { return type.GetTypeInfo().IsGenericType; } public static bool IsValueType(Type type) { return type.GetTypeInfo().IsValueType; } public static bool IsEnum(Type type) { return type.GetTypeInfo().IsEnum; } public static bool HasParameterlessConstructor(Type type) { foreach (var cInfo in type.GetTypeInfo().DeclaredConstructors) { if (!cInfo.IsFamily && !cInfo.IsStatic && cInfo.GetParameters().Length == 0) return true; } return false; } public static ConstructorInfo GetParameterlessConstructor(Type type) { foreach (var cInfo in type.GetTypeInfo().DeclaredConstructors) { if (!cInfo.IsFamily && cInfo.GetParameters().Length == 0) return cInfo; } return null; } public static string GetShortAssemblyQualifiedName(Type type) { if (IsPrimitive (type)) return type.ToString (); return type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name; } public static PropertyInfo GetProperty(Type type, string propertyName) { var property = type.GetTypeInfo().GetDeclaredProperty(propertyName); if (property == null && type.BaseType != typeof(object)) return GetProperty(type.BaseType, propertyName); return property; } public static FieldInfo GetField(Type type, string fieldName) { return type.GetTypeInfo().GetDeclaredField(fieldName); } public static bool IsPrimitive(Type type) { return (type.GetTypeInfo().IsPrimitive || type == typeof(string) || type == typeof(decimal)); } public static bool AttributeIsDefined(MemberInfo info, Type attributeType) { var attributes = info.GetCustomAttributes(attributeType, true); foreach(var attribute in attributes) return true; return false; } public static bool AttributeIsDefined(Type type, Type attributeType) { var attributes = type.GetTypeInfo().GetCustomAttributes(attributeType, true); foreach(var attribute in attributes) return true; return false; } public static bool ImplementsInterface(Type type, Type interfaceType) { return type.GetTypeInfo().ImplementedInterfaces.Contains(interfaceType); } public static Type BaseType(Type type) { return type.GetTypeInfo().BaseType; } #else public static bool IsAbstract(Type type) { return type.IsAbstract; } public static bool IsInterface(Type type) { return type.IsInterface; } public static bool IsGenericType(Type type) { return type.IsGenericType; } public static bool IsValueType(Type type) { return type.IsValueType; } public static bool IsEnum(Type type) { return type.IsEnum; } public static bool HasParameterlessConstructor(Type type) { return type.GetConstructor (Type.EmptyTypes) != null || IsValueType(type); } public static ConstructorInfo GetParameterlessConstructor(Type type) { return type.GetConstructor (Type.EmptyTypes); } public static string GetShortAssemblyQualifiedName(Type type) { if (IsPrimitive (type)) return type.ToString (); return type.FullName + "," + type.Assembly.GetName().Name; } public static PropertyInfo GetProperty(Type type, string propertyName) { var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (property == null && BaseType(type) != typeof(object)) return GetProperty(BaseType(type), propertyName); return property; } public static FieldInfo GetField(Type type, string fieldName) { var field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (field == null && BaseType(type) != typeof(object)) return GetField(BaseType(type), fieldName); return field; } public static bool IsPrimitive(Type type) { return (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)); } public static bool AttributeIsDefined(MemberInfo info, Type attributeType) { return Attribute.IsDefined(info, attributeType, true); } public static bool AttributeIsDefined(Type type, Type attributeType) { return type.IsDefined(attributeType, true); } public static bool ImplementsInterface(Type type, Type interfaceType) { return (type.GetInterface(interfaceType.Name) != null); } public static Type BaseType(Type type) { return type.BaseType; } #endif /* * Allows us to use FieldInfo and PropertyInfo interchangably. */ public struct ES3ReflectedMember { // The FieldInfo or PropertyInfo for this field. private FieldInfo fieldInfo; private PropertyInfo propertyInfo; public bool isProperty; public bool IsNull { get{ return fieldInfo == null && propertyInfo == null; } } public string Name { get{ return (isProperty ? propertyInfo.Name : fieldInfo.Name); } } public Type MemberType { get{ return (isProperty ? propertyInfo.PropertyType : fieldInfo.FieldType); } } public bool IsPublic { get{ return (isProperty ? (propertyInfo.GetGetMethod(true).IsPublic && propertyInfo.GetSetMethod(true).IsPublic) : fieldInfo.IsPublic); } } public bool IsProtected { get{ return (isProperty ? (propertyInfo.GetGetMethod(true).IsFamily) : fieldInfo.IsFamily); } } public bool IsStatic { get{ return (isProperty ? (propertyInfo.GetGetMethod(true).IsStatic) : fieldInfo.IsStatic); } } public ES3ReflectedMember(System.Object fieldPropertyInfo) { if(fieldPropertyInfo == null) { this.propertyInfo = null; this.fieldInfo = null; isProperty = false; return; } isProperty = ES3Reflection.IsAssignableFrom(typeof(PropertyInfo), fieldPropertyInfo.GetType()); if(isProperty) { this.propertyInfo = (PropertyInfo)fieldPropertyInfo; this.fieldInfo = null; } else { this.fieldInfo = (FieldInfo)fieldPropertyInfo; this.propertyInfo = null; } } public void SetValue(System.Object obj, System.Object value) { if(isProperty) propertyInfo.SetValue(obj, value, null); else fieldInfo.SetValue(obj, value); } public System.Object GetValue(System.Object obj) { if(isProperty) return propertyInfo.GetValue(obj, null); else return fieldInfo.GetValue(obj); } } public class ES3ReflectedMethod { private MethodInfo method; public ES3ReflectedMethod(Type type, string methodName, Type[] genericParameters, Type[] parameterTypes) { MethodInfo nonGenericMethod = type.GetMethod(methodName, parameterTypes); this.method = nonGenericMethod.MakeGenericMethod(genericParameters); } public ES3ReflectedMethod(Type type, string methodName, Type[] genericParameters, Type[] parameterTypes, BindingFlags bindingAttr) { MethodInfo nonGenericMethod = type.GetMethod(methodName, bindingAttr, null, parameterTypes, null); this.method = nonGenericMethod.MakeGenericMethod(genericParameters); } public object Invoke(object obj, object[] parameters = null) { return method.Invoke(obj, parameters); } } } }