[new] MonoPInvokeCallback支持任意签名的函数

[refactor] 重构模板文件,除去重复。统一调整生成文件的输出目录到 hybridclr/generated
main
walon 2022-10-17 21:38:39 +08:00
parent 9ea240ab82
commit d8e155dc25
24 changed files with 204 additions and 279 deletions

View File

@ -1,5 +1,3 @@
#include "MethodBridge.h"
#include <codegen/il2cpp-codegen-metadata.h>
#include "vm/ClassInlines.h"
#include "vm/Object.h"
@ -8,14 +6,15 @@
#include "../metadata/MetadataModule.h"
#include "../metadata/MetadataUtil.h"
#include "Interpreter.h"
#include "MemoryUtil.h"
#include "InstrinctDef.h"
#include "../interpreter/MethodBridge.h"
#include "../interpreter/Interpreter.h"
#include "../interpreter/MemoryUtil.h"
#include "../interpreter/InstrinctDef.h"
using namespace hybridclr::interpreter;
#if HYBRIDCLR_ABI_ARM_64
//!!!{{INVOKE_STUB
#if {PLATFORM_ABI}
//!!!{{CODE
//!!!}}INVOKE_STUB
//!!!}}CODE
#endif

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 1249fb131a848a14f8dd0938e3cc2558
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: e06e14385d246aa4ab853d3fd04d6eec
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 2965fa89651ee0c4eb4c4fdcf95b76b8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
#include "../metadata/ReversePInvokeMethodStub.h"
#include "../metadata/MetadataModule.h"
namespace hybridclr
{
namespace metadata
{
#if {PLATFORM_ABI}
//!!!{{CODE
void __ReversePInvokeMethod_0(void* xState)
{
CallLuaFunction(xState, 0);
}
void __ReversePInvokeMethod_1(void* xState)
{
CallLuaFunction(xState, 1);
}
Il2CppMethodPointer s_ReversePInvokeMethodStub[]
{
(Il2CppMethodPointer)__ReversePInvokeMethod_0,
(Il2CppMethodPointer)__ReversePInvokeMethod_1,
nullptr,
};
//!!!}}CODE
#endif
}
}

View File

@ -1,37 +0,0 @@
#include "ReversePInvokeMethodStub.h"
#include "MetadataModule.h"
namespace hybridclr
{
namespace metadata
{
void CallLuaFunction(void* xState, int32_t wrapperIndex)
{
const MethodInfo* method = MetadataModule::GetMethodInfoByReversePInvokeWrapperIndex(wrapperIndex);
typedef void (*Callback)(void* xState, const MethodInfo* method);
((Callback)(method->methodPointerCallByInterp))(xState, method);
}
//!!!{{REVERSE_PINVOKE_METHOD_STUB
void __ReversePInvokeMethod_0(void* xState)
{
CallLuaFunction(xState, 0);
}
void __ReversePInvokeMethod_1(void* xState)
{
CallLuaFunction(xState, 1);
}
Il2CppMethodPointer s_ReversePInvokeMethodStub[]
{
(Il2CppMethodPointer)__ReversePInvokeMethod_0,
(Il2CppMethodPointer)__ReversePInvokeMethod_1,
nullptr,
};
//!!!}}REVERSE_PINVOKE_METHOD_STUB
}
}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6447cdc1825eeef49a3679545ac31d95
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

