public static string GetSimpleClassName(ISerializableTypeDescription serializableType) { #pragma warning disable RS1024 // Compare symbols correctly var uniquifier = RuntimeHelpers.GetHashCode(serializableType).ToString("X"); #pragma warning restore RS1024 // Compare symbols correctly return($"{CodeGenerator.CodeGeneratorName}_Activator_{serializableType.Name}_{uniquifier}"); }
public SerializableMember(LibraryTypes wellKnownTypes, ISerializableTypeDescription type, IMemberDescription member, int ordinal) { this.wellKnownTypes = wellKnownTypes; this.model = type.SemanticModel; this.Description = member; this.ordinal = ordinal; }
public static TypeSyntax GetPartialSerializerTypeName(this ISerializableTypeDescription type) { var genericArity = type.TypeParameters.Length; var name = SerializerGenerator.GetSimpleClassName(type); if (genericArity > 0) { name += $"<{new string(',', genericArity - 1)}>"; } return(ParseTypeName(name)); }
public static TypeSyntax GetActivatorTypeName(this ISerializableTypeDescription type) { var genericArity = type.TypeParameters.Count; var name = ActivatorGenerator.GetSimpleClassName(type); if (genericArity > 0) { name += $"<{new string(',', genericArity - 1)}>"; } return(ParseTypeName(type.GeneratedNamespace + "." + name)); }
public static ClassDeclarationSyntax GenerateCopier( LibraryTypes libraryTypes, ISerializableTypeDescription type) { var simpleClassName = GetSimpleClassName(type); var members = new List <ISerializableMember>(); foreach (var member in type.Members) { if (member is ISerializableMember serializable) { members.Add(serializable); } else if (member is IFieldDescription or IPropertyDescription) { members.Add(new SerializableMember(libraryTypes, type, member, members.Count)); }
private static ClassDeclarationSyntax AddGenericTypeConstraints(ClassDeclarationSyntax classDeclaration, ISerializableTypeDescription serializableType) { classDeclaration = classDeclaration.WithTypeParameterList(TypeParameterList(SeparatedList(serializableType.TypeParameters.Select(tp => TypeParameter(tp.Name))))); var constraints = new List <TypeParameterConstraintSyntax>(); foreach (var tp in serializableType.TypeParameters) { constraints.Clear(); if (tp.HasReferenceTypeConstraint) { constraints.Add(ClassOrStructConstraint(SyntaxKind.ClassConstraint)); } if (tp.HasValueTypeConstraint) { constraints.Add(ClassOrStructConstraint(SyntaxKind.StructConstraint)); } foreach (var c in tp.ConstraintTypes) { constraints.Add(TypeConstraint(c.ToTypeSyntax())); } if (tp.HasConstructorConstraint) { constraints.Add(ConstructorConstraint()); } if (constraints.Count > 0) { classDeclaration = classDeclaration.AddConstraintClauses(TypeParameterConstraintClause(tp.Name).AddConstraints(constraints.ToArray())); } } return(classDeclaration); }
public static string GetSimpleClassName(ISerializableTypeDescription serializableType) { var uniquifier = RuntimeHelpers.GetHashCode(serializableType).ToString("X"); return($"{CodeGenerator.CodeGeneratorName}_Serializer_{serializableType.Name}_{uniquifier}"); }
private static MemberDeclarationSyntax GenerateDeserializeMethod( ISerializableTypeDescription type, List <FieldDescription> serializerFields, List <SerializableMember> members, LibraryTypes libraryTypes) { var returnType = PredefinedType(Token(SyntaxKind.VoidKeyword)); var readerParam = "reader".ToIdentifierName(); var instanceParam = "instance".ToIdentifierName(); var fieldIdVar = "fieldId".ToIdentifierName(); var headerVar = "header".ToIdentifierName(); var body = new List <StatementSyntax> { // C#: uint fieldId = 0; LocalDeclarationStatement( VariableDeclaration( PredefinedType(Token(SyntaxKind.UIntKeyword)), SingletonSeparatedList(VariableDeclarator(fieldIdVar.Identifier) .WithInitializer(EqualsValueClause(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))))))) }; if (type.HasComplexBaseType) { // C#: this.baseTypeSerializer.Deserialize(ref reader, instance); body.Add( ExpressionStatement( InvocationExpression( ThisExpression().Member(BaseTypeSerializerFieldName.ToIdentifierName()).Member(DeserializeMethodName), ArgumentList(SeparatedList(new[] { Argument(readerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)), Argument(instanceParam) }))))); } body.Add(WhileStatement(LiteralExpression(SyntaxKind.TrueLiteralExpression), Block(GetDeserializerLoopBody()))); var parameters = new[] { Parameter(readerParam.Identifier).WithType(libraryTypes.Reader.ToTypeSyntax()).WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))), Parameter(instanceParam.Identifier).WithType(type.TypeSyntax) }; if (type.IsValueType) { parameters[1] = parameters[1].WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))); } return(MethodDeclaration(returnType, DeserializeMethodName) .AddModifiers(Token(SyntaxKind.PublicKeyword)) .AddParameterListParameters(parameters) .AddBodyStatements(body.ToArray())); // Create the loop body. List <StatementSyntax> GetDeserializerLoopBody() { return(new List <StatementSyntax> { // C#: var header = reader.ReadFieldHeader(); LocalDeclarationStatement( VariableDeclaration( IdentifierName("var"), SingletonSeparatedList( VariableDeclarator(headerVar.Identifier) .WithInitializer(EqualsValueClause(InvocationExpression(readerParam.Member("ReadFieldHeader"), ArgumentList())))))), // C#: if (header.IsEndBaseOrEndObject) break; IfStatement(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, headerVar, IdentifierName("IsEndBaseOrEndObject")), BreakStatement()), // C#: fieldId += header.FieldIdDelta; ExpressionStatement( AssignmentExpression( SyntaxKind.AddAssignmentExpression, fieldIdVar, Token(SyntaxKind.PlusEqualsToken), MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, headerVar, IdentifierName("FieldIdDelta")))), // C#: switch (fieldId) { ... } SwitchStatement(ParenthesizedExpression(fieldIdVar), List(GetSwitchSections())) }); } // Creates switch sections for each member. List <SwitchSectionSyntax> GetSwitchSections() { var switchSections = new List <SwitchSectionSyntax>(); foreach (var member in members) { var description = member.Description; // C#: case <fieldId>: var label = CaseSwitchLabel(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(description.FieldId))); // C#: instance.<member> = this.<codec>.ReadValue(ref reader, header); var codec = serializerFields.OfType <ICodecDescription>() .Concat(libraryTypes.StaticCodecs) .First(f => f.UnderlyingType.Equals(GetExpectedType(description.Type))); // Codecs can either be static classes or injected into the constructor. // Either way, the member signatures are the same. var memberType = GetExpectedType(description.Type); var staticCodec = libraryTypes.StaticCodecs.FirstOrDefault(c => c.UnderlyingType.Equals(memberType)); ExpressionSyntax codecExpression; if (staticCodec != null) { codecExpression = staticCodec.CodecType.ToNameSyntax(); } else { var instanceCodec = serializerFields.OfType <CodecFieldDescription>().First(f => f.UnderlyingType.Equals(memberType)); codecExpression = ThisExpression().Member(instanceCodec.FieldName); } ExpressionSyntax readValueExpression = InvocationExpression( codecExpression.Member("ReadValue"), ArgumentList(SeparatedList(new[] { Argument(readerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)), Argument(headerVar) }))); if (!codec.UnderlyingType.Equals(member.Type)) { // If the member type type differs from the codec type (eg because the member is an array), cast the result. readValueExpression = CastExpression(description.Type.ToTypeSyntax(), readValueExpression); } var memberAssignment = ExpressionStatement(member.GetSetter(instanceParam, readValueExpression)); var caseBody = List(new StatementSyntax[] { memberAssignment, BreakStatement() }); // Create the switch section with a break at the end. // C#: break; switchSections.Add(SwitchSection(SingletonList <SwitchLabelSyntax>(label), caseBody)); } // Add the default switch section. var consumeUnknown = ExpressionStatement(InvocationExpression(readerParam.Member("ConsumeUnknownField"), ArgumentList(SeparatedList(new[] { Argument(headerVar) })))); switchSections.Add(SwitchSection(SingletonList <SwitchLabelSyntax>(DefaultSwitchLabel()), List(new StatementSyntax[] { consumeUnknown, BreakStatement() }))); return(switchSections); } }
private static MemberDeclarationSyntax GenerateSerializeMethod( ISerializableTypeDescription type, List <FieldDescription> serializerFields, List <SerializableMember> members, LibraryTypes libraryTypes) { var returnType = PredefinedType(Token(SyntaxKind.VoidKeyword)); var writerParam = "writer".ToIdentifierName(); var instanceParam = "instance".ToIdentifierName(); var body = new List <StatementSyntax>(); if (type.HasComplexBaseType) { body.Add( ExpressionStatement( InvocationExpression( ThisExpression().Member(BaseTypeSerializerFieldName.ToIdentifierName()).Member(SerializeMethodName), ArgumentList(SeparatedList(new[] { Argument(writerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)), Argument(instanceParam) }))))); body.Add(ExpressionStatement(InvocationExpression(writerParam.Member("WriteEndBase"), ArgumentList()))); } // Order members according to their FieldId, since fields must be serialized in order and FieldIds are serialized as deltas. uint previousFieldId = 0; foreach (var member in members.OrderBy(m => m.Description.FieldId)) { var description = member.Description; var fieldIdDelta = description.FieldId - previousFieldId; previousFieldId = description.FieldId; // Codecs can either be static classes or injected into the constructor. // Either way, the member signatures are the same. var memberType = GetExpectedType(description.Type); var staticCodec = libraryTypes.StaticCodecs.FirstOrDefault(c => c.UnderlyingType.Equals(memberType)); ExpressionSyntax codecExpression; if (staticCodec != null) { codecExpression = staticCodec.CodecType.ToNameSyntax(); } else { var instanceCodec = serializerFields.OfType <CodecFieldDescription>().First(f => f.UnderlyingType.Equals(memberType)); codecExpression = ThisExpression().Member(instanceCodec.FieldName); } var expectedType = serializerFields.OfType <TypeFieldDescription>().First(f => f.UnderlyingType.Equals(memberType)); body.Add( ExpressionStatement( InvocationExpression( codecExpression.Member("WriteField"), ArgumentList( SeparatedList( new[] { Argument(writerParam).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)), Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(fieldIdDelta))), Argument(expectedType.FieldName.ToIdentifierName()), Argument(member.GetGetter(instanceParam)) }))))); } var parameters = new[] { Parameter("writer".ToIdentifier()).WithType(libraryTypes.Writer.ToTypeSyntax()).WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))), Parameter("instance".ToIdentifier()).WithType(type.TypeSyntax) }; if (type.IsValueType) { parameters[1] = parameters[1].WithModifiers(TokenList(Token(SyntaxKind.InKeyword))); } return(MethodDeclaration(returnType, SerializeMethodName) .AddModifiers(Token(SyntaxKind.PublicKeyword)) .AddParameterListParameters(parameters) .AddTypeParameterListParameters(TypeParameter("TBufferWriter")) .AddConstraintClauses(TypeParameterConstraintClause("TBufferWriter").AddConstraints(TypeConstraint(libraryTypes.IBufferWriter.Construct(libraryTypes.Byte).ToTypeSyntax()))) .AddBodyStatements(body.ToArray())); }
private static List <FieldDescription> GetFieldDescriptions( ISerializableTypeDescription serializableTypeDescription, List <SerializableMember> members, LibraryTypes libraryTypes) { var fields = new List <FieldDescription>(); fields.AddRange(serializableTypeDescription.Members.Select(m => GetExpectedType(m.Type)).Distinct().Select(GetTypeDescription)); if (serializableTypeDescription.HasComplexBaseType) { fields.Add(new InjectedFieldDescription(libraryTypes.PartialSerializer.Construct(serializableTypeDescription.BaseType).ToTypeSyntax(), BaseTypeSerializerFieldName)); } // Add a codec field for any field in the target which does not have a static codec. fields.AddRange(serializableTypeDescription.Members .Select(m => GetExpectedType(m.Type)).Distinct() .Where(t => !libraryTypes.StaticCodecs.Any(c => c.UnderlyingType.Equals(t))) .Select(GetCodecDescription)); foreach (var member in members) { if (!member.HasAccessibleGetter) { fields.Add(GetGetterDescription(member)); } if (!member.HasAccessibleSetter) { fields.Add(GetSetterDescription(member)); } } return(fields); CodecFieldDescription GetCodecDescription(ITypeSymbol t) { var codecType = libraryTypes.FieldCodec.Construct(t).ToTypeSyntax(); var fieldName = '_' + ToLowerCamelCase(t.GetValidIdentifier()) + "Codec"; return(new CodecFieldDescription(codecType, fieldName, t)); } TypeFieldDescription GetTypeDescription(ITypeSymbol t) { var fieldName = ToLowerCamelCase(t.GetValidIdentifier()) + "Type"; return(new TypeFieldDescription(libraryTypes.Type.ToTypeSyntax(), fieldName, t)); } GetterFieldDescription GetGetterDescription(SerializableMember member) { var containingType = member.Field.ContainingType; var getterType = libraryTypes.Func_2.Construct(containingType, member.SafeType).ToTypeSyntax(); return(new GetterFieldDescription(getterType, member.GetterFieldName, member.Field.Type, member)); } SetterFieldDescription GetSetterDescription(SerializableMember member) { var containingType = member.Field.ContainingType; TypeSyntax fieldType; if (containingType != null && containingType.IsValueType) { fieldType = libraryTypes.ValueTypeSetter_2.Construct(containingType, member.SafeType).ToTypeSyntax(); } else { fieldType = libraryTypes.Action_2.Construct(containingType, member.SafeType).ToTypeSyntax(); } return(new SetterFieldDescription(fieldType, member.SetterFieldName, member.Field.Type, member)); } string ToLowerCamelCase(string input) => char.IsLower(input, 0) ? input : char.ToLowerInvariant(input[0]) + input.Substring(1); }
public static ClassDeclarationSyntax GenerateSerializer(Compilation compilation, LibraryTypes libraryTypes, ISerializableTypeDescription type) { var simpleClassName = GetSimpleClassName(type); var serializerInterface = type.IsValueType ? libraryTypes.ValueSerializer : libraryTypes.PartialSerializer; var baseInterface = serializerInterface.ToTypeSyntax(type.TypeSyntax); var members = new List <SerializableMember>(); foreach (var member in type.Members) { if (member.Member is IFieldSymbol field) { members.Add(new SerializableMember(libraryTypes, type, member, members.Count)); } } var fieldDescriptions = GetFieldDescriptions(type, members, libraryTypes); var fieldDeclarations = GetFieldDeclarations(fieldDescriptions); var ctor = GenerateConstructor(simpleClassName, fieldDescriptions); var serializeMethod = GenerateSerializeMethod(type, fieldDescriptions, members, libraryTypes); var deserializeMethod = GenerateDeserializeMethod(type, fieldDescriptions, members, libraryTypes); var classDeclaration = ClassDeclaration(simpleClassName) .AddBaseListTypes(SimpleBaseType(baseInterface)) .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword)) .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax()))) .AddMembers(fieldDeclarations) .AddMembers(ctor, serializeMethod, deserializeMethod); if (type.IsGenericType) { classDeclaration = AddGenericTypeConstraints(classDeclaration, type); } return(classDeclaration); }
public static string GetSimpleClassName(ISerializableTypeDescription serializableType) => GetSimpleClassName(serializableType.Name);
public static ClassDeclarationSyntax GenerateActivator(Compilation compilation, LibraryTypes libraryTypes, ISerializableTypeDescription type) { var simpleClassName = GetSimpleClassName(type); var baseInterface = libraryTypes.IActivator_1.ToTypeSyntax(type.TypeSyntax); var classDeclaration = ClassDeclaration(simpleClassName) .AddBaseListTypes(SimpleBaseType(baseInterface)) .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword)) .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax()))) .AddMembers(GenerateCreateMethod(libraryTypes, type)); if (type.IsGenericType) { classDeclaration = AddGenericTypeConstraints(classDeclaration, type); } return(classDeclaration); }
public static string GetSimpleClassName(ISerializableTypeDescription serializableType) => $"Activator_{serializableType.Name}";
public static ClassDeclarationSyntax GenerateActivator(LibraryTypes libraryTypes, ISerializableTypeDescription type) { var simpleClassName = GetSimpleClassName(type); var baseInterface = libraryTypes.IActivator_1.ToTypeSyntax(type.TypeSyntax); var orderedFields = new List <ConstructorArgument>(); var index = 0; if (type.ActivatorConstructorParameters is { Count : > 0 } parameters) { foreach (var arg in parameters) { orderedFields.Add(new ConstructorArgument { Type = arg, FieldName = $"_arg{index}", ParameterName = $"arg{index}" }); index++; } } var members = new List <MemberDeclarationSyntax>(); foreach (var field in orderedFields) { members.Add( FieldDeclaration(VariableDeclaration(field.Type, SingletonSeparatedList(VariableDeclarator(field.FieldName)))) .AddModifiers( Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ReadOnlyKeyword))); } members.Add(GenerateConstructor(libraryTypes, simpleClassName, orderedFields)); members.Add(GenerateCreateMethod(libraryTypes, type, orderedFields)); var classDeclaration = ClassDeclaration(simpleClassName) .AddBaseListTypes(SimpleBaseType(baseInterface)) .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword)) .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(libraryTypes.RegisterActivatorAttribute.ToNameSyntax())))) .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax()))) .AddMembers(members.ToArray()); if (type.IsGenericType) { classDeclaration = SyntaxFactoryUtility.AddGenericTypeParameters(classDeclaration, type.TypeParameters); } return(classDeclaration); }
private static MemberDeclarationSyntax GenerateCreateMethod(LibraryTypes libraryTypes, ISerializableTypeDescription type) { var createObject = type.GetObjectCreationExpression(libraryTypes); return(MethodDeclaration(type.TypeSyntax, "Create") .WithExpressionBody(ArrowExpressionClause(createObject)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) .AddModifiers(Token(SyntaxKind.PublicKeyword))); }
public static ClassDeclarationSyntax GenerateCopier( LibraryTypes libraryTypes, ISerializableTypeDescription type) { var simpleClassName = GetSimpleClassName(type); var members = new List <ISerializableMember>(); foreach (var member in type.Members) { if (member is ISerializableMember serializable) { members.Add(serializable); } else if (member is IFieldDescription field) { members.Add(new SerializableMember(libraryTypes, type, field, members.Count)); } else if (member is MethodParameterFieldDescription methodParameter) { members.Add(new SerializableMethodMember(methodParameter)); } } var accessibility = type.Accessibility switch { Accessibility.Public => SyntaxKind.PublicKeyword, _ => SyntaxKind.InternalKeyword, }; var classDeclaration = ClassDeclaration(simpleClassName) .AddBaseListTypes(SimpleBaseType(libraryTypes.DeepCopier_1.ToTypeSyntax(type.TypeSyntax))) .AddModifiers(Token(accessibility), Token(SyntaxKind.SealedKeyword)) .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax()))); if (type.IsImmutable) { var copyMethod = GenerateImmutableTypeCopyMethod(type, libraryTypes); classDeclaration = classDeclaration.AddMembers(copyMethod); } else { var fieldDescriptions = GetFieldDescriptions(type, members, libraryTypes); var fieldDeclarations = GetFieldDeclarations(fieldDescriptions); var ctor = GenerateConstructor(libraryTypes, simpleClassName, fieldDescriptions); var copyMethod = GenerateMemberwiseDeepCopyMethod(type, fieldDescriptions, members, libraryTypes); classDeclaration = classDeclaration .AddMembers(copyMethod) .AddMembers(fieldDeclarations) .AddMembers(ctor); if (!type.IsSealedType) { classDeclaration = classDeclaration .AddMembers(GenerateBaseCopierDeepCopyMethod(type, fieldDescriptions, members, libraryTypes)) .AddBaseListTypes(SimpleBaseType(libraryTypes.BaseCopier_1.ToTypeSyntax(type.TypeSyntax))); } } if (type.IsGenericType) { classDeclaration = SyntaxFactoryUtility.AddGenericTypeParameters(classDeclaration, type.TypeParameters); } return(classDeclaration); }