You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2019/05/13 13:04:43 UTC

[commons-jexl] branch master updated: JEXL-299: added argument classes to method signature in Exception reporting

This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git


The following commit(s) were added to refs/heads/master by this push:
     new fb59841  JEXL-299: added argument classes to method signature in Exception reporting
fb59841 is described below

commit fb598419c2c6b3997b60b3b1c022a7a309a26220
Author: henrib <he...@apache.org>
AuthorDate: Mon May 13 15:03:57 2019 +0200

    JEXL-299: added argument classes to method signature in Exception reporting
---
 .../org/apache/commons/jexl3/JexlException.java    | 137 +++++++++++++++++++--
 .../apache/commons/jexl3/internal/Interpreter.java |  34 +----
 .../commons/jexl3/internal/InterpreterBase.java    |  46 ++++++-
 3 files changed, 172 insertions(+), 45 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlException.java b/src/main/java/org/apache/commons/jexl3/JexlException.java
index 63ed9c6..1b23b17 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlException.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlException.java
@@ -97,7 +97,7 @@ public class JexlException extends RuntimeException {
     public JexlInfo getInfo() {
         return getInfo(mark, info);
     }
-
+    
     /**
      * Creates a string builder pre-filled with common error information (if possible).
      *
@@ -176,13 +176,12 @@ public class JexlException extends RuntimeException {
      * @return the cause
      */
     private static Throwable unwrap(Throwable xthrow) {
-        if (xthrow instanceof InvocationTargetException) {
-            return xthrow.getCause();
-        } else if (xthrow instanceof UndeclaredThrowableException) {
+        if (xthrow instanceof TryFailed
+            || xthrow instanceof InvocationTargetException
+            || xthrow instanceof UndeclaredThrowableException) {
             return xthrow.getCause();
-        } else {
-            return xthrow;
         }
+        return xthrow;
     }
 
     /**
@@ -647,36 +646,114 @@ public class JexlException extends RuntimeException {
          *
          * @param node  the offending ASTnode
          * @param name  the method name
+         * @deprecated as of 3.2, use call with method arguments
          */
+        @Deprecated
         public Method(JexlNode node, String name) {
-            super(node, name);
+            this(node, name, null);
         }
-
+         
         /**
          * Creates a new Method exception instance.
          *
          * @param info  the location information
          * @param name  the unknown method
          * @param cause the exception causing the error
+         * @deprecated as of 3.2, use call with method arguments
          */
+        @Deprecated
         public Method(JexlInfo info, String name, Throwable cause) {
-            super(info, name, cause);
+            this(info, name, null, cause);
+        }
+        
+        /**
+         * Creates a new Method exception instance.
+         *
+         * @param node  the offending ASTnode
+         * @param name  the method name
+         * @param args  the method arguments
+         * @since 3.2
+         */
+        public Method(JexlNode node, String name, Object[] args) {
+            super(node, methodSignature(name, args));
+        }
+        
+        /**
+         * Creates a new Method exception instance.
+         *
+         * @param info  the location information
+         * @param name  the method name
+         * @param args  the method arguments
+         * @since 3.2
+         */
+        public Method(JexlInfo info, String name, Object[] args) {
+            this(info, name, args, null);
         }
 
+                
+        /**
+         * Creates a new Method exception instance.
+         *
+         * @param info  the location information
+         * @param name  the method name
+         * @param cause the exception causing the error
+         * @param args  the method arguments
+         * @since 3.2
+         */
+        public Method(JexlInfo info, String name, Object[] args, Throwable cause) {
+            super(info, methodSignature(name, args), cause);
+        }
+        
         /**
          * @return the method name
          */
         public String getMethod() {
+            String signature = getMethodSignature();
+            int lparen = signature.indexOf('(');
+            return lparen > 0? signature.substring(0, lparen) : signature;
+        }  
+        
+        /**
+         * @return the method signature
+         * @since 3.2
+         */
+        public String getMethodSignature() {
             return super.detailedMessage();
         }
 
         @Override
         protected String detailedMessage() {
-            return "unsolvable function/method '" + getMethod() + "'";
+            return "unsolvable function/method '" + getMethodSignature() + "'";
         }
     }
 
     /**
+     * Creates a signed-name for a given method name and arguments.
+     * @param name the method name
+     * @param args the method arguments
+     * @return a suitable signed name
+     */
+    private static String methodSignature(String name, Object[] args) {
+        if (args != null && args.length > 0) {
+            StringBuilder strb = new StringBuilder(name);
+            strb.append('(');
+            for (int a = 0; a < args.length; ++a) {
+                if (a > 0) {
+                    strb.append(", ");
+                }
+                Class<?> clazz = args[a] == null ? Object.class : args[a].getClass();
+                strb.append(clazz.getSimpleName());
+                if (clazz.isArray()) {
+                    strb.append("[]");
+                }
+            }
+            strb.append(')');
+            return strb.toString();
+        }
+        return name;
+    }
+
+    /**
      * Generates a message for a unsolvable method error.
      *
      * @param node the node where the error occurred
@@ -684,9 +761,21 @@ public class JexlException extends RuntimeException {
      * @return the error message
      */
     public static String methodError(JexlNode node, String method) {
+        return methodError(node, method, null);
+    }
+    
+    /**
+     * Generates a message for a unsolvable method error.
+     *
+     * @param node the node where the error occurred
+     * @param method the method name
+     * @param args the method arguments
+     * @return the error message
+     */
+    public static String methodError(JexlNode node, String method, Object[] args) {
         StringBuilder msg = errorAt(node);
         msg.append("unsolvable function/method '");
-        msg.append(method);
+        msg.append(methodSignature(method, args));
         msg.append('\'');
         return msg.toString();
     }
@@ -861,6 +950,32 @@ public class JexlException extends RuntimeException {
     }
 
     /**
+     * Thrown when method/ctor invocation fails.
+     * <p>These wrap InvocationTargetException as runtime exception
+     * allowing to go through without signature modifications.
+     * @since 3.2
+     */
+    public static class TryFailed extends JexlException {
+        /**
+         * Creates a new instance.
+         * @param xany the original invocation target exception
+         */
+        private TryFailed(InvocationTargetException xany) {
+            super((JexlInfo) null, "tryFailed", xany.getCause());
+        }
+    }
+    
+    /**
+     * Wrap an invocation exception.
+     * <p>Return the cause if it is already a JexlException.
+     * @param xinvoke the invocation exception
+     * @return a JexlException
+     */
+    public static RuntimeException tryFailed(InvocationTargetException xinvoke) {
+        return new JexlException.TryFailed(xinvoke); // fail
+    }
+    
+    /**
      * Detailed info message about this error.
      * Format is "debug![begin,end]: string \n msg" where:
      *
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 7844369..82ff17d 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -123,8 +123,6 @@ import java.util.concurrent.Callable;
 public class Interpreter extends InterpreterBase {
     /** The operators evaluation delegate. */
     protected final Operators operators;
-    /** Cache executors. */
-    protected final boolean cache;
     /** Frame height. */
     protected int fp = 0;
     /** Symbol values. */
@@ -151,7 +149,6 @@ public class Interpreter extends InterpreterBase {
     protected Interpreter(Engine engine, JexlContext aContext, Scope.Frame eFrame) {
         super(engine, aContext);
         this.operators = new Operators(this);
-        this.cache = jexl.cache != null;
         this.frame = eFrame;
         if (this.context instanceof JexlContext.NamespaceResolver) {
             ns = ((JexlContext.NamespaceResolver) context);
@@ -170,7 +167,6 @@ public class Interpreter extends InterpreterBase {
     protected Interpreter(Interpreter ii, JexlArithmetic jexla) {
         super(ii, jexla);
         operators = ii.operators;
-        cache = ii.cache;
         frame = ii.frame;
         ns = ii.ns;
         functions = ii.functions;
@@ -1078,28 +1074,6 @@ public class Interpreter extends InterpreterBase {
     }
 
     /**
-     * Checks whether a reference child node holds a local variable reference.
-     * @param node  the reference node
-     * @param which the child we are checking
-     * @return true if child is local variable, false otherwise
-     */
-    protected boolean isLocalVariable(ASTReference node, int which) {
-        return (node.jjtGetNumChildren() > which
-                && node.jjtGetChild(which) instanceof ASTIdentifier
-                && ((ASTIdentifier) node.jjtGetChild(which)).getSymbol() >= 0);
-    }
-    
-    /**
-     * Checks whether a reference child node holds a function call.
-     * @param node  the reference node
-     * @return true if child is function call, false otherwise
-     */
-    protected boolean isFunctionCall(ASTReference node) {
-        return (node.jjtGetNumChildren() > 0
-                && node.jjtGetChild(0) instanceof ASTFunctionNode);
-    }
-    
-    /**
      * Evaluates an access identifier based on the 2 main implementations;
      * static (name or numbered identifier) or dynamic (jxlt).
      * @param node the identifier access node
@@ -1679,7 +1653,7 @@ public class Interpreter extends InterpreterBase {
                 // attempt to narrow the parameters and if this succeeds, try again in next loop
                 if (!narrow && arithmetic.narrowArguments(argv)) {
                     narrow = true;
-                    continue;
+                    // continue;
                 } else {
                     break;
                 }
@@ -1687,7 +1661,9 @@ public class Interpreter extends InterpreterBase {
             // we have either evaluated and returned or no method was found
             return node.isSafeLhs(jexl.safe)
                     ? null
-                    : unsolvableMethod(node, methodName);
+                    : unsolvableMethod(node, methodName, argv);
+        } catch (JexlException.TryFailed xany) {
+            throw invocationException(node, methodName, xany.getCause());
         } catch (JexlException xthru) {
             throw xthru;
         } catch (Exception xany) {
@@ -1762,7 +1738,7 @@ public class Interpreter extends InterpreterBase {
                 return eval;
             }
             String tstr = target != null ? target.toString() : "?";
-            return unsolvableMethod(node, tstr);
+            return unsolvableMethod(node, tstr, argv);
         } catch (JexlException.Method xmethod) {
             throw xmethod;
         } catch (Exception xany) {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
index 7c2f615..3c473c2 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -50,6 +50,8 @@ public abstract class InterpreterBase extends ParserVisitor {
     protected final JexlArithmetic arithmetic;
     /** The context to store/retrieve variables. */
     protected final JexlContext context;
+    /** Cache executors. */
+    protected final boolean cache;
     /** Cancellation support. */
     protected volatile boolean cancelled = false;
     /** Empty parameters for method matching. */
@@ -65,6 +67,7 @@ public abstract class InterpreterBase extends ParserVisitor {
         this.logger = jexl.logger;
         this.uberspect = jexl.uberspect;
         this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT;
+        this.cache = engine.cache != null;
         JexlArithmetic jexla = jexl.arithmetic;
         this.arithmetic = jexla.options(context);
         if (arithmetic != jexla && !arithmetic.getClass().equals(jexla.getClass())) {
@@ -85,6 +88,7 @@ public abstract class InterpreterBase extends ParserVisitor {
         uberspect = ii.uberspect;
         context = ii.context;
         arithmetic = ii.arithmetic;
+        cache = ii.cache;
     }
 
 
@@ -219,10 +223,21 @@ public abstract class InterpreterBase extends ParserVisitor {
      * @return throws JexlException if strict and not silent, null otherwise
      */
     protected Object unsolvableMethod(JexlNode node, String method) {
+        return unsolvableMethod(node, method, null);
+    }
+   
+    /**
+     * Triggered when a method can not be resolved.
+     * @param node   the node where the error originated from
+     * @param method the method name
+     * @param args the method arguments
+     * @return throws JexlException if strict and not silent, null otherwise
+     */
+    protected Object unsolvableMethod(JexlNode node, String method, Object[] args) {
         if (isStrictEngine()) {
-            throw new JexlException.Method(node, method);
+            throw new JexlException.Method(node, method, args);
         } else if (logger.isDebugEnabled()) {
-            logger.debug(JexlException.methodError(node, method));
+            logger.debug(JexlException.methodError(node, method, args));
         }
         return null;
     }
@@ -245,6 +260,28 @@ public abstract class InterpreterBase extends ParserVisitor {
     }
     
     /**
+     * Checks whether a reference child node holds a local variable reference.
+     * @param node  the reference node
+     * @param which the child we are checking
+     * @return true if child is local variable, false otherwise
+     */
+    protected boolean isLocalVariable(ASTReference node, int which) {
+        return (node.jjtGetNumChildren() > which
+                && node.jjtGetChild(which) instanceof ASTIdentifier
+                && ((ASTIdentifier) node.jjtGetChild(which)).getSymbol() >= 0);
+    }
+    
+    /**
+     * Checks whether a reference child node holds a function call.
+     * @param node  the reference node
+     * @return true if child is function call, false otherwise
+     */
+    protected boolean isFunctionCall(ASTReference node) {
+        return (node.jjtGetNumChildren() > 0
+                && node.jjtGetChild(0) instanceof ASTFunctionNode);
+    }
+    
+    /**
      * Pretty-prints a failing property (de)reference.
      * <p>Used by calls to unsolvableProperty(...).</p>
      * @param node the property node
@@ -320,7 +357,7 @@ public abstract class InterpreterBase extends ParserVisitor {
      * @param xany       the cause
      * @return a JexlException that will be thrown
      */
-    protected JexlException invocationException(JexlNode node, String methodName, Exception xany) {
+    protected JexlException invocationException(JexlNode node, String methodName, Throwable xany) {
         Throwable cause = xany.getCause();
         if (cause instanceof JexlException) {
             return (JexlException) cause;
@@ -653,8 +690,7 @@ public abstract class InterpreterBase extends ParserVisitor {
                 }
                 return eval;
             }
-            return unsolvableMethod(node, mname);
+            return unsolvableMethod(node, mname, argv);
         }
     }
-
 }