Exemple #1
0
        public async Task <ProgramComposition> ProcessAsync([NotNull] ProgramComposition composition, [NotNull] ProjectScriptInfo config)
        {
            if (composition == null)
            {
                throw new ArgumentNullException(nameof(composition));
            }
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            var analyzer = new UsageAnalyzer();

            while (true)
            {
                var symbolDefinitions = await analyzer.FindUsagesAsync(composition, config);

                var symbol = symbolDefinitions.FirstOrDefault(s => !s.IsProtected && s.SyntaxNode.IsTypeDeclaration() && HasNoUsage(s));
                if (symbol == null)
                {
                    break;
                }

                var rootNode = RemoveDefinition(composition.RootNode, symbol);
                composition = await composition.WithNewDocumentRootAsync(rootNode);
            }

            return(composition);
        }
Exemple #2
0
        public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, ProjectScriptInfo config)
        {
            var root = composition.RootNode;

            root = Visit(root);
            return(await composition.WithNewDocumentRootAsync(root));
        }
Exemple #3
0
        /// <summary>
        /// Generates a final script of the given document. This document has been generated by the build process and
        /// contains the entire script in syntax tree form.
        /// </summary>
        /// <param name="composition"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        public override async Task <string> GenerateAsync(ProgramComposition composition, ProjectScriptInfo config)
        {
            var analyzer = new DocumentAnalyzer();
            var result   = await analyzer.AnalyzeAsync(composition.Document);

            var buffer = new StringBuilder();

            var programContent = string.Join("\n", result.Parts.OfType <ProgramScriptPart>().Select(p => p.GenerateContent()));

            buffer.Append(programContent);
            buffer.Append("\n");

            var extensionContent = string.Join("\n", result.Parts.OfType <ExtensionScriptPart>().Select(p => p.GenerateContent()));

            if (!string.IsNullOrWhiteSpace(extensionContent))
            {
                // Extension classes are made possible by forcefully ending Space Engineer's wrapping Program class
                // and removing the final ending brace of the last extension class to let Space Engineers close it
                // for itself.

                // Close off the Program class
                buffer.Append("}\n");
                buffer.Append(extensionContent);
                // Remove the ending brace of the last extension class
                var index = FindEndBrace(buffer);
                if (index >= 0)
                {
                    buffer.Length = index;
                }
            }

            return(TrimPointlessWhitespace(buffer.ToString()));
        }
Exemple #4
0
        public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, ProjectScriptInfo config)
        {
            _externallyReferencedTypes.Clear();
            var usageAnalyzer = new UsageAnalyzer();
            var typeUsages    = await usageAnalyzer.FindUsagesAsync(composition, config);

            foreach (var typeUsage in typeUsages)
            {
                foreach (var part in typeUsage.Usage)
                {
                    foreach (var location in part.Locations)
                    {
                        var node = composition.RootNode.FindNode(location.Location.SourceSpan);
                        if (IsInProgram(node))
                        {
                            _externallyReferencedTypes.Add(typeUsage.FullName);
                        }
                    }
                }
            }

            var root = composition.RootNode;

            root = Visit(root);
            return(await composition.WithNewDocumentRootAsync(root));
        }
Exemple #5
0
        public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config)
        {
            var newDocument = await Simplifier.ReduceAsync(composition.Document).ConfigureAwait(false);

            composition = await(composition.WithDocumentAsync(newDocument).ConfigureAwait(false));

            _externallyReferencedMembers.Clear();
            var usageAnalyzer = new UsageAnalyzer();
            var typeUsages    = await usageAnalyzer.FindUsagesAsync(composition, config);

            foreach (var typeUsage in typeUsages)
            {
                foreach (var part in typeUsage.Usage)
                {
                    foreach (var location in part.Locations)
                    {
                        var node = composition.RootNode.FindNode(location.Location.SourceSpan);
                        if (!IsInProgram(node))
                        {
                            _externallyReferencedMembers.Add(typeUsage.FullName);
                        }
                    }
                }
            }

            var root = composition.RootNode;

            root = Visit(root);
            return(await composition.WithNewDocumentRootAsync(root));
        }
