Skip to content

Commit

Permalink
Support property factory converters
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter committed Sep 2, 2021
1 parent f94d0ff commit c6582d8
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 69 deletions.
80 changes: 59 additions & 21 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ public sealed partial class JsonSourceGenerator
private sealed partial class Emitter
{
// Literals in generated source
private const string RuntimeCustomConverterFetchingMethodName = "GetRuntimeProvidedCustomConverter";
private const string OptionsInstanceVariableName = "Options";
private const string PropInitMethodNameSuffix = "PropInit";
private const string CtorParamInitMethodNameSuffix = "CtorParamInit";
private const string SerializeMethodNameSuffix = "Serialize";
private const string CreateValueInfoMethodName = "CreateValueInfo";
private const string CtorParamInitMethodNameSuffix = "CtorParamInit";
private const string DefaultOptionsStaticVarName = "s_defaultOptions";
private const string DefaultContextBackingStaticVarName = "s_defaultContext";
private const string WriterVarName = "writer";
private const string ValueVarName = "value";
internal const string GetConverterFromFactoryMethodName = "GetConverterFromFactory";
private const string JsonSerializerContextName = "JsonSerializerContext";
internal const string JsonContextVarName = "jsonContext";
internal const string OptionsInstanceVariableName = "Options";
private const string PropInitMethodNameSuffix = "PropInit";
private const string RuntimeCustomConverterFetchingMethodName = "GetRuntimeProvidedCustomConverter";
private const string SerializeMethodNameSuffix = "Serialize";
private const string ValueVarName = "value";
private const string WriterVarName = "writer";

private static AssemblyName _assemblyName = typeof(Emitter).Assembly.GetName();
private static readonly string s_generatedCodeAttributeSource = $@"
Expand All @@ -52,7 +54,7 @@ private sealed partial class Emitter
private const string JsonSerializerTypeRef = "global::System.Text.Json.JsonSerializer";
private const string JsonSerializerOptionsTypeRef = "global::System.Text.Json.JsonSerializerOptions";
private const string Utf8JsonWriterTypeRef = "global::System.Text.Json.Utf8JsonWriter";
private const string JsonConverterTypeRef = "global::System.Text.Json.Serialization.JsonConverter";
internal const string JsonConverterTypeRef = "global::System.Text.Json.Serialization.JsonConverter";
private const string JsonConverterFactoryTypeRef = "global::System.Text.Json.Serialization.JsonConverterFactory";
private const string JsonIgnoreConditionTypeRef = "global::System.Text.Json.Serialization.JsonIgnoreCondition";
private const string JsonNumberHandlingTypeRef = "global::System.Text.Json.Serialization.JsonNumberHandling";
Expand Down Expand Up @@ -99,15 +101,24 @@ public void Emit()
{
_currentContext = contextGenerationSpec;

bool generateGetConverterMethodForTypes = false;
bool generateGetConverterMethodForProperties = false;

foreach (TypeGenerationSpec typeGenerationSpec in _currentContext.RootSerializableTypes)
{
GenerateTypeInfo(typeGenerationSpec);

generateGetConverterMethodForTypes |= typeGenerationSpec.HasTypeFactoryConverter;
generateGetConverterMethodForProperties |= typeGenerationSpec.HasPropertyFactoryConverters;
}

string contextName = _currentContext.ContextType.Name;

// Add root context implementation.
AddSource($"{contextName}.g.cs", GetRootJsonContextImplementation(), isRootContextDef: true);
AddSource(
$"{contextName}.g.cs",
GetRootJsonContextImplementation(generateGetConverterMethodForTypes, generateGetConverterMethodForProperties),
isRootContextDef: true);

// Add GetJsonTypeInfo override implementation.
AddSource($"{contextName}.GetJsonTypeInfo.g.cs", GetGetTypeInfoImplementation());
Expand Down Expand Up @@ -298,6 +309,7 @@ private string GenerateForTypeWithUnknownConverter(TypeGenerationSpec typeMetada
{{
// Allow nullable handling to forward to the underlying type's converter.
converter = {JsonMetadataServicesTypeRef}.GetNullableConverter<{typeCompilableName}>(this.{typeFriendlyName})!;
converter = (({ JsonConverterFactoryTypeRef })converter).CreateConverter(typeToConvert, { OptionsInstanceVariableName })!;
}}
else
{{
Expand All @@ -315,15 +327,6 @@ private string GenerateForTypeWithUnknownConverter(TypeGenerationSpec typeMetada
}

metadataInitSource.Append($@"
else if (converter is { JsonConverterFactoryTypeRef } factory)
{{
{JsonConverterTypeRef}? actualConverter = factory.CreateConverter(typeToConvert, { OptionsInstanceVariableName });
if (actualConverter == null || actualConverter is { JsonConverterFactoryTypeRef })
{{
throw new { InvalidOperationExceptionTypeRef }($""JsonConverterFactory '{{factory.GetType()}}' cannot return a 'null' or 'JsonConverterFactory' value."");
}}
converter = actualConverter;
}}
_{typeFriendlyName} = { JsonMetadataServicesTypeRef }.{ GetCreateValueInfoMethodRef(typeCompilableName)} ({ OptionsInstanceVariableName}, converter); ");

return GenerateForType(typeMetadata, metadataInitSource.ToString());
Expand Down Expand Up @@ -480,7 +483,7 @@ private string GenerateFastPathFuncForEnumerable(TypeGenerationSpec typeGenerati

Type elementType = valueTypeGenerationSpec.Type;
string? writerMethodToCall = GetWriterMethod(elementType);

string iterationLogic;
string valueToWrite;

Expand Down Expand Up @@ -634,7 +637,6 @@ private string GenerateForObject(TypeGenerationSpec typeMetadata)
private string GeneratePropMetadataInitFunc(TypeGenerationSpec typeGenerationSpec)
{
const string PropVarName = "properties";
const string JsonContextVarName = "jsonContext";

List<PropertyGenerationSpec> properties = typeGenerationSpec.PropertyGenSpecList!;

Expand Down Expand Up @@ -1089,7 +1091,9 @@ private string WrapWithCheckForCustomConverterIfRequired(string source, string t
}}";
}

private string GetRootJsonContextImplementation()
private string GetRootJsonContextImplementation(
bool generateGetConverterMethodForTypes,
bool generateGetConverterMethodForProperties)
{
string contextTypeRef = _currentContext.ContextTypeRef;
string contextTypeName = _currentContext.ContextType.Name;
Expand All @@ -1111,6 +1115,16 @@ private string GetRootJsonContextImplementation()
{GetFetchLogicForRuntimeSpecifiedCustomConverter()}");

if (generateGetConverterMethodForProperties)
{
sb.Append(GetFetchLogicForGetCustomConverter_PropertiesWithFactories());
}

if (generateGetConverterMethodForProperties || generateGetConverterMethodForTypes)
{
sb.Append(GetFetchLogicForGetCustomConverter_TypesWithFactories());
}

return sb.ToString();
}

Expand Down Expand Up @@ -1169,6 +1183,30 @@ private string GetFetchLogicForRuntimeSpecifiedCustomConverter()
}}";
}

private string GetFetchLogicForGetCustomConverter_PropertiesWithFactories()
{
return @$"
private {JsonConverterTypeRef}<T> {GetConverterFromFactoryMethodName}<T>({JsonConverterFactoryTypeRef} factory)
{{
return ({JsonConverterTypeRef}<T>) {GetConverterFromFactoryMethodName}(typeof(T), factory);
}}";
}

private string GetFetchLogicForGetCustomConverter_TypesWithFactories()
{
return @$"
private {JsonConverterTypeRef} {GetConverterFromFactoryMethodName}({TypeTypeRef} type, {JsonConverterFactoryTypeRef} factory)
{{
{JsonConverterTypeRef}? converter = factory.CreateConverter(type, {Emitter.OptionsInstanceVariableName});
if (converter == null || converter is {JsonConverterFactoryTypeRef})
{{
throw new {InvalidOperationExceptionTypeRef}($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
}}
return converter;
}}";
}

private string GetGetTypeInfoImplementation()
{
StringBuilder sb = new();
Expand Down
Loading

0 comments on commit c6582d8

Please sign in to comment.