#if REFLECTION_EMIT_COMPILER using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp; using System.Reflection.Emit; using System.Reflection; using System.Diagnostics; using System.Linq.Expressions; using System; using System.Collections.Generic; using System.Security.Cryptography; using FastReport.Utils; using System.Linq; using System.Runtime.CompilerServices; #if DEBUG using ILGenerator = FastReport.Code.Expressions.ILGeneratorDebugger; #endif namespace FastReport.Code.Expressions { /* TODO: * new [] {} * static Type searching * extension methods * 'dynamic' support */ internal sealed class CSharpILParser : ILParser { ILGenerator gen; private readonly Report _report; private Type[] argTypes; private static readonly Type _returnType = typeof(object); public CSharpILParser(Report report) { _report = report; } protected override MethodInfo CompileExpression(string expression, Type[] argTypes) { ExpressionSyntax parse = SyntaxFactory.ParseExpression(expression); return StartDynamic(parse, argTypes); } private MethodInfo StartDynamic(ExpressionSyntax expressionSyntax, Type[] argTypes) { const string myMthdName = "CalcExpression"; this.argTypes = argTypes; DynamicMethod dynamicMethod = new DynamicMethod(myMthdName, _returnType, argTypes, typeof(ILGenerator).Module, true //typeof(string).Module ); #if DEBUG gen = new ILGeneratorDebugger(dynamicMethod.GetILGenerator()); #else gen = dynamicMethod.GetILGenerator(); #endif StartParse(expressionSyntax); return dynamicMethod; } private void StartParse(ExpressionSyntax expressionSyntax) { Type returnType = ParseToIL(expressionSyntax); CastTo(returnType, _returnType); gen.Emit(OpCodes.Ret); } public Type ParseToIL(ExpressionSyntax expression, Type leftType = null) { if (expression is BinaryExpressionSyntax binary) return BinaryOp(binary); else if (expression is LiteralExpressionSyntax literal) return PutValueOnStack(literal.Token.Value); else if (expression is InvocationExpressionSyntax invocation) return ParseToIL(invocation.Expression, leftType); else if (expression is MemberAccessExpressionSyntax memberAccess) return MemberAccessOp(memberAccess); else if (expression is IdentifierNameSyntax identifierName) return SearchIdentifier(identifierName, leftType); else if (expression is PredefinedTypeSyntax predefinedType) return ParsePredefinedType(predefinedType.Keyword.Value.ToString()); else if (expression is PostfixUnaryExpressionSyntax postfixUnary) return UnaryPostOp(postfixUnary); else if (expression is PrefixUnaryExpressionSyntax prefixUnary) return UnaryPreOp(prefixUnary); else if (expression is ConditionalExpressionSyntax conditional) // a ? b : c return ConditionalOp(conditional); else if (expression is AssignmentExpressionSyntax assignment) // = return AssignmentOp(assignment); else if (expression is ParenthesizedExpressionSyntax parenthesized) // (a) return ParseToIL(parenthesized.Expression, leftType); else if (expression is CastExpressionSyntax cast) // ()a return ExplicitCastOp(cast); else if (expression is ConditionalAccessExpressionSyntax conditionalAccess) // ?. return ConditionalAccessOp(conditionalAccess); else if (expression is MemberBindingExpressionSyntax memberBinding) return MemberAccessOp(memberBinding, leftType); else if (expression is ElementAccessExpressionSyntax elementAccess) // a[0] return ElementAccessOp(elementAccess); else if (expression is ArrayCreationExpressionSyntax arrayCreation) // new int[] { } { // TODO: var arrayType = ParseToIL(arrayCreation.Type); } else if (expression is ImplicitArrayCreationExpressionSyntax implicitArrayCreation) // new[] { } { } throw new NotImplementedException(); } private Type SearchIdentifier(IdentifierNameSyntax identifierName, Type leftType) { var identifier = identifierName.Identifier.ValueText; // leftType can be null => it's static class etc if (identifierName.Parent is InvocationExpressionSyntax invocation) { // method: Type[] argumentTypes = ExecuteArguments(invocation.ArgumentList); FunctionInfo info = RegisteredObjects.Functions.Find(identifier); if (info == null) throw new NotSupportedException($"Method '{identifier}' wasn't found"); return MethodCall(leftType, info.Function); } else { // class, parameter, property, field etc if (identifier == "Report") // hard { return LoadReport(); } else if (identifier == "Value") { gen.Emit(OpCodes.Ldarg_1); return argTypes[1]; } else if (identifier == "Engine") { var reportType = LoadReport(); return MemberAccessOp(identifier, identifierName, reportType); } var type = ParseKnownType(identifier); if (type != null) return type; // report objects Base obj = _report.FindObject(identifier); if (obj != null) { // IT'S DYNAMIC STATE! We should't store it! var reportType = LoadReport(); var argType = PutValueOnStack(identifier); var findObjectMethod = reportType.GetMethod(nameof(_report.FindObject), new[] {argType}); var findObjectReturnType = MethodCall(reportType, findObjectMethod); // cast to necessary type: var reportObjectType = obj.GetType(); CastTo(findObjectReturnType, reportObjectType); return reportObjectType; } if (type == null) type = Type.GetType(identifier, false, false); if (type != null) return type; } throw new NotSupportedException(); } private Type LoadReport() { gen.Emit(OpCodes.Ldarg_0); return argTypes[0]; } private static Type ParsePredefinedType(string typeName) { switch (typeName) { case "object": return typeof(object); case "string": return typeof(string); case "bool": return typeof(bool); case "byte": return typeof(byte); case "sbyte": return typeof(sbyte); case "char": return typeof(char); case "short": return typeof(short); case "ushort": return typeof(ushort); case "int": return typeof(int); case "uint": return typeof(uint); case "long": return typeof(long); case "ulong": return typeof(ulong); case "float": return typeof(float); case "double": return typeof(double); case "decimal": return typeof(decimal); } throw new NotSupportedException(); } private static Type ParseKnownType(string typeName) { switch (typeName) { case "Object": return typeof(object); case "String": return typeof(string); case "Boolean": return typeof(Boolean); case "Byte": return typeof(Byte); case "SByte": return typeof(SByte); case "Char": return typeof(Char); case "Int16": return typeof(Int16); case "UInt16": return typeof(UInt16); case "Int32": return typeof(Int32); case "UInt32": return typeof(UInt32); case "Int64": return typeof(Int64); case "UInt64": return typeof(UInt64); case "Single": return typeof(Single); case "Double": return typeof(Double); case "Decimal": return typeof(Decimal); case "DateTime": return typeof(DateTime); case "DateTimeOffset": return typeof(DateTimeOffset); } return null; } private Type MemberAccessOp(MemberAccessExpressionSyntax memberAccess) { Type type = ParseToIL(memberAccess.Expression); var name = memberAccess.Name.Identifier.ValueText; return MemberAccessOp(name, memberAccess, type); } private Type MemberAccessOp(MemberBindingExpressionSyntax memberAccess, Type leftType) { var name = memberAccess.Name.Identifier.ValueText; return MemberAccessOp(name, memberAccess, leftType); } private Type MemberAccessOp(string name, ExpressionSyntax expression, Type leftType) { if (expression.Parent is InvocationExpressionSyntax invocation) { // method Type[] argumentTypes = ExecuteArguments(invocation.ArgumentList); var methodInfo = CustomMethodSearcher.Search(leftType, name, argumentTypes); return MethodCall(leftType, methodInfo); } else { // property or field var property = leftType.GetProperty(name); // if it's not a property => try to find a field if (property == null) { var field = leftType.GetField(name); gen.Emit(OpCodes.Ldfld, field); return field.FieldType; } // only get var methodInfo = property.GetGetMethod(); return MethodCall(leftType, methodInfo); } } private Type MethodCall(Type targetType, MethodInfo methodInfo) { if (methodInfo.IsStatic) { gen.EmitCall(OpCodes.Call, methodInfo, null); } else { if (targetType.IsValueType) { // if local: var local = gen.DeclareLocal(targetType); gen.Emit(OpCodes.Stloc, local); gen.Emit(OpCodes.Ldloca_S, local); gen.EmitCall(OpCodes.Call, methodInfo, null); } else gen.EmitCall(OpCodes.Callvirt, methodInfo, null); } return methodInfo.ReturnType; } private Type AssignmentOp(AssignmentExpressionSyntax assignment) { var type = ParseToIL(assignment.Right); ParseToIL(assignment.Left); if (false) // if field { var local = gen.DeclareLocal(type); gen.Emit(OpCodes.Starg_S, local); } // TODO: assignment throw new NotImplementedException(); //return type; return typeof(void); } private Type[] ExecuteArguments(BaseArgumentListSyntax argumentList) { var arguments = argumentList.Arguments; List types = new List(); foreach (var argument in arguments) { var resultTypeArg = ParseToIL(argument.Expression); types.Add(resultTypeArg); } return types.ToArray(); } private Type ConditionalOp(ConditionalExpressionSyntax conditional) { Label ifTrue = gen.DefineLabel(); Label next = gen.DefineLabel(); var conditionType = ParseToIL(conditional.Condition); //if (conditionType != typeof(bool)) // throw new Exception(); gen.Emit(OpCodes.Brtrue_S, ifTrue); var type1 = ParseToIL(conditional.WhenFalse); gen.Emit(OpCodes.Br_S, next); gen.MarkLabel(ifTrue); var type2 = ParseToIL(conditional.WhenTrue); gen.MarkLabel(next); //if (type1 != type2) // throw new Exception(); return type1; } private Type ConditionalAccessOp(ConditionalAccessExpressionSyntax conditionalAccess) { Label ifTrue = gen.DefineLabel(); Label next = gen.DefineLabel(); var type = ParseToIL(conditionalAccess.Expression); gen.Emit(OpCodes.Dup); gen.Emit(OpCodes.Brtrue_S, ifTrue); gen.Emit(OpCodes.Pop); gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Br_S, next); gen.MarkLabel(ifTrue); var resultType = ParseToIL(conditionalAccess.WhenNotNull, type); gen.MarkLabel(next); return resultType; } private Type UnaryPreOp(PrefixUnaryExpressionSyntax prefixUnary) { var kind = prefixUnary.Kind(); var op = prefixUnary.Operand; Type returnType = ParseToIL(op); if (returnType == typeof(decimal)) return BinaryCustomTypeOp(returnType, kind); switch (kind) { case SyntaxKind.BitwiseNotExpression: // ~ gen.Emit(OpCodes.Not); break; case SyntaxKind.UnaryMinusExpression: // - gen.Emit(OpCodes.Neg); break; case SyntaxKind.LogicalNotExpression: // ! returnType = CompareWithZero(); break; case SyntaxKind.PreIncrementExpression: // ++a gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Add); break; case SyntaxKind.PreDecrementExpression: // --a gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Sub); break; } return returnType; } private Type UnaryPostOp(PostfixUnaryExpressionSyntax postfixUnary) { var op = postfixUnary.Operand; var kind = postfixUnary.Kind(); Type returnType = ParseToIL(op); if (returnType == typeof(decimal)) return BinaryCustomTypeOp(returnType, kind); switch (kind) { case SyntaxKind.PostIncrementExpression: // a++ gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Add); break; case SyntaxKind.PostDecrementExpression: // a-- gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Sub); break; } return returnType; } private Type PutValueOnStack(object value) { if (value == null) { gen.Emit(OpCodes.Ldnull); return typeof(object); } var type = value.GetType(); if (type == typeof(int)) { gen.Emit(OpCodes.Ldc_I4, Convert.ToInt32(value)); } else if (type == typeof(bool)) { if (Convert.ToBoolean(value)) gen.Emit(OpCodes.Ldc_I4_1); else gen.Emit(OpCodes.Ldc_I4_0); } else if (type == typeof(float)) { gen.Emit(OpCodes.Ldc_R4, Convert.ToSingle(value)); } else if (type == typeof(double)) { gen.Emit(OpCodes.Ldc_R8, Convert.ToDouble(value)); } //else if (type == typeof(char)) // => int32 //{} else if (type == typeof(string)) { gen.Emit(OpCodes.Ldstr, Convert.ToString(value)); } else if (type == typeof(long)) { gen.Emit(OpCodes.Ldc_I8, Convert.ToInt64(value)); } else { gen.Emit(OpCodes.Ldc_I4, Convert.ToInt32(value)); } return type; } private Type ElementAccessOp(ElementAccessExpressionSyntax elementAccess) { var type = ParseToIL(elementAccess.Expression); var args = ExecuteArguments(elementAccess.ArgumentList); if(type.IsArray) { var elementType = type.GetElementType(); if (elementType.IsClass || elementType.IsInterface) gen.Emit(OpCodes.Ldelem_Ref); else gen.Emit(OpCodes.Ldelem, elementType); return elementType; } else { string propertyName = "Item"; if (type == typeof(string)) propertyName = "Chars"; var property = type.GetProperty(propertyName, args); var method = property.GetGetMethod(); return MethodCall(type, method); } } private Type BinaryOp(BinaryExpressionSyntax binary) { var kind = binary.Kind(); switch(kind) { case SyntaxKind.LogicalOrExpression: case SyntaxKind.LogicalAndExpression: return LogicalOp(binary, kind); case SyntaxKind.IsExpression: case SyntaxKind.AsExpression: return IsAsOp(binary, kind); default: return BinaryOpWithCast(binary, kind); } } private Type BinaryOpWithCast(BinaryExpressionSyntax binary, SyntaxKind kind) { Type leftType = ParseToIL(binary.Left); Type rightType = ParseToIL(binary.Right); if (leftType == typeof(Variant) || rightType == typeof(Variant)) return VariantOp(kind, leftType, rightType); Type returnType = CastToGreaterType(leftType, rightType); if (returnType == typeof(decimal) || returnType == typeof(string)) return BinaryCustomTypeOp(returnType, kind); switch (kind) { case SyntaxKind.AddExpression: // + gen.Emit(OpCodes.Add); break; case SyntaxKind.SubtractExpression: // - gen.Emit(OpCodes.Sub); break; case SyntaxKind.MultiplyExpression: // * gen.Emit(OpCodes.Mul); break; case SyntaxKind.DivideExpression: // / gen.Emit(OpCodes.Div); break; case SyntaxKind.ModuloExpression: // % gen.Emit(OpCodes.Rem); break; case SyntaxKind.EqualsExpression: // == gen.Emit(OpCodes.Ceq); returnType = typeof(bool); break; case SyntaxKind.NotEqualsExpression: // != // == and NOT gen.Emit(OpCodes.Ceq); returnType = CompareWithZero(); break; case SyntaxKind.LessThanExpression: // < gen.Emit(OpCodes.Clt); returnType = typeof(bool); break; case SyntaxKind.LessThanOrEqualExpression: // <= // > and NOT gen.Emit(OpCodes.Cgt); returnType = CompareWithZero(); break; case SyntaxKind.GreaterThanExpression: // > gen.Emit(OpCodes.Cgt); returnType = typeof(bool); break; case SyntaxKind.GreaterThanOrEqualExpression: // >= // < and NOT gen.Emit(OpCodes.Clt); returnType = CompareWithZero(); break; case SyntaxKind.BitwiseOrExpression: // | gen.Emit(OpCodes.Or); break; case SyntaxKind.ExclusiveOrExpression: // ^ gen.Emit(OpCodes.Xor); break; case SyntaxKind.BitwiseAndExpression: // & gen.Emit(OpCodes.And); break; } return returnType; } private Type BinaryCustomTypeOp(Type type, SyntaxKind kind) { string methodName = GetOperatorName(kind); MethodInfo method; if (type == typeof(string) && kind == SyntaxKind.AddExpression) method = type.GetMethod(nameof(string.Concat), new Type[] { type, type }); else method = type.GetMethod(methodName); return MethodCall(type, method); } private Type VariantOp(SyntaxKind kind, Type left, Type right) { Type variantType = typeof(Variant); string methodName = GetOperatorName(kind); MethodInfo method = variantType.GetMethod(methodName); // because 1 argument in Variant must be 'Variant' if (left != variantType) PreviousCastTo(left, variantType); // because 2 argument in Variant operator is object var parameters = method.GetParameters(); if (parameters.Length == 2) { CastTo(right, parameters[1].ParameterType); } return MethodCall(variantType, method); } private static string GetOperatorName(SyntaxKind kind) { string methodName; switch (kind) { case SyntaxKind.AddExpression: // + methodName = "op_Addition"; break; case SyntaxKind.SubtractExpression: // - methodName = "op_Subtraction"; break; case SyntaxKind.MultiplyExpression: // * methodName = "op_Multiply"; break; case SyntaxKind.DivideExpression: // / methodName = "op_Division"; break; case SyntaxKind.ModuloExpression: // % methodName = "op_Modulus"; break; case SyntaxKind.EqualsExpression: // == methodName = "op_Equality"; break; case SyntaxKind.NotEqualsExpression: // != methodName = "op_Inequality"; break; case SyntaxKind.LessThanExpression: // < methodName = "op_LessThan"; break; case SyntaxKind.LessThanOrEqualExpression: // <= methodName = "op_LessThanOrEqual"; break; case SyntaxKind.GreaterThanExpression: // > methodName = "op_GreaterThan"; break; case SyntaxKind.GreaterThanOrEqualExpression: // >= methodName = "op_GreaterThanOrEqual"; break; case SyntaxKind.UnaryMinusExpression: // unary - methodName = "op_UnaryNegation"; break; case SyntaxKind.PreIncrementExpression: // ++a case SyntaxKind.PostIncrementExpression: // a++ methodName = "op_Increment"; break; case SyntaxKind.PreDecrementExpression: // --a case SyntaxKind.PostDecrementExpression: // a-- methodName = "op_Decrement"; break; default: throw new NotSupportedException("This operation isn't supported with this type"); } return methodName; } private Type IsAsOp(BinaryExpressionSyntax binary, SyntaxKind kind) { var type1 = ParseToIL(binary.Left); var type2 = ParseToIL(binary.Right); Type returnType = type1; switch (kind) { case SyntaxKind.IsExpression: // is gen.Emit(OpCodes.Isinst, type2); gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Cgt_Un); returnType = typeof(bool); break; case SyntaxKind.AsExpression: // as // 'as' cannot be applied to non-reference types if (type2.IsValueType) throw new NotSupportedException(); gen.Emit(OpCodes.Isinst, type2); returnType = type2; break; } return returnType; } private Type CastToGreaterType(Type leftType, Type rightType) { if (leftType == rightType) return leftType; Type result; result = CompareAndCastTo(leftType, rightType, typeof(Variant)); if (result != null) return result; result = CompareAndCastTo(leftType, rightType, typeof(string)); // string is the strongest type if (result != null) return result; result = CompareAndCastTo(leftType, rightType, typeof(decimal)); if (result != null) return result; result = CompareAndCastTo(leftType, rightType, typeof(double)); if (result != null) return result; result = CompareAndCastTo(leftType, rightType, typeof(float)); if (result != null) return result; result = CompareAndCastTo(leftType, rightType, typeof(ulong)); if (result != null) return result; result = CompareAndCastTo(leftType, rightType, typeof(long)); if (result != null) return result; throw new NotImplementedException(); } private Type CompareAndCastTo(Type leftType, Type rightType, Type comparableType) { if (rightType == comparableType || leftType == comparableType) { if (leftType == comparableType) return CastTo(rightType, leftType); else return PreviousCastTo(leftType, rightType); } return null; } private Type PreviousCastTo(Type original, Type toType) { var local = gen.DeclareLocal(toType); gen.Emit(OpCodes.Stloc, local); // cast leftType: toType = CastTo(original, toType); gen.Emit(OpCodes.Ldloc, local); return toType; } private Type LogicalOp(BinaryExpressionSyntax binary, SyntaxKind kind) { var type1 = ParseToIL(binary.Left); Label answer = gen.DefineLabel(); OpCode answerOpcode; Label next = gen.DefineLabel(); switch (kind) { case SyntaxKind.LogicalOrExpression: // || gen.Emit(OpCodes.Brtrue_S, answer); answerOpcode = OpCodes.Ldc_I4_1; break; case SyntaxKind.LogicalAndExpression: // && gen.Emit(OpCodes.Brfalse_S, answer); answerOpcode = OpCodes.Ldc_I4_0; break; default: throw new Exception(); } Type type2 = ParseToIL(binary.Right); gen.Emit(OpCodes.Br_S, next); gen.MarkLabel(answer); gen.Emit(answerOpcode); gen.MarkLabel(next); return typeof(bool); } private Type ExplicitCastOp(CastExpressionSyntax cast) { var originType = ParseToIL(cast.Expression); var type = ParseToIL(cast.Type); return CastTo(originType, type, true); } private Type CastTo(Type original, Type toType, bool _explicit = false) { if (original.IsClass) { if(toType.IsValueType) { if (original != typeof(object)) // Unbox only for object throw new NotSupportedException($"You cannot cast type \"{original}\" to type \"{toType}\""); gen.Emit(OpCodes.Unbox_Any, toType); } else if (toType == typeof(object)) return toType; // do nothing else gen.Emit(OpCodes.Castclass, toType); } else if (original.IsValueType) { if (toType == typeof(object)) gen.Emit(OpCodes.Box, original); // decimal has special operators for parsing into different types (exclude object) else if (original == typeof(decimal)) { var methods = original.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (var method in methods) if (method.ReturnType == toType && method.IsSpecialName) return MethodCall(original, method); } else if (toType == typeof(decimal)) { var method = toType.GetMethod("op_Implicit", new Type[] { original }); if (method != null) return MethodCall(toType, method); // float and double require explicit casting if (_explicit) { method = toType.GetMethod("op_Explicit", new Type[] { original }); if (method != null) return MethodCall(toType, method); } throw new NotSupportedException($"You cannot cast type \"{original}\" to type \"{toType}\""); } else if (toType == typeof(double)) gen.Emit(OpCodes.Conv_R8); else if (toType == typeof(float)) gen.Emit(OpCodes.Conv_R4); else if (toType == typeof(ulong)) gen.Emit(OpCodes.Conv_U8); else if (toType == typeof(long)) gen.Emit(OpCodes.Conv_I8); else if (toType.IsPrimitive) { // else int, uint etc // do nothing // TODO: // int + uint => long } else { var method = toType.GetMethod("op_Implicit", new Type[] { original }); if (method != null) return MethodCall(toType, method); if (_explicit) { method = toType.GetMethod("op_Explicit", new Type[] { original }); if (method != null) return MethodCall(toType, method); } if (toType.IsClass || toType.IsInterface) throw new NotSupportedException($"You cannot cast type \"{original}\" to type \"{toType}\""); } } else if (original.IsInterface) { if (toType == typeof(object)) return toType; // do nothing if (toType.IsValueType) { if (original.IsAssignableFrom(toType)) throw new NotSupportedException($"You cannot cast type \"{original}\" to type \"{toType}\""); gen.Emit(OpCodes.Unbox_Any, toType); } else gen.Emit(OpCodes.Castclass, toType); } else gen.Emit(OpCodes.Castclass, toType); return toType; } private Type CompareWithZero() { gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Ceq); return typeof(bool); } } } #endif