Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Orleans.CodeGenerator/CodeGeneratorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ public class CodeGeneratorOptions
/// Gets or sets a value indicating whether generated serializers and copiers support adding members using .NET Hot Reload.
/// </summary>
public bool HotReloadSafe { get; set; }

internal bool SupportsUnsafeAccessors { get; set; }
internal bool SupportsGenericUnsafeAccessors { get; set; }
internal bool SupportsVolatileUnsafeAccessors { get; set; }
}
38 changes: 2 additions & 36 deletions src/Orleans.CodeGenerator/CopierGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,33 +131,7 @@ static MemberDeclarationSyntax GetFieldDeclaration(GeneratedFieldDescription des
SingletonSeparatedList(VariableDeclarator(accessor.FieldName).WithInitializer(EqualsValueClause(accessor.InitializationSyntax)))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.ReadOnlyKeyword));
case FieldAccessorDescription accessor when accessor.InitializationSyntax == null:
//[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_Amount")]
//extern static void SetAmount(External instance, int value);
return
MethodDeclaration(
PredefinedType(Token(SyntaxKind.VoidKeyword)),
accessor.AccessorName)
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ExternKeyword), Token(SyntaxKind.StaticKeyword))
.AddAttributeLists(AttributeList(SingletonSeparatedList(
Attribute(IdentifierName("System.Runtime.CompilerServices.UnsafeAccessor"))
.AddArgumentListArguments(
AttributeArgument(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("System.Runtime.CompilerServices.UnsafeAccessorKind"),
IdentifierName("Method"))),
AttributeArgument(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal($"set_{accessor.FieldName}")))
.WithNameEquals(NameEquals("Name"))))))
.WithParameterList(
ParameterList(SeparatedList(
[
Parameter(Identifier("instance")).WithType(accessor.ContainingType),
Parameter(Identifier("value")).WithType(description.FieldType)
])))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
return GetUnsafeAccessorDeclaration(accessor);
default:
var declaration = FieldDeclaration(VariableDeclaration(description.FieldType, SingletonSeparatedList(VariableDeclarator(description.FieldName))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword));
Expand Down Expand Up @@ -264,15 +238,7 @@ private List<GeneratedFieldDescription> GetFieldDescriptions(
{
if (onlyDeepFields && member.IsShallowCopyable) continue;

if (member.GetGetterFieldDescription() is { } getterFieldDescription)
{
fields.Add(getterFieldDescription);
}

if (member.GetSetterFieldDescription() is { } setterFieldDescription)
{
fields.Add(setterFieldDescription);
}
AddFieldAccessorDescriptions(fields, member);
}

for (var hookIndex = 0; hookIndex < serializationHooks.Count; ++hookIndex)
Expand Down
2 changes: 2 additions & 0 deletions src/Orleans.CodeGenerator/LibraryTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ private LibraryTypes(Compilation compilation)
IInvokable = Type("Orleans.Serialization.Invocation.IInvokable");
InvokeMethodNameAttribute = Type("Orleans.InvokeMethodNameAttribute");
RuntimeHelpers = Type("System.Runtime.CompilerServices.RuntimeHelpers");
HasUnsafeAccessorAttribute = TypeOrDefault("System.Runtime.CompilerServices.UnsafeAccessorAttribute") is not null;
InvokableCustomInitializerAttribute = Type("Orleans.InvokableCustomInitializerAttribute");
DefaultInvokableBaseTypeAttribute = Type("Orleans.DefaultInvokableBaseTypeAttribute");
GenerateCodeForDeclaringAssemblyAttribute = Type("Orleans.GenerateCodeForDeclaringAssemblyAttribute");
Expand Down Expand Up @@ -206,6 +207,7 @@ INamedTypeSymbol Type(string metadataName)
}

public INamedTypeSymbol Action_2 { get; private set; }
public bool HasUnsafeAccessorAttribute { get; }
public INamedTypeSymbol TypeManifestProviderBase { get; private set; }
public INamedTypeSymbol Field { get; private set; }
public INamedTypeSymbol DeepCopier_1 { get; private set; }
Expand Down
124 changes: 75 additions & 49 deletions src/Orleans.CodeGenerator/SerializerGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,33 +167,7 @@ static MemberDeclarationSyntax GetFieldDeclaration(GeneratedFieldDescription des
SingletonSeparatedList(VariableDeclarator(accessor.FieldName).WithInitializer(EqualsValueClause(accessor.InitializationSyntax)))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.ReadOnlyKeyword));
case FieldAccessorDescription accessor when accessor.InitializationSyntax == null:
//[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_Amount")]
//extern static void SetAmount(External instance, int value);
return
MethodDeclaration(
PredefinedType(Token(SyntaxKind.VoidKeyword)),
accessor.AccessorName)
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ExternKeyword), Token(SyntaxKind.StaticKeyword))
.AddAttributeLists(AttributeList(SingletonSeparatedList(
Attribute(IdentifierName("System.Runtime.CompilerServices.UnsafeAccessor"))
.AddArgumentListArguments(
AttributeArgument(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("System.Runtime.CompilerServices.UnsafeAccessorKind"),
IdentifierName("Method"))),
AttributeArgument(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal($"set_{accessor.FieldName}")))
.WithNameEquals(NameEquals("Name"))))))
.WithParameterList(
ParameterList(SeparatedList(
[
Parameter(Identifier("instance")).WithType(accessor.ContainingType),
Parameter(Identifier("value")).WithType(description.FieldType)
])))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
return GetUnsafeAccessorDeclaration(accessor);
default:
var declaration = FieldDeclaration(VariableDeclaration(description.FieldType, SingletonSeparatedList(VariableDeclarator(description.FieldName))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword));
Expand All @@ -202,6 +176,39 @@ static MemberDeclarationSyntax GetFieldDeclaration(GeneratedFieldDescription des
}
}

