//TODO: for high optimization add ParseBoolUnsafe ect. that doesn't contains the mValue type check for scenarios where we already had to check the mValue type //TODO: add support for own function arguments parser for significant performance improvements with all benefits // Returns null when function signature isn't supported public static Function Create <T>(T func) where T : Delegate { var type = func.GetType(); var genericArguments = type.GetGenericArguments(); Type returnType; if (type.Name.StartsWith("Func")) { // Return type is last generic argument // Function never has empty generic arguments, so no need for size check, but we do it anyway if (genericArguments.Length == 0) { returnType = FunctionTypes.Void; } else { returnType = genericArguments[genericArguments.Length - 1]; genericArguments = genericArguments.SkipLast(1).ToArray(); } } else { returnType = FunctionTypes.Void; } //TODO: check for unsupported types //TODO: check if last type is a object[] and add all to it that didnt fit with length, like (string bla, object[] args) //TODO: add parameter type attribute to annotate object[] with var parsers = new FunctionMValueParser[genericArguments.Length]; var objectParsers = new FunctionObjectParser[genericArguments.Length]; var stringParsers = new FunctionStringParser[genericArguments.Length]; var typeInfos = new FunctionTypeInfo[genericArguments.Length]; var requiredArgsCount = 0; for (int i = 0, length = genericArguments.Length; i < length; i++) { var arg = genericArguments[i]; var typeInfo = new FunctionTypeInfo(arg); typeInfos[i] = typeInfo; if (!typeInfo.IsNullable && !typeInfo.IsEventParams) { requiredArgsCount++; } if (arg == FunctionTypes.Obj) { //TODO: use MValue.ToObject here parsers[i] = FunctionMValueParsers.ParseObject; objectParsers[i] = FunctionObjectParsers.ParseObject; stringParsers[i] = FunctionStringParsers.ParseObject; } else if (arg == FunctionTypes.Bool) { parsers[i] = FunctionMValueParsers.ParseBool; objectParsers[i] = FunctionObjectParsers.ParseBool; stringParsers[i] = FunctionStringParsers.ParseBool; } else if (arg == FunctionTypes.Int) { parsers[i] = FunctionMValueParsers.ParseInt; objectParsers[i] = FunctionObjectParsers.ParseInt; stringParsers[i] = FunctionStringParsers.ParseInt; } else if (arg == FunctionTypes.Long) { parsers[i] = FunctionMValueParsers.ParseLong; objectParsers[i] = FunctionObjectParsers.ParseLong; stringParsers[i] = FunctionStringParsers.ParseLong; } else if (arg == FunctionTypes.UInt) { parsers[i] = FunctionMValueParsers.ParseUInt; objectParsers[i] = FunctionObjectParsers.ParseUInt; stringParsers[i] = FunctionStringParsers.ParseUInt; } else if (arg == FunctionTypes.ULong) { parsers[i] = FunctionMValueParsers.ParseULong; objectParsers[i] = FunctionObjectParsers.ParseULong; stringParsers[i] = FunctionStringParsers.ParseULong; } else if (arg == FunctionTypes.Float) { parsers[i] = FunctionMValueParsers.ParseFloat; objectParsers[i] = FunctionObjectParsers.ParseFloat; stringParsers[i] = FunctionStringParsers.ParseFloat; } else if (arg == FunctionTypes.Double) { parsers[i] = FunctionMValueParsers.ParseDouble; objectParsers[i] = FunctionObjectParsers.ParseDouble; stringParsers[i] = FunctionStringParsers.ParseDouble; } else if (arg == FunctionTypes.String) { parsers[i] = FunctionMValueParsers.ParseString; objectParsers[i] = FunctionObjectParsers.ParseString; stringParsers[i] = FunctionStringParsers.ParseString; } else if (arg.BaseType == FunctionTypes.Array) { parsers[i] = FunctionMValueParsers.ParseArray; objectParsers[i] = FunctionObjectParsers.ParseArray; stringParsers[i] = FunctionStringParsers.ParseArray; } else if (typeInfo.IsEntity) { parsers[i] = FunctionMValueParsers.ParseEntity; objectParsers[i] = FunctionObjectParsers.ParseEntity; stringParsers[i] = FunctionStringParsers.ParseEntity; } else if (typeInfo.IsDict) { parsers[i] = FunctionMValueParsers.ParseDictionary; objectParsers[i] = FunctionObjectParsers.ParseDictionary; stringParsers[i] = FunctionStringParsers.ParseDictionary; } else if (typeInfo.IsMValueConvertible) { parsers[i] = FunctionMValueParsers.ParseConvertible; objectParsers[i] = FunctionObjectParsers.ParseConvertible; stringParsers[i] = FunctionStringParsers.ParseConvertible; } else if (arg == FunctionTypes.FunctionType) { parsers[i] = FunctionMValueParsers.ParseFunction; objectParsers[i] = FunctionObjectParsers.ParseFunction; stringParsers[i] = FunctionStringParsers.ParseFunction; } else if (typeInfo.IsEnum) { parsers[i] = FunctionMValueParsers.ParseEnum; objectParsers[i] = FunctionObjectParsers.ParseEnum; } else { // Unsupported type return(null); } } for (int i = 0, length = typeInfos.Length; i < length; i++) { if (!typeInfos[i].IsNullable || i - 1 >= length) { continue; } if (!typeInfos[i + 1].IsNullable && !typeInfos[i + 1].IsEventParams) { throw new ArgumentException( "Method nullable needs to be at the end of the method. E.g. (int p1, int? p2, int p3?)."); } } CallArgsLengthCheck callArgsLengthCheck = CheckArgsLength; CallArgsLengthCheckPlayer callArgsLengthCheckPlayer = CheckArgsLengthWithPlayer; if (typeInfos.Length > 0) { var lastTypeInfo = typeInfos[typeInfos.Length - 1]; if (lastTypeInfo.IsEventParams) { if (lastTypeInfo.IsList) { throw new ArgumentException("EventParams needs to be a array."); } callArgsLengthCheck = CheckArgsLengthWithEventParams; callArgsLengthCheckPlayer = CheckArgsLengthWithPlayerWithEventParams; } } return(new Function(func, returnType, genericArguments, parsers, objectParsers, stringParsers, typeInfos, callArgsLengthCheck, callArgsLengthCheckPlayer, requiredArgsCount)); }
/*private delegate bool CallArgsLengthCheck(Type[] args, int requiredArgsCount, FunctionTypeInfo[] typeInfos, * MValueConst[] values); * * private delegate bool CallArgsLengthCheckPlayer(Type[] args, int requiredArgsCount, * FunctionTypeInfo[] typeInfos, IPlayer player, * MValueConst[] values);*/ //TODO: for high optimization add ParseBoolUnsafe ect. that doesn't contains the mValue type check for scenarios where we already had to check the mValue type // Returns null when function signature isn't supported public static Function Create <T>(T func) where T : Delegate { var genericArguments = func.GetType().GetGenericArguments(); var parameters = func.Method.GetParameters(); var returnType = func.Method.ReturnType; if (returnType != FunctionTypes.Void) { genericArguments = genericArguments.SkipLast(1).ToArray(); } //TODO: check for unsupported types var constParsers = new FunctionMValueConstParser[parameters.Length]; var objectParsers = new FunctionObjectParser[parameters.Length]; var stringParsers = new FunctionStringParser[parameters.Length]; var typeInfos = new FunctionTypeInfo[parameters.Length]; var requiredArgsCount = 0; for (int i = 0, length = parameters.Length; i < length; i++) { var parameterInfo = parameters[i]; var arg = parameterInfo.ParameterType; var typeInfo = typeInfos[i] = new FunctionTypeInfo(arg, parameterInfo); if (!typeInfo.IsNullable && !typeInfo.IsParamArray && !parameterInfo.HasDefaultValue) { requiredArgsCount++; } constParsers[i] = typeInfo.ConstParser; objectParsers[i] = typeInfo.ObjectParser; stringParsers[i] = typeInfo.StringParser; if (constParsers[i] == null || objectParsers[i] == null || stringParsers[i] == null) { // Unsupported type return(null); } } for (int i = 0, length = typeInfos.Length; i < length; i++) { if (typeInfos[i].IsParamArray && i + 1 < length) { throw new ArgumentException( "params array needs to be at the end of the method. E.g. (int p1, int p2, params int[] args)"); } if (!typeInfos[i].IsNullable || i + 1 >= length) { continue; } if (!typeInfos[i + 1].IsNullable && !typeInfos[i + 1].IsParamArray) { throw new ArgumentException( "Method nullable needs to be at the end of the method. E.g. (int p1, int? p2, int p3?)."); } } return(new Function(func, returnType, genericArguments, constParsers, objectParsers, stringParsers, typeInfos, requiredArgsCount)); }
public FunctionTypeInfo(Type paramType, ParameterInfo paramInfo = null) { var param = Expression.Parameter(typeof(int)); CreateArrayOfElementType = Expression.Lambda <Func <int, Array> >( Expression.NewArrayBounds(paramType, param), param).Compile(); IsList = paramType.BaseType == FunctionTypes.Array; IsDict = paramType.Name.StartsWith("Dictionary") || paramType.Name.StartsWith("IDictionary"); if (IsDict) { GenericArguments = paramType.GetGenericArguments(); //TODO: dont create this for primitive type dictionaries DictType = typeof(Dictionary <,>).MakeGenericType(GenericArguments[0], GenericArguments[1]); DictionaryValue = GenericArguments.Length == 2 ? new FunctionTypeInfo(GenericArguments[1]) : null; CreateDictionary = Expression.Lambda <Func <IDictionary> >( Expression.New(DictType) ).Compile(); } else { GenericArguments = null; DictType = null; DictionaryValue = null; CreateDictionary = null; } if (paramInfo != null && paramInfo.HasDefaultValue) { DefaultValue = paramInfo.DefaultValue; } else { if (paramType.IsValueType && paramType != FunctionTypes.String) { DefaultValue = Activator.CreateInstance(paramType); } else { DefaultValue = null; } } IsPosition = paramType == FunctionTypes.Position; IsRotation = paramType == FunctionTypes.Rotation; IsRgba = paramType == FunctionTypes.Rgba; IsVector3 = paramType == FunctionTypes.Vector3; IsByteArray = paramType == FunctionTypes.ByteArray; var interfaces = paramType.GetInterfaces(); if (interfaces.Contains(FunctionTypes.BaseObject)) { IsBaseObject = true; IsVehicle = paramType == FunctionTypes.Vehicle || interfaces.Contains(FunctionTypes.Vehicle); IsPlayer = paramType == FunctionTypes.Player || interfaces.Contains(FunctionTypes.Player); } else { IsBaseObject = false; IsVehicle = false; IsPlayer = false; } IsMValueConvertible = interfaces.Contains(FunctionTypes.MValueConvertible); var elementType = paramType.GetElementType(); if (elementType != null) { ElementType = elementType; Element = new FunctionTypeInfo(elementType); var arraySizeParam = Expression.Parameter(typeof(int)); CreateArrayOfTypeExp = Expression.Lambda <Func <int, Array> >( Expression.NewArrayBounds(ElementType, arraySizeParam), new[] { arraySizeParam } ).Compile(); EmptyArrayOfType = Array.CreateInstance(ElementType, 0); } else { CreateArrayOfTypeExp = null; EmptyArrayOfType = null; } if (paramInfo != null) { IsParamArray = paramInfo.GetCustomAttribute <ParamArrayAttribute>() != null; } IsNullable = paramType.Name.StartsWith("Nullable"); if (IsNullable) { var genericArguments = paramType.GetGenericArguments(); if (genericArguments.Length != 1) { IsNullable = false; } else { NullableType = genericArguments[0]; DefaultValue = Activator.CreateInstance(typeof(Nullable <>).MakeGenericType(NullableType)); } } IsEnum = paramType.IsEnum; if (IsNullable) { paramType = NullableType; } if (paramType == FunctionTypes.Obj) { ConstParser = FunctionMValueConstParsers.ParseObject; ObjectParser = FunctionObjectParsers.ParseObject; StringParser = FunctionStringParsers.ParseObject; } else if (paramType == FunctionTypes.Bool) { ConstParser = FunctionMValueConstParsers.ParseBool; ObjectParser = FunctionObjectParsers.ParseBool; StringParser = FunctionStringParsers.ParseBool; } else if (paramType == FunctionTypes.SByte) { ConstParser = FunctionMValueConstParsers.ParseSByte; ObjectParser = FunctionObjectParsers.ParseSByte; StringParser = FunctionStringParsers.ParseSByte; } else if (paramType == FunctionTypes.Short) { ConstParser = FunctionMValueConstParsers.ParseShort; ObjectParser = FunctionObjectParsers.ParseShort; StringParser = FunctionStringParsers.ParseShort; } else if (paramType == FunctionTypes.Int) { ConstParser = FunctionMValueConstParsers.ParseInt; ObjectParser = FunctionObjectParsers.ParseInt; StringParser = FunctionStringParsers.ParseInt; } else if (paramType == FunctionTypes.Long) { ConstParser = FunctionMValueConstParsers.ParseLong; ObjectParser = FunctionObjectParsers.ParseLong; StringParser = FunctionStringParsers.ParseLong; } else if (paramType == FunctionTypes.Byte) { ConstParser = FunctionMValueConstParsers.ParseByte; ObjectParser = FunctionObjectParsers.ParseByte; StringParser = FunctionStringParsers.ParseByte; } else if (paramType == FunctionTypes.UShort) { ConstParser = FunctionMValueConstParsers.ParseUShort; ObjectParser = FunctionObjectParsers.ParseUShort; StringParser = FunctionStringParsers.ParseUShort; } else if (paramType == FunctionTypes.UInt) { ConstParser = FunctionMValueConstParsers.ParseUInt; ObjectParser = FunctionObjectParsers.ParseUInt; StringParser = FunctionStringParsers.ParseUInt; } else if (paramType == FunctionTypes.ULong) { ConstParser = FunctionMValueConstParsers.ParseULong; ObjectParser = FunctionObjectParsers.ParseULong; StringParser = FunctionStringParsers.ParseULong; } else if (paramType == FunctionTypes.Float) { ConstParser = FunctionMValueConstParsers.ParseFloat; ObjectParser = FunctionObjectParsers.ParseFloat; StringParser = FunctionStringParsers.ParseFloat; } else if (paramType == FunctionTypes.Double) { ConstParser = FunctionMValueConstParsers.ParseDouble; ObjectParser = FunctionObjectParsers.ParseDouble; StringParser = FunctionStringParsers.ParseDouble; } else if (paramType == FunctionTypes.String) { ConstParser = FunctionMValueConstParsers.ParseString; ObjectParser = FunctionObjectParsers.ParseString; StringParser = FunctionStringParsers.ParseString; } else if (paramType.BaseType == FunctionTypes.Array) { ConstParser = FunctionMValueConstParsers.ParseArray; ObjectParser = FunctionObjectParsers.ParseArray; StringParser = FunctionStringParsers.ParseArray; } else if (IsBaseObject) { ConstParser = FunctionMValueConstParsers.ParseBaseObject; ObjectParser = FunctionObjectParsers.ParseBaseObject; StringParser = FunctionStringParsers.ParseBaseObject; } else if (IsDict) { ConstParser = FunctionMValueConstParsers.ParseDictionary; ObjectParser = FunctionObjectParsers.ParseDictionary; StringParser = FunctionStringParsers.ParseDictionary; } else if (IsMValueConvertible) { ConstParser = FunctionMValueConstParsers.ParseConvertible; ObjectParser = FunctionObjectParsers.ParseConvertible; StringParser = FunctionStringParsers.ParseConvertible; } else if (paramType == FunctionTypes.FunctionType) { ConstParser = FunctionMValueConstParsers.ParseFunction; ObjectParser = FunctionObjectParsers.ParseFunction; StringParser = FunctionStringParsers.ParseFunction; } else if (IsEnum) { ConstParser = FunctionMValueConstParsers.ParseEnum; ObjectParser = FunctionObjectParsers.ParseEnum; StringParser = FunctionStringParsers.ParseEnum; } else if (IsPosition) { ConstParser = FunctionMValueConstParsers.ParsePosition; ObjectParser = FunctionObjectParsers.ParsePosition; StringParser = FunctionStringParsers.ParsePosition; } else if (IsRotation) { ConstParser = FunctionMValueConstParsers.ParseRotation; ObjectParser = FunctionObjectParsers.ParseRotation; StringParser = FunctionStringParsers.ParseRotation; } else if (IsVector3) { ConstParser = FunctionMValueConstParsers.ParseVector3; ObjectParser = FunctionObjectParsers.ParseVector3; StringParser = FunctionStringParsers.ParseVector3; } else if (IsRgba) { ConstParser = FunctionMValueConstParsers.ParseRgba; ObjectParser = FunctionObjectParsers.ParseRgba; StringParser = FunctionStringParsers.ParseRgba; } else if (IsByteArray) { ConstParser = FunctionMValueConstParsers.ParseByteArray; ObjectParser = FunctionObjectParsers.ParseByteArray; StringParser = FunctionStringParsers.ParseByteArray; } else if (AltShared.Core.IsMValueConvertible(paramType)) { ConstParser = FunctionMValueConstParsers.ParseConvertible; ObjectParser = FunctionObjectParsers.ParseConvertible; StringParser = FunctionStringParsers.ParseConvertible; } else { ConstParser = null; ObjectParser = null; StringParser = null; } }