private SyntaxNodeOrToken YieldSerializerTypeName() { //We need special case handling for string arrays. if (ElementType.IsTypeExact <string>()) { StringTypeSerializationStatementsBlockEmitter stringSerializerEmitter = new StringTypeSerializationStatementsBlockEmitter(ActualType, Member, Mode); InvocationExpressionSyntax expressionSyntax = stringSerializerEmitter.Create(); TypeNameTypeCollector genericNameCollector = new TypeNameTypeCollector(); genericNameCollector.Visit(expressionSyntax); //Now we analyze the expression to determine the string type information return(genericNameCollector.Types.First()); } else if (ElementType.IsEnumType()) { //TODO: Enum string arrays aren't supported. This will break if they send EnumString //Send element type instead of array type, it's SO much easier that way! But kinda hacky EnumTypeSerializerStatementsBlockEmitter emitter = new EnumTypeSerializerStatementsBlockEmitter(ActualType.ElementType, Member, Mode); InvocationExpressionSyntax invokeSyntax = emitter.Create(); TypeNameTypeCollector genericNameCollector = new TypeNameTypeCollector(); genericNameCollector.Visit(invokeSyntax); //Now we analyze the expression to determine the Enum serializer type. return(genericNameCollector.Types.First()); } else { return(IdentifierName(GeneratedSerializerNameStringBuilder.Create(ElementType).BuildName(Member))); } }
private SyntaxList <StatementSyntax> EmitTypesMemberSerialization(ITypeSymbol currentType, SyntaxList <StatementSyntax> statements) { if (currentType is INamedTypeSymbol namedSymbol) { if (namedSymbol.IsUnboundGenericType) { throw new InvalidOperationException($"Cannot emit member serialization for open generic Type: {namedSymbol.Name}"); } } //Conceptually, we need to find ALL serializable members foreach (ISymbol mi in currentType .GetMembers() .Where(m => !m.IsStatic) .Where(m => m.HasAttributeExact <WireMemberAttribute>()) .OrderBy(m => { //Seperated lines for debugging purposes. AttributeData attri = m.GetAttributeExact <WireMemberAttribute>(); ImmutableArray <TypedConstant> attriArgs = attri.ConstructorArguments; string value = attriArgs.First().ToCSharpString(); return(WireMemberAttribute.Parse(value)); })) //order is important, we must emit in order!! { //Basically doesn't matter if it's a field or property, we just wanna know the Type //The reason is, setting and getting fields vs members are same syntax ITypeSymbol memberType = GetMemberTypeInfo(mi); AnalyzeMemberTypeForGenerics(memberType); FieldDocumentationStatementsBlockEmitter commentEmitter = new FieldDocumentationStatementsBlockEmitter(memberType, mi); statements = statements.AddRange(commentEmitter.CreateStatements()); //The serializer is requesting we DON'T WRITE THIS! So we skip if (Mode == SerializationMode.Write) { if (mi.HasAttributeExact <DontWriteAttribute>()) { continue; } } else if (Mode == SerializationMode.Read) { if (mi.HasAttributeExact <DontReadAttribute>()) { continue; } } //This handles OPTIONAL fields that may or may not be included if (mi.HasAttributeExact <OptionalAttribute>()) { OptionalFieldStatementsBlockEmitter emitter = new OptionalFieldStatementsBlockEmitter(memberType, mi); statements = statements.AddRange(emitter.CreateStatements()); } InvocationExpressionSyntax invokeSyntax = null; //We know the type, but we have to do special handling depending on on its type if (mi.HasAttributeExact <CustomTypeSerializerAttribute>() || memberType.HasAttributeExact <CustomTypeSerializerAttribute>()) { //So TYPES and PROPERTIES may both reference a custom serializer. //So we should prefer field/prop attributes over the type. AttributeData attribute = mi.HasAttributeExact <CustomTypeSerializerAttribute>() ? mi.GetAttributeExact <CustomTypeSerializerAttribute>() : memberType.GetAttributeExact <CustomTypeSerializerAttribute>(); //It's DEFINITELY not null. OverridenSerializationGenerator emitter = new OverridenSerializationGenerator(memberType, mi, Mode, (ITypeSymbol)attribute.ConstructorArguments.First().Value); invokeSyntax = emitter.Create(); } else if (memberType.IsPrimitive()) { //Easy case of primitive serialization PrimitiveTypeSerializationStatementsBlockEmitter emitter = new PrimitiveTypeSerializationStatementsBlockEmitter(memberType, mi, Mode); invokeSyntax = emitter.Create(); } else if (memberType.SpecialType == SpecialType.System_String) { var emitter = new StringTypeSerializationStatementsBlockEmitter(memberType, mi, Mode); invokeSyntax = emitter.Create(); } else if (memberType.SpecialType == SpecialType.System_Array || memberType is IArrayTypeSymbol) { var emitter = new ArrayTypeSerializationStatementsBlockEmitter((IArrayTypeSymbol)memberType, mi, Mode); invokeSyntax = emitter.Create(); } else if (memberType.IsEnumType()) //Enum type { var emitter = new EnumTypeSerializerStatementsBlockEmitter(memberType, mi, Mode); invokeSyntax = emitter.Create(); } else if (memberType.IsReferenceType && memberType.TypeKind == TypeKind.Class) { var emitter = new ComplexTypeSerializerStatementsBlockEmitter((INamedTypeSymbol)memberType, mi, Mode); invokeSyntax = emitter.Create(); } else { bool isGenericType = currentType is INamedTypeSymbol n && n.IsGenericType; bool isUnboundedGenericType = currentType is INamedTypeSymbol n2 && n2.IsUnboundGenericType; throw new NotImplementedException($"TODO: Cannot handle Type: {memberType} ContainingType: {currentType} MetadataType: {currentType.GetType().Name} Generic: {isGenericType} UnboundGeneric: {isUnboundedGenericType}"); } //Now we check if compression was requested if (invokeSyntax != null && mi.HasAttributeExact <CompressAttribute>()) { TypeNameTypeCollector collector = new TypeNameTypeCollector(); collector.Visit(invokeSyntax); //Replace the invokcation with an invokation to the compression decorator. FullSerializerMethodInvokationEmitter emitter = new FullSerializerMethodInvokationEmitter(Mode, $"{WoWZLibCompressionTypeSerializerDecorator.TYPE_NAME}<{collector.Types.First().ToFullString()}, {memberType.ToFullName()}>", mi); invokeSyntax = emitter.Create(); } if (invokeSyntax != null) { if (Mode == SerializationMode.Write) { statements = statements.Add(invokeSyntax.ToStatement()); } else if (Mode == SerializationMode.Read) { //Read generation is abit more complicated. //We must emit the assignment too ReadAssignmentStatementsBlockEmitter emitter = new ReadAssignmentStatementsBlockEmitter(memberType, mi, Mode, invokeSyntax); statements = statements.AddRange(emitter.CreateStatements()); } } //TODO: These don't work!! //Add 2 line breaks //statements = statements.AddRange(new EmptyLineStatementBlockEmitter().CreateStatements()); //statements = statements.AddRange(new EmptyLineStatementBlockEmitter().CreateStatements()); } return(statements); }