2025-08-02 21:37:10 -05:00
|
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
|
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
|
using Microsoft.Iris.Asm;
|
|
|
|
|
using Microsoft.Iris.Asm.Models;
|
2025-07-17 14:07:44 -05:00
|
|
|
using Microsoft.Iris.Markup;
|
2025-11-24 01:22:19 -06:00
|
|
|
using System.Collections.Generic;
|
2025-08-14 02:09:19 -05:00
|
|
|
using System.Linq;
|
2025-07-17 14:07:44 -05:00
|
|
|
|
|
|
|
|
namespace Microsoft.Iris.DecompXml.Mock;
|
|
|
|
|
|
|
|
|
|
internal record IrisObject(object Object, TypeSchema Type)
|
|
|
|
|
{
|
2025-08-14 02:09:19 -05:00
|
|
|
public static IrisObject Create(object objIn, TypeSchema type, DecompileContext context, MarkupTypeSchema parentSchema = null)
|
2025-07-17 14:07:44 -05:00
|
|
|
{
|
|
|
|
|
object obj = objIn;
|
|
|
|
|
|
|
|
|
|
if (objIn is IrisObject irisObj)
|
|
|
|
|
{
|
|
|
|
|
return irisObj.Type is null
|
|
|
|
|
? (irisObj with { Type = type })
|
|
|
|
|
: irisObj;
|
|
|
|
|
}
|
2025-07-20 02:03:16 -05:00
|
|
|
else if (objIn is null)
|
|
|
|
|
{
|
|
|
|
|
type ??= UIXTypes.MapIDToType(UIXTypeID.Null);
|
|
|
|
|
}
|
2025-08-02 21:37:10 -05:00
|
|
|
else if (objIn is ExpressionSyntax expr)
|
|
|
|
|
{
|
|
|
|
|
type ??= GuessExpressionReturnType(expr, context);
|
2025-08-04 21:08:02 -05:00
|
|
|
return new(obj, type);
|
2025-08-02 21:37:10 -05:00
|
|
|
}
|
2025-08-14 02:09:19 -05:00
|
|
|
else if (objIn is SymbolReference symRef && type is null && parentSchema is not null)
|
|
|
|
|
{
|
|
|
|
|
if (symRef.Origin is SymbolOrigin.Properties or SymbolOrigin.Locals)
|
|
|
|
|
type = parentSchema.InheritableSymbolsTable.First(sr => sr.Name == symRef.Symbol).Type;
|
|
|
|
|
}
|
2025-07-17 14:07:44 -05:00
|
|
|
|
|
|
|
|
if (objIn is Disassembler.RawConstantInfo constantInfo)
|
|
|
|
|
{
|
|
|
|
|
obj = constantInfo.Value;
|
|
|
|
|
type ??= constantInfo.Type;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-24 19:21:12 -05:00
|
|
|
type ??= Disassembler.TryGuessTypeSchema(obj.GetType(), context.LoadResult);
|
2025-07-17 14:07:44 -05:00
|
|
|
|
|
|
|
|
return new(obj, type);
|
|
|
|
|
}
|
2025-08-02 21:37:10 -05:00
|
|
|
|
|
|
|
|
private static TypeSchema GuessExpressionReturnType(ExpressionSyntax expr, DecompileContext ctx)
|
|
|
|
|
{
|
2025-08-04 21:08:02 -05:00
|
|
|
if (expr is ParenthesizedExpressionSyntax parenExpr)
|
|
|
|
|
expr = parenExpr.Expression;
|
|
|
|
|
|
2025-11-24 02:10:55 -06:00
|
|
|
if (expr is InvocationExpressionSyntax or MemberAccessExpressionSyntax)
|
2025-08-02 21:37:10 -05:00
|
|
|
{
|
2025-11-24 02:10:55 -06:00
|
|
|
// TODO: Handle invocation of instance methods
|
2025-08-14 02:09:19 -05:00
|
|
|
try
|
|
|
|
|
{
|
2025-11-24 02:10:55 -06:00
|
|
|
ExpressionSyntax sourceExpr = expr;
|
|
|
|
|
|
|
|
|
|
Stack<ArgumentListSyntax> argumentLists = [];
|
|
|
|
|
List<NameSyntax> names = [];
|
|
|
|
|
|
|
|
|
|
while (sourceExpr is not NameSyntax)
|
|
|
|
|
{
|
|
|
|
|
if (sourceExpr is MemberAccessExpressionSyntax innerSourceExpr)
|
|
|
|
|
{
|
|
|
|
|
names.Add(innerSourceExpr.Name);
|
|
|
|
|
sourceExpr = innerSourceExpr.Expression;
|
|
|
|
|
}
|
|
|
|
|
else if (sourceExpr is InvocationExpressionSyntax innerInvocationExpr)
|
|
|
|
|
{
|
|
|
|
|
argumentLists.Push(innerInvocationExpr.ArgumentList);
|
|
|
|
|
sourceExpr = innerInvocationExpr.Expression;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sourceExpr is not NameSyntax sourceName)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
names.Reverse();
|
|
|
|
|
|
|
|
|
|
var sourceTypeName = QualifiedTypeName.Parse(sourceName.ToString());
|
2025-08-14 02:09:19 -05:00
|
|
|
var sourceType = ctx.GetImportedType(sourceTypeName);
|
2025-08-02 21:37:10 -05:00
|
|
|
|
2025-11-24 02:10:55 -06:00
|
|
|
foreach (var name in names)
|
|
|
|
|
{
|
|
|
|
|
var memberName = name.ToString();
|
2025-08-02 21:37:10 -05:00
|
|
|
|
2025-11-24 02:10:55 -06:00
|
|
|
var prop = sourceType.FindPropertyDeep(memberName);
|
|
|
|
|
if (prop is not null)
|
|
|
|
|
{
|
|
|
|
|
sourceType = prop.PropertyType;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var argumentList = argumentLists.Pop();
|
|
|
|
|
|
|
|
|
|
// Try to disambiguate overloads using number of parameters
|
|
|
|
|
var parameterCount = argumentList.Arguments.Count;
|
|
|
|
|
|
|
|
|
|
var method = sourceType.Methods.FirstOrDefault(
|
|
|
|
|
m => m.Name == memberName && m.ParameterTypes.Length == parameterCount);
|
|
|
|
|
|
|
|
|
|
sourceType = method.ReturnType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sourceType;
|
2025-08-14 02:09:19 -05:00
|
|
|
}
|
|
|
|
|
catch { }
|
2025-08-02 21:37:10 -05:00
|
|
|
}
|
2025-08-04 21:41:16 -05:00
|
|
|
else if (expr is CastExpressionSyntax castExpr)
|
|
|
|
|
{
|
|
|
|
|
var castTargetName = QualifiedTypeName.Parse(castExpr.Type.ToString());
|
|
|
|
|
return ctx.GetImportedType(castTargetName);
|
|
|
|
|
}
|
2025-08-14 02:09:19 -05:00
|
|
|
else if (expr is LiteralExpressionSyntax literalExpr)
|
|
|
|
|
{
|
|
|
|
|
return literalExpr.Kind() switch
|
|
|
|
|
{
|
|
|
|
|
// FIXME: This should be more specific. Detect the best numeric type.
|
|
|
|
|
SyntaxKind.NumericLiteralExpression => UIXTypes.MapIDToType(UIXTypeID.Int32),
|
|
|
|
|
SyntaxKind.Utf8StringLiteralExpression or
|
|
|
|
|
SyntaxKind.StringLiteralExpression => UIXTypes.MapIDToType(UIXTypeID.String),
|
|
|
|
|
SyntaxKind.CharacterLiteralExpression => UIXTypes.MapIDToType(UIXTypeID.Char),
|
|
|
|
|
SyntaxKind.TrueLiteralExpression or
|
|
|
|
|
SyntaxKind.FalseLiteralExpression => UIXTypes.MapIDToType(UIXTypeID.Boolean),
|
|
|
|
|
SyntaxKind.NullLiteralExpression => UIXTypes.MapIDToType(UIXTypeID.Null),
|
|
|
|
|
|
|
|
|
|
SyntaxKind.ArgListExpression or
|
|
|
|
|
SyntaxKind.DefaultLiteralExpression or
|
|
|
|
|
_ => throw new System.NotImplementedException(),
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-08-02 21:37:10 -05:00
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-07-17 14:07:44 -05:00
|
|
|
}
|