Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update method builder #352

Merged
merged 2 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Natasha.CSharp.Extension.MethodCreator
{
public class NatashaSlimMethodBuilder
{
public readonly AssemblyCSharpBuilder Builder;
public readonly string Script;
public readonly HashSet<string> Usings;
public object[]? PrivateObjects;
private Action<NatashaLoadContext>? _ctxConfig;
private Action<AssemblyCSharpBuilder>? _builderConfig;
public NatashaSlimMethodBuilder(string script)
{
Usings = [];
Builder = new AssemblyCSharpBuilder();
Script = script;
}
public NatashaSlimMethodBuilder WithPrivateAccess(params object[] objs)
{
PrivateObjects = objs;
return this;
}

public NatashaSlimMethodBuilder ConfigBuilder(Action<AssemblyCSharpBuilder> config)
{
_builderConfig = config;
return this;
}
public NatashaSlimMethodBuilder ConfigBuilder(Action<NatashaLoadContext> config)
{
_ctxConfig = config;
return this;
}
public NatashaSlimMethodBuilder WithSimpleBuilder()
{
Builder.UseRandomLoadContext();
Builder.UseSimpleMode();
Builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode<object>());
return this;
}

public NatashaSlimMethodBuilder WithUsings(params string[] usings)
{
Usings.UnionWith(usings);
return this;
}

public NatashaSlimMethodBuilder WithMetadata<T>()
{
Builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode<T>());
return this;
}

public NatashaSlimMethodBuilder WithMetadata(params Type[] types)
{
if (types!=null && types.Length > 0)
{
foreach (var type in types)
{
Builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(type));
}
}
return this;
}

public T ToDelegate<T>(string modifier = "") where T : Delegate
{
_builderConfig?.Invoke(Builder);
_ctxConfig?.Invoke(Builder.LoadContext);

var className = $"N{Guid.NewGuid():N}";
var methodInfo = typeof(T).GetMethod("Invoke")!;

var returnTypeScript = methodInfo.ReturnType.GetDevelopName();
var parameterScript = new StringBuilder();

var methodParameters = methodInfo.GetParameters();
for (int i = 0; i < methodParameters.Length; i += 1)
{
var paramType = methodParameters[i].ParameterType;
Builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(paramType));
parameterScript.Append($"{paramType.GetDevelopName()} arg{i + 1},");
}
if (parameterScript.Length > 0)
{
parameterScript.Length -= 1;
}

StringBuilder usingCode = new();
foreach (var item in Usings)
{

usingCode.AppendLine($"using {item};");

}
var fullScript = $"{usingCode} public static class {className} {{ public static {(modifier ?? string.Empty)} {returnTypeScript} Invoke({parameterScript}){{ {Script} }} }}";
if (PrivateObjects!=null)
{
Builder.Add(fullScript.ToAccessPrivateTree(PrivateObjects));
}
else
{
Builder.Add(fullScript);
}
var asm = Builder.GetAssembly();
var type = asm.GetType(className);
if (type != null)
{
return (T)Delegate.CreateDelegate(typeof(T), type.GetMethod("Invoke")!);
}
throw new Exception($"未找到 {className} 类型!");
}
Comment on lines +69 to +115
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential null reference for methodInfo.

The methodInfo variable should be checked for null before accessing its properties.

-  var methodInfo = typeof(T).GetMethod("Invoke")!;
+  var methodInfo = typeof(T).GetMethod("Invoke");
+  if (methodInfo == null)
+  {
+      throw new InvalidOperationException($"The delegate type {typeof(T)} does not have an Invoke method.");
+  }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public T ToDelegate<T>(string modifier = "") where T : Delegate
{
_builderConfig?.Invoke(Builder);
_ctxConfig?.Invoke(Builder.LoadContext);
var className = $"N{Guid.NewGuid():N}";
var methodInfo = typeof(T).GetMethod("Invoke")!;
var returnTypeScript = methodInfo.ReturnType.GetDevelopName();
var parameterScript = new StringBuilder();
var methodParameters = methodInfo.GetParameters();
for (int i = 0; i < methodParameters.Length; i += 1)
{
var paramType = methodParameters[i].ParameterType;
Builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(paramType));
parameterScript.Append($"{paramType.GetDevelopName()} arg{i + 1},");
}
if (parameterScript.Length > 0)
{
parameterScript.Length -= 1;
}
StringBuilder usingCode = new();
foreach (var item in Usings)
{
usingCode.AppendLine($"using {item};");
}
var fullScript = $"{usingCode} public static class {className} {{ public static {(modifier ?? string.Empty)} {returnTypeScript} Invoke({parameterScript}){{ {Script} }} }}";
if (PrivateObjects!=null)
{
Builder.Add(fullScript.ToAccessPrivateTree(PrivateObjects));
}
else
{
Builder.Add(fullScript);
}
var asm = Builder.GetAssembly();
var type = asm.GetType(className);
if (type != null)
{
return (T)Delegate.CreateDelegate(typeof(T), type.GetMethod("Invoke")!);
}
throw new Exception($"未找到 {className} 类型!");
}
public T ToDelegate<T>(string modifier = "") where T : Delegate
{
_builderConfig?.Invoke(Builder);
_ctxConfig?.Invoke(Builder.LoadContext);
var className = $"N{Guid.NewGuid():N}";
var methodInfo = typeof(T).GetMethod("Invoke");
if (methodInfo == null)
{
throw new InvalidOperationException($"The delegate type {typeof(T)} does not have an Invoke method.");
}
var returnTypeScript = methodInfo.ReturnType.GetDevelopName();
var parameterScript = new StringBuilder();
var methodParameters = methodInfo.GetParameters();
for (int i = 0; i < methodParameters.Length; i += 1)
{
var paramType = methodParameters[i].ParameterType;
Builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(paramType));
parameterScript.Append($"{paramType.GetDevelopName()} arg{i + 1},");
}
if (parameterScript.Length > 0)
{
parameterScript.Length -= 1;
}
StringBuilder usingCode = new();
foreach (var item in Usings)
{
usingCode.AppendLine($"using {item};");
}
var fullScript = $"{usingCode} public static class {className} {{ public static {(modifier ?? string.Empty)} {returnTypeScript} Invoke({parameterScript}){{ {Script} }} }}";
if (PrivateObjects!=null)
{
Builder.Add(fullScript.ToAccessPrivateTree(PrivateObjects));
}
else
{
Builder.Add(fullScript);
}
var asm = Builder.GetAssembly();
var type = asm.GetType(className);
if (type != null)
{
return (T)Delegate.CreateDelegate(typeof(T), type.GetMethod("Invoke")!);
}
throw new Exception($"未找到 {className} 类型!");
}

