using System; using System.CodeDom; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Text; using System.Workflow.ComponentModel; namespace System.Workflow.Activities.Rules { internal static class RuleDecompiler { #region Decompile literals internal static void DecompileObjectLiteral(StringBuilder decompilation, object primitiveValue) { if (primitiveValue == null) { decompilation.Append("null"); } else { Type primitiveType = primitiveValue.GetType(); if (primitiveType == typeof(string)) DecompileStringLiteral(decompilation, (string)primitiveValue); else if (primitiveType == typeof(char)) DecompileCharacterLiteral(decompilation, (char)primitiveValue); else if (primitiveType == typeof(long)) DecompileSuffixedIntegerLiteral(decompilation, primitiveValue, "L"); else if (primitiveType == typeof(uint)) DecompileSuffixedIntegerLiteral(decompilation, primitiveValue, "U"); else if (primitiveType == typeof(ulong)) DecompileSuffixedIntegerLiteral(decompilation, primitiveValue, "UL"); else if (primitiveType == typeof(float)) DecompileFloatingPointLiteral(decompilation, primitiveValue, 'f'); else if (primitiveType == typeof(double)) DecompileFloatingPointLiteral(decompilation, primitiveValue, 'd'); else if (primitiveType == typeof(decimal)) DecompileFloatingPointLiteral(decompilation, primitiveValue, 'm'); else decompilation.Append(primitiveValue.ToString()); } } private static void DecompileFloatingPointLiteral(StringBuilder decompilation, object value, char suffix) { // Make sure decimal point isn't converted to a comma in European locales. string svalue = Convert.ToString(value, CultureInfo.InvariantCulture); decompilation.Append(svalue); if (suffix == 'd') { // Don't append 'd' suffixes, they're ugly. Only if the string-ified value contains // no decimal and no exponent do we need to append a ".0" to make it a double (as // opposed to an integer). bool hasDecimal = svalue.IndexOf('.') >= 0; bool hasExponent = svalue.IndexOfAny(new char[] { 'e', 'E' }) >= 0; if (!hasDecimal && !hasExponent) decompilation.Append(".0"); } else { decompilation.Append(suffix); } } private static void DecompileSuffixedIntegerLiteral(StringBuilder decompilation, object value, string suffix) { decompilation.Append(value.ToString()); decompilation.Append(suffix); } private static void DecompileStringLiteral(StringBuilder decompilation, string strValue) { decompilation.Append("\""); for (int i = 0; i < strValue.Length; ++i) { char c = strValue[i]; // is this character a surrogate pair? if ((char.IsHighSurrogate(c)) && (i + 1 < strValue.Length) && (char.IsLowSurrogate(strValue[i + 1]))) { // yes, so leave the two characters unchanged decompilation.Append(c); ++i; decompilation.Append(strValue[i]); } else AppendCharacter(decompilation, c, '"'); } decompilation.Append("\""); } private static void DecompileCharacterLiteral(StringBuilder decompilation, char charValue) { decompilation.Append("'"); AppendCharacter(decompilation, charValue, '\''); decompilation.Append("'"); } private static void AppendCharacter(StringBuilder decompilation, char charValue, char quoteCharacter) { if (charValue == quoteCharacter) { decompilation.Append("\\"); decompilation.Append(quoteCharacter); } else if (charValue == '\\') { decompilation.Append("\\\\"); } else if ((charValue >= ' ' && charValue < '\u007f') || char.IsLetterOrDigit(charValue) || char.IsPunctuation(charValue)) { decompilation.Append(charValue); } else { string escapeSequence = null; switch (charValue) { case '\0': escapeSequence = "\\0"; break; case '\n': escapeSequence = "\\n"; break; case '\r': escapeSequence = "\\r"; break; case '\b': escapeSequence = "\\b"; break; case '\a': escapeSequence = "\\a"; break; case '\t': escapeSequence = "\\t"; break; case '\f': escapeSequence = "\\f"; break; case '\v': escapeSequence = "\\v"; break; } if (escapeSequence != null) { decompilation.Append(escapeSequence); } else { decompilation.Append("\\u"); UInt16 cv = (UInt16)charValue; for (int i = 12; i >= 0; i -= 4) { int mask = 0xF << i; byte c = (byte)((cv & mask) >> i); decompilation.Append("0123456789ABCDEF"[c]); } } } } #endregion #region Type decompilation internal static string DecompileType(Type type) { if (type == null) return string.Empty; StringBuilder sb = new StringBuilder(); DecompileType_Helper(sb, type); return sb.ToString(); } private static void DecompileType_Helper(StringBuilder decompilation, Type type) { int i; if (type.HasElementType) { DecompileType_Helper(decompilation, type.GetElementType()); if (type.IsArray) { decompilation.Append("["); decompilation.Append(',', type.GetArrayRank() - 1); decompilation.Append("]"); } else if (type.IsByRef) { decompilation.Append('&'); } else if (type.IsPointer) { decompilation.Append('*'); } } else { string typeName = type.FullName; if (typeName == null) // Full name may be null for an unbound generic. typeName = type.Name; typeName = UnmangleTypeName(typeName); decompilation.Append(typeName); if (type.IsGenericType) { decompilation.Append("<"); Type[] typeArgs = type.GetGenericArguments(); DecompileType_Helper(decompilation, typeArgs[0]); // decompile the first type arg for (i = 1; i < typeArgs.Length; ++i) { decompilation.Append(", "); DecompileType_Helper(decompilation, typeArgs[i]); } decompilation.Append(">"); } } } internal static void DecompileType(StringBuilder decompilation, CodeTypeReference typeRef) { // Remove any back-tick decorations on generic types, if present. string baseType = UnmangleTypeName(typeRef.BaseType); decompilation.Append(baseType); if (typeRef.TypeArguments != null && typeRef.TypeArguments.Count > 0) { decompilation.Append("<"); bool first = true; foreach (CodeTypeReference argTypeRef in typeRef.TypeArguments) { if (!first) decompilation.Append(", "); first = false; DecompileType(decompilation, argTypeRef); } decompilation.Append(">"); } if (typeRef.ArrayRank > 0) { do { decompilation.Append("["); for (int i = 1; i < typeRef.ArrayRank; ++i) decompilation.Append(","); decompilation.Append("]"); typeRef = typeRef.ArrayElementType; } while (typeRef.ArrayRank > 0); } } private static Dictionary knownTypeMap = InitializeKnownTypeMap(); private static Dictionary InitializeKnownTypeMap() { Dictionary map = new Dictionary(); map.Add("System.Char", "char"); map.Add("System.Byte", "byte"); map.Add("System.SByte", "sbyte"); map.Add("System.Int16", "short"); map.Add("System.UInt16", "ushort"); map.Add("System.Int32", "int"); map.Add("System.UInt32", "uint"); map.Add("System.Int64", "long"); map.Add("System.UInt64", "ulong"); map.Add("System.Single", "float"); map.Add("System.Double", "double"); map.Add("System.Decimal", "decimal"); map.Add("System.Boolean", "bool"); map.Add("System.String", "string"); map.Add("System.Object", "object"); map.Add("System.Void", "void"); return map; } private static string TryReplaceKnownTypes(string typeName) { string newTypeName = null; if (!knownTypeMap.TryGetValue(typeName, out newTypeName)) newTypeName = typeName; return newTypeName; } private static string UnmangleTypeName(string typeName) { int tickIndex = typeName.IndexOf('`'); if (tickIndex > 0) typeName = typeName.Substring(0, tickIndex); // Replace the '+' for a nested type with a '.' typeName = typeName.Replace('+', '.'); typeName = TryReplaceKnownTypes(typeName); return typeName; } #endregion #region Method decompilation internal static string DecompileMethod(MethodInfo method) { if (method == null) return string.Empty; StringBuilder sb = new StringBuilder(); string operatorName; DecompileType_Helper(sb, method.DeclaringType); sb.Append('.'); if (knownOperatorMap.TryGetValue(method.Name, out operatorName)) sb.Append(operatorName); else sb.Append(method.Name); sb.Append('('); ParameterInfo[] parms = method.GetParameters(); for (int i = 0; i < parms.Length; ++i) { DecompileType_Helper(sb, parms[i].ParameterType); if (i != parms.Length - 1) sb.Append(", "); } sb.Append(')'); return sb.ToString(); } private static Dictionary knownOperatorMap = InitializeKnownOperatorMap(); private static Dictionary InitializeKnownOperatorMap() { Dictionary map = new Dictionary(27); // unary operators map.Add("op_UnaryPlus", "operator +"); map.Add("op_UnaryNegation", "operator -"); map.Add("op_OnesComplement", "operator ~"); map.Add("op_LogicalNot", "operator !"); map.Add("op_Increment", "operator ++"); map.Add("op_Decrement", "operator --"); map.Add("op_True", "operator true"); map.Add("op_False", "operator false"); map.Add("op_Implicit", "implicit operator"); map.Add("op_Explicit", "explicit operator"); // binary operators map.Add("op_Equality", "operator =="); map.Add("op_Inequality", "operator !="); map.Add("op_GreaterThan", "operator >"); map.Add("op_GreaterThanOrEqual", "operator >="); map.Add("op_LessThan", "operator <"); map.Add("op_LessThanOrEqual", "operator <="); map.Add("op_Addition", "operator +"); map.Add("op_Subtraction", "operator -"); map.Add("op_Multiply", "operator *"); map.Add("op_Division", "operator /"); map.Add("op_IntegerDivision", "operator \\"); map.Add("op_Modulus", "operator %"); map.Add("op_LeftShift", "operator <<"); map.Add("op_RightShift", "operator >>"); map.Add("op_BitwiseAnd", "operator &"); map.Add("op_BitwiseOr", "operator |"); map.Add("op_ExclusiveOr", "operator ^"); return map; } #endregion #region Operator Precedence // These operations are sorted in order of precedence, lowest-to-highest private enum Operation { RootExpression, LogicalOr, // || LogicalAnd, // && BitwiseOr, // | BitwiseAnd, // & Equality, // == != Comparitive, // < <= > >= Additive, // + - Multiplicative, // * / % Unary, // - ! (cast) Postfix, // field/property ref and method call NoParentheses // Highest } private delegate Operation ComputePrecedence(CodeExpression expresssion); private static Dictionary precedenceMap = InitializePrecedenceMap(); private static Dictionary InitializePrecedenceMap() { Dictionary map = new Dictionary(7); map.Add(typeof(CodeBinaryOperatorExpression), GetBinaryPrecedence); map.Add(typeof(CodeCastExpression), GetCastPrecedence); map.Add(typeof(CodeFieldReferenceExpression), GetPostfixPrecedence); map.Add(typeof(CodePropertyReferenceExpression), GetPostfixPrecedence); map.Add(typeof(CodeMethodInvokeExpression), GetPostfixPrecedence); map.Add(typeof(CodeObjectCreateExpression), GetPostfixPrecedence); map.Add(typeof(CodeArrayCreateExpression), GetPostfixPrecedence); return map; } private static Operation GetPostfixPrecedence(CodeExpression expression) { return Operation.Postfix; } private static Operation GetCastPrecedence(CodeExpression expression) { return Operation.Unary; } private static Operation GetBinaryPrecedence(CodeExpression expression) { CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression; Operation operation = Operation.NoParentheses; switch (binaryExpr.Operator) { case CodeBinaryOperatorType.Multiply: case CodeBinaryOperatorType.Divide: case CodeBinaryOperatorType.Modulus: operation = Operation.Multiplicative; break; case CodeBinaryOperatorType.Subtract: case CodeBinaryOperatorType.Add: operation = Operation.Additive; break; case CodeBinaryOperatorType.LessThan: case CodeBinaryOperatorType.LessThanOrEqual: case CodeBinaryOperatorType.GreaterThan: case CodeBinaryOperatorType.GreaterThanOrEqual: operation = Operation.Comparitive; break; case CodeBinaryOperatorType.IdentityEquality: case CodeBinaryOperatorType.ValueEquality: case CodeBinaryOperatorType.IdentityInequality: operation = Operation.Equality; break; case CodeBinaryOperatorType.BitwiseAnd: operation = Operation.BitwiseAnd; break; case CodeBinaryOperatorType.BitwiseOr: operation = Operation.BitwiseOr; break; case CodeBinaryOperatorType.BooleanAnd: operation = Operation.LogicalAnd; break; case CodeBinaryOperatorType.BooleanOr: operation = Operation.LogicalOr; break; default: string message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString()); NotSupportedException exception = new NotSupportedException(message); exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr; throw exception; } return operation; } private static Operation GetPrecedence(CodeExpression expression) { // Assume the operation needs no parentheses. Operation operation = Operation.NoParentheses; ComputePrecedence computePrecedence; if (precedenceMap.TryGetValue(expression.GetType(), out computePrecedence)) operation = computePrecedence(expression); return operation; } internal static bool MustParenthesize(CodeExpression childExpr, CodeExpression parentExpr) { // No parent... we're at the root, so no need to parenthesize the root. if (parentExpr == null) return false; Operation childOperation = GetPrecedence(childExpr); Operation parentOperation = GetPrecedence(parentExpr); if (parentOperation == childOperation) { CodeBinaryOperatorExpression parentBinary = parentExpr as CodeBinaryOperatorExpression; if (parentBinary != null) { if (childExpr == parentBinary.Right) { // Something like 2 - (3 - 4) needs parentheses. return true; } else { // Something like (2 - 3) - 4 doesn't need parentheses. return false; } } else { return false; } } else if (parentOperation > childOperation) { return true; } else { return false; } } #endregion } }