/**************************************************************************** ** ** 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> Properties { get; private set; } public string Name { get; private set; } public int FrameNumber { get; private set; } public IEnumerable Scopes { get; private set; } public Task InitThread { get; private set; } static public StackFrame Create( string name, int number, IEnumerable 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 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()); 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 { 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 // f: enum_FRAMEINFO_FLAGS // i: StackFrameInfo // v: value of i.<> new StackFrameInfo.Mapping ((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 ////////////////////////////////////////////////////// } }