Xamarin Public Jenkins (auto-signing) 7d7f676260 Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
2018-08-07 15:19:03 +00:00

389 lines
9.5 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
namespace Mono
{
class AssemblyLocationProvider
{
string assemblyFullPath;
Logger logger;
public AssemblyLocationProvider (string assemblyPath, Logger logger)
{
assemblyPath = Path.GetFullPath (assemblyPath);
this.logger = logger;
if (!File.Exists (assemblyPath))
throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
assemblyFullPath = assemblyPath;
}
public bool TryResolveLocation (StackFrameData sfData, SeqPointInfo seqPointInfo, out Location location)
{
var readerParameters = new ReaderParameters { ReadSymbols = true };
using (var assembly = AssemblyDefinition.ReadAssembly (assemblyFullPath, readerParameters)) {
if (!assembly.MainModule.HasSymbols) {
location = default;
return false;
}
TypeDefinition type = null;
string[] nested;
if (sfData.TypeFullName.IndexOf ('/') >= 0)
nested = sfData.TypeFullName.Split ('/');
else
nested = sfData.TypeFullName.Split ('+');
var types = assembly.MainModule.Types;
foreach (var ntype in nested) {
if (type == null) {
// Use namespace first time.
type = types.FirstOrDefault (t => t.FullName == ntype);
} else {
type = types.FirstOrDefault (t => t.Name == ntype);
}
if (type == null) {
logger.LogWarning ("Could not find type: {0}", ntype);
location = default;
return false;
}
types = type.NestedTypes;
}
var parensStart = sfData.MethodSignature.IndexOf ('(');
var methodName = sfData.MethodSignature.Substring (0, parensStart).TrimEnd ();
var methodParameters = sfData.MethodSignature.Substring (parensStart);
var methods = type.Methods.Where (m => CompareName (m, methodName) && CompareParameters (m.Parameters, methodParameters)).ToArray ();
if (methods.Length == 0) {
logger.LogWarning ("Could not find method: {0}", methodName);
location = default;
return false;
}
if (methods.Length > 1) {
logger.LogWarning ("Ambiguous match for method: {0}", sfData.MethodSignature);
location = default;
return false;
}
var method = methods [0];
int ilOffset;
if (sfData.IsILOffset) {
ilOffset = sfData.Offset;
} else {
if (seqPointInfo == null) {
location = default;
return false;
}
ilOffset = seqPointInfo.GetILOffset (method.MetadataToken.ToInt32 (), sfData.MethodIndex, sfData.Offset);
}
if (ilOffset < 0) {
location = default;
return false;
}
if (!method.DebugInformation.HasSequencePoints) {
var async_method = GetAsyncStateMachine (method);
if (async_method?.ConstructorArguments?.Count == 1) {
string state_machine = ((TypeReference)async_method.ConstructorArguments [0].Value).FullName;
return TryResolveLocation (sfData.Relocate (state_machine, "MoveNext ()"), seqPointInfo, out location);
}
location = default;
return false;
}
SequencePoint prev = null;
foreach (var sp in method.DebugInformation.SequencePoints.OrderBy (l => l.Offset)) {
if (sp.Offset >= ilOffset) {
location = new Location (sp.Document.Url, sp.StartLine);
return true;
}
prev = sp;
}
if (prev != null) {
location = new Location (prev.Document.Url, prev.StartLine);
return true;
}
location = default;
return false;
}
}
static CustomAttribute GetAsyncStateMachine (MethodDefinition method)
{
if (!method.HasCustomAttributes)
return null;
return method.CustomAttributes.FirstOrDefault (l =>
l.AttributeType.Name == "AsyncStateMachineAttribute" && l.AttributeType.Namespace == "System.Runtime.CompilerServices");
}
static bool CompareName (MethodDefinition candidate, string expected)
{
if (candidate.Name == expected)
return true;
if (!candidate.HasGenericParameters)
return false;
var genStart = expected.IndexOf ('[');
if (genStart < 0)
genStart = expected.IndexOf ('<');
if (genStart < 0)
return false;
if (candidate.Name != expected.Substring (0, genStart))
return false;
int arity = 1;
for (int pos = genStart; pos < expected.Length; ++pos) {
if (expected [pos] == ',')
++arity;
}
return candidate.GenericParameters.Count == arity;
}
static string RemoveGenerics (string expected, char open, char close)
{
if (expected.IndexOf (open) < 0)
return expected;
var sb = new StringBuilder ();
for (int i = 0; i < expected.Length;) {
int start = expected.IndexOf (open, i);
int end = expected.IndexOf (close, i);
if (start < 0 || end < 0) {
sb.Append (expected, i, expected.Length - i);
break;
}
bool is_ginst = false;
for (int j = start + 1; j < end; ++j) {
if (expected [j] != ',')
is_ginst = true;
}
if (is_ginst) //discard the the generic args
sb.Append (expected, i, start - i);
else //include array arity
sb.Append (expected, i, end + 1 - i);
i = end + 1;
}
return sb.ToString ();
}
static bool CompareParameters (Collection<ParameterDefinition> candidate, string expected)
{
var builder = new StringBuilder ();
builder.Append ("(");
for (int i = 0; i < candidate.Count; i++) {
var parameter = candidate [i];
if (i > 0)
builder.Append (", ");
if (parameter.ParameterType.IsSentinel)
builder.Append ("...,");
var pt = parameter.ParameterType;
FormatElementType (pt, builder);
builder.Append (" ");
builder.Append (parameter.Name);
}
builder.Append (")");
if (builder.ToString () == RemoveGenerics (expected, '[', ']'))
return true;
//now try the compact runtime format.
builder.Clear ();
builder.Append ("(");
for (int i = 0; i < candidate.Count; i++) {
var parameter = candidate [i];
if (i > 0)
builder.Append (",");
if (parameter.ParameterType.IsSentinel)
builder.Append ("...,");
var pt = parameter.ParameterType;
RuntimeFormatElementType (pt, builder);
}
builder.Append (")");
if (builder.ToString () == RemoveGenerics (expected, '<', '>'))
return true;
return false;
}
static void RuntimeFormatElementType (TypeReference tr, StringBuilder builder)
{
var ts = tr as TypeSpecification;
if (ts != null) {
if (ts.IsByReference) {
RuntimeFormatElementType (ts.ElementType, builder);
builder.Append ("&");
return;
}
}
switch (tr.MetadataType) {
case MetadataType.Void:
builder.Append ("void");
break;
case MetadataType.Boolean:
builder.Append ("bool");
break;
case MetadataType.Char:
builder.Append ("char");
break;
case MetadataType.SByte:
builder.Append ("sbyte");
break;
case MetadataType.Byte:
builder.Append ("byte");
break;
case MetadataType.Int16:
builder.Append ("int16");
break;
case MetadataType.UInt16:
builder.Append ("uint16");
break;
case MetadataType.Int32:
builder.Append ("int");
break;
case MetadataType.UInt32:
builder.Append ("uint");
break;
case MetadataType.Int64:
builder.Append ("long");
break;
case MetadataType.UInt64:
builder.Append ("ulong");
break;
case MetadataType.Single:
builder.Append ("single");
break;
case MetadataType.Double:
builder.Append ("double");
break;
case MetadataType.String:
builder.Append ("string");
break;
case MetadataType.Pointer:
builder.Append (((TypeSpecification)tr).ElementType);
builder.Append ("*");
break;
case MetadataType.ValueType:
case MetadataType.Class:
case MetadataType.GenericInstance: {
FormatName (tr, builder, '/');
break;
}
case MetadataType.Var:
case MetadataType.MVar:
builder.Append (tr.Name);
builder.Append ("_REF");
break;
case MetadataType.Array: {
var array = (ArrayType)tr;
RuntimeFormatElementType (array.ElementType, builder);
builder.Append ("[");
for (int i = 0; i < array.Rank - 1; ++i)
builder.Append (",");
builder.Append ("]");
break;
}
case MetadataType.TypedByReference:
builder.Append ("typedbyref");
break;
case MetadataType.IntPtr:
builder.Append ("intptr");
break;
case MetadataType.UIntPtr:
builder.Append ("uintptr");
break;
case MetadataType.FunctionPointer:
builder.Append ("*()");
break;
case MetadataType.Object:
builder.Append ("object");
break;
default:
builder.Append ("-unknown-");
break;
}
}
static void FormatName (TypeReference tr, StringBuilder builder, char sep)
{
if (tr.IsNested && !(tr.MetadataType == MetadataType.Var || tr.MetadataType == MetadataType.MVar)) {
FormatName (tr.DeclaringType, builder, sep);
builder.Append (sep);
}
if (!string.IsNullOrEmpty (tr.Namespace)) {
builder.Append (tr.Namespace);
builder.Append (".");
}
builder.Append (tr.Name);
}
static void FormatElementType (TypeReference tr, StringBuilder builder)
{
var ts = tr as TypeSpecification;
if (ts != null) {
if (ts.IsByReference) {
FormatElementType (ts.ElementType, builder);
builder.Append ("&");
return;
}
var array = ts as ArrayType;
if (array != null) {
FormatElementType (ts.ElementType, builder);
builder.Append ("[");
for (int ii = 0; ii < array.Rank - 1; ++ii) {
builder.Append (",");
}
builder.Append ("]");
return;
}
}
FormatName (tr, builder, '+');
}
}
}