diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs
index beb0d60d4799af..cab9d351cf6bb2 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs
@@ -19,7 +19,7 @@ internal partial record ArrayValue
public static MultiValue Create(MultiValue size, TypeDesc elementType)
{
MultiValue result = MultiValueLattice.Top;
- foreach (var sizeValue in size.AsEnumerable ())
+ foreach (var sizeValue in size.AsEnumerable())
{
result = MultiValueLattice.Meet(result, new MultiValue(new ArrayValue(sizeValue, elementType)));
}
@@ -92,7 +92,7 @@ public override SingleValue DeepCopy()
// Since it's possible to store a reference to array as one of its own elements
// simple deep copy could lead to endless recursion.
// So instead we simply disallow arrays as element values completely - and treat that case as "too complex to analyze".
- foreach (SingleValue v in kvp.Value.Value.AsEnumerable ())
+ foreach (SingleValue v in kvp.Value.Value.AsEnumerable())
{
System.Diagnostics.Debug.Assert(v is not ArrayValue);
}
@@ -123,7 +123,7 @@ public override string ToString()
result.Append(element.Key);
result.Append(",(");
bool firstValue = true;
- foreach (var v in element.Value.Value.AsEnumerable ())
+ foreach (var v in element.Value.Value.AsEnumerable())
{
if (firstValue)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/CompilerGeneratedState.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/CompilerGeneratedState.cs
index 24707c75f8c293..a96ccdcf5e94ee 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/CompilerGeneratedState.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/CompilerGeneratedState.cs
@@ -346,15 +346,15 @@ referencedMethod.OwningType is MetadataType generatedType &&
/// Attempts to reverse the process of the compiler's alpha renaming. So if the original code was
/// something like this:
///
- /// void M<T> () {
- /// Action a = () => { Console.WriteLine (typeof (T)); };
+ /// void M<T>() {
+ /// Action a = () => { Console.WriteLine(typeof(T)); };
/// }
///
/// The compiler will generate a nested class like this:
///
/// class <>c__DisplayClass0<T> {
- /// public void <M>b__0 () {
- /// Console.WriteLine (typeof (T));
+ /// public void <M>b__0() {
+ /// Console.WriteLine(typeof(T));
/// }
/// }
///
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs
index 870a969c7e2b90..9c5927a7532c03 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs
@@ -48,7 +48,7 @@ public HandleCallAction(
_requireDynamicallyAccessedMembersAction = new(reflectionMarker, diagnosticContext, reason);
}
- private partial bool TryHandleIntrinsic (
+ private partial bool TryHandleIntrinsic(
MethodProxy calledMethod,
MultiValue instanceValue,
IReadOnlyList argumentValues,
@@ -221,7 +221,7 @@ private partial bool TryHandleIntrinsic (
//
// System.Array
//
- // CreateInstance (Type, Int32)
+ // CreateInstance(Type, Int32)
//
case IntrinsicId.Array_CreateInstance:
{
@@ -234,7 +234,7 @@ private partial bool TryHandleIntrinsic (
//
// System.Enum
//
- // static GetValues (Type)
+ // static GetValues(Type)
//
case IntrinsicId.Enum_GetValues:
{
@@ -243,7 +243,7 @@ private partial bool TryHandleIntrinsic (
// type instead).
//
// At least until we have shared enum code, this needs extra handling to get it right.
- foreach (var value in argumentValues[0].AsEnumerable ())
+ foreach (var value in argumentValues[0].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue
&& !systemTypeValue.RepresentedType.Type.IsGenericDefinition
@@ -263,10 +263,10 @@ private partial bool TryHandleIntrinsic (
//
// System.Runtime.InteropServices.Marshal
//
- // static SizeOf (Type)
- // static PtrToStructure (IntPtr, Type)
- // static DestroyStructure (IntPtr, Type)
- // static OffsetOf (Type, string)
+ // static SizeOf(Type)
+ // static PtrToStructure(IntPtr, Type)
+ // static DestroyStructure(IntPtr, Type)
+ // static OffsetOf(Type, string)
//
case IntrinsicId.Marshal_SizeOf:
case IntrinsicId.Marshal_PtrToStructure:
@@ -278,7 +278,7 @@ private partial bool TryHandleIntrinsic (
? 0 : 1;
// We need the data to do struct marshalling.
- foreach (var value in argumentValues[paramIndex].AsEnumerable ())
+ foreach (var value in argumentValues[paramIndex].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue
&& !systemTypeValue.RepresentedType.Type.IsGenericDefinition
@@ -304,12 +304,12 @@ private partial bool TryHandleIntrinsic (
//
// System.Runtime.InteropServices.Marshal
//
- // static GetDelegateForFunctionPointer (IntPtr, Type)
+ // static GetDelegateForFunctionPointer(IntPtr, Type)
//
case IntrinsicId.Marshal_GetDelegateForFunctionPointer:
{
// We need the data to do delegate marshalling.
- foreach (var value in argumentValues[1].AsEnumerable ())
+ foreach (var value in argumentValues[1].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue
&& !systemTypeValue.RepresentedType.Type.IsGenericDefinition
@@ -329,11 +329,11 @@ private partial bool TryHandleIntrinsic (
//
// System.Delegate
//
- // get_Method ()
+ // get_Method()
//
// System.Reflection.RuntimeReflectionExtensions
//
- // GetMethodInfo (System.Delegate)
+ // GetMethodInfo(System.Delegate)
//
case IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo:
case IntrinsicId.Delegate_get_Method:
@@ -373,12 +373,12 @@ private partial bool TryHandleIntrinsic (
//
case IntrinsicId.Object_GetType:
{
- if (instanceValue.IsEmpty ()) {
- AddReturnValue (MultiValueLattice.Top);
+ if (instanceValue.IsEmpty()) {
+ AddReturnValue(MultiValueLattice.Top);
break;
}
- foreach (var valueNode in instanceValue.AsEnumerable ())
+ foreach (var valueNode in instanceValue.AsEnumerable())
{
// Note that valueNode can be statically typed in IL as some generic argument type.
// For example:
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HoistedLocalKey.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HoistedLocalKey.cs
index 76b3fd499086cb..908381d9a7600e 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HoistedLocalKey.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HoistedLocalKey.cs
@@ -29,7 +29,7 @@ public HoistedLocalKey(FieldDesc field)
public override int GetHashCode() => Field.GetHashCode();
- public static bool operator ==(HoistedLocalKey left, HoistedLocalKey right) => left.Equals (right);
+ public static bool operator ==(HoistedLocalKey left, HoistedLocalKey right) => left.Equals(right);
public static bool operator !=(HoistedLocalKey left, HoistedLocalKey right) => !(left == right);
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/InterproceduralState.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/InterproceduralState.cs
index 7627312e81550a..71e3518a42bd81 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/InterproceduralState.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/InterproceduralState.cs
@@ -67,8 +67,8 @@ public void TrackMethod(MethodIL methodBody)
methodBody = GetInstantiatedMethodIL(methodBody);
// Work around the fact that ValueSet is readonly
- Debug.Assert (!MethodBodies.IsUnknown ());
- var methodsList = new List(MethodBodies.GetKnownValues ());
+ Debug.Assert(!MethodBodies.IsUnknown());
+ var methodsList = new List(MethodBodies.GetKnownValues());
methodsList.Add(new MethodBodyValue(methodBody));
// For state machine methods, also scan the state machine members.
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs
index eef3878c24461b..77de311145e890 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs
@@ -207,7 +207,7 @@ private static void ValidateNoReferenceToReference(ValueBasicBlockPair?[] locals
continue;
MultiValue localValue = localVariable.Value.Value;
- foreach (var val in localValue.AsEnumerable ())
+ foreach (var val in localValue.AsEnumerable())
{
if (val is LocalVariableReferenceValue localReference && localReference.ReferencedType.IsByRefOrPointer())
{
@@ -306,8 +306,8 @@ public virtual void InterproceduralScan(MethodIL startingMethodBody)
// Flow state through all methods encountered so far, as long as there
// are changes discovered in the hoisted local state on entry to any method.
- Debug.Assert (!oldInterproceduralState.MethodBodies.IsUnknown ());
- foreach (var methodBodyValue in oldInterproceduralState.MethodBodies.GetKnownValues ())
+ Debug.Assert(!oldInterproceduralState.MethodBodies.IsUnknown());
+ foreach (var methodBodyValue in oldInterproceduralState.MethodBodies.GetKnownValues())
Scan(methodBodyValue.MethodBody, ref interproceduralState);
}
@@ -319,14 +319,14 @@ public virtual void InterproceduralScan(MethodIL startingMethodBody)
var calleeMethods = compilerGeneratedCallees.OfType();
// https://github.com/dotnet/linker/issues/2845
// Disabled asserts due to a bug
- // Debug.Assert (interproceduralState.Count == 1 + calleeMethods.Count ());
+ // Debug.Assert(interproceduralState.Count == 1 + calleeMethods.Count());
// foreach (var method in calleeMethods)
- // Debug.Assert (interproceduralState.Any (kvp => kvp.Key.Method == method));
+ // Debug.Assert(interproceduralState.Any(kvp => kvp.Key.Method == method));
}
else
{
- Debug.Assert (!interproceduralState.MethodBodies.IsUnknown ());
- Debug.Assert(interproceduralState.MethodBodies.GetKnownValues ().Count() == 1);
+ Debug.Assert(!interproceduralState.MethodBodies.IsUnknown());
+ Debug.Assert(interproceduralState.MethodBodies.GetKnownValues().Count() == 1);
}
#endif
}
@@ -1020,7 +1020,7 @@ private void ScanIndirectStore(
/// Throws if is not a valid target for an indirect store.
protected void StoreInReference(MultiValue target, MultiValue source, MethodIL method, int offset, ValueBasicBlockPair?[] locals, int curBasicBlock, ref InterproceduralState ipState, int? parameterIndex)
{
- foreach (var value in target.AsEnumerable ())
+ foreach (var value in target.AsEnumerable())
{
switch (value)
{
@@ -1139,7 +1139,7 @@ private void ScanStfld(
return;
}
- foreach (var value in HandleGetField(methodBody, offset, field).AsEnumerable ())
+ foreach (var value in HandleGetField(methodBody, offset, field).AsEnumerable())
{
// GetFieldValue may return different node types, in which case they can't be stored to.
// At least not yet.
@@ -1187,7 +1187,7 @@ internal MultiValue DereferenceValue(
ref InterproceduralState interproceduralState)
{
MultiValue dereferencedValue = MultiValueLattice.Top;
- foreach (var value in maybeReferenceValue.AsEnumerable ())
+ foreach (var value in maybeReferenceValue.AsEnumerable())
{
switch (value)
{
@@ -1299,7 +1299,7 @@ private void HandleCall(
foreach (var param in methodArguments)
{
- foreach (var v in param.AsEnumerable ())
+ foreach (var v in param.AsEnumerable())
{
if (v is ArrayValue arr)
{
@@ -1344,7 +1344,7 @@ private void ScanStelem(
StackSlot indexToStoreAt = PopUnknown(currentStack, 1, methodBody, offset);
StackSlot arrayToStoreIn = PopUnknown(currentStack, 1, methodBody, offset);
int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt();
- foreach (var array in arrayToStoreIn.Value.AsEnumerable ())
+ foreach (var array in arrayToStoreIn.Value.AsEnumerable())
{
if (array is ArrayValue arrValue)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisAssignmentPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisAssignmentPattern.cs
index 2f44acb76c90b6..89cebd2e73586b 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisAssignmentPattern.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisAssignmentPattern.cs
@@ -55,9 +55,9 @@ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger
logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute),
logger);
- foreach (var sourceValue in Source.AsEnumerable ())
+ foreach (var sourceValue in Source.AsEnumerable())
{
- foreach (var targetValue in Target.AsEnumerable ())
+ foreach (var targetValue in Target.AsEnumerable())
{
if (targetValue is not ValueWithDynamicallyAccessedMembers targetWithDynamicallyAccessedMembers)
throw new NotImplementedException();
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/CecilExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/CecilExtensions.cs
index 5e4dc5ffa1e137..5afa83d0a8957f 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/CecilExtensions.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/CecilExtensions.cs
@@ -10,355 +10,376 @@
namespace Mono.Linker.Tests.Extensions
{
- public static class CecilExtensions
- {
- public static IEnumerable AllDefinedTypes (this AssemblyDefinition assemblyDefinition)
- {
- return assemblyDefinition.Modules.SelectMany (m => m.AllDefinedTypes ());
- }
-
- public static IEnumerable AllDefinedTypes (this ModuleDefinition moduleDefinition)
- {
- foreach (var typeDefinition in moduleDefinition.Types) {
- yield return typeDefinition;
-
- foreach (var definition in typeDefinition.AllDefinedTypes ())
- yield return definition;
- }
- }
-
- public static IEnumerable AllDefinedTypes (this TypeDefinition typeDefinition)
- {
- foreach (var nestedType in typeDefinition.NestedTypes) {
- yield return nestedType;
-
- foreach (var definition in nestedType.AllDefinedTypes ())
- yield return definition;
- }
- }
-
- public static IEnumerable AllMembers (this ModuleDefinition module)
- {
- foreach (var type in module.AllDefinedTypes ()) {
- yield return type;
-
- foreach (var member in type.AllMembers ())
- yield return member;
- }
- }
-
- public static IEnumerable AllMembers (this TypeDefinition type)
- {
- foreach (var field in type.Fields)
- yield return field;
-
- foreach (var prop in type.Properties)
- yield return prop;
-
- foreach (var method in type.Methods)
- yield return method;
-
- foreach (var @event in type.Events)
- yield return @event;
- }
-
- public static IEnumerable AllMethods (this TypeDefinition type)
- {
- foreach (var m in type.AllMembers ()) {
- switch (m) {
- case MethodDefinition method:
- yield return method;
- break;
- case PropertyDefinition @property:
- if (@property.GetMethod != null)
- yield return @property.GetMethod;
-
- if (@property.SetMethod != null)
- yield return @property.SetMethod;
-
- break;
- case EventDefinition @event:
- if (@event.AddMethod != null)
- yield return @event.AddMethod;
-
- if (@event.RemoveMethod != null)
- yield return @event.RemoveMethod;
-
- break;
-
- default:
- break;
- }
- }
- }
-
- public static bool HasAttribute (this ICustomAttributeProvider provider, string name)
- {
- return provider.CustomAttributes.Any (ca => ca.AttributeType.Name == name);
- }
-
- public static bool HasAttributeDerivedFrom (this ICustomAttributeProvider provider, string name)
- {
- return provider.CustomAttributes.Any (ca => ca.AttributeType.Resolve ().DerivesFrom (name));
- }
-
- public static bool DerivesFrom (this TypeDefinition type, string baseTypeName)
- {
- if (type.Name == baseTypeName)
- return true;
-
- if (type.BaseType == null)
- return false;
-
- if (type.BaseType.Name == baseTypeName)
- return true;
-
- return type.BaseType.Resolve ()?.DerivesFrom (baseTypeName) ?? false;
- }
-
- public static PropertyDefinition GetPropertyDefinition (this MethodDefinition method)
- {
- if (!method.IsSetter && !method.IsGetter)
+ public static class CecilExtensions
+ {
+ public static IEnumerable AllDefinedTypes(this AssemblyDefinition assemblyDefinition)
+ {
+ return assemblyDefinition.Modules.SelectMany(m => m.AllDefinedTypes());
+ }
+
+ public static IEnumerable AllDefinedTypes(this ModuleDefinition moduleDefinition)
+ {
+ foreach (var typeDefinition in moduleDefinition.Types)
+ {
+ yield return typeDefinition;
+
+ foreach (var definition in typeDefinition.AllDefinedTypes())
+ yield return definition;
+ }
+ }
+
+ public static IEnumerable AllDefinedTypes(this TypeDefinition typeDefinition)
+ {
+ foreach (var nestedType in typeDefinition.NestedTypes)
+ {
+ yield return nestedType;
+
+ foreach (var definition in nestedType.AllDefinedTypes())
+ yield return definition;
+ }
+ }
+
+ public static IEnumerable AllMembers(this ModuleDefinition module)
+ {
+ foreach (var type in module.AllDefinedTypes())
+ {
+ yield return type;
+
+ foreach (var member in type.AllMembers())
+ yield return member;
+ }
+ }
+
+ public static IEnumerable AllMembers(this TypeDefinition type)
+ {
+ foreach (var field in type.Fields)
+ yield return field;
+
+ foreach (var prop in type.Properties)
+ yield return prop;
+
+ foreach (var method in type.Methods)
+ yield return method;
+
+ foreach (var @event in type.Events)
+ yield return @event;
+ }
+
+ public static IEnumerable AllMethods(this TypeDefinition type)
+ {
+ foreach (var m in type.AllMembers())
+ {
+ switch (m)
+ {
+ case MethodDefinition method:
+ yield return method;
+ break;
+ case PropertyDefinition @property:
+ if (@property.GetMethod != null)
+ yield return @property.GetMethod;
+
+ if (@property.SetMethod != null)
+ yield return @property.SetMethod;
+
+ break;
+ case EventDefinition @event:
+ if (@event.AddMethod != null)
+ yield return @event.AddMethod;
+
+ if (@event.RemoveMethod != null)
+ yield return @event.RemoveMethod;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ public static bool HasAttribute(this ICustomAttributeProvider provider, string name)
+ {
+ return provider.CustomAttributes.Any(ca => ca.AttributeType.Name == name);
+ }
+
+ public static bool HasAttributeDerivedFrom(this ICustomAttributeProvider provider, string name)
+ {
+ return provider.CustomAttributes.Any(ca => ca.AttributeType.Resolve().DerivesFrom(name));
+ }
+
+ public static bool DerivesFrom(this TypeDefinition type, string baseTypeName)
+ {
+ if (type.Name == baseTypeName)
+ return true;
+
+ if (type.BaseType == null)
+ return false;
+
+ if (type.BaseType.Name == baseTypeName)
+ return true;
+
+ return type.BaseType.Resolve()?.DerivesFrom(baseTypeName) ?? false;
+ }
+
+ public static PropertyDefinition GetPropertyDefinition(this MethodDefinition method)
+ {
+ if (!method.IsSetter && !method.IsGetter)
#pragma warning disable CA2208 // Instantiate argument exceptions correctly
- throw new ArgumentException ();
+ throw new ArgumentException();
#pragma warning restore CA2208 // Instantiate argument exceptions correctly
- var propertyName = method.Name.Substring (4);
- return method.DeclaringType.Properties.First (p => p.Name == propertyName);
- }
-
- public static string GetSignature (this MethodDefinition method)
- {
- var builder = new StringBuilder ();
- builder.Append (method.Name);
- if (method.HasGenericParameters) {
- builder.Append ($"<#{method.GenericParameters.Count}>");
- }
-
- builder.Append ('(');
-
- if (method.HasParameters) {
- for (int i = 0; i < method.Parameters.Count - 1; i++) {
- // TODO: modifiers
- // TODO: default values
- builder.Append ($"{method.Parameters[i].ParameterType},");
- }
-
- builder.Append (method.Parameters[method.Parameters.Count - 1].ParameterType);
- }
-
- builder.Append (')');
-
- return builder.ToString ();
- }
-
- public static object GetConstructorArgumentValue (this CustomAttribute attr, int argumentIndex)
- {
- return attr.ConstructorArguments[argumentIndex].Value;
- }
-
- public static object? GetPropertyValue (this CustomAttribute attr, string propertyName)
- {
- foreach (var prop in attr.Properties)
- if (prop.Name == propertyName)
- return prop.Argument.Value;
-
- return null;
- }
-
- public static bool IsEventMethod (this MethodDefinition md)
- {
- return (md.SemanticsAttributes & MethodSemanticsAttributes.AddOn) != 0 ||
- (md.SemanticsAttributes & MethodSemanticsAttributes.Fire) != 0 ||
- (md.SemanticsAttributes & MethodSemanticsAttributes.RemoveOn) != 0;
- }
-
- public static string GetDisplayName (this MethodReference method)
- {
- var sb = new System.Text.StringBuilder ();
-
- // Match C# syntaxis name if setter or getter
- var methodDefinition = method.Resolve ();
- if (methodDefinition != null && (methodDefinition.IsSetter || methodDefinition.IsGetter)) {
- // Append property name
- string name = GetPropertyNameFromAccessorName (methodDefinition.Name, methodDefinition.IsSetter);
- sb.Append (name);
- // Insert declaring type name and namespace
- sb.Insert (0, '.').Insert (0, method.DeclaringType?.GetDisplayName ());
- return sb.ToString ();
- }
-
- if (methodDefinition != null && methodDefinition.IsEventMethod ()) {
- // Append event name
- string name = methodDefinition.SemanticsAttributes switch {
- MethodSemanticsAttributes.AddOn => string.Concat (methodDefinition.Name.AsSpan (4), ".add"),
- MethodSemanticsAttributes.RemoveOn => string.Concat (methodDefinition.Name.AsSpan (7), ".remove"),
- MethodSemanticsAttributes.Fire => string.Concat (methodDefinition.Name.AsSpan (6), ".raise"),
- _ => throw new NotSupportedException (),
- };
- sb.Append (name);
- // Insert declaring type name and namespace
- sb.Insert (0, '.').Insert (0, method.DeclaringType.GetDisplayName ());
- return sb.ToString ();
- }
-
- // Append parameters
- sb.Append ('(');
- if (method.HasParameters) {
- for (int i = 0; i < method.Parameters.Count - 1; i++)
- sb.Append (method.Parameters[i].ParameterType.GetDisplayNameWithoutNamespace ()).Append (", ");
-
- sb.Append (method.Parameters[method.Parameters.Count - 1].ParameterType.GetDisplayNameWithoutNamespace ());
- }
-
- sb.Append (')');
-
- // Insert generic parameters
- if (method.HasGenericParameters) {
- PrependGenericParameters (method.GenericParameters, sb);
- }
-
- // Insert method name
- if (method.Name == ".ctor")
- sb.Insert (0, method.DeclaringType.Name);
- else
- sb.Insert (0, method.Name);
-
- // Insert declaring type name and namespace
- if (method.DeclaringType != null)
- sb.Insert (0, '.').Insert (0, method.DeclaringType.GetDisplayName ());
-
- return sb.ToString ();
- }
-
- private static string GetPropertyNameFromAccessorName (string methodName, bool isSetter) =>
- isSetter ?
- string.Concat (methodName.StartsWith ("set_") ? methodName.AsSpan (4) : methodName.Replace (".set_", "."), ".set") :
- string.Concat (methodName.StartsWith ("get_") ? methodName.AsSpan (4) : methodName.Replace (".get_", "."), ".get");
-
- public static string GetDisplayName (this TypeReference type)
- {
- var builder = GetDisplayNameWithoutNamespace (type);
- var namespaceDisplayName = type.GetNamespaceDisplayName ();
- if (!string.IsNullOrEmpty (namespaceDisplayName)) {
- builder.Insert (0, ".");
- builder.Insert (0, namespaceDisplayName);
- }
-
- return builder.ToString ();
- }
-
- public static string GetDisplayName (this FieldReference field)
- {
- var builder = new StringBuilder ();
- if (field.DeclaringType != null) {
- builder.Append (field.DeclaringType.GetDisplayName ());
- builder.Append ('.');
- }
-
- builder.Append (field.Name);
-
- return builder.ToString ();
- }
-
- public static string GetNamespaceDisplayName (this MemberReference member)
- {
- var type = member is TypeReference typeReference ? typeReference : member.DeclaringType;
- while (type.DeclaringType != null)
- type = type.DeclaringType;
-
- return type.Namespace;
- }
-
- public static StringBuilder GetDisplayNameWithoutNamespace (this TypeReference type)
- {
- var sb = new StringBuilder ();
- if (type == null)
- return sb;
-
- Stack? genericArguments = null;
- while (true) {
- switch (type) {
- case ArrayType arrayType:
- AppendArrayType (arrayType, sb);
- break;
- case GenericInstanceType genericInstanceType:
- genericArguments = new Stack (genericInstanceType.GenericArguments);
- type = genericInstanceType.ElementType;
- continue;
- default:
- if (type.HasGenericParameters) {
- int genericParametersCount = type.GenericParameters.Count;
- int declaringTypeGenericParametersCount = type.DeclaringType?.GenericParameters?.Count ?? 0;
-
- string simpleName;
- if (genericParametersCount > declaringTypeGenericParametersCount) {
- if (genericArguments?.Count > 0)
- PrependGenericArguments (genericArguments, genericParametersCount - declaringTypeGenericParametersCount, sb);
- else
- PrependGenericParameters (type.GenericParameters.Skip (declaringTypeGenericParametersCount).ToList (), sb);
-
- int explicitArityIndex = type.Name.IndexOf ('`');
- simpleName = explicitArityIndex != -1 ? type.Name.Substring (0, explicitArityIndex) : type.Name;
- } else
- simpleName = type.Name;
-
- sb.Insert (0, simpleName);
- break;
- }
-
- sb.Insert (0, type.Name);
- break;
- }
-
- type = type.GetElementType ();
- if (type.DeclaringType is not TypeReference declaringType)
- break;
-
- type = declaringType;
-
- sb.Insert (0, '.');
- }
-
- return sb;
- }
-
- public static void PrependGenericParameters (IList genericParameters, StringBuilder sb)
- {
- sb.Insert (0, '>').Insert (0, genericParameters[genericParameters.Count - 1]);
- for (int i = genericParameters.Count - 2; i >= 0; i--)
- sb.Insert (0, ',').Insert (0, genericParameters[i]);
-
- sb.Insert (0, '<');
- }
-
- private static void PrependGenericArguments (Stack genericArguments, int argumentsToTake, StringBuilder sb)
- {
- sb.Insert (0, '>').Insert (0, genericArguments.Pop ().GetDisplayNameWithoutNamespace ().ToString ());
- while (--argumentsToTake > 0)
- sb.Insert (0, ',').Insert (0, genericArguments.Pop ().GetDisplayNameWithoutNamespace ().ToString ());
-
- sb.Insert (0, '<');
- }
-
- private static void AppendArrayType (ArrayType arrayType, StringBuilder sb)
- {
- void parseArrayDimensions (ArrayType at)
- {
- sb.Append ('[');
- for (int i = 0; i < at.Dimensions.Count - 1; i++)
- sb.Append (',');
-
- sb.Append (']');
- }
-
- sb.Append (arrayType.Name.AsSpan (0, arrayType.Name.IndexOf ('[')));
- parseArrayDimensions (arrayType);
- var element = arrayType.ElementType as ArrayType;
- while (element != null) {
- parseArrayDimensions (element);
- element = element.ElementType as ArrayType;
- }
- }
- }
+ var propertyName = method.Name.Substring(4);
+ return method.DeclaringType.Properties.First(p => p.Name == propertyName);
+ }
+
+ public static string GetSignature(this MethodDefinition method)
+ {
+ var builder = new StringBuilder();
+ builder.Append(method.Name);
+ if (method.HasGenericParameters)
+ {
+ builder.Append($"<#{method.GenericParameters.Count}>");
+ }
+
+ builder.Append('(');
+
+ if (method.HasParameters)
+ {
+ for (int i = 0; i < method.Parameters.Count - 1; i++)
+ {
+ // TODO: modifiers
+ // TODO: default values
+ builder.Append($"{method.Parameters[i].ParameterType},");
+ }
+
+ builder.Append(method.Parameters[method.Parameters.Count - 1].ParameterType);
+ }
+
+ builder.Append(')');
+
+ return builder.ToString();
+ }
+
+ public static object GetConstructorArgumentValue(this CustomAttribute attr, int argumentIndex)
+ {
+ return attr.ConstructorArguments[argumentIndex].Value;
+ }
+
+ public static object? GetPropertyValue(this CustomAttribute attr, string propertyName)
+ {
+ foreach (var prop in attr.Properties)
+ if (prop.Name == propertyName)
+ return prop.Argument.Value;
+
+ return null;
+ }
+
+ public static bool IsEventMethod(this MethodDefinition md)
+ {
+ return (md.SemanticsAttributes & MethodSemanticsAttributes.AddOn) != 0 ||
+ (md.SemanticsAttributes & MethodSemanticsAttributes.Fire) != 0 ||
+ (md.SemanticsAttributes & MethodSemanticsAttributes.RemoveOn) != 0;
+ }
+
+ public static string GetDisplayName(this MethodReference method)
+ {
+ var sb = new System.Text.StringBuilder();
+
+ // Match C# syntaxis name if setter or getter
+ var methodDefinition = method.Resolve();
+ if (methodDefinition != null && (methodDefinition.IsSetter || methodDefinition.IsGetter))
+ {
+ // Append property name
+ string name = GetPropertyNameFromAccessorName(methodDefinition.Name, methodDefinition.IsSetter);
+ sb.Append(name);
+ // Insert declaring type name and namespace
+ sb.Insert(0, '.').Insert(0, method.DeclaringType?.GetDisplayName());
+ return sb.ToString();
+ }
+
+ if (methodDefinition != null && methodDefinition.IsEventMethod())
+ {
+ // Append event name
+ string name = methodDefinition.SemanticsAttributes switch
+ {
+ MethodSemanticsAttributes.AddOn => string.Concat(methodDefinition.Name.AsSpan(4), ".add"),
+ MethodSemanticsAttributes.RemoveOn => string.Concat(methodDefinition.Name.AsSpan(7), ".remove"),
+ MethodSemanticsAttributes.Fire => string.Concat(methodDefinition.Name.AsSpan(6), ".raise"),
+ _ => throw new NotSupportedException(),
+ };
+ sb.Append(name);
+ // Insert declaring type name and namespace
+ sb.Insert(0, '.').Insert(0, method.DeclaringType.GetDisplayName());
+ return sb.ToString();
+ }
+
+ // Append parameters
+ sb.Append('(');
+ if (method.HasParameters)
+ {
+ for (int i = 0; i < method.Parameters.Count - 1; i++)
+ sb.Append(method.Parameters[i].ParameterType.GetDisplayNameWithoutNamespace()).Append(", ");
+
+ sb.Append(method.Parameters[method.Parameters.Count - 1].ParameterType.GetDisplayNameWithoutNamespace());
+ }
+
+ sb.Append(')');
+
+ // Insert generic parameters
+ if (method.HasGenericParameters)
+ {
+ PrependGenericParameters(method.GenericParameters, sb);
+ }
+
+ // Insert method name
+ if (method.Name == ".ctor")
+ sb.Insert(0, method.DeclaringType.Name);
+ else
+ sb.Insert(0, method.Name);
+
+ // Insert declaring type name and namespace
+ if (method.DeclaringType != null)
+ sb.Insert(0, '.').Insert(0, method.DeclaringType.GetDisplayName());
+
+ return sb.ToString();
+ }
+
+ private static string GetPropertyNameFromAccessorName(string methodName, bool isSetter) =>
+ isSetter ?
+ string.Concat(methodName.StartsWith("set_") ? methodName.AsSpan(4) : methodName.Replace(".set_", "."), ".set") :
+ string.Concat(methodName.StartsWith("get_") ? methodName.AsSpan(4) : methodName.Replace(".get_", "."), ".get");
+
+ public static string GetDisplayName(this TypeReference type)
+ {
+ var builder = GetDisplayNameWithoutNamespace(type);
+ var namespaceDisplayName = type.GetNamespaceDisplayName();
+ if (!string.IsNullOrEmpty(namespaceDisplayName))
+ {
+ builder.Insert(0, ".");
+ builder.Insert(0, namespaceDisplayName);
+ }
+
+ return builder.ToString();
+ }
+
+ public static string GetDisplayName(this FieldReference field)
+ {
+ var builder = new StringBuilder();
+ if (field.DeclaringType != null)
+ {
+ builder.Append(field.DeclaringType.GetDisplayName());
+ builder.Append('.');
+ }
+
+ builder.Append(field.Name);
+
+ return builder.ToString();
+ }
+
+ public static string GetNamespaceDisplayName(this MemberReference member)
+ {
+ var type = member is TypeReference typeReference ? typeReference : member.DeclaringType;
+ while (type.DeclaringType != null)
+ type = type.DeclaringType;
+
+ return type.Namespace;
+ }
+
+ public static StringBuilder GetDisplayNameWithoutNamespace(this TypeReference type)
+ {
+ var sb = new StringBuilder();
+ if (type == null)
+ return sb;
+
+ Stack? genericArguments = null;
+ while (true)
+ {
+ switch (type)
+ {
+ case ArrayType arrayType:
+ AppendArrayType(arrayType, sb);
+ break;
+ case GenericInstanceType genericInstanceType:
+ genericArguments = new Stack(genericInstanceType.GenericArguments);
+ type = genericInstanceType.ElementType;
+ continue;
+ default:
+ if (type.HasGenericParameters)
+ {
+ int genericParametersCount = type.GenericParameters.Count;
+ int declaringTypeGenericParametersCount = type.DeclaringType?.GenericParameters?.Count ?? 0;
+
+ string simpleName;
+ if (genericParametersCount > declaringTypeGenericParametersCount)
+ {
+ if (genericArguments?.Count > 0)
+ PrependGenericArguments(genericArguments, genericParametersCount - declaringTypeGenericParametersCount, sb);
+ else
+ PrependGenericParameters(type.GenericParameters.Skip(declaringTypeGenericParametersCount).ToList(), sb);
+
+ int explicitArityIndex = type.Name.IndexOf('`');
+ simpleName = explicitArityIndex != -1 ? type.Name.Substring(0, explicitArityIndex) : type.Name;
+ }
+ else
+ simpleName = type.Name;
+
+ sb.Insert(0, simpleName);
+ break;
+ }
+
+ sb.Insert(0, type.Name);
+ break;
+ }
+
+ type = type.GetElementType();
+ if (type.DeclaringType is not TypeReference declaringType)
+ break;
+
+ type = declaringType;
+
+ sb.Insert(0, '.');
+ }
+
+ return sb;
+ }
+
+ public static void PrependGenericParameters(IList genericParameters, StringBuilder sb)
+ {
+ sb.Insert(0, '>').Insert(0, genericParameters[genericParameters.Count - 1]);
+ for (int i = genericParameters.Count - 2; i >= 0; i--)
+ sb.Insert(0, ',').Insert(0, genericParameters[i]);
+
+ sb.Insert(0, '<');
+ }
+
+ private static void PrependGenericArguments(Stack genericArguments, int argumentsToTake, StringBuilder sb)
+ {
+ sb.Insert(0, '>').Insert(0, genericArguments.Pop().GetDisplayNameWithoutNamespace().ToString());
+ while (--argumentsToTake > 0)
+ sb.Insert(0, ',').Insert(0, genericArguments.Pop().GetDisplayNameWithoutNamespace().ToString());
+
+ sb.Insert(0, '<');
+ }
+
+ private static void AppendArrayType(ArrayType arrayType, StringBuilder sb)
+ {
+ void parseArrayDimensions(ArrayType at)
+ {
+ sb.Append('[');
+ for (int i = 0; i < at.Dimensions.Count - 1; i++)
+ sb.Append(',');
+
+ sb.Append(']');
+ }
+
+ sb.Append(arrayType.Name.AsSpan(0, arrayType.Name.IndexOf('[')));
+ parseArrayDimensions(arrayType);
+ var element = arrayType.ElementType as ArrayType;
+ while (element != null)
+ {
+ parseArrayDimensions(element);
+ element = element.ElementType as ArrayType;
+ }
+ }
+ }
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/NiceIO.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/NiceIO.cs
index 791d6b4d408539..30e04ade65b183 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/NiceIO.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/Extensions/NiceIO.cs
@@ -35,829 +35,877 @@
namespace Mono.Linker.Tests.Extensions
{
- public class NPath : IEquatable, IComparable
- {
- private static readonly StringComparison PathStringComparison = IsLinux () ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
-
- private readonly string[] _elements;
- private readonly bool _isRelative;
- private readonly string? _driveLetter;
-
- #region construction
-
- public NPath (string path)
- {
- ArgumentNullException.ThrowIfNull (path);
-
- path = ParseDriveLetter (path, out _driveLetter);
-
- if (path == "/") {
- _isRelative = false;
- _elements = Array.Empty ();
- } else {
- var split = path.Split ('/', '\\');
-
- _isRelative = _driveLetter == null && IsRelativeFromSplitString (split);
-
- _elements = ParseSplitStringIntoElements (split.Where (s => s.Length > 0).ToArray ());
- }
- }
-
- private NPath (string[] elements, bool isRelative, string? driveLetter)
- {
- _elements = elements;
- _isRelative = isRelative;
- _driveLetter = driveLetter;
- }
-
- private string[] ParseSplitStringIntoElements (IEnumerable inputs)
- {
- var stack = new List ();
-
- foreach (var input in inputs.Where (input => input.Length != 0)) {
- if (input == ".") {
- if ((stack.Count > 0) && (stack.Last () != "."))
- continue;
- } else if (input == "..") {
- if (HasNonDotDotLastElement (stack)) {
- stack.RemoveAt (stack.Count - 1);
- continue;
- }
- if (!_isRelative)
- throw new ArgumentException ("You cannot create a path that tries to .. past the root");
- }
- stack.Add (input);
- }
- return stack.ToArray ();
- }
-
- private static bool HasNonDotDotLastElement (List stack)
- {
- return stack.Count > 0 && stack[stack.Count - 1] != "..";
- }
-
- private static string ParseDriveLetter (string path, out string? driveLetter)
- {
- if (path.Length >= 2 && path[1] == ':') {
- driveLetter = path[0].ToString ();
- return path.Substring (2);
- }
-
- driveLetter = null;
- return path;
- }
-
- private static bool IsRelativeFromSplitString (string[] split)
- {
- if (split.Length < 2)
- return true;
-
- return split[0].Length != 0 || !split.Any (s => s.Length > 0);
- }
-
- public NPath Combine (params string[] append)
- {
- return Combine (append.Select (a => new NPath (a)).ToArray ());
- }
-
- public NPath Combine (params NPath[] append)
- {
- if (!append.All (p => p.IsRelative))
- throw new ArgumentException ("You cannot .Combine a non-relative path");
-
- return new NPath (ParseSplitStringIntoElements (_elements.Concat (append.SelectMany (p => p._elements))), _isRelative, _driveLetter);
- }
-
- public NPath Parent {
- get {
- if (_elements.Length == 0)
- throw new InvalidOperationException ("Parent is called on an empty path");
-
- var newElements = _elements.Take (_elements.Length - 1).ToArray ();
-
- return new NPath (newElements, _isRelative, _driveLetter);
- }
- }
-
- public NPath RelativeTo (NPath path)
- {
- if (!IsChildOf (path)) {
- if (!IsRelative && !path.IsRelative && _driveLetter != path._driveLetter)
- throw new ArgumentException ("Path.RelativeTo() was invoked with two paths that are on different volumes. invoked on: " + ToString () + " asked to be made relative to: " + path);
-
- NPath? commonParent = null;
- foreach (var parent in RecursiveParents) {
- commonParent = path.RecursiveParents.FirstOrDefault (otherParent => otherParent == parent);
-
- if (commonParent != null)
- break;
- }
-
- if (commonParent == null)
- throw new ArgumentException ("Path.RelativeTo() was unable to find a common parent between " + ToString () + " and " + path);
-
- if (IsRelative && path.IsRelative && commonParent.IsEmpty ())
- throw new ArgumentException ("Path.RelativeTo() was invoked with two relative paths that do not share a common parent. Invoked on: " + ToString () + " asked to be made relative to: " + path);
-
- var depthDiff = path.Depth - commonParent.Depth;
- return new NPath (Enumerable.Repeat ("..", depthDiff).Concat (_elements.Skip (commonParent.Depth)).ToArray (), true, null);
- }
-
- return new NPath (_elements.Skip (path._elements.Length).ToArray (), true, null);
- }
-
- public NPath ChangeExtension (string extension)
- {
- ThrowIfRoot ();
-
- var newElements = (string[]) _elements.Clone ();
- newElements[newElements.Length - 1] = Path.ChangeExtension (_elements[_elements.Length - 1], WithDot (extension));
- if (extension == string.Empty)
- newElements[newElements.Length - 1] = newElements[newElements.Length - 1].TrimEnd ('.');
- return new NPath (newElements, _isRelative, _driveLetter);
- }
- #endregion construction
-
- #region inspection
-
- public bool IsRelative {
- get { return _isRelative; }
- }
-
- public string FileName {
- get {
- ThrowIfRoot ();
-
- return _elements.Last ();
- }
- }
-
- public string FileNameWithoutExtension {
- get { return Path.GetFileNameWithoutExtension (FileName); }
- }
-
- public IEnumerable Elements {
- get { return _elements; }
- }
-
- public int Depth {
- get { return _elements.Length; }
- }
-
- public bool Exists (string append = "")
- {
- return Exists (new NPath (append));
- }
-
- public bool Exists (NPath append)
- {
- return FileExists (append) || DirectoryExists (append);
- }
-
- public bool DirectoryExists (string append = "")
- {
- return DirectoryExists (new NPath (append));
- }
-
- public bool DirectoryExists (NPath append)
- {
- return Directory.Exists (Combine (append).ToString ());
- }
-
- public bool FileExists (string append = "")
- {
- return FileExists (new NPath (append));
- }
-
- public bool FileExists (NPath append)
- {
- return File.Exists (Combine (append).ToString ());
- }
-
- public string ExtensionWithDot {
- get {
- if (IsRoot)
- throw new ArgumentException ("A root directory does not have an extension");
-
- var last = _elements.Last ();
- var index = last.LastIndexOf (".");
- if (index < 0) return string.Empty;
- return last.Substring (index);
- }
- }
-
- public string InQuotes ()
- {
- return "\"" + ToString () + "\"";
- }
-
- public string InQuotes (SlashMode slashMode)
- {
- return "\"" + ToString (slashMode) + "\"";
- }
-
- public override string ToString ()
- {
- return ToString (SlashMode.Native);
- }
-
- public string ToString (SlashMode slashMode)
- {
- // Check if it's linux root /
- if (IsRoot && string.IsNullOrEmpty (_driveLetter))
- return Slash (slashMode).ToString ();
-
- if (_isRelative && _elements.Length == 0)
- return ".";
-
- var sb = new StringBuilder ();
- if (_driveLetter != null) {
- sb.Append (_driveLetter);
- sb.Append (':');
- }
- if (!_isRelative)
- sb.Append (Slash (slashMode));
- var first = true;
- foreach (var element in _elements) {
- if (!first)
- sb.Append (Slash (slashMode));
-
- sb.Append (element);
- first = false;
- }
- return sb.ToString ();
- }
-
- public static implicit operator string (NPath path)
- {
- return path.ToString ();
- }
-
- private static char Slash (SlashMode slashMode)
- {
- return slashMode switch {
- SlashMode.Backward => '\\',
- SlashMode.Forward => '/',
- _ => Path.DirectorySeparatorChar,
- };
- }
-
- public override bool Equals (object? obj)
- {
- if (obj == null)
- return false;
-
- // If parameter cannot be cast to Point return false.
- if (!(obj is NPath p))
- return false;
-
- return Equals (p);
- }
-
- public bool Equals (NPath? p)
- {
- if (p == null)
- return false;
-
- if (p._isRelative != _isRelative)
- return false;
-
- if (!string.Equals (p._driveLetter, _driveLetter, PathStringComparison))
- return false;
-
- if (p._elements.Length != _elements.Length)
- return false;
-
- for (var i = 0; i < _elements.Length; i++)
- if (!string.Equals (p._elements[i], _elements[i], PathStringComparison))
- return false;
-
- return true;
- }
-
- public static bool operator == (NPath? a, NPath? b)
- {
- // If both are null, or both are same instance, return true.
- if (ReferenceEquals (a, b))
- return true;
-
- // If one is null, but not both, return false.
- if ((a is null) || (b is null))
- return false;
-
- // Return true if the fields match:
- return a.Equals (b);
- }
-
- public override int GetHashCode ()
- {
- unchecked {
- int hash = 17;
- // Suitable nullity checks etc, of course :)
- hash = hash * 23 + _isRelative.GetHashCode ();
- foreach (var element in _elements)
- hash = hash * 23 + element.GetHashCode ();
- if (_driveLetter != null)
- hash = hash * 23 + _driveLetter.GetHashCode ();
- return hash;
- }
- }
-
- public int CompareTo (object? obj)
- {
- if (obj == null)
- return -1;
-
- return this.ToString ().CompareTo (((NPath) obj).ToString ());
- }
-
- public static bool operator != (NPath? a, NPath? b)
- {
- return !(a == b);
- }
-
- public bool HasExtension (params string[] extensions)
- {
- var extensionWithDotLower = ExtensionWithDot.ToLowerInvariant ();
- return extensions.Any (e => WithDot (e).ToLowerInvariant () == extensionWithDotLower);
- }
-
- private static string WithDot (string extension)
- {
- return extension.StartsWith (".") ? extension : "." + extension;
- }
-
- private bool IsEmpty ()
- {
- return _elements.Length == 0;
- }
-
- public bool IsRoot {
- get { return _elements.Length == 0 && !_isRelative; }
- }
-
- #endregion inspection
-
- #region directory enumeration
-
- public IEnumerable Files (string filter, bool recurse = false)
- {
- return Directory.GetFiles (ToString (), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select (s => new NPath (s));
- }
-
- public IEnumerable Files (bool recurse = false)
- {
- return Files ("*", recurse);
- }
-
- public IEnumerable Contents (string filter, bool recurse = false)
- {
- return Files (filter, recurse).Concat (Directories (filter, recurse));
- }
-
- public IEnumerable Contents (bool recurse = false)
- {
- return Contents ("*", recurse);
- }
-
- public IEnumerable Directories (string filter, bool recurse = false)
- {
- return Directory.GetDirectories (ToString (), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select (s => new NPath (s));
- }
-
- public IEnumerable Directories (bool recurse = false)
- {
- return Directories ("*", recurse);
- }
-
- #endregion
-
- #region filesystem writing operations
- public NPath CreateFile ()
- {
- ThrowIfRelative ();
- ThrowIfRoot ();
- EnsureParentDirectoryExists ();
- File.WriteAllBytes (ToString (), Array.Empty ());
- return this;
- }
-
- public NPath CreateFile (string file)
- {
- return CreateFile (new NPath (file));
- }
-
- public NPath CreateFile (NPath file)
- {
- if (!file.IsRelative)
- throw new ArgumentException ("You cannot call CreateFile() on an existing path with a non relative argument");
- return Combine (file).CreateFile ();
- }
-
- public NPath CreateDirectory ()
- {
- ThrowIfRelative ();
-
- if (IsRoot)
- throw new NotSupportedException ("CreateDirectory is not supported on a root level directory because it would be dangerous:" + ToString ());
-
- Directory.CreateDirectory (ToString ());
- return this;
- }
-
- public NPath CreateDirectory (string directory)
- {
- return CreateDirectory (new NPath (directory));
- }
-
- public NPath CreateDirectory (NPath directory)
- {
- if (!directory.IsRelative)
- throw new ArgumentException ("Cannot call CreateDirectory with an absolute argument");
-
- return Combine (directory).CreateDirectory ();
- }
-
- public NPath? Copy (string dest)
- {
- return Copy (new NPath (dest));
- }
-
- public NPath? Copy (string dest, Func fileFilter)
- {
- return Copy (new NPath (dest), fileFilter);
- }
-
- public NPath? Copy (NPath dest)
- {
- return Copy (dest, p => true);
- }
-
- public NPath? Copy (NPath dest, Func fileFilter)
- {
- ThrowIfRelative ();
- if (dest.IsRelative)
- dest = Parent.Combine (dest);
-
- if (dest.DirectoryExists ())
- return CopyWithDeterminedDestination (dest.Combine (FileName), fileFilter);
-
- return CopyWithDeterminedDestination (dest, fileFilter);
- }
-
- public NPath MakeAbsolute ()
- {
- if (!IsRelative)
- return this;
-
- return CurrentDirectory.Combine (this);
- }
-
- private NPath? CopyWithDeterminedDestination (NPath absoluteDestination, Func fileFilter)
- {
- if (absoluteDestination.IsRelative)
- throw new ArgumentException ("absoluteDestination must be absolute");
-
- if (FileExists ()) {
- if (!fileFilter (absoluteDestination))
- return null;
-
- absoluteDestination.EnsureParentDirectoryExists ();
-
- File.Copy (ToString (), absoluteDestination.ToString (), true);
- return absoluteDestination;
- }
-
- if (DirectoryExists ()) {
- absoluteDestination.EnsureDirectoryExists ();
- foreach (var thing in Contents ())
- thing.CopyWithDeterminedDestination (absoluteDestination.Combine (thing.RelativeTo (this)), fileFilter);
- return absoluteDestination;
- }
-
- throw new ArgumentException ("Copy() called on path that doesnt exist: " + ToString ());
- }
-
- public void Delete (DeleteMode deleteMode = DeleteMode.Normal)
- {
- ThrowIfRelative ();
-
- if (IsRoot)
- throw new NotSupportedException ("Delete is not supported on a root level directory because it would be dangerous:" + ToString ());
-
- if (FileExists ())
- File.Delete (ToString ());
- else if (DirectoryExists ())
- try {
- Directory.Delete (ToString (), true);
- } catch (IOException) {
- if (deleteMode == DeleteMode.Normal)
- throw;
- }
- else
- throw new InvalidOperationException ("Trying to delete a path that does not exist: " + ToString ());
- }
-
- public void DeleteIfExists (DeleteMode deleteMode = DeleteMode.Normal)
- {
- ThrowIfRelative ();
-
- if (FileExists () || DirectoryExists ())
- Delete (deleteMode);
- }
-
- public NPath DeleteContents ()
- {
- ThrowIfRelative ();
-
- if (IsRoot)
- throw new NotSupportedException ("DeleteContents is not supported on a root level directory because it would be dangerous:" + ToString ());
-
- if (FileExists ())
- throw new InvalidOperationException ("It is not valid to perform this operation on a file");
-
- if (DirectoryExists ()) {
- try {
- Files ().Delete ();
- Directories ().Delete ();
- } catch (IOException) {
- if (Files (true).Any ())
- throw;
- }
-
- return this;
- }
-
- return EnsureDirectoryExists ();
- }
-
- public static NPath CreateTempDirectory (string myprefix)
- {
- var random = new Random ();
- while (true) {
- var candidate = new NPath (Path.GetTempPath () + "/" + myprefix + "_" + random.Next ());
- if (!candidate.Exists ())
- return candidate.CreateDirectory ();
- }
- }
-
- public NPath Move (string dest)
- {
- return Move (new NPath (dest));
- }
-
- public NPath Move (NPath dest)
- {
- ThrowIfRelative ();
-
- if (IsRoot)
- throw new NotSupportedException ("Move is not supported on a root level directory because it would be dangerous:" + ToString ());
-
- if (dest.IsRelative)
- return Move (Parent.Combine (dest));
-
- if (dest.DirectoryExists ())
- return Move (dest.Combine (FileName));
-
- if (FileExists ()) {
- dest.EnsureParentDirectoryExists ();
- File.Move (ToString (), dest.ToString ());
- return dest;
- }
-
- if (DirectoryExists ()) {
- Directory.Move (ToString (), dest.ToString ());
- return dest;
- }
-
- throw new ArgumentException ("Move() called on a path that doesn't exist: " + ToString ());
- }
-
- #endregion
-
- #region special paths
-
- public static NPath CurrentDirectory {
- get {
- return new NPath (Directory.GetCurrentDirectory ());
- }
- }
-
- public static NPath HomeDirectory {
- get {
- if (Path.DirectorySeparatorChar == '\\')
- return new NPath (Environment.GetEnvironmentVariable ("USERPROFILE")!);
- return new NPath (Environment.GetEnvironmentVariable ("HOME")!);
- }
- }
-
- public static NPath SystemTemp {
- get {
- return new NPath (Path.GetTempPath ());
- }
- }
-
- #endregion
-
- private void ThrowIfRelative ()
- {
- if (_isRelative)
- throw new ArgumentException ("You are attempting an operation on a Path that requires an absolute path, but the path is relative");
- }
-
- private void ThrowIfRoot ()
- {
- if (IsRoot)
- throw new ArgumentException ("You are attempting an operation that is not valid on a root level directory");
- }
-
- public NPath EnsureDirectoryExists (string append = "")
- {
- return EnsureDirectoryExists (new NPath (append));
- }
-
- public NPath EnsureDirectoryExists (NPath append)
- {
- var combined = Combine (append);
- if (combined.DirectoryExists ())
- return combined;
- combined.EnsureParentDirectoryExists ();
- combined.CreateDirectory ();
- return combined;
- }
-
- public NPath EnsureParentDirectoryExists ()
- {
- var parent = Parent;
- parent.EnsureDirectoryExists ();
- return parent;
- }
-
- public NPath FileMustExist ()
- {
- if (!FileExists ())
- throw new FileNotFoundException ("File was expected to exist : " + ToString ());
-
- return this;
- }
-
- public NPath DirectoryMustExist ()
- {
- if (!DirectoryExists ())
- throw new DirectoryNotFoundException ("Expected directory to exist : " + ToString ());
-
- return this;
- }
-
- public bool IsChildOf (string potentialBasePath)
- {
- return IsChildOf (new NPath (potentialBasePath));
- }
-
- public bool IsChildOf (NPath potentialBasePath)
- {
- if ((IsRelative && !potentialBasePath.IsRelative) || !IsRelative && potentialBasePath.IsRelative)
- throw new ArgumentException ("You can only call IsChildOf with two relative paths, or with two absolute paths");
-
- // If the other path is the root directory, then anything is a child of it as long as it's not a Windows path
- if (potentialBasePath.IsRoot) {
- if (_driveLetter != potentialBasePath._driveLetter)
- return false;
- return true;
- }
-
- if (IsEmpty ())
- return false;
-
- if (Equals (potentialBasePath))
- return true;
-
- return Parent.IsChildOf (potentialBasePath);
- }
-
- public IEnumerable RecursiveParents {
- get {
- var candidate = this;
- while (true) {
- if (candidate.IsEmpty ())
- yield break;
-
- candidate = candidate.Parent;
- yield return candidate;
- }
- }
- }
-
- public NPath WriteAllText (string contents)
- {
- ThrowIfRelative ();
- EnsureParentDirectoryExists ();
- File.WriteAllText (ToString (), contents);
- return this;
- }
-
- public string ReadAllText ()
- {
- ThrowIfRelative ();
- return File.ReadAllText (ToString ());
- }
-
- public NPath WriteAllLines (string[] contents)
- {
- ThrowIfRelative ();
- EnsureParentDirectoryExists ();
- File.WriteAllLines (ToString (), contents);
- return this;
- }
-
- public string[] ReadAllLines ()
- {
- ThrowIfRelative ();
- return File.ReadAllLines (ToString ());
- }
-
- public IEnumerable CopyFiles (NPath destination, bool recurse, Func? fileFilter = null)
- {
- destination.EnsureDirectoryExists ();
- return Files (recurse).Where (fileFilter ?? AlwaysTrue).Select (file => file.Copy (destination.Combine (file.RelativeTo (this)))).ToArray ();
- }
-
- public IEnumerable MoveFiles (NPath destination, bool recurse, Func? fileFilter = null)
- {
- if (IsRoot)
- throw new NotSupportedException ("MoveFiles is not supported on this directory because it would be dangerous:" + ToString ());
-
- destination.EnsureDirectoryExists ();
- return Files (recurse).Where (fileFilter ?? AlwaysTrue).Select (file => file.Move (destination.Combine (file.RelativeTo (this)))).ToArray ();
- }
-
- private static bool AlwaysTrue (NPath p)
- {
- return true;
- }
-
- private static bool IsLinux ()
- {
- return Directory.Exists ("/proc");
- }
- }
-
- public static class Extensions
- {
- public static IEnumerable Copy (this IEnumerable self, string dest)
- {
- return Copy (self, new NPath (dest));
- }
-
- public static IEnumerable Copy (this IEnumerable self, NPath dest)
- {
- if (dest.IsRelative)
- throw new ArgumentException ("When copying multiple files, the destination cannot be a relative path");
- dest.EnsureDirectoryExists ();
- return self.Select (p => p.Copy (dest.Combine (p.FileName))).ToArray ();
- }
-
- public static IEnumerable Move (this IEnumerable self, string dest)
- {
- return Move (self, new NPath (dest));
- }
-
- public static IEnumerable Move (this IEnumerable self, NPath dest)
- {
- if (dest.IsRelative)
- throw new ArgumentException ("When moving multiple files, the destination cannot be a relative path");
- dest.EnsureDirectoryExists ();
- return self.Select (p => p.Move (dest.Combine (p.FileName))).ToArray ();
- }
-
- public static IEnumerable Delete (this IEnumerable self)
- {
- foreach (var p in self)
- p.Delete ();
- return self;
- }
-
- public static IEnumerable InQuotes (this IEnumerable self, SlashMode forward = SlashMode.Native)
- {
- return self.Select (p => p.InQuotes (forward));
- }
-
- public static NPath ToNPath (this string path)
- {
- return new NPath (path);
- }
- }
-
- public enum SlashMode
- {
- Native,
- Forward,
- Backward
- }
-
- public enum DeleteMode
- {
- Normal,
- Soft
- }
+ public class NPath : IEquatable, IComparable
+ {
+ private static readonly StringComparison PathStringComparison = IsLinux() ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
+
+ private readonly string[] _elements;
+ private readonly bool _isRelative;
+ private readonly string? _driveLetter;
+
+ #region construction
+
+ public NPath(string path)
+ {
+ ArgumentNullException.ThrowIfNull(path);
+
+ path = ParseDriveLetter(path, out _driveLetter);
+
+ if (path == "/")
+ {
+ _isRelative = false;
+ _elements = Array.Empty();
+ }
+ else
+ {
+ var split = path.Split('/', '\\');
+
+ _isRelative = _driveLetter == null && IsRelativeFromSplitString(split);
+
+ _elements = ParseSplitStringIntoElements(split.Where(s => s.Length > 0).ToArray());
+ }
+ }
+
+ private NPath(string[] elements, bool isRelative, string? driveLetter)
+ {
+ _elements = elements;
+ _isRelative = isRelative;
+ _driveLetter = driveLetter;
+ }
+
+ private string[] ParseSplitStringIntoElements(IEnumerable inputs)
+ {
+ var stack = new List();
+
+ foreach (var input in inputs.Where(input => input.Length != 0))
+ {
+ if (input == ".")
+ {
+ if ((stack.Count > 0) && (stack.Last() != "."))
+ continue;
+ }
+ else if (input == "..")
+ {
+ if (HasNonDotDotLastElement(stack))
+ {
+ stack.RemoveAt(stack.Count - 1);
+ continue;
+ }
+ if (!_isRelative)
+ throw new ArgumentException("You cannot create a path that tries to .. past the root");
+ }
+ stack.Add(input);
+ }
+ return stack.ToArray();
+ }
+
+ private static bool HasNonDotDotLastElement(List stack)
+ {
+ return stack.Count > 0 && stack[stack.Count - 1] != "..";
+ }
+
+ private static string ParseDriveLetter(string path, out string? driveLetter)
+ {
+ if (path.Length >= 2 && path[1] == ':')
+ {
+ driveLetter = path[0].ToString();
+ return path.Substring(2);
+ }
+
+ driveLetter = null;
+ return path;
+ }
+
+ private static bool IsRelativeFromSplitString(string[] split)
+ {
+ if (split.Length < 2)
+ return true;
+
+ return split[0].Length != 0 || !split.Any(s => s.Length > 0);
+ }
+
+ public NPath Combine(params string[] append)
+ {
+ return Combine(append.Select(a => new NPath(a)).ToArray());
+ }
+
+ public NPath Combine(params NPath[] append)
+ {
+ if (!append.All(p => p.IsRelative))
+ throw new ArgumentException("You cannot .Combine a non-relative path");
+
+ return new NPath(ParseSplitStringIntoElements(_elements.Concat(append.SelectMany(p => p._elements))), _isRelative, _driveLetter);
+ }
+
+ public NPath Parent
+ {
+ get
+ {
+ if (_elements.Length == 0)
+ throw new InvalidOperationException("Parent is called on an empty path");
+
+ var newElements = _elements.Take(_elements.Length - 1).ToArray();
+
+ return new NPath(newElements, _isRelative, _driveLetter);
+ }
+ }
+
+ public NPath RelativeTo(NPath path)
+ {
+ if (!IsChildOf(path))
+ {
+ if (!IsRelative && !path.IsRelative && _driveLetter != path._driveLetter)
+ throw new ArgumentException("Path.RelativeTo() was invoked with two paths that are on different volumes. invoked on: " + ToString() + " asked to be made relative to: " + path);
+
+ NPath? commonParent = null;
+ foreach (var parent in RecursiveParents)
+ {
+ commonParent = path.RecursiveParents.FirstOrDefault(otherParent => otherParent == parent);
+
+ if (commonParent != null)
+ break;
+ }
+
+ if (commonParent == null)
+ throw new ArgumentException("Path.RelativeTo() was unable to find a common parent between " + ToString() + " and " + path);
+
+ if (IsRelative && path.IsRelative && commonParent.IsEmpty())
+ throw new ArgumentException("Path.RelativeTo() was invoked with two relative paths that do not share a common parent. Invoked on: " + ToString() + " asked to be made relative to: " + path);
+
+ var depthDiff = path.Depth - commonParent.Depth;
+ return new NPath(Enumerable.Repeat("..", depthDiff).Concat(_elements.Skip(commonParent.Depth)).ToArray(), true, null);
+ }
+
+ return new NPath(_elements.Skip(path._elements.Length).ToArray(), true, null);
+ }
+
+ public NPath ChangeExtension(string extension)
+ {
+ ThrowIfRoot();
+
+ var newElements = (string[])_elements.Clone();
+ newElements[newElements.Length - 1] = Path.ChangeExtension(_elements[_elements.Length - 1], WithDot(extension));
+ if (extension == string.Empty)
+ newElements[newElements.Length - 1] = newElements[newElements.Length - 1].TrimEnd('.');
+ return new NPath(newElements, _isRelative, _driveLetter);
+ }
+ #endregion construction
+
+ #region inspection
+
+ public bool IsRelative
+ {
+ get { return _isRelative; }
+ }
+
+ public string FileName
+ {
+ get
+ {
+ ThrowIfRoot();
+
+ return _elements.Last();
+ }
+ }
+
+ public string FileNameWithoutExtension
+ {
+ get { return Path.GetFileNameWithoutExtension(FileName); }
+ }
+
+ public IEnumerable Elements
+ {
+ get { return _elements; }
+ }
+
+ public int Depth
+ {
+ get { return _elements.Length; }
+ }
+
+ public bool Exists(string append = "")
+ {
+ return Exists(new NPath(append));
+ }
+
+ public bool Exists(NPath append)
+ {
+ return FileExists(append) || DirectoryExists(append);
+ }
+
+ public bool DirectoryExists(string append = "")
+ {
+ return DirectoryExists(new NPath(append));
+ }
+
+ public bool DirectoryExists(NPath append)
+ {
+ return Directory.Exists(Combine(append).ToString());
+ }
+
+ public bool FileExists(string append = "")
+ {
+ return FileExists(new NPath(append));
+ }
+
+ public bool FileExists(NPath append)
+ {
+ return File.Exists(Combine(append).ToString());
+ }
+
+ public string ExtensionWithDot
+ {
+ get
+ {
+ if (IsRoot)
+ throw new ArgumentException("A root directory does not have an extension");
+
+ var last = _elements.Last();
+ var index = last.LastIndexOf(".");
+ if (index < 0) return string.Empty;
+ return last.Substring(index);
+ }
+ }
+
+ public string InQuotes()
+ {
+ return "\"" + ToString() + "\"";
+ }
+
+ public string InQuotes(SlashMode slashMode)
+ {
+ return "\"" + ToString(slashMode) + "\"";
+ }
+
+ public override string ToString()
+ {
+ return ToString(SlashMode.Native);
+ }
+
+ public string ToString(SlashMode slashMode)
+ {
+ // Check if it's linux root /
+ if (IsRoot && string.IsNullOrEmpty(_driveLetter))
+ return Slash(slashMode).ToString();
+
+ if (_isRelative && _elements.Length == 0)
+ return ".";
+
+ var sb = new StringBuilder();
+ if (_driveLetter != null)
+ {
+ sb.Append(_driveLetter);
+ sb.Append(':');
+ }
+ if (!_isRelative)
+ sb.Append(Slash(slashMode));
+ var first = true;
+ foreach (var element in _elements)
+ {
+ if (!first)
+ sb.Append(Slash(slashMode));
+
+ sb.Append(element);
+ first = false;
+ }
+ return sb.ToString();
+ }
+
+ public static implicit operator string(NPath path)
+ {
+ return path.ToString();
+ }
+
+ private static char Slash(SlashMode slashMode)
+ {
+ return slashMode switch
+ {
+ SlashMode.Backward => '\\',
+ SlashMode.Forward => '/',
+ _ => Path.DirectorySeparatorChar,
+ };
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj == null)
+ return false;
+
+ // If parameter cannot be cast to Point return false.
+ if (!(obj is NPath p))
+ return false;
+
+ return Equals(p);
+ }
+
+ public bool Equals(NPath? p)
+ {
+ if (p == null)
+ return false;
+
+ if (p._isRelative != _isRelative)
+ return false;
+
+ if (!string.Equals(p._driveLetter, _driveLetter, PathStringComparison))
+ return false;
+
+ if (p._elements.Length != _elements.Length)
+ return false;
+
+ for (var i = 0; i < _elements.Length; i++)
+ if (!string.Equals(p._elements[i], _elements[i], PathStringComparison))
+ return false;
+
+ return true;
+ }
+
+ public static bool operator ==(NPath? a, NPath? b)
+ {
+ // If both are null, or both are same instance, return true.
+ if (ReferenceEquals(a, b))
+ return true;
+
+ // If one is null, but not both, return false.
+ if ((a is null) || (b is null))
+ return false;
+
+ // Return true if the fields match:
+ return a.Equals(b);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hash = 17;
+ // Suitable nullity checks etc, of course :)
+ hash = hash * 23 + _isRelative.GetHashCode();
+ foreach (var element in _elements)
+ hash = hash * 23 + element.GetHashCode();
+ if (_driveLetter != null)
+ hash = hash * 23 + _driveLetter.GetHashCode();
+ return hash;
+ }
+ }
+
+ public int CompareTo(object? obj)
+ {
+ if (obj == null)
+ return -1;
+
+ return this.ToString().CompareTo(((NPath)obj).ToString());
+ }
+
+ public static bool operator !=(NPath? a, NPath? b)
+ {
+ return !(a == b);
+ }
+
+ public bool HasExtension(params string[] extensions)
+ {
+ var extensionWithDotLower = ExtensionWithDot.ToLowerInvariant();
+ return extensions.Any(e => WithDot(e).ToLowerInvariant() == extensionWithDotLower);
+ }
+
+ private static string WithDot(string extension)
+ {
+ return extension.StartsWith(".") ? extension : "." + extension;
+ }
+
+ private bool IsEmpty()
+ {
+ return _elements.Length == 0;
+ }
+
+ public bool IsRoot
+ {
+ get { return _elements.Length == 0 && !_isRelative; }
+ }
+
+ #endregion inspection
+
+ #region directory enumeration
+
+ public IEnumerable Files(string filter, bool recurse = false)
+ {
+ return Directory.GetFiles(ToString(), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(s => new NPath(s));
+ }
+
+ public IEnumerable Files(bool recurse = false)
+ {
+ return Files("*", recurse);
+ }
+
+ public IEnumerable Contents(string filter, bool recurse = false)
+ {
+ return Files(filter, recurse).Concat(Directories(filter, recurse));
+ }
+
+ public IEnumerable Contents(bool recurse = false)
+ {
+ return Contents("*", recurse);
+ }
+
+ public IEnumerable Directories(string filter, bool recurse = false)
+ {
+ return Directory.GetDirectories(ToString(), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(s => new NPath(s));
+ }
+
+ public IEnumerable Directories(bool recurse = false)
+ {
+ return Directories("*", recurse);
+ }
+
+ #endregion
+
+ #region filesystem writing operations
+ public NPath CreateFile()
+ {
+ ThrowIfRelative();
+ ThrowIfRoot();
+ EnsureParentDirectoryExists();
+ File.WriteAllBytes(ToString(), Array.Empty());
+ return this;
+ }
+
+ public NPath CreateFile(string file)
+ {
+ return CreateFile(new NPath(file));
+ }
+
+ public NPath CreateFile(NPath file)
+ {
+ if (!file.IsRelative)
+ throw new ArgumentException("You cannot call CreateFile() on an existing path with a non relative argument");
+ return Combine(file).CreateFile();
+ }
+
+ public NPath CreateDirectory()
+ {
+ ThrowIfRelative();
+
+ if (IsRoot)
+ throw new NotSupportedException("CreateDirectory is not supported on a root level directory because it would be dangerous:" + ToString());
+
+ Directory.CreateDirectory(ToString());
+ return this;
+ }
+
+ public NPath CreateDirectory(string directory)
+ {
+ return CreateDirectory(new NPath(directory));
+ }
+
+ public NPath CreateDirectory(NPath directory)
+ {
+ if (!directory.IsRelative)
+ throw new ArgumentException("Cannot call CreateDirectory with an absolute argument");
+
+ return Combine(directory).CreateDirectory();
+ }
+
+ public NPath? Copy(string dest)
+ {
+ return Copy(new NPath(dest));
+ }
+
+ public NPath? Copy(string dest, Func fileFilter)
+ {
+ return Copy(new NPath(dest), fileFilter);
+ }
+
+ public NPath? Copy(NPath dest)
+ {
+ return Copy(dest, p => true);
+ }
+
+ public NPath? Copy(NPath dest, Func fileFilter)
+ {
+ ThrowIfRelative();
+ if (dest.IsRelative)
+ dest = Parent.Combine(dest);
+
+ if (dest.DirectoryExists())
+ return CopyWithDeterminedDestination(dest.Combine(FileName), fileFilter);
+
+ return CopyWithDeterminedDestination(dest, fileFilter);
+ }
+
+ public NPath MakeAbsolute()
+ {
+ if (!IsRelative)
+ return this;
+
+ return CurrentDirectory.Combine(this);
+ }
+
+ private NPath? CopyWithDeterminedDestination(NPath absoluteDestination, Func fileFilter)
+ {
+ if (absoluteDestination.IsRelative)
+ throw new ArgumentException("absoluteDestination must be absolute");
+
+ if (FileExists())
+ {
+ if (!fileFilter(absoluteDestination))
+ return null;
+
+ absoluteDestination.EnsureParentDirectoryExists();
+
+ File.Copy(ToString(), absoluteDestination.ToString(), true);
+ return absoluteDestination;
+ }
+
+ if (DirectoryExists())
+ {
+ absoluteDestination.EnsureDirectoryExists();
+ foreach (var thing in Contents())
+ thing.CopyWithDeterminedDestination(absoluteDestination.Combine(thing.RelativeTo(this)), fileFilter);
+ return absoluteDestination;
+ }
+
+ throw new ArgumentException("Copy() called on path that doesnt exist: " + ToString());
+ }
+
+ public void Delete(DeleteMode deleteMode = DeleteMode.Normal)
+ {
+ ThrowIfRelative();
+
+ if (IsRoot)
+ throw new NotSupportedException("Delete is not supported on a root level directory because it would be dangerous:" + ToString());
+
+ if (FileExists())
+ File.Delete(ToString());
+ else if (DirectoryExists())
+ try
+ {
+ Directory.Delete(ToString(), true);
+ }
+ catch (IOException)
+ {
+ if (deleteMode == DeleteMode.Normal)
+ throw;
+ }
+ else
+ throw new InvalidOperationException("Trying to delete a path that does not exist: " + ToString());
+ }
+
+ public void DeleteIfExists(DeleteMode deleteMode = DeleteMode.Normal)
+ {
+ ThrowIfRelative();
+
+ if (FileExists() || DirectoryExists())
+ Delete(deleteMode);
+ }
+
+ public NPath DeleteContents()
+ {
+ ThrowIfRelative();
+
+ if (IsRoot)
+ throw new NotSupportedException("DeleteContents is not supported on a root level directory because it would be dangerous:" + ToString());
+
+ if (FileExists())
+ throw new InvalidOperationException("It is not valid to perform this operation on a file");
+
+ if (DirectoryExists())
+ {
+ try
+ {
+ Files().Delete();
+ Directories().Delete();
+ }
+ catch (IOException)
+ {
+ if (Files(true).Any())
+ throw;
+ }
+
+ return this;
+ }
+
+ return EnsureDirectoryExists();
+ }
+
+ public static NPath CreateTempDirectory(string myprefix)
+ {
+ var random = new Random();
+ while (true)
+ {
+ var candidate = new NPath(Path.GetTempPath() + "/" + myprefix + "_" + random.Next());
+ if (!candidate.Exists())
+ return candidate.CreateDirectory();
+ }
+ }
+
+ public NPath Move(string dest)
+ {
+ return Move(new NPath(dest));
+ }
+
+ public NPath Move(NPath dest)
+ {
+ ThrowIfRelative();
+
+ if (IsRoot)
+ throw new NotSupportedException("Move is not supported on a root level directory because it would be dangerous:" + ToString());
+
+ if (dest.IsRelative)
+ return Move(Parent.Combine(dest));
+
+ if (dest.DirectoryExists())
+ return Move(dest.Combine(FileName));
+
+ if (FileExists())
+ {
+ dest.EnsureParentDirectoryExists();
+ File.Move(ToString(), dest.ToString());
+ return dest;
+ }
+
+ if (DirectoryExists())
+ {
+ Directory.Move(ToString(), dest.ToString());
+ return dest;
+ }
+
+ throw new ArgumentException("Move() called on a path that doesn't exist: " + ToString());
+ }
+
+ #endregion
+
+ #region special paths
+
+ public static NPath CurrentDirectory
+ {
+ get
+ {
+ return new NPath(Directory.GetCurrentDirectory());
+ }
+ }
+
+ public static NPath HomeDirectory
+ {
+ get
+ {
+ if (Path.DirectorySeparatorChar == '\\')
+ return new NPath(Environment.GetEnvironmentVariable("USERPROFILE")!);
+ return new NPath(Environment.GetEnvironmentVariable("HOME")!);
+ }
+ }
+
+ public static NPath SystemTemp
+ {
+ get
+ {
+ return new NPath(Path.GetTempPath());
+ }
+ }
+
+ #endregion
+
+ private void ThrowIfRelative()
+ {
+ if (_isRelative)
+ throw new ArgumentException("You are attempting an operation on a Path that requires an absolute path, but the path is relative");
+ }
+
+ private void ThrowIfRoot()
+ {
+ if (IsRoot)
+ throw new ArgumentException("You are attempting an operation that is not valid on a root level directory");
+ }
+
+ public NPath EnsureDirectoryExists(string append = "")
+ {
+ return EnsureDirectoryExists(new NPath(append));
+ }
+
+ public NPath EnsureDirectoryExists(NPath append)
+ {
+ var combined = Combine(append);
+ if (combined.DirectoryExists())
+ return combined;
+ combined.EnsureParentDirectoryExists();
+ combined.CreateDirectory();
+ return combined;
+ }
+
+ public NPath EnsureParentDirectoryExists()
+ {
+ var parent = Parent;
+ parent.EnsureDirectoryExists();
+ return parent;
+ }
+
+ public NPath FileMustExist()
+ {
+ if (!FileExists())
+ throw new FileNotFoundException("File was expected to exist : " + ToString());
+
+ return this;
+ }
+
+ public NPath DirectoryMustExist()
+ {
+ if (!DirectoryExists())
+ throw new DirectoryNotFoundException("Expected directory to exist : " + ToString());
+
+ return this;
+ }
+
+ public bool IsChildOf(string potentialBasePath)
+ {
+ return IsChildOf(new NPath(potentialBasePath));
+ }
+
+ public bool IsChildOf(NPath potentialBasePath)
+ {
+ if ((IsRelative && !potentialBasePath.IsRelative) || !IsRelative && potentialBasePath.IsRelative)
+ throw new ArgumentException("You can only call IsChildOf with two relative paths, or with two absolute paths");
+
+ // If the other path is the root directory, then anything is a child of it as long as it's not a Windows path
+ if (potentialBasePath.IsRoot)
+ {
+ if (_driveLetter != potentialBasePath._driveLetter)
+ return false;
+ return true;
+ }
+
+ if (IsEmpty())
+ return false;
+
+ if (Equals(potentialBasePath))
+ return true;
+
+ return Parent.IsChildOf(potentialBasePath);
+ }
+
+ public IEnumerable RecursiveParents
+ {
+ get
+ {
+ var candidate = this;
+ while (true)
+ {
+ if (candidate.IsEmpty())
+ yield break;
+
+ candidate = candidate.Parent;
+ yield return candidate;
+ }
+ }
+ }
+
+ public NPath WriteAllText(string contents)
+ {
+ ThrowIfRelative();
+ EnsureParentDirectoryExists();
+ File.WriteAllText(ToString(), contents);
+ return this;
+ }
+
+ public string ReadAllText()
+ {
+ ThrowIfRelative();
+ return File.ReadAllText(ToString());
+ }
+
+ public NPath WriteAllLines(string[] contents)
+ {
+ ThrowIfRelative();
+ EnsureParentDirectoryExists();
+ File.WriteAllLines(ToString(), contents);
+ return this;
+ }
+
+ public string[] ReadAllLines()
+ {
+ ThrowIfRelative();
+ return File.ReadAllLines(ToString());
+ }
+
+ public IEnumerable CopyFiles(NPath destination, bool recurse, Func? fileFilter = null)
+ {
+ destination.EnsureDirectoryExists();
+ return Files(recurse).Where(fileFilter ?? AlwaysTrue).Select(file => file.Copy(destination.Combine(file.RelativeTo(this)))).ToArray();
+ }
+
+ public IEnumerable MoveFiles(NPath destination, bool recurse, Func? fileFilter = null)
+ {
+ if (IsRoot)
+ throw new NotSupportedException("MoveFiles is not supported on this directory because it would be dangerous:" + ToString());
+
+ destination.EnsureDirectoryExists();
+ return Files(recurse).Where(fileFilter ?? AlwaysTrue).Select(file => file.Move(destination.Combine(file.RelativeTo(this)))).ToArray();
+ }
+
+ private static bool AlwaysTrue(NPath p)
+ {
+ return true;
+ }
+
+ private static bool IsLinux()
+ {
+ return Directory.Exists("/proc");
+ }
+ }
+
+ public static class Extensions
+ {
+ public static IEnumerable Copy(this IEnumerable self, string dest)
+ {
+ return Copy(self, new NPath(dest));
+ }
+
+ public static IEnumerable Copy(this IEnumerable self, NPath dest)
+ {
+ if (dest.IsRelative)
+ throw new ArgumentException("When copying multiple files, the destination cannot be a relative path");
+ dest.EnsureDirectoryExists();
+ return self.Select(p => p.Copy(dest.Combine(p.FileName))).ToArray();
+ }
+
+ public static IEnumerable Move(this IEnumerable self, string dest)
+ {
+ return Move(self, new NPath(dest));
+ }
+
+ public static IEnumerable Move(this IEnumerable self, NPath dest)
+ {
+ if (dest.IsRelative)
+ throw new ArgumentException("When moving multiple files, the destination cannot be a relative path");
+ dest.EnsureDirectoryExists();
+ return self.Select(p => p.Move(dest.Combine(p.FileName))).ToArray();
+ }
+
+ public static IEnumerable Delete(this IEnumerable self)
+ {
+ foreach (var p in self)
+ p.Delete();
+ return self;
+ }
+
+ public static IEnumerable InQuotes(this IEnumerable self, SlashMode forward = SlashMode.Native)
+ {
+ return self.Select(p => p.InQuotes(forward));
+ }
+
+ public static NPath ToNPath(this string path)
+ {
+ return new NPath(path);
+ }
+ }
+
+ public enum SlashMode
+ {
+ Native,
+ Forward,
+ Backward
+ }
+
+ public enum DeleteMode
+ {
+ Normal,
+ Soft
+ }
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestCase.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestCase.cs
index be690ac7a60df9..09965c24c30dda 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestCase.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestCase.cs
@@ -9,98 +9,103 @@
namespace Mono.Linker.Tests.TestCases
{
- public class TestCase
- {
- public TestCase (NPath sourceFile, NPath rootCasesDirectory, NPath originalTestCaseAssemblyPath)
- {
- SourceFile = sourceFile;
- RootCasesDirectory = rootCasesDirectory;
- OriginalTestCaseAssemblyPath = originalTestCaseAssemblyPath;
- Name = sourceFile.FileNameWithoutExtension;
- var fullyRelative = sourceFile.RelativeTo (rootCasesDirectory);
- var displayNameRelative = fullyRelative.RelativeTo (new NPath (fullyRelative.Elements.First ()));
- string displayNameBase = displayNameRelative.Depth == 1 ? "" : displayNameRelative.Parent.ToString (SlashMode.Forward).Replace ('/', '.');
- DisplayName = sourceFile.FileNameWithoutExtension == "Program" && sourceFile.Parent.FileName == originalTestCaseAssemblyPath.FileNameWithoutExtension
- ? displayNameBase
- : $"{displayNameBase}.{sourceFile.FileNameWithoutExtension}";
- if (DisplayName.StartsWith("."))
- DisplayName = DisplayName.Substring(1);
-
- // A little hacky, but good enough for name. No reason why namespace & type names
- // should not follow the directory structure
- //ReconstructedFullTypeName = $"{sourceFile.Parent.RelativeTo (rootCasesDirectory.Parent).ToString (SlashMode.Forward).Replace ('/', '.')}.{sourceFile.FileNameWithoutExtension}";
- reconstructedFullTypeName = $"Mono.Linker.Tests.Cases.{fullyRelative.Parent.ToString (SlashMode.Forward).Replace ('/', '.')}.{sourceFile.FileNameWithoutExtension}";
-
- var firstParentRelativeToRoot = SourceFile.RelativeTo (rootCasesDirectory).Elements.First ();
- TestSuiteDirectory = rootCasesDirectory.Combine (firstParentRelativeToRoot);
- }
-
- public NPath RootCasesDirectory { get; }
-
- public string Name { get; }
-
- public string DisplayName { get; }
-
- public NPath SourceFile { get; }
-
- public NPath OriginalTestCaseAssemblyPath { get; }
-
- private string reconstructedFullTypeName;
-
- public bool HasLinkXmlFile {
- get { return SourceFile.ChangeExtension ("xml").FileExists (); }
- }
-
- public NPath LinkXmlFile {
- get {
- if (!HasLinkXmlFile)
- throw new InvalidOperationException ("This test case does not have a link xml file");
-
- return SourceFile.ChangeExtension ("xml");
- }
- }
-
- public NPath TestSuiteDirectory { get; }
-
- public TypeDefinition FindTypeDefinition (AssemblyDefinition assemblyDefinition)
- => TryFindTypeDefinition (assemblyDefinition) is TypeDefinition typeDefinition
- ? typeDefinition
- : throw new InvalidOperationException ($"Could not find the type definition for {Name} in {assemblyDefinition.Name}");
-
- public TypeDefinition? TryFindTypeDefinition (AssemblyDefinition caseAssemblyDefinition)
- {
- var typeDefinition = caseAssemblyDefinition.MainModule.GetType (reconstructedFullTypeName);
-
- // For all of the Test Cases, the full type name we constructed from the directory structure will be correct and we can successfully find
- // the type from GetType.
- if (typeDefinition != null)
- return typeDefinition;
-
- // However, some of types are supporting types rather than test cases and may not follow the standardized naming scheme of the test cases.
- // We still need to be able to locate these type defs so that we can parse some of the metadata on them.
- // One example, Unity run's into this with its tests that require a type UnityEngine.MonoBehaviours to exist. This type is defined in its own
- // file and it cannot follow our standardized naming directory & namespace naming scheme since the namespace must be UnityEngine.
- // Also look for compiler-generated Program type for top-level statements.
- foreach (var type in caseAssemblyDefinition.MainModule.Types) {
- if (type.Name == "Program" &&
- type.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (CompilerGeneratedAttribute)))
- return type;
-
- // Let's assume we should never have to search for a test case that has no namespace. If we don't find the type from GetType, then o well, that's not a test case.
- if (string.IsNullOrEmpty (type.Namespace))
- continue;
-
- if (type.Name == Name) {
- // This isn't foolproof, but let's do a little extra vetting to make sure the type we found corresponds to the source file we are
- // processing.
- if (!SourceFile.ReadAllText ().Contains ($"namespace {type.Namespace}"))
- continue;
-
- return type;
- }
- }
-
- return null;
- }
- }
+ public class TestCase
+ {
+ public TestCase(NPath sourceFile, NPath rootCasesDirectory, NPath originalTestCaseAssemblyPath)
+ {
+ SourceFile = sourceFile;
+ RootCasesDirectory = rootCasesDirectory;
+ OriginalTestCaseAssemblyPath = originalTestCaseAssemblyPath;
+ Name = sourceFile.FileNameWithoutExtension;
+ var fullyRelative = sourceFile.RelativeTo(rootCasesDirectory);
+ var displayNameRelative = fullyRelative.RelativeTo(new NPath(fullyRelative.Elements.First()));
+ string displayNameBase = displayNameRelative.Depth == 1 ? "" : displayNameRelative.Parent.ToString(SlashMode.Forward).Replace('/', '.');
+ DisplayName = sourceFile.FileNameWithoutExtension == "Program" && sourceFile.Parent.FileName == originalTestCaseAssemblyPath.FileNameWithoutExtension
+ ? displayNameBase
+ : $"{displayNameBase}.{sourceFile.FileNameWithoutExtension}";
+ if (DisplayName.StartsWith("."))
+ DisplayName = DisplayName.Substring(1);
+
+ // A little hacky, but good enough for name. No reason why namespace & type names
+ // should not follow the directory structure
+ // ReconstructedFullTypeName = $"{sourceFile.Parent.RelativeTo(rootCasesDirectory.Parent).ToString(SlashMode.Forward).Replace('/', '.')}.{sourceFile.FileNameWithoutExtension}";
+ reconstructedFullTypeName = $"Mono.Linker.Tests.Cases.{fullyRelative.Parent.ToString(SlashMode.Forward).Replace('/', '.')}.{sourceFile.FileNameWithoutExtension}";
+
+ var firstParentRelativeToRoot = SourceFile.RelativeTo(rootCasesDirectory).Elements.First();
+ TestSuiteDirectory = rootCasesDirectory.Combine(firstParentRelativeToRoot);
+ }
+
+ public NPath RootCasesDirectory { get; }
+
+ public string Name { get; }
+
+ public string DisplayName { get; }
+
+ public NPath SourceFile { get; }
+
+ public NPath OriginalTestCaseAssemblyPath { get; }
+
+ private string reconstructedFullTypeName;
+
+ public bool HasLinkXmlFile
+ {
+ get { return SourceFile.ChangeExtension("xml").FileExists(); }
+ }
+
+ public NPath LinkXmlFile
+ {
+ get
+ {
+ if (!HasLinkXmlFile)
+ throw new InvalidOperationException("This test case does not have a link xml file");
+
+ return SourceFile.ChangeExtension("xml");
+ }
+ }
+
+ public NPath TestSuiteDirectory { get; }
+
+ public TypeDefinition FindTypeDefinition(AssemblyDefinition assemblyDefinition)
+ => TryFindTypeDefinition(assemblyDefinition) is TypeDefinition typeDefinition
+ ? typeDefinition
+ : throw new InvalidOperationException($"Could not find the type definition for {Name} in {assemblyDefinition.Name}");
+
+ public TypeDefinition? TryFindTypeDefinition(AssemblyDefinition caseAssemblyDefinition)
+ {
+ var typeDefinition = caseAssemblyDefinition.MainModule.GetType(reconstructedFullTypeName);
+
+ // For all of the Test Cases, the full type name we constructed from the directory structure will be correct and we can successfully find
+ // the type from GetType.
+ if (typeDefinition != null)
+ return typeDefinition;
+
+ // However, some of types are supporting types rather than test cases and may not follow the standardized naming scheme of the test cases.
+ // We still need to be able to locate these type defs so that we can parse some of the metadata on them.
+ // One example, Unity run's into this with its tests that require a type UnityEngine.MonoBehaviours to exist. This type is defined in its own
+ // file and it cannot follow our standardized naming directory & namespace naming scheme since the namespace must be UnityEngine.
+ // Also look for compiler-generated Program type for top-level statements.
+ foreach (var type in caseAssemblyDefinition.MainModule.Types)
+ {
+ if (type.Name == "Program" &&
+ type.CustomAttributes.Any(attr => attr.AttributeType.Name == nameof(CompilerGeneratedAttribute)))
+ return type;
+
+ // Let's assume we should never have to search for a test case that has no namespace. If we don't find the type from GetType, then o well, that's not a test case.
+ if (string.IsNullOrEmpty(type.Namespace))
+ continue;
+
+ if (type.Name == Name)
+ {
+ // This isn't foolproof, but let's do a little extra vetting to make sure the type we found corresponds to the source file we are
+ // processing.
+ if (!SourceFile.ReadAllText().Contains($"namespace {type.Namespace}"))
+ continue;
+
+ return type;
+ }
+ }
+
+ return null;
+ }
+ }
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs
index f742d409d9b75a..8f5b20deef2c96 100644
--- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs
@@ -10,29 +10,29 @@
namespace Mono.Linker.Tests.TestCases
{
- public static class TestDatabase
- {
- private static TestCase[]? _cachedAllCases;
-
- public static IEnumerable