22
Editor/ABI/ABIUtil.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HybridCLR.Editor.ABI
{
public static class ABIUtil
{
public static string GetHybridCLRPlatformMacro(PlatformABI abi)
{
switch(abi)
{
case PlatformABI.Arm64: return "HYBRIDCLR_ABI_ARM_64";
case PlatformABI.Universal64: return "HYBRIDCLR_ABI_UNIVERSAL_64";
case PlatformABI.Universal32: return "HYBRIDCLR_ABI_UNIVERSAL_32";
default: throw new NotSupportedException();
}
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 69e748de769c24948afd4f8a9df82b8c
guid: edeb86f4b5b13ca4cb0fe9d87ce509bb
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -11,20 +11,21 @@ namespace HybridCLR.Editor.ABI
{
public class MethodDesc : IEquatable<MethodDesc>
{
public string Sig { get; private set; }
public MethodDef MethodDef { get; set; }
public ReturnInfo ReturnInfo { get; set; }
public List<ParamInfo> ParamInfos { get; set; }
private int _hashCode;
public void Init()
{
for(int i = 0; i < ParamInfos.Count; i++)
{
ParamInfos[i].Index = i;
}
Sig = CreateCallSigName();
}
public void TransfromSigTypes(Func<TypeInfo, bool, TypeInfo> transformer)
@ -65,45 +66,12 @@ namespace HybridCLR.Editor.ABI
public bool Equals(MethodDesc other)
{
if (other == null)
{
return false;
}
if (!ReturnInfo.Type.Equals(other.ReturnInfo.Type))
{
return false;
}
if (ParamInfos.Count != other.ParamInfos.Count)
{
return false;
}
for(int i = 0; i < ParamInfos.Count; i++)
{
if (!ParamInfos[i].Type.Equals(other.ParamInfos[i].Type))
{
return false;
}
}
return true;
return Sig == other.Sig;
}
public override int GetHashCode()
{
if (_hashCode != 0)
{
return _hashCode;
}
int hash = 17;
hash = hash * 23 + ReturnInfo.Type.GetHashCode();
foreach(var p in ParamInfos)
{
hash = hash * 23 + p.Type.GetHashCode();
}
return _hashCode = hash;
return Sig.GetHashCode();
}
}
}

View File

@ -28,20 +28,6 @@ namespace HybridCLR.Editor.Commands
Directory.Delete(il2cppBuildCachePath, true);
}
private static string GetTemplateCode(PlatformABI platform)
{
string tplFile;
switch (platform)
{
case PlatformABI.Universal32: tplFile = "Universal32"; break;
case PlatformABI.Universal64: tplFile = "Universal64"; break;
case PlatformABI.Arm64: tplFile = "Arm64"; break;
default: throw new NotSupportedException();
};
return File.ReadAllText($"{SettingsUtil.TemplatePathInPackage}/MethodBridge_{tplFile}.cpp.txt");
}
private static void GenerateMethodBridgeCppFile(Analyzer analyzer, PlatformABI platform, string templateCode, string outputFile)
{
var g = new Generator(new Generator.Options()
@ -84,18 +70,11 @@ namespace HybridCLR.Editor.Commands
analyzer.Run();
var generateJobs = new List<(PlatformABI, string)>()
{
(PlatformABI.Arm64, "MethodBridge_Arm64"),
(PlatformABI.Universal64, "MethodBridge_Universal64"),
(PlatformABI.Universal32, "MethodBridge_Universal32"),
};
var tasks = new List<Task>();
foreach (var (platform, stubFile) in generateJobs)
string templateCode = File.ReadAllText($"{SettingsUtil.TemplatePathInPackage}/MethodBridgeStub.cpp");
foreach (PlatformABI platform in Enum.GetValues(typeof(PlatformABI)))
{
string templateCode = GetTemplateCode(platform);
string outputFile = $"{SettingsUtil.MethodBridgeCppDir}/{stubFile}.cpp";
string outputFile = $"{SettingsUtil.GeneratedCppDir}/MethodBridge_{platform}.cpp";
tasks.Add(Task.Run(() =>
{
GenerateMethodBridgeCppFile(analyzer, platform, templateCode, outputFile);

View File

@ -1,6 +1,7 @@
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;
@ -24,28 +25,21 @@ namespace HybridCLR.Editor.Commands
using (var cache = new AssemblyCache(MetaUtil.CreateBuildTargetAssemblyResolver(EditorUserBuildSettings.activeBuildTarget)))
{
var analyzer = new ReversePInvokeWrap.Analyzer(cache, SettingsUtil.HotUpdateAssemblyNames);
var methods = analyzer.CollectMonoPInvokeCallbackMethods();
foreach (var method in methods)
{
Debug.Log($"method:{method.Method}");
}
analyzer.Run();
var generateJobs = new List<(PlatformABI, string)>()
{
(PlatformABI.Arm64, "HYBRIDCLR_ABI_ARM_64"),
(PlatformABI.Universal64, "HYBRIDCLR_ABI_UNIVERSAL_64"),
(PlatformABI.Universal32, "HYBRIDCLR_ABI_UNIVERSAL_32"),
};
}
return;
//string ReversePInvokeWrapperStubFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/metadata/ReversePInvokeMethodStub.cpp";
//string wrapperTemplateStr = File.ReadAllText($"{SettingsUtil.TemplatePathInPackage}/ReversePInvokeMethodStub.cpp.txt");
//int wrapperCount = SettingsUtil.HybridCLRSettings.ReversePInvokeWrapperCount;
//var generator = new Generator();
//generator.Generate(wrapperTemplateStr, wrapperCount,ReversePInvokeWrapperStubFile);
//Debug.Log($"GenerateReversePInvokeWrapper. wraperCount:{wrapperCount} output:{ReversePInvokeWrapperStubFile}");
//MethodBridgeGeneratorCommand.CleanIl2CppBuildCache();
string templateCode = File.ReadAllText($"{SettingsUtil.TemplatePathInPackage}/ReversePInvokeMethodStub.cpp");
foreach (PlatformABI abi in Enum.GetValues(typeof(PlatformABI)))
{
string outputFile = $"{SettingsUtil.GeneratedCppDir}/ReversePInvokeMethodStub_{abi}.cpp";
List<ABIReversePInvokeMethodInfo> methods = analyzer.BuildABIMethods(abi);
Debug.Log($"GenerateReversePInvokeWrapper. abi:{abi} wraperCount:{methods.Sum(m => m.Count)} output:{outputFile}");
var generator = new Generator();
generator.Generate(templateCode, abi, methods, outputFile);
}
}
MethodBridgeGeneratorCommand.CleanIl2CppBuildCache();
}
}
}

View File

@ -30,6 +30,8 @@ namespace HybridCLR.Editor.MethodBridge
public IReadOnlyCollection<GenericMethod> GenericMethods { get; set; }
}
private PlatformABI _platformABI;
private readonly IReadOnlyList<MethodDef> _notGenericMethods;
private readonly IReadOnlyCollection<GenericMethod> _genericMethods;
@ -56,6 +58,7 @@ namespace HybridCLR.Editor.MethodBridge
public Generator(Options options)
{
_platformABI = options.PlatformABI;
_notGenericMethods = options.NotGenericMethods;
_genericMethods = options.GenericMethods;
_templateCode = options.TemplateCode;
@ -97,27 +100,21 @@ namespace HybridCLR.Editor.MethodBridge
}
private void AddManaged2NativeMethod(MethodDesc method)
{
if (_managed2nativeMethodSet.Add(method))
{
method.Init();
}
_managed2nativeMethodSet.Add(method);
}
private void AddNative2ManagedMethod(MethodDesc method)
{
if (_native2managedMethodSet.Add(method))
{
method.Init();
}
_native2managedMethodSet.Add(method);
}
private void AddAdjustThunkMethod(MethodDesc method)
{
if (_adjustThunkMethodSet.Add(method))
{
method.Init();
}
_adjustThunkMethodSet.Add(method);
}
private void ProcessMethod(MethodDef method, List<TypeSig> klassInst, List<TypeSig> methodInst)
@ -200,7 +197,7 @@ namespace HybridCLR.Editor.MethodBridge
public void Generate()
{
var frr = new FileRegionReplace(_templateCode);
var frr = new FileRegionReplace(_templateCode.Replace("{PLATFORM_ABI}", ABIUtil.GetHybridCLRPlatformMacro(_platformABI)));
List<string> lines = new List<string>(20_0000);
@ -228,7 +225,7 @@ namespace HybridCLR.Editor.MethodBridge
_platformAdaptor.GenerateAdjustThunkStub(_adjustThunkMethodList, lines);
frr.Replace("INVOKE_STUB", string.Join("\n", lines));
frr.Replace("CODE", string.Join("\n", lines));
Directory.CreateDirectory(Path.GetDirectoryName(_outputFile));

View File

@ -36,7 +36,7 @@ Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] =
lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", __M2N_{method.CreateInvokeSigName()}}},");
}
lines.Add($"\t{{nullptr, nullptr}},");
lines.Add($"\t{{\"\", nullptr}},");
lines.Add("};");
}

