Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ internal sealed class MethodInfo
public int KickOffMethod { get; }
internal bool IsCompilerGenerated { get; }
private AsyncScopeDebugInformation[] _asyncScopes { get; set; }
private static SignatureTypeProvider _signatureTypeProvider = new();

public MethodInfo(AssemblyInfo assembly, string methodName, int methodToken, TypeInfo type, MethodAttributes attrs)
{
Expand Down Expand Up @@ -436,6 +437,11 @@ public ParameterInfo[] GetParametersInfo()
if (_parametersInfo != null)
return _parametersInfo;

var signature = methodDef.Signature;
var sigReader = Assembly.asmMetadataReader.GetBlobReader(signature);
var decoder = new SignatureDecoder<ElementType, object>(_signatureTypeProvider, Assembly.asmMetadataReader, genericContext: null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

genericContext: null - 👍 for adding the param name!

var methodSignature = decoder.DecodeMethodSignature(ref sigReader);

var paramsHandles = methodDef.GetParameters().ToArray();
var paramsCnt = paramsHandles.Length;
var paramsInfo = new ParameterInfo[paramsCnt];
Expand All @@ -447,7 +453,7 @@ public ParameterInfo[] GetParametersInfo()
var isOptional = parameter.Attributes.HasFlag(ParameterAttributes.Optional) && parameter.Attributes.HasFlag(ParameterAttributes.HasDefault);
if (!isOptional)
{
paramsInfo[i] = new ParameterInfo(paramName);
paramsInfo[i] = new ParameterInfo(paramName, methodSignature.ParameterTypes[i]);
continue;
}
var constantHandle = parameter.GetDefaultValue();
Expand Down Expand Up @@ -653,7 +659,11 @@ internal sealed class ParameterInfo
public ElementType? TypeCode { get; init; }

public object Value { get; init; }

public ParameterInfo(string name, ElementType type)
{
Name = name;
TypeCode = type;
}
public ParameterInfo(string name, ConstantTypeCode? typeCode = null, byte[] value = null)
{
Name = name;
Expand All @@ -670,11 +680,11 @@ public ParameterInfo(string name, ConstantTypeCode? typeCode = null, byte[] valu
TypeCode = ElementType.Char;
break;
case ConstantTypeCode.Byte:
Value = (int)value[0];
Value = (uint)value[0];
TypeCode = ElementType.U1;
break;
case ConstantTypeCode.SByte:
Value = (uint)value[0];
Value = (int)value[0];
TypeCode = ElementType.I1;
break;
case ConstantTypeCode.Int16:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ public void ClearCache()
public bool TryGetValueTypeById(int valueTypeId, out ValueTypeClass vt) => _valueTypes.TryGetValue(valueTypeId, out vt);
public PointerValue GetPointerValue(int pointerId) => _pointerValues.TryGetValue(pointerId, out PointerValue pv) ? pv : null;

private static JObject CreateJObjectForNumber<T>(T value) => Create(value, "number", value.ToString(), writable: true);
private static JObject CreateJObjectForNumber<T>(T value) => Create(value, "number", value.ToString(), writable: true, className: typeof(T).Name);

private static JObject CreateJObjectForChar(int value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,15 +439,15 @@ public async Task<JObject> Resolve(ElementAccessExpressionSyntax elementAccess,
{
MethodInfoWithDebugInformation methodInfo = await context.SdbAgent.GetMethodInfo(methodIds[i], token);
ParameterInfo[] paramInfo = methodInfo.GetParametersInfo();

// get_Item should not have an overload, but if user defined it, take the default one: with one param (key)
if (paramInfo.Length == 1)
{
try
{
if (indexObject != null && !CheckParametersCompatibility(paramInfo[0].TypeCode, indexObject))
continue;
ArraySegment<byte> buffer = indexObject is null ?
await WriteLiteralExpressionAsIndex(objectId, elementIdxInfo.IndexingExpression, elementIdxInfo.ElementIdxStr) :
await WriteJObjectAsIndex(objectId, indexObject, elementIdxInfo.ElementIdxStr);
await WriteLiteralExpressionAsIndex(objectId, elementIdxInfo.IndexingExpression, elementIdxInfo.ElementIdxStr) :
await WriteJObjectAsIndex(objectId, indexObject, elementIdxInfo.ElementIdxStr, paramInfo[0].TypeCode);
JObject getItemRetObj = await context.SdbAgent.InvokeMethod(buffer, methodIds[i], token);
return (JObject)getItemRetObj["value"];
}
Expand Down Expand Up @@ -524,12 +524,12 @@ async Task<ElementIndexInfo> GetElementIndexInfo()
IndexingExpression: indexingExpression);
}

async Task<ArraySegment<byte>> WriteJObjectAsIndex(DotnetObjectId rootObjId, JObject indexObject, string elementIdxStr)
async Task<ArraySegment<byte>> WriteJObjectAsIndex(DotnetObjectId rootObjId, JObject indexObject, string elementIdxStr, ElementType? expectedType)
{
using var writer = new MonoBinaryWriter();
writer.WriteObj(rootObjId, context.SdbAgent);
writer.Write(1); // number of method args
if (!await writer.WriteJsonValue(indexObject, context.SdbAgent, token))
if (!await writer.WriteJsonValue(indexObject, context.SdbAgent, expectedType, token))
throw new InternalErrorException($"Parsing index of type {indexObject["type"].Value<string>()} to write it into the buffer failed.");
return writer.GetParameterBuffer();
}
Expand All @@ -545,6 +545,56 @@ async Task<ArraySegment<byte>> WriteLiteralExpressionAsIndex(DotnetObjectId root
}
}

private static bool CheckParametersCompatibility(ElementType? paramTypeCode, JObject value)
{
Comment thread
radical marked this conversation as resolved.
if (!paramTypeCode.HasValue)
return true;
var argumentType = value["type"]?.Value<string>();
var argumentClassName = value["className"]?.Value<string>();

switch (paramTypeCode.Value)
{
case ElementType.Object:
if (argumentType != "object")
return false;
break;
case ElementType.I2:
case ElementType.I4:
case ElementType.I8:
case ElementType.R4:
case ElementType.R8:
case ElementType.U2:
case ElementType.U4:
case ElementType.U8:
if (argumentType != "number")
return false;
if (argumentType == "object")
return false;
Comment thread
ilonatommy marked this conversation as resolved.
break;
case ElementType.Char:
if (argumentType != "string" && argumentType != "symbol")
return false;
if (argumentType == "object")
return false;
break;
case ElementType.Boolean:
if (argumentType == "boolean")
return true;
if (argumentType == "number" && (argumentClassName == "Single" || argumentClassName == "Double"))
return false;
if (argumentType == "object")
return false;
break;
case ElementType.String:
if (argumentType != "string")
return false;
break;
default:
Comment thread
ilonatommy marked this conversation as resolved.
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing on this list:

  • ElementType.U1,
  • ElementType.Ptr,
  • ElementType.I1,
  • ElementType.ValueType,
  • ElementType.Ptr,
  • ElementType.Void,

}
return true;
}

public async Task<(JObject, string)> ResolveInvocationInfo(InvocationExpressionSyntax method, CancellationToken token)
{
var methodName = "";
Expand Down Expand Up @@ -664,7 +714,7 @@ public async Task<JObject> Resolve(InvocationExpressionSyntax method, Dictionary
}
else if (arg.Expression is IdentifierNameSyntax identifierName)
{
if (!await commandParamsObjWriter.WriteJsonValue(memberAccessValues[identifierName.Identifier.Text], context.SdbAgent, token))
if (!await commandParamsObjWriter.WriteJsonValue(memberAccessValues[identifierName.Identifier.Text], context.SdbAgent, methodParamsInfo[argIndex].TypeCode, token))
throw new InternalErrorException($"Unable to evaluate method '{methodName}'. Unable to write IdentifierNameSyntax into binary writer.");
}
else
Expand Down
37 changes: 32 additions & 5 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ public async Task<bool> WriteConst(ElementType? type, object value, MonoSDBHelpe
{
switch (type)
{
case ElementType.U1:
case ElementType.I1:
case ElementType.I2:
case ElementType.I4:
Write((ElementType)type, (int)value);
Expand All @@ -604,7 +604,7 @@ public async Task<bool> WriteConst(ElementType? type, object value, MonoSDBHelpe
intBoolVal = (bool)value ? 1 : 0;
Write((ElementType)type, intBoolVal);
return true;
case ElementType.I1:
case ElementType.U1:
case ElementType.U2:
case ElementType.U4:
Write((ElementType)type, (uint)value);
Expand Down Expand Up @@ -715,14 +715,41 @@ public async Task<bool> WriteConst(LiteralExpressionSyntax constValue, MonoSDBHe
return false;
}

public async Task<bool> WriteJsonValue(JObject objValue, MonoSDBHelper SdbHelper, CancellationToken token)
public async Task<bool> WriteJsonValue(JObject objValue, MonoSDBHelper SdbHelper, ElementType? expectedType, CancellationToken token)
{
switch (objValue["type"].Value<string>())
{
case "number":
{
// FixMe: what if the number is not int but single/double?
Write(ElementType.I4, objValue["value"].Value<int>());
var expected = expectedType is not null ? expectedType.Value : ElementType.I4;
switch (expected)
{
case ElementType.I1:
case ElementType.I2:
case ElementType.I4:
Write(expected, objValue["value"].Value<int>());
break;
case ElementType.U1:
case ElementType.U2:
case ElementType.U4:
Write(expected, objValue["value"].Value<uint>());
break;
case ElementType.I8:
Write(expected, objValue["value"].Value<long>());
break;
case ElementType.U8:
Write(expected, objValue["value"].Value<ulong>());
break;
case ElementType.R4:
Write(expected, objValue["value"].Value<float>());
break;
case ElementType.R8:
Write(expected, objValue["value"].Value<double>());
break;
default:
objValue["value"].Value<int>();
break;
};
return true;
}
case "symbol":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WebAssembly.Diagnostics;

namespace Microsoft.WebAssembly.Diagnostics;

internal sealed class SignatureTypeProvider : ISignatureTypeProvider<ElementType, object>
{
public ElementType GetPrimitiveType(PrimitiveTypeCode typeCode)
=> typeCode switch
{
PrimitiveTypeCode.Boolean => ElementType.Boolean,
PrimitiveTypeCode.Byte => ElementType.U1,
PrimitiveTypeCode.Char => ElementType.Char,
PrimitiveTypeCode.Double => ElementType.R8,
PrimitiveTypeCode.Int16 => ElementType.I2,
PrimitiveTypeCode.Int32 => ElementType.I4,
PrimitiveTypeCode.Int64 => ElementType.I8,
PrimitiveTypeCode.IntPtr => ElementType.Ptr,
PrimitiveTypeCode.Object => ElementType.Object,
PrimitiveTypeCode.SByte => ElementType.I1,
PrimitiveTypeCode.Single => ElementType.R4,
PrimitiveTypeCode.String => ElementType.String,
PrimitiveTypeCode.TypedReference => ElementType.ValueType,
PrimitiveTypeCode.UInt16 => ElementType.U2,
PrimitiveTypeCode.UInt32 => ElementType.U4,
PrimitiveTypeCode.UInt64 => ElementType.U8,
PrimitiveTypeCode.UIntPtr => ElementType.Ptr,
PrimitiveTypeCode.Void => ElementType.Void,
_ => ElementType.End,
};

ElementType ISignatureTypeProvider<ElementType, object>.GetFunctionPointerType(MethodSignature<ElementType> signature) => ElementType.FnPtr;
ElementType ISignatureTypeProvider<ElementType, object>.GetModifiedType(ElementType modifier, ElementType unmodifiedType, bool isRequired) => ElementType.Object;
ElementType ISignatureTypeProvider<ElementType, object>.GetPinnedType(ElementType elementType) => ElementType.Object;
ElementType IConstructedTypeProvider<ElementType>.GetArrayType(ElementType elementType, ArrayShape shape) => ElementType.Array;
ElementType IConstructedTypeProvider<ElementType>.GetByReferenceType(ElementType elementType) => ElementType.Object;
ElementType IConstructedTypeProvider<ElementType>.GetGenericInstantiation(ElementType genericType, ImmutableArray<ElementType> typeArguments) => ElementType.Object;
ElementType IConstructedTypeProvider<ElementType>.GetPointerType(ElementType elementType) => ElementType.Ptr;
ElementType ISZArrayTypeProvider<ElementType>.GetSZArrayType(ElementType elementType) => ElementType.SzArray;
ElementType ISignatureTypeProvider<ElementType, object>.GetGenericMethodParameter(object genericContext, int index) => ElementType.Object;
ElementType ISignatureTypeProvider<ElementType, object>.GetGenericTypeParameter(object genericContext, int index) => ElementType.Object;
ElementType ISignatureTypeProvider<ElementType, object>.GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) => ElementType.Object;
ElementType ISimpleTypeProvider<ElementType>.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) => ElementType.Object;
ElementType ISimpleTypeProvider<ElementType>.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) => ElementType.Object;
}
Original file line number Diff line number Diff line change
Expand Up @@ -659,12 +659,10 @@ await EvaluateOnCallFrameAndCheck(id,
("f[longString]", TBool(true)),
("f[aBool]", TString("True")),
("f[aChar]", TString("res_9")),
("f[shortString]", TBool(false))
// ("f[aFloat]", TNumber(1)),
// ("f[aDouble]", TNumber(2)),

// FixMe: https://github.com/dotnet/runtime/issues/76014
// ("f[aDecimal]", TNumber(3)) // object
("f[shortString]", TBool(false)),
("f[aFloat]", TNumber(1)),
("f[aDouble]", TNumber(2)),
("f[aDecimal]", TNumber(3)) // object

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests corresponding to the comment in MonoSDBHelper.

);
});

Expand Down