=== 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-18 12:29:40 +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,8 @@
        *          The target proxy object.
        * @param   method 
        *          The method to be executed.
+       * @param   f
+       *          A function to invoke as target, when specified.
        * @param   args 
        *          The method arguments.
        *
@@ -11747,7 +11751,11 @@
        * @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, 
+                           Object[] args) 
       throws Throwable
       {
          Object result = null;
@@ -11763,7 +11771,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, RecordBuffer.this, args);
             }
             
             // TODO: use something more reliable (but efficient) here to determine whether this is a setter
@@ -11777,6 +11785,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 +11806,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, tempRecord, args);
             }
             
             if (isSetter)
@@ -11988,7 +11999,7 @@
             }
             
             // invoke the method on the concrete DMO implementation
-            result = Utils.invoke(method, currentRecord, args);
+            result = Utils.invoke(method, f, currentRecord, args);
             
             if (writeSnapshot != null && !currentRecord.checkState(DmoState.CHANGED))
             {
@@ -12120,7 +12131,7 @@
                   if (!ok)
                   {
                      // the assign trigger vetoed: rolling back the change
-                     Utils.invoke(method, currentRecord, oldVal);
+                     Utils.invoke(method, f, currentRecord, oldVal);
                      processChange = false;
                   }
                }
@@ -12795,7 +12806,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 +12896,11 @@
        * 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, 
