6939_20221212a.patch
new/src/com/goldencode/p2j/persist/RecordBuffer.java 2022-12-12 15:27:53 +0000 | ||
---|---|---|
1249 | 1249 |
** CA 20221010 Performance improvements - avoid the ProcedureData lookup, by keeping a parallel |
1250 | 1250 |
** stack of this data for THIS-PROCEDURE. Refs #6826 |
1251 | 1251 |
** OM 20221103 New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache. |
1252 |
** CA 20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda |
|
1253 |
** expressions to perform a direct call of the target, and avoid reflection. |
|
1252 | 1254 |
*/ |
1253 | 1255 | |
1254 | 1256 |
/* |
... | ... | |
11719 | 11721 |
* purposes. |
11720 | 11722 |
*/ |
11721 | 11723 |
private final class Handler |
11722 |
implements InvocationHandler |
|
11724 |
implements LambdaInvocationHandler
|
|
11723 | 11725 |
{ |
11724 | 11726 |
/** Exception thrown during validation of no-undo temp-tables, which must be deferred */ |
11725 | 11727 |
private ValidationException deferredValidationError = null; |
... | ... | |
11739 | 11741 |
* The target proxy object. |
11740 | 11742 |
* @param method |
11741 | 11743 |
* The method to be executed. |
11744 |
* @param f |
|
11745 |
* A function to invoke as target, when specified. |
|
11746 |
* @param c |
|
11747 |
* A consumer to invoke as target, when specified. |
|
11742 | 11748 |
* @param args |
11743 | 11749 |
* The method arguments. |
11744 | 11750 |
* |
... | ... | |
11747 | 11753 |
* @throws Throwable |
11748 | 11754 |
* For various reasons, like illegal access to fields, illegal arguments or target. |
11749 | 11755 |
*/ |
11750 |
public Object invoke(Object proxy, Method method, Object[] args) |
|
11756 |
@Override |
|
11757 |
public Object invoke(Object proxy, |
|
11758 |
final Method method, |
|
11759 |
BiFunction<Object, Object[], Object> f, |
|
11760 |
BiConsumer<Object, Object[]> c, |
|
11761 |
Object[] args) |
|
11751 | 11762 |
throws Throwable |
11752 | 11763 |
{ |
11753 | 11764 |
Object result = null; |
... | ... | |
11763 | 11774 |
Class<?> declClass = method.getDeclaringClass(); |
11764 | 11775 |
if (declClass.equals(BufferReference.class) || declClass.equals(Object.class)) |
11765 | 11776 |
{ |
11766 |
return Utils.invoke(method, RecordBuffer.this, args); |
|
11777 |
return Utils.invoke(method, f, c, RecordBuffer.this, args);
|
|
11767 | 11778 |
} |
11768 | 11779 |
|
11769 | 11780 |
// TODO: use something more reliable (but efficient) here to determine whether this is a setter |
... | ... | |
11777 | 11788 |
// to ensure all validation and flushing is tracked properly |
11778 | 11789 |
if (creatingBuffer != null && currentRecord != null && isSetter) |
11779 | 11790 |
{ |
11791 |
// this can't receive the lambdas, as they are statically linked with the this buffer's |
|
11792 |
// proxy types (a ClassCastException may be raised if the instance passed as first argument at |
|
11793 |
// the lambda is not of the expected type). |
|
11780 | 11794 |
return creatingBuffer.handler.invoke(proxy, method, args); |
11781 | 11795 |
} |
11782 | 11796 |
|
... | ... | |
11795 | 11809 |
// if a temporary record is set, use it to back the invocation |
11796 | 11810 |
if (tempRecord != null) |
11797 | 11811 |
{ |
11798 |
return Utils.invoke(method, tempRecord, args); |
|
11812 |
return Utils.invoke(method, f, c, tempRecord, args);
|
|
11799 | 11813 |
} |
11800 | 11814 |
|
11801 | 11815 |
if (isSetter) |
... | ... | |
11988 | 12002 |
} |
11989 | 12003 |
|
11990 | 12004 |
// invoke the method on the concrete DMO implementation |
11991 |
result = Utils.invoke(method, currentRecord, args); |
|
12005 |
result = Utils.invoke(method, f, c, currentRecord, args);
|
|
11992 | 12006 |
|
11993 | 12007 |
if (writeSnapshot != null && !currentRecord.checkState(DmoState.CHANGED)) |
11994 | 12008 |
{ |
... | ... | |
12120 | 12134 |
if (!ok) |
12121 | 12135 |
{ |
12122 | 12136 |
// the assign trigger vetoed: rolling back the change |
12123 |
Utils.invoke(method, currentRecord, oldVal); |
|
12137 |
Utils.invoke(method, f, c, currentRecord, oldVal);
|
|
12124 | 12138 |
processChange = false; |
12125 | 12139 |
} |
12126 | 12140 |
} |
... | ... | |
12795 | 12809 |
* {@link RecordBuffer} proxies. |
12796 | 12810 |
*/ |
12797 | 12811 |
private static class ArgumentBuffer |
12798 |
implements InvocationHandler |
|
12812 |
implements LambdaInvocationHandler
|
|
12799 | 12813 |
{ |
12800 | 12814 |
/** The backing buffer of this proxy, which should handle the majority of the methods */ |
12801 | 12815 |
private BufferImpl buffer; |
... | ... | |
12885 | 12899 |
* imply the DMO alias or buffer name, which will be handled here. |
12886 | 12900 |
*/ |
12887 | 12901 |
@Override |
12888 |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable |
|
12902 |
public Object invoke(Object proxy, |
|
12903 |
final Method method, |
|
12904 |
BiFunction<Object, Object[], Object> f, |
|
12905 |
BiConsumer<Object, Object[]> c, |
|
12906 |
Object[] args) |
|
12907 |
throws Throwable |
|
12889 | 12908 |
{ |
12890 | 12909 |
try |
12891 | 12910 |
{ |
... | ... | |
12906 | 12925 |
if (method.getName().equals("overrideName") || |
12907 | 12926 |
method.getName().equals("restoreName")) |
12908 | 12927 |
{ |
12909 |
return Utils.invoke(method, buffer, args); |
|
12928 |
return Utils.invoke(method, f, c, buffer, args);
|
|
12910 | 12929 |
} |
12911 | 12930 |
|
12912 | 12931 |
boolean overridden = buffer.overrideName(bufferName); |
... | ... | |
12914 | 12933 |
|
12915 | 12934 |
try |
12916 | 12935 |
{ |
12917 |
invokeReturn = Utils.invoke(method, buffer, args); |
|
12936 |
invokeReturn = Utils.invoke(method, f, c, buffer, args);
|
|
12918 | 12937 |
} |
12919 | 12938 |
finally |
12920 | 12939 |
{ |
... | ... | |
12944 | 12963 |
if (method.getName().equals("overrideDMOAlias") || |
12945 | 12964 |
method.getName().equals("restoreDMOAlias")) |
12946 | 12965 |
{ |
12947 |
return Utils.invoke(method, backBuffer, args); |
|
12966 |
return Utils.invoke(method, f, c, backBuffer, args);
|
|
12948 | 12967 |
} |
12949 | 12968 |
|
12950 | 12969 |
boolean overridden = backBuffer.overrideDMOAlias(dmoAlias); |
... | ... | |
12952 | 12971 |
|
12953 | 12972 |
try |
12954 | 12973 |
{ |
12955 |
invokeReturn = Utils.invoke(method, backBuffer, args); |
|
12974 |
invokeReturn = Utils.invoke(method, f, c, backBuffer, args);
|
|
12956 | 12975 |
} |
12957 | 12976 |
finally |
12958 | 12977 |
{ |
new/src/com/goldencode/p2j/persist/StaticDataSet.java 2022-12-12 15:27:59 +0000 | ||
---|---|---|
15 | 15 |
** OM 20211011 Improved debugging support for proxy objects. |
16 | 16 |
** CA 20220707 Used a static 'doNotProxy' field, instead of creating the array each time. |
17 | 17 |
** GES 20210501 Upgraded table-handle parameter processing to match API changes. |
18 |
** CA 20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions |
|
19 |
** to perform a direct call of the target, and avoid reflection. |
|
18 | 20 |
*/ |
19 | 21 | |
20 | 22 |
/* |
... | ... | |
77 | 79 |
import com.goldencode.proxy.*; |
78 | 80 |
import java.lang.reflect.*; |
79 | 81 |
import java.util.*; |
82 |
import java.util.function.*; |
|
80 | 83 | |
81 | 84 | |
82 | 85 |
/** |
... | ... | |
448 | 451 |
* {@code DataSet} or will throw errors. |
449 | 452 |
*/ |
450 | 453 |
static class Handler |
451 |
implements InvocationHandler |
|
454 |
implements LambdaInvocationHandler
|
|
452 | 455 |
{ |
453 | 456 |
/** |
454 | 457 |
* Processes a method invocation on a proxy instance and returns the result. This method |
... | ... | |
462 | 465 |
* proxy instance. The declaring class of the {@code Method} object will be the |
463 | 466 |
* interface that the method was declared in, which may be a superinterface of the |
464 | 467 |
* proxy interface that the proxy class inherits the method through. |
468 |
* @param f |
|
469 |
* A function to invoke as target, when specified. |
|
470 |
* @param c |
|
471 |
* A consumer to invoke as target, when specified. |
|
465 | 472 |
* @param args |
466 | 473 |
* an array of objects containing the values of the arguments passed in the method |
467 | 474 |
* invocation on the proxy instance, or {@code null} if interface method takes no |
... | ... | |
475 | 482 |
* @see UndeclaredThrowableException |
476 | 483 |
*/ |
477 | 484 |
@Override |
478 |
public Object invoke(Object proxy, Method method, Object[] args) |
|
485 |
public Object invoke(Object proxy, |
|
486 |
final Method method, |
|
487 |
BiFunction<Object, Object[], Object> f, |
|
488 |
BiConsumer<Object, Object[]> c, |
|
489 |
Object[] args) |
|
479 | 490 |
throws Throwable |
480 | 491 |
{ |
481 | 492 |
StaticDataSet dsp = (StaticDataSet) proxy; |
482 | 493 |
if (dsp.bound != null) |
483 | 494 |
{ |
484 |
return Utils.invoke(method, dsp.bound, args); |
|
495 |
return Utils.invoke(method, f, c, dsp.bound, args);
|
|
485 | 496 |
} |
486 | 497 |
|
487 | 498 |
String[] errParam = new String[1]; |
new/src/com/goldencode/p2j/persist/TemporaryBuffer.java 2022-12-12 15:28:04 +0000 | ||
---|---|---|
628 | 628 |
** IAS 20221103 Fixed support for a binded table parameter. |
629 | 629 |
** CA 20221206 If a REFERENCE-ONLY table is already bound to the same target table, do not |
630 | 630 |
** re-bind. |
631 |
** CA 20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda |
|
632 |
** expressions to perform a direct call of the target, and avoid reflection. |
|
631 | 633 |
*/ |
632 | 634 | |
633 | 635 |
/* |
... | ... | |
708 | 710 |
import com.goldencode.p2j.util.*; |
709 | 711 |
import com.goldencode.p2j.util.ErrorManager; |
710 | 712 |
import com.goldencode.p2j.util.LogHelper; |
711 |
import com.goldencode.proxy.ProxyFactory;
|
|
713 |
import com.goldencode.proxy.*;
|
|
712 | 714 |
import com.goldencode.util.*; |
713 | 715 | |
714 | 716 |
/** |
... | ... | |
8870 | 8872 |
* The handler to switch between references, when binding a definition table to another one. |
8871 | 8873 |
*/ |
8872 | 8874 |
private static class ReferenceProxy |
8873 |
implements InvocationHandler |
|
8875 |
implements LambdaInvocationHandler
|
|
8874 | 8876 |
{ |
8875 | 8877 |
/** The default properties - PK and any other properties in {@link TempRecord}. */ |
8876 | 8878 |
private static final Map<String, String> defaultProps; |
... | ... | |
9051 | 9053 |
* </ul> |
9052 | 9054 |
*/ |
9053 | 9055 |
@Override |
9054 |
public Object invoke(Object proxy, Method method, Object[] args) |
|
9056 |
public Object invoke(Object proxy, |
|
9057 |
Method method, |
|
9058 |
BiFunction<Object, Object[], Object> f, |
|
9059 |
BiConsumer<Object, Object[]> c, |
|
9060 |
Object[] args) |
|
9055 | 9061 |
throws Throwable |
9056 | 9062 |
{ |
9057 | 9063 |
Object instance = (bound == null ? def : bound); |
... | ... | |
9074 | 9080 |
declClass.equals(Object.class) || |
9075 | 9081 |
HandleResource.class.isAssignableFrom(declClass)) |
9076 | 9082 |
{ |
9077 |
return Utils.invoke(method, instance, args); |
|
9083 |
return Utils.invoke(method, f, c, instance, args);
|
|
9078 | 9084 |
} |
9079 | 9085 |
|
9080 | 9086 |
if (bound != null && boundBuffer.getDMOInterface() != defBuffer.getDMOInterface()) |
... | ... | |
9094 | 9100 |
|
9095 | 9101 |
Method mbound = (setter ? boundSetterByProp.get(boundProperty) |
9096 | 9102 |
: boundGetterByProp.get(boundProperty)); |
9097 |
|
|
9103 | ||
9104 |
// instance has switched |
|
9105 |
f = null; |
|
9106 |
c = null; |
|
9098 | 9107 |
method = mbound; |
9099 | 9108 |
} |
9100 | 9109 |
} |
... | ... | |
9107 | 9116 |
method.setAccessible(true); |
9108 | 9117 |
} |
9109 | 9118 |
|
9110 |
return Utils.invoke(method, instance, args); |
|
9119 |
return Utils.invoke(method, f, c, instance, args);
|
|
9111 | 9120 |
} |
9112 | 9121 |
catch (Throwable t) |
9113 | 9122 |
{ |
new/src/com/goldencode/p2j/util/Utils.java 2022-12-12 15:28:22 +0000 | ||
---|---|---|
184 | 184 |
** RFB 20220504 Minor javadoc fixes. |
185 | 185 |
** SBI 20220504 Fixed toChar(int, String) to return -1 if key code is not represented by |
186 | 186 |
** the given char set. |
187 |
** CA 20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda |
|
188 |
** expressions to perform a direct call of the target, and avoid reflection. |
|
187 | 189 |
*/ |
188 | 190 | |
189 | 191 |
/* |
... | ... | |
1601 | 1603 |
* More, the maps are identity-keyed, for fast access. |
1602 | 1604 |
* |
1603 | 1605 |
* @param method |
1606 |
* The method to execute, only if the function or consumer are both null. |
|
1607 |
* @param f |
|
1608 |
* When not-null, invoke this function and return its value. |
|
1609 |
* @param f |
|
1610 |
* When not-null, invoke this consumer and return null. |
|
1611 |
* @param instance |
|
1612 |
* The instance on which the call will be made; can be <code>null</code> for static methods. |
|
1613 |
* @param args |
|
1614 |
* The method's arguments. |
|
1615 |
* |
|
1616 |
* @return The method's return value. |
|
1617 |
* |
|
1618 |
* @throws IllegalAccessException |
|
1619 |
* @throws IllegalArgumentException |
|
1620 |
* @throws InvocationTargetException |
|
1621 |
*/ |
|
1622 |
public static Object invoke(Method method, |
|
1623 |
BiFunction<Object, Object[], Object> f, |
|
1624 |
BiConsumer<Object, Object[]> c, |
|
1625 |
Object instance, |
|
1626 |
Object... args) |
|
1627 |
throws IllegalAccessException, |
|
1628 |
IllegalArgumentException, |
|
1629 |
InvocationTargetException |
|
1630 |
{ |
|
1631 |
if (f != null) |
|
1632 |
{ |
|
1633 |
return f.apply(instance, args); |
|
1634 |
} |
|
1635 |
else if (c != null) |
|
1636 |
{ |
|
1637 |
c.accept(instance, args); |
|
1638 |
return null; |
|
1639 |
} |
|
1640 |
else |
|
1641 |
{ |
|
1642 |
return invoke(method, instance, args); |
|
1643 |
} |
|
1644 |
} |
|
1645 |
|
|
1646 |
/** |
|
1647 |
* Invoke the specified {@link Method} using ReflectASM instead of standard reflection. |
|
1648 |
* <p> |
|
1649 |
* ReflectASM related data is cached in {@link #METHOD_DATA}, thread-local, to avoid the concurrent access. |
|
1650 |
* More, the maps are identity-keyed, for fast access. |
|
1651 |
* |
|
1652 |
* @param method |
|
1604 | 1653 |
* The method to execute. |
1605 | 1654 |
* @param instance |
1606 | 1655 |
* The instance on which the call will be made; can be <code>null</code> for static methods. |
new/src/com/goldencode/proxy/ProxyAssembler.java 2022-12-12 15:29:09 +0000 | ||
---|---|---|
2 | 2 |
** Module : ProxyAssembler.java |
3 | 3 |
** Abstract : Assembles a dynamic proxy subclass. |
4 | 4 |
** |
5 |
** Copyright (c) 2010-2019, Golden Code Development Corporation.
|
|
5 |
** Copyright (c) 2010-2022, Golden Code Development Corporation.
|
|
6 | 6 |
** |
7 | 7 |
** -#- -I- --Date-- --------------------------------Description---------------------------------- |
8 | 8 |
** 001 ECF 20101119 Created initial version. |
... | ... | |
18 | 18 |
** 005 ECF 20160219 Added support for an optional plugin to override the assembly of arbitrary |
19 | 19 |
** methods. |
20 | 20 |
** 006 OM 20191123 Minor collection change. |
21 |
** 007 CA 20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions |
|
22 |
** to perform a direct call of the target, and avoid reflection. |
|
21 | 23 |
*/ |
22 | 24 |
/* |
23 | 25 |
** This program is free software: you can redistribute it and/or modify |
... | ... | |
213 | 215 |
/** Object used to create class fields, methods, etc. */ |
214 | 216 |
private final ClassWriter classWriter = |
215 | 217 |
new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); |
218 | ||
219 |
/** A plugin to assemble the 'invoke' method for the proxy handler. */ |
|
220 |
private final InvocationHandlerPlugin handlerPlugin; |
|
216 | 221 |
|
217 | 222 |
/** |
218 | 223 |
* Constructor. |
... | ... | |
236 | 241 |
* interface, else <code>false</code>. If <code>true</code>, no separate instance |
237 | 242 |
* field will be assembled to store the invocation handler reference, as the instance |
238 | 243 |
* of this class itself will act as the handler. |
244 |
* @param handlerPlugin |
|
245 |
* A plugin to assemble the 'invoke' method for the proxy handler. If <code>null</code>, |
|
246 |
* defaults to {@link DefaultHandlerPlugin}. |
|
239 | 247 |
*/ |
240 | 248 |
ProxyAssembler(String proxyName, |
241 | 249 |
String parentName, |
242 | 250 |
Class<?>[] interfaces, |
243 | 251 |
Method[] methods, |
244 | 252 |
ProxyAssemblerPlugin plugin, |
245 |
boolean parentIsHandler) |
|
253 |
boolean parentIsHandler, |
|
254 |
InvocationHandlerPlugin handlerPlugin) |
|
246 | 255 |
{ |
247 | 256 |
if (plugin == null) |
248 | 257 |
{ |
... | ... | |
265 | 274 |
this.methods = methods; |
266 | 275 |
this.plugin = plugin; |
267 | 276 |
this.parentIsHandler = parentIsHandler; |
277 | ||
278 |
if (handlerPlugin == null) |
|
279 |
{ |
|
280 |
handlerPlugin = new DefaultHandlerPlugin(); |
|
281 |
} |
|
282 |
this.handlerPlugin = handlerPlugin; |
|
268 | 283 |
} |
269 | 284 |
|
270 | 285 |
/** |
... | ... | |
301 | 316 |
} |
302 | 317 |
|
303 | 318 |
// create the proxy class |
304 |
classWriter.visit(V1_7,
|
|
305 |
ACC_PUBLIC | ACC_FINAL, |
|
319 |
classWriter.visit(V1_8,
|
|
320 |
ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
|
|
306 | 321 |
proxyName, |
307 | 322 |
null, |
308 | 323 |
parentName, |
... | ... | |
320 | 335 |
// add an instance member field for the InvocationHandler |
321 | 336 |
classWriter.visitField(ACC_PRIVATE | ACC_FINAL, |
322 | 337 |
"h", |
323 |
"Ljava/lang/reflect/InvocationHandler;",
|
|
338 |
"L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";",
|
|
324 | 339 |
null, |
325 | 340 |
null); |
326 | 341 |
} |
... | ... | |
356 | 371 |
ProxyUtils.getCaughtExceptionTypeNames(method)); |
357 | 372 |
|
358 | 373 |
mv.visitCode(); |
359 |
proxyMethod.assemble(classWriter, mv, method, i, parentIsHandler, proxyName); |
|
374 |
proxyMethod.assemble(classWriter, mv, method, i, parentIsHandler, proxyName, handlerPlugin);
|
|
360 | 375 |
mv.visitMaxs(0, 0); |
361 | 376 |
mv.visitEnd(); |
362 | 377 |
} |
... | ... | |
466 | 481 |
MethodVisitor mv = |
467 | 482 |
classWriter.visitMethod(ACC_PUBLIC, |
468 | 483 |
"<init>", |
469 |
"(Ljava/lang/reflect/InvocationHandler;)V",
|
|
484 |
"(L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";)V",
|
|
470 | 485 |
null, |
471 | 486 |
null); |
472 | 487 |
|
... | ... | |
486 | 501 |
mv.visitFieldInsn(PUTFIELD, |
487 | 502 |
proxyName, |
488 | 503 |
"h", |
489 |
"Ljava/lang/reflect/InvocationHandler;");
|
|
504 |
"L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";");
|
|
490 | 505 |
|
491 | 506 |
// return from c'tor |
492 | 507 |
mv.visitInsn(RETURN); |
... | ... | |
562 | 577 |
boolean parentIsHandler, |
563 | 578 |
String proxyName) |
564 | 579 |
{ |
580 |
assemble(classWriter, mv, method, methodIndex, parentIsHandler, proxyName, null); |
|
581 |
} |
|
582 |
|
|
583 |
/** |
|
584 |
* Assemble the bytecode which comprises the proxy method. |
|
585 |
* |
|
586 |
* @param classWriter |
|
587 |
* Class writer for proxy class. |
|
588 |
* @param mv |
|
589 |
* Method visitor for this method. |
|
590 |
* @param method |
|
591 |
* Method for which this proxy is being implemented. |
|
592 |
* @param methodIndex |
|
593 |
* Index of this method within the array of methods used by the invocation handler. |
|
594 |
* @param parentIsHandler |
|
595 |
* <code>true</code> if the parent class implements the |
|
596 |
* <code>InvocationHandler</code> interface, else <code>false</code>. If |
|
597 |
* <code>true</code>, no separate instance field will be assembled to store the |
|
598 |
* invocation handler reference, as the instance of this class itself will act as |
|
599 |
* the handler. |
|
600 |
* @param proxyName |
|
601 |
* Internal form of the proxy class' name. |
|
602 |
* @param handlerPlugin |
|
603 |
* A plugin to assemble the 'invoke' method for the proxy handler. If <code>null</code>, |
|
604 |
* defaults to {@link DefaultHandlerPlugin}. |
|
605 |
*/ |
|
606 |
public void assemble(ClassWriter classWriter, |
|
607 |
MethodVisitor mv, |
|
608 |
Method method, |
|
609 |
int methodIndex, |
|
610 |
boolean parentIsHandler, |
|
611 |
String proxyName, |
|
612 |
InvocationHandlerPlugin handlerPlugin) |
|
613 |
{ |
|
565 | 614 |
// load invocation handler from field "h" or as "this" if the proxy |
566 | 615 |
// itself is the invocation handler |
567 | 616 |
mv.visitVarInsn(ALOAD, 0); |
... | ... | |
570 | 619 |
mv.visitFieldInsn(GETFIELD, |
571 | 620 |
proxyName, |
572 | 621 |
"h", |
573 |
"Ljava/lang/reflect/InvocationHandler;");
|
|
622 |
"L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";");
|
|
574 | 623 |
} |
575 | 624 |
|
576 | 625 |
// load "this" object ref as the first parameter for later call to |
577 | 626 |
// InvocationHandler.invoke |
578 | 627 |
mv.visitVarInsn(ALOAD, 0); |
579 | 628 |
|
580 |
// load target method from array of methods in static field "ma" |
|
581 |
mv.visitFieldInsn(GETSTATIC, |
|
582 |
proxyName, |
|
583 |
"ma", |
|
584 |
"[Ljava/lang/reflect/Method;"); |
|
585 |
|
|
586 |
// target method is at index "index" in the "ma" array |
|
587 |
AsmUtils.pushInt(mv, methodIndex); |
|
588 |
mv.visitInsn(AALOAD); |
|
629 |
handlerPlugin.createInvocationArgs(classWriter, mv, proxyName, method, methodIndex); |
|
589 | 630 |
|
590 | 631 |
// create new array of Objects for method's arguments or push null |
591 | 632 |
// if there are no arguments |
... | ... | |
640 | 681 |
|
641 | 682 |
// call InvocationHandler.invoke |
642 | 683 |
mv.visitMethodInsn(INVOKEINTERFACE, |
643 |
"java/lang/reflect/InvocationHandler",
|
|
684 |
AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()),
|
|
644 | 685 |
"invoke", |
645 |
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
|
|
686 |
handlerPlugin.getInvokeSignature(),
|
|
646 | 687 |
true); |
647 | 688 |
|
648 | 689 |
// determine method's return type |
new/src/com/goldencode/proxy/ProxyFactory.java 2022-12-12 15:29:13 +0000 | ||
---|---|---|
39 | 39 |
** equal 'doNotProxy' array, as the cache is kept in an IdentityHashMap. |
40 | 40 |
** CA 20221006 Use a Supplier for the proxy plugin instance, as when the proxy is already created, there |
41 | 41 |
** is no need to use this instance. Refs #6822 |
42 |
** CA 20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions |
|
43 |
** to perform a direct call of the target, and avoid reflection. |
|
42 | 44 |
*/ |
43 | 45 |
/* |
44 | 46 |
** This program is free software: you can redistribute it and/or modify |
... | ... | |
555 | 557 |
String proxyName = makeProxyClassName(parent, parentName); |
556 | 558 |
Method[] methods = gatherMethods(parent, proxyParent, interfaces, declaredOnly, doNotProxy); |
557 | 559 |
|
560 |
InvocationHandlerPlugin handlerPlugin = handler instanceof LambdaInvocationHandler |
|
561 |
? new LambdaHandlerPlugin() |
|
562 |
: new DefaultHandlerPlugin(); |
|
563 |
|
|
558 | 564 |
// assemble code for the proxy class |
559 | 565 |
ProxyAssembler asm = new ProxyAssembler(proxyName, |
560 | 566 |
parentName, |
561 | 567 |
interfaces, |
562 | 568 |
methods, |
563 | 569 |
(plugin == null ? null : plugin.get()), |
564 |
proxyIsHandler); |
|
570 |
proxyIsHandler, |
|
571 |
handlerPlugin); |
|
565 | 572 |
byte[] code = asm.assembleClass(); |
566 | 573 |
|
567 | 574 |
/* |
... | ... | |
574 | 581 |
? proxyName.substring(pos + 1) |
575 | 582 |
: proxyName); |
576 | 583 |
fname = fname + ".class"; |
584 |
System.out.println(parent.toString()); |
|
585 |
for (int i = 0; i < interfaces.length; i++) |
|
586 |
System.out.println(interfaces[i].toString()); |
|
577 | 587 |
System.out.println("Proxy class file written to: " + fname); |
578 | 588 |
fos = new FileOutputStream(fname); |
579 | 589 |
fos.write(code); |
... | ... | |
605 | 615 |
|
606 | 616 |
if (!proxyIsHandler) |
607 | 617 |
{ |
608 |
classCtors.put(clazz, clazz.getDeclaredConstructor(InvocationHandler.class));
|
|
618 |
classCtors.put(clazz, clazz.getDeclaredConstructor(handlerPlugin.getHandlerClass()));
|
|
609 | 619 |
} |
610 | 620 |
} |
611 | 621 |
|