public T ToAsyncDelegate<T>() where T : Delegate
{
return ToDelegate<T>("async");
}
public T ToUnsafeDelegate<T>() where T : Delegate
{
return ToDelegate<T>("unsafe");
}
public T ToUnsafeAsyncDelegate<T>() where T : Delegate
{
return ToDelegate<T>("unsafe async");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,95 +1,31 @@
using System;
using Natasha.CSharp.Extension.MethodCreator;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

public static class StringExtension
{

public static (string script, string[] usings, AssemblyCSharpBuilder builder) WithAssemblyBuilder(this string script, Func<AssemblyCSharpBuilder, AssemblyCSharpBuilder> config)
public static NatashaSlimMethodBuilder WithSlimMethodBuilder(this string script, Action<AssemblyCSharpBuilder> config)
{
AssemblyCSharpBuilder builder = new();
config?.Invoke(builder);
return (script, [], builder);
var builder = new NatashaSlimMethodBuilder(script);
builder.ConfigBuilder(config);
return builder;
}


public static (string script, string[] usings, AssemblyCSharpBuilder builder) WithAssemblyBuilder(this string script, Func<NatashaLoadContext, NatashaLoadContext> config)
{
AssemblyCSharpBuilder builder = new();
builder.ConfigLoadContext(config);
return (script, [], builder);
}

public static (string script, string[] usings, AssemblyCSharpBuilder builder) WithSimpleBuilder(this string script)
{
AssemblyCSharpBuilder builder = new();
builder.UseRandomLoadContext();
builder.UseSimpleMode();
builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode<object>());
return (script, [], builder);
}

public static (string script, string[] usings, AssemblyCSharpBuilder builder) WithMetadata<T>(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo)
{
return (buildInfo.script, buildInfo.usings, buildInfo.builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(typeof(T))));
}
public static (string script, string[] usings, AssemblyCSharpBuilder builder) WithMetadata(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo, Type type)
public static NatashaSlimMethodBuilder WithSlimMethodBuilder(this string script, Action<NatashaLoadContext> config)
{
return (buildInfo.script, buildInfo.usings, buildInfo.builder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(type)));
var builder = new NatashaSlimMethodBuilder(script);
builder.ConfigBuilder(config);
return builder;
}
public static (string script, string[] usings, AssemblyCSharpBuilder builder) WithUsings(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo,params string[] usings)
{
return (buildInfo.script, usings, buildInfo.builder);
}
public static T ToDelegate<T>(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo, string modifier = "") where T: Delegate
{
var scriptBuilder = buildInfo.builder;
var className = $"N{Guid.NewGuid():N}";
var methodInfo = typeof(T).GetMethod("Invoke")!;

var returnTypeScript = methodInfo.ReturnType.GetDevelopName();
var parameterScript = new StringBuilder();

var methodParameters = methodInfo.GetParameters();
for (int i = 0; i < methodParameters.Length; i+=1)
{
var paramType = methodParameters[i].ParameterType;
scriptBuilder.ConfigLoadContext(ctx => ctx.AddReferenceAndUsingCode(paramType));
parameterScript.Append($"{paramType.GetDevelopName()} arg{i+1},");
}
if (parameterScript.Length > 0)
{
parameterScript.Length -= 1;
}

StringBuilder usingCode = new();
foreach (var item in buildInfo.usings)
{

usingCode.AppendLine($"using {item};");

}
buildInfo.builder.Add($"{usingCode} public static class {className} {{ public static {(modifier ?? string.Empty)} {returnTypeScript} Invoke({parameterScript}){{ {buildInfo.script} }} }}");
var asm = buildInfo.builder.GetAssembly();
var type = asm.GetType(className);
if (type != null)
{
return (T)Delegate.CreateDelegate(typeof(T), type.GetMethod("Invoke")!);
}
throw new Exception($"未找到 {className} 类型!");
}
public static T ToAsyncDelegate<T>(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo) where T : Delegate
{
return ToDelegate<T>(buildInfo, "async");
}
public static T ToUnsafeDelegate<T>(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo) where T : Delegate
{
return ToDelegate<T>(buildInfo, "unsafe");
}
public static T ToUnsafeAsyncDelegate<T>(in this (string script, string[] usings, AssemblyCSharpBuilder builder) buildInfo) where T : Delegate
public static NatashaSlimMethodBuilder WithSlimMethodBuilder(this string script)
{
return ToDelegate<T>(buildInfo, "unsafe async");
var builder = new NatashaSlimMethodBuilder(script);
builder.WithSimpleBuilder();
return builder;
}
}

Loading