Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ComplexClassSerializerGenerator.Members.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System.Runtime.CompilerServices;
4 using SiliconStudio.Core.Serialization.Serializers;
5 #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP
6 using System;
7 using System.CodeDom.Compiler;
8 using System.Collections.Generic;
9 using System.Linq;
10 using System.Text;
11 using Mono.Cecil;
13 
14 namespace SiliconStudio.AssemblyProcessor
15 {
16  internal partial class ComplexClassSerializerGenerator
17  {
18  private TypeDefinition type;
19  private bool hasParentSerializer;
20  private static HashSet<string> forbiddenKeywords;
21 
22  static ComplexClassSerializerGenerator()
23  {
24  forbiddenKeywords = new HashSet<string>(new[]
25  { "obj", "stream", "mode",
26  "abstract", "event", "new", "struct",
27  "as", "explicit", "null", "switch",
28  "base", "extern", "object", "this",
29  "bool", "false", "operator", "throw",
30  "break", "finally", "out", "true",
31  "byte", "fixed", "override", "try",
32  "case", "float", "params", "typeof",
33  "catch", "for", "private", "uint",
34  "char", "foreach", "protected", "ulong",
35  "checked", "goto", "public", "unchecked",
36  "class", "if", "readonly", "unsafe",
37  "const", "implicit", "ref", "ushort",
38  "continue", "in", "return", "using",
39  "decimal", "int", "sbyte", "virtual",
40  "default", "interface", "sealed", "volatile",
41  "delegate", "internal", "short", "void",
42  "do", "is", "sizeof", "while",
43  "double", "lock", "stackalloc",
44  "else", "long", "static",
45  "enum", "namespace", "string" });
46  }
47 
48  public ComplexClassSerializerGenerator(TypeDefinition type, bool hasParentSerializer)
49  {
50  this.type = type;
51  this.hasParentSerializer = hasParentSerializer;
52  }
53 
54  private static string TypeNameWithoutGenericEnding(TypeReference type)
55  {
56  var typeName = type.Name;
57 
58  // Remove generics ending (i.e. `1)
59  var genericCharIndex = typeName.LastIndexOf('`');
60  if (genericCharIndex != -1)
61  typeName = typeName.Substring(0, genericCharIndex);
62 
63  return typeName;
64  }
65 
66  public static string SerializerTypeName(TypeReference type, bool appendGenerics, bool appendSerializer)
67  {
68  var typeName = TypeNameWithoutGenericEnding(type);
69 
70  // Prepend nested class
71  if (type.IsNested)
72  typeName = TypeNameWithoutGenericEnding(type.DeclaringType) + "_" + typeName;
73 
74  // Prepend namespace
75  if (!String.IsNullOrEmpty(type.Namespace))
76  typeName = type.Namespace.Replace(".", String.Empty) + "_" + typeName;
77 
78  // Append Serializer
79  if (appendSerializer)
80  typeName += "Serializer";
81 
82  // Append Generics
83  if (appendGenerics)
84  typeName += type.GenerateGenerics();
85 
86  return typeName;
87  }
88 
89  public static string GetSerializerInstantiateMethodName(TypeReference serializerType, bool appendGenerics)
90  {
91  return "Instantiate_" + SerializerTypeName(serializerType, appendGenerics, false);
92  }
93 
94  /// <summary>
95  /// Generates the generic constraints in a code form.
96  /// </summary>
97  /// <param name="type">The type.</param>
98  /// <returns></returns>
99  public static string GenerateGenericConstraints(TypeReference type)
100  {
101  if (!type.HasGenericParameters)
102  return String.Empty;
103 
104  var result = new StringBuilder();
105  foreach (var genericParameter in type.GenericParameters)
106  {
107  // If no constraints, skip it
108  var hasContraints = genericParameter.HasReferenceTypeConstraint || genericParameter.HasNotNullableValueTypeConstraint || genericParameter.Constraints.Count > 0 || genericParameter.HasDefaultConstructorConstraint;
109  if (!hasContraints)
110  {
111  continue;
112  }
113 
114  bool hasFirstContraint = false;
115 
116  result.AppendFormat(" where {0}: ", genericParameter.Name);
117 
118  // Where class/struct constraint must be before any other constraint
119  if (genericParameter.HasReferenceTypeConstraint)
120  {
121  result.AppendFormat("class");
122  hasFirstContraint = true;
123  }
124  else if (genericParameter.HasNotNullableValueTypeConstraint)
125  {
126  result.AppendFormat("struct");
127  hasFirstContraint = true;
128  }
129 
130  foreach (var genericParameterConstraint in genericParameter.Constraints)
131  {
132  // Skip value type constraint
133  if (genericParameterConstraint.FullName != typeof(ValueType).FullName)
134  {
135  if (hasFirstContraint)
136  {
137  result.Append(", ");
138  }
139 
140  result.AppendFormat("{0}", genericParameterConstraint.ConvertCSharp());
141  result.AppendLine();
142 
143  hasFirstContraint = true;
144  }
145  }
146 
147 
148  // New constraint must be last
149  if (!genericParameter.HasNotNullableValueTypeConstraint && genericParameter.HasDefaultConstructorConstraint)
150  {
151  if (hasFirstContraint)
152  {
153  result.Append(", ");
154  }
155 
156  result.AppendFormat("new()");
157  result.AppendLine();
158  }
159  }
160 
161  return result.ToString();
162  }
163 
164  /// <summary>
165  /// Determines whether the specified type has an empty constructor.
166  /// </summary>
167  /// <param name="type">The type.</param>
168  /// <returns></returns>
169  protected static bool HasEmptyConstructor(TypeReference type)
170  {
171  return type.Resolve().Methods.Any(x => x.IsConstructor && x.IsPublic && !x.IsStatic && x.Parameters.Count == 0);
172  }
173 
174  public static IEnumerable<SerializableItem> GetSerializableItems(TypeReference type, bool serializeFields)
175  {
176  foreach (var serializableItemOriginal in GetSerializableItems(type.Resolve(), serializeFields))
177  {
178  var serializableItem = serializableItemOriginal;
179 
180  // Try to resolve open generic types with context to have closed types.
181  if (serializableItem.Type.ContainsGenericParameter())
182  {
183  serializableItem.Type = ResolveGenericsVisitor.Process(type, serializableItem.Type);
184  }
185 
186  yield return serializableItem;
187  }
188  }
189 
190  public static IEnumerable<SerializableItem> GetSerializableItems(TypeDefinition type, bool serializeFields)
191  {
193 
194  var fields = new List<FieldDefinition>();
195  var properties = new List<PropertyDefinition>();
196 
197  var fieldEnum = type.Fields.Where(x => x.IsPublic && !x.IsStatic);
198 
199  // If there is a explicit or sequential layout, use offset, otherwise use name
200  // (not sure if Cecil follow declaration order, in which case it could be OK to not sort;
201  // sorting has the advantage of being more resistant to type upgrade, when field is added/remove, as long as field name is saved)
202  if (type.IsSequentialLayout || type.IsExplicitLayout)
203  fieldEnum = fieldEnum.OrderBy(x => x.Offset);
204  else
205  fieldEnum = fieldEnum.OrderBy(x => x.Name);
206 
207  foreach (var field in fieldEnum)
208  {
209  fields.Add(field);
210  }
211 
212  foreach (var property in type.Properties.OrderBy(x => x.Name))
213  {
214  // Need a non-static public get method
215  if (property.GetMethod == null || !property.GetMethod.IsPublic || property.GetMethod.IsStatic)
216  continue;
217 
218  // If it's a struct (!IsValueType), we need a public set method as well
219  if (property.PropertyType.IsValueType && (property.SetMethod == null || !property.SetMethod.IsPublic))
220  continue;
221 
222  // Only take virtual properties (override ones will be handled by parent serializers)
223  if (property.GetMethod.IsVirtual && !property.GetMethod.IsNewSlot)
224  continue;
225 
226  properties.Add(property);
227  }
228 
229  if (type.IsClass && !type.IsValueType)
230  flags = ComplexTypeSerializerFlags.SerializePublicFields | ComplexTypeSerializerFlags.SerializePublicProperties;
231  else if (type.Fields.Any(x => x.IsPublic && !x.IsStatic))
232  flags = ComplexTypeSerializerFlags.SerializePublicFields;
233  else
234  flags = ComplexTypeSerializerFlags.SerializePublicProperties;
235 
236  if ((flags & ComplexTypeSerializerFlags.SerializePublicFields) != 0)
237  {
238  foreach (var field in fields)
239  {
240  if (field.IsInitOnly)
241  continue;
242  if (field.CustomAttributes.Any(x => x.AttributeType.FullName == "SiliconStudio.Core.DataMemberIgnoreAttribute"))
243  continue;
244  var attributes = field.CustomAttributes;
245  var fixedAttribute = field.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == typeof(FixedBufferAttribute).FullName);
246  yield return new SerializableItem { MemberInfo = field, Type = field.FieldType, Name = field.Name, Attributes = attributes, AssignBack = true, NeedReference = false, HasFixedAttribute = fixedAttribute != null };
247  }
248  }
249  if ((flags & ComplexTypeSerializerFlags.SerializePublicProperties) != 0)
250  {
251  // Only process properties with public get and set methods
252  foreach (var property in properties)
253  {
254  // Ignore properties with indexer
255  if (property.GetMethod.Parameters.Count > 0)
256  continue;
257  if (property.CustomAttributes.Any(x => x.AttributeType.FullName == "SiliconStudio.Core.DataMemberIgnoreAttribute"))
258  continue;
259  var attributes = property.CustomAttributes;
260  bool assignBack = property.SetMethod != null && property.SetMethod.IsPublic;
261  yield return new SerializableItem { MemberInfo = property, Type = property.PropertyType, Name = property.Name, Attributes = attributes, AssignBack = assignBack, NeedReference = !type.IsClass || type.IsValueType };
262  }
263  }
264  }
265 
266  protected static string CreateMemberVariableName(IMemberDefinition memberInfo)
267  {
268  var memberVariableName = Char.ToLowerInvariant(memberInfo.Name[0]) + memberInfo.Name.Substring(1);
269  if (forbiddenKeywords.Contains(memberVariableName))
270  memberVariableName += "_";
271  return memberVariableName;
272  }
273 
274  protected IEnumerable<TypeReference> EnumerateSerializerTypes(IEnumerable<TypeReference> memberTypes)
275  {
276  var result = new HashSet<TypeReference>();
277  var objectTypes = new HashSet<string>();
278  foreach (var memberType in memberTypes)
279  {
280  EnumerateSerializerTypes(memberType, objectTypes, result);
281  }
282  return result;
283  }
284 
285  protected void EnumerateSerializerTypes(TypeReference objectType, HashSet<string> objectTypes, HashSet<TypeReference> serializerTypes)
286  {
287  if (objectType.IsGenericParameter)
288  return;
289 
290  // Already processed?
291  if (!objectTypes.Add(objectType.FullName))
292  return;
293 
294  //foreach (var serializerFactory in serializerFactories)
295  //{
296  // var serializerType = serializerFactory.GetSerializer(objectType);
297  //
298  // // Did we find a new serializer type?
299  // if (serializerType == null || !serializerTypes.Add(serializerType))
300  // continue;
301  //
302  // // If yes, recurse on object types this serializer might require, so that they can in turn ask for their serializer
303  // // It is useful for cases such as List<List<A>>.
304  // foreach (var serializerDependency in serializerDependencies)
305  // {
306  // var subObjectTypes = serializerDependency.EnumerateSubTypesFromSerializer(serializerType);
307  //
308  // // Could be null (to avoid unecessary empty enumerables)
309  // if (subObjectTypes == null)
310  // continue;
311  //
312  // foreach (var subObjectType in subObjectTypes)
313  // {
314  // EnumerateSerializerTypes(subObjectType, objectTypes, serializerTypes);
315  // }
316  // }
317  //}
318  }
319 
320 
321  public struct SerializableItem
322  {
323  public bool HasFixedAttribute;
324  public string Name;
325  public IMemberDefinition MemberInfo;
326  public TypeReference Type;
327  public bool NeedReference;
328  public bool AssignBack;
329  public IList<CustomAttribute> Attributes;
330  }
331  }
332 }
333 #endif
Mono.Cecil.TypeAttributes TypeAttributes
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.