6819_20221220a.patch
new/src/com/goldencode/p2j/persist/RecordBuffer.java 2022-12-18 12:29:40 +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. |
|
11742 | 11746 |
* @param args |
11743 | 11747 |
* The method arguments. |
11744 | 11748 |
* |
... | ... | |
11747 | 11751 |
* @throws Throwable |
11748 | 11752 |
* For various reasons, like illegal access to fields, illegal arguments or target. |
11749 | 11753 |
*/ |
11750 |
public Object invoke(Object proxy, Method method, Object[] args) |
|
11754 |
@Override |
|
11755 |
public Object invoke(Object proxy, |
|
11756 |
final Method method, |
|
11757 |
BiFunction<Object, Object[], Object> f, |
|
11758 |
Object[] args) |
|
11751 | 11759 |
throws Throwable |
11752 | 11760 |
{ |
11753 | 11761 |
Object result = null; |
... | ... | |
11763 | 11771 |
Class<?> declClass = method.getDeclaringClass(); |
11764 | 11772 |
if (declClass.equals(BufferReference.class) || declClass.equals(Object.class)) |
11765 | 11773 |
{ |
11766 |
return Utils.invoke(method, RecordBuffer.this, args); |
|
11774 |
return Utils.invoke(method, f, RecordBuffer.this, args);
|
|
11767 | 11775 |
} |
11768 | 11776 |
|
11769 | 11777 |
// TODO: use something more reliable (but efficient) here to determine whether this is a setter |
... | ... | |
11777 | 11785 |
// to ensure all validation and flushing is tracked properly |
11778 | 11786 |
if (creatingBuffer != null && currentRecord != null && isSetter) |
11779 | 11787 |
{ |
11788 |
// this can't receive the lambdas, as they are statically linked with the this buffer's |
|
11789 |
// proxy types (a ClassCastException may be raised if the instance passed as first argument at |
|
11790 |
// the lambda is not of the expected type). |
|
11780 | 11791 |
return creatingBuffer.handler.invoke(proxy, method, args); |
11781 | 11792 |
} |
11782 | 11793 |
|
... | ... | |
11795 | 11806 |
// if a temporary record is set, use it to back the invocation |
11796 | 11807 |
if (tempRecord != null) |
11797 | 11808 |
{ |
11798 |
return Utils.invoke(method, tempRecord, args); |
|
11809 |
return Utils.invoke(method, f, tempRecord, args);
|
|
11799 | 11810 |
} |
11800 | 11811 |
|
11801 | 11812 |
if (isSetter) |
... | ... | |
11988 | 11999 |
} |
11989 | 12000 |
|
11990 | 12001 |
// invoke the method on the concrete DMO implementation |
11991 |
result = Utils.invoke(method, currentRecord, args); |
|
12002 |
result = Utils.invoke(method, f, currentRecord, args);
|
|
11992 | 12003 |
|
11993 | 12004 |
if (writeSnapshot != null && !currentRecord.checkState(DmoState.CHANGED)) |
11994 | 12005 |
{ |
... | ... | |
12120 | 12131 |
if (!ok) |
12121 | 12132 |
{ |
12122 | 12133 |
// the assign trigger vetoed: rolling back the change |
12123 |
Utils.invoke(method, currentRecord, oldVal); |
|
12134 |
Utils.invoke(method, f, currentRecord, oldVal);
|
|
12124 | 12135 |
processChange = false; |
12125 | 12136 |
} |
12126 | 12137 |
} |
... | ... | |
12795 | 12806 |
* {@link RecordBuffer} proxies. |
12796 | 12807 |
*/ |
12797 | 12808 |
private static class ArgumentBuffer |
12798 |
implements InvocationHandler |
|
12809 |
implements LambdaInvocationHandler
|
|
12799 | 12810 |
{ |
12800 | 12811 |
/** The backing buffer of this proxy, which should handle the majority of the methods */ |
12801 | 12812 |
private BufferImpl buffer; |
... | ... | |
12885 | 12896 |
* imply the DMO alias or buffer name, which will be handled here. |
12886 | 12897 |
*/ |
12887 | 12898 |
@Override |
12888 |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable |
|
12899 |
public Object invoke(Object proxy, |
|
12900 |
final Method method, |
|
12901 |
BiFunction<Object, Object[], Object> f, |
|
12902 |
Object[] args) |
|
12903 |
throws Throwable |
|
12889 | 12904 |
{ |
12890 | 12905 |
try |
12891 | 12906 |
{ |
... | ... | |
12906 | 12921 |
if (method.getName().equals("overrideName") || |
12907 | 12922 |
method.getName().equals("restoreName")) |
12908 | 12923 |
{ |
12909 |
return Utils.invoke(method, buffer, args); |
|
12924 |
return Utils.invoke(method, f, buffer, args);
|
|
12910 | 12925 |
} |
12911 | 12926 |
|
12912 | 12927 |
boolean overridden = buffer.overrideName(bufferName); |
... | ... | |
12914 | 12929 |
|
12915 | 12930 |
try |
12916 | 12931 |
{ |
12917 |
invokeReturn = Utils.invoke(method, buffer, args); |
|
12932 |
invokeReturn = Utils.invoke(method, f, buffer, args);
|
|
12918 | 12933 |
} |
12919 | 12934 |
finally |
12920 | 12935 |
{ |
... | ... | |
12944 | 12959 |
if (method.getName().equals("overrideDMOAlias") || |
12945 | 12960 |
method.getName().equals("restoreDMOAlias")) |
12946 | 12961 |
{ |
12947 |
return Utils.invoke(method, backBuffer, args); |
|
12962 |
return Utils.invoke(method, f, backBuffer, args);
|
|
12948 | 12963 |
} |
12949 | 12964 |
|
12950 | 12965 |
boolean overridden = backBuffer.overrideDMOAlias(dmoAlias); |
... | ... | |
12952 | 12967 |
|
12953 | 12968 |
try |
12954 | 12969 |
{ |
12955 |
invokeReturn = Utils.invoke(method, backBuffer, args); |
|
12970 |
invokeReturn = Utils.invoke(method, f, backBuffer, args);
|
|
12956 | 12971 |
} |
12957 | 12972 |
finally |
12958 | 12973 |
{ |
new/src/com/goldencode/p2j/persist/StaticDataSet.java 2022-12-18 12:29:52 +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. |
|
465 | 470 |
* @param args |
466 | 471 |
* an array of objects containing the values of the arguments passed in the method |
467 | 472 |
* invocation on the proxy instance, or {@code null} if interface method takes no |
... | ... | |
475 | 480 |
* @see UndeclaredThrowableException |
476 | 481 |
*/ |
477 | 482 |
@Override |
478 |
public Object invoke(Object proxy, Method method, Object[] args) |
|
483 |
public Object invoke(Object proxy, |
|
484 |
final Method method, |
|
485 |
BiFunction<Object, Object[], Object> f, |
|
486 |
Object[] args) |
|
479 | 487 |
throws Throwable |
480 | 488 |
{ |
481 | 489 |
StaticDataSet dsp = (StaticDataSet) proxy; |
482 | 490 |
if (dsp.bound != null) |
483 | 491 |
{ |
484 |
return Utils.invoke(method, dsp.bound, args); |
|
492 |
return Utils.invoke(method, f, dsp.bound, args);
|
|
485 | 493 |
} |
486 | 494 |
|
487 | 495 |
String[] errParam = new String[1]; |
new/src/com/goldencode/p2j/persist/TemporaryBuffer.java 2022-12-18 12:30:10 +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 |
Object[] args) |
|
9055 | 9060 |
throws Throwable |
9056 | 9061 |
{ |
9057 | 9062 |
Object instance = (bound == null ? def : bound); |
... | ... | |
9074 | 9079 |
declClass.equals(Object.class) || |
9075 | 9080 |
HandleResource.class.isAssignableFrom(declClass)) |
9076 | 9081 |
{ |
9077 |
return Utils.invoke(method, instance, args); |
|
9082 |
return Utils.invoke(method, f, instance, args);
|
|
9078 | 9083 |
} |
9079 | 9084 |
|
9080 | 9085 |
if (bound != null && boundBuffer.getDMOInterface() != defBuffer.getDMOInterface()) |
... | ... | |
9094 | 9099 |
|
9095 | 9100 |
Method mbound = (setter ? boundSetterByProp.get(boundProperty) |
9096 | 9101 |
: boundGetterByProp.get(boundProperty)); |
9097 |
|
|
9102 | ||
9103 |
// instance has switched |
|
9104 |
f = null; |
|
9098 | 9105 |
method = mbound; |
9099 | 9106 |
} |
9100 | 9107 |
} |
... | ... | |
9107 | 9114 |
method.setAccessible(true); |
9108 | 9115 |
} |
9109 | 9116 |
|
9110 |
return Utils.invoke(method, instance, args); |
|
9117 |
return Utils.invoke(method, f, instance, args);
|
|
9111 | 9118 |
} |
9112 | 9119 |
catch (Throwable t) |
9113 | 9120 |
{ |
new/src/com/goldencode/p2j/util/ControlFlowOps.java 2022-12-20 11:06:30 +0000 | ||
---|---|---|
273 | 273 |
** - for typed FUNCTION calls, convert the returned value to the expected type. |
274 | 274 |
** - fixed caching issues when the call does not complete due to errors or for |
275 | 275 |
** recursive calls. |
276 |
** CA 20221220 Use lambdas to execute all public methods in an external program. |
|
276 | 277 |
*/ |
277 | 278 | |
278 | 279 |
/* |
... | ... | |
957 | 958 |
caller.callerInstance = reference; |
958 | 959 |
caller.wa = wa; |
959 | 960 |
caller.mthd = lastInvoke.method; |
961 |
caller.lambda = lastInvoke.lambda; |
|
960 | 962 |
caller.methodName = lastInvoke.method.getName(); |
961 | 963 |
caller.ie = null; |
962 | 964 |
caller.iename = cfg.getTarget(); |
... | ... | |
1003 | 1005 | |
1004 | 1006 |
try |
1005 | 1007 |
{ |
1006 |
BaseDataType res = (BaseDataType) lastInvoke.method.invoke(reference, args); |
|
1008 |
BaseDataType res = (BaseDataType) (lastInvoke.lambda == null |
|
1009 |
? lastInvoke.method.invoke(reference, args) |
|
1010 |
: lastInvoke.lambda.apply(reference, args)); |
|
1007 | 1011 |
res = checkInvokeResult(res, iename, cfg.isFunction(), cfg.isDynamicFunction()); |
1008 | 1012 |
|
1009 | 1013 |
if (cls != null && cfg.isFunction()) |
... | ... | |
8539 | 8543 |
boolean externalProcedure = caller.getPhandle() == null; |
8540 | 8544 |
Object instance = externalProcedure ? caller.getClassName() : caller.getCallerInstance(); |
8541 | 8545 |
Method mthd = externalProcedure ? null : caller.mthd; |
8546 |
BiFunction<Object, Object[], Object> lambda = externalProcedure ? null : caller.lambda; |
|
8542 | 8547 |
|
8543 | 8548 |
if (args != null && args.length > 0) |
8544 | 8549 |
{ |
... | ... | |
8556 | 8561 |
eventProcedureName, |
8557 | 8562 |
eventProcedureContext, |
8558 | 8563 |
instance, |
8559 |
mthd, |
|
8564 |
mthd, |
|
8565 |
lambda, |
|
8560 | 8566 |
argumentTypes); |
8561 | 8567 |
|
8562 | 8568 |
Map<InvokeConfig, LastInvokeDetails> thisCallSite = |
... | ... | |
8602 | 8608 |
private final Method method; |
8603 | 8609 |
|
8604 | 8610 |
/** |
8611 |
* When not-null, a lambda target to execute directly the call, without relying on the {@link #method}. |
|
8612 |
*/ |
|
8613 |
private final BiFunction<Object, Object[], Object> lambda; |
|
8614 |
|
|
8615 |
/** |
|
8605 | 8616 |
* Create a new instance. |
8606 | 8617 |
* |
8607 | 8618 |
* @param legacyName |
... | ... | |
8621 | 8632 |
* {@link #method} is being invoked. |
8622 | 8633 |
* @param method |
8623 | 8634 |
* The Java method to invoke. |
8635 |
* @param lambda |
|
8636 |
* When not-null, a lambda target to execute directly the call, without relying on the |
|
8637 |
* {@link #method}. |
|
8624 | 8638 |
* @param argumentTypes |
8625 | 8639 |
* The argument types for this call. |
8626 | 8640 |
*/ |
... | ... | |
8632 | 8646 |
Object eventProcedureContext, |
8633 | 8647 |
Object reference, |
8634 | 8648 |
Method method, |
8649 |
BiFunction<Object, Object[], Object> lambda, |
|
8635 | 8650 |
Class<?>[] argumentTypes) |
8636 | 8651 |
{ |
8637 | 8652 |
this.legacyName = legacyName; |
... | ... | |
8646 | 8661 |
|
8647 | 8662 |
this.reference = (reference == null ? null : new WeakReference<>(reference)); |
8648 | 8663 |
this.method = method; |
8664 |
this.lambda = lambda; |
|
8649 | 8665 |
} |
8650 | 8666 |
|
8651 | 8667 |
/** |
... | ... | |
9166 | 9182 |
protected WorkArea wa; |
9167 | 9183 |
|
9168 | 9184 |
/** The {@link Method} instance for this internal entry. */ |
9169 |
private Method mthd = null; |
|
9170 |
|
|
9171 |
/** The ReflectASM access point to this method. */ |
|
9172 |
private MethodAccess access = null; |
|
9173 |
|
|
9174 |
/** The method index in the MethodAccess instance. */ |
|
9175 |
private int index = -1; |
|
9185 |
protected Method mthd = null; |
|
9186 |
|
|
9187 |
protected BiFunction<Object, Object[], Object> lambda = null; |
|
9188 |
|
|
9189 |
/** Already resolved internal entry. */ |
|
9190 |
protected InternalEntry ie = null; |
|
9176 | 9191 |
|
9177 | 9192 |
/** The handle associated with the {@link #callerInstance}. */ |
9178 | 9193 |
private handle phandle; |
... | ... | |
9180 | 9195 |
/** Worker which performs the {@link ProcedureManager.ProcedureHelper#pushCalleeInfo} call. */ |
9181 | 9196 |
private Runnable pushWorker; |
9182 | 9197 |
|
9183 |
/** Already resolved internal entry (used when invoking OO methods). */ |
|
9184 |
private InternalEntry ie = null; |
|
9185 |
|
|
9186 | 9198 |
/** |
9187 | 9199 |
* Basic c'tor. |
9188 | 9200 |
* |
... | ... | |
9331 | 9343 |
pushWorker.run(); |
9332 | 9344 |
wa.bm.markPendingDynCall(); |
9333 | 9345 |
|
9334 |
if (access != null)
|
|
9346 |
if (lambda != null)
|
|
9335 | 9347 |
{ |
9336 |
return access.invoke(instance, index, param);
|
|
9348 |
return lambda.apply(instance, param);
|
|
9337 | 9349 |
} |
9338 | 9350 |
else |
9339 | 9351 |
{ |
... | ... | |
9538 | 9550 |
if (value.method != null) |
9539 | 9551 |
{ |
9540 | 9552 |
caller.mthd = value.method; |
9541 |
caller.access = value.access; |
|
9542 |
caller.index = value.index; |
|
9553 |
caller.lambda = value.lambda; |
|
9543 | 9554 |
} |
9544 | 9555 |
|
9545 | 9556 |
return value.returnValue; |
... | ... | |
9552 | 9563 |
method = cls.getMethod(caller.methodName, signature); |
9553 | 9564 |
rv = ArgValidationErrors.NO_ERROR; |
9554 | 9565 |
|
9555 |
value = putCache(key, method, rv, true); |
|
9566 |
value = putCache(key, method, null, rv, true);
|
|
9556 | 9567 | |
9557 | 9568 |
caller.mthd = value.method; |
9558 |
caller.access = value.access; |
|
9559 |
caller.index = value.index; |
|
9569 |
caller.lambda = value.lambda; |
|
9560 | 9570 |
|
9561 | 9571 |
return rv; |
9562 | 9572 |
} |
... | ... | |
9571 | 9581 |
method = cls.getDeclaredMethod(caller.methodName, signature); |
9572 | 9582 |
rv = ArgValidationErrors.NO_ERROR; |
9573 | 9583 |
|
9574 |
value = putCache(key, method, rv, true); |
|
9584 |
value = putCache(key, method, null, rv, true);
|
|
9575 | 9585 |
|
9576 | 9586 |
caller.mthd = value.method; |
9577 |
caller.access = value.access; |
|
9578 |
caller.index = value.index; |
|
9587 |
caller.lambda = value.lambda; |
|
9579 | 9588 | |
9580 | 9589 |
return rv; |
9581 | 9590 |
} |
... | ... | |
9777 | 9786 |
extentInfo[0] = parExtent ? paramExtent : 0; |
9778 | 9787 |
|
9779 | 9788 |
// cache failed result as well, so we don't need to repeat validation |
9780 |
putCache(key, null, rv, true); |
|
9789 |
putCache(key, null, null, rv, true);
|
|
9781 | 9790 |
|
9782 | 9791 |
return rv; |
9783 | 9792 |
} |
... | ... | |
10197 | 10206 |
|
10198 | 10207 |
if (value == null) |
10199 | 10208 |
{ |
10200 |
value = putCache(key, m, rv, false); |
|
10209 |
value = putCache(key, m, m.equals(caller.mthd) ? caller.lambda : null, rv, false);
|
|
10201 | 10210 |
} |
10202 | 10211 |
|
10203 | 10212 |
caller.mthd = value.method; |
10204 |
caller.access = value.access; |
|
10205 |
caller.index = value.index; |
|
10213 |
caller.lambda = value.lambda; |
|
10206 | 10214 |
|
10207 | 10215 |
return rv; |
10208 | 10216 |
} |
... | ... | |
10215 | 10223 |
: ArgValidationErrors.INCORRECT_TYPES; |
10216 | 10224 |
|
10217 | 10225 |
// cache failed result as well, so we don't need to repeat validation |
10218 |
putCache(key, null, rv, true); |
|
10226 |
putCache(key, null, null, rv, true);
|
|
10219 | 10227 |
|
10220 | 10228 |
return rv; |
10221 | 10229 |
} |
... | ... | |
10351 | 10359 |
* @param method |
10352 | 10360 |
* Method found matching a given name and signature, or {@code null} if no |
10353 | 10361 |
* match was found. |
10362 |
* @param lambda |
|
10363 |
* When not-null, a lambda target to execute directly the call, without relying on the method. |
|
10354 | 10364 |
* @param returnValue |
10355 | 10365 |
* Enumerated return value for {@link #valid(boolean, String, int[], Object...)} |
10356 | 10366 |
* method. |
... | ... | |
10362 | 10372 |
* @return the created cache. |
10363 | 10373 |
*/ |
10364 | 10374 |
private static CacheValue putCache(CacheKey key, |
10365 |
Method method, |
|
10366 |
ArgValidationErrors returnValue, |
|
10367 |
boolean exactMatch) |
|
10375 |
Method method, |
|
10376 |
BiFunction<Object, Object[], Object> lambda, |
|
10377 |
ArgValidationErrors returnValue, |
|
10378 |
boolean exactMatch) |
|
10368 | 10379 |
{ |
10369 |
CacheValue value = new CacheValue(method, returnValue, exactMatch); |
|
10380 |
CacheValue value = new CacheValue(method, lambda, returnValue, exactMatch);
|
|
10370 | 10381 |
|
10371 | 10382 |
synchronized (cache) |
10372 | 10383 |
{ |
... | ... | |
10496 | 10507 |
/** Method, if any, else <code>null</code> if none was found */ |
10497 | 10508 |
final Method method; |
10498 | 10509 | |
10499 |
/** The ReflectASM access point to this method. */
|
|
10500 |
final MethodAccess access;
|
|
10501 |
|
|
10502 |
/** The method index in the MethodAccess instance. */
|
|
10503 |
final int index;
|
|
10510 |
/** |
|
10511 |
* When not-null, a lambda target to execute directly the call, without relying on the
|
|
10512 |
* {@link #method}. |
|
10513 |
*/ |
|
10514 |
final BiFunction<Object, Object[], Object> lambda;
|
|
10504 | 10515 |
|
10505 | 10516 |
/** |
10506 | 10517 |
* Enumerated return value for {@link |
... | ... | |
10516 | 10527 |
* |
10517 | 10528 |
* @param method |
10518 | 10529 |
* Method, if any, else <code>null</code> if none was found. |
10530 |
* @param lambda |
|
10531 |
* When not-null, a lambda target to execute directly the call, without relying on the |
|
10532 |
* {@link #method}. |
|
10519 | 10533 |
* @param returnValue |
10520 | 10534 |
* Enumerated return value for {@link |
10521 | 10535 |
* InternalEntryCaller#valid(boolean, String, int[], Object...)}. |
... | ... | |
10523 | 10537 |
* Flag indicating whether signature of caller's parameters exactly matches |
10524 | 10538 |
* method. |
10525 | 10539 |
*/ |
10526 |
CacheValue(Method method, ArgValidationErrors returnValue, boolean exactMatch) |
|
10540 |
CacheValue(Method method, BiFunction<Object, Object[], Object> lambda, ArgValidationErrors returnValue, boolean exactMatch)
|
|
10527 | 10541 |
{ |
10528 | 10542 |
this.method = method; |
10529 |
if (method != null) |
|
10530 |
{ |
|
10531 |
if (!Modifier.isPrivate(method.getModifiers())) |
|
10532 |
{ |
|
10533 |
this.access = MethodAccess.get(method.getDeclaringClass()); |
|
10534 |
this.index = access.getIndex(method.getName(), method.getParameterTypes()); |
|
10535 |
} |
|
10536 |
else |
|
10537 |
{ |
|
10538 |
this.access = null; |
|
10539 |
this.index = -1; |
|
10540 |
} |
|
10541 |
} |
|
10542 |
else |
|
10543 |
{ |
|
10544 |
this.access = null; |
|
10545 |
this.index = -1; |
|
10546 |
} |
|
10543 |
this.lambda = lambda; |
|
10547 | 10544 |
this.returnValue = returnValue; |
10548 | 10545 |
this.exactMatch = exactMatch; |
10549 | 10546 |
} |
... | ... | |
10747 | 10744 |
* |
10748 | 10745 |
* @param wa |
10749 | 10746 |
* The context-local state in {@link WorkArea}. |
10747 |
* @param name |
|
10748 |
* The external program name. |
|
10750 | 10749 |
* @param className |
10751 | 10750 |
* The class name associated with the external program. |
10752 | 10751 |
* @param persistent |
... | ... | |
10755 | 10754 |
* @throws Exception |
10756 | 10755 |
* If the class can't be resolved. |
10757 | 10756 |
*/ |
10758 |
public ExternalProcedureCaller(WorkArea wa, String className, boolean persistent) |
|
10757 |
public ExternalProcedureCaller(WorkArea wa, String name, String className, boolean persistent)
|
|
10759 | 10758 |
throws Exception |
10760 | 10759 |
{ |
10761 | 10760 |
super(wa); |
10761 | ||
10762 |
this.ie = SourceNameMapper.getInternalEntry(name, null, false); |
|
10763 |
this.mthd = (ie == null ? null : ie.getMethod()); |
|
10764 |
this.lambda = (ie == null ? null : ie.getLambda()); |
|
10762 | 10765 |
|
10763 | 10766 |
this.methodName = "execute"; |
10764 | 10767 |
this.className = className; |
... | ... | |
10965 | 10968 |
wa.currentInvoke = null; |
10966 | 10969 |
res = new MapToNotFound(wa, mapTo); |
10967 | 10970 |
} |
10968 |
|
|
10971 |
else |
|
10972 |
{ |
|
10973 |
res.mthd = ie.getMethod(); |
|
10974 |
res.lambda = ie.getLambda(); |
|
10975 |
} |
|
10976 | ||
10969 | 10977 |
res.ie = ie; |
10970 | 10978 |
return res; |
10971 | 10979 |
} |
... | ... | |
10983 | 10991 |
|
10984 | 10992 |
caller = new InternalEntryCaller(wa, hcaller, ie.jname, ieName); |
10985 | 10993 |
caller.ie = ie; |
10994 |
caller.mthd = ie.getMethod(); |
|
10995 |
caller.lambda = ie.getLambda(); |
|
10986 | 10996 |
} |
10987 | 10997 |
/* |
10988 | 10998 |
else if (!superCall) |
... | ... | |
11163 | 11173 |
// name to any errors generated during instantiation (i.e. shared vars error msgs |
11164 | 11174 |
// need to know the procedure name where they are defined, if an error happens) |
11165 | 11175 |
wa.pm.setInstantingExternalProgram(name); |
11166 |
caller = new ExternalProcedureCaller(wa, className, persistent); |
|
11176 |
caller = new ExternalProcedureCaller(wa, name, className, persistent);
|
|
11167 | 11177 |
} |
11168 | 11178 |
catch (Throwable t) |
11169 | 11179 |
{ |
new/src/com/goldencode/p2j/util/InternalEntry.java 2022-12-20 11:06:40 +0000 | ||
---|---|---|
31 | 31 |
** 016 ME 20200810 Added qualified name support in signature processing. |
32 | 32 |
** CA 20210218 Fixed signature processing for qualified return type. |
33 | 33 |
** CA 20220909 Fixed TABLE-HANDLE, DATASET, DATASET-HANDLE parameters reported by GET-SIGNATURE. |
34 |
** CA 20221220 Use lambdas to execute all public methods in an external program. Fixed synchronization |
|
35 |
** in getMethod() and getLambda(), when calling SourceNameMapper.buildLegacyProgram. |
|
34 | 36 |
*/ |
35 | 37 | |
36 | 38 |
/* |
... | ... | |
90 | 92 | |
91 | 93 |
import java.lang.reflect.*; |
92 | 94 |
import java.util.*; |
95 |
import java.util.function.*; |
|
93 | 96 | |
94 | 97 |
import com.goldencode.p2j.util.SourceNameMapper.ExternalProgram; |
95 | 98 | |
... | ... | |
269 | 272 |
* Flag indicating if this is a IN SUPER internal entry (when set to <code>true</code>). |
270 | 273 |
* Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}. |
271 | 274 |
*/ |
272 |
private Boolean isSuper = null;
|
|
275 |
private boolean isSuper = false;
|
|
273 | 276 |
|
274 | 277 |
/** |
275 | 278 |
* Flag indicating if this is a IN handle internal entry (when set to <code>true</code>). |
276 | 279 |
* Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}. |
277 | 280 |
*/ |
278 |
private Boolean inHandle = null;
|
|
281 |
private boolean inHandle = false;
|
|
279 | 282 |
|
280 | 283 |
/** |
281 | 284 |
* Flag indicating if this is a PRIVATE internal entry (when set to <code>true</code>). |
282 | 285 |
* Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}. |
283 | 286 |
*/ |
284 |
private Boolean isPrivate = null;
|
|
287 |
private boolean isPrivate = false;
|
|
285 | 288 |
|
286 | 289 |
/** The return type's extent. */ |
287 | 290 |
private int extent = SourceNameMapper.NO_EXTENT; |
... | ... | |
294 | 297 |
*/ |
295 | 298 |
private Method method = null; |
296 | 299 |
|
300 |
/** When not-null, represents a lambda function to execute directly the target. */ |
|
301 |
private BiFunction<Object, Object[], Object> lambda = null; |
|
302 |
|
|
297 | 303 |
/** The OS library name, if this is a native method. */ |
298 | 304 |
private String libname = null; |
299 | 305 |
|
... | ... | |
452 | 458 |
*/ |
453 | 459 |
public Method getMethod() |
454 | 460 |
{ |
455 |
if (method == null && extprog != null && extprog.ooname == null) |
|
461 |
if (method == null && extprog != null && extprog.buildLegacy && extprog.ooname == null)
|
|
456 | 462 |
{ |
457 |
SourceNameMapper.buildLegacyProgram(extprog); |
|
458 |
|
|
459 |
// in case the Java method is not needed for this entry (is a in-handle or in-super), avoid multiple |
|
460 |
// lookups. |
|
461 |
extprog = null; |
|
463 |
synchronized (extprog) |
|
464 |
{ |
|
465 |
if (extprog.buildLegacy) |
|
466 |
{ |
|
467 |
SourceNameMapper.buildLegacyProgram(extprog); |
|
468 |
extprog.buildLegacy = false; |
|
469 |
} |
|
470 |
} |
|
462 | 471 |
} |
463 | 472 |
|
464 | 473 |
return method; |
465 | 474 |
} |
466 | 475 |
|
467 | 476 |
/** |
477 |
* Get the lambda target to be executed, see {@link #lambda}. |
|
478 |
* |
|
479 |
* @return See above. |
|
480 |
*/ |
|
481 |
public BiFunction<Object, Object[], Object> getLambda() |
|
482 |
{ |
|
483 |
if (method == null && extprog != null && extprog.buildLegacy && extprog.ooname == null) |
|
484 |
{ |
|
485 |
synchronized (extprog) |
|
486 |
{ |
|
487 |
if (extprog.buildLegacy) |
|
488 |
{ |
|
489 |
SourceNameMapper.buildLegacyProgram(extprog); |
|
490 |
extprog.buildLegacy = false; |
|
491 |
} |
|
492 |
} |
|
493 |
} |
|
494 |
|
|
495 |
return lambda; |
|
496 |
} |
|
497 |
|
|
498 |
/** |
|
468 | 499 |
* Get the value of the given attribute. If the attribute is null or |
469 | 500 |
* is not set, return <code>null</code>. |
470 | 501 |
* |
... | ... | |
581 | 612 |
} |
582 | 613 |
|
583 | 614 |
/** |
615 |
* Set the target lambda for this internal entry to the specified one. |
|
616 |
* |
|
617 |
* @param lambda |
|
618 |
* The target lambda. |
|
619 |
*/ |
|
620 |
void setLambda(BiFunction<Object, Object[], Object> lambda) |
|
621 |
{ |
|
622 |
this.lambda = lambda; |
|
623 |
} |
|
624 |
|
|
625 |
/** |
|
584 | 626 |
* Set the external program for this entry. |
585 | 627 |
* |
586 | 628 |
* @param extprog |
new/src/com/goldencode/p2j/util/SourceNameMapper.java 2022-12-20 11:06:48 +0000 | ||
---|---|---|
150 | 150 |
** initialized at server startup, the 'initMappingData' calls were removed from the |
151 | 151 |
** APIs. |
152 | 152 |
** CA 20220609 Fixed a regression in destructor processing. |
153 |
** CA 20221220 Use lambdas to execute all public methods in an external program. |
|
153 | 154 |
*/ |
154 | 155 | |
155 | 156 |
/* |
... | ... | |
223 | 224 |
import com.goldencode.p2j.oo.lang.*; |
224 | 225 |
import com.goldencode.p2j.rest.*; |
225 | 226 |
import com.goldencode.p2j.soap.*; |
227 |
import com.goldencode.proxy.*; |
|
226 | 228 |
import com.goldencode.p2j.security.*; |
227 | 229 | |
228 | 230 |
/** |
... | ... | |
2935 | 2937 |
throw new RuntimeException(e); |
2936 | 2938 |
} |
2937 | 2939 | |
2940 |
Map<Method, BiFunction<Object, Object[], Object>> lambdas = LambdaFactory.buildLambdas(cls); |
|
2941 |
|
|
2938 | 2942 |
Method[] methods = cls.getDeclaredMethods(); |
2939 | 2943 |
for (Method mthd : methods) |
2940 | 2944 |
{ |
... | ... | |
2960 | 2964 |
if (ie != null) |
2961 | 2965 |
{ |
2962 | 2966 |
ie.setMethod(mthd); |
2967 |
|
|
2968 |
if (lambdas != null) |
|
2969 |
{ |
|
2970 |
BiFunction<Object, Object[], Object> lambda = lambdas.get(mthd); |
|
2971 |
ie.setLambda(lambda); |
|
2972 |
} |
|
2963 | 2973 |
} |
2964 | 2974 |
} |
2965 | 2975 |
} |
... | ... | |
3438 | 3448 |
InternalEntry main = null; |
3439 | 3449 |
|
3440 | 3450 |
/** |
3451 |
* Flag indicating if {@link SourceNameMapper#buildLegacyClass(Class)} needs to be called when |
|
3452 |
* {@link InternalEntry#getMethod()} or {@link InternalEntry#getLambda()} are accessed. |
|
3453 |
*/ |
|
3454 |
boolean buildLegacy = true; |
|
3455 |
|
|
3456 |
/** |
|
3441 | 3457 |
* Basic c'tor. |
3442 | 3458 |
* |
3443 | 3459 |
* @param pname |
new/src/com/goldencode/p2j/util/Utils.java 2022-12-18 12:38:06 +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 instance |
|
1610 |
* The instance on which the call will be made; can be <code>null</code> for static methods. |
|
1611 |
* @param args |
|
1612 |
* The method's arguments. |
|
1613 |
* |
|
1614 |
* @return The method's return value. |
|
1615 |
* |
|
1616 |
* @throws IllegalAccessException |
|
1617 |
* @throws IllegalArgumentException |
|
1618 |
* @throws InvocationTargetException |
|
1619 |
*/ |
|
1620 |
public static Object invoke(Method method, |
|
1621 |
BiFunction<Object, Object[], Object> f, |
|
1622 |
Object instance, |
|
1623 |
Object... args) |
|
1624 |
throws IllegalAccessException, |
|
1625 |
IllegalArgumentException, |
|
1626 |
InvocationTargetException |
|
1627 |
{ |
|
1628 |
if (f != null) |
|
1629 |
{ |
|
1630 |
return f.apply(instance, args); |
|
1631 |
} |
|
1632 |
else |
|
1633 |
{ |
|
1634 |
return invoke(method, instance, args); |
|
1635 |
} |
|
1636 |
} |
|
1637 |
|
|
1638 |
/** |
|
1639 |
* Invoke the specified {@link Method} using ReflectASM instead of standard reflection. |
|
1640 |
* <p> |
|
1641 |
* ReflectASM related data is cached in {@link #METHOD_DATA}, thread-local, to avoid the concurrent access. |
|
1642 |
* More, the maps are identity-keyed, for fast access. |
|
1643 |
* |
|
1644 |
* @param method |
|
1604 | 1645 |
* The method to execute. |
1605 | 1646 |
* @param instance |
1606 | 1647 |
* 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:16:53 +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:16:41 +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 |
|