Skip to content

Commit

Permalink
Minor: Add support for combining callback parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
mingyaulee committed Jun 16, 2021
1 parent 0ed46f0 commit b79df43
Show file tree
Hide file tree
Showing 21 changed files with 361 additions and 58 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ MockResolvers.Configure(configure =>
## Limitations

- The browser extension API namespaces that are enabled at the moment is 44 out of a total of 60.
- For callback functions with more than one parameter, it is not converted to async operation with the callback parameter as the return value. Affected functions: 1 (Devtools.InspectedWindow.Eval)
- ~~For callback functions with more than one parameter, it is not converted to async operation with the callback parameter as the return value.~~ __Since v0.7.*__
- ~~Parameter callback is not supported.~~ __Since v0.4.*__
- ~~Event listener is not supported.~~ __Since v0.4.*__
- ~~Function invocation on returned object is not supported.~~ __Since v0.2.*__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,29 +69,37 @@ public ClrMethodInfo TranslateFunctionDefinition(FunctionDefinition functionDefi

private static FunctionReturnDefinition? GetReturnDefinition(FunctionDefinition functionDefinition, List<ParameterDefinition> parameterDefinitions)
{
var returnDefinition = functionDefinition.FunctionReturns;
if (returnDefinition is null && !string.IsNullOrEmpty(functionDefinition.Async))
if (functionDefinition.FunctionReturns is not null)
{
var callbackParameter = parameterDefinitions.Find(parameter => functionDefinition.Async.Equals(parameter.Name) && parameter.Type == ObjectType.Function);
if (callbackParameter is not null)
{
if (callbackParameter.FunctionParameters is null || !callbackParameter.FunctionParameters.Any())
{
parameterDefinitions.Remove(callbackParameter);
}
else if (callbackParameter.FunctionParameters.Count() == 1)
{
parameterDefinitions.Remove(callbackParameter);
var callbackParameterType = callbackParameter.FunctionParameters.Single();
returnDefinition = SerializationHelper.DeserializeTo<FunctionReturnDefinition>(callbackParameterType);
}
else
{
// multiple callback result is not supported yet
}
}
return functionDefinition.FunctionReturns;
}

if (string.IsNullOrEmpty(functionDefinition.Async))
{
return null;
}

var callbackParameter = parameterDefinitions.Find(parameter => functionDefinition.Async.Equals(parameter.Name) && parameter.Type == ObjectType.Function);
if (callbackParameter is null)
{
return null;
}
return returnDefinition;

if (callbackParameter.FunctionParameters is null || !callbackParameter.FunctionParameters.Any())
{
parameterDefinitions.Remove(callbackParameter);
return null;
}

if (callbackParameter.FunctionParameters.Count() > 1)
{
// do not handle callback with multiple parameters
return null;
}

parameterDefinitions.Remove(callbackParameter);
var callbackParameterType = callbackParameter.FunctionParameters.Single();
return SerializationHelper.DeserializeTo<FunctionReturnDefinition>(callbackParameterType);
}

private ClrTypeInfo? GetReturnType(FunctionReturnDefinition? returnDefinition, NamespaceEntity namespaceEntity)
Expand All @@ -104,14 +112,7 @@ public ClrMethodInfo TranslateFunctionDefinition(FunctionDefinition functionDefi
var returnClrType = clrTypeStore.GetClrType(returnDefinition, namespaceEntity);
if (returnClrType.FullName == typeof(object).FullName)
{
var type = typeof(JsonElement);
returnClrType = (ClrTypeInfo)returnClrType.Clone();
returnClrType.CSharpName = type.Name;
#pragma warning disable CS8601, CS8604 // Type should not have these properties as null
returnClrType.Id = type.FullName;
returnClrType.Namespace = type.Namespace;
returnClrType.ReferenceNamespaces.Add(type.Namespace);
#pragma warning restore CS8601, CS8604
returnClrType = returnClrType.MakeJsonElement();
}
return returnClrType;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using WebExtensions.Net.Generator.Extensions;
using WebExtensions.Net.Generator.Models;
using WebExtensions.Net.Generator.Models.ClrTypes;
using WebExtensions.Net.Generator.Models.Entities;
using WebExtensions.Net.Generator.Models.Schema;
Expand All @@ -18,10 +19,19 @@ public PropertyDefinitionTranslator(ClrTypeStore clrTypeStore)
public ClrPropertyInfo TranslatePropertyDefinition(string propertyName, PropertyDefinition propertyDefinition, NamespaceEntity namespaceEntity, ClrTypeInfo clrTypeInfo)
{
var propertyType = clrTypeStore.GetClrType(propertyDefinition, namespaceEntity);

if (clrTypeInfo.Metadata.TryGetValue(Constants.TypeMetadata.ClassType, out var classType) &&
(ClassType)classType == ClassType.CombinedCallbackParameterClass &&
propertyType.FullName == typeof(object).FullName)
{
propertyType = propertyType.MakeJsonElement();
}

if (propertyDefinition.IsOptional && !propertyType.IsNullable)
{
propertyType = propertyType.MakeNullable();
}

clrTypeInfo.AddRequiredNamespaces(propertyType.ReferenceNamespaces);

if (propertyName.Equals(clrTypeInfo.CSharpName, StringComparison.OrdinalIgnoreCase))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using WebExtensions.Net.Generator.CodeGeneration.CodeConverters;
using WebExtensions.Net.Generator.Models.ClrTypes;

namespace WebExtensions.Net.Generator.CodeGeneration.CodeConverterFactories
{
public class CombinedCallbackParameterCodeConverterFactory : ICodeConverterFactory
{
public void AddInterfaceConvertersToCodeFile(ClrTypeInfo clrTypeInfo, CodeFile codeFile)
{
throw new NotImplementedException();
}

public void AddConvertersToCodeFile(ClrTypeInfo clrTypeInfo, CodeFile codeFile)
{
codeFile.UsingNamespaces.Add("System.Text.Json.Serialization");
codeFile.Comments.Add(new CommentCodeConverter("Combined Callback Parameter Class"));
codeFile.Comments.Add(new CommentSummaryCodeConverter(clrTypeInfo.Description));

if (clrTypeInfo.IsObsolete)
{
codeFile.Attributes.Add(new AttributeObsoleteCodeConverter(clrTypeInfo.ObsoleteMessage));
}
codeFile.Attributes.Add(new AttributeCodeConverter($"JsonConverter(typeof(CombinedCallbackParameterJsonConverter<{clrTypeInfo.CSharpName}>))"));

codeFile.Constructors.Add(new CombinedCallbackParameterConstructorCodeConverter(clrTypeInfo.CSharpName, clrTypeInfo.Properties));

foreach (var property in clrTypeInfo.Properties)
{
codeFile.Properties.Add(new TypePropertyCodeConverter(property));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Linq;
using WebExtensions.Net.Generator.Models.ClrTypes;

namespace WebExtensions.Net.Generator.CodeGeneration.CodeConverters
{
public class CombinedCallbackParameterConstructorCodeConverter : ICodeConverter
{
private readonly string className;
private readonly IEnumerable<ClrPropertyInfo> clrPropertyInfos;

public CombinedCallbackParameterConstructorCodeConverter(string className, IEnumerable<ClrPropertyInfo> clrPropertyInfos)
{
this.className = className;
this.clrPropertyInfos = clrPropertyInfos;
}

public void WriteTo(CodeWriter codeWriter, CodeWriterOptions options)
{
codeWriter.WriteUsingStatement("System");

var propertyTypes = clrPropertyInfos.Select(property => $"typeof({property.PropertyType.CSharpName})");
var propertyNames = clrPropertyInfos.Select(property => $"\"{property.PublicName}\"");

codeWriter.Properties
.WriteLine($"private static readonly Type[] propertyTypes = new[] {{ {string.Join(", ", propertyTypes)} }};")
.WriteLine($"private static readonly string[] propertyNames = new[] {{ {string.Join(", ", propertyNames)} }};");

codeWriter.Constructors
.WriteWithConverter(new CommentSummaryCodeConverter($"Creates a new instance of <see cref=\"{className}\" />."))
.WriteLine($"public {className}() : base(propertyTypes, propertyNames)")
.WriteStartBlock()
.WriteEndBlock();
}
}
}
3 changes: 3 additions & 0 deletions src/WebExtensions.Net.Generator/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class CodeGenerator
private readonly ArrayCodeConverterFactory arrayCodeConverterFactory;
private readonly MultitypeCodeConverterFactory multitypeCodeConverterFactory;
private readonly EmptyCodeConverterFactory emptyCodeConverterFactory;
private readonly CombinedCallbackParameterCodeConverterFactory combinedCallbackParameterCodeConverterFactory;

public CodeGenerator(IServiceProvider serviceProvider)
{
Expand All @@ -31,6 +32,7 @@ public CodeGenerator(IServiceProvider serviceProvider)
arrayCodeConverterFactory = serviceProvider.GetRequiredService<ArrayCodeConverterFactory>();
multitypeCodeConverterFactory = serviceProvider.GetRequiredService<MultitypeCodeConverterFactory>();
emptyCodeConverterFactory = serviceProvider.GetRequiredService<EmptyCodeConverterFactory>();
combinedCallbackParameterCodeConverterFactory = serviceProvider.GetRequiredService<CombinedCallbackParameterCodeConverterFactory>();
}

public IEnumerable<CodeFileConverter> GetCodeFileConverters(IEnumerable<ClrTypeInfo> clrTypes)
Expand Down Expand Up @@ -107,6 +109,7 @@ private ICodeConverterFactory GetFactory(ClassType classType)
ClassType.ArrayClass => arrayCodeConverterFactory,
ClassType.MultitypeClass => multitypeCodeConverterFactory,
ClassType.EmptyClass => emptyCodeConverterFactory,
ClassType.CombinedCallbackParameterClass => combinedCallbackParameterCodeConverterFactory,
_ => throw new NotImplementedException()
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using WebExtensions.Net.Generator.Extensions;
using WebExtensions.Net.Generator.Helpers;
using WebExtensions.Net.Generator.Models;
using WebExtensions.Net.Generator.Models.Entities;
using WebExtensions.Net.Generator.Models.Schema;
Expand Down Expand Up @@ -120,6 +121,7 @@ private void Process(IEnumerable<string> nameHierarchy, TypeReference? typeRefer
}

TryHandleSingleTypeChoice(typeReference);
TryHandleCallbackParametersCombination(nameHierarchy, typeReference);

if (typeReference.Ref is null && IsObjectType(typeReference) && ShouldRegisterObjectType(typeReference))
{
Expand Down Expand Up @@ -149,8 +151,9 @@ private void Process(IEnumerable<string> nameHierarchy, TypeReference? typeRefer
private static bool IsObjectType(TypeReference typeReference)
{
return typeReference.Type == ObjectType.Object ||
typeReference.Type == ObjectType.ApiObject||
typeReference.Type == ObjectType.ApiObject ||
typeReference.Type == ObjectType.EventTypeObject ||
typeReference.Type == ObjectType.CombinedCallbackParameterObject ||
typeReference.TypeChoices != null;
}

Expand Down Expand Up @@ -206,6 +209,32 @@ private static void TryHandleSingleTypeChoice(TypeReference typeReference)
}
}

private void TryHandleCallbackParametersCombination(IEnumerable<string> nameHierarchy, TypeReference typeReference)
{
if (typeReference.FunctionParameters is null || registrationOptions.CombineCallbackParameter is null)
{
return;
}

var namePath = string.Join('.', nameHierarchy);
if (registrationOptions.CombineCallbackParameter.TryGetValue(namePath, out var name))
{
var parameters = typeReference.FunctionParameters;
typeReference.FunctionParameters = new[]
{
new ParameterDefinition()
{
Name = name,
Type = ObjectType.CombinedCallbackParameterObject,
ObjectProperties = parameters.ToDictionary(
parameter => parameter.Name ?? throw new InvalidOperationException("Function parameter should have a name."),
parameter => SerializationHelper.DeserializeTo<PropertyDefinition>(parameter)),
Deprecated = parameters.All(parameter => parameter.IsDeprecated) ? "True" : null
}
};
}
}

private static void ThrowIfNameHierarchyDifferent(IEnumerable<string> nameHierarchy, IEnumerable<string> registeredNameHierarchy)
{
var currentNamePaths = string.Join(',', nameHierarchy);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
using WebExtensions.Net.Generator.Models.Schema;
using System;
using Microsoft.Extensions.DependencyInjection;
using WebExtensions.Net.Generator.Models.Schema;

namespace WebExtensions.Net.Generator.EntitiesRegistration.ClassEntityRegistrars
{
public class ClassEntityRegistrarFactory
{
private readonly TypeClassEntityRegistrar typeClassEntityRegistrar;
private readonly EventTypeClassEntityRegistrar eventTypeClassEntityRegistrar;
private readonly CombinedCallbackParameterClassEntityRegistrar combinedCallbackParameterClassEntityRegistrar;
private readonly EnumClassEntityRegistrar enumClassEntityRegistrar;
private readonly StringFormatClassEntityRegistrar stringFormatClassEntityRegistrar;
private readonly ArrayClassEntityRegistrar arrayClassEntityRegistrar;
private readonly MultiTypeClassEntityRegistrar multiTypeClassEntityRegistrar;
private readonly EmptyClassEntityRegistrar emptyClassEntityRegistrar;

public ClassEntityRegistrarFactory(
TypeClassEntityRegistrar typeClassEntityRegistrar,
EventTypeClassEntityRegistrar eventTypeClassEntityRegistrar,
EnumClassEntityRegistrar enumClassEntityRegistrar,
StringFormatClassEntityRegistrar stringFormatClassEntityRegistrar,
ArrayClassEntityRegistrar arrayClassEntityRegistrar,
MultiTypeClassEntityRegistrar multiTypeClassEntityRegistrar,
EmptyClassEntityRegistrar emptyClassEntityRegistrar)
public ClassEntityRegistrarFactory(IServiceProvider serviceProvider)
{
this.typeClassEntityRegistrar = typeClassEntityRegistrar;
this.eventTypeClassEntityRegistrar = eventTypeClassEntityRegistrar;
this.enumClassEntityRegistrar = enumClassEntityRegistrar;
this.stringFormatClassEntityRegistrar = stringFormatClassEntityRegistrar;
this.arrayClassEntityRegistrar = arrayClassEntityRegistrar;
this.multiTypeClassEntityRegistrar = multiTypeClassEntityRegistrar;
this.emptyClassEntityRegistrar = emptyClassEntityRegistrar;
typeClassEntityRegistrar = serviceProvider.GetRequiredService<TypeClassEntityRegistrar>();
eventTypeClassEntityRegistrar = serviceProvider.GetRequiredService<EventTypeClassEntityRegistrar>();
combinedCallbackParameterClassEntityRegistrar = serviceProvider.GetRequiredService<CombinedCallbackParameterClassEntityRegistrar>();
enumClassEntityRegistrar = serviceProvider.GetRequiredService<EnumClassEntityRegistrar>();
stringFormatClassEntityRegistrar = serviceProvider.GetRequiredService<StringFormatClassEntityRegistrar>();
arrayClassEntityRegistrar = serviceProvider.GetRequiredService<ArrayClassEntityRegistrar>();
multiTypeClassEntityRegistrar = serviceProvider.GetRequiredService<MultiTypeClassEntityRegistrar>();
emptyClassEntityRegistrar = serviceProvider.GetRequiredService<EmptyClassEntityRegistrar>();
}

public BaseClassEntityRegistrar? GetClassEntityRegistrar(TypeDefinition typeDefinition)
Expand All @@ -40,6 +37,10 @@ public ClassEntityRegistrarFactory(
{
return eventTypeClassEntityRegistrar;
}
else if (typeDefinition.Type == ObjectType.CombinedCallbackParameterObject)
{
return combinedCallbackParameterClassEntityRegistrar;
}
else if (typeDefinition.Type == ObjectType.String && typeDefinition.EnumValues is not null)
{
return enumClassEntityRegistrar;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using WebExtensions.Net.Generator.Models;
using WebExtensions.Net.Generator.Repositories;

namespace WebExtensions.Net.Generator.EntitiesRegistration.ClassEntityRegistrars
{
public class CombinedCallbackParameterClassEntityRegistrar : BaseClassEntityRegistrar
{
private readonly RegistrationOptions registrationOptions;

public CombinedCallbackParameterClassEntityRegistrar(EntitiesContext entitiesContext, RegistrationOptions registrationOptions) : base(entitiesContext)
{
this.registrationOptions = registrationOptions;
}

protected override ClassType GetClassType() => ClassType.CombinedCallbackParameterClass;
protected override bool ShouldSortProperties() => false;
protected override string? GetBaseClassName() => registrationOptions.CombinedCallbackParameterClassBaseClassName;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using WebExtensions.Net.Generator.Models.ClrTypes;

namespace WebExtensions.Net.Generator.Extensions
Expand Down Expand Up @@ -33,6 +34,19 @@ public static ClrTypeInfo MakeNullable(this ClrTypeInfo clrTypeInfo)
return genericClrType;
}

public static ClrTypeInfo MakeJsonElement(this ClrTypeInfo clrTypeInfo)
{
var type = typeof(JsonElement);
var jsonElementClrType = (ClrTypeInfo)clrTypeInfo.Clone();
jsonElementClrType.CSharpName = type.Name;
#pragma warning disable CS8601, CS8604 // Type should not have these properties as null
jsonElementClrType.Id = type.FullName;
jsonElementClrType.Namespace = type.Namespace;
jsonElementClrType.ReferenceNamespaces.Add(type.Namespace);
#pragma warning restore CS8601, CS8604
return jsonElementClrType;
}

public static void AddReferenceNamespaces(this ClrTypeInfo clrTypeInfo, IEnumerable<string> referenceNamespaces)
{
foreach (var referenceNamespace in referenceNamespaces)
Expand Down
3 changes: 2 additions & 1 deletion src/WebExtensions.Net.Generator/Models/ClassType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum ClassType
StringFormatClass,
ArrayClass,
MultitypeClass,
EmptyClass
EmptyClass,
CombinedCallbackParameterClass
}
}
2 changes: 2 additions & 0 deletions src/WebExtensions.Net.Generator/Models/RegistrationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class RegistrationOptions
public string StringFormatClassBaseClassName { get; init; }
public string ArrayClassBaseClassName { get; init; }
public string MultiTypeClassBaseClassName { get; init; }
public string CombinedCallbackParameterClassBaseClassName { get; init; }
public IDictionary<string, string> CombineCallbackParameter { get; init; }
public IEnumerable<string> IncludeNamespaces { get; init; }
public IEnumerable<string> ExcludeNamespaces { get; init; }
public string BaseEventTypeName { get; init; }
Expand Down
Loading

0 comments on commit b79df43

Please sign in to comment.