Author: henrib Date: Wed Oct 1 08:57:12 2014 New Revision: 1628650 URL: http://svn.apache.org/r1628650 Log: Fixing JEXL-146; Better error reporting and improved arithmetic overloading; Clean up javadoc/checkstyle;
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlContext.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java commons/proper/jexl/trunk/src/site/xdoc/changes.xml commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreator.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/scripting/JexlScriptEngineTest.java Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java Wed Oct 1 08:57:12 2014 @@ -44,28 +44,47 @@ import java.math.MathContext; */ public class JexlArithmetic { /** - * The overridable operators. + * The overload-able operators. + * Note that logical and (ie &&) and logical or (ie ||) are not in this list to avoid breaking + * their shortcut semantics. * @since 3.0 */ public enum Operator { + /** add(x, y). */ ADD("+", "add", 2), + /** subtract(x, y). */ SUBTRACT("-", "subtract", 2), + /** multiply(x, y). */ MULTIPLY("*", "multiply", 2), + /** divide(x, y). */ DIVIDE("/", "divide", 2), + /** mod(x, y). */ MOD("%", "mod", 2), + /** bitwiseAnd(x, y). */ AND("&", "bitwiseAnd", 2), + /** bitwiseOr(x, y). */ OR("|", "bitwiseOr", 2), + /** bitwiseXor(x, y). */ XOR("^", "bitwiseXor", 2), + /** logicalNot(x). */ NOT("!", "logicalNot", 1), + /** bitiwiseComplement(x). */ COMPLEMENT("-", "bitwiseComplement", 1), + /** equals(x, y). */ EQ("==", "equals", 2), + /** lessThan(x, y). */ LT("<", "lessThan", 2), + /** lessThanOrEqual(x, y). */ LTE("<=", "lessThanOrEqual", 2), + /** greaterThan(x, y). */ GT(">", "greaterThan", 2), + /** greaterThanOrEqual(x, y). */ GTE(">=", "greaterThanOrEqual", 2), - ABS("+", "abs", 1), + /** negate(x). */ NEGATE("-", "negate", 1), + /** size(x). */ SIZE("size", "size", 1), + /** empty(x). */ EMPTY("empty", "empty", 1); /** @@ -85,12 +104,12 @@ public class JexlArithmetic { * Creates an operator. * @param o the operator name * @param m the method name associated to this operator in a JexlArithmetic - * @param arity the number of parameters for the method + * @param argc the number of parameters for the method */ - Operator(String o, String m, int arity) { + Operator(String o, String m, int argc) { this.operator = o; this.methodName = m; - this.arity = arity; + this.arity = argc; } /** @@ -124,13 +143,19 @@ public class JexlArithmetic { */ public interface Uberspect { /** + * Checks whether this uberspect has overloads for a given operator. + * @param operator the operator to check + * @return true if an overload exists, false otherwise + */ + boolean overloads(JexlArithmetic.Operator operator); + + /** * Gets the most specific method for a monadic operator. * @param operator the operator * @param arg the argument * @return the most specific method or null if no specific override could be found */ JexlMethod getOperator(JexlArithmetic.Operator operator, Object arg); - Object tryInvokeOperator(JexlArithmetic.Operator operator, Object arg); /** * Gets the most specific method for a diadic operator. @@ -140,10 +165,9 @@ public class JexlArithmetic { * @return the most specific method or null if no specific override could be found */ JexlMethod getOperator(JexlArithmetic.Operator operator, Object lhs, Object rhs); - Object tryInvokeOperator(JexlArithmetic.Operator operator, Object lhs, Object rhs); } - /** Maker class for null operand exceptions. */ + /** Marker class for null operand exceptions. */ public static class NullOperand extends ArithmeticException {} /** Double.MAX_VALUE as BigDecimal. */ protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); @@ -190,7 +214,7 @@ public class JexlArithmetic { public JexlArithmetic options(JexlEngine.Options options) { boolean ostrict = options.isStrictArithmetic() == null ? this.strict - : options.isStrictArithmetic().booleanValue(); + : options.isStrictArithmetic(); MathContext bigdContext = options.getArithmeticMathContext(); if (bigdContext == null) { bigdContext = mathContext; Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlContext.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlContext.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlContext.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlContext.java Wed Oct 1 08:57:12 2014 @@ -100,10 +100,10 @@ public interface JexlContext { * A marker interface that indicates the interpreter to put this context in the JexlEngine thread local context * instance during evaluation. * This allows user functions or methods to access the context during a call. - * Note that the usual caveats wrt using thread local apply (caching/leaking references, etc.); in particular, keeping - * a reference to such a context is to be considered with great care and caution. - * It should also be noted that sharing such a context between threads should implicate synchronizing variable access - * in the implementation class. + * Note that the usual caveats wrt using thread local apply (caching/leaking references, etc.); in particular, + * keeping a reference to such a context is to be considered with great care and caution. + * It should also be noted that sharing such a context between threads should implicate synchronizing variable + * accessing the implementation class. * @see JexlEngine#setThreadContext() * @see JexlEngine#getThreadContext() */ Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java Wed Oct 1 08:57:12 2014 @@ -83,9 +83,36 @@ public class JexlException extends Runti * @return the information */ public JexlInfo getInfo() { - if (info != null && mark != null) { + return getInfo(mark, info); + } + + /** + * Creates a string builder pre-filled with common error information (if possible). + * @param node the node + * @return a string builder + */ + private static StringBuilder errorAt(JexlNode node) { + JexlInfo info = node != null? getInfo(node, node.jexlInfo()) : null; + StringBuilder msg = new StringBuilder(); + if (info != null) { + msg.append(info.toString()); + } else { + msg.append("?:"); + } + msg.append(' '); + return msg; + } + + /** + * Gets the most specific information attached to a node. + * @param node the node + * @param info the information + * @return the information or null + */ + public static JexlInfo getInfo(JexlNode node, JexlInfo info) { + if (info != null && node != null) { final Debugger dbg = new Debugger(); - if (dbg.debug(mark)) { + if (dbg.debug(node)) { return new JexlInfo(info) { @Override public JexlInfo.Detail getDetail() { @@ -300,12 +327,26 @@ public class JexlException extends Runti */ public static class Variable extends JexlException { /** + * Undefined variable flag. + */ + private final boolean undefined; + /** * Creates a new Variable exception instance. * @param node the offending ASTnode * @param var the unknown variable + * @param undef whether the variable is undefined or evaluated as null */ - public Variable(JexlNode node, String var) { + public Variable(JexlNode node, String var, boolean undef) { super(node, var, null); + undefined = undef; + } + + /** + * Whether the variable causing an error is undefined or evaluated as null. + * @return true if undefined, false otherwise + */ + public boolean isUndefined() { + return undefined; } /** @@ -317,8 +358,27 @@ public class JexlException extends Runti @Override protected String detailedMessage() { - return "undefined variable " + getVariable(); + return (undefined? "undefined" : "null value") + " variable " + getVariable(); + } + } + + /** + * Generates a message for a variable error. + * @param node the node where the error occurred + * @param variable the variable + * @param undef whether the variable is null or undefined + * @return the error message + */ + public static String variableError(JexlNode node, String variable, boolean undef) { + StringBuilder msg = errorAt(node); + if (undef) { + msg.append("undefined"); + } else { + msg.append("null value"); } + msg.append(" variable "); + msg.append(variable); + return msg.toString(); } /** @@ -359,6 +419,20 @@ public class JexlException extends Runti } /** + * Generates a message for an unsolvable property error. + * @param node the node where the error occurred + * @param var the variable + * @return the error message + */ + public static String propertyError(JexlNode node, String var) { + StringBuilder msg = errorAt(node); + msg.append("unsolvable property '"); + msg.append(var); + msg.append('\''); + return msg.toString(); + } + + /** * Thrown when a method or ctor is unknown, ambiguous or inaccessible. * @since 3.0 */ @@ -366,11 +440,10 @@ public class JexlException extends Runti /** * Creates a new Method exception instance. * @param node the offending ASTnode - * @param name the unknown method - * @param cause the exception causing the error + * @param name the method name */ - public Method(JexlNode node, String name, Throwable cause) { - super(node, name, cause); + public Method(JexlNode node, String name) { + super(node, name); } /** @@ -397,6 +470,20 @@ public class JexlException extends Runti } /** + * Generates a message for a unsolvable method error. + * @param node the node where the error occurred + * @param method the method name + * @return the error message + */ + public static String methodError(JexlNode node, String method) { + StringBuilder msg = errorAt(node); + msg.append("unsolvable function/method '"); + msg.append(method); + msg.append('\''); + return msg.toString(); + } + + /** * Thrown to return a value. * @since 3.0 */ Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlInfo.java Wed Oct 1 08:57:12 2014 @@ -37,14 +37,20 @@ public class JexlInfo { } /** - * Describes errors more precicely. + * Describes errors more precisely. */ - public static interface Detail { - /** The start column on the line that triggered the error. */ + public interface Detail { + /** + * @return he start column on the line that triggered the error + */ int start(); - /** The end column on the line that triggered the error. */ + /** + * @return the end column on the line that triggered the error + */ int end(); - /** The actual part of code that triggered the error. */ + /** + * @return the actual part of code that triggered the error + */ @Override String toString(); } @@ -61,10 +67,20 @@ public class JexlInfo { column = c; } + /** + * Creates info reusing the name. + * @param l the line + * @param c the column + * @return a new info instance + */ public JexlInfo at(int l, int c) { return new JexlInfo(name, l, c); } + /** + * The copy constructor. + * @param copy the instance to copy + */ protected JexlInfo(JexlInfo copy) { name = copy.getName(); line = copy.getLine(); Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JxltEngine.java Wed Oct 1 08:57:12 2014 @@ -16,7 +16,6 @@ */ package org.apache.commons.jexl3; -import org.apache.commons.jexl3.internal.TemplateEngine; import java.io.Reader; import java.io.StringReader; import java.io.Writer; @@ -49,6 +48,7 @@ public abstract class JxltEngine { /** * Creates an Exception. + * @param info the contextual information * @param msg the exception message * @param cause the exception cause */ Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java Wed Oct 1 08:57:12 2014 @@ -26,8 +26,9 @@ import java.util.Map; * Helper class to create typed arrays. */ public class ArrayBuilder implements JexlArithmetic.ArrayBuilder { - /** The boxing types to primitive conversion map. */ + /** The number of primitive types. */ private static final int PRIMITIVE_SIZE = 8; + /** The boxing types to primitive conversion map. */ private static final Map<Class<?>, Class<?>> BOXING_CLASSES; static { BOXING_CLASSES = new IdentityHashMap<Class<?>, Class<?>>(PRIMITIVE_SIZE); @@ -41,6 +42,11 @@ public class ArrayBuilder implements Jex BOXING_CLASSES.put(Short.class, Short.TYPE); } + /** + * Gets the primitive type of a given class (when it exists). + * @param parm a class + * @return the primitive type or null it the argument is not unboxable + */ private static Class<?> unboxingClass(Class<?> parm) { Class<?> prim = BOXING_CLASSES.get(parm); return prim == null ? parm : prim; Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java Wed Oct 1 08:57:12 2014 @@ -44,12 +44,38 @@ public class Closure extends Script { } @Override + public String toString() { + return getParsedText(); + } + + @Override public String getParsedText() { Debugger debug = new Debugger(); - debug.debug(script); + debug.debug(script, false); return debug.toString(); } + /** + * Sets the hoisted index of a given symbol, ie the target index of a parent hoisted symbol in this closure's frame. + * <p>This is meant to allow a locally defined function to "see" and call itself as a local (hoisted) variable; + * in other words, this allows recursive call of a function. + * @param symbol the symbol index (in the caller of this closure) + * @param value the value to set in the local frame + */ + public void setHoisted(int symbol, Object value) { + if (script instanceof ASTJexlLambda) { + ASTJexlLambda lambda = (ASTJexlLambda) script; + Scope scope = lambda.getScope(); + if (scope != null) { + Integer reg = scope.getHoisted(symbol); + if (reg != null) { + frame.set(reg, value); + return; + } + } + } + } + @Override public Object evaluate(JexlContext context) { return execute(context, (Object[])null); @@ -74,10 +100,11 @@ public class Closure extends Script { @Override public Callable<Object> callable(JexlContext context, Object... args) { + Scope.Frame local = null; if (frame != null) { - frame.assign(args); + local = frame.assign(args); } - final Interpreter interpreter = jexl.createInterpreter(context, frame); + final Interpreter interpreter = jexl.createInterpreter(context, local); interpreter.functors = functors; return new Callable<Object>() { /** Use interpreter as marker for not having run. */ @@ -87,7 +114,7 @@ public class Closure extends Script { public Object call() throws Exception { if (result == interpreter) { JexlNode block = script.jjtGetChild(script.jjtGetNumChildren() - 1); - return interpreter.interpret(block); + result = interpreter.interpret(block); } return result; } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java Wed Oct 1 08:57:12 2014 @@ -144,6 +144,9 @@ public final class Debugger extends Pars * @return true if the cause was located, false otherwise */ public boolean debug(JexlNode node) { + return debug(node, true); + } + public boolean debug(JexlNode node, boolean r) { start = 0; end = 0; indentLevel = 0; @@ -153,9 +156,11 @@ public final class Debugger extends Pars cause = node; // make arg cause become the root cause JexlNode root = node; + if (r) { while (root.jjtGetParent() != null) { root = root.jjtGetParent(); } + } root.jjtAccept(this, null); } return end > 0; Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java Wed Oct 1 08:57:12 2014 @@ -123,8 +123,8 @@ public class Interpreter extends ParserV protected final JexlContext context; /** The context to store/retrieve variables. */ protected final JexlContext.NamespaceResolver ns; - /** Strict interpreter flag. */ - protected final boolean strictEngine; + /** Strict interpreter flag (may temporarily change during when calling size & empty as functions). */ + protected boolean strictEngine; /** Strict interpreter flag. */ protected final boolean strictArithmetic; /** Silent intepreter flag. */ @@ -153,8 +153,8 @@ public class Interpreter extends ParserV JexlEngine.Options opts = (JexlEngine.Options) context; Boolean ostrict = opts.isStrict(); Boolean osilent = opts.isSilent(); - this.strictEngine = ostrict == null ? jexl.isStrict() : ostrict.booleanValue(); - this.silent = osilent == null ? jexl.isSilent() : osilent.booleanValue(); + this.strictEngine = ostrict == null ? jexl.isStrict() : ostrict; + this.silent = osilent == null ? jexl.isSilent() : osilent; this.arithmetic = jexl.arithmetic.options(opts); } else { this.strictEngine = jexl.isStrict(); @@ -199,7 +199,7 @@ public class Interpreter extends ParserV } throw xjexl.clean(); } finally { - if (functors != null && AUTOCLOSEABLE != null ) { + if (functors != null && AUTOCLOSEABLE != null) { for(Object functor : functors.values()) { if (functor != null && AUTOCLOSEABLE.isAssignableFrom(functor.getClass())) { try { @@ -250,20 +250,56 @@ public class Interpreter extends ParserV } /** - * Triggered when variable can not be resolved. - * @param xjexl the JexlException ("undefined variable " + variable) + * Triggered when a variable can not be resolved. + * @param node the node where the error originated from + * @param var the variable name + * @param undef whether the variable is undefined or null * @return throws JexlException if isStrict, null otherwise */ - protected Object unknownVariable(JexlException xjexl) { + protected Object unsolvableVariable(JexlNode node, String var, boolean undef) { + if (strictEngine && (undef || arithmetic.isStrict())) { + throw new JexlException.Variable(node, var, undef); + } + if (!silent) { + logger.warn(JexlException.variableError(node, var, undef)); + } + return null; + } + + /** + * Triggered when a method can not be resolved. + * @param node the node where the error originated from + * @param method the method name + * @return throws JexlException if isStrict, null otherwise + */ + protected Object unsolvableMethod(JexlNode node, String method) { if (strictEngine) { - throw xjexl; + throw new JexlException.Method(node, method); } if (!silent) { - logger.warn(xjexl.getMessage()); + logger.warn(JexlException.methodError(node, method)); + } + return null; + } + + /** + * Triggered when a property can not be resolved. + * @param node the node where the error originated from + * @param var the property name + * @param cause the cause if any + * @return throws JexlException if isStrict, null otherwise + */ + protected Object unsolvableProperty(JexlNode node, String var, Throwable cause) { + if (strictEngine) { + throw new JexlException.Property(node, var, cause); + } + if (!silent) { + logger.warn(JexlException.propertyError(node, var)); } return null; } + /** * Triggered when method, function or constructor invocation fails. * @param xjexl the JexlException wrapping the original error @@ -341,6 +377,79 @@ public class Interpreter extends ParserV } } + /** + * Attempts to call a monadic operator. + * <p>This takes care of finding and caching the operator method when appropriate + * @param node the syntactic node + * @param operator the operator + * @param arg the argument + * @return the result of the operator evaluation or TRY_FAILED + */ + protected Object callOperator(JexlNode node, Operator operator, Object arg) { + if (operators != null && operators.overloads(operator)) { + if (cache) { + Object cached = node.jjtGetValue(); + if (cached instanceof JexlMethod) { + JexlMethod me = (JexlMethod) cached; + Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, arg); + if (!me.tryFailed(eval)) { + return eval; + } + } + } + try { + JexlMethod emptym = operators.getOperator(operator, arg); + if (emptym != null) { + Object result = emptym.invoke(arithmetic, arg); + if (cache) { + node.jjtSetValue(emptym); + } + return result; + } + } catch (Exception xany) { + return invocationFailed(new JexlException(node, operator.getMethodName(), xany)); + } + } + return JexlEngine.TRY_FAILED; + } + + /** + * Attempts to call a diadic operator. + * <p>This takes care of finding and caching the operator method when appropriate + * @param node the syntactic node + * @param operator the operator + * @param lhs the left hand side argument + * @param rhs the right hand side argument + * @return the result of the operator evaluation or TRY_FAILED + */ + protected Object callOperator(JexlNode node, Operator operator, Object lhs, Object rhs) { + if (operators != null && operators.overloads(operator)) { + if (cache) { + Object cached = node.jjtGetValue(); + if (cached instanceof JexlMethod) { + JexlMethod me = (JexlMethod) cached; + Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, lhs, rhs); + if (!me.tryFailed(eval)) { + return eval; + } + } + } + try { + JexlMethod emptym = operators.getOperator(operator, lhs, rhs); + if (emptym != null) { + Object result = emptym.invoke(arithmetic, lhs, rhs); + if (cache) { + node.jjtSetValue(emptym); + } + return result; + } + } catch (Exception xany) { + return invocationFailed(new JexlException(node, operator.getMethodName(), xany)); + } + } + return JexlEngine.TRY_FAILED; + } + @Override protected Object visit(ASTAndNode node, Object data) { /** @@ -389,13 +498,8 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.ADD, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.add(left, right); + Object result = callOperator(node, Operator.ADD, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.add(left, right); } catch (ArithmeticException xrt) { throw new JexlException(node, "+ error", xrt); } @@ -406,13 +510,8 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.SUBTRACT, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.subtract(left, right); + Object result = callOperator(node, Operator.SUBTRACT, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.subtract(left, right); } catch (ArithmeticException xrt) { throw new JexlException(node, "- error", xrt); } @@ -423,13 +522,8 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.AND, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.bitwiseAnd(left, right); + Object result = callOperator(node, Operator.AND, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.bitwiseAnd(left, right); } catch (ArithmeticException xrt) { throw new JexlException(node, "& error", xrt); } @@ -437,15 +531,10 @@ public class Interpreter extends ParserV @Override protected Object visit(ASTBitwiseComplNode node, Object data) { - Object left = node.jjtGetChild(0).jjtAccept(this, data); + Object arg = node.jjtGetChild(0).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.COMPLEMENT, left); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.bitwiseComplement(left); + Object result = callOperator(node, Operator.COMPLEMENT, arg); + return result != JexlEngine.TRY_FAILED? result : arithmetic.bitwiseComplement(arg); } catch (ArithmeticException xrt) { throw new JexlException(node, "~ error", xrt); } @@ -456,13 +545,8 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.OR, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.bitwiseOr(left, right); + Object result = callOperator(node, Operator.OR, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.bitwiseOr(left, right); } catch (ArithmeticException xrt) { throw new JexlException(node, "| error", xrt); } @@ -473,13 +557,8 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.XOR, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.bitwiseXor(left, right); + Object result = callOperator(node, Operator.XOR, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.bitwiseXor(left, right); } catch (ArithmeticException xrt) { throw new JexlException(node, "^ error", xrt); } @@ -500,16 +579,11 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.DIVIDE, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.divide(left, right); + Object result = callOperator(node, Operator.DIVIDE, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.divide(left, right); } catch (ArithmeticException xrt) { if (!strictArithmetic) { - return new Double(0.0); + return 0.0d; } JexlNode xnode = findNullOperand(xrt, node, left, right); throw new JexlException(xnode, "divide error", xrt); @@ -521,13 +595,10 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.EQ, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE; + Object result = callOperator(node, Operator.EQ, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE; } catch (ArithmeticException xrt) { throw new JexlException(node, "== error", xrt); } @@ -595,13 +666,10 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.GTE, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.greaterThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; + Object result = callOperator(node, Operator.GTE, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.greaterThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; } catch (ArithmeticException xrt) { throw new JexlException(node, ">= error", xrt); } @@ -612,13 +680,10 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.GT, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.greaterThan(left, right) ? Boolean.TRUE : Boolean.FALSE; + Object result = callOperator(node, Operator.GT, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.greaterThan(left, right) ? Boolean.TRUE : Boolean.FALSE; } catch (ArithmeticException xrt) { throw new JexlException(node, "> error", xrt); } @@ -844,13 +909,10 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.LTE, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.lessThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; + Object result = callOperator(node, Operator.LTE, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.lessThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; } catch (ArithmeticException xrt) { throw new JexlException(node, "<= error", xrt); } @@ -861,13 +923,10 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.LT, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.lessThan(left, right) ? Boolean.TRUE : Boolean.FALSE; + Object result = callOperator(node, Operator.LT, left, right); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.lessThan(left, right) ? Boolean.TRUE : Boolean.FALSE; } catch (ArithmeticException xrt) { throw new JexlException(node, "< error", xrt); } @@ -912,16 +971,11 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.MOD, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.mod(left, right); + Object result = callOperator(node, Operator.MOD, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.mod(left, right); } catch (ArithmeticException xrt) { if (!strictArithmetic) { - return new Double(0.0); + return 0.0d; } JexlNode xnode = findNullOperand(xrt, node, left, right); throw new JexlException(xnode, "% error", xrt); @@ -933,13 +987,8 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.MULTIPLY, left, right); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.multiply(left, right); + Object result = callOperator(node, Operator.MULTIPLY, left, right); + return result != JexlEngine.TRY_FAILED? result : arithmetic.multiply(left, right); } catch (ArithmeticException xrt) { JexlNode xnode = findNullOperand(xrt, node, left, right); throw new JexlException(xnode, "* error", xrt); @@ -951,13 +1000,10 @@ public class Interpreter extends ParserV Object left = node.jjtGetChild(0).jjtAccept(this, data); Object right = node.jjtGetChild(1).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.EQ, left, right); - if (result != JexlEngine.TRY_FAILED) { - return arithmetic.toBoolean(result)? Boolean.FALSE : Boolean.TRUE; - } - } - return arithmetic.equals(left, right) ? Boolean.FALSE : Boolean.TRUE; + Object result = callOperator(node, Operator.EQ, left, right); + return result != JexlEngine.TRY_FAILED + ? arithmetic.toBoolean(result) ? Boolean.FALSE : Boolean.TRUE + : arithmetic.equals(left, right) ? Boolean.FALSE : Boolean.TRUE; } catch (ArithmeticException xrt) { JexlNode xnode = findNullOperand(xrt, node, left, right); throw new JexlException(xnode, "!= error", xrt); @@ -968,13 +1014,10 @@ public class Interpreter extends ParserV protected Object visit(ASTNotNode node, Object data) { Object val = node.jjtGetChild(0).jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.NOT, val); - if (result != JexlEngine.TRY_FAILED) { - return result; - } - } - return arithmetic.toBoolean(val) ? Boolean.FALSE : Boolean.TRUE; + Object result = callOperator(node, Operator.NOT, val); + return result != JexlEngine.TRY_FAILED + ? result + : arithmetic.toBoolean(val) ? Boolean.FALSE : Boolean.TRUE; } catch (ArithmeticException xrt) { throw new JexlException(node, "arithmetic error", xrt); } @@ -1054,13 +1097,11 @@ public class Interpreter extends ParserV JexlNode valNode = node.jjtGetChild(0); Object val = valNode.jjtAccept(this, data); try { - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.NEGATE, val); - if (result != JexlEngine.TRY_FAILED) { - return result; - } + Object result = callOperator(node, Operator.NEGATE, val); + if (result != JexlEngine.TRY_FAILED) { + return result; } - Object number = arithmetic.negate(val); + Object number = result != JexlEngine.TRY_FAILED? result : arithmetic.negate(val); // attempt to recoerce to literal class if (valNode instanceof ASTNumberLiteral && number instanceof Number) { number = arithmetic.narrowNumber((Number) number, ((ASTNumberLiteral) valNode).getLiteralClass()); @@ -1096,11 +1137,14 @@ public class Interpreter extends ParserV @Override protected Object visit(ASTSizeFunction node, Object data) { - Object val = node.jjtGetChild(0).jjtAccept(this, data); - if (val == null) { - throw new JexlException(node, "size() : argument is null", null); + boolean isStrict = this.strictEngine; + try { + strictEngine = false; + Object val = node.jjtGetChild(0).jjtAccept(this, data); + return sizeOf(node, val); + } finally { + strictEngine = isStrict; } - return sizeOf(node, val); } @Override @@ -1111,13 +1155,20 @@ public class Interpreter extends ParserV @Override protected Object visit(ASTEmptyFunction node, Object data) { - return isEmpty(node, node.jjtGetChild(0).jjtAccept(this, data)); + boolean isStrict = this.strictEngine; + try { + strictEngine = false; + Object value = node.jjtGetChild(0).jjtAccept(this, data); + return callEmpty(node, value); + } finally { + strictEngine = isStrict; + } } @Override protected Object visit(ASTEmptyMethod node, Object data) { Object val = node.jjtGetChild(0).jjtAccept(this, data); - return isEmpty(node, val); + return callEmpty(node, val); } /** @@ -1125,18 +1176,16 @@ public class Interpreter extends ParserV * method. * * @param node the node holding the object - * @param object the object to check the rmptyness of. + * @param object the object to check the emptyness of. * @return the boolean */ - private Object isEmpty(JexlNode node, Object object) { + private Object callEmpty(JexlNode node, Object object) { if (object == null) { return Boolean.TRUE; } - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.EMPTY, object); - if (result != JexlEngine.TRY_FAILED) { - return result; - } + Object opcall = callOperator(node, Operator.EMPTY, object); + if (opcall != JexlEngine.TRY_FAILED) { + return opcall; } if (object instanceof Number) { return ((Number) object).intValue() == 0 ? Boolean.TRUE : Boolean.FALSE; @@ -1181,11 +1230,9 @@ public class Interpreter extends ParserV if (object == null) { return 0; } - if (operators != null) { - Object result = operators.tryInvokeOperator(Operator.SIZE, object); - if (result != JexlEngine.TRY_FAILED) { - return result; - } + Object opcall = callOperator(node, Operator.SIZE, object); + if (opcall != JexlEngine.TRY_FAILED) { + return opcall; } if (object instanceof Collection<?>) { return ((Collection<?>) object).size(); @@ -1245,8 +1292,7 @@ public class Interpreter extends ParserV && !(node.jjtGetParent() instanceof ASTReference) && !context.has(name) && !isTernaryProtected(node)) { - JexlException xjexl = new JexlException.Variable(node, name); - return unknownVariable(xjexl); + return unsolvableVariable(node, name, true); } return value; } else { @@ -1331,6 +1377,9 @@ public class Interpreter extends ParserV throw new JexlException.Cancel(node); } objectNode = node.jjtGetChild(c); + if (objectNode instanceof ASTMethodNode && object == null) { + break; + } // attempt to evaluate the property within the object object = objectNode.jjtAccept(this, object); if (object == null && isVariable) { @@ -1348,21 +1397,22 @@ public class Interpreter extends ParserV // subsequent nodes must be identifier access objectNode = node.jjtGetChild(v); if (objectNode instanceof ASTIdentifierAccess) { + // variableName can *not* be null; it has been necessarily set by the (v == 0) condition variableName.append('.'); variableName.append(((ASTIdentifierAccess) objectNode).getName()); } else { break main; } } + // variableName can *not* be null; the code before this line made sure of that object = context.get(variableName.toString()); } isVariable &= object == null; } - if (object == null && isVariable && variableName != null - && !isTernaryProtected(node) && !(context.has(variableName.toString()) || isLocalVariable(node, 0))) { - JexlException xjexl = new JexlException.Variable(node, variableName.toString()); + if (object == null && isVariable && variableName != null && !isTernaryProtected(node)) { + boolean undefined = !(context.has(variableName.toString()) || isLocalVariable(node, 0)); // variable unknown in context and not a local - return unknownVariable(xjexl); + return unsolvableVariable(node, variableName.toString(), undefined); } return object; } @@ -1384,6 +1434,10 @@ public class Interpreter extends ParserV // check we are not assigning a symbol itself if (last < 0) { frame.set(symbol, right); + // make the closure accessible to itself, ie hoist the currently set variable after frame creation + if (right instanceof Closure) { + ((Closure) right).setHoisted(symbol, right); + } return right; } object = frame.get(symbol); @@ -1627,8 +1681,10 @@ public class Interpreter extends ParserV } return eval; } else { - xjexl = new JexlException.Method(node, methodName, null); + return unsolvableMethod(node, methodName); } + } catch(JexlException.Method xmethod) { + throw xmethod; } catch (Exception xany) { xjexl = new JexlException(node, methodName, xany); } @@ -1692,17 +1748,17 @@ public class Interpreter extends ParserV } if (ctor == null) { String dbgStr = cobject != null ? cobject.toString() : null; - xjexl = new JexlException.Method(node, dbgStr, null); + return unsolvableMethod(node, dbgStr); } } - if (xjexl == null) { - Object instance = ctor.invoke(cobject, argv); - // cache executor in volatile JexlNode.value - if (cache && ctor.isCacheable()) { - node.jjtSetValue(ctor); - } - return instance; + Object instance = ctor.invoke(cobject, argv); + // cache executor in volatile JexlNode.value + if (cache && ctor.isCacheable()) { + node.jjtSetValue(ctor); } + return instance; + } catch(JexlException.Method xmethod) { + throw xmethod; } catch (Exception xany) { String dbgStr = cobject != null ? cobject.toString() : null; xjexl = new JexlException(node, dbgStr, xany); @@ -1759,7 +1815,7 @@ public class Interpreter extends ParserV return value; } catch (Exception xany) { String attrStr = attribute != null ? attribute.toString() : null; - xjexl = new JexlException.Property(node, attrStr, xany); + return unsolvableProperty(node, attrStr, xany); } } if (xjexl == null) { @@ -1770,7 +1826,7 @@ public class Interpreter extends ParserV throw new UnsupportedOperationException(error); } String attrStr = attribute != null ? attribute.toString() : null; - xjexl = new JexlException.Property(node, attrStr, null); + return unsolvableProperty(node, attrStr, null); } if (strictEngine) { throw xjexl; @@ -1842,7 +1898,8 @@ public class Interpreter extends ParserV } } String attrStr = attribute != null ? attribute.toString() : null; - xjexl = new JexlException.Property(node, attrStr, xany); + unsolvableProperty(node, attrStr, xany); + return; } } if (xjexl == null) { @@ -1854,7 +1911,8 @@ public class Interpreter extends ParserV throw new UnsupportedOperationException(error); } String attrStr = attribute != null ? attribute.toString() : null; - xjexl = new JexlException.Property(node, attrStr, null); + unsolvableProperty(node, attrStr, null); + return; } if (strictEngine) { throw xjexl; Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java Wed Oct 1 08:57:12 2014 @@ -194,6 +194,23 @@ public final class Scope { } /** + * Gets the hoisted index of a given symbol, ie the target index of a symbol in a child frame. + * @param symbol the symbol index + * @return the target symbol index or null if the symbol is not hoisted + */ + public Integer getHoisted(int symbol) { + if (hoistedVariables != null) { + for (Map.Entry<Integer, Integer> hoist : hoistedVariables.entrySet()) { + Integer source = hoist.getValue(); + if (source == symbol) { + return hoist.getKey(); + } + } + } + return null; + } + + /** * Gets the (maximum) number of arguments this script expects. * @return the number of parameters */ Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java Wed Oct 1 08:57:12 2014 @@ -210,6 +210,7 @@ public final class TemplateEngine extend */ abstract ExpressionType getType(); + /** @return the info */ JexlInfo getInfo() { return null; } @@ -644,6 +645,7 @@ public final class TemplateEngine extend /** * Creates a JxltEngine.Exception from a JexlException. + * @param info the source info * @param action createExpression, prepare, evaluate * @param expr the template expression * @param xany the exception @@ -686,6 +688,7 @@ public final class TemplateEngine extend /** * Parses a unified expression. + * @param info the source info * @param expr the string expression * @param scope the template scope * @return the unified expression instance @@ -878,7 +881,8 @@ public final class TemplateEngine extend /** * Creates a new block. - * @param theType the type + * @param theType the block type + * @param theLine the line number * @param theBlock the content */ Block(BlockType theType, int theLine, String theBlock) { @@ -898,6 +902,11 @@ public final class TemplateEngine extend } } + /** + * Appends this block string representation to a builder. + * @param strb the string builder to append to + * @param prefix the line prefix (immediate or deferred) + */ protected void toString(StringBuilder strb, String prefix) { if (BlockType.VERBATIM.equals(type)) { strb.append(body); @@ -926,6 +935,7 @@ public final class TemplateEngine extend /** * Creates a new template from an character input. + * @param info the source info * @param directive the prefix for lines of code; can not be "$", "${", "#" or "#{" * since this would preclude being able to differentiate directives and template expressions * @param reader the input reader @@ -978,7 +988,7 @@ public final class TemplateEngine extend for (int b = 0; b < blocks.size(); ++b) { Block block = blocks.get(b); if (block.type == BlockType.VERBATIM) { - uexprs.add(TemplateEngine.this.parseExpression(info.at(block.line, 0), block.body, b > codeStart ? scope : null)); + uexprs.add(parseExpression(info.at(block.line, 0), block.body, b > codeStart ? scope : null)); } } source = blocks.toArray(new Block[blocks.size()]); @@ -1181,7 +1191,8 @@ public final class TemplateEngine extend * <p>This will dynamically try to find the best suitable method in the writer through uberspection. * Subclassing Writer by adding 'print' methods should be the preferred way to specialize output. * </p> - * @param arg the argument to print out + * @param info the source info + * @param arg the argument to print out */ private void doPrint(JexlInfo info, Object arg) { try { @@ -1237,7 +1248,7 @@ public final class TemplateEngine extend throw new IllegalArgumentException("mark support in reader required"); } return new Iterator<CharSequence>() { - CharSequence next = doNext(); + private CharSequence next = doNext(); private CharSequence doNext() { StringBuffer strb = new StringBuffer(64); @@ -1284,7 +1295,6 @@ public final class TemplateEngine extend /** * Reads lines of a template grouping them by typed blocks. - * @param info the source info * @param prefix the directive prefix * @param source the source reader * @return the list of blocks Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodExecutor.java Wed Oct 1 08:57:12 2014 @@ -114,11 +114,6 @@ public final class MethodExecutor extend /** * Reassembles arguments if the method is a vararg method. - * @param type The vararg class type (aka component type - * of the expected array arg) - * @param index The index of the vararg in the method declaration - * (This will always be one less than the number of - * expected arguments.) * @param actual The actual arguments being passed to this method * @return The actual parameters adjusted for the varargs in order * to fit the method declaration. Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/MethodKey.java Wed Oct 1 08:57:12 2014 @@ -451,6 +451,11 @@ public final class MethodKey { */ protected abstract Class<?>[] getParameterTypes(T app); + /** + * Whether a constructor or method handles varargs. + * @param app the constructor or method + * @return true if varargs, false otherwise + */ protected abstract boolean isVarArgs(T app); // CSOFF: RedundantThrows @@ -605,7 +610,7 @@ public final class MethodKey { /** * Checks whether a parameter class is a primitive. * @param c the parameter class - * @param possibleVararg true if this is the last parameter which can be a primitive array (vararg call) + * @param possibleVarArg true if this is the last parameter which can be a primitive array (vararg call) * @return true if primitive, false otherwise */ private boolean isPrimitive(Class<?> c, boolean possibleVarArg) { @@ -644,7 +649,7 @@ public final class MethodKey { * argument types. * * @param method method that will be called - * @param classes arguments to method + * @param actuals arguments signature for method * @return true if method is applicable to arguments */ private boolean isApplicable(T method, Class<?>[] actuals) { Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/PropertySetExecutor.java Wed Oct 1 08:57:12 2014 @@ -150,12 +150,12 @@ public class PropertySetExecutor extends * <p>This checks only one method with that name accepts an array as sole parameter. * @param is the introspector * @param clazz the class to find the get method from - * @param methodName the method name to find + * @param mname the method name to find * @return the sole method that accepts an array as parameter */ - private static java.lang.reflect.Method lookupSetEmptyArray(Introspector is, final Class<?> clazz, String methodName) { + private static java.lang.reflect.Method lookupSetEmptyArray(Introspector is, final Class<?> clazz, String mname) { java.lang.reflect.Method candidate = null; - java.lang.reflect.Method[] methods = is.getMethods(clazz, methodName); + java.lang.reflect.Method[] methods = is.getMethods(clazz, mname); if (methods != null) { for (java.lang.reflect.Method method : methods) { Class<?>[] paramTypes = method.getParameterTypes(); Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java Wed Oct 1 08:57:12 2014 @@ -367,50 +367,40 @@ public class Uberspect implements JexlUb * The concrete uberspect Arithmetic class. */ protected class ArithmeticUberspect implements JexlArithmetic.Uberspect { + /** The arithmetic instance being analyzed. */ private final JexlArithmetic arithmetic; + /** The set of overloaded operators. */ private final EnumSet<Operator> overloads; - private ArithmeticUberspect(JexlArithmetic arithmetic, Set<Operator> overloads) { - this.arithmetic = arithmetic; - this.overloads = EnumSet.copyOf(overloads); + /** + * Creates an instance. + * @param theArithmetic the arithmetic instance + * @param theOverloads the overloaded operators + */ + private ArithmeticUberspect(JexlArithmetic theArithmetic, Set<Operator> theOverloads) { + this.arithmetic = theArithmetic; + this.overloads = EnumSet.copyOf(theOverloads); + // register this arithmetic class in the operator map + operatorMap.put(arithmetic.getClass(), overloads); } @Override public JexlMethod getOperator(JexlArithmetic.Operator operator, Object arg) { - return overloads.contains(operator) && arg != null? - getMethod(arithmetic, operator.getMethodName(), arg) : null; + return overloads.contains(operator) && arg != null + ? getMethod(arithmetic, operator.getMethodName(), arg) + : null; } @Override public JexlMethod getOperator(JexlArithmetic.Operator operator, Object lhs, Object rhs) { - return overloads.contains(operator) && lhs != null && rhs != null? - getMethod(arithmetic, operator.getMethodName(), lhs, rhs) : null; + return overloads.contains(operator) && lhs != null && rhs != null + ? getMethod(arithmetic, operator.getMethodName(), lhs, rhs) + : null; } @Override - public Object tryInvokeOperator(JexlArithmetic.Operator operator, Object lhs, Object rhs) { - JexlMethod method = getOperator(operator, lhs, rhs); - if (method != null) { - try { - return method.invoke(arithmetic, lhs, rhs); - } catch(Exception xany) { - throw new ArithmeticException(xany.getMessage()); - } - } - return JexlEngine.TRY_FAILED; - } - - @Override - public Object tryInvokeOperator(JexlArithmetic.Operator operator, Object arg) { - JexlMethod method = getOperator(operator, arg); - if (method != null) { - try { - return method.invoke(arithmetic, arg); - } catch(Exception xany) { - throw new ArithmeticException(xany.getMessage()); - } - } - return JexlEngine.TRY_FAILED; + public boolean overloads(Operator operator) { + return overloads.contains(operator); } } @@ -429,6 +419,7 @@ public class Uberspect implements JexlUb if (parms.length != op.getArity()) { continue; } + // eliminate method(Object) and method(Object, Object) boolean root = true; for (int p = 0; root && p < parms.length; ++p) { if (!Object.class.equals(parms[p])) { @@ -441,7 +432,6 @@ public class Uberspect implements JexlUb } } } - operatorMap.put(arithmetic.getClass(), ops); } if (!ops.isEmpty()) { jau = new ArithmeticUberspect(arithmetic, ops); Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java Wed Oct 1 08:57:12 2014 @@ -18,6 +18,9 @@ package org.apache.commons.jexl3.parser; import org.apache.commons.jexl3.internal.Debugger; +/** + * An array literal. + */ public final class ASTArrayLiteral extends JexlNode { /** Whether this array is constant or not. */ private boolean constant = false; Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java Wed Oct 1 08:57:12 2014 @@ -30,12 +30,16 @@ public final class ASTJexlLambda extends super(p, id); } + /** + * @return true if outermost script. + */ public boolean isTopLevel() { return parent == null; } /** * Creates an array of arguments by copying values up to the number of parameters. + * @param frame the calling frame * @param values the argument values * @return the arguments array */ Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java Wed Oct 1 08:57:12 2014 @@ -74,7 +74,7 @@ public class ASTJexlScript extends JexlN } /** - * Gets this script scope. + * @return this script scope */ public Scope getScope() { return scope; Modified: commons/proper/jexl/trunk/src/site/xdoc/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/changes.xml?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original) +++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Wed Oct 1 08:57:12 2014 @@ -26,6 +26,18 @@ </properties> <body> <release version="3.0.1" date="unreleased"> + <action dev="henrib" type="fix" issue="JEXL-146" due-to="David Maplesden"> + Performance problem in Interpreter.unknownVariable mechanism + </action> + <action dev="henrib" type="fix"> + Functions assigned to local variables can not perform recursive calls + </action> + <action dev="henrib" type="fix"> + Improved error reporting on undefined or null variables + </action> + <action dev="henrib" type="fix"> + Improved operator overloading logic in JexlArithmeric (caching) + </action> <action dev="henrib" type="fix" issue="JEXL-145" due-to="Ian Connor"> Sandbox calling wrong check (classname vs class) </action> Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java Wed Oct 1 08:57:12 2014 @@ -83,7 +83,6 @@ public class ArithmeticTest extends Jexl public void testNullOperand() throws Exception { asserter.setVariable("right", null); asserter.failExpression("~right", ".*null.*"); - asserter.failExpression("-right", ".*arithmetic.*"); } public void testBigDecimal() throws Exception { @@ -411,4 +410,189 @@ public class ArithmeticTest extends Jexl assertEquals("failed on " + stext, expected, result); } } + + public static class Var { + final int value; + Var(int v) { + value = v; + } + } + + // an arithmetic that know how to subtract strings + public static class ArithmeticPlus extends JexlArithmetic { + public ArithmeticPlus(boolean strict) { + super(strict); + } + public boolean equals(Var lhs, Var rhs) { + return lhs.value == rhs.value; + } + public boolean lessThan(Var lhs, Var rhs) { + return lhs.value < rhs.value; + } + public boolean lessThanOrEqual(Var lhs, Var rhs) { + return lhs.value <= rhs.value; + } + public boolean greaterThan(Var lhs, Var rhs) { + return lhs.value > rhs.value; + } + public boolean greaterThanOrEqual(Var lhs, Var rhs) { + return lhs.value >= rhs.value; + } + public Var add(Var lhs, Var rhs) { + return new Var(lhs.value + rhs.value); + } + public Var subtract(Var lhs, Var rhs) { + return new Var(lhs.value - rhs.value); + } + public Var divide(Var lhs, Var rhs) { + return new Var(lhs.value / rhs.value); + } + public Var multiply(Var lhs, Var rhs) { + return new Var(lhs.value * rhs.value); + } + public Var mod(Var lhs, Var rhs) { + return new Var(lhs.value / rhs.value); + } + public Var negate(Var arg) { + return new Var(-arg.value); + } + public Var bitwiseAnd(Var lhs, Var rhs) { + return new Var(lhs.value & rhs.value); + } + public Var bitwiseOr(Var lhs, Var rhs) { + return new Var(lhs.value | rhs.value); + } + public Var bitwiseXor(Var lhs, Var rhs) { + return new Var(lhs.value ^ rhs.value); + } + public Var bitwiseComplement(Var arg) { + return new Var(~arg.value); + } + + public Object subtract(String x, String y) { + int ix = x.indexOf(y); + if (ix < 0) { + return x; + } + StringBuilder strb = new StringBuilder(x.substring(0, ix)); + strb.append(x.substring(ix + y.length())); + return strb.toString(); + } + + public Object negate(final String str) { + final int length = str.length(); + StringBuilder strb = new StringBuilder(str.length()); + for(int c = length - 1; c >= 0; --c) { + strb.append(str.charAt(c)); + } + return strb.toString(); + } + } + + public void testArithmeticPlus() throws Exception { + JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new ArithmeticPlus(false)).create(); + JexlContext jc = new EmptyTestContext(); + runOverload(jexl, jc); + } + + public void testArithmeticPlusNoCache() throws Exception { + JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new ArithmeticPlus(false)).create(); + JexlContext jc = new EmptyTestContext(); + runOverload(jexl, jc); + } + + protected void runOverload(JexlEngine jexl,JexlContext jc) { + JexlScript script; + Object result; + + script = jexl.createScript("(x, y)->{ x < y }"); + result = script.execute(jc, 42, 43); + assertEquals(true, result); + result = script.execute(jc, new Var(42), new Var(43)); + assertEquals(true, result); + result = script.execute(jc, new Var(42), new Var(43)); + assertEquals(true, result); + result = script.execute(jc, 43, 42); + assertEquals(false, result); + result = script.execute(jc, new Var(43), new Var(42)); + assertEquals(false, result); + result = script.execute(jc, new Var(43), new Var(42)); + assertEquals(false, result); + + script = jexl.createScript("(x, y)->{ x <= y }"); + result = script.execute(jc, 42, 43); + assertEquals(true, result); + result = script.execute(jc, new Var(42), new Var(43)); + assertEquals(true, result); + result = script.execute(jc, new Var(41), new Var(44)); + assertEquals(true, result); + result = script.execute(jc, 43, 42); + assertEquals(false, result); + result = script.execute(jc, new Var(45), new Var(40)); + assertEquals(false, result); + result = script.execute(jc, new Var(46), new Var(39)); + assertEquals(false, result); + + script = jexl.createScript("(x, y)->{ x == y }"); + result = script.execute(jc, 42, 43); + assertEquals(false, result); + result = script.execute(jc, new Var(42), new Var(43)); + assertEquals(false, result); + result = script.execute(jc, new Var(41), new Var(44)); + assertEquals(false, result); + result = script.execute(jc, 43, 42); + assertEquals(false, result); + result = script.execute(jc, new Var(45), new Var(40)); + assertEquals(false, result); + result = script.execute(jc, new Var(46), new Var(39)); + assertEquals(false, result); + + script = jexl.createScript("(x, y)->{ x % y }"); + result = script.execute(jc, 4242, 100); + assertEquals(42, result); + result = script.execute(jc, new Var(4242), new Var(100)); + assertEquals(42, ((Var) result).value); + result = script.execute(jc, new Var(4242), new Var(100)); + assertEquals(42, ((Var) result).value); + + script = jexl.createScript("(x, y)->{ x * y }"); + result = script.execute(jc, 6, 7); + assertEquals(42, result); + result = script.execute(jc, new Var(6), new Var(7)); + assertEquals(42, ((Var) result).value); + result = script.execute(jc, new Var(6), new Var(7)); + assertEquals(42, ((Var) result).value); + + script = jexl.createScript("(x, y)->{ x + y }"); + result = script.execute(jc, 35, 7); + assertEquals(42, result); + result = script.execute(jc, new Var(35), new Var(7)); + assertEquals(42, ((Var) result).value); + result = script.execute(jc, new Var(35), new Var(7)); + assertEquals(42, ((Var) result).value); + + script = jexl.createScript("(x, y)->{ x - y }"); + result = script.execute(jc, 49, 7); + assertEquals(42, result); + result = script.execute(jc, "foobarquux", "bar"); + assertEquals("fooquux", result); + result = script.execute(jc, 50, 8); + assertEquals(42, result); + result = script.execute(jc, new Var(50), new Var(8)); + assertEquals(42, ((Var) result).value); + result = script.execute(jc, new Var(50), new Var(8)); + assertEquals(42, ((Var) result).value); + + script = jexl.createScript("(x)->{ -x }"); + result = script.execute(jc, -42); + assertEquals(42, result); + result = script.execute(jc, new Var(-42)); + assertEquals(42, ((Var) result).value); + result = script.execute(jc, new Var(-42)); + assertEquals(42, ((Var) result).value); + result = script.execute(jc, "pizza"); + assertEquals("azzip", result); + result = script.execute(jc, -142); + assertEquals(142, result); + } } \ No newline at end of file Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreator.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreator.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreator.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreator.java Wed Oct 1 08:57:12 2014 @@ -22,6 +22,12 @@ import java.io.FileWriter; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; +import java.util.Arrays; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; /** * Helper class to test GC / reference interactions. @@ -36,10 +42,11 @@ public class ClassCreator { private String className = null; private String sourceName = null; private ClassLoader loader = null; - public static final boolean canRun = comSunToolsJavacMain(); + public static final boolean canRun = true;//comSunToolsJavacMain(); static final String GEN_PATH = "/org/apache/commons/jexl3/generated"; - static final String GEN_CLASS = "org.apache.commons.jexl3.generated."; + static final String GEN_PACKAGE = "org.apache.commons.jexl3.generated"; + static final String GEN_CLASS = GEN_PACKAGE + "."; /** * Check if we can invoke Sun's java compiler. * @return true if it is possible, false otherwise @@ -109,7 +116,9 @@ public class ClassCreator { void generate() throws Exception { FileWriter aWriter = new FileWriter(new File(packageDir, sourceName), false); - aWriter.write("package org.apache.commons.jexl3.generated;"); + aWriter.write("package "); + aWriter.write(GEN_PACKAGE); + aWriter.write(";\n"); aWriter.write("public class " + className + "{\n"); aWriter.write("private int value ="); aWriter.write(Integer.toString(seed)); @@ -125,7 +134,7 @@ public class ClassCreator { aWriter.close(); } - Class<?> compile() throws Exception { + Class<?> compile0() throws Exception { String source = packageDir.getPath() + "/" + sourceName; Class<?> javac = getClassLoader().loadClass("com.sun.tools.javac.Main"); if (javac == null) { @@ -135,18 +144,36 @@ public class ClassCreator { try { r = (Integer) jexl.invokeMethod(javac, "compile", source); if (r.intValue() >= 0) { - return getClassLoader().loadClass("org.apache.commons.jexl3.generated." + className); + return getClassLoader().loadClass(GEN_CLASS + className); } } catch (JexlException xignore) { // ignore } r = (Integer) jexl.invokeMethod(javac, "compile", (Object) new String[]{source}); if (r.intValue() >= 0) { - return getClassLoader().loadClass("org.apache.commons.jexl3.generated." + className); + return getClassLoader().loadClass(GEN_CLASS + className); } return null; } + Class<?> compile() throws Exception { + String source = packageDir.getPath() + "/" + sourceName; + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); + Iterable<? extends JavaFileObject> compilationUnits = fileManager + .getJavaFileObjectsFromStrings(Arrays.asList(source)); + JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, + null, compilationUnits); + boolean success = task.call(); + fileManager.close(); + if (success) { + return getClassLoader().loadClass(GEN_CLASS + className); + } else { + return null; + } + } + Object validate(Class<?> clazz) throws Exception { Class<?> params[] = {}; Object paramsObj[] = {}; Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java?rev=1628650&r1=1628649&r2=1628650&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java Wed Oct 1 08:57:12 2014 @@ -165,7 +165,7 @@ public class ClassCreatorTest extends Je // attempt to force GC: // while we still have a MB free, create & store big objects - for (int b = 0; b < 64 && Runtime.getRuntime().freeMemory() > MEGA; ++b) { + for (int b = 0; b < 1024 && Runtime.getRuntime().freeMemory() > MEGA; ++b) { BigObject big = new BigObject(b); stuff.add(new InstanceReference(big, queue)); }