continue;
nfields ++;
}
+ if (CHECK_PROTOCOL_VERSION(2, 65)) {
+ if (m_class_is_inlinearray (klass) && nfields == 1)
+ buffer_add_int (buf, m_class_inlinearray_value (klass));
+ else
+ buffer_add_int (buf, -1);
+ }
+
buffer_add_int (buf, nfields);
iter = NULL;
}
buffer_add_value_full (buf, f->type, mono_vtype_get_field_addr (addr, f), domain, FALSE, parent_vtypes, len_fixed_array != 1 ? len_fixed_array : isFixedSizeArray(f));
+ if (CHECK_PROTOCOL_VERSION(2, 65)) {
+ if (m_class_is_inlinearray (klass) && nfields == 1) {
+ int element_size = mono_class_instance_size (mono_class_from_mono_type_internal (f->type)) - MONO_ABI_SIZEOF (MonoObject);
+ int array_size = m_class_inlinearray_value (klass);
+ for (int i = 1; i < array_size; i++)
+ buffer_add_value_full (buf, f->type, ((char*)mono_vtype_get_field_addr (addr, f)) + (i*element_size), domain, FALSE, parent_vtypes, len_fixed_array != 1 ? len_fixed_array : isFixedSizeArray(f));
+ }
+ }
}
if (boxed_vtype) {
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit);
klass = decode_typeid (buf, &buf, limit, &d, &err);
+ if (CHECK_PROTOCOL_VERSION(2, 65))
+ decode_int (buf, &buf, limit); //ignore inline array
if (err != ERR_NONE)
return err;
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit);
klass = decode_typeid (buf, &buf, limit, &d, &err);
+ if (CHECK_PROTOCOL_VERSION(2, 65))
+ decode_int (buf, &buf, limit); //ignore inline array
if (err != ERR_NONE)
goto end;
decode_byte (buf, &buf, limit);
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit); //ignore is boxed
+ if (CHECK_PROTOCOL_VERSION(2, 65))
+ decode_int (buf, &buf, limit); //ignore inline array
decode_typeid (buf, &buf, limit, &d, &err);
ret += decode_vtype_compute_size (NULL, domain, buf, &buf, limit, from_by_ref_value_type);
} else {
if (CHECK_PROTOCOL_VERSION(2, 61))
decode_byte (buf, &buf, limit); //ignore is boxed
klass = decode_typeid (buf, &buf, limit, &d, &err);
+ if (CHECK_PROTOCOL_VERSION(2, 65))
+ decode_int (buf, &buf, limit); //ignore inline array
if (err != ERR_NONE)
return err;
buffer_add_int (buf, header->code_size);
}
}
+ if (CHECK_PROTOCOL_VERSION (2, 65)) {
+ for (int i = 0; i < num_locals; ++i) {
+ buffer_add_int (buf, locals->locals [i].index);
+ }
+ }
}
mono_metadata_free_mh (header);
pos = - pos - 1;
cmd_stack_frame_get_parameter (frame, sig, pos, buf, jit);
} else {
- if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from newer protocol versions it's sent the pdb index
+ if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from older protocol versions it's sent the pdb index
MonoDebugLocalsInfo *locals;
locals = mono_debug_lookup_locals (frame->de.method);
if (locals) {
var = &jit->params [pos];
is_arg = TRUE;
} else {
- if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from newer protocol versions it's sent the pdb index
+ if (!CHECK_PROTOCOL_VERSION (2, 59)) { //from older protocol versions it's sent the pdb index
MonoDebugLocalsInfo *locals;
locals = mono_debug_lookup_locals (frame->de.method);
if (locals) {
*/
#define MAJOR_VERSION 2
-#define MINOR_VERSION 64
+#define MINOR_VERSION 65
typedef enum {
MDBGPROT_CMD_COMPOSITE = 100
var isBoxed = retDebuggerCmdReader.ReadByte() == 1;
var typeId = retDebuggerCmdReader.ReadInt32();
var className = await _sdbAgent.GetTypeName(typeId, token);
+ var inlineArraySize = -1;
+ (int MajorVersion, int MinorVersion) = await _sdbAgent.GetVMVersion(token);
+ if (MajorVersion == 2 && MinorVersion >= 65)
+ inlineArraySize = retDebuggerCmdReader.ReadInt32();
var numValues = retDebuggerCmdReader.ReadInt32();
if (className.StartsWith("System.Nullable<", StringComparison.Ordinal)) //should we call something on debugger-agent to check???
typeId,
isEnum,
includeStatic,
+ inlineArraySize,
token);
_valueTypes[valueType.Id.Value] = valueType;
return await valueType.ToJObject(_sdbAgent, forDebuggerDisplayAttribute, token);
switch (objectId.Scheme)
{
+ case "valuetype": //can be an inlined array
+ {
+ if (!context.SdbAgent.ValueCreator.TryGetValueTypeById(objectId.Value, out ValueTypeClass valueType))
+ throw new InvalidOperationException($"Cannot apply indexing with [] to an expression of scheme '{objectId.Scheme}'");
+ var typeInfo = await context.SdbAgent.GetTypeInfo(valueType.TypeId, token);
+ if (int.TryParse(elementIdxInfo.ElementIdxStr, out elementIdx) && elementIdx >= 0 && elementIdx < valueType.InlineArray.Count)
+ return (JObject)valueType.InlineArray[elementIdx]["value"];
+ throw new InvalidOperationException($"Index is outside the bounds of the inline array");
+ }
case "array":
rootObject["value"] = await context.SdbAgent.GetArrayValues(objectId.Value, token);
if (!elementIdxInfo.IsMultidimensional)
private static int debuggerObjectId;
private static int cmdId = 1; //cmdId == 0 is used by events which come from runtime
- private const int MINOR_VERSION = 62;
+ private const int MINOR_VERSION = 65;
private const int MAJOR_VERSION = 2;
private int VmMinorVersion { get; set; }
private bool fieldsExpanded;
private readonly string className;
private JArray fields;
-
+ public List<JObject> InlineArray { get; init; }
public DotnetObjectId Id { get; init; }
public byte[] Buffer { get; init; }
public int TypeId { get; init; }
public bool IsEnum { get; init; }
- public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId, bool isEnum)
+ public ValueTypeClass(byte[] buffer, string className, JArray fields, int typeId, bool isEnum, List<JObject> inlineArray = null)
{
var valueTypeId = MonoSDBHelper.GetNewObjectId();
var objectId = new DotnetObjectId("valuetype", valueTypeId);
autoExpand = ShouldAutoExpand(className);
Id = objectId;
IsEnum = isEnum;
+ InlineArray = inlineArray;
}
public override string ToString() => $"{{ ValueTypeClass: typeId: {TypeId}, Id: {Id}, Id: {Id}, fields: {fields} }}";
int typeId,
bool isEnum,
bool includeStatic,
+ int inlineArraySize,
CancellationToken token)
{
var typeInfo = await sdbAgent.GetTypeInfo(typeId, token);
IReadOnlyList<FieldTypeClass> fieldTypes = await sdbAgent.GetTypeFields(typeId, token);
JArray fields = new();
+ List<JObject> inlineArray = null;
+ JObject lastWritableFieldValue = null;
if (includeStatic)
{
IEnumerable<FieldTypeClass> staticFields =
IEnumerable<FieldTypeClass> writableFields = fieldTypes
.Where(f => !f.Attributes.HasFlag(FieldAttributes.Literal)
&& !f.Attributes.HasFlag(FieldAttributes.Static));
-
foreach (var field in writableFields)
{
- var fieldValue = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, field.Name, token, true, field.TypeId, false);
- fields.Add(GetFieldWithMetadata(field, fieldValue, isStatic: false));
+ lastWritableFieldValue = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, field.Name, token, isOwn: true, field.TypeId, forDebuggerDisplayAttribute: false);
+ fields.Add(GetFieldWithMetadata(field, lastWritableFieldValue, isStatic: false));
+ }
+ if (inlineArraySize > 0)
+ {
+ inlineArray = new(inlineArraySize+1);
+ inlineArray.Add(lastWritableFieldValue);
+ var firstFieldtypeId = writableFields.First().TypeId;
+ for (int i = 1; i < inlineArraySize; i++)
+ {
+ //the valuetype has a single instance field in inline-arrays
+ var inlineArrayItem = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, $"{i}", token, isOwn: true, firstFieldtypeId, forDebuggerDisplayAttribute: false);
+ inlineArray.Add(inlineArrayItem);
+ }
}
-
long endPos = cmdReader.BaseStream.Position;
cmdReader.BaseStream.Position = initialPos;
byte[] valueTypeBuffer = new byte[endPos - initialPos];
cmdReader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos));
cmdReader.BaseStream.Position = endPos;
- return new ValueTypeClass(valueTypeBuffer, className, fields, typeId, isEnum);
+ return new ValueTypeClass(valueTypeBuffer, className, fields, typeId, isEnum, inlineArray);
JObject GetFieldWithMetadata(FieldTypeClass field, JObject fieldValue, bool isStatic)
{
CheckNumber(int_arr_3, "1, 2, 1", 121);
CheckNumber(int_arr_3, "1, 2, 2", 122);
}
+
+ [ConditionalTheory(nameof(RunningOnChrome))]
+ [InlineData("s")]
+ [InlineData("classWithInlineArrayField.myInlineArray")]
+ [InlineData("classWithInlineArrayField.InlineArrayProp")]
+ [InlineData("classWithInlineArrayField.myStructWithInlineArray.myInlineArray")]
+ public async Task InspectInlineArray(string varName)
+ {
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs";
+
+ var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
+ $"'[debugger-test] DebuggerTests.InlineArray:run'" +
+ "); }, 1);";
+
+ var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, 441, 12, "DebuggerTests.InlineArray.run");
+ var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
+ await EvaluateOnCallFrameAndCheck(id,
+ ($"{varName}[0]", TObject("DebuggerTests.InlineArray.E")),
+ ($"{varName}[a]", TObject("DebuggerTests.InlineArray.E")),
+ ($"{varName}[b]", TObject("DebuggerTests.InlineArray.E")),
+ ($"{varName}[1]", TObject("DebuggerTests.InlineArray.E")));
+ var (_, res) = await EvaluateOnCallFrame(id,$"{varName}[43]", expect_ok: false);
+ Assert.Equal($"Unable to evaluate element access '{varName}[43]': Index is outside the bounds of the inline array", res.Error["result"]?["description"]?.Value<string>());
+
+ var (s_zero, _) = await EvaluateOnCallFrame(id, $"{varName}[0]");
+ await CheckValue(s_zero, TObject("DebuggerTests.InlineArray.E"), nameof(s_zero));
+ var s_zero_props = await GetProperties(s_zero["objectId"]?.Value<string>());
+ await CheckProps(s_zero_props, new
+ {
+ x = TNumber(1),
+ y = TNumber(2),
+ o = TObject("DebuggerTests.InlineArray.One")
+ }, "s_zero_props#1");
+
+ var (s_one, _) = await EvaluateOnCallFrame(id, $"{varName}[1]");
+ await CheckValue(s_one, TObject("DebuggerTests.InlineArray.E"), nameof(s_one));
+ var ss_one_props = await GetProperties(s_one["objectId"]?.Value<string>());
+ await CheckProps(ss_one_props, new
+ {
+ x = TNumber(3),
+ y = TNumber(4),
+ o = TObject("DebuggerTests.InlineArray.Two")
+ }, "s_one_props#1");
+ }
+ [Fact]
+ public async Task InspectInlineArray2()
+ {
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs";
+
+ var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
+ $"'[debugger-test] DebuggerTests.InlineArray:run2'" +
+ "); }, 1);";
+
+ var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, 459, 12, "DebuggerTests.InlineArray.run2");
+ var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
+ await EvaluateOnCallFrameAndCheck(id,
+ ($"a2[0]", TNumber(1)),
+ ($"a3[0]", TNumber(2)),
+ ($"a4[0]", TObject("DebuggerTests.InlineArray.E")),
+ ($"Arr4.myStaticField", TNumber(50))
+ );
+
+ var (_, res) = await EvaluateOnCallFrame(id,$"a3[1]", expect_ok: false);
+ Assert.Equal($"Unable to evaluate element access 'a3[1]': Index is outside the bounds of the inline array", res.Error["result"]?["description"]?.Value<string>());
+
+ var (s_zero, _) = await EvaluateOnCallFrame(id, $"a4[0]");
+ await CheckValue(s_zero, TObject("DebuggerTests.InlineArray.E"), nameof(s_zero));
+ var s_zero_props = await GetProperties(s_zero["objectId"]?.Value<string>());
+ await CheckProps(s_zero_props, new
+ {
+ x = TNumber(1),
+ y = TNumber(2),
+ o = TObject("DebuggerTests.InlineArray.One")
+ }, "s_zero_props#1");
+
+ var (s_one, _) = await EvaluateOnCallFrame(id, $"a4[1]");
+ await CheckValue(s_one, TObject("DebuggerTests.InlineArray.E"), nameof(s_one));
+ var ss_one_props = await GetProperties(s_one["objectId"]?.Value<string>());
+ await CheckProps(ss_one_props, new
+ {
+ x = TNumber(3),
+ y = TNumber(4),
+ o = TObject("DebuggerTests.InlineArray.Two")
+ }, "s_one_props#1");
+
+ var (s_two, _) = await EvaluateOnCallFrame(id, $"a4");
+ await CheckValue(s_two, TObject("DebuggerTests.InlineArray.Arr4"), nameof(s_two));
+ var s_two_props = await GetProperties(s_two["objectId"]?.Value<string>());
+ await CheckProps(s_two_props, new
+ {
+ myStaticField = TNumber(50),
+ e = TObject("DebuggerTests.InlineArray.E"),
+ Length = TNumber(42)
+ }, "s_two_props#1");
+ }
}
}
Console.WriteLine($"int_arr: {int_arr_3.Length}");
}
}
+
+ public class InlineArray
+ {
+ struct StructWithInlineArray
+ {
+ public Arr1 myInlineArray;
+ }
+ class ClassWithInlineArrayField
+ {
+ public Arr1 myInlineArray;
+ public Arr1 InlineArrayProp => myInlineArray;
+ public StructWithInlineArray myStructWithInlineArray;
+ }
+ class One {}
+ class Two {}
+ class Three {}
+ class Four {}
+ struct E
+ {
+ public int x;
+ public int y;
+ public object o;
+ }
+
+ [System.Runtime.CompilerServices.InlineArray(Length)]
+ struct Arr1
+ {
+ public const int Length = 42;
+ public E e;
+ }
+
+ [System.Runtime.CompilerServices.InlineArray(Length)]
+ struct Arr2
+ {
+ public const int Length = 42;
+ public int e;
+ }
+
+ [System.Runtime.CompilerServices.InlineArray(1)]
+ struct Arr3
+ {
+ public int e;
+ }
+
+ [System.Runtime.CompilerServices.InlineArray(Length)]
+ struct Arr4
+ {
+ public static int myStaticField = 50;
+ public const int Length = 42;
+ public E e;
+ }
+
+ private static Arr1 Initialize(Arr1 s)
+ {
+ s[0].o = new One();
+ s[0].x = 1;
+ s[0].y = 2;
+ s[1].o = new Two();
+ s[1].x = 3;
+ s[1].y = 4;
+ s[2].o = new Three();
+ s[2].x = 5;
+ s[2].y = 6;
+ s[3].o = new Four();
+ s[3].x = 7;
+ s[3].y = 8;
+ return s;
+ }
+ public static void run()
+ {
+ int a = 0;
+ int b = 1;
+ Arr1 s = default;
+ s = Initialize(s);
+ ClassWithInlineArrayField classWithInlineArrayField = new ();
+ s = Initialize(classWithInlineArrayField.myInlineArray);
+ classWithInlineArrayField.myInlineArray[0].o = new One();
+ classWithInlineArrayField.myInlineArray[0].x = 1;
+ classWithInlineArrayField.myInlineArray[0].y = 2;
+ classWithInlineArrayField.myInlineArray[1].o = new Two();
+ classWithInlineArrayField.myInlineArray[1].x = 3;
+ classWithInlineArrayField.myInlineArray[1].y = 4;
+ //classWithInlineArrayField.InlineArrayProp[0].o = new One();
+ //classWithInlineArrayField.InlineArrayProp[0].x = 1;
+ //classWithInlineArrayField.InlineArrayProp[0].y = 2;
+ //classWithInlineArrayField.InlineArrayProp[1].o = new Two();
+ //classWithInlineArrayField.InlineArrayProp[1].x = 3;
+ //classWithInlineArrayField.InlineArrayProp[1].y = 4;
+ classWithInlineArrayField.myStructWithInlineArray.myInlineArray[0].o = new One();
+ classWithInlineArrayField.myStructWithInlineArray.myInlineArray[0].x = 1;
+ classWithInlineArrayField.myStructWithInlineArray.myInlineArray[0].y = 2;
+ classWithInlineArrayField.myStructWithInlineArray.myInlineArray[1].o = new Two();
+ classWithInlineArrayField.myStructWithInlineArray.myInlineArray[1].x = 3;
+ classWithInlineArrayField.myStructWithInlineArray.myInlineArray[1].y = 4;
+ System.Diagnostics.Debugger.Break();
+ Console.WriteLine(s[0].o.GetType().Name);
+ }
+
+ public static void run2()
+ {
+ Arr2 a2 = default; //test with primitive type
+ Arr3 a3 = default; //test with length==1
+ Arr4 a4 = default; //test with static field
+ a2[0] = 1;
+ a3[0] = 2;
+ a4[0].o = new One();
+ a4[0].x = 1;
+ a4[0].y = 2;
+ a4[1].o = new Two();
+ a4[1].x = 3;
+ a4[1].y = 4;
+ Console.WriteLine($"olha thays - {Arr4.myStaticField}");
+ System.Diagnostics.Debugger.Break();
+ }
+ }
}