Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SystemObjectProvider and type replacement #5487

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Generator.CSharp\src\Shared\StringExtensions.cs" LinkBase="Shared" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.CSharp;

namespace Microsoft.Generator.CSharp
namespace Microsoft.Generator.CSharp.Input
{
internal static class StringExtensions
public static class StringExtensions
{
private static bool IsWordSeparator(char c) => !SyntaxFacts.IsIdentifierPartCharacter(c) || c == '_';
private static readonly Regex HumanizedCamelCaseRegex = new Regex(@"([A-Z])", RegexOptions.Compiled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using Microsoft.Generator.CSharp.Input;

namespace Microsoft.Generator.CSharp.Expressions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private static TypeProvider[] BuildModels()
foreach (var inputModel in input.Models)
{
var outputModel = CodeModelPlugin.Instance.TypeFactory.CreateModel(inputModel);
if (outputModel != null)
if (outputModel != null && outputModel is not SystemObjectProvider)
{
models.Add(outputModel);
var unknownVariant = inputModel.DiscriminatedSubtypes.Values.FirstOrDefault(m => m.IsUnknownDiscriminatorModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Statements;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected override MethodProvider[] BuildMethods()
var methods = new List<MethodProvider>(_models.Count());
foreach (var model in _models)
{
var modelProvider = CodeModelPlugin.Instance.TypeFactory.CreateModel(model);
var modelProvider = CodeModelPlugin.Instance.TypeFactory.CreateModel(model) as ModelProvider;
if (modelProvider is null || modelProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal))
continue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace Microsoft.Generator.CSharp.Providers
{
public sealed class ModelProvider : TypeProvider
public class ModelProvider : TypeProvider
{
private const string AdditionalBinaryDataPropsFieldDescription = "Keeps track of any properties unknown to the library.";
private readonly InputModelType _inputModel;
Expand Down Expand Up @@ -57,7 +57,7 @@ private IReadOnlyList<ModelProvider> BuildDerivedModels()
// add discriminated subtypes
foreach (var subtype in _inputModel.DiscriminatedSubtypes)
{
var model = CodeModelPlugin.Instance.TypeFactory.CreateModel(subtype.Value);
var model = CodeModelPlugin.Instance.TypeFactory.CreateModel(subtype.Value) as ModelProvider;
if (model != null)
{
derivedModels.Add(model);
Expand All @@ -67,7 +67,7 @@ private IReadOnlyList<ModelProvider> BuildDerivedModels()
// add derived models
foreach (var derivedModel in _inputModel.DerivedModels)
{
var model = CodeModelPlugin.Instance.TypeFactory.CreateModel(derivedModel);
var model = CodeModelPlugin.Instance.TypeFactory.CreateModel(derivedModel) as ModelProvider;
if (model != null)
{
derivedModels.Add(model);
Expand All @@ -76,7 +76,23 @@ private IReadOnlyList<ModelProvider> BuildDerivedModels()

return [.. derivedModels];
}
internal override TypeProvider? BaseTypeProvider => BaseModelProvider;

internal override TypeProvider? BaseTypeProvider
{
get
{
if (_baseTypeProvider?.Value is ModelProvider modelProvider)
{
return modelProvider;
}
else if (_baseTypeProvider?.Value is SystemObjectProvider systemObjectProvider)
{
return systemObjectProvider;
}

return null;
}
}

public ModelProvider? BaseModelProvider
=> _baseModelProvider ??= (_baseTypeProvider?.Value is ModelProvider baseModelProvider ? baseModelProvider : null);
Expand All @@ -90,7 +106,7 @@ public ModelProvider? BaseModelProvider

protected override CSharpType? GetBaseType()
{
return BaseModelProvider?.Type;
return BaseTypeProvider?.Type;
}

protected override TypeProvider[] BuildSerializationProviders()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ protected PropertyProvider()

internal static bool TryCreate(InputModelProperty inputProperty, TypeProvider enclosingType, [NotNullWhen(true)] out PropertyProvider? property)
{
var inputPropertyType = inputProperty.Type as InputModelType;
if (inputPropertyType != null && CodeModelPlugin.Instance.TypeFactory.TryGetPropertyTypeReplacement(inputPropertyType, out var replacement))
{
property = new PropertyProvider(inputProperty, replacement.Type, enclosingType);
return true;
}

var type = CodeModelPlugin.Instance.TypeFactory.CreateCSharpType(inputProperty.Type);
if (type == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Statements;

namespace Microsoft.Generator.CSharp.Providers
{
public class SystemObjectProvider : TypeProvider
{
private readonly Type _type;
private const string InitializationCtorAttributeName = "InitializationConstructorAttribute";
private const string SerializationCtorAttributeName = "SerializationConstructorAttribute";

public SystemObjectProvider(Type type) : base()
{
_type = type;
}

protected override string BuildName() => _type.Name;

protected override string BuildRelativeFilePath() => throw new InvalidOperationException("This type should not be writing in generation");

protected override ConstructorProvider[] BuildConstructors()
{
var initializationCtor = GetCtor(_type, InitializationCtorAttributeName);
var serializationCtor = GetCtor(_type, SerializationCtorAttributeName);

return [BuildConstructor(initializationCtor), BuildConstructor(serializationCtor)];
}

private ConstructorProvider BuildConstructor(ConstructorInfo ctor)
{
var parameters = new List<ParameterProvider>();
foreach (var param in ctor.GetParameters())
{
var parameter = new ParameterProvider(param.Name!, $"The {param.Name}", param.ParameterType);
parameters.Add(parameter);
}

// we should only add initializers when there is a corresponding parameter
List<ParameterProvider> arguments = new List<ParameterProvider>();
foreach (var property in Properties)
{
var parameter = parameters.FirstOrDefault(p => p.Name == property.Name.ToVariableName());
if (parameter is not null)
{
arguments.Add(parameter);
}
}

var modifiers = ctor.IsFamily ? MethodSignatureModifiers.Protected : MethodSignatureModifiers.Public;
var signature = new ConstructorSignature(Type, null, modifiers, parameters, Initializer: new ConstructorInitializer(false, arguments));

return new ConstructorProvider(signature, MethodBodyStatement.Empty, this);
}

private static ConstructorInfo GetCtor(Type type, string attributeType)
{
if (TryGetCtor(type, attributeType, out var ctor))
return ctor;

throw new InvalidOperationException($"{attributeType} ctor was not found for {type.Name}");
}

private static bool TryGetCtor(Type type, string attributeType, [MaybeNullWhen(false)] out ConstructorInfo result)
{
foreach (var ctor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance))
{
if (ctor.GetCustomAttributes().FirstOrDefault(a => a.GetType().Name == attributeType) != null)
{
result = ctor;
return true;
}
}

result = null;
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,7 @@ public string? Deprecated

private TypeSignatureModifiers? _declarationModifiers;

public TypeSignatureModifiers DeclarationModifiers
{
get => _declarationModifiers ??= GetDeclarationModifiersInternal();
private set => _declarationModifiers = value;
}
public TypeSignatureModifiers DeclarationModifiers => _declarationModifiers ??= GetDeclarationModifiersInternal();

protected virtual TypeSignatureModifiers GetDeclarationModifiers() => TypeSignatureModifiers.None;

Expand Down Expand Up @@ -332,7 +328,9 @@ public void Update(
IEnumerable<FieldProvider>? fields = null,
IEnumerable<TypeProvider>? serializations = null,
IEnumerable<TypeProvider>? nestedTypes = null,
XmlDocProvider? xmlDocs = null)
XmlDocProvider? xmlDocs = null,
TypeSignatureModifiers? modifiers = null,
string? relativeFilePath = null)
{
if (methods != null)
{
Expand Down Expand Up @@ -362,6 +360,14 @@ public void Update(
{
XmlDocs = xmlDocs;
}
if (modifiers != null)
{
_declarationModifiers = modifiers;
}
if (relativeFilePath != null)
{
_relativeFilePath = relativeFilePath;
}
}
public IReadOnlyList<EnumTypeMember> EnumValues => _enumValues ??= BuildEnumValues();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
Expand Down Expand Up @@ -43,6 +44,20 @@ protected internal TypeFactory()

public CSharpType? CreateCSharpType(InputType inputType)
{
var inputModelType = inputType as InputModelType;

if (inputModelType is not null)
{
if (TryGetTypeReplacement(inputModelType, out var typeReplacement))
{
return typeReplacement.Type;
}
else if (TryGetPropertyTypeReplacement(inputModelType, out var propertyReplacement))
{
return propertyReplacement.Type;
}
}

if (TypeCache.TryGetValue(inputType, out var type))
{
return type;
Expand Down Expand Up @@ -141,8 +156,13 @@ protected internal TypeFactory()
/// </summary>
/// <param name="model">The <see cref="InputModelType"/> to convert.</param>
/// <returns>An instance of <see cref="TypeProvider"/>.</returns>
public ModelProvider? CreateModel(InputModelType model)
public TypeProvider? CreateModel(InputModelType model)
{
if (TryGetTypeReplacement(model, out var replacement))
{
return replacement;
}

if (CSharpToModelProvider.TryGetValue(model, out var modelProvider))
return modelProvider;

Expand All @@ -151,6 +171,18 @@ protected internal TypeFactory()
return modelProvider;
}

public virtual bool TryGetTypeReplacement(InputModelType inputModelType, [NotNullWhen(true)] out SystemObjectProvider? replacement)
{
replacement = null;
return false;
}

public virtual bool TryGetPropertyTypeReplacement(InputModelType inputModelType, [NotNullWhen(true)] out SystemObjectProvider? replacement)
{
replacement = null;
return false;
}

private ModelProvider? CreateModelCore(InputModelType model)
{
ModelProvider? type = new ModelProvider(model);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Providers;

namespace Microsoft.Generator.CSharp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Snippets;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using Microsoft.Generator.CSharp.Input;
using NUnit.Framework;

namespace Microsoft.Generator.CSharp.Tests.Utilities
Expand Down
Loading