コード例 #1
0
        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}>
コード例 #2
0
        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());
        }
コード例 #3
0
        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());
        }
コード例 #4
0
 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));
     }
 }
コード例 #5
0
        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());
        }
コード例 #6
0
        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());
        }
コード例 #7
0
 internal CalculatorMethodGenerator(GeneratorExecutionContext context, RecursiveCalculatorClassInfo calculatorClassInfo, RecursiveMethodInfo methodInfo)
 {
     _context             = context;
     _calculatorClassInfo = calculatorClassInfo;
     _methodInfo          = methodInfo;
 }
コード例 #8
0
 private CalculatorClassGenerator(GeneratorExecutionContext context, RecursiveCalculatorClassInfo classInfo, IReadOnlyList <RecursiveMethodInfo> methodInfos)
 {
     _context     = context;
     _classInfo   = classInfo;
     _methodInfos = methodInfos;
 }
コード例 #9
0
        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());
        }