[fix] fixed the bug in the MethodBridge generator where it incorrectly handle [StructLayout] and blittable types when generating code for struct classes.

main
walon 2025-02-28 22:05:52 +08:00
parent 9e4946aa45
commit 355e24fbe8
1 changed files with 199 additions and 98 deletions

View File

@ -16,6 +16,7 @@ using UnityEngine;
using TypeInfo = HybridCLR.Editor.ABI.TypeInfo;
using CallingConvention = System.Runtime.InteropServices.CallingConvention;
using TypeAttributes = dnlib.DotNet.TypeAttributes;
using System.Runtime.InteropServices;
namespace HybridCLR.Editor.MethodBridge
{
@ -306,43 +307,105 @@ namespace HybridCLR.Editor.MethodBridge
private class AnalyzeTypeInfo
{
public TypeInfo toSharedType;
public TypeInfo isoType;
public List<AnalyzeFieldInfo> fields;
public string signature;
public ClassLayout classLayout;
public TypeAttributes layout;
public uint originalPackingSize;
public uint packingSize;
public uint classSize;
public LayoutKind layout;
public bool blittable;
}
private readonly Dictionary<TypeInfo, AnalyzeTypeInfo> _analyzeTypeInfos = new Dictionary<TypeInfo, AnalyzeTypeInfo>();
private readonly Dictionary<string, TypeInfo> _signature2Type = new Dictionary<string, TypeInfo>();
private bool IsBlittable(TypeSig typeSig)
{
typeSig = typeSig.RemovePinnedAndModifiers();
if (typeSig.IsByRef)
{
return true;
}
switch (typeSig.ElementType)
{
case ElementType.Void: return false;
case ElementType.Boolean:
case ElementType.I1:
case ElementType.U1:
case ElementType.I2:
case ElementType.Char:
case ElementType.U2:
case ElementType.I4:
case ElementType.U4:
case ElementType.I8:
case ElementType.U8:
case ElementType.R4:
case ElementType.R8:
case ElementType.I:
case ElementType.U:
case ElementType.Ptr:
case ElementType.ByRef:
case ElementType.FnPtr:
case ElementType.TypedByRef: return true;
case ElementType.String:
case ElementType.Class:
case ElementType.Array:
case ElementType.SZArray:
case ElementType.Object:
case ElementType.Module:
case ElementType.Var:
case ElementType.MVar: return false;
case ElementType.ValueType:
{
TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDef();
if (typeDef == null)
{
throw new Exception($"type:{typeSig} definition could not be found. Please try `HybridCLR/Genergate/LinkXml`, then Build once to generate the AOT dll, and then regenerate the bridge function");
}
if (typeDef.IsEnum)
{
return true;
}
return CalculateAnalyzeTypeInfoBasic(GetSharedTypeInfo(typeSig)).blittable;
}
case ElementType.GenericInst:
{
GenericInstSig gis = (GenericInstSig)typeSig;
if (!gis.GenericType.IsValueType)
{
return false;
}
TypeDef typeDef = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef();
if (typeDef.IsEnum)
{
return true;
}
return CalculateAnalyzeTypeInfoBasic(GetSharedTypeInfo(typeSig)).blittable;
}
default: throw new NotSupportedException($"{typeSig.ElementType}");
}
}
private AnalyzeTypeInfo CalculateAnalyzeTypeInfoBasic(TypeInfo typeInfo)
{
Debug.Assert(typeInfo.IsStruct);
if (_analyzeTypeInfos.TryGetValue(typeInfo, out var ati))
{
return ati;
}
TypeSig type = typeInfo.Klass;
TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow();
List<TypeSig> klassInst = type.ToGenericInstSig()?.GenericArguments?.ToList();
GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
ClassLayout sa = typeDef.ClassLayout;
var analyzeTypeInfo = new AnalyzeTypeInfo()
{
classLayout = sa,
layout = typeDef.Layout,
};
// don't share type with explicit layout
if (sa != null)
{
analyzeTypeInfo.toSharedType = typeInfo;
analyzeTypeInfo.signature = typeInfo.CreateSigName();
_signature2Type.Add(analyzeTypeInfo.signature, typeInfo);
return analyzeTypeInfo;
}
var fields = analyzeTypeInfo.fields = new List<AnalyzeFieldInfo>();
var fields = new List<AnalyzeFieldInfo>();
bool blittable = true;
foreach (FieldDef field in typeDef.Fields)
{
if (field.IsStatic)
@ -350,8 +413,39 @@ namespace HybridCLR.Editor.MethodBridge
continue;
}
TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
fields.Add(new AnalyzeFieldInfo { field = field, type = GetSharedTypeInfo(fieldType) });
blittable &= IsBlittable(fieldType);
TypeInfo sharedFieldTypeInfo = GetSharedTypeInfo(fieldType);
TypeInfo isoType = ToIsomorphicType(sharedFieldTypeInfo);
fields.Add(new AnalyzeFieldInfo { field = field, type = isoType });
}
//analyzeTypeInfo.blittable = blittable;
//analyzeTypeInfo.packingSize = blittable ? analyzeTypeInfo.originalPackingSize : 0;
ClassLayout sa = typeDef.ClassLayout;
uint originalPackingSize = sa?.PackingSize ?? 0;
var analyzeTypeInfo = new AnalyzeTypeInfo()
{
originalPackingSize = originalPackingSize,
packingSize = blittable && !typeDef.IsAutoLayout ? originalPackingSize : 0,
classSize = sa?.ClassSize ?? 0,
layout = typeDef.IsAutoLayout ? LayoutKind.Auto : (typeDef.IsExplicitLayout ? LayoutKind.Explicit : LayoutKind.Sequential),
fields = fields,
blittable = blittable,
};
_analyzeTypeInfos.Add(typeInfo, analyzeTypeInfo);
analyzeTypeInfo.signature = GetOrCalculateTypeInfoSignature(typeInfo);
if (_signature2Type.TryGetValue(analyzeTypeInfo.signature, out var sharedType))
{
// Debug.Log($"[ToIsomorphicType] type:{type.Klass} ==> sharedType:{sharedType.Klass} signature:{signature} ");
analyzeTypeInfo.isoType = sharedType;
}
else
{
analyzeTypeInfo.isoType = typeInfo;
_signature2Type.Add(analyzeTypeInfo.signature, typeInfo);
}
return analyzeTypeInfo;
}
@ -376,15 +470,10 @@ namespace HybridCLR.Editor.MethodBridge
}
var sigBuf = new StringBuilder();
if (ati.classLayout != null)
if (ati.packingSize != 0 || ati.classSize != 0 || ati.layout != LayoutKind.Sequential || !ati.blittable)
{
sigBuf.Append($"[{ati.classLayout.ClassSize}|{ati.classLayout.PackingSize}|{ati.classLayout}]");
sigBuf.Append($"[{ati.classSize}|{ati.packingSize}|{ati.layout}|{(ati.blittable ? 0 : 1)}]");
}
if (ati.layout != 0)
{
sigBuf.Append($"[{(int)ati.layout}]");
}
foreach (var field in ati.fields)
{
string fieldOffset = field.field.FieldOffset != null ? field.field.FieldOffset.ToString() + "|" : "";
@ -399,27 +488,7 @@ namespace HybridCLR.Editor.MethodBridge
{
return type;
}
if (!_analyzeTypeInfos.TryGetValue(type, out var ati))
{
ati = CalculateAnalyzeTypeInfoBasic(type);
_analyzeTypeInfos.Add(type, ati);
}
if (ati.toSharedType == null)
{
string signature = GetOrCalculateTypeInfoSignature(type);
Debug.Assert(signature == ati.signature);
if (_signature2Type.TryGetValue(signature, out var sharedType))
{
// Debug.Log($"[ToIsomorphicType] type:{type.Klass} ==> sharedType:{sharedType.Klass} signature:{signature} ");
ati.toSharedType = sharedType;
}
else
{
ati.toSharedType = type;
_signature2Type.Add(signature, type);
}
}
return ati.toSharedType;
return CalculateAnalyzeTypeInfoBasic(type).isoType;
}
private MethodDesc ToIsomorphicMethod(MethodDesc method)
@ -527,8 +596,8 @@ namespace HybridCLR.Editor.MethodBridge
var sharedMethod = new MethodDesc
{
MethodDef = method.Method,
ReturnInfo = new ReturnInfo { Type = _typeCreator.CreateTypeInfo(method.Method.ReturnType) },
ParamInfos = method.Method.Parameters.Select(p => new ParamInfo { Type = _typeCreator.CreateTypeInfo(p.Type) }).ToList(),
ReturnInfo = new ReturnInfo { Type = GetSharedTypeInfo(method.Method.ReturnType) },
ParamInfos = method.Method.Parameters.Select(p => new ParamInfo { Type = GetSharedTypeInfo(p.Type) }).ToList(),
};
sharedMethod.Init();
sharedMethod = ToIsomorphicMethod(sharedMethod);
@ -563,8 +632,8 @@ namespace HybridCLR.Editor.MethodBridge
var sharedMethod = new MethodDesc
{
MethodDef = null,
ReturnInfo = new ReturnInfo { Type = _typeCreator.CreateTypeInfo(method.MethodSig.RetType) },
ParamInfos = method.MethodSig.Params.Select(p => new ParamInfo { Type = _typeCreator.CreateTypeInfo(p) }).ToList(),
ReturnInfo = new ReturnInfo { Type = GetSharedTypeInfo(method.MethodSig.RetType) },
ParamInfos = method.MethodSig.Params.Select(p => new ParamInfo { Type = GetSharedTypeInfo(p) }).ToList(),
};
sharedMethod.Init();
sharedMethod = ToIsomorphicMethod(sharedMethod);
@ -617,7 +686,7 @@ namespace HybridCLR.Editor.MethodBridge
};
var classInfos = new List<ClassInfo>();
var classTypeSet = new HashSet<TypeInfo>();
var classTypeSet = new Dictionary<TypeInfo, ClassInfo>();
foreach (var type in structTypes)
{
GenerateClassInfo(type, classTypeSet, classInfos);
@ -770,47 +839,68 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu
class ClassInfo
{
public TypeInfo type;
public TypeDef typeDef;
public List<FieldInfo> fields = new List<FieldInfo>();
public ClassLayout layout;
public List<AnalyzeFieldInfo> fields;
public uint packingSize;
public uint classSize;
public LayoutKind layout;
public bool blittable;
}
private void GenerateClassInfo(TypeInfo type, HashSet<TypeInfo> typeSet, List<ClassInfo> classInfos)
private void GenerateClassInfo(TypeInfo type, Dictionary<TypeInfo, ClassInfo> typeSet, List<ClassInfo> classInfos)
{
if (!typeSet.Add(type))
if (typeSet.ContainsKey(type))
{
return;
}
TypeSig typeSig = type.Klass;
var fields = new List<FieldInfo>();
TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow();
AnalyzeTypeInfo ati = CalculateAnalyzeTypeInfoBasic(type);
//TypeSig typeSig = type.Klass;
//var fields = new List<FieldInfo>();
List<TypeSig> klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList();
GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
//TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow();
ClassLayout sa = typeDef.ClassLayout;
//List<TypeSig> klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList();
//GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
ICorLibTypes corLibTypes = typeDef.Module.CorLibTypes;
foreach (FieldDef field in typeDef.Fields)
//ClassLayout sa = typeDef.ClassLayout;
//ICorLibTypes corLibTypes = typeDef.Module.CorLibTypes;
//bool blittable = true;
//foreach (FieldDef field in typeDef.Fields)
//{
// if (field.IsStatic)
// {
// continue;
// }
// TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
// fieldType = MetaUtil.ToShareTypeSig(corLibTypes, fieldType);
// var fieldTypeInfo = ToIsomorphicType(GetSharedTypeInfo(fieldType));
// if (fieldTypeInfo.IsStruct)
// {
// GenerateClassInfo(fieldTypeInfo, typeSet, classInfos);
// }
// blittable &= IsBlittable(fieldType, fieldTypeInfo, typeSet);
// fields.Add(new FieldInfo { field = field, type = fieldTypeInfo });
//}
foreach (var field in ati.fields)
{
if (field.IsStatic)
if (field.type.IsStruct)
{
continue;
GenerateClassInfo(field.type, typeSet, classInfos);
}
TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
fieldType = MetaUtil.ToShareTypeSig(corLibTypes, fieldType);
var fieldTypeInfo = ToIsomorphicType(_typeCreator.CreateTypeInfo(fieldType));
if (fieldTypeInfo.IsStruct)
}
var classInfo = new ClassInfo()
{
GenerateClassInfo(fieldTypeInfo, typeSet, classInfos);
}
fields.Add(new FieldInfo { field = field, type = fieldTypeInfo });
}
classInfos.Add(new ClassInfo() { type = type, typeDef = typeDef, fields = fields, layout = sa });
type = type,
fields = ati.fields,
packingSize = ati.packingSize,
classSize = ati.classSize,
layout = ati.layout,
blittable = ati.blittable,
};
typeSet.Add(type, classInfo);
classInfos.Add(classInfo);
}
private void GenerateStructDefines(List<ClassInfo> classInfos, List<string> lines)
@ -818,16 +908,13 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu
foreach (var ci in classInfos)
{
lines.Add($"// {ci.type.Klass}");
uint packingSize = ci.layout?.PackingSize ?? 0;
if (packingSize != 0)
{
lines.Add($"#pragma pack(push, {packingSize})");
}
uint classSize = ci.layout?.ClassSize ?? 0;
uint packingSize = ci.packingSize;
uint classSize = ci.classSize;
if (ci.typeDef.IsExplicitLayout)
if (ci.layout == LayoutKind.Explicit)
{
lines.Add($"union {ci.type.GetTypeName()} {{");
lines.Add($"struct {ci.type.GetTypeName()} {{");
lines.Add("\tunion {");
if (classSize > 0)
{
lines.Add($"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};");
@ -839,14 +926,28 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu
string fieldName = $"__{index}";
string commentFieldName = $"{field.field.Name}";
lines.Add("\t#pragma pack(push, 1)");
lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding[{offset}];" : "")} {field.type.GetTypeName()} {fieldName};}}; // {commentFieldName}");
lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding[{offset}]; " : "")}{field.type.GetTypeName()} {fieldName};}}; // {commentFieldName}");
lines.Add($"\t#pragma pack(pop)");
lines.Add($"\tstruct {{ {field.type.GetTypeName()} {fieldName}_forAlignmentOnly;}}; // {commentFieldName}");
if (packingSize > 0)
{
lines.Add($"\t#pragma pack(push, {packingSize})");
}
lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding_forAlignmentOnly[{offset}]; " : "")}{field.type.GetTypeName()} {fieldName}_forAlignmentOnly;}}; // {commentFieldName}");
if (packingSize > 0)
{
lines.Add($"\t#pragma pack(pop)");
}
++index;
}
lines.Add("\t};");
lines.Add("};");
}
else
{
if (packingSize != 0)
{
lines.Add($"#pragma pack(push, {packingSize})");
}
lines.Add($"{(classSize > 0 ? "union" : "struct")} {ci.type.GetTypeName()} {{");
if (classSize > 0)
{
@ -865,7 +966,6 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu
{
lines.Add("\t};");
}
}
lines.Add("};");
if (packingSize != 0)
{
@ -873,6 +973,7 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu
}
}
}
}
private const string SigOfObj = "u";