IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
Proxy.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2011-2014 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Collections.Generic;
26
27using IKVM.Attributes;
28using IKVM.Reflection;
30using IKVM.Runtime;
31
32using Type = IKVM.Reflection.Type;
33
34namespace IKVM.Tools.Importer
35{
36
38 {
39
40 readonly RuntimeContext context;
41 internal readonly RuntimeJavaType proxyClass;
42 internal readonly RuntimeJavaType errorClass;
43 internal readonly RuntimeJavaType runtimeExceptionClass;
44 internal readonly RuntimeJavaMethod undeclaredThrowableExceptionConstructor;
45 internal readonly RuntimeJavaField invocationHandlerField;
46 internal readonly RuntimeJavaType javaLangReflectMethod;
47 internal readonly RuntimeJavaType javaLangNoSuchMethodException;
48 internal readonly RuntimeJavaMethod javaLangNoClassDefFoundErrorConstructor;
49 internal readonly RuntimeJavaMethod javaLangThrowable_getMessage;
50 internal readonly RuntimeJavaMethod javaLangClass_getMethod;
51 internal readonly RuntimeJavaType invocationHandlerClass;
52 internal readonly RuntimeJavaMethod invokeMethod;
53 internal readonly RuntimeJavaMethod proxyConstructor;
54 internal readonly RuntimeJavaMethod hashCodeMethod;
55 internal readonly RuntimeJavaMethod equalsMethod;
56 internal readonly RuntimeJavaMethod toStringMethod;
57
62 {
63 this.context = context;
64
65 var bootClassLoader = context.ClassLoaderFactory.GetBootstrapClassLoader();
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);
88 }
89
90 internal void Create(ImportClassLoader loader, string proxy)
91 {
92 var interfaces = proxy.Split(',');
93 var wrappers = new RuntimeJavaType[interfaces.Length];
94 for (int i = 0; i < interfaces.Length; i++)
95 {
96 try
97 {
98 wrappers[i] = loader.TryLoadClassByName(interfaces[i]);
99 }
101 {
102
103 }
104
105 if (wrappers[i] == null)
106 {
107 loader.Diagnostics.UnableToCreateProxy(proxy, "unable to load interface " + interfaces[i]);
108 return;
109 }
110 }
111
112 Create(loader, proxy, wrappers);
113 }
114
115 private void Create(ImportClassLoader loader, string proxy, RuntimeJavaType[] interfaces)
116 {
117 List<ProxyMethod> methods;
118 try
119 {
120 methods = CheckAndCollect(loader, interfaces);
121 }
123 {
124 loader.Diagnostics.UnableToCreateProxy(proxy, x.Message);
125 return;
126 }
127 catch (ProxyException x)
128 {
129 loader.Diagnostics.UnableToCreateProxy(proxy, x.Message);
130 return;
131 }
132
133 CreateNoFail(loader, interfaces, methods);
134 }
135
136 private List<ProxyMethod> CheckAndCollect(ImportClassLoader loader, RuntimeJavaType[] interfaces)
137 {
138 var methods = new List<RuntimeJavaMethod>();
139
140 // The java.lang.Object methods precede any interface methods.
141 methods.Add(equalsMethod);
142 methods.Add(hashCodeMethod);
143 methods.Add(toStringMethod);
144
145 // Add the interfaces methods in order.
146 foreach (var tw in interfaces)
147 {
148 if (!tw.IsInterface)
149 {
150 throw new ProxyException(tw.Name + " is not an interface");
151 }
152 if (tw.IsRemapped)
153 {
154 // TODO handle java.lang.Comparable
155 throw new ProxyException(tw.Name + " is a remapped interface (not currently supported)");
156 }
157 foreach (var mw in GetInterfaceMethods(tw))
158 {
159 // Check for duplicates
160 if (!MethodExists(methods, mw))
161 {
162 mw.Link();
163 methods.Add(mw);
164 }
165 }
166 }
167
168 // TODO verify restrictions
169
170 // Collect declared exceptions.
171 var exceptions = new Dictionary<string, RuntimeJavaType[]>();
172 foreach (var mw in methods)
173 Add(loader, exceptions, mw);
174
175 // Build the definitive proxy method list.
176 List<ProxyMethod> proxyMethods = new List<ProxyMethod>();
177 foreach (var mw in methods)
178 {
179 proxyMethods.Add(new ProxyMethod(mw, exceptions[mw.Signature]));
180 }
181 return proxyMethods;
182 }
183
184 private bool MethodExists(List<RuntimeJavaMethod> methods, RuntimeJavaMethod mw)
185 {
186 foreach (var mw1 in methods)
187 {
188 // TODO what do we do with differing return types?
189 if (mw1.Name == mw.Name && mw1.Signature == mw.Signature)
190 {
191 return true;
192 }
193 }
194 return false;
195 }
196
197 private void Add(ImportClassLoader loader, Dictionary<string, RuntimeJavaType[]> exceptions, RuntimeJavaMethod mw)
198 {
199 string signature = mw.Signature;
200 RuntimeJavaType[] newExceptionTypes = LoadTypes(loader, mw.GetDeclaredExceptions());
201 RuntimeJavaType[] curExceptionTypes;
202 if (exceptions.TryGetValue(signature, out curExceptionTypes))
203 {
204 exceptions[signature] = Merge(newExceptionTypes, curExceptionTypes);
205 }
206 else
207 {
208 exceptions.Add(signature, newExceptionTypes);
209 }
210 }
211
212 static RuntimeJavaType[] Merge(RuntimeJavaType[] newExceptionTypes, RuntimeJavaType[] curExceptionTypes)
213 {
214 var list = new List<RuntimeJavaType>();
215 foreach (var twNew in newExceptionTypes)
216 {
217 RuntimeJavaType match = null;
218 foreach (var twCur in curExceptionTypes)
219 if (twNew.IsAssignableTo(twCur))
220 if (match == null || twCur.IsAssignableTo(match))
221 match = twCur;
222
223 if (match != null && !list.Contains(match))
224 list.Add(match);
225 }
226
227 return list.ToArray();
228 }
229
230 void CreateNoFail(ImportClassLoader loader, RuntimeJavaType[] interfaces, List<ProxyMethod> methods)
231 {
232 var ispublic = true;
233 var interfaceTypes = new Type[interfaces.Length];
234 for (int i = 0; i < interfaceTypes.Length; i++)
235 {
236 ispublic &= interfaces[i].IsPublic;
237 interfaceTypes[i] = interfaces[i].TypeAsBaseType;
238 }
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);
244 // we apply an InnerClass attribute to avoid the CompiledTypeWrapper heuristics for figuring out the modifiers
245 loader.Context.AttributeHelper.SetInnerClass(tb, null, ispublic ? Modifiers.Public | Modifiers.Final : Modifiers.Final);
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);
249
250 foreach (var method in methods)
251 CreateMethod(loader, tb, method);
252
253 CreateStaticInitializer(tb, methods, loader);
254 }
255
256 void CreateConstructor(TypeBuilder tb)
257 {
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);
263 ilgen.DoEmit();
264 }
265
266 void CreateMethod(ImportClassLoader loader, TypeBuilder tb, ProxyMethod pm)
267 {
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);
272
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)
282 {
283 ilgen.Emit(OpCodes.Ldnull);
284 }
285 else
286 {
287 ilgen.EmitLdc_I4(parameters.Length);
288 ilgen.Emit(OpCodes.Newarr, loader.Context.Types.Object);
289 for (int i = 0; i < parameters.Length; i++)
290 {
291 ilgen.Emit(OpCodes.Dup);
292 ilgen.EmitLdc_I4(i);
293 ilgen.EmitLdarg(i);
294 if (parameters[i].IsNonPrimitiveValueType)
295 {
296 parameters[i].EmitBox(ilgen);
297 }
298 else if (parameters[i].IsPrimitive)
299 {
300 context.Boxer.EmitBox(ilgen, parameters[i]);
301 }
302 ilgen.Emit(OpCodes.Stelem_Ref);
303 }
304 }
305 invokeMethod.EmitCallvirt(ilgen);
306 var returnType = pm.mw.ReturnType;
307 CodeEmitterLocal returnValue = null;
308 if (returnType != context.PrimitiveJavaTypeFactory.VOID)
309 {
310 returnValue = ilgen.DeclareLocal(returnType.TypeAsSignatureType);
311 if (returnType.IsNonPrimitiveValueType)
312 {
313 returnType.EmitUnbox(ilgen);
314 }
315 else if (returnType.IsPrimitive)
316 {
317 context.Boxer.EmitUnbox(ilgen, returnType, true);
318 }
319 else if (returnType != context.JavaBase.TypeOfJavaLangObject)
320 {
321 ilgen.EmitCastclass(returnType.TypeAsSignatureType);
322 }
323 ilgen.Emit(OpCodes.Stloc, returnValue);
324 }
325 CodeEmitterLabel returnLabel = ilgen.DefineLabel();
326 ilgen.EmitLeave(returnLabel);
327 // TODO consider using a filter here (but we would need to add filter support to CodeEmitter)
328 ilgen.BeginCatchBlock(loader.Context.Types.Exception);
329 ilgen.EmitLdc_I4(0);
330 ilgen.Emit(OpCodes.Call, loader.Context.ByteCodeHelperMethods.MapException.MakeGenericMethod(loader.Context.Types.Exception));
331 CodeEmitterLocal exception = ilgen.DeclareLocal(context.Types.Exception);
332 ilgen.Emit(OpCodes.Stloc, exception);
333 CodeEmitterLabel rethrow = ilgen.DefineLabel();
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)
341 {
342 ilgen.Emit(OpCodes.Ldloc, exception);
343 tw.EmitInstanceOf(ilgen);
344 ilgen.EmitBrtrue(rethrow);
345 }
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)
354 {
355 ilgen.Emit(OpCodes.Ldloc, returnValue);
356 }
357 ilgen.Emit(OpCodes.Ret);
358 ilgen.DoEmit();
359 }
360
361 void CreateStaticInitializer(TypeBuilder tb, List<ProxyMethod> methods, ImportClassLoader loader)
362 {
363 var ilgen = loader.Context.CodeEmitterFactory.Create(ReflectUtil.DefineTypeInitializer(tb, loader));
364 var callerID = ilgen.DeclareLocal(loader.Context.JavaBase.TypeOfIkvmInternalCallerID.TypeAsSignatureType);
365 var tbCallerID = RuntimeByteCodeJavaType.FinishContext.EmitCreateCallerID(loader.Context, tb, ilgen);
366 ilgen.Emit(OpCodes.Stloc, callerID);
367 // HACK we shouldn't create the nested type here (the outer type must be created first)
368 tbCallerID.CreateType();
369 ilgen.BeginExceptionBlock();
370 foreach (ProxyMethod method in methods)
371 {
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++)
378 {
379 ilgen.Emit(OpCodes.Dup);
380 ilgen.EmitLdc_I4(i);
381 parameters[i].EmitClassLiteral(ilgen);
382 ilgen.Emit(OpCodes.Stelem_Ref);
383 }
384 if (javaLangClass_getMethod.HasCallerID)
385 {
386 ilgen.Emit(OpCodes.Ldloc, callerID);
387 }
388 javaLangClass_getMethod.EmitCallvirt(ilgen);
389 ilgen.Emit(OpCodes.Stsfld, method.fb);
390 }
391 CodeEmitterLabel label = ilgen.DefineLabel();
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);
400 ilgen.DoEmit();
401 }
402
403 sealed class ProxyMethod
404 {
405
406 internal readonly RuntimeJavaMethod mw;
407 internal readonly RuntimeJavaType[] exceptions;
408 internal FieldBuilder fb;
409
410 internal ProxyMethod(RuntimeJavaMethod mw, RuntimeJavaType[] exceptions)
411 {
412 this.mw = mw;
413 this.exceptions = exceptions;
414 }
415
416 }
417
418 IEnumerable<RuntimeJavaMethod> GetInterfaceMethods(RuntimeJavaType tw)
419 {
420 var methods = new Dictionary<string, RuntimeJavaMethod>();
421 foreach (var mw in tw.GetMethods())
422 if (mw.IsVirtual)
423 methods.Add(mw.Name + mw.Signature, mw);
424
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);
429
430 return methods.Values;
431 }
432
433 RuntimeJavaType[] LoadTypes(RuntimeClassLoader loader, string[] classes)
434 {
435 if (classes == null || classes.Length == 0)
436 return Array.Empty<RuntimeJavaType>();
437
438 var tw = new RuntimeJavaType[classes.Length];
439 for (int i = 0; i < tw.Length; i++)
440 tw[i] = loader.LoadClassByName(classes[i]);
441
442 return tw;
443 }
444
445 sealed class ProxyException : Exception
446 {
447
448 internal ProxyException(string msg) :
449 base(msg)
450 {
451
452 }
453
454 }
455
456 }
457
458}
.NET exception that corresponds to a Java exception.
Runtime support for a class loader.
Maintains services relevant to an instane of the IKVM runtime.
CoreClasses JavaBase
Gets the CoreClasses associated with this instance of the runtime.
RuntimeClassLoaderFactory ClassLoaderFactory
Gets the RuntimeClassLoaderFactory associated with this instance of the runtime.
Implementation of RuntimeClassLoader that emits loaded Java types to an AssemblyBuilder.
override IDiagnosticHandler Diagnostics
ProxyGenerator(RuntimeContext context)
Initializes the static instance.
Definition Proxy.cs:61
void UnableToCreateProxy(string arg0, string arg1)
The 'UnableToCreateProxy' diagnostic.