Project

General

Profile

6939_6819_lambdas_3821c_14461.patch

Constantin Asofiei, 12/21/2022 01:19 PM

Download (91.3 KB)

View differences:

new/src/com/goldencode/p2j/persist/RecordBuffer.java 2022-12-21 16:46:32 +0000
1243 1243
**     CA  20221101          Accessing a setter while not in a TRANSACTION will raise error 7369.
1244 1244
**     TJD 20220504          Upgrade do Java 11 minor changes
1245 1245
**     OM  20221125          Added referenceOnly method.
1246
**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
1247
**                           expressions to perform a direct call of the target, and avoid reflection.
1246 1248
*/
1247 1249

  
1248 1250
/*
......
11823 11825
    * purposes.
11824 11826
    */
11825 11827
   private final class Handler
11826
   implements InvocationHandler
11828
   implements LambdaInvocationHandler
11827 11829
   {
11828 11830
      /** Exception thrown during validation of no-undo temp-tables, which must be deferred */
11829 11831
      private ValidationException deferredValidationError = null;
......
11843 11845
       *          The target proxy object.
11844 11846
       * @param   method 
11845 11847
       *          The method to be executed.
11848
       * @param   f
11849
       *          A function to invoke as target, when specified.
11846 11850
       * @param   args 
11847 11851
       *          The method arguments.
11848 11852
       *
......
11851 11855
       * @throws  Throwable
11852 11856
       *          For various reasons, like illegal access to fields, illegal arguments or target.
11853 11857
       */
11854
      public Object invoke(Object proxy, Method method, Object[] args)
11858
      @Override
11859
      public Object invoke(Object proxy, 
11860
                           final Method method, 
11861
                           BiFunction<Object, Object[], Object> f, 
11862
                           Object[] args) 
11855 11863
      throws Throwable
11856 11864
      {
11857 11865
         Object result = null;
......
11867 11875
            Class<?> declClass = method.getDeclaringClass();
11868 11876
            if (declClass.equals(BufferReference.class) || declClass.equals(Object.class))
11869 11877
            {
11870
               return Utils.invoke(method, RecordBuffer.this, args);
11878
               return Utils.invoke(method, f, RecordBuffer.this, args);
11871 11879
            }
11872 11880
            
11873 11881
            // TODO: use something more reliable (but efficient) here to determine whether this is a setter
......
11889 11897
            // to ensure all validation and flushing is tracked properly
11890 11898
            if (creatingBuffer != null && currentRecord != null && isSetter)
11891 11899
            {
11900
               // this can't receive the lambdas, as they are statically linked with the this buffer's
11901
               // proxy types (a ClassCastException may be raised if the instance passed as first argument at
11902
               // the lambda is not of the expected type).
11892 11903
               return creatingBuffer.handler.invoke(proxy, method, args);
11893 11904
            }
11894 11905
            
......
11907 11918
            // if a temporary record is set, use it to back the invocation
11908 11919
            if (tempRecord != null)
11909 11920
            {
11910
               return Utils.invoke(method, tempRecord, args);
11921
               return Utils.invoke(method, f, tempRecord, args);
11911 11922
            }
11912 11923
            
11913 11924
            if (isSetter)
......
12101 12112
            }
12102 12113
            
12103 12114
            // invoke the method on the concrete DMO implementation
12104
            result = Utils.invoke(method, currentRecord, args);
12115
            result = Utils.invoke(method, f, currentRecord, args);
12105 12116
            
12106 12117
            if (writeSnapshot != null && !currentRecord.checkState(DmoState.CHANGED))
12107 12118
            {
......
12233 12244
                  if (!ok)
12234 12245
                  {
12235 12246
                     // the assign trigger vetoed: rolling back the change
12236
                     Utils.invoke(method, currentRecord, oldVal);
12247
                     Utils.invoke(method, f, currentRecord, oldVal);
12237 12248
                     processChange = false;
12238 12249
                  }
12239 12250
               }
......
12908 12919
    * {@link RecordBuffer} proxies.
12909 12920
    */
12910 12921
   private static class ArgumentBuffer
12911
   implements InvocationHandler
12922
   implements LambdaInvocationHandler
12912 12923
   {
12913 12924
      /** The backing buffer of this proxy, which should handle the majority of the methods */
12914 12925
      private BufferImpl buffer;
......
12998 13009
       * imply the DMO alias or buffer name, which will be handled here.
12999 13010
       */
13000 13011
      @Override
13001
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
13012
      public Object invoke(Object proxy, 
13013
                           final Method method, 
13014
                           BiFunction<Object, Object[], Object> f, 
13015
                           Object[] args)
13016
      throws Throwable
13002 13017
      {
13003 13018
         try 
13004 13019
         {
......
13019 13034
               if (method.getName().equals("overrideName") ||
13020 13035
                   method.getName().equals("restoreName"))
13021 13036
               {
13022
                  return Utils.invoke(method, buffer, args);
13037
                  return Utils.invoke(method, f, buffer, args);
13023 13038
               }
13024 13039
               
13025 13040
               boolean overridden = buffer.overrideName(bufferName);
......
13027 13042
               
13028 13043
               try
13029 13044
               {
13030
                  invokeReturn = Utils.invoke(method, buffer, args);
13045
                  invokeReturn = Utils.invoke(method, f, buffer, args);
13031 13046
               }
13032 13047
               finally
13033 13048
               {
......
13057 13072
               if (method.getName().equals("overrideDMOAlias") ||
13058 13073
                   method.getName().equals("restoreDMOAlias"))
13059 13074
               {
13060
                  return Utils.invoke(method, backBuffer, args);
13075
                  return Utils.invoke(method, f, backBuffer, args);
13061 13076
               }
13062 13077
               
13063 13078
               boolean overridden = backBuffer.overrideDMOAlias(dmoAlias);
......
13065 13080
               
13066 13081
               try
13067 13082
               {
13068
                  invokeReturn = Utils.invoke(method, backBuffer, args);
13083
                  invokeReturn = Utils.invoke(method, f, backBuffer, args);
13069 13084
               }
13070 13085
               finally
13071 13086
               {
new/src/com/goldencode/p2j/persist/StaticDataSet.java 2022-12-21 16:37:00 +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
**     OM  20221206 Reworked REFERENCE-ONLY attribute.
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
/**
......
444 447
    * {@code DataSet} or will throw errors.
445 448
    */
446 449
   static class Handler
447
   implements InvocationHandler
450
   implements LambdaInvocationHandler
448 451
   {
449 452
      /**
450 453
       * Processes a method invocation on a proxy instance and returns the result. This method
......
458 461
       *          proxy instance. The declaring class of the {@code Method} object will be the
459 462
       *          interface that the method was declared in, which may be a superinterface of the
460 463
       *          proxy interface that the proxy class inherits the method through.
464
       * @param   f
465
       *          A function to invoke as target, when specified.
461 466
       * @param   args
462 467
       *          an array of objects containing the values of the arguments passed in the method
463 468
       *          invocation on the proxy instance, or {@code null} if interface method takes no
......
471 476
       * @see UndeclaredThrowableException
472 477
       */
473 478
      @Override
474
      public Object invoke(Object proxy, Method method, Object[] args)
479
      public Object invoke(Object proxy, 
480
                           final Method method, 
481
                           BiFunction<Object, Object[], Object> f, 
482
                           Object[] args) 
475 483
      throws Throwable
476 484
      {
477 485
         StaticDataSet dsp = (StaticDataSet) proxy;
478 486
         if (dsp.bound != null)
479 487
         {
480
            return Utils.invoke(method, dsp.bound, args);
488
            return Utils.invoke(method, f, dsp.bound, args);
481 489
         }
482 490
         
483 491
         String[] errParam = new String[1];
new/src/com/goldencode/p2j/persist/TemporaryBuffer.java 2022-12-21 16:49:46 +0000
591 591
**     OM  20221125          Handled BINDing of REFERENCE-ONLY OUTPUT buffers.
592 592
**     AL2 20221205          Replaced AdaptiveQuery with PreselectQuery for loopDelete.
593 593
**                           Use null sort instead of empty sort for PreselectQuery/
594
**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
595
**                           expressions to perform a direct call of the target, and avoid reflection.
594 596
*/
595 597

  
596 598
/*
......
670 672
import com.goldencode.p2j.util.*;
671 673
import com.goldencode.p2j.util.ErrorManager;
672 674
import com.goldencode.p2j.util.LogHelper;
673
import com.goldencode.proxy.ProxyFactory;
675
import com.goldencode.proxy.*;
674 676
import com.goldencode.util.*;
675 677

  
676 678
/**
......
8838 8840
    * The handler to switch between references, when binding a definition table to another one.
8839 8841
    */
8840 8842
   private static class ReferenceProxy
8841
   implements InvocationHandler
8843
   implements LambdaInvocationHandler
8842 8844
   {
8843 8845
      /** The default properties - PK and any other properties in {@link TempRecord}. */
8844 8846
      private static final Map<String, String> defaultProps;
......
8995 8997
       */
8996 8998
      @SuppressWarnings("deprecation")
8997 8999
      @Override
8998
      public Object invoke(Object proxy, Method method, Object[] args)
9000
      public Object invoke(Object proxy, 
9001
                           Method method, 
9002
                           BiFunction<Object, Object[], Object> f, 
9003
                           Object[] args) 
8999 9004
      throws Throwable
9000 9005
      {
9001 9006
         Object instance = (bound == null ? def : bound);
......
9018 9023
             declClass.equals(Object.class)          ||
9019 9024
             HandleResource.class.isAssignableFrom(declClass))
9020 9025
         {
9021
            return Utils.invoke(method, instance, args);
9026
            return Utils.invoke(method, f, instance, args);
9022 9027
         }
9023 9028
         
9024 9029
         if (bound != null && boundBuffer.getDMOInterface() != defBuffer.getDMOInterface())
......
9038 9043
               
9039 9044
               Method mbound = (setter ? boundSetterByProp.get(boundProperty) 
9040 9045
                                       : boundGetterByProp.get(boundProperty));
9041
               
9046

  
9047
               // instance has switched
9048
               f = null;
9042 9049
               method = mbound;
9043 9050
            }
9044 9051
         }
......
9053 9060
               method.setAccessible(true);
9054 9061
            }
9055 9062
            
9056
            return Utils.invoke(method, instance, args);
9063
            return Utils.invoke(method, f, instance, args);
9057 9064
         }
9058 9065
         catch (Throwable t)
9059 9066
         {
new/src/com/goldencode/p2j/util/ControlFlowOps.java 2022-12-21 16:44:00 +0000
264 264
**     TW  20221123          Augmented the error feedback, which omitted the target invocation name if
265 265
**                           no parameters expected. Troubleshooting went haywire after that, since
266 266
**                           root causes were sought at the wrong frame in the stack (refs: #6906).
267
**     CA  20221220          Use lambdas to execute all public methods in an external program.
267 268
*/
268 269

  
269 270
/*
......
948 949
            caller.callerInstance = reference;
949 950
            caller.wa = wa;
950 951
            caller.mthd = lastInvoke.method;
952
            caller.lambda = lastInvoke.lambda;
951 953
            caller.methodName = lastInvoke.method.getName();
952 954
            caller.ie = null;
953 955
            caller.iename = cfg.getTarget();
......
994 996

  
995 997
                  try
996 998
                  {
997
                     BaseDataType res = (BaseDataType) lastInvoke.method.invoke(reference, args);
999
                     BaseDataType res = (BaseDataType) (lastInvoke.lambda == null 
1000
                                                         ? lastInvoke.method.invoke(reference, args)
1001
                                                         : lastInvoke.lambda.apply(reference, args));
998 1002
                     res = checkInvokeResult(res, iename, cfg.isFunction(), cfg.isDynamicFunction());
999 1003
                     
1000 1004
                     if (cls != null && cfg.isFunction())
......
8392 8396
      boolean externalProcedure = caller.getPhandle() == null;
8393 8397
      Object instance = externalProcedure ? caller.getClassName() : caller.getCallerInstance();
8394 8398
      Method mthd = externalProcedure ? null : caller.mthd;
8399
      BiFunction<Object, Object[], Object> lambda = externalProcedure ? null : caller.lambda;
8395 8400
      
8396 8401
      if (args != null && args.length > 0)
8397 8402
      {
......
8409 8414
                                                       eventProcedureName,
8410 8415
                                                       eventProcedureContext,
8411 8416
                                                       instance,
8412
                                                       mthd, 
8417
                                                       mthd,
8418
                                                       lambda,
8413 8419
                                                       argumentTypes);
8414 8420
      
8415 8421
      Map<InvokeConfig, LastInvokeDetails> thisCallSite = 
......
8455 8461
      private final Method method;
8456 8462
      
8457 8463
      /**
8464
       * When not-null, a lambda target to execute directly the call, without relying on the {@link #method}. 
8465
       */
8466
      private final BiFunction<Object, Object[], Object> lambda;
8467
      
8468
      /**
8458 8469
       * Create a new instance.
8459 8470
       * 
8460 8471
       * @param    legacyName
......
8474 8485
       *           {@link #method} is being invoked.
8475 8486
       * @param    method
8476 8487
       *           The Java method to invoke.
8488
       * @param    lambda
8489
       *           When not-null, a lambda target to execute directly the call, without relying on the 
8490
       *           {@link #method}. 
8477 8491
       * @param    argumentTypes
8478 8492
       *           The argument types for this call.
8479 8493
       */
......
8485 8499
                               Object     eventProcedureContext,
8486 8500
                               Object     reference,
8487 8501
                               Method     method,
8502
                               BiFunction<Object, Object[], Object> lambda,
8488 8503
                               Class<?>[] argumentTypes)
8489 8504
      {
8490 8505
         this.legacyName = legacyName;
......
8499 8514
         
8500 8515
         this.reference = (reference == null ? null : new WeakReference<>(reference));
8501 8516
         this.method = method;
8517
         this.lambda = lambda;
8502 8518
      }
8503 8519
      
8504 8520
      /**
......
8934 8950
      protected WorkArea wa;
8935 8951
      
8936 8952
      /** The {@link Method} instance for this internal entry. */
8937
      private Method mthd = null;
8938
      
8939
      /** The ReflectASM access point to this method. */
8940
      private MethodAccess access = null;
8941
      
8942
      /** The method index in the MethodAccess instance. */
8943
      private int index = -1;
8953
      protected Method mthd = null;
8954
      
8955
      protected BiFunction<Object, Object[], Object> lambda = null;
8956
      
8957
      /** Already resolved internal entry. */
8958
      protected InternalEntry ie = null;
8944 8959
      
8945 8960
      /** The handle associated with the {@link #callerInstance}. */
8946 8961
      private handle phandle;
......
8948 8963
      /** Worker which performs the {@link ProcedureManager.ProcedureHelper#pushCalleeInfo} call. */
8949 8964
      private Runnable pushWorker;
8950 8965
      
8951
      /** Already resolved internal entry (used when invoking OO methods). */
8952
      private InternalEntry ie = null;
8953
      
8954 8966
      /**
8955 8967
       * Basic c'tor.
8956 8968
       * 
......
9099 9111
            pushWorker.run();
9100 9112
            wa.bm.markPendingDynCall();
9101 9113
            
9102
            if (access != null)
9114
            if (lambda != null)
9103 9115
            {
9104
               return access.invoke(instance, index, param);
9116
               return lambda.apply(instance, param);
9105 9117
            }
9106 9118
            else
9107 9119
            {
......
9306 9318
                  if (value.method != null)
9307 9319
                  {
9308 9320
                     caller.mthd = value.method;
9309
                     caller.access = value.access;
9310
                     caller.index = value.index;
9321
                     caller.lambda = value.lambda;
9311 9322
                  }
9312 9323
                  
9313 9324
                  return value.returnValue;
......
9320 9331
                  method = cls.getMethod(caller.methodName, signature);
9321 9332
                  rv = ArgValidationErrors.NO_ERROR;
9322 9333
                  
9323
                  value = putCache(key, method, rv, true);
9334
                  value = putCache(key, method, null, rv, true);
9324 9335

  
9325 9336
                  caller.mthd = value.method;
9326
                  caller.access = value.access;
9327
                  caller.index = value.index;
9337
                  caller.lambda = value.lambda;
9328 9338
                  
9329 9339
                  return rv;
9330 9340
               }
......
9339 9349
                  method = cls.getDeclaredMethod(caller.methodName, signature);
9340 9350
                  rv = ArgValidationErrors.NO_ERROR;
9341 9351
                  
9342
                  value = putCache(key, method, rv, true);
9352
                  value = putCache(key, method, null, rv, true);
9343 9353
                  
9344 9354
                  caller.mthd = value.method;
9345
                  caller.access = value.access;
9346
                  caller.index = value.index;
9355
                  caller.lambda = value.lambda;
9347 9356

  
9348 9357
                  return rv;
9349 9358
               }
......
9541 9550
                  extentInfo[0] = parExtent ? paramExtent : 0;
9542 9551
                  
9543 9552
                  // cache failed result as well, so we don't need to repeat validation
9544
                  putCache(key, null, rv, true);
9553
                  putCache(key, null, null, rv, true);
9545 9554
                  
9546 9555
                  return rv;
9547 9556
               }
......
9961 9970
               
9962 9971
               if (value == null)
9963 9972
               {
9964
                  value = putCache(key, m, rv, false);
9973
                  value = putCache(key, m, m.equals(caller.mthd) ? caller.lambda : null, rv, false);
9965 9974
               }
9966 9975
               
9967 9976
               caller.mthd = value.method;
9968
               caller.access = value.access;
9969
               caller.index = value.index;
9977
               caller.lambda = value.lambda;
9970 9978
               
9971 9979
               return rv;
9972 9980
            }
......
9979 9987
              : ArgValidationErrors.INCORRECT_TYPES;
9980 9988
         
9981 9989
         // cache failed result as well, so we don't need to repeat validation
9982
         putCache(key, null, rv, true);
9990
         putCache(key, null, null, rv, true);
9983 9991
         
9984 9992
         return rv;
9985 9993
      }
......
10115 10123
       * @param   method
10116 10124
       *          Method found matching a given name and signature, or {@code null} if no
10117 10125
       *          match was found.
10126
       * @param   lambda
10127
       *          When not-null, a lambda target to execute directly the call, without relying on the method. 
10118 10128
       * @param   returnValue
10119 10129
       *          Enumerated return value for {@link #valid(boolean, String, int[], Object...)}
10120 10130
       *          method.
......
10126 10136
       * @return  the created cache.
10127 10137
       */
10128 10138
      private static CacheValue putCache(CacheKey key,
10129
                                  Method method,
10130
                                  ArgValidationErrors returnValue,
10131
                                  boolean exactMatch)
10139
                                         Method method,
10140
                                         BiFunction<Object, Object[], Object> lambda,
10141
                                         ArgValidationErrors returnValue,
10142
                                         boolean exactMatch)
10132 10143
      {
10133
         CacheValue value = new CacheValue(method, returnValue, exactMatch);
10144
         CacheValue value = new CacheValue(method, lambda, returnValue, exactMatch);
10134 10145
         
10135 10146
         synchronized (cache)
10136 10147
         {
......
10260 10271
         /** Method, if any, else <code>null</code> if none was found */
10261 10272
         final Method method;
10262 10273

  
10263
         /** The ReflectASM access point to this method. */
10264
         final MethodAccess access;
10265
         
10266
         /** The method index in the MethodAccess instance. */
10267
         final int index;
10274
         /**
10275
          * When not-null, a lambda target to execute directly the call, without relying on the 
10276
          * {@link #method}. 
10277
          */
10278
         final BiFunction<Object, Object[], Object> lambda;
10268 10279
         
10269 10280
         /** 
10270 10281
          * Enumerated return value for {@link 
......
10280 10291
          * 
10281 10292
          * @param   method
10282 10293
          *          Method, if any, else <code>null</code> if none was found.
10294
          * @param   lambda
10295
          *          When not-null, a lambda target to execute directly the call, without relying on the 
10296
          *          {@link #method}. 
10283 10297
          * @param   returnValue
10284 10298
          *          Enumerated return value for {@link
10285 10299
          *          InternalEntryCaller#valid(boolean, String, int[], Object...)}.
......
10287 10301
          *          Flag indicating whether signature of caller's parameters exactly matches
10288 10302
          *          method.
10289 10303
          */
10290
         CacheValue(Method method, ArgValidationErrors returnValue, boolean exactMatch)
10304
         CacheValue(Method method, BiFunction<Object, Object[], Object> lambda, ArgValidationErrors returnValue, boolean exactMatch)
10291 10305
         {
10292 10306
            this.method = method;
10293
            if (method != null)
10294
            {
10295
               if (!Modifier.isPrivate(method.getModifiers()))
10296
               {
10297
                  this.access = MethodAccess.get(method.getDeclaringClass());
10298
                  this.index = access.getIndex(method.getName(), method.getParameterTypes());
10299
               }
10300
               else
10301
               {
10302
                  this.access = null;
10303
                  this.index = -1;
10304
               }
10305
            }
10306
            else
10307
            {
10308
               this.access = null;
10309
               this.index = -1;
10310
            }
10307
            this.lambda = lambda;
10311 10308
            this.returnValue = returnValue;
10312 10309
            this.exactMatch = exactMatch;
10313 10310
         }
......
10511 10508
       * 
10512 10509
       * @param    wa
10513 10510
       *           The context-local state in {@link WorkArea}.
10511
       * @param    name
10512
       *           The external program name.
10514 10513
       * @param    className
10515 10514
       *           The class name associated with the external program.
10516 10515
       * @param    persistent
......
10519 10518
       * @throws   Exception
10520 10519
       *           If the class can't be resolved. 
10521 10520
       */
10522
      public ExternalProcedureCaller(WorkArea wa, String className, boolean persistent)
10521
      public ExternalProcedureCaller(WorkArea wa, String name, String className, boolean persistent)
10523 10522
      throws Exception
10524 10523
      {
10525 10524
         super(wa);
10525

  
10526
         this.ie = SourceNameMapper.getInternalEntry(name, null, false);
10527
         this.mthd = (ie == null ? null : ie.getMethod());
10528
         this.lambda = (ie == null ? null : ie.getLambda());
10526 10529
         
10527 10530
         this.methodName = "execute";
10528 10531
         this.className = className;
......
10729 10732
               wa.currentInvoke = null;
10730 10733
               res = new MapToNotFound(wa, mapTo);
10731 10734
            }
10732
            
10735
            else
10736
            {
10737
               res.mthd = ie.getMethod();
10738
               res.lambda = ie.getLambda();
10739
            }
10740

  
10733 10741
            res.ie = ie;
10734 10742
            return res;
10735 10743
         }
......
10747 10755
            
10748 10756
            caller = new InternalEntryCaller(wa, hcaller, ie.jname, ieName);
10749 10757
            caller.ie = ie;
10758
            caller.mthd = ie.getMethod();
10759
            caller.lambda = ie.getLambda();
10750 10760
         }
10751 10761
         /*
10752 10762
         else if (!superCall)
......
10939 10949
               // name to any errors generated during instantiation (i.e. shared vars error msgs 
10940 10950
               // need to know the procedure name where they are defined, if an error happens)
10941 10951
               wa.pm.setInstantingExternalProgram(name);
10942
               caller = new ExternalProcedureCaller(wa, className, persistent);
10952
               caller = new ExternalProcedureCaller(wa, name, className, persistent);
10943 10953
            }
10944 10954
            catch (Throwable t)
10945 10955
            {
new/src/com/goldencode/p2j/util/InternalEntry.java 2022-12-21 16:40:49 +0000
30 30
** 015 CA  20200605 The Java method instance is resolved on first access of getMethod().
31 31
** 016 ME  20200810 Added qualified name support in signature processing.
32 32
**     CA  20210218 Fixed signature processing for qualified return type.
33
**     CA  20221220 Use lambdas to execute all public methods in an external program.  Fixed synchronization
34
**                  in getMethod() and getLambda(), when calling SourceNameMapper.buildLegacyProgram.
33 35
*/
34 36

  
35 37
/*
......
89 91

  
90 92
import java.lang.reflect.*;
91 93
import java.util.*;
94
import java.util.function.*;
92 95

  
93 96
import com.goldencode.p2j.util.SourceNameMapper.ExternalProgram;
94 97

  
......
268 271
    * Flag indicating if this is a IN SUPER internal entry (when set to <code>true</code>).
269 272
    * Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}.
270 273
    */
271
   private Boolean isSuper = null;
274
   private boolean isSuper = false;
272 275
   
273 276
   /** 
274 277
    * Flag indicating if this is a IN handle internal entry (when set to <code>true</code>).
275 278
    * Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}.
276 279
    */
277
   private Boolean inHandle = null;
280
   private boolean inHandle = false;
278 281
   
279 282
   /** 
280 283
    * Flag indicating if this is a PRIVATE internal entry (when set to <code>true</code>).
281 284
    * Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}.
282 285
    */
283
   private Boolean isPrivate = null;
286
   private boolean isPrivate = false;
284 287
   
285 288
   /** The return type's extent. */
286 289
   private int extent = SourceNameMapper.NO_EXTENT;
......
293 296
    */
294 297
   private Method method = null;
295 298
   
299
   /** When not-null, represents a lambda function to execute directly the target. */
300
   private BiFunction<Object, Object[], Object> lambda = null;
301
   
296 302
   /** The OS library name, if this is a native method. */
297 303
   private String libname = null;
298 304
   
......
451 457
    */
452 458
   public Method getMethod()
453 459
   {
454
      if (method == null && extprog != null && extprog.ooname == null)
460
      if (method == null && extprog != null && extprog.buildLegacy && extprog.ooname == null)
455 461
      {
456
         SourceNameMapper.buildLegacyProgram(extprog);
457
         
458
         // in case the Java method is not needed for this entry (is a in-handle or in-super), avoid multiple 
459
         // lookups.
460
         extprog = null;
462
         synchronized (extprog)
463
         {
464
            if (extprog.buildLegacy)
465
            {
466
               SourceNameMapper.buildLegacyProgram(extprog);
467
               extprog.buildLegacy = false;
468
            }
469
         }
461 470
      }
462 471
      
463 472
      return method;
464 473
   }
465 474
   
466 475
   /**
476
    * Get the lambda target to be executed, see {@link #lambda}.
477
    * 
478
    * @return   See above.
479
    */
480
   public BiFunction<Object, Object[], Object> getLambda()
481
   {
482
      if (method == null && extprog != null && extprog.buildLegacy && extprog.ooname == null)
483
      {
484
         synchronized (extprog)
485
         {
486
            if (extprog.buildLegacy)
487
            {
488
               SourceNameMapper.buildLegacyProgram(extprog);
489
               extprog.buildLegacy = false;
490
            }
491
         }
492
      }
493
      
494
      return lambda;
495
   }
496
   
497
   /**
467 498
    * Get the value of the given attribute.  If the attribute is null or
468 499
    * is not set, return <code>null</code>.
469 500
    * 
......
580 611
   }
581 612
   
582 613
   /**
614
    * Set the target lambda for this internal entry to the specified one.
615
    * 
616
    * @param    lambda
617
    *           The target lambda.
618
    */
619
   void setLambda(BiFunction<Object, Object[], Object> lambda)
620
   {
621
      this.lambda = lambda;
622
   }
623
   
624
   /**
583 625
    * Set the external program for this entry.
584 626
    * 
585 627
    * @param    extprog
new/src/com/goldencode/p2j/util/SourceNameMapper.java 2022-12-21 16:39:47 +0000
143 143
**     TJD 20220823          Prevent recursive calls to initMappingData() workaround
144 144
**     TJD 20220824          Prevent recursive calls to initMappingData() workaround in more effective way
145 145
**     SBI 20221215          Filter the same REST java methods having erased parameters types.
146
**     CA  20221220          Use lambdas to execute all public methods in an external program.
146 147
*/
147 148

  
148 149
/*
......
216 217
import com.goldencode.p2j.oo.lang.*;
217 218
import com.goldencode.p2j.rest.*;
218 219
import com.goldencode.p2j.soap.*;
220
import com.goldencode.proxy.*;
219 221
import com.goldencode.p2j.security.*;
220 222

  
221 223
/**
......
2897 2899
         throw new RuntimeException(e);
2898 2900
      }
2899 2901

  
2902
      Map<Method, BiFunction<Object, Object[], Object>> lambdas = LambdaFactory.buildLambdas(cls);
2903
      
2900 2904
      Method[] methods = cls.getDeclaredMethods();
2901 2905
      for (Method mthd : methods)
2902 2906
      {
......
2922 2926
            if (ie != null)
2923 2927
            {
2924 2928
               ie.setMethod(mthd);
2929
               
2930
               if (lambdas != null)
2931
               {
2932
                  BiFunction<Object, Object[], Object> lambda = lambdas.get(mthd);
2933
                  ie.setLambda(lambda);
2934
               }
2925 2935
            }
2926 2936
         }
2927 2937
      }
......
3334 3344
      InternalEntry main = null;
3335 3345
      
3336 3346
      /**
3347
       * Flag indicating if {@link SourceNameMapper#buildLegacyClass(Class)} needs to be called when 
3348
       * {@link InternalEntry#getMethod()} or {@link InternalEntry#getLambda()} are accessed.
3349
       */
3350
      boolean buildLegacy = true;
3351
      
3352
      /**
3337 3353
       * Basic c'tor.
3338 3354
       * 
3339 3355
       * @param    pname
new/src/com/goldencode/p2j/util/Utils.java 2022-12-21 16:37:39 +0000
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 187
**     TJD 20220504          Java 11 compatibility minor changes
188
**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
189
**                           expressions to perform a direct call of the target, and avoid reflection.
188 190
*/
189 191

  
190 192
/*
......
1602 1604
    * More, the maps are identity-keyed, for fast access.
1603 1605
    * 
1604 1606
    * @param    method
1607
    *           The method to execute, only if the function or consumer are both null.
1608
    * @param    f
1609
    *           When not-null, invoke this function and return its value.
1610
    * @param    instance
1611
    *           The instance on which the call will be made; can be <code>null</code> for static methods.
1612
    * @param    args
1613
    *           The method's arguments.
1614
    *           
1615
    * @return   The method's return value.
1616
    * 
1617
    * @throws   IllegalAccessException
1618
    * @throws   IllegalArgumentException
1619
    * @throws   InvocationTargetException
1620
    */
1621
   public static Object invoke(Method method, 
1622
                               BiFunction<Object, Object[], Object> f, 
1623
                               Object instance, 
1624
                               Object... args) 
1625
   throws IllegalAccessException, 
1626
          IllegalArgumentException, 
1627
          InvocationTargetException
1628
    {
1629
      if (f != null)
1630
      {
1631
         return f.apply(instance, args);
1632
      }
1633
      else
1634
      {
1635
         return invoke(method, instance, args);
1636
      }
1637
    }
1638
   
1639
   /**
1640
    * Invoke the specified {@link Method} using ReflectASM instead of standard reflection.
1641
    * <p>
1642
    * ReflectASM related data is cached in {@link #METHOD_DATA}, thread-local, to avoid the concurrent access.
1643
    * More, the maps are identity-keyed, for fast access.
1644
    * 
1645
    * @param    method
1605 1646
    *           The method to execute.
1606 1647
    * @param    instance
1607 1648
    *           The instance on which the call will be made; can be <code>null</code> for static methods.
new/src/com/goldencode/proxy/DefaultHandlerPlugin.java 2022-12-12 15:05:06 +0000
1
/*
2
** Module   : DefaultHandlerPlugin.java
3
** Abstract : 
4
**
5
** Copyright (c) 2022, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
8
** 001 CA  20221212 Created initial version.
9
*/
10
/*
11
** This program is free software: you can redistribute it and/or modify
12
** it under the terms of the GNU Affero General Public License as
13
** published by the Free Software Foundation, either version 3 of the
14
** License, or (at your option) any later version.
15
**
16
** This program is distributed in the hope that it will be useful,
17
** but WITHOUT ANY WARRANTY; without even the implied warranty of
18
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
** GNU Affero General Public License for more details.
20
**
21
** You may find a copy of the GNU Affero GPL version 3 at the following
22
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
23
** 
24
** Additional terms under GNU Affero GPL version 3 section 7:
25
** 
26
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
27
**   terms apply to the works covered under the License.  These additional terms
28
**   are non-permissive additional terms allowed under Section 7 of the GNU
29
**   Affero GPL version 3 and may not be removed by you.
30
** 
31
**   0. Attribution Requirement.
32
** 
33
**     You must preserve all legal notices or author attributions in the covered
34
**     work or Appropriate Legal Notices displayed by works containing the covered
35
**     work.  You may not remove from the covered work any author or developer
36
**     credit already included within the covered work.
37
** 
38
**   1. No License To Use Trademarks.
39
** 
40
**     This license does not grant any license or rights to use the trademarks
41
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
42
**     of Golden Code Development Corporation. You are not authorized to use the
43
**     name Golden Code, FWD, or the names of any author or contributor, for
44
**     publicity purposes without written authorization.
45
** 
46
**   2. No Misrepresentation of Affiliation.
47
** 
48
**     You may not represent yourself as Golden Code Development Corporation or FWD.
49
** 
50
**     You may not represent yourself for publicity purposes as associated with
51
**     Golden Code Development Corporation, FWD, or any author or contributor to
52
**     the covered work, without written authorization.
53
** 
54
**   3. No Misrepresentation of Source or Origin.
55
** 
56
**     You may not represent the covered work as solely your work.  All modified
57
**     versions of the covered work must be marked in a reasonable way to make it
58
**     clear that the modified work is not originating from Golden Code Development
59
**     Corporation or FWD.  All modified versions must contain the notices of
60
**     attribution required in this license.
61
*/
62

  
63
package com.goldencode.proxy;
64

  
65
import java.lang.reflect.*;
66

  
67
import org.objectweb.asm.*;
68

  
69
import com.goldencode.asm.AsmUtils;
70

  
71
/**
72
 * Assembler for the invocation arguments emitted for the {@link InvocationHandler#invoke} method.
73
 */
74
class DefaultHandlerPlugin
75
implements InvocationHandlerPlugin
76
{
77
   /**
78
    * Get the handler's class.
79
    * 
80
    * @return   {@link InvocationHandler} type.
81
    */
82
   @Override
83
   public Class<? extends InvocationHandler> getHandlerClass()
84
   {
85
      return InvocationHandler.class;
86
   }
87

  
88
   /**
89
    * Get the signature of the 'invoke' method for the {@link InvocationHandler}.
90
    */
91
   @Override
92
   public String getInvokeSignature()
93
   {
94
      return "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
95
   }
96

  
97
   /**
98
    * Create the arguments related to invoking the target of this proxied call.  This will emit a {@link Method}
99
    * reference from within the 'ma' array at the specified 'methodIndex'.
100
    * 
101
    * @param    classWriter
102
    *           Class writer for proxy class.
103
    * @param    mv
104
    *           Method visitor for this method.
105
    * @param    proxyName
106
    *           Internal form of the proxy class' name.
107
    * @param    method
108
    *           Method for which this proxy is being implemented.
109
    * @param    methodIndex
110
    *           Index of this method within the array of methods used by the invocation handler.
111
    */
112
   @Override
113
   public void createInvocationArgs(ClassWriter   classWriter, 
114
                                    MethodVisitor mv, 
115
                                    String        proxyName, 
116
                                    Method        method, 
117
                                    int           methodIndex)
118
   {
119
      // load target method from array of methods in static field "ma"
120
      mv.visitFieldInsn(ProxyAssembler.GETSTATIC,
121
                        proxyName,
122
                        "ma",
123
                        "[Ljava/lang/reflect/Method;");
124
      
125
      // target method is at index "index" in the "ma" array
126
      AsmUtils.pushInt(mv, methodIndex);
127
      mv.visitInsn(Opcodes.AALOAD);
128
  }
129
}
new/src/com/goldencode/proxy/InvocationHandlerPlugin.java 2022-12-12 15:27:10 +0000
1
/*
2
** Module   : InvocationHandlerPlugin.java
3
** Abstract : 
4
**
5
** Copyright (c) 2022, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
8
** 001 CA  20221212 Created initial version.
9
*/
10
/*
11
** This program is free software: you can redistribute it and/or modify
12
** it under the terms of the GNU Affero General Public License as
13
** published by the Free Software Foundation, either version 3 of the
14
** License, or (at your option) any later version.
15
**
16
** This program is distributed in the hope that it will be useful,
17
** but WITHOUT ANY WARRANTY; without even the implied warranty of
18
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
** GNU Affero General Public License for more details.
20
**
21
** You may find a copy of the GNU Affero GPL version 3 at the following
22
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
23
** 
24
** Additional terms under GNU Affero GPL version 3 section 7:
25
** 
26
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
27
**   terms apply to the works covered under the License.  These additional terms
28
**   are non-permissive additional terms allowed under Section 7 of the GNU
29
**   Affero GPL version 3 and may not be removed by you.
30
** 
31
**   0. Attribution Requirement.
32
** 
33
**     You must preserve all legal notices or author attributions in the covered
34
**     work or Appropriate Legal Notices displayed by works containing the covered
35
**     work.  You may not remove from the covered work any author or developer
36
**     credit already included within the covered work.
37
** 
38
**   1. No License To Use Trademarks.
39
** 
40
**     This license does not grant any license or rights to use the trademarks
41
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
42
**     of Golden Code Development Corporation. You are not authorized to use the
43
**     name Golden Code, FWD, or the names of any author or contributor, for
44
**     publicity purposes without written authorization.
45
** 
46
**   2. No Misrepresentation of Affiliation.
47
** 
48
**     You may not represent yourself as Golden Code Development Corporation or FWD.
49
** 
50
**     You may not represent yourself for publicity purposes as associated with
51
**     Golden Code Development Corporation, FWD, or any author or contributor to
52
**     the covered work, without written authorization.
53
** 
54
**   3. No Misrepresentation of Source or Origin.
55
** 
56
**     You may not represent the covered work as solely your work.  All modified
57
**     versions of the covered work must be marked in a reasonable way to make it
58
**     clear that the modified work is not originating from Golden Code Development
59
**     Corporation or FWD.  All modified versions must contain the notices of
60
**     attribution required in this license.
61
*/
62

  
63
package com.goldencode.proxy;
64

  
65
import java.lang.reflect.*;
66

  
67
import org.objectweb.asm.*;
68

  
69
/**
70
 * Assembler for the invocation arguments emitted for the 'invoke' method at the handler.
71
 */
72
public interface InvocationHandlerPlugin
73
{
74
   /**
75
    * Get the handler's class.
76
    * 
77
    * @return   A sub-class of {@link InvocationHandler}.
78
    */
79
   public Class<? extends InvocationHandler> getHandlerClass();
80

  
81
   /**
82
    * Get the signature of the 'invoke' method for the {@link #getHandlerClass() handler type}.
83
    */
84
   public String getInvokeSignature();
85

  
86
   /**
87
    * Create the arguments related to invoking the target of this proxied call.  This will emit at least the
88
    * {@link Method} reference.
89
    * 
90
    * @param    classWriter
91
    *           Class writer for proxy class.
92
    * @param    mv
93
    *           Method visitor for this method.
94
    * @param    proxyName
95
    *           Internal form of the proxy class' name.
96
    * @param    method
97
    *           Method for which this proxy is being implemented.
98
    * @param    methodIndex
99
    *           Index of this method within the array of methods used by the invocation handler.
100
    */
101
   public void createInvocationArgs(ClassWriter   classWriter, 
102
                                    MethodVisitor mv, 
103
                                    String        proxyName, 
104
                                    Method        method, 
105
                                    int           methodIndex);
106
}
new/src/com/goldencode/proxy/LambdaAssembler.java 2022-12-20 11:02:28 +0000
1
/*
2
** Module   : LambdaAssembler.java
3
** Abstract : Assembles a dynamic class which defines lambdas for all reachable public methods in a certain 
4
**            type.
5
**
6
** Copyright (c) 2022, Golden Code Development Corporation.
7
**
8
** -#- -I- --Date-- --------------------------------Description----------------------------------
9
** 001 CA  20221220 Created initial version.
10
*/
11
/*
12
** This program is free software: you can redistribute it and/or modify
13
** it under the terms of the GNU Affero General Public License as
14
** published by the Free Software Foundation, either version 3 of the
15
** License, or (at your option) any later version.
16
**
17
** This program is distributed in the hope that it will be useful,
18
** but WITHOUT ANY WARRANTY; without even the implied warranty of
19
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
** GNU Affero General Public License for more details.
21
**
22
** You may find a copy of the GNU Affero GPL version 3 at the following
23
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
24
** 
25
** Additional terms under GNU Affero GPL version 3 section 7:
26
** 
27
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
28
**   terms apply to the works covered under the License.  These additional terms
29
**   are non-permissive additional terms allowed under Section 7 of the GNU
30
**   Affero GPL version 3 and may not be removed by you.
31
** 
32
**   0. Attribution Requirement.
33
** 
34
**     You must preserve all legal notices or author attributions in the covered
35
**     work or Appropriate Legal Notices displayed by works containing the covered
36
**     work.  You may not remove from the covered work any author or developer
37
**     credit already included within the covered work.
38
** 
39
**   1. No License To Use Trademarks.
40
** 
41
**     This license does not grant any license or rights to use the trademarks
42
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
43
**     of Golden Code Development Corporation. You are not authorized to use the
44
**     name Golden Code, FWD, or the names of any author or contributor, for
45
**     publicity purposes without written authorization.
46
** 
47
**   2. No Misrepresentation of Affiliation.
48
** 
49
**     You may not represent yourself as Golden Code Development Corporation or FWD.
50
** 
51
**     You may not represent yourself for publicity purposes as associated with
52
**     Golden Code Development Corporation, FWD, or any author or contributor to
53
**     the covered work, without written authorization.
54
** 
55
**   3. No Misrepresentation of Source or Origin.
56
** 
57
**     You may not represent the covered work as solely your work.  All modified
58
**     versions of the covered work must be marked in a reasonable way to make it
59
**     clear that the modified work is not originating from Golden Code Development
60
**     Corporation or FWD.  All modified versions must contain the notices of
61
**     attribution required in this license.
62
*/
63

  
64
package com.goldencode.proxy;
65

  
66
import java.lang.reflect.*;
67
import java.util.*;
68
import java.util.function.*;
69

  
70
import org.objectweb.asm.*;
71
import org.objectweb.asm.Type;
72

  
73
import com.goldencode.asm.*;
74

  
75
/**
76
 * A class which assembles dynamic types defining lambda functions for all public, non-static, methods of
77
 * that type (and its super-classes and super-interfaceS).  This is done using the ASM bytecode engineering 
78
 * library, v5.0.4 (http://asm.ow2.org).  As a package private class, it is intended to be invoked only by the 
79
 * {@link LambadFactory}, which exposes a public API for the definition of new proxy objects.
80
 * <p>
81
 * This mechanism is used to replace Java reflection (via {@link Method#invoke(Object, Object...)} or 
82
 * ReflectASM, to perform dynamic calls.
83
 * <p>
84
 * The following pseudo-code representation is intended to convey the general structure of the generated type. 
85
 *  However, please note that no intermediate source code representation is produced.
86
 * <p>
87
 * <pre>
88
 * public final class $__[type]Lambdas 
89
 * {
90
 *    // the mapping of Method instances to the BiFunction lambda
91
 *    private static final HashMap lambdas = new HashMap();
92
 *    
93
 *    // get the 'lambdas' map.
94
 *    public static Map getLambdas() 
95
 *    {
96
 *       return lambdas;
97
 *    }
98
 *    
99
 *    static
100
 *    {
101
 *       try
102
 *       {
103
 *          Method m = [type].class.getDeclaredMethod("[method]", [arg types...]);
104
 *          lambdas.put(m, (ref, args) -> { return (([type]) ref).[method](args[0], args[1], ...); });
105
 *          ...
106
 *          // each method in the given type
107
 *          ...
108
 *       }
109
 *       catch (Throwable t)
110
 *       {
111
 *          throw new RuntimeException(t);
112
 *       }
113
 *    }
114
 * }
115
 * </pre>
116
 */
117
class LambdaAssembler
118
implements Opcodes
119
{
120
   /** The name of the static map holding the lambdas. */
121
   private static final String FIELD_LAMBDAS = "lambdas";
122
   
123
   /** Object used to create class fields, methods, etc. */
124
   private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | 
125
                                                           ClassWriter.COMPUTE_MAXS);
126
   
127
   /** The name of the type where the lambdas will be defined. */
128
   private final String typeName;
129
   
130
   /** The methods which require lambda definitions. */
131
   private final HashSet<Method> methods;
132

  
133
   /** A counter for the number of lambdas defined in this type. */
134
   private int lambdaNum = 0;
135
   
136
   /**
137
    * Constructor.
138
    * 
139
    * @param    typeName
140
    *           The name of the type where the lambdas will be defined.
141
    * @param    methods
142
    *           The methods which require lambda definitions.
143
    */
144
   LambdaAssembler(String typeName, HashSet<Method> methods)
145
   {
146
      this.typeName = AsmUtils.commonToInternalTypeName(typeName);
147
      this.methods = methods;
148
   }
149

  
150
   /**
151
    * Assemble the class in accordance with the Java class file format as specified in <u>The Java Virtual 
152
    * Machine Specification, Second Edition</u> by Tim Lindholm and Frank Yellin.
153
    * <p>
154
    * See the class description above for details regarding the content of the assembled class.
155
    * 
156
    * @return   Byte array containing the class definition.
157
    */
158
   public byte[] assembleClass()
159
   {
160
      // create the dynamic class
161
      classWriter.visit(V1_8,
162
                        ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
163
                        typeName,
164
                        null,
165
                        "java/lang/Object",
166
                        null);
167
      
168
      // add a class member field for the lambda map
169
      classWriter.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
170
                             FIELD_LAMBDAS,
171
                             "Ljava/util/HashMap;",
172
                             null,
173
                             null);
174
      
175
      assembleLambdaGetter();
176
      
177
      assembleLambdas();
178
      
179
      // complete the assembly
180
      classWriter.visitEnd();
181

  
182
      // get the byte array that represents the class file
183
      byte[] code = classWriter.toByteArray();
184

  
185
      return code;
186
   }
187

  
188
   /**
189
    * Emit the bytecode for the lambda call.
190
    * 
191
    * @param    typeName
192
    *           The type where this lambda is defined.
193
    * @param    method
194
    *           The target method.
195
    * @param    lambdaName
196
    *           The name of this synthetic lambda method.
197
    * @param    classWriter
198
    *           Class writer for proxy class.
199
    * @param    mv
200
    *           Method writer, where this lambda is being invoked.
201
    */
202
   public static void emitLambdaCall(String        typeName, 
203
                                     Method        method, 
204
                                     String        lambdaName, 
205
                                     ClassWriter   classWriter, 
206
                                     MethodVisitor mv)
207
   {
208
      String retType = "Ljava/lang/Object;";
209
      
210
      Object[] mthdArgs = 
211
      { 
212
         Type.getMethodType("(Ljava/lang/Object;Ljava/lang/Object;)" + retType),
213
         new Handle(H_INVOKESTATIC, 
214
                    typeName, 
215
                    lambdaName, 
216
                    "(Ljava/lang/Object;[Ljava/lang/Object;)" + retType),
217
                    Type.getMethodType("(Ljava/lang/Object;[Ljava/lang/Object;)" + retType)
218
      };
219
      
220
      Handle h = new Handle(H_INVOKESTATIC, 
221
                            "java/lang/invoke/LambdaMetafactory", 
222
                            "metafactory", 
223
                            "(Ljava/lang/invoke/MethodHandles$Lookup;" + 
224
                            "Ljava/lang/String;Ljava/lang/invoke/MethodType;" +
225
                            "Ljava/lang/invoke/MethodType;" +
226
                            "Ljava/lang/invoke/MethodHandle;" +
227
                            "Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"
228
                            );
229

  
230
      mv.visitInvokeDynamicInsn("apply", "()Ljava/util/function/BiFunction;", h, mthdArgs);
231
      
232
      Class<?> clazz = method.getDeclaringClass();
233
      if (clazz.isAnonymousClass())
234
      {
235
         classWriter.visitInnerClass(AsmUtils.commonToInternalTypeName(clazz.getName()), 
236
                                     AsmUtils.commonToInternalTypeName(clazz.getDeclaringClass().getName()), 
237
                                     clazz.getSimpleName(), 
238
                                     clazz.getModifiers());
239
      }
240
      classWriter.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", 
241
                                  "java/lang/invoke/MethodHandles", 
242
                                  "Lookup", 
243
                                  ACC_PUBLIC | ACC_STATIC | ACC_FINAL);
244
   }
245

  
246
   /**
247
    * Emit a the bytecode for the synthetic lambda method.  This will perform the actual Java method call, 
248
    * by using the first lambda argument as the reference (in case of instance methods) and emiting the 
249
    * arguments from the second <code>Object[]</code> lambda argument.
250
    * <p>
251
    * Arguments for Java native type parameters are automatically unwrapped.
252
    *  
253
    * @param    classWriter
254
    *           Class writer for proxy class.
255
    * @param    lambdaName
256
    *           The name of this synthetic lambda method.
257
    * @param    method
258
    *           Method for which this proxy is being implemented.
259
    */
260
   static void emitLambdaMethod(ClassWriter classWriter, String lambdaName, Method method)
261
   {
262
      boolean isVoid = method.getReturnType() == void.class;
263
      
264
      String desc = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
265
      
266
      MethodVisitor mv = classWriter.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, 
267
                                                 lambdaName, 
268
                                                 desc, 
269
                                                 null,
270
                                                 new String[0]);
271
      mv.visitCode();
272
      
273
      int maxStack = 0;
274
      
275
      {
276
         Class<?> clazz = method.getDeclaringClass();
277
         String clazzDesc = AsmUtils.commonToInternalTypeName(clazz);
278
         // TODO: null instances (static methods)
279
         mv.visitVarInsn(ALOAD, 0);
280
         mv.visitTypeInsn(CHECKCAST, clazzDesc);
281
         
282
         Class<?>[] parmTypes = method.getParameterTypes();
283
         for (int i = 0; i < parmTypes.length; i++)
284
         {
285
            TypeInfo parmInfo = ProxyUtils.getTypeInfo(method.getParameterTypes()[i]);
286

  
287
            maxStack += parmInfo.getWidth();
288
            
289
            mv.visitVarInsn(ALOAD, 1);
290
            AsmUtils.pushInt(mv, i);
291
            mv.visitInsn(AALOAD);
292
            
293
            MethodInfo unwrapInfo = parmInfo.getUnwrapInfo();
294
            String castName = parmInfo.getCastName();
295
            if (castName != null && !"java/lang/Object".equals(castName))
296
            {
297
               mv.visitTypeInsn(CHECKCAST, parmInfo.getCastName());
298
            }
299
            
300
            if (unwrapInfo != null)
301
            {
302
               mv.visitMethodInsn(INVOKEVIRTUAL,
303
                                  unwrapInfo.getClassName(),
304
                                  unwrapInfo.getMethodName(),
305
                                  unwrapInfo.getDescriptor(),
306
                                  false);
307
            }
308
         }
309
         desc = ProxyUtils.makeMethodDescriptor(method);
310
         boolean isStatic = Modifier.isStatic(method.getModifiers());
311
         boolean isIFace = clazz.isInterface();
312
         boolean isAbstract = Modifier.isAbstract(method.getModifiers());
313
         boolean isPublic = Modifier.isPublic(method.getModifiers());
314
         mv.visitMethodInsn(isStatic ? INVOKESTATIC : isIFace ? INVOKEINTERFACE : INVOKEVIRTUAL, 
315
                            clazzDesc, 
316
                            method.getName(), 
317
                            desc, 
318
                            isIFace);
319
         
320
         if (isVoid)
321
         {
322
            mv.visitInsn(ACONST_NULL);
323
         }
324
         else
325
         {
326
            TypeInfo retInfo = ProxyUtils.getTypeInfo(method.getReturnType());
327
            MethodInfo wrapInfo = retInfo.getWrapInfo();
328
            if (wrapInfo != null)
329
            {
330
               mv.visitMethodInsn(INVOKESTATIC,
331
                                  wrapInfo.getClassName(),
332
                                  wrapInfo.getMethodName(),
333
                                  wrapInfo.getDescriptor(),
334
                                  false);
335
            }
336
         }
337
         
338
         mv.visitInsn(ARETURN);
339
      }
340
      
341
      mv.visitMaxs(maxStack, 2);
342
      mv.visitEnd();
343
   }
344

  
345
   /**
346
    * Assemble the <code>getLambdas</code> public, static, method, which returns the {@link #FIELD_LAMBDAS} 
347
    * map.
348
    */
349
   private void assembleLambdaGetter()
350
   {
351
      MethodVisitor mv =
352
               classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC,
353
                                       "getLambdas",
354
                                       "()Ljava/util/Map;",
355
                                       null,
356
                                       null);
357

  
358
      // get the current value of the "lambdas" static field
359
      mv.visitFieldInsn(GETSTATIC,
360
                        typeName,
361
                        FIELD_LAMBDAS,
362
                        "Ljava/util/HashMap;");
363
      
364
      mv.visitInsn(ARETURN);
365
      
366
      mv.visitMaxs(0, 0);
367
      mv.visitEnd();
368
   }
369

  
370
   /**
371
    * Assemble all lambdas, to populate the 'lambdas' map.  This will create a static constructor definition
372
    * where, for each {@link #methods method}, the lambda is created and put in the map.
373
    * <p>
374
    * All lambdas are a {@link BiFunction}, with <code>Object</code> as first type (the reference on which the
375
    * target method is being invoked), and a <code>Object[]</code> second type, with the arguments to be 
376
    * passed to the method call.
377
    */
378
   private void assembleLambdas()
379
   {
380
      // create the class static constructor
381
      MethodVisitor mv = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
382
      
383
      Label tryLblStart = new Label();
384
      Label tryLblEnd = new Label();
385
      Label catchLblStart = new Label();
386
      Label catchLblEnd = new Label();
387
      Label tryBlock = new Label();
388

  
389
      Label mlblStart = tryLblStart;
390
      Label mlblEnd = tryLblEnd;
391
      Label tlblStart = catchLblStart;
392
      Label tlblEnd = catchLblEnd;
393
      
394
      mv.visitLabel(new Label());
395
      // initialize the 'lambdas' static field
396
      mv.visitTypeInsn(NEW, "java/util/HashMap");
... This diff was truncated because it exceeds the maximum size that can be displayed.