Project

General

Profile

6939_20221212a.patch

Constantin Asofiei, 12/12/2022 10:30 AM

Download (25.1 KB)

View differences:

new/src/com/goldencode/p2j/persist/RecordBuffer.java 2022-12-12 15:27:53 +0000
1249 1249
**     CA  20221010          Performance improvements - avoid the ProcedureData lookup, by keeping a parallel 
1250 1250
**                           stack of this data for THIS-PROCEDURE.  Refs #6826
1251 1251
**     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
1252
**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
1253
**                           expressions to perform a direct call of the target, and avoid reflection.
1252 1254
*/
1253 1255

  
1254 1256
/*
......
11719 11721
    * purposes.
11720 11722
    */
11721 11723
   private final class Handler
11722
   implements InvocationHandler
11724
   implements LambdaInvocationHandler
11723 11725
   {
11724 11726
      /** Exception thrown during validation of no-undo temp-tables, which must be deferred */
11725 11727
      private ValidationException deferredValidationError = null;
......
11739 11741
       *          The target proxy object.
11740 11742
       * @param   method 
11741 11743
       *          The method to be executed.
11744
       * @param   f
11745
       *          A function to invoke as target, when specified.
11746
       * @param   c
11747
       *          A consumer to invoke as target, when specified.
11742 11748
       * @param   args 
11743 11749
       *          The method arguments.
11744 11750
       *
......
11747 11753
       * @throws  Throwable
11748 11754
       *          For various reasons, like illegal access to fields, illegal arguments or target.
11749 11755
       */
11750
      public Object invoke(Object proxy, Method method, Object[] args)
11756
      @Override
11757
      public Object invoke(Object proxy, 
11758
                           final Method method, 
11759
                           BiFunction<Object, Object[], Object> f, 
11760
                           BiConsumer<Object, Object[]> c,
11761
                           Object[] args) 
11751 11762
      throws Throwable
11752 11763
      {
11753 11764
         Object result = null;
......
11763 11774
            Class<?> declClass = method.getDeclaringClass();
11764 11775
            if (declClass.equals(BufferReference.class) || declClass.equals(Object.class))
11765 11776
            {
11766
               return Utils.invoke(method, RecordBuffer.this, args);
11777
               return Utils.invoke(method, f, c, RecordBuffer.this, args);
11767 11778
            }
11768 11779
            
11769 11780
            // TODO: use something more reliable (but efficient) here to determine whether this is a setter
......
11777 11788
            // to ensure all validation and flushing is tracked properly
11778 11789
            if (creatingBuffer != null && currentRecord != null && isSetter)
11779 11790
            {
11791
               // this can't receive the lambdas, as they are statically linked with the this buffer's
11792
               // proxy types (a ClassCastException may be raised if the instance passed as first argument at
11793
               // the lambda is not of the expected type).
11780 11794
               return creatingBuffer.handler.invoke(proxy, method, args);
11781 11795
            }
11782 11796
            
......
11795 11809
            // if a temporary record is set, use it to back the invocation
11796 11810
            if (tempRecord != null)
11797 11811
            {
11798
               return Utils.invoke(method, tempRecord, args);
11812
               return Utils.invoke(method, f, c, tempRecord, args);
11799 11813
            }
11800 11814
            
11801 11815
            if (isSetter)
......
11988 12002
            }
11989 12003
            
11990 12004
            // invoke the method on the concrete DMO implementation
11991
            result = Utils.invoke(method, currentRecord, args);
12005
            result = Utils.invoke(method, f, c, currentRecord, args);
11992 12006
            
11993 12007
            if (writeSnapshot != null && !currentRecord.checkState(DmoState.CHANGED))
11994 12008
            {
......
12120 12134
                  if (!ok)
12121 12135
                  {
12122 12136
                     // the assign trigger vetoed: rolling back the change
12123
                     Utils.invoke(method, currentRecord, oldVal);
12137
                     Utils.invoke(method, f, c, currentRecord, oldVal);
12124 12138
                     processChange = false;
12125 12139
                  }
12126 12140
               }
......
12795 12809
    * {@link RecordBuffer} proxies.
12796 12810
    */
12797 12811
   private static class ArgumentBuffer
12798
   implements InvocationHandler
12812
   implements LambdaInvocationHandler
12799 12813
   {
12800 12814
      /** The backing buffer of this proxy, which should handle the majority of the methods */
12801 12815
      private BufferImpl buffer;
......
12885 12899
       * imply the DMO alias or buffer name, which will be handled here.
12886 12900
       */
12887 12901
      @Override
12888
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
12902
      public Object invoke(Object proxy, 
12903
                           final Method method, 
12904
                           BiFunction<Object, Object[], Object> f, 
12905
                           BiConsumer<Object, Object[]> c,
12906
                           Object[] args)
12907
      throws Throwable
12889 12908
      {
12890 12909
         try 
12891 12910
         {
......
12906 12925
               if (method.getName().equals("overrideName") ||
12907 12926
                   method.getName().equals("restoreName"))
12908 12927
               {
12909
                  return Utils.invoke(method, buffer, args);
12928
                  return Utils.invoke(method, f, c, buffer, args);
12910 12929
               }
12911 12930
               
12912 12931
               boolean overridden = buffer.overrideName(bufferName);
......
12914 12933
               
12915 12934
               try
12916 12935
               {
12917
                  invokeReturn = Utils.invoke(method, buffer, args);
12936
                  invokeReturn = Utils.invoke(method, f, c, buffer, args);
12918 12937
               }
12919 12938
               finally
12920 12939
               {
......
12944 12963
               if (method.getName().equals("overrideDMOAlias") ||
12945 12964
                   method.getName().equals("restoreDMOAlias"))
12946 12965
               {
12947
                  return Utils.invoke(method, backBuffer, args);
12966
                  return Utils.invoke(method, f, c, backBuffer, args);
12948 12967
               }
12949 12968
               
12950 12969
               boolean overridden = backBuffer.overrideDMOAlias(dmoAlias);
......
12952 12971
               
12953 12972
               try
12954 12973
               {
12955
                  invokeReturn = Utils.invoke(method, backBuffer, args);
12974
                  invokeReturn = Utils.invoke(method, f, c, backBuffer, args);
12956 12975
               }
12957 12976
               finally
12958 12977
               {
new/src/com/goldencode/p2j/persist/StaticDataSet.java 2022-12-12 15:27:59 +0000
15 15
**     OM  20211011 Improved debugging support for proxy objects.
16 16
**     CA  20220707 Used a static 'doNotProxy' field, instead of creating the array each time.
17 17
**     GES 20210501 Upgraded table-handle parameter processing to match API changes.
18
**     CA  20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions 
19
**                  to perform a direct call of the target, and avoid reflection.
18 20
*/
19 21

  
20 22
/*
......
77 79
import com.goldencode.proxy.*;
78 80
import java.lang.reflect.*;
79 81
import java.util.*;
82
import java.util.function.*;
80 83

  
81 84

  
82 85
/**
......
448 451
    * {@code DataSet} or will throw errors.
449 452
    */
450 453
   static class Handler
451
   implements InvocationHandler
454
   implements LambdaInvocationHandler
452 455
   {
453 456
      /**
454 457
       * Processes a method invocation on a proxy instance and returns the result. This method
......
462 465
       *          proxy instance. The declaring class of the {@code Method} object will be the
463 466
       *          interface that the method was declared in, which may be a superinterface of the
464 467
       *          proxy interface that the proxy class inherits the method through.
468
       * @param   f
469
       *          A function to invoke as target, when specified.
470
       * @param   c
471
       *          A consumer to invoke as target, when specified.
465 472
       * @param   args
466 473
       *          an array of objects containing the values of the arguments passed in the method
467 474
       *          invocation on the proxy instance, or {@code null} if interface method takes no
......
475 482
       * @see UndeclaredThrowableException
476 483
       */
477 484
      @Override
478
      public Object invoke(Object proxy, Method method, Object[] args)
485
      public Object invoke(Object proxy, 
486
                           final Method method, 
487
                           BiFunction<Object, Object[], Object> f, 
488
                           BiConsumer<Object, Object[]> c,
489
                           Object[] args) 
479 490
      throws Throwable
480 491
      {
481 492
         StaticDataSet dsp = (StaticDataSet) proxy;
482 493
         if (dsp.bound != null)
483 494
         {
484
            return Utils.invoke(method, dsp.bound, args);
495
            return Utils.invoke(method, f, c, dsp.bound, args);
485 496
         }
486 497
         
487 498
         String[] errParam = new String[1];
new/src/com/goldencode/p2j/persist/TemporaryBuffer.java 2022-12-12 15:28:04 +0000
628 628
**     IAS 20221103          Fixed support for a binded table parameter.
629 629
**     CA  20221206          If a REFERENCE-ONLY table is already bound to the same target table, do not 
630 630
**                           re-bind. 
631
**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
632
**                           expressions to perform a direct call of the target, and avoid reflection.
631 633
*/
632 634

  
633 635
/*
......
708 710
import com.goldencode.p2j.util.*;
709 711
import com.goldencode.p2j.util.ErrorManager;
710 712
import com.goldencode.p2j.util.LogHelper;
711
import com.goldencode.proxy.ProxyFactory;
713
import com.goldencode.proxy.*;
712 714
import com.goldencode.util.*;
713 715

  
714 716
/**
......
8870 8872
    * The handler to switch between references, when binding a definition table to another one.
8871 8873
    */
8872 8874
   private static class ReferenceProxy
8873
   implements InvocationHandler
8875
   implements LambdaInvocationHandler
8874 8876
   {
8875 8877
      /** The default properties - PK and any other properties in {@link TempRecord}. */
8876 8878
      private static final Map<String, String> defaultProps;
......
9051 9053
       * </ul>
9052 9054
       */
9053 9055
      @Override
9054
      public Object invoke(Object proxy, Method method, Object[] args)
9056
      public Object invoke(Object proxy, 
9057
                           Method method, 
9058
                           BiFunction<Object, Object[], Object> f, 
9059
                           BiConsumer<Object, Object[]> c,
9060
                           Object[] args) 
9055 9061
      throws Throwable
9056 9062
      {
9057 9063
         Object instance = (bound == null ? def : bound);
......
9074 9080
             declClass.equals(Object.class)          ||
9075 9081
             HandleResource.class.isAssignableFrom(declClass))
9076 9082
         {
9077
            return Utils.invoke(method, instance, args);
9083
            return Utils.invoke(method, f, c, instance, args);
9078 9084
         }
9079 9085
         
9080 9086
         if (bound != null && boundBuffer.getDMOInterface() != defBuffer.getDMOInterface())
......
9094 9100
               
9095 9101
               Method mbound = (setter ? boundSetterByProp.get(boundProperty) 
9096 9102
                                       : boundGetterByProp.get(boundProperty));
9097
               
9103

  
9104
               // instance has switched
9105
               f = null;
9106
               c = null;
9098 9107
               method = mbound;
9099 9108
            }
9100 9109
         }
......
9107 9116
               method.setAccessible(true);
9108 9117
            }
9109 9118
            
9110
            return Utils.invoke(method, instance, args);
9119
            return Utils.invoke(method, f, c, instance, args);
9111 9120
         }
9112 9121
         catch (Throwable t)
9113 9122
         {
new/src/com/goldencode/p2j/util/Utils.java 2022-12-12 15:28:22 +0000
184 184
**     RFB 20220504          Minor javadoc fixes.
185 185
**     SBI 20220504          Fixed toChar(int, String) to return -1 if key code is not represented by
186 186
**                           the given char set.
187
**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
188
**                           expressions to perform a direct call of the target, and avoid reflection.
187 189
*/
188 190

  
189 191
/*
......
1601 1603
    * More, the maps are identity-keyed, for fast access.
1602 1604
    * 
1603 1605
    * @param    method
1606
    *           The method to execute, only if the function or consumer are both null.
1607
    * @param    f
1608
    *           When not-null, invoke this function and return its value.
1609
    * @param    f
1610
    *           When not-null, invoke this consumer and return null.
1611
    * @param    instance
1612
    *           The instance on which the call will be made; can be <code>null</code> for static methods.
1613
    * @param    args
1614
    *           The method's arguments.
1615
    *           
1616
    * @return   The method's return value.
1617
    * 
1618
    * @throws   IllegalAccessException
1619
    * @throws   IllegalArgumentException
1620
    * @throws   InvocationTargetException
1621
    */
1622
   public static Object invoke(Method method, 
1623
                               BiFunction<Object, Object[], Object> f, 
1624
                               BiConsumer<Object, Object[]> c,
1625
                               Object instance, 
1626
                               Object... args) 
1627
   throws IllegalAccessException, 
1628
          IllegalArgumentException, 
1629
          InvocationTargetException
1630
    {
1631
      if (f != null)
1632
      {
1633
         return f.apply(instance, args);
1634
      }
1635
      else if (c != null)
1636
      {
1637
         c.accept(instance, args);
1638
         return null;
1639
      }
1640
      else
1641
      {
1642
         return invoke(method, instance, args);
1643
      }
1644
    }
1645
   
1646
   /**
1647
    * Invoke the specified {@link Method} using ReflectASM instead of standard reflection.
1648
    * <p>
1649
    * ReflectASM related data is cached in {@link #METHOD_DATA}, thread-local, to avoid the concurrent access.
1650
    * More, the maps are identity-keyed, for fast access.
1651
    * 
1652
    * @param    method
1604 1653
    *           The method to execute.
1605 1654
    * @param    instance
1606 1655
    *           The instance on which the call will be made; can be <code>null</code> for static methods.
new/src/com/goldencode/proxy/ProxyAssembler.java 2022-12-12 15:29:09 +0000
2 2
** Module   : ProxyAssembler.java
3 3
** Abstract : Assembles a dynamic proxy subclass.
4 4
**
5
** Copyright (c) 2010-2019, Golden Code Development Corporation.
5
** Copyright (c) 2010-2022, Golden Code Development Corporation.
6 6
**
7 7
** -#- -I- --Date-- --------------------------------Description----------------------------------
8 8
** 001 ECF 20101119 Created initial version.
......
18 18
** 005 ECF 20160219 Added support for an optional plugin to override the assembly of arbitrary
19 19
**                  methods.
20 20
** 006 OM  20191123 Minor collection change.
21
** 007 CA  20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions
22
**                  to perform a direct call of the target, and avoid reflection.
21 23
*/
22 24
/*
23 25
** This program is free software: you can redistribute it and/or modify
......
213 215
   /** Object used to create class fields, methods, etc. */
214 216
   private final ClassWriter classWriter =
215 217
      new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
218

  
219
   /** A plugin to assemble the 'invoke' method for the proxy handler. */
220
   private final InvocationHandlerPlugin handlerPlugin;
216 221
   
217 222
   /**
218 223
    * Constructor.
......
236 241
    *          interface, else <code>false</code>.  If <code>true</code>, no separate instance
237 242
    *          field will be assembled to store the invocation handler reference, as the instance
238 243
    *          of this class itself will act as the handler.
244
    * @param   handlerPlugin
245
    *          A plugin to assemble the 'invoke' method for the proxy handler.  If <code>null</code>, 
246
    *          defaults to {@link DefaultHandlerPlugin}.
239 247
    */
240 248
   ProxyAssembler(String proxyName,
241 249
                  String parentName,
242 250
                  Class<?>[] interfaces,
243 251
                  Method[] methods,
244 252
                  ProxyAssemblerPlugin plugin,
245
                  boolean parentIsHandler)
253
                  boolean parentIsHandler,
254
                  InvocationHandlerPlugin handlerPlugin)
246 255
   {
247 256
      if (plugin == null)
248 257
      {
......
265 274
      this.methods = methods;
266 275
      this.plugin = plugin;
267 276
      this.parentIsHandler = parentIsHandler;
277

  
278
      if (handlerPlugin == null)
279
      {
280
         handlerPlugin = new DefaultHandlerPlugin();
281
      }
282
      this.handlerPlugin = handlerPlugin;
268 283
   }
269 284
   
270 285
   /**
......
301 316
      }
302 317
      
303 318
      // create the proxy class
304
      classWriter.visit(V1_7,
305
                        ACC_PUBLIC | ACC_FINAL,
319
      classWriter.visit(V1_8,
320
                        ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
306 321
                        proxyName,
307 322
                        null,
308 323
                        parentName,
......
320 335
         // add an instance member field for the InvocationHandler
321 336
         classWriter.visitField(ACC_PRIVATE | ACC_FINAL,
322 337
                                "h",
323
                                "Ljava/lang/reflect/InvocationHandler;",
338
                                "L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";",
324 339
                                null,
325 340
                                null);
326 341
      }
......
356 371
                                    ProxyUtils.getCaughtExceptionTypeNames(method));
357 372
         
358 373
         mv.visitCode();
359
         proxyMethod.assemble(classWriter, mv, method, i, parentIsHandler, proxyName);
374
         proxyMethod.assemble(classWriter, mv, method, i, parentIsHandler, proxyName, handlerPlugin);
360 375
         mv.visitMaxs(0, 0);
361 376
         mv.visitEnd();
362 377
      }
......
466 481
      MethodVisitor mv =
467 482
         classWriter.visitMethod(ACC_PUBLIC,
468 483
                                 "<init>",
469
                                 "(Ljava/lang/reflect/InvocationHandler;)V",
484
                                 "(L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";)V",
470 485
                                 null,
471 486
                                 null);
472 487
      
......
486 501
      mv.visitFieldInsn(PUTFIELD,
487 502
                        proxyName,
488 503
                        "h",
489
                        "Ljava/lang/reflect/InvocationHandler;");
504
                        "L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";");
490 505
      
491 506
      // return from c'tor
492 507
      mv.visitInsn(RETURN);
......
562 577
                           boolean parentIsHandler,
563 578
                           String proxyName)
564 579
      {
580
         assemble(classWriter, mv, method, methodIndex, parentIsHandler, proxyName, null);
581
      }
582
      
583
      /**
584
       * Assemble the bytecode which comprises the proxy method.
585
       * 
586
       * @param   classWriter
587
       *          Class writer for proxy class.
588
       * @param   mv
589
       *          Method visitor for this method.
590
       * @param   method
591
       *          Method for which this proxy is being implemented.
592
       * @param   methodIndex
593
       *          Index of this method within the array of methods used by the invocation handler.
594
       * @param   parentIsHandler
595
       *          <code>true</code> if the parent class implements the
596
       *          <code>InvocationHandler</code> interface, else <code>false</code>.  If
597
       *          <code>true</code>, no separate instance field will be assembled to store the
598
       *          invocation handler reference, as the instance of this class itself will act as
599
       *          the handler.
600
       * @param   proxyName
601
       *          Internal form of the proxy class' name.
602
       * @param   handlerPlugin
603
       *          A plugin to assemble the 'invoke' method for the proxy handler.  If <code>null</code>, 
604
       *          defaults to {@link DefaultHandlerPlugin}.
605
       */
606
      public void assemble(ClassWriter classWriter,
607
                           MethodVisitor mv,
608
                           Method method,
609
                           int methodIndex,
610
                           boolean parentIsHandler,
611
                           String proxyName,
612
                           InvocationHandlerPlugin handlerPlugin)
613
      {
565 614
         // load invocation handler from field "h" or as "this" if the proxy
566 615
         // itself is the invocation handler
567 616
         mv.visitVarInsn(ALOAD, 0);
......
570 619
            mv.visitFieldInsn(GETFIELD,
571 620
                              proxyName,
572 621
                              "h",
573
                              "Ljava/lang/reflect/InvocationHandler;");
622
                              "L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";");
574 623
         }
575 624
         
576 625
         // load "this" object ref as the first parameter for later call to
577 626
         // InvocationHandler.invoke
578 627
         mv.visitVarInsn(ALOAD, 0);
579 628
         
580
         // load target method from array of methods in static field "ma"
581
         mv.visitFieldInsn(GETSTATIC,
582
                           proxyName,
583
                           "ma",
584
                           "[Ljava/lang/reflect/Method;");
585
         
586
         // target method is at index "index" in the "ma" array
587
         AsmUtils.pushInt(mv, methodIndex);
588
         mv.visitInsn(AALOAD);
629
         handlerPlugin.createInvocationArgs(classWriter, mv, proxyName, method, methodIndex);
589 630
         
590 631
         // create new array of Objects for method's arguments or push null
591 632
         // if there are no arguments
......
640 681
         
641 682
         // call InvocationHandler.invoke
642 683
         mv.visitMethodInsn(INVOKEINTERFACE,
643
                            "java/lang/reflect/InvocationHandler",
684
                            AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()),
644 685
                            "invoke",
645
                            "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
686
                            handlerPlugin.getInvokeSignature(),
646 687
                            true);
647 688
         
648 689
         // determine method's return type
new/src/com/goldencode/proxy/ProxyFactory.java 2022-12-12 15:29:13 +0000
39 39
**                  equal 'doNotProxy' array, as the cache is kept in an IdentityHashMap. 
40 40
**     CA  20221006 Use a Supplier for the proxy plugin instance, as when the proxy is already created, there
41 41
**                  is no need to use this instance.  Refs #6822
42
**     CA  20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions
43
**                  to perform a direct call of the target, and avoid reflection.
42 44
*/
43 45
/*
44 46
** This program is free software: you can redistribute it and/or modify
......
555 557
               String proxyName = makeProxyClassName(parent, parentName);
556 558
               Method[] methods = gatherMethods(parent, proxyParent, interfaces, declaredOnly, doNotProxy);
557 559
               
560
               InvocationHandlerPlugin handlerPlugin = handler instanceof LambdaInvocationHandler 
561
                                                          ? new LambdaHandlerPlugin() 
562
                                                          : new DefaultHandlerPlugin();
563
               
558 564
               // assemble code for the proxy class
559 565
               ProxyAssembler asm = new ProxyAssembler(proxyName,
560 566
                                                       parentName,
561 567
                                                       interfaces,
562 568
                                                       methods,
563 569
                                                       (plugin == null ? null : plugin.get()),
564
                                                       proxyIsHandler);
570
                                                       proxyIsHandler,
571
                                                       handlerPlugin);
565 572
               byte[] code = asm.assembleClass();
566 573
               
567 574
               /*
......
574 581
                                  ? proxyName.substring(pos + 1)
575 582
                                  : proxyName);
576 583
                  fname = fname + ".class";
584
                  System.out.println(parent.toString());
585
                  for (int i = 0; i < interfaces.length; i++)
586
                     System.out.println(interfaces[i].toString());
577 587
                  System.out.println("Proxy class file written to: " + fname);
578 588
                  fos = new FileOutputStream(fname);
579 589
                  fos.write(code);
......
605 615
               
606 616
               if (!proxyIsHandler)
607 617
               {
608
                  classCtors.put(clazz, clazz.getDeclaredConstructor(InvocationHandler.class));
618
                  classCtors.put(clazz, clazz.getDeclaredConstructor(handlerPlugin.getHandlerClass()));
609 619
               }
610 620
            }
611 621