public void Generate() { //Find all serializable types that are serializable from/to INamedTypeSymbol[] serializableTypes = TargetAssembly .Where(IsSerializableType) .ToArray(); foreach (INamedTypeSymbol type in serializableTypes) { //THIS SHOULD NEVER HAPPEN! //if (type.IsGenericType && !type.IsUnboundGenericType) // throw new InvalidOperationException($"Encountered unbound generic Type: {type.Name} in top-level search."); //This is for GENERIC types. //We can only realistically support closed generic forward declared types //AND also primitive generic type support. if (type.IsGenericType) { if (type.HasAttributeExact <PrimitiveGenericAttribute>()) { foreach (Type[] gta in PrimitiveGenericAttribute.Instance .Permutations(type.Arity) .Select(em => em.ToArray()) .ToArray()) { //Represents an array of type arguments for symbols ITypeSymbol[] genericTypeArgSymbols = gta.Select(t => CompilationUnit.GetTypeByMetadataName(t.FullName)) .Cast <ITypeSymbol>() .ToArray(); INamedTypeSymbol boundGenericType = type.Construct(genericTypeArgSymbols); WriteSerializerStrategyClass(boundGenericType); } } //If it's marked with KnownGeneric attribute then we should generate //a serializer for each closed generic type. if (type.HasAttributeExact <KnownGenericAttribute>()) { foreach (AttributeData data in type.GetAttributesExact <KnownGenericAttribute>()) { //Skip, this is include attribute. Maybe warn? if (data.ConstructorArguments.IsDefaultOrEmpty) { continue; } //TODO: Does this work? Arrays may work different with API //create types for each tuple of generic attributes INamedTypeSymbol boundGenericType = type.Construct(data.ConstructorArguments.First().Values.Select(v => v.Value).Cast <ITypeSymbol>().ToArray()); WriteSerializerStrategyClass(boundGenericType); } } if (type.HasAttributeExact <ClosedGenericAttribute>()) { foreach (AttributeData closedTypeAttri in type.GetAttributesExact <ClosedGenericAttribute>()) { WriteSerializerStrategyClass((INamedTypeSymbol)closedTypeAttri.ConstructorArguments.First().Value); } } #warning TODO: Reimplement BaseGenericListAttribute support //To support custom generic lists we created this open-ended base attribute //and we just create combinations from it. /*foreach (BaseGenericListAttribute genericAttri in type.GetCustomAttributes<BaseGenericListAttribute>()) * { * //Iterate the generic type list and we can build. * //closed generic types for all variants * foreach(IEnumerable<Type> genericParameterList in genericAttri.Permutations(type.GetGenericParameterCount())) * WriteSerializerStrategyClass(type.MakeGenericType(genericParameterList.ToArray())); * }*/ } else { WriteSerializerStrategyClass(type); } } //It's important that we dynamically emit generic serializers //if none are found in refences or soon-to-be emitted serializers //copy because something will modify this array and then we need to continue until it's empty //because we could have an object graph that goes deep into multiple generics. int genericDepth = 0; do { ITypeSymbol[] genericTypeSymbols = RequiredGenericSerializers.ToArray(); RequiredGenericSerializers.Clear(); EmitRequestedGenericTypeSerializers(genericTypeSymbols); genericDepth++; //This loop is kinda dangerous, but we assume it'll eventually terminate. } while (RequiredGenericSerializers.Any() && genericDepth < 10); if (genericDepth >= 10) { throw new InvalidOperationException($"Automatic generic type serialization depth exceed maximum depth."); } }