2
Copyright (C) 2011 Jeroen Frijters
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.
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:
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.
25
using System.Collections.Generic;
26
using IKVM.Reflection;
27
using IKVM.Reflection.Emit;
28
using Type = IKVM.Reflection.Type;
30
namespace IKVM.Internal
32
static class ProxyGenerator
34
private static readonly TypeWrapper proxyClass;
35
private static readonly TypeWrapper errorClass;
36
private static readonly TypeWrapper runtimeExceptionClass;
37
private static readonly MethodWrapper undeclaredThrowableExceptionConstructor;
38
private static readonly FieldWrapper invocationHandlerField;
39
private static readonly TypeWrapper javaLangReflectMethod;
40
private static readonly TypeWrapper javaLangNoSuchMethodException;
41
private static readonly MethodWrapper javaLangNoClassDefFoundErrorConstructor;
42
private static readonly MethodWrapper javaLangThrowable_getMessage;
43
private static readonly MethodWrapper javaLangClass_getMethod;
44
private static readonly TypeWrapper invocationHandlerClass;
45
private static readonly MethodWrapper invokeMethod;
46
private static readonly MethodWrapper proxyConstructor;
47
private static readonly MethodWrapper hashCodeMethod;
48
private static readonly MethodWrapper equalsMethod;
49
private static readonly MethodWrapper toStringMethod;
51
static ProxyGenerator()
53
ClassLoaderWrapper bootClassLoader = ClassLoaderWrapper.GetBootstrapClassLoader();
54
proxyClass = bootClassLoader.LoadClassByDottedNameFast("java.lang.reflect.Proxy");
55
errorClass = bootClassLoader.LoadClassByDottedNameFast("java.lang.Error");
56
runtimeExceptionClass = bootClassLoader.LoadClassByDottedNameFast("java.lang.RuntimeException");
57
undeclaredThrowableExceptionConstructor = bootClassLoader.LoadClassByDottedNameFast("java.lang.reflect.UndeclaredThrowableException").GetMethodWrapper("<init>", "(Ljava.lang.Throwable;)V", false);
58
undeclaredThrowableExceptionConstructor.Link();
59
invocationHandlerField = proxyClass.GetFieldWrapper("h", "Ljava.lang.reflect.InvocationHandler;");
60
invocationHandlerField.Link();
61
javaLangReflectMethod = bootClassLoader.LoadClassByDottedNameFast("java.lang.reflect.Method");
62
javaLangNoSuchMethodException = bootClassLoader.LoadClassByDottedNameFast("java.lang.NoSuchMethodException");
63
javaLangNoClassDefFoundErrorConstructor = bootClassLoader.LoadClassByDottedNameFast("java.lang.NoClassDefFoundError").GetMethodWrapper("<init>", "(Ljava.lang.String;)V", false);
64
javaLangNoClassDefFoundErrorConstructor.Link();
65
javaLangThrowable_getMessage = bootClassLoader.LoadClassByDottedNameFast("java.lang.Throwable").GetMethodWrapper("getMessage", "()Ljava.lang.String;", false);
66
javaLangThrowable_getMessage.Link();
67
javaLangClass_getMethod = CoreClasses.java.lang.Class.Wrapper.GetMethodWrapper("getMethod", "(Ljava.lang.String;[Ljava.lang.Class;)Ljava.lang.reflect.Method;", false);
68
javaLangClass_getMethod.Link();
69
invocationHandlerClass = bootClassLoader.LoadClassByDottedNameFast("java.lang.reflect.InvocationHandler");
70
invokeMethod = invocationHandlerClass.GetMethodWrapper("invoke", "(Ljava.lang.Object;Ljava.lang.reflect.Method;[Ljava.lang.Object;)Ljava.lang.Object;", false);
71
proxyConstructor = proxyClass.GetMethodWrapper("<init>", "(Ljava.lang.reflect.InvocationHandler;)V", false);
72
proxyConstructor.Link();
73
hashCodeMethod = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper("hashCode", "()I", false);
74
equalsMethod = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper("equals", "(Ljava.lang.Object;)Z", false);
75
toStringMethod = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper("toString", "()Ljava.lang.String;", false);
78
internal static void Create(CompilerClassLoader loader, string proxy)
80
string[] interfaces = proxy.Split(',');
81
TypeWrapper[] wrappers = new TypeWrapper[interfaces.Length];
82
for (int i = 0; i < interfaces.Length; i++)
86
wrappers[i] = loader.LoadClassByDottedNameFast(interfaces[i]);
88
catch (RetargetableJavaException)
91
if (wrappers[i] == null)
93
StaticCompiler.IssueMessage(Message.UnableToCreateProxy, proxy, "unable to load interface " + interfaces[i]);
97
Create(loader, proxy, wrappers);
100
private static void Create(CompilerClassLoader loader, string proxy, TypeWrapper[] interfaces)
102
List<ProxyMethod> methods;
105
methods = CheckAndCollect(loader, interfaces);
107
catch (RetargetableJavaException x)
109
StaticCompiler.IssueMessage(Message.UnableToCreateProxy, proxy, x.Message);
112
catch (ProxyException x)
114
StaticCompiler.IssueMessage(Message.UnableToCreateProxy, proxy, x.Message);
117
CreateNoFail(loader, interfaces, methods);
120
private static List<ProxyMethod> CheckAndCollect(CompilerClassLoader loader, TypeWrapper[] interfaces)
122
List<MethodWrapper> methods = new List<MethodWrapper>();
124
// The java.lang.Object methods precede any interface methods.
125
methods.Add(equalsMethod);
126
methods.Add(hashCodeMethod);
127
methods.Add(toStringMethod);
129
// Add the interfaces methods in order.
130
foreach (TypeWrapper tw in interfaces)
134
throw new ProxyException(tw.Name + " is not an interface");
138
// TODO handle java.lang.Comparable
139
throw new ProxyException(tw.Name + " is a remapped interface (not currently supported)");
141
foreach (MethodWrapper mw in GetInterfaceMethods(tw))
143
// Check for duplicates
144
if (!MethodExists(methods, mw))
151
// TODO verify restrictions
153
// Collect declared exceptions.
154
Dictionary<string, TypeWrapper[]> exceptions = new Dictionary<string, TypeWrapper[]>();
155
foreach (MethodWrapper mw in methods)
157
Add(loader, exceptions, mw);
160
// Build the definitive proxy method list.
161
List<ProxyMethod> proxyMethods = new List<ProxyMethod>();
162
foreach (MethodWrapper mw in methods)
164
proxyMethods.Add(new ProxyMethod(mw, exceptions[mw.Signature]));
169
private static bool MethodExists(List<MethodWrapper> methods, MethodWrapper mw)
171
foreach (MethodWrapper mw1 in methods)
173
// TODO what do we do with differing return types?
174
if (mw1.Name == mw.Name && mw1.Signature == mw.Signature)
182
private static void Add(CompilerClassLoader loader, Dictionary<string, TypeWrapper[]> exceptions, MethodWrapper mw)
184
string signature = mw.Signature;
185
TypeWrapper[] newExceptionTypes = LoadTypes(loader, mw.GetDeclaredExceptions());
186
TypeWrapper[] curExceptionTypes;
187
if (exceptions.TryGetValue(signature, out curExceptionTypes))
189
exceptions[signature] = Merge(newExceptionTypes, curExceptionTypes);
193
exceptions.Add(signature, newExceptionTypes);
197
private static TypeWrapper[] Merge(TypeWrapper[] newExceptionTypes, TypeWrapper[] curExceptionTypes)
199
List<TypeWrapper> list = new List<TypeWrapper>();
200
foreach (TypeWrapper twNew in newExceptionTypes)
202
TypeWrapper match = null;
203
foreach (TypeWrapper twCur in curExceptionTypes)
205
if (twNew.IsAssignableTo(twCur))
207
if (match == null || twCur.IsAssignableTo(match))
213
if (match != null && !list.Contains(match))
218
return list.ToArray();
221
private static void CreateNoFail(CompilerClassLoader loader, TypeWrapper[] interfaces, List<ProxyMethod> methods)
223
DynamicClassLoader factory = (DynamicClassLoader)loader.GetTypeWrapperFactory();
224
TypeBuilder tb = factory.DefineProxy(proxyClass, interfaces);
225
AttributeHelper.SetImplementsAttribute(tb, interfaces);
226
CreateConstructor(tb);
227
for (int i = 0; i < methods.Count; i++)
229
methods[i].fb = tb.DefineField("m" + i, javaLangReflectMethod.TypeAsSignatureType, FieldAttributes.Private | FieldAttributes.Static);
231
foreach (ProxyMethod method in methods)
233
CreateMethod(loader, tb, method);
235
CreateStaticInitializer(tb, methods);
238
private static void CreateConstructor(TypeBuilder tb)
240
CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineConstructor(tb, MethodAttributes.Public, new Type[] { invocationHandlerClass.TypeAsSignatureType }));
241
ilgen.Emit(OpCodes.Ldarg_0);
242
ilgen.Emit(OpCodes.Ldarg_1);
243
proxyConstructor.EmitCall(ilgen);
244
ilgen.Emit(OpCodes.Ret);
248
private static void CreateMethod(CompilerClassLoader loader, TypeBuilder tb, ProxyMethod pm)
250
MethodBuilder mb = pm.mw.GetDefineMethodHelper().DefineMethod(loader.GetTypeWrapperFactory(), tb, pm.mw.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final);
251
List<string> exceptions = new List<string>();
252
foreach (TypeWrapper tw in pm.exceptions)
254
exceptions.Add(tw.Name);
256
AttributeHelper.SetThrowsAttribute(mb, exceptions.ToArray());
257
CodeEmitter ilgen = CodeEmitter.Create(mb);
258
ilgen.BeginExceptionBlock();
259
ilgen.Emit(OpCodes.Ldarg_0);
260
invocationHandlerField.EmitGet(ilgen);
261
ilgen.Emit(OpCodes.Ldarg_0);
262
ilgen.Emit(OpCodes.Ldsfld, pm.fb);
263
TypeWrapper[] parameters = pm.mw.GetParameters();
264
if (parameters.Length == 0)
266
ilgen.Emit(OpCodes.Ldnull);
270
ilgen.EmitLdc_I4(parameters.Length);
271
ilgen.Emit(OpCodes.Newarr, Types.Object);
272
for (int i = 0; i < parameters.Length; i++)
274
ilgen.Emit(OpCodes.Dup);
277
if (parameters[i].IsNonPrimitiveValueType)
279
parameters[i].EmitBox(ilgen);
281
else if (parameters[i].IsPrimitive)
283
Boxer.EmitBox(ilgen, parameters[i]);
285
ilgen.Emit(OpCodes.Stelem_Ref);
288
invokeMethod.EmitCallvirt(ilgen);
289
TypeWrapper returnType = pm.mw.ReturnType;
290
CodeEmitterLocal returnValue = null;
291
if (returnType != PrimitiveTypeWrapper.VOID)
293
returnValue = ilgen.DeclareLocal(returnType.TypeAsSignatureType);
294
if (returnType.IsNonPrimitiveValueType)
296
returnType.EmitUnbox(ilgen);
298
else if (returnType.IsPrimitive)
300
Boxer.EmitUnbox(ilgen, returnType);
302
else if (returnType != CoreClasses.java.lang.Object.Wrapper)
304
ilgen.EmitCastclass(returnType.TypeAsSignatureType);
306
ilgen.Emit(OpCodes.Stloc, returnValue);
308
CodeEmitterLabel returnLabel = ilgen.DefineLabel();
309
ilgen.EmitLeave(returnLabel);
310
// TODO consider using a filter here (but we would need to add filter support to CodeEmitter)
311
ilgen.BeginCatchBlock(Types.Exception);
313
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(Types.Exception));
314
CodeEmitterLocal exception = ilgen.DeclareLocal(Types.Exception);
315
ilgen.Emit(OpCodes.Stloc, exception);
316
CodeEmitterLabel rethrow = ilgen.DefineLabel();
317
ilgen.Emit(OpCodes.Ldloc, exception);
318
errorClass.EmitInstanceOf(null, ilgen);
319
ilgen.EmitBrtrue(rethrow);
320
ilgen.Emit(OpCodes.Ldloc, exception);
321
runtimeExceptionClass.EmitInstanceOf(null, ilgen);
322
ilgen.EmitBrtrue(rethrow);
323
foreach (TypeWrapper tw in pm.exceptions)
325
ilgen.Emit(OpCodes.Ldloc, exception);
326
tw.EmitInstanceOf(null, ilgen);
327
ilgen.EmitBrtrue(rethrow);
329
ilgen.Emit(OpCodes.Ldloc, exception);
330
undeclaredThrowableExceptionConstructor.EmitNewobj(ilgen);
331
ilgen.Emit(OpCodes.Throw);
332
ilgen.MarkLabel(rethrow);
333
ilgen.Emit(OpCodes.Rethrow);
334
ilgen.EndExceptionBlock();
335
ilgen.MarkLabel(returnLabel);
336
if (returnValue != null)
338
ilgen.Emit(OpCodes.Ldloc, returnValue);
340
ilgen.Emit(OpCodes.Ret);
344
private static void CreateStaticInitializer(TypeBuilder tb, List<ProxyMethod> methods)
346
CodeEmitter ilgen = CodeEmitter.Create(ReflectUtil.DefineTypeInitializer(tb));
347
CodeEmitterLocal callerID = ilgen.DeclareLocal(CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType);
348
TypeBuilder tbCallerID = DynamicTypeWrapper.FinishContext.EmitCreateCallerID(tb, ilgen);
349
ilgen.Emit(OpCodes.Stloc, callerID);
350
// HACK we shouldn't create the nested type here (the outer type must be created first)
351
tbCallerID.CreateType();
352
ilgen.BeginExceptionBlock();
353
foreach (ProxyMethod method in methods)
355
method.mw.DeclaringType.EmitClassLiteral(ilgen);
356
ilgen.Emit(OpCodes.Ldstr, method.mw.Name);
357
TypeWrapper[] parameters = method.mw.GetParameters();
358
ilgen.EmitLdc_I4(parameters.Length);
359
ilgen.Emit(OpCodes.Newarr, CoreClasses.java.lang.Class.Wrapper.TypeAsArrayType);
360
for (int i = 0; i < parameters.Length; i++)
362
ilgen.Emit(OpCodes.Dup);
364
parameters[i].EmitClassLiteral(ilgen);
365
ilgen.Emit(OpCodes.Stelem_Ref);
367
if (javaLangClass_getMethod.HasCallerID)
369
ilgen.Emit(OpCodes.Ldloc, callerID);
371
javaLangClass_getMethod.EmitCallvirt(ilgen);
372
ilgen.Emit(OpCodes.Stsfld, method.fb);
374
CodeEmitterLabel label = ilgen.DefineLabel();
375
ilgen.EmitLeave(label);
376
ilgen.BeginCatchBlock(javaLangNoSuchMethodException.TypeAsExceptionType);
377
javaLangThrowable_getMessage.EmitCallvirt(ilgen);
378
javaLangNoClassDefFoundErrorConstructor.EmitNewobj(ilgen);
379
ilgen.Emit(OpCodes.Throw);
380
ilgen.EndExceptionBlock();
381
ilgen.MarkLabel(label);
382
ilgen.Emit(OpCodes.Ret);
386
private sealed class ProxyMethod
388
internal readonly MethodWrapper mw;
389
internal readonly TypeWrapper[] exceptions;
390
internal FieldBuilder fb;
392
internal ProxyMethod(MethodWrapper mw, TypeWrapper[] exceptions)
395
this.exceptions = exceptions;
399
private static IEnumerable<MethodWrapper> GetInterfaceMethods(TypeWrapper tw)
401
Dictionary<string, MethodWrapper> methods = new Dictionary<string, MethodWrapper>();
402
foreach (MethodWrapper mw in tw.GetMethods())
404
methods.Add(mw.Name + mw.Signature, mw);
406
foreach (TypeWrapper iface in tw.Interfaces)
408
foreach (MethodWrapper mw in GetInterfaceMethods(iface))
410
if (!methods.ContainsKey(mw.Name + mw.Signature))
412
methods.Add(mw.Name + mw.Signature, mw);
416
return methods.Values;
419
private static TypeWrapper[] LoadTypes(ClassLoaderWrapper loader, string[] classes)
421
if (classes == null || classes.Length == 0)
423
return TypeWrapper.EmptyArray;
425
TypeWrapper[] tw = new TypeWrapper[classes.Length];
426
for (int i = 0; i < tw.Length; i++)
428
tw[i] = loader.LoadClassByDottedName(classes[i]);
433
private sealed class ProxyException : Exception
435
internal ProxyException(string msg)
444
private static readonly TypeWrapper javaLangByte;
445
private static readonly MethodWrapper byteValue;
446
private static readonly MethodWrapper valueOfByte;
447
private static readonly TypeWrapper javaLangBoolean;
448
private static readonly MethodWrapper booleanValue;
449
private static readonly MethodWrapper valueOfBoolean;
450
private static readonly TypeWrapper javaLangShort;
451
private static readonly MethodWrapper shortValue;
452
private static readonly MethodWrapper valueOfShort;
453
private static readonly TypeWrapper javaLangCharacter;
454
private static readonly MethodWrapper charValue;
455
private static readonly MethodWrapper valueOfCharacter;
456
private static readonly TypeWrapper javaLangInteger;
457
private static readonly MethodWrapper intValue;
458
private static readonly MethodWrapper valueOfInteger;
459
private static readonly TypeWrapper javaLangFloat;
460
private static readonly MethodWrapper floatValue;
461
private static readonly MethodWrapper valueOfFloat;
462
private static readonly TypeWrapper javaLangLong;
463
private static readonly MethodWrapper longValue;
464
private static readonly MethodWrapper valueOfLong;
465
private static readonly TypeWrapper javaLangDouble;
466
private static readonly MethodWrapper doubleValue;
467
private static readonly MethodWrapper valueOfDouble;
471
ClassLoaderWrapper bootClassLoader = ClassLoaderWrapper.GetBootstrapClassLoader();
472
javaLangByte = bootClassLoader.LoadClassByDottedNameFast("java.lang.Byte");
473
byteValue = javaLangByte.GetMethodWrapper("byteValue", "()B", false);
475
valueOfByte = javaLangByte.GetMethodWrapper("valueOf", "(B)Ljava.lang.Byte;", false);
477
javaLangBoolean = bootClassLoader.LoadClassByDottedNameFast("java.lang.Boolean");
478
booleanValue = javaLangBoolean.GetMethodWrapper("booleanValue", "()Z", false);
480
valueOfBoolean = javaLangBoolean.GetMethodWrapper("valueOf", "(Z)Ljava.lang.Boolean;", false);
481
valueOfBoolean.Link();
482
javaLangShort = bootClassLoader.LoadClassByDottedNameFast("java.lang.Short");
483
shortValue = javaLangShort.GetMethodWrapper("shortValue", "()S", false);
485
valueOfShort = javaLangShort.GetMethodWrapper("valueOf", "(S)Ljava.lang.Short;", false);
487
javaLangCharacter = bootClassLoader.LoadClassByDottedNameFast("java.lang.Character");
488
charValue = javaLangCharacter.GetMethodWrapper("charValue", "()C", false);
490
valueOfCharacter = javaLangCharacter.GetMethodWrapper("valueOf", "(C)Ljava.lang.Character;", false);
491
valueOfCharacter.Link();
492
javaLangInteger = bootClassLoader.LoadClassByDottedNameFast("java.lang.Integer");
493
intValue = javaLangInteger.GetMethodWrapper("intValue", "()I", false);
495
valueOfInteger = javaLangInteger.GetMethodWrapper("valueOf", "(I)Ljava.lang.Integer;", false);
496
valueOfInteger.Link();
497
javaLangFloat = bootClassLoader.LoadClassByDottedNameFast("java.lang.Float");
498
floatValue = javaLangFloat.GetMethodWrapper("floatValue", "()F", false);
500
valueOfFloat = javaLangFloat.GetMethodWrapper("valueOf", "(F)Ljava.lang.Float;", false);
502
javaLangLong = bootClassLoader.LoadClassByDottedNameFast("java.lang.Long");
503
longValue = javaLangLong.GetMethodWrapper("longValue", "()J", false);
505
valueOfLong = javaLangLong.GetMethodWrapper("valueOf", "(J)Ljava.lang.Long;", false);
507
javaLangDouble = bootClassLoader.LoadClassByDottedNameFast("java.lang.Double");
508
doubleValue = javaLangDouble.GetMethodWrapper("doubleValue", "()D", false);
510
valueOfDouble = javaLangDouble.GetMethodWrapper("valueOf", "(D)Ljava.lang.Double;", false);
511
valueOfDouble.Link();
514
internal static void EmitUnbox(CodeEmitter ilgen, TypeWrapper tw)
516
if (tw == PrimitiveTypeWrapper.BYTE)
518
javaLangByte.EmitCheckcast(null, ilgen);
519
byteValue.EmitCall(ilgen);
521
else if (tw == PrimitiveTypeWrapper.BOOLEAN)
523
javaLangBoolean.EmitCheckcast(null, ilgen);
524
booleanValue.EmitCall(ilgen);
526
else if (tw == PrimitiveTypeWrapper.SHORT)
528
javaLangShort.EmitCheckcast(null, ilgen);
529
shortValue.EmitCall(ilgen);
531
else if (tw == PrimitiveTypeWrapper.CHAR)
533
javaLangCharacter.EmitCheckcast(null, ilgen);
534
charValue.EmitCall(ilgen);
536
else if (tw == PrimitiveTypeWrapper.INT)
538
javaLangInteger.EmitCheckcast(null, ilgen);
539
intValue.EmitCall(ilgen);
541
else if (tw == PrimitiveTypeWrapper.FLOAT)
543
javaLangFloat.EmitCheckcast(null, ilgen);
544
floatValue.EmitCall(ilgen);
546
else if (tw == PrimitiveTypeWrapper.LONG)
548
javaLangLong.EmitCheckcast(null, ilgen);
549
longValue.EmitCall(ilgen);
551
else if (tw == PrimitiveTypeWrapper.DOUBLE)
553
javaLangDouble.EmitCheckcast(null, ilgen);
554
doubleValue.EmitCall(ilgen);
558
throw new InvalidOperationException();
562
internal static void EmitBox(CodeEmitter ilgen, TypeWrapper tw)
564
if (tw == PrimitiveTypeWrapper.BYTE)
566
valueOfByte.EmitCall(ilgen);
568
else if (tw == PrimitiveTypeWrapper.BOOLEAN)
570
valueOfBoolean.EmitCall(ilgen);
572
else if (tw == PrimitiveTypeWrapper.SHORT)
574
valueOfShort.EmitCall(ilgen);
576
else if (tw == PrimitiveTypeWrapper.CHAR)
578
valueOfCharacter.EmitCall(ilgen);
580
else if (tw == PrimitiveTypeWrapper.INT)
582
valueOfInteger.EmitCall(ilgen);
584
else if (tw == PrimitiveTypeWrapper.FLOAT)
586
valueOfFloat.EmitCall(ilgen);
588
else if (tw == PrimitiveTypeWrapper.LONG)
590
valueOfLong.EmitCall(ilgen);
592
else if (tw == PrimitiveTypeWrapper.DOUBLE)
594
valueOfDouble.EmitCall(ilgen);
598
throw new InvalidOperationException();