View File

@ -1,4 +1,5 @@
using dnlib.DotNet;
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Meta;
using System;
using System.Collections.Generic;
@ -9,18 +10,27 @@ using UnityEngine;
namespace HybridCLR.Editor.ReversePInvokeWrap
{
public class ReversePInvokeMethodInfo
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)
@ -29,10 +39,9 @@ namespace HybridCLR.Editor.ReversePInvokeWrap
}
}
public List<ReversePInvokeMethodInfo> CollectMonoPInvokeCallbackMethods()
private void CollectReversePInvokeMethods()
{
var wrapperMethods = new List<ReversePInvokeMethodInfo>();
foreach(var mod in _rootModules)
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++)
@ -52,14 +61,48 @@ namespace HybridCLR.Editor.ReversePInvokeWrap
//{
// Debug.Log($"{ca.AttributeType.FullName} {ca.TypeFullName}");
//}
wrapperMethods.Add(new ReversePInvokeMethodInfo()
_reversePInvokeMethods.Add(new RawReversePInvokeMethodInfo()
{
Method = method,
GenerationAttribute = method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "HybridCLR.ReversePInvokeWrapperGenerationAttribute"),
});
}
}
return wrapperMethods;
}
public List<ABIReversePInvokeMethodInfo> BuildABIMethods(PlatformABI abi)
{
var methodsBySig = new Dictionary<string, ABIReversePInvokeMethodInfo>();
var typeCreator = TypeCreatorFactory.CreateTypeCreator(abi);
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) => a.Method.Sig.CompareTo(b.Method.Sig));
return methods;
}
public void Run()
{
CollectReversePInvokeMethods();
}
}
}