internal static MethodDeclarationSyntax GetUnsafeAccessorDeclaration(FieldAccessorDescription accessor)
{
var instance = Parameter(Identifier("instance")).WithType(accessor.ContainingType);
if (accessor.ContainingTypeIsValueType)
{
instance = instance.AddModifiers(Token(SyntaxKind.RefKeyword));
}

var parameters = new List<ParameterSyntax> { instance };
TypeSyntax returnType;
if (accessor.IsUnsafeFieldAccessor)
{
returnType = RefType(accessor.FieldType);
}
else
{
returnType = PredefinedType(Token(SyntaxKind.VoidKeyword));
parameters.Add(Parameter(Identifier("value")).WithType(accessor.FieldType));
}

return MethodDeclaration(returnType, accessor.AccessorName)
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ExternKeyword), Token(SyntaxKind.StaticKeyword))
.AddAttributeLists(AttributeList(SingletonSeparatedList(
Attribute(ParseName("global::System.Runtime.CompilerServices.UnsafeAccessorAttribute"))
.AddArgumentListArguments(
AttributeArgument(ParseName("global::System.Runtime.CompilerServices.UnsafeAccessorKind")
.Member(accessor.IsUnsafeFieldAccessor ? "Field" : "Method")),
AttributeArgument((accessor.IsUnsafeFieldAccessor ? accessor.FieldName : $"set_{accessor.FieldName}").GetLiteralExpression())
.WithNameEquals(NameEquals("Name"))))))
.WithParameterList(ParameterList(SeparatedList(parameters)))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
}

private ConstructorDeclarationSyntax? GenerateConstructor(string simpleClassName, List<GeneratedFieldDescription> fieldDescriptions)
{
var codecProviderAdded = false;
Expand Down Expand Up @@ -311,15 +318,7 @@ private List<GeneratedFieldDescription> GetFieldDescriptions(

foreach (var member in members)
{
if (member.GetGetterFieldDescription() is { } getterFieldDescription)
{
fields.Add(getterFieldDescription);
}

if (member.GetSetterFieldDescription() is { } setterFieldDescription)
{
fields.Add(setterFieldDescription);
}
AddFieldAccessorDescriptions(fields, member);
}

for (var hookIndex = 0; hookIndex < serializableTypeDescription.SerializationHooks.Count; ++hookIndex)
Expand Down Expand Up @@ -375,6 +374,21 @@ CodecFieldDescription GetCodecDescription(IMemberDescription member, string fiel
}
}

internal static void AddFieldAccessorDescriptions(List<GeneratedFieldDescription> fields, ISerializableMember member)
{
var getter = member.GetGetterFieldDescription();
if (getter is not null)
{
fields.Add(getter);
}

if (member.GetSetterFieldDescription() is { } setter
&& (!setter.IsUnsafeFieldAccessor || setter.AccessorName != getter?.AccessorName))
{
fields.Add(setter);
}
}

private BaseCodecFieldDescription GetBaseTypeField(ISerializableTypeDescription serializableTypeDescription)
{
var baseType = serializableTypeDescription.BaseType;
Expand Down Expand Up @@ -1127,13 +1141,15 @@ internal sealed class CodecFieldTypeFieldDescription(TypeSyntax fieldType, strin
public override bool IsInjected => false;
}

internal sealed class FieldAccessorDescription(TypeSyntax containingType, TypeSyntax fieldType, string fieldName, string accessorName, ExpressionSyntax? initializationSyntax = null, bool lazyInitialization = false) : GeneratedFieldDescription(fieldType, fieldName)
internal sealed class FieldAccessorDescription(TypeSyntax containingType, TypeSyntax fieldType, string fieldName, string accessorName, ExpressionSyntax? initializationSyntax = null, bool lazyInitialization = false, bool isUnsafeFieldAccessor = false, bool containingTypeIsValueType = false) : GeneratedFieldDescription(fieldType, fieldName)
{
public override bool IsInjected => false;
public override bool LazyInitialization { get; } = lazyInitialization;
public readonly string AccessorName = accessorName;
public readonly TypeSyntax ContainingType = containingType;
public readonly ExpressionSyntax? InitializationSyntax = initializationSyntax;
public bool IsUnsafeFieldAccessor { get; } = isUnsafeFieldAccessor;
public bool ContainingTypeIsValueType { get; } = containingTypeIsValueType;
}

internal sealed class SerializationHookFieldDescription(TypeSyntax fieldType, string fieldName) : GeneratedFieldDescription(fieldType, fieldName)
Expand Down Expand Up @@ -1247,14 +1263,14 @@ internal class SerializableMember(IGeneratorServices generatorServices, IMemberD
public string MemberName => Field?.Name ?? Property?.Name ?? Member.Symbol.Name;

/// <summary>
/// Gets the name of the getter field.
/// Gets the name of the accessor used to read the field.
/// </summary>
private string GetterFieldName => GeneratedFieldNames.Accessor("getField", Member);
private string GetterFieldName => GeneratedFieldNames.Accessor(UseUnsafeFieldAccessor ? "accessField" : "getField", Member);

/// <summary>
/// Gets the name of the setter field.
/// Gets the name of the accessor used to write the field.
/// </summary>
private string SetterFieldName => GeneratedFieldNames.Accessor("setField", Member);
private string SetterFieldName => GeneratedFieldNames.Accessor(UseUnsafeFieldAccessor ? "accessField" : "setField", Member);

/// <summary>
/// Gets a value indicating if the member is a property.
Expand Down Expand Up @@ -1382,9 +1398,11 @@ public ExpressionSyntax GetSetter(ExpressionSyntax instance, ExpressionSyntax va
instanceArg = instanceArg.WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword));
}

return
InvocationExpression(GetAccessorExpression(GetSetterFieldDescription()!))
.AddArgumentListArguments(instanceArg, Argument(value));
var accessor = GetSetterFieldDescription()!;
var invocation = InvocationExpression(GetAccessorExpression(accessor)).AddArgumentListArguments(instanceArg);
return accessor.IsUnsafeFieldAccessor
? AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, invocation, value)
: invocation.AddArgumentListArguments(Argument(value));
}

