Xamarin Public Jenkins (auto-signing) 95fdb59ea6 Imported Upstream version 6.6.0.89
Former-commit-id: b39a328747c2f3414dc52e009fb6f0aa80ca2492
2019-09-24 08:53:40 +00:00

1090 lines
41 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Mono.Cecil;
using Mono.Collections.Generic;
using Mono.Documentation.Util;
namespace Mono.Documentation.Updater
{
public class FSharpFormatter : MemberFormatter
{
private static readonly Dictionary<string, string> operators = new Dictionary<string, string>()
{
{ "op_Nil" ,"[]" },
{ "op_Cons" ,"::" },
{ "op_Addition" ,"+" },
{ "op_Subtraction" ,"-" },
{ "op_Multiply" ,"*" },
{ "op_Division" ,"/" },
{ "op_Append" ,"@" },
{ "op_Concatenate" ,"^" },
{ "op_Modulus" ,"%" },
{ "op_BitwiseAnd" ,"&&&" },
{ "op_BitwiseOr" ,"|||" },
{ "op_ExclusiveOr" ,"^^^" },
{ "op_LeftShift" ,"<<<" },
{ "op_LogicalNot" ,"~~~" },
{ "op_RightShift" ,">>>" },
{ "op_UnaryPlus" ,"~+" },
{ "op_UnaryNegation" ,"~-" },
{ "op_Equality" ,"=" },
{ "op_LessThanOrEqual" ,"<=" },
{ "op_GreaterThanOrEqual" ,">=" },
{ "op_LessThan" ,"<" },
{ "op_GreaterThan" ,">" },
{ "op_Dynamic" ,"?" },
{ "op_DynamicAssignment" ,"?<-" },
{ "op_PipeRight" ,"|>" },
{ "op_PipeRight2" ,"||>" },
{ "op_PipeRight3" ,"|||>" },
{ "op_PipeLeft" ,"<|" },
{ "op_PipeLeft2" ,"<||" },
{ "op_PipeLeft3" ,"<|||" },
{ "op_Dereference" ,"!" },
{ "op_ComposeRight" ,">>" },
{ "op_ComposeLeft" ,"<<" },
{ "op_Quotation" ,"<@ @>" },
{ "op_QuotationUntyped" ,"<@@ @@>" },
{ "op_AdditionAssignment" ,"+=" },
{ "op_SubtractionAssignment" ,"-=" },
{ "op_MultiplyAssignment" ,"*=" },
{ "op_DivisionAssignment" ,"/=" },
{ "op_Range" ,".." },
{ "op_RangeStep" ,".. .." },
};
// Other combinations of operator characters that are not listed here can be used as operators and have names
// that are made up by concatenating names for the individual characters from the following table.
// For example, +! becomes op_PlusBang
private static readonly Dictionary<string, string> combinatedOperators = new Dictionary<string, string>()
{
{"Greater" , ">"},
{"Less" , "<"},
{"Plus" , "+"},
{"Minus" , "-"},
{"Multiply", "*"},
{"Equals" , "="},
{"Twiddle" , "~"},
{"Percent" , "%"},
{"Dot" , "."},
{"Amp" , "&"},
{"Bar" , "|"},
{"At" , "@"},
{"Hash" , "#"},
{"Hat" , "^"},
{"Bang" , "!"},
{"Qmark" , "?"},
{"Divide" , "/"},
{"Colon" , ":"},
{"LParen" , "("},
{"Comma" , ","},
{"RParen" , ")"},
{"LBrack" , "["},
{"RBrack" , "]"},
};
private static readonly Dictionary<string, string> typeAbbreviations = new Dictionary<string, string>()
{
{"System.Boolean", "bool"},
{"System.Byte", "byte"},
{"System.SByte", "sbyte"},
{"System.Int16", "int16"},
{"System.UInt16", "uint16"},
{"System.Int32", "int"},
{"System.UInt32", "uint32"},
{"System.Int64", "int64"},
{"System.UInt64", "uint64"},
{"System.IntPtr", "nativeint"},
{"System.UIntPtr", "unativeint"},
{"System.Char", "char"},
{"System.String", "string"},
{"System.Decimal", "decimal"},
{"System.Void", "unit"},
{"System.Single", "single"},// Can be float32
{"System.Double", "double"},// Can be float
{"System.Object", "obj"},
{"Microsoft.FSharp.Core.Unit", "unit"},
{"Microsoft.FSharp.Core.FSharpOption`1", "option"},
{"System.Collections.Generic.IEnumerable`1", "seq"},
{"Microsoft.FSharp.Core.FSharpRef`1", "ref"},
};
private static readonly HashSet<string> fSharpPrefixes = new HashSet<string>()
{
"Microsoft.FSharp.Collections.FSharp",
"Microsoft.FSharp.Core.FSharp",
"Microsoft.FSharp.Control.FSharp",
"Microsoft.FSharp.Data.FSharp",
"Microsoft.FSharp.Linq.FSharp",
"Microsoft.FSharp.NativeInterop.FSharp",
"Microsoft.FSharp.Quotations.FSharp",
"Microsoft.FSharp.Reflection.FSharp",
};
private static readonly HashSet<string> ignoredValueTypeInterfaces = new HashSet<string>()
{
"System.IEquatable`1",
"System.Collections.IStructuralEquatable",
"System.IComparable`1",
"System.IComparable",
"System.Collections.IStructuralComparable",
};
private GenericParameterState genericParameterState = GenericParameterState.None;
protected string GetFSharpType(TypeReference type)
{
string typeToCompare = type.FullName;
var fullName = type.IsGenericInstance ? type.GetElementType().FullName : type.FullName;
if (typeAbbreviations.ContainsKey(fullName))
{
typeToCompare = typeAbbreviations[fullName];
}
else
{
var prefixToRemove = fSharpPrefixes.FirstOrDefault(i => fullName.StartsWith(i));
if (prefixToRemove != null)
{
typeToCompare = base.AppendTypeName(new StringBuilder(), type.FullName.Replace(prefixToRemove, "")).ToString();
}
if (type.IsGenericParameter)
{
typeToCompare = GetGenericName(type.Name);
}
}
return typeToCompare == type.FullName ? null : typeToCompare;
}
protected override StringBuilder AppendTypeName(StringBuilder buf, TypeReference type, DynamicParserContext context)
{
string fSharpType = GetFSharpType(type);
if (fSharpType != null)
{
return buf.Append(fSharpType);
}
if (type.IsGenericParameter)
{
return buf.Append(GetGenericName(type.Name));
}
return base.AppendTypeName(buf, type, context);
}
protected override string GetTypeDeclaration(TypeDefinition type)
{
string visibility = GetTypeVisibility(type.Attributes);
if (visibility == null)
return null;
StringBuilder buf = new StringBuilder();
if (IsModule(type))
{
AppendModuleDeclaraion(buf, type);
return buf.ToString();
}
if (IsDiscriminatedUnionCase(type))
{
AppendDiscriminatedUnionCase(buf, type);
return buf.ToString();
}
buf.Append("type ");
buf.Append(visibility);
buf.Append(GetTypeName(type));
buf.Append(" = ");
if (IsRecord(type))
{
buf.Append("{}");
return buf.ToString();
}
if (IsDiscriminatedUnion(type))
{
return buf.ToString();
}
if (type.IsEnum)
{
return buf.ToString();
}
buf.Append($"{GetTypeKind(type)}");
if (DocUtils.IsDelegate(type))
{
buf.Append(" ");
MethodDefinition invoke = type.GetMethod("Invoke");
AppendFunctionSignature(buf, invoke);
return buf.ToString();
}
AppendBaseType(buf, type);
foreach (var interfaceImplementation in type.Interfaces)
{
var resolvedInterface = interfaceImplementation.InterfaceType.Resolve ();
if (type.IsValueType
&& ignoredValueTypeInterfaces.Any(i => interfaceImplementation.InterfaceType.FullName.StartsWith(i))
|| (resolvedInterface != null && resolvedInterface.IsNotPublic))
continue;
buf.Append($"{GetLineEnding()}{Consts.Tab}interface ");
AppendTypeName(buf, GetTypeName(interfaceImplementation.InterfaceType));
}
return buf.ToString();
}
private void AppendDiscriminatedUnionCase(StringBuilder buf, TypeDefinition type)
{
buf.Append(GetName(type));
buf.Append(" : ");
var constructor = type.Methods.First(i => i.Name == ".ctor");
AppendParameters(buf, constructor, constructor.Parameters);
buf.Append(" -> ");
buf.Append(GetName(type.DeclaringType));
}
private void AppendBaseType(StringBuilder buf, TypeDefinition type)
{
TypeReference basetype = type.BaseType;
if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType)
basetype = null;
if (basetype != null)
{
buf.Append($"{GetLineEnding()}{Consts.Tab}inherit ");
buf.Append(GetName(basetype));
}
}
private void AppendFunctionSignature(StringBuilder buf, MethodDefinition method)
{
bool isTuple = method.Parameters.Count == 1 && IsTuple(method.Parameters[0].ParameterType);
if (isTuple)
buf.Append("(");
AppendParameters(buf, method, method.Parameters);
if (isTuple)
buf.Append(")");
buf.Append(" -> ");
if (method.IsConstructor)
buf.Append(GetTypeName(method.DeclaringType));
else
{
if (IsFSharpFunction(method.ReturnType))
buf.Append("(");
buf.Append(GetTypeName(method.ReturnType));
if (IsFSharpFunction(method.ReturnType))
buf.Append(")");
}
}
private string GetTypeKind(TypeDefinition type)
{
if (type.IsInterface)
{
return "interface";
}
if (type.IsValueType)
{
return "struct";
}
if (DocUtils.IsDelegate(type))
{
return "delegate of";
}
return "class";
}
protected override StringBuilder AppendGenericType(StringBuilder buf, TypeReference type, DynamicParserContext context, bool appendGeneric = true)
{
List<TypeReference> decls = DocUtils.GetDeclaringTypes(
type is GenericInstanceType ? type.GetElementType() : type);
List<TypeReference> genArgs = GetGenericArguments(type);
int argIdx = 0;
int displayedInParentArguments = 0;
bool insertNested = false;
foreach (var decl in decls)
{
TypeReference declDef = decl.Resolve() ?? decl;
if (insertNested)
{
buf.Append(NestedTypeSeparator);
}
insertNested = true;
bool isTuple = IsTuple(type)
// If this is a declaration of `Tuple` type
// treat it as an ordinary type
&& !(type is TypeDefinition);
bool isFSharpFunction = IsFSharpFunction(type);
if (!isTuple && !isFSharpFunction)
AppendTypeName(buf, declDef, context);
int argumentCount = DocUtils.GetGenericArgumentCount(declDef);
int notYetDisplayedArguments = argumentCount - displayedInParentArguments;
displayedInParentArguments = argumentCount;// nested TypeReferences have parents' generic arguments, but we shouldn't display them
if (notYetDisplayedArguments > 0)
{
if (!isTuple && !isFSharpFunction)
buf.Append(GenericTypeContainer[0]);
if (isFSharpFunction && genericParameterState == GenericParameterState.WithinTuple)
buf.Append("(");
var origState = MemberFormatterState;
var genericParameterStateOrigState = genericParameterState;
MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
genericParameterState = isTuple ? GenericParameterState.WithinTuple : GenericParameterState.None;
for (int i = 0; i < notYetDisplayedArguments; ++i)
{
if (i > 0)
buf.Append(isTuple ? " * " : isFSharpFunction ? " -> " : ", ");
var genArg = genArgs[argIdx++];
var genericParameter = genArg as GenericParameter;
if (genericParameter != null && IsFlexibleType(genericParameter))
{
buf.Append("#");// replace genericParameter which is a flexible type with its constraint type
#if NEW_CECIL
_AppendTypeName(buf, genericParameter.Constraints[0].ConstraintType, context);
#else
_AppendTypeName(buf, genericParameter.Constraints[0], context);
#endif
}
else
{
_AppendTypeName(buf, genArg, context);
}
}
MemberFormatterState = origState;
genericParameterState = genericParameterStateOrigState;
if (MemberFormatterState == MemberFormatterState.None)
{
AppendConstraints(buf,
genArgs.GetRange(0, notYetDisplayedArguments)
.SafeCast<GenericParameter>()
.ToList());
}
if (!isTuple && !isFSharpFunction)
buf.Append(GenericTypeContainer[1]);
if (isFSharpFunction && genericParameterState == GenericParameterState.WithinTuple)
buf.Append(")");
}
}
return buf;
}
private void AppendModuleDeclaraion(StringBuilder buf, TypeDefinition type)
{
buf.Append("module ");
buf.Append(GetModuleName(type));
}
private string GetModuleName(TypeDefinition type)
{
var moduleName = GetTypeName(type);
const string moduleKeyWord = "Module";
if (moduleName.EndsWith(moduleKeyWord))
{
moduleName = moduleName.Substring(0, moduleName.Length - moduleKeyWord.Length);
}
return moduleName;
}
protected override StringBuilder AppendGenericTypeConstraints(StringBuilder buf, TypeReference type)
{
return buf;
}
private void AppendConstraints(StringBuilder buf, IList<GenericParameter> genArgs)
{
var origMemberFormatterState = MemberFormatterState;
MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
List<string> constraintStrings = new List<string>();
foreach (GenericParameter genArg in genArgs.Where(i => !IsFlexibleType(i)))
{
GenericParameterAttributes attrs = genArg.Attributes;
#if NEW_CECIL
Mono.Collections.Generic.Collection<GenericParameterConstraint> constraints = genArg.Constraints;
#else
IList<TypeReference> constraints = genArg.Constraints;
#endif
if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
continue;
bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
if (!isref && !isvt && !isnew && constraints.Count == 0)
continue;
var genericName = GetGenericName(genArg.Name);
if (isref)
{
// May be "not struct". We don't know it here
constraintStrings.Add($"{genericName} : null");
}
else if (isvt)
{
constraintStrings.Add($"{genericName} : struct");
}
if (constraints.Count > 0 && !isvt)
{
foreach (var typeReference in constraints)
{
#if NEW_CECIL
constraintStrings.Add($"{genericName} :> {GetTypeName(typeReference.ConstraintType)}");
#else
constraintStrings.Add($"{genericName} :> {GetTypeName(typeReference)}");
#endif
}
}
if (isnew && !isvt)
{
constraintStrings.Add($"{genericName} : (new : unit -> {GetTypeName(genArg)})");
}
}
if (constraintStrings.Count > 0)
{
buf.Append($" (requires {string.Join(" and ", constraintStrings)})");
}
MemberFormatterState = origMemberFormatterState;
}
protected override string GetConstructorDeclaration(MethodDefinition constructor)
{
StringBuilder buf = new StringBuilder();
if (constructor.Parameters.Count == 0)
return null;
if (AppendVisibility(buf, constructor) == null)
return null;
buf.Append("new ");
buf.Append(GetTypeName(constructor.DeclaringType));
buf.Append(" : ");
AppendFunctionSignature(buf, constructor);
return buf.ToString();
}
protected override string GetMethodDeclaration(MethodDefinition method)
{
var visibilityBuf = new StringBuilder();
if (AppendVisibility(visibilityBuf, method) == null)
return null;
string visibility = visibilityBuf.ToString();
StringBuilder buf = new StringBuilder();
var kind = GetMethodKind(method);
switch (kind)
{
case FSharpMethodKind.InModule:
AppendModuleMethod(buf, method);
break;
case FSharpMethodKind.Static:
AppendStaticMethod(buf, method, visibility);
break;
case FSharpMethodKind.Override:
AppendOverrideMethod(buf, method, visibility);
break;
case FSharpMethodKind.Abstract:
AppendAbstractMethod(buf, method, visibility);
break;
case FSharpMethodKind.Virtual:
AppendVirtualMethod(buf, method, visibility);
break;
case FSharpMethodKind.Common:
AppendCommonMethod(buf, method, visibility);
break;
default:
throw new ArgumentOutOfRangeException();
}
return buf.ToString();
}
private FSharpMethodKind GetMethodKind(MethodDefinition method)
{
if (IsModule(method.DeclaringType))
return FSharpMethodKind.InModule;
if (method.IsStatic)
return FSharpMethodKind.Static;
if (IsOverride(method))
return FSharpMethodKind.Override;
if (method.IsAbstract)
return FSharpMethodKind.Abstract;
if (method.IsVirtual)
return FSharpMethodKind.Virtual;
return FSharpMethodKind.Common;
}
private void AppendModuleMethod(StringBuilder buf, MethodDefinition method)
{
if (!IsOperator(method))
{
buf.Append($"{GetModuleName(method.DeclaringType)}.");
}
AppendMethodDeclarationEnding(buf, method);
}
private void AppendVirtualMethod(StringBuilder buf, MethodDefinition method, string visibility)
{
AppendAbstractMethod(buf, method, visibility);
buf.Append(GetLineEnding());
AppendOverrideMethod(buf, method, visibility);
}
private void AppendStaticMethod(StringBuilder buf, MethodDefinition method, string visibility)
{
buf.Append("static member ");
buf.Append(visibility);
AppendMethodDeclarationEnding(buf, method);
}
private void AppendAbstractMethod(StringBuilder buf, MethodDefinition method, string visibility)
{
buf.Append("abstract member ");
buf.Append(visibility);
AppendMethodDeclarationEnding(buf, method);
}
private void AppendOverrideMethod(StringBuilder buf, MethodDefinition method, string visibility)
{
buf.Append("override ");
buf.Append(visibility);
buf.Append("this.");
AppendMethodDeclarationEnding(buf, method);
}
private void AppendCommonMethod(StringBuilder buf, MethodDefinition method, string visibility)
{
buf.Append("member ");
buf.Append(visibility);
buf.Append("this.");
AppendMethodDeclarationEnding(buf, method);
}
private void AppendMethodDeclarationEnding(StringBuilder buf, MethodDefinition method)
{
AppendMethodName(buf, method);
buf.Append(" : ");
AppendFunctionSignature(buf, method);
AppendConstraints(buf, method.GenericParameters);
}
protected override StringBuilder AppendMethodName(StringBuilder buf, MethodDefinition method)
{
if (IsOperator(method))
{
// this is an operator
if (TryAppendOperatorName(buf, method))
return buf;
return base.AppendMethodName(buf, method);
}
var compilationSourceNameAttribute = method.CustomAttributes.FirstOrDefault
(i => i.AttributeType.FullName == "Microsoft.FSharp.Core.CompilationSourceNameAttribute");
if (compilationSourceNameAttribute != null)
return buf.Append(compilationSourceNameAttribute.ConstructorArguments.First().Value);
return buf.Append(method.Name);
}
protected override StringBuilder AppendGenericMethodConstraints(StringBuilder buf, MethodDefinition method)
{
return buf;
}
protected override StringBuilder AppendRefTypeName(StringBuilder buf, TypeReference type, DynamicParserContext context)
{
return buf;
}
protected override StringBuilder AppendModifiers(StringBuilder buf, MethodDefinition method)
{
return buf;
}
private bool IsOverride(MethodDefinition method)
{
return IsOverride(method.DeclaringType, method);
}
private bool IsOverride(TypeDefinition type, MethodDefinition method)
{
if (type == null || type.BaseType == null)
return false;
var baseType = type.BaseType.Resolve();
if (baseType != null && baseType.Methods.Any(i => i.Name == method.Name))
return true;
return IsOverride(type.BaseType.Resolve(), method);
}
protected override StringBuilder AppendGenericMethod(StringBuilder buf, MethodDefinition method)
{
return buf;
}
protected void AppendRecordParameter(StringBuilder buf, PropertyDefinition property)
{
bool hasParameterName = !string.IsNullOrEmpty(property.Name);
if (property.SetMethod != null && property.SetMethod.IsPublic)
buf.Append("mutable ");
if (hasParameterName)
{
buf.Append(property.Name.TrimEnd('@'));
}
if (property.PropertyType.FullName != "System.Object")
{
var typeName = GetTypeName(property.PropertyType, new DynamicParserContext(property));
if (hasParameterName)
buf.Append(" : ");
buf.Append(typeName);
}
}
protected override StringBuilder AppendParameters(StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
{
var curryBorders = GetCurryBorders(method);
if (parameters.Count > 0)
{
bool isExtensionMethod = IsExtensionMethod(method);
if (!isExtensionMethod)
{// If it's an extension method, first parameter is ignored
AppendParameter(buf, parameters[0]);
}
for (int i = 1; i < parameters.Count; ++i)
{
if (!(isExtensionMethod && i == 1))
{
if (curryBorders.Contains(i))
buf.Append(" -> ");
else
buf.Append(" * ");
}
AppendParameter(buf, parameters[i]);
}
}
else
{
buf.Append("unit");
}
return buf;
}
private void AppendParameter(StringBuilder buf, ParameterDefinition parameter)
{
bool isFSharpFunction = IsFSharpFunction(parameter.ParameterType);
if (isFSharpFunction)
buf.Append("(");
var typeName = GetTypeName(parameter.ParameterType, new DynamicParserContext(parameter));
buf.Append(typeName);
if (isFSharpFunction)
buf.Append(")");
}
protected override string GetPropertyDeclaration(PropertyDefinition property)
{
string getVisible = null;
if (DocUtils.IsAvailablePropertyMethod(property.GetMethod))
getVisible = AppendVisibility(new StringBuilder(), property.GetMethod)?.ToString();
string setVisible = null;
if (DocUtils.IsAvailablePropertyMethod(property.SetMethod))
setVisible = AppendVisibility(new StringBuilder(), property.SetMethod)?.ToString();
if (setVisible == null && getVisible == null)
return null;
bool isField = GetFSharpFlags(property.CustomAttributes).Any(i => i == SourceConstructFlags.Field);
StringBuilder buf = new StringBuilder();
if (IsRecord(property.DeclaringType))
{
AppendRecordParameter(buf, property);
return buf.ToString();
}
if (IsModule(property.DeclaringType))
{
buf.Append($"{GetName(property.DeclaringType)}.");
}
else
{
if (isField)
buf.Append("val ");
else
buf.Append("member this.");
}
buf.Append(DocUtils.GetPropertyName(property, NestedTypeSeparator));
if (property.Parameters.Count != 0)
{
buf.Append("(");
AppendParameters(buf, property.GetMethod ?? property.SetMethod, property.Parameters);
buf.Append(")");
}
buf.Append(" : ");
buf.Append(GetTypeName(property.PropertyType));
if (getVisible != null && setVisible != null)
{
buf.Append(" with get, set");
}
return buf.ToString();
}
protected override string GetFieldDeclaration(FieldDefinition field)
{
TypeDefinition declType = (TypeDefinition)field.DeclaringType;
if (declType.IsEnum && field.Name == "value__")
return null; // This member of enums aren't documented.
var visibility = GetFieldVisibility(field);
if (visibility == null)
return null;
var buf = new StringBuilder();
if (declType.IsEnum)
{
buf.Append(field.Name);
if (field.IsLiteral)
{
buf.Append($" = {field.Constant}");
}
return buf.ToString();
}
if (field.IsStatic && !field.IsLiteral)
buf.Append(" static");
buf.Append("val mutable");
if (!string.IsNullOrEmpty(visibility))
buf.Append(" ");
buf.Append(visibility);
buf.Append(" ");
buf.Append(field.Name);
buf.Append(" : ");
buf.Append(GetTypeName(field.FieldType, new DynamicParserContext(field)));
return buf.ToString();
}
protected override string GetEventDeclaration(EventDefinition e)
{
StringBuilder buf = new StringBuilder();
StringBuilder visibilityBuf = new StringBuilder();
if (AppendVisibility(visibilityBuf, e.AddMethod) == null)
{
return null;
}
buf.Append("member this.");
if (visibilityBuf.Length > 0)
buf.Append(visibilityBuf).Append(' ');
buf.Append(e.Name).Append(" : ");
buf.Append(GetTypeName(e.EventType, new DynamicParserContext(e.AddMethod.Parameters[0]))).Append(' ');
return buf.ToString();
}
private static IEnumerable<SourceConstructFlags> GetFSharpFlags(Collection<CustomAttribute> customAttributes)
{
foreach (var attribute in customAttributes)
{
if (attribute.AttributeType.Name.Contains("CompilationMapping"))
{
var sourceConstructFlags = attribute.ConstructorArguments.Where(
i => i.Type.FullName == "Microsoft.FSharp.Core.SourceConstructFlags");
foreach (var customAttributeArgument in sourceConstructFlags)
{
var constructFlags = (SourceConstructFlags)customAttributeArgument.Value;
yield return constructFlags;
}
}
}
}
private static string GetGenericName(string name)
{
var trimmedName = name.TrimStart('T');
if (trimmedName.Length == 0 || !Regex.IsMatch(trimmedName, @"^[a-zA-Z]+$"))
trimmedName = name;
return $"\'{trimmedName}";
}
/// <summary>
/// Get sequencing of curried arguments
/// </summary>
/// <returns>Positions between arguments which are curried</returns>
protected HashSet<int> GetCurryBorders(MethodDefinition method)
{
var compilationArgumentCounts = GetCustomAttribute(method.CustomAttributes,
"CompilationArgumentCountsAttribute");
int[] curryCounts = { };
if (compilationArgumentCounts != null)
{
var customAttributeArguments =
(CustomAttributeArgument[])compilationArgumentCounts.ConstructorArguments[0].Value;
curryCounts = customAttributeArguments.Select(i => (int)i.Value).ToArray();
}
var curryBorders = new HashSet<int>();
int sum = 0;
foreach (var curryCount in curryCounts)
{
sum += curryCount;
curryBorders.Add(sum);
}
return curryBorders;
}
private CustomAttribute GetCustomAttribute(Collection<CustomAttribute> customAttributes, string name)
{
return customAttributes.SingleOrDefault(i => i.AttributeType.Name == name);
}
protected bool TryAppendOperatorName(StringBuilder buf, MethodDefinition method)
{
if (!IsOperator(method))
return false;
if (operators.ContainsKey(method.Name))
{
buf.Append($"( {operators[method.Name]} )");
return true;
}
if (TryAppendCombinatedOperandName(buf, method))
return true;
return false;
}
private static bool TryAppendCombinatedOperandName(StringBuilder buf, MethodDefinition method)
{
var oldName = method.Name.Remove(0, 3);
var newName = new StringBuilder();
bool found;
do
{
found = false;
foreach (var op in combinatedOperators)
{
if (oldName.StartsWith(op.Key))
{
oldName = oldName.Remove(0, op.Key.Length);
newName.Append(op.Value);
found = true;
}
}
} while (found);
if (oldName.Length == 0)
{
buf.Append($"( {newName} )");
return true;
}
return false;
}
protected override StringBuilder AppendPointerTypeName(StringBuilder buf, TypeReference type, DynamicParserContext context)
{
TypeSpecification spec = type as TypeSpecification;
buf.Append("nativeptr<");
_AppendTypeName(buf, spec != null ? spec.ElementType : type.GetElementType(), context);
buf.Append(">");
return buf;
}
#region "Is" methods
private static bool IsOperator(MethodDefinition method)
{
return method.Name.StartsWith("op_", StringComparison.Ordinal);
}
private static bool IsFSharpFunction(TypeReference type)
{
return type.FullName.StartsWith("Microsoft.FSharp.Core.FSharpFunc`");
}
private static bool IsTuple(TypeReference type)
{
return type.FullName.StartsWith("System.Tuple");
}
private bool IsFlexibleType(GenericParameter genericParameter)
{
#if NEW_CECIL
return genericParameter.Constraints.Count == 1 && GetFSharpType(genericParameter.Constraints[0].ConstraintType.GetElementType()) != null;
#else
return genericParameter.Constraints.Count == 1 && GetFSharpType(genericParameter.Constraints[0].GetElementType()) != null;
#endif
}
private static bool IsModule(TypeDefinition type)
{
var fSharpFlags = GetFSharpFlags(type.CustomAttributes);
return fSharpFlags.Any(i => i == SourceConstructFlags.Module);
}
private static bool IsDiscriminatedUnion(TypeDefinition type)
{
var fSharpFlags = GetFSharpFlags(type.CustomAttributes);
return fSharpFlags.Any(i => i == SourceConstructFlags.SumType);
}
private static bool IsDiscriminatedUnionCase(TypeDefinition type)
{
return type.DeclaringType != null && IsDiscriminatedUnion(type.DeclaringType);
}
private static bool IsRecord(TypeDefinition type)
{
var fSharpFlags = GetFSharpFlags(type.CustomAttributes);
return fSharpFlags.Any(i => i == SourceConstructFlags.RecordType);
}
protected static bool IsExtensionMethod(MethodDefinition method)
{
var firstParameter = method.Parameters.FirstOrDefault();
return firstParameter != null && firstParameter.Name == "this";
}
#endregion
#region Visibility
protected override StringBuilder AppendVisibility(StringBuilder buf, MethodDefinition method)
{
if (method.IsPublic
|| method.IsFamily
|| method.IsFamilyOrAssembly)
return buf.Append("");
return null;
}
private static string GetTypeVisibility(TypeAttributes ta)
{
switch (ta & TypeAttributes.VisibilityMask)
{
case TypeAttributes.Public:
case TypeAttributes.NestedPublic:
return "";
}
return null;
}
private static string GetFieldVisibility(FieldDefinition field)
{
if (field.IsPublic
|| field.IsFamily
|| field.IsFamilyOrAssembly)
{
return "";
}
return null;
}
#endregion
#region Supported
public override bool IsSupported(TypeReference tref)
{
if (tref.DeclaringType != null
&& IsDiscriminatedUnion(tref.DeclaringType.Resolve())
&& tref.Name == "Tags")
{
return false;
}
return true;
}
public override bool IsSupported(MemberReference mref)
{
if (mref.DeclaringType != null && IsDiscriminatedUnion(mref.DeclaringType.Resolve()))
{
var property = mref as PropertyDefinition;
if (property?.GetMethod != null)
{
var fSharpFlags = GetFSharpFlags(property.GetMethod.CustomAttributes);
if (fSharpFlags.Any(i => i == SourceConstructFlags.UnionCase))// For descriminated unions show only properties with UnionCase attribute
return true;
}
return false;
}
switch (mref)
{
case MethodDefinition method:
return !(method.HasCustomAttributes && method.CustomAttributes.Any(
ca => ca.GetDeclaringType() ==
"System.Diagnostics.Contracts.ContractInvariantMethodAttribute"
|| ca.GetDeclaringType() ==
Consts.CompilerGeneratedAttribute))
&& AppendVisibility(new StringBuilder(), method) != null;
}
return true;
}
#endregion
#region Private types
// Copied from F# Core
private enum SourceConstructFlags
{
/// <summary>Indicates that the compiled entity has no relationship to an element in F# source code.</summary>
None,
/// <summary>Indicates that the compiled entity is part of the representation of an F# union type declaration.</summary>
SumType,
/// <summary>Indicates that the compiled entity is part of the representation of an F# record type declaration.</summary>
RecordType,
/// <summary>Indicates that the compiled entity is part of the representation of an F# class or other object type declaration.</summary>
ObjectType,
/// <summary>Indicates that the compiled entity is part of the representation of an F# record or union case field declaration.</summary>
Field,
/// <summary>Indicates that the compiled entity is part of the representation of an F# exception declaration.</summary>
Exception,
/// <summary>Indicates that the compiled entity is part of the representation of an F# closure.</summary>
Closure,
/// <summary>Indicates that the compiled entity is part of the representation of an F# module declaration.</summary>
Module,
/// <summary>Indicates that the compiled entity is part of the representation of an F# union case declaration.</summary>
UnionCase,
/// <summary>Indicates that the compiled entity is part of the representation of an F# value declaration.</summary>
Value,
/// <summary>The mask of values related to the kind of the compiled entity.</summary>
KindMask = 31,
/// <summary>Indicates that the compiled entity had private or internal representation in F# source code.</summary>
NonPublicRepresentation
}
private enum GenericParameterState
{
None,
WithinTuple
}
private enum FSharpMethodKind
{
InModule,
Static,
Override,
Abstract,
Virtual,
Common,
}
#endregion
}
}