Compare commits

...

138 Commits
v5.2.1 ... main

Author SHA1 Message Date
刘涛 434160afa8 【obfz】兼容删除dnlib 2025-10-17 12:25:37 +08:00
walon 01754c8fb4 [version] Release v8.6.0 2025-09-27 17:15:06 +08:00
walon 1934d90f27 [remove] remove unused ReversePInvokeWrap.meta 2025-09-03 09:35:55 +08:00
walon 814572e1a1 [fix] fix the bug that BashUtil.CopyDir calls UnityEditor.FileUtil.CopyFileOrDirectory failed when parent directory of dst does not exist. 2025-08-26 21:17:03 +08:00
walon d505624c78 [version] Release v8.5.1 2025-08-25 19:23:54 +08:00
walon e36bbb9022 [fix] fixed PInvokeAnalyzer bug in computing PInvoke function calling conventions. 2025-08-25 19:23:29 +08:00
walon 0642726469 [version] Release v8.5.0 2025-08-20 12:45:19 +08:00
walon d86572db37 [change] change domain hybridclr.doc.code-philosophy.com to www.hybridclr.cn 2025-08-20 12:44:19 +08:00
xiewen b5944ea834 [change] BashUtil::CopyDir replaces CopyWithCheckLongFile with CopyUnityEditor.FileUtil.CopyFileOrDirectory 2025-08-20 12:44:17 +08:00
walon 73654e76d2 [version] Release v8.4.0 2025-07-26 10:24:27 +08:00
walon 1537690bb5 [change] add new LoadImageErrorCode items 2025-07-25 21:07:45 +08:00
walon 7f158f814a [fix] fix the issue that `Texture Compression` option in Build Settings was changed after running `HybridCLR/Generate/All` on Android platform 2025-07-04 17:17:44 +08:00
walon ee7af5c592 [version] Release v8.3.0 2025-07-03 20:59:20 +08:00
walon ab41cd0144 [fix] fix the bug that not collect struct in calli and extern method signature in generating MethodBridge. 2025-06-30 12:12:02 +08:00
walon 59a3c3974a [version] Release v8.2.0 2025-06-12 08:38:36 +08:00
walon c9ff293dfe [change] changed from throw exception to logError when not supported pinvoke or reverse pinvoke method parameter type was found 2025-06-06 16:57:53 +08:00
walon 56486b8a76 [change] change package category to Scripting 2025-06-01 18:00:09 +08:00
walon 8e27e8d41b [change] remove warning log when the result of resolving method is null in GenericMethod::ResolveMethod. 2025-05-29 17:04:10 +08:00
walon fca130bbfa [version] Release v8.1.0 2025-05-29 14:26:45 +08:00
walon c21dbcc60a [change] Remove redundant `buildOptions |= BuildOptions.CleanBuildCache;` code since it's already added in the GetBuildPlayerOptions function. 2025-05-29 14:22:50 +08:00
walon 7f2fa52f37 [change] change visibility of LinkXmlWriter to public. 2025-05-22 19:55:36 +08:00
walon da46e71534 [fix] fix bug of GenericArgumentContext that inflate ByRef and SZArray to Ptr. 2025-05-22 19:53:49 +08:00
walon 804418fddb [change] update supported unity versions in README 2025-05-09 19:17:35 +08:00
walon 7a0032934b [version] Release v8.0.0 2025-05-02 10:41:51 +08:00
walon b301b44cd3 [refactor] move ReversePInvokeWrap/Analyzer.cs to MethodBridge/MonoPInvokeCallbackAnalyzer.cs
[change] validate unsupported parameter type(.e.g string) in MonoPInvokeCallback signature when generate MethodBridge file
2025-04-30 16:20:03 +08:00
walon ddc3332958 [change] validate unsupported parameter type(.e.g string) in PInvoke signature when generate MethodBridge file 2025-04-30 16:11:41 +08:00
walon 52f25bdb2a [change] AssemblyResolver also resolves `*.dll.bytes` files besides `*.dll`. 2025-04-30 15:33:46 +08:00
walon 31be818bb6 [fix] fix error of computing CallingConvention in MethodBridge/Generator::BuildCalliMethods 2025-04-30 15:30:03 +08:00
walon 213a11ff25 [new] generate Managed2NativeFunction for PInvoke method 2025-04-30 15:05:40 +08:00
walon 585be97be9 [change] change type of the first argument `methodPointer` of Managed2NativeFunctionPointer from `const void*` to `Il2CppMethodPointer` 2025-04-30 12:40:21 +08:00
walon 361873803f [refactor] refactor code of settings. 2025-04-28 10:20:08 +08:00
walon 46f308ed5e [opt] optimization unnecessary initialization of typeArgsStack and methodArgsStack of GenericArgumentContext 2025-04-25 22:02:54 +08:00
walon 34406b1c13 [change] the shared type of ElementType.FnPtr is changed from IntPtr to UIntPtr 2025-04-25 21:59:15 +08:00
walon e5b52922f7 [version] Release v7.10.0 2025-04-22 12:05:15 +08:00
walon e44fa663db [fix] fix bug of `CompileDll(BuildTarget target)` that use EditorUserBuildSettings.activeBuildTarget instead of target to call CompileDll 2025-04-12 14:21:35 +08:00
walon 903cf9faa2 [change] add information of beginner QQ group 3 in README.md 2025-04-08 08:50:48 +08:00
L0veTomori 3fe41a8cba
[opt] strip AOT assembly resources. (#54) 2025-04-03 10:48:40 +08:00
walon 603b0ccb24 [version] Release v7.9.0 2025-03-31 09:44:50 +08:00
walon 9429a4d24d [version] Release v7.8.1 2025-03-24 19:34:25 +08:00
walon ea592ba20c [version] Release v7.8.0 2025-03-23 23:18:11 +08:00
walon 8d1280a1bd [fix] fixed the bug where BashUtil.RemoveDir failed to run under certain circumstances on macOS systems. 2025-03-14 08:59:11 +08:00
walon 528240b52b [version] Release v7.7.0 2025-03-12 15:29:30 +08:00
walon 4768c039d2 [fix] fixed hook failed in version below MacOS 11 2025-03-07 13:29:47 +08:00
walon 10dc749d7a [change] CompileDllActiveBuildTarget and GenerateAll use EditorUserBuildSettings.development to compile hot update dll. 2025-03-05 16:14:08 +08:00
walon 0c5ef00c12 [remove] remove option HybridCLRSettings.enableStraceTraceInWebGLReleaseBuild 2025-03-05 15:59:37 +08:00
walon 1553582ec1 [remove] remove option HybridCLRSettings.enableProfilerInReleaseBuild 2025-03-05 15:58:11 +08:00
walon 756b906a21 [version] Release v7.6.0 2025-03-01 13:51:01 +08:00
walon 355e24fbe8 [fix] fixed the bug in the MethodBridge generator where it incorrectly handle [StructLayout] and blittable types when generating code for struct classes. 2025-03-01 10:39:05 +08:00
walon 9e4946aa45 [new] add AssemblySorter to sort assemblies by reference order 2025-02-08 16:53:11 +08:00
walon 8e571d594d [version] Release v7.5.0 2025-02-05 10:42:54 +08:00
walon b8dd2be1e4 [revert] Revert "[new] support preserve UnityEngine core types when GenerateLinkXml"
This reverts commit 82163d2e50.
2025-02-05 10:35:49 +08:00
walon de7b60695a [version] Release v7.4.1 2025-01-19 11:41:54 +08:00
walon a11ebecf93 [fix] fixe the bug that preserving UnityEngine.PerformanceReportingModule when generating link.xml would cause the Android app built with Unity 2019 to crash on startup. 2025-01-18 22:55:37 +08:00
walon 019412fa81 [version] Release v7.4.0 2025-01-17 21:40:09 +08:00
walon 84bf6d59db [change] removed unnecessary field initialization in the HybridCLRSettings class. 2025-01-08 16:28:12 +08:00
walon 82163d2e50 [new] support preserve UnityEngine core types when GenerateLinkXml 2025-01-08 16:20:39 +08:00
walon 208372e6af [new] add MethodBridge.cpp.tpl. MethodBridgeGeneratorCommand doesn't generate and override from same file 2025-01-08 15:26:48 +08:00
walon dd8d812641 [change] add UnityVersion.h.tpl and AssemblyManifest.cpp.tpl, Il2CppDefGenerator doesn't generates and override code file from same one 2025-01-08 15:21:43 +08:00
walon c19b34542e [fix] fixed the bug where StripAOTDllCommand did not set BuildPlayerOptions.subtarget in Unity 2021+ versions, causing failure when publishing dedicated buildTarget. 2025-01-08 15:00:54 +08:00
walon e30306d7e1 [fix] fixed the bug in AOTAssemblyMetadataStripper::Strip where ModuleWriterOptions MetadataFlags.PreserveRids was not used. 2025-01-07 16:19:11 +08:00
walon 20f34c9b14 [fix] fix bug of computing CallingConvention of calli 2025-01-03 12:23:04 +08:00
walon 45749ffb5c [new] add Managed2NativeFunctionPointer MethodBridge functions 2025-01-02 22:22:59 +08:00
walon b176d63d93 [version] Release v7.3.0 2024-12-31 12:49:10 +08:00
walon 0c99afd58e [fix] fix the bug where MissingMetadataChecker can't detect references to newly added AOT assemblies. 2024-12-18 18:21:37 +08:00
walon 2458c9a9ba [fix] fix the issue in Unity 6000 where the modification of the trimmed AOT DLL output directory for the visionOS build target caused CopyStrippedAOTAssemblies::GetStripAssembliesDir2021 to fail in copying AOT DLLs. 2024-12-16 19:22:56 +08:00
walon 4d0fef095b [version] Release v7.2.0 2024-12-09 09:36:12 +08:00
walon 642d2cd644 [version] Release v7.1.0 2024-12-04 18:15:52 +08:00
walon e54ce36b0e [fix] fix the bug that CopyStrippedAOTAssemblies didn't work on UWP platform of 6000.0.x 2024-12-01 12:53:38 +08:00
walon ac8b649d50 [fix] fix the issue that CopyStrippedAOTAssemblies didn't support HMIAndroid in tuanjie engine 2024-11-25 12:46:13 +08:00
walon 9ca9cd5aa7 [new] support prejit interpreter class and method 2024-11-20 14:05:34 +08:00
walon 37dafd7b3c [change] change the attributes on fields of HybridCLRSettings from `[Header]` to `[ToolTip]` 2024-11-20 11:27:02 +08:00
walon f58d12c2cb [change] refactor code comments and translate them to English 2024-11-20 11:25:31 +08:00
walon d5eaa350c5 [change] add RuntimeOptionId::MaxInlineableMethodBodySize 2024-11-20 09:15:28 +08:00
walon 1f0053a3c9 [version] Release v7.0.0 2024-11-15 20:59:11 +08:00
walon 15dccc7427 [change] add RuntimeOptionId::MaxMethodBodyCacheSize and RuntimeOptionId::MaxMethodInlineDepth 2024-11-15 20:51:10 +08:00
walon 7d287a2293 [fix] fix the bug in `MissingMetadataChecker` where it did not check for missing fields. 2024-11-06 12:09:06 +08:00
walon f19ba3eb30 [fix] fix the bug in GenericReferenceWriter where _systemTypePattern did not properly escape the '.' in type names. This caused issues when compiler-generated anonymous types and functions contained string sequences like 'System-Int', incorrectly matching them to 'System.Int', resulting in runtime exceptions. 2024-11-05 18:19:13 +08:00
walon 4e172129d8 [version] Release v6.11.0 2024-10-31 09:44:33 +08:00
walon 2414e77fef [version] Release v6.10.1 2024-10-24 15:44:37 +08:00
walon 77517a9814 [fix] Fixs HookUtils compile errors in Unity 2019 and 2020 2024-10-24 15:43:35 +08:00
walon 162f21f6a5 [change] remove README_zh.md.meta, add README_EN.md.meta 2024-10-23 20:48:15 +08:00
walon bd7c3293bc [version] Release v6.10.0 2024-10-23 12:05:33 +08:00
walon e25be945c7 [change] update README.md 2024-10-23 12:02:02 +08:00
walon 925921c27c [fix] 修复MonoHook判定processorType时没有正确处理某些CPU上processorType为全大写的情况(如有些机器上返回INTEL而不是Intel) 2024-09-30 12:13:37 +08:00
walon 64a6d26fa3 [version] 发布v6.9.0版本 2024-09-27 19:37:24 +08:00
walon c24617d864 [version] 发布v6.8.0版本 2024-09-14 12:10:06 +08:00
walon 59bc259cc8 [version] 发布v6.7.1版本 2024-08-26 12:29:07 +08:00
walon c413cd6143 [version] 发布v6.7.0版本 2024-08-25 14:28:35 +08:00
walon c614cdcf4e [fix] 修复PatchScriptingAssembliesJsonHook中的断言在从WebGL平台切换到其他平台时断言失败的问题 2024-08-22 22:19:14 +08:00
walon 08f493ab3e [new] HybridCLRSettings新增enableProfilerInReleaseBuild和enableStraceTraceInWebGLReleaseBuild两个选项 2024-08-22 21:57:40 +08:00
walon 3705ca6e1f [version] 发布v6.6.0版本 2024-08-10 20:47:35 +08:00
walon 34df279943 [version] 发布v6.5.0版本 2024-08-02 15:14:05 +08:00
walon b85ed1aa92 [change] 新增LoadImageErrorCode::PDB_BAD_FILE错误码 2024-08-02 14:31:31 +08:00
walon faf4a328af [fix] 修复RELEASELOG未包含6.3和6.4版本标题的问题 2024-07-31 10:20:03 +08:00
walon 53f4f33bf0 [version] 发布v6.4.0版本 2024-07-25 10:10:16 +08:00
walon 48c91f497b [fix] 修复MethodBridge/Generator GetOrCalculateTypeInfoSignature计算等价类时未考虑到ClassLayout、Layout和FieldOffset因素的bug 2024-07-18 14:12:23 +08:00
walon 9afbe3bd84 [fix] 修复Library/PlayerDataCache目录不存在时,PatchScriptingAssembliesJsonHook运行异常的bug 2024-07-18 13:37:20 +08:00
walon 2d5f71f6b7 [version] 发布v6.3.0版本 2024-07-15 10:26:26 +08:00
walon 5747458bf2 [fix] 修复微信小游戏平台当TextureCompression非默认值时临时目录名为WinxinMiniGame{xxx},导致没有成功修改scriptingassemblies.json文件的bug 2024-07-12 17:54:59 +08:00
walon 0b455eb882 [fix] 修复 Unity 2022导出的xcode工程包含多个ShellScript片段时错误地删除了非重复片断的bug 2024-07-12 09:47:57 +08:00
walon e086228d90 [fix] 修复团结引擎微信小游戏平台由于同时定义了UNITY_WEIXINMINIGAME和UNITY_WEBGL宏,导致从错误路径查找scriptingassemblies.json文件失败,运行时出现脚本missing的bug 2024-07-02 17:00:05 +08:00
walon 909e96ca7c [version] 发布v6.2.0版本 2024-06-30 19:40:27 +08:00
walon 75b24345f6 [change] 更新README 2024-06-17 10:57:07 +08:00
walon 88e20505f8 [version] 发布v6.1.0版本 2024-06-15 01:23:09 +08:00
walon b4b486944c [fix] 修复`Generate/LinkXml`生成的link.xml中对UnityEngine.Debug preserve all导致在Unity 2023及更高版本的iOS、visionOS等平台上出现Undefined symbols for architecture arm64: "CheckApplicationIntegrity(IntegrityCheckLevel)" 编译错误的问题。此bug由Unity引起,但通过在生成link.xml时忽略UnityEngine.Debug类来临时解决这个问题 2024-06-13 22:04:35 +08:00
walon 360ba38805 [change] 升级dnlib版本,修复ModuleMD保存dll时将未加Assembly限定的mscorlib程序集中类型的程序集设置为当前程序集的严重bug 2024-06-13 19:44:23 +08:00
walon e5a6952777 [version] 发布v6.0.0版本 2024-06-11 00:24:54 +08:00
walon 9330cf682c [fix] 修复在Unity 2023及6000版本在iOS平台的构建bug 2024-06-11 00:24:48 +08:00
walon 16a7d5d529 [remove] 删除无用的 ReversePInvokeWrapperGeneratorCommand代码文件 2024-06-10 13:19:06 +08:00
walon 303c6c7b35 [fix] 修复计算Native2Manager桥接函数未考虑到MonoPInvokeCallback函数,导致从lua或者其他语言调用c#热更新函数有时候会出现UnsupportedNative2ManagedMethod的bug 2024-06-10 13:18:46 +08:00
walon e4cf9e9086 [fix] 修复在某些不支持visionOS的Unity版本上CopyStrippedAOTAssemblies类有编译错误的bug 2024-06-10 12:44:52 +08:00
walon b977a85b23 [opt] 优化 PatchScriptingAssembliesJsonHook.cs在WebGL和WeixinMiniGame平台的宏开关 2024-05-30 19:22:52 +08:00
walon 0fcb51a369 [fix] PatchScriptingAssemblyList.cs在Unity 2023+版本webgl平台的编译错误 2024-05-30 19:21:58 +08:00
walon d61511924d [fix] 修复tuanjie引擎及Unity2023.2.x不支持visionOS引发的编译错误 2024-05-30 19:15:23 +08:00
walon aca7dd0a67 [new] 新增GeneratedAOTGenericReferenceExcludeExistsAOTClassAndMethods,计算热更新引用的AOT泛型类型和函数时排除掉AOT中已经存在的泛型和函数,最终生成更精准的补充元数据程序集列表 2024-05-30 13:08:24 +08:00
walon fc685a99ca [fix] 修复计算 MonoPInvokeCallback的CallingConvention时,如果delegate在其他程序集中定义,会被错误当作Winapi,导致wrapper签名计算错误的bug 2024-05-30 13:08:24 +08:00
walon d431aeb019 [new] 支持MonoPInvokeCallback函数的参数或返回类型为struct类型
[fix] 修复ReversePInvokeWrapper函数定义未包含调用约定的bug
[refactor] 合并ReversePInvokeMethodStub到MethodBridge,同时将MetadataModule中ReversePInvoke相关代码移到InterpreterModule
2024-05-30 13:08:21 +08:00
walon 63d0aaa6e6 [opt] `Generate/All`在生成之前检查是否已经安装HybridCLR 2024-05-30 13:07:23 +08:00
walon d45974c6b4 [new] 支持Unity 2023 2024-05-30 13:07:23 +08:00
walon 3de931a3af [opt] 打包时检查生成桥接函数时的development选项与当前development选项一致。`Generate/All`之后切换development选项再打包,将会产生严重的崩溃 2024-05-30 13:07:23 +08:00
walon b96d6fc10d [new] 支持Unity 6 2024-05-30 13:07:23 +08:00
walon ef1b78caae [version] 发布v5.4.1版本 2024-05-30 12:49:11 +08:00
walon 47622bf49c [new] 支持visionOS平台
[fix] 修复tvOS平台使用了错误的Unity-iPhone.xcodeproj路径导致找不到project.pbxproj的bug
2024-05-30 12:45:21 +08:00
walon 846d7103a3 [fix] 修复计算 MonoPInvokeCallback的CallingConvention时,如果delegate在其他程序集中定义,会被错误当作Winapi,导致wrapper签名计算错误的bug 2024-05-30 12:26:48 +08:00
walon 170f13082e [version] 发布v5.4.0版本 2024-05-17 21:43:21 +08:00
walon 60016d7481 [fix] 修复当dll中存在指向本程序集内的TypeRef时,dnlib的TypeDef.DefinitionAssembly返回null导致 Link/Analyzer.cs运行抛出异常的bug 2024-05-17 21:43:15 +08:00
walon 97dae7fed2 [fix] 修复当Append xcode项目到现存的xcode项目时,第1次会导致'Run Script'命令被重复追加,从第2次起将会找不到--external-lib-il2-cpp而打印错误日志的bug 2024-05-14 22:38:38 +08:00
walon f935127cd3 [new] ReversePInvoke支持CallingConvention 2024-05-14 12:48:15 +08:00
walon ec5f2ef05e [fix] 修复计算struct等价性时,将struct平铺展开计算等价,在某些平台并不适用的bug。例如 struct A { uint8_t x; A2 y; } struct A2 { uint8_t x; int32_t y;}; 跟 struct B {uint8_t x; uint8_t y; int32_t z;} 在x86_64 abi下并不等价 2024-05-14 12:48:15 +08:00
Rorschach 6d0ceaa4f5
[fix] 修正HybridCLRSettingProvider.cs中文档链接 (#46) 2024-05-14 12:47:09 +08:00
walon 95922aec1e [version] 发布v5.3.0版本 2024-04-22 10:17:59 +08:00
walon 3259c11828 [fix] 修复导出tvOS工程时未修改xcode工程设置,导致打包失败的bug 2024-04-21 13:45:47 +08:00
walon 74bab1d935 [fix] 修复tvOS目标时,未复制裁剪AOT dll,导致生成桥接函数失败的bug 2024-04-21 13:44:54 +08:00
walon fd81edb495 [fix] 解决StripAOTDllCommand生成的临时项目的locationPathName不规范导致与某些插件如Embeded Browser不兼容的问题 2024-04-12 00:35:09 +08:00
walon 12bafb897f [fix] 修复团结引擎1.1.0起删除TUANJIE_2022宏导致没有复制裁剪后的AOT程序集的bug 2024-04-10 10:32:23 +08:00
walon a6befb1312 [fix] 修复开启development build选项时出现mono相关头文件找不到的bug 2024-04-10 10:31:30 +08:00
walon a36e3fe72d [fix] 修复__ReversePInvokeMethod_XXX函数未设置Il2CppThreadContext,导致从native线程回调时获取Thread变量崩溃的bug 2024-04-10 10:31:30 +08:00
walon d9776d1793 [new] UnityVersion.h中新增 HYBRIDCLR_TUANJIE_VERSION 宏 2024-04-08 21:55:13 +08:00
77 changed files with 2655 additions and 1432 deletions

View File

@ -0,0 +1,12 @@
#include "../Il2CppCompatibleDef.h"
namespace hybridclr
{
const char* g_placeHolderAssemblies[] =
{
//!!!{{PLACE_HOLDER
//!!!}}PLACE_HOLDER
nullptr,
};
}

View File

@ -0,0 +1,32 @@
#include <codegen/il2cpp-codegen-metadata.h>
#if HYBRIDCLR_UNITY_2023_OR_NEW
#include <codegen/il2cpp-codegen.h>
#elif HYBRIDCLR_UNITY_2022
#include <codegen/il2cpp-codegen-il2cpp.h>
#elif HYBRIDCLR_UNITY_2020 || HYBRIDCLR_UNITY_2021
#include <codegen/il2cpp-codegen-common-big.h>
#else
#include <codegen/il2cpp-codegen-common.h>
#endif
#include "vm/ClassInlines.h"
#include "vm/Object.h"
#include "vm/Class.h"
#include "vm/ScopedThreadAttacher.h"
#include "../metadata/MetadataUtil.h"
#include "../interpreter/InterpreterModule.h"
#include "../interpreter/MethodBridge.h"
#include "../interpreter/Interpreter.h"
#include "../interpreter/MemoryUtil.h"
#include "../interpreter/InstrinctDef.h"
using namespace hybridclr::interpreter;
using namespace hybridclr::metadata;
//!!!{{CODE
//!!!}}CODE

View File

@ -0,0 +1,6 @@
#pragma once
//!!!{{UNITY_VERSION
//!!!}}UNITY_VERSION

View File

@ -2,28 +2,38 @@
"versions": [ "versions": [
{ {
"unity_version":"2019", "unity_version":"2019",
"hybridclr" : { "branch":"v5.2.1"}, "hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v2019-5.2.1"} "il2cpp_plus": { "branch":"v2019-8.1.0"}
}, },
{ {
"unity_version":"2020", "unity_version":"2020",
"hybridclr" : { "branch":"v5.2.1"}, "hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v2020-5.2.1"} "il2cpp_plus": { "branch":"v2020-8.1.0"}
}, },
{ {
"unity_version":"2021", "unity_version":"2021",
"hybridclr" : { "branch":"v5.2.1"}, "hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v2021-5.2.1"} "il2cpp_plus": { "branch":"v2021-8.1.0"}
}, },
{ {
"unity_version":"2022", "unity_version":"2022",
"hybridclr" : { "branch":"v5.2.1"}, "hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v2022-5.2.1"} "il2cpp_plus": { "branch":"v2022-8.2.0"}
}, },
{ {
"unity_version":"2022-tuanjie", "unity_version":"2022-tuanjie",
"hybridclr" : { "branch":"v5.2.1"}, "hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v2022-tuanjie-5.2.1"} "il2cpp_plus": { "branch":"v2022-tuanjie-8.6.0"}
},
{
"unity_version":"2023",
"hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v2023-8.1.0"}
},
{
"unity_version":"6000",
"hybridclr" : { "branch":"v8.6.0"},
"il2cpp_plus": { "branch":"v6000-8.1.0"}
} }
] ]
} }

View File

