IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
MethodAccessor.cs
Go to the documentation of this file.
1using System;
2using System.Linq;
3using System.Reflection;
4using System.Reflection.Emit;
5
7{
8
12 internal abstract partial class MethodAccessor
13 {
14
15 readonly Type type;
16 readonly string name;
17 readonly Type returnType;
18 readonly Type[] parameterTypes;
19 MethodBase method;
20
28 protected MethodAccessor(Type type, string name, Type returnType, Type[] parameterTypes)
29 {
30
31 this.type = type ?? throw new ArgumentNullException(nameof(type));
32 this.name = name ?? throw new ArgumentNullException(nameof(name));
33 this.returnType = returnType ?? throw new ArgumentNullException(nameof(returnType));
34 this.parameterTypes = parameterTypes ?? throw new ArgumentNullException(nameof(parameterTypes));
35 }
36
40 protected Type Type => type;
41
45 protected string Name => name;
46
50 protected Type ReturnType => returnType;
51
55 protected Type[] ParameterTypes => parameterTypes;
56
60 protected MethodBase Method => AccessorUtil.LazyGet(ref method, FindMethod) ?? throw new InternalException($"Could not locate method {Name}.");
61
66 MethodBase FindMethod() => type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
67 .OfType<MethodBase>()
68 .Where(i => i.Name == Name)
69 .Where(i => i.IsConstructor && ReturnType == typeof(void) || i is MethodInfo m && m.ReturnType == ReturnType)
70 .Where(i => i.GetParameters().Select(i => i.ParameterType).SequenceEqual(ParameterTypes))
71 .FirstOrDefault();
72
73 }
74
78 internal sealed class MethodAccessor<TDelegate> : MethodAccessor
79 where TDelegate : Delegate
80 {
81
82 public static MethodAccessor<TDelegate> LazyGet(ref MethodAccessor<TDelegate> location, Type type, string name, Type returnType, params Type[] parameterTypes)
83 {
84 return AccessorUtil.LazyGet(ref location, () => new MethodAccessor<TDelegate>(type, name, returnType, parameterTypes));
85 }
86
87 TDelegate invoker;
88
96 public MethodAccessor(Type type, string name, Type returnType, Type[] parameters) :
97 base(type, name, returnType, parameters)
98 {
99
100 }
101
105 public TDelegate Invoker => AccessorUtil.LazyGet(ref invoker, MakeInvoker);
106
111 TDelegate MakeInvoker()
112 {
113#if FIRST_PASS || IMPORTER || EXPORTER
114 throw new NotImplementedException();
115#else
116 // validate return type
117 var delegateReturnType = GetDelegateReturnType(typeof(TDelegate));
118 if (Method.IsConstructor)
119 {
120 if (delegateReturnType == typeof(void))
121 throw new InternalException("Delegate has no return type for constructor.");
122 }
123 else if (Method is MethodInfo mi)
124 {
125 if (delegateReturnType == typeof(void) && mi.ReturnType != typeof(void))
126 throw new InternalException("Delegate has no return type for method with return type.");
127 if (delegateReturnType != typeof(void) && mi.ReturnType == typeof(void))
128 throw new InternalException("Delegate has return type for method without return type.");
129 }
130
131 var parameters = Method.GetParameters();
132
133 // validate parameter counts
134 var delegateParameterTypes = GetDelegateParameterTypes(typeof(TDelegate));
135 if ((Method.IsConstructor || Method.IsStatic) && delegateParameterTypes.Length != parameters.Length)
136 throw new InternalException("Delegate has wrong number of parameters for constructor or static method.");
137 else if (Method.IsConstructor == false && Method.IsStatic == false && delegateParameterTypes.Length != parameters.Length + 1)
138 throw new InternalException("Delegate has wrong number of parameters for instance method.");
139
140 // generate new dynamic method
141 var dm = DynamicMethodUtil.Create($"__<MethodAccessor>__{Type.Name.Replace(".", "_")}__{Method.Name}", Type, false, delegateReturnType, delegateParameterTypes);
142 var il = JVM.Context.CodeEmitterFactory.Create(dm);
143
144 // advance through each argument
145 var n = 0;
146
147 // load first argument, which is the instance if non-static
148 if (Method.IsStatic == false && Method.IsConstructor == false)
149 {
150 il.EmitLdarg(n++);
151 if (delegateParameterTypes[0] != Type)
152 il.EmitCastclass(Type);
153 }
154
155 // emit conversion code for the remainder of the arguments
156 for (var i = 0; i < parameters.Length; i++)
157 {
158 var delegateParameterType = delegateParameterTypes[n];
159 il.EmitLdarg(n++);
160 if (parameters[i].ParameterType != delegateParameterType)
161 il.EmitCastclass(parameters[i].ParameterType);
162 }
163
164 if (Method.IsConstructor)
165 il.Emit(OpCodes.Newobj, Method);
166 else if (Method.IsStatic || Method.IsVirtual == false)
167 il.Emit(OpCodes.Call, Method);
168 else
169 il.Emit(OpCodes.Callvirt, Method);
170
171 // convert to delegate value
172 if (delegateReturnType != typeof(void))
173 if (Method.IsConstructor && delegateReturnType != Type || Method.IsConstructor == false && delegateReturnType != ((MethodInfo)Method).ReturnType)
174 il.EmitCastclass(delegateReturnType);
175
176 il.Emit(OpCodes.Ret);
177 il.DoEmit();
178
179 return (TDelegate)dm.CreateDelegate(typeof(TDelegate));
180#endif
181 }
182
189 Type[] GetDelegateParameterTypes(Type d)
190 {
191 if (d.BaseType != typeof(MulticastDelegate))
192 throw new ArgumentException("Not a delegate.", nameof(d));
193
194 var invoke = d.GetMethod("Invoke");
195 if (invoke == null)
196 throw new ArgumentException("Not a delegate.", nameof(d));
197
198 var parameters = invoke.GetParameters();
199 var typeParameters = new Type[parameters.Length];
200 for (int i = 0; i < parameters.Length; i++)
201 typeParameters[i] = parameters[i].ParameterType;
202
203 return typeParameters;
204 }
205
212 Type GetDelegateReturnType(Type d)
213 {
214 if (d.BaseType != typeof(MulticastDelegate))
215 throw new ArgumentException("Not a delegate.", nameof(d));
216
217 var invoke = d.GetMethod("Invoke");
218 if (invoke == null)
219 throw new ArgumentException("Not a delegate.", nameof(d));
220
221 return invoke.ReturnType;
222 }
223
224 }
225
226}
IKVM.Reflection.Type Type
IKVM.Reflection.MethodInfo MethodInfo
IKVM.Reflection.MethodBase MethodBase
global::java.lang.invoke.LambdaForm.Name Name