Exemple #6
0
        /// <summary>
        /// Generates a final script of the given document. This document has been generated by the build process and
        /// contains the entire script in syntax tree form.
        /// </summary>
        /// <param name="composition"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        public override async Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config)
        {
            //var dumper = new PreserveDebugDumper(@"e:\dump0.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            var simplifier = new CodeSimplifier();

            composition = await simplifier.ProcessAsync(composition, config);

            //dumper = new PreserveDebugDumper(@"e:\dump1.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            var renamer = new SymbolRenamer();

            composition = await renamer.ProcessAsync(composition, config);

            //dumper = new PreserveDebugDumper(@"e:\dump2.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            var compactor = new WhitespaceCompactor();

            composition = await compactor.ProcessAsync(composition, config);

            //dumper = new PreserveDebugDumper(@"e:\dump3.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            var lineWrapper = new LineWrapper();

            composition = await lineWrapper.ProcessAsync(composition, config);

            //dumper = new PreserveDebugDumper(@"e:\dump4.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            return(await GenerateScriptAsync(composition));
        }
Exemple #7
0
        public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config)
        {
            ClearLineInfo();
            var root = composition.RootNode;

            root = Visit(root);
            return(await composition.WithNewDocumentRootAsync(root));
        }
Exemple #8
0
        //static char[] GetSymbolChars()
        //{
        //    var chars = new List<char>(50000);
        //    for (var u = 0; u <= ushort.MaxValue; u++)
        //    {
        //        var ch = (char)u;
        //        if (IsDeniedCharacter(ch))
        //            continue;
        //        if (char.IsLetter(ch))
        //            chars.Add(ch);
        //    }
        //    return chars.ToArray();
        //}

        //static bool IsDeniedCharacter(char ch)
        //{
        //    switch (ch)
        //    {
        //        case '\u180E':
        //        case '\u0600':
        //        case '\u00AD':
        //            return true;
        //    }

        //    return false;
        //}

        //static readonly char[] BaseNChars = GetSymbolChars();

        public async Task <ProgramComposition> ProcessAsync([NotNull] ProgramComposition composition, [NotNull] MDKProjectProperties config)
        {
            if (composition == null)
            {
                throw new ArgumentNullException(nameof(composition));
            }
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            var analyzer          = new SymbolAnalyzer();
            var symbolDefinitions = analyzer.FindSymbols(composition, config).ToList();

            symbolDefinitions.Sort((a, b) => a.SyntaxNode.FullSpan.Start);
            var distinctSymbolNames = new HashSet <string>(symbolDefinitions
                                                           .Where(s => !s.IsProtected)
                                                           .Select(s => s.Symbol.Name)
                                                           .Distinct());
            var symbolSrc           = 0;
            var minifiedSymbolNames = distinctSymbolNames
                                      .ToDictionary(n => n, n => GenerateNewName(distinctSymbolNames, ref symbolSrc));

            while (true)
            {
                SymbolDefinitionInfo definition = null;
                string newName = null;
                foreach (var symbolDefinition in symbolDefinitions)
                {
                    if (symbolDefinition.IsProtected)
                    {
                        continue;
                    }
                    if (!minifiedSymbolNames.TryGetValue(symbolDefinition.Symbol.Name, out newName))
                    {
                        continue;
                    }
                    definition = symbolDefinition;
                    break;
                }
                if (definition == null)
                {
                    break;
                }

                var document    = composition.Document;
                var documentId  = document.Id;
                var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, definition.Symbol, newName, document.Project.Solution.Options);

                composition = await composition.WithDocumentAsync(newSolution.GetDocument(documentId));

                symbolDefinitions = analyzer.FindSymbols(composition, config).ToList();
                symbolDefinitions.Sort((a, b) => a.SyntaxNode.FullSpan.Start);
            }

            return(composition);
        }
Exemple #9
0
        public ImmutableArray <SymbolDefinitionInfo> FindSymbols(ProgramComposition composition, ProjectScriptInfo config)
        {
            var root          = composition.RootNode;
            var semanticModel = composition.SemanticModel;

            return(root.DescendantNodes().Where(node => node.IsSymbolDeclaration())
                   .Select(n => new SymbolDefinitionInfo(semanticModel.GetDeclaredSymbol(n), n))
                   .Where(IsNotSpecialDefinitions)
                   .Select(WithUpdatedProtectionFlag)
                   .ToImmutableArray());
        }
Exemple #10
0
        public async Task <ImmutableArray <SymbolDefinitionInfo> > FindUsagesAsync(ProgramComposition composition, ProjectScriptInfo config)
        {
            var symbolDefinitions = _symbolAnalyzer.FindSymbols(composition, config).ToArray();

            for (var index = 0; index < symbolDefinitions.Length; index++)
            {
                symbolDefinitions[index] = await WithUsageDataAsync(symbolDefinitions[index], composition);
            }

            return(symbolDefinitions.ToImmutableArray());
        }
