Project

General

Profile

6819_20221220a.patch

Constantin Asofiei, 12/20/2022 02:19 PM

Download (43.5 KB)

View differences:

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