41 internal readonly RuntimeJavaType proxyClass;
42 internal readonly RuntimeJavaType errorClass;
43 internal readonly RuntimeJavaType runtimeExceptionClass;
46 internal readonly RuntimeJavaType javaLangReflectMethod;
47 internal readonly RuntimeJavaType javaLangNoSuchMethodException;
51 internal readonly RuntimeJavaType invocationHandlerClass;
63 this.context = context;
66 proxyClass = bootClassLoader.TryLoadClassByName(
"java.lang.reflect.Proxy");
67 errorClass = bootClassLoader.TryLoadClassByName(
"java.lang.Error");
68 runtimeExceptionClass = bootClassLoader.TryLoadClassByName(
"java.lang.RuntimeException");
69 undeclaredThrowableExceptionConstructor = bootClassLoader.TryLoadClassByName(
"java.lang.reflect.UndeclaredThrowableException").GetMethod(
"<init>",
"(Ljava.lang.Throwable;)V",
false);
70 undeclaredThrowableExceptionConstructor.Link();
71 invocationHandlerField = proxyClass.GetFieldWrapper(
"h",
"Ljava.lang.reflect.InvocationHandler;");
72 invocationHandlerField.Link();
73 javaLangReflectMethod = bootClassLoader.TryLoadClassByName(
"java.lang.reflect.Method");
74 javaLangNoSuchMethodException = bootClassLoader.TryLoadClassByName(
"java.lang.NoSuchMethodException");
75 javaLangNoClassDefFoundErrorConstructor = bootClassLoader.TryLoadClassByName(
"java.lang.NoClassDefFoundError").GetMethod(
"<init>",
"(Ljava.lang.String;)V",
false);
76 javaLangNoClassDefFoundErrorConstructor.Link();
77 javaLangThrowable_getMessage = bootClassLoader.TryLoadClassByName(
"java.lang.Throwable").GetMethod(
"getMessage",
"()Ljava.lang.String;",
false);
78 javaLangThrowable_getMessage.Link();
79 javaLangClass_getMethod = context.
JavaBase.TypeOfJavaLangClass.GetMethod(
"getMethod",
"(Ljava.lang.String;[Ljava.lang.Class;)Ljava.lang.reflect.Method;",
false);
80 javaLangClass_getMethod.Link();
81 invocationHandlerClass = bootClassLoader.TryLoadClassByName(
"java.lang.reflect.InvocationHandler");
82 invokeMethod = invocationHandlerClass.GetMethod(
"invoke",
"(Ljava.lang.Object;Ljava.lang.reflect.Method;[Ljava.lang.Object;)Ljava.lang.Object;",
false);
83 proxyConstructor = proxyClass.GetMethod(
"<init>",
"(Ljava.lang.reflect.InvocationHandler;)V",
false);
84 proxyConstructor.Link();
85 hashCodeMethod = context.
JavaBase.TypeOfJavaLangObject.GetMethod(
"hashCode",
"()I",
false);
86 equalsMethod = context.
JavaBase.TypeOfJavaLangObject.GetMethod(
"equals",
"(Ljava.lang.Object;)Z",
false);
87 toStringMethod = context.
JavaBase.TypeOfJavaLangObject.GetMethod(
"toString",
"()Ljava.lang.String;",
false);
92 var interfaces = proxy.Split(
',');
93 var wrappers =
new RuntimeJavaType[interfaces.Length];
94 for (
int i = 0; i < interfaces.Length; i++)
98 wrappers[i] = loader.TryLoadClassByName(interfaces[i]);
105 if (wrappers[i] ==
null)
112 Create(loader, proxy, wrappers);
115 private void Create(ImportClassLoader loader,
string proxy, RuntimeJavaType[] interfaces)
117 List<ProxyMethod> methods;
120 methods = CheckAndCollect(loader, interfaces);
124 loader.Diagnostics.UnableToCreateProxy(proxy, x.Message);
127 catch (ProxyException x)
129 loader.Diagnostics.UnableToCreateProxy(proxy, x.Message);
133 CreateNoFail(loader, interfaces, methods);
136 private List<ProxyMethod> CheckAndCollect(ImportClassLoader loader, RuntimeJavaType[] interfaces)
138 var methods =
new List<RuntimeJavaMethod>();
141 methods.Add(equalsMethod);
142 methods.Add(hashCodeMethod);
143 methods.Add(toStringMethod);
146 foreach (var tw
in interfaces)
150 throw new ProxyException(tw.Name +
" is not an interface");
155 throw new ProxyException(tw.Name +
" is a remapped interface (not currently supported)");
157 foreach (var mw
in GetInterfaceMethods(tw))
160 if (!MethodExists(methods, mw))
171 var exceptions =
new Dictionary<string, RuntimeJavaType[]>();
172 foreach (var mw
in methods)
173 Add(loader, exceptions, mw);
176 List<ProxyMethod> proxyMethods =
new List<ProxyMethod>();
177 foreach (var mw
in methods)
179 proxyMethods.Add(
new ProxyMethod(mw, exceptions[mw.Signature]));
186 foreach (var mw1
in methods)
189 if (mw1.Name == mw.Name && mw1.Signature == mw.Signature)
197 private void Add(ImportClassLoader loader, Dictionary<
string, RuntimeJavaType[]> exceptions,
RuntimeJavaMethod mw)
199 string signature = mw.Signature;
200 RuntimeJavaType[] newExceptionTypes = LoadTypes(loader, mw.GetDeclaredExceptions());
201 RuntimeJavaType[] curExceptionTypes;
202 if (exceptions.TryGetValue(signature, out curExceptionTypes))
204 exceptions[signature] = Merge(newExceptionTypes, curExceptionTypes);
208 exceptions.Add(signature, newExceptionTypes);
212 static RuntimeJavaType[] Merge(RuntimeJavaType[] newExceptionTypes, RuntimeJavaType[] curExceptionTypes)
214 var list =
new List<RuntimeJavaType>();
215 foreach (var twNew
in newExceptionTypes)
217 RuntimeJavaType match =
null;
218 foreach (var twCur
in curExceptionTypes)
219 if (twNew.IsAssignableTo(twCur))
220 if (match ==
null || twCur.IsAssignableTo(match))
223 if (match !=
null && !list.Contains(match))
227 return list.ToArray();
230 void CreateNoFail(ImportClassLoader loader, RuntimeJavaType[] interfaces, List<ProxyMethod> methods)
233 var interfaceTypes =
new Type[interfaces.Length];
234 for (
int i = 0; i < interfaceTypes.Length; i++)
236 ispublic &= interfaces[i].IsPublic;
237 interfaceTypes[i] = interfaces[i].TypeAsBaseType;
239 var attr = TypeAttributes.Class | TypeAttributes.Sealed;
240 attr |= ispublic ? TypeAttributes.NestedPublic : TypeAttributes.NestedAssembly;
241 var factory = (DynamicClassLoader)loader.GetTypeWrapperFactory();
242 var tb = factory.DefineProxy(
TypeNameUtil.GetProxyNestedName(interfaces), attr, proxyClass.TypeAsBaseType, interfaceTypes);
243 loader.Context.AttributeHelper.SetImplementsAttribute(tb, interfaces);
246 CreateConstructor(tb);
247 for (
int i = 0; i < methods.Count; i++)
248 methods[i].fb = tb.DefineField(
"m" + i, javaLangReflectMethod.TypeAsSignatureType, FieldAttributes.Private | FieldAttributes.Static);
250 foreach (var method
in methods)
251 CreateMethod(loader, tb, method);
253 CreateStaticInitializer(tb, methods, loader);
256 void CreateConstructor(TypeBuilder tb)
258 var ilgen = context.CodeEmitterFactory.Create(
ReflectUtil.DefineConstructor(tb, MethodAttributes.Public,
new Type[] { invocationHandlerClass.TypeAsSignatureType }));
259 ilgen.Emit(OpCodes.Ldarg_0);
260 ilgen.Emit(OpCodes.Ldarg_1);
261 proxyConstructor.EmitCall(ilgen);
262 ilgen.Emit(OpCodes.Ret);
266 void CreateMethod(ImportClassLoader loader, TypeBuilder tb, ProxyMethod pm)
268 var mb = pm.mw.GetDefineMethodHelper().DefineMethod(loader.GetTypeWrapperFactory(), tb, pm.mw.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final);
269 var exceptions =
new List<string>();
270 foreach (var tw
in pm.exceptions)
271 exceptions.Add(tw.Name);
273 loader.Context.AttributeHelper.SetThrowsAttribute(mb, exceptions.ToArray());
274 var ilgen = loader.Context.CodeEmitterFactory.Create(mb);
275 ilgen.BeginExceptionBlock();
276 ilgen.Emit(OpCodes.Ldarg_0);
277 invocationHandlerField.EmitGet(ilgen);
278 ilgen.Emit(OpCodes.Ldarg_0);
279 ilgen.Emit(OpCodes.Ldsfld, pm.fb);
280 var parameters = pm.mw.GetParameters();
281 if (parameters.Length == 0)
283 ilgen.Emit(OpCodes.Ldnull);
287 ilgen.EmitLdc_I4(parameters.Length);
288 ilgen.Emit(OpCodes.Newarr, loader.Context.Types.Object);
289 for (
int i = 0; i < parameters.Length; i++)
291 ilgen.Emit(OpCodes.Dup);
294 if (parameters[i].IsNonPrimitiveValueType)
296 parameters[i].EmitBox(ilgen);
298 else if (parameters[i].IsPrimitive)
300 context.Boxer.EmitBox(ilgen, parameters[i]);
302 ilgen.Emit(OpCodes.Stelem_Ref);
305 invokeMethod.EmitCallvirt(ilgen);
306 var returnType = pm.mw.ReturnType;
308 if (returnType != context.PrimitiveJavaTypeFactory.VOID)
310 returnValue = ilgen.DeclareLocal(returnType.TypeAsSignatureType);
311 if (returnType.IsNonPrimitiveValueType)
313 returnType.EmitUnbox(ilgen);
315 else if (returnType.IsPrimitive)
317 context.Boxer.EmitUnbox(ilgen, returnType,
true);
319 else if (returnType != context.JavaBase.TypeOfJavaLangObject)
321 ilgen.EmitCastclass(returnType.TypeAsSignatureType);
323 ilgen.Emit(OpCodes.Stloc, returnValue);
326 ilgen.EmitLeave(returnLabel);
328 ilgen.BeginCatchBlock(loader.Context.Types.Exception);
330 ilgen.Emit(OpCodes.Call, loader.Context.ByteCodeHelperMethods.MapException.MakeGenericMethod(loader.Context.Types.Exception));
332 ilgen.Emit(OpCodes.Stloc, exception);
334 ilgen.Emit(OpCodes.Ldloc, exception);
335 errorClass.EmitInstanceOf(ilgen);
336 ilgen.EmitBrtrue(rethrow);
337 ilgen.Emit(OpCodes.Ldloc, exception);
338 runtimeExceptionClass.EmitInstanceOf(ilgen);
339 ilgen.EmitBrtrue(rethrow);
340 foreach (var tw
in pm.exceptions)
342 ilgen.Emit(OpCodes.Ldloc, exception);
343 tw.EmitInstanceOf(ilgen);
344 ilgen.EmitBrtrue(rethrow);
346 ilgen.Emit(OpCodes.Ldloc, exception);
347 undeclaredThrowableExceptionConstructor.EmitNewobj(ilgen);
348 ilgen.Emit(OpCodes.Throw);
349 ilgen.MarkLabel(rethrow);
350 ilgen.Emit(OpCodes.Rethrow);
351 ilgen.EndExceptionBlock();
352 ilgen.MarkLabel(returnLabel);
353 if (returnValue !=
null)
355 ilgen.Emit(OpCodes.Ldloc, returnValue);
357 ilgen.Emit(OpCodes.Ret);
361 void CreateStaticInitializer(TypeBuilder tb, List<ProxyMethod> methods, ImportClassLoader loader)
363 var ilgen = loader.Context.CodeEmitterFactory.Create(
ReflectUtil.DefineTypeInitializer(tb, loader));
364 var callerID = ilgen.DeclareLocal(loader.Context.JavaBase.TypeOfIkvmInternalCallerID.TypeAsSignatureType);
366 ilgen.Emit(OpCodes.Stloc, callerID);
368 tbCallerID.CreateType();
369 ilgen.BeginExceptionBlock();
370 foreach (ProxyMethod method
in methods)
372 method.mw.DeclaringType.EmitClassLiteral(ilgen);
373 ilgen.Emit(OpCodes.Ldstr, method.mw.Name);
374 var parameters = method.mw.GetParameters();
375 ilgen.EmitLdc_I4(parameters.Length);
376 ilgen.Emit(OpCodes.Newarr, loader.Context.JavaBase.TypeOfJavaLangClass.TypeAsArrayType);
377 for (
int i = 0; i < parameters.Length; i++)
379 ilgen.Emit(OpCodes.Dup);
381 parameters[i].EmitClassLiteral(ilgen);
382 ilgen.Emit(OpCodes.Stelem_Ref);
384 if (javaLangClass_getMethod.HasCallerID)
386 ilgen.Emit(OpCodes.Ldloc, callerID);
388 javaLangClass_getMethod.EmitCallvirt(ilgen);
389 ilgen.Emit(OpCodes.Stsfld, method.fb);
392 ilgen.EmitLeave(label);
393 ilgen.BeginCatchBlock(javaLangNoSuchMethodException.TypeAsExceptionType);
394 javaLangThrowable_getMessage.EmitCallvirt(ilgen);
395 javaLangNoClassDefFoundErrorConstructor.EmitNewobj(ilgen);
396 ilgen.Emit(OpCodes.Throw);
397 ilgen.EndExceptionBlock();
398 ilgen.MarkLabel(label);
399 ilgen.Emit(OpCodes.Ret);
403 sealed class ProxyMethod
407 internal readonly RuntimeJavaType[] exceptions;
408 internal FieldBuilder fb;
413 this.exceptions = exceptions;
418 IEnumerable<RuntimeJavaMethod> GetInterfaceMethods(RuntimeJavaType tw)
420 var methods =
new Dictionary<string, RuntimeJavaMethod>();
421 foreach (var mw
in tw.GetMethods())
423 methods.Add(mw.Name + mw.Signature, mw);
425 foreach (var iface
in tw.Interfaces)
426 foreach (var mw
in GetInterfaceMethods(iface))
427 if (!methods.ContainsKey(mw.Name + mw.Signature))
428 methods.Add(mw.Name + mw.Signature, mw);
430 return methods.Values;
435 if (classes ==
null || classes.Length == 0)
436 return Array.Empty<RuntimeJavaType>();
438 var tw =
new RuntimeJavaType[classes.Length];
439 for (
int i = 0; i < tw.Length; i++)
440 tw[i] = loader.LoadClassByName(classes[i]);
445 sealed class ProxyException : Exception
448 internal ProxyException(
string msg) :