Exemple #11
0
        public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config)
        {
            var newDocument = await Simplifier.ReduceAsync(composition.Document).ConfigureAwait(false);

            composition = await(composition.WithDocumentAsync(newDocument).ConfigureAwait(false));

            _externallyReferencedMembers.Clear();

            var root = composition.RootNode;

            root = Visit(root);
            return(await composition.WithNewDocumentRootAsync(root));
        }
Exemple #12
0
        /// <summary>
        /// Generates a final script of the given document. This document has been generated by the build process and
        /// contains the entire script in syntax tree form.
        /// </summary>
        /// <param name="composition"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        public override async Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config)
        {
            //var dumper = new PreserveDebugDumper(@"e:\dump0.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            var simplifier = new CommentStripper();

            composition = await simplifier.ProcessAsync(composition, config);

            //dumper = new PreserveDebugDumper(@"e:\dump1.txt");
            //dumper.Visit(await composition.Document.GetSyntaxRootAsync());

            return(await GenerateScriptAsync(composition));
        }
Exemple #13
0
        public async override Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config)
        {
            //simplify
            var simplifier = new CodeSimplifier();

            composition = await simplifier.ProcessAsync(composition, config);

            //Compact
            var compactor = new WhitespaceCompactor();

            composition = await compactor.ProcessAsync(composition, config);

            //Line Wrapper
            var lineWrapper = new LineWrapper();

            composition = await lineWrapper.ProcessAsync(composition, config);

            return(await base.GenerateScriptAsync(composition));
        }
        /// <summary>
        /// Generates a final script of the given document. This document has been generated by the build process and
        /// contains the entire script in syntax tree form.
        /// </summary>
        /// <param name="composition"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        public override async Task <string> GenerateAsync(ProgramComposition composition, ProjectScriptInfo config)
        {
            var simplifier = new CodeSimplifier();

            composition = await simplifier.ProcessAsync(composition, config);

            var renamer = new SymbolRenamer();

            composition = await renamer.ProcessAsync(composition, config);

            var compactor = new WhitespaceCompactor();

            composition = await compactor.ProcessAsync(composition, config);

            var lineWrapper = new LineWrapper();

            composition = await lineWrapper.ProcessAsync(composition, config);

            // return (await composition.Document.GetTextAsync()).ToString();

            return(await GenerateScriptAsync(composition));
        }
Exemple #15
0
        async Task <string> GenerateScriptAsync(ProgramComposition composition)
        {
            var root = composition.RootNode;

            composition = await composition.WithNewDocumentRootAsync(root);

            var analyzer = new DocumentAnalyzer();
            var result   = await analyzer.AnalyzeAsync(composition.Document);

            var buffer = new StringBuilder();

            var programContent = string.Join("", result.Parts.OfType <ProgramScriptPart>().Select(p => p.GenerateContent()));

            buffer.Append(programContent);
            var extensionContent = string.Join("", result.Parts.OfType <ExtensionScriptPart>().Select(p => p.GenerateContent()));

            if (!string.IsNullOrWhiteSpace(extensionContent))
            {
                // Extension classes are made possible by forcefully ending Space Engineer's wrapping Program class
                // and removing the final ending brace of the last extension class to let Space Engineers close it
                // for itself.

                // Close off the Program class. Unfortunately we do need a newline because the whitespace compactor
                // can't properly deal with the separation.
                buffer.Append("\n}");
                buffer.Append(extensionContent);
                // Remove the ending brace of the last extension class
                var index = FindEndBrace(buffer);
                if (index >= 0)
                {
                    buffer.Length = index;
                }
            }

            return(TrimPointlessWhitespace(buffer.ToString()));
        }