+                           Object[] args)
+      throws Throwable
       {
          try 
          {
@@ -12906,7 +12921,7 @@
                if (method.getName().equals("overrideName") ||
                    method.getName().equals("restoreName"))
                {
-                  return Utils.invoke(method, buffer, args);
+                  return Utils.invoke(method, f, buffer, args);
                }
                
                boolean overridden = buffer.overrideName(bufferName);
@@ -12914,7 +12929,7 @@
                
                try
                {
-                  invokeReturn = Utils.invoke(method, buffer, args);
+                  invokeReturn = Utils.invoke(method, f, buffer, args);
                }
                finally
                {
@@ -12944,7 +12959,7 @@
                if (method.getName().equals("overrideDMOAlias") ||
                    method.getName().equals("restoreDMOAlias"))
                {
-                  return Utils.invoke(method, backBuffer, args);
+                  return Utils.invoke(method, f, backBuffer, args);
                }
                
                boolean overridden = backBuffer.overrideDMOAlias(dmoAlias);
@@ -12952,7 +12967,7 @@
                
                try
                {
-                  invokeReturn = Utils.invoke(method, backBuffer, args);
+                  invokeReturn = Utils.invoke(method, f, 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-18 12:29:52 +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,8 @@
        *          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   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 +480,16 @@
        * @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, 
+                           Object[] args) 
       throws Throwable
       {
          StaticDataSet dsp = (StaticDataSet) proxy;
          if (dsp.bound != null)
          {
-            return Utils.invoke(method, dsp.bound, args);
+            return Utils.invoke(method, f, 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-18 12:30:10 +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,10 @@
        * </ul>
        */
       @Override
-      public Object invoke(Object proxy, Method method, Object[] args)
+      public Object invoke(Object proxy, 
+                           Method method, 
+                           BiFunction<Object, Object[], Object> f, 
+                           Object[] args) 
       throws Throwable
       {
          Object instance = (bound == null ? def : bound);
@@ -9074,7 +9079,7 @@
              declClass.equals(Object.class)          ||
              HandleResource.class.isAssignableFrom(declClass))
          {
-            return Utils.invoke(method, instance, args);
+            return Utils.invoke(method, f, instance, args);
          }
          
          if (bound != null && boundBuffer.getDMOInterface() != defBuffer.getDMOInterface())
@@ -9094,7 +9099,9 @@
                
                Method mbound = (setter ? boundSetterByProp.get(boundProperty) 
                                        : boundGetterByProp.get(boundProperty));
-               
+
+               // instance has switched
+               f = null;
                method = mbound;
             }
          }
@@ -9107,7 +9114,7 @@
                method.setAccessible(true);
             }
             
-            return Utils.invoke(method, instance, args);
+            return Utils.invoke(method, f, instance, args);
          }
          catch (Throwable t)
          {

=== modified file 'src/com/goldencode/p2j/util/ControlFlowOps.java'
--- old/src/com/goldencode/p2j/util/ControlFlowOps.java	2022-10-10 14:11:51 +0000
+++ new/src/com/goldencode/p2j/util/ControlFlowOps.java	2022-12-20 11:06:30 +0000
@@ -273,6 +273,7 @@
 **                           - for typed FUNCTION calls, convert the returned value to the expected type.
 **                           - fixed caching issues when the call does not complete due to errors or for 
 **                           recursive calls.
+**     CA  20221220          Use lambdas to execute all public methods in an external program.
 */
 
 /*
@@ -957,6 +958,7 @@
             caller.callerInstance = reference;
             caller.wa = wa;
             caller.mthd = lastInvoke.method;
+            caller.lambda = lastInvoke.lambda;
             caller.methodName = lastInvoke.method.getName();
             caller.ie = null;
             caller.iename = cfg.getTarget();
@@ -1003,7 +1005,9 @@
 
                   try
                   {
-                     BaseDataType res = (BaseDataType) lastInvoke.method.invoke(reference, args);
+                     BaseDataType res = (BaseDataType) (lastInvoke.lambda == null 
+                                                         ? lastInvoke.method.invoke(reference, args)
+                                                         : lastInvoke.lambda.apply(reference, args));
                      res = checkInvokeResult(res, iename, cfg.isFunction(), cfg.isDynamicFunction());
                      
                      if (cls != null && cfg.isFunction())
@@ -8539,6 +8543,7 @@
       boolean externalProcedure = caller.getPhandle() == null;
       Object instance = externalProcedure ? caller.getClassName() : caller.getCallerInstance();
       Method mthd = externalProcedure ? null : caller.mthd;
+      BiFunction<Object, Object[], Object> lambda = externalProcedure ? null : caller.lambda;
       
       if (args != null && args.length > 0)
       {
@@ -8556,7 +8561,8 @@
                                                        eventProcedureName,
                                                        eventProcedureContext,
                                                        instance,
-                                                       mthd, 
+                                                       mthd,
+                                                       lambda,
                                                        argumentTypes);
       
       Map<InvokeConfig, LastInvokeDetails> thisCallSite = 
@@ -8602,6 +8608,11 @@
       private final Method method;
       
       /**
+       * When not-null, a lambda target to execute directly the call, without relying on the {@link #method}. 
+       */
+      private final BiFunction<Object, Object[], Object> lambda;
+      
+      /**
        * Create a new instance.
        * 
        * @param    legacyName
@@ -8621,6 +8632,9 @@
        *           {@link #method} is being invoked.
        * @param    method
        *           The Java method to invoke.
+       * @param    lambda
+       *           When not-null, a lambda target to execute directly the call, without relying on the 
+       *           {@link #method}. 
        * @param    argumentTypes
        *           The argument types for this call.
        */
@@ -8632,6 +8646,7 @@
                                Object     eventProcedureContext,
                                Object     reference,
                                Method     method,
+                               BiFunction<Object, Object[], Object> lambda,
                                Class<?>[] argumentTypes)
       {
          this.legacyName = legacyName;
@@ -8646,6 +8661,7 @@
          
          this.reference = (reference == null ? null : new WeakReference<>(reference));
          this.method = method;
+         this.lambda = lambda;
       }
       
       /**
@@ -9166,13 +9182,12 @@
       protected WorkArea wa;
       
       /** The {@link Method} instance for this internal entry. */
-      private Method mthd = null;
-      
-      /** The ReflectASM access point to this method. */
-      private MethodAccess access = null;
-      
-      /** The method index in the MethodAccess instance. */
-      private int index = -1;
+      protected Method mthd = null;
+      
+      protected BiFunction<Object, Object[], Object> lambda = null;
+      
+      /** Already resolved internal entry. */
+      protected InternalEntry ie = null;
       
       /** The handle associated with the {@link #callerInstance}. */
       private handle phandle;
@@ -9180,9 +9195,6 @@
       /** Worker which performs the {@link ProcedureManager.ProcedureHelper#pushCalleeInfo} call. */
       private Runnable pushWorker;
       
-      /** Already resolved internal entry (used when invoking OO methods). */
-      private InternalEntry ie = null;
-      
       /**
        * Basic c'tor.
        * 
@@ -9331,9 +9343,9 @@
             pushWorker.run();
             wa.bm.markPendingDynCall();
             
-            if (access != null)
+            if (lambda != null)
             {
-               return access.invoke(instance, index, param);
+               return lambda.apply(instance, param);
             }
             else
             {
@@ -9538,8 +9550,7 @@
                   if (value.method != null)
                   {
                      caller.mthd = value.method;
-                     caller.access = value.access;
-                     caller.index = value.index;
+                     caller.lambda = value.lambda;
                   }
                   
                   return value.returnValue;
@@ -9552,11 +9563,10 @@
                   method = cls.getMethod(caller.methodName, signature);
                   rv = ArgValidationErrors.NO_ERROR;
                   
-                  value = putCache(key, method, rv, true);
+                  value = putCache(key, method, null, rv, true);
 
                   caller.mthd = value.method;
-                  caller.access = value.access;
-                  caller.index = value.index;
+                  caller.lambda = value.lambda;
                   
                   return rv;
                }
@@ -9571,11 +9581,10 @@
                   method = cls.getDeclaredMethod(caller.methodName, signature);
                   rv = ArgValidationErrors.NO_ERROR;
                   
-                  value = putCache(key, method, rv, true);
+                  value = putCache(key, method, null, rv, true);
                   
                   caller.mthd = value.method;
-                  caller.access = value.access;
-                  caller.index = value.index;
+                  caller.lambda = value.lambda;
 
                   return rv;
                }
@@ -9777,7 +9786,7 @@
                   extentInfo[0] = parExtent ? paramExtent : 0;
                   
                   // cache failed result as well, so we don't need to repeat validation
-                  putCache(key, null, rv, true);
+                  putCache(key, null, null, rv, true);
                   
                   return rv;
                }
@@ -10197,12 +10206,11 @@
                
                if (value == null)
                {
-                  value = putCache(key, m, rv, false);
+                  value = putCache(key, m, m.equals(caller.mthd) ? caller.lambda : null, rv, false);
                }
                
                caller.mthd = value.method;
-               caller.access = value.access;
-               caller.index = value.index;
+               caller.lambda = value.lambda;
                
                return rv;
             }
@@ -10215,7 +10223,7 @@
               : ArgValidationErrors.INCORRECT_TYPES;
          
          // cache failed result as well, so we don't need to repeat validation
-         putCache(key, null, rv, true);
+         putCache(key, null, null, rv, true);
          
          return rv;
       }
@@ -10351,6 +10359,8 @@
        * @param   method
        *          Method found matching a given name and signature, or {@code null} if no
        *          match was found.
+       * @param   lambda
+       *          When not-null, a lambda target to execute directly the call, without relying on the method. 
        * @param   returnValue
        *          Enumerated return value for {@link #valid(boolean, String, int[], Object...)}
        *          method.
@@ -10362,11 +10372,12 @@
        * @return  the created cache.
        */
       private static CacheValue putCache(CacheKey key,
-                                  Method method,
-                                  ArgValidationErrors returnValue,
-                                  boolean exactMatch)
+                                         Method method,
+                                         BiFunction<Object, Object[], Object> lambda,
+                                         ArgValidationErrors returnValue,
+                                         boolean exactMatch)
       {
-         CacheValue value = new CacheValue(method, returnValue, exactMatch);
+         CacheValue value = new CacheValue(method, lambda, returnValue, exactMatch);
          
          synchronized (cache)
          {
@@ -10496,11 +10507,11 @@
          /** Method, if any, else <code>null</code> if none was found */
          final Method method;
 
-         /** The ReflectASM access point to this method. */
-         final MethodAccess access;
-         
-         /** The method index in the MethodAccess instance. */
-         final int index;
+         /**
+          * When not-null, a lambda target to execute directly the call, without relying on the 
+          * {@link #method}. 
+          */
+         final BiFunction<Object, Object[], Object> lambda;
          
          /** 
           * Enumerated return value for {@link 
@@ -10516,6 +10527,9 @@
           * 
           * @param   method
           *          Method, if any, else <code>null</code> if none was found.
+          * @param   lambda
+          *          When not-null, a lambda target to execute directly the call, without relying on the 
+          *          {@link #method}. 
           * @param   returnValue
           *          Enumerated return value for {@link
           *          InternalEntryCaller#valid(boolean, String, int[], Object...)}.
@@ -10523,27 +10537,10 @@
           *          Flag indicating whether signature of caller's parameters exactly matches
           *          method.
           */
-         CacheValue(Method method, ArgValidationErrors returnValue, boolean exactMatch)
+         CacheValue(Method method, BiFunction<Object, Object[], Object> lambda, ArgValidationErrors returnValue, boolean exactMatch)
          {
             this.method = method;
-            if (method != null)
-            {
-               if (!Modifier.isPrivate(method.getModifiers()))
-               {
-                  this.access = MethodAccess.get(method.getDeclaringClass());
-                  this.index = access.getIndex(method.getName(), method.getParameterTypes());
-               }
-               else
-               {
-                  this.access = null;
-                  this.index = -1;
-               }
-            }
-            else
-            {
-               this.access = null;
-               this.index = -1;
-            }
+            this.lambda = lambda;
             this.returnValue = returnValue;
             this.exactMatch = exactMatch;
          }
@@ -10747,6 +10744,8 @@
        * 
        * @param    wa
        *           The context-local state in {@link WorkArea}.
+       * @param    name
+       *           The external program name.
        * @param    className
        *           The class name associated with the external program.
        * @param    persistent
@@ -10755,10 +10754,14 @@
        * @throws   Exception
        *           If the class can't be resolved. 
        */
-      public ExternalProcedureCaller(WorkArea wa, String className, boolean persistent)
+      public ExternalProcedureCaller(WorkArea wa, String name, String className, boolean persistent)
       throws Exception
       {
          super(wa);
+
+         this.ie = SourceNameMapper.getInternalEntry(name, null, false);
+         this.mthd = (ie == null ? null : ie.getMethod());
+         this.lambda = (ie == null ? null : ie.getLambda());
          
          this.methodName = "execute";
          this.className = className;
@@ -10965,7 +10968,12 @@
                wa.currentInvoke = null;
                res = new MapToNotFound(wa, mapTo);
             }
-            
+            else
+            {
+               res.mthd = ie.getMethod();
+               res.lambda = ie.getLambda();
+            }
+
             res.ie = ie;
             return res;
          }
@@ -10983,6 +10991,8 @@
             
             caller = new InternalEntryCaller(wa, hcaller, ie.jname, ieName);
             caller.ie = ie;
+            caller.mthd = ie.getMethod();
+            caller.lambda = ie.getLambda();
          }
          /*
          else if (!superCall)
@@ -11163,7 +11173,7 @@
                // name to any errors generated during instantiation (i.e. shared vars error msgs 
                // need to know the procedure name where they are defined, if an error happens)
                wa.pm.setInstantingExternalProgram(name);
-               caller = new ExternalProcedureCaller(wa, className, persistent);
+               caller = new ExternalProcedureCaller(wa, name, className, persistent);
             }
             catch (Throwable t)
             {

=== modified file 'src/com/goldencode/p2j/util/InternalEntry.java'
--- old/src/com/goldencode/p2j/util/InternalEntry.java	2022-09-09 16:35:44 +0000
+++ new/src/com/goldencode/p2j/util/InternalEntry.java	2022-12-20 11:06:40 +0000
@@ -31,6 +31,8 @@
 ** 016 ME  20200810 Added qualified name support in signature processing.
 **     CA  20210218 Fixed signature processing for qualified return type.
 **     CA  20220909 Fixed TABLE-HANDLE, DATASET, DATASET-HANDLE parameters reported by GET-SIGNATURE.
+**     CA  20221220 Use lambdas to execute all public methods in an external program.  Fixed synchronization
+**                  in getMethod() and getLambda(), when calling SourceNameMapper.buildLegacyProgram.
 */
 
 /*
@@ -90,6 +92,7 @@
 
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.function.*;
 
 import com.goldencode.p2j.util.SourceNameMapper.ExternalProgram;
 
@@ -269,19 +272,19 @@
     * Flag indicating if this is a IN SUPER internal entry (when set to <code>true</code>).
     * Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}.
     */
-   private Boolean isSuper = null;
+   private boolean isSuper = false;
    
    /** 
     * Flag indicating if this is a IN handle internal entry (when set to <code>true</code>).
     * Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}.
     */
-   private Boolean inHandle = null;
+   private boolean inHandle = false;
    
    /** 
     * Flag indicating if this is a PRIVATE internal entry (when set to <code>true</code>).
     * Gets its permanent value when internal entry is initialized by {@link SourceNameMapper}.
     */
-   private Boolean isPrivate = null;
+   private boolean isPrivate = false;
    
    /** The return type's extent. */
    private int extent = SourceNameMapper.NO_EXTENT;
@@ -294,6 +297,9 @@
     */
    private Method method = null;
    
+   /** When not-null, represents a lambda function to execute directly the target. */
+   private BiFunction<Object, Object[], Object> lambda = null;
+   
    /** The OS library name, if this is a native method. */
    private String libname = null;
    
@@ -452,19 +458,44 @@
     */
    public Method getMethod()
    {
-      if (method == null && extprog != null && extprog.ooname == null)
+      if (method == null && extprog != null && extprog.buildLegacy && extprog.ooname == null)
       {
-         SourceNameMapper.buildLegacyProgram(extprog);
-         
-         // in case the Java method is not needed for this entry (is a in-handle or in-super), avoid multiple 
-         // lookups.
-         extprog = null;
+         synchronized (extprog)
+         {
+            if (extprog.buildLegacy)
+            {
+               SourceNameMapper.buildLegacyProgram(extprog);
+               extprog.buildLegacy = false;
+            }
+         }
       }
       
       return method;
    }
    
    /**
+    * Get the lambda target to be executed, see {@link #lambda}.
+    * 
+    * @return   See above.
+    */
+   public BiFunction<Object, Object[], Object> getLambda()
+   {
+      if (method == null && extprog != null && extprog.buildLegacy && extprog.ooname == null)
+      {
+         synchronized (extprog)
+         {
+            if (extprog.buildLegacy)
+            {
+               SourceNameMapper.buildLegacyProgram(extprog);
+               extprog.buildLegacy = false;
+            }
+         }
+      }
+      
+      return lambda;
+   }
+   
+   /**
     * Get the value of the given attribute.  If the attribute is null or
     * is not set, return <code>null</code>.
     * 
@@ -581,6 +612,17 @@
    }
    
    /**
+    * Set the target lambda for this internal entry to the specified one.
+    * 
+    * @param    lambda
+    *           The target lambda.
+    */
+   void setLambda(BiFunction<Object, Object[], Object> lambda)
+   {
+      this.lambda = lambda;
+   }
+   
+   /**
     * Set the external program for this entry.
     * 
     * @param    extprog

=== modified file 'src/com/goldencode/p2j/util/SourceNameMapper.java'
--- old/src/com/goldencode/p2j/util/SourceNameMapper.java	2022-06-09 16:40:19 +0000
+++ new/src/com/goldencode/p2j/util/SourceNameMapper.java	2022-12-20 11:06:48 +0000
@@ -150,6 +150,7 @@
 **                           initialized at server startup, the 'initMappingData' calls were removed from the
 **                           APIs.
 **     CA  20220609          Fixed a regression in destructor processing.
+**     CA  20221220          Use lambdas to execute all public methods in an external program.
 */
 
 /*
@@ -223,6 +224,7 @@
 import com.goldencode.p2j.oo.lang.*;
 import com.goldencode.p2j.rest.*;
 import com.goldencode.p2j.soap.*;
+import com.goldencode.proxy.*;
 import com.goldencode.p2j.security.*;
 
 /**
@@ -2935,6 +2937,8 @@
          throw new RuntimeException(e);
       }
 
+      Map<Method, BiFunction<Object, Object[], Object>> lambdas = LambdaFactory.buildLambdas(cls);
+      
       Method[] methods = cls.getDeclaredMethods();
       for (Method mthd : methods)
       {
@@ -2960,6 +2964,12 @@
             if (ie != null)
             {
                ie.setMethod(mthd);
+               
+               if (lambdas != null)
+               {
+                  BiFunction<Object, Object[], Object> lambda = lambdas.get(mthd);
+                  ie.setLambda(lambda);
+               }
             }
          }
       }
@@ -3438,6 +3448,12 @@
       InternalEntry main = null;
       
       /**
+       * Flag indicating if {@link SourceNameMapper#buildLegacyClass(Class)} needs to be called when 
+       * {@link InternalEntry#getMethod()} or {@link InternalEntry#getLambda()} are accessed.
+       */
+      boolean buildLegacy = true;
+      
+      /**
        * Basic c'tor.
        * 
        * @param    pname

=== 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-18 12:38:06 +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,45 @@
     * 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    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, 
+                               Object instance, 
+                               Object... args) 
+   throws IllegalAccessException, 
+          IllegalArgumentException, 
+          InvocationTargetException
+    {
+      if (f != null)
+      {
+         return f.apply(instance, args);
+      }
+      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:16:53 +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:16:41 +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()));
                }
             }
             

