From a92bc8dc4d601707469146543d0e18e0a524d948 Mon Sep 17 00:00:00 2001 From: TiLied Date: Thu, 19 Mar 2026 22:02:07 +0300 Subject: [PATCH 1/8] Walker2 --- CSharpToJavaScript/CSTOJS.cs | 23 +- .../Walker/StringBuilderWalker.cs | 385 ++++++++++++++++++ CSharpToJavaScript/Walker/Walker2.cs | 69 ++++ .../Walker/WithSemanticRewriter.cs | 180 ++++++++ .../Walker/WithoutSemanticRewriter.cs | 110 +++++ 5 files changed, 762 insertions(+), 5 deletions(-) create mode 100644 CSharpToJavaScript/Walker/StringBuilderWalker.cs create mode 100644 CSharpToJavaScript/Walker/Walker2.cs create mode 100644 CSharpToJavaScript/Walker/WithSemanticRewriter.cs create mode 100644 CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs diff --git a/CSharpToJavaScript/CSTOJS.cs b/CSharpToJavaScript/CSTOJS.cs index 68685446..3275d59a 100644 --- a/CSharpToJavaScript/CSTOJS.cs +++ b/CSharpToJavaScript/CSTOJS.cs @@ -22,8 +22,10 @@ public static class CSTOJS /// public static FileData Translate(FileData file, MetadataReference[]? references = null) { - FileData[] files = Translate([file], references); - return files[0]; + new Walker2(file); + return file; + //FileData[] files = Translate([file], references); + //return files[0]; } /// /// This method translates CSharp string into JavaScript with specified options. @@ -122,7 +124,7 @@ public static FileData[] Translate(FileData[] files, MetadataReference[]? refere return files; } - private static MetadataReference[] GetReferences(CSTOJSOptions options) + public static MetadataReference[] GetReferences(CSTOJSOptions options) { HashSet assemblyMetadata = new(); @@ -222,7 +224,7 @@ private static MetadataReference[] GetReferences(CSTOJSOptions options) return references; } - private static SyntaxTree AddGlobalUsings(SyntaxTree tree) + public static SyntaxTree AddGlobalUsings(SyntaxTree tree) { CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); UsingDirectiveSyntax[] oldUsing = root.Usings.ToArray(); @@ -402,4 +404,15 @@ public class FileData /// JS translated string. /// public string TranslatedStr { get; set; } = string.Empty; -} \ No newline at end of file + + + /// + /// Debug string. + /// + public string Debug_WithSemanticRewriter { get; set; } = string.Empty; + + /// + /// Debug string. + /// + public string Debug_WithoutSemanticRewriter { get; set; } = string.Empty; +} diff --git a/CSharpToJavaScript/Walker/StringBuilderWalker.cs b/CSharpToJavaScript/Walker/StringBuilderWalker.cs new file mode 100644 index 00000000..e44751a1 --- /dev/null +++ b/CSharpToJavaScript/Walker/StringBuilderWalker.cs @@ -0,0 +1,385 @@ +using CSharpToJavaScript.Utils; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Text; +using System; + +namespace CSharpToJavaScript; + +internal class StringBuilderWalker : CSharpSyntaxWalker +{ + public StringBuilder JSSB { get; set; } = new(512); + + public StringBuilderWalker(): base(SyntaxWalkerDepth.Trivia) + { + + } + +#if DEBUG + public override void Visit(SyntaxNode? node) + { + if(node != null) + Log.InfoLine($"kind: {node.Kind()} \n\t{node.ToString()}"); + + base.Visit(node); + } +#endif + + public override void VisitTrivia(SyntaxTrivia trivia) + { + switch (trivia.Kind()) + { + case SyntaxKind.SingleLineCommentTrivia: + { + string _full = trivia.ToString(); + + //special syntax. + //for writing js code //...\\ + if (_full.EndsWith(@"\\")) + JSSB.Append(_full.AsSpan(2, _full.Length - 4)); + else + JSSB.Append(_full); + return; + } + case SyntaxKind.MultiLineCommentTrivia: + case SyntaxKind.WhitespaceTrivia: + { + string _full = trivia.ToFullString(); + JSSB.Append(_full); + return; + } + case SyntaxKind.EndOfLineTrivia: + { + string _full = trivia.ToFullString(); + JSSB.Append(_full); + return; + } + // Todo? how? convert to jsdoc? + case SyntaxKind.SingleLineDocumentationCommentTrivia: + case SyntaxKind.MultiLineDocumentationCommentTrivia: + { + //JSSB.Append("/**"); + string _full = trivia.ToFullString(); + JSSB.Append(_full); + //JSSB.AppendLine(""); + return; + } + //TODO??? + case SyntaxKind.SkippedTokensTrivia: + { + string _full = trivia.ToFullString(); + JSSB.Append(_full); + return; + } + default: + Log.ErrorLine($"Trivia : {trivia.Kind()}"); + break; + } + + base.VisitTrivia(trivia); + } + + public override void VisitToken(SyntaxToken token) + { + switch (token.Kind()) + { + //TODO? + case SyntaxKind.IdentifierToken: + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text.Replace("DollarSign_", "$")); + + VisitTrailingTrivia(token); + return; + } + case SyntaxKind.InKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.ReturnKeyword: + case SyntaxKind.ClassKeyword: + case SyntaxKind.OpenParenToken: + case SyntaxKind.CloseParenToken: + case SyntaxKind.SemicolonToken: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.DotToken: + case SyntaxKind.EqualsToken: + case SyntaxKind.StringLiteralToken: + case SyntaxKind.ForKeyword: + case SyntaxKind.IfKeyword: + case SyntaxKind.GreaterThanEqualsToken: + case SyntaxKind.PlusPlusToken: + case SyntaxKind.PlusToken: + case SyntaxKind.LessThanToken: + case SyntaxKind.NumericLiteralToken: + case SyntaxKind.CommaToken: + case SyntaxKind.NewKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.MinusToken: + case SyntaxKind.MinusMinusToken: + case SyntaxKind.SlashToken: + case SyntaxKind.AsteriskToken: + case SyntaxKind.NullKeyword: + case SyntaxKind.BreakKeyword: + case SyntaxKind.CloseBracketToken: + case SyntaxKind.OpenBracketToken: + case SyntaxKind.EqualsGreaterThanToken: + case SyntaxKind.AwaitKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.ElseKeyword: + case SyntaxKind.SwitchKeyword: + case SyntaxKind.CaseKeyword: + case SyntaxKind.ColonToken: + case SyntaxKind.DefaultKeyword: + case SyntaxKind.BarBarToken: + case SyntaxKind.ExclamationToken: + case SyntaxKind.ContinueKeyword: + case SyntaxKind.GreaterThanToken: + case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.PlusEqualsToken: + case SyntaxKind.MinusEqualsToken: + case SyntaxKind.AsteriskEqualsToken: + case SyntaxKind.SlashEqualsToken: + case SyntaxKind.WhileKeyword: + case SyntaxKind.InterpolatedStringTextToken: + case SyntaxKind.QuestionToken: + case SyntaxKind.LessThanEqualsToken: + case SyntaxKind.ConstKeyword: + case SyntaxKind.PercentToken: + case SyntaxKind.QuestionQuestionToken: + case SyntaxKind.TildeToken: + case SyntaxKind.AmpersandToken: + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.LessThanLessThanToken: + case SyntaxKind.GreaterThanGreaterThanEqualsToken: + case SyntaxKind.LessThanLessThanEqualsToken: + case SyntaxKind.CaretEqualsToken: + case SyntaxKind.BarEqualsToken: + case SyntaxKind.AmpersandEqualsToken: + case SyntaxKind.BarToken: + case SyntaxKind.DoKeyword: + case SyntaxKind.ThrowStatement: + case SyntaxKind.CatchKeyword: + case SyntaxKind.TryKeyword: + case SyntaxKind.ThrowKeyword: + case SyntaxKind.FinallyKeyword: + case SyntaxKind.CaretToken: + case SyntaxKind.CharacterLiteralToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.PercentEqualsToken: + case SyntaxKind.QuestionQuestionEqualsToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + case SyntaxKind.TypeOfKeyword: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.EqualsEqualsToken: + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text); + + VisitTrailingTrivia(token); + return; + } + case SyntaxKind.EndOfFileToken: + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text); + + VisitTrailingTrivia(token); + + return; + } + case SyntaxKind.OpenBraceToken: + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text); + + VisitTrailingTrivia(token); + return; + } + default: + Log.ErrorLine($"Token : {token.Kind()}"); + break; + } + + base.VisitToken(token); + } + + public override void VisitCompilationUnit(CompilationUnitSyntax node) + { + ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); + + for (int i = 0; i < nodesAndTokens.Count; i++) + { + SyntaxNode? asNode = nodesAndTokens[i].AsNode(); + + if (asNode != null) + { + SyntaxKind kind = asNode.Kind(); + + switch (kind) + { + case SyntaxKind.AttributeList: + case SyntaxKind.ExternAliasDirective: + case SyntaxKind.UsingDirective: + { + #if DEBUG + Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); + #endif + break; + } + case SyntaxKind.ClassDeclaration: + VisitClassDeclaration((ClassDeclarationSyntax)asNode); + break; + case SyntaxKind.GlobalStatement: + VisitGlobalStatement((GlobalStatementSyntax)asNode); + break; + case SyntaxKind.NamespaceDeclaration: + VisitNamespaceDeclaration((NamespaceDeclarationSyntax)asNode); + break; + case SyntaxKind.FileScopedNamespaceDeclaration: + VisitFileScopedNamespaceDeclaration((FileScopedNamespaceDeclarationSyntax)asNode); + break; + default: + Log.ErrorLine($"asNode : {kind}\n|{asNode.ToFullString()}|"); + break; + } + } + else + { + SyntaxToken asToken = nodesAndTokens[i].AsToken(); + SyntaxKind kind = asToken.Kind(); + + switch (kind) + { + case SyntaxKind.EndOfFileToken: + VisitToken(asToken); + break; + default: + Log.ErrorLine($"asToken : {kind}"); + break; + } + } + } + } + public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) + { + ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); + + for (int i = 0; i < nodesAndTokens.Count; i++) + { + SyntaxNode? asNode = nodesAndTokens[i].AsNode(); + + if (asNode != null) + { + SyntaxKind kind = asNode.Kind(); + + switch (kind) + { + case SyntaxKind.UsingDirective: + case SyntaxKind.DelegateDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.QualifiedName: + case SyntaxKind.IdentifierName: + { + #if DEBUG + Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); + #endif + break; + } + case SyntaxKind.ClassDeclaration: + VisitClassDeclaration((ClassDeclarationSyntax)asNode); + break; + case SyntaxKind.EnumDeclaration: + VisitEnumDeclaration((EnumDeclarationSyntax)asNode); + break; + case SyntaxKind.NamespaceDeclaration: + VisitNamespaceDeclaration((NamespaceDeclarationSyntax)asNode); + break; + default: + Log.ErrorLine($"asNode : {kind}\n|{asNode.ToFullString()}|"); + break; + } + } + else + { + SyntaxToken asToken = nodesAndTokens[i].AsToken(); + SyntaxKind kind = asToken.Kind(); + + switch (kind) + { + //Todo? make a scope??? {...} + //OpenBraceToken and CloseBraceToken + case SyntaxKind.OpenBraceToken: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.NamespaceKeyword: + break; + default: + Log.ErrorLine($"asToken : {kind}"); + break; + } + } + } + } + public override void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) + { + ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); + + for (int i = 0; i < nodesAndTokens.Count; i++) + { + SyntaxNode? asNode = nodesAndTokens[i].AsNode(); + + if (asNode != null) + { + SyntaxKind kind = asNode.Kind(); + + switch (kind) + { + case SyntaxKind.DelegateDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.QualifiedName: + case SyntaxKind.IdentifierName: + { + #if DEBUG + Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); + #endif + break; + } + case SyntaxKind.ClassDeclaration: + VisitClassDeclaration((ClassDeclarationSyntax)asNode); + break; + case SyntaxKind.EnumDeclaration: + VisitEnumDeclaration((EnumDeclarationSyntax)asNode); + break; + case SyntaxKind.NamespaceDeclaration: + VisitNamespaceDeclaration((NamespaceDeclarationSyntax)asNode); + break; + default: + Log.ErrorLine($"asNode : {kind}\n|{asNode.ToFullString()}|"); + break; + } + } + else + { + SyntaxToken asToken = nodesAndTokens[i].AsToken(); + SyntaxKind kind = asToken.Kind(); + + switch (kind) + { + case SyntaxKind.SemicolonToken: + case SyntaxKind.NamespaceKeyword: + break; + default: + Log.ErrorLine($"asToken : {kind}"); + break; + } + } + } + } +} diff --git a/CSharpToJavaScript/Walker/Walker2.cs b/CSharpToJavaScript/Walker/Walker2.cs new file mode 100644 index 00000000..6949d97f --- /dev/null +++ b/CSharpToJavaScript/Walker/Walker2.cs @@ -0,0 +1,69 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using CSharpToJavaScript.Utils; +using System.Collections.Immutable; + +namespace CSharpToJavaScript; + +internal class Walker2 +{ + public Walker2(FileData file) + { + SyntaxTree tree = CSharpSyntaxTree.ParseText(file.SourceStr); + tree = CSTOJS.AddGlobalUsings(tree); + + MetadataReference[]? references = CSTOJS.GetReferences(file.OptionsForFile); + + CSharpCompilation compilation = CSharpCompilation + .Create("Walker2") + .AddReferences(references) + .AddSyntaxTrees(tree); + + SemanticModel model = compilation.GetSemanticModel(tree); + + ImmutableArray diagnostics = model.GetDiagnostics(); + for (int i = 0; i < diagnostics.Length; i++) + { + if (file.OptionsForFile.Debug) + Log.WarningLine(diagnostics[i].ToString()); + + //Print an error if compilation fails. + if (diagnostics[i].Severity == DiagnosticSeverity.Error) + { + if(file.OptionsForFile.DisableCompilationErrors == false) + Log.ErrorLine(diagnostics[i].ToString()); + } + } + + SyntaxNode root = tree.GetRoot(); + + WithSemanticRewriter withSemanticRewriter = new(model, file.OptionsForFile); + WithoutSemanticRewriter withoutSemanticRewriter = new(file.OptionsForFile); + StringBuilderWalker stringBuilderWalker = new(); + + SyntaxNode newRoot1 = withSemanticRewriter.Visit(root); + if (root != newRoot1) + root = newRoot1; + root = root.ReplaceTokens(withSemanticRewriter.ReplaceTokens.Keys,(o,r)=> + { + return withSemanticRewriter.ReplaceTokens[o]; + }); + + + if(file.OptionsForFile.Debug) + file.Debug_WithSemanticRewriter = root.ToFullString(); + + SyntaxNode newRoot2 = withoutSemanticRewriter.Visit(root); + if (root != newRoot2) + root = newRoot2; + + if(file.OptionsForFile.Debug) + file.Debug_WithoutSemanticRewriter = root.ToFullString(); + + stringBuilderWalker.JSSB.Append(file.OptionsForFile.AddSBAtTheTop); + stringBuilderWalker.Visit(root); + stringBuilderWalker.JSSB.Append(file.OptionsForFile.AddSBAtTheBottom); + + file.TranslatedStr = stringBuilderWalker.JSSB.ToString(); + } +} diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs new file mode 100644 index 00000000..ba49c8a1 --- /dev/null +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -0,0 +1,180 @@ +using CSharpToJavaScript.Utils; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace CSharpToJavaScript; + +internal class WithSemanticRewriter : CSharpSyntaxRewriter +{ + private readonly NETAPI _NETAPI = new(); + + private readonly SemanticModel _Model; + private readonly CSTOJSOptions _Options; + + public Dictionary ReplaceTokens = new(); + + public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) + { + _Model = semanticModel; + _Options = options; + } + +#if DEBUG + public override SyntaxNode? Visit(SyntaxNode? node) + { + if(node != null) + Log.InfoLine($"kind: {node.Kind()} \n\t{node.ToString()}"); + + return base.Visit(node); + } +#endif + + public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) + { + if(node.Expression is IdentifierNameSyntax identifier) + VisitIdentifier(identifier); + + VisitIdentifier((IdentifierNameSyntax)node.Name); + + node = (MemberAccessExpressionSyntax)base.VisitMemberAccessExpression(node)!; + + return node; + } + + private void VisitIdentifier(IdentifierNameSyntax identifier) + { + ISymbol? symbol = _Model.GetSymbolInfo(identifier).Symbol; + + if (symbol != null) + { + CheckParentAttributes: + + ImmutableArray _attributeData = symbol.GetAttributes(); + for (int i = 0; i < _attributeData.Length; i++) + { + if (_attributeData[i].AttributeClass != null) + { + if (_attributeData[i].AttributeClass!.Name == nameof(ValueAttribute)) + { + string _v = _attributeData[i].ConstructorArguments[0].Value.ToString(); + + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_v).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); + //return node; + return; + } + + if (_attributeData[i].AttributeClass!.Name == nameof(ToAttribute)) + { + ToAttribute _toAttr = new(_attributeData[i].ConstructorArguments[0].Value.ToString()); + + //TODO! + //how? + //if (_toAttr.To == ToAttribute.NoneWithLeadingDotRemoved) + // JSSB.Remove(JSSB.Length - 1, 1); + + //if (_toAttr.To == ToAttribute.NoneWithTrailingDotRemoved) + // _IgnoreTailingDot = true; + + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_toAttr.Convert(identifier.Identifier.Text)).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); + //return node; + return; + } + } + } + if (symbol.ContainingType != null) + { + symbol = symbol.ContainingType; + goto CheckParentAttributes; + } + } + + symbol = _Model.GetSymbolInfo(identifier).Symbol; + + if (_Options.CustomCSNamesToJS.TryGetValue(identifier.Identifier.Text, out string? _value)) + { + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_value)); + //return node; + return; + } + + if(BuiltInTypesGenerics(identifier, symbol, out string str)) + { + if(str == string.Empty) + { + Log.ErrorLine("str == string.Empty!"); + //return node; + return; + } + + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(str).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); + //return node; + return; + } + } + + private bool BuiltInTypesGenerics(SimpleNameSyntax node, ISymbol? symbol, out string str) + { + str = string.Empty; + + if (symbol == null) + { + Log.WarningLine($"node: \"{node}\", symbol is null. USE \"CustomCSNamesToJS\"!"); + return false; + } + if (symbol.Name == "dynamic") + { + //Hitting with "AllInOneNoPreprocessor-v6.cs" + Log.WarningLine($"node: \"{node}\", symbol is \"dynamic\"."); + return false; + } + + ISymbol typeSymbol = symbol; + + if (typeSymbol.Kind != SymbolKind.NamedType) + { + typeSymbol = symbol.ContainingSymbol; + if(typeSymbol == null) + { + Log.WarningLine($"node: \"{node}\", typeSymbol is null. USE \"CustomCSNamesToJS\"!"); + return false; + } + + if (typeSymbol.Kind != SymbolKind.NamedType) + { + Log.WarningLine($"node: \"{node}\", typeSymbol is \"{typeSymbol.Kind}\". USE \"CustomCSNamesToJS\"!"); + return false; + } + } + + string typeName = typeSymbol.Name; + + string? jsStr = _NETAPI.ReturnJSString(typeName); + if (jsStr == null) + { + Log.WarningLine($"typeSymbol: \"{typeSymbol}\" Is not supported! USE \"CustomCSNamesToJS\""); + return false; + } + else + { + if (typeName == symbol.Name) + { + str = jsStr; + return true; + } + jsStr = _NETAPI.ReturnJSString(typeName, symbol.Name); + if (jsStr == null) + { + Log.WarningLine($"node: \"{node}\", typeSymbol: \"{typeSymbol}\", symbol: \"{symbol}\", Is not supported! USE \"CustomCSNamesToJS\""); + return false; + } + else + { + str = jsStr; + return true; + } + } + } +} diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs new file mode 100644 index 00000000..67fe8491 --- /dev/null +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -0,0 +1,110 @@ +using CSharpToJavaScript.Utils; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace CSharpToJavaScript; + +internal class WithoutSemanticRewriter : CSharpSyntaxRewriter +{ + private readonly CSTOJSOptions _Options; + + public WithoutSemanticRewriter(CSTOJSOptions options) + { + _Options = options; + } + +#if DEBUG + public override SyntaxNode? Visit(SyntaxNode? node) + { + if(node != null) + Log.InfoLine($"kind: {node.Kind()} \n\t{node.ToString()}"); + + return base.Visit(node); + } +#endif + + public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) + { + node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node)!; + + if(node.Modifiers.Count >= 1) + { + node = node.ReplaceToken(node.Keyword, node.Keyword.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + + return node; + } + + public override SyntaxNode? VisitConstructorDeclaration(ConstructorDeclarationSyntax node) + { + node = (ConstructorDeclarationSyntax)base.VisitConstructorDeclaration(node)!; + + //TODO! Static constructor! + node = node.ReplaceToken(node.Identifier, SyntaxFactory.Identifier("constructor")); + + if(node.Modifiers.Count >= 1) + { + node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + + return node; + } + + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) + { + node = (FieldDeclarationSyntax)base.VisitFieldDeclaration(node)!; + + //TODO! Static field! + if(node.Modifiers.Count >= 1) + { + node = node.ReplaceNode(node.Declaration.Type, node.Declaration.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + return node; + } + public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node) + { + node = (MethodDeclarationSyntax)base.VisitMethodDeclaration(node)!; + + node = node.ReplaceNode(node.ReturnType, SyntaxFactory.ParseTypeName("")); + + //TODO! Static method! + if(node.Modifiers.Count >= 1) + { + node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + + return node; + } + + public override SyntaxNode? VisitVariableDeclaration(VariableDeclarationSyntax node) + { + if(_Options.UseVarOverLet) + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("var").WithLeadingTrivia(node.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Type.GetTrailingTrivia())); + else + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("let").WithLeadingTrivia(node.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Type.GetTrailingTrivia())); + + node = (VariableDeclarationSyntax)base.VisitVariableDeclaration(node)!; + + return node; + } + + public override SyntaxNode? VisitParameter(ParameterSyntax node) + { + return SyntaxFactory.Parameter(node.Identifier); + } + + public override SyntaxNode? VisitAttributeList(AttributeListSyntax node) + { + return null; + } + public override SyntaxNode? VisitTypeParameterList(TypeParameterListSyntax node) + { + return null; + } + +} From 5b51c0b3cc49dbc05dc4050639f5dd2e50fa6eff Mon Sep 17 00:00:00 2001 From: TiLied Date: Fri, 20 Mar 2026 21:52:27 +0300 Subject: [PATCH 2/8] p2. Total tests: 424/36 failed. --- .../Walker/StringBuilderWalker.cs | 275 +++++++++++------- CSharpToJavaScript/Walker/Walker2.cs | 40 +-- .../Walker/WithSemanticRewriter.cs | 89 +++--- .../Walker/WithoutSemanticRewriter.cs | 123 +++++--- 4 files changed, 311 insertions(+), 216 deletions(-) diff --git a/CSharpToJavaScript/Walker/StringBuilderWalker.cs b/CSharpToJavaScript/Walker/StringBuilderWalker.cs index e44751a1..18b55419 100644 --- a/CSharpToJavaScript/Walker/StringBuilderWalker.cs +++ b/CSharpToJavaScript/Walker/StringBuilderWalker.cs @@ -10,90 +10,90 @@ namespace CSharpToJavaScript; internal class StringBuilderWalker : CSharpSyntaxWalker { public StringBuilder JSSB { get; set; } = new(512); - - public StringBuilderWalker(): base(SyntaxWalkerDepth.Trivia) + + public StringBuilderWalker() : base(SyntaxWalkerDepth.Trivia) { - + } - + #if DEBUG public override void Visit(SyntaxNode? node) { - if(node != null) + if (node != null) Log.InfoLine($"kind: {node.Kind()} \n\t{node.ToString()}"); - + base.Visit(node); } #endif - + public override void VisitTrivia(SyntaxTrivia trivia) { switch (trivia.Kind()) - { + { case SyntaxKind.SingleLineCommentTrivia: - { - string _full = trivia.ToString(); - - //special syntax. - //for writing js code //...\\ - if (_full.EndsWith(@"\\")) - JSSB.Append(_full.AsSpan(2, _full.Length - 4)); - else - JSSB.Append(_full); - return; - } + { + string _full = trivia.ToString(); + + //special syntax. + //for writing js code //...\\ + if (_full.EndsWith(@"\\")) + JSSB.Append(_full.AsSpan(2, _full.Length - 4)); + else + JSSB.Append(_full); + return; + } case SyntaxKind.MultiLineCommentTrivia: case SyntaxKind.WhitespaceTrivia: - { - string _full = trivia.ToFullString(); - JSSB.Append(_full); - return; - } + { + string _full = trivia.ToFullString(); + JSSB.Append(_full); + return; + } case SyntaxKind.EndOfLineTrivia: - { - string _full = trivia.ToFullString(); - JSSB.Append(_full); - return; - } + { + string _full = trivia.ToFullString(); + JSSB.Append(_full); + return; + } // Todo? how? convert to jsdoc? case SyntaxKind.SingleLineDocumentationCommentTrivia: case SyntaxKind.MultiLineDocumentationCommentTrivia: - { - //JSSB.Append("/**"); - string _full = trivia.ToFullString(); - JSSB.Append(_full); - //JSSB.AppendLine(""); - return; - } + { + //JSSB.Append("/**"); + string _full = trivia.ToFullString(); + JSSB.Append(_full); + //JSSB.AppendLine(""); + return; + } //TODO??? case SyntaxKind.SkippedTokensTrivia: - { - string _full = trivia.ToFullString(); - JSSB.Append(_full); - return; - } + { + string _full = trivia.ToFullString(); + JSSB.Append(_full); + return; + } default: Log.ErrorLine($"Trivia : {trivia.Kind()}"); break; } - + base.VisitTrivia(trivia); } - + public override void VisitToken(SyntaxToken token) { switch (token.Kind()) { //TODO? case SyntaxKind.IdentifierToken: - { - VisitLeadingTrivia(token); - - JSSB.Append(token.Text.Replace("DollarSign_", "$")); - - VisitTrailingTrivia(token); - return; - } + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text.Replace("DollarSign_", "$")); + + VisitTrailingTrivia(token); + return; + } case SyntaxKind.InKeyword: case SyntaxKind.StaticKeyword: case SyntaxKind.TrueKeyword: @@ -174,64 +174,64 @@ public override void VisitToken(SyntaxToken token) case SyntaxKind.TypeOfKeyword: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsToken: - { - VisitLeadingTrivia(token); - - JSSB.Append(token.Text); - - VisitTrailingTrivia(token); - return; - } + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text); + + VisitTrailingTrivia(token); + return; + } case SyntaxKind.EndOfFileToken: - { - VisitLeadingTrivia(token); - - JSSB.Append(token.Text); - - VisitTrailingTrivia(token); - - return; - } - case SyntaxKind.OpenBraceToken: - { - VisitLeadingTrivia(token); - - JSSB.Append(token.Text); - - VisitTrailingTrivia(token); - return; - } + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text); + + VisitTrailingTrivia(token); + + return; + } + case SyntaxKind.OpenBraceToken: + { + VisitLeadingTrivia(token); + + JSSB.Append(token.Text); + + VisitTrailingTrivia(token); + return; + } default: Log.ErrorLine($"Token : {token.Kind()}"); break; } - + base.VisitToken(token); } - + public override void VisitCompilationUnit(CompilationUnitSyntax node) { ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); - + for (int i = 0; i < nodesAndTokens.Count; i++) { SyntaxNode? asNode = nodesAndTokens[i].AsNode(); - + if (asNode != null) { SyntaxKind kind = asNode.Kind(); - + switch (kind) { case SyntaxKind.AttributeList: case SyntaxKind.ExternAliasDirective: case SyntaxKind.UsingDirective: - { - #if DEBUG - Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); - #endif - break; - } + { +#if DEBUG + Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); +#endif + break; + } case SyntaxKind.ClassDeclaration: VisitClassDeclaration((ClassDeclarationSyntax)asNode); break; @@ -253,7 +253,7 @@ public override void VisitCompilationUnit(CompilationUnitSyntax node) { SyntaxToken asToken = nodesAndTokens[i].AsToken(); SyntaxKind kind = asToken.Kind(); - + switch (kind) { case SyntaxKind.EndOfFileToken: @@ -269,15 +269,15 @@ public override void VisitCompilationUnit(CompilationUnitSyntax node) public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) { ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); - + for (int i = 0; i < nodesAndTokens.Count; i++) { SyntaxNode? asNode = nodesAndTokens[i].AsNode(); - + if (asNode != null) { SyntaxKind kind = asNode.Kind(); - + switch (kind) { case SyntaxKind.UsingDirective: @@ -286,12 +286,12 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) case SyntaxKind.StructDeclaration: case SyntaxKind.QualifiedName: case SyntaxKind.IdentifierName: - { - #if DEBUG - Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); - #endif - break; - } + { +#if DEBUG + Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); +#endif + break; + } case SyntaxKind.ClassDeclaration: VisitClassDeclaration((ClassDeclarationSyntax)asNode); break; @@ -310,7 +310,7 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) { SyntaxToken asToken = nodesAndTokens[i].AsToken(); SyntaxKind kind = asToken.Kind(); - + switch (kind) { //Todo? make a scope??? {...} @@ -329,15 +329,15 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) public override void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) { ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); - + for (int i = 0; i < nodesAndTokens.Count; i++) { SyntaxNode? asNode = nodesAndTokens[i].AsNode(); - + if (asNode != null) { SyntaxKind kind = asNode.Kind(); - + switch (kind) { case SyntaxKind.DelegateDeclaration: @@ -345,12 +345,12 @@ public override void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDecl case SyntaxKind.StructDeclaration: case SyntaxKind.QualifiedName: case SyntaxKind.IdentifierName: - { - #if DEBUG - Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); - #endif - break; - } + { +#if DEBUG + Log.WarningLine($"\"{kind}\" not implemented or unlikely to be implemented. Ignoring! (FullSpan: {node.FullSpan}|Location{node.GetLocation().GetLineSpan()})\n|{asNode.ToFullString()}|"); +#endif + break; + } case SyntaxKind.ClassDeclaration: VisitClassDeclaration((ClassDeclarationSyntax)asNode); break; @@ -369,7 +369,7 @@ public override void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDecl { SyntaxToken asToken = nodesAndTokens[i].AsToken(); SyntaxKind kind = asToken.Kind(); - + switch (kind) { case SyntaxKind.SemicolonToken: @@ -382,4 +382,61 @@ public override void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDecl } } } + + //Can't do in "WithoutSemanticRewriter". Throws an error: + //An exception of type 'System.ArgumentException' occurred in Microsoft.CodeAnalysis.CSharp.dll but was not handled in user code: 'colonToken' + //Code: + /* + public override SyntaxNode? VisitBaseList(BaseListSyntax node) + { + node = (BaseListSyntax)base.VisitBaseList(node)!; + + node = node.ReplaceToken(node.ColonToken, SyntaxFactory.Token(SyntaxKind.None)); + + return node; + } + */ + public override void VisitBaseList(BaseListSyntax node) + { + ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); + + for (int i = 0; i < nodesAndTokens.Count; i++) + { + SyntaxNode? asNode = nodesAndTokens[i].AsNode(); + + if (asNode != null) + { + SyntaxKind kind = asNode.Kind(); + + switch (kind) + { + case SyntaxKind.SimpleBaseType: + VisitSimpleBaseType((SimpleBaseTypeSyntax)asNode); + break; + default: + Log.ErrorLine($"asNode : {kind}\n|{asNode.ToFullString()}|"); + break; + } + } + else + { + SyntaxToken asToken = nodesAndTokens[i].AsToken(); + SyntaxKind kind = asToken.Kind(); + + switch (kind) + { + case SyntaxKind.ColonToken: + { + VisitLeadingTrivia(asToken); + JSSB.Append("extends"); + VisitTrailingTrivia(asToken); + break; + } + default: + Log.ErrorLine($"asToken : {kind}"); + break; + } + } + } + } } diff --git a/CSharpToJavaScript/Walker/Walker2.cs b/CSharpToJavaScript/Walker/Walker2.cs index 6949d97f..ca3d7560 100644 --- a/CSharpToJavaScript/Walker/Walker2.cs +++ b/CSharpToJavaScript/Walker/Walker2.cs @@ -11,59 +11,59 @@ public Walker2(FileData file) { SyntaxTree tree = CSharpSyntaxTree.ParseText(file.SourceStr); tree = CSTOJS.AddGlobalUsings(tree); - + MetadataReference[]? references = CSTOJS.GetReferences(file.OptionsForFile); - + CSharpCompilation compilation = CSharpCompilation .Create("Walker2") .AddReferences(references) .AddSyntaxTrees(tree); - + SemanticModel model = compilation.GetSemanticModel(tree); - + ImmutableArray diagnostics = model.GetDiagnostics(); for (int i = 0; i < diagnostics.Length; i++) { if (file.OptionsForFile.Debug) Log.WarningLine(diagnostics[i].ToString()); - + //Print an error if compilation fails. if (diagnostics[i].Severity == DiagnosticSeverity.Error) { - if(file.OptionsForFile.DisableCompilationErrors == false) + if (file.OptionsForFile.DisableCompilationErrors == false) Log.ErrorLine(diagnostics[i].ToString()); } - } - + } + SyntaxNode root = tree.GetRoot(); - + WithSemanticRewriter withSemanticRewriter = new(model, file.OptionsForFile); WithoutSemanticRewriter withoutSemanticRewriter = new(file.OptionsForFile); StringBuilderWalker stringBuilderWalker = new(); - + SyntaxNode newRoot1 = withSemanticRewriter.Visit(root); if (root != newRoot1) root = newRoot1; - root = root.ReplaceTokens(withSemanticRewriter.ReplaceTokens.Keys,(o,r)=> + root = root.ReplaceTokens(withSemanticRewriter.ReplaceTokens.Keys, (o, r) => { - return withSemanticRewriter.ReplaceTokens[o]; + return withSemanticRewriter.ReplaceTokens[o]; }); - - - if(file.OptionsForFile.Debug) + + + if (file.OptionsForFile.Debug) file.Debug_WithSemanticRewriter = root.ToFullString(); - + SyntaxNode newRoot2 = withoutSemanticRewriter.Visit(root); if (root != newRoot2) root = newRoot2; - - if(file.OptionsForFile.Debug) + + if (file.OptionsForFile.Debug) file.Debug_WithoutSemanticRewriter = root.ToFullString(); - + stringBuilderWalker.JSSB.Append(file.OptionsForFile.AddSBAtTheTop); stringBuilderWalker.Visit(root); stringBuilderWalker.JSSB.Append(file.OptionsForFile.AddSBAtTheBottom); - + file.TranslatedStr = stringBuilderWalker.JSSB.ToString(); } } diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs index ba49c8a1..a93a4c5e 100644 --- a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -10,48 +10,51 @@ namespace CSharpToJavaScript; internal class WithSemanticRewriter : CSharpSyntaxRewriter { private readonly NETAPI _NETAPI = new(); - + private readonly SemanticModel _Model; private readonly CSTOJSOptions _Options; - + public Dictionary ReplaceTokens = new(); - - public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) + + public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) { _Model = semanticModel; _Options = options; } - + #if DEBUG - public override SyntaxNode? Visit(SyntaxNode? node) - { - if(node != null) + public override SyntaxNode? Visit(SyntaxNode? node) + { + if (node != null) Log.InfoLine($"kind: {node.Kind()} \n\t{node.ToString()}"); - - return base.Visit(node); + + return base.Visit(node); } #endif - + public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) { - if(node.Expression is IdentifierNameSyntax identifier) - VisitIdentifier(identifier); - - VisitIdentifier((IdentifierNameSyntax)node.Name); - + if (node.Expression is IdentifierNameSyntax || + node.Expression is GenericNameSyntax) + VisitSimpleName((SimpleNameSyntax)node.Expression); + + if (node.Name is IdentifierNameSyntax || + node.Name is GenericNameSyntax) + VisitSimpleName(node.Name); + node = (MemberAccessExpressionSyntax)base.VisitMemberAccessExpression(node)!; - + return node; } - - private void VisitIdentifier(IdentifierNameSyntax identifier) + + private void VisitSimpleName(SimpleNameSyntax identifier) { ISymbol? symbol = _Model.GetSymbolInfo(identifier).Symbol; - + if (symbol != null) { - CheckParentAttributes: - + CheckParentAttributes: + ImmutableArray _attributeData = symbol.GetAttributes(); for (int i = 0; i < _attributeData.Length; i++) { @@ -60,24 +63,24 @@ private void VisitIdentifier(IdentifierNameSyntax identifier) if (_attributeData[i].AttributeClass!.Name == nameof(ValueAttribute)) { string _v = _attributeData[i].ConstructorArguments[0].Value.ToString(); - + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_v).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); //return node; return; } - + if (_attributeData[i].AttributeClass!.Name == nameof(ToAttribute)) { ToAttribute _toAttr = new(_attributeData[i].ConstructorArguments[0].Value.ToString()); - + //TODO! //how? //if (_toAttr.To == ToAttribute.NoneWithLeadingDotRemoved) // JSSB.Remove(JSSB.Length - 1, 1); - + //if (_toAttr.To == ToAttribute.NoneWithTrailingDotRemoved) // _IgnoreTailingDot = true; - + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_toAttr.Convert(identifier.Identifier.Text)).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); //return node; return; @@ -90,36 +93,36 @@ private void VisitIdentifier(IdentifierNameSyntax identifier) goto CheckParentAttributes; } } - + symbol = _Model.GetSymbolInfo(identifier).Symbol; - + if (_Options.CustomCSNamesToJS.TryGetValue(identifier.Identifier.Text, out string? _value)) { ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_value)); //return node; return; } - - if(BuiltInTypesGenerics(identifier, symbol, out string str)) + + if (BuiltInTypesGenerics(identifier, symbol, out string str)) { - if(str == string.Empty) + if (str == string.Empty) { Log.ErrorLine("str == string.Empty!"); //return node; return; } - + ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(str).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); //return node; return; } } - - private bool BuiltInTypesGenerics(SimpleNameSyntax node, ISymbol? symbol, out string str) + + private bool BuiltInTypesGenerics(SimpleNameSyntax node, ISymbol? symbol, out string str) { str = string.Empty; - - if (symbol == null) + + if (symbol == null) { Log.WarningLine($"node: \"{node}\", symbol is null. USE \"CustomCSNamesToJS\"!"); return false; @@ -130,27 +133,27 @@ private bool BuiltInTypesGenerics(SimpleNameSyntax node, ISymbol? symbol, out st Log.WarningLine($"node: \"{node}\", symbol is \"dynamic\"."); return false; } - + ISymbol typeSymbol = symbol; - + if (typeSymbol.Kind != SymbolKind.NamedType) { typeSymbol = symbol.ContainingSymbol; - if(typeSymbol == null) + if (typeSymbol == null) { Log.WarningLine($"node: \"{node}\", typeSymbol is null. USE \"CustomCSNamesToJS\"!"); return false; } - + if (typeSymbol.Kind != SymbolKind.NamedType) { Log.WarningLine($"node: \"{node}\", typeSymbol is \"{typeSymbol.Kind}\". USE \"CustomCSNamesToJS\"!"); return false; } } - + string typeName = typeSymbol.Name; - + string? jsStr = _NETAPI.ReturnJSString(typeName); if (jsStr == null) { diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index 67fe8491..22b7853b 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -8,103 +8,138 @@ namespace CSharpToJavaScript; internal class WithoutSemanticRewriter : CSharpSyntaxRewriter { private readonly CSTOJSOptions _Options; - - public WithoutSemanticRewriter(CSTOJSOptions options) + + public WithoutSemanticRewriter(CSTOJSOptions options) { _Options = options; } - + #if DEBUG public override SyntaxNode? Visit(SyntaxNode? node) { - if(node != null) + if (node != null) + { Log.InfoLine($"kind: {node.Kind()} \n\t{node.ToString()}"); - - return base.Visit(node); + return base.Visit(node); + } + else + return null; } #endif - + public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) { node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node)!; - - if(node.Modifiers.Count >= 1) + + if (node.Modifiers.Count >= 1) { node = node.ReplaceToken(node.Keyword, node.Keyword.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); - node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } - + return node; } - + public override SyntaxNode? VisitConstructorDeclaration(ConstructorDeclarationSyntax node) { node = (ConstructorDeclarationSyntax)base.VisitConstructorDeclaration(node)!; - + //TODO! Static constructor! node = node.ReplaceToken(node.Identifier, SyntaxFactory.Identifier("constructor")); - - if(node.Modifiers.Count >= 1) + + if (node.Modifiers.Count >= 1) { - node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); - node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } - + return node; } - - public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) + + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) { node = (FieldDeclarationSyntax)base.VisitFieldDeclaration(node)!; - + //TODO! Static field! - if(node.Modifiers.Count >= 1) + if (node.Modifiers.Count >= 1) { node = node.ReplaceNode(node.Declaration.Type, node.Declaration.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); - node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } + + node = node.WithDeclaration(node.Declaration.WithType(SyntaxFactory.IdentifierName("")).WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia())); + return node; } public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node) { node = (MethodDeclarationSyntax)base.VisitMethodDeclaration(node)!; - + node = node.ReplaceNode(node.ReturnType, SyntaxFactory.ParseTypeName("")); - + //TODO! Static method! - if(node.Modifiers.Count >= 1) + if (node.Modifiers.Count >= 1) { - node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); - node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } - + return node; } - - public override SyntaxNode? VisitVariableDeclaration(VariableDeclarationSyntax node) + + public override SyntaxNode? VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) { - if(_Options.UseVarOverLet) - node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("var").WithLeadingTrivia(node.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Type.GetTrailingTrivia())); + if (_Options.UseVarOverLet) + node = node.WithDeclaration(node.Declaration.ReplaceNode(node.Declaration.Type, SyntaxFactory.IdentifierName("var").WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Declaration.Type.GetTrailingTrivia()))); else - node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("let").WithLeadingTrivia(node.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Type.GetTrailingTrivia())); - - node = (VariableDeclarationSyntax)base.VisitVariableDeclaration(node)!; - + node = node.WithDeclaration(node.Declaration.ReplaceNode(node.Declaration.Type, SyntaxFactory.IdentifierName("let").WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Declaration.Type.GetTrailingTrivia()))); + + node = (LocalDeclarationStatementSyntax)base.VisitLocalDeclarationStatement(node)!; + return node; } - + public override SyntaxNode? VisitParameter(ParameterSyntax node) { return SyntaxFactory.Parameter(node.Identifier); } - - public override SyntaxNode? VisitAttributeList(AttributeListSyntax node) - { - return null; - } + public override SyntaxNode? VisitCastExpression(CastExpressionSyntax node) + { + return node.Expression; + } + public override SyntaxNode? VisitParenthesizedExpression(ParenthesizedExpressionSyntax node) + { + if (node.Expression.IsKind(SyntaxKind.AsExpression) || node.Expression.IsKind( SyntaxKind.CastExpression)) + { + node = (ParenthesizedExpressionSyntax)base.VisitParenthesizedExpression(node)!; + return node.Expression; + } + else + { + node = (ParenthesizedExpressionSyntax)base.VisitParenthesizedExpression(node)!; + return node; + } + } + public override SyntaxNode? VisitBinaryExpression(BinaryExpressionSyntax node) + { + node = (BinaryExpressionSyntax)base.VisitBinaryExpression(node)!; + + if (node.OperatorToken.IsKind(SyntaxKind.AsKeyword)) + return node.Left.WithoutTrailingTrivia(); + + return node; + } + public override SyntaxNode? VisitGenericName(GenericNameSyntax node) + { + return SyntaxFactory.IdentifierName(node.Identifier); + } + public override SyntaxNode? VisitAttributeList(AttributeListSyntax node) + { + return null; + } public override SyntaxNode? VisitTypeParameterList(TypeParameterListSyntax node) { return null; } - + } From 1a9e0c42b417a0c5cce1a6b15217016314a7f998 Mon Sep 17 00:00:00 2001 From: TiLied Date: Wed, 25 Mar 2026 22:03:33 +0300 Subject: [PATCH 3/8] p3. --- .../Walker/WithoutSemanticRewriter.cs | 91 ++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index 22b7853b..e69f0e77 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -37,6 +37,76 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } + for(int i = 0; i < node.Members.Count; i++) + { + //properties need to be handled in VisitClassDeclaration + if(node.Members[i].IsKind(SyntaxKind.PropertyDeclaration)) + { + PropertyDeclarationSyntax _prop = (PropertyDeclarationSyntax)node.Members[i]; + + if(_prop.AccessorList != null) + { + //TODO! + //somehow without ToString. + string _getSetStr = _prop.AccessorList.ToString().Trim().Replace(" ", ""); + if (_getSetStr == "{get;set;}" || _getSetStr == "{get;}") + { + string _fieldIdentifier = $"#_{_prop.Identifier}_"; + + FieldDeclarationSyntax _field = SyntaxFactory.FieldDeclaration( + SyntaxFactory.VariableDeclaration(SyntaxFactory.IdentifierName(""), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator( + SyntaxFactory.Identifier(_fieldIdentifier)) + .WithInitializer(_prop.Initializer)))) + .WithLeadingTrivia(_prop.GetLeadingTrivia()) + .WithTrailingTrivia(_prop.GetTrailingTrivia()); + + node = node.ReplaceNode(node.Members[i], _field); + + if(_getSetStr.Contains("get;")) + { + MethodDeclarationSyntax _getM = SyntaxFactory.MethodDeclaration( + SyntaxFactory.IdentifierName("get "), _prop.Identifier.WithoutTrivia()) + .WithBody( + SyntaxFactory.Block( + SyntaxFactory.ReturnStatement( + SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("this"), SyntaxFactory.IdentifierName(_fieldIdentifier)) + ).NormalizeWhitespace())) + .WithLeadingTrivia(_prop.GetLeadingTrivia()) + .WithTrailingTrivia(_prop.GetTrailingTrivia()); + + //TODO! + //Insert after field! + node = node.AddMembers(_getM); + } + if(_getSetStr.Contains("set;")) + { + MethodDeclarationSyntax _setM = SyntaxFactory.MethodDeclaration( + SyntaxFactory.IdentifierName("set "), _prop.Identifier.WithoutTrivia()) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("value"))))) + .WithBody( + SyntaxFactory.Block(SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("this"), SyntaxFactory.IdentifierName(_fieldIdentifier)), + SyntaxFactory.IdentifierName("value")).NormalizeWhitespace()))) + .WithLeadingTrivia(_prop.GetLeadingTrivia()) + .WithTrailingTrivia(_prop.GetTrailingTrivia()); + + //TODO! + //Insert after field! + node = node.AddMembers(_setM); + } + } + } + } + } + return node; } @@ -75,8 +145,9 @@ public WithoutSemanticRewriter(CSTOJSOptions options) { node = (MethodDeclarationSyntax)base.VisitMethodDeclaration(node)!; + node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.ReturnType.GetLeadingTrivia())); node = node.ReplaceNode(node.ReturnType, SyntaxFactory.ParseTypeName("")); - + //TODO! Static method! if (node.Modifiers.Count >= 1) { @@ -86,7 +157,23 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return node; } - + + public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) + { + node = (PropertyDeclarationSyntax)base.VisitPropertyDeclaration(node)!; + + //TODO! Static property! + if (node.Modifiers.Count >= 1) + { + node = node.ReplaceNode(node.Type, node.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("").WithLeadingTrivia(node.Type.GetLeadingTrivia())); + + return node; + } + public override SyntaxNode? VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) { if (_Options.UseVarOverLet) From e51731f8363c72c4d616fd77847d17a00a6e34e6 Mon Sep 17 00:00:00 2001 From: TiLied Date: Tue, 31 Mar 2026 23:12:13 +0300 Subject: [PATCH 4/8] p4. --- CSharpToJavaScript.csproj | 2 +- CSharpToJavaScript/Walker/Walker2.cs | 6 +- .../Walker/WithSemanticRewriter.cs | 82 ++++++++++++++++--- .../Walker/WithoutSemanticRewriter.cs | 2 +- 4 files changed, 77 insertions(+), 15 deletions(-) diff --git a/CSharpToJavaScript.csproj b/CSharpToJavaScript.csproj index 5b7b016d..d5572e43 100644 --- a/CSharpToJavaScript.csproj +++ b/CSharpToJavaScript.csproj @@ -6,7 +6,7 @@ net10.0 disable enable - True + False TiLied A library for translating/converting cs into js, using Roslyn. https://tilied.github.io/CSTOJS_Pages/ diff --git a/CSharpToJavaScript/Walker/Walker2.cs b/CSharpToJavaScript/Walker/Walker2.cs index ca3d7560..cdfd0858 100644 --- a/CSharpToJavaScript/Walker/Walker2.cs +++ b/CSharpToJavaScript/Walker/Walker2.cs @@ -44,12 +44,16 @@ public Walker2(FileData file) SyntaxNode newRoot1 = withSemanticRewriter.Visit(root); if (root != newRoot1) root = newRoot1; + /* root = root.ReplaceTokens(withSemanticRewriter.ReplaceTokens.Keys, (o, r) => { return withSemanticRewriter.ReplaceTokens[o]; + });*/ + root = root.ReplaceNodes(withSemanticRewriter.ReplaceNodes.Keys, (o, r) => + { + return withSemanticRewriter.ReplaceNodes[o]; }); - if (file.OptionsForFile.Debug) file.Debug_WithSemanticRewriter = root.ToFullString(); diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs index a93a4c5e..38a921ba 100644 --- a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -13,9 +13,11 @@ internal class WithSemanticRewriter : CSharpSyntaxRewriter private readonly SemanticModel _Model; private readonly CSTOJSOptions _Options; - - public Dictionary ReplaceTokens = new(); - + + private ITypeSymbol? _CurrentClassSymbol = null; + + public Dictionary ReplaceNodes = new(); + public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) { _Model = semanticModel; @@ -31,7 +33,16 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return base.Visit(node); } #endif - + + public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) + { + _CurrentClassSymbol = _Model.GetDeclaredSymbol(node); + + node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node)!; + + return node; + } + public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) { if (node.Expression is IdentifierNameSyntax || @@ -46,7 +57,57 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } - + + public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node) + { + node = (InvocationExpressionSyntax)base.VisitInvocationExpression(node)!; + + if(node.Expression is MemberAccessExpressionSyntax expr) + { + ISymbol? symbol = _Model.GetSymbolInfo(node).Symbol; + + if (symbol != null && !symbol.IsStatic) + { + if (((symbol.Kind == SymbolKind.Method && + ((IMethodSymbol)symbol).MethodKind == MethodKind.Ordinary)) || + symbol.Kind == SymbolKind.Property || + symbol.Kind == SymbolKind.Field) + { + ITypeSymbol? _base = _CurrentClassSymbol; + + string? _type = symbol.ContainingType?.ToString(); + + if (_type != null) + { + while (_base != null) + { + if (_type.EndsWith(_base.ToString())) + { + MemberAccessExpressionSyntax _member = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + expr.Expression as IdentifierNameSyntax), + expr.Name); + + ReplaceNodes.Add(node.Expression, _member); + + return node; + } + _base = _base.BaseType; + } + } + } + } + } + return node; + } + + + + private void VisitSimpleName(SimpleNameSyntax identifier) { ISymbol? symbol = _Model.GetSymbolInfo(identifier).Symbol; @@ -64,7 +125,7 @@ private void VisitSimpleName(SimpleNameSyntax identifier) { string _v = _attributeData[i].ConstructorArguments[0].Value.ToString(); - ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_v).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); + ReplaceNodes.Add(identifier, SyntaxFactory.IdentifierName(_v).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); //return node; return; } @@ -81,7 +142,7 @@ private void VisitSimpleName(SimpleNameSyntax identifier) //if (_toAttr.To == ToAttribute.NoneWithTrailingDotRemoved) // _IgnoreTailingDot = true; - ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_toAttr.Convert(identifier.Identifier.Text)).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); + ReplaceNodes.Add(identifier, SyntaxFactory.IdentifierName(_toAttr.Convert(identifier.Identifier.Text)).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); //return node; return; } @@ -98,8 +159,7 @@ private void VisitSimpleName(SimpleNameSyntax identifier) if (_Options.CustomCSNamesToJS.TryGetValue(identifier.Identifier.Text, out string? _value)) { - ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(_value)); - //return node; + ReplaceNodes.Add(identifier, SyntaxFactory.IdentifierName(_value)); return; } @@ -108,12 +168,10 @@ private void VisitSimpleName(SimpleNameSyntax identifier) if (str == string.Empty) { Log.ErrorLine("str == string.Empty!"); - //return node; return; } - ReplaceTokens.Add(identifier.Identifier, SyntaxFactory.Identifier(str).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); - //return node; + ReplaceNodes.Add(identifier, SyntaxFactory.IdentifierName(str).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); return; } } diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index e69f0e77..075216c7 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -115,7 +115,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = (ConstructorDeclarationSyntax)base.VisitConstructorDeclaration(node)!; //TODO! Static constructor! - node = node.ReplaceToken(node.Identifier, SyntaxFactory.Identifier("constructor")); + node = node.ReplaceToken(node.Identifier, SyntaxFactory.Identifier("constructor").WithLeadingTrivia(node.Identifier.LeadingTrivia)); if (node.Modifiers.Count >= 1) { From e6444cdfdc559a055666a9fec227129839586d32 Mon Sep 17 00:00:00 2001 From: TiLied Date: Wed, 1 Apr 2026 22:13:02 +0300 Subject: [PATCH 5/8] p5. Total test:424/26 failed. --- CSharpToJavaScript/Walker/Walker2.cs | 6 +- .../Walker/WithSemanticRewriter.cs | 166 ++++++++++++------ .../Walker/WithoutSemanticRewriter.cs | 96 +++++++--- 3 files changed, 185 insertions(+), 83 deletions(-) diff --git a/CSharpToJavaScript/Walker/Walker2.cs b/CSharpToJavaScript/Walker/Walker2.cs index cdfd0858..2e289758 100644 --- a/CSharpToJavaScript/Walker/Walker2.cs +++ b/CSharpToJavaScript/Walker/Walker2.cs @@ -44,11 +44,7 @@ public Walker2(FileData file) SyntaxNode newRoot1 = withSemanticRewriter.Visit(root); if (root != newRoot1) root = newRoot1; - /* - root = root.ReplaceTokens(withSemanticRewriter.ReplaceTokens.Keys, (o, r) => - { - return withSemanticRewriter.ReplaceTokens[o]; - });*/ + root = root.ReplaceNodes(withSemanticRewriter.ReplaceNodes.Keys, (o, r) => { return withSemanticRewriter.ReplaceNodes[o]; diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs index 38a921ba..360fd09c 100644 --- a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -13,11 +13,11 @@ internal class WithSemanticRewriter : CSharpSyntaxRewriter private readonly SemanticModel _Model; private readonly CSTOJSOptions _Options; - - private ITypeSymbol? _CurrentClassSymbol = null; - + + private ITypeSymbol? _CurrentClassSymbol = null; + public Dictionary ReplaceNodes = new(); - + public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) { _Model = semanticModel; @@ -33,18 +33,41 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return base.Visit(node); } #endif - - public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) - { + + public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) + { _CurrentClassSymbol = _Model.GetDeclaredSymbol(node); - + node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node)!; - + return node; - } - + } + public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) { + MemberAccessExpressionSyntax? _expr = (MemberAccessExpressionSyntax)node; + + while (_expr != null) + { + if (_expr.Expression is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node).Symbol; + + if (symbol != null) + { + if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)_expr.Expression)) + return node; + else + break; + } + } + + if (_expr.Expression is MemberAccessExpressionSyntax) + _expr = (MemberAccessExpressionSyntax)_expr.Expression; + else + _expr = null; + } + if (node.Expression is IdentifierNameSyntax || node.Expression is GenericNameSyntax) VisitSimpleName((SimpleNameSyntax)node.Expression); @@ -57,57 +80,102 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } - + public override SyntaxNode? VisitArgument(ArgumentSyntax node) + { + if (node.Expression is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; + + if (symbol != null) + { + if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression)) + return node; + } + } + node = (ArgumentSyntax)base.VisitArgument(node)!; + + return node; + } + public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node) { + if (node.Expression is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; + + if (symbol != null) + { + if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression)) + return node; + } + } node = (InvocationExpressionSyntax)base.VisitInvocationExpression(node)!; - - if(node.Expression is MemberAccessExpressionSyntax expr) + + return node; + } + public override SyntaxNode? VisitBinaryExpression(BinaryExpressionSyntax node) + { + if (node.Left is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Left).Symbol; + + if (symbol != null) + { + TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Left); + } + } + if (node.Right is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Right).Symbol; + + if (symbol != null) + { + TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Right); + } + } + node = (BinaryExpressionSyntax)base.VisitBinaryExpression(node)!; + + return node; + } + private bool TryReplaceIdentifierWithThis(ISymbol symbol, IdentifierNameSyntax identifier) + { + if (symbol != null && !symbol.IsStatic) { - ISymbol? symbol = _Model.GetSymbolInfo(node).Symbol; - - if (symbol != null && !symbol.IsStatic) + if (((symbol.Kind == SymbolKind.Method && + ((IMethodSymbol)symbol).MethodKind == MethodKind.Ordinary)) || + symbol.Kind == SymbolKind.Property || + symbol.Kind == SymbolKind.Field) { - if (((symbol.Kind == SymbolKind.Method && - ((IMethodSymbol)symbol).MethodKind == MethodKind.Ordinary)) || - symbol.Kind == SymbolKind.Property || - symbol.Kind == SymbolKind.Field) + ITypeSymbol? _base = _CurrentClassSymbol; + + while (_base != null) { - ITypeSymbol? _base = _CurrentClassSymbol; - - string? _type = symbol.ContainingType?.ToString(); - - if (_type != null) + ImmutableArray _members = _base.GetMembers(); + + for (int i = 0; i < _members.Length; i++) { - while (_base != null) + if (_members[i].Name == identifier.Identifier.Text) { - if (_type.EndsWith(_base.ToString())) - { - MemberAccessExpressionSyntax _member = SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.ThisExpression(), - expr.Expression as IdentifierNameSyntax), - expr.Name); - - ReplaceNodes.Add(node.Expression, _member); - - return node; - } - _base = _base.BaseType; + MemberAccessExpressionSyntax _member = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ThisExpression(), + (IdentifierNameSyntax)identifier.WithoutTrivia()) + .WithLeadingTrivia(identifier.GetLeadingTrivia()); + + ReplaceNodes.Add(identifier, _member); + + return true; } } + _base = _base.BaseType; } + } } - return node; + + return false; } - - - - + private void VisitSimpleName(SimpleNameSyntax identifier) { ISymbol? symbol = _Model.GetSymbolInfo(identifier).Symbol; @@ -175,7 +243,7 @@ private void VisitSimpleName(SimpleNameSyntax identifier) return; } } - + private bool BuiltInTypesGenerics(SimpleNameSyntax node, ISymbol? symbol, out string str) { str = string.Empty; diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index 075216c7..a1e3cfd5 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; namespace CSharpToJavaScript; @@ -37,14 +38,14 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } - for(int i = 0; i < node.Members.Count; i++) + for (int i = 0; i < node.Members.Count; i++) { //properties need to be handled in VisitClassDeclaration - if(node.Members[i].IsKind(SyntaxKind.PropertyDeclaration)) + if (node.Members[i].IsKind(SyntaxKind.PropertyDeclaration)) { PropertyDeclarationSyntax _prop = (PropertyDeclarationSyntax)node.Members[i]; - - if(_prop.AccessorList != null) + + if (_prop.AccessorList != null) { //TODO! //somehow without ToString. @@ -52,7 +53,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) if (_getSetStr == "{get;set;}" || _getSetStr == "{get;}") { string _fieldIdentifier = $"#_{_prop.Identifier}_"; - + FieldDeclarationSyntax _field = SyntaxFactory.FieldDeclaration( SyntaxFactory.VariableDeclaration(SyntaxFactory.IdentifierName(""), SyntaxFactory.SingletonSeparatedList( @@ -61,10 +62,10 @@ public WithoutSemanticRewriter(CSTOJSOptions options) .WithInitializer(_prop.Initializer)))) .WithLeadingTrivia(_prop.GetLeadingTrivia()) .WithTrailingTrivia(_prop.GetTrailingTrivia()); - + node = node.ReplaceNode(node.Members[i], _field); - - if(_getSetStr.Contains("get;")) + + if (_getSetStr.Contains("get;")) { MethodDeclarationSyntax _getM = SyntaxFactory.MethodDeclaration( SyntaxFactory.IdentifierName("get "), _prop.Identifier.WithoutTrivia()) @@ -76,12 +77,12 @@ public WithoutSemanticRewriter(CSTOJSOptions options) ).NormalizeWhitespace())) .WithLeadingTrivia(_prop.GetLeadingTrivia()) .WithTrailingTrivia(_prop.GetTrailingTrivia()); - + //TODO! //Insert after field! node = node.AddMembers(_getM); } - if(_getSetStr.Contains("set;")) + if (_getSetStr.Contains("set;")) { MethodDeclarationSyntax _setM = SyntaxFactory.MethodDeclaration( SyntaxFactory.IdentifierName("set "), _prop.Identifier.WithoutTrivia()) @@ -97,7 +98,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) SyntaxFactory.IdentifierName("value")).NormalizeWhitespace()))) .WithLeadingTrivia(_prop.GetLeadingTrivia()) .WithTrailingTrivia(_prop.GetTrailingTrivia()); - + //TODO! //Insert after field! node = node.AddMembers(_setM); @@ -106,7 +107,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) } } } - + return node; } @@ -130,11 +131,21 @@ public WithoutSemanticRewriter(CSTOJSOptions options) { node = (FieldDeclarationSyntax)base.VisitFieldDeclaration(node)!; - //TODO! Static field! if (node.Modifiers.Count >= 1) { - node = node.ReplaceNode(node.Declaration.Type, node.Declaration.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); - node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + SyntaxToken? _static = CreateStaticKeyword(node.Modifiers); + if (_static == null) + { + node = node.ReplaceNode(node.Declaration.Type, node.Declaration.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + else + { + //Clear modifiers first. + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + //Add static modifier. + node = node.WithModifiers(SyntaxFactory.TokenList((SyntaxToken)_static)); + } } node = node.WithDeclaration(node.Declaration.WithType(SyntaxFactory.IdentifierName("")).WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia())); @@ -147,33 +158,43 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.ReturnType.GetLeadingTrivia())); node = node.ReplaceNode(node.ReturnType, SyntaxFactory.ParseTypeName("")); - - //TODO! Static method! + if (node.Modifiers.Count >= 1) { - node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia)); - node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + SyntaxToken? _static = CreateStaticKeyword(node.Modifiers); + if (_static == null) + { + node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia)); + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + } + else + { + //Clear modifiers first. + node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); + //Add static modifier. + node = node.WithModifiers(SyntaxFactory.TokenList((SyntaxToken)_static)); + } } return node; } - - public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) - { + + public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) + { node = (PropertyDeclarationSyntax)base.VisitPropertyDeclaration(node)!; - + //TODO! Static property! if (node.Modifiers.Count >= 1) { node = node.ReplaceNode(node.Type, node.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } - + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("").WithLeadingTrivia(node.Type.GetLeadingTrivia())); - + return node; - } - + } + public override SyntaxNode? VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) { if (_Options.UseVarOverLet) @@ -185,7 +206,10 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return node; } - + public override SyntaxNode? VisitBaseExpression(BaseExpressionSyntax node) + { + return SyntaxFactory.IdentifierName("super").WithLeadingTrivia(node.GetLeadingTrivia()); + } public override SyntaxNode? VisitParameter(ParameterSyntax node) { return SyntaxFactory.Parameter(node.Identifier); @@ -196,7 +220,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) } public override SyntaxNode? VisitParenthesizedExpression(ParenthesizedExpressionSyntax node) { - if (node.Expression.IsKind(SyntaxKind.AsExpression) || node.Expression.IsKind( SyntaxKind.CastExpression)) + if (node.Expression.IsKind(SyntaxKind.AsExpression) || node.Expression.IsKind(SyntaxKind.CastExpression)) { node = (ParenthesizedExpressionSyntax)base.VisitParenthesizedExpression(node)!; return node.Expression; @@ -229,4 +253,18 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return null; } + private SyntaxToken? CreateStaticKeyword(SyntaxTokenList modifiers) + { + SyntaxToken? _static = null; + for (int i = 0; i < modifiers.Count; i++) + { + if (modifiers[i].Text == "static") + { + _static = SyntaxFactory.Token(SyntaxKind.StaticKeyword).WithLeadingTrivia(modifiers[0].LeadingTrivia).WithTrailingTrivia(modifiers[0].TrailingTrivia); + break; + } + } + return _static; + } + } From cc5102efaab1c7648377739ed383dbc9a51ece80 Mon Sep 17 00:00:00 2001 From: TiLied Date: Fri, 3 Apr 2026 22:11:16 +0300 Subject: [PATCH 6/8] p6. 424/16 --- CSharpToJavaScript/APIs/JS/GlobalObject.cs | 12 ++++---- CSharpToJavaScript/Utils/Attributes.cs | 5 +++- .../Walker/StringBuilderWalker.cs | 29 ++++++++++++++++++ CSharpToJavaScript/Walker/Walker2.cs | 3 ++ .../Walker/WithSemanticRewriter.cs | 30 +++++++++++++++++-- .../Walker/WithoutSemanticRewriter.cs | 28 +++++++++-------- 6 files changed, 86 insertions(+), 21 deletions(-) diff --git a/CSharpToJavaScript/APIs/JS/GlobalObject.cs b/CSharpToJavaScript/APIs/JS/GlobalObject.cs index 426758cb..81e0b013 100644 --- a/CSharpToJavaScript/APIs/JS/GlobalObject.cs +++ b/CSharpToJavaScript/APIs/JS/GlobalObject.cs @@ -43,7 +43,7 @@ public static bool InequalsStrict(dynamic left, dynamic right) /// /// object.property /// true for all cases except when the property is an own non-configurable property, in which case false is returned in non-strict mode. - [Unary("delete ")] + [Unary("delete")] public static bool Delete(dynamic arg) { return true; @@ -57,7 +57,7 @@ public static bool Delete(dynamic arg) /// /// expression /// Undefined - [Unary("void ")] + [Unary("void")] public static Undefined Void(dynamic arg) { return new Undefined(); @@ -71,7 +71,7 @@ public static Undefined Void(dynamic arg) /// /// An expression representing the object or primitive whose type is to be returned. /// string - [Unary("typeof ")] + [Unary("typeof")] public static string TypeOf(object operand) { return string.Empty; @@ -84,7 +84,7 @@ public static string TypeOf(object operand) /// /// An expression representing the object or primitive whose type is to be returned. /// string - [Unary("typeof ")] + [Unary("typeof")] public static string TypeOf(Func operand) { return string.Empty; @@ -97,7 +97,7 @@ public static string TypeOf(Func operand) /// /// An expression representing the object or primitive whose type is to be returned. /// string - [GenericUnary("typeof ")] + [GenericUnary("typeof")] public static string TypeOf() { return string.Empty; @@ -112,7 +112,7 @@ public static string TypeOf() /// Constructor to test against. /// The object to test. /// bool - [GenericBinary(" instanceof ")] + [GenericBinary("instanceof")] public static bool InstanceOf(dynamic obj) { return true; diff --git a/CSharpToJavaScript/Utils/Attributes.cs b/CSharpToJavaScript/Utils/Attributes.cs index b7ad2ec2..88126dc6 100644 --- a/CSharpToJavaScript/Utils/Attributes.cs +++ b/CSharpToJavaScript/Utils/Attributes.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.CodeAnalysis; +using System; namespace CSharpToJavaScript.Utils; @@ -76,6 +77,7 @@ public string Convert(string str) [AttributeUsage(AttributeTargets.All)] public class BinaryAttribute : Attribute { + public static SyntaxAnnotation Annotation { get; } = new(nameof(BinaryAttribute)); public string Value { get; init; } public BinaryAttribute(string value) { @@ -100,6 +102,7 @@ public GenericBinaryAttribute(string value) [AttributeUsage(AttributeTargets.All)] public class UnaryAttribute : Attribute { + public static SyntaxAnnotation Annotation { get; } = new(nameof(UnaryAttribute)); public string Value { get; init; } public UnaryAttribute(string value) { diff --git a/CSharpToJavaScript/Walker/StringBuilderWalker.cs b/CSharpToJavaScript/Walker/StringBuilderWalker.cs index 18b55419..b90750f8 100644 --- a/CSharpToJavaScript/Walker/StringBuilderWalker.cs +++ b/CSharpToJavaScript/Walker/StringBuilderWalker.cs @@ -383,6 +383,35 @@ public override void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDecl } } + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + if (node.Expression.HasAnnotation(BinaryAttribute.Annotation)) + { + VisitArgument(node.ArgumentList.Arguments[0]); + + if (!node.ArgumentList.Arguments[0].HasTrailingTrivia) + JSSB.Append(' '); + + JSSB.Append(node.Expression.ToString()); + + VisitTrailingTrivia(node.ArgumentList.Arguments.GetSeparator(0)); + + VisitArgument(node.ArgumentList.Arguments[1]); + return; + } + if (node.Expression.HasAnnotation(UnaryAttribute.Annotation)) + { + JSSB.Append(node.Expression.ToString()); + + if (!node.Expression.HasTrailingTrivia) + JSSB.Append(' '); + + VisitArgument(node.ArgumentList.Arguments[0]); + return; + } + base.VisitInvocationExpression(node); + } + //Can't do in "WithoutSemanticRewriter". Throws an error: //An exception of type 'System.ArgumentException' occurred in Microsoft.CodeAnalysis.CSharp.dll but was not handled in user code: 'colonToken' //Code: diff --git a/CSharpToJavaScript/Walker/Walker2.cs b/CSharpToJavaScript/Walker/Walker2.cs index 2e289758..cbf39c46 100644 --- a/CSharpToJavaScript/Walker/Walker2.cs +++ b/CSharpToJavaScript/Walker/Walker2.cs @@ -60,6 +60,9 @@ public Walker2(FileData file) if (file.OptionsForFile.Debug) file.Debug_WithoutSemanticRewriter = root.ToFullString(); + if (file.OptionsForFile.NormalizeWhitespace) + root = root.NormalizeWhitespace(); + stringBuilderWalker.JSSB.Append(file.OptionsForFile.AddSBAtTheTop); stringBuilderWalker.Visit(root); stringBuilderWalker.JSSB.Append(file.OptionsForFile.AddSBAtTheBottom); diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs index 360fd09c..fba11db5 100644 --- a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -99,6 +99,8 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node) { + node = (InvocationExpressionSyntax)base.VisitInvocationExpression(node)!; + if (node.Expression is IdentifierNameSyntax) { ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; @@ -107,9 +109,34 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) { if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression)) return node; + + ImmutableArray _attributeData = symbol.GetAttributes(); + for (int i = 0; i < _attributeData.Length; i++) + { + if (_attributeData[i].AttributeClass != null) + { + if (_attributeData[i].AttributeClass!.Name == nameof(BinaryAttribute)) + { + ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) + .WithAdditionalAnnotations(BinaryAttribute.Annotation) + .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) + .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + + return node; + } + if (_attributeData[i].AttributeClass!.Name == nameof(UnaryAttribute)) + { + ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) + .WithAdditionalAnnotations(UnaryAttribute.Annotation) + .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) + .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + + return node; + } + } + } } } - node = (InvocationExpressionSyntax)base.VisitInvocationExpression(node)!; return node; } @@ -194,7 +221,6 @@ private void VisitSimpleName(SimpleNameSyntax identifier) string _v = _attributeData[i].ConstructorArguments[0].Value.ToString(); ReplaceNodes.Add(identifier, SyntaxFactory.IdentifierName(_v).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); - //return node; return; } diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index a1e3cfd5..d953c288 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -2,7 +2,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Generic; namespace CSharpToJavaScript; @@ -52,6 +51,8 @@ public WithoutSemanticRewriter(CSTOJSOptions options) string _getSetStr = _prop.AccessorList.ToString().Trim().Replace(" ", ""); if (_getSetStr == "{get;set;}" || _getSetStr == "{get;}") { + int _indexToInsert = i + 1; + string _fieldIdentifier = $"#_{_prop.Identifier}_"; FieldDeclarationSyntax _field = SyntaxFactory.FieldDeclaration( @@ -78,11 +79,9 @@ public WithoutSemanticRewriter(CSTOJSOptions options) .WithLeadingTrivia(_prop.GetLeadingTrivia()) .WithTrailingTrivia(_prop.GetTrailingTrivia()); - //TODO! - //Insert after field! - node = node.AddMembers(_getM); + node = node.WithMembers(node.Members.Insert(_indexToInsert++, _getM)); } - if (_getSetStr.Contains("set;")) + if (_getSetStr.Contains("get;") && _getSetStr.Contains("set;")) { MethodDeclarationSyntax _setM = SyntaxFactory.MethodDeclaration( SyntaxFactory.IdentifierName("set "), _prop.Identifier.WithoutTrivia()) @@ -99,9 +98,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) .WithLeadingTrivia(_prop.GetLeadingTrivia()) .WithTrailingTrivia(_prop.GetTrailingTrivia()); - //TODO! - //Insert after field! - node = node.AddMembers(_setM); + node = node.WithMembers(node.Members.Insert(_indexToInsert++, _setM)); } } } @@ -133,7 +130,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) if (node.Modifiers.Count >= 1) { - SyntaxToken? _static = CreateStaticKeyword(node.Modifiers); + SyntaxToken? _static = CreateStaticModifier(node.Modifiers); if (_static == null) { node = node.ReplaceNode(node.Declaration.Type, node.Declaration.Type.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia).WithTrailingTrivia(node.Modifiers[0].TrailingTrivia)); @@ -161,7 +158,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) if (node.Modifiers.Count >= 1) { - SyntaxToken? _static = CreateStaticKeyword(node.Modifiers); + SyntaxToken? _static = CreateStaticModifier(node.Modifiers); if (_static == null) { node = node.ReplaceToken(node.Identifier, node.Identifier.WithLeadingTrivia(node.Modifiers[0].LeadingTrivia)); @@ -194,7 +191,14 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return node; } + public override SyntaxNode? VisitLocalFunctionStatement(LocalFunctionStatementSyntax node) + { + node = (LocalFunctionStatementSyntax)base.VisitLocalFunctionStatement(node)!; + node = node.ReplaceNode(node.ReturnType, SyntaxFactory.IdentifierName("function").WithLeadingTrivia(node.ReturnType.GetLeadingTrivia()).WithTrailingTrivia(node.ReturnType.GetTrailingTrivia())); + + return node; + } public override SyntaxNode? VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) { if (_Options.UseVarOverLet) @@ -223,7 +227,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) if (node.Expression.IsKind(SyntaxKind.AsExpression) || node.Expression.IsKind(SyntaxKind.CastExpression)) { node = (ParenthesizedExpressionSyntax)base.VisitParenthesizedExpression(node)!; - return node.Expression; + return node.Expression.WithLeadingTrivia(node.GetLeadingTrivia()); } else { @@ -253,7 +257,7 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return null; } - private SyntaxToken? CreateStaticKeyword(SyntaxTokenList modifiers) + private static SyntaxToken? CreateStaticModifier(SyntaxTokenList modifiers) { SyntaxToken? _static = null; for (int i = 0; i < modifiers.Count; i++) From 7657c368443622d785e5f3868d39a3716b5b7ca8 Mon Sep 17 00:00:00 2001 From: TiLied Date: Sat, 4 Apr 2026 19:26:11 +0300 Subject: [PATCH 7/8] p7. 424/6 --- .../Walker/StringBuilderWalker.cs | 66 ++++++- .../Walker/WithSemanticRewriter.cs | 174 +++++++++++++++--- .../Walker/WithoutSemanticRewriter.cs | 84 +++++++-- 3 files changed, 285 insertions(+), 39 deletions(-) diff --git a/CSharpToJavaScript/Walker/StringBuilderWalker.cs b/CSharpToJavaScript/Walker/StringBuilderWalker.cs index b90750f8..e23810f3 100644 --- a/CSharpToJavaScript/Walker/StringBuilderWalker.cs +++ b/CSharpToJavaScript/Walker/StringBuilderWalker.cs @@ -405,7 +405,7 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node) if (!node.Expression.HasTrailingTrivia) JSSB.Append(' '); - + VisitArgument(node.ArgumentList.Arguments[0]); return; } @@ -468,4 +468,68 @@ public override void VisitBaseList(BaseListSyntax node) } } } + + //Can't replace a token. Same as VisitBaseList + //see "tokenInList" + //https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.syntaxnodeextensions.replacetoken?view=roslyn-dotnet-4.13.0#microsoft-codeanalysis-syntaxnodeextensions-replacetoken-1(-0-microsoft-codeanalysis-syntaxtoken-system-collections-generic-ienumerable((microsoft-codeanalysis-syntaxtoken))) + //"The token to be replaced. This must be a direct element of a SyntaxTokenList (such as a modifier in a list of modifiers), and a descendant of the root node. If the token is not part of a SyntaxTokenList, an InvalidOperationException will be thrown." + //But it throws a 'System.ArgumentException', not an 'InvalidOperationException': + //An exception of type 'System.ArgumentException' occurred in Microsoft.CodeAnalysis.CSharp.dll but was not handled in user code: 'stringStartToken' + //Code: + /* + public override SyntaxNode? VisitInterpolatedStringExpression(InterpolatedStringExpressionSyntax node) + { + node = (InterpolatedStringExpressionSyntax)base.VisitInterpolatedStringExpression(node)!; + node = node.ReplaceToken(node.StringStartToken, SyntaxFactory.Token(SyntaxKind.SingleQuoteToken)); + node = node.ReplaceToken(node.StringEndToken, SyntaxFactory.Token(SyntaxKind.SingleQuoteToken)); + return node; + } + */ + public override void VisitInterpolatedStringExpression(InterpolatedStringExpressionSyntax node) + { + ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); + + for (int i = 0; i < nodesAndTokens.Count; i++) + { + SyntaxNode? asNode = nodesAndTokens[i].AsNode(); + + if (asNode != null) + { + SyntaxKind kind = asNode.Kind(); + + switch (kind) + { + case SyntaxKind.InterpolatedStringText: + VisitInterpolatedStringText((InterpolatedStringTextSyntax)asNode); + break; + case SyntaxKind.Interpolation: + { + JSSB.Append('$'); + VisitInterpolation((InterpolationSyntax)asNode); + break; + } + default: + Log.ErrorLine($"asNode : {kind}\n|{asNode.ToFullString()}|"); + break; + } + } + else + { + SyntaxToken asToken = nodesAndTokens[i].AsToken(); + SyntaxKind kind = asToken.Kind(); + + switch (kind) + { + case SyntaxKind.InterpolatedStringStartToken: + case SyntaxKind.InterpolatedStringEndToken: + JSSB.Append('`'); + break; + default: + Log.ErrorLine($"asToken : {kind}"); + break; + } + } + } + } + } diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs index fba11db5..58cfd173 100644 --- a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -45,38 +45,61 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) { + node = (MemberAccessExpressionSyntax)base.VisitMemberAccessExpression(node)!; + MemberAccessExpressionSyntax? _expr = (MemberAccessExpressionSyntax)node; - while (_expr != null) + if (_expr.Expression is IdentifierNameSyntax) { - if (_expr.Expression is IdentifierNameSyntax) - { - ISymbol? symbol = _Model.GetSymbolInfo(node).Symbol; + ISymbol? symbol = _Model.GetSymbolInfo(node).Symbol; - if (symbol != null) - { - if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)_expr.Expression)) - return node; - else - break; - } + if (symbol != null) + { + if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)_expr.Expression)) + goto Name; } - - if (_expr.Expression is MemberAccessExpressionSyntax) - _expr = (MemberAccessExpressionSyntax)_expr.Expression; - else - _expr = null; } if (node.Expression is IdentifierNameSyntax || node.Expression is GenericNameSyntax) VisitSimpleName((SimpleNameSyntax)node.Expression); - + Name: if (node.Name is IdentifierNameSyntax || node.Name is GenericNameSyntax) VisitSimpleName(node.Name); - node = (MemberAccessExpressionSyntax)base.VisitMemberAccessExpression(node)!; + return node; + } + public override SyntaxNode? VisitElementAccessExpression(ElementAccessExpressionSyntax node) + { + node = (ElementAccessExpressionSyntax)base.VisitElementAccessExpression(node)!; + + if (node.Expression is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; + + if (symbol != null) + { + if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression)) + return node; + } + } + + return node; + } + public override SyntaxNode? VisitInterpolation(InterpolationSyntax node) + { + if (node.Expression is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; + + if (symbol != null) + { + if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression)) + return node; + } + } + node = (InterpolationSyntax)base.VisitInterpolation(node)!; return node; } @@ -96,18 +119,27 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } + public override SyntaxNode? VisitTypeArgumentList(TypeArgumentListSyntax node) + { + if (node.Arguments[0] is IdentifierNameSyntax) + VisitSimpleName((SimpleNameSyntax)node.Arguments[0]); + + node = (TypeArgumentListSyntax)base.VisitTypeArgumentList(node)!; + return node; + } public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node) { node = (InvocationExpressionSyntax)base.VisitInvocationExpression(node)!; - - if (node.Expression is IdentifierNameSyntax) + + if (node.Expression is IdentifierNameSyntax || + node.Expression is GenericNameSyntax) { ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; if (symbol != null) { - if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression)) + if (TryReplaceIdentifierWithThis(symbol, (SimpleNameSyntax)node.Expression)) return node; ImmutableArray _attributeData = symbol.GetAttributes(); @@ -131,6 +163,45 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + return node; + } + if (_attributeData[i].AttributeClass!.Name == nameof(GenericUnaryAttribute)) + { + TypeSyntax _arg = ((GenericNameSyntax)node.Expression).TypeArgumentList.Arguments[0]; + + //Check if the generic is contained in ReplaceNodes. + //If yes, then use the replaced node. + //The same applies to GenericBinaryAttribute. + if (ReplaceNodes.ContainsKey(_arg)) + _arg = (TypeSyntax)ReplaceNodes[_arg]; + + //Replace GenericName with IdentifierName from an attribute. + ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(_attributeData[i].ConstructorArguments[0].Value.ToString())) + .WithAdditionalAnnotations(UnaryAttribute.Annotation) + .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) + .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + + ReplaceNodes.Add(node.ArgumentList, node.ArgumentList + .AddArguments(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(_arg.ToString())))); + + return node; + } + if (_attributeData[i].AttributeClass!.Name == nameof(GenericBinaryAttribute)) + { + TypeSyntax _arg = ((GenericNameSyntax)node.Expression).TypeArgumentList.Arguments[0]; + + if (ReplaceNodes.ContainsKey(_arg)) + _arg = (TypeSyntax)ReplaceNodes[_arg]; + + ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) + .WithAdditionalAnnotations(BinaryAttribute.Annotation) + .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) + .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + + ReplaceNodes.Add(node.ArgumentList, node.ArgumentList + .AddArguments(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(_arg.ToString()))) + .NormalizeWhitespace()); + return node; } } @@ -164,7 +235,58 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } - private bool TryReplaceIdentifierWithThis(ISymbol symbol, IdentifierNameSyntax identifier) + public override SyntaxNode? VisitAssignmentExpression(AssignmentExpressionSyntax node) + { + if (node.Left is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Left).Symbol; + + if (symbol != null) + { + TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Left); + } + } + if (node.Right is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Right).Symbol; + + if (symbol != null) + { + TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Right); + } + } + node = (AssignmentExpressionSyntax)base.VisitAssignmentExpression(node)!; + + return node; + } + public override SyntaxNode? VisitReturnStatement(ReturnStatementSyntax node) + { + if (node.Expression is IdentifierNameSyntax) + { + ISymbol? symbol = _Model.GetSymbolInfo(node.Expression).Symbol; + + if (symbol != null) + { + TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)node.Expression); + } + } + node = (ReturnStatementSyntax)base.VisitReturnStatement(node)!; + + return node; + } + public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) + { + node = (PropertyDeclarationSyntax)base.VisitPropertyDeclaration(node)!; + + if (node.Type is IdentifierNameSyntax || + node.Type is GenericNameSyntax) + VisitSimpleName((SimpleNameSyntax)node.Type); + + return node; + } + + + private bool TryReplaceIdentifierWithThis(ISymbol symbol, SimpleNameSyntax identifier) { if (symbol != null && !symbol.IsStatic) { @@ -185,9 +307,9 @@ private bool TryReplaceIdentifierWithThis(ISymbol symbol, IdentifierNameSyntax i { MemberAccessExpressionSyntax _member = SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.ThisExpression(), - (IdentifierNameSyntax)identifier.WithoutTrivia()) - .WithLeadingTrivia(identifier.GetLeadingTrivia()); + SyntaxFactory.ThisExpression(), identifier.WithoutTrivia()) + .WithLeadingTrivia(identifier.GetLeadingTrivia()) + .WithTrailingTrivia(identifier.GetTrailingTrivia()); ReplaceNodes.Add(identifier, _member); @@ -202,7 +324,6 @@ private bool TryReplaceIdentifierWithThis(ISymbol symbol, IdentifierNameSyntax i return false; } - private void VisitSimpleName(SimpleNameSyntax identifier) { ISymbol? symbol = _Model.GetSymbolInfo(identifier).Symbol; @@ -269,7 +390,6 @@ private void VisitSimpleName(SimpleNameSyntax identifier) return; } } - private bool BuiltInTypesGenerics(SimpleNameSyntax node, ISymbol? symbol, out string str) { str = string.Empty; diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index d953c288..3f8ab86a 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -46,13 +46,13 @@ public WithoutSemanticRewriter(CSTOJSOptions options) if (_prop.AccessorList != null) { + int _indexToInsert = i + 1; + //TODO! //somehow without ToString. string _getSetStr = _prop.AccessorList.ToString().Trim().Replace(" ", ""); if (_getSetStr == "{get;set;}" || _getSetStr == "{get;}") { - int _indexToInsert = i + 1; - string _fieldIdentifier = $"#_{_prop.Identifier}_"; FieldDeclarationSyntax _field = SyntaxFactory.FieldDeclaration( @@ -101,6 +101,42 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = node.WithMembers(node.Members.Insert(_indexToInsert++, _setM)); } } + else + { + for (int j = 0; j < _prop.AccessorList.Accessors.Count; j++) + { + if (_prop.AccessorList.Accessors[j].Keyword.Text == "get") + { + MethodDeclarationSyntax _getM = SyntaxFactory.MethodDeclaration( + SyntaxFactory.IdentifierName("get ") + .WithLeadingTrivia(_prop.GetLeadingTrivia()), _prop.Identifier.WithoutTrivia()) + .WithTrailingTrivia(_prop.AccessorList.Accessors[j].Keyword.TrailingTrivia) + .WithBody(_prop.AccessorList.Accessors[j].Body) + .WithLeadingTrivia(_prop.GetLeadingTrivia()) + .WithTrailingTrivia(_prop.AccessorList.Accessors[j].GetTrailingTrivia()); + + node = node.WithMembers(node.Members.Insert(_indexToInsert++, _getM)); + } + if (_prop.AccessorList.Accessors[j].Keyword.Text == "set") + { + MethodDeclarationSyntax _setM = SyntaxFactory.MethodDeclaration( + SyntaxFactory.IdentifierName("set ") + .WithLeadingTrivia(_prop.GetLeadingTrivia()), _prop.Identifier.WithoutTrivia()) + .WithParameterList( + SyntaxFactory.ParameterList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Parameter(SyntaxFactory.Identifier("value"))))) + .WithTrailingTrivia(_prop.AccessorList.Accessors[j].Keyword.TrailingTrivia) + .WithBody(_prop.AccessorList.Accessors[j].Body) + .WithLeadingTrivia(_prop.GetLeadingTrivia()) + .WithTrailingTrivia(_prop.AccessorList.Accessors[j].GetTrailingTrivia()); + + node = node.WithMembers(node.Members.Insert(_indexToInsert++, _setM)); + } + } + + node = node.RemoveNode(node.Members[i], SyntaxRemoveOptions.KeepNoTrivia); + } } } } @@ -123,7 +159,6 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return node; } - public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) { node = (FieldDeclarationSyntax)base.VisitFieldDeclaration(node)!; @@ -175,7 +210,6 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return node; } - public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) { node = (PropertyDeclarationSyntax)base.VisitPropertyDeclaration(node)!; @@ -187,6 +221,16 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = node.ReplaceTokens(node.Modifiers, (o, r) => SyntaxFactory.Token(SyntaxKind.None)); } + if (node.Initializer != null && + node.Initializer is EqualsValueClauseSyntax evc) + { + if (evc.Value is ImplicitObjectCreationExpressionSyntax ioce) + { + ObjectCreationExpressionSyntax _obj = SyntaxFactory.ObjectCreationExpression(node.Type, ioce.ArgumentList, ioce.Initializer).NormalizeWhitespace(); + node = node.ReplaceNode(ioce, _obj); + } + } + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("").WithLeadingTrivia(node.Type.GetLeadingTrivia())); return node; @@ -196,17 +240,23 @@ public WithoutSemanticRewriter(CSTOJSOptions options) node = (LocalFunctionStatementSyntax)base.VisitLocalFunctionStatement(node)!; node = node.ReplaceNode(node.ReturnType, SyntaxFactory.IdentifierName("function").WithLeadingTrivia(node.ReturnType.GetLeadingTrivia()).WithTrailingTrivia(node.ReturnType.GetTrailingTrivia())); - + return node; } - public override SyntaxNode? VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) + + public override SyntaxNode? VisitVariableDeclaration(VariableDeclarationSyntax node) { - if (_Options.UseVarOverLet) - node = node.WithDeclaration(node.Declaration.ReplaceNode(node.Declaration.Type, SyntaxFactory.IdentifierName("var").WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Declaration.Type.GetTrailingTrivia()))); - else - node = node.WithDeclaration(node.Declaration.ReplaceNode(node.Declaration.Type, SyntaxFactory.IdentifierName("let").WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Declaration.Type.GetTrailingTrivia()))); + if (node.Parent is ForStatementSyntax || + node.Parent is LocalDeclarationStatementSyntax) + { + if (_Options.UseVarOverLet) + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("var").WithLeadingTrivia(node.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Type.GetTrailingTrivia())); + else + node = node.ReplaceNode(node.Type, SyntaxFactory.IdentifierName("let").WithLeadingTrivia(node.Type.GetLeadingTrivia()).WithTrailingTrivia(node.Type.GetTrailingTrivia())); + + } - node = (LocalDeclarationStatementSyntax)base.VisitLocalDeclarationStatement(node)!; + node = (VariableDeclarationSyntax)base.VisitVariableDeclaration(node)!; return node; } @@ -256,6 +306,18 @@ public WithoutSemanticRewriter(CSTOJSOptions options) { return null; } + public override SyntaxNode? VisitUsingDirective(UsingDirectiveSyntax node) + { + return null; + } + public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node) + { + return null; + } + public override SyntaxNode? VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) + { + return null; + } private static SyntaxToken? CreateStaticModifier(SyntaxTokenList modifiers) { From 71c5463178facf10889d9ab5a4e5da8d23337be9 Mon Sep 17 00:00:00 2001 From: TiLied Date: Sun, 5 Apr 2026 20:33:09 +0300 Subject: [PATCH 8/8] p8. 424/2 --- CSharpToJavaScript/Utils/Attributes.cs | 2 +- .../Walker/StringBuilderWalker.cs | 109 ++++++++++-- .../Walker/WithSemanticRewriter.cs | 160 +++++++++++++----- .../Walker/WithoutSemanticRewriter.cs | 12 ++ 4 files changed, 222 insertions(+), 61 deletions(-) diff --git a/CSharpToJavaScript/Utils/Attributes.cs b/CSharpToJavaScript/Utils/Attributes.cs index 88126dc6..1182a8d2 100644 --- a/CSharpToJavaScript/Utils/Attributes.cs +++ b/CSharpToJavaScript/Utils/Attributes.cs @@ -128,7 +128,7 @@ public GenericUnaryAttribute(string value) [AttributeUsage(AttributeTargets.All)] public class ToObjectAttribute : Attribute { - + public static SyntaxAnnotation Annotation { get; } = new(nameof(ToObjectAttribute)); } [AttributeUsage(AttributeTargets.Method)] diff --git a/CSharpToJavaScript/Walker/StringBuilderWalker.cs b/CSharpToJavaScript/Walker/StringBuilderWalker.cs index e23810f3..d1db58c9 100644 --- a/CSharpToJavaScript/Walker/StringBuilderWalker.cs +++ b/CSharpToJavaScript/Walker/StringBuilderWalker.cs @@ -174,24 +174,7 @@ public override void VisitToken(SyntaxToken token) case SyntaxKind.TypeOfKeyword: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsToken: - { - VisitLeadingTrivia(token); - - JSSB.Append(token.Text); - - VisitTrailingTrivia(token); - return; - } case SyntaxKind.EndOfFileToken: - { - VisitLeadingTrivia(token); - - JSSB.Append(token.Text); - - VisitTrailingTrivia(token); - - return; - } case SyntaxKind.OpenBraceToken: { VisitLeadingTrivia(token); @@ -532,4 +515,96 @@ public override void VisitInterpolatedStringExpression(InterpolatedStringExpress } } + public override void VisitInitializerExpression(InitializerExpressionSyntax node) + { + ChildSyntaxList nodesAndTokens = node.ChildNodesAndTokens(); + + for (int i = 0; i < nodesAndTokens.Count; i++) + { + SyntaxNode? asNode = nodesAndTokens[i].AsNode(); + + if (asNode != null) + { + SyntaxKind kind = asNode.Kind(); + + switch (kind) + { + case SyntaxKind.IdentifierName: + VisitIdentifierName((IdentifierNameSyntax)asNode); + break; + case SyntaxKind.ArgListExpression: + case SyntaxKind.NumericLiteralExpression: + case SyntaxKind.StringLiteralExpression: + case SyntaxKind.Utf8StringLiteralExpression: + case SyntaxKind.CharacterLiteralExpression: + case SyntaxKind.TrueLiteralExpression: + case SyntaxKind.FalseLiteralExpression: + case SyntaxKind.NullLiteralExpression: + case SyntaxKind.DefaultLiteralExpression: + VisitLiteralExpression((LiteralExpressionSyntax)asNode); + break; + case SyntaxKind.ObjectInitializerExpression: + case SyntaxKind.CollectionInitializerExpression: + case SyntaxKind.ArrayInitializerExpression: + case SyntaxKind.ComplexElementInitializerExpression: + case SyntaxKind.WithInitializerExpression: + VisitInitializerExpression((InitializerExpressionSyntax)asNode); + break; + case SyntaxKind.ObjectCreationExpression: + VisitObjectCreationExpression((ObjectCreationExpressionSyntax)asNode); + break; + case SyntaxKind.SimpleAssignmentExpression: + case SyntaxKind.AddAssignmentExpression: + case SyntaxKind.SubtractAssignmentExpression: + case SyntaxKind.MultiplyAssignmentExpression: + case SyntaxKind.DivideAssignmentExpression: + case SyntaxKind.ModuloAssignmentExpression: + case SyntaxKind.AndAssignmentExpression: + case SyntaxKind.ExclusiveOrAssignmentExpression: + case SyntaxKind.OrAssignmentExpression: + case SyntaxKind.LeftShiftAssignmentExpression: + case SyntaxKind.RightShiftAssignmentExpression: + case SyntaxKind.UnsignedRightShiftAssignmentExpression: + case SyntaxKind.CoalesceAssignmentExpression: + { + AssignmentExpressionSyntax _expr = (AssignmentExpressionSyntax)asNode; + Visit(_expr.Left); + + VisitLeadingTrivia(_expr.OperatorToken); + JSSB.Append(':'); + VisitTrailingTrivia(_expr.OperatorToken); + + Visit(_expr.Right); + break; + } + default: + Log.ErrorLine($"asNode : {kind}\n|{asNode.ToFullString()}|"); + break; + } + } + else + { + SyntaxToken asToken = nodesAndTokens[i].AsToken(); + SyntaxKind kind = asToken.Kind(); + + switch (kind) + { + case SyntaxKind.OpenBraceToken: + case SyntaxKind.CloseBraceToken: + { + VisitToken(asToken); + break; + } + case SyntaxKind.CommaToken: + VisitToken(asToken); + break; + default: + Log.ErrorLine($"asToken : {kind}"); + + break; + } + } + } + } + } diff --git a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs index 58cfd173..ae4fc467 100644 --- a/CSharpToJavaScript/Walker/WithSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithSemanticRewriter.cs @@ -1,3 +1,4 @@ +using CSharpToJavaScript.APIs.JS; using CSharpToJavaScript.Utils; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -16,6 +17,8 @@ internal class WithSemanticRewriter : CSharpSyntaxRewriter private ITypeSymbol? _CurrentClassSymbol = null; + private bool _NoneWithTrailingDotRemoved = false; + public Dictionary ReplaceNodes = new(); public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) @@ -42,11 +45,20 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } - public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) { node = (MemberAccessExpressionSyntax)base.VisitMemberAccessExpression(node)!; + if (_NoneWithTrailingDotRemoved) + { + SyntaxNode _node = node.Name; + if (ReplaceNodes.ContainsKey(_node)) + _node = ReplaceNodes[_node]; + + ReplaceNodes.Add(node, _node); + _NoneWithTrailingDotRemoved = false; + } + MemberAccessExpressionSyntax? _expr = (MemberAccessExpressionSyntax)node; if (_expr.Expression is IdentifierNameSyntax) @@ -56,18 +68,9 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) if (symbol != null) { if (TryReplaceIdentifierWithThis(symbol, (IdentifierNameSyntax)_expr.Expression)) - goto Name; + return node; } } - - if (node.Expression is IdentifierNameSyntax || - node.Expression is GenericNameSyntax) - VisitSimpleName((SimpleNameSyntax)node.Expression); - Name: - if (node.Name is IdentifierNameSyntax || - node.Name is GenericNameSyntax) - VisitSimpleName(node.Name); - return node; } public override SyntaxNode? VisitElementAccessExpression(ElementAccessExpressionSyntax node) @@ -119,15 +122,6 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } - public override SyntaxNode? VisitTypeArgumentList(TypeArgumentListSyntax node) - { - if (node.Arguments[0] is IdentifierNameSyntax) - VisitSimpleName((SimpleNameSyntax)node.Arguments[0]); - - node = (TypeArgumentListSyntax)base.VisitTypeArgumentList(node)!; - - return node; - } public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node) { node = (InvocationExpressionSyntax)base.VisitInvocationExpression(node)!; @@ -149,19 +143,29 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) { if (_attributeData[i].AttributeClass!.Name == nameof(BinaryAttribute)) { - ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) + IdentifierNameSyntax _identifier = SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) .WithAdditionalAnnotations(BinaryAttribute.Annotation) .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) - .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + .WithTrailingTrivia(node.Expression.GetTrailingTrivia()); + + if (ReplaceNodes.ContainsKey(node.Expression)) + ReplaceNodes[node.Expression] = _identifier; + else + ReplaceNodes.Add(node.Expression, _identifier); return node; } if (_attributeData[i].AttributeClass!.Name == nameof(UnaryAttribute)) { - ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) + IdentifierNameSyntax _identifier = SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) .WithAdditionalAnnotations(UnaryAttribute.Annotation) .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) - .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + .WithTrailingTrivia(node.Expression.GetTrailingTrivia()); + + if (ReplaceNodes.ContainsKey(node.Expression)) + ReplaceNodes[node.Expression] = _identifier; + else + ReplaceNodes.Add(node.Expression, _identifier); return node; } @@ -176,10 +180,15 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) _arg = (TypeSyntax)ReplaceNodes[_arg]; //Replace GenericName with IdentifierName from an attribute. - ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(_attributeData[i].ConstructorArguments[0].Value.ToString())) + IdentifierNameSyntax _identifier = SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(_attributeData[i].ConstructorArguments[0].Value.ToString())) .WithAdditionalAnnotations(UnaryAttribute.Annotation) .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) - .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + .WithTrailingTrivia(node.Expression.GetTrailingTrivia()); + + if (ReplaceNodes.ContainsKey(node.Expression)) + ReplaceNodes[node.Expression] = _identifier; + else + ReplaceNodes.Add(node.Expression, _identifier); ReplaceNodes.Add(node.ArgumentList, node.ArgumentList .AddArguments(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(_arg.ToString())))); @@ -193,10 +202,15 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) if (ReplaceNodes.ContainsKey(_arg)) _arg = (TypeSyntax)ReplaceNodes[_arg]; - ReplaceNodes.Add(node.Expression, SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) + IdentifierNameSyntax _identifier = SyntaxFactory.IdentifierName(_attributeData[i].ConstructorArguments[0].Value.ToString()) .WithAdditionalAnnotations(BinaryAttribute.Annotation) .WithLeadingTrivia(node.Expression.GetLeadingTrivia()) - .WithTrailingTrivia(node.Expression.GetTrailingTrivia())); + .WithTrailingTrivia(node.Expression.GetTrailingTrivia()); + + if (ReplaceNodes.ContainsKey(node.Expression)) + ReplaceNodes[node.Expression] = _identifier; + else + ReplaceNodes.Add(node.Expression, _identifier); ReplaceNodes.Add(node.ArgumentList, node.ArgumentList .AddArguments(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(_arg.ToString()))) @@ -274,18 +288,44 @@ public WithSemanticRewriter(SemanticModel semanticModel, CSTOJSOptions options) return node; } - public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) + + public override SyntaxNode? VisitObjectCreationExpression(ObjectCreationExpressionSyntax node) { - node = (PropertyDeclarationSyntax)base.VisitPropertyDeclaration(node)!; + node = (ObjectCreationExpressionSyntax)base.VisitObjectCreationExpression(node)!; - if (node.Type is IdentifierNameSyntax || - node.Type is GenericNameSyntax) - VisitSimpleName((SimpleNameSyntax)node.Type); + ISymbol? symbol = _Model.GetSymbolInfo(node.Type).Symbol; + if (symbol != null) + { + ImmutableArray _attributeData = symbol.GetAttributes(); + for (int i = 0; i < _attributeData.Length; i++) + { + if (_attributeData[i].AttributeClass != null) + { + if (_attributeData[i].AttributeClass!.Name == nameof(ToObjectAttribute)) + { + ReplaceNodes.Add(node.Type, node.Type.WithAdditionalAnnotations(ToObjectAttribute.Annotation)); + break; + } + } + } + } + return node; } - - + + public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) + { + VisitSimpleName(node); + return node; + } + public override SyntaxNode? VisitGenericName(GenericNameSyntax node) + { + node = (GenericNameSyntax)base.VisitGenericName(node)!; + VisitSimpleName(node); + return node; + } + private bool TryReplaceIdentifierWithThis(ISymbol symbol, SimpleNameSyntax identifier) { if (symbol != null && !symbol.IsStatic) @@ -349,16 +389,50 @@ private void VisitSimpleName(SimpleNameSyntax identifier) { ToAttribute _toAttr = new(_attributeData[i].ConstructorArguments[0].Value.ToString()); - //TODO! - //how? - //if (_toAttr.To == ToAttribute.NoneWithLeadingDotRemoved) - // JSSB.Remove(JSSB.Length - 1, 1); - - //if (_toAttr.To == ToAttribute.NoneWithTrailingDotRemoved) - // _IgnoreTailingDot = true; + if (_toAttr.To == ToAttribute.NoneWithLeadingDotRemoved) + { + SyntaxNode? _mae = identifier.Parent; + if (_mae == null) + { + Log.ErrorLine($"Parent of {identifier} is null"); + return; + } + if (_mae is not MemberAccessExpressionSyntax) + { + Log.ErrorLine($"Parent of {identifier} is {_mae.GetType()}. Should be MemberAccessExpressionSyntax"); + return; + } + + //Check if node is in ReplaceNodes. + //If yes, then use replaced node. + SyntaxNode _node = ((MemberAccessExpressionSyntax)_mae).Expression; + if (ReplaceNodes.ContainsKey(_node)) + _node = ReplaceNodes[_node]; + + ReplaceNodes.Add(identifier.Parent, _node); + return; + } + if (_toAttr.To == ToAttribute.NoneWithTrailingDotRemoved) + { + SyntaxNode? _mae = identifier.Parent; + if (_mae == null) + { + Log.ErrorLine($"Parent of {identifier} is null"); + return; + } + if (_mae is not MemberAccessExpressionSyntax) + { + Log.ErrorLine($"Parent of {identifier} is {_mae.GetType()}. Should be MemberAccessExpressionSyntax"); + return; + } + + //Delay adding to ReplaceNodes until the full MemberAccessExpressionSyntax is processed. + _NoneWithTrailingDotRemoved = true; + return; + } + ReplaceNodes.Add(identifier, SyntaxFactory.IdentifierName(_toAttr.Convert(identifier.Identifier.Text)).WithLeadingTrivia(identifier.Identifier.LeadingTrivia).WithTrailingTrivia(identifier.Identifier.TrailingTrivia)); - //return node; return; } } diff --git a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs index 3f8ab86a..7637b691 100644 --- a/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs +++ b/CSharpToJavaScript/Walker/WithoutSemanticRewriter.cs @@ -243,7 +243,19 @@ public WithoutSemanticRewriter(CSTOJSOptions options) return node; } + public override SyntaxNode? VisitObjectCreationExpression(ObjectCreationExpressionSyntax node) + { + node = (ObjectCreationExpressionSyntax)base.VisitObjectCreationExpression(node)!; + if (node.Type.HasAnnotation(ToObjectAttribute.Annotation)) + { + SyntaxTriviaList trivias = node.ArgumentList.GetTrailingTrivia(); + trivias = trivias.AddRange(node.Initializer.GetLeadingTrivia()); + + return node.Initializer.WithLeadingTrivia(trivias); + } + return node; + } public override SyntaxNode? VisitVariableDeclaration(VariableDeclarationSyntax node) { if (node.Parent is ForStatementSyntax ||