Пример #1
0
        internal string GenerateMethodParamsStruct()
        {
            var code = new CodeBuilder();

            code.AddHeaderLine($"\tprivate struct {_methodInfo.MethodParamsStructName} {_methodInfo.GenericParams}");
            if (!string.IsNullOrEmpty(_methodInfo.ConstraintClauses))
            {
                code.AddHeaderLine($"\t\t{_methodInfo.ConstraintClauses}");
            }
            code.AddBlockHeader("\t");

            var paramsSymbols = _methodInfo.MethodSyntax.ParameterList.Parameters
                                .Select(parameterSyntax => (IParameterSymbol)_methodInfo.MethodModel.GetDeclaredSymbol(parameterSyntax))
                                .ToList();

            // fields
            code.AddHeaderLine(string.Join("\n",
                                           paramsSymbols.Select(paramSymbol => $"\t\tinternal readonly {paramSymbol.Type.ToDisplayString()} {paramSymbol.Name};")));
            code.AddHeaderLine();

            // constructor header
            code.AddBlockHeader($"\t\tinternal {_methodInfo.MethodParamsStructName}(" + string.Join(", ",
                                                                                                    paramsSymbols.Select(paramSymbol => $"{paramSymbol.Type.ToDisplayString()} {paramSymbol.Name}")) + ")");
            // constructor body
            code.AddHeaderLine(string.Join("\n",
                                           paramsSymbols.Select(paramSymbol => $"\t\t\tthis.{paramSymbol.Name} = {paramSymbol.Name};")));

            return(code.BuildString());
        }
Пример #2
0
        private void GenerateProxyMethodBody(CodeBuilder code)
        {
            // method body
            code.AddBlockHeader("\t");
            // create the initial CallParams struct
            code.AddHeader($"\t\tvar {ParamsVarName} = new {_methodInfo.MethodParamsStructName}{_methodInfo.GenericParams} (")
            .AddHeader(string.Join(", ",
                                   _methodInfo.MethodSyntax.ParameterList.Parameters.Select(parameterSyntax => _methodInfo.MethodModel.GetDeclaredSymbol(parameterSyntax).Name)))
            .AddHeaderLine(");");

            code.AddHeaderLine($"\t\tvar runner = new {_calculatorClassInfo.CalculatorClassName}{_calculatorClassInfo.GenericParams}();");
            code.AddHeaderLine($"\t\trunner.RunRecursion({ParamsVarName});");
            if (_methodInfo.ShouldGenerateReturnField)
            {
                code.AddHeaderLine($"\t\treturn runner.{_methodInfo.ReturnFieldName};");
            }
        }
Пример #3
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());
        }
Пример #4
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());
        }
Пример #5
0
        internal string GenerateProxyMethod()
        {
            var code = new CodeBuilder();

            code.AddHeaderLine("\t//Proxy method");
            GenerateProxyMethodAttributes(code);
            GenerateProxyMethodModifiers(code);
            // method signature itself
            GenerateProxyMethodSignature(code);
            GenerateProxyMethodBody(code);

            return(code.BuildString());
        }
Пример #6
0
        private void GenerateProxyMethodSignature(CodeBuilder code)
        {
            code.AddHeader(" ");
            code.AddHeader(_methodInfo.ReturnType);
            code.AddHeader(_methodInfo.MethodName);
            code.AddHeader(_methodInfo.GenericParams);

            code.AddHeader(GetProxyMethodParamsList().ToFullString());

            if (!string.IsNullOrEmpty(_methodInfo.ConstraintClauses))
            {
                code.AddHeader("\t\t");
                code.AddHeaderLine(_methodInfo.ConstraintClauses);
            }
        }
Пример #7
0
        private string GenerateProxyMethods(List <CalculatorMethodGenerator> methodGens)
        {
            bool alwaysExpose = methodGens.Count == 1;
            var  code         = new CodeBuilder();

            foreach (var methodGen in methodGens)
            {
                if (alwaysExpose || methodGen.MethodInfo.CodeGenProps.ExposeAsEntryPoint)
                {
                    code.AddHeaderLine(methodGen.GenerateProxyMethod());
                }
            }

            return(code.BuildString());
        }
Пример #8
0
        private string GenerateAll()
        {
            var allCode       = new CodeBuilder();
            var classInfo     = _classInfo;
            var classSyntax   = _classInfo.ContainingClassSyntax;
            var namespaceName = classInfo.ContainingClassSymbol.ContainingNamespace.ToDisplayString();

            var methodGens    = _methodInfos.Select(mi => new CalculatorMethodGenerator(_context, classInfo, mi)).ToList();
            var targetMethods = new TargetMethodsInfo(_methodInfos);

            // copy all the usings from the original file
            var root = (CompilationUnitSyntax)classSyntax.SyntaxTree.GetRoot();

            allCode.AddHeaderLine(root.Usings.ToFullString());
            allCode.AddHeaderLine();

            // namespace
            allCode.AddBlockHeader($"namespace {namespaceName}");

            // class declaration
            var classModifiersString = classSyntax.Modifiers.ToFullString();

            if (!classModifiersString.Contains("partial"))
            {
                _context.ReportUnsupportedSyntaxError(classSyntax, $"Target class '{_classInfo.ContainingClassSymbol.Name}' for '{classInfo.RecursionId}' is not partial");
                return(null);
            }

            allCode.AddBlockHeader($"{classModifiersString} class {_classInfo.ContainingClassSymbol.Name}");

            allCode.AddHeaderLine();
            allCode.AddHeaderLine(GenerateProxyMethods(methodGens));

            allCode.AddHeaderLine();
            allCode.AddHeaderLine(GenerateParamsStructs(classInfo, methodGens));

            allCode.AddHeaderLine();
            allCode.AddHeaderLine(GenerateCalculatorClass(classInfo, methodGens, targetMethods));

            return(allCode.BuildString());
        }
