public override DbgDotNetValueNode[] GetChildren(LanguageValueNodeFactory valueNodeFactory, DbgEvaluationInfo evalInfo, ulong index, int count, DbgValueNodeEvaluationOptions options, ReadOnlyCollection <string>?formatSpecifiers) { var res = count == 0 ? Array.Empty <DbgDotNetValueNode>() : new DbgDotNetValueNode[count]; DbgDotNetValueResult newValue = default; try { var output = ObjectCache.AllocDotNetTextOutput(); var elementType = valueInfo.Value.Type.GetElementType() !; var castType = NeedCast(slotType, valueInfo.Value.Type) ? valueInfo.Value.Type : null; for (int i = 0; i < res.Length; i++) { evalInfo.CancellationToken.ThrowIfCancellationRequested(); string expression; uint arrayIndex = (uint)index + (uint)i; newValue = valueInfo.Value.GetArrayElementAt(arrayIndex); if (dimensionInfos.Length == 1) { int baseIndex = (int)arrayIndex + dimensionInfos[0].BaseIndex; expression = valueNodeFactory.GetExpression(valueInfo.Expression, baseIndex, castType, addParens); owner.FormatArrayName(output, baseIndex); } else { uint indexLeft = arrayIndex; Debug2.Assert(indexes is not null); for (int j = dimensionInfos.Length - 1; j >= 0; j--) { indexes[j] = (int)(indexLeft % dimensionInfos[j].Length) + dimensionInfos[j].BaseIndex; indexLeft = indexLeft / dimensionInfos[j].Length; } expression = valueNodeFactory.GetExpression(valueInfo.Expression, indexes, castType, addParens); owner.FormatArrayName(output, indexes); } var name = output.CreateAndReset(); DbgDotNetValueNode?newNode; if (newValue.HasError) { newNode = valueNodeFactory.CreateError(evalInfo, name, newValue.ErrorMessage !, expression, false); } else { newNode = null; if (CSharpDynamicPropertyHelper.IsCSharpDynamicProperty(newValue.Value !.Type)) { var info = CSharpDynamicPropertyHelper.GetRealValue(evalInfo, newValue.Value); if (info.name is not null) { newValue.Value.Dispose(); name = new DbgDotNetText(new DbgDotNetTextPart(DbgTextColor.DebugViewPropertyName, info.name)); expression = valueNodeFactory.GetFieldExpression(expression, info.valueField.Name, null, false); newNode = valueNodeFactory.Create(evalInfo, name, info.value, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.DynamicViewElement, true, false, info.valueField.FieldType, false); } } if (newNode is null) { newNode = valueNodeFactory.Create(evalInfo, name, newValue.Value, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.ArrayElement, false, false, elementType, false); } } newValue = default; res[i] = newNode; } ObjectCache.Free(ref output); } catch { evalInfo.Context.Process.DbgManager.Close(res.Where(a => a is not null)); newValue.Value?.Dispose(); throw; } return(res); }
protected (DbgDotNetValueNode node, bool canHide) CreateValueNode(DbgEvaluationInfo evalInfo, bool addParens, DmdType slotType, DbgDotNetValue value, int index, DbgValueNodeEvaluationOptions options, string baseExpression, ReadOnlyCollection <string> formatSpecifiers) { var runtime = evalInfo.Runtime.GetDotNetRuntime(); if ((evalOptions & DbgValueNodeEvaluationOptions.RawView) != 0) { options |= DbgValueNodeEvaluationOptions.RawView; } DbgDotNetValueResult valueResult = default; try { ref var info = ref membersCollection.Members[index]; string expression, imageName; bool isReadOnly; DmdType expectedType; var castType = info.NeedCast || NeedCast(slotType, GetMemberDeclaringType(info.Member)) ? info.Member.DeclaringType : null; switch (info.Member.MemberType) { case DmdMemberTypes.Field: var field = (DmdFieldInfo)info.Member; expression = valueNodeFactory.GetFieldExpression(baseExpression, field.Name, castType, addParens); expectedType = field.FieldType; imageName = ImageNameUtils.GetImageName(field); valueResult = runtime.LoadField(evalInfo, value, field); // We should be able to change read only fields (we're a debugger), but since the // compiler will complain, we have to prevent the user from editing the value. isReadOnly = field.IsInitOnly; break; case DmdMemberTypes.Property: var property = (DmdPropertyInfo)info.Member; expression = valueNodeFactory.GetPropertyExpression(baseExpression, property.Name, castType, addParens); expectedType = property.PropertyType; imageName = ImageNameUtils.GetImageName(property); if ((options & DbgValueNodeEvaluationOptions.NoFuncEval) != 0) { isReadOnly = true; valueResult = DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalDisabled); } else { var getter = property.GetGetMethod(DmdGetAccessorOptions.All) ?? throw new InvalidOperationException(); valueResult = runtime.Call(evalInfo, value, getter, Array.Empty <object>(), DbgDotNetInvokeOptions.None); isReadOnly = (object)property.GetSetMethod(DmdGetAccessorOptions.All) == null; } break; default: throw new InvalidOperationException(); } DbgDotNetValueNode newNode; bool canHide = true; var customInfo = TryCreateInstanceValueNode(evalInfo, valueResult); if (customInfo.node != null) { (newNode, canHide) = customInfo; } else if (valueResult.HasError) { newNode = valueNodeFactory.CreateError(evalInfo, info.Name, valueResult.ErrorMessage, expression, false); canHide = false; } else if (valueResult.ValueIsException) { newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value, formatSpecifiers, options, expression, PredefinedDbgValueNodeImageNames.Error, true, false, expectedType, false); } else { newNode = valueNodeFactory.Create(evalInfo, info.Name, valueResult.Value, formatSpecifiers, options, expression, imageName, isReadOnly, false, expectedType, false); } valueResult = default; return(newNode, canHide); }