using System.Reflection;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using System.Linq;
using CSharpToJavaScript.Utils;
using System.Collections.Immutable;
namespace CSharpToJavaScript;
///
/// Main type for CSharpToJavaScript.
///
public static class CSTOJS
{
///
/// This method translates CSharp string into JavaScript with specified options.
///
/// . CS string with options.
/// Optional. Array of references.
///
public static FileData Translate(FileData file, MetadataReference[]? references = null)
{
FileData[] files = Translate([file], references);
return files[0];
}
///
/// This method translates CSharp string into JavaScript with specified options.
///
/// Array of . CS strings with options.
/// Optional. Array of references.
/// Array of . JS strings
public static FileData[] Translate(FileData[] files, MetadataReference[]? references = null)
{
Assembly assembly = Assembly.GetExecutingAssembly();
//https://stackoverflow.com/a/73474279
Log.InfoLine($"{assembly.GetName().Name} {assembly.GetCustomAttribute()?.InformationalVersion}");
if (references == null)
references = GetReferences(files[0].OptionsForFile);
SyntaxTree[] trees = new SyntaxTree[files.Length];
for (int i = 0; i < files.Length; i++)
{
trees[i] = CSharpSyntaxTree.ParseText(files[i].SourceStr);
}
trees[0] = AddGlobalUsings(trees[0]);
CSharpCompilation compilation = CSharpCompilation
.Create("HelloWorld")
.AddReferences(references)
.AddSyntaxTrees(trees);
for (int i = 0; i < files.Length; i++)
{
if (files[i].OptionsForFile.TranslateFile == false)
continue;
SemanticModel _model = compilation.GetSemanticModel(trees[i]);
ImmutableArray diagnostics = _model.GetDiagnostics();
for (int j = 0; j < diagnostics.Length; j++)
{
if (files[i].OptionsForFile.Debug)
Log.WarningLine(diagnostics[j].ToString());
//Print an error if compilation fails.
if (diagnostics[j].Severity == DiagnosticSeverity.Error)
{
if (files[i].OptionsForFile.DisableCompilationErrors == false)
Log.ErrorLine(diagnostics[i].ToString());
}
}
SyntaxNode _root = trees[i].GetRoot();
WithSemanticRewriter _withSemanticRewriter = new(_model, files[i].OptionsForFile);
WithoutSemanticRewriter _withoutSemanticRewriter = new(files[i].OptionsForFile);
StringBuilderWalker _stringBuilderWalker = new();
_root = _withSemanticRewriter.Visit(_root);
_root = _root.ReplaceNodes(_withSemanticRewriter.ReplaceNodes.Keys, (o, r) =>
{
return _withSemanticRewriter.ReplaceNodes[o];
});
if (files[i].OptionsForFile.Debug)
files[i].Debug_WithSemanticRewriter = _root.ToFullString();
_root = _withoutSemanticRewriter.Visit(_root);
if (files[i].OptionsForFile.Debug)
files[i].Debug_WithoutSemanticRewriter = _root.ToFullString();
if (files[i].OptionsForFile.NormalizeWhitespace)
_root = _root.NormalizeWhitespace();
if (files[i].OptionsForFile.KeepBraceOnTheSameLine)
{
KeepBraceOnTheSameLineRewriter _keepBraceOnTheSameLineRewriter = new();
_root = _keepBraceOnTheSameLineRewriter.Visit(_root);
}
_stringBuilderWalker.JSSB.Append(files[i].OptionsForFile.AddSBAtTheTop);
_stringBuilderWalker.Visit(_root);
_stringBuilderWalker.JSSB.Append(files[i].OptionsForFile.AddSBAtTheBottom);
files[i].TranslatedStr = _stringBuilderWalker.JSSB.ToString();
}
return files;
}
private static MetadataReference[] GetReferences(CSTOJSOptions options)
{
HashSet assemblyMetadata = new();
Assembly? assembly = Assembly.GetEntryAssembly();
AssemblyName[] assemblyNames = assembly?.GetReferencedAssemblies() ?? [];
string? assemblyPath = Path.GetDirectoryName(assembly?.Location);
string? objectAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
string? binPath = Directory.Exists("./bin/") ? "./bin/" : null;
if (assemblyNames.Length > 0)
{
for (int j = 0; j < assemblyNames.Length; j++)
{
if (assemblyPath != null && File.Exists(Path.Combine(assemblyPath, assemblyNames[j].Name + ".dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, assemblyNames[j].Name + ".dll")));
if (objectAssemblyPath != null && File.Exists(Path.Combine(objectAssemblyPath, assemblyNames[j].Name + ".dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, assemblyNames[j].Name + ".dll")));
}
}
if (assemblyPath != null)
{
if (File.Exists(Path.Combine(assemblyPath, "CSharpToJavaScript.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "CSharpToJavaScript.dll")));
}
if (objectAssemblyPath != null)
{
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Private.CoreLib.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Private.CoreLib.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Collections.Generics.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Collections.Generics.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.IO.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.IO.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Linq.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Linq.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Net.Http.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Net.Http.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Threading.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Threading.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Threading.Tasks.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Threading.Tasks.dll")));
if (File.Exists(Path.Combine(objectAssemblyPath, "System.Console.dll")))
assemblyMetadata.Add(MetadataReference.CreateFromFile(Path.Combine(objectAssemblyPath, "System.Console.dll")));
}
if (binPath != null)
{
string[] files = Directory.GetFiles(binPath, "*.dll", SearchOption.AllDirectories);
for (int j = 0; j < files.Length; j++)
{
assemblyMetadata.Add(MetadataReference.CreateFromFile(files[j]));
}
}
if (options.Debug)
{
Log.InfoLine($"+++");
Log.InfoLine($"assemblyPath: {assemblyPath}");
Log.InfoLine($"objectAssemblyPath: {objectAssemblyPath}");
Log.InfoLine($"binPath: {binPath}");
foreach (MetadataReference metadata in assemblyMetadata)
{
Log.WriteLine(metadata.Display ?? "null display string");
}
Log.InfoLine($"---");
}
MetadataReference[] references = new MetadataReference[assemblyMetadata.Count];
int i = 0;
foreach (MetadataReference metadata in assemblyMetadata)
{
references[i] = metadata;
i++;
}
if (options.Debug)
{
for (int j = 0; j < references.Length; j++)
{
Log.WriteLine(references[j].Display ?? "null display string");
}
Log.InfoLine($"+++");
}
return references;
}
private static SyntaxTree AddGlobalUsings(SyntaxTree tree)
{
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
UsingDirectiveSyntax[] oldUsing = root.Usings.ToArray();
//https://stackoverflow.com/a/72938702
root = root.WithUsings
(
SyntaxFactory.List
(
new UsingDirectiveSyntax[]
{
SyntaxFactory.UsingDirective
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.UsingDirective
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
),
SyntaxFactory.IdentifierName("Collections")
),
SyntaxFactory.IdentifierName("Generic")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.UsingDirective
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
),
SyntaxFactory.IdentifierName("IO")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.UsingDirective
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
),
SyntaxFactory.IdentifierName("Linq")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.UsingDirective
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
),
SyntaxFactory.IdentifierName("Net")
),
SyntaxFactory.IdentifierName("Http")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.UsingDirective
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
),
SyntaxFactory.IdentifierName("Threading")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.UsingDirective
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.QualifiedName
(
SyntaxFactory.AliasQualifiedName
(
SyntaxFactory.IdentifierName
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
),
SyntaxFactory.IdentifierName("System")
),
SyntaxFactory.IdentifierName("Threading")
),
SyntaxFactory.IdentifierName("Tasks")
)
)
.WithGlobalKeyword
(
SyntaxFactory.Token(SyntaxKind.GlobalKeyword)
)
}
)
).AddUsings(oldUsing);
return root.SyntaxTree;
}
}
///
/// FileData includes a CSharp string, options, and a JavaScript string.
///
public class FileData
{
///
/// Options for a translation.
///
public CSTOJSOptions OptionsForFile { get; set; } = new();
///
/// CS input string.
///
public string SourceStr { get; set; } = string.Empty;
///
/// JS translated string.
///
public string TranslatedStr { get; set; } = string.Empty;
///
/// Debug string.
///
public string Debug_WithSemanticRewriter { get; set; } = string.Empty;
///
/// Debug string.
///
public string Debug_WithoutSemanticRewriter { get; set; } = string.Empty;
}