feat: Source generated Serialization/Deserialization (#550)
### Features
* Fully abstracts serialization by using compile-time attributes.
* Supports serializing the following:
- Primitives (integers, strings, etc)
- IP Addresses
- BigDecimal
- DateTime, Delta DateTimes
- TimeSpan
- Server.Race
- Server.Map
- Point2D, Point3D, Rect2D, Rect3D
- Existing/New `ISerializable` references
- Lists/Sets of serializable types
- Type with a `Serialize` method and constructor that takes an `IGenericReader`
* Supports forward-only migration
* Supports existing RunUO deserialization for older versions by changing to the following signature:
- `public void OldDeserialize(IGenericReader reader, int version)`
- Must remove deserializing the version since this is already done
* Supports serializing from private fields or custom made properties.
* Types do not require inheriting Item/Mobile. Code gen will fully create `ISerializable` information.
- This is not recommended yet, since it requires wiring to `Persistence` which will cause lots of unresolved symbol errors until code gen is built.
### Example
```cs
using System.Collections.Generic;
namespace Server.Items
{
[Serializable(1)]
public partial class TestItem1 : Item
{
[SerializableField(1)]
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
private List<Item> _someProperty;
private void Deserialize(IGenericReader reader, int version)
{
}
}
}
```
Generates this:
```cs
namespace Server.Items
{
public partial class TestItem1
{
#pragma warning disable 0414
private const int _version = 1;
#pragma warning restore 0414
[CommandProperty(AccessLevel.Administrator)]
public System.Collections.Generic.List<Server.Item> SomeProperty
{
get => _someProperty;
set
{
if (value != _someProperty)
{
((ISerializable)this).MarkDirty();
_someProperty = value;
}
}
}
public TestItem1(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
var savePosition = ((Server.ISerializable)this).SavePosition;
if (savePosition > -1)
{
writer.Seek(savePosition, System.IO.SeekOrigin.Begin);
return;
}
writer.WriteEncodedInt(_version);
writer.Write(_someProperty);
}
public override void Deserialize(IGenericReader reader)
{
var version = reader.ReadEncodedInt();
if (version < 1)
{
OldDeserialize(reader, version);
((Server.ISerializable)this).MarkDirty();
return;
}
SomeProperty = reader.ReadEntityList<Server.Item>();
}
}
}
```
And this:
```json
{
"version": 1,
"type": "TestItem1",
"properties": [
{
"name": "SomeProperty",
"type": "System.Collections.Generic.List\u003CServer.Item\u003E",
"rule": "ListMigrationRule",
"ruleArguments": [
"Server.Item",
"SerializableInterfaceMigrationRule"
]
}
]
}
```
This commit is contained in:
parent
cb66bef0e5
commit
9afa4e4cab
61 changed files with 3203 additions and 142 deletions
|
|
@ -0,0 +1,262 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializableEntityGeneration.Class.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SerializationGenerator
|
||||
{
|
||||
public static partial class SerializableEntityGeneration
|
||||
{
|
||||
public static bool WillBeSerializable(this INamedTypeSymbol classSymbol, GeneratorExecutionContext context)
|
||||
{
|
||||
var compilation = context.Compilation;
|
||||
|
||||
var serializableEntityAttribute =
|
||||
compilation.GetTypeByMetadataName(SERIALIZABLE_ATTRIBUTE);
|
||||
var serializableInterface = compilation.GetTypeByMetadataName(SERIALIZABLE_INTERFACE);
|
||||
|
||||
if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!classSymbol.ContainsInterface(serializableInterface))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var versionValue = classSymbol.GetAttributes()
|
||||
.FirstOrDefault(
|
||||
attr => attr.AttributeClass?.Equals(serializableEntityAttribute, SymbolEqualityComparer.Default) ?? false
|
||||
)?.ConstructorArguments.FirstOrDefault().Value;
|
||||
|
||||
return versionValue != null;
|
||||
}
|
||||
|
||||
public static string GenerateSerializationPartialClass(
|
||||
INamedTypeSymbol classSymbol,
|
||||
IList<IFieldSymbol> fields,
|
||||
GeneratorExecutionContext context,
|
||||
string migrationPath,
|
||||
JsonSerializerOptions jsonSerializerOptions,
|
||||
ImmutableArray<INamedTypeSymbol> serializableTypes
|
||||
)
|
||||
{
|
||||
var compilation = context.Compilation;
|
||||
|
||||
var serializableEntityAttribute =
|
||||
compilation.GetTypeByMetadataName(SERIALIZABLE_ATTRIBUTE);
|
||||
var serializableFieldAttribute =
|
||||
compilation.GetTypeByMetadataName(SERIALIZABLE_FIELD_ATTRIBUTE);
|
||||
var serializableFieldAttrAttribute =
|
||||
compilation.GetTypeByMetadataName(SERIALIZABLE_FIELD_ATTR_ATTRIBUTE);
|
||||
var serializableInterface = compilation.GetTypeByMetadataName(SERIALIZABLE_INTERFACE);
|
||||
|
||||
// This is a class symbol if the containing symbol is the namespace
|
||||
if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we have a parent that is or derives from ISerializable, then we are in override
|
||||
var isOverride = classSymbol.BaseType.ContainsInterface(serializableInterface);
|
||||
|
||||
if (!isOverride && !classSymbol.ContainsInterface(serializableInterface))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var version = classSymbol.GetAttributes()
|
||||
.FirstOrDefault(
|
||||
attr => attr.AttributeClass?.Equals(serializableEntityAttribute, SymbolEqualityComparer.Default) ?? false
|
||||
)?.ConstructorArguments.FirstOrDefault().Value?.ToString();
|
||||
|
||||
if (version == null)
|
||||
{
|
||||
return null; // We don't have the attribute
|
||||
}
|
||||
|
||||
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
|
||||
var className = classSymbol.Name;
|
||||
|
||||
StringBuilder source = new StringBuilder();
|
||||
|
||||
source.GenerateNamespaceStart(namespaceName);
|
||||
|
||||
source.GenerateClassStart(
|
||||
className,
|
||||
isOverride ?
|
||||
ImmutableArray<ITypeSymbol>.Empty :
|
||||
ImmutableArray.Create<ITypeSymbol>(serializableInterface)
|
||||
);
|
||||
|
||||
source.GenerateClassField(
|
||||
AccessModifier.Private,
|
||||
InstanceModifier.Const,
|
||||
"int",
|
||||
"_version",
|
||||
version,
|
||||
true
|
||||
);
|
||||
source.AppendLine();
|
||||
|
||||
var serializableProperties = new List<SerializableProperty>();
|
||||
|
||||
foreach (IFieldSymbol fieldSymbol in fields)
|
||||
{
|
||||
var allAttributes = fieldSymbol.GetAttributes();
|
||||
|
||||
var hasAttribute = allAttributes
|
||||
.Any(
|
||||
attr =>
|
||||
SymbolEqualityComparer.Default.Equals(attr.AttributeClass, serializableFieldAttribute)
|
||||
);
|
||||
|
||||
if (hasAttribute)
|
||||
{
|
||||
foreach (var attr in allAttributes)
|
||||
{
|
||||
if (!SymbolEqualityComparer.Default.Equals(attr.AttributeClass, serializableFieldAttrAttribute))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.AttributeClass == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var ctorArgs = attr.ConstructorArguments;
|
||||
var attrTypeArg = ctorArgs[0];
|
||||
|
||||
if (attrTypeArg.Kind == TypedConstantKind.Primitive && attrTypeArg.Value is string attrStr)
|
||||
{
|
||||
source.AppendLine($" {attrStr}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var attrType = (ITypeSymbol)attrTypeArg.Value;
|
||||
source.GenerateAttribute(attrType.Name, ctorArgs[1].Values);
|
||||
}
|
||||
}
|
||||
|
||||
source.GenerateSerializableProperty(fieldSymbol);
|
||||
source.AppendLine();
|
||||
|
||||
var serializableProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
|
||||
compilation,
|
||||
fieldSymbol.GetPropertyName(),
|
||||
fieldSymbol.Type,
|
||||
allAttributes,
|
||||
serializableTypes
|
||||
);
|
||||
|
||||
serializableProperties.Add(serializableProperty);
|
||||
}
|
||||
}
|
||||
|
||||
// If we are not inheriting ISerializable, then we need to define some stuff
|
||||
if (!isOverride)
|
||||
{
|
||||
// long ISerializable.SavePosition { get; set; }
|
||||
source.GenerateAutoProperty(
|
||||
AccessModifier.None,
|
||||
"long",
|
||||
"ISerializable.SavePosition",
|
||||
AccessModifier.None,
|
||||
AccessModifier.None
|
||||
);
|
||||
source.AppendLine();
|
||||
|
||||
// BufferWriter ISerializable.SaveBuffer { get; set; }
|
||||
source.GenerateAutoProperty(
|
||||
AccessModifier.None,
|
||||
"BufferWriter",
|
||||
"ISerializable.SaveBuffer",
|
||||
AccessModifier.None,
|
||||
AccessModifier.None
|
||||
);
|
||||
source.AppendLine();
|
||||
}
|
||||
|
||||
// Serial constructor
|
||||
source.GenerateSerialCtor(context, className, isOverride);
|
||||
source.AppendLine();
|
||||
|
||||
var versionValue = int.Parse(version);
|
||||
List<SerializableMetadata> migrations;
|
||||
|
||||
if (versionValue > 0)
|
||||
{
|
||||
migrations = SerializableMigration.GetMigrations(
|
||||
migrationPath,
|
||||
classSymbol,
|
||||
versionValue,
|
||||
jsonSerializerOptions
|
||||
);
|
||||
|
||||
for (var i = 0; i < migrations.Count; i++)
|
||||
{
|
||||
var migration = migrations[i];
|
||||
if (migration.Version < versionValue)
|
||||
{
|
||||
source.GenerateMigrationContentStruct(migration);
|
||||
source.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
migrations = new List<SerializableMetadata>();
|
||||
}
|
||||
|
||||
// Serialize Method
|
||||
source.GenerateSerializeMethod(
|
||||
compilation,
|
||||
isOverride,
|
||||
serializableProperties
|
||||
);
|
||||
source.AppendLine();
|
||||
|
||||
// Deserialize Method
|
||||
source.GenerateDeserializeMethod(
|
||||
compilation,
|
||||
isOverride,
|
||||
versionValue,
|
||||
migrations,
|
||||
serializableProperties
|
||||
);
|
||||
|
||||
source.GenerateClassEnd();
|
||||
source.GenerateNamespaceEnd();
|
||||
|
||||
// Write the migration file
|
||||
var newMigration = new SerializableMetadata
|
||||
{
|
||||
Version = versionValue,
|
||||
Type = classSymbol.ToDisplayString(),
|
||||
Properties = serializableProperties
|
||||
};
|
||||
SerializableMigration.WriteMigration(migrationPath, newMigration, jsonSerializerOptions);
|
||||
|
||||
return source.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializableEntityGeneration.DeserializeMethod.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SerializationGenerator
|
||||
{
|
||||
public static partial class SerializableEntityGeneration
|
||||
{
|
||||
public static void GenerateDeserializeMethod(
|
||||
this StringBuilder source,
|
||||
Compilation compilation,
|
||||
bool isOverride,
|
||||
int version,
|
||||
List<SerializableMetadata> migrations,
|
||||
List<SerializableProperty> properties
|
||||
)
|
||||
{
|
||||
var genericReaderInterface = compilation.GetTypeByMetadataName(GENERIC_READER_INTERFACE);
|
||||
|
||||
source.GenerateMethodStart(
|
||||
"Deserialize",
|
||||
AccessModifier.Public,
|
||||
isOverride,
|
||||
"void",
|
||||
ImmutableArray.Create<(ITypeSymbol, string)>((genericReaderInterface, "reader"))
|
||||
);
|
||||
|
||||
const string indent = " ";
|
||||
|
||||
// Version
|
||||
source.AppendLine($"{indent}var version = reader.ReadEncodedInt();");
|
||||
|
||||
if (version > 0)
|
||||
{
|
||||
var nextVersion = 0;
|
||||
|
||||
for (var i = 0; i < migrations.Count; i++)
|
||||
{
|
||||
var migrationVersion = migrations[i].Version;
|
||||
if (migrationVersion == nextVersion)
|
||||
{
|
||||
nextVersion++;
|
||||
}
|
||||
|
||||
source.AppendLine();
|
||||
source.AppendLine($"{indent}if (version == {migrationVersion})");
|
||||
source.AppendLine($"{indent}{{");
|
||||
source.AppendLine($"{indent} MigrateFrom(new V{migrationVersion}Content(reader));");
|
||||
source.AppendLine($"{indent} ((Server.ISerializable)this).MarkDirty();");
|
||||
source.AppendLine($"{indent} return;");
|
||||
source.AppendLine($"{indent}}}");
|
||||
}
|
||||
|
||||
if (nextVersion < version)
|
||||
{
|
||||
source.AppendLine();
|
||||
source.AppendLine($"{indent}if (version < _version)");
|
||||
source.AppendLine($"{indent}{{");
|
||||
source.AppendLine($"{indent} Deserialize(reader, version);");
|
||||
source.AppendLine($"{indent} ((Server.ISerializable)this).MarkDirty();");
|
||||
source.AppendLine($"{indent} return;");
|
||||
source.AppendLine($"{indent}}}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
source.AppendLine();
|
||||
SerializableMigrationRulesEngine.Rules[property.Rule].GenerateDeserializationMethod(
|
||||
source,
|
||||
indent,
|
||||
property
|
||||
);
|
||||
}
|
||||
|
||||
source.GenerateMethodEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializableEntityGeneration.MetadataTypes.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SerializationGenerator
|
||||
{
|
||||
public static partial class SerializableEntityGeneration
|
||||
{
|
||||
public const string LIST_CLASS = "System.Collections.Generic.List`1";
|
||||
public const string HASHSET_CLASS = "System.Collections.Generic.HashSet`1";
|
||||
public const string IP_CLASS = "System.Net.IPAddress";
|
||||
public const string KEYVALUEPAIR_STRUCT = "System.Collections.Generic.KeyValuePair";
|
||||
|
||||
public const string SERIALIZABLE_ATTRIBUTE = "Server.SerializableAttribute";
|
||||
public const string SERIALIZABLE_FIELD_ATTRIBUTE = "Server.SerializableFieldAttribute";
|
||||
public const string SERIALIZABLE_FIELD_ATTR_ATTRIBUTE = "Server.SerializableFieldAttrAttribute";
|
||||
public const string SERIALIZABLE_INTERFACE = "Server.ISerializable";
|
||||
public const string GENERIC_WRITER_INTERFACE = "Server.IGenericWriter";
|
||||
public const string GENERIC_READER_INTERFACE = "Server.IGenericReader";
|
||||
public const string DELTA_DATE_TIME_ATTRIBUTE = "Server.DeltaDateTimeAttribute";
|
||||
public const string POINT2D_STRUCT = "Server.Point2D";
|
||||
public const string POINT3D_STRUCT = "Server.Point3D";
|
||||
public const string RECTANGLE2D_STRUCT = "Server.Rectangle2D";
|
||||
public const string RECTANGLE3D_STRUCT = "Server.Rectangle3D";
|
||||
public const string RACE_CLASS = "Server.Race";
|
||||
public const string MAP_CLASS = "Server.Map";
|
||||
|
||||
public static bool IsDeltaDateTime(this AttributeData attr, Compilation compilation) =>
|
||||
attr?.IsAttribute(compilation.GetTypeByMetadataName(DELTA_DATE_TIME_ATTRIBUTE)) == true;
|
||||
|
||||
public static bool IsAttribute(this AttributeData attr, ISymbol symbol) =>
|
||||
attr?.AttributeClass?.Equals(symbol, SymbolEqualityComparer.Default) == true;
|
||||
|
||||
public static bool IsEnum(this ITypeSymbol symbol) =>
|
||||
symbol.SpecialType == SpecialType.System_Enum || symbol.TypeKind == TypeKind.Enum;
|
||||
|
||||
public static bool HasSerializableInterface(
|
||||
this ITypeSymbol symbol,
|
||||
Compilation compilation,
|
||||
ImmutableArray<INamedTypeSymbol> serializableTypes
|
||||
) =>
|
||||
symbol.ContainsInterface(compilation.GetTypeByMetadataName(SERIALIZABLE_INTERFACE)) ||
|
||||
serializableTypes.Contains(symbol);
|
||||
|
||||
public static bool Contains(this ImmutableArray<INamedTypeSymbol> symbols, ITypeSymbol symbol) =>
|
||||
symbol is INamedTypeSymbol namedSymbol &&
|
||||
symbols.Contains(namedSymbol, SymbolEqualityComparer.Default);
|
||||
|
||||
public static bool HasGenericReaderCtor(this INamedTypeSymbol symbol, Compilation compilation, out bool requiresParent)
|
||||
{
|
||||
var genericReaderInterface = compilation.GetTypeByMetadataName(GENERIC_READER_INTERFACE);
|
||||
var genericCtor = symbol.Constructors.FirstOrDefault(
|
||||
m => !m.IsStatic &&
|
||||
m.MethodKind == MethodKind.Constructor &&
|
||||
m.Parameters.Length <= 2 &&
|
||||
m.Parameters[0].Equals(genericReaderInterface, SymbolEqualityComparer.Default)
|
||||
);
|
||||
|
||||
requiresParent = genericCtor?.Parameters.Length == 2 && genericCtor.Parameters[1].Equals(symbol, SymbolEqualityComparer.Default);
|
||||
return genericCtor != null;
|
||||
}
|
||||
|
||||
public static bool HasPublicSerializeMethod(
|
||||
this ITypeSymbol symbol,
|
||||
Compilation compilation,
|
||||
ImmutableArray<INamedTypeSymbol> serializableTypes
|
||||
)
|
||||
{
|
||||
if (symbol.HasSerializableInterface(compilation, serializableTypes))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var genericWriterInterface = compilation.GetTypeByMetadataName(GENERIC_WRITER_INTERFACE);
|
||||
|
||||
return symbol.GetAllMethods("Serialize")
|
||||
.Any(
|
||||
m => !m.IsStatic &&
|
||||
m.ReturnsVoid &&
|
||||
m.Parameters.Length == 1 &&
|
||||
m.Parameters[0].Equals(genericWriterInterface, SymbolEqualityComparer.Default) &&
|
||||
m.DeclaredAccessibility == Accessibility.Public
|
||||
);
|
||||
}
|
||||
|
||||
public static bool IsPoint2D(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(POINT2D_STRUCT),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsPoint3D(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(POINT3D_STRUCT),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsRectangle2D(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(RECTANGLE2D_STRUCT),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsRectangle3D(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(RECTANGLE3D_STRUCT),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsIpAddress(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(IP_CLASS),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsRace(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(RACE_CLASS),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsMap(this ISymbol symbol, Compilation compilation) =>
|
||||
symbol.Equals(
|
||||
compilation.GetTypeByMetadataName(MAP_CLASS),
|
||||
SymbolEqualityComparer.Default
|
||||
);
|
||||
|
||||
public static bool IsKeyValuePair(this ISymbol symbol, Compilation compilation) =>
|
||||
(symbol as INamedTypeSymbol)?.ConstructedFrom.Equals(
|
||||
compilation.GetTypeByMetadataName(KEYVALUEPAIR_STRUCT),
|
||||
SymbolEqualityComparer.Default
|
||||
) == true;
|
||||
|
||||
public static bool IsList(this ISymbol symbol, Compilation compilation) =>
|
||||
(symbol as INamedTypeSymbol)?.ConstructedFrom.Equals(
|
||||
compilation.GetTypeByMetadataName(LIST_CLASS),
|
||||
SymbolEqualityComparer.Default
|
||||
) == true;
|
||||
|
||||
public static bool IsHashSet(this ISymbol symbol, Compilation compilation) =>
|
||||
(symbol as INamedTypeSymbol)?.ConstructedFrom.Equals(
|
||||
compilation.GetTypeByMetadataName(HASHSET_CLASS),
|
||||
SymbolEqualityComparer.Default
|
||||
) == true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializableEntityGeneration.Property.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SerializationGenerator
|
||||
{
|
||||
public static partial class SerializableEntityGeneration
|
||||
{
|
||||
public static void GenerateSerializableProperty(
|
||||
this StringBuilder source,
|
||||
IFieldSymbol fieldSymbol
|
||||
)
|
||||
{
|
||||
var fieldName = fieldSymbol.Name;
|
||||
|
||||
source.GeneratePropertyStart(AccessModifier.Public, fieldSymbol);
|
||||
|
||||
// Getter
|
||||
source.GeneratePropertyGetterReturnsField(fieldSymbol);
|
||||
|
||||
// Setter
|
||||
source.GeneratePropertySetterStart(false);
|
||||
source.AppendLine(
|
||||
$@" if (value != {fieldName})
|
||||
{{
|
||||
((ISerializable)this).MarkDirty();
|
||||
{fieldName} = value;
|
||||
}}"
|
||||
);
|
||||
source.GeneratePropertyGetSetEnd(false);
|
||||
|
||||
source.GeneratePropertyEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializableEntityGeneration.SerialCtor.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SerializationGenerator
|
||||
{
|
||||
public static partial class SerializableEntityGeneration
|
||||
{
|
||||
private static readonly ImmutableArray<string> _baseParameters = new[] { "serial" }.ToImmutableArray();
|
||||
public static void GenerateSerialCtor(
|
||||
this StringBuilder source,
|
||||
GeneratorExecutionContext context,
|
||||
string className,
|
||||
bool isOverride
|
||||
)
|
||||
{
|
||||
var serialType = (ITypeSymbol)context.Compilation.GetTypeByMetadataName("Server.Serial");
|
||||
|
||||
source.GenerateConstructorStart(
|
||||
className,
|
||||
AccessModifier.Public,
|
||||
new []{ (serialType, "serial") }.ToImmutableArray(),
|
||||
isOverride ? _baseParameters : ImmutableArray<string>.Empty
|
||||
);
|
||||
|
||||
if (!isOverride)
|
||||
{
|
||||
source.Append(@$" Serial = serial;
|
||||
SetTypeRef(typeof({className}));");
|
||||
}
|
||||
|
||||
source.GenerateMethodEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializableEntityGeneration.SerializeMethod.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SerializationGenerator
|
||||
{
|
||||
public static partial class SerializableEntityGeneration
|
||||
{
|
||||
public static void GenerateSerializeMethod(
|
||||
this StringBuilder source,
|
||||
Compilation compilation,
|
||||
bool isOverride,
|
||||
List<SerializableProperty> properties
|
||||
)
|
||||
{
|
||||
var genericWriterInterface = compilation.GetTypeByMetadataName(GENERIC_WRITER_INTERFACE);
|
||||
|
||||
source.GenerateMethodStart(
|
||||
"Serialize",
|
||||
AccessModifier.Public,
|
||||
isOverride,
|
||||
"void",
|
||||
ImmutableArray.Create<(ITypeSymbol, string)>((genericWriterInterface, "writer"))
|
||||
);
|
||||
|
||||
const string indent = " ";
|
||||
|
||||
source.AppendLine($"{indent}var savePosition = ((Server.ISerializable)this).SavePosition;");
|
||||
source.AppendLine(@$"{indent}if (savePosition > -1)
|
||||
{indent}{{
|
||||
{indent} writer.Seek(savePosition, System.IO.SeekOrigin.Begin);
|
||||
{indent} return;
|
||||
{indent}}}");
|
||||
|
||||
// Version
|
||||
source.AppendLine();
|
||||
source.AppendLine($"{indent}writer.WriteEncodedInt(_version);");
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
source.AppendLine();
|
||||
SerializableMigrationRulesEngine.Rules[property.Rule].GenerateSerializationMethod(
|
||||
source,
|
||||
indent,
|
||||
property
|
||||
);
|
||||
}
|
||||
|
||||
source.GenerateMethodEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue