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

Allow static method injection #95

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
68 changes: 44 additions & 24 deletions Il2CppInterop.Runtime/Injection/ClassInjector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public static void RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options)
classPointer.InstanceSize = (uint)(fieldOffset + sizeof(InjectedClassData));
classPointer.ActualSize = classPointer.InstanceSize;

var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray();
var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray();
var methodsOffset = type.IsAbstract ? 1 : 2; // 1 is the finalizer, 1 is empty ctor
var methodCount = methodsOffset + eligibleMethods.Length;

Expand Down Expand Up @@ -550,7 +550,6 @@ private static bool IsFieldEligible(FieldInfo field)
private static bool IsMethodEligible(MethodInfo method)
{
if (method.Name == "Finalize") return false;
if (method.IsStatic) return false;
if (method.CustomAttributes.Any(it => typeof(HideFromIl2CppAttribute).IsAssignableFrom(it.AttributeType)))
return false;

Expand Down Expand Up @@ -583,10 +582,12 @@ private static bool IsMethodEligible(MethodInfo method)
foreach (var parameter in method.GetParameters())
{
var parameterType = parameter.ParameterType;
if (!IsTypeSupported(parameterType))
if (!IsTypeSupported(parameterType) ||
method.IsStatic && parameterType.IsGenericType && parameterType.ContainsGenericParameters)
Copy link
Member

Choose a reason for hiding this comment

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

What's this check for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't remember exactly, but best guess would be that somewhere there was a static method with one of the parameters being a generic type, and likely that was causing issues.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does it matter here if the method is static or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I mean Il2cpp Interop supports normal methods that are generic. If I don't include static part, those won't work.

This check is here so that only non generic static methods are included.
Most likely I didn't want to bother supporting generic static methods.

{
Logger.Instance.LogWarning(
"Method {Method} on type {DeclaringType} has unsupported parameter {Parameter} of type {ParameterType}", method.ToString(), method.DeclaringType, parameter, parameterType);
"Method {Method} on type {DeclaringType} has unsupported parameter {Parameter} of type {ParameterType}", method.ToString(),
method.DeclaringType, parameter, parameterType);
return false;
}
}
Expand Down Expand Up @@ -726,6 +727,11 @@ private static bool IsMethodEligible(MethodInfo method)
converted.Flags = Il2CppMethodFlags.METHOD_ATTRIBUTE_PUBLIC |
Il2CppMethodFlags.METHOD_ATTRIBUTE_HIDE_BY_SIG;

if (monoMethod.IsStatic)
{
converted.Flags |= Il2CppMethodFlags.METHOD_ATTRIBUTE_STATIC;
}

if (monoMethod.IsAbstract)
{
converted.Flags |= Il2CppMethodFlags.METHOD_ATTRIBUTE_ABSTRACT;
Expand Down Expand Up @@ -812,6 +818,16 @@ private static Delegate GetOrCreateTrampoline(MethodInfo monoMethod)
return CreateTrampoline(monoMethod);
}

private static Type[] GetNativeParameterTypes(MethodInfo monoMethod)
{
var nativeParameterTypes = new List<Type>();
if (!monoMethod.IsStatic)
nativeParameterTypes.Add(typeof(IntPtr));
nativeParameterTypes.AddRange(monoMethod.GetParameters().Select(it => it.ParameterType.NativeType()));
nativeParameterTypes.Add(typeof(Il2CppMethodInfo*));
return nativeParameterTypes.ToArray();
}

