예제 #1
0
        public RunnerGenerator(GeneratorExecutionContext context, string targetFramework)
        {
            Context = context;

            TargetFramework = targetFramework;

            context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationId", out var applicationId);
            context.Log($"ApplicationId: {applicationId}");
            ApplicationId = applicationId ?? throw new Exception("ApplicationId needs to be set.");

            context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationTitle", out var applicationTitle);
            context.Log($"ApplicationTitle: {applicationTitle}");
            ApplicationTitle = applicationTitle ?? "Tests";

            context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace);
            context.Log($"RootNamespace: {rootNamespace}");
            RootNamespace = rootNamespace ?? "TestRunnerNamespace";

            ContainsSplashScreen = false;
            foreach (var file in context.AdditionalFiles)
            {
                var options = context.AnalyzerConfigOptions.GetOptions(file);
                if (options.TryGetValue("build_metadata.AdditionalFiles.IsMauiSplashScreen", out var isMauiSplashScreen) && bool.TryParse(isMauiSplashScreen, out var isSplash) && isSplash)
                {
                    ContainsSplashScreen = true;
                    break;
                }
            }
            context.Log($"ContainsSplashScreen: {ContainsSplashScreen}");
        }
예제 #2
0
        public void Execute(GeneratorExecutionContext context)
        {
            if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.TargetFramework", out var targetFramework))
            {
                return;
            }

            context.Log($"TargetFramework: {targetFramework}");

            var generator = new RunnerGenerator(context, targetFramework);

            generator?.Generate();
        }
예제 #3
0
        public void Execute(GeneratorExecutionContext context)
        {
            // retrieve the populated receiver
            if (!(context.SyntaxReceiver is RecursionSyntaxReceiver receiver))
            {
                return;
            }

            var compilation = context.Compilation;

            if ("C#" != compilation.Language)
            {
                context.ReportUnsupportedLanguageError(compilation.Language);
                return;
            }

            var goodCandidates = receiver.CandidateMethods.Select(methodSyntax =>
            {
                var methodModel  = compilation.GetSemanticModel(methodSyntax.SyntaxTree);
                var methodSymbol = methodModel.GetDeclaredSymbol(methodSyntax);
                var attr         = methodSymbol.GetAttributes().SingleOrDefault(a => a.AttributeClass.MatchesRuntimeType(TargetAttribute));
                var codeGenProps = (attr != null) ? SafeRecursionCodeGenProps.ParseFromAttribute(context, methodSyntax, attr) : null;
                return(new RecursiveMethodInfo(methodSyntax, methodSymbol, methodModel, codeGenProps));
            })
                                 .Where(t => t.CodeGenProps != null)
                                 .GroupBy(t => t.CodeGenProps.MutualRecursionId ?? "") //convert null to empty string for GroupBy
                                 .ToDictionary(g => g.Key, g => g.ToList());

            if (!goodCandidates.TryGetValue("", out var simpleCandidates))
            {
                simpleCandidates = new List <RecursiveMethodInfo>();
            }

            context.Log($"Found {simpleCandidates.Count} method(s) for simple recursion and {goodCandidates.Count - 1} method group(s) for mutual recursion generation.");

            foreach (var methodInfo in simpleCandidates)
            {
                try
                {
                    var generatedCode = CalculatorClassGenerator.GenerateSimpleSafeRec(context, methodInfo);
                    if (generatedCode != null)
                    {
                        context.AddSource($"{methodInfo.MethodSymbol.ContainingType.Name}_{methodInfo.MethodSymbol.Name}_SafeRecursion.cs",
                                          SourceText.From(generatedCode, Encoding.UTF8));
                    }
                }
                catch (Exception e)
                {
                    context.LogInternalError(methodInfo.MethodSyntax.GetLocation(),
                                             $"Processing '{methodInfo.MethodSymbol.ContainingType.Name}' resulted in an internal error '{e}'");
                }
            }

            foreach (var(recursionId, methodInfos) in goodCandidates)
            {
                // skip the simple candidates
                if (string.IsNullOrEmpty(recursionId))
                {
                    continue;
                }
                var someMethodInfo = methodInfos.First();
                try
                {
                    var generatedCode = CalculatorClassGenerator.GenerateMutualSafeRec(context, methodInfos);
                    if (generatedCode != null)
                    {
                        context.AddSource($"{someMethodInfo.MethodSymbol.ContainingType.Name}_{recursionId}_SafeRecursion.cs",
                                          SourceText.From(generatedCode, Encoding.UTF8));
                    }
                }
                catch (Exception e)
                {
                    context.LogInternalError(someMethodInfo.MethodSyntax.Parent.GetLocation(),
                                             $"Processing '{recursionId}' mutual recursion resulted in an internal error '{e}'");
                }
            }
        }
예제 #4
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());
        }