diff --git a/Editor/MethodBridge/Generator.cs b/Editor/MethodBridge/Generator.cs index f5eb201..73a29e1 100644 --- a/Editor/MethodBridge/Generator.cs +++ b/Editor/MethodBridge/Generator.cs @@ -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 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 _analyzeTypeInfos = new Dictionary(); private readonly Dictionary _signature2Type = new Dictionary(); + + + 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 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(); + var fields = new List(); + 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; } @@ -374,17 +468,12 @@ namespace HybridCLR.Editor.MethodBridge { return ati.signature; } - + 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(); - var classTypeSet = new HashSet(); + var classTypeSet = new Dictionary(); 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 fields = new List(); - - public ClassLayout layout; + public List fields; + public uint packingSize; + public uint classSize; + public LayoutKind layout; + public bool blittable; } - private void GenerateClassInfo(TypeInfo type, HashSet typeSet, List classInfos) + private void GenerateClassInfo(TypeInfo type, Dictionary typeSet, List classInfos) { - if (!typeSet.Add(type)) + if (typeSet.ContainsKey(type)) { return; } - TypeSig typeSig = type.Klass; - var fields = new List(); - TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow(); + AnalyzeTypeInfo ati = CalculateAnalyzeTypeInfoBasic(type); + //TypeSig typeSig = type.Klass; + //var fields = new List(); - List klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList(); - GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null; + //TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow(); - ClassLayout sa = typeDef.ClassLayout; - - ICorLibTypes corLibTypes = typeDef.Module.CorLibTypes; - foreach (FieldDef field in typeDef.Fields) + //List klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList(); + //GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null; + + //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) - { - GenerateClassInfo(fieldTypeInfo, typeSet, classInfos); - } - fields.Add(new FieldInfo { field = field, type = fieldTypeInfo }); } - classInfos.Add(new ClassInfo() { type = type, typeDef = typeDef, fields = fields, layout = sa }); + var classInfo = new ClassInfo() + { + 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 classInfos, List 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,11 +966,11 @@ const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStu { lines.Add("\t};"); } - } - lines.Add("};"); - if (packingSize != 0) - { - lines.Add($"#pragma pack(pop)"); + lines.Add("};"); + if (packingSize != 0) + { + lines.Add($"#pragma pack(pop)"); + } } } }