rabidus-test/Assets/Plugins/QFSW/Quantum Console/Source/Extras/MegaCommands.cs

249 lines
10 KiB
C#

using QFSW.QC.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace QFSW.QC.Extras
{
public static class MegaCommands
{
private static readonly QuantumSerializer Serializer = new QuantumSerializer();
private static readonly QuantumParser Parser = new QuantumParser();
private static MethodInfo[] ExtractMethods(Type type, string name)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod |
BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
MethodInfo[] methods = type.GetMethods(flags).Where(x => x.Name == name).ToArray();
if (!methods.Any())
{
PropertyInfo property = type.GetProperty(name, flags);
if (property != null)
{
methods = new[] {property.GetMethod, property.SetMethod}.Where(x => x != null).ToArray();
if (methods.Length > 0)
{
return methods;
}
}
throw new ArgumentException($"No method or property named {name} could be found in class {Serializer.SerializeFormatted(type)}");
}
return methods;
}
private static string GenerateSignature(MethodInfo method)
{
IEnumerable<string> paramParts = method.GetParameters()
.Select(x => (x.Name, x.ParameterType))
.Select(x => $"{x.ParameterType.GetDisplayName()} {x.Name}");
string paramSignature = string.Join(", ", paramParts);
return $"{method.Name}({paramSignature})";
}
private static MethodInfo GetIdealOverload(MethodInfo[] methods, bool isStatic, int argc)
{
methods = methods.Where(x => x.IsStatic == isStatic).ToArray();
if (methods.Length == 0)
{
throw new ArgumentException($"No {(isStatic ? "static" : "non-static")} overloads could be found.");
}
if (methods.Length == 1)
{
return methods[0];
}
methods = methods.Where(x => !x.IsGenericMethod).ToArray();
if (methods.Length == 0)
{
throw new ArgumentException("Generic methods are not supported.");
}
MethodInfo[] argcMatches = methods.Where(x => x.GetParameters().Length == argc).ToArray();
if (argcMatches.Length == 1)
{
return argcMatches[0];
}
else if (argcMatches.Length == 0)
{
IEnumerable<string> signatures = methods.Select(GenerateSignature);
string combinedSignatures = string.Join("\n", signatures);
throw new ArgumentException($"No overloads with {argc} arguments were found. the following overloads are available:\n{combinedSignatures}");
}
else
{
IEnumerable<string> signatures = argcMatches.Select(GenerateSignature);
string combinedSignatures = string.Join("\n", signatures);
throw new ArgumentException($"Multiple overloads with the same argument count were found: please specify the types explicitly.\n{combinedSignatures}");
}
}
private static MethodInfo GetIdealOverload(MethodInfo[] methods, bool isStatic, Type[] argTypes)
{
// Exact matching
foreach (MethodInfo method in methods)
{
if (method.IsStatic == isStatic)
{
IEnumerable<Type> methodParamTypes = method.GetParameters().Select(x => x.ParameterType);
if (methodParamTypes.SequenceEqual(argTypes))
{
return method;
}
}
}
// Polymorphic matching
foreach (MethodInfo method in methods)
{
if (method.IsStatic == isStatic)
{
ParameterInfo[] methodParams = method.GetParameters();
if (methodParams.Length == argTypes.Length)
{
bool isMatch = methodParams
.Select(x => x.ParameterType)
.Zip(argTypes, (x, y) => (x, y))
.All(pair => pair.x.IsAssignableFrom(pair.y));
if (isMatch)
{
return method;
}
}
}
}
throw new ArgumentException("No overload with the supplied argument types could be found.");
}
private static object[] CreateArgs(MethodInfo method, string[] rawArgs)
{
ParameterInfo[] methodParams = method.GetParameters();
Type[] argTypes = methodParams.Select(x => x.ParameterType).ToArray();
return CreateArgs(method, argTypes, rawArgs);
}
private static object[] CreateArgs(MethodInfo method, Type[] argTypes, string[] rawArgs)
{
ParameterInfo[] methodParams = method.GetParameters();
int defaultArgs = methodParams.Count(x => x.HasDefaultValue);
if (rawArgs.Length < argTypes.Length - defaultArgs || rawArgs.Length > argTypes.Length)
{
throw new ArgumentException($"Incorrect number ({rawArgs.Length}) of arguments supplied for {Serializer.SerializeFormatted(method.DeclaringType)}.{method.Name}" +
$", expected {argTypes.Length}");
}
object[] parsedArgs = new object[argTypes.Length];
for (int i = 0; i < parsedArgs.Length; i++)
{
if (i < rawArgs.Length)
{
parsedArgs[i] = Parser.Parse(rawArgs[i], argTypes[i]);
}
else
{
parsedArgs[i] = methodParams[i].DefaultValue;
}
}
return parsedArgs;
}
private static object InvokeAndUnwrapException(this MethodInfo method, object[] args)
{
try
{
return method.Invoke(null, args);
}
catch (TargetInvocationException e)
{
throw e.InnerException;
}
}
private static object InvokeAndUnwrapException(this MethodInfo method, IEnumerable<object> targets, object[] args)
{
try
{
return InvocationTargetFactory.InvokeOnTargets(method, targets, args);
}
catch (TargetInvocationException e)
{
throw e.InnerException;
}
}
[Command("call-static")]
private static object CallStatic(Type classType, string funcName)
{
return CallStatic(classType, funcName, Array.Empty<string>());
}
[Command("call-static")]
private static object CallStatic(Type classType, string funcName, string[] args)
{
MethodInfo[] methods = ExtractMethods(classType, funcName);
MethodInfo method = GetIdealOverload(methods, true, args.Length);
object[] parsedArgs = CreateArgs(method, args);
return method.InvokeAndUnwrapException(parsedArgs);
}
[Command("call-static")]
[CommandDescription("Invokes the specified static method or property with the provided arguments. Provide [argTypes] if there are ambiguous overloads")]
private static object CallStatic(
[CommandParameterDescription("Namespace qualified typename of the class.")] Type classType,
[CommandParameterDescription("Name of the method or property.")] string funcName,
[CommandParameterDescription("The arguments for the function call.")] string[] args,
[CommandParameterDescription("The types of the arguments to resolve ambiguous overloads.")] Type[] argTypes)
{
MethodInfo[] methods = ExtractMethods(classType, funcName);
MethodInfo method = GetIdealOverload(methods, true, argTypes);
object[] parsedArgs = CreateArgs(method, argTypes, args);
return method.InvokeAndUnwrapException(parsedArgs);
}
[Command("call-instance")]
private static object CallInstance(Type classType, string funcName, MonoTargetType targetType)
{
return CallInstance(classType, funcName, targetType, Array.Empty<string>());
}
[Command("call-instance")]
private static object CallInstance(Type classType, string funcName, MonoTargetType targetType, string[] args)
{
MethodInfo[] methods = ExtractMethods(classType, funcName);
MethodInfo method = GetIdealOverload(methods, false, args.Length);
object[] parsedArgs = CreateArgs(method, args);
IEnumerable<object> targets = InvocationTargetFactory.FindTargets(classType, targetType);
return method.InvokeAndUnwrapException(targets, parsedArgs);
}
[Command("call-instance")]
[CommandDescription("Invokes the specified non-static method or property with the provided arguments. Provide [argTypes] if there are ambiguous overloads")]
private static object CallInstance(
[CommandParameterDescription("Namespace qualified typename of the class.")] Type classType,
[CommandParameterDescription("Name of the method or property.")] string funcName,
[CommandParameterDescription("The MonoTargetType used to find the target instances.")] MonoTargetType targetType,
[CommandParameterDescription("The arguments for the function call.")] string[] args,
[CommandParameterDescription("The types of the arguments to resolve ambiguous overloads.")] Type[] argTypes)
{
MethodInfo[] methods = ExtractMethods(classType, funcName);
MethodInfo method = GetIdealOverload(methods, false, argTypes);
object[] parsedArgs = CreateArgs(method, argTypes, args);
IEnumerable<object> targets = InvocationTargetFactory.FindTargets(classType, targetType);
return method.InvokeAndUnwrapException(targets, parsedArgs);
}
}
}