diff --git a/.gitignore b/.gitignore index b5ca78e..9e96a35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +tools/ +*.nupkg + ################# ## Eclipse ################# diff --git a/CSharpMinifier.GUI/App.config b/CSharpMinifier.GUI/App.config index f8ac8ea..6de17de 100644 --- a/CSharpMinifier.GUI/App.config +++ b/CSharpMinifier.GUI/App.config @@ -1,12 +1,12 @@ - + -
+
- - + + diff --git a/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj b/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj index ec4383a..110bea7 100644 --- a/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj +++ b/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,7 +9,7 @@ Properties CSharpMinifier.GUI CSharpMinifier.GUI - v4.5 + v4.6.1 512 @@ -35,6 +35,9 @@ 4 false + + true + @@ -76,20 +79,20 @@ - - - {a26c936c-846b-4165-9574-42c74075d4cd} - CSharpMinifier - - + + + {a26c936c-846b-4165-9574-42c74075d4cd} + CSharpMinifier + + - - \ No newline at end of file + + diff --git a/CSharpMinifier.Tests/IdGeneratorTests.cs b/CSharpMinifier.Tests/IdGeneratorTests.cs deleted file mode 100644 index b62ed78..0000000 --- a/CSharpMinifier.Tests/IdGeneratorTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NUnit.Framework; -using System.Collections.Generic; - -namespace CSharpMinifier.Tests -{ - [TestFixture] - public class IdGeneratorTests - { - [Test] - public void MinIdGeneratorTest() - { - var minIdGenerator = new MinIdGenerator(); - for (int i = 0; i < MinIdGenerator.Chars0.Length; i++) - Assert.AreEqual(MinIdGenerator.Chars0[i].ToString(), minIdGenerator.Next()); - for (int i = 0; i < MinIdGenerator.CharsN.Length; i++) - Assert.AreEqual(MinIdGenerator.Chars0[0].ToString() + MinIdGenerator.CharsN[i], minIdGenerator.Next()); - } - - [Test] - public void RandomIdGeneratorTest() - { - var randomIdGenerator = new RandomIdGenerator(1, 1); - - var generatedIds = new HashSet(); - for (int i = 0; i < MinIdGenerator.Chars0.Length; i++) - generatedIds.Add(randomIdGenerator.Next()); - - Assert.AreEqual(MinIdGenerator.Chars0.Length, generatedIds.Count); - Assert.AreEqual(MinIdGenerator.Chars0.Length - 1, randomIdGenerator.CurrentCombinationNumber); - } - } -} diff --git a/CSharpMinifier.Tests/MinifierTests.cs b/CSharpMinifier.Tests/MinifierTests.cs index a6a3757..b33245c 100644 --- a/CSharpMinifier.Tests/MinifierTests.cs +++ b/CSharpMinifier.Tests/MinifierTests.cs @@ -10,16 +10,24 @@ public class MinifierTests { Dictionary Samples; + string _cscPath; + + bool CanCompile(string program) => + CompileUtils.CanCompile(program, _cscPath); + [SetUp] public void Init() { + var baseDirectory = System.AppDomain.CurrentDomain.BaseDirectory; + _cscPath = Path.Combine(baseDirectory, "..", "..", "..", "..", "tools", "Microsoft.Net.Compilers", "tools", "csc.exe"); + Samples = new Dictionary(); - var sampleFiles = Directory.GetFiles(@"..\..\Samples"); + var sampleFiles = Directory.GetFiles(Path.Combine(baseDirectory, "..", "..", "..", "Samples")); foreach (var file in sampleFiles) { var code = File.ReadAllText(file); Samples.Add(Path.GetFileNameWithoutExtension(file), code); - if (!CompileUtils.CanCompile(code)) + if (!CanCompile(code)) Assert.Inconclusive("All input code should be compilied"); } } @@ -31,11 +39,11 @@ public void RemoveSpaces() { SpacesRemoving = true }; - var minifier = new Minifier(minifierOptions); + var minifier = new RoslynMinifier(minifierOptions); foreach (var sample in Samples) { var minified = minifier.MinifyFromString(sample.Value); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); if (sample.Key == "Test1") Assert.IsFalse(minified.Contains(" /*")); } @@ -46,16 +54,16 @@ public void LineLengthConstraint() { var minifierOptions = new MinifierOptions { - SpacesRemoving = true, + SpacesRemoving = false, CommentsRemoving = true, LineLength = 80, RegionsRemoving = true }; - var minifier = new Minifier(minifierOptions); + var minifier = new RoslynMinifier(minifierOptions); foreach (var sample in Samples) { var minified = minifier.MinifyFromString(sample.Value); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); } } @@ -67,13 +75,13 @@ public void RemoveComments() SpacesRemoving = true, CommentsRemoving = true }; - var minifier = new Minifier(minifierOptions); + var minifier = new RoslynMinifier(minifierOptions); var test = Samples["Test1"]; if (!test.Contains("//") || !test.Contains("/*") || !test.Contains("*/")) Assert.Inconclusive("Invalid test sample for RemoveComments test"); var minified = minifier.MinifyFromString(test); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); Assert.IsFalse(minified.Contains("//")); Assert.IsFalse(minified.Contains("/*")); Assert.IsFalse(minified.Contains("*/")); @@ -87,13 +95,13 @@ public void RemoveRegions() SpacesRemoving = true, RegionsRemoving = true }; - var minifier = new Minifier(minifierOptions); + var minifier = new RoslynMinifier(minifierOptions); var test = Samples["Test1"]; if (!test.Contains("#region") || !test.Contains("#endregion")) Assert.Inconclusive("Invalid test sample for RemoveRegions test"); var minified = minifier.MinifyFromString(test); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); Assert.IsFalse(minified.Contains("#region")); Assert.IsFalse(minified.Contains("#endregion")); } @@ -107,11 +115,11 @@ public void CompressIdentifiers() MembersCompressing = true, TypesCompressing = true }; - var minifier = new Minifier(minifierOptions); + var minifier = new RoslynMinifier(minifierOptions); foreach (var sample in Samples) { var minified = minifier.MinifyFromString(sample.Value); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); } } @@ -122,7 +130,7 @@ public void CompressMisc() { MiscCompressing = true }; - var minifier = new Minifier(minifierOptions); + var minifier = new RoslynMinifier(minifierOptions); var minified = minifier.MinifyFromString(Samples["MiscCompression"]); Assert.IsTrue(minified.Contains("255")); Assert.IsTrue(minified.Contains("0x7048860F9180")); @@ -134,7 +142,7 @@ public void CompressMisc() [Test] public void IgnoredIdAndComments() { - var minifier = new Minifier(null, new string[] { "unminifiedId" }, new string[] { "unremovableComment", "/*unremovableComment1*/" }); + var minifier = new RoslynMinifier(null, new string[] { "unminifiedId" }, new string[] { "unremovableComment", "/*unremovableComment1*/" }); var test = Samples["Test1"]; if (!test.Contains("unminifiedId") || !test.Contains("unremovableComment") || !test.Contains("/*unremovableComment1*/")) Assert.Inconclusive("Invalid test sample for IgnoredIdAndComments test"); @@ -147,7 +155,7 @@ public void IgnoredIdAndComments() [Test] public void IncrementPlus() { - var minifier = new Minifier(new MinifierOptions { LocalVarsCompressing = false }); + var minifier = new RoslynMinifier(new MinifierOptions { LocalVarsCompressing = false }); var testCode = @"public class Test { @@ -171,7 +179,7 @@ public void Main() [Test] public void NestedClassesWithSameInnterName() { - var minifier = new Minifier(); + var minifier = new RoslynMinifier(); var testCode = @"public static class A { @@ -196,10 +204,10 @@ public void foo() {} [Test] public void ShouldProperlyConvertEnumWithoutInitializersToInt() { - var minifier = new Minifier(); + var minifier = new RoslynMinifier(); var minified = minifier.MinifyFromString(Samples["EnumToIntConversion"]); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); Assert.AreEqual( "using System.Collections.Generic;class c{static int a=5;static Dictionary>b=new Dictionary>{{5,new Dictionary{{' ',8}}},{6,new Dictionary{{' ',24}}}};}", minified); diff --git a/CSharpMinifier.Tests/Properties/AssemblyInfo.cs b/CSharpMinifier.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5a918ca..0000000 --- a/CSharpMinifier.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("CSharpMinifier.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("CSharpMinifier.Tests")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8f63cfda-b2cf-4ab1-81e2-0c6c7e9ce794")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharpMinifier.Tests/app.config b/CSharpMinifier.Tests/app.config new file mode 100644 index 0000000..b4aac43 --- /dev/null +++ b/CSharpMinifier.Tests/app.config @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpMinifier.Tests/packages.config b/CSharpMinifier.Tests/packages.config index 967502d..2f17a5b 100644 --- a/CSharpMinifier.Tests/packages.config +++ b/CSharpMinifier.Tests/packages.config @@ -1,4 +1,47 @@  - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpMinifier.sln b/CSharpMinifier.sln index 01fcdfe..f2ea04f 100644 --- a/CSharpMinifier.sln +++ b/CSharpMinifier.sln @@ -1,13 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpMinifier", "CSharpMinifier\CSharpMinifier.csproj", "{A26C936C-846B-4165-9574-42C74075D4CD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpMinifier", "CSharpMinifier\CSharpMinifier.csproj", "{A26C936C-846B-4165-9574-42C74075D4CD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpMinifier.GUI", "CSharpMinifier.GUI\CSharpMinifier.GUI.csproj", "{1E6C7AB5-3E2F-4F5E-B5BD-B24E2258D0E2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpMinifier.Tests", "CSharpMinifier.Tests\CSharpMinifier.Tests.csproj", "{24A6BB34-4BE9-4504-926D-16D52FE5AC41}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpMinifier.Tests", "CSharpMinifier.Tests\CSharpMinifier.Tests.csproj", "{24A6BB34-4BE9-4504-926D-16D52FE5AC41}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D6B4D5F0-B989-40F3-9F60-0D96701A1103}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,4 +36,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A1AE996D-FA41-4FA8-B2F4-5AA1AF1719F4} + EndGlobalSection EndGlobal diff --git a/CSharpMinifier/CSharpMinifier.csproj b/CSharpMinifier/CSharpMinifier.csproj index a59b708..599c39f 100644 --- a/CSharpMinifier/CSharpMinifier.csproj +++ b/CSharpMinifier/CSharpMinifier.csproj @@ -1,102 +1,17 @@ - - - + + - Debug - AnyCPU - {A26C936C-846B-4165-9574-42C74075D4CD} - Library - Properties - CSharpMinifier - CSharpMinifier - v4.5 - 512 - - + netstandard2.0;net461 + true + ../dist - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - False - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - False - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Cecil.dll - - - False - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - - - False - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Xml.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Mdb.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Pdb.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Rocks.dll - - - - + - - - - - - - - - - - - - - - + + - - Designer - + - - - \ No newline at end of file + + diff --git a/CSharpMinifier/CompileUtils.cs b/CSharpMinifier/CompileUtils.cs index 8ed8029..900b7a1 100644 --- a/CSharpMinifier/CompileUtils.cs +++ b/CSharpMinifier/CompileUtils.cs @@ -1,27 +1,135 @@ -using Microsoft.CSharp; -using System.CodeDom.Compiler; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; namespace CSharpMinifier { - public class CompileUtils - { - public static bool CanCompile(string program) - { - return !Compile(program).Errors.HasErrors; - } - - public static CompilerResults Compile(string program) - { - CompilerResults compilerResults = null; - using (CSharpCodeProvider provider = new CSharpCodeProvider()) - { - compilerResults = provider.CompileAssemblyFromSource(new CompilerParameters(new string[] - { - "System.dll" - }), - new string[] { program }); - } - return compilerResults; - } - } + using System; + using System.Collections.Generic; + + /// + /// Emulates System.CodeDom.Compiler.CompilerError. + /// + + public sealed class CompilerError + { + public string ErrorNumber { get; } + public string ErrorText { get; } + public string FileName { get; } + public bool IsWarning { get; } + public int Line { get; } + public int Column { get; } + + public CompilerError(bool isWarning, string errorNumber, string errorText, + string fileName, int line, int column) + { + IsWarning = isWarning; + ErrorNumber = errorNumber; + ErrorText = errorText; + FileName = fileName; + Line = line; + Column = column; + } + + public override string ToString() => + $"{FileName}({Line},{Column}): {(IsWarning ? "warning" : "error")} {ErrorNumber}: {ErrorText}"; + } + + /// + /// Emulates System.CodeDom.Compiler.CompilerErrorCollection. + /// + + public sealed class CompilerErrorCollection : Collection + { + public CompilerErrorCollection(IEnumerable errors) : + base(Array.AsReadOnly(errors.ToArray())) {} + + public bool HasErrors => this.Any(e => !e.IsWarning); + public bool HasWarnings => this.Any(e => e.IsWarning); + } + + /// + /// Emulates System.CodeDom.Compiler.CompilerResults. + /// + + public class CompilerResults + { + internal CompilerResults(IEnumerable errors, + int nativeCompilerReturnValue, + IList output) + { + Errors = new CompilerErrorCollection(errors); + NativeCompilerReturnValue = nativeCompilerReturnValue; + Output = new ReadOnlyCollection(output); + } + + public CompilerErrorCollection Errors { get; } + public int NativeCompilerReturnValue { get; } + public IReadOnlyCollection Output { get; } + } + + public class CompileUtils + { + public static bool CanCompile(string program, string cscPath) + { + return !Compile(program, cscPath).Errors.HasErrors; + } + + public static CompilerResults Compile(string program, string cscPath) + { + if (!File.Exists(cscPath)) + throw new FileNotFoundException("C# compiler not found at: " + cscPath); + + var path = Path.ChangeExtension(Path.GetTempFileName(), ".cs"); + File.WriteAllText(path, program, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); + using (var process = Process.Start(new ProcessStartInfo + { + FileName = cscPath, + Arguments = path, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + })) + { + var output = new List(); + + void OnData(object sender, DataReceivedEventArgs args) + { + if (args.Data == null) + return; // EOI + lock (output) + output.Add(args.Data); + } + + process.OutputDataReceived += OnData; + process.ErrorDataReceived += OnData; + + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + process.WaitForExit(); + + var errors = + from line in output + from Match m in Regex.Matches(line, @"^(.+) *\( *([0-9]+) *, *([0-9]+) *\) *: *(error|warning) +(CS[0-9]+) *: *(.+)$") + select m.Groups.Cast() + .Skip(1) + .Select(g => g.Value) + .ToArray() + into groups + select new CompilerError(fileName: groups[0].Trim(), + line: int.Parse(groups[1], NumberStyles.None, CultureInfo.InvariantCulture), + column: int.Parse(groups[2], NumberStyles.None, CultureInfo.InvariantCulture), + isWarning: groups[3] == "warning", + errorNumber: groups[4], + errorText: groups[5].Trim()); + + return new CompilerResults(errors, process.ExitCode, output); + } + } + } } diff --git a/CSharpMinifier/IMinifier.cs b/CSharpMinifier/IMinifier.cs new file mode 100644 index 0000000..58f8574 --- /dev/null +++ b/CSharpMinifier/IMinifier.cs @@ -0,0 +1,9 @@ +namespace CSharpMinifier +{ + public interface IMinifier + { + string MinifyFiles(string[] csFiles); + + string MinifyFromString(string csharpCode); + } +} diff --git a/CSharpMinifier/IdentifierGenerator.cs b/CSharpMinifier/IdentifierGenerator.cs new file mode 100644 index 0000000..02c7422 --- /dev/null +++ b/CSharpMinifier/IdentifierGenerator.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace CSharpMinifier +{ + public class IdentifierGenerator + { + public Dictionary RenamedVariables { get; } + + public Dictionary RenamedMethods { get; } + + public Dictionary RenamedTypes { get; } + + public Dictionary RenamedProperties { get; set; } + + private List _existingNames = new List { FirstSymbolCode.ToString() }; + + private const char FirstSymbolCode = 'a'; + private const char LastSymbolCode = 'z'; + + private static string[] _cSharpKeywords = new string[] + { + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", + "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", + "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", + "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", + "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", + "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", + "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", + "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", + "ushort", "using", "virtual", "void", "volatile", "while" + }; + + public IdentifierGenerator() + { + RenamedVariables = new Dictionary(); + RenamedMethods = new Dictionary(); + RenamedTypes = new Dictionary(); + RenamedProperties = new Dictionary(); + } + + public string GetNextName(SyntaxNode node) + { + if (_existingNames.Count > 0) + { + string lastName = _existingNames[_existingNames.Count - 1]; + string nextName = IncrementName(lastName); + _existingNames.Add(nextName); + AddToDictionary(node); + return nextName; + } + _existingNames.Add(FirstSymbolCode.ToString()); + AddToDictionary(node); + return FirstSymbolCode.ToString(); + } + + private void AddToDictionary(SyntaxNode node) + { + switch (node.Kind()) + { + case SyntaxKind.MethodDeclaration: + RenamedMethods.Add((MethodDeclarationSyntax)node, String.Empty); + break; + case SyntaxKind.VariableDeclarator: + RenamedVariables.Add((VariableDeclaratorSyntax)node, String.Empty); + break; + case SyntaxKind.ClassDeclaration: + RenamedTypes.Add((ClassDeclarationSyntax)node, String.Empty); + break; + case SyntaxKind.PropertyDeclaration: + RenamedProperties.Add((PropertyDeclarationSyntax)node, String.Empty); + break; + } + } + + private string IncrementName(string lastName) + { + char lastChar = lastName[lastName.Length - 1]; + string fragment = lastName.Substring(0, lastName.Length - 1); + while (lastChar < LastSymbolCode) + { + lastChar++; + if (_cSharpKeywords.Contains(fragment + lastChar)) + continue; + return fragment + lastChar; + } + return IncrementName(fragment) + FirstSymbolCode; + } + } +} \ No newline at end of file diff --git a/CSharpMinifier/MinIdGenerator.cs b/CSharpMinifier/MinIdGenerator.cs deleted file mode 100644 index 7f128b3..0000000 --- a/CSharpMinifier/MinIdGenerator.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace CSharpMinifier -{ - public class MinIdGenerator : NamesGenerator - { - public static string Chars0 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; - public static string CharsN = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; - - List _indexes; - - public MinIdGenerator() - : base() - { - } - - public override void Reset() - { - base.Reset(); - _indexes = new List(); - } - - public override string Next() - { - int i = _indexes.Count - 1; - bool inc; - do - { - if (i == -1) - { - _indexes.Add(0); - break; - } - _indexes[i] = _indexes[i] + 1; - inc = false; - if ((i == 0 && _indexes[i] >= Chars0.Length) || _indexes[i] >= CharsN.Length) - { - _indexes[i--] = 0; - inc = true; - } - } - while (inc); - - StringBuilder result = new StringBuilder(_indexes.Count); - for (int j = 0; j < _indexes.Count; j++) - result.Append(j == 0 ? Chars0[_indexes[j]] : CharsN[_indexes[j]]); - - CurrentCombination = result.ToString(); - CurrentCombinationNumber++; - - return Prefix + CurrentCombination + Postfix; - } - - public override int CurrentCombinationNumber - { - get - { - return base.CurrentCombinationNumber; - } - set - { - base.CurrentCombinationNumber = value; - } - } - } -} diff --git a/CSharpMinifier/Minifier.cs b/CSharpMinifier/Minifier.cs deleted file mode 100644 index 07a7c3b..0000000 --- a/CSharpMinifier/Minifier.cs +++ /dev/null @@ -1,910 +0,0 @@ -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.CSharp.Resolver; -using ICSharpCode.NRefactory.CSharp.TypeSystem; -using ICSharpCode.NRefactory.Semantics; -using ICSharpCode.NRefactory.TypeSystem; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace CSharpMinifier -{ - public class Minifier - { - private static string[] NameKeys = new string[] { "Name", "LiteralValue", "Keyword" }; - private const string VarId = "var"; - public static string ParserTempFileName = "temp.cs"; - - private CSharpUnresolvedFile _unresolvedFile; - private IProjectContent _projectContent; - private ICompilation _compilation; - private CSharpAstResolver _resolver; - private AstNode _prevNode; - - public SyntaxTree SyntaxTree - { - get; - private set; - } - - public MinifierOptions Options - { - get; - private set; - } - - public List IgnoredIdentifiers - { - get; - private set; - } - - public List IgnoredComments - { - get; - private set; - } - - #region Public - - public Minifier(MinifierOptions options = null, string[] ignoredIdentifiers = null, string[] ignoredComments = null) - { - Options = options ?? new MinifierOptions(); - - _projectContent = new CSharpProjectContent(); - var assemblies = new List - { - typeof(object).Assembly, // mscorlib - typeof(Uri).Assembly, // System.dll - typeof(Enumerable).Assembly, // System.Core.dll - }; - - var unresolvedAssemblies = new IUnresolvedAssembly[assemblies.Count]; - Parallel.For( - 0, assemblies.Count, - delegate (int i) - { - var loader = new CecilLoader(); - var path = assemblies[i].Location; - unresolvedAssemblies[i] = loader.LoadAssemblyFile(assemblies[i].Location); - }); - _projectContent = _projectContent.AddAssemblyReferences((IEnumerable)unresolvedAssemblies); - - - IgnoredIdentifiers = ignoredIdentifiers == null ? new List() : ignoredIdentifiers.ToList(); - IgnoredComments = new List(); - if (ignoredComments != null) - { - foreach (string comment in ignoredComments) - { - var str = comment; - if (str.StartsWith("//")) - str = str.Substring("//".Length); - else if (str.StartsWith("/*") && str.EndsWith("*/")) - str = str.Substring("/*".Length, str.Length - "/*".Length - "*/".Length); - if (!IgnoredComments.Contains(str)) - IgnoredComments.Add(str); - } - } - } - - public string MinifyFiles(string[] csFiles) - { - CSharpParser parser = new CSharpParser(); - SyntaxTree[] trees = csFiles.Select(file => parser.Parse(file, file + "_" + ParserTempFileName)).ToArray(); - - SyntaxTree globalTree = new SyntaxTree(); - globalTree.FileName = ParserTempFileName; - - var usings = new List(); - var types = new List(); - foreach (var tree in trees) - { - List treeUsings = new List(); - GetUsingsAndRemoveUsingsAndNamespaces(tree, treeUsings, types); - usings.AddRange(treeUsings.Where(u1 => !usings.Exists(u2 => u2.Namespace == u1.Namespace))); - } - - foreach (var u in usings) - globalTree.AddChild(u.Clone(), new Role("UsingDeclaration")); - foreach (var t in types) - globalTree.AddChild(t.Clone(), new Role("TypeDeclaration")); - - SyntaxTree = globalTree; - - return Minify(); - } - - public string MinifyFromString(string csharpCode) - { - SyntaxTree = new CSharpParser().Parse(csharpCode, ParserTempFileName); - - return Minify(); - } - - public string Minify() - { - if (Options.CommentsRemoving || Options.RegionsRemoving) - RemoveCommentsAndRegions(); - - if (Options.LocalVarsCompressing || Options.UselessMembersCompressing) - CompressLocals(); - if (Options.MembersCompressing || Options.UselessMembersCompressing) - CompressMembers(); - if (Options.TypesCompressing) - CompressTypes(); - if (Options.EnumToIntConversion) - ConvertEnumToInts(); - if (Options.MiscCompressing || Options.NamespacesRemoving) - CompressMixed(); - - string result; - if (Options.SpacesRemoving) - result = GetStringWithoutSpaces(); - else - result = SyntaxTree.ToString(); - - return result; - } - - private void GetUsingsAndRemoveUsingsAndNamespaces(SyntaxTree tree, List usings, List types) - { - foreach (var child in tree.Children) - GetUsingsAndRemoveUsingsAndNamespaces(child, usings, types); - } - - private void GetUsingsAndRemoveUsingsAndNamespaces(AstNode node, List usings, List types) - { - if (node.Role.ToString() == "Member" && node.GetType().Name == "UsingDeclaration") - { - usings.Add((UsingDeclaration)node); - } - else if (node.Role.ToString() == "Member" && node.GetType().Name == "NamespaceDeclaration") - { - AstNode parent = node.Parent; - foreach (AstNode child in node.Children) - { - if (child.NodeType == NodeType.TypeDeclaration) - { - types.Add((TypeDeclaration)child); - } - else if (child.ToString() == "Member" && child.GetType().Name == "NamespaceDeclaration") - { - GetUsingsAndRemoveUsingsAndNamespaces(child, usings, types); - } - } - } - else - { - if (node.Children.Count() >= 1) - { - foreach (var child in node.Children) - GetUsingsAndRemoveUsingsAndNamespaces(child, usings, types); - } - } - } - - #endregion - - #region Misc Compression - - private void CompressMixed() - { - UpdateSyntaxTree(); - CompileAndResolve(); - TraverseNodes(SyntaxTree); - } - - private void TraverseNodes(AstNode node) - { - foreach (var child in node.Children) - { - if (Options.MiscCompressing && child is PrimitiveExpression) - { - var primitiveExpression = (PrimitiveExpression)child; - if (IsIntegerNumber(primitiveExpression.Value)) - { - string str = primitiveExpression.Value.ToString(); - long number; - if (long.TryParse(str, out number)) - { - string hex = "0x" + number.ToString("X"); - primitiveExpression.SetValue(primitiveExpression.Value, str.Length < hex.Length ? str : hex); - } - } - } - else if (Options.MiscCompressing && child is CSharpModifierToken) - { - // private int a => int a - var modifier = ((CSharpModifierToken)child).Modifier; - if (modifier.HasFlag(Modifiers.Private) && (modifier & ~Modifiers.Private) == 0) - child.Remove(); - else - modifier &= ~Modifiers.Private; - } - else if (Options.NamespacesRemoving && child is NamespaceDeclaration) - { - var childrenToRemove = child.Children.TakeWhile(c => !(c is CSharpTokenNode && c.Role.ToString() == "{")); - foreach (AstNode childToRemove in childrenToRemove) - childToRemove.Remove(); - if (child.Children.Count() > 0) - child.Children.First().Remove(); - if (child.Children.Count() > 0) - child.Children.Last().Remove(); - var namespaceChildrens = child.Children; - - var parent = child.Parent; - foreach (var c in parent.Children) - { - if (c == child) - { - foreach (var nsChildren in namespaceChildrens) - parent.InsertChildAfter(c, nsChildren.Clone(), new Role(nsChildren.Role.ToString())); - c.Remove(); - break; - } - } - foreach (AstNode c in parent.Children) - TraverseNodes(c); - } - else if (Options.MiscCompressing && child is VariableDeclarationStatement) - { - // List a = new List() => var a = new List() - // var a = new b() => b a = new b() - var varDecExpr = (VariableDeclarationStatement)child; - if (!varDecExpr.Modifiers.HasFlag(Modifiers.Const)) - { - var type = varDecExpr.Type.ToString().Replace(" ", ""); - if (type == VarId) - { - // Resolving expression type. - CompileAndResolve(); - var expectedType = _resolver.GetExpectedType(varDecExpr.Variables.Single().Initializer); - if (expectedType.Namespace != "System.Collections.Generic") - { - string typeStr = expectedType.Name; - bool replace = NamesGenerator.CSharpTypeSynonyms.TryGetValue(typeStr, out typeStr); - if (!replace) - typeStr = expectedType.Name; - if (typeStr.Length <= VarId.Length) - replace = true; - else - replace = false; - if (replace) - { - if (expectedType.Namespace == "System") - varDecExpr.Type = new PrimitiveType(typeStr); - else - varDecExpr.Type = new SimpleType(typeStr); - } - } - } - else - { - if (varDecExpr.Variables.Count == 1) - { - string typeStr; - var typeStrWithoutNamespaces = varDecExpr.Type.ToString(); - typeStrWithoutNamespaces = typeStrWithoutNamespaces.Substring(typeStrWithoutNamespaces.LastIndexOf('.') + 1); - NamesGenerator.CSharpTypeSynonyms.TryGetValue(typeStrWithoutNamespaces, out typeStr); - if (typeStr == null) - typeStr = varDecExpr.Type.ToString(); - var initializer = varDecExpr.Variables.Single().Initializer; - if (((typeStr == "string" || typeStr == "char" || typeStr == "bool") && initializer != NullReferenceExpression.Null) - || initializer is ObjectCreateExpression) - { - if (VarId.Length < type.Length) - varDecExpr.Type = new SimpleType(VarId); - } - } - } - } - foreach (var variable in varDecExpr.Variables) - TraverseNodes(child); - } - else if (child is EmptyStatement) - { - // { ; ; } => {} - if (!(child.Parent is BlockStatement)) - { - if (Options.Unsafe) - { - node.Remove(); - } - } - else - { - child.Remove(); - } - } - else - { - string role = child.Role.ToString(); - if (Options.MiscCompressing && child is BlockStatement && role != "Body" && role != "TryBlock") - { - // if (a) { b; } => if (a) b; - var childrenCount = child.Children.Count(c => !(c is NewLineNode)); - if (childrenCount == 3) - child.ReplaceWith(child.Children.Skip(1).FirstOrDefault(c => !(c is NewLineNode))); - else if (childrenCount < 3) - { - if (!(child.Parent is BlockStatement)) - { - if (Options.Unsafe) - { - node.Remove(); - } - else - { - child.ReplaceWith(new EmptyStatement()); - } - } - else - { - child.Remove(); - } - } - } - else if (Options.Unsafe && child is BinaryOperatorExpression) - { - // if (a == true) => if (a) - // if (a == false) => if (!a) - var binaryExpression = (BinaryOperatorExpression)child; - var primitiveExpression = binaryExpression.Left as PrimitiveExpression; - var expression = binaryExpression.Right; - if (primitiveExpression == null) - { - primitiveExpression = binaryExpression.Right as PrimitiveExpression; - expression = binaryExpression.Left; - } - if (primitiveExpression != null && primitiveExpression.Value is bool) - { - var boolean = (bool)primitiveExpression.Value; - expression.Remove(); - if (boolean) - child.ReplaceWith(expression); - else - child.ReplaceWith(new UnaryOperatorExpression(UnaryOperatorType.Not, expression)); - } - } - TraverseNodes(child); - } - } - } - - public static bool IsIntegerNumber(object value) - { - return value is sbyte || value is byte || - value is short || value is ushort || value is int || - value is uint || value is long || value is ulong; - } - - #endregion - - #region Comments & Regions Removing - - private void RemoveCommentsAndRegions() - { - RemoveCommentsAndRegions(SyntaxTree); - } - - private void RemoveCommentsAndRegions(AstNode node) - { - foreach (AstNode children in node.Children) - { - if (Options.CommentsRemoving && children is Comment) - { - var properties = children.GetProperties(); - var commentType = properties.GetPropertyValueEnum(children, "CommentType"); - var content = properties.GetPropertyValue(children, "Content"); - if (!IgnoredComments.Contains(content) && commentType != CommentType.InactiveCode) - children.Remove(); - } - else if (Options.RegionsRemoving && children is PreProcessorDirective) - { - var type = children.GetPropertyValueEnum("Type"); - switch (type) - { - case PreProcessorDirectiveType.Region: - case PreProcessorDirectiveType.Endregion: - case PreProcessorDirectiveType.Warning: - children.Remove(); - break; - } - } - else - RemoveCommentsAndRegions(children); - } - } - - #endregion - - #region Identifiers Compressing - - private void CompressLocals() - { - var localsVisitor = new MinifyLocalsAstVisitor(IgnoredIdentifiers); - CompileAndAcceptVisitor(localsVisitor); - var substitutor = new Substitutor(new MinIdGenerator()); - var ignoredNames = new List(IgnoredIdentifiers); - ignoredNames.AddRange(localsVisitor.NotLocalsIdNames); - var substituton = substitutor.Generate(localsVisitor.MethodVars, ignoredNames.ToArray()); - - var astSubstitution = new Dictionary>>>(); - foreach (var method in localsVisitor.MethodVars) - { - var localVarsAstNodes = new List>>(); - astSubstitution[method.Key] = localVarsAstNodes; - var localsSubst = substituton[method.Key]; - foreach (NameNode localVar in method.Value) - localVarsAstNodes.Add(new Tuple>(localsSubst[localVar.Name], GetResolvedNodes(ResolveResultType.Local, localVar.Node))); - } - - RenameOrRemoveNodes(astSubstitution, true, Options.LocalVarsCompressing); - } - - private void CompressMembers() - { - var membersVisitor = new MinifyMembersAstVisitor(IgnoredIdentifiers, Options.ConsoleApp, Options.PublicCompressing, Options.ToStringMethodsRemoving); - CompileAndAcceptVisitor(membersVisitor); - var substitutor = new Substitutor(new MinIdGenerator()); - var ignoredNames = new List(IgnoredIdentifiers); - ignoredNames.AddRange(membersVisitor.NotMembersIdNames); - var substituton = substitutor.Generate(membersVisitor.TypesMembers, ignoredNames.ToArray()); - - var astSubstitution = new Dictionary>>>(); - foreach (var typeMembers in membersVisitor.TypesMembers) - { - var typeMembersAstNodes = new List>>(); - astSubstitution[typeMembers.Key] = typeMembersAstNodes; - var membersSubst = substituton[typeMembers.Key]; - foreach (NameNode member in typeMembers.Value) - typeMembersAstNodes.Add(new Tuple>(membersSubst[member.Name], GetResolvedNodes(ResolveResultType.Member, member.Node))); - } - - RenameOrRemoveNodes(astSubstitution, true, Options.MembersCompressing); - } - - private void CompressTypes() - { - var typesVisitor = new MinifyTypesAstVisitor(IgnoredIdentifiers, Options.PublicCompressing); - CompileAndAcceptVisitor(typesVisitor); - var substitutor = new Substitutor(new MinIdGenerator()); - var ignoredNames = new List(IgnoredIdentifiers); - ignoredNames.AddRange(typesVisitor.NotTypesIdNames); - var substitution = substitutor.Generate(typesVisitor.Types, ignoredNames.ToArray()); - - var astSubstitution = new List>>(); - foreach (var type in typesVisitor.Types) - astSubstitution.Add(new Tuple>(substitution[type.Name], GetResolvedNodes(ResolveResultType.Type, type.Node))); - - RenameOrRemoveNodes(astSubstitution, false, Options.TypesCompressing); - } - - private void ConvertEnumToInts() - { - UpdateSyntaxTree(); - var enumVisitor = new MinifyEnumsAstVisitor(); - CompileAndAcceptVisitor(enumVisitor); - - var members = enumVisitor.EnumMembers; - foreach (var member in members) - { - var enumTypeRefs = GetResolvedNodes(ResolveResultType.Type, member.Key); - int currentEnumValue = 0; - foreach (var enumMember in member.Value) - { - Expression initExpr = ((EnumMemberDeclaration)enumMember.Node).Initializer; - if (initExpr is PrimitiveExpression) - { - int.TryParse(((PrimitiveExpression)initExpr).Value.ToString(), out currentEnumValue); - } - var enumMemberRefs = GetResolvedNodes(ResolveResultType.Member, enumMember.Node); - foreach (var node in enumMemberRefs) - { - if (!(node is EntityDeclaration)) - { - node.ReplaceWith(new PrimitiveExpression(currentEnumValue)); - } - } - currentEnumValue++; - } - foreach (var typeRef in enumTypeRefs) - if (typeRef is SimpleType) - typeRef.ReplaceWith(new PrimitiveType("int")); - member.Key.Remove(); - } - } - - private void CompileAndAcceptVisitor(DepthFirstAstVisitor visitor) - { - CompileAndResolve(); - SyntaxTree.AcceptVisitor(visitor); - } - - private void CompileAndResolve() - { - _unresolvedFile = SyntaxTree.ToTypeSystem(); - _projectContent = _projectContent.AddOrUpdateFiles(_unresolvedFile); - _compilation = _projectContent.CreateCompilation(); - _resolver = new CSharpAstResolver(_compilation, SyntaxTree, _unresolvedFile); - } - - private List GetResolvedNodes(ResolveResultType type, AstNode node) - { - var resolvedNodes = new List(); - ResolveResult resolveResult = _resolver.Resolve(node); - if (resolveResult != null) - { - var findReferences = new FindReferences(); - FoundReferenceCallback callback = delegate (AstNode matchNode, ResolveResult result) - { - resolvedNodes.Add(matchNode); - }; - - if (type == ResolveResultType.Local) - { - var localResolveResult = resolveResult as LocalResolveResult; - if (localResolveResult != null) - findReferences.FindLocalReferences(localResolveResult.Variable, _unresolvedFile, SyntaxTree, _compilation, callback, CancellationToken.None); - } - else if (type == ResolveResultType.Member) - { - var memberResolveResult = resolveResult as MemberResolveResult; - if (memberResolveResult != null) - { - var searchScopes = findReferences.GetSearchScopes(memberResolveResult.Member); - findReferences.FindReferencesInFile(searchScopes, _unresolvedFile, SyntaxTree, _compilation, callback, CancellationToken.None); - } - } - else if (type == ResolveResultType.Type) - { - var typeResolveResult = resolveResult as TypeResolveResult; - if (typeResolveResult != null) - { - var searchScopes = findReferences.GetSearchScopes(typeResolveResult.Type.GetDefinition()); - findReferences.FindReferencesInFile(searchScopes, _unresolvedFile, SyntaxTree, _compilation, callback, CancellationToken.None); - } - } - } - else - { - } - return resolvedNodes; - } - - private void RenameOrRemoveNodes(Dictionary>>> substitution, bool removeOneRefNodes, bool rename) - { - foreach (var resolveNode in substitution) - RenameOrRemoveNodes(resolveNode.Value, removeOneRefNodes, rename); - } - - private void RenameOrRemoveNodes(List>> substitution, bool removeOneRefNodes, bool rename) - { - foreach (var node in substitution) - { - if (rename) - { - foreach (var astNode in node.Item2) - RenameNode(astNode, node.Item1); - } - - var first = node.Item2.FirstOrDefault() as VariableInitializer; - if (removeOneRefNodes && Options.UselessMembersCompressing && first != null) - { - bool constNode = false; - var fieldDeclaration = first.Parent as FieldDeclaration; - if (fieldDeclaration != null) - constNode = fieldDeclaration.Modifiers.HasFlag(Modifiers.Const); - else - { - var varDeclaration = first.Parent as VariableDeclarationStatement; - if (varDeclaration != null) - constNode = varDeclaration.Modifiers.HasFlag(Modifiers.Const); - } - - if (!constNode || first.Initializer == NullReferenceExpression.Null) - { - //if (node.Item2.Count == 1) - // first.Parent.Remove(); - } - else - { - var initializerStr = GetStringWithoutSpaces(first.Parent); - var exprStr = GetStringWithoutSpaces(first.Initializer); - if (initializerStr.Length + (node.Item2.Count - 1) * node.Item1.Length > (node.Item2.Count - 1) * exprStr.Length) - { - foreach (var child in node.Item2.Skip(1)) - { - child.ReplaceWith(first.Initializer.Clone()); - } - first.Parent.Remove(); - } - } - } - } - } - - private void RenameNode(AstNode node, string newName) - { - if (node is ParameterDeclaration) - ((ParameterDeclaration)node).Name = newName; - else if (node is VariableInitializer) - ((VariableInitializer)node).Name = newName; - - else if (node is VariableInitializer) - ((VariableInitializer)node).Name = newName; - else if (node is MethodDeclaration) - ((MethodDeclaration)node).Name = newName; - else if (node is PropertyDeclaration) - ((PropertyDeclaration)node).Name = newName; - else if (node is IndexerDeclaration) - ((IndexerDeclaration)node).Name = newName; - else if (node is OperatorDeclaration) - ((OperatorDeclaration)node).Name = newName; - else if (node is MemberReferenceExpression) - ((MemberReferenceExpression)node).MemberName = newName; - else if (node is IdentifierExpression) - ((IdentifierExpression)node).Identifier = newName; - else if (node is InvocationExpression) - { - var invExpression = (InvocationExpression)node; - if (invExpression.Target is IdentifierExpression) - ((IdentifierExpression)invExpression.Target).Identifier = newName; - else if (invExpression.Target is MemberReferenceExpression) - ((MemberReferenceExpression)invExpression.Target).MemberName = newName; - else - { - } - } - else if (node is NamedExpression) - ((NamedExpression)node).Name = newName; - else if (node is TypeDeclaration) - ((TypeDeclaration)node).Name = newName; - else if (node is SimpleType) - ((SimpleType)node).Identifier = newName; - else if (node is EnumMemberDeclaration) - ((EnumMemberDeclaration)node).Name = newName; - else - { - } - } - - private void UpdateSyntaxTree() - { - // TODO: Fix it. - SyntaxTree = new CSharpParser().Parse(SyntaxTree.ToString(), ParserTempFileName); - } - - #endregion - - #region Removing of spaces and line breaks - - private string GetStringWithoutSpaces() - { - UpdateSyntaxTree(); - return GetStringWithoutSpaces(SyntaxTree.Children); - } - - private string GetStringWithoutSpaces(AstNode node) - { - return GetStringWithoutSpaces(new List() { node }); - } - - private string GetStringWithoutSpaces(IEnumerable nodes) - { - StringBuilder line; - StringBuilder result; - - result = new StringBuilder(); - line = new StringBuilder(Options.LineLength); - - _prevNode = null; - foreach (var node in nodes) - { - RemoveSpacesAndAppend(node, line, result); - if (node.Children.Count() <= 1 && !(node is NewLineNode)) - _prevNode = node; - } - result.Append(line); - - return result.ToString(); - } - - private void RemoveSpacesAndAppend(AstNode node, StringBuilder line, StringBuilder result) - { - if (node.Children.Count() == 0) - { - string indent = GetIndent(node, line); - - string newString = indent + GetLeafNodeString(node); - if (Options.LineLength == 0) - result.Append(newString); - else - { - if (line.Length + newString.Length > Options.LineLength) - { - result.AppendLine(line.ToString()); - line.Clear(); - line.Append(newString.TrimStart()); - } - else - { - line.Append(newString); - } - } - } - else - { - List children; - if (node is ArraySpecifier) - { - children = new List(); - children.Add(node.Children.FirstOrDefault(c => c.Role.ToString() == "[")); - children.AddRange(node.Children.Where(c => c.Role.ToString() != "[" && c.Role.ToString() != "]")); - children.AddRange(node.Children.Where(c => c.Role.ToString() == "]")); - } - else if (node is ArrayInitializerExpression) - { - var arrayInitExpr = (ArrayInitializerExpression)node; - children = node.Children.ToList(); - int exprNodeInd1 = -1, exprNodeInd2 = -1, commaInd1 = -1; - for (int i = 0; i < children.Count; i++) - { - var c = children[i]; - if (!(c is NewLineNode || c is CSharpTokenNode || c is Comment)) - { - if (exprNodeInd1 == -1) - exprNodeInd1 = i; - else - { - exprNodeInd2 = i; - break; - } - } - else if (exprNodeInd1 > -1 && exprNodeInd2 == -1 && c.ToString() == ",") - commaInd1 = i; - } - if (exprNodeInd1 != -1 && commaInd1 == -1 && exprNodeInd2 != -1) - children.Insert(exprNodeInd1 + 1, - new CSharpTokenNode(TextLocation.Empty, Roles.Comma) { Role = Roles.Comma }); - } - else - children = node.Children.ToList(); - foreach (AstNode child in children) - { - RemoveSpacesAndAppend(child, line, result); - if (child.Children.Count() <= 1 && !(child is NewLineNode)) - _prevNode = child; - } - } - } - - private string GetIndent(AstNode node, StringBuilder line) - { - string indent = " "; - char last = (char)0; - if (line.Length != 0) - { - last = line[line.Length - 1]; - } - - if (last == ' ' || last == '\r' || last == '\n' || last == ';' || _prevNode == null || node == null) - { - indent = ""; - } - else - { - var prevComment = _prevNode as Comment; - if (prevComment != null) - { - if (prevComment.CommentType == CommentType.SingleLine || prevComment.CommentType == CommentType.Documentation) - indent = Environment.NewLine; - else - indent = ""; - } - else if (node is PreProcessorDirective || _prevNode is PreProcessorDirective) - { - indent = Environment.NewLine; - } - else - { - string prevNodeString = _prevNode.Role.ToString(); - string nodeString = node.Role.ToString(); - if (_prevNode is CSharpTokenNode && prevNodeString.All(c => !char.IsLetterOrDigit(c))) - { - if ((prevNodeString == "+" && nodeString != "++") || - (prevNodeString == "-" && nodeString != "--") || - (prevNodeString != "+" && prevNodeString != "-")) - { - indent = ""; - } - } - else if (_prevNode is NewLineNode || - _prevNode is EmptyStatement || - (node is CSharpTokenNode && nodeString.All(c => !char.IsLetterOrDigit(c))) || - node is NewLineNode || - node is Comment) - { - indent = ""; - } - } - } - - return indent; - } - - public static string GetLeafNodeString(AstNode node) - { - string nodeRole = node.Role.ToString(); - var properties = node.GetProperties(); - if (nodeRole == "Comment") - { - CommentType commentType = properties.GetPropertyValueEnum(node, "CommentType"); - string content = properties.GetPropertyValue(node, "Content"); - switch (commentType) - { - default: - case CommentType.SingleLine: - return "//" + content; - case CommentType.Documentation: - return "///" + content; - case CommentType.MultiLine: - return "/*" + content + "*/"; - case CommentType.InactiveCode: - return content; - case CommentType.MultiLineDocumentation: - return "/**" + content + "*/"; - } - } - else if (nodeRole == "Modifier") - { - return properties.GetPropertyValue(node, "Modifier").ToLower(); - } - else if (nodeRole == "Target" || nodeRole == "Right") - { - string typeName = node.GetType().Name; - if (typeName == nameof(ThisReferenceExpression)) - return "this"; - else if (typeName == nameof(BaseReferenceExpression)) - return "base"; - else if (typeName == nameof(NullReferenceExpression)) - return "null"; - } - else if (nodeRole == "PreProcessorDirective") - { - var type = properties.GetPropertyValue(node, "Type").ToLower(); - var argument = properties.GetPropertyValue(node, "Argument"); - var result = "#" + type; - if (argument != string.Empty) - result += " " + argument; - return result; - } - - if (node is ThisReferenceExpression) - return "this"; - else if (node is BaseReferenceExpression) - return "base"; - else if (node is NullReferenceExpression) - return "null"; - else if (node is CSharpTokenNode || node is CSharpModifierToken) - return nodeRole; - else if (node is NewLineNode) - return ""; - else if (node is EmptyStatement) - return ";"; - - return properties - .FirstOrDefault(p => NameKeys.Contains(p.Name)) - .GetValue(node, null) - .ToString(); - } - - #endregion - } -} diff --git a/CSharpMinifier/MinifyEnumsAstVisitor.cs b/CSharpMinifier/MinifyEnumsAstVisitor.cs deleted file mode 100644 index 57471c4..0000000 --- a/CSharpMinifier/MinifyEnumsAstVisitor.cs +++ /dev/null @@ -1,44 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; -using System.Collections.Generic; - -namespace CSharpMinifier -{ - public class MinifyEnumsAstVisitor : DepthFirstAstVisitor - { - private string _currentNamespace; - private List _currentEnumMembers; - - public Dictionary> EnumMembers - { - get; - private set; - } - - public MinifyEnumsAstVisitor() - { - EnumMembers = new Dictionary>(); - } - - public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) - { - _currentNamespace = namespaceDeclaration.Name; - base.VisitNamespaceDeclaration(namespaceDeclaration); - } - - public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) - { - if (typeDeclaration.TypeKeyword.ToString() == "enum") - { - _currentEnumMembers = new List(); - EnumMembers.Add(typeDeclaration, _currentEnumMembers); - } - base.VisitTypeDeclaration(typeDeclaration); - } - - public override void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) - { - _currentEnumMembers.Add(new NameNode(enumMemberDeclaration.Name, enumMemberDeclaration)); - base.VisitEnumMemberDeclaration(enumMemberDeclaration); - } - } -} diff --git a/CSharpMinifier/MinifyLocalsAstVisitor.cs b/CSharpMinifier/MinifyLocalsAstVisitor.cs deleted file mode 100644 index 51d55b0..0000000 --- a/CSharpMinifier/MinifyLocalsAstVisitor.cs +++ /dev/null @@ -1,147 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace CSharpMinifier -{ - public class MinifyLocalsAstVisitor : DepthFirstAstVisitor - { - private string _currentNamespace; - private List _currentTypes; - private List _currentMethodVars; - private IEnumerable _ignoredLocals; - - public HashSet NotLocalsIdNames - { - get; - private set; - } - - public Dictionary> MethodVars - { - get; - private set; - } - - public MinifyLocalsAstVisitor(IEnumerable ignoredLocals = null) - { - NotLocalsIdNames = new HashSet(); - MethodVars = new Dictionary>(); - _ignoredLocals = ignoredLocals ?? new List(); - _currentTypes = new List(); - } - - public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) - { - _currentNamespace = namespaceDeclaration.Name; - base.VisitNamespaceDeclaration(namespaceDeclaration); - } - - public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) - { - _currentTypes.Add(typeDeclaration.Name); - base.VisitTypeDeclaration(typeDeclaration); - _currentTypes.RemoveAt(_currentTypes.Count - 1); - } - - #region Visit members with body declarations - - public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) - { - VisitMember(methodDeclaration.Name, methodDeclaration.Parameters.Select(p => p.Type.ToString())); - base.VisitMethodDeclaration(methodDeclaration); - } - - public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) - { - VisitMember(propertyDeclaration.Name); - base.VisitPropertyDeclaration(propertyDeclaration); - } - - public override void VisitEventDeclaration(EventDeclaration eventDeclaration) - { - foreach (var v in eventDeclaration.Variables) - VisitMember(v.Name); - base.VisitEventDeclaration(eventDeclaration); - } - - public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) - { - VisitMember(indexerDeclaration.Name, indexerDeclaration.Parameters.Select(p => p.Type.ToString())); - base.VisitIndexerDeclaration(indexerDeclaration); - } - - public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) - { - VisitMember(operatorDeclaration.Name, operatorDeclaration.Parameters.Select(p => p.Type.ToString())); - base.VisitOperatorDeclaration(operatorDeclaration); - } - - public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) - { - string prefix = ""; - if ((constructorDeclaration.Modifiers & Modifiers.Static) == Modifiers.Static) - prefix = "static."; - VisitMember(prefix + constructorDeclaration.Name, constructorDeclaration.Parameters.Select(p => p.Type.ToString())); - base.VisitConstructorDeclaration(constructorDeclaration); - } - - public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) - { - VisitMember(destructorDeclaration.Name); - base.VisitDestructorDeclaration(destructorDeclaration); - } - - private void VisitMember(string memberName, IEnumerable parameters = null) - { - StringBuilder memberKey = new StringBuilder(); - string prefix = string.IsNullOrEmpty(_currentNamespace) ? "" : "." + _currentNamespace; - memberKey.AppendFormat("{0}{1}.{2}", prefix, string.Join(".", _currentTypes), memberName); - if (parameters != null) - { - memberKey.Append('('); - foreach (var param in parameters) - { - memberKey.Append(param); - memberKey.Append(','); - } - if (parameters.Count() != 0) - memberKey.Remove(memberKey.Length - 1, 1); - memberKey.Append(')'); - } - _currentMethodVars = new List(); - MethodVars.Add(memberKey.ToString(), _currentMethodVars); - } - - #endregion - - #region Visit local declarations - - public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) - { - if (!_ignoredLocals.Contains(parameterDeclaration.Name)) - _currentMethodVars.Add(new NameNode(parameterDeclaration.Name, parameterDeclaration)); - base.VisitParameterDeclaration(parameterDeclaration); - } - - public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) - { - foreach (var varInitializer in variableDeclarationStatement.Variables) - { - if (!_ignoredLocals.Contains(varInitializer.Name)) - _currentMethodVars.Add(new NameNode(varInitializer.Name, varInitializer)); - } - base.VisitVariableDeclarationStatement(variableDeclarationStatement); - } - - #endregion - - public override void VisitIdentifier(Identifier identifier) - { - if (_currentMethodVars == null || _currentMethodVars.FirstOrDefault(v => v.Name == identifier.Name) == null) - NotLocalsIdNames.Add(identifier.Name); - base.VisitIdentifier(identifier); - } - } -} diff --git a/CSharpMinifier/MinifyMembersAstVisitor.cs b/CSharpMinifier/MinifyMembersAstVisitor.cs deleted file mode 100644 index 7a4c5f8..0000000 --- a/CSharpMinifier/MinifyMembersAstVisitor.cs +++ /dev/null @@ -1,178 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace CSharpMinifier -{ - public class MinifyMembersAstVisitor : DepthFirstAstVisitor - { - private Stack> _currentNamespaces = new Stack>(); - private Stack, CSharpTokenNode>> _currentMembers = new Stack, CSharpTokenNode>>(); - private IEnumerable _ignoredMembers; - - public bool ConsoleApp - { - get; - private set; - } - - public bool CompressPublic - { - get; - private set; - } - - public bool RemoveToString - { - get; - private set; - } - - public HashSet NotMembersIdNames - { - get; - private set; - } - - public Dictionary> TypesMembers - { - get; - private set; - } - - public MinifyMembersAstVisitor(IEnumerable ignoredMembers, bool consoleApp, bool compressPublic, bool removeToString) - { - TypesMembers = new Dictionary>(); - NotMembersIdNames = new HashSet(); - _ignoredMembers = ignoredMembers ?? new List(); - ConsoleApp = consoleApp; - CompressPublic = compressPublic; - RemoveToString = removeToString; - } - - public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) - { - _currentNamespaces.Push(new Tuple(namespaceDeclaration.Name, namespaceDeclaration.Children.LastOrDefault() as CSharpTokenNode)); - base.VisitNamespaceDeclaration(namespaceDeclaration); - } - - public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) - { - string key = string.Join(".", _currentNamespaces.ToArray().Reverse().Select(ns => ns.Item1)); - if (key != "") - key += "."; - key += string.Join(".", _currentMembers.ToArray().Reverse().Select(m => m.Item1)); - if (key != "" && key[key.Length - 1] != '.') - key += "."; - key += typeDeclaration.Name; - - List nameNodes; - if (!TypesMembers.ContainsKey(key)) - { - nameNodes = new List(); - TypesMembers.Add(key, nameNodes); - } - else - nameNodes = TypesMembers[key]; - - _currentMembers.Push(new Tuple, CSharpTokenNode>(typeDeclaration.Name, nameNodes, - (CSharpTokenNode)typeDeclaration.Children.Last())); - - base.VisitTypeDeclaration(typeDeclaration); - } - - #region Visit type members - - public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) - { - if (CheckModifiers(fieldDeclaration)) - foreach (var v in fieldDeclaration.Variables) - if (!_ignoredMembers.Contains(v.Name)) - _currentMembers.Peek().Item2.Add(new NameNode(v.Name, v)); - base.VisitFieldDeclaration(fieldDeclaration); - } - - public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) - { - if (CheckModifiers(methodDeclaration) && !_ignoredMembers.Contains(methodDeclaration.Name) && - (ConsoleApp ? methodDeclaration.Name != "Main" : true) && - (methodDeclaration.Modifiers & Modifiers.Override) != Modifiers.Override) - _currentMembers.Peek().Item2.Add(new NameNode(methodDeclaration.Name, methodDeclaration)); - if (RemoveToString && methodDeclaration.Name == "ToString" && (methodDeclaration.Modifiers & Modifiers.Override) == Modifiers.Override) - methodDeclaration.Remove(); - base.VisitMethodDeclaration(methodDeclaration); - } - - public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) - { - if (CheckModifiers(propertyDeclaration) && !_ignoredMembers.Contains(propertyDeclaration.Name) && - (propertyDeclaration.Modifiers & Modifiers.Override) != Modifiers.Override) - _currentMembers.Peek().Item2.Add(new NameNode(propertyDeclaration.Name, propertyDeclaration)); - base.VisitPropertyDeclaration(propertyDeclaration); - } - - public override void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) - { - if (!_ignoredMembers.Contains(enumMemberDeclaration.Name)) - _currentMembers.Peek().Item2.Add(new NameNode(enumMemberDeclaration.Name, enumMemberDeclaration)); - base.VisitEnumMemberDeclaration(enumMemberDeclaration); - } - - public override void VisitEventDeclaration(EventDeclaration eventDeclaration) - { - if (CheckModifiers(eventDeclaration) && !_ignoredMembers.Contains(eventDeclaration.Name)) - foreach (var v in eventDeclaration.Variables) - _currentMembers.Peek().Item2.Add(new NameNode(v.Name, v)); - base.VisitEventDeclaration(eventDeclaration); - } - - public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) - { - base.VisitIndexerDeclaration(indexerDeclaration); - } - - public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) - { - base.VisitOperatorDeclaration(operatorDeclaration); - } - - public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) - { - base.VisitConstructorDeclaration(constructorDeclaration); - } - - public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) - { - base.VisitDestructorDeclaration(destructorDeclaration); - } - - #endregion - - public override void VisitCSharpTokenNode(CSharpTokenNode token) - { - if (_currentNamespaces.Count != 0 && token == _currentNamespaces.Peek().Item2) - { - _currentNamespaces.Pop(); - } - else if (_currentMembers.Count != 0 && token == _currentMembers.Peek().Item3) - { - _currentMembers.Pop(); - } - base.VisitCSharpTokenNode(token); - } - - public override void VisitIdentifier(Identifier identifier) - { - if (_currentMembers.Count == 0 || _currentMembers.Peek().Item2.FirstOrDefault(v => v.Name == identifier.Name) == null) - NotMembersIdNames.Add(identifier.Name); - base.VisitIdentifier(identifier); - } - - private bool CheckModifiers(EntityDeclaration declaration) - { - return CompressPublic || (declaration.Modifiers & Modifiers.Public) == Modifiers.None; - } - } -} diff --git a/CSharpMinifier/MinifyTypesAstVisitor.cs b/CSharpMinifier/MinifyTypesAstVisitor.cs deleted file mode 100644 index c9347dc..0000000 --- a/CSharpMinifier/MinifyTypesAstVisitor.cs +++ /dev/null @@ -1,91 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; -using System.Collections.Generic; -using System.Linq; - -namespace CSharpMinifier -{ - public class MinifyTypesAstVisitor : DepthFirstAstVisitor - { - private string _currentNamespace; - private List _currentMembers; - private IEnumerable _ignoredTypes; - - public bool CompressPublic - { - get; - private set; - } - - public HashSet NotTypesIdNames - { - get; - private set; - } - - public List Types - { - get; - private set; - } - - public MinifyTypesAstVisitor(IEnumerable ignoredTypes, bool compressPublic) - { - _ignoredTypes = ignoredTypes; - CompressPublic = compressPublic; - Types = new List(); - NotTypesIdNames = new HashSet(); - } - - public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) - { - _currentNamespace = namespaceDeclaration.Name; - base.VisitNamespaceDeclaration(namespaceDeclaration); - } - - public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) - { - _currentMembers = new List(); - if (CheckModifiers(typeDeclaration) && !_ignoredTypes.Contains(typeDeclaration.Name)) - Types.Add(new NameNode(typeDeclaration.Name, typeDeclaration)); - base.VisitTypeDeclaration(typeDeclaration); - } - - public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) - { - foreach (var v in fieldDeclaration.Variables) - NotTypesIdNames.Add(v.Name); - base.VisitFieldDeclaration(fieldDeclaration); - } - - public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) - { - NotTypesIdNames.Add(methodDeclaration.Name); - base.VisitMethodDeclaration(methodDeclaration); - } - - public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) - { - NotTypesIdNames.Add(propertyDeclaration.Name); - base.VisitPropertyDeclaration(propertyDeclaration); - } - - public override void VisitEventDeclaration(EventDeclaration eventDeclaration) - { - foreach (var v in eventDeclaration.Variables) - NotTypesIdNames.Add(v.Name); - base.VisitEventDeclaration(eventDeclaration); - } - - public override void VisitIdentifier(Identifier identifier) - { - if (Types.FirstOrDefault(t => t.Name == identifier.Name) == null) - NotTypesIdNames.Add(identifier.Name); - base.VisitIdentifier(identifier); - } - - private bool CheckModifiers(EntityDeclaration declaration) - { - return CompressPublic || (declaration.Modifiers & Modifiers.Public) == Modifiers.None; - } - } -} diff --git a/CSharpMinifier/NRefactoryUtils.cs b/CSharpMinifier/NRefactoryUtils.cs deleted file mode 100644 index 0ebb452..0000000 --- a/CSharpMinifier/NRefactoryUtils.cs +++ /dev/null @@ -1,47 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; -using System; -using System.Linq; -using System.Reflection; - -namespace CSharpMinifier -{ - public static class NRefactoryUtils - { - public static PropertyInfo[] GetProperties(this AstNode node) - { - return node.GetType().GetProperties(); - } - - public static string GetPropertyValue(this AstNode node, string propertyName) - { - return node.GetType() - .GetProperties() - .GetPropertyValue(node, propertyName); - } - - public static T GetPropertyValueEnum(this AstNode node, string propertyName) - { - return (T)node.GetType() - .GetProperties() - .GetPropertyValueEnum(node, propertyName); - } - - public static string GetPropertyValue(this PropertyInfo[] properties, AstNode node, string propertyName) - { - return properties - .Where(p => p.Name == propertyName) - .FirstOrDefault() - .GetValue(node, null) - .ToString(); - } - - public static T GetPropertyValueEnum(this PropertyInfo[] properties, AstNode node, string propertyName) - { - return (T)Enum.Parse(typeof(T), properties - .Where(p => p.Name == propertyName) - .FirstOrDefault() - .GetValue(node, null) - .ToString()); - } - } -} diff --git a/CSharpMinifier/NameNode.cs b/CSharpMinifier/NameNode.cs deleted file mode 100644 index cc58b82..0000000 --- a/CSharpMinifier/NameNode.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ICSharpCode.NRefactory.CSharp; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace CSharpMinifier -{ - public class NameNode - { - public string Name; - public AstNode Node; - - public NameNode(string name, AstNode node) - { - Name = name; - Node = node; - } - - public override string ToString() - { - return Name + "; " + Node.ToString(); - } - } -} diff --git a/CSharpMinifier/NamesGenerator.cs b/CSharpMinifier/NamesGenerator.cs deleted file mode 100644 index 62a1136..0000000 --- a/CSharpMinifier/NamesGenerator.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Collections.Generic; - -namespace CSharpMinifier -{ - public abstract class NamesGenerator - { - public static string[] CSharpKeywords = new string[] - { - "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", - "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", - "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", - "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", - "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", - "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", - "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", - "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", - "ushort", "using", "virtual", "void", "volatile", "while" - }; - - public static Dictionary CSharpTypeSynonyms = new Dictionary() - { - { "Boolean", "bool" }, - { "Char", "char" }, - { "Byte", "byte" }, - { "Double", "double" }, - { "False", "false" }, - { "Single", "float" }, - { "Int32", "int" }, - { "Int64", "long" }, - { "Object", "object" }, - { "SByte", "sbyte" }, - { "Int16", "short" }, - { "String", "string" }, - { "True", "true" }, - { "UInt32", "uint" }, - { "UInt64", "ulong" }, - { "UInt16", "ushort" } - }; - - public abstract string Next(); - - public NamesGenerator() - { - Reset(); - } - - public virtual void Reset() - { - CurrentCombinationNumber = -1; - CurrentCombination = string.Empty; - Prefix = ""; - Postfix = ""; - } - - public virtual int CurrentCombinationNumber - { - get; - set; - } - - public string CurrentCombination - { - get; - protected set; - } - - public string Prefix - { - get; - protected set; - } - - public string Postfix - { - get; - protected set; - } - } -} diff --git a/CSharpMinifier/Properties/AssemblyInfo.cs b/CSharpMinifier/Properties/AssemblyInfo.cs deleted file mode 100644 index 2635b8c..0000000 --- a/CSharpMinifier/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("CSharpMinifier")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("CSharpMinifier")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c10cd76d-98b2-43b5-9813-035afa0051b6")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharpMinifier/RandomIdGenerator.cs b/CSharpMinifier/RandomIdGenerator.cs deleted file mode 100644 index fbb39ca..0000000 --- a/CSharpMinifier/RandomIdGenerator.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace CSharpMinifier -{ - public class RandomIdGenerator : NamesGenerator - { - private List _generatedIds; - private Random _random; - - public int MinLength - { - get; - set; - } - - public int MaxLength - { - get; - set; - } - - public RandomIdGenerator(int minLength, int maxLength) - : base() - { - MinLength = minLength; - MaxLength = MaxLength; - } - - public override void Reset() - { - _generatedIds = new List(); - _random = new Random(); - base.Reset(); - } - - public override string Next() - { - int length = _random.Next(MinLength, MaxLength + 1); - - if (CurrentCombinationNumber < _generatedIds.Count) - { - StringBuilder result = new StringBuilder(length); - do - { - result.Clear(); - result.Append(MinIdGenerator.Chars0[_random.Next(MinIdGenerator.Chars0.Length)]); - for (int i = 1; i < length; i++) - result.Append(MinIdGenerator.CharsN[_random.Next(MinIdGenerator.CharsN.Length)]); - CurrentCombination = result.ToString(); - } - while (_generatedIds.Contains(CurrentCombination)); - _generatedIds.Add(CurrentCombination); - } - else - CurrentCombination = _generatedIds[CurrentCombinationNumber]; - - CurrentCombinationNumber++; - return Prefix + CurrentCombination + Postfix; - } - - public override int CurrentCombinationNumber - { - get - { - return base.CurrentCombinationNumber; - } - set - { - CurrentCombination = CurrentCombinationNumber >= 0 && CurrentCombinationNumber < _generatedIds.Count ? _generatedIds[CurrentCombinationNumber] : ""; - base.CurrentCombinationNumber = value; - } - } - } -} diff --git a/CSharpMinifier/ResolveResultType.cs b/CSharpMinifier/ResolveResultType.cs deleted file mode 100644 index 6ba7436..0000000 --- a/CSharpMinifier/ResolveResultType.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace CSharpMinifier -{ - public enum ResolveResultType - { - Local, - Member, - Type - } -} diff --git a/CSharpMinifier/Rewriters/TokensMinifier.cs b/CSharpMinifier/Rewriters/TokensMinifier.cs new file mode 100644 index 0000000..ec8ae98 --- /dev/null +++ b/CSharpMinifier/Rewriters/TokensMinifier.cs @@ -0,0 +1,185 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Rename; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace CSharpMinifier.Rewriters +{ + class TokensMinifier : CSharpSyntaxRewriter + { + private SemanticModel _semanticModel; + private readonly AdhocWorkspace _workspace; + private readonly MinifierOptions _options; + private readonly IdentifierGenerator _identifierGenerator; + readonly string PublicModifier = "public"; + readonly string PrivateModifier = "private"; + + public TokensMinifier(AdhocWorkspace workspace, MinifierOptions options = null, bool visitIntoStructuredTrivia = true) : base(visitIntoStructuredTrivia) + { + _options = options ?? new MinifierOptions(); + _identifierGenerator = new IdentifierGenerator(); + _workspace = workspace; + //Add cause MSBuild does not copy Charp.Workspace.dll + var _ = typeof(Microsoft.CodeAnalysis.CSharp.Formatting.CSharpFormattingOptions); + } + + public AdhocWorkspace MinifyIdentifiers() + { + foreach (var project in _workspace.CurrentSolution.Projects) + { + foreach (var document in project.Documents) + { + _semanticModel = document.GetSemanticModelAsync().Result; + var node = _semanticModel.SyntaxTree.GetRoot(); + node = node.ReplaceTrivia(node.DescendantTrivia(), ReplaceSpaces); + node = node.ReplaceTrivia(node.DescendantTrivia(), ReplaceCommentsAndRegions); + node = Visit(node); + RenameAll(document.WithSyntaxRoot(node)); + } + } + + return _workspace; + } + + private void RenameAll(Document document) + { + _semanticModel = document.GetSemanticModelAsync().Result; + var newNode = _semanticModel.SyntaxTree.GetRoot(); + + foreach (KeyValuePair variable in _identifierGenerator.RenamedVariables) + { + var nodeToSearch = newNode.DescendantNodes() + .OfType() + .FirstOrDefault(x => + x.Identifier.ValueText.Equals(variable.Key.Identifier.ValueText)); + (newNode, document) = Rename(nodeToSearch, document, variable.Value); + } + + foreach (var method in _identifierGenerator.RenamedMethods) + { + var nodeToSearch = newNode.DescendantNodes() + .OfType() + .FirstOrDefault(x => + x.Identifier.ValueText.Equals(method.Key.Identifier.ValueText)); + (newNode, document) = Rename(nodeToSearch, document, method.Value); + } + + foreach (var classToRename in _identifierGenerator.RenamedTypes) + { + var nodeToSearch = newNode.DescendantNodes() + .OfType() + .FirstOrDefault(x => + x.Identifier.ValueText.Equals(classToRename.Key.Identifier.ValueText)); + (newNode, document) = Rename(nodeToSearch, document, classToRename.Value); + } + + } + + private (SyntaxNode node, Document document) Rename(SyntaxNode nodeToRename, Document document, string newName) + { + var symbolInfo = _semanticModel.GetSymbolInfo(nodeToRename).Symbol ?? + _semanticModel.GetDeclaredSymbol(nodeToRename); + var solution = Renamer.RenameSymbolAsync(document.Project.Solution, symbolInfo, newName, + _workspace.Options).Result; + _workspace.TryApplyChanges(solution); + document = _workspace.CurrentSolution.GetDocument(document.Id); + _semanticModel = document.GetSemanticModelAsync().Result; + return (_semanticModel.SyntaxTree.GetRoot(), document); + } + + public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) + { + if (_options.LocalVarsCompressing) + { + _identifierGenerator.GetNextName(node.Declaration.Variables.First()); + } + return base.VisitLocalDeclarationStatement(node); + } + + public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) + { + if (_options.PublicCompressing) + { + _identifierGenerator.GetNextName(node); + } + return base.VisitClassDeclaration(node); + } + + public override SyntaxNode VisitFieldDeclaration(FieldDeclarationSyntax node) + { + if (_options.LocalVarsCompressing && node.Modifiers.Any(m => m.Value.Equals(PrivateModifier))) + { + _identifierGenerator.GetNextName(node.Declaration.Variables.First()); + } + else if (_options.PublicCompressing && node.Modifiers.Any(m => m.Value.Equals(PublicModifier))) + { + _identifierGenerator.GetNextName(node.Declaration.Variables.First()); + } + return base.VisitFieldDeclaration(node); + } + + public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) + { + if (_options.LocalVarsCompressing && node.Modifiers.Any(m => m.Value.Equals(PrivateModifier))) + { + _identifierGenerator.GetNextName(node); + } + else if (_options.PublicCompressing && node.Modifiers.Any(m => m.Value.Equals(PublicModifier))) + { + _identifierGenerator.GetNextName(node); + } + return base.VisitMethodDeclaration(node); + } + + public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node) + { + if (_options.PublicCompressing && node.Modifiers.Any(m => m.Value.Equals(PublicModifier))) + { + _identifierGenerator.GetNextName(node); + } + else if (_options.LocalVarsCompressing && node.Modifiers.Any(m => m.Value.Equals(PublicModifier))) + { + _identifierGenerator.GetNextName(node); + } + return base.VisitPropertyDeclaration(node); + } + + + public SyntaxTrivia ReplaceSpaces(SyntaxTrivia arg1, SyntaxTrivia arg2) + { + if (_options.SpacesRemoving) + { + if (arg1.IsKind(SyntaxKind.WhitespaceTrivia) || (arg1.IsKind(SyntaxKind.EndOfLineTrivia) + && arg1.Span.Length > 1)) + { + arg2 = Space; + } + else + { + arg2 = arg1; + } + } + return arg2; + } + + public SyntaxTrivia ReplaceCommentsAndRegions(SyntaxTrivia arg1, SyntaxTrivia arg2) + { + if (_options.CommentsRemoving || _options.RegionsRemoving) + { + if (arg1.IsKind(SyntaxKind.SingleLineCommentTrivia) || arg1.IsKind(SyntaxKind.MultiLineCommentTrivia) + || arg1.IsKind(SyntaxKind.RegionDirectiveTrivia) || arg1.IsKind(SyntaxKind.EndRegionDirectiveTrivia)) + { + arg2 = Space; + } + else + { + arg2 = arg1; + } + } + return arg2; + } + } +} diff --git a/CSharpMinifier/RoslynMinifier.cs b/CSharpMinifier/RoslynMinifier.cs new file mode 100644 index 0000000..7fe8448 --- /dev/null +++ b/CSharpMinifier/RoslynMinifier.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using CSharpMinifier.Rewriters; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace CSharpMinifier +{ + public class RoslynMinifier : IMinifier + { + public MinifierOptions Options { get; private set; } + + public List IgnoredIdentifiers { get; private set; } + + public List IgnoredComments { get; private set; } + + private AdhocWorkspace _workspace; + private PortableExecutableReference _mscorlib; + + public RoslynMinifier(MinifierOptions options = null, string[] ignoredIdentifiers = null, string[] ignoredComments = null) + { + Options = options ?? new MinifierOptions(); + IgnoredIdentifiers = ignoredIdentifiers?.ToList() ?? new List(); + IgnoredComments = ignoredComments?.ToList() ?? new List(); + _workspace = new AdhocWorkspace(); + _mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); + } + + public string MinifyFiles(string[] csFiles) + { + var project = _workspace.AddProject("MinifierProject", LanguageNames.CSharp); + project = project.AddMetadataReference(_mscorlib); + _workspace.TryApplyChanges(project.Solution); + var tempName = "tempFile"; + + for (int i = 0; i < csFiles.Length; i++) + { + var syntaxTree = CSharpSyntaxTree.ParseText(csFiles[i]); + _workspace.AddDocument(project.Id, tempName + i, syntaxTree.GetText()); + } + + _workspace = Minify(_workspace); + StringBuilder builder = new StringBuilder(); + var minifiedSources = _workspace.CurrentSolution.Projects.First().Documents.Select(d => d.GetTextAsync().Result.ToString()); + + foreach (var source in minifiedSources) + builder.Append(source); + + return builder.ToString(); + } + + public string MinifyFromString(string csharpCode) + { + var syntaxTree = CSharpSyntaxTree.ParseText(csharpCode); + Project project; + if (_workspace.CurrentSolution.Projects.Any()) + { + project = _workspace.AddProject("MinifierProject", LanguageNames.CSharp); + project = project.AddMetadataReference(_mscorlib); + _workspace.TryApplyChanges(project.Solution); + } + else + { + project = _workspace.CurrentSolution.Projects.First(); + } + var document = _workspace.AddDocument(project.Id, "Doc", syntaxTree.GetText()); + _workspace = Minify(_workspace); + var resultCode = _workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync().Result.ToString(); + project = _workspace.CurrentSolution.Projects.First().RemoveDocument(document.Id); + _workspace.TryApplyChanges(project.Solution); + return resultCode; + } + + private AdhocWorkspace Minify(AdhocWorkspace workspace) + { + var rewriter = new TokensMinifier(workspace, Options); + return rewriter.MinifyIdentifiers(); + } + } +} \ No newline at end of file diff --git a/CSharpMinifier/Substitutor.cs b/CSharpMinifier/Substitutor.cs deleted file mode 100644 index 8f9a475..0000000 --- a/CSharpMinifier/Substitutor.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace CSharpMinifier -{ - public class Substitutor - { - public NamesGenerator Generator - { - get; - set; - } - - public Substitutor(NamesGenerator generator) - { - Generator = generator; - } - - public Dictionary> Generate(Dictionary> idNameNodes, IEnumerable excludedNames) - { - var result = new Dictionary>(); - - foreach (var vars in idNameNodes) - result.Add(vars.Key, Generate(vars.Value, excludedNames)); - - return result; - } - - public Dictionary Generate(List idNameNodes, IEnumerable excludedNames) - { - Generator.Reset(); - - int varCount = idNameNodes.Count; - string[] newNames = new string[varCount]; - var newSubstitution = new Dictionary(); - - for (int i = 0; i < varCount; i++) - { - string newName; - do - { - newName = Generator.Next(); - } - while (excludedNames.Contains(newName) || NamesGenerator.CSharpKeywords.Contains(newName)); - newNames[i] = newName; - } - - int ind = 0; - foreach (NameNode v in idNameNodes) - if (!newSubstitution.ContainsKey(v.Name)) - newSubstitution.Add(v.Name, newNames[ind++]); - - return newSubstitution; - } - } -} diff --git a/CSharpMinifier/app.config b/CSharpMinifier/app.config new file mode 100644 index 0000000..49196a3 --- /dev/null +++ b/CSharpMinifier/app.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpMinifier/packages.config b/CSharpMinifier/packages.config deleted file mode 100644 index 3cccab0..0000000 --- a/CSharpMinifier/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 0000000..21e27ce --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "2.1.500" + } +} \ No newline at end of file diff --git a/install.cmd b/install.cmd new file mode 100644 index 0000000..92c0e05 --- /dev/null +++ b/install.cmd @@ -0,0 +1,3 @@ +@echo off +NuGet install Microsoft.Net.Compilers -Version 2.10.0 -OutputDirectory "%~dp0tools" ^ + && ren "%~dp0tools\Microsoft.Net.Compilers.2.10.0" Microsoft.Net.Compilers