#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
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.
 
 
 
 
 
 

634 lines
22 KiB

// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Debugger.MetaData;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn
{
public static class Extensions
{
public static ResolveResult ToResolveResult(this Value value, StackFrame context)
{
return new ValueResolveResult(value);
}
}
public class ValueResolveResult : ResolveResult
{
Value value;
public ValueResolveResult(Value value) : base(value.Type)
{
this.value = value;
}
public Value Value {
get { return value; }
}
public override bool IsCompileTimeConstant {
get { return value.Type.IsKnownType(KnownTypeCode.String) || value.Type.IsPrimitiveType(); }
}
public override object ConstantValue {
get { return value.PrimitiveValue; }
}
}
public class ExpressionEvaluationVisitor
{
StackFrame context;
ICompilation debuggerTypeSystem;
Thread evalThread;
bool allowMethodInvoke;
bool allowSetValue;
public ExpressionEvaluationVisitor(StackFrame context, Thread evalThread, ICompilation debuggerTypeSystem,
bool allowMethodInvoke = false, bool allowSetValue = false)
{
if (evalThread == null)
throw new ArgumentNullException("evalThread");
if (context == null)
throw new ArgumentNullException("context");
if (debuggerTypeSystem == null)
throw new ArgumentNullException("debuggerTypeSystem");
this.context = context;
this.debuggerTypeSystem = debuggerTypeSystem;
this.evalThread = evalThread;
this.allowMethodInvoke = allowMethodInvoke;
this.allowSetValue = allowSetValue;
}
/// <summary>
/// Imports a type into the debugger's type system, and into the current generic context.
/// </summary>
IType Import(IType type)
{
IType importedType = debuggerTypeSystem.Import(type);
if (importedType != null)
return importedType.AcceptVisitor(context.MethodInfo.Substitution);
else
return null;
}
/// <summary>
/// Imports a type into the debugger's type system, and into the current generic context.
/// </summary>
IMember Import(IMember member)
{
IMember importedMember = debuggerTypeSystem.Import(member);
if (importedMember != null)
return importedMember.Specialize(context.MethodInfo.Substitution);
else
return null;
}
public Value Convert(ResolveResult result)
{
if (result.IsCompileTimeConstant && !result.IsError)
return Eval.CreateValue(evalThread, result.ConstantValue);
return Visit((dynamic)result);
}
Value Visit(ResolveResult result)
{
if (result is ErrorResolveResult) {
var err = (ErrorResolveResult)result;
if (!string.IsNullOrWhiteSpace(err.Message))
throw new GetValueException(err.Message);
}
if (result.IsError)
throw new GetValueException("Unknown error");
if (result.ConstantValue == null && result.Type.Equals(SpecialType.NullType))
return Eval.CreateValue(evalThread, null);
throw new GetValueException("Unsupported language construct: " + result.GetType().Name);
}
Value Visit(ValueResolveResult result)
{
return result.Value;
}
Value Visit(ThisResolveResult result)
{
return context.GetThisValue(true);
}
Value Visit(MemberResolveResult result)
{
var importedMember = Import(result.Member);
if (importedMember == null)
throw new GetValueException("Member not found!");
Value target = null;
if (!importedMember.IsStatic) {
if (importedMember.DeclaringType.Equals(context.MethodInfo.DeclaringType) && result.TargetResult == null)
target = context.GetThisValue(true);
else if (result.TargetResult == null)
throw new GetValueException("An object reference is required for the non-static field, method, or property '" + importedMember.FullName + "'");
else
target = Convert(result.TargetResult);
}
if (!allowMethodInvoke && (importedMember is IMethod))
throw new InvalidOperationException("Method invocation not allowed in the current context!");
Value val = Value.GetMemberValue(evalThread, target, importedMember);
if (val == null)
throw new GetValueException("Member not found!");
return val;
}
Value Visit(OperatorResolveResult result)
{
switch (result.OperatorType) {
case ExpressionType.Assign:
if (!allowSetValue)
throw new GetValueException("Setting values is not allowed in the current context!");
Debug.Assert(result.Operands.Count == 2);
return VisitAssignment((dynamic)result.Operands[0], (dynamic)result.Operands[1]);
case ExpressionType.Add:
return VisitBinaryOperator(result, BinaryOperatorType.Add, false);
case ExpressionType.AddChecked:
return VisitBinaryOperator(result, BinaryOperatorType.Add, true);
case ExpressionType.Subtract:
return VisitBinaryOperator(result, BinaryOperatorType.Subtract, false);
case ExpressionType.SubtractChecked:
return VisitBinaryOperator(result, BinaryOperatorType.Subtract, true);
case ExpressionType.Multiply:
return VisitBinaryOperator(result, BinaryOperatorType.Multiply, false);
case ExpressionType.MultiplyChecked:
return VisitBinaryOperator(result, BinaryOperatorType.Multiply, true);
case ExpressionType.Divide:
return VisitBinaryOperator(result, BinaryOperatorType.Divide, false);
case ExpressionType.Modulo:
return VisitBinaryOperator(result, BinaryOperatorType.Modulus, false);
case ExpressionType.And:
return VisitBinaryOperator(result, BinaryOperatorType.BitwiseAnd);
case ExpressionType.AndAlso:
return VisitConditionalOperator(result, BinaryOperatorType.BitwiseAnd);
case ExpressionType.Or:
return VisitBinaryOperator(result, BinaryOperatorType.BitwiseOr);
case ExpressionType.OrElse:
return VisitConditionalOperator(result, BinaryOperatorType.BitwiseOr);
case ExpressionType.ExclusiveOr:
return VisitBinaryOperator(result, BinaryOperatorType.ExclusiveOr);
case ExpressionType.Not:
return VisitUnaryOperator(result, UnaryOperatorType.Not);
// case ExpressionType.OnesComplement:
case ExpressionType.LeftShift:
return VisitBinaryOperator(result, BinaryOperatorType.ShiftLeft);
case ExpressionType.RightShift:
return VisitBinaryOperator(result, BinaryOperatorType.ShiftRight);
case ExpressionType.Equal:
return VisitBinaryOperator(result, BinaryOperatorType.Equality);
case ExpressionType.NotEqual:
return VisitBinaryOperator(result, BinaryOperatorType.InEquality);
case ExpressionType.GreaterThan:
return VisitBinaryOperator(result, BinaryOperatorType.GreaterThan);
case ExpressionType.GreaterThanOrEqual:
return VisitBinaryOperator(result, BinaryOperatorType.GreaterThanOrEqual);
case ExpressionType.LessThan:
return VisitBinaryOperator(result, BinaryOperatorType.LessThan);
case ExpressionType.LessThanOrEqual:
return VisitBinaryOperator(result, BinaryOperatorType.LessThanOrEqual);
case ExpressionType.Conditional:
return VisitTernaryOperator(result);
default:
throw new GetValueException("Unsupported operator: " + result.OperatorType);
}
}
Value VisitTernaryOperator(OperatorResolveResult result)
{
Debug.Assert(result.Operands.Count == 3);
var condition = Convert(result.Operands[0]);
if (!condition.Type.IsKnownType(KnownTypeCode.Boolean))
throw new GetValueException("Boolean expression expected!");
if ((bool)condition.PrimitiveValue)
return Convert(result.Operands[1]);
return Convert(result.Operands[2]);
}
Value VisitAssignment(ResolveResult lhs, ResolveResult rhs)
{
throw new GetValueException("Assignment not supported!");
}
Value VisitAssignment(LocalResolveResult lhs, ResolveResult rhs)
{
var value = Convert(rhs);
if (lhs.IsParameter)
context.GetArgumentValue(lhs.Variable.Name).SetValue(evalThread, value);
else
context.GetLocalVariableValue(lhs.Variable.Name).SetValue(evalThread, value);
return value;
}
Value VisitUnaryOperator(OperatorResolveResult result, UnaryOperatorType operatorType, bool checkForOverflow = false)
{
Debug.Assert(result.Operands.Count == 1);
var operand = Convert(result.Operands[0]).ToResolveResult(context);
CSharpResolver resolver = new CSharpResolver(debuggerTypeSystem).WithCheckForOverflow(checkForOverflow);
var val = resolver.ResolveUnaryOperator(operatorType, operand);
if (val.IsCompileTimeConstant)
return Convert(val);
throw new GetValueException("Operator {0} is not supported for {1}!", operatorType, new CSharpAmbience().ConvertType(operand.Type));
}
Value VisitBinaryOperator(OperatorResolveResult result, BinaryOperatorType operatorType, bool checkForOverflow = false)
{
Debug.Assert(result.Operands.Count == 2);
Debug.Assert(operatorType != BinaryOperatorType.ConditionalAnd && operatorType != BinaryOperatorType.ConditionalOr && operatorType != BinaryOperatorType.NullCoalescing);
var lhs = Convert(result.Operands[0]).GetPermanentReference(evalThread);
var rhs = Convert(result.Operands[1]).GetPermanentReference(evalThread);
if (result.UserDefinedOperatorMethod != null)
return InvokeMethod(null, result.UserDefinedOperatorMethod, lhs, rhs);
var lhsRR = lhs.ToResolveResult(context);
var rhsRR = rhs.ToResolveResult(context);
CSharpResolver resolver = new CSharpResolver(debuggerTypeSystem).WithCheckForOverflow(checkForOverflow);
var val = resolver.ResolveBinaryOperator(operatorType, lhsRR, rhsRR);
if (val.IsCompileTimeConstant)
return Convert(val);
switch (operatorType) {
case BinaryOperatorType.Equality:
case BinaryOperatorType.InEquality:
bool equality = (operatorType == BinaryOperatorType.Equality);
if (lhsRR.Type.IsKnownType(KnownTypeCode.String) && rhsRR.Type.IsKnownType(KnownTypeCode.String)) {
if (lhs.IsNull || rhs.IsNull) {
bool bothNull = lhs.IsNull && rhs.IsNull;
return Eval.CreateValue(evalThread, equality ? bothNull : !bothNull);
} else {
bool equal = (string)lhs.PrimitiveValue == (string)rhs.PrimitiveValue;
return Eval.CreateValue(evalThread, equality ? equal : !equal);
}
}
if (lhsRR.Type.IsReferenceType != false && rhsRR.Type.IsReferenceType != false) {
// Reference comparison
if (lhs.IsNull || rhs.IsNull) {
bool bothNull = lhs.IsNull && rhs.IsNull;
return Eval.CreateValue(evalThread, equality ? bothNull : !bothNull);
} else {
bool equal = lhs.Address == rhs.Address;
return Eval.CreateValue(evalThread, equality ? equal : !equal);
}
}
goto default;
case BinaryOperatorType.Add:
if (lhsRR.Type.IsKnownType(KnownTypeCode.String) || rhsRR.Type.IsKnownType(KnownTypeCode.String)) {
var method = debuggerTypeSystem.FindType(KnownTypeCode.String)
.GetMethods(m => m.Name == "Concat" && m.Parameters.Count == 2)
.Single(m => m.Parameters.All(p => p.Type.IsKnownType(KnownTypeCode.Object)));
return InvokeMethod(null, method, lhs, rhs);
}
goto default;
default:
throw new GetValueException("Operator {0} is not supported for {1} and {2}!", operatorType, new CSharpAmbience().ConvertType(lhsRR.Type), new CSharpAmbience().ConvertType(rhsRR.Type));
}
}
Value VisitConditionalOperator(OperatorResolveResult result, BinaryOperatorType bitwiseOperatorType)
{
Debug.Assert(result.Operands.Count == 2);
var lhs = Convert(result.Operands[0]).GetPermanentReference(evalThread);
CSharpResolver resolver = new CSharpResolver(debuggerTypeSystem);
Value condVal;
if (bitwiseOperatorType == BinaryOperatorType.BitwiseAnd)
condVal = Convert(resolver.ResolveConditionFalse(lhs.ToResolveResult(context)));
else
condVal = Convert(resolver.ResolveCondition(lhs.ToResolveResult(context)));
if ((bool)condVal.PrimitiveValue)
return lhs;
var rhs = Convert(result.Operands[1]);
var val = resolver.ResolveBinaryOperator(bitwiseOperatorType, lhs.ToResolveResult(context), rhs.ToResolveResult(context));
return Convert(val);
}
/// <remarks>
/// See $7.10.10 of C# 4 Spec for details.
/// </remarks>
Value Visit(TypeIsResolveResult result)
{
var importedType = NullableType.GetUnderlyingType(Import(result.TargetType));
var val = Convert(result.Input);
var conversions = CSharpConversions.Get(debuggerTypeSystem);
bool evalResult = false;
if (!val.IsNull) {
IType inputType = NullableType.GetUnderlyingType(val.Type);
if (inputType.Equals(importedType))
evalResult = true;
else if (conversions.IsImplicitReferenceConversion(inputType, importedType))
evalResult = true;
else if (conversions.IsBoxingConversion(inputType, importedType))
evalResult = true;
}
return Eval.CreateValue(evalThread, evalResult);
}
Value Visit(TypeOfResolveResult result)
{
var type = Import(result.ReferencedType);
if (type == null)
throw new GetValueException("Error: cannot find '{0}'.", result.ReferencedType.FullName);
return Eval.TypeOf(evalThread, type);
}
Value Visit(TypeResolveResult result)
{
throw new GetValueException("Error: Types not supported.");
}
Value Visit(UnknownMemberResolveResult result)
{
throw new GetValueException("Error: Member '{0}' not found.", result.MemberName);
}
Value Visit(UnknownIdentifierResolveResult result)
{
throw new GetValueException("Error: Identifier '{0}' not declared.", result.Identifier);
}
Value Visit(ArrayAccessResolveResult result)
{
var val = Convert(result.Array).GetPermanentReference(evalThread);
return val.GetArrayElement(result.Indexes.Select(rr => (uint)(int)Convert(rr).PrimitiveValue).ToArray());
}
Value Visit(ArrayCreateResolveResult result)
{
throw new NotImplementedException();
}
Value Visit(ConversionResolveResult result)
{
if (result.IsError)
throw new GetValueException("Cannot convert from '{0}' to '{1}'.", new CSharpAmbience().ConvertType(result.Input.Type), new CSharpAmbience().ConvertType(result.Type));
var val = Convert(result.Input);
if (result.Conversion.IsBoxingConversion)
return val;
if (result.Conversion.IsIdentityConversion)
return val;
if (result.Conversion.IsNumericConversion) {
var convVal = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(result.Type), val.PrimitiveValue, false);
return Eval.CreateValue(evalThread, convVal);
}
if (result.Conversion.IsUserDefined)
return InvokeMethod(null, result.Conversion.Method, val);
if (result.Conversion.IsReferenceConversion && result.Conversion.IsImplicit)
return val;
if (result.Conversion.IsNullLiteralConversion)
return val;
throw new GetValueException("conversion '{0}' not implemented!", result.Conversion);
}
Value Visit(LocalResolveResult result)
{
if (result.IsParameter)
return context.GetArgumentValue(result.Variable.Name);
return context.GetLocalVariableValue(result.Variable.Name);
}
Value Visit(AmbiguousMemberResolveResult result)
{
throw new GetValueException("Ambiguous member: " + result.Member.FullName);
}
Value Visit(InvocationResolveResult result)
{
// InvokeMethod() will import the member, so work in the original compilation to find the method to invoke:
IMethod usedMethod;
if (result.Member is IProperty) {
var prop = (IProperty)result.Member;
if (!prop.CanGet)
throw new GetValueException("Indexer does not have a getter.");
usedMethod = prop.Getter;
} else if (result.Member is IMethod) {
if (!allowMethodInvoke)
throw new InvalidOperationException("Method invocation not allowed in the current context!");
usedMethod = (IMethod)result.Member;
} else
throw new GetValueException("Invoked member must be a method or property");
Value target = null;
if (!usedMethod.IsStatic)
target = Convert(result.TargetResult).GetPermanentReference(evalThread);
return InvokeMethod(target, usedMethod, result.Arguments.Select(rr => Convert(rr).GetPermanentReference(evalThread)).ToArray());
}
Value Visit(NamespaceResolveResult result)
{
throw new GetValueException("Namespace not supported!");
}
Value InvokeMethod(Value thisValue, IMethod method, params Value[] arguments)
{
method = Import(method) as IMethod;
if (method == null)
throw new GetValueException("Method not found!");
return Value.InvokeMethod(evalThread, thisValue, method, arguments);
}
public static string FormatValue(Thread evalThread, Value val)
{
if (val.IsNull) {
return "null";
} else if (val.Type.Kind == TypeKind.Array) {
StringBuilder sb = new StringBuilder();
sb.Append(new CSharpAmbience().ConvertType(val.Type));
sb.Append(" {");
bool first = true;
int size = val.ArrayLength;
for (int i = 0; i < size; i++) {
if (!first) sb.Append(", ");
first = false;
sb.Append(FormatValue(evalThread, val.GetElementAtPosition(i)));
}
sb.Append("}");
return sb.ToString();
} else if (val.Type.GetAllBaseTypeDefinitions().Any(def => def.IsKnownType(KnownTypeCode.ICollection))) {
StringBuilder sb = new StringBuilder();
sb.Append(new CSharpAmbience().ConvertType(val.Type));
sb.Append(" {");
val = val.GetPermanentReference(evalThread);
var countProp = val.Type.GetProperties(p => p.Name == "Count" && !p.IsExplicitInterfaceImplementation).Single();
int count = (int)val.GetMemberValue(evalThread, countProp).PrimitiveValue;
for(int i = 0; i < count; i++) {
if (i > 0) sb.Append(", ");
var itemProperty = val.Type.GetProperties(p => p.IsIndexer && p.Name == "Item" && !p.IsExplicitInterfaceImplementation).Single();
Value item = val.GetPropertyValue(evalThread, itemProperty, Eval.CreateValue(evalThread, i));
sb.Append(FormatValue(evalThread, item));
}
sb.Append("}");
return sb.ToString();
} else if (val.Type.IsKnownType(KnownTypeCode.String) || val.Type.IsPrimitiveType()) {
return TextWriterTokenWriter.PrintPrimitiveValue(val.PrimitiveValue);
} else {
return val.InvokeToString(evalThread);
}
}
}
public class ResolveResultPrettyPrinter
{
ResolveResultPrettyPrinter()
{
}
public static string Print(ResolveResult result)
{
try {
return new ResolveResultPrettyPrinter().PrintInternal(result);
} catch (NotImplementedException ex) {
SD.Log.Warn(ex);
return "";
}
}
string PrintInternal(ResolveResult result)
{
if (result == null)
return "";
if (result.IsError)
return "{Error}";
return Visit((dynamic)result);
}
string Visit(ResolveResult result)
{
return "Not supported: " + result.GetType().Name;
}
// string Visit(ValueResolveResult result)
// {
// throw new NotImplementedException();
// }
string Visit(ThisResolveResult result)
{
return "this";
}
string Visit(MemberResolveResult result)
{
string text = PrintInternal(result.TargetResult);
if (!string.IsNullOrWhiteSpace(text))
text += ".";
return text + result.Member.Name;
}
string Visit(OperatorResolveResult result)
{
throw new NotImplementedException();
}
string Visit(TypeIsResolveResult result)
{
throw new NotImplementedException();
}
string Visit(TypeOfResolveResult result)
{
throw new NotImplementedException();
}
string Visit(TypeResolveResult result)
{
return new CSharpAmbience().ConvertType(result.Type);
}
string Visit(UnknownMemberResolveResult result)
{
return result.MemberName;
}
string Visit(UnknownIdentifierResolveResult result)
{
return result.Identifier;
}
string Visit(ArrayAccessResolveResult result)
{
throw new NotImplementedException();
}
string Visit(ArrayCreateResolveResult result)
{
throw new NotImplementedException();
}
string Visit(ConversionResolveResult result)
{
throw new NotImplementedException();
}
string Visit(LocalResolveResult result)
{
if (result.IsParameter)
return result.Variable.Name;
return result.Variable.Name;
}
string Visit(AmbiguousMemberResolveResult result)
{
throw new NotImplementedException();
}
string Visit(InvocationResolveResult result)
{
StringBuilder sb = new StringBuilder();
sb.Append(PrintInternal(result.TargetResult));
sb.Append('.');
sb.Append(result.Member.Name);
sb.Append(result.Member is IProperty ? "[" : "(");
bool first = true;
foreach (var p in result.Member.Parameters) {
if (first) first = false;
else sb.Append(", ");
sb.Append(p.Name);
}
sb.Append(result.Member is IProperty ? "]" : ")");
return sb.ToString();
}
string Visit(NamespaceResolveResult result)
{
return "namespace " + result.NamespaceName;
}
}
}