mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
HybirdCLR Helper
HybirdCLR Helper
This commit is contained in:
8
Assets/TEngine/Scripts/Editor/HybridCLR.meta
Normal file
8
Assets/TEngine/Scripts/Editor/HybridCLR.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0cca09dc9d27f314da15fa746a01d51c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
70
Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs
Normal file
70
Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor
|
||||||
|
{
|
||||||
|
public static partial class BuildConfig
|
||||||
|
{
|
||||||
|
#if !UNITY_IOS
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
private static void Setup()
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// unity允许使用UNITY_IL2CPP_PATH环境变量指定il2cpp的位置,因此我们不再直接修改安装位置的il2cpp,
|
||||||
|
/// 而是在本地目录
|
||||||
|
///
|
||||||
|
var localIl2cppDir = LocalIl2CppDir;
|
||||||
|
if (!Directory.Exists(localIl2cppDir))
|
||||||
|
{
|
||||||
|
Debug.LogError($"本地il2cpp目录:{localIl2cppDir} 不存在,未安装本地il2cpp。请在菜单 HybridCLR/Installer 中执行安装");
|
||||||
|
}
|
||||||
|
Environment.SetEnvironmentVariable("UNITY_IL2CPP_PATH", localIl2cppDir);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static string ProjectDir => Directory.GetParent(Application.dataPath).ToString();
|
||||||
|
|
||||||
|
public static string ScriptingAssembliesJsonFile { get; } = "ScriptingAssemblies.json";
|
||||||
|
|
||||||
|
public static string HybridCLRBuildCacheDir => Application.dataPath + "/HybridCLRBuildCache";
|
||||||
|
|
||||||
|
public static string HotFixDllsOutputDir => $"{HybridCLRDataDir}/HotFixDlls";
|
||||||
|
|
||||||
|
public static string AssetBundleOutputDir => $"{HybridCLRBuildCacheDir}/AssetBundleOutput";
|
||||||
|
|
||||||
|
public static string AssetBundleSourceDataTempDir => $"{HybridCLRBuildCacheDir}/AssetBundleSourceData";
|
||||||
|
|
||||||
|
public static string HybridCLRDataDir { get; } = $"{ProjectDir}/HybridCLRData";
|
||||||
|
|
||||||
|
public static string AssembliesPostIl2CppStripDir => $"{HybridCLRDataDir}/AssembliesPostIl2CppStrip";
|
||||||
|
|
||||||
|
public static string LocalIl2CppDir => $"{HybridCLRDataDir}/LocalIl2CppData/il2cpp";
|
||||||
|
|
||||||
|
public static string MethodBridgeCppDir => $"{LocalIl2CppDir}/libil2cpp/hybridclr/interpreter";
|
||||||
|
|
||||||
|
public static string Il2CppBuildCacheDir { get; } = $"{ProjectDir}/Library/Il2cppBuildCache";
|
||||||
|
|
||||||
|
public static string GetHotFixDllsOutputDirByTarget(BuildTarget target)
|
||||||
|
{
|
||||||
|
return $"{HotFixDllsOutputDir}/{target}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAssembliesPostIl2CppStripDir(BuildTarget target)
|
||||||
|
{
|
||||||
|
return $"{AssembliesPostIl2CppStripDir}/{target}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAssetBundleOutputDirByTarget(BuildTarget target)
|
||||||
|
{
|
||||||
|
return $"{AssetBundleOutputDir}/{target}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAssetBundleTempDirByTarget(BuildTarget target)
|
||||||
|
{
|
||||||
|
return $"{AssetBundleSourceDataTempDir}/{target}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
11
Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs.meta
Normal file
11
Assets/TEngine/Scripts/Editor/HybridCLR/BuildConfig.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bf7714fc37515834382cd5836b503a9f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor
|
||||||
|
{
|
||||||
|
public static partial class BuildConfig
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所有热更新dll列表。放到此列表中的dll在打包时OnFilterAssemblies回调中被过滤。
|
||||||
|
/// </summary>
|
||||||
|
public static List<string> HotUpdateAssemblies { get; } = new List<string>
|
||||||
|
{
|
||||||
|
"HotFix.dll",
|
||||||
|
"HotFix2.dll",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<string> AOTMetaAssemblies { get; } = new List<string>()
|
||||||
|
{
|
||||||
|
"mscorlib.dll",
|
||||||
|
"System.dll",
|
||||||
|
"System.Core.dll", // 如果使用了Linq,需要这个
|
||||||
|
|
||||||
|
//
|
||||||
|
// 注意!修改这个列表请同步修改HotFix2模块中App.cs文件中的 LoadMetadataForAOTAssembly函数中aotDllList列表。
|
||||||
|
// 两者需要完全一致
|
||||||
|
//
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 33c09b498146d144a947ed6d8ff104f3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5d7e4bb1668e0b341b9613b967e27f88
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f7884710ec2f8e545b3fe9aa05def5a8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Build;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.BuildProcessors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将热更新dll从Build过程中过滤,防止打包到主工程中
|
||||||
|
/// </summary>
|
||||||
|
internal class BPFilterHotFixAssemblies : IFilterBuildAssemblies
|
||||||
|
{
|
||||||
|
public int callbackOrder => 0;
|
||||||
|
|
||||||
|
public string[] OnFilterAssemblies(BuildOptions buildOptions, string[] assemblies)
|
||||||
|
{
|
||||||
|
List<string> allHotUpdateDllNames = BuildConfig.HotUpdateAssemblies;
|
||||||
|
|
||||||
|
// 检查是否重复填写
|
||||||
|
var hotUpdateDllSet = new HashSet<string>();
|
||||||
|
foreach(var hotUpdateDll in allHotUpdateDllNames)
|
||||||
|
{
|
||||||
|
if (!hotUpdateDllSet.Add(hotUpdateDll))
|
||||||
|
{
|
||||||
|
throw new Exception($"热更新 assembly:{hotUpdateDll} 在列表中重复,请除去重复条目");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否填写了正确的dll名称
|
||||||
|
foreach (var hotUpdateDll in BuildConfig.HotUpdateAssemblies)
|
||||||
|
{
|
||||||
|
if (assemblies.All(ass => !ass.EndsWith(hotUpdateDll)))
|
||||||
|
{
|
||||||
|
throw new Exception($"热更新 assembly:{hotUpdateDll} 不存在,请检查拼写错误");
|
||||||
|
}
|
||||||
|
Debug.Log($"[BPFilterHotFixAssemblies] 过滤热更新assembly:{hotUpdateDll}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将热更dll从打包列表中移除
|
||||||
|
return assemblies.Where(ass => BuildConfig.HotUpdateAssemblies.All(dll => !ass.EndsWith(dll, StringComparison.OrdinalIgnoreCase))).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9dec2922e3df5464aa047b636eb19e0d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,148 @@
|
|||||||
|
using HybridCLR.Editor.GlobalManagers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Android;
|
||||||
|
using UnityEditor.Build;
|
||||||
|
using UnityEditor.Build.Reporting;
|
||||||
|
using UnityEditor.Il2Cpp;
|
||||||
|
using UnityEditor.UnityLinker;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.BuildProcessors
|
||||||
|
{
|
||||||
|
public class BPPatchScriptAssembliesJson : IPreprocessBuildWithReport,
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
IPostGenerateGradleAndroidProject,
|
||||||
|
#endif
|
||||||
|
IPostprocessBuildWithReport
|
||||||
|
{
|
||||||
|
public int callbackOrder => 0;
|
||||||
|
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private class ScriptingAssemblies
|
||||||
|
{
|
||||||
|
public List<string> names;
|
||||||
|
public List<int> types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPostGenerateGradleAndroidProject(string path)
|
||||||
|
{
|
||||||
|
// 如果直接打包apk,没有机会在PostprocessBuild中修改ScriptingAssemblies.json。
|
||||||
|
// 因此需要在这个时机处理
|
||||||
|
PathScriptingAssembilesFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPostprocessBuild(BuildReport report)
|
||||||
|
{
|
||||||
|
// 如果target为Android,由于已经在OnPostGenerateGradelAndroidProject中处理过,
|
||||||
|
// 这里不再重复处理
|
||||||
|
#if !UNITY_ANDROID
|
||||||
|
|
||||||
|
PathScriptingAssembilesFile(report.summary.outputPath);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PathScriptingAssembilesFile(string path)
|
||||||
|
{
|
||||||
|
#if UNITY_2020_1_OR_NEWER
|
||||||
|
AddHotFixAssembliesToScriptingAssembliesJson(path);
|
||||||
|
#else
|
||||||
|
AddBackHotFixAssembliesToBinFile(path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddHotFixAssembliesToScriptingAssembliesJson(string path)
|
||||||
|
{
|
||||||
|
Debug.Log($"AddBackHotFixAssembliesToJson. path:{path}");
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
path = Directory.GetParent(path).ToString();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* ScriptingAssemblies.json 文件中记录了所有的dll名称,此列表在游戏启动时自动加载,
|
||||||
|
* 不在此列表中的dll在资源反序列化时无法被找到其类型
|
||||||
|
* 因此 OnFilterAssemblies 中移除的条目需要再加回来
|
||||||
|
*/
|
||||||
|
string[] jsonFiles = Directory.GetFiles(path, BuildConfig.ScriptingAssembliesJsonFile, SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
if (jsonFiles.Length == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError($"can not find file {BuildConfig.ScriptingAssembliesJsonFile}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string file in jsonFiles)
|
||||||
|
{
|
||||||
|
string content = File.ReadAllText(file);
|
||||||
|
ScriptingAssemblies scriptingAssemblies = JsonUtility.FromJson<ScriptingAssemblies>(content);
|
||||||
|
foreach (string name in BuildConfig.HotUpdateAssemblies)
|
||||||
|
{
|
||||||
|
if (!scriptingAssemblies.names.Contains(name))
|
||||||
|
{
|
||||||
|
scriptingAssemblies.names.Add(name);
|
||||||
|
scriptingAssemblies.types.Add(16); // user dll type
|
||||||
|
Debug.Log($"[PatchScriptAssembliesJson] add hotfix assembly:{name} to {file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content = JsonUtility.ToJson(scriptingAssemblies);
|
||||||
|
|
||||||
|
File.WriteAllText(file, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddBackHotFixAssembliesToBinFile(string path)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Unity2019 中 dll 加载列表存储在 globalgamemanagers 文件中,此列表在游戏启动时自动加载,
|
||||||
|
* 不在此列表中的dll在资源反序列化时无法被找到其类型
|
||||||
|
* 因此 OnFilterAssemblies 中移除的条目需要再加回来
|
||||||
|
*/
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
string[] binFiles = new string[] { "Temp/gradleOut/unityLibrary/src/main/assets/bin/Data/globalgamemanagers" }; // report.files 不包含 Temp/gradleOut 等目录
|
||||||
|
#else
|
||||||
|
// 直接出包和输出vs工程时路径不同,report.summary.outputPath 记录的是前者路径
|
||||||
|
string[] binFiles = Directory.GetFiles(Path.GetDirectoryName(path), "globalgamemanagers", SearchOption.AllDirectories);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (binFiles.Length == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("can not find file ScriptingAssemblies.json");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string binPath in binFiles)
|
||||||
|
{
|
||||||
|
var binFile = new UnityBinFile();
|
||||||
|
binFile.LoadFromFile(binPath);
|
||||||
|
|
||||||
|
ScriptsData scriptsData = binFile.scriptsData;
|
||||||
|
foreach (string name in BuildConfig.HotUpdateAssemblies)
|
||||||
|
{
|
||||||
|
if (!scriptsData.dllNames.Contains(name))
|
||||||
|
{
|
||||||
|
scriptsData.dllNames.Add(name);
|
||||||
|
scriptsData.dllTypes.Add(16); // user dll type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binFile.scriptsData = scriptsData;
|
||||||
|
|
||||||
|
binFile.RebuildAndFlushToFile(binPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region useless
|
||||||
|
|
||||||
|
public void OnPreprocessBuild(BuildReport report)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9bb6e2908d8948648979c9ff6bb7937d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e52fb09e14efee949ae80ae8aa9f9d44
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,107 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Text;
|
||||||
|
using System.Reflection;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.GlobalManagers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unity 生成的二进制文件(本代码不支持5.x之前的版本)
|
||||||
|
/// </summary>
|
||||||
|
public unsafe class UnityBinFile
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* MonoManager: idx: 6;
|
||||||
|
* type: metaData.types[objects[6].typeID]
|
||||||
|
*/
|
||||||
|
public const int kMonoManagerIdx = 6;
|
||||||
|
|
||||||
|
public string path { get; private set; }
|
||||||
|
|
||||||
|
public FileHeader header;
|
||||||
|
public MetaData metaData;
|
||||||
|
public ScriptsData scriptsData;
|
||||||
|
|
||||||
|
public void LoadFromFile(string path)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
|
||||||
|
var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||||
|
var br = new BinaryReader(fs, Encoding.UTF8, true);
|
||||||
|
|
||||||
|
header.LoadFromStream(br);
|
||||||
|
// 按理说 metaData 应该新开一个buffer来避免加载时的对齐逻辑问题,但由于 sizeof(Header) = 20,已经对齐到4了,所以可以连续读
|
||||||
|
metaData.LoadFromStream(br, header.dataOffset);
|
||||||
|
scriptsData = metaData.GetScriptData(br);
|
||||||
|
|
||||||
|
br.Close();
|
||||||
|
fs.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RebuildAndFlushToFile(string newPath)
|
||||||
|
{
|
||||||
|
var fsR = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||||
|
var brR = new BinaryReader(fsR, Encoding.UTF8, true);
|
||||||
|
|
||||||
|
var ms = new MemoryStream((int)(header.fileSize * 1.5f));
|
||||||
|
var bw = new BinaryWriter(ms, Encoding.UTF8, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 开始写入data
|
||||||
|
* dll名称列表存储于 data 区段,修改其数据并不会影响 MetaData 大小,因此 dataOffset 不会改变
|
||||||
|
*/
|
||||||
|
ms.Position = header.dataOffset;
|
||||||
|
|
||||||
|
Dictionary<long, ObjectInfo> newObjInfos = new Dictionary<long, ObjectInfo>();
|
||||||
|
foreach (var kv in metaData.objects)
|
||||||
|
{
|
||||||
|
long objID = kv.Key;
|
||||||
|
ObjectInfo objInfo = kv.Value;
|
||||||
|
|
||||||
|
byte[] buff = new byte[objInfo.size];
|
||||||
|
fsR.Position = objInfo.realPos;
|
||||||
|
brR.Read(buff, 0, buff.Length);
|
||||||
|
|
||||||
|
|
||||||
|
{// unity 的数据偏移貌似会对齐到 8
|
||||||
|
int newPos = (((int)ms.Position + 7) >> 3) << 3;
|
||||||
|
int gapSize = newPos - (int)ms.Position;
|
||||||
|
|
||||||
|
for (int i = 0; i < gapSize; i++)
|
||||||
|
bw.Write((byte)0);
|
||||||
|
|
||||||
|
objInfo.dataPos = (uint)ms.Position - header.dataOffset; // 重定位数据偏移
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objID != kMonoManagerIdx)
|
||||||
|
bw.Write(buff, 0, buff.Length);
|
||||||
|
else
|
||||||
|
objInfo.size = (uint)scriptsData.SaveToStream(bw);
|
||||||
|
|
||||||
|
newObjInfos.Add(objID, objInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
metaData.objects = newObjInfos;
|
||||||
|
header.fileSize = (uint)ms.Position;
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
header.SaveToStream(bw);
|
||||||
|
metaData.SaveToStream(bw);
|
||||||
|
|
||||||
|
brR.Close();
|
||||||
|
fsR.Close();
|
||||||
|
|
||||||
|
// 写入新文件
|
||||||
|
ms.Position = 0;
|
||||||
|
File.WriteAllBytes(newPath, ms.ToArray());
|
||||||
|
|
||||||
|
bw.Close();
|
||||||
|
ms.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ae7ec6e3674077d46898fe821d24bf85
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,397 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
using static HybridCLR.Editor.GlobalManagers.UnityBinUtils;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.GlobalManagers
|
||||||
|
{
|
||||||
|
public struct FileHeader
|
||||||
|
{
|
||||||
|
public const int kSize = 20;
|
||||||
|
|
||||||
|
public uint dataSize => fileSize - metadataSize;
|
||||||
|
|
||||||
|
public uint metadataSize;
|
||||||
|
public uint fileSize;
|
||||||
|
public uint version;
|
||||||
|
public uint dataOffset;
|
||||||
|
public byte endianess;
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
long startPos = br.BaseStream.Position;
|
||||||
|
metadataSize = br.ReadUInt32();
|
||||||
|
fileSize = br.ReadUInt32();
|
||||||
|
version = br.ReadUInt32();
|
||||||
|
dataOffset = br.ReadUInt32();
|
||||||
|
endianess = br.ReadByte();
|
||||||
|
br.BaseStream.Position = startPos + kSize;
|
||||||
|
|
||||||
|
SwapEndianess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
SwapEndianess();
|
||||||
|
|
||||||
|
long startPos = bw.BaseStream.Position;
|
||||||
|
bw.Write(metadataSize);
|
||||||
|
bw.Write(fileSize);
|
||||||
|
bw.Write(version);
|
||||||
|
bw.Write(dataOffset);
|
||||||
|
bw.Write(endianess);
|
||||||
|
bw.BaseStream.Position = startPos + kSize;
|
||||||
|
return kSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapEndianess()
|
||||||
|
{
|
||||||
|
SwapUInt(ref metadataSize);
|
||||||
|
SwapUInt(ref fileSize);
|
||||||
|
SwapUInt(ref version);
|
||||||
|
SwapUInt(ref dataOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MetaData
|
||||||
|
{
|
||||||
|
public long dataStartPos;
|
||||||
|
|
||||||
|
public string version;
|
||||||
|
public uint platform;
|
||||||
|
public bool enableTypeTree;
|
||||||
|
public int typeCount;
|
||||||
|
public ObjectType[] types;
|
||||||
|
public int objectCount;
|
||||||
|
public Dictionary<long, ObjectInfo> objects;
|
||||||
|
public int scriptTypeCount;
|
||||||
|
public ScriptType[] scriptTypes;
|
||||||
|
public int externalsCount;
|
||||||
|
public ExternalInfo[] externals;
|
||||||
|
|
||||||
|
#if UNITY_2019_2_OR_NEWER
|
||||||
|
public int refTypeCount;
|
||||||
|
public ObjectType[] refTypes;
|
||||||
|
#endif
|
||||||
|
public string dummyStr;
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br, uint dataOffset)
|
||||||
|
{
|
||||||
|
long startPos = br.BaseStream.Position;
|
||||||
|
dataStartPos = startPos;
|
||||||
|
|
||||||
|
version = br.ReadRawString();
|
||||||
|
platform = br.ReadUInt32();
|
||||||
|
enableTypeTree = br.ReadBoolean();
|
||||||
|
typeCount = br.ReadInt32();
|
||||||
|
types = new ObjectType[typeCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < typeCount; i++)
|
||||||
|
{
|
||||||
|
types[i].LoadFromStream(br);
|
||||||
|
}
|
||||||
|
|
||||||
|
objectCount = br.ReadInt32();
|
||||||
|
objects = new Dictionary<long, ObjectInfo>();
|
||||||
|
for(int i = 0; i < objectCount; i++)
|
||||||
|
{
|
||||||
|
long id = br.AlignedReadInt64();
|
||||||
|
ObjectInfo objInfo = new ObjectInfo();
|
||||||
|
objInfo.LoadFromStream(br);
|
||||||
|
objInfo.realPos = objInfo.dataPos + dataOffset;
|
||||||
|
|
||||||
|
objects.Add(id, objInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptTypeCount = br.ReadInt32();
|
||||||
|
scriptTypes = new ScriptType[scriptTypeCount];
|
||||||
|
for(int i = 0; i < scriptTypeCount; i++)
|
||||||
|
{
|
||||||
|
scriptTypes[i].LoadFromStream(br);
|
||||||
|
}
|
||||||
|
|
||||||
|
externalsCount = br.ReadInt32();
|
||||||
|
externals = new ExternalInfo[externalsCount];
|
||||||
|
for(int i = 0; i < externalsCount; i++)
|
||||||
|
{
|
||||||
|
externals[i].LoadFromStream(br);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2019_2_OR_NEWER
|
||||||
|
refTypeCount = br.ReadInt32();
|
||||||
|
refTypes = new ObjectType[refTypeCount];
|
||||||
|
for(int i = 0; i < refTypeCount; i++)
|
||||||
|
{
|
||||||
|
refTypes[i].LoadFromStream(br);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
dummyStr = br.ReadRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
long startPos = bw.BaseStream.Position;
|
||||||
|
bw.WriteRawString(version);
|
||||||
|
bw.Write(platform);
|
||||||
|
bw.Write(enableTypeTree);
|
||||||
|
|
||||||
|
bw.Write(typeCount);
|
||||||
|
foreach(var type in types)
|
||||||
|
type.SaveToStream(bw);
|
||||||
|
|
||||||
|
bw.Write(objectCount);
|
||||||
|
foreach (var kv in objects)
|
||||||
|
{
|
||||||
|
bw.AlignedWriteInt64(kv.Key);
|
||||||
|
kv.Value.SaveToStream(bw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.Write(scriptTypeCount);
|
||||||
|
foreach(var st in scriptTypes)
|
||||||
|
st.SaveToStream(bw);
|
||||||
|
|
||||||
|
bw.Write(externalsCount);
|
||||||
|
foreach(var external in externals)
|
||||||
|
external.SaveToStream(bw);
|
||||||
|
|
||||||
|
#if UNITY_2019_2_OR_NEWER
|
||||||
|
bw.Write(refTypeCount);
|
||||||
|
foreach(var refT in refTypes)
|
||||||
|
refT.SaveToStream(bw);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bw.WriteRawString(dummyStr);
|
||||||
|
|
||||||
|
return bw.BaseStream.Position - startPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScriptsData GetScriptData(BinaryReader br)
|
||||||
|
{
|
||||||
|
ObjectInfo objInfo = objects[UnityBinFile.kMonoManagerIdx];
|
||||||
|
br.BaseStream.Seek(objInfo.realPos, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
ScriptsData data = new ScriptsData();
|
||||||
|
data.LoadFromStream(br);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ObjectType
|
||||||
|
{
|
||||||
|
public int typeID;
|
||||||
|
public bool isStriped;
|
||||||
|
public short scriptTypeIndex;
|
||||||
|
|
||||||
|
public bool needReadScriptHash; // dont save
|
||||||
|
|
||||||
|
public Hash scriptSigHash;
|
||||||
|
public Hash typeHash;
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
typeID = br.ReadInt32();
|
||||||
|
isStriped = br.ReadBoolean();
|
||||||
|
scriptTypeIndex = br.ReadInt16();
|
||||||
|
|
||||||
|
needReadScriptHash = typeID == -1 || typeID == 0x72;
|
||||||
|
if(needReadScriptHash)
|
||||||
|
scriptSigHash.LoadFromStream(br);
|
||||||
|
|
||||||
|
typeHash.LoadFromStream(br);
|
||||||
|
|
||||||
|
// GlobalManagers does not has TypeTrees
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
long startPos = bw.BaseStream.Position;
|
||||||
|
bw.Write(typeID);
|
||||||
|
bw.Write(isStriped);
|
||||||
|
bw.Write(scriptTypeIndex);
|
||||||
|
|
||||||
|
if(needReadScriptHash)
|
||||||
|
scriptSigHash.SaveToStream(bw);
|
||||||
|
|
||||||
|
typeHash.SaveToStream(bw);
|
||||||
|
return bw.BaseStream.Position - startPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Size()
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
ret += sizeof(int);
|
||||||
|
ret += sizeof(bool);
|
||||||
|
ret += sizeof(short);
|
||||||
|
|
||||||
|
if (needReadScriptHash)
|
||||||
|
ret += Hash.kSize;
|
||||||
|
|
||||||
|
ret += Hash.kSize;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ObjectInfo
|
||||||
|
{
|
||||||
|
public const int kSize = 12;
|
||||||
|
|
||||||
|
public uint dataPos;
|
||||||
|
public uint size;
|
||||||
|
public uint typeID;
|
||||||
|
|
||||||
|
public uint realPos; // dataPos + Header.dataOffset; // dont save
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
dataPos = br.ReadUInt32();
|
||||||
|
size = br.ReadUInt32();
|
||||||
|
typeID = br.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
bw.Write(dataPos);
|
||||||
|
bw.Write(size);
|
||||||
|
bw.Write(typeID);
|
||||||
|
return kSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ScriptType
|
||||||
|
{
|
||||||
|
public int localFileIndex;
|
||||||
|
public long localIdentifierOfBin;
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
localFileIndex = br.ReadInt32();
|
||||||
|
localIdentifierOfBin = br.AlignedReadInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
long startPos = bw.BaseStream.Position;
|
||||||
|
bw.Write(localFileIndex);
|
||||||
|
bw.AlignedWriteInt64(localIdentifierOfBin);
|
||||||
|
return bw.BaseStream.Position - startPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ExternalInfo
|
||||||
|
{
|
||||||
|
public string dummy;
|
||||||
|
public Hash guid;
|
||||||
|
public int type;
|
||||||
|
public string name;
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
dummy = br.ReadRawString();
|
||||||
|
guid.LoadFromStream(br);
|
||||||
|
type = br.ReadInt32();
|
||||||
|
name = br.ReadRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
long startPos = bw.BaseStream.Position;
|
||||||
|
bw.WriteRawString(dummy);
|
||||||
|
guid.SaveToStream(bw);
|
||||||
|
bw.Write(type);
|
||||||
|
bw.WriteRawString(name);
|
||||||
|
return bw.BaseStream.Position - startPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ScriptsData
|
||||||
|
{
|
||||||
|
public ScriptID[] scriptIDs;
|
||||||
|
public List<string> dllNames;
|
||||||
|
public List<int> dllTypes; // 16 is user type
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
int count = br.ReadInt32();
|
||||||
|
scriptIDs = new ScriptID[count];
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
scriptIDs[i].LoadFromStream(br);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int count = br.ReadInt32();
|
||||||
|
dllNames = new List<string>(count);
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
dllNames.Add(br.ReadSizeString());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int count = br.ReadInt32();
|
||||||
|
dllTypes = new List<int>(count);
|
||||||
|
for(var i = 0; i < count; i++)
|
||||||
|
dllTypes.Add(br.ReadInt32());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
long startPos = bw.BaseStream.Position;
|
||||||
|
bw.Write(scriptIDs.Length);
|
||||||
|
for(int i = 0; i < scriptIDs.Length; i++)
|
||||||
|
scriptIDs[i].SaveToStream(bw);
|
||||||
|
|
||||||
|
bw.Write(dllNames.Count);
|
||||||
|
for(int i = 0, imax = dllNames.Count; i < imax; i++)
|
||||||
|
bw.WriteSizeString(dllNames[i]);
|
||||||
|
|
||||||
|
bw.Write(dllTypes.Count);
|
||||||
|
for(int i = 0, imax = dllTypes.Count; i < imax; i++)
|
||||||
|
bw.Write(dllTypes[i]);
|
||||||
|
|
||||||
|
return bw.BaseStream.Position - startPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ScriptID
|
||||||
|
{
|
||||||
|
public int fileID;
|
||||||
|
public long pathID; // localIdentifier
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
fileID = br.ReadInt32();
|
||||||
|
pathID = br.ReadInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
bw.Write(fileID);
|
||||||
|
bw.Write(pathID);
|
||||||
|
return 4 + 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Hash
|
||||||
|
{
|
||||||
|
public const int kSize = 16;
|
||||||
|
|
||||||
|
public int[] data;
|
||||||
|
|
||||||
|
public void LoadFromStream(BinaryReader br)
|
||||||
|
{
|
||||||
|
data = new int[4];
|
||||||
|
for(int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
data[i] = br.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SaveToStream(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
bw.Write(data[i]);
|
||||||
|
}
|
||||||
|
return kSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 96788c7fe08d5d54d95a87cfbdcb643a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bf7c4cf970660614fb54d838ec6e7eda
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
65
Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs
Normal file
65
Assets/TEngine/Scripts/Editor/HybridCLR/CompileDllHelper.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Build.Player;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor
|
||||||
|
{
|
||||||
|
internal class CompileDllHelper
|
||||||
|
{
|
||||||
|
public static void CompileDll(string buildDir, BuildTarget target)
|
||||||
|
{
|
||||||
|
var group = BuildPipeline.GetBuildTargetGroup(target);
|
||||||
|
|
||||||
|
ScriptCompilationSettings scriptCompilationSettings = new ScriptCompilationSettings();
|
||||||
|
scriptCompilationSettings.group = group;
|
||||||
|
scriptCompilationSettings.target = target;
|
||||||
|
Directory.CreateDirectory(buildDir);
|
||||||
|
ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, buildDir);
|
||||||
|
foreach (var ass in scriptCompilationResult.assemblies)
|
||||||
|
{
|
||||||
|
Debug.LogFormat("compile assemblies:{1}/{0}", ass, buildDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CompileDll(BuildTarget target)
|
||||||
|
{
|
||||||
|
CompileDll(BuildConfig.GetHotFixDllsOutputDirByTarget(target), target);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/CompileDll/ActiveBuildTarget")]
|
||||||
|
public static void CompileDllActiveBuildTarget()
|
||||||
|
{
|
||||||
|
CompileDll(EditorUserBuildSettings.activeBuildTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/CompileDll/Win32")]
|
||||||
|
public static void CompileDllWin32()
|
||||||
|
{
|
||||||
|
CompileDll(BuildTarget.StandaloneWindows);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/CompileDll/Win64")]
|
||||||
|
public static void CompileDllWin64()
|
||||||
|
{
|
||||||
|
CompileDll(BuildTarget.StandaloneWindows64);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/CompileDll/Android")]
|
||||||
|
public static void CompileDllAndroid()
|
||||||
|
{
|
||||||
|
CompileDll(BuildTarget.Android);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/CompileDll/IOS")]
|
||||||
|
public static void CompileDllIOS()
|
||||||
|
{
|
||||||
|
CompileDll(BuildTarget.iOS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 91aca02079207d746b74aeea1b595127
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/TEngine/Scripts/Editor/HybridCLR/Generators.meta
Normal file
8
Assets/TEngine/Scripts/Editor/HybridCLR/Generators.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f85b93bff3478c045ae862a7cc216b18
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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*";
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e1465458aaad6884d95af7c4fc2de7a1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators
|
||||||
|
{
|
||||||
|
public class FileRegionReplace
|
||||||
|
{
|
||||||
|
private readonly string _tplFile;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, string> _regionReplaceContents = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
public FileRegionReplace(string tplFile)
|
||||||
|
{
|
||||||
|
_tplFile = tplFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Replace(string regionName, string regionContent)
|
||||||
|
{
|
||||||
|
_regionReplaceContents.Add(regionName, regionContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit(string outputFile)
|
||||||
|
{
|
||||||
|
string originContent = File.ReadAllText(_tplFile, Encoding.UTF8);
|
||||||
|
|
||||||
|
string resultContent = originContent;
|
||||||
|
|
||||||
|
foreach (var c in _regionReplaceContents)
|
||||||
|
{
|
||||||
|
resultContent = TemplateUtil.ReplaceRegion(resultContent, c.Key, c.Value);
|
||||||
|
}
|
||||||
|
var utf8WithoutBOM = new System.Text.UTF8Encoding(false);
|
||||||
|
File.WriteAllText(outputFile, resultContent, utf8WithoutBOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5aa6559bd1b1e4488aa6746eda8202b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators
|
||||||
|
{
|
||||||
|
internal class GeneratorConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 目前已经根据热更新dll的依赖自动计算需要扫描哪些dll来收集桥接函数。
|
||||||
|
/// 只要你的热更新以assembly def形式放到项目中,是不需要改这个的
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<string> GetExtraAssembiles()
|
||||||
|
{
|
||||||
|
return new List<string>
|
||||||
|
{
|
||||||
|
// "mscorlib",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 暂时没有仔细扫描泛型,如果运行时发现有生成缺失,先手动在此添加类
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<Type> PrepareCustomGenericTypes()
|
||||||
|
{
|
||||||
|
return new List<Type>
|
||||||
|
{
|
||||||
|
typeof(Action<int, string, Vector3>),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 如果提示缺失桥接函数,将提示缺失的签名加入到下列列表是简单的做法。
|
||||||
|
/// 这里添加64位App缺失的桥接函数签名
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<string> PrepareCustomMethodSignatures64()
|
||||||
|
{
|
||||||
|
return new List<string>
|
||||||
|
{
|
||||||
|
"vi8i8",
|
||||||
|
"i4i8i8i4i4i8i8",
|
||||||
|
"i8i8S12",
|
||||||
|
"S12i8S12",
|
||||||
|
"S12i8S12S12",
|
||||||
|
"i16i8i16i16",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 如果提示缺失桥接函数,将提示缺失的签名加入到下列列表是简单的做法。
|
||||||
|
/// 这里添加32位App缺失的桥接函数签名
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<string> PrepareCustomMethodSignatures32()
|
||||||
|
{
|
||||||
|
return new List<string>
|
||||||
|
{
|
||||||
|
"vi4i4",
|
||||||
|
"S12i4S12S12",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1d14f8f858560948ae1dfa0fa572e51
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4aa66b41c89ed7742ad067898725f7ff
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
public interface IPlatformAdaptor
|
||||||
|
{
|
||||||
|
bool IsArch32 { get; }
|
||||||
|
|
||||||
|
TypeInfo CreateTypeInfo(Type type, bool returnValue);
|
||||||
|
|
||||||
|
void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> outputLines);
|
||||||
|
|
||||||
|
void GenerateManaged2NativeStub(List<MethodBridgeSig> methods, List<string> lines);
|
||||||
|
|
||||||
|
void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> outputLines);
|
||||||
|
|
||||||
|
void GenerateNative2ManagedStub(List<MethodBridgeSig> methods, List<string> lines);
|
||||||
|
|
||||||
|
void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> outputLines);
|
||||||
|
|
||||||
|
void GenerateAdjustThunkStub(List<MethodBridgeSig> methods, List<string> lines);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 983dbdeb1fb7d0b43a863c6b22160ace
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,353 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
|
||||||
|
public class TypeGenInfo
|
||||||
|
{
|
||||||
|
public Type Type { get; set; }
|
||||||
|
|
||||||
|
public List<MethodInfo> GenericMethods { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MethodBridgeGeneratorOptions
|
||||||
|
{
|
||||||
|
public List<Assembly> HotfixAssemblies { get; set; }
|
||||||
|
|
||||||
|
public List<Assembly> AllAssemblies { get; set; }
|
||||||
|
|
||||||
|
public PlatformABI CallConvention { get; set; }
|
||||||
|
|
||||||
|
public string OutputFile { get; set; }
|
||||||
|
|
||||||
|
public bool Optimized { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MethodBridgeGenerator
|
||||||
|
{
|
||||||
|
private readonly HashSet<Assembly> _hotfixAssemblies;
|
||||||
|
|
||||||
|
private readonly List<Assembly> _assemblies;
|
||||||
|
|
||||||
|
private readonly PlatformABI _callConvention;
|
||||||
|
|
||||||
|
private readonly string _outputFile;
|
||||||
|
|
||||||
|
public readonly bool _optimized;
|
||||||
|
|
||||||
|
private readonly IPlatformAdaptor _platformAdaptor;
|
||||||
|
|
||||||
|
private readonly HashSet<MethodBridgeSig> _managed2nativeMethodSet = new HashSet<MethodBridgeSig>();
|
||||||
|
|
||||||
|
private List<MethodBridgeSig> _managed2nativeMethodList;
|
||||||
|
|
||||||
|
private readonly HashSet<MethodBridgeSig> _native2managedMethodSet = new HashSet<MethodBridgeSig>();
|
||||||
|
|
||||||
|
private List<MethodBridgeSig> _native2managedMethodList;
|
||||||
|
|
||||||
|
private readonly HashSet<MethodBridgeSig> _adjustThunkMethodSet = new HashSet<MethodBridgeSig>();
|
||||||
|
|
||||||
|
private List<MethodBridgeSig> _adjustThunkMethodList;
|
||||||
|
|
||||||
|
public bool IsHotFixType(Type type)
|
||||||
|
{
|
||||||
|
return _hotfixAssemblies.Contains(type.Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodBridgeGenerator(MethodBridgeGeneratorOptions options)
|
||||||
|
{
|
||||||
|
_hotfixAssemblies = new HashSet<Assembly>(options.HotfixAssemblies);
|
||||||
|
_assemblies = options.AllAssemblies;
|
||||||
|
_callConvention = options.CallConvention;
|
||||||
|
_outputFile = options.OutputFile;
|
||||||
|
_platformAdaptor = CreatePlatformAdaptor(options.CallConvention);
|
||||||
|
_optimized = options.Optimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IPlatformAdaptor CreatePlatformAdaptor(PlatformABI type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case PlatformABI.Universal32: return new PlatformAdaptor_Universal32();
|
||||||
|
case PlatformABI.Universal64: return new PlatformAdaptor_Universal64();
|
||||||
|
case PlatformABI.Arm64: return new PlatformAdaptor_Arm64();
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTemplateFile()
|
||||||
|
{
|
||||||
|
string tplFile;
|
||||||
|
|
||||||
|
switch (_callConvention)
|
||||||
|
{
|
||||||
|
case PlatformABI.Universal32: tplFile = "Universal32"; break;
|
||||||
|
case PlatformABI.Universal64: tplFile = "Universal64"; break;
|
||||||
|
case PlatformABI.Arm64: tplFile = "Arm64"; break;
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
};
|
||||||
|
return $"{Application.dataPath}/Editor/HybridCLR/Generators/Templates/MethodBridge_{tplFile}.cpp";
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<TypeGenInfo> GetGenerateTypes()
|
||||||
|
{
|
||||||
|
return new List<TypeGenInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodBridgeSig CreateMethodBridgeSig(bool isStatic, ParameterInfo returnType, ParameterInfo[] parameters)
|
||||||
|
{
|
||||||
|
var paramInfos = new List<ParamInfo>();
|
||||||
|
if (!isStatic)
|
||||||
|
{
|
||||||
|
// FIXME arm32 is s_i4u4
|
||||||
|
paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.IsArch32 ? TypeInfo.s_i4u4 : TypeInfo.s_i8u8 });
|
||||||
|
}
|
||||||
|
foreach (var paramInfo in parameters)
|
||||||
|
{
|
||||||
|
paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.CreateTypeInfo(paramInfo.ParameterType, false) });
|
||||||
|
}
|
||||||
|
var mbs = new MethodBridgeSig()
|
||||||
|
{
|
||||||
|
ReturnInfo = new ReturnInfo() { Type = returnType != null ? _platformAdaptor.CreateTypeInfo(returnType.ParameterType, true) : TypeInfo.s_void },
|
||||||
|
ParamInfos = paramInfos,
|
||||||
|
};
|
||||||
|
return mbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddManaged2NativeMethod(MethodBridgeSig method)
|
||||||
|
{
|
||||||
|
if (_managed2nativeMethodSet.Add(method))
|
||||||
|
{
|
||||||
|
method.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddNative2ManagedMethod(MethodBridgeSig method)
|
||||||
|
{
|
||||||
|
if (_native2managedMethodSet.Add(method))
|
||||||
|
{
|
||||||
|
method.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAdjustThunkMethod(MethodBridgeSig method)
|
||||||
|
{
|
||||||
|
if (_adjustThunkMethodSet.Add(method))
|
||||||
|
{
|
||||||
|
method.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScanType(Type type)
|
||||||
|
{
|
||||||
|
if (type.IsGenericTypeDefinition)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_optimized)
|
||||||
|
{
|
||||||
|
if (!type.IsNested)
|
||||||
|
{
|
||||||
|
if (!type.IsPublic)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type.IsNestedPrivate)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var typeDel = typeof(MulticastDelegate);
|
||||||
|
if (typeDel.IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
var method = type.GetMethod("Invoke");
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
//Debug.LogError($"delegate:{typeDel.FullName} Invoke not exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Debug.Log($"== delegate:{type}");
|
||||||
|
var instanceCallMethod = CreateMethodBridgeSig(false, method.ReturnParameter, method.GetParameters());
|
||||||
|
AddManaged2NativeMethod(instanceCallMethod);
|
||||||
|
AddNative2ManagedMethod(instanceCallMethod);
|
||||||
|
|
||||||
|
var staticCallMethod = CreateMethodBridgeSig(true, method.ReturnParameter, method.GetParameters());
|
||||||
|
AddManaged2NativeMethod(staticCallMethod);
|
||||||
|
AddNative2ManagedMethod(staticCallMethod);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public
|
||||||
|
| BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
|
||||||
|
{
|
||||||
|
if (method.IsGenericMethodDefinition)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_optimized && (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_optimized || (method.IsFamily || method.IsPublic))
|
||||||
|
{
|
||||||
|
var m2nMethod = CreateMethodBridgeSig(method.IsStatic, method.ReturnParameter, method.GetParameters());
|
||||||
|
AddManaged2NativeMethod(m2nMethod);
|
||||||
|
|
||||||
|
if (type.IsValueType && !method.IsStatic)
|
||||||
|
{
|
||||||
|
var adjustThunkMethod = CreateMethodBridgeSig(false, method.ReturnParameter, method.GetParameters());
|
||||||
|
AddAdjustThunkMethod(adjustThunkMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.IsVirtual)
|
||||||
|
{
|
||||||
|
AddNative2ManagedMethod(m2nMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var method in type.GetConstructors(BindingFlags.Instance | BindingFlags.Public
|
||||||
|
| BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy))
|
||||||
|
{
|
||||||
|
if (_optimized && (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_optimized || (method.IsFamily || method.IsPublic))
|
||||||
|
{
|
||||||
|
var callMethod = CreateMethodBridgeSig(false, null, method.GetParameters());
|
||||||
|
AddManaged2NativeMethod(callMethod);
|
||||||
|
|
||||||
|
if (type.IsValueType && !method.IsStatic)
|
||||||
|
{
|
||||||
|
var invokeMethod = CreateMethodBridgeSig(false, null, method.GetParameters());
|
||||||
|
AddAdjustThunkMethod(invokeMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var subType in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
|
{
|
||||||
|
ScanType(subType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFromAssemblies()
|
||||||
|
{
|
||||||
|
foreach (var ass in _assemblies)
|
||||||
|
{
|
||||||
|
if (_hotfixAssemblies.Contains(ass))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Debug.Log("prepare assembly:" + ass.FullName);
|
||||||
|
foreach (var type in ass.GetTypes())
|
||||||
|
{
|
||||||
|
ScanType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrepareMethodsFromCustomeGenericTypes()
|
||||||
|
{
|
||||||
|
foreach (var type in GeneratorConfig.PrepareCustomGenericTypes())
|
||||||
|
{
|
||||||
|
ScanType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareMethods()
|
||||||
|
{
|
||||||
|
PrepareMethodsFromCustomeGenericTypes();
|
||||||
|
|
||||||
|
|
||||||
|
foreach(var methodSig in _platformAdaptor.IsArch32 ? GeneratorConfig.PrepareCustomMethodSignatures32() : GeneratorConfig.PrepareCustomMethodSignatures64())
|
||||||
|
{
|
||||||
|
var method = MethodBridgeSig.CreateBySignatuer(methodSig);
|
||||||
|
AddManaged2NativeMethod(method);
|
||||||
|
AddAdjustThunkMethod(method);
|
||||||
|
}
|
||||||
|
PrepareFromAssemblies();
|
||||||
|
|
||||||
|
{
|
||||||
|
var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
|
||||||
|
foreach (var method in _managed2nativeMethodSet)
|
||||||
|
{
|
||||||
|
sortedMethods.Add(method.CreateCallSigName(), method);
|
||||||
|
}
|
||||||
|
_managed2nativeMethodList = sortedMethods.Values.ToList();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
|
||||||
|
foreach (var method in _native2managedMethodSet)
|
||||||
|
{
|
||||||
|
sortedMethods.Add(method.CreateCallSigName(), method);
|
||||||
|
}
|
||||||
|
_native2managedMethodList = sortedMethods.Values.ToList();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var sortedMethods = new SortedDictionary<string, MethodBridgeSig>();
|
||||||
|
foreach (var method in _adjustThunkMethodSet)
|
||||||
|
{
|
||||||
|
sortedMethods.Add(method.CreateCallSigName(), method);
|
||||||
|
}
|
||||||
|
_adjustThunkMethodList = sortedMethods.Values.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Generate()
|
||||||
|
{
|
||||||
|
var frr = new FileRegionReplace(GetTemplateFile());
|
||||||
|
|
||||||
|
List<string> lines = new List<string>(20_0000);
|
||||||
|
|
||||||
|
Debug.LogFormat("== managed2native method count:{0}", _managed2nativeMethodList.Count);
|
||||||
|
|
||||||
|
foreach(var method in _managed2nativeMethodList)
|
||||||
|
{
|
||||||
|
_platformAdaptor.GenerateManaged2NativeMethod(method, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
_platformAdaptor.GenerateManaged2NativeStub(_managed2nativeMethodList, lines);
|
||||||
|
|
||||||
|
Debug.LogFormat("== native2managed method count:{0}", _native2managedMethodList.Count);
|
||||||
|
|
||||||
|
foreach (var method in _native2managedMethodList)
|
||||||
|
{
|
||||||
|
_platformAdaptor.GenerateNative2ManagedMethod(method, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
_platformAdaptor.GenerateNative2ManagedStub(_native2managedMethodList, lines);
|
||||||
|
|
||||||
|
Debug.LogFormat("== adjustThunk method count:{0}", _adjustThunkMethodList.Count);
|
||||||
|
|
||||||
|
foreach (var method in _adjustThunkMethodList)
|
||||||
|
{
|
||||||
|
_platformAdaptor.GenerateAdjustThunkMethod(method, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
_platformAdaptor.GenerateAdjustThunkStub(_adjustThunkMethodList, lines);
|
||||||
|
|
||||||
|
frr.Replace("INVOKE_STUB", string.Join("\n", lines));
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));
|
||||||
|
|
||||||
|
frr.Commit(_outputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3171ed79d43e06c4b8c889b0a3f38969
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,156 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
public class MethodBridgeSig : IEquatable<MethodBridgeSig>
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly static Regex s_sigPattern = new Regex(@"^(v|i1|i2|i4|i8|r4|r8|i16|sr|vf2|vf3|vf4|vd2|vd3|vd4|S\d+|A\d+|B\d+|C\d+)+$");
|
||||||
|
|
||||||
|
public static MethodBridgeSig CreateBySignatuer(string sigName)
|
||||||
|
{
|
||||||
|
var re = s_sigPattern.Match(sigName);
|
||||||
|
if (!re.Success)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"{sigName} is not valid signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
var mbs = new MethodBridgeSig() { ParamInfos = new List<ParamInfo>()};
|
||||||
|
var sigs = re.Groups[1].Captures;
|
||||||
|
mbs.ReturnInfo = new ReturnInfo() { Type = CreateTypeInfoBySignature(sigs[0].Value)};
|
||||||
|
for(int i = 1; i < sigs.Count; i++)
|
||||||
|
{
|
||||||
|
mbs.ParamInfos.Add(new ParamInfo() { Type = CreateTypeInfoBySignature(sigs[i].Value)});
|
||||||
|
}
|
||||||
|
return mbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static TypeInfo CreateTypeInfoBySignature(string sigName)
|
||||||
|
{
|
||||||
|
switch(sigName)
|
||||||
|
{
|
||||||
|
case "v": return new TypeInfo(typeof(void), ParamOrReturnType.VOID);
|
||||||
|
case "i1": return new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1);
|
||||||
|
case "i2": return new TypeInfo(typeof(short), ParamOrReturnType.I2_U2);
|
||||||
|
case "i4": return new TypeInfo(typeof(int), ParamOrReturnType.I4_U4);
|
||||||
|
case "i8": return new TypeInfo(typeof(long), ParamOrReturnType.I8_U8);
|
||||||
|
case "r4": return new TypeInfo(typeof(float), ParamOrReturnType.R4);
|
||||||
|
case "r8": return new TypeInfo(typeof(double), ParamOrReturnType.R8);
|
||||||
|
case "i16": return TypeInfo.s_i16;
|
||||||
|
case "sr": return TypeInfo.s_ref;
|
||||||
|
case "vf2": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_FLOAT_2);
|
||||||
|
case "vf3": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_FLOAT_3);
|
||||||
|
case "vf4": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_FLOAT_4);
|
||||||
|
case "vd2": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_DOUBLE_2);
|
||||||
|
case "vd3": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_DOUBLE_3);
|
||||||
|
case "vd4": return new TypeInfo(null, ParamOrReturnType.ARM64_HFA_DOUBLE_4);
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (sigName.StartsWith("S"))
|
||||||
|
{
|
||||||
|
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN1, int.Parse(sigName.Substring(1)));
|
||||||
|
}
|
||||||
|
if (sigName.StartsWith("A"))
|
||||||
|
{
|
||||||
|
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN2, int.Parse(sigName.Substring(1)));
|
||||||
|
}
|
||||||
|
if (sigName.StartsWith("B"))
|
||||||
|
{
|
||||||
|
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN4, int.Parse(sigName.Substring(1)));
|
||||||
|
}
|
||||||
|
if (sigName.StartsWith("C"))
|
||||||
|
{
|
||||||
|
return new TypeInfo(null, ParamOrReturnType.STRUCTURE_ALIGN8, int.Parse(sigName.Substring(1)));
|
||||||
|
}
|
||||||
|
throw new ArgumentException($"invalid signature:{sigName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ReturnInfo ReturnInfo { get; set; }
|
||||||
|
|
||||||
|
public List<ParamInfo> ParamInfos { get; set; }
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < ParamInfos.Count; i++)
|
||||||
|
{
|
||||||
|
ParamInfos[i].Index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateCallSigName()
|
||||||
|
{
|
||||||
|
var n = new StringBuilder();
|
||||||
|
n.Append(ReturnInfo.Type.CreateSigName());
|
||||||
|
foreach(var param in ParamInfos)
|
||||||
|
{
|
||||||
|
n.Append(param.Type.CreateSigName());
|
||||||
|
}
|
||||||
|
return n.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateInvokeSigName()
|
||||||
|
{
|
||||||
|
var n = new StringBuilder();
|
||||||
|
n.Append(ReturnInfo.Type.CreateSigName());
|
||||||
|
foreach (var param in ParamInfos)
|
||||||
|
{
|
||||||
|
n.Append(param.Type.CreateSigName());
|
||||||
|
}
|
||||||
|
return n.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals((MethodBridgeSig)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(MethodBridgeSig other)
|
||||||
|
{
|
||||||
|
if (other == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReturnInfo.Type.Equals(other.ReturnInfo.Type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ParamInfos.Count != other.ParamInfos.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < ParamInfos.Count; i++)
|
||||||
|
{
|
||||||
|
if (!ParamInfos[i].Type.Equals(other.ParamInfos[i].Type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 17;
|
||||||
|
|
||||||
|
hash = hash * 23 + ReturnInfo.Type.GetHashCode();
|
||||||
|
|
||||||
|
foreach(var p in ParamInfos)
|
||||||
|
{
|
||||||
|
hash = hash * 23 + p.Type.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cbe63fb39b5bdcc448f3589b475fb5d8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c04e163336b93af44b4b565d0ab195e2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7886905937fafb64999dc4e358565982
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
|||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
public enum PlatformABI
|
||||||
|
{
|
||||||
|
Universal32,
|
||||||
|
Universal64,
|
||||||
|
Arm64,
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: deee8d06a7f05bf4d9ab4b78f0a8a47c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,160 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
internal abstract class PlatformAdaptorBase : IPlatformAdaptor
|
||||||
|
{
|
||||||
|
public abstract bool IsArch32 { get; }
|
||||||
|
|
||||||
|
public abstract TypeInfo PointerType { get; }
|
||||||
|
|
||||||
|
protected abstract Dictionary<Type, TypeInfo> CacheTypes { get; }
|
||||||
|
|
||||||
|
protected abstract TypeInfo CreateValueType(Type type, bool returnValue);
|
||||||
|
|
||||||
|
public abstract void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines);
|
||||||
|
|
||||||
|
public abstract void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines);
|
||||||
|
|
||||||
|
public abstract void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> outputLines);
|
||||||
|
|
||||||
|
private static Dictionary<Type, (int, int)> _typeSizeCache64 = new Dictionary<Type, (int, int)>();
|
||||||
|
|
||||||
|
private static Dictionary<Type, (int, int)> _typeSizeCache32 = new Dictionary<Type, (int, int)>();
|
||||||
|
|
||||||
|
private static ValueTypeSizeAligmentCalculator s_calculator64 = new ValueTypeSizeAligmentCalculator(false);
|
||||||
|
|
||||||
|
private static ValueTypeSizeAligmentCalculator s_calculator32 = new ValueTypeSizeAligmentCalculator(true);
|
||||||
|
|
||||||
|
public static (int Size, int Aligment) ComputeSizeAndAligmentOfArch64(Type t)
|
||||||
|
{
|
||||||
|
if (_typeSizeCache64.TryGetValue(t, out var sizeAndAligment))
|
||||||
|
{
|
||||||
|
return sizeAndAligment;
|
||||||
|
}
|
||||||
|
// all this just to invoke one opcode with no arguments!
|
||||||
|
var method = new DynamicMethod("ComputeSizeOfImpl", typeof(int), Type.EmptyTypes, typeof(PlatformAdaptorBase), false);
|
||||||
|
var gen = method.GetILGenerator();
|
||||||
|
gen.Emit(OpCodes.Sizeof, t);
|
||||||
|
gen.Emit(OpCodes.Ret);
|
||||||
|
int clrSize = ((Func<int>)method.CreateDelegate(typeof(Func<int>)))();
|
||||||
|
|
||||||
|
sizeAndAligment = s_calculator64.SizeAndAligmentOf(t);
|
||||||
|
int customSize = sizeAndAligment.Item1;
|
||||||
|
if (customSize != clrSize)
|
||||||
|
{
|
||||||
|
s_calculator64.SizeAndAligmentOf(t);
|
||||||
|
Debug.LogError($"type:{t} size calculate error. HybridCLR Comput:{sizeAndAligment} CLR:{clrSize}");
|
||||||
|
}
|
||||||
|
_typeSizeCache64.Add(t, sizeAndAligment);
|
||||||
|
return sizeAndAligment;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static (int Size, int Aligment) ComputeSizeAndAligmentOfArch32(Type t)
|
||||||
|
{
|
||||||
|
if (_typeSizeCache32.TryGetValue(t, out var sa))
|
||||||
|
{
|
||||||
|
return sa;
|
||||||
|
}
|
||||||
|
// all this just to invoke one opcode with no arguments!
|
||||||
|
sa = s_calculator32.SizeAndAligmentOf(t);
|
||||||
|
_typeSizeCache32.Add(t, sa);
|
||||||
|
return sa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeInfo CreateTypeInfo(Type type, bool returnValue)
|
||||||
|
{
|
||||||
|
if (type.IsByRef)
|
||||||
|
{
|
||||||
|
return PointerType;
|
||||||
|
}
|
||||||
|
if (type == typeof(void))
|
||||||
|
{
|
||||||
|
return TypeInfo.s_void;
|
||||||
|
}
|
||||||
|
if (!type.IsValueType)
|
||||||
|
{
|
||||||
|
return PointerType;
|
||||||
|
}
|
||||||
|
if (CacheTypes.TryGetValue(type, out var cache))
|
||||||
|
{
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
if (type.IsEnum)
|
||||||
|
{
|
||||||
|
return CreateTypeInfo(type.GetEnumUnderlyingType(), returnValue);
|
||||||
|
}
|
||||||
|
var ti = CreateValueType(type, returnValue);
|
||||||
|
// s_typeInfoCaches.Add(type, ti);
|
||||||
|
return ti;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static TypeInfo CreateGeneralValueType(Type type, int size, int aligment)
|
||||||
|
{
|
||||||
|
Debug.Assert(size % aligment == 0);
|
||||||
|
switch (aligment)
|
||||||
|
{
|
||||||
|
case 1: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN1, size);
|
||||||
|
case 2: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN2, size);
|
||||||
|
case 4: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN4, size);
|
||||||
|
case 8: return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN8, size);
|
||||||
|
default: throw new NotSupportedException($"type:{type} not support aligment:{aligment}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateManaged2NativeStub(List<MethodBridgeSig> methods, List<string> lines)
|
||||||
|
{
|
||||||
|
lines.Add($@"
|
||||||
|
Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] =
|
||||||
|
{{
|
||||||
|
");
|
||||||
|
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", __M2N_{method.CreateInvokeSigName()}}},");
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Add($"\t{{nullptr, nullptr}},");
|
||||||
|
lines.Add("};");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateNative2ManagedStub(List<MethodBridgeSig> methods, List<string> lines)
|
||||||
|
{
|
||||||
|
lines.Add($@"
|
||||||
|
Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] =
|
||||||
|
{{
|
||||||
|
");
|
||||||
|
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_{method.CreateInvokeSigName()}}},");
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Add($"\t{{nullptr, nullptr}},");
|
||||||
|
lines.Add("};");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateAdjustThunkStub(List<MethodBridgeSig> methods, List<string> lines)
|
||||||
|
{
|
||||||
|
lines.Add($@"
|
||||||
|
NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] =
|
||||||
|
{{
|
||||||
|
");
|
||||||
|
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_AdjustorThunk_{method.CreateCallSigName()}}},");
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Add($"\t{{nullptr, nullptr}},");
|
||||||
|
lines.Add("};");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ebf14107fd82b364cb7d61276d444829
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,208 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
internal class PlatformAdaptor_Arm64 : PlatformAdaptorBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, TypeInfo> s_typeInfoCaches = new Dictionary<Type, TypeInfo>()
|
||||||
|
{
|
||||||
|
{ typeof(void), new TypeInfo(typeof(void), ParamOrReturnType.VOID)},
|
||||||
|
{ typeof(bool), new TypeInfo(typeof(bool), ParamOrReturnType.I1_U1)},
|
||||||
|
{ typeof(byte), new TypeInfo(typeof(byte), ParamOrReturnType.I1_U1)},
|
||||||
|
{ typeof(sbyte), new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1) },
|
||||||
|
{ typeof(short), new TypeInfo(typeof(short), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(ushort), new TypeInfo(typeof(ushort), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(char), new TypeInfo(typeof(char), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(int), new TypeInfo(typeof(int), ParamOrReturnType.I4_U4) },
|
||||||
|
{ typeof(uint), new TypeInfo(typeof(uint), ParamOrReturnType.I4_U4) },
|
||||||
|
{ typeof(long), new TypeInfo(typeof(long), ParamOrReturnType.I8_U8) },
|
||||||
|
{ typeof(ulong), new TypeInfo(typeof(ulong), ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(float), new TypeInfo(typeof(float), ParamOrReturnType.R4)},
|
||||||
|
{ typeof(double), new TypeInfo(typeof(double), ParamOrReturnType.R8)},
|
||||||
|
{ typeof(IntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(UIntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(Vector2), new TypeInfo(typeof(Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
|
||||||
|
{ typeof(Vector3), new TypeInfo(typeof(Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
|
||||||
|
{ typeof(Vector4), new TypeInfo(typeof(Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
|
||||||
|
{ typeof(System.Numerics.Vector2), new TypeInfo(typeof(System.Numerics.Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
|
||||||
|
{ typeof(System.Numerics.Vector3), new TypeInfo(typeof(System.Numerics.Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
|
||||||
|
{ typeof(System.Numerics.Vector4), new TypeInfo(typeof(System.Numerics.Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
|
||||||
|
};
|
||||||
|
|
||||||
|
public PlatformABI CallConventionType { get; } = PlatformABI.Universal64;
|
||||||
|
|
||||||
|
public override bool IsArch32 => false;
|
||||||
|
|
||||||
|
public override TypeInfo PointerType => TypeInfo.s_i8u8;
|
||||||
|
|
||||||
|
protected override Dictionary<Type, TypeInfo> CacheTypes => s_typeInfoCaches;
|
||||||
|
|
||||||
|
public class HFATypeInfo
|
||||||
|
{
|
||||||
|
public Type Type { get; set; }
|
||||||
|
|
||||||
|
public int Count { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNotHFAFastCheck(int typeSize)
|
||||||
|
{
|
||||||
|
return typeSize != 8 && typeSize != 12 && typeSize != 16 && typeSize != 24 && typeSize != 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ComputHFATypeInfo0(Type type, HFATypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
Type ftype = field.FieldType;
|
||||||
|
if (ftype != typeof(float) && ftype != typeof(double))
|
||||||
|
{
|
||||||
|
if (!ftype.IsPrimitive && ftype.IsValueType)
|
||||||
|
{
|
||||||
|
if (!ComputHFATypeInfo0(ftype, typeInfo))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ftype == typeInfo.Type || typeInfo.Type == null)
|
||||||
|
{
|
||||||
|
typeInfo.Type = ftype;
|
||||||
|
++typeInfo.Count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typeInfo.Count <= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ComputHFATypeInfo(Type type, int typeSize, out HFATypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
typeInfo = new HFATypeInfo();
|
||||||
|
if (IsNotHFAFastCheck(typeSize))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ok = ComputHFATypeInfo0(type, typeInfo);
|
||||||
|
if (ok && typeInfo.Count >= 2 && typeInfo.Count <= 4)
|
||||||
|
{
|
||||||
|
int fieldSize = typeInfo.Type == typeof(float) ? 4 : 8;
|
||||||
|
return typeSize == fieldSize * typeInfo.Count;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TypeInfo CreateValueType(Type type, bool returnValue)
|
||||||
|
{
|
||||||
|
(int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch64(type);
|
||||||
|
if (ComputHFATypeInfo(type, typeSize, out HFATypeInfo hfaTypeInfo))
|
||||||
|
{
|
||||||
|
if (hfaTypeInfo.Type == typeof(float))
|
||||||
|
{
|
||||||
|
switch (hfaTypeInfo.Count)
|
||||||
|
{
|
||||||
|
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_2);
|
||||||
|
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_3);
|
||||||
|
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_4);
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Assert(hfaTypeInfo.Type == typeof(double));
|
||||||
|
switch (hfaTypeInfo.Count)
|
||||||
|
{
|
||||||
|
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_2);
|
||||||
|
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_3);
|
||||||
|
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_4);
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 64位下结构体内存对齐规则是一样的
|
||||||
|
return CreateArm64GeneralValueType(type, typeSize,returnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeInfo CreateArm64GeneralValueType(Type type, int typeSize, bool returnValue)
|
||||||
|
{
|
||||||
|
if (typeSize <= 8)
|
||||||
|
{
|
||||||
|
return TypeInfo.s_i8u8;
|
||||||
|
}
|
||||||
|
if (typeSize <= 16)
|
||||||
|
{
|
||||||
|
return TypeInfo.s_i16;
|
||||||
|
}
|
||||||
|
if (returnValue)
|
||||||
|
{
|
||||||
|
return new TypeInfo(type, ParamOrReturnType.STRUCTURE_ALIGN1, typeSize);
|
||||||
|
}
|
||||||
|
return TypeInfo.s_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
|
||||||
|
{{
|
||||||
|
typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr});
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr})
|
||||||
|
{{
|
||||||
|
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }};
|
||||||
|
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
|
||||||
|
Interpreter::Execute(method, args, ret);
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr})
|
||||||
|
{{
|
||||||
|
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }};
|
||||||
|
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
|
||||||
|
Interpreter::Execute(method, args, ret);
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bff17a6e8ee060c4eb9ac97fcf30bf78
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,96 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
internal class PlatformAdaptor_Universal32 : PlatformAdaptorBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, TypeInfo> s_typeInfoCaches = new Dictionary<Type, TypeInfo>()
|
||||||
|
{
|
||||||
|
{ typeof(void), new TypeInfo(typeof(void), ParamOrReturnType.VOID)},
|
||||||
|
{ typeof(bool), new TypeInfo(typeof(bool), ParamOrReturnType.I1_U1)},
|
||||||
|
{ typeof(byte), new TypeInfo(typeof(byte), ParamOrReturnType.I1_U1)},
|
||||||
|
{ typeof(sbyte), new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1) },
|
||||||
|
{ typeof(short), new TypeInfo(typeof(short), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(ushort), new TypeInfo(typeof(ushort), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(char), new TypeInfo(typeof(char), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(int), new TypeInfo(typeof(int), ParamOrReturnType.I4_U4) },
|
||||||
|
{ typeof(uint), new TypeInfo(typeof(uint), ParamOrReturnType.I4_U4) },
|
||||||
|
{ typeof(long), new TypeInfo(typeof(long), ParamOrReturnType.I8_U8) },
|
||||||
|
{ typeof(ulong), new TypeInfo(typeof(ulong), ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(float), new TypeInfo(typeof(float), ParamOrReturnType.R4)},
|
||||||
|
{ typeof(double), new TypeInfo(typeof(double), ParamOrReturnType.R8)},
|
||||||
|
{ typeof(IntPtr), new TypeInfo(null, ParamOrReturnType.I4_U4)},
|
||||||
|
{ typeof(UIntPtr), new TypeInfo(null, ParamOrReturnType.I4_U4)},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public PlatformABI CallConventionType { get; } = PlatformABI.Universal32;
|
||||||
|
|
||||||
|
public override bool IsArch32 => true;
|
||||||
|
|
||||||
|
public override TypeInfo PointerType => TypeInfo.s_i4u4;
|
||||||
|
|
||||||
|
protected override Dictionary<Type, TypeInfo> CacheTypes => s_typeInfoCaches;
|
||||||
|
|
||||||
|
protected override TypeInfo CreateValueType(Type type, bool returnValue)
|
||||||
|
{
|
||||||
|
(int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch32(type);
|
||||||
|
int actualAliment = typeAligment <= 4 ? 1 : 8;
|
||||||
|
return CreateGeneralValueType(type, typeSize, actualAliment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
|
||||||
|
{{
|
||||||
|
typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr});
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr})
|
||||||
|
{{
|
||||||
|
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }};
|
||||||
|
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
|
||||||
|
Interpreter::Execute(method, args, ret);
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr})
|
||||||
|
{{
|
||||||
|
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }};
|
||||||
|
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
|
||||||
|
Interpreter::Execute(method, args, ret);
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb741900113b22443a2054ddba6b131b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
internal class PlatformAdaptor_Universal64 : PlatformAdaptorBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, TypeInfo> s_typeInfoCaches = new Dictionary<Type, TypeInfo>()
|
||||||
|
{
|
||||||
|
{ typeof(void), new TypeInfo(typeof(void), ParamOrReturnType.VOID)},
|
||||||
|
{ typeof(bool), new TypeInfo(typeof(bool), ParamOrReturnType.I1_U1)},
|
||||||
|
{ typeof(byte), new TypeInfo(typeof(byte), ParamOrReturnType.I1_U1)},
|
||||||
|
{ typeof(sbyte), new TypeInfo(typeof(sbyte), ParamOrReturnType.I1_U1) },
|
||||||
|
{ typeof(short), new TypeInfo(typeof(short), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(ushort), new TypeInfo(typeof(ushort), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(char), new TypeInfo(typeof(char), ParamOrReturnType.I2_U2) },
|
||||||
|
{ typeof(int), new TypeInfo(typeof(int), ParamOrReturnType.I4_U4) },
|
||||||
|
{ typeof(uint), new TypeInfo(typeof(uint), ParamOrReturnType.I4_U4) },
|
||||||
|
{ typeof(long), new TypeInfo(typeof(long), ParamOrReturnType.I8_U8) },
|
||||||
|
{ typeof(ulong), new TypeInfo(typeof(ulong), ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(float), new TypeInfo(typeof(float), ParamOrReturnType.R4)},
|
||||||
|
{ typeof(double), new TypeInfo(typeof(double), ParamOrReturnType.R8)},
|
||||||
|
{ typeof(IntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(UIntPtr), new TypeInfo(null, ParamOrReturnType.I8_U8)},
|
||||||
|
{ typeof(Vector2), new TypeInfo(typeof(Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
|
||||||
|
{ typeof(Vector3), new TypeInfo(typeof(Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
|
||||||
|
{ typeof(Vector4), new TypeInfo(typeof(Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
|
||||||
|
{ typeof(System.Numerics.Vector2), new TypeInfo(typeof(System.Numerics.Vector2), ParamOrReturnType.ARM64_HFA_FLOAT_2) },
|
||||||
|
{ typeof(System.Numerics.Vector3), new TypeInfo(typeof(System.Numerics.Vector3), ParamOrReturnType.ARM64_HFA_FLOAT_3) },
|
||||||
|
{ typeof(System.Numerics.Vector4), new TypeInfo(typeof(System.Numerics.Vector4), ParamOrReturnType.ARM64_HFA_FLOAT_4) },
|
||||||
|
};
|
||||||
|
|
||||||
|
public PlatformABI CallConventionType { get; } = PlatformABI.Universal64;
|
||||||
|
|
||||||
|
public override bool IsArch32 => false;
|
||||||
|
|
||||||
|
public override TypeInfo PointerType => TypeInfo.s_i8u8;
|
||||||
|
|
||||||
|
protected override Dictionary<Type, TypeInfo> CacheTypes => s_typeInfoCaches;
|
||||||
|
|
||||||
|
public class HFATypeInfo
|
||||||
|
{
|
||||||
|
public Type Type { get; set; }
|
||||||
|
|
||||||
|
public int Count { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNotHFAFastCheck(int typeSize)
|
||||||
|
{
|
||||||
|
return typeSize != 8 && typeSize != 12 && typeSize != 16 && typeSize != 24 && typeSize != 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ComputHFATypeInfo0(Type type, HFATypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
Type ftype = field.FieldType;
|
||||||
|
if (ftype != typeof(float) && ftype != typeof(double))
|
||||||
|
{
|
||||||
|
if (!ftype.IsPrimitive && ftype.IsValueType)
|
||||||
|
{
|
||||||
|
if (!ComputHFATypeInfo0(ftype, typeInfo))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ftype == typeInfo.Type || typeInfo.Type == null)
|
||||||
|
{
|
||||||
|
typeInfo.Type = ftype;
|
||||||
|
++typeInfo.Count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typeInfo.Count <= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ComputHFATypeInfo(Type type, int typeSize, out HFATypeInfo typeInfo)
|
||||||
|
{
|
||||||
|
typeInfo = new HFATypeInfo();
|
||||||
|
if (IsNotHFAFastCheck(typeSize))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ok = ComputHFATypeInfo0(type, typeInfo);
|
||||||
|
if (ok && typeInfo.Count >= 2 && typeInfo.Count <= 4)
|
||||||
|
{
|
||||||
|
int fieldSize = typeInfo.Type == typeof(float) ? 4 : 8;
|
||||||
|
return typeSize == fieldSize * typeInfo.Count;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TypeInfo CreateValueType(Type type, bool returnValue)
|
||||||
|
{
|
||||||
|
(int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch64(type);
|
||||||
|
if (ComputHFATypeInfo(type, typeSize, out HFATypeInfo hfaTypeInfo))
|
||||||
|
{
|
||||||
|
if (hfaTypeInfo.Type == typeof(float))
|
||||||
|
{
|
||||||
|
switch (hfaTypeInfo.Count)
|
||||||
|
{
|
||||||
|
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_2);
|
||||||
|
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_3);
|
||||||
|
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_FLOAT_4);
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Assert(hfaTypeInfo.Type == typeof(double));
|
||||||
|
switch (hfaTypeInfo.Count)
|
||||||
|
{
|
||||||
|
case 2: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_2);
|
||||||
|
case 3: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_3);
|
||||||
|
case 4: return new TypeInfo(type, ParamOrReturnType.ARM64_HFA_DOUBLE_4);
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 64位下结构体内存对齐规则是一样的
|
||||||
|
return CreateGeneralValueType(type, typeSize, 1 /*typeAligment*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
string paramTypeListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()}").Concat(new string[] { "const MethodInfo*" })); ;
|
||||||
|
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" }));
|
||||||
|
|
||||||
|
lines.Add($@"
|
||||||
|
static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
|
||||||
|
{{
|
||||||
|
typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr});
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr});
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
lines.Add($@"
|
||||||
|
static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr})
|
||||||
|
{{
|
||||||
|
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }};
|
||||||
|
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
|
||||||
|
Interpreter::Execute(method, args, ret);
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List<string> lines)
|
||||||
|
{
|
||||||
|
int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType);
|
||||||
|
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" }));
|
||||||
|
lines.Add($@"
|
||||||
|
static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr})
|
||||||
|
{{
|
||||||
|
StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }};
|
||||||
|
StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)};
|
||||||
|
Interpreter::Execute(method, args, ret);
|
||||||
|
{(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")}
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 26324be9505c9f54996bcbb62ba49132
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class SignatureProviderAttribute : Attribute
|
||||||
|
{
|
||||||
|
public List<PlatformABI> Platforms { get; }
|
||||||
|
|
||||||
|
public SignatureProviderAttribute(params PlatformABI[] platforms)
|
||||||
|
{
|
||||||
|
Platforms = new List<PlatformABI>(platforms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 50c5ec2d43d3fda4a8b8cfa75cb37a74
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,132 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
public class TypeInfo : IEquatable<TypeInfo>
|
||||||
|
{
|
||||||
|
|
||||||
|
public static readonly TypeInfo s_void = new TypeInfo(typeof(void), ParamOrReturnType.VOID);
|
||||||
|
public static readonly TypeInfo s_i4u4 = new TypeInfo(null, ParamOrReturnType.I4_U4);
|
||||||
|
public static readonly TypeInfo s_i8u8 = new TypeInfo(null, ParamOrReturnType.I8_U8);
|
||||||
|
public static readonly TypeInfo s_i16 = new TypeInfo(null, ParamOrReturnType.I16);
|
||||||
|
public static readonly TypeInfo s_ref = new TypeInfo(null, ParamOrReturnType.STRUCTURE_AS_REF_PARAM);
|
||||||
|
|
||||||
|
public TypeInfo(Type type, ParamOrReturnType portype)
|
||||||
|
{
|
||||||
|
this.Type = type;
|
||||||
|
PorType = portype;
|
||||||
|
Size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeInfo(Type type, ParamOrReturnType portype, int size)
|
||||||
|
{
|
||||||
|
this.Type = type;
|
||||||
|
PorType = portype;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type Type { get; }
|
||||||
|
|
||||||
|
public ParamOrReturnType PorType { get; }
|
||||||
|
|
||||||
|
public int Size { get; }
|
||||||
|
|
||||||
|
public bool Equals(TypeInfo other)
|
||||||
|
{
|
||||||
|
return PorType == other.PorType && Size == other.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals((TypeInfo)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return (int)PorType * 23 + Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateSigName()
|
||||||
|
{
|
||||||
|
switch (PorType)
|
||||||
|
{
|
||||||
|
case ParamOrReturnType.VOID: return "v";
|
||||||
|
case ParamOrReturnType.I1_U1: return "i1";
|
||||||
|
case ParamOrReturnType.I2_U2: return "i2";
|
||||||
|
case ParamOrReturnType.I4_U4: return "i4";
|
||||||
|
case ParamOrReturnType.I8_U8: return "i8";
|
||||||
|
case ParamOrReturnType.R4: return "r4";
|
||||||
|
case ParamOrReturnType.R8: return "r8";
|
||||||
|
case ParamOrReturnType.I16: return "i16";
|
||||||
|
case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return "sr";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_2: return "vf2";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_3: return "vf3";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_4: return "vf4";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return "vd2";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return "vd3";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return "vd4";
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN1: return "S" + Size;
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN2: return "A" + Size;
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN4: return "B" + Size;
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN8: return "C" + Size;
|
||||||
|
default: throw new NotSupportedException(PorType.ToString());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeName()
|
||||||
|
{
|
||||||
|
switch (PorType)
|
||||||
|
{
|
||||||
|
case ParamOrReturnType.VOID: return "void";
|
||||||
|
case ParamOrReturnType.I1_U1: return "int8_t";
|
||||||
|
case ParamOrReturnType.I2_U2: return "int16_t";
|
||||||
|
case ParamOrReturnType.I4_U4: return "int32_t";
|
||||||
|
case ParamOrReturnType.I8_U8: return "int64_t";
|
||||||
|
case ParamOrReturnType.R4: return "float";
|
||||||
|
case ParamOrReturnType.R8: return "double";
|
||||||
|
case ParamOrReturnType.I16: return "ValueTypeSize16";
|
||||||
|
case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return "uint64_t";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_2: return "HtVector2f";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_3: return "HtVector3f";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_4: return "HtVector4f";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return "HtVector2d";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return "HtVector3d";
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return "HtVector4d";
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN1: return $"ValueTypeSize<{Size}>";
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN2: return $"ValueTypeSizeAlign2<{Size}>";
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN4: return $"ValueTypeSizeAlign4<{Size}>";
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN8: return $"ValueTypeSizeAlign8<{Size}>";
|
||||||
|
default: throw new NotImplementedException(PorType.ToString());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public int GetParamSlotNum()
|
||||||
|
{
|
||||||
|
switch (PorType)
|
||||||
|
{
|
||||||
|
case ParamOrReturnType.VOID: return 0;
|
||||||
|
case ParamOrReturnType.I16: return 2;
|
||||||
|
case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return 1;
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_3: return 2;
|
||||||
|
case ParamOrReturnType.ARM64_HFA_FLOAT_4: return 2;
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return 2;
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return 3;
|
||||||
|
case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return 4;
|
||||||
|
case ParamOrReturnType.ARM64_HVA_8:
|
||||||
|
case ParamOrReturnType.ARM64_HVA_16: throw new NotSupportedException();
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN1:
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN2:
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN4:
|
||||||
|
case ParamOrReturnType.STRUCTURE_ALIGN8: return (Size + 7) / 8;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
Debug.Assert(PorType < ParamOrReturnType.STRUCT_NOT_PASS_AS_VALUE);
|
||||||
|
Debug.Assert(Size <= 8);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5afd7344483678a4abcb56158fd1442d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,148 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Generators.MethodBridge
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public class ValueTypeSizeAligmentCalculator
|
||||||
|
{
|
||||||
|
private static Dictionary<string, int> s_primitives = new Dictionary<string, int>(14) {
|
||||||
|
{ "Byte", 1 },
|
||||||
|
{ "SByte", 1 },
|
||||||
|
{ "Boolean", 1 },
|
||||||
|
{ "Int16", 2 },
|
||||||
|
{ "UInt16", 2 },
|
||||||
|
{ "Char", 2 },
|
||||||
|
{ "Int32", 4 },
|
||||||
|
{ "UInt32", 4 },
|
||||||
|
{ "Single", 4 },
|
||||||
|
{ "Int64", 8 },
|
||||||
|
{ "UInt64", 8 },
|
||||||
|
{ "Double", 8 },
|
||||||
|
//{ "IntPtr", _referenceSize }, // so rule return the same results
|
||||||
|
//{ "UIntPtr", _referenceSize }, // on 32 and 64 bits architectures
|
||||||
|
};
|
||||||
|
|
||||||
|
public ValueTypeSizeAligmentCalculator(bool arch32)
|
||||||
|
{
|
||||||
|
_referenceSize = arch32 ? 4 : 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually we should use IntPtr.Size but that would make the rule
|
||||||
|
// return different results on 64 bits systems
|
||||||
|
private readonly int _referenceSize;
|
||||||
|
|
||||||
|
// Note: Needs to be public since this is being tested by our unit tests
|
||||||
|
|
||||||
|
|
||||||
|
private static bool IsIgnoreField(FieldInfo field)
|
||||||
|
{
|
||||||
|
var ignoreAttr = field.GetCustomAttributes().Where(a => a.GetType().Name == "IgnoreAttribute").FirstOrDefault();
|
||||||
|
if (ignoreAttr == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = ignoreAttr.GetType().GetProperty("DoesNotContributeToSize");
|
||||||
|
return (bool)p.GetValue(ignoreAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private (int Size, int Aligment) SizeAndAligmentOfStruct(Type type)
|
||||||
|
{
|
||||||
|
int totalSize = 0;
|
||||||
|
int packAligment = 8;
|
||||||
|
int maxAligment = 1;
|
||||||
|
|
||||||
|
StructLayoutAttribute sa = type.StructLayoutAttribute;
|
||||||
|
if (sa != null && sa.Pack > 0)
|
||||||
|
{
|
||||||
|
packAligment = sa.Pack;
|
||||||
|
}
|
||||||
|
bool useSLSize = true;
|
||||||
|
foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
|
{
|
||||||
|
// add size of the type
|
||||||
|
var (fs, fa) = SizeAndAligmentOf(field.FieldType);
|
||||||
|
fa = Math.Min(fa, packAligment);
|
||||||
|
if (fa > maxAligment)
|
||||||
|
{
|
||||||
|
maxAligment = fa;
|
||||||
|
}
|
||||||
|
if (IsIgnoreField(field))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sa != null && sa.Value == LayoutKind.Explicit)
|
||||||
|
{
|
||||||
|
int offset = field.GetCustomAttribute<FieldOffsetAttribute>().Value;
|
||||||
|
totalSize = Math.Max(totalSize, offset + fs);
|
||||||
|
if (offset > sa.Size)
|
||||||
|
{
|
||||||
|
useSLSize = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (totalSize % fa != 0)
|
||||||
|
{
|
||||||
|
totalSize = (totalSize + fa - 1) / fa * fa;
|
||||||
|
}
|
||||||
|
totalSize += fs;
|
||||||
|
if (sa != null && sa.Value == LayoutKind.Sequential && totalSize > sa.Size)
|
||||||
|
{
|
||||||
|
useSLSize = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (totalSize == 0)
|
||||||
|
{
|
||||||
|
totalSize = maxAligment;
|
||||||
|
}
|
||||||
|
if (totalSize % maxAligment != 0)
|
||||||
|
{
|
||||||
|
totalSize = (totalSize + maxAligment - 1) / maxAligment * maxAligment;
|
||||||
|
}
|
||||||
|
if (sa != null && sa.Size > 0)
|
||||||
|
{
|
||||||
|
if (/*sa.Value == LayoutKind.Explicit &&*/ useSLSize)
|
||||||
|
{
|
||||||
|
totalSize = sa.Size;
|
||||||
|
while(totalSize % maxAligment != 0)
|
||||||
|
{
|
||||||
|
maxAligment /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (totalSize, maxAligment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public (int Size, int Aligment) SizeAndAligmentOf(Type type)
|
||||||
|
{
|
||||||
|
if (type.IsByRef || !type.IsValueType || type.IsArray)
|
||||||
|
return (_referenceSize, _referenceSize);
|
||||||
|
|
||||||
|
// list based on Type.IsPrimitive
|
||||||
|
if (type.Namespace == "System")
|
||||||
|
{
|
||||||
|
if (s_primitives.TryGetValue(type.Name, out var size))
|
||||||
|
{
|
||||||
|
return (size, size);
|
||||||
|
}
|
||||||
|
if (type.Name == "IntPtr" || type.Name == "UIntPtr")
|
||||||
|
{
|
||||||
|
return (_referenceSize, _referenceSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type.IsEnum)
|
||||||
|
return SizeAndAligmentOf(type.GetEnumUnderlyingType());
|
||||||
|
|
||||||
|
return SizeAndAligmentOfStruct(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 49b3dbcebadb1b543a42e01afec07ed1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 32ec2cc1cf5c468468308fa6308fba19
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a1563e7820e5b648a6a6691677f564c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,22 @@
|
|||||||
|
#include "MethodBridge.h"
|
||||||
|
|
||||||
|
#include <codegen/il2cpp-codegen-metadata.h>
|
||||||
|
#include "vm/ClassInlines.h"
|
||||||
|
#include "vm/Object.h"
|
||||||
|
#include "vm/Class.h"
|
||||||
|
|
||||||
|
#include "../metadata/MetadataModule.h"
|
||||||
|
#include "../metadata/MetadataUtil.h"
|
||||||
|
|
||||||
|
#include "Interpreter.h"
|
||||||
|
#include "MemoryUtil.h"
|
||||||
|
#include "InstrinctDef.h"
|
||||||
|
|
||||||
|
using namespace hybridclr::interpreter;
|
||||||
|
using hybridclr::GetInterpreterDirectlyCallMethodPointer;
|
||||||
|
|
||||||
|
#if HYBRIDCLR_ABI_ARM_64
|
||||||
|
//!!!{{INVOKE_STUB
|
||||||
|
|
||||||
|
//!!!}}INVOKE_STUB
|
||||||
|
#endif
|
@@ -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:
|
@@ -0,0 +1,22 @@
|
|||||||
|
#include "MethodBridge.h"
|
||||||
|
|
||||||
|
#include <codegen/il2cpp-codegen-metadata.h>
|
||||||
|
#include "vm/ClassInlines.h"
|
||||||
|
#include "vm/Object.h"
|
||||||
|
#include "vm/Class.h"
|
||||||
|
|
||||||
|
#include "../metadata/MetadataModule.h"
|
||||||
|
#include "../metadata/MetadataUtil.h"
|
||||||
|
|
||||||
|
#include "Interpreter.h"
|
||||||
|
#include "MemoryUtil.h"
|
||||||
|
#include "InstrinctDef.h"
|
||||||
|
|
||||||
|
using namespace hybridclr::interpreter;
|
||||||
|
using hybridclr::GetInterpreterDirectlyCallMethodPointer;
|
||||||
|
|
||||||
|
#if HYBRIDCLR_ABI_UNIVERSAL_32
|
||||||
|
//!!!{{INVOKE_STUB
|
||||||
|
|
||||||
|
//!!!}}INVOKE_STUB
|
||||||
|
#endif
|
@@ -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:
|
@@ -0,0 +1,22 @@
|
|||||||
|
#include "MethodBridge.h"
|
||||||
|
|
||||||
|
#include <codegen/il2cpp-codegen-metadata.h>
|
||||||
|
#include "vm/ClassInlines.h"
|
||||||
|
#include "vm/Object.h"
|
||||||
|
#include "vm/Class.h"
|
||||||
|
|
||||||
|
#include "../metadata/MetadataModule.h"
|
||||||
|
#include "../metadata/MetadataUtil.h"
|
||||||
|
|
||||||
|
#include "Interpreter.h"
|
||||||
|
#include "MemoryUtil.h"
|
||||||
|
#include "InstrinctDef.h"
|
||||||
|
|
||||||
|
using namespace hybridclr::interpreter;
|
||||||
|
using hybridclr::GetInterpreterDirectlyCallMethodPointer;
|
||||||
|
|
||||||
|
#if HYBRIDCLR_ABI_UNIVERSAL_64
|
||||||
|
//!!!{{INVOKE_STUB
|
||||||
|
|
||||||
|
//!!!}}INVOKE_STUB
|
||||||
|
#endif
|
@@ -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:
|
8
Assets/TEngine/Scripts/Editor/HybridCLR/Installer.meta
Normal file
8
Assets/TEngine/Scripts/Editor/HybridCLR/Installer.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a4a3d499c10d66f43af10a859e276a8f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -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("安装成功!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44c8627d126b30d4e9560b1f738264ca
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,117 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEditor ;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor.Installer
|
||||||
|
{
|
||||||
|
public class InstallerWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private InstallerController m_Controller;
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/Installer...", false, 0)]
|
||||||
|
private static void Open()
|
||||||
|
{
|
||||||
|
InstallerWindow window = GetWindow<InstallerWindow>("HybridCLR Installer", true);
|
||||||
|
window.minSize = new Vector2(800f, 500f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
m_Controller = new InstallerController();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
GUI.enabled = true;
|
||||||
|
GUILayout.Space(10f);
|
||||||
|
EditorGUILayout.LabelField("=======================说明====================");
|
||||||
|
EditorGUILayout.LabelField(
|
||||||
|
$"你所在项目的Unity版本可以与il2cpp_plus版本:{m_Controller.Il2CppBranch} 不一样。\n"
|
||||||
|
+ $"由于安装HybridCLR时需要从il2cpp_plus对应版本(而不是你项目版本)拷贝il2cpp目录,\n"
|
||||||
|
+ $"你必须同时也安装相应版本 {m_Controller.Il2CppBranch},否则无法安装", EditorStyles.wordWrappedLabel);
|
||||||
|
EditorGUILayout.LabelField("==============================================");
|
||||||
|
GUILayout.Space(10f);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical("box");
|
||||||
|
EditorGUILayout.LabelField($"安装状态:{(m_Controller.HasInstalledHybridCLR() ? "已安装" : "未安装")}", EditorStyles.boldLabel);
|
||||||
|
GUILayout.Space(5f);
|
||||||
|
EditorGUILayout.LabelField($"当前Unity版本: {Application.unityVersion},匹配的il2cpp_plus分支: {m_Controller.Il2CppBranch}");
|
||||||
|
GUISelectUnityDirectory($"il2cpp_plus分支对应Unity版本的il2cpp路径", "Select");
|
||||||
|
GUILayout.Space(10f);
|
||||||
|
GUIInstallButton("安装最新HybridCLR插件代码到本项目", "安装", InitHybridCLR);
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GUIInstallButton(string content, string button, Action onClick)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(content);
|
||||||
|
GUI.enabled = m_Controller.CheckValidIl2CppInstallDirectory(m_Controller.Il2CppBranch, m_Controller.Il2CppInstallDirectory) == InstallErrorCode.Ok;
|
||||||
|
if (GUILayout.Button(button, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
onClick?.Invoke();
|
||||||
|
GUIUtility.ExitGUI();
|
||||||
|
}
|
||||||
|
GUI.enabled = true;
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void GUISelectUnityDirectory(string content, string selectButton)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(content, GUILayout.MaxWidth(300));
|
||||||
|
string il2cppInstallDirectory = m_Controller.Il2CppInstallDirectory = EditorGUILayout.TextField(m_Controller.Il2CppInstallDirectory);
|
||||||
|
if (GUILayout.Button(selectButton, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
string temp = EditorUtility.OpenFolderPanel(content, m_Controller.Il2CppInstallDirectory, string.Empty);
|
||||||
|
if (!string.IsNullOrEmpty(temp))
|
||||||
|
{
|
||||||
|
il2cppInstallDirectory = m_Controller.Il2CppInstallDirectory = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
InstallErrorCode err = m_Controller.CheckValidIl2CppInstallDirectory(m_Controller.Il2CppBranch, il2cppInstallDirectory);
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case InstallErrorCode.Ok:
|
||||||
|
{
|
||||||
|
if (!il2cppInstallDirectory.Contains(m_Controller.Il2CppBranch))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox($"li2cpp 路径未包含 '{m_Controller.Il2CppBranch}',请确保选择了 {m_Controller.Il2CppBranch} 版本的安装目录 ", MessageType.Warning);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case InstallErrorCode.Il2CppInstallPathNotExists:
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("li2cpp 路径不存在", MessageType.Error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case InstallErrorCode.Il2CppInstallPathNotMatchIl2CppBranch:
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox($"il2cpp 版本不匹配,必须为 {m_Controller.Il2CppBranch} 版本相应目录", MessageType.Error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case InstallErrorCode.NotIl2CppPath:
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox($"当前选择的路径不是il2cpp目录(必须类似 xxx/il2cpp)", MessageType.Error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw new Exception($"not support {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitHybridCLR()
|
||||||
|
{
|
||||||
|
m_Controller.InitHybridCLR(m_Controller.Il2CppBranch, m_Controller.Il2CppInstallDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 959fbf0bb06629542969354505189240
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
129
Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs
Normal file
129
Assets/TEngine/Scripts/Editor/HybridCLR/MethodBridgeHelper.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using HybridCLR.Editor.Generators;
|
||||||
|
using HybridCLR.Editor.Generators.MethodBridge;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace HybridCLR.Editor
|
||||||
|
{
|
||||||
|
internal class MethodBridgeHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
private static void CleanIl2CppBuildCache()
|
||||||
|
{
|
||||||
|
string il2cppBuildCachePath = BuildConfig.Il2CppBuildCacheDir;
|
||||||
|
if (!Directory.Exists(il2cppBuildCachePath))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Debug.Log($"clean il2cpp build cache:{il2cppBuildCachePath}");
|
||||||
|
Directory.Delete(il2cppBuildCachePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Assembly> CollectDependentAssemblies(Dictionary<string, Assembly> allAssByName, List<Assembly> dlls)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < dlls.Count; i++)
|
||||||
|
{
|
||||||
|
Assembly ass = dlls[i];
|
||||||
|
foreach (var depAssName in ass.GetReferencedAssemblies())
|
||||||
|
{
|
||||||
|
if (!allAssByName.ContainsKey(depAssName.Name))
|
||||||
|
{
|
||||||
|
Debug.Log($"ignore ref assembly:{depAssName.Name}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Assembly depAss = allAssByName[depAssName.Name];
|
||||||
|
if (!dlls.Contains(depAss))
|
||||||
|
{
|
||||||
|
dlls.Add(depAss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dlls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Assembly> GetScanAssembiles()
|
||||||
|
{
|
||||||
|
var allAssByName = new Dictionary<string, Assembly>();
|
||||||
|
foreach(var ass in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
allAssByName[ass.GetName().Name] = ass;
|
||||||
|
}
|
||||||
|
//CompileDllHelper.CompileDllActiveBuildTarget();
|
||||||
|
|
||||||
|
var rootAssemblies = BuildConfig.HotUpdateAssemblies
|
||||||
|
.Select(dll => Path.GetFileNameWithoutExtension(dll)).Concat(GeneratorConfig.GetExtraAssembiles())
|
||||||
|
.Where(name => allAssByName.ContainsKey(name)).Select(name => allAssByName[name]).ToList();
|
||||||
|
//var rootAssemblies = GeneratorConfig.GetExtraAssembiles()
|
||||||
|
// .Where(name => allAssByName.ContainsKey(name)).Select(name => allAssByName[name]).ToList();
|
||||||
|
CollectDependentAssemblies(allAssByName, rootAssemblies);
|
||||||
|
rootAssemblies.Sort((a, b) => a.GetName().Name.CompareTo(b.GetName().Name));
|
||||||
|
Debug.Log($"assembly count:{rootAssemblies.Count}");
|
||||||
|
foreach(var ass in rootAssemblies)
|
||||||
|
{
|
||||||
|
//Debug.Log($"scan assembly:{ass.GetName().Name}");
|
||||||
|
}
|
||||||
|
return rootAssemblies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateMethodBridgeCppFile(PlatformABI platform, string fileName, bool optimized)
|
||||||
|
{
|
||||||
|
string outputFile = $"{BuildConfig.MethodBridgeCppDir}/{fileName}.cpp";
|
||||||
|
var g = new MethodBridgeGenerator(new MethodBridgeGeneratorOptions()
|
||||||
|
{
|
||||||
|
CallConvention = platform,
|
||||||
|
HotfixAssemblies = BuildConfig.HotUpdateAssemblies.Select(name =>
|
||||||
|
AppDomain.CurrentDomain.GetAssemblies().First(ass => ass.GetName().Name + ".dll" == name)).ToList(),
|
||||||
|
AllAssemblies = optimized ? GetScanAssembiles() : AppDomain.CurrentDomain.GetAssemblies().ToList(),
|
||||||
|
OutputFile = outputFile,
|
||||||
|
Optimized = optimized,
|
||||||
|
});
|
||||||
|
|
||||||
|
g.PrepareMethods();
|
||||||
|
g.Generate();
|
||||||
|
Debug.LogFormat("== output:{0} ==", outputFile);
|
||||||
|
CleanIl2CppBuildCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
//[MenuItem("HybridCLR/MethodBridge/Arm64")]
|
||||||
|
//public static void MethodBridge_Arm64()
|
||||||
|
//{
|
||||||
|
// GenerateMethodBridgeCppFile(PlatformABI.Arm64, "MethodBridge_Arm64");
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[MenuItem("HybridCLR/MethodBridge/Universal64")]
|
||||||
|
//public static void MethodBridge_Universal64()
|
||||||
|
//{
|
||||||
|
// GenerateMethodBridgeCppFile(PlatformABI.Universal64, "MethodBridge_Universal64");
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[MenuItem("HybridCLR/MethodBridge/Universal32")]
|
||||||
|
//public static void MethodBridge_Universal32()
|
||||||
|
//{
|
||||||
|
// GenerateMethodBridgeCppFile(PlatformABI.Universal32, "MethodBridge_Universal32");
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static void GenerateMethodBridgeAll(bool optimized)
|
||||||
|
{
|
||||||
|
GenerateMethodBridgeCppFile(PlatformABI.Arm64, "MethodBridge_Arm64", optimized);
|
||||||
|
GenerateMethodBridgeCppFile(PlatformABI.Universal64, "MethodBridge_Universal64", optimized);
|
||||||
|
GenerateMethodBridgeCppFile(PlatformABI.Universal32, "MethodBridge_Universal32", optimized);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/MethodBridge/All_高度精简")]
|
||||||
|
public static void MethodBridge_All_Optimized()
|
||||||
|
{
|
||||||
|
GenerateMethodBridgeAll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("HybridCLR/MethodBridge/All_完整(新手及开发期推荐)")]
|
||||||
|
public static void MethodBridge_All_Normal()
|
||||||
|
{
|
||||||
|
GenerateMethodBridgeAll(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 18beea79433bdce44af834574cd9c212
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -7,7 +7,7 @@
|
|||||||
"Editor"
|
"Editor"
|
||||||
],
|
],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
"allowUnsafeCode": false,
|
"allowUnsafeCode": true,
|
||||||
"overrideReferences": false,
|
"overrideReferences": false,
|
||||||
"precompiledReferences": [],
|
"precompiledReferences": [],
|
||||||
"autoReferenced": true,
|
"autoReferenced": true,
|
||||||
|
Reference in New Issue
Block a user