e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
545 lines
20 KiB
C#
545 lines
20 KiB
C#
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<string, string> knownTypeMap = InitializeKnownTypeMap();
|
|
|
|
private static Dictionary<string, string> InitializeKnownTypeMap()
|
|
{
|
|
Dictionary<string, string> map = new Dictionary<string, string>();
|
|
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<string, string> knownOperatorMap = InitializeKnownOperatorMap();
|
|
|
|
private static Dictionary<string, string> InitializeKnownOperatorMap()
|
|
{
|
|
Dictionary<string, string> map = new Dictionary<string, string>(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<Type, ComputePrecedence> precedenceMap = InitializePrecedenceMap();
|
|
|
|
|
|
private static Dictionary<Type, ComputePrecedence> InitializePrecedenceMap()
|
|
{
|
|
Dictionary<Type, ComputePrecedence> map = new Dictionary<Type, ComputePrecedence>(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
|
|
}
|
|
}
|