=== modified file 'src/com/goldencode/p2j/persist/RecordBuffer.java'
--- old/src/com/goldencode/p2j/persist/RecordBuffer.java	2022-11-03 00:24:27 +0000
+++ new/src/com/goldencode/p2j/persist/RecordBuffer.java	2022-12-12 15:27:53 +0000
@@ -1249,6 +1249,8 @@
 **     CA  20221010          Performance improvements - avoid the ProcedureData lookup, by keeping a parallel 
 **                           stack of this data for THIS-PROCEDURE.  Refs #6826
 **     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
+**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
+**                           expressions to perform a direct call of the target, and avoid reflection.
 */
 
 /*
@@ -11719,7 +11721,7 @@
     * purposes.
     */
    private final class Handler
-   implements InvocationHandler
+   implements LambdaInvocationHandler
    {
       /** Exception thrown during validation of no-undo temp-tables, which must be deferred */
       private ValidationException deferredValidationError = null;
@@ -11739,6 +11741,10 @@
        *          The target proxy object.
        * @param   method 
        *          The method to be executed.
+       * @param   f
+       *          A function to invoke as target, when specified.
+       * @param   c
+       *          A consumer to invoke as target, when specified.
        * @param   args 
        *          The method arguments.
        *
@@ -11747,7 +11753,12 @@
        * @throws  Throwable
        *          For various reasons, like illegal access to fields, illegal arguments or target.
        */
-      public Object invoke(Object proxy, Method method, Object[] args)
+      @Override
+      public Object invoke(Object proxy, 
+                           final Method method, 
+                           BiFunction<Object, Object[], Object> f, 
+                           BiConsumer<Object, Object[]> c,
+                           Object[] args) 
       throws Throwable
       {
          Object result = null;
@@ -11763,7 +11774,7 @@
             Class<?> declClass = method.getDeclaringClass();
             if (declClass.equals(BufferReference.class) || declClass.equals(Object.class))
             {
-               return Utils.invoke(method, RecordBuffer.this, args);
+               return Utils.invoke(method, f, c, RecordBuffer.this, args);
             }
             
             // TODO: use something more reliable (but efficient) here to determine whether this is a setter
@@ -11777,6 +11788,9 @@
             // to ensure all validation and flushing is tracked properly
             if (creatingBuffer != null && currentRecord != null && isSetter)
             {
+               // this can't receive the lambdas, as they are statically linked with the this buffer's
+               // proxy types (a ClassCastException may be raised if the instance passed as first argument at
+               // the lambda is not of the expected type).
                return creatingBuffer.handler.invoke(proxy, method, args);
             }
             
@@ -11795,7 +11809,7 @@
             // if a temporary record is set, use it to back the invocation
             if (tempRecord != null)
             {
-               return Utils.invoke(method, tempRecord, args);
+               return Utils.invoke(method, f, c, tempRecord, args);
             }
             
             if (isSetter)
@@ -11988,7 +12002,7 @@
             }
             
             // invoke the method on the concrete DMO implementation
-            result = Utils.invoke(method, currentRecord, args);
+            result = Utils.invoke(method, f, c, currentRecord, args);
             
             if (writeSnapshot != null && !currentRecord.checkState(DmoState.CHANGED))
             {
@@ -12120,7 +12134,7 @@
                   if (!ok)
                   {
                      // the assign trigger vetoed: rolling back the change
-                     Utils.invoke(method, currentRecord, oldVal);
+                     Utils.invoke(method, f, c, currentRecord, oldVal);
                      processChange = false;
                   }
                }
@@ -12795,7 +12809,7 @@
     * {@link RecordBuffer} proxies.
     */
    private static class ArgumentBuffer
-   implements InvocationHandler
+   implements LambdaInvocationHandler
    {
       /** The backing buffer of this proxy, which should handle the majority of the methods */
       private BufferImpl buffer;
@@ -12885,7 +12899,12 @@
        * imply the DMO alias or buffer name, which will be handled here.
        */
       @Override
-      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+      public Object invoke(Object proxy, 
+                           final Method method, 
+                           BiFunction<Object, Object[], Object> f, 
+                           BiConsumer<Object, Object[]> c,
+                           Object[] args)
+      throws Throwable
       {
          try 
          {
@@ -12906,7 +12925,7 @@
                if (method.getName().equals("overrideName") ||
                    method.getName().equals("restoreName"))
                {
-                  return Utils.invoke(method, buffer, args);
+                  return Utils.invoke(method, f, c, buffer, args);
                }
                
                boolean overridden = buffer.overrideName(bufferName);
@@ -12914,7 +12933,7 @@
                
                try
                {
-                  invokeReturn = Utils.invoke(method, buffer, args);
+                  invokeReturn = Utils.invoke(method, f, c, buffer, args);
                }
                finally
                {
@@ -12944,7 +12963,7 @@
                if (method.getName().equals("overrideDMOAlias") ||
                    method.getName().equals("restoreDMOAlias"))
                {
-                  return Utils.invoke(method, backBuffer, args);
+                  return Utils.invoke(method, f, c, backBuffer, args);
                }
                
                boolean overridden = backBuffer.overrideDMOAlias(dmoAlias);
@@ -12952,7 +12971,7 @@
                
                try
                {
-                  invokeReturn = Utils.invoke(method, backBuffer, args);
+                  invokeReturn = Utils.invoke(method, f, c, backBuffer, args);
                }
                finally
                {

=== modified file 'src/com/goldencode/p2j/persist/StaticDataSet.java'
--- old/src/com/goldencode/p2j/persist/StaticDataSet.java	2022-05-26 17:40:26 +0000
+++ new/src/com/goldencode/p2j/persist/StaticDataSet.java	2022-12-12 15:27:59 +0000
@@ -15,6 +15,8 @@
 **     OM  20211011 Improved debugging support for proxy objects.
 **     CA  20220707 Used a static 'doNotProxy' field, instead of creating the array each time.
 **     GES 20210501 Upgraded table-handle parameter processing to match API changes.
+**     CA  20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions 
+**                  to perform a direct call of the target, and avoid reflection.
 */
 
 /*
@@ -77,6 +79,7 @@
 import com.goldencode.proxy.*;
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.function.*;
 
 
 /**
@@ -448,7 +451,7 @@
     * {@code DataSet} or will throw errors.
     */
    static class Handler
-   implements InvocationHandler
+   implements LambdaInvocationHandler
    {
       /**
        * Processes a method invocation on a proxy instance and returns the result. This method
@@ -462,6 +465,10 @@
        *          proxy instance. The declaring class of the {@code Method} object will be the
        *          interface that the method was declared in, which may be a superinterface of the
        *          proxy interface that the proxy class inherits the method through.
+       * @param   f
+       *          A function to invoke as target, when specified.
+       * @param   c
+       *          A consumer to invoke as target, when specified.
        * @param   args
        *          an array of objects containing the values of the arguments passed in the method
        *          invocation on the proxy instance, or {@code null} if interface method takes no
@@ -475,13 +482,17 @@
        * @see UndeclaredThrowableException
        */
       @Override
-      public Object invoke(Object proxy, Method method, Object[] args)
+      public Object invoke(Object proxy, 
+                           final Method method, 
+                           BiFunction<Object, Object[], Object> f, 
+                           BiConsumer<Object, Object[]> c,
+                           Object[] args) 
       throws Throwable
       {
          StaticDataSet dsp = (StaticDataSet) proxy;
          if (dsp.bound != null)
          {
-            return Utils.invoke(method, dsp.bound, args);
+            return Utils.invoke(method, f, c, dsp.bound, args);
          }
          
          String[] errParam = new String[1];

=== modified file 'src/com/goldencode/p2j/persist/TemporaryBuffer.java'
--- old/src/com/goldencode/p2j/persist/TemporaryBuffer.java	2022-12-06 13:47:32 +0000
+++ new/src/com/goldencode/p2j/persist/TemporaryBuffer.java	2022-12-12 15:28:04 +0000
@@ -628,6 +628,8 @@
 **     IAS 20221103          Fixed support for a binded table parameter.
 **     CA  20221206          If a REFERENCE-ONLY table is already bound to the same target table, do not 
 **                           re-bind. 
+**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
+**                           expressions to perform a direct call of the target, and avoid reflection.
 */
 
 /*
@@ -708,7 +710,7 @@
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.util.ErrorManager;
 import com.goldencode.p2j.util.LogHelper;
-import com.goldencode.proxy.ProxyFactory;
+import com.goldencode.proxy.*;
 import com.goldencode.util.*;
 
 /**
@@ -8870,7 +8872,7 @@
     * The handler to switch between references, when binding a definition table to another one.
     */
    private static class ReferenceProxy
-   implements InvocationHandler
+   implements LambdaInvocationHandler
    {
       /** The default properties - PK and any other properties in {@link TempRecord}. */
       private static final Map<String, String> defaultProps;
@@ -9051,7 +9053,11 @@
        * </ul>
        */
       @Override
-      public Object invoke(Object proxy, Method method, Object[] args)
+      public Object invoke(Object proxy, 
+                           Method method, 
+                           BiFunction<Object, Object[], Object> f, 
+                           BiConsumer<Object, Object[]> c,
+                           Object[] args) 
       throws Throwable
       {
          Object instance = (bound == null ? def : bound);
@@ -9074,7 +9080,7 @@
              declClass.equals(Object.class)          ||
              HandleResource.class.isAssignableFrom(declClass))
          {
-            return Utils.invoke(method, instance, args);
+            return Utils.invoke(method, f, c, instance, args);
          }
          
          if (bound != null && boundBuffer.getDMOInterface() != defBuffer.getDMOInterface())
@@ -9094,7 +9100,10 @@
                
                Method mbound = (setter ? boundSetterByProp.get(boundProperty) 
                                        : boundGetterByProp.get(boundProperty));
-               
+
+               // instance has switched
+               f = null;
+               c = null;
                method = mbound;
             }
          }
@@ -9107,7 +9116,7 @@
                method.setAccessible(true);
             }
             
-            return Utils.invoke(method, instance, args);
+            return Utils.invoke(method, f, c, instance, args);
          }
          catch (Throwable t)
          {

=== modified file 'src/com/goldencode/p2j/util/Utils.java'
--- old/src/com/goldencode/p2j/util/Utils.java	2022-05-04 18:00:37 +0000
+++ new/src/com/goldencode/p2j/util/Utils.java	2022-12-12 15:28:22 +0000
@@ -184,6 +184,8 @@
 **     RFB 20220504          Minor javadoc fixes.
 **     SBI 20220504          Fixed toChar(int, String) to return -1 if key code is not represented by
 **                           the given char set.
+**     CA  20221212          Added support for a LambdaInvocationHandler which receives as arguments lambda 
+**                           expressions to perform a direct call of the target, and avoid reflection.
 */
 
 /*
@@ -1601,6 +1603,53 @@
     * More, the maps are identity-keyed, for fast access.
     * 
     * @param    method
+    *           The method to execute, only if the function or consumer are both null.
+    * @param    f
+    *           When not-null, invoke this function and return its value.
+    * @param    f
+    *           When not-null, invoke this consumer and return null.
+    * @param    instance
+    *           The instance on which the call will be made; can be <code>null</code> for static methods.
+    * @param    args
+    *           The method's arguments.
+    *           
+    * @return   The method's return value.
+    * 
+    * @throws   IllegalAccessException
+    * @throws   IllegalArgumentException
+    * @throws   InvocationTargetException
+    */
+   public static Object invoke(Method method, 
+                               BiFunction<Object, Object[], Object> f, 
+                               BiConsumer<Object, Object[]> c,
+                               Object instance, 
+                               Object... args) 
+   throws IllegalAccessException, 
+          IllegalArgumentException, 
+          InvocationTargetException
+    {
+      if (f != null)
+      {
+         return f.apply(instance, args);
+      }
+      else if (c != null)
+      {
+         c.accept(instance, args);
+         return null;
+      }
+      else
+      {
+         return invoke(method, instance, args);
+      }
+    }
+   
+   /**
+    * Invoke the specified {@link Method} using ReflectASM instead of standard reflection.
+    * <p>
+    * ReflectASM related data is cached in {@link #METHOD_DATA}, thread-local, to avoid the concurrent access.
+    * More, the maps are identity-keyed, for fast access.
+    * 
+    * @param    method
     *           The method to execute.
     * @param    instance
     *           The instance on which the call will be made; can be <code>null</code> for static methods.

=== modified file 'src/com/goldencode/proxy/ProxyAssembler.java'
--- old/src/com/goldencode/proxy/ProxyAssembler.java	2020-09-06 23:15:41 +0000
+++ new/src/com/goldencode/proxy/ProxyAssembler.java	2022-12-12 15:29:09 +0000
@@ -2,7 +2,7 @@
 ** Module   : ProxyAssembler.java
 ** Abstract : Assembles a dynamic proxy subclass.
 **
-** Copyright (c) 2010-2019, Golden Code Development Corporation.
+** Copyright (c) 2010-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description----------------------------------
 ** 001 ECF 20101119 Created initial version.
@@ -18,6 +18,8 @@
 ** 005 ECF 20160219 Added support for an optional plugin to override the assembly of arbitrary
 **                  methods.
 ** 006 OM  20191123 Minor collection change.
+** 007 CA  20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions
+**                  to perform a direct call of the target, and avoid reflection.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -213,6 +215,9 @@
    /** Object used to create class fields, methods, etc. */
    private final ClassWriter classWriter =
       new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+
+   /** A plugin to assemble the 'invoke' method for the proxy handler. */
+   private final InvocationHandlerPlugin handlerPlugin;
    
    /**
     * Constructor.
@@ -236,13 +241,17 @@
     *          interface, else <code>false</code>.  If <code>true</code>, no separate instance
     *          field will be assembled to store the invocation handler reference, as the instance
     *          of this class itself will act as the handler.
+    * @param   handlerPlugin
+    *          A plugin to assemble the 'invoke' method for the proxy handler.  If <code>null</code>, 
+    *          defaults to {@link DefaultHandlerPlugin}.
     */
    ProxyAssembler(String proxyName,
                   String parentName,
                   Class<?>[] interfaces,
                   Method[] methods,
                   ProxyAssemblerPlugin plugin,
-                  boolean parentIsHandler)
+                  boolean parentIsHandler,
+                  InvocationHandlerPlugin handlerPlugin)
    {
       if (plugin == null)
       {
@@ -265,6 +274,12 @@
       this.methods = methods;
       this.plugin = plugin;
       this.parentIsHandler = parentIsHandler;
+
+      if (handlerPlugin == null)
+      {
+         handlerPlugin = new DefaultHandlerPlugin();
+      }
+      this.handlerPlugin = handlerPlugin;
    }
    
    /**
@@ -301,8 +316,8 @@
       }
       
       // create the proxy class
-      classWriter.visit(V1_7,
-                        ACC_PUBLIC | ACC_FINAL,
+      classWriter.visit(V1_8,
+                        ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
                         proxyName,
                         null,
                         parentName,
@@ -320,7 +335,7 @@
          // add an instance member field for the InvocationHandler
          classWriter.visitField(ACC_PRIVATE | ACC_FINAL,
                                 "h",
-                                "Ljava/lang/reflect/InvocationHandler;",
+                                "L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";",
                                 null,
                                 null);
       }
@@ -356,7 +371,7 @@
                                     ProxyUtils.getCaughtExceptionTypeNames(method));
          
          mv.visitCode();
-         proxyMethod.assemble(classWriter, mv, method, i, parentIsHandler, proxyName);
+         proxyMethod.assemble(classWriter, mv, method, i, parentIsHandler, proxyName, handlerPlugin);
          mv.visitMaxs(0, 0);
          mv.visitEnd();
       }
@@ -466,7 +481,7 @@
       MethodVisitor mv =
          classWriter.visitMethod(ACC_PUBLIC,
                                  "<init>",
-                                 "(Ljava/lang/reflect/InvocationHandler;)V",
+                                 "(L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";)V",
                                  null,
                                  null);
       
@@ -486,7 +501,7 @@
       mv.visitFieldInsn(PUTFIELD,
                         proxyName,
                         "h",
-                        "Ljava/lang/reflect/InvocationHandler;");
+                        "L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";");
       
       // return from c'tor
       mv.visitInsn(RETURN);
@@ -562,6 +577,40 @@
                            boolean parentIsHandler,
                            String proxyName)
       {
+         assemble(classWriter, mv, method, methodIndex, parentIsHandler, proxyName, null);
+      }
+      
+      /**
+       * Assemble the bytecode which comprises the proxy method.
+       * 
+       * @param   classWriter
+       *          Class writer for proxy class.
+       * @param   mv
+       *          Method visitor for this method.
+       * @param   method
+       *          Method for which this proxy is being implemented.
+       * @param   methodIndex
+       *          Index of this method within the array of methods used by the invocation handler.
+       * @param   parentIsHandler
+       *          <code>true</code> if the parent class implements the
+       *          <code>InvocationHandler</code> interface, else <code>false</code>.  If
+       *          <code>true</code>, no separate instance field will be assembled to store the
+       *          invocation handler reference, as the instance of this class itself will act as
+       *          the handler.
+       * @param   proxyName
+       *          Internal form of the proxy class' name.
+       * @param   handlerPlugin
+       *          A plugin to assemble the 'invoke' method for the proxy handler.  If <code>null</code>, 
+       *          defaults to {@link DefaultHandlerPlugin}.
+       */
+      public void assemble(ClassWriter classWriter,
+                           MethodVisitor mv,
+                           Method method,
+                           int methodIndex,
+                           boolean parentIsHandler,
+                           String proxyName,
+                           InvocationHandlerPlugin handlerPlugin)
+      {
          // load invocation handler from field "h" or as "this" if the proxy
          // itself is the invocation handler
          mv.visitVarInsn(ALOAD, 0);
@@ -570,22 +619,14 @@
             mv.visitFieldInsn(GETFIELD,
                               proxyName,
                               "h",
-                              "Ljava/lang/reflect/InvocationHandler;");
+                              "L" + AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()) + ";");
          }
          
          // load "this" object ref as the first parameter for later call to
          // InvocationHandler.invoke
          mv.visitVarInsn(ALOAD, 0);
          
-         // load target method from array of methods in static field "ma"
-         mv.visitFieldInsn(GETSTATIC,
-                           proxyName,
-                           "ma",
-                           "[Ljava/lang/reflect/Method;");
-         
-         // target method is at index "index" in the "ma" array
-         AsmUtils.pushInt(mv, methodIndex);
-         mv.visitInsn(AALOAD);
+         handlerPlugin.createInvocationArgs(classWriter, mv, proxyName, method, methodIndex);
          
          // create new array of Objects for method's arguments or push null
          // if there are no arguments
@@ -640,9 +681,9 @@
          
          // call InvocationHandler.invoke
          mv.visitMethodInsn(INVOKEINTERFACE,
-                            "java/lang/reflect/InvocationHandler",
+                            AsmUtils.commonToInternalTypeName(handlerPlugin.getHandlerClass()),
                             "invoke",
-                            "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
+                            handlerPlugin.getInvokeSignature(),
                             true);
          
          // determine method's return type

=== modified file 'src/com/goldencode/proxy/ProxyFactory.java'
--- old/src/com/goldencode/proxy/ProxyFactory.java	2022-10-06 08:18:44 +0000
+++ new/src/com/goldencode/proxy/ProxyFactory.java	2022-12-12 15:29:13 +0000
@@ -39,6 +39,8 @@
 **                  equal 'doNotProxy' array, as the cache is kept in an IdentityHashMap. 
 **     CA  20221006 Use a Supplier for the proxy plugin instance, as when the proxy is already created, there
 **                  is no need to use this instance.  Refs #6822
+**     CA  20221212 Added support for a LambdaInvocationHandler which receives as arguments lambda expressions
+**                  to perform a direct call of the target, and avoid reflection.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -555,13 +557,18 @@
                String proxyName = makeProxyClassName(parent, parentName);
                Method[] methods = gatherMethods(parent, proxyParent, interfaces, declaredOnly, doNotProxy);
                
+               InvocationHandlerPlugin handlerPlugin = handler instanceof LambdaInvocationHandler 
+                                                          ? new LambdaHandlerPlugin() 
+                                                          : new DefaultHandlerPlugin();
+               
                // assemble code for the proxy class
                ProxyAssembler asm = new ProxyAssembler(proxyName,
                                                        parentName,
                                                        interfaces,
                                                        methods,
                                                        (plugin == null ? null : plugin.get()),
-                                                       proxyIsHandler);
+                                                       proxyIsHandler,
+                                                       handlerPlugin);
                byte[] code = asm.assembleClass();
                
                /*
@@ -574,6 +581,9 @@
                                   ? proxyName.substring(pos + 1)
                                   : proxyName);
                   fname = fname + ".class";
+                  System.out.println(parent.toString());
+                  for (int i = 0; i < interfaces.length; i++)
+                     System.out.println(interfaces[i].toString());
                   System.out.println("Proxy class file written to: " + fname);
                   fos = new FileOutputStream(fname);
                   fos.write(code);
@@ -605,7 +615,7 @@
                
                if (!proxyIsHandler)
                {
-                  classCtors.put(clazz, clazz.getDeclaredConstructor(InvocationHandler.class));
+                  classCtors.put(clazz, clazz.getDeclaredConstructor(handlerPlugin.getHandlerClass()));
                }
             }
             

