private void ProcessDataSerializerGlobalAttributes(CecilSerializerContext context, AssemblyDefinition assembly, bool local) { // Already processed? if (!processedAssemblies.Add(assembly)) { return; } // TODO: Add a flag for ComplexSerializer and transmit it properly (it needs different kind of analysis) // Let's recurse over referenced assemblies foreach (var referencedAssemblyName in assembly.MainModule.AssemblyReferences.ToArray()) { // Avoid processing system assemblies // TODO: Scan what is actually in framework folders if (referencedAssemblyName.Name == "mscorlib" || referencedAssemblyName.Name.StartsWith("System") || referencedAssemblyName.FullName.Contains("PublicKeyToken=31bf3856ad364e35") || // Signed with Microsoft public key (likely part of system libraries) referencedAssemblyName.Name.StartsWith("SharpDX")) { continue; } try { var referencedAssembly = context.Assembly.MainModule.AssemblyResolver.Resolve(referencedAssemblyName); ProcessDataSerializerGlobalAttributes(context, referencedAssembly, false); } catch (AssemblyResolutionException) { continue; } } // Find DataSerializer attribute on assembly and/or types foreach (var dataSerializerAttribute in assembly.CustomAttributes.Concat(assembly.MainModule.GetAllTypes().SelectMany(x => x.CustomAttributes)).Where( x => x.AttributeType.FullName == "Xenko.Core.Serialization.DataSerializerGlobalAttribute") .OrderBy(x => x.ConstructorArguments[0].Value != null ? -1 : 1)) // Order so that we first have the ones which don't require us to go through GenerateSerializer { var dataSerializerType = (TypeReference)dataSerializerAttribute.ConstructorArguments[0].Value; var dataType = (TypeReference)dataSerializerAttribute.ConstructorArguments[1].Value; var mode = (DataSerializerGenericMode)dataSerializerAttribute.ConstructorArguments[2].Value; var inherited = (bool)dataSerializerAttribute.ConstructorArguments[3].Value; var complexSerializer = (bool)dataSerializerAttribute.ConstructorArguments[4].Value; var profile = dataSerializerAttribute.Properties.Where(x => x.Name == "Profile").Select(x => (string)x.Argument.Value).FirstOrDefault() ?? "Default"; if (dataType == null) { if (mode == DataSerializerGenericMode.None) { dataType = FindSerializerDataType(dataSerializerType); } else { throw new InvalidOperationException("Can't deduce data serializer type for generic types."); } } // Reading from custom arguments doesn't have its ValueType properly set dataType = dataType.FixupValueType(); dataSerializerType = dataSerializerType?.FixupValueType(); CecilSerializerContext.SerializableTypeInfo serializableTypeInfo; if (dataSerializerType == null) { // TODO: We should avoid calling GenerateSerializer now just to have the dataSerializerType (we should do so only in a second step) serializableTypeInfo = context.GenerateSerializer(dataType, profile: profile); if (serializableTypeInfo == null) { throw new InvalidOperationException(string.Format("Can't find serializer for type {0}", dataType)); } serializableTypeInfo.Local = local; serializableTypeInfo.ExistingLocal = local; dataSerializerType = serializableTypeInfo.SerializerType; } else { // Add it to list of serializable types serializableTypeInfo = new CecilSerializerContext.SerializableTypeInfo(dataSerializerType, local, mode) { ExistingLocal = local, Inherited = inherited, ComplexSerializer = complexSerializer }; context.AddSerializableType(dataType, serializableTypeInfo, profile); } } }
public void ProcessSerializers(CecilSerializerContext context) { ProcessDataSerializerGlobalAttributes(context, context.Assembly, true); }