From a7446cfddf82bf9e5f2ae3549d842aad056e63b3 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Feb 2020 23:37:48 +0100 Subject: [PATCH] #1922: Refactor PrimitiveExpression to store the literal format in the AST --- .../OutputVisitor/CSharpOutputVisitor.cs | 6 +-- .../CSharp/OutputVisitor/ITokenWriter.cs | 16 ++++-- .../InsertMissingTokensDecorator.cs | 6 +-- .../InsertRequiredSpacesDecorator.cs | 4 +- .../OutputVisitor/TextWriterTokenWriter.cs | 28 +++++------ .../Syntax/Expressions/PrimitiveExpression.cs | 50 ++++++++++--------- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 12 ++--- .../Output/TextTokenWriter.cs | 11 ++-- .../CSharpHighlightingTokenWriter.cs | 4 +- ILSpy/Languages/CSharpLexer.cs | 6 +-- 10 files changed, 77 insertions(+), 66 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 5e155fb6ea..1f399b9d9e 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -992,7 +992,7 @@ public virtual void VisitPointerReferenceExpression(PointerReferenceExpression p public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { StartNode(primitiveExpression); - writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.UnsafeLiteralValue); + writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.Format); isAfterSpace = false; EndNode(primitiveExpression); } @@ -1019,7 +1019,7 @@ public virtual void VisitInterpolation(Interpolation interpolation) interpolation.Expression.AcceptVisitor(this); if (interpolation.Suffix != null) { writer.WriteToken(Roles.Colon, ":"); - writer.WritePrimitiveValue("", interpolation.Suffix); + writer.WriteInterpolatedText(interpolation.Suffix); } writer.WriteToken(Interpolation.RBrace, "}"); @@ -1029,7 +1029,7 @@ public virtual void VisitInterpolation(Interpolation interpolation) public virtual void VisitInterpolatedStringText(InterpolatedStringText interpolatedStringText) { StartNode(interpolatedStringText); - writer.WritePrimitiveValue("", TextWriterTokenWriter.ConvertString(interpolatedStringText.Text)); + writer.WriteInterpolatedText(interpolatedStringText.Text); EndNode(interpolatedStringText); } #endregion diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs index e97e1288c6..4d9bd0f43a 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/ITokenWriter.cs @@ -45,10 +45,15 @@ public abstract class TokenWriter /// /// Writes a primitive/literal value /// - public abstract void WritePrimitiveValue(object value, string literalValue = null); + public abstract void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None); public abstract void WritePrimitiveType(string type); + /// + /// Write a piece of text in an interpolated string literal. + /// + public abstract void WriteInterpolatedText(string text); + public abstract void Space(); public abstract void Indent(); public abstract void Unindent(); @@ -123,9 +128,9 @@ public override void WriteToken(Role role, string token) decoratedWriter.WriteToken(role, token); } - public override void WritePrimitiveValue(object value, string literalValue = null) + public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { - decoratedWriter.WritePrimitiveValue(value, literalValue); + decoratedWriter.WritePrimitiveValue(value, format); } public override void WritePrimitiveType(string type) @@ -133,6 +138,11 @@ public override void WritePrimitiveType(string type) decoratedWriter.WritePrimitiveType(type); } + public override void WriteInterpolatedText(string text) + { + decoratedWriter.WriteInterpolatedText(text); + } + public override void Space() { decoratedWriter.Space(); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs index 220a9194e0..d674695ddb 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs @@ -116,11 +116,11 @@ public override void WriteIdentifier(Identifier identifier) base.WriteIdentifier(identifier); } - public override void WritePrimitiveValue(object value, string literalValue = null) + public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { Expression node = nodes.Peek().LastOrDefault() as Expression; var startLocation = locationProvider.Location; - base.WritePrimitiveValue(value, literalValue); + base.WritePrimitiveValue(value, format); if (node is PrimitiveExpression) { ((PrimitiveExpression)node).SetLocation(startLocation, locationProvider.Location); } @@ -128,7 +128,7 @@ public override void WritePrimitiveValue(object value, string literalValue = nul ((NullReferenceExpression)node).SetStartLocation(startLocation); } } - + public override void WritePrimitiveType(string type) { PrimitiveType node = nodes.Peek().LastOrDefault() as PrimitiveType; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs index 3dff8f8cde..e5e83f0a26 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs @@ -129,12 +129,12 @@ public override void WritePreProcessorDirective(PreProcessorDirectiveType type, lastWritten = LastWritten.Whitespace; } - public override void WritePrimitiveValue(object value, string literalValue = null) + public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { if (lastWritten == LastWritten.KeywordOrIdentifier) { Space(); } - base.WritePrimitiveValue(value, literalValue); + base.WritePrimitiveValue(value, format); if (value == null || value is bool) return; if (value is string) { diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs index 8aa81a7581..a41042ca0a 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -224,16 +224,9 @@ public static string PrintPrimitiveValue(object value) tokenWriter.WritePrimitiveValue(value); return writer.ToString(); } - - public override void WritePrimitiveValue(object value, string literalValue = null) + + public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { - if (literalValue != null) { - textWriter.Write(literalValue); - column += literalValue.Length; - Length += literalValue.Length; - return; - } - if (value == null) { // usually NullReferenceExpression should be used for this, but we'll handle it anyways textWriter.Write("null"); @@ -346,12 +339,12 @@ public override void WritePrimitiveValue(object value, string literalValue = nul Length += number.Length; } else if (value is IFormattable) { StringBuilder b = new StringBuilder(); - // if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) { - // b.Append("0x"); - // b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo)); - // } else { - b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo)); - // } + if (format == LiteralFormat.HexadecimalNumber) { + b.Append("0x"); + b.Append(((IFormattable)value).ToString("X", NumberFormatInfo.InvariantInfo)); + } else { + b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo)); + } if (value is uint || value is ulong) { b.Append("u"); } @@ -369,6 +362,11 @@ public override void WritePrimitiveValue(object value, string literalValue = nul } } + public override void WriteInterpolatedText(string text) + { + textWriter.Write(ConvertString(text)); + } + /// /// Gets the escape sequence for the specified character within a char literal. /// Does not include the single quotes surrounding the char literal. diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs index 3e6d49724d..6ec67431de 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs @@ -29,6 +29,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { + /// + /// Form of a C# literal. + /// + public enum LiteralFormat : byte + { + None, + DecimalNumber, + HexadecimalNumber, + BinaryNumber, + StringLiteral, + VerbatimStringLiteral, + CharLiteral, + } + /// /// Represents a literal value. /// @@ -63,26 +77,16 @@ public override TextLocation EndLocation { } object value; - + LiteralFormat format; + public object Value { get { return this.value; } set { ThrowIfFrozen(); this.value = value; - literalValue = null; } } - /// Never returns null. - public string LiteralValue { - get { return literalValue ?? ""; } - } - - /// Can be null. - public string UnsafeLiteralValue { - get { return literalValue; } - } - public void SetValue(object value, string literalValue) { if (value == null) @@ -91,24 +95,24 @@ public void SetValue(object value, string literalValue) this.value = value; this.literalValue = literalValue; } - - public PrimitiveExpression (object value) - { - this.Value = value; - this.literalValue = null; + + public LiteralFormat Format { + get { return format;} + set { + ThrowIfFrozen(); + format = value; + } } - - public PrimitiveExpression (object value, string literalValue) + + public PrimitiveExpression (object value) { this.Value = value; - this.literalValue = literalValue; } - public PrimitiveExpression (object value, TextLocation startLocation, string literalValue) + public PrimitiveExpression (object value, LiteralFormat format) { this.Value = value; - this.startLocation = startLocation; - this.literalValue = literalValue; + this.format = format; } public override void AcceptVisitor (IAstVisitor visitor) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index fb8ef0695d..d43e616cb5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -784,17 +784,11 @@ public Expression ConvertConstantValue(IType expectedType, IType type, object co constantValue = CSharpPrimitiveCast.Cast(TypeCode.Int32, constantValue, false); literalType = type.GetDefinition().Compilation.FindType(KnownTypeCode.Int32); } - string literalValue = null; + LiteralFormat format = LiteralFormat.None; if (PrintIntegralValuesAsHex) { - literalValue = $"0x{constantValue:X}"; - if (constantValue is uint || constantValue is ulong) { - literalValue += "u"; - } - if (constantValue is long || constantValue is ulong) { - literalValue += "L"; - } + format = LiteralFormat.HexadecimalNumber; } - expr = new PrimitiveExpression(constantValue, literalValue); + expr = new PrimitiveExpression(constantValue, format); if (AddResolveResultAnnotations) expr.AddAnnotation(new ConstantResolveResult(literalType, constantValue)); if (smallInteger && !type.Equals(expectedType)) { diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 85d62c7046..1ca9a792af 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -322,11 +322,16 @@ public override void WritePreProcessorDirective(PreProcessorDirectiveType type, output.WriteLine(); } - public override void WritePrimitiveValue(object value, string literalValue = null) + public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None) { - new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, literalValue); + new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, format); } - + + public override void WriteInterpolatedText(string text) + { + output.Write(TextWriterTokenWriter.ConvertString(text)); + } + public override void WritePrimitiveType(string type) { switch (type) { diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index 781f33b7ab..bd2e430c8d 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -392,7 +392,7 @@ public override void WriteIdentifier(Identifier identifier) } } - public override void WritePrimitiveValue(object value, string literalValue = null) + public override void WritePrimitiveValue(object value, Decompiler.CSharp.Syntax.LiteralFormat format) { HighlightingColor color = null; if (value is null) { @@ -404,7 +404,7 @@ public override void WritePrimitiveValue(object value, string literalValue = nul if (color != null) { BeginSpan(color); } - base.WritePrimitiveValue(value, literalValue); + base.WritePrimitiveValue(value, format); if (color != null) { EndSpan(); } diff --git a/ILSpy/Languages/CSharpLexer.cs b/ILSpy/Languages/CSharpLexer.cs index 4d839adeef..8328414621 100644 --- a/ILSpy/Languages/CSharpLexer.cs +++ b/ILSpy/Languages/CSharpLexer.cs @@ -9,7 +9,7 @@ namespace ICSharpCode.ILSpy { - public class LATextReader : TextReader + class LATextReader : TextReader { List buffer; TextReader reader; @@ -52,7 +52,7 @@ protected override void Dispose(bool disposing) } } - public enum LiteralFormat : byte + enum LiteralFormat : byte { None, DecimalNumber, @@ -64,7 +64,7 @@ public enum LiteralFormat : byte DateTimeLiteral } - public class Literal + class Literal { internal readonly LiteralFormat literalFormat; internal readonly object literalValue;