.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

537 lines
23 KiB

// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Reflection;
using ICSharpCode.NRefactory.Ast;
using Attribute = ICSharpCode.NRefactory.Ast.Attribute;
namespace ICSharpCode.NRefactory.Visitors
{
/// <summary>
/// Converts special VB constructs to use more general AST classes.
/// </summary>
public class VBNetConstructsConvertVisitor : ConvertVisitorBase
{
// The following conversions are implemented:
// MyBase.New() and MyClass.New() calls inside the constructor are converted to :base() and :this()
// Add Public Modifier to inner types, methods, properties and fields in structures
// Override Finalize => Destructor
// IIF(cond, true, false) => ConditionalExpression
// Built-in methods => Prefix with class name
// Function A() \n A = SomeValue \n End Function -> convert to return statement
// Array creation => add 1 to upper bound to get array length
// Comparison with empty string literal -> string.IsNullOrEmpty
// Add default value to local variable declarations without initializer
/// <summary>
/// Specifies whether the "Add default value to local variable declarations without initializer"
/// operation is executed by this convert visitor.
/// </summary>
public bool AddDefaultValueInitializerToLocalVariableDeclarations = true;
Dictionary<string, string> usings;
List<UsingDeclaration> addedUsings;
TypeDeclaration currentTypeDeclaration;
public override object VisitCompilationUnit(CompilationUnit compilationUnit, object data)
{
usings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
addedUsings = new List<UsingDeclaration>();
base.VisitCompilationUnit(compilationUnit, data);
int i;
for (i = 0; i < compilationUnit.Children.Count; i++) {
if (!(compilationUnit.Children[i] is UsingDeclaration))
break;
}
foreach (UsingDeclaration decl in addedUsings) {
decl.Parent = compilationUnit;
compilationUnit.Children.Insert(i++, decl);
}
usings = null;
addedUsings = null;
return null;
}
public override object VisitUsing(Using @using, object data)
{
if (usings != null && !@using.IsAlias) {
usings[@using.Name] = @using.Name;
}
return base.VisitUsing(@using, data);
}
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
// fix default visibility of inner classes
if (currentTypeDeclaration != null && (typeDeclaration.Modifier & Modifiers.Visibility) == 0)
typeDeclaration.Modifier |= Modifiers.Public;
TypeDeclaration oldTypeDeclaration = currentTypeDeclaration;
currentTypeDeclaration = typeDeclaration;
base.VisitTypeDeclaration(typeDeclaration, data);
currentTypeDeclaration = oldTypeDeclaration;
return null;
}
public override object VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, object data)
{
// fix default visibility of inner classes
if (currentTypeDeclaration != null && (delegateDeclaration.Modifier & Modifiers.Visibility) == 0)
delegateDeclaration.Modifier |= Modifiers.Public;
return base.VisitDelegateDeclaration(delegateDeclaration, data);
}
bool IsClassType(ClassType c)
{
if (currentTypeDeclaration == null) return false;
return currentTypeDeclaration.Type == c;
}
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
// make constructor public if visiblity is not set (unless constructor is static)
if ((constructorDeclaration.Modifier & (Modifiers.Visibility | Modifiers.Static)) == 0)
constructorDeclaration.Modifier |= Modifiers.Public;
// MyBase.New() and MyClass.New() calls inside the constructor are converted to :base() and :this()
BlockStatement body = constructorDeclaration.Body;
if (body != null && body.Children.Count > 0) {
ExpressionStatement se = body.Children[0] as ExpressionStatement;
if (se != null) {
InvocationExpression ie = se.Expression as InvocationExpression;
if (ie != null) {
MemberReferenceExpression fre = ie.TargetObject as MemberReferenceExpression;
if (fre != null && "New".Equals(fre.MemberName, StringComparison.InvariantCultureIgnoreCase)) {
if (fre.TargetObject is BaseReferenceExpression || fre.TargetObject is ClassReferenceExpression || fre.TargetObject is ThisReferenceExpression) {
body.Children.RemoveAt(0);
ConstructorInitializer ci = new ConstructorInitializer();
ci.Arguments = ie.Arguments;
if (fre.TargetObject is BaseReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.Base;
else
ci.ConstructorInitializerType = ConstructorInitializerType.This;
constructorDeclaration.ConstructorInitializer = ci;
}
}
}
}
}
return base.VisitConstructorDeclaration(constructorDeclaration, data);
}
public override object VisitDeclareDeclaration(DeclareDeclaration declareDeclaration, object data)
{
if (usings != null && !usings.ContainsKey("System.Runtime.InteropServices")) {
UsingDeclaration @using = new UsingDeclaration("System.Runtime.InteropServices");
addedUsings.Add(@using);
base.VisitUsingDeclaration(@using, data);
}
MethodDeclaration method = new MethodDeclaration {
Name = declareDeclaration.Name,
Modifier = declareDeclaration.Modifier,
TypeReference = declareDeclaration.TypeReference,
Parameters = declareDeclaration.Parameters,
Attributes = declareDeclaration.Attributes
};
if ((method.Modifier & Modifiers.Visibility) == 0)
method.Modifier |= Modifiers.Public;
method.Modifier |= Modifiers.Extern | Modifiers.Static;
if (method.TypeReference.IsNull) {
method.TypeReference = new TypeReference("System.Void");
}
Attribute att = new Attribute("DllImport", null, null);
att.PositionalArguments.Add(CreateStringLiteral(declareDeclaration.Library));
if (declareDeclaration.Alias.Length > 0) {
att.NamedArguments.Add(new NamedArgumentExpression("EntryPoint", CreateStringLiteral(declareDeclaration.Alias)));
}
switch (declareDeclaration.Charset) {
case CharsetModifier.Auto:
att.NamedArguments.Add(new NamedArgumentExpression("CharSet",
new MemberReferenceExpression(new IdentifierExpression("CharSet"),
"Auto")));
break;
case CharsetModifier.Unicode:
att.NamedArguments.Add(new NamedArgumentExpression("CharSet",
new MemberReferenceExpression(new IdentifierExpression("CharSet"),
"Unicode")));
break;
default:
att.NamedArguments.Add(new NamedArgumentExpression("CharSet",
new MemberReferenceExpression(new IdentifierExpression("CharSet"),
"Ansi")));
break;
}
att.NamedArguments.Add(new NamedArgumentExpression("SetLastError", new PrimitiveExpression(true, true.ToString())));
att.NamedArguments.Add(new NamedArgumentExpression("ExactSpelling", new PrimitiveExpression(true, true.ToString())));
method.Attributes.Add(new AttributeSection { Attributes = { att } });
ReplaceCurrentNode(method);
return base.VisitMethodDeclaration(method, data);
}
static PrimitiveExpression CreateStringLiteral(string text)
{
return new PrimitiveExpression(text, text);
}
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
if (!IsClassType(ClassType.Interface) && (methodDeclaration.Modifier & Modifiers.Visibility) == 0)
methodDeclaration.Modifier |= Modifiers.Public;
if ("Finalize".Equals(methodDeclaration.Name, StringComparison.InvariantCultureIgnoreCase)
&& methodDeclaration.Parameters.Count == 0
&& methodDeclaration.Modifier == (Modifiers.Protected | Modifiers.Override)
&& methodDeclaration.Body.Children.Count == 1)
{
TryCatchStatement tcs = methodDeclaration.Body.Children[0] as TryCatchStatement;
if (tcs != null
&& tcs.StatementBlock is BlockStatement
&& tcs.CatchClauses.Count == 0
&& tcs.FinallyBlock is BlockStatement
&& tcs.FinallyBlock.Children.Count == 1)
{
ExpressionStatement se = tcs.FinallyBlock.Children[0] as ExpressionStatement;
if (se != null) {
InvocationExpression ie = se.Expression as InvocationExpression;
if (ie != null
&& ie.Arguments.Count == 0
&& ie.TargetObject is MemberReferenceExpression
&& (ie.TargetObject as MemberReferenceExpression).TargetObject is BaseReferenceExpression
&& "Finalize".Equals((ie.TargetObject as MemberReferenceExpression).MemberName, StringComparison.InvariantCultureIgnoreCase))
{
DestructorDeclaration des = new DestructorDeclaration("Destructor", Modifiers.None, methodDeclaration.Attributes);
ReplaceCurrentNode(des);
des.Body = (BlockStatement)tcs.StatementBlock;
return base.VisitDestructorDeclaration(des, data);
}
}
}
}
if ((methodDeclaration.Modifier & (Modifiers.Static | Modifiers.Extern)) == Modifiers.Static
&& methodDeclaration.Body.Children.Count == 0)
{
foreach (AttributeSection sec in methodDeclaration.Attributes) {
foreach (Attribute att in sec.Attributes) {
if ("DllImport".Equals(att.Name, StringComparison.InvariantCultureIgnoreCase)) {
methodDeclaration.Modifier |= Modifiers.Extern;
methodDeclaration.Body = null;
}
}
}
}
if (methodDeclaration.TypeReference.SystemType != "System.Void" && methodDeclaration.Body.Children.Count > 0) {
if (IsAssignmentTo(methodDeclaration.Body.Children[methodDeclaration.Body.Children.Count - 1], methodDeclaration.Name))
{
ReturnStatement rs = new ReturnStatement(GetAssignmentFromStatement(methodDeclaration.Body.Children[methodDeclaration.Body.Children.Count - 1]).Right);
methodDeclaration.Body.Children.RemoveAt(methodDeclaration.Body.Children.Count - 1);
methodDeclaration.Body.AddChild(rs);
} else {
ReturnStatementForFunctionAssignment visitor = new ReturnStatementForFunctionAssignment(methodDeclaration.Name);
methodDeclaration.Body.AcceptVisitor(visitor, null);
if (visitor.replacementCount > 0) {
Expression init;
init = GetDefaultValueForType(methodDeclaration.TypeReference);
methodDeclaration.Body.Children.Insert(0, new LocalVariableDeclaration(new VariableDeclaration(FunctionReturnValueName, init, methodDeclaration.TypeReference)));
methodDeclaration.Body.Children[0].Parent = methodDeclaration.Body;
methodDeclaration.Body.AddChild(new ReturnStatement(new IdentifierExpression(FunctionReturnValueName)));
}
}
}
return base.VisitMethodDeclaration(methodDeclaration, data);
}
public const string FunctionReturnValueName = "functionReturnValue";
static AssignmentExpression GetAssignmentFromStatement(INode statement)
{
ExpressionStatement se = statement as ExpressionStatement;
if (se == null) return null;
return se.Expression as AssignmentExpression;
}
static bool IsAssignmentTo(INode statement, string varName)
{
AssignmentExpression ass = GetAssignmentFromStatement(statement);
if (ass == null) return false;
IdentifierExpression ident = ass.Left as IdentifierExpression;
if (ident == null) return false;
return ident.Identifier.Equals(varName, StringComparison.InvariantCultureIgnoreCase);
}
#region Create return statement for assignment to function name
class ReturnStatementForFunctionAssignment : AbstractAstTransformer
{
string functionName;
internal int replacementCount = 0;
public ReturnStatementForFunctionAssignment(string functionName)
{
this.functionName = functionName;
}
public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data)
{
if (identifierExpression.Identifier.Equals(functionName, StringComparison.InvariantCultureIgnoreCase)) {
if (!(identifierExpression.Parent is AddressOfExpression) && !(identifierExpression.Parent is InvocationExpression)) {
identifierExpression.Identifier = FunctionReturnValueName;
replacementCount++;
}
}
return base.VisitIdentifierExpression(identifierExpression, data);
}
}
#endregion
public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data)
{
fieldDeclaration.Modifier &= ~Modifiers.Dim; // remove "Dim" flag
if (IsClassType(ClassType.Struct)) {
if ((fieldDeclaration.Modifier & Modifiers.Visibility) == 0)
fieldDeclaration.Modifier |= Modifiers.Public;
}
return base.VisitFieldDeclaration(fieldDeclaration, data);
}
public override object VisitEventDeclaration(EventDeclaration eventDeclaration, object data)
{
if (!IsClassType(ClassType.Interface) && (eventDeclaration.Modifier & Modifiers.Visibility) == 0)
eventDeclaration.Modifier |= Modifiers.Public;
return base.VisitEventDeclaration(eventDeclaration, data);
}
public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data)
{
if (!IsClassType(ClassType.Interface) && (propertyDeclaration.Modifier & Modifiers.Visibility) == 0)
propertyDeclaration.Modifier |= Modifiers.Public;
if (propertyDeclaration.HasSetRegion) {
string from = "Value";
if (propertyDeclaration.SetRegion.Parameters.Count > 0) {
ParameterDeclarationExpression p = propertyDeclaration.SetRegion.Parameters[0];
from = p.ParameterName;
p.ParameterName = "Value";
}
propertyDeclaration.SetRegion.AcceptVisitor(new RenameIdentifierVisitor(from, "value", StringComparer.InvariantCultureIgnoreCase), null);
}
return base.VisitPropertyDeclaration(propertyDeclaration, data);
}
static volatile Dictionary<string, Expression> constantTable;
static volatile Dictionary<string, Expression> methodTable;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")]
public static readonly string VBAssemblyName = "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
static Dictionary<string, Expression> CreateDictionary(params string[] classNames)
{
Dictionary<string, Expression> d = new Dictionary<string, Expression>(StringComparer.InvariantCultureIgnoreCase);
Assembly asm = Assembly.Load(VBAssemblyName);
foreach (string className in classNames) {
Type type = asm.GetType("Microsoft.VisualBasic." + className);
Expression expr = new IdentifierExpression(className);
foreach (MemberInfo member in type.GetMembers()) {
if (member.DeclaringType == type) { // only direct members
d[member.Name] = expr;
}
}
}
return d;
}
public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data)
{
if (constantTable == null) {
constantTable = CreateDictionary("Constants");
}
Expression expr;
if (constantTable.TryGetValue(identifierExpression.Identifier, out expr)) {
MemberReferenceExpression fre = new MemberReferenceExpression(expr, identifierExpression.Identifier);
ReplaceCurrentNode(fre);
return base.VisitMemberReferenceExpression(fre, data);
}
return base.VisitIdentifierExpression(identifierExpression, data);
}
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{
IdentifierExpression ident = invocationExpression.TargetObject as IdentifierExpression;
if (ident != null) {
if ("IIF".Equals(ident.Identifier, StringComparison.InvariantCultureIgnoreCase)
&& invocationExpression.Arguments.Count == 3)
{
ConditionalExpression ce = new ConditionalExpression(invocationExpression.Arguments[0],
invocationExpression.Arguments[1],
invocationExpression.Arguments[2]);
ReplaceCurrentNode(new ParenthesizedExpression(ce));
return base.VisitConditionalExpression(ce, data);
}
if ("IsNothing".Equals(ident.Identifier, StringComparison.InvariantCultureIgnoreCase)
&& invocationExpression.Arguments.Count == 1)
{
BinaryOperatorExpression boe = new BinaryOperatorExpression(invocationExpression.Arguments[0],
BinaryOperatorType.ReferenceEquality,
new PrimitiveExpression(null, "null"));
ReplaceCurrentNode(new ParenthesizedExpression(boe));
return base.VisitBinaryOperatorExpression(boe, data);
}
if (methodTable == null) {
methodTable = CreateDictionary("Conversion", "FileSystem", "Financial", "Information",
"Interaction", "Strings", "VBMath");
}
Expression expr;
if (methodTable.TryGetValue(ident.Identifier, out expr)) {
MemberReferenceExpression fre = new MemberReferenceExpression(expr, ident.Identifier);
invocationExpression.TargetObject = fre;
}
}
return base.VisitInvocationExpression(invocationExpression, data);
}
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data)
{
base.VisitUnaryOperatorExpression(unaryOperatorExpression, data);
if (unaryOperatorExpression.Op == UnaryOperatorType.Not) {
if (unaryOperatorExpression.Expression is BinaryOperatorExpression) {
unaryOperatorExpression.Expression = new ParenthesizedExpression(unaryOperatorExpression.Expression);
}
ParenthesizedExpression pe = unaryOperatorExpression.Expression as ParenthesizedExpression;
if (pe != null) {
BinaryOperatorExpression boe = pe.Expression as BinaryOperatorExpression;
if (boe != null && boe.Op == BinaryOperatorType.ReferenceEquality) {
boe.Op = BinaryOperatorType.ReferenceInequality;
ReplaceCurrentNode(pe);
}
}
}
return null;
}
public override object VisitUsingStatement(UsingStatement usingStatement, object data)
{
LocalVariableDeclaration lvd = usingStatement.ResourceAcquisition as LocalVariableDeclaration;
if (lvd != null && lvd.Variables.Count > 1) {
usingStatement.ResourceAcquisition = new LocalVariableDeclaration(lvd.Variables[0]);
for (int i = 1; i < lvd.Variables.Count; i++) {
UsingStatement n = new UsingStatement(new LocalVariableDeclaration(lvd.Variables[i]),
usingStatement.EmbeddedStatement);
usingStatement.EmbeddedStatement = new BlockStatement();
usingStatement.EmbeddedStatement.AddChild(n);
usingStatement = n;
}
}
return base.VisitUsingStatement(usingStatement, data);
}
public override object VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data)
{
for (int i = 0; i < arrayCreateExpression.Arguments.Count; i++) {
arrayCreateExpression.Arguments[i] = Expression.AddInteger(arrayCreateExpression.Arguments[i], 1);
}
if (arrayCreateExpression.ArrayInitializer.CreateExpressions.Count == 0) {
arrayCreateExpression.ArrayInitializer = null;
}
return base.VisitArrayCreateExpression(arrayCreateExpression, data);
}
bool IsEmptyStringLiteral(Expression expression)
{
PrimitiveExpression pe = expression as PrimitiveExpression;
if (pe != null) {
return (pe.Value as string) == "";
} else {
return false;
}
}
Expression CallStringIsNullOrEmpty(Expression stringVariable)
{
List<Expression> arguments = new List<Expression>();
arguments.Add(stringVariable);
return new InvocationExpression(
new MemberReferenceExpression(new TypeReferenceExpression("System.String"), "IsNullOrEmpty"),
arguments);
}
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
{
base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
if (IsEmptyStringLiteral(binaryOperatorExpression.Right)) {
if (binaryOperatorExpression.Op == BinaryOperatorType.Equality) {
ReplaceCurrentNode(CallStringIsNullOrEmpty(binaryOperatorExpression.Left));
} else if (binaryOperatorExpression.Op == BinaryOperatorType.InEquality) {
ReplaceCurrentNode(new UnaryOperatorExpression(CallStringIsNullOrEmpty(binaryOperatorExpression.Left),
UnaryOperatorType.Not));
}
} else if (IsEmptyStringLiteral(binaryOperatorExpression.Left)) {
if (binaryOperatorExpression.Op == BinaryOperatorType.Equality) {
ReplaceCurrentNode(CallStringIsNullOrEmpty(binaryOperatorExpression.Right));
} else if (binaryOperatorExpression.Op == BinaryOperatorType.InEquality) {
ReplaceCurrentNode(new UnaryOperatorExpression(CallStringIsNullOrEmpty(binaryOperatorExpression.Right),
UnaryOperatorType.Not));
}
}
return null;
}
public override object VisitLocalVariableDeclaration(LocalVariableDeclaration localVariableDeclaration, object data)
{
if (AddDefaultValueInitializerToLocalVariableDeclarations) {
for (int i = 0; i < localVariableDeclaration.Variables.Count; i++) {
VariableDeclaration decl = localVariableDeclaration.Variables[i];
if (decl.FixedArrayInitialization.IsNull && decl.Initializer.IsNull) {
TypeReference type = localVariableDeclaration.GetTypeForVariable(i);
decl.Initializer = GetDefaultValueForType(type);
}
}
}
return base.VisitLocalVariableDeclaration(localVariableDeclaration, data);
}
Expression GetDefaultValueForType(TypeReference type)
{
if (type != null && !type.IsArrayType) {
switch (type.SystemType) {
case "System.SByte":
case "System.Byte":
case "System.Int16":
case "System.UInt16":
case "System.Int32":
case "System.UInt32":
case "System.Int64":
case "System.UInt64":
case "System.Single":
case "System.Double":
return new PrimitiveExpression(0, "0");
case "System.Char":
return new PrimitiveExpression('\0', "'\\0'");
case "System.Object":
case "System.String":
return new PrimitiveExpression(null, "null");
case "System.Boolean":
return new PrimitiveExpression(false, "false");
default:
return new DefaultValueExpression(type);
}
} else {
return new PrimitiveExpression(null, "null");
}
}
}
}