private string GenerateSimpleCalculatorClass(RecursiveCalculatorClassInfo classInfo, List <CalculatorMethodGenerator> methodGens, TargetMethodsInfo targetMethods) { var code = new CodeBuilder(); var methodGen = methodGens.Single(); var methodInfo = methodGen.MethodInfo; code.AddBlockHeader(@$ " private class {classInfo.CalculatorClassName}{classInfo.GenericParams} : AntiSO.Infrastructure.SimpleRecursionRunner<{methodInfo.MethodParamsStructName}{methodInfo.GenericParams}>
private string GenerateCallSiteEnum(RecursiveCalculatorClassInfo classInfo, IReadOnlyList <CalculatorMethodGenerator> methodGens) { var code = new CodeBuilder(); code.AddBlockHeader($"\tprivate enum {classInfo.CallSiteEnumName} : int"); foreach (var methodGen in methodGens) { code.AddHeaderLine($"\t\t{methodGen.MethodInfo.MethodEnumValueName},"); } return(code.BuildString()); }
internal static string GenerateSimpleSafeRec(GeneratorExecutionContext context, RecursiveMethodInfo methodInfo) { var classSyntax = (ClassDeclarationSyntax)methodInfo.MethodSyntax.Parent; var classSymbol = methodInfo.MethodSymbol.ContainingType; var recursionId = methodInfo.MethodSymbol.Name; var classInfo = new RecursiveCalculatorClassInfo(classSyntax, classSymbol, isMultiSite: false, recursionId, methodInfo.GenericParams, methodInfo.ConstraintClauses, methodInfo.MethodParamsStructName); return(new CalculatorClassGenerator(context, classInfo, new List <RecursiveMethodInfo>() { methodInfo }).GenerateAll()); }
private string GenerateCalculatorClass(RecursiveCalculatorClassInfo classInfo, List <CalculatorMethodGenerator> methodGens, TargetMethodsInfo targetMethods) { if (classInfo.IsMultiSite) { // mutual recursion with a dispatcher method return(GenerateMultiSiteCalculatorClass(classInfo, methodGens, targetMethods)); } else { // simple recursion return(GenerateSimpleCalculatorClass(classInfo, methodGens, targetMethods)); } }
private string GenerateParamsStructs(RecursiveCalculatorClassInfo classInfo, IReadOnlyList <CalculatorMethodGenerator> methodGens) { var code = new CodeBuilder(); if (classInfo.IsMultiSite) { code.AddHeaderLine(GenerateCallSiteEnum(classInfo, methodGens)); code.AddHeaderLine(GenerateDispatchStruct(classInfo, methodGens)); code.AddHeaderLine(); } foreach (var methodGen in methodGens) { code.AddHeaderLine(methodGen.GenerateMethodParamsStruct()); } return(code.BuildString()); }
internal static string GenerateMutualSafeRec(GeneratorExecutionContext context, IReadOnlyList <RecursiveMethodInfo> methodInfos) { var firstMethodInfo = methodInfos.First(); if (string.IsNullOrEmpty(firstMethodInfo.CodeGenProps.MutualRecursionId)) { throw new ArgumentException($"Empty {nameof(SafeRecursionCodeGenProps.MutualRecursionId)} in the first methodInfo of {nameof(methodInfos)}."); } if (methodInfos.Count == 1) { // Warn the user but continue anyway as we should generate a working code anyway if there // is actually exactly 1 MutualRecursionId. The code will be slower but it should work. context.ReportConfigurationWarning(firstMethodInfo.MethodSyntax, $"Suspiciously only one method is found for the mutual recursive group '{firstMethodInfo.CodeGenProps.MutualRecursionId}'"); } // TODO really check that all GenericParams and ConstraintClauses for all methodInfos are // the same. If it is not so, the compilation probably will fail anyway but the message will // not be very user-friendly foreach (var methodInfo in methodInfos) { if ((methodInfo.GenericParams != firstMethodInfo.GenericParams) || (methodInfo.ConstraintClauses != firstMethodInfo.ConstraintClauses)) { // string equality check is not really correct so only generate a warning context.ReportConfigurationWarning(methodInfo.MethodSyntax, $"Suspiciously generic methods '{firstMethodInfo.MethodName}' and '{methodInfo.MethodName}' have differently looking generic params" + $" '{firstMethodInfo.GenericParams}' '{firstMethodInfo.ConstraintClauses}'" + $" != '{methodInfo.GenericParams}' '{methodInfo.ConstraintClauses}'"); } } var classSyntax = (ClassDeclarationSyntax)firstMethodInfo.MethodSyntax.Parent; var classSymbol = firstMethodInfo.MethodSymbol.ContainingType; var recursionId = firstMethodInfo.CodeGenProps.MutualRecursionId; var classInfo = new RecursiveCalculatorClassInfo(classSyntax, classSymbol, isMultiSite: true, recursionId, firstMethodInfo.GenericParams, firstMethodInfo.ConstraintClauses, paramsStructName: recursionId + RecursiveCalculatorClassInfo.DispatchStructSuffix); return(new CalculatorClassGenerator(context, classInfo, methodInfos).GenerateAll()); }
internal CalculatorMethodGenerator(GeneratorExecutionContext context, RecursiveCalculatorClassInfo calculatorClassInfo, RecursiveMethodInfo methodInfo) { _context = context; _calculatorClassInfo = calculatorClassInfo; _methodInfo = methodInfo; }
private CalculatorClassGenerator(GeneratorExecutionContext context, RecursiveCalculatorClassInfo classInfo, IReadOnlyList <RecursiveMethodInfo> methodInfos) { _context = context; _classInfo = classInfo; _methodInfos = methodInfos; }
private string GenerateDispatchStruct(RecursiveCalculatorClassInfo classInfo, IReadOnlyList <CalculatorMethodGenerator> methodGens) { var code = new CodeBuilder(); // generic struct can't have an explicit layout so use just a simple class in such case instead bool isGeneric = !string.IsNullOrWhiteSpace(classInfo.GenericParams); if (!isGeneric) { code.AddHeaderLine("\t[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]"); code.AddHeaderLine($"\tprivate struct {classInfo.ParamsStructName} {classInfo.GenericParams}"); } else { code.AddHeaderLine($"\tprivate class {classInfo.ParamsStructName} {classInfo.GenericParams}"); } if (!string.IsNullOrEmpty(classInfo.ConstraintClauses)) { code.AddHeaderLine($"\t\t{classInfo.ConstraintClauses}"); } code.AddBlockHeader("\t"); // the call site dispatch enum field if (!isGeneric) { code.AddHeaderLine("\t\t[System.Runtime.InteropServices.FieldOffset(0)]"); } code.AddHeaderLine($"\t\tinternal readonly {classInfo.CallSiteEnumName} {RecursiveCalculatorClassInfo.CallSiteEnumFieldName};"); // params fields for each method foreach (var methodGen in methodGens) { code.AddHeaderLine(); if (!isGeneric) { code.AddHeaderLine($"\t\t[System.Runtime.InteropServices.FieldOffset(sizeof({classInfo.CallSiteEnumName}))]"); } code.AddHeaderLine( $"\t\tinternal readonly {methodGen.MethodInfo.MethodParamsStructName}{methodGen.MethodInfo.GenericParams} {methodGen.MethodInfo.MethodParamsStructName};"); } // constructors: 1 for each method foreach (var methodGen in methodGens) { var methodInfo = methodGen.MethodInfo; // constructor header //TODO: improve CodeBuilder to use code.AddBlockHeader() here code.AddHeaderLine($"\t\tinternal {classInfo.ParamsStructName}({methodInfo.MethodParamsStructName}{methodInfo.GenericParams} {methodInfo.MethodParamsStructName})"); if (!isGeneric) { code.AddHeaderLine("\t\t\t:this()"); } code.AddHeaderLine("\t\t{"); // constructor body code.AddHeaderLine($"\t\t\tthis.{RecursiveCalculatorClassInfo.CallSiteEnumFieldName} = {classInfo.CallSiteEnumName}.{methodInfo.MethodEnumValueName};"); code.AddHeaderLine($"\t\t\tthis.{methodInfo.MethodParamsStructName} = {methodInfo.MethodParamsStructName};"); code.AddHeaderLine("\t\t}"); // using an implicit operator is a small hack that simplifies code rewriting code.AddHeaderLine( $"\t\tpublic static implicit operator {classInfo.ParamsStructName}{classInfo.GenericParams}({methodInfo.MethodParamsStructName}{methodInfo.GenericParams} {methodInfo.MethodParamsStructName}) " + $" => new {classInfo.ParamsStructName}{classInfo.GenericParams}({methodInfo.MethodParamsStructName});"); } return(code.BuildString()); }