236 lines
8.6 KiB
C#
236 lines
8.6 KiB
C#
using dnlib.DotNet;
|
|
using HybridCLR.Editor.ABI;
|
|
using HybridCLR.Editor.Meta;
|
|
using HybridCLR.Editor.Template;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using TypeInfo = HybridCLR.Editor.ABI.TypeInfo;
|
|
|
|
namespace HybridCLR.Editor.MethodBridge
|
|
{
|
|
public class Generator
|
|
{
|
|
public class Options
|
|
{
|
|
public PlatformABI PlatformABI { get; set; }
|
|
|
|
public string TemplateCode { get; set; }
|
|
|
|
public string OutputFile { get; set; }
|
|
|
|
public IReadOnlyList<MethodDef> NotGenericMethods { get; set; }
|
|
|
|
public IReadOnlyCollection<GenericMethod> GenericMethods { get; set; }
|
|
|
|
public HashSet<GenericMethod> SpeicalPreserveMethods { get; set; }
|
|
}
|
|
|
|
private PlatformABI _platformABI;
|
|
|
|
private readonly List<MethodDef> _notGenericMethods;
|
|
|
|
private readonly List<GenericMethod> _genericMethods;
|
|
|
|
private readonly HashSet<GenericMethod> _preservedMethods;
|
|
|
|
private readonly string _templateCode;
|
|
|
|
private readonly string _outputFile;
|
|
|
|
private readonly PlatformGeneratorBase _platformAdaptor;
|
|
|
|
private readonly TypeCreatorBase _typeCreator;
|
|
|
|
private readonly HashSet<MethodDesc> _managed2nativeMethodSet = new HashSet<MethodDesc>();
|
|
|
|
private readonly HashSet<MethodDesc> _native2managedMethodSet = new HashSet<MethodDesc>();
|
|
|
|
private readonly HashSet<MethodDesc> _adjustThunkMethodSet = new HashSet<MethodDesc>();
|
|
|
|
public Generator(Options options)
|
|
{
|
|
_platformABI = options.PlatformABI;
|
|
|
|
List<(MethodDef, string)> notGenericMethodInfo = options.NotGenericMethods.Select(m => (m, m.FullName)).ToList();
|
|
notGenericMethodInfo.Sort((a, b) => string.Compare(a.Item2, b.Item2, StringComparison.Ordinal));
|
|
_notGenericMethods = notGenericMethodInfo.Select(m => m.Item1).ToList();
|
|
|
|
List<(GenericMethod, string)> genericMethodInfo = options.GenericMethods.Select(m => (m, m.ToString())).ToList();
|
|
genericMethodInfo.Sort((a, b) => string.CompareOrdinal(a.Item2, b.Item2));
|
|
_genericMethods = genericMethodInfo.Select(m => m.Item1).ToList();
|
|
|
|
_preservedMethods = options.SpeicalPreserveMethods;
|
|
_templateCode = options.TemplateCode;
|
|
_outputFile = options.OutputFile;
|
|
_platformAdaptor = CreatePlatformAdaptor(options.PlatformABI);
|
|
_typeCreator = TypeCreatorFactory.CreateTypeCreator(options.PlatformABI);
|
|
}
|
|
|
|
private static PlatformGeneratorBase CreatePlatformAdaptor(PlatformABI type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case PlatformABI.Universal32: return new PlatformGeneratorUniversal32();
|
|
case PlatformABI.Universal64: return new PlatformGeneratorUniversal64();
|
|
case PlatformABI.Arm64: return new PlatformGeneratorArm64();
|
|
case PlatformABI.WebGL32: return new PlatformGeneratorWebGL32();
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private MethodDesc CreateMethodDesc(MethodDef methodDef, bool forceRemoveThis, TypeSig returnType, List<TypeSig> parameters)
|
|
{
|
|
var paramInfos = new List<ParamInfo>();
|
|
if (forceRemoveThis && !methodDef.IsStatic)
|
|
{
|
|
parameters.RemoveAt(0);
|
|
}
|
|
foreach (var paramInfo in parameters)
|
|
{
|
|
paramInfos.Add(new ParamInfo() { Type = _typeCreator.CreateTypeInfo(paramInfo) });
|
|
}
|
|
var mbs = new MethodDesc()
|
|
{
|
|
MethodDef = methodDef,
|
|
ReturnInfo = new ReturnInfo() { Type = returnType != null ? _typeCreator.CreateTypeInfo(returnType) : TypeInfo.s_void },
|
|
ParamInfos = paramInfos,
|
|
};
|
|
_typeCreator.OptimizeMethod(mbs);
|
|
return mbs;
|
|
}
|
|
|
|
private void AddManaged2NativeMethod(MethodDesc method)
|
|
{
|
|
method.Init();
|
|
_managed2nativeMethodSet.Add(method);
|
|
}
|
|
|
|
private void AddNative2ManagedMethod(MethodDesc method)
|
|
{
|
|
method.Init();
|
|
_native2managedMethodSet.Add(method);
|
|
}
|
|
|
|
private void AddAdjustThunkMethod(MethodDesc method)
|
|
{
|
|
method.Init();
|
|
_adjustThunkMethodSet.Add(method);
|
|
}
|
|
|
|
private void ProcessMethod(MethodDef method, List<TypeSig> klassInst, List<TypeSig> methodInst)
|
|
{
|
|
if (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily))
|
|
{
|
|
if (!_preservedMethods.Contains(new GenericMethod(method, klassInst, methodInst)))
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//Debug.Log($"[PreservedMethod] method:{method}");
|
|
}
|
|
}
|
|
|
|
TypeSig returnType;
|
|
List<TypeSig> parameters;
|
|
if (klassInst == null && methodInst == null)
|
|
{
|
|
returnType = method.ReturnType;
|
|
parameters = method.Parameters.Select(p => p.Type).ToList();
|
|
}
|
|
else
|
|
{
|
|
var gc = new GenericArgumentContext(klassInst, methodInst);
|
|
returnType = MetaUtil.Inflate(method.ReturnType, gc);
|
|
parameters = method.Parameters.Select(p => MetaUtil.Inflate(p.Type, gc)).ToList();
|
|
}
|
|
|
|
var m2nMethod = CreateMethodDesc(method, false, returnType, parameters);
|
|
AddManaged2NativeMethod(m2nMethod);
|
|
|
|
if (method.IsVirtual)
|
|
{
|
|
if (method.DeclaringType.IsInterface)
|
|
{
|
|
AddAdjustThunkMethod(m2nMethod);
|
|
}
|
|
//var adjustThunkMethod = CreateMethodDesc(method, true, returnType, parameters);
|
|
AddNative2ManagedMethod(m2nMethod);
|
|
}
|
|
if (method.Name == "Invoke" && method.DeclaringType.IsDelegate)
|
|
{
|
|
var openMethod = CreateMethodDesc(method, true, returnType, parameters);
|
|
AddNative2ManagedMethod(openMethod);
|
|
}
|
|
}
|
|
|
|
public void PrepareMethods()
|
|
{
|
|
foreach(var method in _notGenericMethods)
|
|
{
|
|
ProcessMethod(method, null, null);
|
|
}
|
|
|
|
foreach(var method in _genericMethods)
|
|
{
|
|
ProcessMethod(method.Method, method.KlassInst, method.MethodInst);
|
|
}
|
|
}
|
|
|
|
public void Generate()
|
|
{
|
|
var frr = new FileRegionReplace(_templateCode.Replace("{PLATFORM_ABI}", ABIUtil.GetHybridCLRPlatformMacro(_platformABI)));
|
|
|
|
List<string> lines = new List<string>(20_0000);
|
|
|
|
List<MethodDesc> managed2NativeMethodList = _managed2nativeMethodSet.ToList();
|
|
managed2NativeMethodList.Sort((a, b) => string.CompareOrdinal(a.Sig, b.Sig));
|
|
|
|
List<MethodDesc> native2ManagedMethodList = _native2managedMethodSet.ToList();
|
|
native2ManagedMethodList.Sort((a, b) => string.CompareOrdinal(a.Sig, b.Sig));
|
|
|
|
List<MethodDesc> adjustThunkMethodList = _adjustThunkMethodSet.ToList();
|
|
adjustThunkMethodList.Sort((a, b) => string.CompareOrdinal(a.Sig, b.Sig));
|
|
|
|
Debug.LogFormat("== managed2native:{0} native2managed:{1} adjustThunk:{2}",
|
|
managed2NativeMethodList.Count, native2ManagedMethodList.Count, adjustThunkMethodList.Count);
|
|
|
|
foreach(var method in managed2NativeMethodList)
|
|
{
|
|
_platformAdaptor.GenerateManaged2NativeMethod(method, lines);
|
|
}
|
|
|
|
_platformAdaptor.GenerateManaged2NativeStub(managed2NativeMethodList, lines);
|
|
|
|
foreach (var method in native2ManagedMethodList)
|
|
{
|
|
_platformAdaptor.GenerateNative2ManagedMethod(method, lines);
|
|
}
|
|
|
|
_platformAdaptor.GenerateNative2ManagedStub(native2ManagedMethodList, lines);
|
|
|
|
foreach (var method in adjustThunkMethodList)
|
|
{
|
|
_platformAdaptor.GenerateAdjustThunkMethod(method, lines);
|
|
}
|
|
|
|
_platformAdaptor.GenerateAdjustThunkStub(adjustThunkMethodList, lines);
|
|
|
|
frr.Replace("CODE", string.Join("\n", lines));
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));
|
|
|
|
frr.Commit(_outputFile);
|
|
}
|
|
|
|
}
|
|
}
|