private static Delegate CreateInvoker(MethodInfo monoMethod)
{
DynamicMethod method;
Expand All @@ -834,11 +850,12 @@ private static Delegate CreateInvoker(MethodInfo monoMethod)

var body = method.GetILGenerator();

body.Emit(OpCodes.Ldarg_2);
if (!monoMethod.IsStatic)
body.Emit(OpCodes.Ldarg_2); // obj
for (var i = 0; i < monoMethod.GetParameters().Length; i++)
{
var parameterInfo = monoMethod.GetParameters()[i];
body.Emit(OpCodes.Ldarg_3);
body.Emit(OpCodes.Ldarg_3); // args
body.Emit(OpCodes.Ldc_I4, i * IntPtr.Size);
body.Emit(OpCodes.Add_Ovf_Un);
var nativeType = parameterInfo.ParameterType.NativeType();
Expand All @@ -847,10 +864,9 @@ private static Delegate CreateInvoker(MethodInfo monoMethod)
body.Emit(OpCodes.Ldobj, nativeType);
}

body.Emit(OpCodes.Ldarg_0);
body.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, monoMethod.ReturnType.NativeType(),
new[] { typeof(IntPtr) }.Concat(monoMethod.GetParameters().Select(it => it.ParameterType.NativeType()))
.ToArray());
body.Emit(OpCodes.Ldarg_1); // methodMetadata
body.Emit(OpCodes.Ldarg_0); // methodPointer
body.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, monoMethod.ReturnType.NativeType(), GetNativeParameterTypes(monoMethod));

if (UnityVersionHandler.IsMetadataV29OrHigher)
{
Expand Down Expand Up @@ -916,33 +932,37 @@ private static void StaticVoidIntPtrInvoker_MetadataV29(IntPtr methodPointer, Il

private static Delegate CreateTrampoline(MethodInfo monoMethod)
{
var nativeParameterTypes = new[] { typeof(IntPtr) }.Concat(monoMethod.GetParameters()
.Select(it => it.ParameterType.NativeType()).Concat(new[] { typeof(Il2CppMethodInfo*) })).ToArray();

var managedParameters = new[] { monoMethod.DeclaringType }
.Concat(monoMethod.GetParameters().Select(it => it.ParameterType)).ToArray();
var managedParameters = new List<Type>();
if (!monoMethod.IsStatic)
managedParameters.Add(monoMethod.DeclaringType);
managedParameters.AddRange(monoMethod.GetParameters().Select(it => it.ParameterType));

var method = new DynamicMethod(
"Trampoline_" + ExtractSignature(monoMethod) + monoMethod.DeclaringType + monoMethod.Name,
MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard,
monoMethod.ReturnType.NativeType(), nativeParameterTypes,
monoMethod.ReturnType.NativeType(), GetNativeParameterTypes(monoMethod),
monoMethod.DeclaringType, true);

var signature = new DelegateSupport.MethodSignature(monoMethod, true);
var signature = new DelegateSupport.MethodSignature(monoMethod, !monoMethod.IsStatic);
var delegateType = DelegateSupport.GetOrCreateDelegateType(signature, monoMethod);

var body = method.GetILGenerator();

body.BeginExceptionBlock();

body.Emit(OpCodes.Ldarg_0);
body.Emit(OpCodes.Call,
typeof(ClassInjectorBase).GetMethod(nameof(ClassInjectorBase.GetMonoObjectFromIl2CppPointer))!);
body.Emit(OpCodes.Castclass, monoMethod.DeclaringType);
if (!monoMethod.IsStatic)
{
body.Emit(OpCodes.Ldarg_0);
body.Emit(OpCodes.Call,
typeof(ClassInjectorBase).GetMethod(nameof(ClassInjectorBase.GetMonoObjectFromIl2CppPointer))!);
body.Emit(OpCodes.Castclass, monoMethod.DeclaringType);
}

var indirectVariables = new LocalBuilder[managedParameters.Count];

var indirectVariables = new LocalBuilder[managedParameters.Length];
var argOffset = method.IsStatic ? 0 : 1;

for (var i = 1; i < managedParameters.Length; i++)
for (var i = argOffset; i < managedParameters.Count; i++)
{
var parameter = managedParameters[i];
if (parameter.IsSubclassOf(typeof(ValueType)))
Expand Down Expand Up @@ -1012,7 +1032,7 @@ void HandleTypeConversion(Type type)
body.Emit(OpCodes.Stloc, managedReturnVariable);
}

for (var i = 1; i < managedParameters.Length; i++)
for (var i = argOffset; i < managedParameters.Count; i++)
{
var variable = indirectVariables[i];
if (variable == null)
Expand Down
Loading