diff --git a/src/AddIns/Debugger/Debugger.Core/Eval.cs b/src/AddIns/Debugger/Debugger.Core/Eval.cs index 136cfba8da..0675c415e7 100644 --- a/src/AddIns/Debugger/Debugger.Core/Eval.cs +++ b/src/AddIns/Debugger/Debugger.Core/Eval.cs @@ -187,12 +187,15 @@ namespace Debugger /// <summary> Synchronously calls a function and returns its return value </summary> public static Value InvokeMethod(Thread evalThread, IMethod method, Value thisValue, Value[] args) { - #warning Fast property eval -// var field = Value.GetBackingField(method); -// if (field != null) { -// evalThread.Process.TraceMessage("Using backing field for " + method.FullName); -// return Value.GetMemberValue(evalThread, thisValue, field, args); -// } + Module module = method.DeclaringTypeDefinition.ParentAssembly.GetModule(); + uint fieldToken = module.GetBackingFieldToken(method.ToCorFunction()); + if (fieldToken != 0) { + var field = method.DeclaringType.ImportField(fieldToken); + if (field != null) { + evalThread.Process.TraceMessage("Using backing field for " + method.FullName); + return Value.GetMemberValue(evalThread, thisValue, field, args); + } + } return AsyncInvokeMethod(evalThread, method, thisValue, args).WaitForResult(); } diff --git a/src/AddIns/Debugger/Debugger.Core/Module.cs b/src/AddIns/Debugger/Debugger.Core/Module.cs index 92abd7198c..dddc3efae0 100644 --- a/src/AddIns/Debugger/Debugger.Core/Module.cs +++ b/src/AddIns/Debugger/Debugger.Core/Module.cs @@ -356,5 +356,71 @@ namespace Debugger } return jcf; } + + Dictionary<ICorDebugFunction, uint> backingFieldCache = new Dictionary<ICorDebugFunction, uint>(); + + /// <summary> Is this method in form 'return this.field;'? </summary> + internal uint GetBackingFieldToken(ICorDebugFunction corFunction) + { + uint token; + if (backingFieldCache.TryGetValue(corFunction, out token)) { + return token; + } + + ICorDebugCode corCode; + try { + corCode = corFunction.GetILCode(); + } catch (COMException) { + backingFieldCache[corFunction] = 0; + return 0; + } + + if (corCode == null || corCode.IsIL() == 0 || corCode.GetSize() > 12) { + backingFieldCache[corFunction] = 0; + return 0; + } + + List<byte> code = new List<byte>(corCode.GetCode()); + + bool success = + (Read(code, 0x00) || true) && // nop || nothing + (Read(code, 0x02, 0x7B) || Read(code, 0x7E)) && // ldarg.0; ldfld || ldsfld + ReadToken(code, ref token) && // <field token> + (Read(code, 0x0A, 0x2B, 0x00, 0x06) || true) && // stloc.0; br.s; offset+00; ldloc.0 || nothing + Read(code, 0x2A); // ret + + if (!success) { + backingFieldCache[corFunction] = 0; + return 0; + } + + backingFieldCache[corFunction] = token; + return token; + } + + // Read expected sequence of bytes + static bool Read(List<byte> code, params byte[] expected) + { + if (code.Count < expected.Length) + return false; + for(int i = 0; i < expected.Length; i++) { + if (code[i] != expected[i]) + return false; + } + code.RemoveRange(0, expected.Length); + return true; + } + + // Read field token + static bool ReadToken(List<byte> code, ref uint token) + { + if (code.Count < 4) + return false; + if (code[3] != 0x04) // field token + return false; + token = ((uint)code[0]) + ((uint)code[1] << 8) + ((uint)code[2] << 16) + ((uint)code[3] << 24); + code.RemoveRange(0, 4); + return true; + } } } diff --git a/src/AddIns/Debugger/Debugger.Core/StackFrame.cs b/src/AddIns/Debugger/Debugger.Core/StackFrame.cs index 2afc57eb28..86aa48dad1 100644 --- a/src/AddIns/Debugger/Debugger.Core/StackFrame.cs +++ b/src/AddIns/Debugger/Debugger.Core/StackFrame.cs @@ -391,7 +391,7 @@ namespace Debugger if (this.MethodInfo.IsAccessor) return true; } if (opt.StepOverFieldAccessProperties) { - if (this.MethodInfo.IsAccessor && Value.GetBackingFieldToken(this.MethodInfo) != 0) return true; + if (this.MethodInfo.IsAccessor && this.Module.GetBackingFieldToken(this.CorFunction) != 0) return true; } return false; } diff --git a/src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs b/src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs index e7065d7d41..9a936ba16b 100644 --- a/src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs +++ b/src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs @@ -522,5 +522,12 @@ namespace Debugger } return unresolvedMethod.Resolve(new SimpleTypeResolveContext(module.Assembly)); } + + public static IField ImportField(this IType declaringType, uint fieldToken) + { + var module = declaringType.GetDefinition().ParentAssembly.GetModule(); + var info = GetInfo(module.Assembly); + return declaringType.GetFields(f => info.GetMetadataToken(f) == fieldToken, GetMemberOptions.IgnoreInheritedMembers).SingleOrDefault(); + } } } diff --git a/src/AddIns/Debugger/Debugger.Core/Value.cs b/src/AddIns/Debugger/Debugger.Core/Value.cs index 90dbff8302..b1753b7f3e 100644 --- a/src/AddIns/Debugger/Debugger.Core/Value.cs +++ b/src/AddIns/Debugger/Debugger.Core/Value.cs @@ -602,63 +602,6 @@ namespace Debugger #endregion - /// <summary> Is this method in form 'return this.field;'? </summary> - internal static uint GetBackingFieldToken(IMethod method) - { - ICorDebugFunction corFunction = method.ToCorFunction(); - - ICorDebugCode corCode; - try { - corCode = corFunction.GetILCode(); - } catch (COMException) { - return 0; - } - - if (corCode == null || corCode.IsIL() == 0 || corCode.GetSize() > 12) - return 0; - - List<byte> code = new List<byte>(corCode.GetCode()); - - uint token = 0; - - bool success = - (Read(code, 0x00) || true) && // nop || nothing - (Read(code, 0x02, 0x7B) || Read(code, 0x7E)) && // ldarg.0; ldfld || ldsfld - ReadToken(code, ref token) && // <field token> - (Read(code, 0x0A, 0x2B, 0x00, 0x06) || true) && // stloc.0; br.s; offset+00; ldloc.0 || nothing - Read(code, 0x2A); // ret - - if (!success) - return 0; - - return token; - } - - // Read expected sequence of bytes - static bool Read(List<byte> code, params byte[] expected) - { - if (code.Count < expected.Length) - return false; - for(int i = 0; i < expected.Length; i++) { - if (code[i] != expected[i]) - return false; - } - code.RemoveRange(0, expected.Length); - return true; - } - - // Read field token - static bool ReadToken(List<byte> code, ref uint token) - { - if (code.Count < 4) - return false; - if (code[3] != 0x04) // field token - return false; - token = ((uint)code[0]) + ((uint)code[1] << 8) + ((uint)code[2] << 16) + ((uint)code[3] << 24); - code.RemoveRange(0, 4); - return true; - } - public override string ToString() { return this.AsString();