@ -116,8 +116,8 @@ namespace MonoHook
static void SetupFlushICacheFunc() static void SetupFlushICacheFunc()
{ {
string processorType = SystemInfo.processorType; string processorType = SystemInfo.processorType.ToLowerInvariant();
if (processorType.Contains("Intel") || processorType.Contains("AMD")) if (processorType.Contains("intel") || processorType.Contains("amd"))
return; return;
if (IntPtr.Size == 4) if (IntPtr.Size == 4)

View File

@ -15,8 +15,12 @@ namespace MonoHook
static HookUtils() static HookUtils()
{
try
{ {
jit_write_protect_supported = pthread_jit_write_protect_supported_np() != 0; jit_write_protect_supported = pthread_jit_write_protect_supported_np() != 0;
}
catch { }
PropertyInfo p_SystemPageSize = typeof(Environment).GetProperty("SystemPageSize"); PropertyInfo p_SystemPageSize = typeof(Environment).GetProperty("SystemPageSize");
if (p_SystemPageSize == null) if (p_SystemPageSize == null)
@ -35,6 +39,16 @@ namespace MonoHook
public static void MemCpy_Jit(void* pDst, byte[] src) public static void MemCpy_Jit(void* pDst, byte[] src)
{ {
if (!jit_write_protect_supported)
{
fixed(void * pSrc = &src[0])
{
MemCpy(pDst, pSrc, src.Length);
}
return;
}
fixed(void * p = &src[0]) fixed(void * p = &src[0])
{ {
memcpy_jit(new IntPtr(pDst), new IntPtr(p), src.Length); memcpy_jit(new IntPtr(pDst), new IntPtr(p), src.Length);

View File

@ -12,7 +12,7 @@ using System.IO;
namespace HybridCLR.MonoHook namespace HybridCLR.MonoHook
{ {
#if UNITY_2021_1_OR_NEWER #if UNITY_2021_1_OR_NEWER && !UNITY_2023_1_OR_NEWER
[InitializeOnLoad] [InitializeOnLoad]
public class CopyStrippedAOTAssembliesHook public class CopyStrippedAOTAssembliesHook
{ {

View File

@ -12,7 +12,7 @@ using System.IO;
namespace HybridCLR.MonoHook namespace HybridCLR.MonoHook
{ {
#if UNITY_2022 #if UNITY_2022 || UNITY_2023_1_OR_NEWER
[InitializeOnLoad] [InitializeOnLoad]
public class GetIl2CppFolderHook public class GetIl2CppFolderHook
{ {

View File

@ -12,7 +12,7 @@ using System.IO;
namespace HybridCLR.MonoHook namespace HybridCLR.MonoHook
{ {
#if (UNITY_2021_1_OR_NEWER && UNITY_WEBGL) || TUANJIE_2022 #if UNITY_2021_1_OR_NEWER && (UNITY_WEBGL || UNITY_WEIXINMINIGAME)
[InitializeOnLoad] [InitializeOnLoad]
public class PatchScriptingAssembliesJsonHook public class PatchScriptingAssembliesJsonHook
{ {
@ -35,18 +35,29 @@ namespace HybridCLR.MonoHook
private static string BuildMainWindowTitle() private static string BuildMainWindowTitle()
{ {
string tempJsonPath = $"{Application.dataPath}/../Library/PlayerDataCache/WebGL/Data/ScriptingAssemblies.json"; var cacheDir = $"{Application.dataPath}/../Library/PlayerDataCache";
#if TUANJIE_2022 if (Directory.Exists(cacheDir))
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WeixinMiniGame)
{ {
tempJsonPath = $"{Application.dataPath}/../Library/PlayerDataCache/WeixinMiniGame/Data/ScriptingAssemblies.json"; foreach (var tempJsonPath in Directory.GetDirectories(cacheDir, "*", SearchOption.TopDirectoryOnly))
{
string dirName = Path.GetFileName(tempJsonPath);
#if UNITY_WEIXINMINIGAME
if (!dirName.Contains("WeixinMiniGame"))
{
continue;
}
#else
if (!dirName.Contains("WebGL"))
{
continue;
} }
#endif #endif
if (File.Exists(tempJsonPath))
{
var patcher = new PatchScriptingAssemblyList(); var patcher = new PatchScriptingAssemblyList();
patcher.PathScriptingAssembilesFile(Path.GetDirectoryName(tempJsonPath)); patcher.PathScriptingAssembilesFile(tempJsonPath);
} }
}
string newTitle = BuildMainWindowTitleProxy(); string newTitle = BuildMainWindowTitleProxy();
return newTitle; return newTitle;
} }

View File

@ -13,7 +13,14 @@ namespace HybridCLR.Editor.AOT
{ {
public static byte[] Strip(byte[] assemblyBytes) public static byte[] Strip(byte[] assemblyBytes)
{ {
var mod = ModuleDefMD.Load(assemblyBytes); var context = ModuleDef.CreateModuleContext();
var readerOption = new ModuleCreationOptions(context)
{
Runtime = CLRRuntimeReaderKind.Mono
};
var mod = ModuleDefMD.Load(assemblyBytes, readerOption);
// remove all resources
mod.Resources.Clear();
foreach (var type in mod.GetTypes()) foreach (var type in mod.GetTypes())
{ {
if (type.HasGenericParameters) if (type.HasGenericParameters)
@ -31,8 +38,8 @@ namespace HybridCLR.Editor.AOT
} }
var writer = new System.IO.MemoryStream(); var writer = new System.IO.MemoryStream();
var options = new ModuleWriterOptions(mod); var options = new ModuleWriterOptions(mod);
options.MetadataOptions.Flags |= MetadataFlags.PreserveAll; options.MetadataOptions.Flags |= MetadataFlags.PreserveRids;
mod.Write(writer); mod.Write(writer, options);
writer.Flush(); writer.Flush();
return writer.ToArray(); return writer.ToArray();
} }

View File

@ -17,12 +17,16 @@ namespace HybridCLR.Editor.AOT
public AssemblyReferenceDeepCollector Collector { get; set; } public AssemblyReferenceDeepCollector Collector { get; set; }
public int MaxIterationCount { get; set; } public int MaxIterationCount { get; set; }
public bool ComputeAotAssembly { get; set; }
} }
private readonly int _maxInterationCount; private readonly int _maxInterationCount;
private readonly AssemblyReferenceDeepCollector _assemblyCollector; private readonly AssemblyReferenceDeepCollector _assemblyCollector;
private readonly bool _computeAotAssembly;
private readonly HashSet<GenericClass> _genericTypes = new HashSet<GenericClass>(); private readonly HashSet<GenericClass> _genericTypes = new HashSet<GenericClass>();
private readonly HashSet<GenericMethod> _genericMethods = new HashSet<GenericMethod>(); private readonly HashSet<GenericMethod> _genericMethods = new HashSet<GenericMethod>();
@ -47,6 +51,7 @@ namespace HybridCLR.Editor.AOT
{ {
_assemblyCollector = options.Collector; _assemblyCollector = options.Collector;
_maxInterationCount = options.MaxIterationCount; _maxInterationCount = options.MaxIterationCount;
_computeAotAssembly = options.ComputeAotAssembly;
_methodReferenceAnalyzer = new MethodReferenceAnalyzer(this.OnNewMethod); _methodReferenceAnalyzer = new MethodReferenceAnalyzer(this.OnNewMethod);
_hotUpdateAssemblyFiles = new HashSet<string>(options.Collector.GetRootAssemblyNames().Select(assName => assName + ".dll")); _hotUpdateAssemblyFiles = new HashSet<string>(options.Collector.GetRootAssemblyNames().Select(assName => assName + ".dll"));
} }
@ -71,7 +76,7 @@ namespace HybridCLR.Editor.AOT
private bool IsAotType(TypeDef type) private bool IsAotType(TypeDef type)
{ {
return !_hotUpdateAssemblyFiles.Contains(type.Module.Name); return _computeAotAssembly || !_hotUpdateAssemblyFiles.Contains(type.Module.Name);
} }
private bool IsAotGenericMethod(MethodDef method) private bool IsAotGenericMethod(MethodDef method)

View File

@ -39,7 +39,7 @@ namespace HybridCLR.Editor.AOT
{ {
_typeSimpleNameMapping.Add(e.Key.FullName, e.Value); _typeSimpleNameMapping.Add(e.Key.FullName, e.Value);
} }
_systemTypePattern = new Regex(string.Join("|", _typeSimpleNameMapping.Keys.Select (k => $@"\b{k}\b"))); _systemTypePattern = new Regex(string.Join("|", _typeSimpleNameMapping.Keys.Select (k => $@"\b{Regex.Escape(k)}\b")));
} }
public string PrettifyTypeSig(string typeSig) public string PrettifyTypeSig(string typeSig)

View File

@ -7,7 +7,7 @@ using System.Text;
using UnityEditor; using UnityEditor;
using System.Reflection; using System.Reflection;
using HybridCLR.Editor.Settings; using HybridCLR.Editor.Settings;
#if UNITY_2019 && UNITY_IOS #if UNITY_2019 && (UNITY_IOS || UNITY_TVOS)
using UnityEditor.Build; using UnityEditor.Build;
using UnityEditor.Callbacks; using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode; using UnityEditor.iOS.Xcode;
@ -17,22 +17,10 @@ namespace HybridCLR.Editor.BuildProcessors
{ {
public static class AddLil2cppSourceCodeToXcodeproj2019 public static class AddLil2cppSourceCodeToXcodeproj2019
{ {
//[MenuItem("Test/GenProj")]
//public static void Modify()
//{
// OnPostProcessBuild(BuildTarget.iOS, $"{SettingsUtil.ProjectDir}/Build-iOS");
//}
//[MenuItem("Test/CreateLumps")]
//public static void CreateLumpsCmd()
//{
// CreateLumps($"{SettingsUtil.LocalIl2CppDir}/libil2cpp", $"{SettingsUtil.HybridCLRDataDir}/lumps");
//}
[PostProcessBuild] [PostProcessBuild]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{ {
if (target != BuildTarget.iOS || !HybridCLRSettings.Instance.enable) if (!HybridCLRSettings.Instance.enable)
return; return;
/* /*
* 1. lump * 1. lump
@ -47,7 +35,7 @@ namespace HybridCLR.Editor.BuildProcessors
10. add "#include <stdio.h>" to Classes/Prefix.pch 10. add "#include <stdio.h>" to Classes/Prefix.pch
*/ */
string pbxprojFile = $"{pathToBuiltProject}/Unity-iPhone.xcodeproj/project.pbxproj"; string pbxprojFile = BuildProcessorUtil.GetXcodeProjectFile(pathToBuiltProject);
string srcLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp"; string srcLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp";
string dstLibil2cppDir = $"{pathToBuiltProject}/Libraries/libil2cpp"; string dstLibil2cppDir = $"{pathToBuiltProject}/Libraries/libil2cpp";
string lumpDir = $"{pathToBuiltProject}/Libraries/lumps"; string lumpDir = $"{pathToBuiltProject}/Libraries/lumps";
@ -147,6 +135,7 @@ namespace HybridCLR.Editor.BuildProcessors
{ {
newPro += " $(SRCROOT)/Libraries/external/xxHash"; newPro += " $(SRCROOT)/Libraries/external/xxHash";
} }
newPro += " $(SRCR00T)/Libraries/external/mono";
//Debug.Log($"config:{bcn} new prop:{newPro}"); //Debug.Log($"config:{bcn} new prop:{newPro}");
proj.SetBuildPropertyForConfig(configGuid, headerSearchPaths, newPro); proj.SetBuildPropertyForConfig(configGuid, headerSearchPaths, newPro);

View File

@ -7,7 +7,7 @@ using System.Text;
using UnityEditor; using UnityEditor;
using System.Reflection; using System.Reflection;
using HybridCLR.Editor.Settings; using HybridCLR.Editor.Settings;
#if (UNITY_2020 || UNITY_2021) && UNITY_IOS #if (UNITY_2020 || UNITY_2021) && (UNITY_IOS || UNITY_TVOS)
using UnityEditor.Build; using UnityEditor.Build;
using UnityEditor.Callbacks; using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode; using UnityEditor.iOS.Xcode;
@ -17,22 +17,11 @@ namespace HybridCLR.Editor.BuildProcessors
{ {
public static class AddLil2cppSourceCodeToXcodeproj2020Or2021 public static class AddLil2cppSourceCodeToXcodeproj2020Or2021
{ {
//[MenuItem("Test/GenProj")]
//public static void Modify()
//{
// OnPostProcessBuild(BuildTarget.iOS, $"{SettingsUtil.ProjectDir}/Build-iOS");
//}
//[MenuItem("Test/CreateLumps")]
//public static void CreateLumpsCmd()
//{
// CreateLumps($"{SettingsUtil.LocalIl2CppDir}/libil2cpp", $"{SettingsUtil.HybridCLRDataDir}/lumps");
//}
[PostProcessBuild] [PostProcessBuild]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{ {
if (target != BuildTarget.iOS || !HybridCLRSettings.Instance.enable) if (!HybridCLRSettings.Instance.enable)
return; return;
/* /*
* 1. lump * 1. lump
@ -46,7 +35,7 @@ namespace HybridCLR.Editor.BuildProcessors
9. add external/xxHash 9. add external/xxHash
*/ */
string pbxprojFile = $"{pathToBuiltProject}/Unity-iPhone.xcodeproj/project.pbxproj"; string pbxprojFile = BuildProcessorUtil.GetXcodeProjectFile(pathToBuiltProject);
string srcLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp"; string srcLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp";
string dstLibil2cppDir = $"{pathToBuiltProject}/Libraries/libil2cpp"; string dstLibil2cppDir = $"{pathToBuiltProject}/Libraries/libil2cpp";
string lumpDir = $"{pathToBuiltProject}/Libraries/lumps"; string lumpDir = $"{pathToBuiltProject}/Libraries/lumps";
@ -132,6 +121,7 @@ namespace HybridCLR.Editor.BuildProcessors
{ {
newPro += " $(SRCROOT)/Libraries/external/xxHash"; newPro += " $(SRCROOT)/Libraries/external/xxHash";
} }
newPro += " $(SRCR00T)/Libraries/external/mono";
//Debug.Log($"config:{bcn} new prop:{newPro}"); //Debug.Log($"config:{bcn} new prop:{newPro}");
proj.SetBuildPropertyForConfig(configGuid, headerSearchPaths, newPro); proj.SetBuildPropertyForConfig(configGuid, headerSearchPaths, newPro);

View File

@ -2,51 +2,73 @@ using HybridCLR.Editor.Installer;
using HybridCLR.Editor.Settings; using HybridCLR.Editor.Settings;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using UnityEditor; using UnityEditor;
using UnityEditor.Build; using UnityEditor.Build;
using UnityEditor.Callbacks; using UnityEditor.Callbacks;
using UnityEngine; using UnityEngine;
#if UNITY_2022_2_OR_NEWER && UNITY_IOS #if UNITY_2022 && (UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS)
namespace HybridCLR.Editor.BuildProcessors namespace HybridCLR.Editor.BuildProcessors
{ {
public static class AddLil2cppSourceCodeToXcodeproj2022OrNewer public static class AddLil2cppSourceCodeToXcodeproj2022OrNewer
{ {
//[MenuItem("HybridCLR/Modfiyxcode")]
//public static void Modify()
//{
// OnPostProcessBuild(BuildTarget.iOS, $"{SettingsUtil.ProjectDir}/Build-iOS");
//}
[PostProcessBuild] [PostProcessBuild]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{ {
if (target != BuildTarget.iOS || !HybridCLRSettings.Instance.enable) if (!HybridCLRSettings.Instance.enable)
return; return;
#if TUANJIE_2022_3_OR_NEWER string pbxprojFile = BuildProcessorUtil.GetXcodeProjectFile(pathToBuiltProject);
string pbxprojFile = $"{pathToBuiltProject}/Tuanjie-iPhone.xcodeproj/project.pbxproj";
#else
string pbxprojFile = $"{pathToBuiltProject}/Unity-iPhone.xcodeproj/project.pbxproj";
#endif
RemoveExternalLibil2cppOption(pbxprojFile); RemoveExternalLibil2cppOption(pbxprojFile);
CopyLibil2cppToXcodeProj(pathToBuiltProject); CopyLibil2cppToXcodeProj(pathToBuiltProject);
} }
private static string TryRemoveDunplicateShellScriptSegment(string pbxprojFile, string pbxprojContent)
{
// will appear duplicated Shell Script segment when append to existed xcode project.
// This is unity bug.
// we remove duplicated Shell Script to avoid build error.
string copyFileComment = @"/\* CopyFiles \*/,\s+([A-Z0-9]{24}) /\* ShellScript \*/,\s+([A-Z0-9]{24}) /\* ShellScript \*/,";
var m = Regex.Match(pbxprojContent, copyFileComment, RegexOptions.Multiline);
if (!m.Success)
{
return pbxprojContent;
}
if (m.Groups[1].Value != m.Groups[2].Value)
{
throw new BuildFailedException($"find invalid /* ShellScript */ segment");
}
int startIndexOfDupShellScript = m.Groups[2].Index;
int endIndexOfDupShellScript = pbxprojContent.IndexOf(",", startIndexOfDupShellScript);
pbxprojContent = pbxprojContent.Remove(startIndexOfDupShellScript, endIndexOfDupShellScript + 1 - startIndexOfDupShellScript);
Debug.LogWarning($"[AddLil2cppSourceCodeToXcodeproj] remove duplicated '/* ShellScript */' from file '{pbxprojFile}'");
return pbxprojContent;
}
private static void RemoveExternalLibil2cppOption(string pbxprojFile) private static void RemoveExternalLibil2cppOption(string pbxprojFile)
{ {
string pbxprojContent = File.ReadAllText(pbxprojFile, Encoding.UTF8); string pbxprojContent = File.ReadAllText(pbxprojFile, Encoding.UTF8);
string removeBuildOption = @"--external-lib-il2-cpp=\""$PROJECT_DIR/Libraries/libil2cpp.a\"""; string removeBuildOption = @"--external-lib-il2-cpp=\""$PROJECT_DIR/Libraries/libil2cpp.a\""";
if (!pbxprojContent.Contains(removeBuildOption)) if (pbxprojContent.Contains(removeBuildOption))
{ {
//throw new BuildFailedException("modified project.pbxproj fail");
Debug.LogError("[AddLil2cppSourceCodeToXcodeproj] modified project.pbxproj fail");
return;
}
pbxprojContent = pbxprojContent.Replace(removeBuildOption, ""); pbxprojContent = pbxprojContent.Replace(removeBuildOption, "");
File.WriteAllText(pbxprojFile, pbxprojContent, Encoding.UTF8);
Debug.Log($"[AddLil2cppSourceCodeToXcodeproj] remove il2cpp build option '{removeBuildOption}' from file '{pbxprojFile}'"); Debug.Log($"[AddLil2cppSourceCodeToXcodeproj] remove il2cpp build option '{removeBuildOption}' from file '{pbxprojFile}'");
} }
else
{
Debug.LogWarning($"[AddLil2cppSourceCodeToXcodeproj] project.pbxproj remove building option:'{removeBuildOption}' fail. This may occur when 'Append' to existing xcode project in building");
}
pbxprojContent = TryRemoveDunplicateShellScriptSegment(pbxprojFile, pbxprojContent);
File.WriteAllText(pbxprojFile, pbxprojContent, Encoding.UTF8);
}
private static void CopyLibil2cppToXcodeProj(string pathToBuiltProject) private static void CopyLibil2cppToXcodeProj(string pathToBuiltProject)
{ {

View File

@ -0,0 +1,34 @@
using HybridCLR.Editor.Installer;
using HybridCLR.Editor.Settings;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Callbacks;
using UnityEngine;
#if UNITY_2023_1_OR_NEWER && (UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS)
namespace HybridCLR.Editor.BuildProcessors
{
public static class AddLil2cppSourceCodeToXcodeproj2022OrNewer
{
[PostProcessBuild]
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{
if (!HybridCLRSettings.Instance.enable)
return;
CopyLibil2cppToXcodeProj(pathToBuiltProject);
}
private static void CopyLibil2cppToXcodeProj(string pathToBuiltProject)
{
string srcLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp";
string destLibil2cppDir = $"{pathToBuiltProject}/Il2CppOutputProject/IL2CPP/libil2cpp";
BashUtil.RemoveDir(destLibil2cppDir);
BashUtil.CopyDir(srcLibil2cppDir, destLibil2cppDir, true);
}
}
}
#endif

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2fa46135129b046a28014d58fdfd18ca

View File

@ -0,0 +1,25 @@
using System;
using System.IO;
using UnityEditor.Build;
namespace HybridCLR.Editor.BuildProcessors
{
public static class BuildProcessorUtil
{
public static string GetXcodeProjectFile(string pathToBuiltProject)
{
foreach (string dir in Directory.GetDirectories(pathToBuiltProject, "*.xcodeproj", SearchOption.TopDirectoryOnly))
{
string pbxprojFile = $"{dir}/project.pbxproj";
if (File.Exists(pbxprojFile))
{
return pbxprojFile;
}
}
throw new BuildFailedException($"can't find xxxx.xcodeproj/project.pbxproj in {pathToBuiltProject}");
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 7d883f182f206fa4db31f4085ce0ecdc guid: c680e56f90f2745298a90803c04f6efc
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -1,8 +1,10 @@
using HybridCLR.Editor.Settings; using HybridCLR.Editor.Settings;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEditor; using UnityEditor;
using UnityEditor.Build; using UnityEditor.Build;
@ -16,6 +18,8 @@ namespace HybridCLR.Editor.BuildProcessors
{ {
public int callbackOrder => 0; public int callbackOrder => 0;
public static bool DisableMethodBridgeDevelopmentFlagChecking { get; set; }
public void OnPreprocessBuild(BuildReport report) public void OnPreprocessBuild(BuildReport report)
{ {
HybridCLRSettings globalSettings = SettingsUtil.HybridCLRSettings; HybridCLRSettings globalSettings = SettingsUtil.HybridCLRSettings;
@ -67,6 +71,24 @@ namespace HybridCLR.Editor.BuildProcessors
Debug.LogWarning("[CheckSettings] No hot update modules configured in HybridCLRSettings"); Debug.LogWarning("[CheckSettings] No hot update modules configured in HybridCLRSettings");
} }
if (!DisableMethodBridgeDevelopmentFlagChecking)
{
string methodBridgeFile = $"{SettingsUtil.GeneratedCppDir}/MethodBridge.cpp";
var match = Regex.Match(File.ReadAllText(methodBridgeFile), @"// DEVELOPMENT=(\d)");
if (match.Success)
{
int developmentFlagInMethodBridge = int.Parse(match.Groups[1].Value);
int developmentFlagInEditorSettings = EditorUserBuildSettings.development ? 1 : 0;
if (developmentFlagInMethodBridge != developmentFlagInEditorSettings)
{
Debug.LogError($"[CheckSettings] MethodBridge.cpp DEVELOPMENT flag:{developmentFlagInMethodBridge} is inconsistent with EditorUserBuildSettings.development:{developmentFlagInEditorSettings}. Please run 'HybridCLR/Generate/All' before building.");
}
}
else
{
Debug.LogError("[CheckSettings] MethodBridge.cpp DEVELOPMENT flag not found. Please run 'HybridCLR/Generate/All' before building.");
}
}
} }
} }
} }

View File

@ -8,9 +8,11 @@ using System.Threading.Tasks;
using UnityEditor; using UnityEditor;
using UnityEditor.Build; using UnityEditor.Build;
using UnityEditor.Build.Reporting; using UnityEditor.Build.Reporting;
using UnityEditor.Il2Cpp;
using UnityEditor.UnityLinker; using UnityEditor.UnityLinker;
using UnityEngine; using UnityEngine;
#if !UNITY_2021_1_OR_NEWER
using UnityEditor.Il2Cpp;
#endif
namespace HybridCLR.Editor.BuildProcessors namespace HybridCLR.Editor.BuildProcessors
{ {
@ -33,10 +35,27 @@ namespace HybridCLR.Editor.BuildProcessors
return $"{projectDir}/Library/Bee/artifacts/WinPlayerBuildProgram/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/WinPlayerBuildProgram/ManagedStripped";
case BuildTarget.StandaloneLinux64: case BuildTarget.StandaloneLinux64:
return $"{projectDir}/Library/Bee/artifacts/LinuxPlayerBuildProgram/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/LinuxPlayerBuildProgram/ManagedStripped";
case BuildTarget.WSAPlayer:
return $"{projectDir}/Library/Bee/artifacts/UWPPlayerBuildProgram/ManagedStripped";
case BuildTarget.Android: case BuildTarget.Android:
return $"{projectDir}/Library/Bee/artifacts/Android/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/Android/ManagedStripped";
#if TUANJIE_2022_3_OR_NEWER
case BuildTarget.HMIAndroid:
return $"{projectDir}/Library/Bee/artifacts/HMIAndroid/ManagedStripped";
#endif
case BuildTarget.iOS: case BuildTarget.iOS:
#if UNITY_TVOS
case BuildTarget.tvOS:
#endif
return $"{projectDir}/Library/Bee/artifacts/iOS/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/iOS/ManagedStripped";
#if UNITY_VISIONOS
case BuildTarget.VisionOS:
#if UNITY_6000_0_OR_NEWER
return $"{projectDir}/Library/Bee/artifacts/VisionOS/ManagedStripped";
#else
return $"{projectDir}/Library/Bee/artifacts/iOS/ManagedStripped";
#endif
#endif
case BuildTarget.WebGL: case BuildTarget.WebGL:
return $"{projectDir}/Library/Bee/artifacts/WebGL/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/WebGL/ManagedStripped";
case BuildTarget.StandaloneOSX: case BuildTarget.StandaloneOSX:
@ -45,9 +64,11 @@ namespace HybridCLR.Editor.BuildProcessors
return $"{projectDir}/Library/Bee/artifacts/PS4PlayerBuildProgram/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/PS4PlayerBuildProgram/ManagedStripped";
case BuildTarget.PS5: case BuildTarget.PS5:
return $"{projectDir}/Library/Bee/artifacts/PS5PlayerBuildProgram/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/PS5PlayerBuildProgram/ManagedStripped";
#if TUANJIE_2022 #if UNITY_WEIXINMINIGAME
case BuildTarget.WeixinMiniGame: case BuildTarget.WeixinMiniGame:
return $"{projectDir}/Library/Bee/artifacts/WeixinMiniGame/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/WeixinMiniGame/ManagedStripped";
#endif
#if UNITY_OPENHARMONY
case BuildTarget.OpenHarmony: case BuildTarget.OpenHarmony:
return $"{projectDir}/Library/Bee/artifacts/OpenHarmonyPlayerBuildProgram/ManagedStripped"; return $"{projectDir}/Library/Bee/artifacts/OpenHarmonyPlayerBuildProgram/ManagedStripped";
#endif #endif
@ -65,8 +86,8 @@ namespace HybridCLR.Editor.BuildProcessors
public void OnBeforeConvertRun(BuildReport report, Il2CppBuildPipelineData data) public void OnBeforeConvertRun(BuildReport report, Il2CppBuildPipelineData data)
{ {
// 此回调只在 2020中调用 BuildTarget target = report.summary.platform;
CopyStripDlls(GetStripAssembliesDir2020(data.target), data.target); CopyStripDlls(GetStripAssembliesDir2020(target), target);
} }
#endif #endif
@ -94,7 +115,7 @@ namespace HybridCLR.Editor.BuildProcessors
public void OnPostprocessBuild(BuildReport report) public void OnPostprocessBuild(BuildReport report)
{ {
#if UNITY_2021_1_OR_NEWER #if UNITY_2021_1_OR_NEWER
BuildTarget target = EditorUserBuildSettings.activeBuildTarget; BuildTarget target = report.summary.platform;
string srcStripDllPath = GetStripAssembliesDir2021(target); string srcStripDllPath = GetStripAssembliesDir2021(target);
if (!string.IsNullOrEmpty(srcStripDllPath) && Directory.Exists(srcStripDllPath)) if (!string.IsNullOrEmpty(srcStripDllPath) && Directory.Exists(srcStripDllPath))
{ {
@ -105,7 +126,7 @@ namespace HybridCLR.Editor.BuildProcessors
public void OnPreprocessBuild(BuildReport report) public void OnPreprocessBuild(BuildReport report)
{ {
BuildTarget target = EditorUserBuildSettings.activeBuildTarget; BuildTarget target = report.summary.platform;
var dstPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(target); var dstPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
BashUtil.RecreateDir(dstPath); BashUtil.RecreateDir(dstPath);
} }

View File

@ -9,10 +9,12 @@ using UnityEditor;
using UnityEditor.Android; using UnityEditor.Android;
using UnityEditor.Build; using UnityEditor.Build;
using UnityEditor.Build.Reporting; using UnityEditor.Build.Reporting;
using UnityEditor.Il2Cpp;
using UnityEditor.UnityLinker; using UnityEditor.UnityLinker;
using UnityEngine; using UnityEngine;
using UnityFS; using UnityFS;
#if !UNITY_2023_1_OR_NEWER
using UnityEditor.Il2Cpp;
#endif
namespace HybridCLR.Editor.BuildProcessors namespace HybridCLR.Editor.BuildProcessors
{ {
@ -177,7 +179,7 @@ namespace HybridCLR.Editor.BuildProcessors
return true; return true;
} }
#if UNITY_WEBGL #if UNITY_WEBGL && !UNITY_2022_3_OR_NEWER
public void OnBeforeConvertRun(BuildReport report, Il2CppBuildPipelineData data) public void OnBeforeConvertRun(BuildReport report, Il2CppBuildPipelineData data)
{ {
PathScriptingAssembilesFile($"{SettingsUtil.ProjectDir}/Temp/StagingArea/Data"); PathScriptingAssembilesFile($"{SettingsUtil.ProjectDir}/Temp/StagingArea/Data");

View File

@ -2,6 +2,7 @@
using HybridCLR.Editor.Meta; using HybridCLR.Editor.Meta;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,13 +23,16 @@ namespace HybridCLR.Editor.Commands
GenerateAOTGenericReference(target); GenerateAOTGenericReference(target);
} }
/// <summary>
/// 计算热更代码中的泛型引用
/// </summary>
/// <param name="target"></param>
public static void GenerateAOTGenericReference(BuildTarget target) public static void GenerateAOTGenericReference(BuildTarget target)
{ {
var gs = SettingsUtil.HybridCLRSettings; var gs = SettingsUtil.HybridCLRSettings;
List<string> hotUpdateDllNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved; List<string> hotUpdateDllNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
using (AssemblyReferenceDeepCollector collector = new AssemblyReferenceDeepCollector(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(target, hotUpdateDllNames), hotUpdateDllNames)) AssemblyReferenceDeepCollector collector = new AssemblyReferenceDeepCollector(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(target, hotUpdateDllNames), hotUpdateDllNames);
{
var analyzer = new Analyzer(new Analyzer.Options var analyzer = new Analyzer(new Analyzer.Options
{ {
MaxIterationCount = Math.Min(20, gs.maxGenericReferenceIteration), MaxIterationCount = Math.Min(20, gs.maxGenericReferenceIteration),
@ -41,6 +45,88 @@ namespace HybridCLR.Editor.Commands
writer.Write(analyzer.AotGenericTypes.ToList(), analyzer.AotGenericMethods.ToList(), $"{Application.dataPath}/{gs.outputAOTGenericReferenceFile}"); writer.Write(analyzer.AotGenericTypes.ToList(), analyzer.AotGenericMethods.ToList(), $"{Application.dataPath}/{gs.outputAOTGenericReferenceFile}");
AssetDatabase.Refresh(); AssetDatabase.Refresh();
} }
//[MenuItem("HybridCLR/Generate/AOTGenericReference2", priority = 103)]
//public static void GeneratedAOTGenericReferenceExcludeExists()
//{
// GeneratedAOTGenericReferenceExcludeExists(EditorUserBuildSettings.activeBuildTarget);
//}
/// <summary>
/// 计算热更新代码中的泛型引用但排除AOT已经存在的泛型引用
/// </summary>
/// <param name="target"></param>
///
public static void GeneratedAOTGenericReferenceExcludeExistsAOTClassAndMethods(BuildTarget target)
{
var gs = SettingsUtil.HybridCLRSettings;
List<string> hotUpdateDllNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
AssemblyReferenceDeepCollector hotUpdateCollector = new AssemblyReferenceDeepCollector(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(target, hotUpdateDllNames), hotUpdateDllNames);
var hotUpdateAnalyzer = new Analyzer(new Analyzer.Options
{
MaxIterationCount = Math.Min(10, gs.maxGenericReferenceIteration),
Collector = hotUpdateCollector,
});
hotUpdateAnalyzer.Run();
string aotDllDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
List<string> aotAssemblyNames = Directory.Exists(aotDllDir) ?
Directory.GetFiles(aotDllDir, "*.dll", SearchOption.TopDirectoryOnly).Select(Path.GetFileNameWithoutExtension).ToList()
: new List<string>();
if (aotAssemblyNames.Count == 0)
{
throw new Exception($"no aot assembly found. please run `HybridCLR/Generate/All` or `HybridCLR/Generate/AotDlls` to generate aot dlls before runing `HybridCLR/Generate/AOTGenericReference`");
}
AssemblyReferenceDeepCollector aotCollector = new AssemblyReferenceDeepCollector(MetaUtil.CreateAOTAssemblyResolver(target), aotAssemblyNames);
var aotAnalyzer = new Analyzer(new Analyzer.Options
{
MaxIterationCount = Math.Min(10, gs.maxGenericReferenceIteration),
Collector = aotCollector,
ComputeAotAssembly = true,
});
aotAnalyzer.Run();
var (resultTypes, resultMethods) = ExcludeExistAOTGenericTypeAndMethodss(hotUpdateAnalyzer.AotGenericTypes.ToList(), hotUpdateAnalyzer.AotGenericMethods.ToList(), aotAnalyzer.AotGenericTypes.ToList(), aotAnalyzer.AotGenericMethods.ToList());
var writer = new GenericReferenceWriter();
writer.Write(resultTypes, resultMethods, $"{Application.dataPath}/{gs.outputAOTGenericReferenceFile}");
AssetDatabase.Refresh();
}
private static (List<GenericClass>, List<GenericMethod>) ExcludeExistAOTGenericTypeAndMethodss(List<GenericClass> hotUpdateTypes, List<GenericMethod> hotUpdateMethods, List<GenericClass> aotTypes, List<GenericMethod> aotMethods)
{
var types = new List<GenericClass>();
var typeSig2Type = hotUpdateTypes.ToDictionary(t => t.Type.DefinitionAssembly.Name + ":" + t.ToTypeSig(), t => t);
foreach (var t in aotTypes)
{
string key = t.Type.DefinitionAssembly.Name + ":" + t.ToTypeSig();
if (typeSig2Type.TryGetValue(key, out var removedType))
{
typeSig2Type.Remove(key);
Debug.Log($"remove AOT type:{removedType.ToTypeSig()} ");
}
}
var methodSig2Method = hotUpdateMethods.ToDictionary(m => m.Method.DeclaringType.DefinitionAssembly.Name + ":" + m.ToMethodSpec().ToString(), m => m);
foreach (var m in aotMethods)
{
string key = m.Method.DeclaringType.DefinitionAssembly.Name + ":" + m.ToMethodSpec().ToString();
if (methodSig2Method.TryGetValue(key, out var removedMethod))
{
methodSig2Method.Remove(key);
Debug.Log($"remove AOT method:{removedMethod.ToMethodSpec()} ");
}
}
return (typeSig2Type.Values.ToList(), methodSig2Method.Values.ToList());
} }
} }
} }

View File

@ -25,10 +25,15 @@ namespace HybridCLR.Editor.Commands
#if UNITY_2022 #if UNITY_2022
UnityEditor.EditorUtility.ClearProgressBar(); UnityEditor.EditorUtility.ClearProgressBar();
#endif #endif
Debug.Log("compile finish!!!"); Debug.Log($"compile finish!!! buildDir:{buildDir} target:{target} development:{developmentBuild}");
} }
public static void CompileDll(BuildTarget target, bool developmentBuild = false) public static void CompileDll(BuildTarget target)
{
CompileDll(target, EditorUserBuildSettings.development);
}
public static void CompileDll(BuildTarget target, bool developmentBuild)
{ {
CompileDll(SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), target, developmentBuild); CompileDll(SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), target, developmentBuild);
} }
@ -36,10 +41,16 @@ namespace HybridCLR.Editor.Commands
[MenuItem("HybridCLR/CompileDll/ActiveBuildTarget", priority = 100)] [MenuItem("HybridCLR/CompileDll/ActiveBuildTarget", priority = 100)]
public static void CompileDllActiveBuildTarget() public static void CompileDllActiveBuildTarget()
{ {
CompileDll(EditorUserBuildSettings.activeBuildTarget); CompileDll(EditorUserBuildSettings.activeBuildTarget, EditorUserBuildSettings.development);
} }
[MenuItem("HybridCLR/CompileDll/ActiveBuildTarget_Development", priority = 101)] [MenuItem("HybridCLR/CompileDll/ActiveBuildTarget_Release", priority = 102)]
public static void CompileDllActiveBuildTargetRelease()
{
CompileDll(EditorUserBuildSettings.activeBuildTarget, false);
}
[MenuItem("HybridCLR/CompileDll/ActiveBuildTarget_Development", priority = 104)]
public static void CompileDllActiveBuildTargetDevelopment() public static void CompileDllActiveBuildTargetDevelopment()
{ {
CompileDll(EditorUserBuildSettings.activeBuildTarget, true); CompileDll(EditorUserBuildSettings.activeBuildTarget, true);

View File

@ -1,4 +1,5 @@
using HybridCLR.Editor.Link; using HybridCLR.Editor.Link;
using HybridCLR.Editor.Settings;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -19,8 +20,10 @@ namespace HybridCLR.Editor.Commands
{ {
UnityVersion = Application.unityVersion, UnityVersion = Application.unityVersion,
HotUpdateAssemblies = SettingsUtil.HotUpdateAssemblyNamesIncludePreserved, HotUpdateAssemblies = SettingsUtil.HotUpdateAssemblyNamesIncludePreserved,
OutputFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/generated/UnityVersion.h", UnityVersionTemplateFile = $"{SettingsUtil.TemplatePathInPackage}/UnityVersion.h.tpl",
OutputFile2 = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/generated/AssemblyManifest.cpp", UnityVersionOutputFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/generated/UnityVersion.h",
AssemblyManifestTemplateFile = $"{SettingsUtil.TemplatePathInPackage}/AssemblyManifest.cpp.tpl",
AssemblyManifestOutputFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/generated/AssemblyManifest.cpp",
}; };
var g = new Il2CppDef.Il2CppDefGenerator(options); var g = new Il2CppDef.Il2CppDefGenerator(options);

View File

@ -30,29 +30,31 @@ namespace HybridCLR.Editor.Commands
Directory.Delete(il2cppBuildCachePath, true); Directory.Delete(il2cppBuildCachePath, true);
} }
private static void GenerateMethodBridgeCppFile(Analyzer analyzer, string outputFile) private static void GenerateMethodBridgeCppFile(IReadOnlyCollection<GenericMethod> genericMethods, List<RawMonoPInvokeCallbackMethodInfo> reversePInvokeMethods, IReadOnlyCollection<CallNativeMethodSignatureInfo> calliMethodSignatures, string tempFile, string outputFile)
{ {
string templateCode = File.ReadAllText(outputFile, Encoding.UTF8); string templateCode = File.ReadAllText(tempFile, Encoding.UTF8);
var g = new Generator(new Generator.Options() var g = new Generator(new Generator.Options()
{ {
TemplateCode = templateCode, TemplateCode = templateCode,
OutputFile = outputFile, OutputFile = outputFile,
GenericMethods = analyzer.GenericMethods, GenericMethods = genericMethods,
ReversePInvokeMethods = reversePInvokeMethods,
CalliMethodSignatures = calliMethodSignatures,
Development = EditorUserBuildSettings.development,
}); });
g.PrepareMethods();
g.Generate(); g.Generate();
Debug.LogFormat("[MethodBridgeGeneratorCommand] output:{0}", outputFile); Debug.LogFormat("[MethodBridgeGeneratorCommand] output:{0}", outputFile);
} }
[MenuItem("HybridCLR/Generate/MethodBridge", priority = 101)] [MenuItem("HybridCLR/Generate/MethodBridgeAndReversePInvokeWrapper", priority = 101)]
public static void CompileAndGenerateMethodBridge() public static void GenerateMethodBridgeAndReversePInvokeWrapper()
{ {
BuildTarget target = EditorUserBuildSettings.activeBuildTarget; BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
GenerateMethodBridge(target); GenerateMethodBridgeAndReversePInvokeWrapper(target);
} }
public static void GenerateMethodBridge(BuildTarget target) public static void GenerateMethodBridgeAndReversePInvokeWrapper(BuildTarget target)
{ {
string aotDllDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target); string aotDllDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
List<string> aotAssemblyNames = Directory.Exists(aotDllDir) ? List<string> aotAssemblyNames = Directory.Exists(aotDllDir) ?
@ -62,18 +64,33 @@ namespace HybridCLR.Editor.Commands
{ {
throw new Exception($"no aot assembly found. please run `HybridCLR/Generate/All` or `HybridCLR/Generate/AotDlls` to generate aot dlls before runing `HybridCLR/Generate/MethodBridge`"); throw new Exception($"no aot assembly found. please run `HybridCLR/Generate/All` or `HybridCLR/Generate/AotDlls` to generate aot dlls before runing `HybridCLR/Generate/MethodBridge`");
} }
using (AssemblyReferenceDeepCollector collector = new AssemblyReferenceDeepCollector(MetaUtil.CreateAOTAssemblyResolver(target), aotAssemblyNames)) AssemblyReferenceDeepCollector collector = new AssemblyReferenceDeepCollector(MetaUtil.CreateAOTAssemblyResolver(target), aotAssemblyNames);
{
var analyzer = new Analyzer(new Analyzer.Options var methodBridgeAnalyzer = new Analyzer(new Analyzer.Options
{ {
MaxIterationCount = Math.Min(20, SettingsUtil.HybridCLRSettings.maxMethodBridgeGenericIteration), MaxIterationCount = Math.Min(20, SettingsUtil.HybridCLRSettings.maxMethodBridgeGenericIteration),
Collector = collector, Collector = collector,
}); });
analyzer.Run(); methodBridgeAnalyzer.Run();
List<string> hotUpdateDlls = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
var cache = new AssemblyCache(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(target, hotUpdateDlls));
var reversePInvokeAnalyzer = new MonoPInvokeCallbackAnalyzer(cache, hotUpdateDlls);
reversePInvokeAnalyzer.Run();
var calliAnalyzer = new CalliAnalyzer(cache, hotUpdateDlls);
calliAnalyzer.Run();
var pinvokeAnalyzer = new PInvokeAnalyzer(cache, hotUpdateDlls);
pinvokeAnalyzer.Run();
var callPInvokeMethodSignatures = pinvokeAnalyzer.PInvokeMethodSignatures;
string templateFile = $"{SettingsUtil.TemplatePathInPackage}/MethodBridge.cpp.tpl";
string outputFile = $"{SettingsUtil.GeneratedCppDir}/MethodBridge.cpp"; string outputFile = $"{SettingsUtil.GeneratedCppDir}/MethodBridge.cpp";
GenerateMethodBridgeCppFile(analyzer, outputFile);
} var callNativeMethodSignatures = calliAnalyzer.CalliMethodSignatures.Concat(pinvokeAnalyzer.PInvokeMethodSignatures).ToList();
GenerateMethodBridgeCppFile(methodBridgeAnalyzer.GenericMethods, reversePInvokeAnalyzer.ReversePInvokeMethods, callNativeMethodSignatures, templateFile, outputFile);
CleanIl2CppBuildCache(); CleanIl2CppBuildCache();
} }

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEditor; using UnityEditor;
using UnityEditor.Build;
namespace HybridCLR.Editor.Commands namespace HybridCLR.Editor.Commands
{ {
@ -15,8 +16,13 @@ namespace HybridCLR.Editor.Commands
[MenuItem("HybridCLR/Generate/All", priority = 200)] [MenuItem("HybridCLR/Generate/All", priority = 200)]
public static void GenerateAll() public static void GenerateAll()
{ {
var installer = new Installer.InstallerController();
if (!installer.HasInstalledHybridCLR())
{
throw new BuildFailedException($"You have not initialized HybridCLR, please install it via menu 'HybridCLR/Installer'");
}
BuildTarget target = EditorUserBuildSettings.activeBuildTarget; BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
CompileDllCommand.CompileDll(target); CompileDllCommand.CompileDll(target, EditorUserBuildSettings.development);
Il2CppDefGeneratorCommand.GenerateIl2CppDef(); Il2CppDefGeneratorCommand.GenerateIl2CppDef();
// 这几个生成依赖HotUpdateDlls // 这几个生成依赖HotUpdateDlls
@ -26,8 +32,7 @@ namespace HybridCLR.Editor.Commands
StripAOTDllCommand.GenerateStripedAOTDlls(target); StripAOTDllCommand.GenerateStripedAOTDlls(target);
// 桥接函数生成依赖于AOT dll必须保证已经build过生成AOT dll // 桥接函数生成依赖于AOT dll必须保证已经build过生成AOT dll
MethodBridgeGeneratorCommand.GenerateMethodBridge(target); MethodBridgeGeneratorCommand.GenerateMethodBridgeAndReversePInvokeWrapper(target);
ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper(target);
AOTReferenceGeneratorCommand.GenerateAOTGenericReference(target); AOTReferenceGeneratorCommand.GenerateAOTGenericReference(target);
} }
} }

View File

@ -1,49 +0,0 @@
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Link;
using HybridCLR.Editor.Meta;
using HybridCLR.Editor.ReversePInvokeWrap;
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.Commands
{
public static class ReversePInvokeWrapperGeneratorCommand
{
[MenuItem("HybridCLR/Generate/ReversePInvokeWrapper", priority = 103)]
public static void CompileAndGenerateReversePInvokeWrapper()
{
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
CompileDllCommand.CompileDll(target);
GenerateReversePInvokeWrapper(target);
}
public static void GenerateReversePInvokeWrapper(BuildTarget target)
{
List<string> hotUpdateDlls = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
using (var cache = new AssemblyCache(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(target, hotUpdateDlls)))
{
var analyzer = new ReversePInvokeWrap.Analyzer(cache, hotUpdateDlls);
analyzer.Run();
string outputFile = $"{SettingsUtil.GeneratedCppDir}/ReversePInvokeMethodStub.cpp";
List<ABIReversePInvokeMethodInfo> methods = analyzer.BuildABIMethods();
Debug.Log($"GenerateReversePInvokeWrapper. wraperCount:{methods.Sum(m => m.Count)} output:{outputFile}");
var generator = new Generator();
generator.Generate(methods, outputFile);
Debug.LogFormat("[ReversePInvokeWrapperGeneratorCommand] output:{0}", outputFile);
}
MethodBridgeGeneratorCommand.CleanIl2CppBuildCache();
}
}
}

View File

@ -1,4 +1,5 @@
using HybridCLR.Editor.Installer; using HybridCLR.Editor.BuildProcessors;
using HybridCLR.Editor.Installer;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -56,11 +57,11 @@ namespace HybridCLR.Editor.Commands
switch(target) switch(target)
{ {
case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows:
case BuildTarget.StandaloneWindows64: return $"{buildDir}/{target}"; case BuildTarget.StandaloneWindows64: return $"{buildDir}/{PlayerSettings.productName}.exe";
case BuildTarget.StandaloneOSX: return buildDir; case BuildTarget.StandaloneOSX: return buildDir;
case BuildTarget.iOS: return buildDir; case BuildTarget.iOS: return buildDir;
case BuildTarget.Android: return buildDir; case BuildTarget.Android: return buildDir;
case BuildTarget.StandaloneLinux64: return buildDir; case BuildTarget.StandaloneLinux64: return $"{buildDir}/{PlayerSettings.productName}";
default: return buildDir; default: return buildDir;
} }
} }
@ -71,9 +72,6 @@ namespace HybridCLR.Editor.Commands
BashUtil.RemoveDir(outputPath); BashUtil.RemoveDir(outputPath);
var buildOptions = GetBuildPlayerOptions(target); var buildOptions = GetBuildPlayerOptions(target);
#if UNITY_2021_2_OR_NEWER
buildOptions |= BuildOptions.CleanBuildCache;
#endif
bool oldExportAndroidProj = EditorUserBuildSettings.exportAsGoogleAndroidProject; bool oldExportAndroidProj = EditorUserBuildSettings.exportAsGoogleAndroidProject;
#if UNITY_EDITOR_OSX #if UNITY_EDITOR_OSX
@ -81,14 +79,18 @@ namespace HybridCLR.Editor.Commands
#elif UNITY_EDITOR_WIN #elif UNITY_EDITOR_WIN
bool oldCreateSolution = UnityEditor.WindowsStandalone.UserBuildSettings.createSolution; bool oldCreateSolution = UnityEditor.WindowsStandalone.UserBuildSettings.createSolution;
#endif #endif
#if TUANJIE_2022 #if TUANJIE_2022_3_OR_NEWER
bool oldOpenHarmonyProj = EditorUserBuildSettings.exportAsOpenHarmonyProject; bool oldOpenHarmonyProj = EditorUserBuildSettings.exportAsOpenHarmonyProject;
#endif #endif
bool oldBuildScriptsOnly = EditorUserBuildSettings.buildScriptsOnly; bool oldBuildScriptsOnly = EditorUserBuildSettings.buildScriptsOnly;
string oldBuildLocation = EditorUserBuildSettings.GetBuildLocation(target);
try
{
CheckSettings.DisableMethodBridgeDevelopmentFlagChecking = true;
EditorUserBuildSettings.buildScriptsOnly = true; EditorUserBuildSettings.buildScriptsOnly = true;
string location = GetLocationPathName(outputPath, target); string location = GetLocationPathName(outputPath, target);
string oldBuildLocation = EditorUserBuildSettings.GetBuildLocation(target);
EditorUserBuildSettings.SetBuildLocation(target, location); EditorUserBuildSettings.SetBuildLocation(target, location);
switch (target) switch (target)
@ -96,33 +98,33 @@ namespace HybridCLR.Editor.Commands
case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows:
case BuildTarget.StandaloneWindows64: case BuildTarget.StandaloneWindows64:
{ {
#if UNITY_EDITOR_WIN #if UNITY_EDITOR_WIN
UnityEditor.WindowsStandalone.UserBuildSettings.createSolution = true; UnityEditor.WindowsStandalone.UserBuildSettings.createSolution = true;
#endif #endif
break; break;
} }
case BuildTarget.StandaloneOSX: case BuildTarget.StandaloneOSX:
{ {
#if UNITY_EDITOR_OSX #if UNITY_EDITOR_OSX
UnityEditor.OSXStandalone.UserBuildSettings.createXcodeProject = true; UnityEditor.OSXStandalone.UserBuildSettings.createXcodeProject = true;
#endif #endif
break; break;
} }
#if TUANJIE_2022 #if TUANJIE_2022_3_OR_NEWER
case BuildTarget.HMIAndroid: case BuildTarget.HMIAndroid:
#endif #endif
case BuildTarget.Android: case BuildTarget.Android:
{ {
EditorUserBuildSettings.exportAsGoogleAndroidProject = true; EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
break; break;
} }
#if TUANJIE_2022 #if TUANJIE_2022_3_OR_NEWER
case BuildTarget.OpenHarmony: case BuildTarget.OpenHarmony:
{ {
EditorUserBuildSettings.exportAsOpenHarmonyProject = true; EditorUserBuildSettings.exportAsOpenHarmonyProject = true;
break; break;
} }
#endif #endif
} }
Debug.Log($"GenerateStripedAOTDlls build option:{buildOptions}"); Debug.Log($"GenerateStripedAOTDlls build option:{buildOptions}");
@ -134,10 +136,23 @@ namespace HybridCLR.Editor.Commands
options = buildOptions, options = buildOptions,
target = target, target = target,
targetGroup = BuildPipeline.GetBuildTargetGroup(target), targetGroup = BuildPipeline.GetBuildTargetGroup(target),
#if UNITY_SERVER
subtarget = (int)StandaloneBuildSubtarget.Server,
#endif
}; };
var report = BuildPipeline.BuildPlayer(buildPlayerOptions); var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
if (report.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
{
throw new Exception("GenerateStripedAOTDlls failed");
}
}
finally
{
CheckSettings.DisableMethodBridgeDevelopmentFlagChecking = false;
EditorUserBuildSettings.buildScriptsOnly = oldBuildScriptsOnly; EditorUserBuildSettings.buildScriptsOnly = oldBuildScriptsOnly;
EditorUserBuildSettings.SetBuildLocation(target, oldBuildLocation); EditorUserBuildSettings.SetBuildLocation(target, oldBuildLocation);
@ -158,7 +173,7 @@ namespace HybridCLR.Editor.Commands
#endif #endif
break; break;
} }
#if TUANJIE_2022 #if TUANJIE_2022_3_OR_NEWER
case BuildTarget.HMIAndroid: case BuildTarget.HMIAndroid:
#endif #endif
case BuildTarget.Android: case BuildTarget.Android:
@ -166,7 +181,7 @@ namespace HybridCLR.Editor.Commands
EditorUserBuildSettings.exportAsGoogleAndroidProject = oldExportAndroidProj; EditorUserBuildSettings.exportAsGoogleAndroidProject = oldExportAndroidProj;
break; break;
} }
#if TUANJIE_2022 #if TUANJIE_2022_3_OR_NEWER
case BuildTarget.OpenHarmony: case BuildTarget.OpenHarmony:
{ {
EditorUserBuildSettings.exportAsOpenHarmonyProject = oldOpenHarmonyProj; EditorUserBuildSettings.exportAsOpenHarmonyProject = oldOpenHarmonyProj;
@ -174,10 +189,6 @@ namespace HybridCLR.Editor.Commands
} }
#endif #endif
} }
if (report.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
{
throw new Exception("GenerateStripedAOTDlls failed");
} }
Debug.Log($"GenerateStripedAOTDlls target:{target} path:{outputPath}"); Debug.Log($"GenerateStripedAOTDlls target:{target} path:{outputPath}");
} }

View File

@ -13,17 +13,19 @@ namespace HybridCLR.Editor.HotUpdate
{ {
private readonly HashSet<string> _aotAssNames; private readonly HashSet<string> _aotAssNames;
private readonly HashSet<string> _hotUpdateAssNames;
private readonly AssemblyCache _assCache; private readonly AssemblyCache _assCache;
public MissingMetadataChecker(string aotDllDir, IEnumerable<string> excludeDllNames) public MissingMetadataChecker(string aotDllDir, IEnumerable<string> hotUpdateAssNames)
{ {
var excludeDllNameSet = new HashSet<string>(excludeDllNames ?? new List<string>()); _hotUpdateAssNames = new HashSet<string>(hotUpdateAssNames ?? new List<string>());
_aotAssNames = new HashSet<string>(); _aotAssNames = new HashSet<string>();
foreach (var aotFile in Directory.GetFiles(aotDllDir, "*.dll")) foreach (var aotFile in Directory.GetFiles(aotDllDir, "*.dll"))
{ {
string aotAssName = Path.GetFileNameWithoutExtension(aotFile); string aotAssName = Path.GetFileNameWithoutExtension(aotFile);
if (excludeDllNameSet.Contains(aotAssName)) if (_hotUpdateAssNames.Contains(aotAssName))
{ {
continue; continue;
} }
@ -34,6 +36,8 @@ namespace HybridCLR.Editor.HotUpdate
public bool Check(string hotUpdateDllPath) public bool Check(string hotUpdateDllPath)
{ {
bool anyMissing = false;
ModuleDef mod = ModuleDefMD.Load(File.ReadAllBytes(hotUpdateDllPath), _assCache.ModCtx); ModuleDef mod = ModuleDefMD.Load(File.ReadAllBytes(hotUpdateDllPath), _assCache.ModCtx);
foreach (var refass in mod.GetAssemblyRefs()) foreach (var refass in mod.GetAssemblyRefs())
@ -43,9 +47,13 @@ namespace HybridCLR.Editor.HotUpdate
{ {
_assCache.LoadModule(refass.Name, true); _assCache.LoadModule(refass.Name, true);
} }
else if (!_hotUpdateAssNames.Contains(refAssName))
{
UnityEngine.Debug.LogError($"Missing AOT Assembly: {refAssName}");
anyMissing = true;
}
} }
bool anyMissing = false;
foreach (TypeRef typeRef in mod.GetTypeRefs()) foreach (TypeRef typeRef in mod.GetTypeRefs())
{ {
@ -61,31 +69,36 @@ namespace HybridCLR.Editor.HotUpdate
} }
} }
foreach (IMethodDefOrRef methodRef in mod.GetMemberRefs()) foreach (IMethodDefOrRef memberRef in mod.GetMemberRefs())
{ {
if (methodRef.DeclaringType.DefinitionAssembly == null) if (memberRef.DeclaringType.DefinitionAssembly == null)
{ {
continue; continue;
} }
string defAssName = methodRef.DeclaringType.DefinitionAssembly.Name; string defAssName = memberRef.DeclaringType.DefinitionAssembly.Name;
if (!_aotAssNames.Contains(defAssName)) if (!_aotAssNames.Contains(defAssName))
{ {
continue; continue;
} }
if (methodRef.IsField) if (memberRef.IsField)
{ {
IField field = (IField)memberRef;
if (field.ResolveFieldDef() == null)
{
UnityEngine.Debug.LogError($"Missing Field: {memberRef.FullName}");
anyMissing = true;
} }
else if (methodRef.IsMethod) }
else if (memberRef.IsMethod)
{ {
TypeSig declaringTypeSig = methodRef.DeclaringType.ToTypeSig(); TypeSig declaringTypeSig = memberRef.DeclaringType.ToTypeSig();
if (methodRef.ResolveMethodDef() == null) if (memberRef.ResolveMethodDef() == null)
{ {
if (declaringTypeSig.ElementType == ElementType.Array || declaringTypeSig.ElementType == ElementType.SZArray) if (declaringTypeSig.ElementType == ElementType.Array || declaringTypeSig.ElementType == ElementType.SZArray)
{ {
continue; continue;
} }
UnityEngine.Debug.LogError($"Missing Method: {methodRef.FullName}"); UnityEngine.Debug.LogError($"Missing Method: {memberRef.FullName}");
anyMissing = true; anyMissing = true;
} }
} }

View File

@ -17,9 +17,13 @@ namespace HybridCLR.Editor.Il2CppDef
{ {
public List<string> HotUpdateAssemblies { get; set; } public List<string> HotUpdateAssemblies { get; set; }
public string OutputFile { get; set; } public string UnityVersionTemplateFile { get; set; }
public string OutputFile2 { get; set; } public string UnityVersionOutputFile { get; set; }
public string AssemblyManifestTemplateFile { get; set; }
public string AssemblyManifestOutputFile { get; set; }
public string UnityVersion { get; set; } public string UnityVersion { get; set; }
} }
@ -41,7 +45,7 @@ namespace HybridCLR.Editor.Il2CppDef
private void GenerateIl2CppConfig() private void GenerateIl2CppConfig()
{ {
var frr = new FileRegionReplace(File.ReadAllText(_options.OutputFile)); var frr = new FileRegionReplace(File.ReadAllText(_options.UnityVersionTemplateFile));
List<string> lines = new List<string>(); List<string> lines = new List<string>();
@ -52,7 +56,14 @@ namespace HybridCLR.Editor.Il2CppDef
lines.Add($"#define HYBRIDCLR_UNITY_VERSION {majorVer}{minorVer1.ToString("D2")}{minorVer2.ToString("D2")}"); lines.Add($"#define HYBRIDCLR_UNITY_VERSION {majorVer}{minorVer1.ToString("D2")}{minorVer2.ToString("D2")}");
lines.Add($"#define HYBRIDCLR_UNITY_{majorVer} 1"); lines.Add($"#define HYBRIDCLR_UNITY_{majorVer} 1");
for (int ver = 2019; ver <= 2024; ver++) for (int ver = 2019; ver <= 2023; ver++)
{
if (majorVer >= ver)
{
lines.Add($"#define HYBRIDCLR_UNITY_{ver}_OR_NEW 1");
}
}
for (int ver = 6000; ver <= 6100; ver++)
{ {
if (majorVer >= ver) if (majorVer >= ver)
{ {
@ -60,15 +71,25 @@ namespace HybridCLR.Editor.Il2CppDef
} }
} }
#if TUANJIE_1_1_OR_NEWER
var tuanjieMatch = Regex.Matches(Application.tuanjieVersion, @"(\d+)\.(\d+)\.(\d+)");
int tuanjieMajorVer = int.Parse(tuanjieMatch[0].Groups[1].Value);
int tuanjieMinorVer1 = int.Parse(tuanjieMatch[0].Groups[2].Value);
int tuanjieMinorVer2 = int.Parse(tuanjieMatch[0].Groups[3].Value);
lines.Add($"#define HYBRIDCLR_TUANJIE_VERSION {tuanjieMajorVer}{tuanjieMinorVer1.ToString("D2")}{tuanjieMinorVer2.ToString("D2")}");
#elif TUANJIE_2022_3_OR_NEWER
lines.Add($"#define HYBRIDCLR_TUANJIE_VERSION 10000");
#endif
frr.Replace("UNITY_VERSION", string.Join("\n", lines)); frr.Replace("UNITY_VERSION", string.Join("\n", lines));
frr.Commit(_options.OutputFile); frr.Commit(_options.UnityVersionOutputFile);
Debug.Log($"[HybridCLR.Editor.Il2CppDef.Generator] output:{_options.OutputFile}"); Debug.Log($"[HybridCLR.Editor.Il2CppDef.Generator] output:{_options.UnityVersionOutputFile}");
} }
private void GeneratePlaceHolderAssemblies() private void GeneratePlaceHolderAssemblies()
{ {
var frr = new FileRegionReplace(File.ReadAllText(_options.OutputFile2)); var frr = new FileRegionReplace(File.ReadAllText(_options.AssemblyManifestTemplateFile));
List<string> lines = new List<string>(); List<string> lines = new List<string>();
@ -79,8 +100,8 @@ namespace HybridCLR.Editor.Il2CppDef
frr.Replace("PLACE_HOLDER", string.Join("\n", lines)); frr.Replace("PLACE_HOLDER", string.Join("\n", lines));
frr.Commit(_options.OutputFile2); frr.Commit(_options.AssemblyManifestOutputFile);
Debug.Log($"[HybridCLR.Editor.Il2CppDef.Generator] output:{_options.OutputFile2}"); Debug.Log($"[HybridCLR.Editor.Il2CppDef.Generator] output:{_options.AssemblyManifestOutputFile}");
} }
} }
} }

View File

@ -83,7 +83,7 @@ namespace HybridCLR.Editor.Installer
{ {
RemoveDir(subDir); RemoveDir(subDir);
} }
Directory.Delete(dir); Directory.Delete(dir, true);
break; break;
} }
catch (Exception e) catch (Exception e)
@ -102,42 +102,23 @@ namespace HybridCLR.Editor.Installer
} }
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
} }
private static void CopyWithCheckLongFile(string srcFile, string dstFile)
{
var maxPathLength = 255;
#if UNITY_EDITOR_OSX
maxPathLength = 1024;
#endif
if (srcFile.Length > maxPathLength)
{
UnityEngine.Debug.LogError($"srcFile:{srcFile} path is too long. copy ignore!");
return;
}
if (dstFile.Length > maxPathLength)
{
UnityEngine.Debug.LogError($"dstFile:{dstFile} path is too long. copy ignore!");
return;
}
File.Copy(srcFile, dstFile);
}
public static void CopyDir(string src, string dst, bool log = false) public static void CopyDir(string src, string dst, bool log = false)
{ {
if (log) if (log)
{ {
UnityEngine.Debug.Log($"[BashUtil] CopyDir {src} => {dst}"); UnityEngine.Debug.Log($"[BashUtil] CopyDir {src} => {dst}");
} }
if (Directory.Exists(dst))
{
RemoveDir(dst); RemoveDir(dst);
Directory.CreateDirectory(dst);
foreach(var file in Directory.GetFiles(src))
{
CopyWithCheckLongFile(file, $"{dst}/{Path.GetFileName(file)}");
} }
foreach(var subDir in Directory.GetDirectories(src)) else
{ {
CopyDir(subDir, $"{dst}/{Path.GetFileName(subDir)}"); string parentDir = Path.GetDirectoryName(Path.GetFullPath(dst));
Directory.CreateDirectory(parentDir);
} }
UnityEditor.FileUtil.CopyFileOrDirectory(src, dst);
} }
} }
} }

View File

@ -122,10 +122,12 @@ namespace HybridCLR.Editor.Installer
{ {
switch(majorVersion) switch(majorVersion)
{ {
case 2019: return $"2019.4.0"; case 2019: return "2019.4.0";
case 2020: return $"2020.3.0"; case 2020: return "2020.3.0";
case 2021: return $"2021.3.0"; case 2021: return "2021.3.0";
case 2022: return $"2022.3.0"; case 2022: return "2022.3.0";
case 2023: return "2023.2.0";
case 6000: return "6000.0.0";
default: return $"2020.3.0"; default: return $"2020.3.0";
} }
} }
@ -144,7 +146,8 @@ namespace HybridCLR.Editor.Installer
{ {
return CompatibleType.Incompatible; return CompatibleType.Incompatible;
} }
if ((version.major == 2019 && version.minor1 < 4) || (version.major >= 2020 && version.minor1 < 3)) if ((version.major == 2019 && version.minor1 < 4)
|| (version.major >= 2020 && version.major <= 2022 && version.minor1 < 3))
{ {
return CompatibleType.MaybeIncompatible; return CompatibleType.MaybeIncompatible;
} }

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using dnlib.DotNet; using dnlib.DotNet;
using HybridCLR.Editor.Meta; using HybridCLR.Editor.Meta;
using UnityEditor; using UnityEditor;
using UnityEngine;
using IAssemblyResolver = HybridCLR.Editor.Meta.IAssemblyResolver; using IAssemblyResolver = HybridCLR.Editor.Meta.IAssemblyResolver;
namespace HybridCLR.Editor.Link namespace HybridCLR.Editor.Link
@ -22,8 +23,7 @@ namespace HybridCLR.Editor.Link
public HashSet<TypeRef> CollectRefs(List<string> rootAssemblies) public HashSet<TypeRef> CollectRefs(List<string> rootAssemblies)
{ {
using (var assCollector = new AssemblyCache(_resolver)) var assCollector = new AssemblyCache(_resolver);
{
var rootAssemblyNames = new HashSet<string>(rootAssemblies); var rootAssemblyNames = new HashSet<string>(rootAssemblies);
var typeRefs = new HashSet<TypeRef>(TypeEqualityComparer.Instance); var typeRefs = new HashSet<TypeRef>(TypeEqualityComparer.Instance);
@ -32,16 +32,18 @@ namespace HybridCLR.Editor.Link
var dnAss = assCollector.LoadModule(rootAss, false); var dnAss = assCollector.LoadModule(rootAss, false);
foreach (var type in dnAss.GetTypeRefs()) foreach (var type in dnAss.GetTypeRefs())
{ {
if (type.DefinitionAssembly == null)
{
Debug.LogWarning($"assembly:{dnAss.Name} TypeRef {type.FullName} has no DefinitionAssembly");
continue;
}
if (!rootAssemblyNames.Contains(type.DefinitionAssembly.Name.ToString())) if (!rootAssemblyNames.Contains(type.DefinitionAssembly.Name.ToString()))
{ {
typeRefs.Add(type); typeRefs.Add(type);
} }
} }
} }
assCollector.Dispose();
return typeRefs; return typeRefs;
} }
} }
}
} }

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace HybridCLR.Editor.Link namespace HybridCLR.Editor.Link
{ {
internal class LinkXmlWriter public class LinkXmlWriter
{ {
public void Write(string outputLinkXmlFile, HashSet<TypeRef> refTypes) public void Write(string outputLinkXmlFile, HashSet<TypeRef> refTypes)
{ {
@ -32,6 +32,12 @@ namespace HybridCLR.Editor.Link
assTypeNames.Sort(string.CompareOrdinal); assTypeNames.Sort(string.CompareOrdinal);
foreach(var typeName in assTypeNames) foreach(var typeName in assTypeNames)
{ {
#if UNITY_2023_1_OR_NEWER
if (typeName == "UnityEngine.Debug")
{
continue;
}
#endif
writer.WriteStartElement("type"); writer.WriteStartElement("type");
writer.WriteAttributeString("fullname", typeName); writer.WriteAttributeString("fullname", typeName);
writer.WriteAttributeString("preserve", "all"); writer.WriteAttributeString("preserve", "all");

View File

@ -8,12 +8,11 @@ using System.Threading.Tasks;
namespace HybridCLR.Editor.Meta namespace HybridCLR.Editor.Meta
{ {
public abstract class AssemblyCacheBase : IDisposable public abstract class AssemblyCacheBase
{ {
private readonly IAssemblyResolver _assemblyPathResolver; private readonly IAssemblyResolver _assemblyPathResolver;
private readonly ModuleContext _modCtx; private readonly ModuleContext _modCtx;
private readonly AssemblyResolver _asmResolver; private readonly AssemblyResolver _asmResolver;
private bool disposedValue;
private bool _loadedNetstandard; private bool _loadedNetstandard;
@ -96,28 +95,5 @@ namespace HybridCLR.Editor.Meta
_loadedModulesIncludeNetstandard.Add(mod); _loadedModulesIncludeNetstandard.Add(mod);
return mod; return mod;
} }
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
foreach (var mod in _loadedModulesIncludeNetstandard)
{
mod.Dispose();
}
_loadedModulesIncludeNetstandard.Clear();
LoadedModules.Clear();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
} }
} }

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.Meta
{
public class AssemblySorter
{
class Node
{
public string Name;
public List<Node> Dependencies = new List<Node>();
public Node(string name)
{
Name = name;
}
}
class TopologicalSorter
{
public static List<Node> Sort(List<Node> nodes)
{
List<Node> sorted = new List<Node>();
HashSet<Node> visited = new HashSet<Node>();
HashSet<Node> tempMarks = new HashSet<Node>();
foreach (var node in nodes)
{
if (!visited.Contains(node))
{
Visit(node, visited, tempMarks, sorted);
}
}
return sorted;
}
private static void Visit(Node node, HashSet<Node> visited, HashSet<Node> tempMarks, List<Node> sorted)
{
if (tempMarks.Contains(node))
{
throw new Exception("Detected cyclic dependency!");
}
if (!visited.Contains(node))
{
tempMarks.Add(node);
foreach (var dependency in node.Dependencies)
{
Visit(dependency, visited, tempMarks, sorted);
}
tempMarks.Remove(node);
visited.Add(node);
sorted.Add(node);
}
}
}
private static List<string> SortAssemblyByReferenceOrder(IEnumerable<string> assemblies, Dictionary<string, HashSet<string>> refs)
{
var nodes = new List<Node>();
var nodeMap = new Dictionary<string, Node>();
foreach (var assembly in assemblies)
{
var node = new Node(assembly);
nodes.Add(node);
nodeMap.Add(assembly, node);
}
foreach (var assembly in assemblies)
{
var node = nodeMap[assembly];
foreach (var refAssembly in refs[assembly])
{
node.Dependencies.Add(nodeMap[refAssembly]);
}
}
var sortedNodes = TopologicalSorter.Sort(nodes);
return sortedNodes.Select(node => node.Name).ToList();
}
public static List<string> SortAssemblyByReferenceOrder(IEnumerable<string> assemblies, IAssemblyResolver assemblyResolver)
{
var assCache = new AssemblyCache(assemblyResolver);
var assRefAssemblies = new Dictionary<string, HashSet<string>>();
foreach (var assName in assemblies)
{
var refAssemblies = new HashSet<string>();
var mod = assCache.LoadModule(assName, false);
foreach (var refAss in mod.GetAssemblyRefs())
{
if (assemblies.Contains(refAss.Name.ToString()))
{
refAssemblies.Add(refAss.Name.ToString());
}
}
assRefAssemblies.Add(assName, refAssemblies);
}
return SortAssemblyByReferenceOrder(assemblies, assRefAssemblies);
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 7db18e1736f593c4089c85d764cf8620 guid: b9b8eb45398fa344daa8c6e9b9fbf291
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -29,6 +29,12 @@ namespace HybridCLR.Editor.Meta
Debug.Log($"[FixedSetAssemblyResolver] resolve:{assemblyName} path:{assemblyPath}"); Debug.Log($"[FixedSetAssemblyResolver] resolve:{assemblyName} path:{assemblyPath}");
return true; return true;
} }
assemblyPath = $"{_rootDir}/{assemblyName}.dll.bytes";
if (File.Exists(assemblyPath))
{
Debug.Log($"[FixedSetAssemblyResolver] resolve:{assemblyName} path:{assemblyPath}");
return true;
}
} }
assemblyPath = null; assemblyPath = null;
return false; return false;

View File

@ -7,13 +7,11 @@ using System.Threading.Tasks;
namespace HybridCLR.Editor.Meta namespace HybridCLR.Editor.Meta
{ {
/// <summary>
/// Replaces generic type/method var with its generic argument public class GenericArgumentContext
/// </summary>
public sealed class GenericArgumentContext
{ {
List<TypeSig> typeArgsStack = new List<TypeSig>(); private readonly List<TypeSig> typeArgsStack;
List<TypeSig> methodArgsStack = new List<TypeSig>(); private readonly List<TypeSig> methodArgsStack;
public GenericArgumentContext(List<TypeSig> typeArgsStack, List<TypeSig> methodArgsStack) public GenericArgumentContext(List<TypeSig> typeArgsStack, List<TypeSig> methodArgsStack)
{ {
@ -21,16 +19,6 @@ namespace HybridCLR.Editor.Meta
this.methodArgsStack = methodArgsStack; this.methodArgsStack = methodArgsStack;
} }
/// <summary>
/// Replaces a generic type/method var with its generic argument (if any). If
/// <paramref name="typeSig"/> isn't a generic type/method var or if it can't
/// be resolved, it itself is returned. Else the resolved type is returned.
/// </summary>
/// <param name="typeSig">Type signature</param>
/// <returns>New <see cref="TypeSig"/> which is never <c>null</c> unless
/// <paramref name="typeSig"/> is <c>null</c></returns>
public TypeSig Resolve(TypeSig typeSig) public TypeSig Resolve(TypeSig typeSig)
{ {
if (!typeSig.ContainsGenericParameter) if (!typeSig.ContainsGenericParameter)
@ -41,9 +29,9 @@ namespace HybridCLR.Editor.Meta
switch (typeSig.ElementType) switch (typeSig.ElementType)
{ {
case ElementType.Ptr: return new PtrSig(Resolve(typeSig.Next)); case ElementType.Ptr: return new PtrSig(Resolve(typeSig.Next));
case ElementType.ByRef: return new PtrSig(Resolve(typeSig.Next)); case ElementType.ByRef: return new ByRefSig(Resolve(typeSig.Next));
case ElementType.SZArray: return new PtrSig(Resolve(typeSig.Next)); case ElementType.SZArray: return new SZArraySig(Resolve(typeSig.Next));
case ElementType.Array: case ElementType.Array:
{ {
var ara = (ArraySig)typeSig; var ara = (ArraySig)typeSig;
@ -53,7 +41,7 @@ namespace HybridCLR.Editor.Meta
case ElementType.Var: case ElementType.Var:
{ {
GenericVar genericVar = (GenericVar)typeSig; GenericVar genericVar = (GenericVar)typeSig;
var newSig = Resolve(typeArgsStack, genericVar.Number, true); var newSig = Resolve(typeArgsStack, genericVar.Number);
if (newSig == null) if (newSig == null)
{ {
throw new Exception(); throw new Exception();
@ -64,7 +52,7 @@ namespace HybridCLR.Editor.Meta
case ElementType.MVar: case ElementType.MVar:
{ {
GenericMVar genericVar = (GenericMVar)typeSig; GenericMVar genericVar = (GenericMVar)typeSig;
var newSig = Resolve(methodArgsStack, genericVar.Number, true); var newSig = Resolve(methodArgsStack, genericVar.Number);
if (newSig == null) if (newSig == null)
{ {
throw new Exception(); throw new Exception();
@ -113,13 +101,9 @@ namespace HybridCLR.Editor.Meta
} }
} }
private TypeSig Resolve(List<TypeSig> args, uint number, bool isTypeVar) private TypeSig Resolve(List<TypeSig> args, uint number)
{ {
var typeSig = args[(int)number]; return args[(int)number];
var gvar = typeSig as GenericSig;
if (gvar is null || gvar.IsTypeVar != isTypeVar)
return typeSig;
return gvar;
} }
} }

View File

@ -94,7 +94,7 @@ namespace HybridCLR.Editor.Meta
methodDef = method.ResolveMethodDef(); methodDef = method.ResolveMethodDef();
if (methodDef == null) if (methodDef == null)
{ {
Debug.LogWarning($"method:{method} ResolveMethodDef() == null"); //Debug.LogWarning($"method:{method} ResolveMethodDef() == null");
return null; return null;
} }
if (method is MethodSpec methodSpec) if (method is MethodSpec methodSpec)

View File

@ -122,7 +122,7 @@ namespace HybridCLR.Editor.Meta
} }
return new GenericInstSig(gia.GenericType, gia.GenericArguments.Select(ga => ToShareTypeSig(corTypes, ga)).ToList()); return new GenericInstSig(gia.GenericType, gia.GenericArguments.Select(ga => ToShareTypeSig(corTypes, ga)).ToList());
} }
case ElementType.FnPtr: return corTypes.IntPtr; case ElementType.FnPtr: return corTypes.UIntPtr;
case ElementType.ValueArray: return typeSig; case ElementType.ValueArray: return typeSig;
case ElementType.Module: return typeSig; case ElementType.Module: return typeSig;
default: default:
@ -189,5 +189,29 @@ namespace HybridCLR.Editor.Meta
} }
return methodGenericParams; return methodGenericParams;
} }
public static bool IsSupportedPInvokeTypeSig(TypeSig typeSig)
{
typeSig = typeSig.RemovePinnedAndModifiers();
if (typeSig.IsByRef)
{
return true;
}
switch (typeSig.ElementType)
{
case ElementType.SZArray:
case ElementType.Array:
//case ElementType.Class:
case ElementType.String:
//case ElementType.Object:
return false;
default: return true;
}
}
public static bool IsSupportedPInvokeMethodSignature(MethodSig methodSig)
{
return IsSupportedPInvokeTypeSig(methodSig.RetType) && methodSig.Params.All(p => IsSupportedPInvokeTypeSig(p));
}
} }
} }

View File

@ -20,11 +20,16 @@ namespace HybridCLR.Editor.Meta
{ {
foreach(var path in _searchPaths) foreach(var path in _searchPaths)
{ {
string assPath = Path.Combine(path, assemblyName + ".dll"); assemblyPath = Path.Combine(path, $"{assemblyName}.dll");
if (File.Exists(assPath)) if (File.Exists(assemblyPath))
{ {
Debug.Log($"resolve {assemblyName} at {assPath}"); Debug.Log($"resolve {assemblyName} at {assemblyPath}");
assemblyPath = assPath; return true;
}
assemblyPath = Path.Combine(path, $"{assemblyName}.dll.bytes");
if (File.Exists(assemblyPath))
{
Debug.Log($"resolve {assemblyName} at {assemblyPath}");
return true; return true;
} }
} }

View File

@ -1,4 +1,5 @@
using dnlib.DotNet; using dnlib.DotNet;
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Meta; using HybridCLR.Editor.Meta;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -0,0 +1,11 @@
using dnlib.DotNet;
namespace HybridCLR.Editor.MethodBridge
{
public class CallNativeMethodSignatureInfo
{
public MethodSig MethodSig { get; set; }
public CallingConvention? Callvention { get; set; }
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 851c44a8da67a9742a7ea68815383f27 guid: d2e4ca0a49975a84a8a72dbc70ec7795
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -0,0 +1,65 @@
using dnlib.DotNet;
using HybridCLR.Editor.Meta;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.MethodBridge
{
public class CalliAnalyzer
{
private readonly List<ModuleDefMD> _rootModules = new List<ModuleDefMD>();
private readonly List<CallNativeMethodSignatureInfo> _calliMethodSignatures = new List<CallNativeMethodSignatureInfo>();
public List<CallNativeMethodSignatureInfo> CalliMethodSignatures => _calliMethodSignatures;
public CalliAnalyzer(AssemblyCache cache, List<string> assemblyNames)
{
foreach (var assemblyName in assemblyNames)
{
_rootModules.Add(cache.LoadModule(assemblyName));
}
}
private void CollectCalli()
{
foreach (var mod in _rootModules)
{
Debug.Log($"ass:{mod.FullName} methodcount:{mod.Metadata.TablesStream.MethodTable.Rows}");
for (uint rid = 1, n = mod.Metadata.TablesStream.MethodTable.Rows; rid <= n; rid++)
{
var method = mod.ResolveMethod(rid);
//Debug.Log($"method:{method}");
if (!method.HasBody)
{
continue;
}
foreach (var il in method.Body.Instructions)
{
if (il.OpCode.Code == dnlib.DotNet.Emit.Code.Calli)
{
MethodSig methodSig = (MethodSig)il.Operand;
_calliMethodSignatures.Add(new CallNativeMethodSignatureInfo()
{
MethodSig = methodSig,
});
Debug.Log($"method:{method} calli method signature:{methodSig}");
}
}
}
}
}
public void Run()
{
CollectCalli();
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 093a9e6c1e7399244bbcd8983fdbfdee guid: 6ce33c8e48da5a649b261ba3a60fd3b9
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -13,6 +13,9 @@ using System.Threading.Tasks;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using TypeInfo = HybridCLR.Editor.ABI.TypeInfo; using TypeInfo = HybridCLR.Editor.ABI.TypeInfo;
using CallingConvention = System.Runtime.InteropServices.CallingConvention;
using TypeAttributes = dnlib.DotNet.TypeAttributes;
using System.Runtime.InteropServices;
namespace HybridCLR.Editor.MethodBridge namespace HybridCLR.Editor.MethodBridge
{ {
@ -25,14 +28,46 @@ namespace HybridCLR.Editor.MethodBridge
public string OutputFile { get; set; } public string OutputFile { get; set; }
public IReadOnlyCollection<GenericMethod> GenericMethods { get; set; } public IReadOnlyCollection<GenericMethod> GenericMethods { get; set; }
public List<RawMonoPInvokeCallbackMethodInfo> ReversePInvokeMethods { get; set; }
public IReadOnlyCollection<CallNativeMethodSignatureInfo> CalliMethodSignatures { get; set; }
public bool Development { get; set; }
}
private class ABIReversePInvokeMethodInfo
{
public MethodDesc Method { get; set; }
public CallingConvention Callvention { get; set; }
public int Count { get; set; }
public string Signature { get; set; }
}
private class CalliMethodInfo
{
public MethodDesc Method { get; set; }
public CallingConvention Callvention { get; set; }
public string Signature { get; set; }
} }
private readonly List<GenericMethod> _genericMethods; private readonly List<GenericMethod> _genericMethods;
private readonly List<RawMonoPInvokeCallbackMethodInfo> _originalReversePInvokeMethods;
private readonly List<CallNativeMethodSignatureInfo> _originalCalliMethodSignatures;
private readonly string _templateCode; private readonly string _templateCode;
private readonly string _outputFile; private readonly string _outputFile;
private readonly bool _development;
private readonly TypeCreator _typeCreator; private readonly TypeCreator _typeCreator;
private readonly HashSet<MethodDesc> _managed2nativeMethodSet = new HashSet<MethodDesc>(); private readonly HashSet<MethodDesc> _managed2nativeMethodSet = new HashSet<MethodDesc>();
@ -41,15 +76,22 @@ namespace HybridCLR.Editor.MethodBridge
private readonly HashSet<MethodDesc> _adjustThunkMethodSet = new HashSet<MethodDesc>(); private readonly HashSet<MethodDesc> _adjustThunkMethodSet = new HashSet<MethodDesc>();
private List<ABIReversePInvokeMethodInfo> _reversePInvokeMethods;
private List<CalliMethodInfo> _callidMethods;
public Generator(Options options) public Generator(Options options)
{ {
List<(GenericMethod, string)> genericMethodInfo = options.GenericMethods.Select(m => (m, m.ToString())).ToList(); List<(GenericMethod, string)> genericMethodInfo = options.GenericMethods.Select(m => (m, m.ToString())).ToList();
genericMethodInfo.Sort((a, b) => string.CompareOrdinal(a.Item2, b.Item2)); genericMethodInfo.Sort((a, b) => string.CompareOrdinal(a.Item2, b.Item2));
_genericMethods = genericMethodInfo.Select(m => m.Item1).ToList(); _genericMethods = genericMethodInfo.Select(m => m.Item1).ToList();
_originalReversePInvokeMethods = options.ReversePInvokeMethods;
_originalCalliMethodSignatures = options.CalliMethodSignatures.ToList();
_templateCode = options.TemplateCode; _templateCode = options.TemplateCode;
_outputFile = options.OutputFile; _outputFile = options.OutputFile;
_typeCreator = new TypeCreator(); _typeCreator = new TypeCreator();
_development = options.Development;
} }
private readonly Dictionary<string, TypeInfo> _sig2Types = new Dictionary<string, TypeInfo>(); private readonly Dictionary<string, TypeInfo> _sig2Types = new Dictionary<string, TypeInfo>();
@ -98,6 +140,30 @@ namespace HybridCLR.Editor.MethodBridge
return mbs; return mbs;
} }
private MethodDesc CreateMethodDesc(TypeSig returnType, List<TypeSig> parameters)
{
var paramInfos = new List<ParamInfo>();
if (returnType.ContainsGenericParameter)
{
throw new Exception($"[PreservedMethod] method has generic parameters");
}
foreach (var paramInfo in parameters)
{
if (paramInfo.ContainsGenericParameter)
{
throw new Exception($"[PreservedMethod] method has generic parameters");
}
paramInfos.Add(new ParamInfo() { Type = GetSharedTypeInfo(paramInfo) });
}
var mbs = new MethodDesc()
{
MethodDef = null,
ReturnInfo = new ReturnInfo() { Type = returnType != null ? GetSharedTypeInfo(returnType) : TypeInfo.s_void },
ParamInfos = paramInfos,
};
return mbs;
}
private void AddManaged2NativeMethod(MethodDesc method) private void AddManaged2NativeMethod(MethodDesc method)
{ {
method.Init(); method.Init();
@ -167,12 +233,23 @@ namespace HybridCLR.Editor.MethodBridge
} }
} }
public void PrepareMethods() private void PrepareMethodBridges()
{ {
foreach(var method in _genericMethods) foreach (var method in _genericMethods)
{ {
ProcessMethod(method.Method, method.KlassInst, method.MethodInst); ProcessMethod(method.Method, method.KlassInst, method.MethodInst);
} }
foreach (var reversePInvokeMethod in _originalReversePInvokeMethods)
{
MethodDef method = reversePInvokeMethod.Method;
ICorLibTypes corLibTypes = method.Module.CorLibTypes;
var returnType = MetaUtil.ToShareTypeSig(corLibTypes, method.ReturnType);
var parameters = method.Parameters.Select(p => MetaUtil.ToShareTypeSig(corLibTypes, p.Type)).ToList();
var sharedMethod = CreateMethodDesc(method, true, returnType, parameters);
sharedMethod.Init();
AddNative2ManagedMethod(sharedMethod);
}
} }
static void CheckUnique(IEnumerable<string> names) static void CheckUnique(IEnumerable<string> names)
@ -210,6 +287,7 @@ namespace HybridCLR.Editor.MethodBridge
CollectStructDefs(_managed2NativeMethodList0, structTypeSet); CollectStructDefs(_managed2NativeMethodList0, structTypeSet);
CollectStructDefs(_native2ManagedMethodList0, structTypeSet); CollectStructDefs(_native2ManagedMethodList0, structTypeSet);
CollectStructDefs(_adjustThunkMethodList0, structTypeSet); CollectStructDefs(_adjustThunkMethodList0, structTypeSet);
CollectStructDefs(_originalCalliMethodSignatures.Select(m => m.MethodSig).ToList(), structTypeSet);
_structTypes0 = structTypeSet.ToList(); _structTypes0 = structTypeSet.ToList();
_structTypes0.Sort((a, b) => a.TypeId - b.TypeId); _structTypes0.Sort((a, b) => a.TypeId - b.TypeId);
@ -220,39 +298,114 @@ namespace HybridCLR.Editor.MethodBridge
_managed2NativeMethodList0.Count, _native2ManagedMethodList0.Count, _adjustThunkMethodList0.Count, _structTypes0.Count); _managed2NativeMethodList0.Count, _native2ManagedMethodList0.Count, _adjustThunkMethodList0.Count, _structTypes0.Count);
} }
private class AnalyzeFieldInfo
{
public FieldDef field;
public TypeInfo type;
}
private class AnalyzeTypeInfo private class AnalyzeTypeInfo
{ {
public TypeInfo toSharedType; public TypeInfo isoType;
public List<TypeInfo> fields; public List<AnalyzeFieldInfo> fields;
public string signature; public string signature;
public uint originalPackingSize;
public uint packingSize;
public uint classSize;
public LayoutKind layout;
public bool blittable;
} }
private readonly Dictionary<TypeInfo, AnalyzeTypeInfo> _analyzeTypeInfos = new Dictionary<TypeInfo, AnalyzeTypeInfo>(); private readonly Dictionary<TypeInfo, AnalyzeTypeInfo> _analyzeTypeInfos = new Dictionary<TypeInfo, AnalyzeTypeInfo>();
private readonly Dictionary<string, TypeInfo> _signature2Type = new Dictionary<string, TypeInfo>(); private readonly Dictionary<string, TypeInfo> _signature2Type = new Dictionary<string, TypeInfo>();
private bool IsBlittable(TypeSig typeSig)
{
typeSig = typeSig.RemovePinnedAndModifiers();
if (typeSig.IsByRef)
{
return true;
}
switch (typeSig.ElementType)
{
case ElementType.Void: return false;
case ElementType.Boolean:
case ElementType.I1:
case ElementType.U1:
case ElementType.I2:
case ElementType.Char:
case ElementType.U2:
case ElementType.I4:
case ElementType.U4:
case ElementType.I8:
case ElementType.U8:
case ElementType.R4:
case ElementType.R8:
case ElementType.I:
case ElementType.U:
case ElementType.Ptr:
case ElementType.ByRef:
case ElementType.FnPtr:
case ElementType.TypedByRef: return true;
case ElementType.String:
case ElementType.Class:
case ElementType.Array:
case ElementType.SZArray:
case ElementType.Object:
case ElementType.Module:
case ElementType.Var:
case ElementType.MVar: return false;
case ElementType.ValueType:
{
TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDef();
if (typeDef == null)
{
throw new Exception($"type:{typeSig} definition could not be found. Please try `HybridCLR/Genergate/LinkXml`, then Build once to generate the AOT dll, and then regenerate the bridge function");
}
if (typeDef.IsEnum)
{
return true;
}
return CalculateAnalyzeTypeInfoBasic(GetSharedTypeInfo(typeSig)).blittable;
}
case ElementType.GenericInst:
{
GenericInstSig gis = (GenericInstSig)typeSig;
if (!gis.GenericType.IsValueType)
{
return false;
}
TypeDef typeDef = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef();
if (typeDef.IsEnum)
{
return true;
}
return CalculateAnalyzeTypeInfoBasic(GetSharedTypeInfo(typeSig)).blittable;
}
default: throw new NotSupportedException($"{typeSig.ElementType}");
}
}
private AnalyzeTypeInfo CalculateAnalyzeTypeInfoBasic(TypeInfo typeInfo) private AnalyzeTypeInfo CalculateAnalyzeTypeInfoBasic(TypeInfo typeInfo)
{ {
Debug.Assert(typeInfo.IsStruct);
if (_analyzeTypeInfos.TryGetValue(typeInfo, out var ati))
{
return ati;
}
TypeSig type = typeInfo.Klass; TypeSig type = typeInfo.Klass;
TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow(); TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow();
List<TypeSig> klassInst = type.ToGenericInstSig()?.GenericArguments?.ToList(); List<TypeSig> klassInst = type.ToGenericInstSig()?.GenericArguments?.ToList();
GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null; GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
ClassLayout sa = typeDef.ClassLayout; var fields = new List<AnalyzeFieldInfo>();
var analyzeTypeInfo = new AnalyzeTypeInfo();
// don't share type with explicit layout
if (sa != null)
{
analyzeTypeInfo.toSharedType = typeInfo;
analyzeTypeInfo.signature = typeInfo.CreateSigName();
_signature2Type.Add(analyzeTypeInfo.signature, typeInfo);
return analyzeTypeInfo;
}
var fields = analyzeTypeInfo.fields = new List<TypeInfo>();
bool blittable = true;
foreach (FieldDef field in typeDef.Fields) foreach (FieldDef field in typeDef.Fields)
{ {
if (field.IsStatic) if (field.IsStatic)
@ -260,8 +413,39 @@ namespace HybridCLR.Editor.MethodBridge
continue; continue;
} }
TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType; TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
fields.Add(GetSharedTypeInfo(fieldType)); blittable &= IsBlittable(fieldType);
TypeInfo sharedFieldTypeInfo = GetSharedTypeInfo(fieldType);
TypeInfo isoType = ToIsomorphicType(sharedFieldTypeInfo);
fields.Add(new AnalyzeFieldInfo { field = field, type = isoType });
} }
//analyzeTypeInfo.blittable = blittable;
//analyzeTypeInfo.packingSize = blittable ? analyzeTypeInfo.originalPackingSize : 0;
ClassLayout sa = typeDef.ClassLayout;
uint originalPackingSize = sa?.PackingSize ?? 0;
var analyzeTypeInfo = new AnalyzeTypeInfo()
{
originalPackingSize = originalPackingSize,
packingSize = blittable && !typeDef.IsAutoLayout ? originalPackingSize : 0,
classSize = sa?.ClassSize ?? 0,
layout = typeDef.IsAutoLayout ? LayoutKind.Auto : (typeDef.IsExplicitLayout ? LayoutKind.Explicit : LayoutKind.Sequential),
fields = fields,
blittable = blittable,
};
_analyzeTypeInfos.Add(typeInfo, analyzeTypeInfo);
analyzeTypeInfo.signature = GetOrCalculateTypeInfoSignature(typeInfo);
if (_signature2Type.TryGetValue(analyzeTypeInfo.signature, out var sharedType))
{
// Debug.Log($"[ToIsomorphicType] type:{type.Klass} ==> sharedType:{sharedType.Klass} signature:{signature} ");
analyzeTypeInfo.isoType = sharedType;
}
else
{
analyzeTypeInfo.isoType = typeInfo;
_signature2Type.Add(analyzeTypeInfo.signature, typeInfo);
}
return analyzeTypeInfo; return analyzeTypeInfo;
} }
@ -286,9 +470,14 @@ namespace HybridCLR.Editor.MethodBridge
} }
var sigBuf = new StringBuilder(); var sigBuf = new StringBuilder();
if (ati.packingSize != 0 || ati.classSize != 0 || ati.layout != LayoutKind.Sequential || !ati.blittable)
{
sigBuf.Append($"[{ati.classSize}|{ati.packingSize}|{ati.layout}|{(ati.blittable ? 0 : 1)}]");
}
foreach (var field in ati.fields) foreach (var field in ati.fields)
{ {
sigBuf.Append(GetOrCalculateTypeInfoSignature(ToIsomorphicType(field))); string fieldOffset = field.field.FieldOffset != null ? field.field.FieldOffset.ToString() + "|" : "";
sigBuf.Append("{" + fieldOffset + GetOrCalculateTypeInfoSignature(ToIsomorphicType(field.type)) + "}");
} }
return ati.signature = sigBuf.ToString(); return ati.signature = sigBuf.ToString();
} }
@ -299,27 +488,7 @@ namespace HybridCLR.Editor.MethodBridge
{ {
return type; return type;
} }
if (!_analyzeTypeInfos.TryGetValue(type, out var ati)) return CalculateAnalyzeTypeInfoBasic(type).isoType;
{
ati = CalculateAnalyzeTypeInfoBasic(type);
_analyzeTypeInfos.Add(type, ati);
}
if (ati.toSharedType == null)
{
string signature = GetOrCalculateTypeInfoSignature(type);
Debug.Assert(signature == ati.signature);
if (_signature2Type.TryGetValue(signature, out var sharedType))
{
// Debug.Log($"[ToIsomorphicType] type:{type.Klass} ==> sharedType:{sharedType.Klass} signature:{signature} ");
ati.toSharedType = sharedType;
}
else
{
ati.toSharedType = type;
_signature2Type.Add(signature, type);
}
}
return ati.toSharedType;
} }
private MethodDesc ToIsomorphicMethod(MethodDesc method) private MethodDesc ToIsomorphicMethod(MethodDesc method)
@ -370,11 +539,131 @@ namespace HybridCLR.Editor.MethodBridge
return methodMap.Values.ToList(); return methodMap.Values.ToList();
} }
private static string MakeReversePInvokeSignature(MethodDesc desc, CallingConvention CallingConventionention)
{
string convStr = ((char)('A' + (int)CallingConventionention - 1)).ToString();
return $"{convStr}{desc.Sig}";
}
private static string MakeCalliSignature(MethodDesc desc, CallingConvention CallingConventionention)
{
string convStr = ((char)('A' + Math.Max((int)CallingConventionention - 1, 0))).ToString();
return $"{convStr}{desc.Sig}";
}
private static CallingConvention GetCallingConvention(MethodDef method)
{
var monoPInvokeCallbackAttr = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Name == "MonoPInvokeCallbackAttribute");
if (monoPInvokeCallbackAttr == null)
{
return CallingConvention.Winapi;
}
object delegateTypeSig = monoPInvokeCallbackAttr.ConstructorArguments[0].Value;
TypeDef delegateTypeDef;
if (delegateTypeSig is ClassSig classSig)
{
delegateTypeDef = classSig.TypeDefOrRef.ResolveTypeDefThrow();
}
else if (delegateTypeSig is GenericInstSig genericInstSig)
{
delegateTypeDef = genericInstSig.GenericType.TypeDefOrRef.ResolveTypeDefThrow();
}
else
{
delegateTypeDef = null;
}
if (delegateTypeDef == null)
{
throw new NotSupportedException($"Unsupported delegate type: {delegateTypeSig}");
}
var attr = delegateTypeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute");
if (attr == null)
{
return CallingConvention.Winapi;
}
var conv = attr.ConstructorArguments[0].Value;
return (CallingConvention)conv;
}
private List<ABIReversePInvokeMethodInfo> BuildABIMethods(List<RawMonoPInvokeCallbackMethodInfo> rawMethods)
{
var methodsBySig = new Dictionary<string, ABIReversePInvokeMethodInfo>();
foreach (var method in rawMethods)
{
var sharedMethod = new MethodDesc
{
MethodDef = method.Method,
ReturnInfo = new ReturnInfo { Type = GetSharedTypeInfo(method.Method.ReturnType) },
ParamInfos = method.Method.Parameters.Select(p => new ParamInfo { Type = GetSharedTypeInfo(p.Type) }).ToList(),
};
sharedMethod.Init();
sharedMethod = ToIsomorphicMethod(sharedMethod);
CallingConvention callingConv = GetCallingConvention(method.Method);
string signature = MakeReversePInvokeSignature(sharedMethod, callingConv);
if (!methodsBySig.TryGetValue(signature, out var arm))
{
arm = new ABIReversePInvokeMethodInfo()
{
Method = sharedMethod,
Signature = signature,
Count = 0,
Callvention = callingConv,
};
methodsBySig.Add(signature, arm);
}
int preserveCount = method.GenerationAttribute != null ? (int)method.GenerationAttribute.ConstructorArguments[0].Value : 1;
arm.Count += preserveCount;
}
var newMethods = methodsBySig.Values.ToList();
newMethods.Sort((a, b) => string.CompareOrdinal(a.Signature, b.Signature));
return newMethods;
}
private List<CalliMethodInfo> BuildCalliMethods(List<CallNativeMethodSignatureInfo> rawMethods)
{
var methodsBySig = new Dictionary<string, CalliMethodInfo>();
foreach (var method in rawMethods)
{
var sharedMethod = new MethodDesc
{
MethodDef = null,
ReturnInfo = new ReturnInfo { Type = GetSharedTypeInfo(method.MethodSig.RetType) },
ParamInfos = method.MethodSig.Params.Select(p => new ParamInfo { Type = GetSharedTypeInfo(p) }).ToList(),
};
sharedMethod.Init();
sharedMethod = ToIsomorphicMethod(sharedMethod);
CallingConvention callingConv = (CallingConvention)((int)((method.Callvention ?? method.MethodSig.CallingConvention) & dnlib.DotNet.CallingConvention.Mask) + 1);
string signature = MakeCalliSignature(sharedMethod, callingConv);
if (!methodsBySig.TryGetValue(signature, out var arm))
{
arm = new CalliMethodInfo()
{
Method = sharedMethod,
Signature = signature,
Callvention = callingConv,
};
methodsBySig.Add(signature, arm);
}
}
var newMethods = methodsBySig.Values.ToList();
newMethods.Sort((a, b) => string.CompareOrdinal(a.Signature, b.Signature));
return newMethods;
}
private void BuildOptimizedMethods() private void BuildOptimizedMethods()
{ {
_managed2NativeMethodList = ToUniqueOrderedList(_managed2NativeMethodList0); _managed2NativeMethodList = ToUniqueOrderedList(_managed2NativeMethodList0);
_native2ManagedMethodList = ToUniqueOrderedList(_native2ManagedMethodList0); _native2ManagedMethodList = ToUniqueOrderedList(_native2ManagedMethodList0);
_adjustThunkMethodList = ToUniqueOrderedList(_adjustThunkMethodList0); _adjustThunkMethodList = ToUniqueOrderedList(_adjustThunkMethodList0);
_reversePInvokeMethods = BuildABIMethods(_originalReversePInvokeMethods);
_callidMethods = BuildCalliMethods(_originalCalliMethodSignatures);
} }
private void OptimizationTypesAndMethods() private void OptimizationTypesAndMethods()
@ -389,10 +678,15 @@ namespace HybridCLR.Editor.MethodBridge
{ {
var frr = new FileRegionReplace(_templateCode); var frr = new FileRegionReplace(_templateCode);
List<string> lines = new List<string>(20_0000); List<string> lines = new List<string>(20_0000)
{
"\n",
$"// DEVELOPMENT={(_development ? 1 : 0)}",
"\n"
};
var classInfos = new List<ClassInfo>(); var classInfos = new List<ClassInfo>();
var classTypeSet = new HashSet<TypeInfo>(); var classTypeSet = new Dictionary<TypeInfo, ClassInfo>();
foreach (var type in structTypes) foreach (var type in structTypes)
{ {
GenerateClassInfo(type, classTypeSet, classInfos); GenerateClassInfo(type, classTypeSet, classInfos);
@ -424,6 +718,15 @@ namespace HybridCLR.Editor.MethodBridge
GenerateAdjustThunkStub(_adjustThunkMethodList, lines); GenerateAdjustThunkStub(_adjustThunkMethodList, lines);
GenerateReversePInvokeWrappers(_reversePInvokeMethods, lines);
foreach (var method in _callidMethods)
{
GenerateManaged2NativeFunctionPointerMethod(method, lines);
}
GenerateManaged2NativeFunctionPointerMethodStub(_callidMethods, lines);
frr.Replace("CODE", string.Join("\n", lines)); frr.Replace("CODE", string.Join("\n", lines));
Directory.CreateDirectory(Path.GetDirectoryName(_outputFile)); Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));
@ -431,8 +734,69 @@ namespace HybridCLR.Editor.MethodBridge
frr.Commit(_outputFile); frr.Commit(_outputFile);
} }
private static string GetIl2cppCallConventionName(CallingConvention conv)
{
switch (conv)
{
case 0:
case CallingConvention.Winapi:
return "DEFAULT_CALL";
case CallingConvention.Cdecl:
return "CDECL";
case CallingConvention.StdCall:
return "STDCALL";
case CallingConvention.ThisCall:
return "THISCALL";
case CallingConvention.FastCall:
return "FASTCALL";
default:
throw new NotSupportedException($"Unsupported CallingConvention {conv}");
}
}
private void GenerateReversePInvokeWrappers(List<ABIReversePInvokeMethodInfo> methods, List<string> lines)
{
int methodIndex = 0;
var stubCodes = new List<string>();
foreach (var methodInfo in methods)
{
MethodDesc method = methodInfo.Method;
string il2cppCallConventionName = GetIl2cppCallConventionName(methodInfo.Callvention);
string paramDeclaringListWithoutMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}"));
string paramNameListWithoutMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"__arg{p.Index}").Concat(new string[] { "method" }));
string paramTypeListWithMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()}").Concat(new string[] { "const MethodInfo*" }));
string methodTypeDef = $"typedef {method.ReturnInfo.Type.GetTypeName()} (*Callback)({paramTypeListWithMethodInfoStr})";
for (int i = 0; i < methodInfo.Count; i++, methodIndex++)
{
lines.Add($@"
{method.ReturnInfo.Type.GetTypeName()} {il2cppCallConventionName} __ReversePInvokeMethod_{methodIndex}({paramDeclaringListWithoutMethodInfoStr})
{{
il2cpp::vm::ScopedThreadAttacher _vmThreadHelper;
const MethodInfo* method = InterpreterModule::GetMethodInfoByReversePInvokeWrapperIndex({methodIndex});
{methodTypeDef};
{(method.ReturnInfo.IsVoid ? "" : "return ")}((Callback)(method->methodPointerCallByInterp))({paramNameListWithoutMethodInfoStr});
}}
");
stubCodes.Add($"\t{{\"{methodInfo.Signature}\", (Il2CppMethodPointer)__ReversePInvokeMethod_{methodIndex}}},");
}
Debug.Log($"[ReversePInvokeWrap.Generator] method:{method.MethodDef} wrapperCount:{methodInfo.Count}");
}
lines.Add(@"
const ReversePInvokeMethodData hybridclr::interpreter::g_reversePInvokeMethodStub[]
{
");
lines.AddRange(stubCodes);
lines.Add(@"
{nullptr, nullptr},
};
");
}
public void Generate() public void Generate()
{ {
PrepareMethodBridges();
CollectTypesAndMethods(); CollectTypesAndMethods();
OptimizationTypesAndMethods(); OptimizationTypesAndMethods();
GenerateCode(); GenerateCode();
@ -466,6 +830,38 @@ namespace HybridCLR.Editor.MethodBridge
} }
private void CollectStructDefs(List<MethodSig> methods, HashSet<TypeInfo> structTypes)
{
ICorLibTypes corLibTypes = _genericMethods[0].Method.Module.CorLibTypes;
foreach (var method in methods)
{
foreach (var paramInfo in method.Params)
{
var paramType = GetSharedTypeInfo(MetaUtil.ToShareTypeSig(corLibTypes, paramInfo));
if (paramType.IsStruct)
{
structTypes.Add(paramType);
if (paramType.Klass.ContainsGenericParameter)
{
throw new Exception($"[CollectStructDefs] method:{method} type:{paramType.Klass} contains generic parameter");
}
}
}
var returnType = GetSharedTypeInfo(MetaUtil.ToShareTypeSig(corLibTypes, method.RetType));
if (returnType.IsStruct)
{
structTypes.Add(returnType);
if (returnType.Klass.ContainsGenericParameter)
{
throw new Exception($"[CollectStructDefs] method:{method} type:{returnType.Klass} contains generic parameter");
}
}
}
}
class FieldInfo class FieldInfo
{ {
public FieldDef field; public FieldDef field;
@ -475,47 +871,68 @@ namespace HybridCLR.Editor.MethodBridge
class ClassInfo class ClassInfo
{ {
public TypeInfo type; public TypeInfo type;
public List<AnalyzeFieldInfo> fields;
public TypeDef typeDef; public uint packingSize;
public uint classSize;
public List<FieldInfo> fields = new List<FieldInfo>(); public LayoutKind layout;
public bool blittable;
public ClassLayout layout;
} }
private void GenerateClassInfo(TypeInfo type, HashSet<TypeInfo> typeSet, List<ClassInfo> classInfos) private void GenerateClassInfo(TypeInfo type, Dictionary<TypeInfo, ClassInfo> typeSet, List<ClassInfo> classInfos)
{ {
if (!typeSet.Add(type)) if (typeSet.ContainsKey(type))
{ {
return; return;
} }
TypeSig typeSig = type.Klass;
var fields = new List<FieldInfo>();
TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow(); AnalyzeTypeInfo ati = CalculateAnalyzeTypeInfoBasic(type);
//TypeSig typeSig = type.Klass;
//var fields = new List<FieldInfo>();
List<TypeSig> klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList(); //TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow();
GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
ClassLayout sa = typeDef.ClassLayout; //List<TypeSig> klassInst = typeSig.ToGenericInstSig()?.GenericArguments?.ToList();
//GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null;
ICorLibTypes corLibTypes = typeDef.Module.CorLibTypes; //ClassLayout sa = typeDef.ClassLayout;
foreach (FieldDef field in typeDef.Fields)
//ICorLibTypes corLibTypes = typeDef.Module.CorLibTypes;
//bool blittable = true;
//foreach (FieldDef field in typeDef.Fields)
//{
// if (field.IsStatic)
// {
// continue;
// }
// TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType;
// fieldType = MetaUtil.ToShareTypeSig(corLibTypes, fieldType);
// var fieldTypeInfo = ToIsomorphicType(GetSharedTypeInfo(fieldType));
// if (fieldTypeInfo.IsStruct)
// {
// GenerateClassInfo(fieldTypeInfo, typeSet, classInfos);
// }
// blittable &= IsBlittable(fieldType, fieldTypeInfo, typeSet);
// fields.Add(new FieldInfo { field = field, type = fieldTypeInfo });
//}
foreach (var field in ati.fields)
{ {
if (field.IsStatic) if (field.type.IsStruct)
{ {
continue; GenerateClassInfo(field.type, typeSet, classInfos);
} }
TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType; }
fieldType = MetaUtil.ToShareTypeSig(corLibTypes, fieldType); var classInfo = new ClassInfo()
var fieldTypeInfo = ToIsomorphicType(_typeCreator.CreateTypeInfo(fieldType));
if (fieldTypeInfo.IsStruct)
{ {
GenerateClassInfo(fieldTypeInfo, typeSet, classInfos); type = type,
} fields = ati.fields,
fields.Add(new FieldInfo { field = field, type = fieldTypeInfo }); packingSize = ati.packingSize,
} classSize = ati.classSize,
classInfos.Add(new ClassInfo() { type = type, typeDef = typeDef, fields = fields, layout = sa }); layout = ati.layout,
blittable = ati.blittable,
};
typeSet.Add(type, classInfo);
classInfos.Add(classInfo);
} }
private void GenerateStructDefines(List<ClassInfo> classInfos, List<string> lines) private void GenerateStructDefines(List<ClassInfo> classInfos, List<string> lines)
@ -523,16 +940,13 @@ namespace HybridCLR.Editor.MethodBridge
foreach (var ci in classInfos) foreach (var ci in classInfos)
{ {
lines.Add($"// {ci.type.Klass}"); lines.Add($"// {ci.type.Klass}");
uint packingSize = ci.layout?.PackingSize ?? 0; uint packingSize = ci.packingSize;
if (packingSize != 0) uint classSize = ci.classSize;
{
lines.Add($"#pragma pack(push, {packingSize})");
}
uint classSize = ci.layout?.ClassSize ?? 0;
if (ci.typeDef.IsExplicitLayout) if (ci.layout == LayoutKind.Explicit)
{ {
lines.Add($"union {ci.type.GetTypeName()} {{"); lines.Add($"struct {ci.type.GetTypeName()} {{");
lines.Add("\tunion {");
if (classSize > 0) if (classSize > 0)
{ {
lines.Add($"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};"); lines.Add($"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};");
@ -544,14 +958,28 @@ namespace HybridCLR.Editor.MethodBridge
string fieldName = $"__{index}"; string fieldName = $"__{index}";
string commentFieldName = $"{field.field.Name}"; string commentFieldName = $"{field.field.Name}";
lines.Add("\t#pragma pack(push, 1)"); lines.Add("\t#pragma pack(push, 1)");
lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding[{offset}];" : "")} {field.type.GetTypeName()} {fieldName};}}; // {commentFieldName}"); lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding[{offset}]; " : "")}{field.type.GetTypeName()} {fieldName};}}; // {commentFieldName}");
lines.Add($"\t#pragma pack(pop)"); lines.Add($"\t#pragma pack(pop)");
lines.Add($"\tstruct {{ {field.type.GetTypeName()} {fieldName}_forAlignmentOnly;}}; // {commentFieldName}"); if (packingSize > 0)
{
lines.Add($"\t#pragma pack(push, {packingSize})");
}
lines.Add($"\tstruct {{ {(offset > 0 ? $"char {fieldName}_offsetPadding_forAlignmentOnly[{offset}]; " : "")}{field.type.GetTypeName()} {fieldName}_forAlignmentOnly;}}; // {commentFieldName}");
if (packingSize > 0)
{
lines.Add($"\t#pragma pack(pop)");
}
++index; ++index;
} }
lines.Add("\t};");
lines.Add("};");
} }
else else
{ {
if (packingSize != 0)
{
lines.Add($"#pragma pack(push, {packingSize})");
}
lines.Add($"{(classSize > 0 ? "union" : "struct")} {ci.type.GetTypeName()} {{"); lines.Add($"{(classSize > 0 ? "union" : "struct")} {ci.type.GetTypeName()} {{");
if (classSize > 0) if (classSize > 0)
{ {
@ -570,7 +998,6 @@ namespace HybridCLR.Editor.MethodBridge
{ {
lines.Add("\t};"); lines.Add("\t};");
} }
}
lines.Add("};"); lines.Add("};");
if (packingSize != 0) if (packingSize != 0)
{ {
@ -578,10 +1005,11 @@ namespace HybridCLR.Editor.MethodBridge
} }
} }
} }
}
public const string SigOfObj = "u"; private const string SigOfObj = "u";
public static string ToFullName(TypeSig type) private static string ToFullName(TypeSig type)
{ {
type = type.RemovePinnedAndModifiers(); type = type.RemovePinnedAndModifiers();
switch (type.ElementType) switch (type.ElementType)
@ -676,9 +1104,9 @@ namespace HybridCLR.Editor.MethodBridge
return $"{Path.GetFileNameWithoutExtension(typeDef.Module.Name)}:{typeDef.FullName}"; return $"{Path.GetFileNameWithoutExtension(typeDef.Module.Name)}:{typeDef.FullName}";
} }
public void GenerateStructureSignatureStub(List<TypeInfo> types, List<string> lines) private void GenerateStructureSignatureStub(List<TypeInfo> types, List<string> lines)
{ {
lines.Add("FullName2Signature hybridclr::interpreter::g_fullName2SignatureStub[] = {"); lines.Add("const FullName2Signature hybridclr::interpreter::g_fullName2SignatureStub[] = {");
foreach (var type in types) foreach (var type in types)
{ {
TypeInfo isoType = ToIsomorphicType(type); TypeInfo isoType = ToIsomorphicType(type);
@ -688,10 +1116,10 @@ namespace HybridCLR.Editor.MethodBridge
lines.Add("};"); lines.Add("};");
} }
public void GenerateManaged2NativeStub(List<MethodDesc> methods, List<string> lines) private void GenerateManaged2NativeStub(List<MethodDesc> methods, List<string> lines)
{ {
lines.Add($@" lines.Add($@"
Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] = const Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] =
{{ {{
"); ");
@ -704,10 +1132,10 @@ Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] =
lines.Add("};"); lines.Add("};");
} }
public void GenerateNative2ManagedStub(List<MethodDesc> methods, List<string> lines) private void GenerateNative2ManagedStub(List<MethodDesc> methods, List<string> lines)
{ {
lines.Add($@" lines.Add($@"
Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] = const Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] =
{{ {{
"); ");
@ -720,10 +1148,10 @@ Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] =
lines.Add("};"); lines.Add("};");
} }
public void GenerateAdjustThunkStub(List<MethodDesc> methods, List<string> lines) private void GenerateAdjustThunkStub(List<MethodDesc> methods, List<string> lines)
{ {
lines.Add($@" lines.Add($@"
NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] = const NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] =
{{ {{
"); ");
@ -746,7 +1174,7 @@ NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] =
return type.NeedExpandValue() ? $"(uint64_t)({varName})" : $"N2MAsUint64ValueOrAddress<{type.GetTypeName()}>({varName})"; return type.NeedExpandValue() ? $"(uint64_t)({varName})" : $"N2MAsUint64ValueOrAddress<{type.GetTypeName()}>({varName})";
} }
public void GenerateManaged2NativeMethod(MethodDesc method, List<string> lines) private void GenerateManaged2NativeMethod(MethodDesc 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 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 => GetManaged2NativePassParam(p.Type, $"localVarBase+argVarIndexs[{p.Index}]")).Concat(new string[] { "method" })); string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => GetManaged2NativePassParam(p.Type, $"localVarBase+argVarIndexs[{p.Index}]")).Concat(new string[] { "method" }));
@ -760,7 +1188,7 @@ static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_
"); ");
} }
public string GenerateArgumentSizeAndOffset(List<ParamInfo> paramInfos) private string GenerateArgumentSizeAndOffset(List<ParamInfo> paramInfos)
{ {
StringBuilder s = new StringBuilder(); StringBuilder s = new StringBuilder();
int index = 0; int index = 0;
@ -774,7 +1202,7 @@ static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_
return s.ToString(); return s.ToString();
} }
public string GenerateCopyArgumentToInterpreterStack(List<ParamInfo> paramInfos) private string GenerateCopyArgumentToInterpreterStack(List<ParamInfo> paramInfos)
{ {
StringBuilder s = new StringBuilder(); StringBuilder s = new StringBuilder();
int index = 0; int index = 0;
@ -815,14 +1243,46 @@ static {method.ReturnInfo.Type.GetTypeName()} __N2M_{(adjustorThunk ? "AdjustorT
"); ");
} }
public void GenerateNative2ManagedMethod(MethodDesc method, List<string> lines) private void GenerateNative2ManagedMethod(MethodDesc method, List<string> lines)
{ {
GenerateNative2ManagedMethod0(method, false, lines); GenerateNative2ManagedMethod0(method, false, lines);
} }
public void GenerateAdjustThunkMethod(MethodDesc method, List<string> lines) private void GenerateAdjustThunkMethod(MethodDesc method, List<string> lines)
{ {
GenerateNative2ManagedMethod0(method, true, lines); GenerateNative2ManagedMethod0(method, true, lines);
} }
private void GenerateManaged2NativeFunctionPointerMethod(CalliMethodInfo methodInfo, List<string> lines)
{
MethodDesc method = methodInfo.Method;
string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}"));
string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => GetManaged2NativePassParam(p.Type, $"localVarBase+argVarIndexs[{p.Index}]")));
string il2cppCallConventionName = GetIl2cppCallConventionName(methodInfo.Callvention);
lines.Add($@"
static void __M2NF_{methodInfo.Signature}(Il2CppMethodPointer methodPointer, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret)
{{
typedef {method.ReturnInfo.Type.GetTypeName()} ({il2cppCallConventionName} *NativeMethod)({paramListStr});
{(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(methodPointer))({paramNameListStr});
}}
");
}
private void GenerateManaged2NativeFunctionPointerMethodStub(List<CalliMethodInfo> calliMethodSignatures, List<string> lines)
{
lines.Add(@"
const Managed2NativeFunctionPointerCallData hybridclr::interpreter::g_managed2NativeFunctionPointerCallStub[]
{
");
foreach (var method in calliMethodSignatures)
{
lines.Add($"\t{{\"{method.Signature}\", __M2NF_{method.Signature}}},");
}
lines.Add(@"
{nullptr, nullptr},
};
");
}
} }
} }

View File

@ -0,0 +1,78 @@
using dnlib.DotNet;
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Meta;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CallingConvention = System.Runtime.InteropServices.CallingConvention;
using UnityEngine;
namespace HybridCLR.Editor.MethodBridge
{
public class RawMonoPInvokeCallbackMethodInfo
{
public MethodDef Method { get; set; }
public CustomAttribute GenerationAttribute { get; set; }
}
public class MonoPInvokeCallbackAnalyzer
{
private readonly List<ModuleDefMD> _rootModules = new List<ModuleDefMD>();
private readonly List<RawMonoPInvokeCallbackMethodInfo> _reversePInvokeMethods = new List<RawMonoPInvokeCallbackMethodInfo>();
public List<RawMonoPInvokeCallbackMethodInfo> ReversePInvokeMethods => _reversePInvokeMethods;
public MonoPInvokeCallbackAnalyzer(AssemblyCache cache, List<string> assemblyNames)
{
foreach (var assemblyName in assemblyNames)
{
_rootModules.Add(cache.LoadModule(assemblyName));
}
}
private void CollectReversePInvokeMethods()
{
foreach (var mod in _rootModules)
{
Debug.Log($"ass:{mod.FullName} method count:{mod.Metadata.TablesStream.MethodTable.Rows}");
for (uint rid = 1, n = mod.Metadata.TablesStream.MethodTable.Rows; rid <= n; rid++)
{
var method = mod.ResolveMethod(rid);
//Debug.Log($"method:{method}");
if (!method.IsStatic || !method.HasCustomAttributes)
{
continue;
}
CustomAttribute wa = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Name == "MonoPInvokeCallbackAttribute");
if (wa == null)
{
continue;
}
if (!MetaUtil.IsSupportedPInvokeMethodSignature(method.MethodSig))
{
Debug.LogError($"MonoPInvokeCallback method {method.FullName} has unsupported parameter or return type. Please check the method signature.");
}
//foreach (var ca in method.CustomAttributes)
//{
// Debug.Log($"{ca.AttributeType.FullName} {ca.TypeFullName}");
//}
_reversePInvokeMethods.Add(new RawMonoPInvokeCallbackMethodInfo()
{
Method = method,
GenerationAttribute = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "HybridCLR.ReversePInvokeWrapperGenerationAttribute"),
});
}
}
}
public void Run()
{
CollectReversePInvokeMethods();
}
}
}

View File

@ -0,0 +1,68 @@
using dnlib.DotNet;
using HybridCLR.Editor.Meta;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.MethodBridge
{
public class PInvokeAnalyzer
{
private readonly List<ModuleDefMD> _rootModules = new List<ModuleDefMD>();
private readonly List<CallNativeMethodSignatureInfo> _pinvokeMethodSignatures = new List<CallNativeMethodSignatureInfo>();
public List<CallNativeMethodSignatureInfo> PInvokeMethodSignatures => _pinvokeMethodSignatures;
public PInvokeAnalyzer(AssemblyCache cache, List<string> assemblyNames)
{
foreach (var assemblyName in assemblyNames)
{
_rootModules.Add(cache.LoadModule(assemblyName));
}
}
private CallingConvention GetCallingConvention(MethodDef method)
{
switch (method.ImplMap.CallConv)
{
case PInvokeAttributes.CallConvWinapi: return CallingConvention.Default;
case PInvokeAttributes.CallConvCdecl: return CallingConvention.C;
case PInvokeAttributes.CallConvStdCall: return CallingConvention.StdCall;
case PInvokeAttributes.CallConvThiscall: return CallingConvention.ThisCall;
case PInvokeAttributes.CallConvFastcall: return CallingConvention.FastCall;
default: return CallingConvention.Default;
}
}
public void Run()
{
foreach (var mod in _rootModules)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (MethodDef method in type.Methods)
{
if (method.IsPinvokeImpl)
{
if (!MetaUtil.IsSupportedPInvokeMethodSignature(method.MethodSig))
{
Debug.LogError($"PInvoke method {method.FullName} has unsupported parameter or return type. Please check the method signature.");
}
_pinvokeMethodSignatures.Add(new CallNativeMethodSignatureInfo
{
MethodSig = method.MethodSig,
Callvention = method.HasImplMap? GetCallingConvention(method) : (CallingConvention?)null,
});
}
}
}
}
}
}
}

View File

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

View File

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

View File

@ -1,108 +0,0 @@
using dnlib.DotNet;
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Meta;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.ReversePInvokeWrap
{
public class RawReversePInvokeMethodInfo
{
public MethodDef Method { get; set; }
public CustomAttribute GenerationAttribute { get; set; }
}
public class ABIReversePInvokeMethodInfo
{
public MethodDesc Method { get; set; }
public int Count { get; set; }
}
public class Analyzer
{
private readonly List<ModuleDefMD> _rootModules = new List<ModuleDefMD>();
private readonly List<RawReversePInvokeMethodInfo> _reversePInvokeMethods = new List<RawReversePInvokeMethodInfo>();
public Analyzer(AssemblyCache cache, List<string> assemblyNames)
{
foreach (var assemblyName in assemblyNames)
{
_rootModules.Add(cache.LoadModule(assemblyName));
}
}
private void CollectReversePInvokeMethods()
{
foreach (var mod in _rootModules)
{
Debug.Log($"ass:{mod.FullName} methodcount:{mod.Metadata.TablesStream.MethodTable.Rows}");
for (uint rid = 1, n = mod.Metadata.TablesStream.MethodTable.Rows; rid <= n; rid++)
{
var method = mod.ResolveMethod(rid);
//Debug.Log($"method:{method}");
if (!method.IsStatic || !method.HasCustomAttributes)
{
continue;
}
CustomAttribute wa = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Name == "MonoPInvokeCallbackAttribute");
if (wa == null)
{
continue;
}
//foreach (var ca in method.CustomAttributes)
//{
// Debug.Log($"{ca.AttributeType.FullName} {ca.TypeFullName}");
//}
_reversePInvokeMethods.Add(new RawReversePInvokeMethodInfo()
{
Method = method,
GenerationAttribute = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "HybridCLR.ReversePInvokeWrapperGenerationAttribute"),
});
}
}
}
public List<ABIReversePInvokeMethodInfo> BuildABIMethods()
{
var methodsBySig = new Dictionary<string, ABIReversePInvokeMethodInfo>();
var typeCreator = new TypeCreator();
foreach(var method in _reversePInvokeMethods)
{
MethodDesc desc = new MethodDesc
{
MethodDef = method.Method,
ReturnInfo = new ReturnInfo { Type = typeCreator.CreateTypeInfo(method.Method.ReturnType)},
ParamInfos = method.Method.Parameters.Select(p => new ParamInfo { Type = typeCreator.CreateTypeInfo(p.Type)}).ToList(),
};
desc.Init();
if (!methodsBySig.TryGetValue(desc.Sig, out var arm))
{
arm = new ABIReversePInvokeMethodInfo()
{
Method = desc,
Count = 0,
};
methodsBySig.Add(desc.Sig, arm);
}
int preserveCount = method.GenerationAttribute != null ? (int)method.GenerationAttribute.ConstructorArguments[0].Value : 1;
arm.Count += preserveCount;
}
var methods = methodsBySig.Values.ToList();
methods.Sort((a, b) => String.CompareOrdinal(a.Method.Sig, b.Method.Sig));
return methods;
}
public void Run()
{
CollectReversePInvokeMethods();
}
}
}

View File

@ -1,59 +0,0 @@
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Template;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
namespace HybridCLR.Editor.ReversePInvokeWrap
{
public class Generator
{
public void Generate(List<ABIReversePInvokeMethodInfo> methods, string outputFile)
{
string template = File.ReadAllText(outputFile, Encoding.UTF8);
var frr = new FileRegionReplace(template);
var codes = new List<string>();
int methodIndex = 0;
var stubCodes = new List<string>();
foreach(var methodInfo in methods)
{
MethodDesc method = methodInfo.Method;
string paramDeclaringListWithoutMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}"));
string paramNameListWithoutMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"__arg{p.Index}").Concat(new string[] { "method" }));
string paramTypeListWithMethodInfoStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()}").Concat(new string[] { "const MethodInfo*" }));
string methodTypeDef = $"typedef {method.ReturnInfo.Type.GetTypeName()} (*Callback)({paramTypeListWithMethodInfoStr})";
for (int i = 0; i < methodInfo.Count; i++, methodIndex++)
{
codes.Add($@"
{method.ReturnInfo.Type.GetTypeName()} __ReversePInvokeMethod_{methodIndex}({paramDeclaringListWithoutMethodInfoStr})
{{
const MethodInfo* method = MetadataModule::GetMethodInfoByReversePInvokeWrapperIndex({methodIndex});
{methodTypeDef};
{(method.ReturnInfo.IsVoid ? "" : "return ")}((Callback)(method->methodPointerCallByInterp))({paramNameListWithoutMethodInfoStr});
}}
");
stubCodes.Add($"\t\t{{\"{method.Sig}\", (Il2CppMethodPointer)__ReversePInvokeMethod_{methodIndex}}},\n");
}
Debug.Log($"[ReversePInvokeWrap.Generator] method:{method.MethodDef} wrapperCount:{methodInfo.Count}");
}
codes.Add(@"
ReversePInvokeMethodData g_reversePInvokeMethodStub[]
{
");
codes.AddRange(stubCodes);
codes.Add(@"
{nullptr, nullptr},
};
");
frr.Replace("CODE", string.Join("", codes));
frr.Commit(outputFile);
}
}
}

View File

@ -1,32 +0,0 @@
using HybridCLR.Editor;
using System;
using UnityEditor;
using UnityEditorInternal;
namespace HybridCLR.Editor.Settings
{
/// <summary>
/// 监听编辑器状态,当编辑器重新 focus 时,重新加载实例,避免某些情景下 svn 、git 等外部修改了数据却无法同步的异常。
/// </summary>
[InitializeOnLoad]
public static class EditorStatusWatcher
{
public static Action OnEditorFocused;
static bool isFocused;
static EditorStatusWatcher() => EditorApplication.update += Update;
static void Update()
{
if (isFocused != InternalEditorUtility.isApplicationActive)
{
isFocused = InternalEditorUtility.isApplicationActive;
if (isFocused)
{
HybridCLRSettings.LoadOrCreate();
OnEditorFocused?.Invoke();
}
}
}
}
}

View File

@ -18,20 +18,21 @@ namespace HybridCLR.Editor.Settings
private SerializedProperty _hotUpdateAssemblies; private SerializedProperty _hotUpdateAssemblies;
private SerializedProperty _preserveHotUpdateAssemblies; private SerializedProperty _preserveHotUpdateAssemblies;
private SerializedProperty _hotUpdateDllCompileOutputRootDir; private SerializedProperty _hotUpdateDllCompileOutputRootDir;
private SerializedProperty _externalHotUpdateAssembliyDirs; private SerializedProperty _externalHotUpdateAssemblyDirs;
private SerializedProperty _strippedAOTDllOutputRootDir; private SerializedProperty _strippedAOTDllOutputRootDir;
private SerializedProperty _patchAOTAssemblies; private SerializedProperty _patchAOTAssemblies;
private SerializedProperty _outputLinkFile; private SerializedProperty _outputLinkFile;
private SerializedProperty _outputAOTGenericReferenceFile; private SerializedProperty _outputAOTGenericReferenceFile;
private SerializedProperty _maxGenericReferenceIteration; private SerializedProperty _maxGenericReferenceIteration;
private SerializedProperty _maxMethodBridgeGenericIteration; private SerializedProperty _maxMethodBridgeGenericIteration;
private GUIStyle buttonStyle;
public HybridCLRSettingsProvider() : base("Project/HybridCLR Settings", SettingsScope.Project) { } public HybridCLRSettingsProvider() : base("Project/HybridCLR Settings", SettingsScope.Project) { }
public override void OnActivate(string searchContext, VisualElement rootElement) public override void OnActivate(string searchContext, VisualElement rootElement)
{ {
EditorStatusWatcher.OnEditorFocused += OnEditorFocused;
InitGUI(); InitGUI();
} }
private void InitGUI() private void InitGUI()
{ {
var setting = HybridCLRSettings.LoadOrCreate(); var setting = HybridCLRSettings.LoadOrCreate();
@ -45,7 +46,7 @@ namespace HybridCLR.Editor.Settings
_hotUpdateAssemblies = _serializedObject.FindProperty("hotUpdateAssemblies"); _hotUpdateAssemblies = _serializedObject.FindProperty("hotUpdateAssemblies");
_preserveHotUpdateAssemblies = _serializedObject.FindProperty("preserveHotUpdateAssemblies"); _preserveHotUpdateAssemblies = _serializedObject.FindProperty("preserveHotUpdateAssemblies");
_hotUpdateDllCompileOutputRootDir = _serializedObject.FindProperty("hotUpdateDllCompileOutputRootDir"); _hotUpdateDllCompileOutputRootDir = _serializedObject.FindProperty("hotUpdateDllCompileOutputRootDir");
_externalHotUpdateAssembliyDirs = _serializedObject.FindProperty("externalHotUpdateAssembliyDirs"); _externalHotUpdateAssemblyDirs = _serializedObject.FindProperty("externalHotUpdateAssembliyDirs");
_strippedAOTDllOutputRootDir = _serializedObject.FindProperty("strippedAOTDllOutputRootDir"); _strippedAOTDllOutputRootDir = _serializedObject.FindProperty("strippedAOTDllOutputRootDir");
_patchAOTAssemblies = _serializedObject.FindProperty("patchAOTAssemblies"); _patchAOTAssemblies = _serializedObject.FindProperty("patchAOTAssemblies");
_outputLinkFile = _serializedObject.FindProperty("outputLinkFile"); _outputLinkFile = _serializedObject.FindProperty("outputLinkFile");
@ -53,73 +54,10 @@ namespace HybridCLR.Editor.Settings
_maxGenericReferenceIteration = _serializedObject.FindProperty("maxGenericReferenceIteration"); _maxGenericReferenceIteration = _serializedObject.FindProperty("maxGenericReferenceIteration");
_maxMethodBridgeGenericIteration = _serializedObject.FindProperty("maxMethodBridgeGenericIteration"); _maxMethodBridgeGenericIteration = _serializedObject.FindProperty("maxMethodBridgeGenericIteration");
} }
private void OnEditorFocused()
{
InitGUI();
Repaint();
}
public override void OnTitleBarGUI()
{
base.OnTitleBarGUI();
var rect = GUILayoutUtility.GetLastRect();
buttonStyle = buttonStyle ?? GUI.skin.GetStyle("IconButton");
#region 绘制官方网站跳转按钮
var w = rect.x + rect.width;
rect.x = w - 57;
rect.y += 6;
rect.width = rect.height = 18;
var content = EditorGUIUtility.IconContent("_Help");
content.tooltip = "点击访问 HybridCLR 官方文档";
if (GUI.Button(rect, content, buttonStyle))
{
Application.OpenURL("https://focus-creative-games.github.io/hybridclr/");
}
#endregion
#region 绘制 Preset
rect.x += 19;
content = EditorGUIUtility.IconContent("Preset.Context");
content.tooltip = "点击存储或加载 Preset .";
if (GUI.Button(rect, content, buttonStyle))
{
var target = HybridCLRSettings.Instance;
var receiver = ScriptableObject.CreateInstance<SettingsPresetReceiver>();
receiver.Init(target, this);
PresetSelector.ShowSelector(target, null, true, receiver);
}
#endregion
#region 绘制 Reset
rect.x += 19;
content = EditorGUIUtility.IconContent(
#if UNITY_2021_3_OR_NEWER
"pane options"
#else
"_Popup"
#endif
);
content.tooltip = "Reset";
if (GUI.Button(rect, content, buttonStyle))
{
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("Reset"), false, () =>
{
Undo.RecordObject(HybridCLRSettings.Instance, "Capture Value for Reset");
var dv = ScriptableObject.CreateInstance<HybridCLRSettings>();
var json = EditorJsonUtility.ToJson(dv);
UnityEngine.Object.DestroyImmediate(dv);
EditorJsonUtility.FromJsonOverwrite(json, HybridCLRSettings.Instance);
HybridCLRSettings.Save();
});
menu.ShowAsContext();
}
#endregion
}
public override void OnGUI(string searchContext) public override void OnGUI(string searchContext)
{ {
using (CreateSettingsWindowGUIScope()) if (_serializedObject == null || !_serializedObject.targetObject)
{
//解决编辑器打包时出现的 _serializedObject.targetObject 意外销毁的情况
if (_serializedObject == null||!_serializedObject.targetObject)
{ {
InitGUI(); InitGUI();
} }
@ -133,7 +71,7 @@ namespace HybridCLR.Editor.Settings
EditorGUILayout.PropertyField(_hotUpdateAssemblies); EditorGUILayout.PropertyField(_hotUpdateAssemblies);
EditorGUILayout.PropertyField(_preserveHotUpdateAssemblies); EditorGUILayout.PropertyField(_preserveHotUpdateAssemblies);
EditorGUILayout.PropertyField(_hotUpdateDllCompileOutputRootDir); EditorGUILayout.PropertyField(_hotUpdateDllCompileOutputRootDir);
EditorGUILayout.PropertyField(_externalHotUpdateAssembliyDirs); EditorGUILayout.PropertyField(_externalHotUpdateAssemblyDirs);
EditorGUILayout.PropertyField(_strippedAOTDllOutputRootDir); EditorGUILayout.PropertyField(_strippedAOTDllOutputRootDir);
EditorGUILayout.PropertyField(_patchAOTAssemblies); EditorGUILayout.PropertyField(_patchAOTAssemblies);
EditorGUILayout.PropertyField(_outputLinkFile); EditorGUILayout.PropertyField(_outputLinkFile);
@ -146,33 +84,23 @@ namespace HybridCLR.Editor.Settings
HybridCLRSettings.Save(); HybridCLRSettings.Save();
} }
} }
}
private IDisposable CreateSettingsWindowGUIScope()
{
var unityEditorAssembly = Assembly.GetAssembly(typeof(EditorWindow));
var type = unityEditorAssembly.GetType("UnityEditor.SettingsWindow+GUIScope");
return Activator.CreateInstance(type) as IDisposable;
}
public override void OnDeactivate() public override void OnDeactivate()
{ {
base.OnDeactivate(); base.OnDeactivate();
EditorStatusWatcher.OnEditorFocused -= OnEditorFocused;
HybridCLRSettings.Save(); HybridCLRSettings.Save();
} }
static HybridCLRSettingsProvider provider; static HybridCLRSettingsProvider s_provider;
[SettingsProvider] [SettingsProvider]
public static SettingsProvider CreateMyCustomSettingsProvider() public static SettingsProvider CreateMyCustomSettingsProvider()
{ {
if (HybridCLRSettings.Instance && provider == null) if (s_provider == null)
{ {
provider = new HybridCLRSettingsProvider(); s_provider = new HybridCLRSettingsProvider();
using (var so = new SerializedObject(HybridCLRSettings.Instance))
{
provider.keywords = GetSearchKeywordsFromSerializedObject(so);
} }
} return s_provider;
return provider;
} }
} }
} }

View File

@ -1,54 +1,98 @@
using System.IO;
using UnityEditorInternal; using UnityEditorInternal;
using UnityEngine; using UnityEngine;
namespace HybridCLR.Editor.Settings namespace HybridCLR.Editor.Settings
{ {
[FilePath("ProjectSettings/HybridCLRSettings.asset")]
public class HybridCLRSettings : ScriptableSingleton<HybridCLRSettings> public class HybridCLRSettings : ScriptableObject
{ {
[Header("开启HybridCLR插件")] [Tooltip("enable HybridCLR")]
public bool enable = true; public bool enable = true;
[Header("使用全局安装的il2cpp")] [Tooltip("use il2cpp in unity editor installation location")]
public bool useGlobalIl2cpp; public bool useGlobalIl2cpp;
[Header("hybridclr 仓库 URL")] [Tooltip("hybridclr repo URL")]
public string hybridclrRepoURL = "https://gitee.com/focus-creative-games/hybridclr"; public string hybridclrRepoURL = "https://gitee.com/focus-creative-games/hybridclr";
[Header("il2cpp_plus 仓库 URL")] [Tooltip("il2cpp_plus repo URL")]
public string il2cppPlusRepoURL = "https://gitee.com/focus-creative-games/il2cpp_plus"; public string il2cppPlusRepoURL = "https://gitee.com/focus-creative-games/il2cpp_plus";
[Header("热更新Assembly Definitions")] [Tooltip("hot update assembly definitions(asd)")]
public AssemblyDefinitionAsset[] hotUpdateAssemblyDefinitions; public AssemblyDefinitionAsset[] hotUpdateAssemblyDefinitions;
[Header("热更新dlls")] [Tooltip("hot update assembly names(without .dll suffix)")]
public string[] hotUpdateAssemblies; public string[] hotUpdateAssemblies;
[Header("预留的热更新dlls")] [Tooltip("preserved hot update assembly names(without .dll suffix)")]
public string[] preserveHotUpdateAssemblies; public string[] preserveHotUpdateAssemblies;
[Header("热更新dll编译输出根目录")] [Tooltip("output directory of compiling hot update assemblies")]
public string hotUpdateDllCompileOutputRootDir = "HybridCLRData/HotUpdateDlls"; public string hotUpdateDllCompileOutputRootDir = "HybridCLRData/HotUpdateDlls";
[Header("外部热更新dll搜索路径")] [Tooltip("searching paths of external hot update assemblies")]
public string[] externalHotUpdateAssembliyDirs; public string[] externalHotUpdateAssembliyDirs;
[Header("裁减后AOT dll输出根目录")] [Tooltip("output directory of stripped AOT assemblies")]
public string strippedAOTDllOutputRootDir = "HybridCLRData/AssembliesPostIl2CppStrip"; public string strippedAOTDllOutputRootDir = "HybridCLRData/AssembliesPostIl2CppStrip";
[Header("补充元数据AOT dlls")] [Tooltip("supplementary metadata assembly names(without .dll suffix)")]
public string[] patchAOTAssemblies; public string[] patchAOTAssemblies;
[Header("生成的link.xml路径")] [Tooltip("output file of automatic generated link.xml by scanning hot update assemblies")]
public string outputLinkFile = "HybridCLRGenerate/link.xml"; public string outputLinkFile = "HybridCLRGenerate/link.xml";
[Header("自动扫描生成的AOTGenericReferences.cs路径")] [Tooltip("output file of automatic generated AOTGenericReferences.cs")]
public string outputAOTGenericReferenceFile = "HybridCLRGenerate/AOTGenericReferences.cs"; public string outputAOTGenericReferenceFile = "HybridCLRGenerate/AOTGenericReferences.cs";
[Header("AOT泛型实例化搜索迭代次数")] [Tooltip("max iteration count of searching generic methods in hot update assemblies")]
public int maxGenericReferenceIteration = 10; public int maxGenericReferenceIteration = 10;
[Header("MethodBridge泛型搜索迭代次数")] [Tooltip("max iteration count of searching method bridge generic methods in AOT assemblies")]
public int maxMethodBridgeGenericIteration = 10; public int maxMethodBridgeGenericIteration = 10;
private static HybridCLRSettings s_Instance;
public static HybridCLRSettings Instance
{
get
{
if (!s_Instance)
{
LoadOrCreate();
}
return s_Instance;
}
}
private static string GetFilePath()
{
return "ProjectSettings/HybridCLRSettings.asset";
}
public static HybridCLRSettings LoadOrCreate()
{
string filePath = GetFilePath();
Object[] objs = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
s_Instance = objs.Length > 0 ? (HybridCLRSettings)objs[0] : (s_Instance ?? CreateInstance<HybridCLRSettings>());
return s_Instance;
}
public static void Save()
{
if (!s_Instance)
{
return;
}
string filePath = GetFilePath();
string directoryName = Path.GetDirectoryName(filePath);
Directory.CreateDirectory(directoryName);
var obj = new Object[1] { s_Instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, true);
}
} }
} }

View File

@ -8,7 +8,7 @@ namespace HybridCLR.Editor.Settings
{ {
[MenuItem("HybridCLR/About", priority = 0)] [MenuItem("HybridCLR/About", priority = 0)]
public static void OpenAbout() => Application.OpenURL("https://hybridclr.doc.code-philosophy.com/docs/intro"); public static void OpenAbout() => Application.OpenURL("https://www.hybridclr.cn/docs/intro");
[MenuItem("HybridCLR/Installer...", priority = 60)] [MenuItem("HybridCLR/Installer...", priority = 60)]
private static void Open() private static void Open()
@ -21,19 +21,19 @@ namespace HybridCLR.Editor.Settings
public static void OpenSettings() => SettingsService.OpenProjectSettings("Project/HybridCLR Settings"); public static void OpenSettings() => SettingsService.OpenProjectSettings("Project/HybridCLR Settings");
[MenuItem("HybridCLR/Documents/Quick Start")] [MenuItem("HybridCLR/Documents/Quick Start")]
public static void OpenQuickStart() => Application.OpenURL("https://hybridclr.doc.code-philosophy.com/docs/beginner/quickstart"); public static void OpenQuickStart() => Application.OpenURL("https://www.hybridclr.cn/docs/beginner/quickstart");
[MenuItem("HybridCLR/Documents/Performance")] [MenuItem("HybridCLR/Documents/Performance")]
public static void OpenPerformance() => Application.OpenURL("https://hybridclr.doc.code-philosophy.com/docs/basic/performance"); public static void OpenPerformance() => Application.OpenURL("https://www.hybridclr.cn/docs/basic/performance");
[MenuItem("HybridCLR/Documents/FAQ")] [MenuItem("HybridCLR/Documents/FAQ")]
public static void OpenFAQ() => Application.OpenURL("https://hybridclr.doc.code-philosophy.com/docs/help/faq"); public static void OpenFAQ() => Application.OpenURL("https://www.hybridclr.cn/docs/help/faq");
[MenuItem("HybridCLR/Documents/Common Errors")] [MenuItem("HybridCLR/Documents/Common Errors")]
public static void OpenCommonErrors() => Application.OpenURL("https://hybridclr.doc.code-philosophy.com/docs/help/commonerrors"); public static void OpenCommonErrors() => Application.OpenURL("https://www.hybridclr.cn/docs/help/commonerrors");
[MenuItem("HybridCLR/Documents/Bug Report")] [MenuItem("HybridCLR/Documents/Bug Report")]
public static void OpenBugReport() => Application.OpenURL("https://hybridclr.doc.code-philosophy.com/docs/help/issue"); public static void OpenBugReport() => Application.OpenURL("https://www.hybridclr.cn/docs/help/issue");
} }
} }

View File

@ -1,89 +0,0 @@
using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace HybridCLR.Editor.Settings
{
public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableObject
{
private static T s_Instance;
public static T Instance
{
get
{
if (!s_Instance)
{
LoadOrCreate();
}
return s_Instance;
}
}
public static T LoadOrCreate()
{
string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath))
{
var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
s_Instance = arr.Length > 0 ? arr[0] as T : s_Instance??CreateInstance<T>();
}
else
{
Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid");
}
return s_Instance;
}
public static void Save(bool saveAsText = true)
{
if (!s_Instance)
{
Debug.LogError("Cannot save ScriptableSingleton: no instance!");
return;
}
string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath))
{
string directoryName = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
UnityEngine.Object[] obj = new T[1] { s_Instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText);
}
}
protected static string GetFilePath()
{
return typeof(T).GetCustomAttributes(inherit: true)
.Where(v => v is FilePathAttribute)
.Cast<FilePathAttribute>()
.FirstOrDefault()
?.filepath;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class FilePathAttribute : Attribute
{
internal string filepath;
/// <summary>
/// 单例存放路径
/// </summary>
/// <param name="path">相对 Project 路径</param>
public FilePathAttribute(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("Invalid relative path (it is empty)");
}
if (path[0] == '/')
{
path = path.Substring(1);
}
filepath = path;
}
}
}

View File

@ -1,39 +0,0 @@
using UnityEditor;
using UnityEditor.Presets;
using UnityEngine;
namespace HybridCLR.Editor.Settings
{
public class SettingsPresetReceiver : PresetSelectorReceiver
{
private Object m_Target;
private Preset m_InitialValue;
private SettingsProvider m_Provider;
internal void Init(Object target, SettingsProvider provider)
{
m_Target = target;
m_InitialValue = new Preset(target);
m_Provider = provider;
}
public override void OnSelectionChanged(Preset selection)
{
if (selection != null)
{
Undo.RecordObject(m_Target, "Apply Preset " + selection.name);
selection.ApplyTo(m_Target);
}
else
{
Undo.RecordObject(m_Target, "Cancel Preset");
m_InitialValue.ApplyTo(m_Target);
}
m_Provider.Repaint();
}
public override void OnSelectionClosed(Preset selection)
{
OnSelectionChanged(selection);
Object.DestroyImmediate(this);
}
}
}

View File

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

View File

@ -65,9 +65,6 @@ namespace HybridCLR.Editor
public string name; public string name;
} }
/// <summary>
/// 热更新dll列表。不包含 preserveHotUpdateAssemblies。
/// </summary>
public static List<string> HotUpdateAssemblyNamesExcludePreserved public static List<string> HotUpdateAssemblyNamesExcludePreserved
{ {
get get

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 Code Philosophy Technology Ltd. Copyright (c) 2025 Code Philosophy Technology Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

Binary file not shown.

View File

@ -1,87 +0,0 @@
fileFormatVersion: 2
guid: b93c6604eb031674b80de14cd4458dc0
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: None
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

113
README.md
View File

@ -1,7 +1,3 @@
- [README 中文](./README_zh.md)
- [README English](./README.md)
# HybridCLR # HybridCLR
[![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE) [![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE)
@ -11,81 +7,84 @@
<br/> <br/>
<br/> <br/>
HybridCLR is a **almost perfect** full-platform native c# hot update solution for Unity with complete features, zero cost, high performance, and low memory**. [README 中文](./README.md) | [README English](./README_EN.md)
HybridCLR expands the ability of il2cpp, making it change from pure [AOT](https://en.wikipedia.org/wiki/Ahead-of-time_compilation) runtime to 'AOT+Interpreter' hybrid runtime, and then natively supports dynamic loading of assembly , so that the games packaged based on il2cpp backend can be executed not only on the Android platform, but also on IOS, Consoles and other platforms that limit JIT efficiently in **AOT+interpreter** hybrid mode, completely supporting hot updates from the bottom layer. [Github](https://github.com/focus-creative-games/hybridclr) | [Gitee](https://gitee.com/focus-creative-games/hybridclr)
HybridCLR not only supports the traditional fully interpreted execution mode, but also pioneered [Differential Hybrid Execution](https://hybridclr.doc.code-philosophy.com/en/docs/business/differentialhybridexecution) technique. That is, you can add, delete, or modify the AOT dll at will, and intelligently make the changed or newly added classes and functions run in interpreter mode, but the unchanged classes and functions run in AOT mode, so that the running performance of the hot-updated game logic basically reaches the original AOT level. HybridCLR是一个**特性完整、零成本、高性能、低内存**的**近乎完美**的Unity全平台原生c#热更新解决方案。
Welcome to embrace modern native C# hot update technology! ! ! HybridCLR扩充了il2cpp运行时代码使它由纯[AOT](https://en.wikipedia.org/wiki/Ahead-of-time_compilation) runtime变成AOT+Interpreter 混合runtime进而原生支持动态加载assembly从底层彻底支持了热更新。使用HybridCLR技术的游戏不仅能在Android平台也能在IOS、Consoles、WebGL等所有il2cpp支持的平台上高效运行。
## Documentation 由于HybridCLR对ECMA-335规范 的良好支持以及对Unity开发工作流的高度兼容Unity项目在接入HybridCLR后可以几乎无缝地获得C#代码热更新的能力开发者不需要改变日常开发习惯和要求。HybridCLR首次实现了将Unity平台的全平台代码热更新方案的工程难度降到几乎为零的水平。
- [Official Documentation](https://hybridclr.doc.code-philosophy.com/en/docs/intro) 欢迎拥抱现代原生C#热更新技术
- [Quickstart](https://hybridclr.doc.code-philosophy.com/en/docs/beginner/quickstart)
- [Release Log](./RELEASELOG.md)
## Features ## 文档
- Features complete. A nearly complete implementation of the [ECMA-335 specification](https://www.ecma-international.org/publications-and-standards/standards/ecma-335/), with only a very small number of [unsupported features](https://hybridclr.doc.code-philosophy.com/en/docs/basic/notsupportedfeatures). - [官方文档](https://www.hybridclr.cn/docs/intro)
- Zero learning and use costs. HybridCLR enhances the pure AOT runtime into a complete runtime, making hot update code work seamlessly with AOT code. The script class and the AOT class are in the same runtime, and you can freely write codes such as inheritance, reflection, and multi-threading (volatile, ThreadStatic, Task, async). No need to write any special code, no code generation, almost unlimited. - [快速上手](https://www.hybridclr.cn/docs/beginner/quickstart)
- Efficient execution. Implemented an extremely efficient register interpreter, all indicators are significantly better than other hot update schemes. [Performance Test Report](https://hybridclr.doc.code-philosophy.com/en/docs/basic/performance) - [商业项目案例](https://www.hybridclr.cn/docs/other/businesscase)
- Memory efficient. The classes defined in the hot update script occupy the same memory space as ordinary c# classes, which is far superior to other hot update solutions. [Memory and GC](https://hybridclr.doc.code-philosophy.com/en/docs/basic/memory)
- Due to the perfect support for generics, libraries that are not compatible with il2cpp due to AOT generics issues can now run perfectly under il2cpp
- Support some features not supported by il2cpp, such as __makeref, __reftype, __refvalue directives
- [Differential Hybrid Execution](https://hybridclr.doc.code-philosophy.com/en/docs/business/differentialhybridexecution)
## Working Principle ## 特性
HybridCLR is inspired by mono's [mixed mode execution](https://www.mono-project.com/news/2017/11/13/mono-interpreter/) technology, and provides additional AOT runtimes such as unity's il2cpp The interpreter module is provided to transform them from pure AOT runtime to "AOT + Interpreter" hybrid operation mode. - 近乎完整实现了[ECMA-335规范](https://www.ecma-international.org/publications-and-standards/standards/ecma-335/),只有极少量的[不支持的特性](https://www.hybridclr.cn/docs/basic/notsupportedfeatures)。
- 零学习和使用成本。对绝大多数开发者来说写代码近乎没有限制。 热更新代码与AOT代码无缝工作可以随意写继承、**泛型**、**反射**之类的代码。不需要额外写任何特殊代码、没有代码生成
- 完全支持多线程,包含但不限于 volatile、ThreadStatic、async Task等相关功能和特性。这是其他所有热更新方案都不支持的
- 几乎完全兼容Unity的工作流。包括且不限于支持热更新**MonoBehaviour**、ScriptableObject、**DOTS**技术,资源上挂载的热更新脚本可以正确实例化,这是其他所有热更新方案都不支持的
- 执行高效。实现了一个极其高效的寄存器解释器,所有指标都大幅优于其他热更新方案。[性能测试报告](https://www.hybridclr.cn/docs/basic/performance)
- 内存高效。 热更新脚本中定义的类跟普通c#类占用一样的内存空间,远优于其他热更新方案。[内存占用报告](https://www.hybridclr.cn/docs/basic/memory)
- 支持MonoPInvokeCallback可以与native代码或者其他语言如lua、javascript、python良好交互
- 支持PInvoke
- 支持一些il2cpp不支持的特性如\_\_makeref、 \_\_reftype、\_\_refvalue指令
- 支持独创的 **Differential Hybrid Execution(DHE)** 差分混合执行技术即可以对AOT dll任意增删改会智能地让未改动的函数以AOT方式运行变化或者新增的函数以interpreter模式运行让热更新的游戏逻辑的运行性能基本达到原生AOT的水平
- 支持**热重载**技术可以100%卸载程序集
- 支持动态**Hotfix**技术可以运行过程中无感修复代码bug
- 支持现代的dll加密技术有效保障代码安全
## 支持的版本与平台
- 支持2019.4.x、2020.3.x、2021.3.x、2022.3.x、2023.2.x、6000.x.y 所有LTS版本
- 支持所有il2cpp支持的平台
- 支持团结引擎和鸿蒙平台
## 工作原理
HybridCLR从mono的 [mixed mode execution](https://www.mono-project.com/news/2017/11/13/mono-interpreter/) 技术中得到启发为unity的il2cpp之类的AOT runtime额外提供了interpreter模块将它们由纯AOT运行时改造为"AOT + Interpreter"混合运行方式。
![icon](https://github.com/focus-creative-games/hybridclr/raw/main/docs/images/architecture.png) ![icon](https://github.com/focus-creative-games/hybridclr/raw/main/docs/images/architecture.png)
More specifically, HybridCLR does the following: 更具体地说HybridCLR做了以下几点工作
- Implemented an efficient metadata (dll) parsing library - 实现了一个高效的元数据(dll)解析库
- Modified the metadata management module to realize the dynamic registration of metadata - 改造了元数据管理模块,实现了元数据的动态注册
- Implemented a compiler from IL instruction set to custom register instruction set - 实现了一个IL指令集到自定义的寄存器指令集的compiler
- Implemented an efficient register interpreter - 实现了一个高效的寄存器解释器
- Provide a large number of instinct functions to improve the performance of the interpreter - 额外提供大量的instinct函数提升解释器性能
## The Difference From Other C# Hot-Update Solution ## 稳定性状况
HybridCLR is a native c# hot update solution. In layman's terms, il2cpp is equivalent to the aot module of mono, and HybridCLR is equivalent to the interpreter module of mono, and the combination of the two becomes a complete mono. HybridCLR makes il2cpp a full-featured runtime, natively (that is, through System.Reflection.Assembly.Load) supports dynamic loading of dlls, thereby supporting hot updates of the ios platform. HybridCLR已经被广泛验证是非常高效、稳定的Unity热更新解决方案良好满足大中型商业项目的稳定和性能要求。
Just because HybridCLR is implemented at the native runtime level, the types of the hot update part and the AOT part of the main project are completely equivalent and seamlessly unified. You can call, inherit, reflect, and multi-thread at will, without generating code or writing adapters. 目前已经有数千个商业游戏项目接入了HybridCLR其中有超过千个已经在App Store和Google Player上线仅仅iOS免费榜前500名中就有近百款使用了HybridCLR。上线的项目中包括MMORPG、重度卡牌、重度塔防之类的游戏。国内绝大多数**Top游戏公司**都已经在使用HybridCLR。
Other hot update solutions are independent vm, and the relationship with il2cpp is essentially equivalent to the relationship of embedding lua in mono. Therefore, the type system is not uniform. In order to allow the hot update type to inherit some AOT types, an adapter needs to be written, and the type in the interpreter cannot be recognized by the type system of the main project. Incomplete features, troublesome development, and low operating efficiency. 可查看我们已知的头部公司中使用HybridCLR并且已经上线的[项目列表](https://www.hybridclr.cn/docs/other/businesscase)。
## Supported Versions And Platforms ## 支持与联系
- Support 2019.4.x, 2020.3.x, 2021.3.x, 2022.3.x full series of LTS versions. The `2023.2.0ax` version is also supported, but not released to the public. - 官方1群3000人651188171
- Supports all il2cpp supported platforms - 新手1群3000人428404198
- 新手2群2000人680274677
- 新手3群2000人**920714552推荐**
- discord频道 `https://discord.gg/BATfNfJnm2`
- 商业合作邮箱: business#code-philosophy.com
- [商业化支持](https://www.hybridclr.cn/docs/business/intro)
## Stability Status ## 关于作者
HybridCLR has been widely verified as a very efficient and stable solution for hot update of Unity. **walon** **Code Philosophy代码哲学** 创始人
The official versions of **extremely stable** 1.x, 2.x, and 3.x are currently released, which are sufficient to meet the stability requirements of large and medium-sized commercial projects. 毕业于清华大学物理系2006年CMO金牌奥数国家集训队成员保送清华基科班。专注于游戏技术擅长开发架构和基础技术设施。
At present, at least thousands of commercial game projects have been integrated, and hundreds of them have been launched on both ends. The online projects include MMORPG, heavy card, heavy tower defense and other games. **Most top game companies** (such as Tencent and NetEase) are already using HybridCLR.
You can read the [game projects in top game companies](https://hybridclr.doc.code-philosophy.com/en/docs/other/businesscase) those are using HybridCLR and has been launched. ## license
## Support And Contact
- Official 1 group: 651188171 (full)
- Novice QQ group 1: 428404198 (full)
- Novice QQ group 2: **680274677 (recommended)**
- discord channel [https://discord.gg/BATfNfJnm2](https://discord.gg/BATfNfJnm2)
- Business cooperation email: business#code-philosophy.com
- [Commercial Support](https://hybridclr.doc.code-philosophy.com/en/docs/business/intro)
## About The Author
**walon** : Founder of **Code Philosophy (code philosophy)**
Graduated from the Department of Physics of Tsinghua University, won the CMO gold medal in 2006, a member of the National Mathematical Olympiad Training Team, and was recommended to Tsinghua University for basic courses. Focus on game technology, good at developing architecture and basic technical facilities.
## License
HybridCLR is licensed under the [MIT](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE) license HybridCLR is licensed under the [MIT](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE) license

88
README_EN.md Normal file
View File

@ -0,0 +1,88 @@
- [README Chinese](./README.md)
- [README English](./README_EN.md)
# HybridCLR
[![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE)
![logo](https://github.com/focus-creative-games/hybridclr/raw/main/docs/images/logo.jpg)
<br/>
<br/>
HybridCLR is a **feature-complete, zero-cost, high-performance, low-memory** **near-perfect** Unity cross-platform native C# hot update solution.
HybridCLR extends the il2cpp runtime code, transforming it from a pure [AOT](https://en.wikipedia.org/wiki/Ahead-of-time_compilation) runtime to an AOT+Interpreter hybrid runtime, thereby natively supporting the dynamic loading of assemblies and fundamentally supporting hot updates from the bottom layer. Games using HybridCLR technology can not only run efficiently on the Android platform but also on all platforms supported by il2cpp, including iOS, Consoles, WebGL, etc.
Thanks to HybridCLR's good support for the ECMA-335 specification and its high compatibility with the Unity development workflow, Unity projects can almost seamlessly gain the ability to hot update C# code after integrating HybridCLR. Developers do not need to change their daily development habits and requirements. HybridCLR is the first to achieve the engineering difficulty of a full-platform code hot update solution for the Unity platform to almost zero.
Welcome to embrace modern native C# hot update technology!
## Documentation
- [Official Documentation](https://www.hybridclr.cn/en/docs/intro)
- [Quick Start](https://www.hybridclr.cn/en/docs/beginner/quickstart)
- [Business Project Cases](https://www.hybridclr.cn/en/docs/other/businesscase)
## Features
- Nearly complete implementation of the [ECMA-335 specification](https://www.ecma-international.org/publications-and-standards/standards/ecma-335/), with only a very small number of [unsupported features](https://www.hybridclr.cn/en/docs/basic/notsupportedfeatures).
- Zero learning and usage costs. For most developers, writing code is almost unrestricted. Hot update code works seamlessly with AOT code, allowing for inheritance, **generics**, **reflection**, and other code without additional special code or code generation.
- Full support for multithreading, including but not limited to volatile, ThreadStatic, async Task, and related features and characteristics. This is not supported by any other hot update solution.
- Almost complete compatibility with Unity's workflow. This includes support for hot updating **MonoBehaviour**, ScriptableObject, **DOTS** technology, and correctly instantiating hot update scripts mounted on resources, which is not supported by any other hot update solution.
- Efficient execution. A highly efficient register interpreter has been implemented, with all indicators significantly better than other hot update solutions. [Performance Test Report](https://www.hybridclr.cn/en/docs/basic/performance)
- Efficient memory usage. Classes defined in hot update scripts occupy the same memory space as ordinary C# classes, far superior to other hot update solutions. [Memory Usage Report](https://www.hybridclr.cn/en/docs/basic/memory)
- Supports MonoPInvokeCallback, enabling good interaction with native code or other languages such as Lua, JavaScript, Python.
- Supports some features not supported by il2cpp, such as __makeref, __reftype, __refvalue instructions.
- Supports the unique **Differential Hybrid Execution (DHE)** technology, which allows for arbitrary additions, deletions, and modifications to AOT DLLs. It intelligently runs unchanged functions in AOT mode and changed or newly added functions in interpreter mode, bringing the performance of hot-updated game logic close to that of native AOT.
- Supports **hot reload** technology, allowing 100% unloading of assemblies.
- Supports modern DLL encryption technology to effectively protect code security.
## Supported Versions and Platforms
- Supports all LTS versions including 2019.4.x, 2020.3.x, 2021.3.x, 2022.3.x, 2023.2.x, 6000.x.y.
- Supports all platforms supported by il2cpp.
- Supports Tuanjie(China) Engine and HarmonyOS platform.
## Working Principle
HybridCLR draws inspiration from Mono's [mixed mode execution](https://www.mono-project.com/news/2017/11/13/mono-interpreter/) technology, providing an interpreter module for AOT runtimes like Unity's il2cpp, transforming them from pure AOT runtimes to "AOT + Interpreter" hybrid operation modes.
![icon](https://github.com/focus-creative-games/hybridclr/raw/main/docs/images/architecture.png)
More specifically, HybridCLR has done the following:
- Implemented an efficient metadata (dll) parsing library.
- Modified the metadata management module to achieve dynamic registration of metadata.
- Implemented a compiler that converts IL instructions to a custom register instruction set.
- Implemented an efficient register interpreter.
- Provided a large number of instinct functions to enhance interpreter performance.
## Stability Status
HybridCLR has been widely verified as an efficient and stable Unity hot update solution, meeting the stability and performance requirements of medium and large commercial projects.
Currently, thousands of commercial game projects have integrated HybridCLR, with over a thousand already launched on the App Store and Google Play. Nearly a hundred of the top 500 free iOS games use HybridCLR, including MMORPGs, heavy card games, and heavy tower defense games. Most of the **Top Game Companies** in China are already using HybridCLR.
You can view the [list of known top companies using HybridCLR and their launched projects](https://www.hybridclr.cn/en/docs/other/businesscase).
## Support and Contact
- Official Group 1: 651188171 (Full)
- Beginner Group 1: 428404198 (Full)
- Beginner Group 2: 680274677 (Full)
- Beginner Group 3: **920714552 (Recommended)**
- Discord channel https://discord.gg/BATfNfJnm2
- Business cooperation email: business#code-philosophy.com
- [Commercial Support](https://www.hybridclr.cn/en/docs/business/intro)
## About the Author
**walon**: Founder of **Code Philosophy (代码哲学)**
Graduated from the Department of Physics at Tsinghua University, gold medalist of the 2006 CMO, member of the National Mathematical Olympiad Training Team, and sent to the Basic Science Class of Tsinghua. Focused on game technology, proficient in development architecture and basic technical infrastructure.
## License
HybridCLR is licensed under the [MIT](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE) license.

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 52688f4469ff9454e8c3d8aaf045d9f4 guid: c9e34f237251ef44193538977db6b15f
TextScriptImporter: TextScriptImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -1,91 +0,0 @@
- [README 中文](./README_zh.md)
- [README English](./README.md)
# HybridCLR
[![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE)
![logo](https://github.com/focus-creative-games/hybridclr/raw/main/docs/images/logo.jpg)
<br/>
<br/>
HybridCLR是一个**特性完整、零成本、高性能、低内存**的**近乎完美**的Unity全平台原生c#热更方案。
HybridCLR扩充了il2cpp的代码使它由纯[AOT](https://en.wikipedia.org/wiki/Ahead-of-time_compilation) runtime变成AOT+Interpreter 混合runtime进而原生支持动态加载assembly使得基于il2cpp backend打包的游戏不仅能在Android平台也能在IOS、Consoles等限制了JIT的平台上高效地以**AOT+interpreter**混合模式执行,从底层彻底支持了热更新。
HybridCLR不仅支持传统的全解释执行模式还开创性地实现了 [Differential Hybrid Execution差分混合执行技术](https://hybridclr.doc.code-philosophy.com/docs/business/differentialhybridexecution) 差分混合执行技术。即可以对AOT dll任意增删改会智能地让变化或者新增的类和函数以interpreter模式运行但未改动的类和函数以AOT方式运行让热更新的游戏逻辑的运行性能基本达到原生AOT的水平。
欢迎拥抱现代原生C#热更新技术
## 文档
- [官方文档](https://hybridclr.doc.code-philosophy.com/docs/intro)
- [快速上手](https://hybridclr.doc.code-philosophy.com/docs/beginner/quickstart)
- [发布日志](./RELEASELOG.md)
## 特性
- 特性完整。 近乎完整实现了[ECMA-335规范](https://www.ecma-international.org/publications-and-standards/standards/ecma-335/),只有极少量的[不支持的特性](https://hybridclr.doc.code-philosophy.com/docs/basic/notsupportedfeatures)。
- 零学习和使用成本。 HybridCLR将纯AOT runtime增强为完整的runtime使得热更新代码与AOT代码无缝工作。脚本类与AOT类在同一个运行时内可以随意写继承、反射、多线程(volatile、ThreadStatic、Task、async)之类的代码。不需要额外写任何特殊代码、没有代码生成,几乎没有限制。
- 执行高效。实现了一个极其高效的寄存器解释器,所有指标都大幅优于其他热更新方案。[性能测试报告](https://hybridclr.doc.code-philosophy.com/docs/basic/performance)
- 内存高效。 热更新脚本中定义的类跟普通c#类占用一样的内存空间,远优于其他热更新方案。[内存与GC](https://hybridclr.doc.code-philosophy.com/docs/basic/memory)
- 由于对泛型的完美支持使得因为AOT泛型问题跟il2cpp不兼容的库现在能够完美地在il2cpp下运行
- 支持一些il2cpp不支持的特性如__makeref、 __reftype、__refvalue指令
- [Differential Hybrid Execution差分混合执行技术](https://hybridclr.doc.code-philosophy.com/docs/business/differentialhybridexecution)
## 工作原理
HybridCLR从mono的 [mixed mode execution](https://www.mono-project.com/news/2017/11/13/mono-interpreter/) 技术中得到启发为unity的il2cpp之类的AOT runtime额外提供了interpreter模块将它们由纯AOT运行时改造为"AOT + Interpreter"混合运行方式。
![icon](https://github.com/focus-creative-games/hybridclr/raw/main/docs/images/architecture.png)
更具体地说HybridCLR做了以下几点工作
- 实现了一个高效的元数据(dll)解析库
- 改造了元数据管理模块,实现了元数据的动态注册
- 实现了一个IL指令集到自定义的寄存器指令集的compiler
- 实现了一个高效的寄存器解释器
- 额外提供大量的instinct函数提升解释器性能
## 与其他流行的c#热更新方案的区别
HybridCLR是原生的c#热更新方案。通俗地说il2cpp相当于mono的aot模块HybridCLR相当于mono的interpreter模块两者合一成为完整mono。HybridCLR使得il2cpp变成一个全功能的runtime原生即通过System.Reflection.Assembly.Load支持动态加载dll从而支持ios平台的热更新。
正因为HybridCLR是原生runtime级别实现热更新部分的类型与主工程AOT部分类型是完全等价并且无缝统一的。可以随意调用、继承、反射、多线程不需要生成代码或者写适配器。
其他热更新方案则是独立vm与il2cpp的关系本质上相当于mono中嵌入lua的关系。因此类型系统不统一为了让热更新类型能够继承AOT部分类型需要写适配器并且解释器中的类型不能为主工程的类型系统所识别。特性不完整、开发麻烦、运行效率低下。
## 支持的版本与平台
- 支持2019.4.x、2020.3.x、2021.3.x、2022.3.x全系列LTS版本。`2023.2.0ax`版本也已支持,但未对外发布。
- 支持所有il2cpp支持的平台
## 稳定性状况
HybridCLR已经被广泛验证是非常高效、稳定的Unity热更新解决方案。
当前发布了**极其稳定**的1.x、2.x、3.x正式版本足以满足大中型商业项目的稳定性要求。
目前至少有上千个商业游戏项目完成接入其中有几百款已经双端上线上线的项目中包括MMORPG、重度卡牌、重度塔防之类的游戏。**绝大多数头部游戏公司**如腾讯、网易都已经在使用HybridCLR。
可查看我们已知的头部公司中使用HybridCLR并且已经上线的[项目列表](https://hybridclr.doc.code-philosophy.com/docs/other/businesscase)。
## 支持与联系
- 官方1群651188171
- 新手1群428404198
- 新手2群**680274677推荐**
- discord频道 https://discord.gg/BATfNfJnm2
- 商业合作邮箱: business#code-philosophy.com
- [商业化支持](https://hybridclr.doc.code-philosophy.com/docs/business/intro)
## 关于作者
**walon** **Code Philosophy代码哲学** 创始人
毕业于清华大学物理系2006年CMO金牌奥数国家集训队成员保送清华基科班。专注于游戏技术擅长开发架构和基础技术设施。
## license
HybridCLR is licensed under the [MIT](https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE) license

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,8 @@ namespace HybridCLR
{ {
public enum HomologousImageMode public enum HomologousImageMode
{ {
Consistent, // AOT dll需要跟主工程精确一致即为裁剪后的AO dll Consistent,
SuperSet, // AOT dll不需要跟主工程精确一致但必须包含裁剪后的AOT dll的所有元数据即为裁剪后dll的超集。推荐使用原始aot dll SuperSet,
} }
} }

View File

@ -4,12 +4,16 @@ namespace HybridCLR
public enum LoadImageErrorCode public enum LoadImageErrorCode
{ {
OK = 0, OK = 0,
BAD_IMAGE, // dll 不合法 BAD_IMAGE, // invalid dll file
NOT_IMPLEMENT, // 不支持的元数据特性 NOT_IMPLEMENT, // not implement feature
AOT_ASSEMBLY_NOT_FIND, // 对应的AOT assembly未找到 AOT_ASSEMBLY_NOT_FIND, // AOT assembly not found
HOMOLOGOUS_ONLY_SUPPORT_AOT_ASSEMBLY, // 不能给解释器assembly补充元数据 HOMOLOGOUS_ONLY_SUPPORT_AOT_ASSEMBLY, // can not load supplementary metadata assembly for non-AOT assembly
HOMOLOGOUS_ASSEMBLY_HAS_LOADED, // 已经补充过了,不能再次补充 HOMOLOGOUS_ASSEMBLY_HAS_LOADED, // can not load supplementary metadata assembly for the same assembly
INVALID_HOMOLOGOUS_MODE, // 非法HomologousImageMode INVALID_HOMOLOGOUS_MODE, // invalid homologous image mode
PDB_BAD_FILE, // invalid pdb file
UNKNOWN_IMAGE_FORMAT,
UNSUPPORT_FORMAT_VERSION,
UNMATCH_FORMAT_VARIANT,
}; };
} }

View File

@ -6,6 +6,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEditor;
using UnityEngine.Scripting; using UnityEngine.Scripting;
namespace HybridCLR namespace HybridCLR
@ -14,7 +15,7 @@ namespace HybridCLR
public static class RuntimeApi public static class RuntimeApi
{ {
/// <summary> /// <summary>
/// 加载补充元数据assembly /// load supplementary metadata assembly
/// </summary> /// </summary>
/// <param name="dllBytes"></param> /// <param name="dllBytes"></param>
/// <returns></returns> /// <returns></returns>
@ -30,7 +31,38 @@ namespace HybridCLR
#endif #endif
/// <summary> /// <summary>
/// 获取解释器线程栈的最大StackObject个数(size*8 为最终占用的内存大小) /// prejit method to avoid the jit cost of first time running
/// </summary>
/// <param name="method"></param>
/// <returns>return true if method is jited, return false if method can't be jited </returns>
///
#if UNITY_EDITOR
public static bool PreJitMethod(MethodInfo method)
{
return false;
}
#else
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool PreJitMethod(MethodInfo method);
#endif
/// <summary>
/// prejit all methods of class to avoid the jit cost of first time running
/// </summary>
/// <param name="type"></param>
/// <returns>return true if class is jited, return false if class can't be jited </returns>
#if UNITY_EDITOR
public static bool PreJitClass(Type type)
{
return false;
}
#else
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool PreJitClass(Type type);
#endif
/// <summary>
/// get the maximum number of StackObjects in the interpreter thread stack (size*8 represents the final memory size occupied
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static int GetInterpreterThreadObjectStackSize() public static int GetInterpreterThreadObjectStackSize()
@ -39,7 +71,7 @@ namespace HybridCLR
} }
/// <summary> /// <summary>
/// 设置解释器线程栈的最大StackObject个数(size*8 为最终占用的内存大小) /// set the maximum number of StackObjects for the interpreter thread stack (size*8 represents the final memory size occupied)
/// </summary> /// </summary>
/// <param name="size"></param> /// <param name="size"></param>
public static void SetInterpreterThreadObjectStackSize(int size) public static void SetInterpreterThreadObjectStackSize(int size)
@ -49,7 +81,7 @@ namespace HybridCLR
/// <summary> /// <summary>
/// 获取解释器线程函数帧数量(sizeof(InterpreterFrame)*size 为最终占用的内存大小) /// get the number of interpreter thread function frames (sizeof(InterpreterFrame)*size represents the final memory size occupied)
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static int GetInterpreterThreadFrameStackSize() public static int GetInterpreterThreadFrameStackSize()
@ -58,7 +90,7 @@ namespace HybridCLR
} }
/// <summary> /// <summary>
/// 设置解释器线程函数帧数量(sizeof(InterpreterFrame)*size 为最终占用的内存大小) /// set the number of interpreter thread function frames (sizeof(InterpreterFrame)*size represents the final memory size occupied)
/// </summary> /// </summary>
/// <param name="size"></param> /// <param name="size"></param>
public static void SetInterpreterThreadFrameStackSize(int size) public static void SetInterpreterThreadFrameStackSize(int size)
@ -71,6 +103,11 @@ namespace HybridCLR
private static readonly Dictionary<RuntimeOptionId, int> s_runtimeOptions = new Dictionary<RuntimeOptionId, int>(); private static readonly Dictionary<RuntimeOptionId, int> s_runtimeOptions = new Dictionary<RuntimeOptionId, int>();
/// <summary>
/// set runtime option value
/// </summary>
/// <param name="optionId"></param>
/// <param name="value"></param>
public static void SetRuntimeOption(RuntimeOptionId optionId, int value) public static void SetRuntimeOption(RuntimeOptionId optionId, int value)
{ {
s_runtimeOptions[optionId] = value; s_runtimeOptions[optionId] = value;
@ -80,6 +117,11 @@ namespace HybridCLR
public static extern void SetRuntimeOption(RuntimeOptionId optionId, int value); public static extern void SetRuntimeOption(RuntimeOptionId optionId, int value);
#endif #endif
/// <summary>
/// get runtime option value
/// </summary>
/// <param name="optionId"></param>
/// <returns></returns>
#if UNITY_EDITOR #if UNITY_EDITOR
public static int GetRuntimeOption(RuntimeOptionId optionId) public static int GetRuntimeOption(RuntimeOptionId optionId)
{ {

View File

@ -5,5 +5,8 @@
InterpreterThreadObjectStackSize = 1, InterpreterThreadObjectStackSize = 1,
InterpreterThreadFrameStackSize = 2, InterpreterThreadFrameStackSize = 2,
ThreadExceptionFlowSize = 3, ThreadExceptionFlowSize = 3,
MaxMethodBodyCacheSize = 4,
MaxMethodInlineDepth = 5,
MaxInlineableMethodBodySize = 6,
} }
} }

View File

@ -1,11 +1,11 @@
{ {
"name": "com.code-philosophy.hybridclr", "name": "com.code-philosophy.hybridclr",
"version": "5.2.1", "version": "8.6.0",
"displayName": "HybridCLR", "displayName": "HybridCLR",
"description": "HybridCLR is a fully featured, zero-cost, high-performance, low-memory solution for Unity's all-platform native c# hotupdate.", "description": "HybridCLR is a fully featured, zero-cost, high-performance, low-memory solution for Unity's all-platform native c# hotupdate.",
"category": "Runtime", "category": "Scripting",
"documentationUrl": "https://hybridclr.doc.code-philosophy.com/#/", "documentationUrl": "https://www.hybridclr.cn/#/",
"changelogUrl": "https://hybridclr.doc.code-philosophy.com/#/other/changelog", "changelogUrl": "https://www.hybridclr.cn/#/other/changelog",
"licensesUrl": "https://github.com/focus-creative-games/hybridclr_unity/blob/main/LICENSE", "licensesUrl": "https://github.com/focus-creative-games/hybridclr_unity/blob/main/LICENSE",
"keywords": [ "keywords": [
"HybridCLR", "HybridCLR",