private static ExpressionSyntax GetAccessorExpression(FieldAccessorDescription accessor)
Expand All @@ -1396,22 +1414,30 @@ private static ExpressionSyntax GetAccessorExpression(FieldAccessorDescription a
{
if (IsGettableField || IsGettableProperty) return null;
return GetFieldAccessor(ContainingType, TypeSyntax, MemberName, GetterFieldName, LibraryTypes, false,
IsPrimaryConstructorParameter && IsProperty, _generatorServices.Options.HotReloadSafe);
IsPrimaryConstructorParameter && IsProperty, _generatorServices.Options.HotReloadSafe, UseUnsafeFieldAccessor);
}

public FieldAccessorDescription? GetSetterFieldDescription()
{
if (IsSettableField || IsSettableProperty) return null;
return GetFieldAccessor(ContainingType, TypeSyntax, MemberName, SetterFieldName, LibraryTypes, true,
IsPrimaryConstructorParameter && IsProperty, _generatorServices.Options.HotReloadSafe);
IsPrimaryConstructorParameter && IsProperty, _generatorServices.Options.HotReloadSafe, UseUnsafeFieldAccessor);
}

public static FieldAccessorDescription GetFieldAccessor(INamedTypeSymbol containingType, TypeSyntax fieldType, string fieldName, string accessorName, LibraryTypes library, bool setter, bool useUnsafeAccessor = false, bool lazyInitialization = false)
private bool UseUnsafeFieldAccessor => Field is not null
&& !_generatorServices.Options.HotReloadSafe
&& _generatorServices.Options.SupportsUnsafeAccessors
&& LibraryTypes.HasUnsafeAccessorAttribute
&& (!Field.IsVolatile || _generatorServices.Options.SupportsVolatileUnsafeAccessors)
&& (!ContainingType.IsGenericType || _generatorServices.Options.SupportsGenericUnsafeAccessors);
Comment thread
ReubenBond marked this conversation as resolved.

public static FieldAccessorDescription GetFieldAccessor(INamedTypeSymbol containingType, TypeSyntax fieldType, string fieldName, string accessorName, LibraryTypes library, bool setter, bool useUnsafeAccessor = false, bool lazyInitialization = false, bool useUnsafeFieldAccessor = false)
Comment thread
ReubenBond marked this conversation as resolved.
{
var containingTypeSyntax = containingType.ToTypeSyntax();

if (useUnsafeAccessor)
return new(containingTypeSyntax, fieldType, fieldName, accessorName);
if (useUnsafeFieldAccessor || useUnsafeAccessor)
return new(containingTypeSyntax, fieldType, fieldName, accessorName,
isUnsafeFieldAccessor: useUnsafeFieldAccessor, containingTypeIsValueType: containingType.IsValueType);

var valueType = containingType.IsValueType;

Expand Down
Loading
Loading