Пример #9
0
 private void GenerateProxyMethodAttributes(CodeBuilder code)
 {
     // copy all attributes as is except for our target attribute
     foreach (var attrListSyntax in _methodInfo.MethodSyntax.AttributeLists)
     {
         var rawAttrList = attrListSyntax.Attributes
                           .Where(asy =>
         {
             var attrConstrSymbol   = (IMethodSymbol)_methodInfo.MethodModel.GetSymbolInfo(asy).Symbol;
             ITypeSymbol?attrSymbol = attrConstrSymbol.ReceiverType;
             return(!attrSymbol.MatchesRuntimeType(SafeRecursionGenerator.TargetAttribute));
         }).ToList();
         if (rawAttrList.Any())
         {
             // calling GetSeparators on the original list is a hack but seems to work so far
             var attrList = SyntaxFactory.SeparatedList(rawAttrList, attrListSyntax.Attributes.GetSeparators());
             // remove the trivia like #region that might be attached to the first attribute
             var attrs = attrListSyntax.Update(attrListSyntax.OpenBracketToken, attrListSyntax.Target, attrList, attrListSyntax.CloseBracketToken).WithoutTrivia();
             code.AddHeaderLine("\t" + attrs.ToFullString());
         }
     }
 }
Пример #10
0
        internal string GenerateCalculatorMethodBody(TargetMethodsInfo targetMethods)
        {
            var code = new CodeBuilder();

            // unpack params
            code.AddHeaderLine("\t\t// unpack params");
            code.AddHeaderLine(string.Join("\n",
                                           _methodInfo.MethodSyntax.ParameterList.Parameters.Select(parameterSyntax =>
            {
                var paramSymbol = _methodInfo.MethodModel.GetDeclaredSymbol(parameterSyntax);
                return($"\t\tvar {paramSymbol.Name} = {ParamsVarName}.{paramSymbol.Name};");
            })));

            // find all recursive invocations
            var nodesToReplace = CollectInvocationsWalker.CollectInvocations(_context, _methodInfo, targetMethods);

            if (nodesToReplace.ContainsCriticalFailure)
            {
                _context.Log(_methodInfo.MethodSyntax.GetLocation(), $"An unsupported code was found for '{_methodInfo.MethodSymbol.Name}'. Abort processing.");
                return(null);
            }

            _context.Log(_methodInfo.MethodSyntax.GetLocation(), $"'{_methodInfo.MethodSymbol.Name}':" +
                         $" {nodesToReplace.VoidCalls.Count} void call(s)" +
                         $" {nodesToReplace.Assignments.Count} assignment(s)" +
                         $", {nodesToReplace.DeclarationAndAssignments.Count} var declaration(s)" +
                         $", {nodesToReplace.ReturnsRecursive.Count} recursive call(s) in return(s)" +
                         $", {nodesToReplace.Returns.Count} return(s)");


            // process all supported kinds of recursive invocations
            var allNodesToReplace = nodesToReplace.Returns.Cast <StatementSyntax>()
                                    .Concat(nodesToReplace.ReturnsRecursive)
                                    .Concat(nodesToReplace.VoidCalls.Select(t => t.containingStatement))
                                    .Concat(nodesToReplace.Assignments.Select((t) => t.containingStatement))
                                    .Concat(nodesToReplace.DeclarationAndAssignments.Select(t => t.containingStatement));
            var newRoot = _methodInfo.MethodSyntax.Body.ReplaceNodes(allNodesToReplace, (origNode, curNode) =>
            {
                // _context.Log("!! " + origNode.Kind() + " " + origNode.ToFullStringTrimmed());
                switch (origNode.Kind())
                {
                case SyntaxKind.ReturnStatement:
                    return(ReplaceReturn(targetMethods, (ReturnStatementSyntax)origNode, (ReturnStatementSyntax)curNode));

                case SyntaxKind.ExpressionStatement:
                    var origExpressionNode = (ExpressionStatementSyntax)origNode;
                    var expression         = origExpressionNode.Expression;
                    switch (expression.Kind())
                    {
                    case SyntaxKind.InvocationExpression:
                        return(ReplaceVoidCall(targetMethods, origExpressionNode, (ExpressionStatementSyntax)curNode));

                    case SyntaxKind.SimpleAssignmentExpression:
                        return(ReplaceAssignment(targetMethods, origExpressionNode, (ExpressionStatementSyntax)curNode));

                    default:
                        throw new AntiSOGenException($"Unexpected expression kind {expression.Kind()} to replace {origNode.ToFullStringTrimmed()}");
                    }


                case SyntaxKind.LocalDeclarationStatement:
                    return(ReplaceLocalVarDeclrAssignment(targetMethods, (LocalDeclarationStatementSyntax)origNode, (LocalDeclarationStatementSyntax)curNode));

                default:
                    throw new AntiSOGenException($"Unexpected statement kind {origNode.Kind()} to replace {origNode.ToFullStringTrimmed()}");
                }
            });

            code.AddHeaderLine("\t\t// method body");
            code.AddHeader(newRoot.ToFullString());
            return(code.BuildString());
        }
Пример #11
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());
        }