/****************************************************************************
|
**
|
** Copyright (C) 2018 The Qt Company Ltd.
|
** Contact: https://www.qt.io/licensing/
|
**
|
** This file is part of the Qt VS Tools.
|
**
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
** Commercial License Usage
|
** Licensees holding valid commercial Qt licenses may use this file in
|
** accordance with the commercial license agreement provided with the
|
** Software or, alternatively, in accordance with the terms contained in
|
** a written agreement between you and The Qt Company. For licensing terms
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
** information use the contact form at https://www.qt.io/contact-us.
|
**
|
** GNU General Public License Usage
|
** Alternatively, this file may be used under the terms of the GNU
|
** General Public License version 3 as published by the Free Software
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
** included in the packaging of this file. Please review the following
|
** information to ensure the GNU General Public License requirements will
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
**
|
** $QT_END_LICENSE$
|
**
|
****************************************************************************/
|
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Threading.Tasks;
|
using Microsoft.VisualStudio;
|
using Microsoft.VisualStudio.Debugger.Interop;
|
|
namespace QtVsTools.Qml.Debug.AD7
|
{
|
sealed partial class StackFrame : Concurrent,
|
|
IDebugStackFrame2, // "This interface represents a single stack frame in a call
|
// stack in a particular thread."
|
|
IDebugExpressionContext2, // "This interface represents a context for expression evaluation"
|
|
IDebugProperty2 // "This interface represents a stack frame property, a program
|
// document property, or some other property. The property is
|
// usually the result of an expression evaluation."
|
{
|
public QmlDebugger Debugger { get; private set; }
|
|
public QmlEngine Engine { get; private set; }
|
public Program Program { get; private set; }
|
|
public CodeContext Context { get; private set; }
|
public Dictionary<int, Dictionary<string, Property>> Properties { get; private set; }
|
|
public string Name { get; private set; }
|
public int FrameNumber { get; private set; }
|
public IEnumerable<int> Scopes { get; private set; }
|
public Task InitThread { get; private set; }
|
|
static public StackFrame Create(
|
string name,
|
int number,
|
IEnumerable<int> scopes,
|
CodeContext context)
|
{
|
var _this = new StackFrame();
|
return _this.Initialize(name, number, scopes, context) ? _this : null;
|
}
|
|
private StackFrame()
|
{ }
|
|
private bool Initialize(
|
string name,
|
int number,
|
IEnumerable<int> scopes,
|
CodeContext context)
|
{
|
Context = context;
|
Engine = context.Engine;
|
Program = context.Program;
|
Debugger = Program.Debugger;
|
Name = string.Format("{0}@{1}:{2}", name, context.FilePath, context.FileLine + 1);
|
FrameNumber = number;
|
Scopes = scopes;
|
InitThread = Task.Run(() => InitializeProperties());
|
return true;
|
}
|
|
private void InitializeProperties(bool forceScope = false)
|
{
|
Properties = Scopes.ToDictionary(x => x, x => new Dictionary<string, Property>());
|
foreach (var scopeNumber in Scopes) {
|
var scopeVars = Debugger.RefreshScope(FrameNumber, scopeNumber, forceScope);
|
foreach (var scopeVar in scopeVars) {
|
Properties[scopeNumber]
|
.Add(scopeVar.Name, Property.Create(this, scopeNumber, scopeVar));
|
}
|
}
|
}
|
|
public void Refresh()
|
{
|
InitializeProperties(true);
|
}
|
|
int IDebugExpressionContext2.ParseText(
|
string pszCode,
|
enum_PARSEFLAGS dwFlags,
|
uint nRadix,
|
out IDebugExpression2 ppExpr,
|
out string pbstrError,
|
out uint pichError)
|
{
|
pbstrError = "";
|
pichError = 0;
|
ppExpr = Expression.Create(this, pszCode);
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.EnumProperties(
|
enum_DEBUGPROP_INFO_FLAGS dwFields,
|
uint nRadix,
|
ref Guid guidFilter,
|
uint dwTimeout,
|
out uint pcelt,
|
out IEnumDebugPropertyInfo2 ppEnum)
|
{
|
pcelt = 0;
|
ppEnum = null;
|
|
if (guidFilter != Guid.Empty && !Property.Filter.LocalsSelected(ref guidFilter))
|
return VSConstants.S_OK;
|
|
InitThread.Wait();
|
pcelt = 0;
|
ppEnum = PropertyEnum.Create(Properties
|
.SelectMany(x => x.Value
|
.Select(y => y.Value.GetInfo(dwFields))));
|
|
return VSConstants.S_OK;
|
}
|
|
int IDebugProperty2.EnumChildren(
|
enum_DEBUGPROP_INFO_FLAGS dwFields,
|
uint dwRadix,
|
ref Guid guidFilter,
|
enum_DBG_ATTRIB_FLAGS dwAttribFilter,
|
string pszNameFilter,
|
uint dwTimeout,
|
out IEnumDebugPropertyInfo2 ppEnum)
|
{
|
uint pcelt;
|
return ((IDebugStackFrame2)this)
|
.EnumProperties(dwFields, dwRadix, guidFilter, dwTimeout, out pcelt, out ppEnum);
|
}
|
|
#region //////////////////// Info /////////////////////////////////////////////////////////
|
|
class StackFrameInfo : InfoHelper<StackFrameInfo>
|
{
|
public string FunctionName { get; set; }
|
public string ReturnType { get; set; }
|
public string Arguments { get; set; }
|
public string Language { get; set; }
|
public string ModuleName { get; set; }
|
public ulong? MinAddress { get; set; }
|
public ulong? MaxAddress { get; set; }
|
public IDebugStackFrame2 Frame { get; set; }
|
public IDebugModule2 Module { get; set; }
|
public int? HasDebugInfo { get; set; }
|
public int? StaleCode { get; set; }
|
}
|
|
StackFrameInfo Info
|
{
|
get
|
{
|
return new StackFrameInfo
|
{
|
FunctionName = Name,
|
ReturnType = "",
|
Arguments = "",
|
Language = Context.FileType.ToString(),
|
ModuleName = "",
|
MinAddress = 0,
|
MaxAddress = 9999,
|
Frame = this,
|
Module = Program,
|
HasDebugInfo = 1,
|
StaleCode = 0,
|
};
|
}
|
}
|
|
static readonly StackFrameInfo.Mapping MappingToFRAMEINFO =
|
|
#region //////////////////// FRAMEINFO <-- StackFrameInfo /////////////////////////////////
|
// r: Ref<FRAMEINFO>
|
// f: enum_FRAMEINFO_FLAGS
|
// i: StackFrameInfo
|
// v: value of i.<<property>>
|
|
new StackFrameInfo.Mapping<FRAMEINFO, enum_FRAMEINFO_FLAGS>
|
((r, bit) => r.s.m_dwValidFields |= bit)
|
{
|
{ enum_FRAMEINFO_FLAGS.FIF_FUNCNAME,
|
(r, v) => r.s.m_bstrFuncName = v, i => i.FunctionName },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_RETURNTYPE,
|
(r, v) => r.s.m_bstrReturnType = v, i => i.ReturnType },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_ARGS,
|
(r, v) => r.s.m_bstrArgs = v, i => i.Arguments },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_LANGUAGE,
|
(r, v) => r.s.m_bstrLanguage = v, i => i.Language },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_MODULE,
|
(r, v) => r.s.m_bstrModule = v, i => i.ModuleName },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_STACKRANGE,
|
(r, v) => r.s.m_addrMin = v, i => i.MinAddress },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_STACKRANGE,
|
(r, v) => r.s.m_addrMax = v, i => i.MaxAddress },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_FRAME,
|
(r, v) => r.s.m_pFrame = v, i => i.Frame },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_DEBUG_MODULEP,
|
(r, v) => r.s.m_pModule = v, i => i.Module },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO,
|
(r, v) => r.s.m_fHasDebugInfo = v, i => i.HasDebugInfo },
|
|
{ enum_FRAMEINFO_FLAGS.FIF_STALECODE,
|
(r, v) => r.s.m_fStaleCode = v, i => i.StaleCode },
|
};
|
|
#endregion //////////////////// FRAMEINFO <-- StackFrameInfo //////////////////////////////
|
|
|
int IDebugStackFrame2.GetInfo(
|
enum_FRAMEINFO_FLAGS dwFieldSpec,
|
uint nRadix,
|
FRAMEINFO[] pFrameInfo)
|
{
|
Info.Map(MappingToFRAMEINFO, dwFieldSpec, out pFrameInfo[0]);
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetCodeContext(out IDebugCodeContext2 ppCodeCxt)
|
{
|
ppCodeCxt = Context;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetDocumentContext(out IDebugDocumentContext2 ppCxt)
|
{
|
ppCxt = Context;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetName(out string pbstrName)
|
{
|
pbstrName = Name;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetPhysicalStackRange(out ulong paddrMin, out ulong paddrMax)
|
{
|
paddrMin = ulong.MinValue;
|
paddrMax = ulong.MaxValue;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetExpressionContext(out IDebugExpressionContext2 ppExprCxt)
|
{
|
ppExprCxt = this;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage)
|
{
|
pbstrLanguage = "C++";
|
pguidLanguage = NativeEngine.IdLanguageCpp;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetThread(out IDebugThread2 ppThread)
|
{
|
ppThread = Program;
|
return VSConstants.S_OK;
|
}
|
|
int IDebugStackFrame2.GetDebugProperty(out IDebugProperty2 ppProperty)
|
{
|
ppProperty = this;
|
return VSConstants.S_OK;
|
}
|
|
#endregion //////////////////// Info //////////////////////////////////////////////////////
|
|
}
|
}
|