/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using dnlib.DotNet;
using dnSpy.Contracts.Decompiler;
using dnSpy.Contracts.Text;
using dnSpy.Decompiler.Properties;
namespace dnSpy.Decompiler.VisualBasic {
public struct VisualBasicFormatter {
const string Keyword_true = "True";
const string Keyword_false = "False";
const string Keyword_null = "Nothing";
const string Keyword_As = "As";
const string Keyword_out = "Out";
const string Keyword_in = "In";
const string Keyword_get = "Get";
const string Keyword_set = "Set";
const string Keyword_add = "Add";
const string Keyword_remove = "Remove";
const string Keyword_module = "Module";
const string Keyword_enum = "Enum";
const string Keyword_struct = "Structure";
const string Keyword_interface = "Interface";
const string Keyword_class = "Class";
const string Keyword_namespace = "Namespace";
const string Keyword_params = "ParamArray";
const string Keyword_delegate = "Delegate";
const string Keyword_ByRef = "ByRef";
const string Keyword_New = "New";
const string Keyword_Sub = "Sub";
const string Keyword_Function = "Function";
const string Keyword_ReadOnly = "ReadOnly";
const string Keyword_Property = "Property";
const string Keyword_Event = "Event";
const string HexPrefix = "&H";
const string IdentifierEscapeBegin = "[";
const string IdentifierEscapeEnd = "]";
const string ModuleNameSeparator = "!";
const string CommentBegin = "/*";
const string CommentEnd = "*/";
const string DeprecatedParenOpen = "(";
const string DeprecatedParenClose = ")";
const string MemberSpecialParenOpen = "<";
const string MemberSpecialParenClose = ">";
const string MethodParenOpen = "(";
const string MethodParenClose = ")";
const string DescriptionParenOpen = "(";
const string DescriptionParenClose = ")";
const string PropertyParenOpen = "(";
const string PropertyParenClose = ")";
const string ArrayParenOpen = "(";
const string ArrayParenClose = ")";
const string TupleParenOpen = "(";
const string TupleParenClose = ")";
const string GenericParenOpen = "(";
const string GenericParenClose = ")";
const string Keyword_Of = "Of";
const string DefaultParamValueParenOpen = "[";
const string DefaultParamValueParenClose = "]";
int recursionCounter;
int lineLength;
bool outputLengthExceeded;
bool forceWrite;
readonly ITextColorWriter output;
FormatterOptions options;
readonly CultureInfo cultureInfo;
static readonly Dictionary nameToOperatorName = new Dictionary(StringComparer.Ordinal) {
{ "op_UnaryPlus", "Operator +".Split(' ') },
{ "op_UnaryNegation", "Operator -".Split(' ') },
{ "op_False", "Operator IsFalse".Split(' ') },
{ "op_True", "Operator IsTrue".Split(' ') },
{ "op_OnesComplement", "Operator Not".Split(' ') },
{ "op_Addition", "Operator +".Split(' ') },
{ "op_Subtraction", "Operator -".Split(' ') },
{ "op_Multiply", "Operator *".Split(' ') },
{ "op_Division", "Operator /".Split(' ') },
{ "op_IntegerDivision", @"Operator \".Split(' ') },
{ "op_Concatenate", "Operator &".Split(' ') },
{ "op_Exponent", "Operator ^".Split(' ') },
{ "op_RightShift", "Operator >>".Split(' ') },
{ "op_LeftShift", "Operator <<".Split(' ') },
{ "op_Equality", "Operator =".Split(' ') },
{ "op_Inequality", "Operator <>".Split(' ') },
{ "op_GreaterThan", "Operator >".Split(' ') },
{ "op_GreaterThanOrEqual", "Operator >=".Split(' ') },
{ "op_LessThan", "Operator <".Split(' ') },
{ "op_LessThanOrEqual", "Operator <=".Split(' ') },
{ "op_BitwiseAnd", "Operator And".Split(' ') },
{ "op_Like", "Operator Like".Split(' ') },
{ "op_Modulus", "Operator Mod".Split(' ') },
{ "op_BitwiseOr", "Operator Or".Split(' ') },
{ "op_ExclusiveOr", "Operator Xor".Split(' ') },
{ "op_Implicit", "Widening Operator CType".Split(' ') },
{ "op_Explicit", "Narrowing Operator CType".Split(' ') },
};
bool ShowModuleNames => (options & FormatterOptions.ShowModuleNames) != 0;
bool ShowParameterTypes => (options & FormatterOptions.ShowParameterTypes) != 0;
bool ShowParameterNames => (options & FormatterOptions.ShowParameterNames) != 0;
bool ShowDeclaringTypes => (options & FormatterOptions.ShowDeclaringTypes) != 0;
bool ShowReturnTypes => (options & FormatterOptions.ShowReturnTypes) != 0;
bool ShowNamespaces => (options & FormatterOptions.ShowNamespaces) != 0;
bool ShowIntrinsicTypeKeywords => (options & FormatterOptions.ShowIntrinsicTypeKeywords) != 0;
bool UseDecimal => (options & FormatterOptions.UseDecimal) != 0;
bool ShowTokens => (options & FormatterOptions.ShowTokens) != 0;
bool ShowArrayValueSizes => (options & FormatterOptions.ShowArrayValueSizes) != 0;
bool ShowFieldLiteralValues => (options & FormatterOptions.ShowFieldLiteralValues) != 0;
bool ShowParameterLiteralValues => (options & FormatterOptions.ShowParameterLiteralValues) != 0;
bool DigitSeparators => (options & FormatterOptions.DigitSeparators) != 0;
public VisualBasicFormatter(ITextColorWriter output, FormatterOptions options, CultureInfo? cultureInfo) {
this.output = output;
this.options = options;
this.cultureInfo = cultureInfo ?? CultureInfo.InvariantCulture;
recursionCounter = 0;
lineLength = 0;
outputLengthExceeded = false;
forceWrite = false;
}
static readonly HashSet isKeyword = new HashSet(StringComparer.OrdinalIgnoreCase) {
"#Const", "#Else", "#ElseIf", "#End", "#If", "AddHandler", "AddressOf",
"Alias", "And", "AndAlso", "As", "Boolean", "ByRef", "Byte", "ByVal",
"Call", "Case", "Catch", "CBool", "CByte", "CChar", "CDate", "CDbl",
"CDec", "Char", "CInt", "Class", "CLng", "CObj", "Const", "Continue",
"CSByte", "CShort", "CSng", "CStr", "CType", "CUInt", "CULng", "CUShort",
"Date", "Decimal", "Declare", "Default", "Delegate", "Dim", "DirectCast",
"Do", "Double", "Each", "Else", "ElseIf", "End", "EndIf", "Enum", "Erase",
"Error", "Event", "Exit", "False", "Finally", "For", "Friend", "Function",
"Get", "GetType", "GetXMLNamespace", "Global", "GoSub", "GoTo", "Handles",
"If", "Implements", "Imports", "In", "Inherits", "Integer", "Interface",
"Is", "IsNot", "Let", "Lib", "Like", "Long", "Loop", "Me", "Mod", "Module",
"MustInherit", "MustOverride", "MyBase", "MyClass", "Namespace", "Narrowing",
"New", "Next", "Not", "Nothing", "NotInheritable", "NotOverridable", "Object",
"Of", "On", "Operator", "Option", "Optional", "Or", "OrElse", "Out",
"Overloads", "Overridable", "Overrides", "ParamArray", "Partial", "Private",
"Property", "Protected", "Public", "RaiseEvent", "ReadOnly", "ReDim", "REM",
"RemoveHandler", "Resume", "Return", "SByte", "Select", "Set", "Shadows",
"Shared", "Short", "Single", "Static", "Step", "Stop", "String", "Structure",
"Sub", "SyncLock", "Then", "Throw", "To", "True", "Try", "TryCast", "TypeOf",
"UInteger", "ULong", "UShort", "Using", "Variant", "Wend", "When", "While",
"Widening", "With", "WithEvents", "WriteOnly", "Xor",
};
void WriteIdentifier(string id, object data) {
if (isKeyword.Contains(id))
OutputWrite(IdentifierEscapeBegin + IdentifierEscaper.Escape(id) + IdentifierEscapeEnd, data);
else
OutputWrite(IdentifierEscaper.Escape(id), data);
}
void OutputWrite(string s, object data) {
if (!forceWrite) {
if (outputLengthExceeded)
return;
if (lineLength + s.Length > TypeFormatterUtils.MAX_OUTPUT_LEN) {
s = s.Substring(0, TypeFormatterUtils.MAX_OUTPUT_LEN - lineLength);
s += "[...]";
outputLengthExceeded = true;
}
}
output.Write(data, s);
lineLength += s.Length;
}
void WriteSpace() => OutputWrite(" ", BoxedTextColor.Text);
void WriteCommaSpace() {
OutputWrite(",", BoxedTextColor.Punctuation);
WriteSpace();
}
void WritePeriod() => OutputWrite(".", BoxedTextColor.Operator);
void WriteError() => OutputWrite("???", BoxedTextColor.Error);
void WriteSystemTypeKeyword(string name, string keyword, bool isValueType) {
if (ShowIntrinsicTypeKeywords)
OutputWrite(keyword, BoxedTextColor.Keyword);
else
WriteSystemType(name, isValueType);
}
void WriteSystemType(string name, bool isValueType) {
if (ShowNamespaces) {
OutputWrite("System", BoxedTextColor.Namespace);
WritePeriod();
}
OutputWrite(name, isValueType ? BoxedTextColor.ValueType : BoxedTextColor.Type);
}
void WriteToken(IMDTokenProvider tok) {
if (!ShowTokens)
return;
Debug2.Assert(tok is not null);
if (tok is null)
return;
OutputWrite(CommentBegin + ToFormattedUInt32(tok.MDToken.Raw) + CommentEnd, BoxedTextColor.Comment);
}
public void WriteToolTip(IMemberRef? member) {
if (member is null) {
WriteError();
return;
}
if (member is IMethod method && method.MethodSig is not null) {
WriteToolTip(method);
return;
}
if (member is IField field && field.FieldSig is not null) {
WriteToolTip(field);
return;
}
if (member is PropertyDef prop && prop.PropertySig is not null) {
WriteToolTip(prop);
return;
}
if (member is EventDef evt && evt.EventType is not null) {
WriteToolTip(evt);
return;
}
if (member is ITypeDefOrRef tdr) {
WriteToolTip(tdr);
return;
}
if (member is GenericParam gp) {
WriteToolTip(gp);
return;
}
Debug.Fail("Unknown reference");
}
public void Write(IMemberRef? member) {
if (member is null) {
WriteError();
return;
}
if (member is IMethod method && method.MethodSig is not null) {
Write(method);
return;
}
if (member is IField field && field.FieldSig is not null) {
Write(field);
return;
}
if (member is PropertyDef prop && prop.PropertySig is not null) {
Write(prop);
return;
}
if (member is EventDef evt && evt.EventType is not null) {
Write(evt);
return;
}
if (member is ITypeDefOrRef tdr) {
Write(tdr, ShowModuleNames);
return;
}
if (member is GenericParam gp) {
Write(gp);
return;
}
Debug.Fail("Unknown reference");
}
void WriteDeprecated(bool isDeprecated) {
if (isDeprecated) {
OutputWrite(DeprecatedParenOpen, BoxedTextColor.Punctuation);
OutputWrite(dnSpy_Decompiler_Resources.VisualBasic_Deprecated_Member, BoxedTextColor.Text);
OutputWrite(DeprecatedParenClose, BoxedTextColor.Punctuation);
WriteSpace();
}
}
void Write(MemberSpecialFlags flags) {
if (flags == MemberSpecialFlags.None)
return;
OutputWrite(MemberSpecialParenOpen, BoxedTextColor.Punctuation);
bool comma = false;
if ((flags & MemberSpecialFlags.Awaitable) != 0) {
comma = true;
OutputWrite(dnSpy_Decompiler_Resources.VisualBasic_Awaitable_Method, BoxedTextColor.Text);
}
if ((flags & MemberSpecialFlags.Extension) != 0) {
if (comma)
WriteCommaSpace();
OutputWrite(dnSpy_Decompiler_Resources.VisualBasic_Extension_Method, BoxedTextColor.Text);
}
OutputWrite(MemberSpecialParenClose, BoxedTextColor.Punctuation);
WriteSpace();
}
void WriteToolTip(IMethod? method) {
if (method is null) {
WriteError();
return;
}
WriteDeprecated(TypeFormatterUtils.IsDeprecated(method));
Write(TypeFormatterUtils.GetMemberSpecialFlags(method));
Write(method);
var td = method.DeclaringType.ResolveTypeDef();
if (td is not null) {
var s = TypeFormatterUtils.GetNumberOfOverloadsString(td, method);
if (s is not null)
OutputWrite(s, BoxedTextColor.Text);
}
}
void WriteType(ITypeDefOrRef type, bool useNamespaces, bool useTypeKeywords) {
var td = type as TypeDef;
if (td is null && type is TypeRef)
td = ((TypeRef)type).Resolve();
if (td is null ||
td.GenericParameters.Count == 0 ||
(td.DeclaringType is not null && td.DeclaringType.GenericParameters.Count >= td.GenericParameters.Count)) {
var oldFlags = options;
options &= ~(FormatterOptions.ShowNamespaces | FormatterOptions.ShowIntrinsicTypeKeywords);
if (useNamespaces)
options |= FormatterOptions.ShowNamespaces;
if (useTypeKeywords)
options |= FormatterOptions.ShowIntrinsicTypeKeywords;
Write(type);
options = oldFlags;
return;
}
int numGenParams = td.GenericParameters.Count;
if (type.DeclaringType is not null) {
var oldFlags = options;
options &= ~(FormatterOptions.ShowNamespaces | FormatterOptions.ShowIntrinsicTypeKeywords);
if (useNamespaces)
options |= FormatterOptions.ShowNamespaces;
Write(type.DeclaringType);
options = oldFlags;
WritePeriod();
numGenParams = numGenParams - td.DeclaringType!.GenericParameters.Count;
if (numGenParams < 0)
numGenParams = 0;
}
else if (useNamespaces && !UTF8String.IsNullOrEmpty(td.Namespace)) {
foreach (var ns in td.Namespace.String.Split('.')) {
WriteIdentifier(ns, BoxedTextColor.Namespace);
WritePeriod();
}
}
WriteIdentifier(TypeFormatterUtils.RemoveGenericTick(td.Name), VisualBasicMetadataTextColorProvider.Instance.GetColor(td));
WriteToken(type);
var genParams = td.GenericParameters.Skip(td.GenericParameters.Count - numGenParams).ToArray();
WriteGenerics(genParams, BoxedTextColor.TypeGenericParameter);
}
void WriteAccessor(AccessorKind kind) {
string keyword;
switch (kind) {
case AccessorKind.None:
default:
throw new InvalidOperationException();
case AccessorKind.Getter:
keyword = Keyword_get;
break;
case AccessorKind.Setter:
keyword = Keyword_set;
break;
case AccessorKind.Adder:
keyword = Keyword_add;
break;
case AccessorKind.Remover:
keyword = Keyword_remove;
break;
}
OutputWrite(keyword, BoxedTextColor.Keyword);
WriteSpace();
}
void Write(IMethod? method) {
if (method is null) {
WriteError();
return;
}
var propInfo = TypeFormatterUtils.TryGetProperty(method as MethodDef);
if (propInfo.kind != AccessorKind.None) {
Write(propInfo.property, propInfo.kind);
return;
}
var eventInfo = TypeFormatterUtils.TryGetEvent(method as MethodDef);
if (eventInfo.kind != AccessorKind.None) {
Write(eventInfo.@event, eventInfo.kind);
return;
}
var info = new FormatterMethodInfo(method);
WriteModuleName(info);
string[]? operatorInfo;
if (info.MethodDef is not null && info.MethodDef.IsConstructor && method.DeclaringType is not null)
operatorInfo = null;
else if (info.MethodDef is not null && info.MethodDef.Overrides.Count > 0) {
var ovrMeth = (IMemberRef)info.MethodDef.Overrides[0].MethodDeclaration;
operatorInfo = TryGetOperatorInfo(ovrMeth.Name);
}
else
operatorInfo = TryGetOperatorInfo(method.Name);
if (operatorInfo is not null) {
for (int i = 0; i < operatorInfo.Length - 1; i++) {
WriteOperatorInfoString(operatorInfo[i]);
WriteSpace();
}
}
else {
bool isSub = IsSub(info);
OutputWrite(isSub ? Keyword_Sub : Keyword_Function, BoxedTextColor.Keyword);
WriteSpace();
}
if (ShowDeclaringTypes) {
Write(method.DeclaringType);
WritePeriod();
}
if (info.MethodDef is not null && info.MethodDef.IsConstructor && method.DeclaringType is not null)
OutputWrite(Keyword_New, BoxedTextColor.Keyword);
else if (info.MethodDef is not null && info.MethodDef.Overrides.Count > 0) {
var ovrMeth = (IMemberRef)info.MethodDef.Overrides[0].MethodDeclaration;
WriteMethodName(method, ovrMeth.Name, operatorInfo);
}
else
WriteMethodName(method, method.Name, operatorInfo);
WriteToken(method);
WriteGenericArguments(info);
WriteMethodParameterList(info, MethodParenOpen, MethodParenClose);
WriteReturnType(info);
}
static string[]? TryGetOperatorInfo(string name) {
nameToOperatorName.TryGetValue(name, out var list);
return list;
}
void WriteOperatorInfoString(string s) => OutputWrite(s, 'A' <= s[0] && s[0] <= 'Z' ? BoxedTextColor.Keyword : BoxedTextColor.Operator);
void WriteMethodName(IMethod method, string name, string[]? operatorInfo) {
if (operatorInfo is not null)
WriteOperatorInfoString(operatorInfo[operatorInfo.Length - 1]);
else
WriteIdentifier(name, VisualBasicMetadataTextColorProvider.Instance.GetColor(method));
}
void WriteToolTip(IField field) {
WriteDeprecated(TypeFormatterUtils.IsDeprecated(field));
Write(field, true);
}
void Write(IField field) => Write(field, false);
void Write(IField? field, bool isToolTip) {
if (field is null) {
WriteError();
return;
}
var sig = field.FieldSig;
var td = field.DeclaringType.ResolveTypeDef();
bool isEnumOwner = td is not null && td.IsEnum;
var fd = field.ResolveFieldDef();
object? constant = null;
bool isConstant = fd is not null && (fd.IsLiteral || (fd.IsStatic && fd.IsInitOnly)) && TypeFormatterUtils.HasConstant(fd, out var constantAttribute) && TypeFormatterUtils.TryGetConstant(fd, constantAttribute, out constant);
if (!isEnumOwner || (fd is not null && !fd.IsLiteral)) {
if (isToolTip) {
OutputWrite(DescriptionParenOpen, BoxedTextColor.Punctuation);
OutputWrite(isConstant ? dnSpy_Decompiler_Resources.ToolTip_Constant : dnSpy_Decompiler_Resources.ToolTip_Field, BoxedTextColor.Text);
OutputWrite(DescriptionParenClose, BoxedTextColor.Punctuation);
WriteSpace();
}
}
WriteModuleName(fd?.Module);
if (ShowDeclaringTypes) {
Write(field.DeclaringType);
WritePeriod();
}
WriteIdentifier(field.Name, VisualBasicMetadataTextColorProvider.Instance.GetColor(field));
WriteToken(field);
if (!isEnumOwner) {
WriteSpace();
OutputWrite(Keyword_As, BoxedTextColor.Keyword);
WriteSpace();
Write(sig.Type, null, null, null);
}
if (ShowFieldLiteralValues && isConstant) {
WriteSpace();
OutputWrite("=", BoxedTextColor.Operator);
WriteSpace();
WriteConstant(constant);
}
}
void WriteConstant(object? obj) {
if (obj is null) {
OutputWrite(Keyword_null, BoxedTextColor.Keyword);
return;
}
switch (Type.GetTypeCode(obj.GetType())) {
case TypeCode.Boolean:
FormatBoolean((bool)obj);
break;
case TypeCode.Char:
FormatChar((char)obj);
break;
case TypeCode.SByte:
FormatSByte((sbyte)obj);
break;
case TypeCode.Byte:
FormatByte((byte)obj);
break;
case TypeCode.Int16:
FormatInt16((short)obj);
break;
case TypeCode.UInt16:
FormatUInt16((ushort)obj);
break;
case TypeCode.Int32:
FormatInt32((int)obj);
break;
case TypeCode.UInt32:
FormatUInt32((uint)obj);
break;
case TypeCode.Int64:
FormatInt64((long)obj);
break;
case TypeCode.UInt64:
FormatUInt64((ulong)obj);
break;
case TypeCode.Single:
FormatSingle((float)obj);
break;
case TypeCode.Double:
FormatDouble((double)obj);
break;
case TypeCode.Decimal:
FormatDecimal((decimal)obj);
break;
case TypeCode.String:
FormatString((string)obj);
break;
default:
Debug.Fail($"Unknown constant: '{obj}'");
OutputWrite(obj.ToString() ?? "???", BoxedTextColor.Text);
break;
}
}
void WriteToolTip(PropertyDef prop) {
WriteDeprecated(TypeFormatterUtils.IsDeprecated(prop));
Write(prop);
}
void Write(PropertyDef prop) => Write(prop, AccessorKind.None);
void Write(PropertyDef? prop, AccessorKind accessorKind) {
if (prop is null) {
WriteError();
return;
}
var getMethod = prop.GetMethods.FirstOrDefault();
var setMethod = prop.SetMethods.FirstOrDefault();
var md = getMethod ?? setMethod;
if (md is null) {
WriteError();
return;
}
if (setMethod is null) {
OutputWrite(Keyword_ReadOnly, BoxedTextColor.Keyword);
WriteSpace();
}
OutputWrite(Keyword_Property, BoxedTextColor.Keyword);
WriteSpace();
var sigMethod = md;
if (accessorKind != AccessorKind.None) {
if (accessorKind == AccessorKind.Getter)
sigMethod = getMethod ?? md;
else if (accessorKind == AccessorKind.Setter)
sigMethod = setMethod ?? md;
else
throw new InvalidOperationException();
WriteAccessor(accessorKind);
}
var info = new FormatterMethodInfo(sigMethod, sigMethod == setMethod, accessorKind == AccessorKind.Setter);
WriteModuleName(info);
if (ShowDeclaringTypes) {
Write(prop.DeclaringType);
WritePeriod();
}
var ovrMeth = md is null || md.Overrides.Count == 0 ? null : md.Overrides[0].MethodDeclaration;
if (ovrMeth is not null && TypeFormatterUtils.GetPropertyName(ovrMeth) is string ovrMethPropName)
WriteIdentifier(ovrMethPropName, VisualBasicMetadataTextColorProvider.Instance.GetColor(prop));
else
WriteIdentifier(prop.Name, VisualBasicMetadataTextColorProvider.Instance.GetColor(prop));
WriteToken(prop);
WriteGenericArguments(info);
if (accessorKind != AccessorKind.None || prop.PropertySig.GetParamCount() != 0)
WriteMethodParameterList(info, PropertyParenOpen, PropertyParenClose);
WriteReturnType(info);
}
void WriteToolTip(EventDef evt) {
WriteDeprecated(TypeFormatterUtils.IsDeprecated(evt));
Write(evt);
}
void Write(EventDef? evt) => Write(evt, AccessorKind.None);
void Write(EventDef? evt, AccessorKind accessorKind) {
if (evt is null) {
WriteError();
return;
}
OutputWrite(Keyword_Event, BoxedTextColor.Keyword);
WriteSpace();
if (accessorKind != AccessorKind.None)
WriteAccessor(accessorKind);
WriteModuleName(evt.Module);
if (ShowDeclaringTypes) {
Write(evt.DeclaringType);
WritePeriod();
}
WriteIdentifier(evt.Name, VisualBasicMetadataTextColorProvider.Instance.GetColor(evt));
WriteToken(evt);
WriteSpace();
OutputWrite(Keyword_As, BoxedTextColor.Keyword);
WriteSpace();
Write(evt.EventType);
}
void WriteToolTip(GenericParam? gp) {
if (gp is null) {
WriteError();
return;
}
Write(gp);
WriteSpace();
OutputWrite(dnSpy_Decompiler_Resources.ToolTip_GenericParameterInTypeOrMethod, BoxedTextColor.Text);
WriteSpace();
if (gp.Owner is TypeDef td)
WriteType(td, ShowNamespaces, ShowIntrinsicTypeKeywords);
else
Write(gp.Owner as MethodDef);
}
void Write(GenericParam? gp) {
if (gp is null) {
WriteError();
return;
}
WriteIdentifier(gp.Name, VisualBasicMetadataTextColorProvider.Instance.GetColor(gp));
WriteToken(gp);
}
void WriteToolTip(ITypeDefOrRef type) {
var td = type.ResolveTypeDef();
WriteDeprecated(TypeFormatterUtils.IsDeprecated(type));
Write(TypeFormatterUtils.GetMemberSpecialFlags(type));
MethodDef invoke;
if (TypeFormatterUtils.IsDelegate(td) && (invoke = td.FindMethod("Invoke")) is not null && invoke.MethodSig is not null) {
OutputWrite(Keyword_delegate, BoxedTextColor.Keyword);
WriteSpace();
var info = new FormatterMethodInfo(invoke);
WriteModuleName(info);
bool isSub = IsSub(info);
OutputWrite(isSub ? Keyword_Sub : Keyword_Function, BoxedTextColor.Keyword);
WriteSpace();
// Always print the namespace here because that's what VS does
WriteType(td, true, ShowIntrinsicTypeKeywords);
WriteGenericArguments(info);
WriteMethodParameterList(info, MethodParenOpen, MethodParenClose);
WriteReturnType(info);
return;
}
else
WriteModuleName(td?.Module);
if (td is null) {
Write(type);
return;
}
string keyword;
if (IsModule(td))
keyword = Keyword_module;
else if (td.IsEnum)
keyword = Keyword_enum;
else if (td.IsValueType)
keyword = Keyword_struct;
else if (td.IsInterface)
keyword = Keyword_interface;
else
keyword = Keyword_class;
OutputWrite(keyword, BoxedTextColor.Keyword);
WriteSpace();
// Always print the namespace here because that's what VS does
WriteType(type, true, false);
}
static bool IsModule(TypeDef type) =>
type is not null && type.DeclaringType is null && type.IsSealed && type.IsDefined(stringMicrosoftVisualBasicCompilerServices, stringStandardModuleAttribute);
static readonly UTF8String stringMicrosoftVisualBasicCompilerServices = new UTF8String("Microsoft.VisualBasic.CompilerServices");
static readonly UTF8String stringStandardModuleAttribute = new UTF8String("StandardModuleAttribute");
void Write(ITypeDefOrRef? type, bool showModuleNames = false) {
if (type is null) {
WriteError();
return;
}
if (recursionCounter >= TypeFormatterUtils.MAX_RECURSION)
return;
recursionCounter++;
try {
if (type is TypeSpec ts) {
Write(ts.TypeSig, null, null, null);
return;
}
if (type.DeclaringType is not null) {
Write(type.DeclaringType);
WritePeriod();
}
string? keyword = GetTypeKeyword(type);
if (keyword is not null)
OutputWrite(keyword, BoxedTextColor.Keyword);
else {
if (showModuleNames)
WriteModuleName(type.ResolveTypeDef()?.Module);
WriteNamespace(type.Namespace);
WriteIdentifier(TypeFormatterUtils.RemoveGenericTick(type.Name), VisualBasicMetadataTextColorProvider.Instance.GetColor(type));
}
WriteToken(type);
}
finally {
recursionCounter--;
}
}
void WriteNamespace(string ns) {
if (!ShowNamespaces || string.IsNullOrEmpty(ns))
return;
var namespaces = ns.Split(nsSep);
for (int i = 0; i < namespaces.Length; i++) {
OutputWrite(namespaces[i], BoxedTextColor.Namespace);
WritePeriod();
}
}
static readonly char[] nsSep = new char[] { '.' };
string? GetTypeKeyword(ITypeDefOrRef type) {
if (!ShowIntrinsicTypeKeywords)
return null;
if (type is null || type.DeclaringType is not null || type.Namespace != "System" || !type.DefinitionAssembly.IsCorLib())
return null;
switch (type.TypeName) {
case "Boolean": return "Boolean";
case "Byte": return "Byte";
case "Char": return "Char";
case "DateTime":return "Date";
case "Decimal": return "Decimal";
case "Double": return "Double";
case "Int16": return "Short";
case "Int32": return "Integer";
case "Int64": return "Long";
case "Object": return "Object";
case "SByte": return "SByte";
case "Single": return "Single";
case "String": return "String";
case "UInt16": return "UShort";
case "UInt32": return "UInteger";
case "UInt64": return "ULong";
default: return null;
}
}
void Write(TypeSig? type, ParamDef? ownerParam, IList? typeGenArgs, IList? methGenArgs) => Write(type, typeGenArgs, methGenArgs);
void Write(TypeSig? type, IList? typeGenArgs, IList? methGenArgs) {
if (type is null) {
WriteError();
return;
}
if (recursionCounter >= TypeFormatterUtils.MAX_RECURSION)
return;
recursionCounter++;
try {
if (typeGenArgs is null)
typeGenArgs = Array.Empty();
if (methGenArgs is null)
methGenArgs = Array.Empty();
List? list = null;
while (type is not null && (type.ElementType == ElementType.SZArray || type.ElementType == ElementType.Array)) {
if (list is null)
list = new List();
list.Add((ArraySigBase)type);
type = type.Next;
}
if (list is not null) {
Write(list[list.Count - 1].Next, typeGenArgs, Array.Empty());
foreach (var aryType in list) {
if (aryType.ElementType == ElementType.Array) {
OutputWrite(ArrayParenOpen, BoxedTextColor.Punctuation);
uint rank = aryType.Rank;
if (rank == 0)
OutputWrite("", BoxedTextColor.Error);
else {
var indexes = aryType.GetLowerBounds();
var dims = aryType.GetSizes();
if (ShowArrayValueSizes && (uint)indexes.Count == rank && (uint)dims.Count == rank) {
for (int i = 0; (uint)i < rank; i++) {
if (i > 0)
WriteCommaSpace();
if (i < indexes.Count && indexes[i] == 0)
FormatInt32((int)dims[i]);
else if (i < indexes.Count && i < dims.Count) {
FormatInt32((int)indexes[i]);
OutputWrite("..", BoxedTextColor.Operator);
FormatInt32((int)(indexes[i] + dims[i] - 1));
}
}
}
else {
if (rank == 1)
OutputWrite("*", BoxedTextColor.Operator);
for (uint i = 1; i < rank; i++)
OutputWrite(",", BoxedTextColor.Punctuation);
}
}
OutputWrite(ArrayParenClose, BoxedTextColor.Punctuation);
}
else {
Debug.Assert(aryType.ElementType == ElementType.SZArray);
OutputWrite(ArrayParenOpen, BoxedTextColor.Punctuation);
OutputWrite(ArrayParenClose, BoxedTextColor.Punctuation);
}
}
return;
}
if (type is null)
return;
switch (type.ElementType) {
case ElementType.Void: WriteSystemType("Void", true); break;
case ElementType.Boolean: WriteSystemTypeKeyword("Boolean", "Boolean", true); break;
case ElementType.Char: WriteSystemTypeKeyword("Char", "Char", true); break;
case ElementType.I1: WriteSystemTypeKeyword("SByte", "SByte", true); break;
case ElementType.U1: WriteSystemTypeKeyword("Byte", "Byte", true); break;
case ElementType.I2: WriteSystemTypeKeyword("Int16", "Short", true); break;
case ElementType.U2: WriteSystemTypeKeyword("UInt16", "UShort", true); break;
case ElementType.I4: WriteSystemTypeKeyword("Int32", "Integer", true); break;
case ElementType.U4: WriteSystemTypeKeyword("UInt32", "UInteger", true); break;
case ElementType.I8: WriteSystemTypeKeyword("Int64", "Long", true); break;
case ElementType.U8: WriteSystemTypeKeyword("UInt64", "ULong", true); break;
case ElementType.R4: WriteSystemTypeKeyword("Single", "Single", true); break;
case ElementType.R8: WriteSystemTypeKeyword("Double", "Double", true); break;
case ElementType.String: WriteSystemTypeKeyword("String", "String", false); break;
case ElementType.Object: WriteSystemTypeKeyword("Object", "Object", false); break;
case ElementType.TypedByRef: WriteSystemType("TypedReference", true); break;
case ElementType.I: WriteSystemType("IntPtr", true); break;
case ElementType.U: WriteSystemType("UIntPtr", true); break;
case ElementType.Ptr:
Write(type.Next, typeGenArgs, methGenArgs);
OutputWrite("*", BoxedTextColor.Operator);
break;
case ElementType.ByRef:
OutputWrite(Keyword_ByRef, BoxedTextColor.Keyword);
WriteSpace();
Write(type.Next, typeGenArgs, methGenArgs);
break;
case ElementType.ValueType:
case ElementType.Class:
var cvt = (TypeDefOrRefSig)type;
Write(cvt.TypeDefOrRef);
break;
case ElementType.Var:
case ElementType.MVar:
var gsType = Read(type.ElementType == ElementType.Var ? typeGenArgs : methGenArgs, (int)((GenericSig)type).Number);
if (gsType is not null)
Write(gsType, typeGenArgs, methGenArgs);
else {
var gp = ((GenericSig)type).GenericParam;
if (gp is not null)
Write(gp);
else {
if (type.ElementType == ElementType.MVar) {
OutputWrite("!!", BoxedTextColor.MethodGenericParameter);
OutputWrite(((GenericSig)type).Number.ToString(), BoxedTextColor.MethodGenericParameter);
}
else {
OutputWrite("!", BoxedTextColor.TypeGenericParameter);
OutputWrite(((GenericSig)type).Number.ToString(), BoxedTextColor.TypeGenericParameter);
}
}
}
break;
case ElementType.GenericInst:
var gis = (GenericInstSig?)type;
Debug2.Assert(gis is not null);
if (TypeFormatterUtils.IsSystemNullable(gis)) {
Write(GenericArgumentResolver.Resolve(gis.GenericArguments[0], typeGenArgs, methGenArgs), null, null);
OutputWrite("?", BoxedTextColor.Operator);
}
else if (TypeFormatterUtils.IsSystemValueTuple(gis)) {
OutputWrite(TupleParenOpen, BoxedTextColor.Punctuation);
bool needComma = false;
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < gis.GenericArguments.Count && j < 7; j++) {
if (needComma)
WriteCommaSpace();
needComma = true;
Write(GenericArgumentResolver.Resolve(gis.GenericArguments[j], typeGenArgs, methGenArgs), null, null);
}
if (gis.GenericArguments.Count != 8)
break;
gis = gis.GenericArguments[gis.GenericArguments.Count - 1] as GenericInstSig;
if (gis is null) {
WriteError();
break;
}
}
OutputWrite(TupleParenClose, BoxedTextColor.Punctuation);
}
else {
Write(gis.GenericType, null, null);
OutputWrite(GenericParenOpen, BoxedTextColor.Punctuation);
OutputWrite(Keyword_Of, BoxedTextColor.Keyword);
WriteSpace();
for (int i = 0; i < gis.GenericArguments.Count; i++) {
if (i > 0)
WriteCommaSpace();
Write(GenericArgumentResolver.Resolve(gis.GenericArguments[i], typeGenArgs, methGenArgs), null, null);
}
OutputWrite(GenericParenClose, BoxedTextColor.Punctuation);
}
break;
case ElementType.FnPtr:
var sig = ((FnPtrSig)type).MethodSig;
Write(sig.RetType, typeGenArgs, methGenArgs);
WriteSpace();
OutputWrite(MethodParenOpen, BoxedTextColor.Punctuation);
for (int i = 0; i < sig.Params.Count; i++) {
if (i > 0)
WriteCommaSpace();
Write(sig.Params[i], typeGenArgs, methGenArgs);
}
if (sig.ParamsAfterSentinel is not null) {
if (sig.Params.Count > 0)
WriteCommaSpace();
OutputWrite("...", BoxedTextColor.Punctuation);
for (int i = 0; i < sig.ParamsAfterSentinel.Count; i++) {
WriteCommaSpace();
Write(sig.ParamsAfterSentinel[i], typeGenArgs, methGenArgs);
}
}
OutputWrite(MethodParenClose, BoxedTextColor.Punctuation);
break;
case ElementType.CModReqd:
case ElementType.CModOpt:
case ElementType.Pinned:
Write(type.Next, typeGenArgs, methGenArgs);
break;
case ElementType.End:
case ElementType.Array: // handled above
case ElementType.ValueArray:
case ElementType.R:
case ElementType.SZArray: // handled above
case ElementType.Internal:
case ElementType.Module:
case ElementType.Sentinel:
default:
break;
}
}
finally {
recursionCounter--;
}
}
TypeSig? Read(IList list, int index) {
if ((uint)index < (uint)list.Count)
return list[index];
return null;
}
public void WriteToolTip(ISourceVariable? variable) {
if (variable is null) {
WriteError();
return;
}
var isLocal = variable.IsLocal;
var pd = (variable.Variable as Parameter)?.ParamDef;
var type = variable.Type;
OutputWrite(DescriptionParenOpen, BoxedTextColor.Punctuation);
OutputWrite(isLocal ? dnSpy_Decompiler_Resources.ToolTip_Local : dnSpy_Decompiler_Resources.ToolTip_Parameter, BoxedTextColor.Text);
OutputWrite(DescriptionParenClose, BoxedTextColor.Punctuation);
WriteSpace();
if (type.GetElementType() == ElementType.ByRef) {
type = type.Next;
OutputWrite(Keyword_ByRef, BoxedTextColor.Keyword);
WriteSpace();
}
WriteIdentifier(TypeFormatterUtils.GetName(variable), isLocal ? BoxedTextColor.Local : BoxedTextColor.Parameter);
if (pd is not null)
WriteToken(pd);
WriteSpace();
OutputWrite(Keyword_As, BoxedTextColor.Keyword);
WriteSpace();
Write(type, !isLocal ? ((Parameter)variable.Variable!).ParamDef : null, null, null);
}
public void WriteNamespaceToolTip(string? @namespace) {
if (@namespace is null) {
WriteError();
return;
}
OutputWrite(Keyword_namespace, BoxedTextColor.Keyword);
WriteSpace();
var parts = @namespace.Split(namespaceSeparators);
for (int i = 0; i < parts.Length; i++) {
if (i > 0)
OutputWrite(".", BoxedTextColor.Operator);
OutputWrite(parts[i], BoxedTextColor.Namespace);
}
}
static readonly char[] namespaceSeparators = new char[] { '.' };
void Write(ModuleDef? module) {
try {
if (recursionCounter++ >= TypeFormatterUtils.MAX_RECURSION)
return;
if (module is null) {
OutputWrite("null module", BoxedTextColor.Error);
return;
}
var name = TypeFormatterUtils.GetFileName(module.Location);
OutputWrite(TypeFormatterUtils.FilterName(name), BoxedTextColor.AssemblyModule);
}
finally {
recursionCounter--;
}
}
void WriteModuleName(in FormatterMethodInfo info) {
if (!ShowModuleNames)
return;
Write(info.ModuleDef);
OutputWrite(ModuleNameSeparator, BoxedTextColor.Operator);
return;
}
void WriteModuleName(ModuleDef? module) {
if (module is null)
return;
if (!ShowModuleNames)
return;
Write(module);
OutputWrite(ModuleNameSeparator, BoxedTextColor.Operator);
return;
}
void WriteReturnType(in FormatterMethodInfo info) {
if (!ShowReturnTypes)
return;
if (IsSub(info))
return;
if (!(info.MethodDef is not null && info.MethodDef.IsConstructor)) {
var retInfo = GetReturnTypeInfo(info);
WriteSpace();
OutputWrite(Keyword_As, BoxedTextColor.Keyword);
WriteSpace();
Write(retInfo.returnType, retInfo.paramDef, info.TypeGenericParams, info.MethodGenericParams);
}
}
static bool IsSub(in FormatterMethodInfo info) => GetReturnTypeInfo(info).returnType.RemovePinnedAndModifiers().GetElementType() == ElementType.Void;
static (TypeSig? returnType, ParamDef? paramDef) GetReturnTypeInfo(in FormatterMethodInfo info) {
TypeSig? retType;
ParamDef? retParamDef;
if (info.RetTypeIsLastArgType) {
retType = info.MethodSig.Params.LastOrDefault();
if (info.MethodDef is null)
retParamDef = null;
else {
var l = info.MethodDef.Parameters.LastOrDefault();
retParamDef = l is null ? null : l.ParamDef;
}
}
else {
retType = info.MethodSig.RetType;
retParamDef = info.MethodDef is null ? null : info.MethodDef.Parameters.ReturnParameter.ParamDef;
}
return (retType, retParamDef);
}
void WriteGenericArguments(in FormatterMethodInfo info) {
if (info.MethodSig.GenParamCount > 0) {
if (info.MethodGenericParams is not null)
WriteGenerics(info.MethodGenericParams, BoxedTextColor.MethodGenericParameter, GenericParamContext.Create(info.MethodDef));
else if (info.MethodDef is not null)
WriteGenerics(info.MethodDef.GenericParameters, BoxedTextColor.MethodGenericParameter);
}
}
void WriteMethodParameterList(in FormatterMethodInfo info, string lparen, string rparen) {
if (!ShowParameterTypes && !ShowParameterNames)
return;
OutputWrite(lparen, BoxedTextColor.Punctuation);
int baseIndex = info.MethodSig.HasThis ? 1 : 0;
int count = info.MethodSig.Params.Count;
if (info.RetTypeIsLastArgType && !info.IncludeReturnTypeInArgsList)
count--;
for (int i = 0; i < count; i++) {
if (i > 0)
WriteCommaSpace();
ParamDef? pd;
if (info.MethodDef is not null && baseIndex + i < info.MethodDef.Parameters.Count)
pd = info.MethodDef.Parameters[baseIndex + i].ParamDef;
else
pd = null;
bool isDefault = TypeFormatterUtils.HasConstant(pd, out var constantAttribute);
if (isDefault)
OutputWrite(DefaultParamValueParenOpen, BoxedTextColor.Punctuation);
bool needSpace = false;
var paramType = info.MethodSig.Params[i];
if (ShowParameterNames || ShowParameterTypes) {
if (paramType.GetElementType() == ElementType.ByRef) {
paramType = paramType.Next;
OutputWrite(Keyword_ByRef, BoxedTextColor.Keyword);
WriteSpace();
}
if (pd is not null && pd.CustomAttributes.IsDefined("System.ParamArrayAttribute")) {
OutputWrite(Keyword_params, BoxedTextColor.Keyword);
needSpace = true;
}
}
if (ShowParameterNames) {
if (needSpace)
WriteSpace();
needSpace = true;
if (pd is not null) {
WriteIdentifier(pd.Name, BoxedTextColor.Parameter);
WriteToken(pd);
}
else
WriteIdentifier("A_" + (baseIndex + i).ToString(), BoxedTextColor.Parameter);
}
if (ShowParameterTypes) {
if (ShowParameterNames) {
WriteSpace();
OutputWrite(Keyword_As, BoxedTextColor.Keyword);
}
if (needSpace)
WriteSpace();
needSpace = true;
Write(paramType, pd, info.TypeGenericParams, info.MethodGenericParams);
}
if (ShowParameterLiteralValues && isDefault && TypeFormatterUtils.TryGetConstant(pd, constantAttribute, out var constant)) {
if (needSpace)
WriteSpace();
needSpace = true;
WriteSpace();
OutputWrite("=", BoxedTextColor.Operator);
WriteSpace();
WriteConstant(constant);
}
if (isDefault)
OutputWrite(DefaultParamValueParenClose, BoxedTextColor.Punctuation);
}
OutputWrite(rparen, BoxedTextColor.Punctuation);
}
void WriteGenerics(IList? gps, object gpTokenType) {
if (gps is null || gps.Count == 0)
return;
OutputWrite(GenericParenOpen, BoxedTextColor.Punctuation);
OutputWrite(Keyword_Of, BoxedTextColor.Keyword);
WriteSpace();
for (int i = 0; i < gps.Count; i++) {
if (i > 0)
WriteCommaSpace();
var gp = gps[i];
if (gp.IsCovariant) {
OutputWrite(Keyword_out, BoxedTextColor.Keyword);
WriteSpace();
}
else if (gp.IsContravariant) {
OutputWrite(Keyword_in, BoxedTextColor.Keyword);
WriteSpace();
}
WriteIdentifier(gp.Name, gpTokenType);
WriteToken(gp);
}
OutputWrite(GenericParenClose, BoxedTextColor.Punctuation);
}
void WriteGenerics(IList? gps, object gpTokenType, GenericParamContext gpContext) {
if (gps is null || gps.Count == 0)
return;
OutputWrite(GenericParenOpen, BoxedTextColor.Punctuation);
OutputWrite(Keyword_Of, BoxedTextColor.Keyword);
WriteSpace();
for (int i = 0; i < gps.Count; i++) {
if (i > 0)
WriteCommaSpace();
Write(gps[i], null, null, null);
}
OutputWrite(GenericParenClose, BoxedTextColor.Punctuation);
}
void FormatBoolean(bool value) {
if (value)
OutputWrite(Keyword_true, BoxedTextColor.Keyword);
else
OutputWrite(Keyword_false, BoxedTextColor.Keyword);
}
void FormatChar(char value) {
switch (value) {
case '\r': OutputWrite("vbCr", BoxedTextColor.LiteralField); break;
case '\n': OutputWrite("vbLf", BoxedTextColor.LiteralField); break;
case '\b': OutputWrite("vbBack", BoxedTextColor.LiteralField); break;
case '\f': OutputWrite("vbFormFeed", BoxedTextColor.LiteralField); break;
case '\t': OutputWrite("vbTab", BoxedTextColor.LiteralField); break;
case '\v': OutputWrite("vbVerticalTab", BoxedTextColor.LiteralField); break;
case '\0': OutputWrite("vbNullChar", BoxedTextColor.LiteralField); break;
case '"': OutputWrite("\"\"\"\"c", BoxedTextColor.Char); break;
default:
if (char.IsControl(value))
WriteCharW(value);
else
OutputWrite("\"" + value.ToString() + "\"c", BoxedTextColor.Char);
break;
}
}
void WriteCharW(char value) {
OutputWrite("ChrW", BoxedTextColor.StaticMethod);
OutputWrite("(", BoxedTextColor.Punctuation);
FormatUInt16(value);
OutputWrite(")", BoxedTextColor.Punctuation);
}
void FormatString(string value) {
if (value == string.Empty) {
OutputWrite("\"\"", BoxedTextColor.String);
return;
}
int index = 0;
bool needSep = false;
while (index < value.Length) {
var s = GetSubString(value, ref index);
if (s.Length != 0) {
if (needSep)
WriteStringConcatOperator();
OutputWrite("\"" + s + "\"", BoxedTextColor.String);
needSep = true;
}
if (index < value.Length) {
var c = value[index];
switch (c) {
case '\r':
if (index + 1 < value.Length && value[index + 1] == '\n') {
WriteSpecialConstantString("vbCrLf", ref needSep);
index++;
}
else
WriteSpecialConstantString("vbCr", ref needSep);
break;
case '\n':
WriteSpecialConstantString("vbLf", ref needSep);
break;
case '\b':
WriteSpecialConstantString("vbBack", ref needSep);
break;
case '\f':
WriteSpecialConstantString("vbFormFeed", ref needSep);
break;
case '\t':
WriteSpecialConstantString("vbTab", ref needSep);
break;
case '\v':
WriteSpecialConstantString("vbVerticalTab", ref needSep);
break;
case '\0':
WriteSpecialConstantString("vbNullChar", ref needSep);
break;
default:
if (needSep)
WriteStringConcatOperator();
WriteCharW(c);
break;
}
index++;
needSep = true;
}
}
}
void WriteStringConcatOperator() {
WriteSpace();
OutputWrite("&", BoxedTextColor.Operator);
WriteSpace();
}
void WriteSpecialConstantString(string s, ref bool needSep) {
if (needSep)
WriteStringConcatOperator();
OutputWrite(s, BoxedTextColor.LiteralField);
needSep = true;
}
string GetSubString(string value, ref int index) {
var sb = new StringBuilder();
while (index < value.Length) {
var c = value[index];
bool isSpecial;
switch (c) {
case '"':
sb.Append(c);
isSpecial = false;
break;
case '\r':
case '\n':
case '\b':
case '\f':
case '\t':
case '\v':
case '\0':
// More newline chars
case '\u0085':
case '\u2028':
case '\u2029':
isSpecial = true;
break;
default:
isSpecial = char.IsControl(c);
break;
}
if (isSpecial)
break;
sb.Append(c);
index++;
}
return sb.ToString();
}
string ToFormattedDecimalNumber(string number) => ToFormattedNumber(string.Empty, number, TypeFormatterUtils.DigitGroupSizeDecimal);
string ToFormattedHexNumber(string number) => ToFormattedNumber(HexPrefix, number, TypeFormatterUtils.DigitGroupSizeHex);
string ToFormattedNumber(string prefix, string number, int digitGroupSize) => TypeFormatterUtils.ToFormattedNumber(DigitSeparators, prefix, number, digitGroupSize);
void WriteNumber(string number) => OutputWrite(number, BoxedTextColor.Number);
string ToFormattedSByte(sbyte value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X2"));
}
string ToFormattedByte(byte value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X2"));
}
string ToFormattedInt16(short value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X4"));
}
string ToFormattedUInt16(ushort value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X4"));
}
string ToFormattedInt32(int value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X8"));
}
string ToFormattedUInt32(uint value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X8"));
}
string ToFormattedInt64(long value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X16"));
}
string ToFormattedUInt64(ulong value) {
if (UseDecimal)
return ToFormattedDecimalNumber(value.ToString(cultureInfo));
else
return ToFormattedHexNumber(value.ToString("X16"));
}
void FormatSingle(float value) {
if (float.IsNaN(value))
OutputWrite(TypeFormatterUtils.NaN, BoxedTextColor.Number);
else if (float.IsNegativeInfinity(value))
OutputWrite(TypeFormatterUtils.NegativeInfinity, BoxedTextColor.Number);
else if (float.IsPositiveInfinity(value))
OutputWrite(TypeFormatterUtils.PositiveInfinity, BoxedTextColor.Number);
else
OutputWrite(value.ToString(cultureInfo), BoxedTextColor.Number);
}
void FormatDouble(double value) {
if (double.IsNaN(value))
OutputWrite(TypeFormatterUtils.NaN, BoxedTextColor.Number);
else if (double.IsNegativeInfinity(value))
OutputWrite(TypeFormatterUtils.NegativeInfinity, BoxedTextColor.Number);
else if (double.IsPositiveInfinity(value))
OutputWrite(TypeFormatterUtils.PositiveInfinity, BoxedTextColor.Number);
else
OutputWrite(value.ToString(cultureInfo), BoxedTextColor.Number);
}
void FormatSByte(sbyte value) => WriteNumber(ToFormattedSByte(value));
void FormatByte(byte value) => WriteNumber(ToFormattedByte(value));
void FormatInt16(short value) => WriteNumber(ToFormattedInt16(value));
void FormatUInt16(ushort value) => WriteNumber(ToFormattedUInt16(value));
void FormatInt32(int value) => WriteNumber(ToFormattedInt32(value));
void FormatUInt32(uint value) => WriteNumber(ToFormattedUInt32(value));
void FormatInt64(long value) => WriteNumber(ToFormattedInt64(value));
void FormatUInt64(ulong value) => WriteNumber(ToFormattedUInt64(value));
void FormatDecimal(decimal value) => OutputWrite(value.ToString(cultureInfo), BoxedTextColor.Number);
}
}