HybirdCLR Helper

HybirdCLR Helper
This commit is contained in:
ALEXTANG
2022-08-29 14:33:00 +08:00
parent 052d81e039
commit 072a117643
72 changed files with 3764 additions and 1 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0cca09dc9d27f314da15fa746a01d51c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace HybridCLR.Editor
{
public static partial class BuildConfig
{
#if !UNITY_IOS
[InitializeOnLoadMethod]
private static void Setup()
{
///
/// unity允许使用UNITY_IL2CPP_PATH环境变量指定il2cpp的位置因此我们不再直接修改安装位置的il2cpp
/// 而是在本地目录
///
var localIl2cppDir = LocalIl2CppDir;
if (!Directory.Exists(localIl2cppDir))
{
Debug.LogError($"本地il2cpp目录:{localIl2cppDir} 不存在未安装本地il2cpp。请在菜单 HybridCLR/Installer 中执行安装");
}
Environment.SetEnvironmentVariable("UNITY_IL2CPP_PATH", localIl2cppDir);
}
#endif
public static string ProjectDir => Directory.GetParent(Application.dataPath).ToString();
public static string ScriptingAssembliesJsonFile { get; } = "ScriptingAssemblies.json";
public static string HybridCLRBuildCacheDir => Application.dataPath + "/HybridCLRBuildCache";
public static string HotFixDllsOutputDir => $"{HybridCLRDataDir}/HotFixDlls";
public static string AssetBundleOutputDir => $"{HybridCLRBuildCacheDir}/AssetBundleOutput";
public static string AssetBundleSourceDataTempDir => $"{HybridCLRBuildCacheDir}/AssetBundleSourceData";
public static string HybridCLRDataDir { get; } = $"{ProjectDir}/HybridCLRData";
public static string AssembliesPostIl2CppStripDir => $"{HybridCLRDataDir}/AssembliesPostIl2CppStrip";
public static string LocalIl2CppDir => $"{HybridCLRDataDir}/LocalIl2CppData/il2cpp";
public static string MethodBridgeCppDir => $"{LocalIl2CppDir}/libil2cpp/hybridclr/interpreter";
public static string Il2CppBuildCacheDir { get; } = $"{ProjectDir}/Library/Il2cppBuildCache";
public static string GetHotFixDllsOutputDirByTarget(BuildTarget target)
{
return $"{HotFixDllsOutputDir}/{target}";
}
public static string GetAssembliesPostIl2CppStripDir(BuildTarget target)
{
return $"{AssembliesPostIl2CppStripDir}/{target}";
}
public static string GetAssetBundleOutputDirByTarget(BuildTarget target)
{
return $"{AssetBundleOutputDir}/{target}";
}
public static string GetAssetBundleTempDirByTarget(BuildTarget target)
{
return $"{AssetBundleSourceDataTempDir}/{target}";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf7714fc37515834382cd5836b503a9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace HybridCLR.Editor
{
public static partial class BuildConfig
{
/// <summary>
/// 所有热更新dll列表。放到此列表中的dll在打包时OnFilterAssemblies回调中被过滤。
/// </summary>
public static List<string> HotUpdateAssemblies { get; } = new List<string>
{
"HotFix.dll",
"HotFix2.dll",
};
public static List<string> AOTMetaAssemblies { get; } = new List<string>()
{
"mscorlib.dll",
"System.dll",
"System.Core.dll", // 如果使用了Linq需要这个
//
// 注意修改这个列表请同步修改HotFix2模块中App.cs文件中的 LoadMetadataForAOTAssembly函数中aotDllList列表。
// 两者需要完全一致
//
};
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 33c09b498146d144a947ed6d8ff104f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d7e4bb1668e0b341b9613b967e27f88
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Il2Cpp;
using UnityEditor.UnityLinker;
using UnityEngine;
namespace HybridCLR.Editor.BuildProcessors
{
internal class BPCopyStrippedAOTAssemblies : IPostprocessBuildWithReport
#if !UNITY_2021_1_OR_NEWER
, IIl2CppProcessor
#endif
{
public int callbackOrder => 0;
#if UNITY_2021_1_OR_NEWER
public static string GetStripAssembliesDir2021(BuildTarget target)
{
string projectDir = BuildConfig.ProjectDir;
#if UNITY_STANDALONE_WIN
return $"{projectDir}/Library/Bee/artifacts/WinPlayerBuildProgram/ManagedStripped";
#elif UNITY_ANDROID
return $"{projectDir}/Library/Bee/artifacts/Android/ManagedStripped";
#elif UNITY_IOS
return $"{projectDir}/Temp/StagingArea/Data/Managed/tempStrip";
#elif UNITY_WEBGL
return $"{projectDir}/Library/Bee/artifacts/WebGL/ManagedStripped";
#elif UNITY_EDITOR_OSX
return $"{projectDir}/Library/Bee/artifacts/MacStandalonePlayerBuildProgram/ManagedStripped";
#else
throw new NotSupportedException("GetOriginBuildStripAssembliesDir");
#endif
}
#else
private string GetStripAssembliesDir2020(BuildTarget target)
{
string subPath = target == BuildTarget.Android ?
"assets/bin/Data/Managed" :
"Data/Managed/";
return $"{BuildConfig.ProjectDir}/Temp/StagingArea/{subPath}";
}
public void OnBeforeConvertRun(BuildReport report, Il2CppBuildPipelineData data)
{
// 此回调只在 2020中调用
CopyStripDlls(GetStripAssembliesDir2020(data.target), data.target);
}
#endif
public static void CopyStripDlls(string srcStripDllPath, BuildTarget target)
{
Debug.Log($"[BPCopyStrippedAOTAssemblies] CopyScripDlls. src:{srcStripDllPath} target:{target}");
var dstPath = BuildConfig.GetAssembliesPostIl2CppStripDir(target);
Directory.CreateDirectory(dstPath);
//string srcStripDllPath = BuildConfig.GetOriginBuildStripAssembliesDir(target);
foreach (var fileFullPath in Directory.GetFiles(srcStripDllPath, "*.dll"))
{
var file = Path.GetFileName(fileFullPath);
Debug.Log($"[BPCopyStrippedAOTAssemblies] copy strip dll {fileFullPath} ==> {dstPath}/{file}");
File.Copy($"{fileFullPath}", $"{dstPath}/{file}", true);
}
}
public void OnPostprocessBuild(BuildReport report)
{
#if UNITY_2021_1_OR_NEWER && !UNITY_IOS
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
CopyStripDlls(GetStripAssembliesDir2021(target), target);
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7884710ec2f8e545b3fe9aa05def5a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEngine;
namespace HybridCLR.Editor.BuildProcessors
{
/// <summary>
/// 将热更新dll从Build过程中过滤防止打包到主工程中
/// </summary>
internal class BPFilterHotFixAssemblies : IFilterBuildAssemblies
{
public int callbackOrder => 0;
public string[] OnFilterAssemblies(BuildOptions buildOptions, string[] assemblies)
{
List<string> allHotUpdateDllNames = BuildConfig.HotUpdateAssemblies;
// 检查是否重复填写
var hotUpdateDllSet = new HashSet<string>();
foreach(var hotUpdateDll in allHotUpdateDllNames)
{
if (!hotUpdateDllSet.Add(hotUpdateDll))
{
throw new Exception($"热更新 assembly:{hotUpdateDll} 在列表中重复,请除去重复条目");
}
}
// 检查是否填写了正确的dll名称
foreach (var hotUpdateDll in BuildConfig.HotUpdateAssemblies)
{
if (assemblies.All(ass => !ass.EndsWith(hotUpdateDll)))
{
throw new Exception($"热更新 assembly:{hotUpdateDll} 不存在,请检查拼写错误");
}
Debug.Log($"[BPFilterHotFixAssemblies] 过滤热更新assembly:{hotUpdateDll}");
}
// 将热更dll从打包列表中移除
return assemblies.Where(ass => BuildConfig.HotUpdateAssemblies.All(dll => !ass.EndsWith(dll, StringComparison.OrdinalIgnoreCase))).ToArray();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9dec2922e3df5464aa047b636eb19e0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,148 @@
using HybridCLR.Editor.GlobalManagers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Android;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Il2Cpp;
using UnityEditor.UnityLinker;
using UnityEngine;
namespace HybridCLR.Editor.BuildProcessors
{
public class BPPatchScriptAssembliesJson : IPreprocessBuildWithReport,
#if UNITY_ANDROID
IPostGenerateGradleAndroidProject,
#endif
IPostprocessBuildWithReport
{
public int callbackOrder => 0;
[Serializable]
private class ScriptingAssemblies
{
public List<string> names;
public List<int> types;
}
public void OnPostGenerateGradleAndroidProject(string path)
{
// 如果直接打包apk没有机会在PostprocessBuild中修改ScriptingAssemblies.json。
// 因此需要在这个时机处理
PathScriptingAssembilesFile(path);
}
public void OnPostprocessBuild(BuildReport report)
{
// 如果target为Android,由于已经在OnPostGenerateGradelAndroidProject中处理过
// 这里不再重复处理
#if !UNITY_ANDROID
PathScriptingAssembilesFile(report.summary.outputPath);
#endif
}
private void PathScriptingAssembilesFile(string path)
{
#if UNITY_2020_1_OR_NEWER
AddHotFixAssembliesToScriptingAssembliesJson(path);
#else
AddBackHotFixAssembliesToBinFile(path);
#endif
}
private void AddHotFixAssembliesToScriptingAssembliesJson(string path)
{
Debug.Log($"AddBackHotFixAssembliesToJson. path:{path}");
if (!Directory.Exists(path))
{
path = Directory.GetParent(path).ToString();
}
/*
* ScriptingAssemblies.json 文件中记录了所有的dll名称此列表在游戏启动时自动加载
* 不在此列表中的dll在资源反序列化时无法被找到其类型
* 因此 OnFilterAssemblies 中移除的条目需要再加回来
*/
string[] jsonFiles = Directory.GetFiles(path, BuildConfig.ScriptingAssembliesJsonFile, SearchOption.AllDirectories);
if (jsonFiles.Length == 0)
{
Debug.LogError($"can not find file {BuildConfig.ScriptingAssembliesJsonFile}");
return;
}
foreach (string file in jsonFiles)
{
string content = File.ReadAllText(file);
ScriptingAssemblies scriptingAssemblies = JsonUtility.FromJson<ScriptingAssemblies>(content);
foreach (string name in BuildConfig.HotUpdateAssemblies)
{
if (!scriptingAssemblies.names.Contains(name))
{
scriptingAssemblies.names.Add(name);
scriptingAssemblies.types.Add(16); // user dll type
Debug.Log($"[PatchScriptAssembliesJson] add hotfix assembly:{name} to {file}");
}
}
content = JsonUtility.ToJson(scriptingAssemblies);
File.WriteAllText(file, content);
}
}
private void AddBackHotFixAssembliesToBinFile(string path)
{
/*
* Unity2019 中 dll 加载列表存储在 globalgamemanagers 文件中,此列表在游戏启动时自动加载,
* 不在此列表中的dll在资源反序列化时无法被找到其类型
* 因此 OnFilterAssemblies 中移除的条目需要再加回来
*/
#if UNITY_ANDROID
string[] binFiles = new string[] { "Temp/gradleOut/unityLibrary/src/main/assets/bin/Data/globalgamemanagers" }; // report.files 不包含 Temp/gradleOut 等目录
#else
// 直接出包和输出vs工程时路径不同report.summary.outputPath 记录的是前者路径
string[] binFiles = Directory.GetFiles(Path.GetDirectoryName(path), "globalgamemanagers", SearchOption.AllDirectories);
#endif
if (binFiles.Length == 0)
{
Debug.LogError("can not find file ScriptingAssemblies.json");
return;
}
foreach (string binPath in binFiles)
{
var binFile = new UnityBinFile();
binFile.LoadFromFile(binPath);
ScriptsData scriptsData = binFile.scriptsData;
foreach (string name in BuildConfig.HotUpdateAssemblies)
{
if (!scriptsData.dllNames.Contains(name))
{
scriptsData.dllNames.Add(name);
scriptsData.dllTypes.Add(16); // user dll type
}
}
binFile.scriptsData = scriptsData;
binFile.RebuildAndFlushToFile(binPath);
}
}
#region useless
public void OnPreprocessBuild(BuildReport report)
{
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9bb6e2908d8948648979c9ff6bb7937d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e52fb09e14efee949ae80ae8aa9f9d44
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,107 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System.Text;
using System.Reflection;
using System;
using System.Linq;
namespace HybridCLR.Editor.GlobalManagers
{
/// <summary>
/// Unity 生成的二进制文件(本代码不支持5.x之前的版本)
/// </summary>
public unsafe class UnityBinFile
{
/*
* MonoManager: idx: 6;
* type: metaData.types[objects[6].typeID]
*/
public const int kMonoManagerIdx = 6;
public string path { get; private set; }
public FileHeader header;
public MetaData metaData;
public ScriptsData scriptsData;
public void LoadFromFile(string path)
{
this.path = path;
var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
var br = new BinaryReader(fs, Encoding.UTF8, true);
header.LoadFromStream(br);
// 按理说 metaData 应该新开一个buffer来避免加载时的对齐逻辑问题但由于 sizeof(Header) = 20已经对齐到4了所以可以连续读
metaData.LoadFromStream(br, header.dataOffset);
scriptsData = metaData.GetScriptData(br);
br.Close();
fs.Close();
}
public void RebuildAndFlushToFile(string newPath)
{
var fsR = new FileStream(path, FileMode.Open, FileAccess.Read);
var brR = new BinaryReader(fsR, Encoding.UTF8, true);
var ms = new MemoryStream((int)(header.fileSize * 1.5f));
var bw = new BinaryWriter(ms, Encoding.UTF8, true);
/*
* 开始写入data
* dll名称列表存储于 data 区段,修改其数据并不会影响 MetaData 大小,因此 dataOffset 不会改变
*/
ms.Position = header.dataOffset;
Dictionary<long, ObjectInfo> newObjInfos = new Dictionary<long, ObjectInfo>();
foreach (var kv in metaData.objects)
{
long objID = kv.Key;
ObjectInfo objInfo = kv.Value;
byte[] buff = new byte[objInfo.size];
fsR.Position = objInfo.realPos;
brR.Read(buff, 0, buff.Length);
{// unity 的数据偏移貌似会对齐到 8
int newPos = (((int)ms.Position + 7) >> 3) << 3;
int gapSize = newPos - (int)ms.Position;
for (int i = 0; i < gapSize; i++)
bw.Write((byte)0);
objInfo.dataPos = (uint)ms.Position - header.dataOffset; // 重定位数据偏移
}
if (objID != kMonoManagerIdx)
bw.Write(buff, 0, buff.Length);
else
objInfo.size = (uint)scriptsData.SaveToStream(bw);
newObjInfos.Add(objID, objInfo);
}
metaData.objects = newObjInfos;
header.fileSize = (uint)ms.Position;
ms.Position = 0;
header.SaveToStream(bw);
metaData.SaveToStream(bw);
brR.Close();
fsR.Close();
// 写入新文件
ms.Position = 0;
File.WriteAllBytes(newPath, ms.ToArray());
bw.Close();
ms.Close();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae7ec6e3674077d46898fe821d24bf85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,397 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using static HybridCLR.Editor.GlobalManagers.UnityBinUtils;
namespace HybridCLR.Editor.GlobalManagers
{
public struct FileHeader
{
public const int kSize = 20;
public uint dataSize => fileSize - metadataSize;
public uint metadataSize;
public uint fileSize;
public uint version;
public uint dataOffset;
public byte endianess;
public void LoadFromStream(BinaryReader br)
{
long startPos = br.BaseStream.Position;
metadataSize = br.ReadUInt32();
fileSize = br.ReadUInt32();
version = br.ReadUInt32();
dataOffset = br.ReadUInt32();
endianess = br.ReadByte();
br.BaseStream.Position = startPos + kSize;
SwapEndianess();
}
public long SaveToStream(BinaryWriter bw)
{
SwapEndianess();
long startPos = bw.BaseStream.Position;
bw.Write(metadataSize);
bw.Write(fileSize);
bw.Write(version);
bw.Write(dataOffset);
bw.Write(endianess);
bw.BaseStream.Position = startPos + kSize;
return kSize;
}
void SwapEndianess()
{
SwapUInt(ref metadataSize);
SwapUInt(ref fileSize);
SwapUInt(ref version);
SwapUInt(ref dataOffset);
}
}
public struct MetaData
{
public long dataStartPos;
public string version;
public uint platform;
public bool enableTypeTree;
public int typeCount;
public ObjectType[] types;
public int objectCount;
public Dictionary<long, ObjectInfo> objects;
public int scriptTypeCount;
public ScriptType[] scriptTypes;
public int externalsCount;
public ExternalInfo[] externals;
#if UNITY_2019_2_OR_NEWER
public int refTypeCount;
public ObjectType[] refTypes;
#endif
public string dummyStr;
public void LoadFromStream(BinaryReader br, uint dataOffset)
{
long startPos = br.BaseStream.Position;
dataStartPos = startPos;
version = br.ReadRawString();
platform = br.ReadUInt32();
enableTypeTree = br.ReadBoolean();
typeCount = br.ReadInt32();
types = new ObjectType[typeCount];
for (int i = 0; i < typeCount; i++)
{
types[i].LoadFromStream(br);
}
objectCount = br.ReadInt32();
objects = new Dictionary<long, ObjectInfo>();
for(int i = 0; i < objectCount; i++)
{
long id = br.AlignedReadInt64();
ObjectInfo objInfo = new ObjectInfo();
objInfo.LoadFromStream(br);
objInfo.realPos = objInfo.dataPos + dataOffset;
objects.Add(id, objInfo);
}
scriptTypeCount = br.ReadInt32();
scriptTypes = new ScriptType[scriptTypeCount];
for(int i = 0; i < scriptTypeCount; i++)
{
scriptTypes[i].LoadFromStream(br);
}
externalsCount = br.ReadInt32();
externals = new ExternalInfo[externalsCount];
for(int i = 0; i < externalsCount; i++)
{
externals[i].LoadFromStream(br);
}
#if UNITY_2019_2_OR_NEWER
refTypeCount = br.ReadInt32();
refTypes = new ObjectType[refTypeCount];
for(int i = 0; i < refTypeCount; i++)
{
refTypes[i].LoadFromStream(br);
}
#endif
dummyStr = br.ReadRawString();
}
public long SaveToStream(BinaryWriter bw)
{
long startPos = bw.BaseStream.Position;
bw.WriteRawString(version);
bw.Write(platform);
bw.Write(enableTypeTree);
bw.Write(typeCount);
foreach(var type in types)
type.SaveToStream(bw);
bw.Write(objectCount);
foreach (var kv in objects)
{
bw.AlignedWriteInt64(kv.Key);
kv.Value.SaveToStream(bw);
}
bw.Write(scriptTypeCount);
foreach(var st in scriptTypes)
st.SaveToStream(bw);
bw.Write(externalsCount);
foreach(var external in externals)
external.SaveToStream(bw);
#if UNITY_2019_2_OR_NEWER
bw.Write(refTypeCount);
foreach(var refT in refTypes)
refT.SaveToStream(bw);
#endif
bw.WriteRawString(dummyStr);
return bw.BaseStream.Position - startPos;
}
public ScriptsData GetScriptData(BinaryReader br)
{
ObjectInfo objInfo = objects[UnityBinFile.kMonoManagerIdx];
br.BaseStream.Seek(objInfo.realPos, SeekOrigin.Begin);
ScriptsData data = new ScriptsData();
data.LoadFromStream(br);
return data;
}
}
public struct ObjectType
{
public int typeID;
public bool isStriped;
public short scriptTypeIndex;
public bool needReadScriptHash; // dont save
public Hash scriptSigHash;
public Hash typeHash;
public void LoadFromStream(BinaryReader br)
{
typeID = br.ReadInt32();
isStriped = br.ReadBoolean();
scriptTypeIndex = br.ReadInt16();
needReadScriptHash = typeID == -1 || typeID == 0x72;
if(needReadScriptHash)
scriptSigHash.LoadFromStream(br);
typeHash.LoadFromStream(br);
// GlobalManagers does not has TypeTrees
}
public long SaveToStream(BinaryWriter bw)
{
long startPos = bw.BaseStream.Position;
bw.Write(typeID);
bw.Write(isStriped);
bw.Write(scriptTypeIndex);
if(needReadScriptHash)
scriptSigHash.SaveToStream(bw);
typeHash.SaveToStream(bw);
return bw.BaseStream.Position - startPos;
}
public int Size()
{
int ret = 0;
ret += sizeof(int);
ret += sizeof(bool);
ret += sizeof(short);
if (needReadScriptHash)
ret += Hash.kSize;
ret += Hash.kSize;
return ret;
}
}
public struct ObjectInfo
{
public const int kSize = 12;
public uint dataPos;
public uint size;
public uint typeID;
public uint realPos; // dataPos + Header.dataOffset; // dont save
public void LoadFromStream(BinaryReader br)
{
dataPos = br.ReadUInt32();
size = br.ReadUInt32();
typeID = br.ReadUInt32();
}
public long SaveToStream(BinaryWriter bw)
{
bw.Write(dataPos);
bw.Write(size);
bw.Write(typeID);
return kSize;
}
}
public struct ScriptType
{
public int localFileIndex;
public long localIdentifierOfBin;
public void LoadFromStream(BinaryReader br)
{
localFileIndex = br.ReadInt32();
localIdentifierOfBin = br.AlignedReadInt64();
}
public long SaveToStream(BinaryWriter bw)
{
long startPos = bw.BaseStream.Position;
bw.Write(localFileIndex);
bw.AlignedWriteInt64(localIdentifierOfBin);
return bw.BaseStream.Position - startPos;
}
}
public struct ExternalInfo
{
public string dummy;
public Hash guid;
public int type;
public string name;
public void LoadFromStream(BinaryReader br)
{
dummy = br.ReadRawString();
guid.LoadFromStream(br);
type = br.ReadInt32();
name = br.ReadRawString();
}
public long SaveToStream(BinaryWriter bw)
{
long startPos = bw.BaseStream.Position;
bw.WriteRawString(dummy);
guid.SaveToStream(bw);
bw.Write(type);
bw.WriteRawString(name);
return bw.BaseStream.Position - startPos;
}
}
public struct ScriptsData
{
public ScriptID[] scriptIDs;
public List<string> dllNames;
public List<int> dllTypes; // 16 is user type
public void LoadFromStream(BinaryReader br)
{
{
int count = br.ReadInt32();
scriptIDs = new ScriptID[count];
for(int i = 0; i < count; i++)
scriptIDs[i].LoadFromStream(br);
}
{
int count = br.ReadInt32();
dllNames = new List<string>(count);
for (var i = 0; i < count; i++)
dllNames.Add(br.ReadSizeString());
}
{
int count = br.ReadInt32();
dllTypes = new List<int>(count);
for(var i = 0; i < count; i++)
dllTypes.Add(br.ReadInt32());
}
}
public long SaveToStream(BinaryWriter bw)
{
long startPos = bw.BaseStream.Position;
bw.Write(scriptIDs.Length);
for(int i = 0; i < scriptIDs.Length; i++)
scriptIDs[i].SaveToStream(bw);
bw.Write(dllNames.Count);
for(int i = 0, imax = dllNames.Count; i < imax; i++)
bw.WriteSizeString(dllNames[i]);
bw.Write(dllTypes.Count);
for(int i = 0, imax = dllTypes.Count; i < imax; i++)
bw.Write(dllTypes[i]);
return bw.BaseStream.Position - startPos;
}
}
public struct ScriptID
{
public int fileID;
public long pathID; // localIdentifier
public void LoadFromStream(BinaryReader br)
{
fileID = br.ReadInt32();
pathID = br.ReadInt64();
}
public long SaveToStream(BinaryWriter bw)
{
bw.Write(fileID);
bw.Write(pathID);
return 4 + 8;
}
}
public struct Hash
{
public const int kSize = 16;
public int[] data;
public void LoadFromStream(BinaryReader br)
{
data = new int[4];
for(int i = 0; i < data.Length; i++)
{
data[i] = br.ReadInt32();
}
}
public long SaveToStream(BinaryWriter bw)
{
for(int i = 0; i < data.Length; i++)
{
bw.Write(data[i]);
}
return kSize;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96788c7fe08d5d54d95a87cfbdcb643a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System.Text;
namespace HybridCLR.Editor.GlobalManagers
{
public static class UnityBinUtils
{
public static void SwapUInt(ref uint val)
{
val = (val >> 24) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | (val << 24);
}
public static string ReadRawString(this BinaryReader br)
{
long startPos = br.BaseStream.Position;
while (true)
{
byte val = br.ReadByte();
if(val == 0)
break;
}
int size = (int)(br.BaseStream.Position - startPos);
br.BaseStream.Position = startPos;
byte[] buffer = br.ReadBytes(size);
string ret = Encoding.UTF8.GetString(buffer, 0, size - 1);
return ret;
}
public static void WriteRawString(this BinaryWriter bw, string str)
{
byte[] buffer = Encoding.UTF8.GetBytes(str);
bw.Write(buffer, 0, buffer.Length);
bw.Write((byte)0);
}
public static string ReadSizeString(this BinaryReader br)
{
int size = br.ReadInt32();
byte[] buff = br.ReadBytes(size);
br.BaseStream.AlignOffset4();
string ret = Encoding.UTF8.GetString(buff);
return ret;
}
public static void WriteSizeString(this BinaryWriter bw, string str)
{
byte[] buff = Encoding.UTF8.GetBytes(str);
bw.Write(buff.Length);
bw.Write(buff, 0, buff.Length);
bw.BaseStream.AlignOffset4();
}
public static void AlignOffset4(this Stream stream)
{
int offset = (((int)stream.Position + 3) >> 2) << 2;
stream.Position = offset;
}
public static long AlignedReadInt64(this BinaryReader br)
{
br.BaseStream.AlignOffset4();
return br.ReadInt64();
}
public static void AlignedWriteInt64(this BinaryWriter bw, long val)
{
bw.BaseStream.AlignOffset4();
bw.Write(val);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf7c4cf970660614fb54d838ec6e7eda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Build.Player;
using UnityEngine;
namespace HybridCLR.Editor
{
internal class CompileDllHelper
{
public static void CompileDll(string buildDir, BuildTarget target)
{
var group = BuildPipeline.GetBuildTargetGroup(target);
ScriptCompilationSettings scriptCompilationSettings = new ScriptCompilationSettings();
scriptCompilationSettings.group = group;
scriptCompilationSettings.target = target;
Directory.CreateDirectory(buildDir);
ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, buildDir);
foreach (var ass in scriptCompilationResult.assemblies)
{
Debug.LogFormat("compile assemblies:{1}/{0}", ass, buildDir);
}
}
public static void CompileDll(BuildTarget target)
{
CompileDll(BuildConfig.GetHotFixDllsOutputDirByTarget(target), target);
}
[MenuItem("HybridCLR/CompileDll/ActiveBuildTarget")]
public static void CompileDllActiveBuildTarget()
{
CompileDll(EditorUserBuildSettings.activeBuildTarget);
}
[MenuItem("HybridCLR/CompileDll/Win32")]
public static void CompileDllWin32()
{
CompileDll(BuildTarget.StandaloneWindows);
}
[MenuItem("HybridCLR/CompileDll/Win64")]
public static void CompileDllWin64()
{
CompileDll(BuildTarget.StandaloneWindows64);
}
[MenuItem("HybridCLR/CompileDll/Android")]
public static void CompileDllAndroid()
{
CompileDll(BuildTarget.Android);
}
[MenuItem("HybridCLR/CompileDll/IOS")]
public static void CompileDllIOS()
{
CompileDll(BuildTarget.iOS);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91aca02079207d746b74aeea1b595127
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f85b93bff3478c045ae862a7cc216b18
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators
{
internal static class ConstStrings
{
public const string typeObjectPtr = "Il2CppObject*";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1465458aaad6884d95af7c4fc2de7a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators
{
public class FileRegionReplace
{
private readonly string _tplFile;
private readonly Dictionary<string, string> _regionReplaceContents = new Dictionary<string, string>();
public FileRegionReplace(string tplFile)
{
_tplFile = tplFile;
}
public void Replace(string regionName, string regionContent)
{
_regionReplaceContents.Add(regionName, regionContent);
}
public void Commit(string outputFile)
{
string originContent = File.ReadAllText(_tplFile, Encoding.UTF8);
string resultContent = originContent;
foreach (var c in _regionReplaceContents)
{
resultContent = TemplateUtil.ReplaceRegion(resultContent, c.Key, c.Value);
}
var utf8WithoutBOM = new System.Text.UTF8Encoding(false);
File.WriteAllText(outputFile, resultContent, utf8WithoutBOM);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5aa6559bd1b1e4488aa6746eda8202b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.Generators
{
internal class GeneratorConfig
{
/// <summary>
/// 目前已经根据热更新dll的依赖自动计算需要扫描哪些dll来收集桥接函数。
/// 只要你的热更新以assembly def形式放到项目中是不需要改这个的
/// </summary>
/// <returns></returns>
public static List<string> GetExtraAssembiles()
{
return new List<string>
{
// "mscorlib",
};
}
/// <summary>
/// 暂时没有仔细扫描泛型,如果运行时发现有生成缺失,先手动在此添加类
/// </summary>
/// <returns></returns>
public static List<Type> PrepareCustomGenericTypes()
{
return new List<Type>
{
typeof(Action<int, string, Vector3>),
};
}
/// <summary>
/// 如果提示缺失桥接函数,将提示缺失的签名加入到下列列表是简单的做法。
/// 这里添加64位App缺失的桥接函数签名
/// </summary>
/// <returns></returns>
public static List<string> PrepareCustomMethodSignatures64()
{
return new List<string>
{
"vi8i8",
"i4i8i8i4i4i8i8",
"i8i8S12",
"S12i8S12",
"S12i8S12S12",
"i16i8i16i16",
};
}
/// <summary>
/// 如果提示缺失桥接函数,将提示缺失的签名加入到下列列表是简单的做法。
/// 这里添加32位App缺失的桥接函数签名
/// </summary>
/// <returns></returns>
public static List<string> PrepareCustomMethodSignatures32()
{
return new List<string>
{
"vi4i4",
"S12i4S12S12",
};
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1d14f8f858560948ae1dfa0fa572e51
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4aa66b41c89ed7742ad067898725f7ff
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public interface IPlatformAdaptor
{
bool IsArch32 { get; }
TypeInfo CreateTypeInfo(Type type, bool returnValue);
void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> outputLines);
void GenerateManaged2NativeStub(List<MethodBridgeSig> methods, List<string> lines);
void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> outputLines);
void GenerateNative2ManagedStub(List<MethodBridgeSig> methods, List<string> lines);
void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> outputLines);
void GenerateAdjustThunkStub(List<MethodBridgeSig> methods, List<string> lines);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 983dbdeb1fb7d0b43a863c6b22160ace
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,353 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public class TypeGenInfo
{
public Type Type { get; set; }
public List<MethodInfo> GenericMethods { get; set; }
}
public class MethodBridgeGeneratorOptions
{
public List<Assembly> HotfixAssemblies { get; set; }
public List<Assembly> AllAssemblies { get; set; }
public PlatformABI CallConvention { get; set; }
public string OutputFile { get; set; }
public bool Optimized { get; set; }
}
public class MethodBridgeGenerator
{
private readonly HashSet<Assembly> _hotfixAssemblies;
private readonly List<Assembly> _assemblies;
private readonly PlatformABI _callConvention;
private readonly string _outputFile;
public readonly bool _optimized;
private readonly IPlatformAdaptor _platformAdaptor;
private readonly HashSet<MethodBridgeSig> _managed2nativeMethodSet = new HashSet<MethodBridgeSig>();
private List<MethodBridgeSig> _managed2nativeMethodList;
private readonly HashSet<MethodBridgeSig> _native2managedMethodSet = new HashSet<MethodBridgeSig>();
private List<MethodBridgeSig> _native2managedMethodList;
private readonly HashSet<MethodBridgeSig> _adjustThunkMethodSet = new HashSet<MethodBridgeSig>();
private List<MethodBridgeSig> _adjustThunkMethodList;
public bool IsHotFixType(Type type)
{
return _hotfixAssemblies.Contains(type.Assembly);
}
public MethodBridgeGenerator(MethodBridgeGeneratorOptions options)
{
_hotfixAssemblies = new HashSet<Assembly>(options.HotfixAssemblies);
_assemblies = options.AllAssemblies;
_callConvention = options.CallConvention;
_outputFile = options.OutputFile;
_platformAdaptor = CreatePlatformAdaptor(options.CallConvention);
_optimized = options.Optimized;
}
private static IPlatformAdaptor CreatePlatformAdaptor(PlatformABI type)
{
switch (type)
{
case PlatformABI.Universal32: return new PlatformAdaptor_Universal32();
case PlatformABI.Universal64: return new PlatformAdaptor_Universal64();
case PlatformABI.Arm64: return new PlatformAdaptor_Arm64();
default: throw new NotSupportedException();
}
}
private string GetTemplateFile()
{
string tplFile;
switch (_callConvention)
{
case PlatformABI.Universal32: tplFile = "Universal32"; break;
case PlatformABI.Universal64: tplFile = "Universal64"; break;
case PlatformABI.Arm64: tplFile = "Arm64"; break;
default: throw new NotSupportedException();
};
return $"{Application.dataPath}/Editor/HybridCLR/Generators/Templates/MethodBridge_{tplFile}.cpp";
}
public IEnumerable<TypeGenInfo> GetGenerateTypes()
{
return new List<TypeGenInfo>();
}
private MethodBridgeSig CreateMethodBridgeSig(bool isStatic, ParameterInfo returnType, ParameterInfo[] parameters)
{
var paramInfos = new List<ParamInfo>();
if (!isStatic)
{
// FIXME arm32 is s_i4u4
paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.IsArch32 ? TypeInfo.s_i4u4 : TypeInfo.s_i8u8 });
}
foreach (var paramInfo in parameters)
{
paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.CreateTypeInfo(paramInfo.ParameterType, false) });
}
var mbs = new MethodBridgeSig()
{
ReturnInfo = new ReturnInfo() { Type = returnType != null ? _platformAdaptor.CreateTypeInfo(returnType.ParameterType, true) : TypeInfo.s_void },
ParamInfos = paramInfos,
};
return mbs;
}
private void AddManaged2NativeMethod(MethodBridgeSig method)
{
if (_managed2nativeMethodSet.Add(method))
{
method.Init();
}
}
private void AddNative2ManagedMethod(MethodBridgeSig method)
{
if (_native2managedMethodSet.Add(method))
{
method.Init();
}
}
private void AddAdjustThunkMethod(MethodBridgeSig method)
{
if (_adjustThunkMethodSet.Add(method))
{
method.Init();
}
}
private void ScanType(Type type)
{
if (type.IsGenericTypeDefinition)
{
return;
}
if (_optimized)
{
if (!type.IsNested)
{
if (!type.IsPublic)
{
return;
}
}
else
{
if (type.IsNestedPrivate)
{
return;
}
}
}
var typeDel = typeof(MulticastDelegate);
if (typeDel.IsAssignableFrom(type))
{
var method = type.GetMethod("Invoke");
if (method == null)
{
//Debug.LogError($"delegate:{typeDel.FullName} Invoke not exists");
return;
}
// Debug.Log($"== delegate:{type}");
var instanceCallMethod = CreateMethodBridgeSig(false, method.ReturnParameter, method.GetParameters());
AddManaged2NativeMethod(instanceCallMethod);
AddNative2ManagedMethod(instanceCallMethod);
var staticCallMethod = CreateMethodBridgeSig(true, method.ReturnParameter, method.GetParameters());
AddManaged2NativeMethod(staticCallMethod);
AddNative2ManagedMethod(staticCallMethod);
return;
}
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
{
if (method.IsGenericMethodDefinition)
{
continue;
}
if (_optimized && (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily)))
{
continue;
}
if (!_optimized || (method.IsFamily || method.IsPublic))
{
var m2nMethod = CreateMethodBridgeSig(method.IsStatic, method.ReturnParameter, method.GetParameters());
AddManaged2NativeMethod(m2nMethod);
if (type.IsValueType && !method.IsStatic)
{
var adjustThunkMethod = CreateMethodBridgeSig(false, method.ReturnParameter, method.GetParameters());
AddAdjustThunkMethod(adjustThunkMethod);
}
if (method.IsVirtual)
{
AddNative2ManagedMethod(m2nMethod);
}
}
}
foreach (var method in type.GetConstructors(BindingFlags.Instance | BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
{
if (_optimized && (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily)))
{
continue;
}
if (!_optimized || (method.IsFamily || method.IsPublic))
{
var callMethod = CreateMethodBridgeSig(false, null, method.GetParameters());
AddManaged2NativeMethod(callMethod);
if (type.IsValueType && !method.IsStatic)
{
var invokeMethod = CreateMethodBridgeSig(false, null, method.GetParameters());
AddAdjustThunkMethod(invokeMethod);
}
}
}
foreach (var subType in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
{
ScanType(subType);
}
}
public void PrepareFromAssemblies()
{
foreach (var ass in _assemblies)
{
if (_hotfixAssemblies.Contains(ass))
{
continue;
}
//Debug.Log("prepare assembly:" + ass.FullName);
foreach (var type in ass.GetTypes())
{
ScanType(type);
}
}
}
private void PrepareMethodsFromCustomeGenericTypes()
{
foreach (var type in GeneratorConfig.PrepareCustomGenericTypes())
{
ScanType(type);
}
}
public void PrepareMethods()
{
PrepareMethodsFromCustomeGenericTypes();
foreach(var methodSig in _platformAdaptor.IsArch32 ? GeneratorConfig.PrepareCustomMethodSignatures32() : GeneratorConfig.PrepareCustomMethodSignatures64())
{
var method = MethodBridgeSig.CreateBySignatuer(methodSig);
AddManaged2NativeMethod(method);
AddAdjustThunkMethod(method);
}
PrepareFromAssemblies();
{
var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
foreach (var method in _managed2nativeMethodSet)
{
sortedMethods.Add(method.CreateCallSigName(), method);
}
_managed2nativeMethodList = sortedMethods.Values.ToList();
}
{
var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
foreach (var method in _native2managedMethodSet)
{
sortedMethods.Add(method.CreateCallSigName(), method);
}
_native2managedMethodList = sortedMethods.Values.ToList();
}
{
var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
foreach (var method in _adjustThunkMethodSet)
{
sortedMethods.Add(method.CreateCallSigName(), method);
}
_adjustThunkMethodList = sortedMethods.Values.ToList();
}
}
public void Generate()
{
var frr = new FileRegionReplace(GetTemplateFile());
List<string> lines = new List<string>(20_0000);
Debug.LogFormat("== managed2native method count:{0}", _managed2nativeMethodList.Count);
foreach(var method in _managed2nativeMethodList)
{
_platformAdaptor.GenerateManaged2NativeMethod(method, lines);
}
_platformAdaptor.GenerateManaged2NativeStub(_managed2nativeMethodList, lines);
Debug.LogFormat("== native2managed method count:{0}", _native2managedMethodList.Count);
foreach (var method in _native2managedMethodList)
{
_platformAdaptor.GenerateNative2ManagedMethod(method, lines);
}
_platformAdaptor.GenerateNative2ManagedStub(_native2managedMethodList, lines);
Debug.LogFormat("== adjustThunk method count:{0}", _adjustThunkMethodList.Count);
foreach (var method in _adjustThunkMethodList)
{
_platformAdaptor.GenerateAdjustThunkMethod(method, lines);
}
_platformAdaptor.GenerateAdjustThunkStub(_adjustThunkMethodList, lines);
frr.Replace("INVOKE_STUB", string.Join("\n", lines));
Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));
frr.Commit(_outputFile);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3171ed79d43e06c4b8c889b0a3f38969
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public class MethodBridgeSig : IEquatable<MethodBridgeSig>
{
private readonly static Regex s_sigPattern = new Regex(@"^(v|i1|i2|i4|i8|r4|r8|i16|sr|vf2|vf3|vf4|vd2|vd3|vd4|S\d+|A\d+|B\d+|C\d+)+$");
public static MethodBridgeSig CreateBySignatuer(string sigName)
{
var re = s_sigPattern.Match(sigName);
if (!re.Success)
{
throw new ArgumentException($"{sigName} is not valid signature");
}
var mbs = new MethodBridgeSig() { ParamInfos = new List<ParamInfo>()};
var sigs = re.Groups[1].Captures;
mbs.ReturnInfo = new ReturnInfo() { Type = CreateTypeInfoBySignature(sigs[0].Value)};
for(int i = 1; i < sigs.Count; i++)
{
mbs.ParamInfos.Add(new ParamInfo() { Type = CreateTypeInfoBySignature(sigs[i].Value)});
}
return mbs;
}
private static TypeInfo CreateTypeInfoBySignature(string sigName)
{
switch(sigName)
{
case "v": return new TypeInfo(typeof(void), ParamOrReturnType.VOID);
case "i1": return new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1);
case "i2": return new TypeInfo(typeof(short), ParamOrReturnType.I2_U2);
case "i4": return new TypeInfo(typeof(int), ParamOrReturnType.I4_U4);
case "i8": return new TypeInfo(typeof(long), ParamOrReturnType.I8_U8);
case "r4": return new TypeInfo(typeof(float), ParamOrReturnType.R4);
case "r8": return new TypeInfo(typeof(double), ParamOrReturnType.R8);
case "i16": return TypeInfo.s_i16;
case "sr": return TypeInfo.s_ref;
case "vf2": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_FLOAT_2);
case "vf3": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_FLOAT_3);
case "vf4": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_FLOAT_4);
case "vd2": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_DOUBLE_2);
case "vd3": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_DOUBLE_3);
case "vd4": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_DOUBLE_4);
default:
{
if (sigName.StartsWith("S"))
{
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN1, int.Parse(sigName.Substring(1)));
}
if (sigName.StartsWith("A"))
{
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN2, int.Parse(sigName.Substring(1)));
}
if (sigName.StartsWith("B"))
{
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN4, int.Parse(sigName.Substring(1)));
}
if (sigName.StartsWith("C"))
{
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN8, int.Parse(sigName.Substring(1)));
}
throw new ArgumentException($"invalid signature:{sigName}");
}
}
}
public ReturnInfo ReturnInfo { get; set; }
public List<ParamInfo> ParamInfos { get; set; }
public void Init()
{
for(int i = 0; i < ParamInfos.Count; i++)
{
ParamInfos[i].Index = i;
}
}
public string CreateCallSigName()
{
var n = new StringBuilder();
n.Append(ReturnInfo.Type.CreateSigName());
foreach(var param in ParamInfos)
{
n.Append(param.Type.CreateSigName());
}
return n.ToString();
}
public string CreateInvokeSigName()
{
var n = new StringBuilder();
n.Append(ReturnInfo.Type.CreateSigName());
foreach (var param in ParamInfos)
{
n.Append(param.Type.CreateSigName());
}
return n.ToString();
}
public override bool Equals(object obj)
{
return Equals((MethodBridgeSig)obj);
}
public bool Equals(MethodBridgeSig other)
{
if (other == null)
{
return false;
}
if (!ReturnInfo.Type.Equals(other.ReturnInfo.Type))
{
return false;
}
if (ParamInfos.Count != other.ParamInfos.Count)
{
return false;
}
for(int i = 0; i < ParamInfos.Count; i++)
{
if (!ParamInfos[i].Type.Equals(other.ParamInfos[i].Type))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + ReturnInfo.Type.GetHashCode();
foreach(var p in ParamInfos)
{
hash = hash * 23 + p.Type.GetHashCode();
}
return hash;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbe63fb39b5bdcc448f3589b475fb5d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public class ParamInfo
{
public TypeInfo Type { get; set; }
public int Index { get; set; }
//public bool IsNative2ManagedByAddress => Type.PorType >= ParamOrReturnType.STRUCT_NOT_PASS_AS_VALUE;
public bool IsPassToManagedByAddress => Type.GetParamSlotNum() > 1;
public bool IsPassToNativeByAddress => Type.PorType == ParamOrReturnType.STRUCTURE_AS_REF_PARAM;
public string Native2ManagedParamValue(PlatformABI canv)
{
return IsPassToManagedByAddress ? $"(uint64_t)&__arg{Index}" : $"*(uint64_t*)&__arg{Index}";
}
public string Managed2NativeParamValue(PlatformABI canv)
{
return IsPassToNativeByAddress ? $"(uint64_t)(localVarBase+argVarIndexs[{Index}])" : $"*({Type.GetTypeName()}*)(localVarBase+argVarIndexs[{Index}])";
}
}
public class ReturnInfo
{
public TypeInfo Type { get; set; }
public bool IsVoid => Type.PorType == ParamOrReturnType.VOID;
public int GetParamSlotNum(PlatformABI canv)
{
return Type.GetParamSlotNum();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c04e163336b93af44b4b565d0ab195e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public enum ParamOrReturnType
{
VOID,
I1_U1,
//U1,
I2_U2,
//U2,
I4_U4,
I8_U8,
//I_U,
R4,
R8,
ARM64_HFA_FLOAT_2,
VALUE_TYPE_SIZE_LESS_EQUAL_8,
I16, // 8 < size <= 16
STRUCT_NOT_PASS_AS_VALUE, // struct pass not as value
STRUCTURE_AS_REF_PARAM, // size > 16
ARM64_HFA_FLOAT_3,
ARM64_HFA_FLOAT_4,
ARM64_HFA_DOUBLE_2,
ARM64_HFA_DOUBLE_3,
ARM64_HFA_DOUBLE_4,
ARM64_HVA_8,
ARM64_HVA_16,
STRUCTURE_ALIGN1, // size > 16
STRUCTURE_ALIGN2,
STRUCTURE_ALIGN4,
STRUCTURE_ALIGN8,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7886905937fafb64999dc4e358565982
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
namespace HybridCLR.Editor.Generators.MethodBridge
{
public enum PlatformABI
{
Universal32,
Universal64,
Arm64,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: deee8d06a7f05bf4d9ab4b78f0a8a47c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.Generators.MethodBridge
{
internal abstract class PlatformAdaptorBase : IPlatformAdaptor
{
public abstract bool IsArch32 { get; }
public abstract TypeInfo PointerType { get; }
protected abstract Dictionary<Type, TypeInfo> CacheTypes { get; }
protected abstract TypeInfo CreateValueType(Type type, bool returnValue);
public abstract void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines);
public abstract void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines);
public abstract void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> outputLines);
private static Dictionary<Type, (int, int)> _typeSizeCache64 = new Dictionary<Type, (int, int)>();
private static Dictionary<Type, (int, int)> _typeSizeCache32 = new Dictionary<Type, (int, int)>();
private static ValueTypeSizeAligmentCalculator s_calculator64 = new ValueTypeSizeAligmentCalculator(false);
private static ValueTypeSizeAligmentCalculator s_calculator32 = new ValueTypeSizeAligmentCalculator(true);
public static (int Size, int Aligment) ComputeSizeAndAligmentOfArch64(Type t)
{
if (_typeSizeCache64.TryGetValue(t, out var sizeAndAligment))
{
return sizeAndAligment;
}
// all this just to invoke one opcode with no arguments!
var method = new DynamicMethod("ComputeSizeOfImpl", typeof(int), Type.EmptyTypes, typeof(PlatformAdaptorBase), false);
var gen = method.GetILGenerator();
gen.Emit(OpCodes.Sizeof, t);
gen.Emit(OpCodes.Ret);
int clrSize = ((Func<int>)method.CreateDelegate(typeof(Func<int>)))();
sizeAndAligment = s_calculator64.SizeAndAligmentOf(t);
int customSize = sizeAndAligment.Item1;
if (customSize != clrSize)
{
s_calculator64.SizeAndAligmentOf(t);
Debug.LogError($"type:{t} size calculate error. HybridCLR Comput:{sizeAndAligment} CLR:{clrSize}");
}
_typeSizeCache64.Add(t, sizeAndAligment);
return sizeAndAligment;
}
protected static (int Size, int Aligment) ComputeSizeAndAligmentOfArch32(Type t)
{
if (_typeSizeCache32.TryGetValue(t, out var sa))
{
return sa;
}
// all this just to invoke one opcode with no arguments!
sa = s_calculator32.SizeAndAligmentOf(t);
_typeSizeCache32.Add(t, sa);
return sa;
}
public TypeInfo CreateTypeInfo(Type type, bool returnValue)
{
if (type.IsByRef)
{
return PointerType;
}
if (type == typeof(void))
{
return TypeInfo.s_void;
}
if (!type.IsValueType)
{
return PointerType;
}
if (CacheTypes.TryGetValue(type, out var cache))
{
return cache;
}
if (type.IsEnum)
{
return CreateTypeInfo(type.GetEnumUnderlyingType(), returnValue);
}
var ti = CreateValueType(type, returnValue);
// s_typeInfoCaches.Add(type, ti);
return ti;
}
protected static TypeInfo CreateGeneralValueType(Type type, int size, int aligment)
{
Debug.Assert(size % aligment == 0);
switch (aligment)
{
case 1: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN1, size);
case 2: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN2, size);
case 4: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN4, size);
case 8: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN8, size);
default: throw new NotSupportedException($"type:{type} not support aligment:{aligment}");
}
}
public void GenerateManaged2NativeStub(List<MethodBridgeSig> methods, List<string> lines)
{
lines.Add($@"
Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] =
{{
");
foreach (var method in methods)
{
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", __M2N_{method.CreateInvokeSigName()}}},");
}
lines.Add($"\t{{nullptr, nullptr}},");
lines.Add("};");
}
public void GenerateNative2ManagedStub(List<MethodBridgeSig> methods, List<string> lines)
{
lines.Add($@"
Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] =
{{
");
foreach (var method in methods)
{
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_{method.CreateInvokeSigName()}}},");
}
lines.Add($"\t{{nullptr, nullptr}},");
lines.Add("};");
}
public void GenerateAdjustThunkStub(List<MethodBridgeSig> methods, List<string> lines)
{
lines.Add($@"
NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] =
{{
");
foreach (var method in methods)
{
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_AdjustorThunk_{method.CreateCallSigName()}}},");
}
lines.Add($"\t{{nullptr, nullptr}},");
lines.Add("};");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ebf14107fd82b364cb7d61276d444829
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.Generators.MethodBridge
{
internal class PlatformAdaptor_Arm64 : PlatformAdaptorBase
{
private static readonly Dictionary<Type, TypeInfo> s_typeInfoCaches = new Dictionary<Type, TypeInfo>()
{
{ typeof(void), new TypeInfo(typeof(void), ParamOrReturnType.VOID)},
{ typeof(bool), new TypeInfo(typeof(bool), ParamOrReturnType.I1_U1)},
{ typeof(byte), new TypeInfo(typeof(byte), ParamOrReturnType.I1_U1)},
{ typeof(sbyte), new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1) },
{ typeof(short), new TypeInfo(typeof(short), ParamOrReturnType.I2_U2) },
{ typeof(ushort), new TypeInfo(typeof(ushort), ParamOrReturnType.I2_U2) },
{ typeof(char), new TypeInfo(typeof(char), ParamOrReturnType.I2_U2) },
{ typeof(int), new TypeInfo(typeof(int), ParamOrReturnType.I4_U4) },
{ typeof(uint), new TypeInfo(typeof(uint), ParamOrReturnType.I4_U4) },
{ typeof(long), new TypeInfo(typeof(long), ParamOrReturnType.I8_U8) },
{ typeof(ulong), new TypeInfo(typeof(ulong), ParamOrReturnType.I8_U8)},
{ typeof(float), new TypeInfo(typeof(float), ParamOrReturnType.R4)},
{ typeof(double), new TypeInfo(typeof(double), ParamOrReturnType.R8)},
{ typeof(IntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
{ typeof(UIntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
{ typeof(Vector2), new TypeInfo(typeof(Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
{ typeof(Vector3), new TypeInfo(typeof(Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
{ typeof(Vector4), new TypeInfo(typeof(Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
{ typeof(System.Numerics.Vector2), new TypeInfo(typeof(System.Numerics.Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
{ typeof(System.Numerics.Vector3), new TypeInfo(typeof(System.Numerics.Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
{ typeof(System.Numerics.Vector4), new TypeInfo(typeof(System.Numerics.Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
};
public PlatformABI CallConventionType { get; } = PlatformABI.Universal64;
public override bool IsArch32 => false;
public override TypeInfo PointerType => TypeInfo.s_i8u8;
protected override Dictionary<Type, TypeInfo> CacheTypes => s_typeInfoCaches;
public class HFATypeInfo
{
public Type Type { get; set; }
public int Count { get; set; }
}
private static bool IsNotHFAFastCheck(int typeSize)
{
return typeSize != 8 && typeSize != 12 && typeSize != 16 && typeSize != 24 && typeSize != 32;
}
private static bool ComputHFATypeInfo0(Type type, HFATypeInfo typeInfo)
{
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in fields)
{
Type ftype = field.FieldType;
if (ftype != typeof(float) && ftype != typeof(double))
{
if (!ftype.IsPrimitive && ftype.IsValueType)
{
if (!ComputHFATypeInfo0(ftype, typeInfo))
{
return false;
}
}
else
{
return false;
}
}
else if (ftype == typeInfo.Type || typeInfo.Type == null)
{
typeInfo.Type = ftype;
++typeInfo.Count;
}
else
{
return false;
}
}
return typeInfo.Count <= 4;
}
public static bool ComputHFATypeInfo(Type type, int typeSize, out HFATypeInfo typeInfo)
{
typeInfo = new HFATypeInfo();
if (IsNotHFAFastCheck(typeSize))
{
return false;
}
bool ok = ComputHFATypeInfo0(type, typeInfo);
if (ok && typeInfo.Count >= 2 && typeInfo.Count <= 4)
{
int fieldSize = typeInfo.Type == typeof(float) ? 4 : 8;
return typeSize == fieldSize * typeInfo.Count;
}
return false;
}
protected override TypeInfo CreateValueType(Type type, bool returnValue)
{
(int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch64(type);
if (ComputHFATypeInfo(type, typeSize, out HFATypeInfo hfaTypeInfo))
{
if (hfaTypeInfo.Type == typeof(float))
{
switch (hfaTypeInfo.Count)
{
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_2);
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_3);
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_4);
default: throw new NotSupportedException();
}
}
else
{
Debug.Assert(hfaTypeInfo.Type == typeof(double));
switch (hfaTypeInfo.Count)
{
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_2);
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_3);
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_4);
default: throw new NotSupportedException();
}
}
}
else
{
// 64位下结构体内存对齐规则是一样的
return CreateArm64GeneralValueType(type, typeSize,returnValue);
}
}
private TypeInfo CreateArm64GeneralValueType(Type type, int typeSize, bool returnValue)
{
if (typeSize <= 8)
{
return TypeInfo.s_i8u8;
}
if (typeSize <= 16)
{
return TypeInfo.s_i16;
}
if (returnValue)
{
return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN1, typeSize);
}
return TypeInfo.s_ref;
}
public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" }));
lines.Add($@"
static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
{{
typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr});
}}
");
}
public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
lines.Add($@"
static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr})
{{
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }};
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
Interpreter::Execute(method, args, ret);
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
}}
");
}
public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
lines.Add($@"
static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr})
{{
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }};
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
Interpreter::Execute(method, args, ret);
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
}}
");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bff17a6e8ee060c4eb9ac97fcf30bf78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.Generators.MethodBridge
{
internal class PlatformAdaptor_Universal32 : PlatformAdaptorBase
{
private static readonly Dictionary<Type, TypeInfo> s_typeInfoCaches = new Dictionary<Type, TypeInfo>()
{
{ typeof(void), new TypeInfo(typeof(void), ParamOrReturnType.VOID)},
{ typeof(bool), new TypeInfo(typeof(bool), ParamOrReturnType.I1_U1)},
{ typeof(byte), new TypeInfo(typeof(byte), ParamOrReturnType.I1_U1)},
{ typeof(sbyte), new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1) },
{ typeof(short), new TypeInfo(typeof(short), ParamOrReturnType.I2_U2) },
{ typeof(ushort), new TypeInfo(typeof(ushort), ParamOrReturnType.I2_U2) },
{ typeof(char), new TypeInfo(typeof(char), ParamOrReturnType.I2_U2) },
{ typeof(int), new TypeInfo(typeof(int), ParamOrReturnType.I4_U4) },
{ typeof(uint), new TypeInfo(typeof(uint), ParamOrReturnType.I4_U4) },
{ typeof(long), new TypeInfo(typeof(long), ParamOrReturnType.I8_U8) },
{ typeof(ulong), new TypeInfo(typeof(ulong), ParamOrReturnType.I8_U8)},
{ typeof(float), new TypeInfo(typeof(float), ParamOrReturnType.R4)},
{ typeof(double), new TypeInfo(typeof(double), ParamOrReturnType.R8)},
{ typeof(IntPtr), new TypeInfo(null, ParamOrReturnType.I4_U4)},
{ typeof(UIntPtr), new TypeInfo(null, ParamOrReturnType.I4_U4)},
};
public PlatformABI CallConventionType { get; } = PlatformABI.Universal32;
public override bool IsArch32 => true;
public override TypeInfo PointerType => TypeInfo.s_i4u4;
protected override Dictionary<Type, TypeInfo> CacheTypes => s_typeInfoCaches;
protected override TypeInfo CreateValueType(Type type, bool returnValue)
{
(int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch32(type);
int actualAliment = typeAligment <= 4 ? 1 : 8;
return CreateGeneralValueType(type, typeSize, actualAliment);
}
public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines)
{
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" }));
lines.Add($@"
static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
{{
typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr});
}}
");
}
public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
lines.Add($@"
static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr})
{{
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }};
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
Interpreter::Execute(method, args, ret);
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
}}
");
}
public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
lines.Add($@"
static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr})
{{
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }};
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
Interpreter::Execute(method, args, ret);
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
}}
");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb741900113b22443a2054ddba6b131b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.Generators.MethodBridge
{
internal class PlatformAdaptor_Universal64 : PlatformAdaptorBase
{
private static readonly Dictionary<Type, TypeInfo> s_typeInfoCaches = new Dictionary<Type, TypeInfo>()
{
{ typeof(void), new TypeInfo(typeof(void), ParamOrReturnType.VOID)},
{ typeof(bool), new TypeInfo(typeof(bool), ParamOrReturnType.I1_U1)},
{ typeof(byte), new TypeInfo(typeof(byte), ParamOrReturnType.I1_U1)},
{ typeof(sbyte), new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1) },
{ typeof(short), new TypeInfo(typeof(short), ParamOrReturnType.I2_U2) },
{ typeof(ushort), new TypeInfo(typeof(ushort), ParamOrReturnType.I2_U2) },
{ typeof(char), new TypeInfo(typeof(char), ParamOrReturnType.I2_U2) },
{ typeof(int), new TypeInfo(typeof(int), ParamOrReturnType.I4_U4) },
{ typeof(uint), new TypeInfo(typeof(uint), ParamOrReturnType.I4_U4) },
{ typeof(long), new TypeInfo(typeof(long), ParamOrReturnType.I8_U8) },
{ typeof(ulong), new TypeInfo(typeof(ulong), ParamOrReturnType.I8_U8)},
{ typeof(float), new TypeInfo(typeof(float), ParamOrReturnType.R4)},
{ typeof(double), new TypeInfo(typeof(double), ParamOrReturnType.R8)},
{ typeof(IntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
{ typeof(UIntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
{ typeof(Vector2), new TypeInfo(typeof(Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
{ typeof(Vector3), new TypeInfo(typeof(Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
{ typeof(Vector4), new TypeInfo(typeof(Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
{ typeof(System.Numerics.Vector2), new TypeInfo(typeof(System.Numerics.Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
{ typeof(System.Numerics.Vector3), new TypeInfo(typeof(System.Numerics.Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
{ typeof(System.Numerics.Vector4), new TypeInfo(typeof(System.Numerics.Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
};
public PlatformABI CallConventionType { get; } = PlatformABI.Universal64;
public override bool IsArch32 => false;
public override TypeInfo PointerType => TypeInfo.s_i8u8;
protected override Dictionary<Type, TypeInfo> CacheTypes => s_typeInfoCaches;
public class HFATypeInfo
{
public Type Type { get; set; }
public int Count { get; set; }
}
private static bool IsNotHFAFastCheck(int typeSize)
{
return typeSize != 8 && typeSize != 12 && typeSize != 16 && typeSize != 24 && typeSize != 32;
}
private static bool ComputHFATypeInfo0(Type type, HFATypeInfo typeInfo)
{
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in fields)
{
Type ftype = field.FieldType;
if (ftype != typeof(float) && ftype != typeof(double))
{
if (!ftype.IsPrimitive && ftype.IsValueType)
{
if (!ComputHFATypeInfo0(ftype, typeInfo))
{
return false;
}
}
else
{
return false;
}
}
else if (ftype == typeInfo.Type || typeInfo.Type == null)
{
typeInfo.Type = ftype;
++typeInfo.Count;
}
else
{
return false;
}
}
return typeInfo.Count <= 4;
}
public static bool ComputHFATypeInfo(Type type, int typeSize, out HFATypeInfo typeInfo)
{
typeInfo = new HFATypeInfo();
if (IsNotHFAFastCheck(typeSize))
{
return false;
}
bool ok = ComputHFATypeInfo0(type, typeInfo);
if (ok && typeInfo.Count >= 2 && typeInfo.Count <= 4)
{
int fieldSize = typeInfo.Type == typeof(float) ? 4 : 8;
return typeSize == fieldSize * typeInfo.Count;
}
return false;
}
protected override TypeInfo CreateValueType(Type type, bool returnValue)
{
(int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch64(type);
if (ComputHFATypeInfo(type, typeSize, out HFATypeInfo hfaTypeInfo))
{
if (hfaTypeInfo.Type == typeof(float))
{
switch (hfaTypeInfo.Count)
{
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_2);
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_3);
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_4);
default: throw new NotSupportedException();
}
}
else
{
Debug.Assert(hfaTypeInfo.Type == typeof(double));
switch (hfaTypeInfo.Count)
{
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_2);
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_3);
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_4);
default: throw new NotSupportedException();
}
}
}
else
{
// 64位下结构体内存对齐规则是一样的
return CreateGeneralValueType(type, typeSize, 1 /*typeAligment*/);
}
}
public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
string paramTypeListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()}").Concat(new string[] { "const MethodInfo*" })); ;
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" }));
lines.Add($@"
static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
{{
typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr});
}}
");
}
public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
lines.Add($@"
static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr})
{{
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }};
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
Interpreter::Execute(method, args, ret);
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
}}
");
}
public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> lines)
{
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
lines.Add($@"
static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr})
{{
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }};
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
Interpreter::Execute(method, args, ret);
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
}}
");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26324be9505c9f54996bcbb62ba49132
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators.MethodBridge
{
[AttributeUsage(AttributeTargets.Method)]
public class SignatureProviderAttribute : Attribute
{
public List<PlatformABI> Platforms { get; }
public SignatureProviderAttribute(params PlatformABI[] platforms)
{
Platforms = new List<PlatformABI>(platforms);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50c5ec2d43d3fda4a8b8cfa75cb37a74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public class TypeInfo : IEquatable<TypeInfo>
{
public static readonly TypeInfo s_void = new TypeInfo(typeof(void), ParamOrReturnType.VOID);
public static readonly TypeInfo s_i4u4 = new TypeInfo(null, ParamOrReturnType.I4_U4);
public static readonly TypeInfo s_i8u8 = new TypeInfo(null, ParamOrReturnType.I8_U8);
public static readonly TypeInfo s_i16 = new TypeInfo(null, ParamOrReturnType.I16);
public static readonly TypeInfo s_ref = new TypeInfo(null, ParamOrReturnType.STRUCTURE_AS_REF_PARAM);
public TypeInfo(Type type, ParamOrReturnType portype)
{
this.Type = type;
PorType = portype;
Size = 0;
}
public TypeInfo(Type type, ParamOrReturnType portype, int size)
{
this.Type = type;
PorType = portype;
Size = size;
}
public Type Type { get; }
public ParamOrReturnType PorType { get; }
public int Size { get; }
public bool Equals(TypeInfo other)
{
return PorType == other.PorType && Size == other.Size;
}
public override bool Equals(object obj)
{
return Equals((TypeInfo)obj);
}
public override int GetHashCode()
{
return (int)PorType * 23 + Size;
}
public string CreateSigName()
{
switch (PorType)
{
case ParamOrReturnType.VOID: return "v";
case ParamOrReturnType.I1_U1: return "i1";
case ParamOrReturnType.I2_U2: return "i2";
case ParamOrReturnType.I4_U4: return "i4";
case ParamOrReturnType.I8_U8: return "i8";
case ParamOrReturnType.R4: return "r4";
case ParamOrReturnType.R8: return "r8";
case ParamOrReturnType.I16: return "i16";
case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return "sr";
case ParamOrReturnType.ARM64_HFA_FLOAT_2: return "vf2";
case ParamOrReturnType.ARM64_HFA_FLOAT_3: return "vf3";
case ParamOrReturnType.ARM64_HFA_FLOAT_4: return "vf4";
case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return "vd2";
case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return "vd3";
case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return "vd4";
case ParamOrReturnType.STRUCTURE_ALIGN1: return "S" + Size;
case ParamOrReturnType.STRUCTURE_ALIGN2: return "A" + Size;
case ParamOrReturnType.STRUCTURE_ALIGN4: return "B" + Size;
case ParamOrReturnType.STRUCTURE_ALIGN8: return "C" + Size;
default: throw new NotSupportedException(PorType.ToString());
};
}
public string GetTypeName()
{
switch (PorType)
{
case ParamOrReturnType.VOID: return "void";
case ParamOrReturnType.I1_U1: return "int8_t";
case ParamOrReturnType.I2_U2: return "int16_t";
case ParamOrReturnType.I4_U4: return "int32_t";
case ParamOrReturnType.I8_U8: return "int64_t";
case ParamOrReturnType.R4: return "float";
case ParamOrReturnType.R8: return "double";
case ParamOrReturnType.I16: return "ValueTypeSize16";
case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return "uint64_t";
case ParamOrReturnType.ARM64_HFA_FLOAT_2: return "HtVector2f";
case ParamOrReturnType.ARM64_HFA_FLOAT_3: return "HtVector3f";
case ParamOrReturnType.ARM64_HFA_FLOAT_4: return "HtVector4f";
case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return "HtVector2d";
case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return "HtVector3d";
case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return "HtVector4d";
case ParamOrReturnType.STRUCTURE_ALIGN1: return $"ValueTypeSize<{Size}>";
case ParamOrReturnType.STRUCTURE_ALIGN2: return $"ValueTypeSizeAlign2<{Size}>";
case ParamOrReturnType.STRUCTURE_ALIGN4: return $"ValueTypeSizeAlign4<{Size}>";
case ParamOrReturnType.STRUCTURE_ALIGN8: return $"ValueTypeSizeAlign8<{Size}>";
default: throw new NotImplementedException(PorType.ToString());
};
}
public int GetParamSlotNum()
{
switch (PorType)
{
case ParamOrReturnType.VOID: return 0;
case ParamOrReturnType.I16: return 2;
case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return 1;
case ParamOrReturnType.ARM64_HFA_FLOAT_3: return 2;
case ParamOrReturnType.ARM64_HFA_FLOAT_4: return 2;
case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return 2;
case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return 3;
case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return 4;
case ParamOrReturnType.ARM64_HVA_8:
case ParamOrReturnType.ARM64_HVA_16: throw new NotSupportedException();
case ParamOrReturnType.STRUCTURE_ALIGN1:
case ParamOrReturnType.STRUCTURE_ALIGN2:
case ParamOrReturnType.STRUCTURE_ALIGN4:
case ParamOrReturnType.STRUCTURE_ALIGN8: return (Size + 7) / 8;
default:
{
Debug.Assert(PorType < ParamOrReturnType.STRUCT_NOT_PASS_AS_VALUE);
Debug.Assert(Size <= 8);
return 1;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5afd7344483678a4abcb56158fd1442d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators.MethodBridge
{
public class ValueTypeSizeAligmentCalculator
{
private static Dictionary<string, int> s_primitives = new Dictionary<string, int>(14) {
{ "Byte", 1 },
{ "SByte", 1 },
{ "Boolean", 1 },
{ "Int16", 2 },
{ "UInt16", 2 },
{ "Char", 2 },
{ "Int32", 4 },
{ "UInt32", 4 },
{ "Single", 4 },
{ "Int64", 8 },
{ "UInt64", 8 },
{ "Double", 8 },
//{ "IntPtr", _referenceSize }, // so rule return the same results
//{ "UIntPtr", _referenceSize }, // on 32 and 64 bits architectures
};
public ValueTypeSizeAligmentCalculator(bool arch32)
{
_referenceSize = arch32 ? 4 : 8;
}
// actually we should use IntPtr.Size but that would make the rule
// return different results on 64 bits systems
private readonly int _referenceSize;
// Note: Needs to be public since this is being tested by our unit tests
private static bool IsIgnoreField(FieldInfo field)
{
var ignoreAttr = field.GetCustomAttributes().Where(a => a.GetType().Name == "IgnoreAttribute").FirstOrDefault();
if (ignoreAttr == null)
{
return false;
}
var p = ignoreAttr.GetType().GetProperty("DoesNotContributeToSize");
return (bool)p.GetValue(ignoreAttr);
}
private (int Size, int Aligment) SizeAndAligmentOfStruct(Type type)
{
int totalSize = 0;
int packAligment = 8;
int maxAligment = 1;
StructLayoutAttribute sa = type.StructLayoutAttribute;
if (sa != null && sa.Pack > 0)
{
packAligment = sa.Pack;
}
bool useSLSize = true;
foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
// add size of the type
var (fs, fa) = SizeAndAligmentOf(field.FieldType);
fa = Math.Min(fa, packAligment);
if (fa > maxAligment)
{
maxAligment = fa;
}
if (IsIgnoreField(field))
{
continue;
}
if (sa != null && sa.Value == LayoutKind.Explicit)
{
int offset = field.GetCustomAttribute<FieldOffsetAttribute>().Value;
totalSize = Math.Max(totalSize, offset + fs);
if (offset > sa.Size)
{
useSLSize = false;
}
}
else
{
if (totalSize % fa != 0)
{
totalSize = (totalSize + fa - 1) / fa * fa;
}
totalSize += fs;
if (sa != null && sa.Value == LayoutKind.Sequential && totalSize > sa.Size)
{
useSLSize = false;
}
}
}
if (totalSize == 0)
{
totalSize = maxAligment;
}
if (totalSize % maxAligment != 0)
{
totalSize = (totalSize + maxAligment - 1) / maxAligment * maxAligment;
}
if (sa != null && sa.Size > 0)
{
if (/*sa.Value == LayoutKind.Explicit &&*/ useSLSize)
{
totalSize = sa.Size;
while(totalSize % maxAligment != 0)
{
maxAligment /= 2;
}
}
}
return (totalSize, maxAligment);
}
public (int Size, int Aligment) SizeAndAligmentOf(Type type)
{
if (type.IsByRef || !type.IsValueType || type.IsArray)
return (_referenceSize, _referenceSize);
// list based on Type.IsPrimitive
if (type.Namespace == "System")
{
if (s_primitives.TryGetValue(type.Name, out var size))
{
return (size, size);
}
if (type.Name == "IntPtr" || type.Name == "UIntPtr")
{
return (_referenceSize, _referenceSize);
}
}
if (type.IsEnum)
return SizeAndAligmentOf(type.GetEnumUnderlyingType());
return SizeAndAligmentOfStruct(type);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49b3dbcebadb1b543a42e01afec07ed1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Generators
{
public static class TemplateUtil
{
public static string EscapeIntegerName(int i)
{
return i >= 0 ? i.ToString() : "minus" + (-i);
}
public static string ReplaceRegion(string resultText, string region, string replaceContent)
{
int startIndex = resultText.IndexOf("//!!!{{" + region);
if (startIndex == -1)
{
throw new Exception($"region:{region} start not find");
}
int endIndex = resultText.IndexOf("//!!!}}" + region);
if (endIndex == -1)
{
throw new Exception($"region:{region} end not find");
}
int replaceStart = resultText.IndexOf('\n', startIndex);
int replaceEnd = resultText.LastIndexOf('\n', endIndex);
if (replaceStart == -1 || replaceEnd == -1)
{
throw new Exception($"region:{region} not find");
}
resultText = resultText.Substring(0, replaceStart) + "\n" + replaceContent + "\n" + resultText.Substring(replaceEnd);
return resultText;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32ec2cc1cf5c468468308fa6308fba19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3a1563e7820e5b648a6a6691677f564c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
#include "MethodBridge.h"
#include <codegen/il2cpp-codegen-metadata.h>
#include "vm/ClassInlines.h"
#include "vm/Object.h"
#include "vm/Class.h"
#include "../metadata/MetadataModule.h"
#include "../metadata/MetadataUtil.h"
#include "Interpreter.h"
#include "MemoryUtil.h"
#include "InstrinctDef.h"
using namespace hybridclr::interpreter;
using hybridclr::GetInterpreterDirectlyCallMethodPointer;
#if HYBRIDCLR_ABI_ARM_64
//!!!{{INVOKE_STUB
//!!!}}INVOKE_STUB
#endif

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 7e694feed8374f94380fe48bb70e9be1
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
#include "MethodBridge.h"
#include <codegen/il2cpp-codegen-metadata.h>
#include "vm/ClassInlines.h"
#include "vm/Object.h"
#include "vm/Class.h"
#include "../metadata/MetadataModule.h"
#include "../metadata/MetadataUtil.h"
#include "Interpreter.h"
#include "MemoryUtil.h"
#include "InstrinctDef.h"
using namespace hybridclr::interpreter;
using hybridclr::GetInterpreterDirectlyCallMethodPointer;
#if HYBRIDCLR_ABI_UNIVERSAL_32
//!!!{{INVOKE_STUB
//!!!}}INVOKE_STUB
#endif

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 27135a158cd4b5049966f6f46d15ab10
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
#include "MethodBridge.h"
#include <codegen/il2cpp-codegen-metadata.h>
#include "vm/ClassInlines.h"
#include "vm/Object.h"
#include "vm/Class.h"
#include "../metadata/MetadataModule.h"
#include "../metadata/MetadataUtil.h"
#include "Interpreter.h"
#include "MemoryUtil.h"
#include "InstrinctDef.h"
using namespace hybridclr::interpreter;
using hybridclr::GetInterpreterDirectlyCallMethodPointer;
#if HYBRIDCLR_ABI_UNIVERSAL_64
//!!!{{INVOKE_STUB
//!!!}}INVOKE_STUB
#endif

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: ca76fdf59c873e441aa5043ad00d4537
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a4a3d499c10d66f43af10a859e276a8f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,224 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Debug = UnityEngine.Debug;
namespace HybridCLR.Editor.Installer
{
public enum InstallErrorCode
{
Ok,
Il2CppInstallPathNotMatchIl2CppBranch,
Il2CppInstallPathNotExists,
NotIl2CppPath,
}
public partial class InstallerController
{
private string m_Il2CppInstallDirectory;
public string Il2CppInstallDirectory
{
get
{
return m_Il2CppInstallDirectory;
}
set
{
m_Il2CppInstallDirectory = value?.Replace('\\', '/');
if (!string.IsNullOrEmpty(m_Il2CppInstallDirectory))
{
EditorPrefs.SetString("UnityInstallDirectory", m_Il2CppInstallDirectory);
}
}
}
private string GetIl2CppPlusBranchByUnityVersion(string unityVersion)
{
if (unityVersion.Contains("2019."))
{
return "2019.4.40";
}
if (unityVersion.Contains("2020."))
{
return "2020.3.33";
}
if (unityVersion.Contains("2021."))
{
return "2021.3.1";
}
return "not support";
}
public string Il2CppBranch => GetIl2CppPlusBranchByUnityVersion(Application.unityVersion);
public string InitLocalIl2CppBatFile => Application.dataPath + "/../HybridCLRData/init_local_il2cpp_data.bat";
public string InitLocalIl2CppBashFile => Application.dataPath + "/../HybridCLRData/init_local_il2cpp_data.sh";
public InstallerController()
{
PrepareIl2CppInstallPath();
}
void PrepareIl2CppInstallPath()
{
#if UNITY_EDITOR_OSX
m_Il2CppInstallDirectory = EditorPrefs.GetString("Il2CppInstallDirectory");
if (CheckValidIl2CppInstallDirectory(Il2CppBranch, m_Il2CppInstallDirectory) == InstallErrorCode.Ok)
{
return;
}
var il2cppBranch = Il2CppBranch;
var curAppInstallPath = EditorApplication.applicationPath;
if (curAppInstallPath.Contains(il2cppBranch))
{
Il2CppInstallDirectory = $"{curAppInstallPath}/Contents/il2cpp";
return;
}
string unityHubRootDir = Directory.GetParent(curAppInstallPath).Parent.Parent.ToString();
foreach (var unityInstallDir in Directory.GetDirectories(unityHubRootDir, "*", SearchOption.TopDirectoryOnly))
{
Debug.Log("nity install dir:" + unityInstallDir);
if (unityInstallDir.Contains(il2cppBranch))
{
Il2CppInstallDirectory = $"{unityInstallDir}/Unity.app/Contents/il2cpp";
return;
}
}
Il2CppInstallDirectory = $"{curAppInstallPath}/Contents/il2cpp";
#else
m_Il2CppInstallDirectory = EditorPrefs.GetString("Il2CppInstallDirectory");
if (CheckValidIl2CppInstallDirectory(Il2CppBranch, m_Il2CppInstallDirectory) == InstallErrorCode.Ok)
{
return;
}
var il2cppBranch = Il2CppBranch;
var curAppInstallPath = EditorApplication.applicationPath;
if (curAppInstallPath.Contains(il2cppBranch))
{
Il2CppInstallDirectory = $"{Directory.GetParent(curAppInstallPath)}/Data/il2cpp";
return;
}
string unityHubRootDir = Directory.GetParent(curAppInstallPath).Parent.Parent.ToString();
Debug.Log("unity hub root dir:" + unityHubRootDir);
foreach (var unityInstallDir in Directory.GetDirectories(unityHubRootDir, "*", SearchOption.TopDirectoryOnly))
{
Debug.Log("nity install dir:" + unityInstallDir);
if (unityInstallDir.Contains(il2cppBranch))
{
Il2CppInstallDirectory = $"{unityInstallDir}/Editor/Data/il2cpp";
return;
}
}
Il2CppInstallDirectory = $"{Directory.GetParent(curAppInstallPath)}/Data/il2cpp";
#endif
}
public void InitHybridCLR(string il2cppBranch, string il2cppInstallPath)
{
if (CheckValidIl2CppInstallDirectory(il2cppBranch, il2cppInstallPath) != InstallErrorCode.Ok)
{
Debug.LogError($"请正确设置 il2cpp 安装目录");
return;
}
if (Application.platform == RuntimePlatform.WindowsEditor)
{
RunInitLocalIl2CppDataBat(il2cppBranch, il2cppInstallPath);
}
else
{
RunInitLocalIl2CppDataBash(il2cppBranch, il2cppInstallPath);
}
}
public bool HasInstalledHybridCLR()
{
return Directory.Exists($"{BuildConfig.LocalIl2CppDir}/libil2cpp/hybridclr");
}
public InstallErrorCode CheckValidIl2CppInstallDirectory(string il2cppBranch, string installDir)
{
installDir = installDir.Replace('\\', '/');
if (!Directory.Exists(installDir))
{
return InstallErrorCode.Il2CppInstallPathNotExists;
}
if (!installDir.Contains(il2cppBranch))
{
return InstallErrorCode.Il2CppInstallPathNotMatchIl2CppBranch;
}
if (!installDir.EndsWith("/il2cpp"))
{
return InstallErrorCode.NotIl2CppPath;
}
return InstallErrorCode.Ok;
}
public bool IsUnity2019(string branch)
{
return branch.Contains("2019.");
}
private void RunInitLocalIl2CppDataBat(string il2cppBranch, string il2cppInstallPath)
{
using (Process p = new Process())
{
p.StartInfo.WorkingDirectory = BuildConfig.HybridCLRDataDir;
p.StartInfo.FileName = InitLocalIl2CppBatFile;
p.StartInfo.UseShellExecute = true;
p.StartInfo.Arguments = $"{il2cppBranch} \"{il2cppInstallPath}\"";
p.Start();
p.WaitForExit();
if (IsUnity2019(il2cppBranch))
{
string srcIl2CppDll = $"{BuildConfig.HybridCLRDataDir}/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP.dll";
string dstIl2CppDll = $"{BuildConfig.LocalIl2CppDir}/build/deploy/net471/Unity.IL2CPP.dll";
File.Copy(srcIl2CppDll, dstIl2CppDll, true);
Debug.Log($"copy {srcIl2CppDll} => {dstIl2CppDll}");
}
if (p.ExitCode == 0 && HasInstalledHybridCLR())
{
Debug.Log("HybirdCLR 安装成功");
}
}
}
private void RunInitLocalIl2CppDataBash(string il2cppBranch, string il2cppInstallPath)
{
using (Process p = new Process())
{
p.StartInfo.WorkingDirectory = Application.dataPath + "/../HybridCLRData";
p.StartInfo.FileName = "/bin/bash";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = $"init_local_il2cpp_data.sh {il2cppBranch} '{il2cppInstallPath}'";
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
Debug.Log(output);
p.WaitForExit();
if (HasInstalledHybridCLR())
{
Debug.Log("安装成功!!!");
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44c8627d126b30d4e9560b1f738264ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor ;
using UnityEngine;
namespace HybridCLR.Editor.Installer
{
public class InstallerWindow : EditorWindow
{
private InstallerController m_Controller;
[MenuItem("HybridCLR/Installer...", false, 0)]
private static void Open()
{
InstallerWindow window = GetWindow<InstallerWindow>("HybridCLR Installer", true);
window.minSize = new Vector2(800f, 500f);
}
private void OnEnable()
{
m_Controller = new InstallerController();
}
private void OnGUI()
{
GUI.enabled = true;
GUILayout.Space(10f);
EditorGUILayout.LabelField("=======================说明====================");
EditorGUILayout.LabelField(
$"你所在项目的Unity版本可以与il2cpp_plus版本:{m_Controller.Il2CppBranch} 不一样。\n"
+ $"由于安装HybridCLR时需要从il2cpp_plus对应版本而不是你项目版本拷贝il2cpp目录\n"
+ $"你必须同时也安装相应版本 {m_Controller.Il2CppBranch},否则无法安装", EditorStyles.wordWrappedLabel);
EditorGUILayout.LabelField("==============================================");
GUILayout.Space(10f);
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField($"安装状态:{(m_Controller.HasInstalledHybridCLR() ? "" : "")}", EditorStyles.boldLabel);
GUILayout.Space(5f);
EditorGUILayout.LabelField($"当前Unity版本: {Application.unityVersion}匹配的il2cpp_plus分支: {m_Controller.Il2CppBranch}");
GUISelectUnityDirectory($"il2cpp_plus分支对应Unity版本的il2cpp路径", "Select");
GUILayout.Space(10f);
GUIInstallButton("安装最新HybridCLR插件代码到本项目", "安装", InitHybridCLR);
EditorGUILayout.EndVertical();
}
private void GUIInstallButton(string content, string button, Action onClick)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(content);
GUI.enabled = m_Controller.CheckValidIl2CppInstallDirectory(m_Controller.Il2CppBranch, m_Controller.Il2CppInstallDirectory) == InstallErrorCode.Ok;
if (GUILayout.Button(button, GUILayout.Width(100)))
{
onClick?.Invoke();
GUIUtility.ExitGUI();
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
}
private void GUISelectUnityDirectory(string content, string selectButton)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(content, GUILayout.MaxWidth(300));
string il2cppInstallDirectory = m_Controller.Il2CppInstallDirectory = EditorGUILayout.TextField(m_Controller.Il2CppInstallDirectory);
if (GUILayout.Button(selectButton, GUILayout.Width(100)))
{
string temp = EditorUtility.OpenFolderPanel(content, m_Controller.Il2CppInstallDirectory, string.Empty);
if (!string.IsNullOrEmpty(temp))
{
il2cppInstallDirectory = m_Controller.Il2CppInstallDirectory = temp;
}
}
EditorGUILayout.EndHorizontal();
InstallErrorCode err = m_Controller.CheckValidIl2CppInstallDirectory(m_Controller.Il2CppBranch, il2cppInstallDirectory);
switch (err)
{
case InstallErrorCode.Ok:
{
if (!il2cppInstallDirectory.Contains(m_Controller.Il2CppBranch))
{
EditorGUILayout.HelpBox($"li2cpp 路径未包含 '{m_Controller.Il2CppBranch}',请确保选择了 {m_Controller.Il2CppBranch} 版本的安装目录 ", MessageType.Warning);
}
break;
}
case InstallErrorCode.Il2CppInstallPathNotExists:
{
EditorGUILayout.HelpBox("li2cpp 路径不存在", MessageType.Error);
break;
}
case InstallErrorCode.Il2CppInstallPathNotMatchIl2CppBranch:
{
EditorGUILayout.HelpBox($"il2cpp 版本不匹配,必须为 {m_Controller.Il2CppBranch} 版本相应目录", MessageType.Error);
break;
}
case InstallErrorCode.NotIl2CppPath:
{
EditorGUILayout.HelpBox($"当前选择的路径不是il2cpp目录必须类似 xxx/il2cpp", MessageType.Error);
break;
}
default: throw new Exception($"not support {err}");
}
}
private void InitHybridCLR()
{
m_Controller.InitHybridCLR(m_Controller.Il2CppBranch, m_Controller.Il2CppInstallDirectory);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 959fbf0bb06629542969354505189240
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
using HybridCLR.Editor.Generators;
using HybridCLR.Editor.Generators.MethodBridge;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace HybridCLR.Editor
{
internal class MethodBridgeHelper
{
private static void CleanIl2CppBuildCache()
{
string il2cppBuildCachePath = BuildConfig.Il2CppBuildCacheDir;
if (!Directory.Exists(il2cppBuildCachePath))
{
return;
}
Debug.Log($"clean il2cpp build cache:{il2cppBuildCachePath}");
Directory.Delete(il2cppBuildCachePath, true);
}
private static List<Assembly> CollectDependentAssemblies(Dictionary<string, Assembly> allAssByName, List<Assembly> dlls)
{
for(int i = 0; i < dlls.Count; i++)
{
Assembly ass = dlls[i];
foreach (var depAssName in ass.GetReferencedAssemblies())
{
if (!allAssByName.ContainsKey(depAssName.Name))
{
Debug.Log($"ignore ref assembly:{depAssName.Name}");
continue;
}
Assembly depAss = allAssByName[depAssName.Name];
if (!dlls.Contains(depAss))
{
dlls.Add(depAss);
}
}
}
return dlls;
}
private static List<Assembly> GetScanAssembiles()
{
var allAssByName = new Dictionary<string, Assembly>();
foreach(var ass in AppDomain.CurrentDomain.GetAssemblies())
{
allAssByName[ass.GetName().Name] = ass;
}
//CompileDllHelper.CompileDllActiveBuildTarget();
var rootAssemblies = BuildConfig.HotUpdateAssemblies
.Select(dll => Path.GetFileNameWithoutExtension(dll)).Concat(GeneratorConfig.GetExtraAssembiles())
.Where(name => allAssByName.ContainsKey(name)).Select(name => allAssByName[name]).ToList();
//var rootAssemblies = GeneratorConfig.GetExtraAssembiles()
// .Where(name => allAssByName.ContainsKey(name)).Select(name => allAssByName[name]).ToList();
CollectDependentAssemblies(allAssByName, rootAssemblies);
rootAssemblies.Sort((a, b) => a.GetName().Name.CompareTo(b.GetName().Name));
Debug.Log($"assembly count:{rootAssemblies.Count}");
foreach(var ass in rootAssemblies)
{
//Debug.Log($"scan assembly:{ass.GetName().Name}");
}
return rootAssemblies;
}
private static void GenerateMethodBridgeCppFile(PlatformABI platform, string fileName, bool optimized)
{
string outputFile = $"{BuildConfig.MethodBridgeCppDir}/{fileName}.cpp";
var g = new MethodBridgeGenerator(new MethodBridgeGeneratorOptions()
{
CallConvention = platform,
HotfixAssemblies = BuildConfig.HotUpdateAssemblies.Select(name =>
AppDomain.CurrentDomain.GetAssemblies().First(ass => ass.GetName().Name + ".dll" == name)).ToList(),
AllAssemblies = optimized ? GetScanAssembiles() : AppDomain.CurrentDomain.GetAssemblies().ToList(),
OutputFile = outputFile,
Optimized = optimized,
});
g.PrepareMethods();
g.Generate();
Debug.LogFormat("== output:{0} ==", outputFile);
CleanIl2CppBuildCache();
}
//[MenuItem("HybridCLR/MethodBridge/Arm64")]
//public static void MethodBridge_Arm64()
//{
// GenerateMethodBridgeCppFile(PlatformABI.Arm64, "MethodBridge_Arm64");
//}
//[MenuItem("HybridCLR/MethodBridge/Universal64")]
//public static void MethodBridge_Universal64()
//{
// GenerateMethodBridgeCppFile(PlatformABI.Universal64, "MethodBridge_Universal64");
//}
//[MenuItem("HybridCLR/MethodBridge/Universal32")]
//public static void MethodBridge_Universal32()
//{
// GenerateMethodBridgeCppFile(PlatformABI.Universal32, "MethodBridge_Universal32");
//}
public static void GenerateMethodBridgeAll(bool optimized)
{
GenerateMethodBridgeCppFile(PlatformABI.Arm64, "MethodBridge_Arm64", optimized);
GenerateMethodBridgeCppFile(PlatformABI.Universal64, "MethodBridge_Universal64", optimized);
GenerateMethodBridgeCppFile(PlatformABI.Universal32, "MethodBridge_Universal32", optimized);
}
[MenuItem("HybridCLR/MethodBridge/All_高度精简")]
public static void MethodBridge_All_Optimized()
{
GenerateMethodBridgeAll(true);
}
[MenuItem("HybridCLR/MethodBridge/All_完整(新手及开发期推荐)")]
public static void MethodBridge_All_Normal()
{
GenerateMethodBridgeAll(false);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18beea79433bdce44af834574cd9c212
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -7,7 +7,7 @@
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,