View File

@ -1,45 +1,56 @@
using HybridCLR.Editor.Template;
using HybridCLR.Editor.ABI;
using HybridCLR.Editor.Template;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace HybridCLR.Editor.ReversePInvokeWrap
{
public class Generator
{
public void Generate(string template, int wrapperCount, string outputFile)
public void Generate(string template, PlatformABI abi, List<ABIReversePInvokeMethodInfo> methods, string outputFile)
{
template = template.Replace("{PLATFORM_ABI}", ABIUtil.GetHybridCLRPlatformMacro(abi));
var frr = new FileRegionReplace(template);
var codes = new List<string>();
for(int i = 0; i < wrapperCount; i++)
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}"));
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($@"
void __ReversePInvokeMethod_{i}(void* xState)
void __ReversePInvokeMethod_{methodIndex}({paramDeclaringListWithoutMethodInfoStr})
{{
CallLuaFunction(xState, {i});
const MethodInfo* method = MetadataModule::GetMethodInfoByReversePInvokeWrapperIndex({i});
{methodTypeDef};
((Callback)(method->methodPointerCallByInterp))({paramNameListWithoutMethodInfoStr}, method);
}}
");
stubCodes.Add($"\t\t{{\"{method.Sig}\", (Il2CppMethodPointer)__ReversePInvokeMethod_{methodIndex}}},\n");
}
Debug.Log($"[ReversePInvokeWrap.Generator] method:{method.MethodDef} wrapperCount:{methodInfo.Count}");
}
codes.Add(@"
Il2CppMethodPointer s_ReversePInvokeMethodStub[]
ReversePInvokeMethodData g_reversePInvokeMethodStub[]
{
");
for(int i = 0; i < wrapperCount; i++)
{
codes.Add($"\t\t(Il2CppMethodPointer)__ReversePInvokeMethod_{i},\n");
}
codes.AddRange(stubCodes);
codes.Add(@"
nullptr,
{nullptr, nullptr},
};
");
frr.Replace("REVERSE_PINVOKE_METHOD_STUB", string.Join("", codes));
frr.Replace("CODE", string.Join("", codes));
frr.Commit(outputFile);
}
}

View File

@ -18,7 +18,6 @@ namespace HybridCLR.Editor
private SerializedProperty m_OutputLinkFile;
private SerializedProperty m_OutputAOTGenericReferenceFile;
private SerializedProperty m_MaxGenericReferenceIteration;
private SerializedProperty m_ReversePInvokeWrapperCount;
private SerializedProperty m_MaxMethodBridgeGenericIteration;
private GUIStyle buttonStyle;
public HybridCLRSettingsProvider() : base("Project/HybridCLR Settings", SettingsScope.Project) { }
@ -37,7 +36,6 @@ namespace HybridCLR.Editor
m_OutputLinkFile = m_SerializedObject.FindProperty("outputLinkFile");
m_OutputAOTGenericReferenceFile = m_SerializedObject.FindProperty("outputAOTGenericReferenceFile");
m_MaxGenericReferenceIteration = m_SerializedObject.FindProperty("maxGenericReferenceIteration");
m_ReversePInvokeWrapperCount = m_SerializedObject.FindProperty("ReversePInvokeWrapperCount");
m_MaxMethodBridgeGenericIteration = m_SerializedObject.FindProperty("maxMethodBridgeGenericIteration");
}
public override void OnTitleBarGUI()
@ -110,7 +108,6 @@ namespace HybridCLR.Editor
EditorGUILayout.PropertyField(m_OutputLinkFile);
EditorGUILayout.PropertyField(m_OutputAOTGenericReferenceFile);
EditorGUILayout.PropertyField(m_MaxGenericReferenceIteration);
EditorGUILayout.PropertyField(m_ReversePInvokeWrapperCount);
EditorGUILayout.PropertyField(m_MaxMethodBridgeGenericIteration);
if (EditorGUI.EndChangeCheck())
{

View File

@ -32,8 +32,8 @@ namespace HybridCLR.Editor
[Header("AOT泛型实例化搜索迭代次数")]
public int maxGenericReferenceIteration = 10;
[Header("预留MonoPInvokeCallbackAttribute函数个数")]
public int ReversePInvokeWrapperCount = 10;
//[Header("预留MonoPInvokeCallbackAttribute函数个数")]
//public int ReversePInvokeWrapperCount = 10;
[Header("MethodBridge泛型搜索迭代次数")]
public int maxMethodBridgeGenericIteration = 10;

View File

@ -37,7 +37,7 @@ namespace HybridCLR.Editor
public static string LocalIl2CppDir => $"{LocalUnityDataDir}/il2cpp";
public static string MethodBridgeCppDir => $"{LocalIl2CppDir}/libil2cpp/hybridclr/interpreter";
public static string GeneratedCppDir => $"{LocalIl2CppDir}/libil2cpp/hybridclr/generated";
public static string Il2CppBuildCacheDir { get; } = $"{ProjectDir}/Library/Il2cppBuildCache";

View File

@ -31,16 +31,44 @@ namespace HybridCLR.Editor.Template
foreach (var c in _regionReplaceContents)
{
resultContent = TemplateUtil.ReplaceRegion(resultContent, c.Key, c.Value);
resultContent = ReplaceRegion(resultContent, c.Key, c.Value);
}
return resultContent;
}
public void Commit(string outputFile)
{
string dir = Path.GetDirectoryName(outputFile);
Directory.CreateDirectory(dir);
string resultContent = GenFinalString();
var utf8WithoutBOM = new System.Text.UTF8Encoding(false);
File.WriteAllText(outputFile, resultContent, utf8WithoutBOM);
}
public static string ReplaceRegion(string resultText, string region, string replaceContent)
{
int startIndex = resultText.IndexOf("//!!!{{" + region);
if (startIndex == -1)
{
throw new Exception($"region:{region} start not find");
}
int endIndex = resultText.IndexOf("//!!!}}" + region);
if (endIndex == -1)
{
throw new Exception($"region:{region} end not find");
}
int replaceStart = resultText.IndexOf('\n', startIndex);
int replaceEnd = resultText.LastIndexOf('\n', endIndex);
if (replaceStart == -1 || replaceEnd == -1)
{
throw new Exception($"region:{region} not find");
}
if (resultText.Substring(replaceStart, replaceEnd - replaceStart) == replaceContent)
{
return resultText;
}
resultText = resultText.Substring(0, replaceStart) + "\n" + replaceContent + "\n" + resultText.Substring(replaceEnd);
return resultText;
}
}
}

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "com.focus-creative-games.hybridclr_unity",
"version": "0.4.5",
"version": "0.5.0",
"displayName": "HybridCLR",
"description": "Unity package for HybridCLR. It includes editor and runtime scripts and assets for HybridCLR",
"category": "Runtime",