bluebonnet/Baselib/src/System/Reflection/RuntimeMethodInfo.cs

432 lines
17 KiB
C#
Raw Normal View History

2020-08-26 11:23:24 +03:00
using System;
using System.Reflection;
using System.Globalization;
using System.Runtime.Serialization;
namespace system.reflection
{
[System.Serializable]
public sealed class RuntimeMethodInfo : MethodInfo, ISerializable
{
#pragma warning disable 0436
[java.attr.RetainType] public java.lang.reflect.Method JavaMethod;
#pragma warning restore 0436
[java.attr.RetainType] public string originalName;
[java.attr.RetainType] public string strippedName;
[java.attr.RetainType] public system.RuntimeType reflectedType;
[java.attr.RetainType] public object[] typeArguments;
[java.attr.RetainType] public int genericFlags;
const int flgGenericMethod = 0x10;
const int flgGenericMethodDefinition = 0x20;
const int flgContainsGenericParameters = 0x40;
const int flgCombineGenericArguments = 0x80;
//
// GetMethod (called by system.RuntimeType.GetMethodImpl)
//
public static MethodInfo GetMethod(string name, BindingFlags bindingAttr,
Binder binder, CallingConventions callConvention,
Type[] types, ParameterModifier[] modifiers,
RuntimeType initialType)
{
//
// validate parameters
//
if (name == null)
throw new ArgumentNullException("name");
if (binder != null)
throw new PlatformNotSupportedException("non-null binder");
if (callConvention != CallingConventions.Any)
throw new PlatformNotSupportedException("calling convention must be Any");
if (types != null)
throw new PlatformNotSupportedException("non-null types");
if (modifiers != null)
throw new PlatformNotSupportedException("non-null modifiers");
//
// calculate modifier AND mask and result for matches
//
int modifierMask = 0;
int modifierValue = 0;
BindingFlags chk = bindingAttr & (BindingFlags.Public | BindingFlags.NonPublic);
if (chk != (BindingFlags.Public | BindingFlags.NonPublic))
{
if (chk == 0) // if neither, no methods will match
return null;
// methods with internal access are converted to public access,
// so we cannot honor the distinction between Public and NonPublic
/*
modifierMask |= java.lang.reflect.Modifier.PUBLIC;
if (chk == BindingFlags.Public)
modifierValue |= java.lang.reflect.Modifier.PUBLIC;
*/
bindingAttr &= ~chk;
}
chk = bindingAttr & (BindingFlags.Static | BindingFlags.Instance);
if (chk != (BindingFlags.Static | BindingFlags.Instance))
{
if (chk == 0) // if neither, no methods will match
return null;
modifierMask |= java.lang.reflect.Modifier.STATIC;
if (chk == BindingFlags.Static)
modifierValue |= java.lang.reflect.Modifier.STATIC;
bindingAttr &= ~chk;
}
RuntimeType stopAtType = null;
if ((bindingAttr & BindingFlags.DeclaredOnly) != 0)
{
stopAtType = initialType;
bindingAttr &= ~BindingFlags.DeclaredOnly;
}
if (bindingAttr != 0)
throw new PlatformNotSupportedException("bad binding flags " + bindingAttr);
//
// collect methods from class and base classes
//
var currentType = initialType;
for (;;)
{
#pragma warning disable 0436
java.lang.reflect.Method[] javaMethods =
(java.lang.reflect.Method[]) (object)
currentType.JavaClassForArray().getDeclaredMethods();
#pragma warning restore 0436
foreach (var javaMethod in javaMethods)
{
int jmodifiers = javaMethod.getModifiers();
if ((jmodifiers & modifierMask) == modifierValue)
{
string originalName = javaMethod.getName();
// note the actual suffix character below is configured
// in CilMain.cs, with special considerations for Android.
// we list all possible characters here, just in case.
int idx = originalName.IndexOf('\u00AB'); // U+00AB Left-Pointing Double Angle Quotation Mark
if (idx == -1)
idx = originalName.IndexOf('\u00A1'); // U+00A1 Inverted Exclamation Mark
if (idx == -1)
idx = originalName.IndexOf('(');
if (idx == -1)
idx = originalName.IndexOf('!');
var compareName =
(idx == -1) ? originalName : originalName.Substring(0, idx);
if (name == compareName)
{
javaMethod.setAccessible(true);
return new RuntimeMethodInfo(javaMethod, jmodifiers, initialType,
originalName, compareName);
}
}
}
currentType = (system.RuntimeType) currentType.BaseType;
if (currentType == stopAtType)
break;
}
return null;
}
//
// constructor
//
#pragma warning disable 0436
private RuntimeMethodInfo(java.lang.reflect.Method javaMethod, int modifiers,
system.RuntimeType reflectedType,
string originalName, string strippedName)
#pragma warning restore 0436
{
this.JavaMethod = javaMethod;
this.reflectedType = reflectedType;
this.originalName = originalName;
this.strippedName = strippedName;
if (modifiers == -1) // if called from MakeGenericMethod
return;
// analyze the method and the declaring type in order to decide what
// type of a generic method this is. the general idea is:
// if the method takes more generic parameters, than the number of
// arguments in the type, then it is a generic method definition.
int originalNameLen = originalName.Length;
char lastChar = (originalNameLen > 0)
? originalName[originalNameLen - 1]
: (char) 0;
// the actual suffix character is configured in CilMain.cs
if (lastChar == '\u00A1' || lastChar == '!') // U+00A1 Inverted Exclamation Mark
{
if ((modifiers & java.lang.reflect.Modifier.STATIC) == 0)
{
// if an instance method takes any type arguments at all,
// then it must be a generic method definition
genericFlags |= flgGenericMethod
| flgGenericMethodDefinition
| flgContainsGenericParameters;
}
else
{
// count the number of type arguments in the declaring type
var typeArgsInType = reflectedType.GetGenericArguments();
int numTypeArgsInType = typeArgsInType.Length;
// count the number of type parameters in the method signature
int numTypeArgsInMethod = 0;
var paramTypes = javaMethod.getParameterTypes();
int paramIndex = paramTypes.Length;
while (paramIndex-- > 0)
{
var paramType = paramTypes[paramIndex];
if (paramType != (java.lang.Class) typeof(System.Type))
break;
numTypeArgsInMethod++;
}
if (numTypeArgsInMethod == numTypeArgsInType)
{
// a static method that takes a number of type parameters
// equal to the number of arguments in the declaring type.
// this means it is not a generic method, but it may not
// be invokable, if the declaring type is not concrete.
if (reflectedType.ContainsGenericParameters)
genericFlags |= flgContainsGenericParameters;
else if (reflectedType.IsGenericType)
genericFlags |= flgCombineGenericArguments;
}
else if (numTypeArgsInMethod > numTypeArgsInType)
{
// a static method that takes more parameters than the
// declaring type, i.e. it is a generic method definition.
genericFlags |= flgGenericMethod
| flgGenericMethodDefinition
| flgContainsGenericParameters;
}
else
throw new TypeLoadException(originalName);
}
}
else
{
// a method that does not take any type argument may still be
// not invokable, if the reflected type is not a concrete type.
// note that this is true only for instance methods, as static
// methods in a generic type will always take type arguments.
if ((modifiers & java.lang.reflect.Modifier.STATIC) == 0)
{
if (reflectedType.ContainsGenericParameters)
genericFlags |= flgContainsGenericParameters;
else if (reflectedType.IsGenericType)
genericFlags |= flgCombineGenericArguments;
}
}
}
//
// MakeGenericMethod
//
public override MethodInfo MakeGenericMethod(Type[] typeArguments)
{
if (! IsGenericMethodDefinition)
throw new InvalidOperationException();
if (typeArguments == null)
throw new ArgumentNullException();
var implicitTypeArgs = reflectedType.GetGenericArguments();
int implicitTypeArgsCount = implicitTypeArgs.Length;
var explicitTypeArgsCount = typeArguments.Length;
int combinedTypeArgsCount = implicitTypeArgsCount + explicitTypeArgsCount;
var combinedTypeArgs = new object[combinedTypeArgsCount];
java.lang.System.arraycopy(
/* from */ implicitTypeArgs, 0,
/* into */ combinedTypeArgs, 0,
/* count */ implicitTypeArgsCount);
java.lang.System.arraycopy(
/* from */ typeArguments, 0,
/* into */ combinedTypeArgs, implicitTypeArgsCount,
/* count */ explicitTypeArgsCount);
var newMethod = new RuntimeMethodInfo(JavaMethod, -1, reflectedType,
originalName, strippedName);
newMethod.typeArguments = combinedTypeArgs;
newMethod.genericFlags = flgGenericMethod;
for (int i = 0; i < combinedTypeArgsCount; i++)
{
var arg = combinedTypeArgs[i] as RuntimeType;
if (arg == null)
throw new ArgumentNullException();
if (arg.IsGenericParameter || arg.ContainsGenericParameters)
newMethod.genericFlags |= flgContainsGenericParameters;
}
return newMethod;
}
//
// Invoke
//
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder,
object[] parameters, CultureInfo culture)
{
if (ContainsGenericParameters)
throw new InvalidOperationException();
if (invokeAttr != BindingFlags.Default)
throw new PlatformNotSupportedException("bad binding flags " + invokeAttr);
if (binder != null)
throw new PlatformNotSupportedException("non-null binder");
if (culture != null)
throw new PlatformNotSupportedException("non-null culture");
// combine the provided parameters and any suffix type arguments
// that were previously injected by MakeGenericMethod
var typeArgs = typeArguments;
if (typeArgs == null && (genericFlags & flgCombineGenericArguments) != 0)
typeArgs = typeArguments = reflectedType.GetGenericArguments();
if (typeArgs != null)
{
if (parameters == null)
parameters = typeArgs;
else
{
int nParams = parameters.Length;
int nTypes = typeArgs.Length;
var newParams = new object[nParams + nTypes];
java.lang.System.arraycopy(parameters, 0, newParams, 0, nParams);
java.lang.System.arraycopy(typeArgs, 0, newParams, nParams, nTypes);
parameters = newParams;
}
}
int numParameters = (parameters != null) ? parameters.Length : 0;
if (JavaMethod.getParameterTypes().Length != numParameters)
throw new TargetParameterCountException();
return JavaMethod.invoke(obj, parameters);
}
//
//
//
public override bool IsGenericMethod
=> (genericFlags & flgGenericMethod) != 0;
public override bool IsGenericMethodDefinition
=> (genericFlags & flgGenericMethodDefinition) != 0;
public override bool ContainsGenericParameters
=> (genericFlags & flgContainsGenericParameters) != 0;
//
//
//
public override Type ReflectedType => reflectedType;
public override Type DeclaringType
=> system.RuntimeType.GetType(JavaMethod.getDeclaringClass());
public override MethodAttributes Attributes
=> throw new PlatformNotSupportedException();
public override string Name => strippedName;
public override RuntimeMethodHandle MethodHandle
=> throw new PlatformNotSupportedException();
public override ParameterInfo[] GetParameters()
=> throw new PlatformNotSupportedException();
public override MethodInfo GetBaseDefinition()
=> throw new PlatformNotSupportedException();
public override MethodImplAttributes GetMethodImplementationFlags()
=> throw new PlatformNotSupportedException();
//
//
//
public override string ToString() => strippedName;
//
// custom attributes
//
public override bool IsDefined(Type attributeType, bool inherit)
=> throw new PlatformNotSupportedException();
public override object[] GetCustomAttributes(bool inherit)
=> throw new PlatformNotSupportedException();
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
=> throw new PlatformNotSupportedException();
public override ICustomAttributeProvider ReturnTypeCustomAttributes
=> throw new PlatformNotSupportedException();
//
// ISerializable
//
public void GetObjectData(SerializationInfo info, StreamingContext context)
=> throw new PlatformNotSupportedException();
}
}
//
// declaration of java.lang.reflect.Method and java.lang.reflect.Constructor.
// this is needed because java 1.8 inserts a new java.lang.reflect.Executable
// class as the base class for Method and Constructor.
// this causes an error on Android, because the Executable class is missing.
//
namespace java.lang.reflect
{
[java.attr.Discard] // discard in output
public abstract class Constructor : AccessibleObject
{
public abstract object newInstance(object[] initargs);
public abstract Class[] getParameterTypes();
public abstract int getModifiers();
}
[java.attr.Discard] // discard in output
public abstract class Method : AccessibleObject
{
public abstract Class getDeclaringClass();
public abstract string getName();
public abstract Class[] getParameterTypes();
public abstract Class getReturnType();
public abstract int getModifiers();
public abstract object invoke(object obj, object[] args);
}
}