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
24 changes: 1 addition & 23 deletions src/IceRpc.Slice.Generator/OperationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ internal CodeBlock BuildEncodeStreamMethod(
bool useSegments;
if (streamField.DataTypeIsOptional)
{
encodeLambda = GetStreamOfOptionalEncodeLambda(streamField, currentNamespace);
encodeLambda = streamField.DataType.GetEncodeLambdaWithNullMarker(currentNamespace);
useSegments = true;
}
else
Expand Down Expand Up @@ -368,28 +368,6 @@ private static string BuildServiceReturnTypeCore(string taskType, Operation op,
return count == 1 ? $"{taskType}<{parts[0]}>" : $"{taskType}<({string.Join(", ", parts)})>";
}

/// <summary>Returns an encode lambda for an optional stream element with a one bit bit-sequence.</summary>
internal static string GetStreamOfOptionalEncodeLambda(Field streamField, string currentNamespace)
{
IType elemType = streamField.DataType.Type;
string csType = streamField.DataType.FieldTypeString(true, currentNamespace);
// CustomType → (value ?? default!), value types → value!.Value, reference types → value!
string valueExpr = elemType is CustomType
? "(value ?? default!)"
: streamField.DataType.IsValueType ? "value!.Value" : "value!";
string encodeExpr = elemType.EncodeExpression(currentNamespace, valueExpr);
return $$"""
(ref SliceEncoder encoder, {{csType}} value) =>
{
encoder.EncodeBool(value is not null);
if (value is not null)
{
{{encodeExpr}};
}
}
""";
}

/// <summary>Returns a decode lambda for an optional stream element with a one bit bit-sequence.</summary>
internal static string GetStreamOfOptionalDecodeLambda(Field streamField, string currentNamespace)
{
Expand Down
8 changes: 4 additions & 4 deletions src/ZeroC.Slice.Generator/FieldExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ internal static CodeBlock GenerateEncodeBody(
}
else
{
string valueParam = field.DataType.Type is CustomType
? $"({param} ?? default!)"
: field.DataType.IsValueType ? $"{param}.Value" : param;
CodeBlock encodeExpr = field.DataType.EncodeExpression(currentNamespace, valueParam, encoderName);
CodeBlock encodeExpr = field.DataType.EncodeExpression(
currentNamespace,
field.DataType.UnwrapNonNullOptional(param),
encoderName);
body.WriteLine($$"""
bitSequenceWriter.Write({{param}} != null);
if ({{param}} != null)
Expand Down
59 changes: 7 additions & 52 deletions src/ZeroC.Slice.Generator/ITypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,70 +191,25 @@ static string EncodeSequence(
string param,
string encoderName)
{
IType elemType = seq.ElementType.Type;
if (seq.ElementTypeIsOptional)
{
string csOptType = seq.ElementType.FieldTypeString(true, currentNamespace);
string lambda = EncodeOptionalValueLambda(elemType, csOptType, currentNamespace);
return $$"""
{{encoderName}}.EncodeSequenceOfOptionals(
{{param}},
{{lambda}})
""";
}

// Fixed-size primitives use the optimized EncodeSequence<T> overload (no lambda).
if (!seq.ElementTypeIsOptional && elemType is Builtin builtin && builtin.IsFixedSize)
if (!seq.ElementTypeIsOptional && seq.ElementType.Type is Builtin { IsFixedSize: true })
{
return $"{encoderName}.EncodeSequence({param})";
}

CodeBlock elementEncodeLambda = seq.ElementType.GetEncodeLambda(seq.ElementTypeIsOptional, currentNamespace);
string method = seq.ElementTypeIsOptional ? "EncodeSequenceOfOptionals" : "EncodeSequence";
return $$"""
{{encoderName}}.EncodeSequence(
{{encoderName}}.{{method}}(
{{param}},
{{elementEncodeLambda.Indent()}})
""";

static string EncodeOptionalValueLambda(IType elemType, string csOptType, string currentNamespace)
{
// CustomType → (value ?? default!), value types → value!.Value, reference types → value!
string valueExpr = elemType is CustomType
? "(value ?? default!)"
: elemType is Struct or BasicEnum or Builtin { IsValueType: true } ? "value!.Value" : "value!";
string encodeExpr = elemType.EncodeExpression(currentNamespace, valueExpr);
return $"(ref SliceEncoder encoder, {csOptType} value) => {encodeExpr}";
}
}

// Returns an encode lambda for a result success/failure type, handling optional inner types with a
// one bit bit-sequence.
static string ResultEncodeLambda(TypeRef typeRef, bool isOptional, string currentNamespace)
{
IType type = typeRef.Type;

if (!isOptional)
{
return typeRef.GetEncodeLambda(isOptional: false, currentNamespace);
}
string csType = typeRef.FieldTypeString(true, currentNamespace);

// CustomType → (value ?? default!), value types → value!.Value, reference types → value!
string valueParam = type is CustomType
? "(value ?? default!)"
: typeRef.IsValueType ? "value!.Value" : "value!";
CodeBlock encodeBody = type.EncodeExpression(currentNamespace, valueParam);
return $$"""
(ref SliceEncoder encoder, {{csType}} value) =>
{
encoder.EncodeBool(value is not null);
if (value is not null)
{
{{encodeBody.Indent().Indent()}};
}
}
""";
}
static string ResultEncodeLambda(TypeRef typeRef, bool isOptional, string currentNamespace) =>
isOptional
? typeRef.GetEncodeLambdaWithNullMarker(currentNamespace)
: typeRef.GetEncodeLambda(isOptional: false, currentNamespace);
}

/// <summary>Returns a decode lambda for a type. When <paramref name="withCast"/> is true, a cast to the field
Expand Down
30 changes: 25 additions & 5 deletions src/ZeroC.Slice.Generator/TypeRefExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright (c) ZeroC, Inc.

using ZeroC.CodeBuilder;
using ZeroC.Slice.Symbols;

namespace ZeroC.Slice.Generator;

/// <summary>C#-specific extension methods for <see cref="TypeRef"/>.</summary>
internal static class TypeRefExtensions
{

/// <summary>Generates decode expression for a type reference. When the TypeRef has a cs::type attribute,
/// it is passed through as the concrete type for dictionary/sequence factory construction.</summary>
internal static string DecodeExpression(this TypeRef typeRef, string currentNamespace)
Expand Down Expand Up @@ -45,13 +45,28 @@ internal static string GetEncodeLambda(this TypeRef typeRef, bool isOptional, st
}

string csType = typeRef.Type.ToTypeString(currentNamespace) + "?";
string param = typeRef.Type is CustomType
? "(value ?? default!)"
: typeRef.IsValueType ? "value!.Value" : "value!";
string encodeExpr = typeRef.Type.EncodeExpression(currentNamespace, param);
string encodeExpr = typeRef.Type.EncodeExpression(currentNamespace, typeRef.UnwrapNonNullOptional("value"));
return $"(ref SliceEncoder encoder, {csType} value) => {encodeExpr}";
}

/// <summary>Returns an encode lambda for an optional type reference that writes a bool null marker before the
/// value, for use where the caller has no bit sequence.</summary>
internal static string GetEncodeLambdaWithNullMarker(this TypeRef typeRef, string currentNamespace)
{
string csType = typeRef.FieldTypeString(true, currentNamespace);
CodeBlock encodeBody = typeRef.EncodeExpression(currentNamespace, typeRef.UnwrapNonNullOptional("value"));
return $$"""
(ref SliceEncoder encoder, {{csType}} value) =>
{
encoder.EncodeBool(value is not null);
if (value is not null)
{
{{encodeBody.Indent().Indent()}};
}
}
""";
}

/// <summary>Returns the C# type string for an incoming parameter (decode target). Sequences map to arrays,
/// dictionaries map to <c>Dictionary&lt;K,V&gt;</c>. Respects cs::type attribute.</summary>
internal static string IncomingParameterTypeString(this TypeRef typeRef, bool isOptional, string currentNamespace)
Expand Down Expand Up @@ -109,6 +124,11 @@ internal static string OutgoingParameterTypeString(this TypeRef typeRef, bool is
return (isOptional && !ignoreOptional) ? $"{baseType}?" : baseType;
}

/// <summary>Returns the expression that unwraps a non-null optional value for encoding. A custom type can map to
/// either a C# value type or a reference type, so it uses <c>??</c> instead of <c>!</c>.</summary>
internal static string UnwrapNonNullOptional(this TypeRef typeRef, string param) =>
typeRef.Type is CustomType ? $"({param} ?? default!)" : typeRef.IsValueType ? $"{param}!.Value" : $"{param}!";

extension(TypeRef value)
{
/// <summary>Returns the fixed wire size for a type reference, or null if variable-size.</summary>
Expand Down