-
Notifications
You must be signed in to change notification settings - Fork 75
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
||
|
@@ -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; | ||
|
||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does it matter here if the method is static or not? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
{ | ||
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; | ||
} | ||
} | ||
|
@@ -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; | ||
|
@@ -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; | ||
|
@@ -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(); | ||
|
@@ -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) | ||
{ | ||
|
@@ -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))) | ||
|
@@ -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) | ||
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.