Exemple #16
0
        public async Task <ProgramComposition> ProcessAsync([NotNull] ProgramComposition composition, [NotNull] ProjectScriptInfo config)
        {
            if (composition == null)
            {
                throw new ArgumentNullException(nameof(composition));
            }
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            var nodes             = new Dictionary <ISymbol, Node>();
            var analyzer          = new UsageAnalyzer();
            var symbolDefinitions = await analyzer.FindUsagesAsync(composition, config);

            var symbolLookup = symbolDefinitions.GroupBy(d => d.Symbol).ToDictionary(g => g.Key, g => g.ToList());
            var rootNode     = composition.RootNode;

            foreach (var definition in symbolDefinitions)
            {
                if (!(definition.Symbol is ITypeSymbol typeSymbol))
                {
                    continue;
                }
                if (typeSymbol.TypeKind == TypeKind.TypeParameter)
                {
                    continue;
                }

                if (!nodes.TryGetValue(definition.Symbol, out var node))
                {
                    nodes[definition.Symbol] = node = new Node(definition);
                }
                else
                {
                    node.Definitions.Add(definition);
                }

                foreach (var usage in definition.Usage)
                {
                    foreach (var location in usage.Locations)
                    {
                        var enclosingSymbol = await FindTypeSymbolAsync(rootNode, location);

                        var enclosingSymbolDefinitions = symbolLookup[enclosingSymbol];
                        if (!nodes.TryGetValue(enclosingSymbol, out var referencingNode))
                        {
                            nodes[enclosingSymbol] = referencingNode = new Node(enclosingSymbolDefinitions);
                        }
                        if (node != referencingNode)
                        {
                            referencingNode.References.Add(node);
                        }
                    }
                }
            }

            var program = symbolDefinitions.FirstOrDefault(d => d.FullName == "Program");

            if (program == null || !nodes.TryGetValue(program.Symbol, out var programNode))
            {
                throw new BuildException(string.Format(Text.TypeTrimmer_ProcessAsync_NoEntryPoint, composition.Document.Project.FilePath));
            }

            var usedNodes    = new List <Node>();
            var queue        = new Queue <Node>();
            var visitedNodes = new HashSet <Node>();

            queue.Enqueue(programNode);
            while (queue.Count > 0)
            {
                var node = queue.Dequeue();
                if (!visitedNodes.Add(node))
                {
                    continue;
                }
                usedNodes.Add(node);
                foreach (var reference in node.References)
                {
                    queue.Enqueue(reference);
                }
            }

            var usedSymbolDefinitions   = usedNodes.SelectMany(n => n.Definitions).ToImmutableHashSet();
            var unusedSymbolDefinitions = symbolDefinitions.Where(definition => IsEligibleForRemoval(definition) && !usedSymbolDefinitions.Contains(definition)).ToList();
            var nodesToRemove           = unusedSymbolDefinitions.Select(definition => definition.FullName).ToImmutableHashSet();

            var walker = new RemovalWalker(nodesToRemove);

            rootNode = walker.Visit(rootNode);
            foreach (var symbol in unusedSymbolDefinitions)
            {
                rootNode = RemoveDefinition(rootNode, symbol);
            }

            return(await composition.WithNewDocumentRootAsync(rootNode));
        }
Exemple #17
0
        async Task <SymbolDefinitionInfo> WithUsageDataAsync(SymbolDefinitionInfo definition, ProgramComposition composition)
        {
            var references = (await SymbolFinder.FindReferencesAsync(definition.Symbol, composition.Document.Project.Solution))
                             .ToImmutableArray();

            definition = definition.WithUsageData(references);

            // Check for extension class usage
            var symbol = definition.Symbol;

            if (symbol.IsDefinition && symbol is ITypeSymbol typeSymbol && typeSymbol.TypeKind == TypeKind.Class && typeSymbol.IsStatic && typeSymbol.ContainingType == null)
            {
                var members = typeSymbol.GetMembers().Where(m => m is IMethodSymbol methodSymbol && methodSymbol.IsStatic && methodSymbol.IsExtensionMethod).ToArray();
                foreach (var member in members)
                {
                    references = references.AddRange((await SymbolFinder.FindReferencesAsync(member, composition.Document.Project.Solution)));
                }
                definition = definition.WithUsageData(references);
            }

            return(definition);
        }
Exemple #18
0
        async Task <SymbolDefinitionInfo> WithUsageDataAsync(SymbolDefinitionInfo definition, ProgramComposition composition)
        {
            var references = (await SymbolFinder.FindReferencesAsync(definition.Symbol, composition.Document.Project.Solution))
                             .ToImmutableArray();

            return(definition.WithUsageData(references));
        }
Exemple #19
0
 /// <summary>
 /// Generate a Space Engineers script file from the given document
 /// </summary>
 /// <param name="composition"></param>
 /// <param name="config"></param>
 /// <returns></returns>
 public abstract Task <string> GenerateAsync(ProgramComposition composition, ProjectScriptInfo config);
Exemple #20
0
 /// <summary>
 /// Generate a Space Engineers script file from the given document
 /// </summary>
 /// <param name="composition"></param>
 /// <param name="config"></param>
 /// <returns></returns>
 public abstract Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config);