From 072a1176439768a0b9907fd6eaa6feccd11a9550 Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Mon, 29 Aug 2022 14:33:00 +0800 Subject: [PATCH] HybirdCLR Helper HybirdCLR Helper --- Assets/TEngine/Scripts/Editor/HybridCLR.meta | 8 + .../Scripts/Editor/HybridCLR/BuildConfig.cs | 70 +++ .../Editor/HybridCLR/BuildConfig.cs.meta | 11 + .../Editor/HybridCLR/BuildConfig_Custom.cs | 36 ++ .../HybridCLR/BuildConfig_Custom.cs.meta | 11 + .../Editor/HybridCLR/BuildProcessors.meta | 8 + .../BPCopyStrippedAOTAssemblies.cs | 84 ++++ .../BPCopyStrippedAOTAssemblies.cs.meta | 11 + .../BPFilterHotFixAssemblies.cs | 45 ++ .../BPFilterHotFixAssemblies.cs.meta | 11 + .../BPPatchScriptAssembliesJson.cs | 148 +++++++ .../BPPatchScriptAssembliesJson.cs.meta | 11 + .../BuildProcessors/UnityBinFileReader.meta | 8 + .../UnityBinFileReader/UnityBinFile.cs | 107 +++++ .../UnityBinFileReader/UnityBinFile.cs.meta | 11 + .../UnityBinFileReader/UnityBinFileDefines.cs | 397 ++++++++++++++++++ .../UnityBinFileDefines.cs.meta | 11 + .../UnityBinFileReader/UnityBinUtils.cs | 78 ++++ .../UnityBinFileReader/UnityBinUtils.cs.meta | 11 + .../Editor/HybridCLR/CompileDllHelper.cs | 65 +++ .../Editor/HybridCLR/CompileDllHelper.cs.meta | 11 + .../Scripts/Editor/HybridCLR/Generators.meta | 8 + .../HybridCLR/Generators/ConstStrings.cs | 13 + .../HybridCLR/Generators/ConstStrings.cs.meta | 11 + .../HybridCLR/Generators/FileRegionReplace.cs | 40 ++ .../Generators/FileRegionReplace.cs.meta | 11 + .../HybridCLR/Generators/GeneratorConfig.cs | 70 +++ .../Generators/GeneratorConfig.cs.meta | 11 + .../HybridCLR/Generators/MethodBridge.meta | 8 + .../MethodBridge/IPlatformAdaptor.cs | 28 ++ .../MethodBridge/IPlatformAdaptor.cs.meta | 11 + .../MethodBridge/MethodBridgeGenerator.cs | 353 ++++++++++++++++ .../MethodBridgeGenerator.cs.meta | 11 + .../MethodBridge/MethodBridgeSig.cs | 156 +++++++ .../MethodBridge/MethodBridgeSig.cs.meta | 11 + .../Generators/MethodBridge/ParamInfo.cs | 44 ++ .../Generators/MethodBridge/ParamInfo.cs.meta | 11 + .../MethodBridge/ParamOrReturnType.cs | 38 ++ .../MethodBridge/ParamOrReturnType.cs.meta | 11 + .../Generators/MethodBridge/PlatformABI.cs | 9 + .../MethodBridge/PlatformABI.cs.meta | 11 + .../MethodBridge/PlatformAdaptorBase.cs | 160 +++++++ .../MethodBridge/PlatformAdaptorBase.cs.meta | 11 + .../MethodBridge/PlatformAdaptor_Arm64.cs | 208 +++++++++ .../PlatformAdaptor_Arm64.cs.meta | 11 + .../PlatformAdaptor_Universal32.cs | 96 +++++ .../PlatformAdaptor_Universal32.cs.meta | 11 + .../PlatformAdaptor_Universal64.cs | 189 +++++++++ .../PlatformAdaptor_Universal64.cs.meta | 11 + .../SignatureProviderAttribute.cs | 19 + .../SignatureProviderAttribute.cs.meta | 11 + .../Generators/MethodBridge/TypeInfo.cs | 132 ++++++ .../Generators/MethodBridge/TypeInfo.cs.meta | 11 + .../ValueTypeSizeAligmentCalculator.cs | 148 +++++++ .../ValueTypeSizeAligmentCalculator.cs.meta | 11 + .../HybridCLR/Generators/TemplateUtil.cs | 38 ++ .../HybridCLR/Generators/TemplateUtil.cs.meta | 11 + .../HybridCLR/Generators/Templates.meta | 8 + .../Templates/MethodBridge_Arm64.cpp | 22 + .../Templates/MethodBridge_Arm64.cpp.meta | 27 ++ .../Templates/MethodBridge_Universal32.cpp | 22 + .../MethodBridge_Universal32.cpp.meta | 27 ++ .../Templates/MethodBridge_Universal64.cpp | 22 + .../MethodBridge_Universal64.cpp.meta | 27 ++ .../Scripts/Editor/HybridCLR/Installer.meta | 8 + .../Installer/InstallerController.cs | 224 ++++++++++ .../Installer/InstallerController.cs.meta | 11 + .../HybridCLR/Installer/InstallerWindow.cs | 117 ++++++ .../Installer/InstallerWindow.cs.meta | 11 + .../Editor/HybridCLR/MethodBridgeHelper.cs | 129 ++++++ .../HybridCLR/MethodBridgeHelper.cs.meta | 11 + .../Scripts/Editor/TEngine.Editor.asmdef | 2 +- 72 files changed, 3764 insertions(+), 1 deletion(-) create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Installer.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs.meta create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs create mode 100644 Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs.meta diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR.meta b/Assets/TEngine/Scripts/Editor/HybridCLR.meta new file mode 100644 index 00000000..5f0e9530 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0cca09dc9d27f314da15fa746a01d51c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs new file mode 100644 index 00000000..03f6cada --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs @@ -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}"; + } + + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs.meta new file mode 100644 index 00000000..03ce5891 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf7714fc37515834382cd5836b503a9f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs new file mode 100644 index 00000000..16676171 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs @@ -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 + { + + /// + /// 所有热更新dll列表。放到此列表中的dll在打包时OnFilterAssemblies回调中被过滤。 + /// + public static List HotUpdateAssemblies { get; } = new List + { + "HotFix.dll", + "HotFix2.dll", + }; + + public static List AOTMetaAssemblies { get; } = new List() + { + "mscorlib.dll", + "System.dll", + "System.Core.dll", // 如果使用了Linq,需要这个 + + // + // 注意!修改这个列表请同步修改HotFix2模块中App.cs文件中的 LoadMetadataForAOTAssembly函数中aotDllList列表。 + // 两者需要完全一致 + // + }; + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs.meta new file mode 100644 index 00000000..a8fef316 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig_Custom.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33c09b498146d144a947ed6d8ff104f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors.meta new file mode 100644 index 00000000..3d9bcb51 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d7e4bb1668e0b341b9613b967e27f88 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs new file mode 100644 index 00000000..68bed9ec --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs @@ -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 + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs.meta new file mode 100644 index 00000000..3ebc0e10 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPCopyStrippedAOTAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7884710ec2f8e545b3fe9aa05def5a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs new file mode 100644 index 00000000..30eb4205 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; + +namespace HybridCLR.Editor.BuildProcessors +{ + /// + /// 将热更新dll从Build过程中过滤,防止打包到主工程中 + /// + internal class BPFilterHotFixAssemblies : IFilterBuildAssemblies + { + public int callbackOrder => 0; + + public string[] OnFilterAssemblies(BuildOptions buildOptions, string[] assemblies) + { + List allHotUpdateDllNames = BuildConfig.HotUpdateAssemblies; + + // 检查是否重复填写 + var hotUpdateDllSet = new HashSet(); + 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(); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs.meta new file mode 100644 index 00000000..2ab4ba54 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPFilterHotFixAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9dec2922e3df5464aa047b636eb19e0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs new file mode 100644 index 00000000..d27eadac --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs @@ -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 names; + public List 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(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 + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs.meta new file mode 100644 index 00000000..0affdc71 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/BPPatchScriptAssembliesJson.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9bb6e2908d8948648979c9ff6bb7937d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader.meta new file mode 100644 index 00000000..7cabb704 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e52fb09e14efee949ae80ae8aa9f9d44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs new file mode 100644 index 00000000..f389e351 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs @@ -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 +{ + /// + /// Unity 生成的二进制文件(本代码不支持5.x之前的版本) + /// + 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 newObjInfos = new Dictionary(); + 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(); + } + } + +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs.meta new file mode 100644 index 00000000..e53b7420 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae7ec6e3674077d46898fe821d24bf85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs new file mode 100644 index 00000000..48a7920b --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs @@ -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 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(); + 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 dllNames; + public List 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(count); + for (var i = 0; i < count; i++) + dllNames.Add(br.ReadSizeString()); + } + { + int count = br.ReadInt32(); + dllTypes = new List(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; + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs.meta new file mode 100644 index 00000000..d9192c74 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinFileDefines.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96788c7fe08d5d54d95a87cfbdcb643a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs new file mode 100644 index 00000000..08901404 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs @@ -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); + } + } +} + diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs.meta new file mode 100644 index 00000000..e0f9bf96 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/BuildProcessors/UnityBinFileReader/UnityBinUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf7c4cf970660614fb54d838ec6e7eda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs new file mode 100644 index 00000000..ee19f079 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs @@ -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); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs.meta new file mode 100644 index 00000000..bd9ed55e --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91aca02079207d746b74aeea1b595127 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators.meta new file mode 100644 index 00000000..0dbbc8aa --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f85b93bff3478c045ae862a7cc216b18 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs new file mode 100644 index 00000000..6cc11d7f --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs @@ -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*"; + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs.meta new file mode 100644 index 00000000..81ed29d2 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/ConstStrings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1465458aaad6884d95af7c4fc2de7a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs new file mode 100644 index 00000000..170b5381 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs @@ -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 _regionReplaceContents = new Dictionary(); + + 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); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs.meta new file mode 100644 index 00000000..07c87ea1 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/FileRegionReplace.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5aa6559bd1b1e4488aa6746eda8202b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs new file mode 100644 index 00000000..bc3a3c6f --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs @@ -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 + { + /// + /// 目前已经根据热更新dll的依赖自动计算需要扫描哪些dll来收集桥接函数。 + /// 只要你的热更新以assembly def形式放到项目中,是不需要改这个的 + /// + /// + public static List GetExtraAssembiles() + { + return new List + { + // "mscorlib", + }; + } + + /// + /// 暂时没有仔细扫描泛型,如果运行时发现有生成缺失,先手动在此添加类 + /// + /// + public static List PrepareCustomGenericTypes() + { + return new List + { + typeof(Action), + }; + } + + /// + /// 如果提示缺失桥接函数,将提示缺失的签名加入到下列列表是简单的做法。 + /// 这里添加64位App缺失的桥接函数签名 + /// + /// + public static List PrepareCustomMethodSignatures64() + { + return new List + { + "vi8i8", + "i4i8i8i4i4i8i8", + "i8i8S12", + "S12i8S12", + "S12i8S12S12", + "i16i8i16i16", + }; + } + + /// + /// 如果提示缺失桥接函数,将提示缺失的签名加入到下列列表是简单的做法。 + /// 这里添加32位App缺失的桥接函数签名 + /// + /// + public static List PrepareCustomMethodSignatures32() + { + return new List + { + "vi4i4", + "S12i4S12S12", + }; + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs.meta new file mode 100644 index 00000000..de1bd9ec --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/GeneratorConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1d14f8f858560948ae1dfa0fa572e51 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge.meta new file mode 100644 index 00000000..d04da9be --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4aa66b41c89ed7742ad067898725f7ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs new file mode 100644 index 00000000..3b52d254 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs @@ -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 outputLines); + + void GenerateManaged2NativeStub(List methods, List lines); + + void GenerateNative2ManagedMethod(MethodBridgeSig method, List outputLines); + + void GenerateNative2ManagedStub(List methods, List lines); + + void GenerateAdjustThunkMethod(MethodBridgeSig method, List outputLines); + + void GenerateAdjustThunkStub(List methods, List lines); + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs.meta new file mode 100644 index 00000000..8dd7196a --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/IPlatformAdaptor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 983dbdeb1fb7d0b43a863c6b22160ace +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs new file mode 100644 index 00000000..cd57437d --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs @@ -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 GenericMethods { get; set; } + } + + public class MethodBridgeGeneratorOptions + { + public List HotfixAssemblies { get; set; } + + public List AllAssemblies { get; set; } + + public PlatformABI CallConvention { get; set; } + + public string OutputFile { get; set; } + + public bool Optimized { get; set; } + } + + public class MethodBridgeGenerator + { + private readonly HashSet _hotfixAssemblies; + + private readonly List _assemblies; + + private readonly PlatformABI _callConvention; + + private readonly string _outputFile; + + public readonly bool _optimized; + + private readonly IPlatformAdaptor _platformAdaptor; + + private readonly HashSet _managed2nativeMethodSet = new HashSet(); + + private List _managed2nativeMethodList; + + private readonly HashSet _native2managedMethodSet = new HashSet(); + + private List _native2managedMethodList; + + private readonly HashSet _adjustThunkMethodSet = new HashSet(); + + private List _adjustThunkMethodList; + + public bool IsHotFixType(Type type) + { + return _hotfixAssemblies.Contains(type.Assembly); + } + + public MethodBridgeGenerator(MethodBridgeGeneratorOptions options) + { + _hotfixAssemblies = new HashSet(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 GetGenerateTypes() + { + return new List(); + } + + private MethodBridgeSig CreateMethodBridgeSig(bool isStatic, ParameterInfo returnType, ParameterInfo[] parameters) + { + var paramInfos = new List(); + 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(); + foreach (var method in _managed2nativeMethodSet) + { + sortedMethods.Add(method.CreateCallSigName(), method); + } + _managed2nativeMethodList = sortedMethods.Values.ToList(); + } + { + var sortedMethods = new SortedDictionary(); + foreach (var method in _native2managedMethodSet) + { + sortedMethods.Add(method.CreateCallSigName(), method); + } + _native2managedMethodList = sortedMethods.Values.ToList(); + } + { + var sortedMethods = new SortedDictionary(); + foreach (var method in _adjustThunkMethodSet) + { + sortedMethods.Add(method.CreateCallSigName(), method); + } + _adjustThunkMethodList = sortedMethods.Values.ToList(); + } + } + + public void Generate() + { + var frr = new FileRegionReplace(GetTemplateFile()); + + List lines = new List(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); + } + + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs.meta new file mode 100644 index 00000000..84054d46 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3171ed79d43e06c4b8c889b0a3f38969 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs new file mode 100644 index 00000000..afba4813 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs @@ -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 + { + + 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()}; + 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 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; + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs.meta new file mode 100644 index 00000000..8ae3b558 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/MethodBridgeSig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbe63fb39b5bdcc448f3589b475fb5d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs new file mode 100644 index 00000000..5537e19b --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs @@ -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(); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs.meta new file mode 100644 index 00000000..517be646 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c04e163336b93af44b4b565d0ab195e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs new file mode 100644 index 00000000..08e71134 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs @@ -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, + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs.meta new file mode 100644 index 00000000..78fd4e0c --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ParamOrReturnType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7886905937fafb64999dc4e358565982 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs new file mode 100644 index 00000000..8c94e8b3 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs @@ -0,0 +1,9 @@ +namespace HybridCLR.Editor.Generators.MethodBridge +{ + public enum PlatformABI + { + Universal32, + Universal64, + Arm64, + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs.meta new file mode 100644 index 00000000..e2574724 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformABI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: deee8d06a7f05bf4d9ab4b78f0a8a47c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs new file mode 100644 index 00000000..98358812 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs @@ -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 CacheTypes { get; } + + protected abstract TypeInfo CreateValueType(Type type, bool returnValue); + + public abstract void GenerateManaged2NativeMethod(MethodBridgeSig method, List lines); + + public abstract void GenerateNative2ManagedMethod(MethodBridgeSig method, List lines); + + public abstract void GenerateAdjustThunkMethod(MethodBridgeSig method, List outputLines); + + private static Dictionary _typeSizeCache64 = new Dictionary(); + + private static Dictionary _typeSizeCache32 = new Dictionary(); + + 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)method.CreateDelegate(typeof(Func)))(); + + 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 methods, List 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 methods, List 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 methods, List 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("};"); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs.meta new file mode 100644 index 00000000..f18ef365 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptorBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ebf14107fd82b364cb7d61276d444829 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs new file mode 100644 index 00000000..c0f063a1 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs @@ -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 s_typeInfoCaches = new Dictionary() + { + { 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 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 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 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 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;" : "")} +}} +"); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs.meta new file mode 100644 index 00000000..4b9a4a12 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Arm64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bff17a6e8ee060c4eb9ac97fcf30bf78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs new file mode 100644 index 00000000..6201a9fb --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs @@ -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 s_typeInfoCaches = new Dictionary() + { + { 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 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 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 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 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;" : "")} +}} +"); + + } + } + +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs.meta new file mode 100644 index 00000000..3b2f55a1 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb741900113b22443a2054ddba6b131b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs new file mode 100644 index 00000000..7bdf0962 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs @@ -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 s_typeInfoCaches = new Dictionary() + { + { 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 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 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 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 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;" : "")} +}} +"); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs.meta new file mode 100644 index 00000000..d782aae7 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/PlatformAdaptor_Universal64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26324be9505c9f54996bcbb62ba49132 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs new file mode 100644 index 00000000..d02a888a --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs @@ -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 Platforms { get; } + + public SignatureProviderAttribute(params PlatformABI[] platforms) + { + Platforms = new List(platforms); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs.meta new file mode 100644 index 00000000..939362c2 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/SignatureProviderAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50c5ec2d43d3fda4a8b8cfa75cb37a74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs new file mode 100644 index 00000000..437aeecf --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; + +namespace HybridCLR.Editor.Generators.MethodBridge +{ + public class TypeInfo : IEquatable + { + + 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; + } + } + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs.meta new file mode 100644 index 00000000..1ba9358d --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/TypeInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5afd7344483678a4abcb56158fd1442d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs new file mode 100644 index 00000000..c1ca7d4a --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs @@ -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 s_primitives = new Dictionary(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().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); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs.meta new file mode 100644 index 00000000..97cf075f --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/MethodBridge/ValueTypeSizeAligmentCalculator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49b3dbcebadb1b543a42e01afec07ed1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs new file mode 100644 index 00000000..1373b61c --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs @@ -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; + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs.meta new file mode 100644 index 00000000..7b6b91b1 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/TemplateUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 32ec2cc1cf5c468468308fa6308fba19 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates.meta new file mode 100644 index 00000000..31e4b541 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3a1563e7820e5b648a6a6691677f564c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp new file mode 100644 index 00000000..e5cfc758 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp @@ -0,0 +1,22 @@ +#include "MethodBridge.h" + +#include +#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 diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp.meta new file mode 100644 index 00000000..0ae25622 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Arm64.cpp.meta @@ -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: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp new file mode 100644 index 00000000..7a2ca8cf --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp @@ -0,0 +1,22 @@ +#include "MethodBridge.h" + +#include +#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 diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp.meta new file mode 100644 index 00000000..df0e9bf5 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal32.cpp.meta @@ -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: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp new file mode 100644 index 00000000..459d43df --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp @@ -0,0 +1,22 @@ +#include "MethodBridge.h" + +#include +#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 diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp.meta new file mode 100644 index 00000000..294d27b3 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Generators/Templates/MethodBridge_Universal64.cpp.meta @@ -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: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Installer.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer.meta new file mode 100644 index 00000000..47b8a312 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a4a3d499c10d66f43af10a859e276a8f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs new file mode 100644 index 00000000..45070d9b --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs @@ -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("安装成功!!!"); + } + } + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs.meta new file mode 100644 index 00000000..6b30944d --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44c8627d126b30d4e9560b1f738264ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs new file mode 100644 index 00000000..47783680 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs @@ -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("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); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs.meta new file mode 100644 index 00000000..f404c28d --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/Installer/InstallerWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 959fbf0bb06629542969354505189240 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs b/Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs new file mode 100644 index 00000000..57389a4a --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs @@ -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 CollectDependentAssemblies(Dictionary allAssByName, List 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 GetScanAssembiles() + { + var allAssByName = new Dictionary(); + 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); + } + } +} diff --git a/Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs.meta b/Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs.meta new file mode 100644 index 00000000..91c08cb2 --- /dev/null +++ b/Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18beea79433bdce44af834574cd9c212 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Scripts/Editor/TEngine.Editor.asmdef b/Assets/TEngine/Scripts/Editor/TEngine.Editor.asmdef index 90c2c5e2..9b507b71 100644 --- a/Assets/TEngine/Scripts/Editor/TEngine.Editor.asmdef +++ b/Assets/TEngine/Scripts/Editor/TEngine.Editor.asmdef @@ -7,7 +7,7 @@ "Editor" ], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true,