/****************************************************************************
|
**
|
** 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.Text;
|
using Microsoft.VisualStudio;
|
using Microsoft.VisualStudio.Debugger.Interop;
|
|
namespace QtVsTools.Qml.Debug.AD7
|
{
|
using V4;
|
|
sealed partial class Property : Concurrent,
|
|
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 StackFrame StackFrame { get; private set; }
|
public CodeContext CodeContext { get; private set; }
|
|
public Property Parent { get; private set; }
|
public SortedDictionary<string, Property> Children { get; private set; }
|
|
public int FrameNumber { get; private set; }
|
public int ScopeNumber { get; private set; }
|
public JsValue JsValue { get; private set; }
|
public string Name { get; private set; }
|
public string FullName { get; private set; }
|
public string Type { get; private set; }
|
public string Value { get; private set; }
|
|
public static Property Create(
|
StackFrame frame,
|
int scopeNumber,
|
JsValue value,
|
Property parent = null)
|
{
|
var _this = new Property();
|
return _this.Initialize(frame, scopeNumber, value, parent) ? _this : null;
|
}
|
|
private Property()
|
{ }
|
|
private bool Initialize(
|
StackFrame frame,
|
int scopeNumber,
|
JsValue value,
|
Property parent)
|
{
|
StackFrame = frame;
|
Engine = frame.Engine;
|
Program = frame.Program;
|
Debugger = frame.Debugger;
|
CodeContext = frame.Context;
|
FrameNumber = frame.FrameNumber;
|
ScopeNumber = scopeNumber;
|
Parent = parent;
|
JsValue = value;
|
|
if (Parent != null && Parent.JsValue is JsObject && ((JsObject)Parent.JsValue).IsArray)
|
Name = string.Format("[{0}]", JsValue.Name);
|
else
|
Name = JsValue.Name;
|
|
var nameParts = new Stack<string>(new[] { Name });
|
for (var p = Parent; p != null && !string.IsNullOrEmpty(p.Name); p = p.Parent) {
|
if (!nameParts.Peek().StartsWith("["))
|
nameParts.Push(".");
|
nameParts.Push(p.Name);
|
}
|
FullName = string.Join("", nameParts);
|
|
Type = JsValue.Type.ToString();
|
Value = JsValue.ToString();
|
|
Children = new SortedDictionary<string, Property>();
|
if (JsValue is JsObject) {
|
var obj = JsValue as JsObject;
|
foreach (JsValue objProp in obj.Properties.Where(x => x.HasData)) {
|
Children[GetChildKey(objProp.Name)]
|
= Create(StackFrame, ScopeNumber, objProp, this);
|
}
|
}
|
|
return true;
|
}
|
|
static string GetChildKey(string childName)
|
{
|
int childIndex;
|
if (int.TryParse(childName, out childIndex))
|
return string.Format("{0:D9}", childIndex);
|
else
|
return childName;
|
}
|
|
int IDebugProperty2.SetValueAsString(string pszValue, uint dwRadix, uint dwTimeout)
|
{
|
string expr = string.Format("{0}=({1})", FullName, pszValue);
|
|
var value = Debugger.Evaluate(FrameNumber, expr);
|
if (value == null || value is JsError)
|
return VSConstants.S_FALSE;
|
|
Program.Refresh();
|
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)
|
{
|
ppEnum = null;
|
if (guidFilter != Guid.Empty && !Filter.LocalsSelected(ref guidFilter))
|
return VSConstants.S_OK;
|
|
if (JsValue is JsObjectRef) {
|
var obj = Debugger.Lookup(FrameNumber, ScopeNumber, JsValue as JsObjectRef);
|
if (obj == null)
|
return VSConstants.S_OK;
|
|
JsValue = obj;
|
foreach (JsValue objProp in obj.Properties.Where(x => x.HasData)) {
|
Children[GetChildKey(objProp.Name)]
|
= Create(StackFrame, ScopeNumber, objProp, this);
|
}
|
}
|
|
if (!Children.Any())
|
return VSConstants.S_OK;
|
|
ppEnum = PropertyEnum.Create(Children.Select(x =>
|
{
|
var info = new DEBUG_PROPERTY_INFO[1];
|
(x.Value as IDebugProperty2).GetPropertyInfo(dwFields, dwRadix, 0,
|
new IDebugReference2[0], 0, info);
|
return info[0];
|
}));
|
return VSConstants.S_OK;
|
}
|
|
|
#region //////////////////// Info /////////////////////////////////////////////////////////
|
|
class PropertyInfo : InfoHelper<PropertyInfo>
|
{
|
public string FullName { get; set; }
|
public string Name { get; set; }
|
public string Type { get; set; }
|
public string Value { get; set; }
|
public enum_DBG_ATTRIB_FLAGS? Attribs { get; set; }
|
public IDebugProperty2 Property { get; set; }
|
}
|
|
PropertyInfo Info
|
{
|
get
|
{
|
return new PropertyInfo
|
{
|
Name = Name,
|
FullName = FullName,
|
Type = Type,
|
Value = Value,
|
Property = this,
|
Attribs = ((Children.Any() || JsValue.Type == JsValue.DataType.Object)
|
? enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_OBJ_IS_EXPANDABLE : 0),
|
};
|
}
|
}
|
|
static readonly PropertyInfo.Mapping MappingToDEBUG_PROPERTY_INFO =
|
|
#region //////////////////// DEBUG_PROPERTY_INFO <-- PropertyInfo /////////////////////////
|
// r: Ref<DEBUG_PROPERTY_INFO>
|
// f: enum_DEBUGPROP_INFO_FLAGS
|
// i: PropertyInfo
|
// v: value of i.<<property>>
|
|
new PropertyInfo.Mapping<DEBUG_PROPERTY_INFO, enum_DEBUGPROP_INFO_FLAGS>
|
((r, bit) => r.s.dwFields |= bit)
|
{
|
{ enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_FULLNAME,
|
(r, v) => r.s.bstrFullName = v, i => i.FullName },
|
|
{ enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_NAME,
|
(r, v) => r.s.bstrName = v, i => i.Name },
|
|
{ enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_TYPE,
|
(r, v) => r.s.bstrType = v, i => i.Type },
|
|
{ enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_VALUE,
|
(r, v) => r.s.bstrValue = v, i => i.Value },
|
|
{ enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_ATTRIB,
|
(r, v) => r.s.dwAttrib |= v, i => i.Attribs },
|
|
{ enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_PROP,
|
(r, v) => r.s.pProperty = v, i => i.Property },
|
};
|
|
#endregion //////////////////// DEBUG_PROPERTY_INFO <-- PropertyInfo //////////////////////
|
|
|
public DEBUG_PROPERTY_INFO GetInfo(enum_DEBUGPROP_INFO_FLAGS dwFields)
|
{
|
DEBUG_PROPERTY_INFO info;
|
Info.Map(MappingToDEBUG_PROPERTY_INFO, dwFields, out info);
|
return info;
|
}
|
|
int IDebugProperty2.GetPropertyInfo(
|
enum_DEBUGPROP_INFO_FLAGS dwFields,
|
uint dwRadix,
|
uint dwTimeout,
|
IDebugReference2[] rgpArgs,
|
uint dwArgCount,
|
DEBUG_PROPERTY_INFO[] pPropertyInfo)
|
{
|
Info.Map(MappingToDEBUG_PROPERTY_INFO, dwFields, out pPropertyInfo[0]);
|
return VSConstants.S_OK;
|
}
|
|
int IDebugProperty2.GetParent(out IDebugProperty2 ppParent)
|
{
|
ppParent = Parent;
|
return (Parent != null) ? VSConstants.S_OK : VSConstants.S_FALSE;
|
}
|
|
#endregion //////////////////// Info //////////////////////////////////////////////////////
|
|
|
#region //////////////////// Filter ///////////////////////////////////////////////////////
|
|
public static class Filter
|
{
|
public static readonly Guid Registers
|
= new Guid("223ae797-bd09-4f28-8241-2763bdc5f713");
|
|
public static readonly Guid Locals
|
= new Guid("b200f725-e725-4c53-b36a-1ec27aef12ef");
|
|
public static readonly Guid AllLocals
|
= new Guid("196db21f-5f22-45a9-b5a3-32cddb30db06");
|
|
public static readonly Guid Args
|
= new Guid("804bccea-0475-4ae7-8a46-1862688ab863");
|
|
public static readonly Guid LocalsPlusArgs
|
= new Guid("e74721bb-10c0-40f5-807f-920d37f95419");
|
|
public static readonly Guid AllLocalsPlusArgs
|
= new Guid("939729a8-4cb0-4647-9831-7ff465240d5f");
|
|
public static bool LocalsSelected(ref Guid guidFilter)
|
{
|
return guidFilter == Locals
|
|| guidFilter == AllLocals
|
|| guidFilter == LocalsPlusArgs
|
|| guidFilter == AllLocalsPlusArgs;
|
}
|
}
|
|
#endregion //////////////////// Filter ////////////////////////////////////////////////////
|
}
|
}
|