2024-06-07 15:01:59 +00:00
|
|
|
|
// Copyright © 2023-2024 Xpl0itR
|
2024-01-22 01:33:05 +00:00
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
2023-10-31 02:30:03 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.CodeDom.Compiler;
|
|
|
|
|
using System.Collections.Generic;
|
2024-06-07 15:01:59 +00:00
|
|
|
|
using System.Diagnostics;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
using System.Linq;
|
2024-01-22 01:33:05 +00:00
|
|
|
|
using SystemEx;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
using CommunityToolkit.Diagnostics;
|
2024-06-28 03:08:45 +00:00
|
|
|
|
using LibProtodec.Models.Cil;
|
|
|
|
|
using LibProtodec.Models.Protobuf;
|
|
|
|
|
using LibProtodec.Models.Protobuf.Fields;
|
|
|
|
|
using LibProtodec.Models.Protobuf.TopLevels;
|
|
|
|
|
using LibProtodec.Models.Protobuf.Types;
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
|
|
|
|
namespace LibProtodec;
|
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
public delegate bool NameLookupFunc(string name, [MaybeNullWhen(false)] out string translatedName);
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
// ReSharper disable ClassWithVirtualMembersNeverInherited.Global, MemberCanBePrivate.Global, MemberCanBeProtected.Global
|
|
|
|
|
public class ProtodecContext
|
2024-01-22 01:33:05 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
private readonly Dictionary<string, TopLevel> _parsed = [];
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
public readonly List<Protobuf> Protobufs = [];
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
public ILogger? Logger { get; set; }
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
public NameLookupFunc? NameLookup { get; set; }
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
|
|
|
|
public void WriteAllTo(IndentedTextWriter writer)
|
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
writer.WriteLine("// Decompiled with protodec");
|
|
|
|
|
writer.WriteLine();
|
|
|
|
|
writer.WriteLine("""syntax = "proto3";""");
|
|
|
|
|
writer.WriteLine();
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
foreach (TopLevel topLevel in Protobufs.SelectMany(static proto => proto.TopLevels))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
topLevel.WriteTo(writer);
|
2023-10-31 02:30:03 +00:00
|
|
|
|
writer.WriteLine();
|
|
|
|
|
writer.WriteLine();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
public virtual Message ParseMessage(ICilType messageClass, ParserOptions options = ParserOptions.None)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Guard.IsTrue(messageClass is { IsClass: true, IsSealed: true });
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using IDisposable? _ = Logger?.BeginScopeParsingMessage(messageClass.FullName);
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (_parsed.TryGetValue(messageClass.FullName, out TopLevel? parsedMessage))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedMessage(parsedMessage.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return (Message)parsedMessage;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Message message = new()
|
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Name = TranslateTypeName(messageClass),
|
2024-07-11 04:39:04 +00:00
|
|
|
|
IsObsolete = HasObsoleteAttribute(messageClass.CustomAttributes)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
};
|
2024-06-28 03:08:45 +00:00
|
|
|
|
_parsed.Add(messageClass.FullName, message);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
|
|
|
|
Protobuf protobuf = GetProtobuf(messageClass, message, options);
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
List<ICilField> idFields = messageClass.GetFields()
|
|
|
|
|
.Where(static field => field is { IsPublic: true, IsStatic: true, IsLiteral: true })
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
List<ICilProperty> properties = messageClass.GetProperties()
|
|
|
|
|
.Where(static property => property is { IsInherited: false, CanRead: true, Getter: { IsPublic: true, IsStatic: false, IsVirtual: false } })
|
|
|
|
|
.ToList();
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
for (int pi = 0, fi = 0; pi < properties.Count; pi++)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
ICilProperty property = properties[pi];
|
|
|
|
|
ICilType propertyType = property.Type;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using IDisposable? __ = Logger?.BeginScopeParsingProperty(property.Name, propertyType.FullName);
|
|
|
|
|
|
|
|
|
|
if (((options & ParserOptions.IncludePropertiesWithoutNonUserCodeAttribute) == 0 && !HasNonUserCodeAttribute(property.CustomAttributes)))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogSkippingPropertyWithoutNonUserCodeAttribute();
|
2023-10-31 02:30:03 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// only OneOf enums are defined nested directly in the message class
|
2024-06-07 15:01:59 +00:00
|
|
|
|
if (propertyType.IsEnum && propertyType.DeclaringType?.Name == messageClass.Name)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
string oneOfName = TranslateOneOfPropName(property.Name);
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedOneOfField(oneOfName);
|
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
List<int> oneOfProtoFieldIds = propertyType.GetFields()
|
|
|
|
|
.Where(static field => field.IsLiteral)
|
|
|
|
|
.Select(static field => (int)field.ConstantValue!)
|
|
|
|
|
.Where(static id => id > 0)
|
|
|
|
|
.ToList();
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
|
|
|
|
message.OneOfs.Add(oneOfName, oneOfProtoFieldIds);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
bool msgFieldHasHasProp = false; // some field properties are immediately followed by an additional "Has" get-only boolean property
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (properties.Count > pi + 1 && properties[pi + 1].Type.Name == nameof(Boolean) && !properties[pi + 1].CanWrite)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
msgFieldHasHasProp = true;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
pi++;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
if (idFields.Count <= fi)
|
|
|
|
|
{
|
|
|
|
|
Logger?.LogFailedToLocateIdField();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MessageField field = new(message)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
Type = ParseFieldType(propertyType, options, protobuf),
|
|
|
|
|
Name = TranslateMessageFieldName(property.Name),
|
2024-06-28 03:08:45 +00:00
|
|
|
|
Id = (int)idFields[fi].ConstantValue!,
|
2024-07-11 04:39:04 +00:00
|
|
|
|
IsObsolete = HasObsoleteAttribute(property.CustomAttributes),
|
2024-06-07 15:01:59 +00:00
|
|
|
|
HasHasProp = msgFieldHasHasProp
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedField(field.Name, field.Id, field.Type.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
message.Fields.Add(field.Id, field);
|
2024-06-28 03:08:45 +00:00
|
|
|
|
fi++;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedMessage(message.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return message;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
public virtual Enum ParseEnum(ICilType enumEnum, ParserOptions options = ParserOptions.None)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Guard.IsTrue(enumEnum.IsEnum);
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using IDisposable? _ = Logger?.BeginScopeParsingEnum(enumEnum.FullName);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (_parsed.TryGetValue(enumEnum.FullName, out TopLevel? parsedEnum))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedEnum(parsedEnum.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return (Enum)parsedEnum;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Enum @enum = new()
|
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Name = TranslateTypeName(enumEnum),
|
2024-07-11 04:39:04 +00:00
|
|
|
|
IsObsolete = HasObsoleteAttribute(enumEnum.CustomAttributes)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
};
|
2024-06-28 03:08:45 +00:00
|
|
|
|
_parsed.Add(enumEnum.FullName, @enum);
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Protobuf protobuf = GetProtobuf(enumEnum, @enum, options);
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
foreach (ICilField enumField in enumEnum.GetFields().Where(static field => field.IsLiteral))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using IDisposable? __ = Logger?.BeginScopeParsingField(enumField.Name);
|
2024-06-28 03:08:45 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
EnumField field = new()
|
|
|
|
|
{
|
|
|
|
|
Id = (int)enumField.ConstantValue!,
|
|
|
|
|
Name = TranslateEnumFieldName(enumField.CustomAttributes, enumField.Name, @enum.Name),
|
|
|
|
|
IsObsolete = HasObsoleteAttribute(enumField.CustomAttributes)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Logger?.LogParsedField(field.Name, field.Id);
|
|
|
|
|
@enum.Fields.Add(field);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
if (@enum.Fields.All(static field => field.Id != 0))
|
|
|
|
|
{
|
|
|
|
|
protobuf.Edition = "2023";
|
|
|
|
|
@enum.IsClosed = true;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedEnum(@enum.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return @enum;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
public virtual Service ParseService(ICilType serviceClass, ParserOptions options = ParserOptions.None)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Guard.IsTrue(serviceClass.IsClass);
|
|
|
|
|
|
|
|
|
|
bool? isClientClass = null;
|
|
|
|
|
if (serviceClass.IsAbstract)
|
|
|
|
|
{
|
|
|
|
|
if (serviceClass is { IsSealed: true, IsNested: false })
|
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
List<ICilType> nested = serviceClass.GetNestedTypes().ToList();
|
|
|
|
|
serviceClass = nested.SingleOrDefault(static nested => nested is { IsAbstract: true, IsSealed: false })
|
|
|
|
|
?? nested.Single(static nested => nested is { IsClass: true, IsAbstract: false });
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (serviceClass is { IsNested: true, IsAbstract: true, IsSealed: false })
|
|
|
|
|
{
|
|
|
|
|
isClientClass = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (serviceClass is { IsAbstract: false, IsNested: true, DeclaringType: not null })
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
isClientClass = true;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Guard.IsNotNull(isClientClass);
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using IDisposable? _ = Logger?.BeginScopeParsingService(serviceClass.DeclaringType!.FullName);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (_parsed.TryGetValue(serviceClass.DeclaringType!.FullName, out TopLevel? parsedService))
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedService(parsedService.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return (Service)parsedService;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Service service = new()
|
|
|
|
|
{
|
|
|
|
|
Name = TranslateTypeName(serviceClass.DeclaringType),
|
2024-07-11 04:39:04 +00:00
|
|
|
|
IsObsolete = HasObsoleteAttribute(serviceClass.CustomAttributes)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
};
|
2024-06-28 03:08:45 +00:00
|
|
|
|
_parsed.Add(serviceClass.DeclaringType!.FullName, service);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
|
|
|
|
Protobuf protobuf = NewProtobuf(serviceClass, service);
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
foreach (ICilMethod cilMethod in serviceClass.GetMethods().Where(static method => method is { IsInherited: false, IsPublic: true, IsStatic: false, IsConstructor: false }))
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
using IDisposable? __ = Logger?.BeginScopeParsingMethod(cilMethod.Name);
|
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
if ((options & ParserOptions.IncludeServiceMethodsWithoutGeneratedCodeAttribute) == 0
|
2024-07-11 04:39:04 +00:00
|
|
|
|
&& !HasGeneratedCodeAttribute(cilMethod.CustomAttributes, "grpc_csharp_plugin"))
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogSkippingMethodWithoutGeneratedCodeAttribute();
|
2024-06-07 15:01:59 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
ICilType requestType, responseType, returnType = cilMethod.ReturnType;
|
2024-06-28 03:08:45 +00:00
|
|
|
|
bool streamReq, streamRes;
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
|
|
|
|
if (isClientClass.Value)
|
|
|
|
|
{
|
|
|
|
|
string returnTypeName = TranslateTypeName(returnType);
|
|
|
|
|
if (returnTypeName == "AsyncUnaryCall`1")
|
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogSkippingDuplicateMethod();
|
2024-06-07 15:01:59 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
List<ICilType> parameters = cilMethod.GetParameterTypes().ToList();
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (parameters.Count > 2)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogSkippingDuplicateMethod();
|
2024-06-07 15:01:59 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
switch (returnType.GenericTypeArguments.Count)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
requestType = returnType.GenericTypeArguments[0];
|
|
|
|
|
responseType = returnType.GenericTypeArguments[1];
|
|
|
|
|
streamReq = true;
|
|
|
|
|
streamRes = returnTypeName == "AsyncDuplexStreamingCall`2";
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2024-06-28 03:08:45 +00:00
|
|
|
|
requestType = parameters[0];
|
2024-06-07 15:01:59 +00:00
|
|
|
|
responseType = returnType.GenericTypeArguments[0];
|
|
|
|
|
streamReq = false;
|
|
|
|
|
streamRes = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2024-06-28 03:08:45 +00:00
|
|
|
|
requestType = parameters[0];
|
2024-06-07 15:01:59 +00:00
|
|
|
|
responseType = returnType;
|
|
|
|
|
streamReq = false;
|
|
|
|
|
streamRes = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
List<ICilType> parameters = cilMethod.GetParameterTypes().ToList();
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (parameters[0].GenericTypeArguments.Count == 1)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
streamReq = true;
|
2024-06-28 03:08:45 +00:00
|
|
|
|
requestType = parameters[0].GenericTypeArguments[0];
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
streamReq = false;
|
2024-06-28 03:08:45 +00:00
|
|
|
|
requestType = parameters[0];
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (returnType.GenericTypeArguments.Count == 1)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
streamRes = false;
|
|
|
|
|
responseType = returnType.GenericTypeArguments[0];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
streamRes = true;
|
2024-06-28 03:08:45 +00:00
|
|
|
|
responseType = parameters[1].GenericTypeArguments[0];
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
ServiceMethod method = new(service)
|
|
|
|
|
{
|
|
|
|
|
Name = TranslateMethodName(cilMethod.Name),
|
|
|
|
|
IsObsolete = HasObsoleteAttribute(cilMethod.CustomAttributes),
|
|
|
|
|
RequestType = ParseFieldType(requestType, options, protobuf),
|
|
|
|
|
ResponseType = ParseFieldType(responseType, options, protobuf),
|
|
|
|
|
IsRequestStreamed = streamReq,
|
|
|
|
|
IsResponseStreamed = streamRes
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Logger?.LogParsedMethod(method.Name, method.RequestType.Name, method.ResponseType.Name);
|
|
|
|
|
service.Methods.Add(method);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
Logger?.LogParsedService(service.Name);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return service;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected IProtobufType ParseFieldType(ICilType type, ParserOptions options, Protobuf referencingProtobuf)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
switch (type.GenericTypeArguments.Count)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
|
|
|
|
case 1:
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return new Repeated(
|
|
|
|
|
ParseFieldType(type.GenericTypeArguments[0], options, referencingProtobuf));
|
2023-10-31 02:30:03 +00:00
|
|
|
|
case 2:
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return new Map(
|
|
|
|
|
ParseFieldType(type.GenericTypeArguments[0], options, referencingProtobuf),
|
|
|
|
|
ParseFieldType(type.GenericTypeArguments[1], options, referencingProtobuf));
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
if (!LookupType(type, out IProtobufType? fieldType))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
if (type.IsEnum)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
if ((options & ParserOptions.SkipEnums) > 0)
|
|
|
|
|
{
|
|
|
|
|
return Scalar.Int32;
|
|
|
|
|
}
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
fieldType = ParseEnum(type, options);
|
|
|
|
|
}
|
|
|
|
|
else
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
fieldType = ParseMessage(type, options);
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-11 04:39:04 +00:00
|
|
|
|
|
|
|
|
|
switch (fieldType)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
case WellKnown wellKnown:
|
|
|
|
|
referencingProtobuf.Imports.Add(
|
|
|
|
|
wellKnown.FileName);
|
|
|
|
|
break;
|
|
|
|
|
case INestableType nestableType:
|
|
|
|
|
Protobuf protobuf = nestableType.Protobuf!;
|
|
|
|
|
if (referencingProtobuf != protobuf)
|
|
|
|
|
referencingProtobuf.Imports.Add(
|
|
|
|
|
protobuf.FileName);
|
|
|
|
|
break;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
return fieldType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual bool LookupType(ICilType cilType, [NotNullWhen(true)] out IProtobufType? protobufType)
|
|
|
|
|
{
|
|
|
|
|
switch (cilType.FullName)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-07-11 04:39:04 +00:00
|
|
|
|
case "System.String":
|
|
|
|
|
protobufType = Scalar.String;
|
|
|
|
|
break;
|
|
|
|
|
case "System.Boolean":
|
|
|
|
|
protobufType = Scalar.Bool;
|
|
|
|
|
break;
|
|
|
|
|
case "System.Double":
|
|
|
|
|
protobufType = Scalar.Double;
|
|
|
|
|
break;
|
|
|
|
|
case "System.UInt32":
|
|
|
|
|
protobufType = Scalar.UInt32;
|
|
|
|
|
break;
|
|
|
|
|
case "System.UInt64":
|
|
|
|
|
protobufType = Scalar.UInt64;
|
|
|
|
|
break;
|
|
|
|
|
case "System.Int32":
|
|
|
|
|
protobufType = Scalar.Int32;
|
|
|
|
|
break;
|
|
|
|
|
case "System.Int64":
|
|
|
|
|
protobufType = Scalar.Int64;
|
|
|
|
|
break;
|
|
|
|
|
case "System.Single":
|
|
|
|
|
protobufType = Scalar.Float;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.ByteString":
|
|
|
|
|
protobufType = Scalar.Bytes;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Any":
|
|
|
|
|
protobufType = WellKnown.Any;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Api":
|
|
|
|
|
protobufType = WellKnown.Api;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.BoolValue":
|
|
|
|
|
protobufType = WellKnown.BoolValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.BytesValue":
|
|
|
|
|
protobufType = WellKnown.BytesValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.DoubleValue":
|
|
|
|
|
protobufType = WellKnown.DoubleValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Duration":
|
|
|
|
|
protobufType = WellKnown.Duration;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Empty":
|
|
|
|
|
protobufType = WellKnown.Empty;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Enum":
|
|
|
|
|
protobufType = WellKnown.Enum;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.EnumValue":
|
|
|
|
|
protobufType = WellKnown.EnumValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Field":
|
|
|
|
|
protobufType = WellKnown.Field;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.FieldMask":
|
|
|
|
|
protobufType = WellKnown.FieldMask;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.FloatValue":
|
|
|
|
|
protobufType = WellKnown.FloatValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Int32Value":
|
|
|
|
|
protobufType = WellKnown.Int32Value;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Int64Value":
|
|
|
|
|
protobufType = WellKnown.Int64Value;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.ListValue":
|
|
|
|
|
protobufType = WellKnown.ListValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Method":
|
|
|
|
|
protobufType = WellKnown.Method;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Mixin":
|
|
|
|
|
protobufType = WellKnown.Mixin;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.NullValue":
|
|
|
|
|
protobufType = WellKnown.NullValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Option":
|
|
|
|
|
protobufType = WellKnown.Option;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.SourceContext":
|
|
|
|
|
protobufType = WellKnown.SourceContext;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.StringValue":
|
|
|
|
|
protobufType = WellKnown.StringValue;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Struct":
|
|
|
|
|
protobufType = WellKnown.Struct;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Syntax":
|
|
|
|
|
protobufType = WellKnown.Syntax;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Timestamp":
|
|
|
|
|
protobufType = WellKnown.Timestamp;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Type":
|
|
|
|
|
protobufType = WellKnown.Type;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.UInt32Value":
|
|
|
|
|
protobufType = WellKnown.UInt32Value;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.UInt64Value":
|
|
|
|
|
protobufType = WellKnown.UInt64Value;
|
|
|
|
|
break;
|
|
|
|
|
case "Google.Protobuf.WellKnownTypes.Value":
|
|
|
|
|
protobufType = WellKnown.Value;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
protobufType = null;
|
|
|
|
|
return false;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
return true;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected Protobuf NewProtobuf(ICilType topLevelType, TopLevel topLevel)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
Protobuf protobuf = new()
|
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
AssemblyName = topLevelType.DeclaringAssemblyName,
|
2024-06-07 15:01:59 +00:00
|
|
|
|
Namespace = topLevelType.Namespace
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
topLevel.Protobuf = protobuf;
|
|
|
|
|
protobuf.TopLevels.Add(topLevel);
|
|
|
|
|
Protobufs.Add(protobuf);
|
|
|
|
|
|
|
|
|
|
return protobuf;
|
|
|
|
|
}
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected Protobuf GetProtobuf<T>(ICilType topLevelType, T topLevel, ParserOptions options)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
where T : TopLevel, INestableType
|
|
|
|
|
{
|
|
|
|
|
Protobuf protobuf;
|
|
|
|
|
if (topLevelType.IsNested)
|
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
ICilType parent = topLevelType.DeclaringType!.DeclaringType!;
|
|
|
|
|
if (!_parsed.TryGetValue(parent.FullName, out TopLevel? parentTopLevel))
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
parentTopLevel = ParseMessage(parent, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protobuf = parentTopLevel.Protobuf!;
|
|
|
|
|
topLevel.Protobuf = protobuf;
|
|
|
|
|
topLevel.Parent = parentTopLevel;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
((Message)parentTopLevel).Nested.Add(topLevelType.Name, topLevel);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
protobuf = NewProtobuf(topLevelType, topLevel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return protobuf;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected string TranslateMethodName(string methodName) =>
|
2024-06-07 15:01:59 +00:00
|
|
|
|
NameLookup?.Invoke(methodName, out string? translatedName) == true
|
2023-10-31 02:30:03 +00:00
|
|
|
|
? translatedName
|
2024-06-07 15:01:59 +00:00
|
|
|
|
: methodName;
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected string TranslateOneOfPropName(string oneOfPropName)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
if (NameLookup?.Invoke(oneOfPropName, out string? translatedName) != true)
|
|
|
|
|
{
|
|
|
|
|
if (IsBeebyted(oneOfPropName))
|
|
|
|
|
{
|
|
|
|
|
return oneOfPropName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
translatedName = oneOfPropName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return translatedName!.TrimEnd("Case").ToSnakeCaseLower();
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected string TranslateMessageFieldName(string fieldName)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
|
|
|
|
if (NameLookup?.Invoke(fieldName, out string? translatedName) != true)
|
|
|
|
|
{
|
|
|
|
|
if (IsBeebyted(fieldName))
|
|
|
|
|
{
|
|
|
|
|
return fieldName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
translatedName = fieldName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return translatedName!.ToSnakeCaseLower();
|
|
|
|
|
}
|
2023-10-31 02:30:03 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected string TranslateEnumFieldName(IEnumerable<ICilAttribute> attributes, string fieldName, string enumName)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (attributes.SingleOrDefault(static attr => attr.Type.Name == "OriginalNameAttribute")
|
2024-07-11 04:39:04 +00:00
|
|
|
|
?.ConstructorArgumentValues[0] is string originalName)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
|
|
|
|
return originalName;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (NameLookup?.Invoke(fieldName, out string? translatedName) == true)
|
2024-06-07 15:01:59 +00:00
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
fieldName = translatedName;
|
2024-06-07 15:01:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
if (!IsBeebyted(fieldName))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-28 03:08:45 +00:00
|
|
|
|
fieldName = fieldName.ToSnakeCaseUpper();
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-22 01:33:05 +00:00
|
|
|
|
if (!IsBeebyted(enumName))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
|
|
|
|
enumName = enumName.ToSnakeCaseUpper();
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return enumName + '_' + fieldName;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected string TranslateTypeName(ICilType type)
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
if (NameLookup is null)
|
|
|
|
|
return type.Name;
|
|
|
|
|
|
2024-06-28 03:08:45 +00:00
|
|
|
|
string fullName = type.FullName;
|
2024-06-07 15:01:59 +00:00
|
|
|
|
int genericArgs = fullName.IndexOf('[');
|
|
|
|
|
if (genericArgs != -1)
|
|
|
|
|
fullName = fullName[..genericArgs];
|
|
|
|
|
|
|
|
|
|
if (!NameLookup(fullName, out string? translatedName))
|
2023-10-31 02:30:03 +00:00
|
|
|
|
{
|
2024-06-07 15:01:59 +00:00
|
|
|
|
return type.Name;
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 15:01:59 +00:00
|
|
|
|
int lastSlash = translatedName.LastIndexOf('/');
|
|
|
|
|
if (lastSlash != -1)
|
|
|
|
|
translatedName = translatedName[lastSlash..];
|
|
|
|
|
|
|
|
|
|
int lastDot = translatedName.LastIndexOf('.');
|
|
|
|
|
if (lastDot != -1)
|
|
|
|
|
translatedName = translatedName[lastDot..];
|
|
|
|
|
|
|
|
|
|
return translatedName;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-22 01:33:05 +00:00
|
|
|
|
// ReSharper disable once IdentifierTypo
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected static bool IsBeebyted(string name) =>
|
2024-01-22 01:33:05 +00:00
|
|
|
|
name.Length == 11 && name.CountUpper() == 11;
|
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected static bool HasGeneratedCodeAttribute(IEnumerable<ICilAttribute> attributes, string tool) =>
|
|
|
|
|
attributes.Any(attr => attr.Type.Name == nameof(GeneratedCodeAttribute)
|
|
|
|
|
&& attr.ConstructorArgumentValues[0] as string == tool);
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected static bool HasNonUserCodeAttribute(IEnumerable<ICilAttribute> attributes) =>
|
2024-06-28 03:08:45 +00:00
|
|
|
|
attributes.Any(static attr => attr.Type.Name == nameof(DebuggerNonUserCodeAttribute));
|
2024-06-07 15:01:59 +00:00
|
|
|
|
|
2024-07-11 04:39:04 +00:00
|
|
|
|
protected static bool HasObsoleteAttribute(IEnumerable<ICilAttribute> attributes) =>
|
2024-06-28 03:08:45 +00:00
|
|
|
|
attributes.Any(static attr => attr.Type.Name == nameof(ObsoleteAttribute));
|
2023-10-31 02:30:03 +00:00
|
|
|
|
}
|