This is an automated email from the ASF dual-hosted git repository. henrib pushed a commit to branch JEXL-418 in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
commit 01310b5ef80288bcfe5e8269c7ccda388a5b0042 Author: Henri Biestro <hbies...@cloudera.com> AuthorDate: Fri Feb 16 22:10:10 2024 +0100 JEXL-418: initial drop; --- .../apache/commons/jexl3/internal/Debugger.java | 129 ++++------ .../apache/commons/jexl3/internal/Interpreter.java | 262 ++++++++++++++------- .../commons/jexl3/internal/ScriptVisitor.java | 93 +------- .../commons/jexl3/parser/ASTTryStatement.java | 49 ++++ .../commons/jexl3/parser/FeatureController.java | 8 + .../org/apache/commons/jexl3/parser/Parser.jjt | 36 ++- .../apache/commons/jexl3/parser/ParserVisitor.java | 2 + .../apache/commons/jexl3/TryCatchFinallyTest.java | 58 +++++ 8 files changed, 369 insertions(+), 268 deletions(-) diff --git a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java index 6353a57f..4cb224c7 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java @@ -25,95 +25,7 @@ import org.apache.commons.jexl3.JexlExpression; import org.apache.commons.jexl3.JexlFeatures; import org.apache.commons.jexl3.JexlInfo; import org.apache.commons.jexl3.JexlScript; -import org.apache.commons.jexl3.parser.ASTAddNode; -import org.apache.commons.jexl3.parser.ASTAndNode; -import org.apache.commons.jexl3.parser.ASTAnnotatedStatement; -import org.apache.commons.jexl3.parser.ASTAnnotation; -import org.apache.commons.jexl3.parser.ASTArguments; -import org.apache.commons.jexl3.parser.ASTArrayAccess; -import org.apache.commons.jexl3.parser.ASTArrayLiteral; -import org.apache.commons.jexl3.parser.ASTAssignment; -import org.apache.commons.jexl3.parser.ASTBitwiseAndNode; -import org.apache.commons.jexl3.parser.ASTBitwiseComplNode; -import org.apache.commons.jexl3.parser.ASTBitwiseOrNode; -import org.apache.commons.jexl3.parser.ASTBitwiseXorNode; -import org.apache.commons.jexl3.parser.ASTBlock; -import org.apache.commons.jexl3.parser.ASTBreak; -import org.apache.commons.jexl3.parser.ASTConstructorNode; -import org.apache.commons.jexl3.parser.ASTContinue; -import org.apache.commons.jexl3.parser.ASTDecrementGetNode; -import org.apache.commons.jexl3.parser.ASTDefineVars; -import org.apache.commons.jexl3.parser.ASTDivNode; -import org.apache.commons.jexl3.parser.ASTDoWhileStatement; -import org.apache.commons.jexl3.parser.ASTEQNode; -import org.apache.commons.jexl3.parser.ASTERNode; -import org.apache.commons.jexl3.parser.ASTEWNode; -import org.apache.commons.jexl3.parser.ASTEmptyFunction; -import org.apache.commons.jexl3.parser.ASTExtendedLiteral; -import org.apache.commons.jexl3.parser.ASTFalseNode; -import org.apache.commons.jexl3.parser.ASTForeachStatement; -import org.apache.commons.jexl3.parser.ASTFunctionNode; -import org.apache.commons.jexl3.parser.ASTGENode; -import org.apache.commons.jexl3.parser.ASTGTNode; -import org.apache.commons.jexl3.parser.ASTGetDecrementNode; -import org.apache.commons.jexl3.parser.ASTGetIncrementNode; -import org.apache.commons.jexl3.parser.ASTIdentifier; -import org.apache.commons.jexl3.parser.ASTIdentifierAccess; -import org.apache.commons.jexl3.parser.ASTIfStatement; -import org.apache.commons.jexl3.parser.ASTIncrementGetNode; -import org.apache.commons.jexl3.parser.ASTJexlLambda; -import org.apache.commons.jexl3.parser.ASTJexlScript; -import org.apache.commons.jexl3.parser.ASTJxltLiteral; -import org.apache.commons.jexl3.parser.ASTLENode; -import org.apache.commons.jexl3.parser.ASTLTNode; -import org.apache.commons.jexl3.parser.ASTMapEntry; -import org.apache.commons.jexl3.parser.ASTMapLiteral; -import org.apache.commons.jexl3.parser.ASTMethodNode; -import org.apache.commons.jexl3.parser.ASTModNode; -import org.apache.commons.jexl3.parser.ASTMulNode; -import org.apache.commons.jexl3.parser.ASTNENode; -import org.apache.commons.jexl3.parser.ASTNEWNode; -import org.apache.commons.jexl3.parser.ASTNRNode; -import org.apache.commons.jexl3.parser.ASTNSWNode; -import org.apache.commons.jexl3.parser.ASTNotNode; -import org.apache.commons.jexl3.parser.ASTNullLiteral; -import org.apache.commons.jexl3.parser.ASTNullpNode; -import org.apache.commons.jexl3.parser.ASTNumberLiteral; -import org.apache.commons.jexl3.parser.ASTOrNode; -import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier; -import org.apache.commons.jexl3.parser.ASTRangeNode; -import org.apache.commons.jexl3.parser.ASTReference; -import org.apache.commons.jexl3.parser.ASTReferenceExpression; -import org.apache.commons.jexl3.parser.ASTRegexLiteral; -import org.apache.commons.jexl3.parser.ASTReturnStatement; -import org.apache.commons.jexl3.parser.ASTSWNode; -import org.apache.commons.jexl3.parser.ASTSetAddNode; -import org.apache.commons.jexl3.parser.ASTSetAndNode; -import org.apache.commons.jexl3.parser.ASTSetDivNode; -import org.apache.commons.jexl3.parser.ASTSetLiteral; -import org.apache.commons.jexl3.parser.ASTSetModNode; -import org.apache.commons.jexl3.parser.ASTSetMultNode; -import org.apache.commons.jexl3.parser.ASTSetOrNode; -import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode; -import org.apache.commons.jexl3.parser.ASTSetShiftRightNode; -import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode; -import org.apache.commons.jexl3.parser.ASTSetSubNode; -import org.apache.commons.jexl3.parser.ASTSetXorNode; -import org.apache.commons.jexl3.parser.ASTShiftLeftNode; -import org.apache.commons.jexl3.parser.ASTShiftRightNode; -import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode; -import org.apache.commons.jexl3.parser.ASTSizeFunction; -import org.apache.commons.jexl3.parser.ASTStringLiteral; -import org.apache.commons.jexl3.parser.ASTSubNode; -import org.apache.commons.jexl3.parser.ASTTernaryNode; -import org.apache.commons.jexl3.parser.ASTTrueNode; -import org.apache.commons.jexl3.parser.ASTUnaryMinusNode; -import org.apache.commons.jexl3.parser.ASTUnaryPlusNode; -import org.apache.commons.jexl3.parser.ASTVar; -import org.apache.commons.jexl3.parser.ASTWhileStatement; -import org.apache.commons.jexl3.parser.JexlNode; -import org.apache.commons.jexl3.parser.ParserVisitor; -import org.apache.commons.jexl3.parser.StringParser; +import org.apache.commons.jexl3.parser.*; /** * Helps pinpoint the cause of problems in expressions that fail during evaluation. @@ -381,6 +293,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { || child instanceof ASTBlock || child instanceof ASTIfStatement || child instanceof ASTForeachStatement + || child instanceof ASTTryStatement || child instanceof ASTWhileStatement || child instanceof ASTDoWhileStatement || child instanceof ASTAnnotation; @@ -785,6 +698,44 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail { return data; } + @Override + protected Object visit(final ASTTryStatement node, final Object data) { + final int form = node.getTryForm(); + builder.append("try"); + int nc = 0; + // try with-resource named + if ((form & 1) != 0) { + builder.append('('); + accept(node.jjtGetChild(nc++), data); + if ((form & 2) != 0) { + builder.append(" = "); + accept(node.jjtGetChild(nc++), data); + } + builder.append(')'); + } else if ((form & 2) != 0) { + // try with-resource un-named + builder.append('('); + accept(node.jjtGetChild(nc++), data); + builder.append(')'); + } + // try-body + if ((form & 4) != 0) { + accept(node.jjtGetChild(nc++), data); + } + // catch-body + if ((form & 8) != 0) { + builder.append(" catch("); + accept(node.jjtGetChild(nc++), data); + builder.append(')'); + } + // finally-body + if ((form & 16) != 0) { + builder.append(" finally "); + accept(node.jjtGetChild(nc++), data); + } + return data; + } + @Override protected Object visit(final ASTGENode node, final Object data) { return infixChildren(node, " >= ", false, data); 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 4c690273..c2553001 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -33,94 +33,7 @@ import org.apache.commons.jexl3.JexlScript; import org.apache.commons.jexl3.JxltEngine; import org.apache.commons.jexl3.introspection.JexlMethod; import org.apache.commons.jexl3.introspection.JexlPropertyGet; -import org.apache.commons.jexl3.parser.ASTAddNode; -import org.apache.commons.jexl3.parser.ASTAndNode; -import org.apache.commons.jexl3.parser.ASTAnnotatedStatement; -import org.apache.commons.jexl3.parser.ASTAnnotation; -import org.apache.commons.jexl3.parser.ASTArguments; -import org.apache.commons.jexl3.parser.ASTArrayAccess; -import org.apache.commons.jexl3.parser.ASTArrayLiteral; -import org.apache.commons.jexl3.parser.ASTAssignment; -import org.apache.commons.jexl3.parser.ASTBitwiseAndNode; -import org.apache.commons.jexl3.parser.ASTBitwiseComplNode; -import org.apache.commons.jexl3.parser.ASTBitwiseOrNode; -import org.apache.commons.jexl3.parser.ASTBitwiseXorNode; -import org.apache.commons.jexl3.parser.ASTBlock; -import org.apache.commons.jexl3.parser.ASTBreak; -import org.apache.commons.jexl3.parser.ASTConstructorNode; -import org.apache.commons.jexl3.parser.ASTContinue; -import org.apache.commons.jexl3.parser.ASTDecrementGetNode; -import org.apache.commons.jexl3.parser.ASTDefineVars; -import org.apache.commons.jexl3.parser.ASTDivNode; -import org.apache.commons.jexl3.parser.ASTDoWhileStatement; -import org.apache.commons.jexl3.parser.ASTEQNode; -import org.apache.commons.jexl3.parser.ASTERNode; -import org.apache.commons.jexl3.parser.ASTEWNode; -import org.apache.commons.jexl3.parser.ASTEmptyFunction; -import org.apache.commons.jexl3.parser.ASTExtendedLiteral; -import org.apache.commons.jexl3.parser.ASTFalseNode; -import org.apache.commons.jexl3.parser.ASTForeachStatement; -import org.apache.commons.jexl3.parser.ASTFunctionNode; -import org.apache.commons.jexl3.parser.ASTGENode; -import org.apache.commons.jexl3.parser.ASTGTNode; -import org.apache.commons.jexl3.parser.ASTGetDecrementNode; -import org.apache.commons.jexl3.parser.ASTGetIncrementNode; -import org.apache.commons.jexl3.parser.ASTIdentifier; -import org.apache.commons.jexl3.parser.ASTIdentifierAccess; -import org.apache.commons.jexl3.parser.ASTIdentifierAccessJxlt; -import org.apache.commons.jexl3.parser.ASTIfStatement; -import org.apache.commons.jexl3.parser.ASTIncrementGetNode; -import org.apache.commons.jexl3.parser.ASTJexlLambda; -import org.apache.commons.jexl3.parser.ASTJexlScript; -import org.apache.commons.jexl3.parser.ASTJxltLiteral; -import org.apache.commons.jexl3.parser.ASTLENode; -import org.apache.commons.jexl3.parser.ASTLTNode; -import org.apache.commons.jexl3.parser.ASTMapEntry; -import org.apache.commons.jexl3.parser.ASTMapLiteral; -import org.apache.commons.jexl3.parser.ASTMethodNode; -import org.apache.commons.jexl3.parser.ASTModNode; -import org.apache.commons.jexl3.parser.ASTMulNode; -import org.apache.commons.jexl3.parser.ASTNENode; -import org.apache.commons.jexl3.parser.ASTNEWNode; -import org.apache.commons.jexl3.parser.ASTNRNode; -import org.apache.commons.jexl3.parser.ASTNSWNode; -import org.apache.commons.jexl3.parser.ASTNotNode; -import org.apache.commons.jexl3.parser.ASTNullLiteral; -import org.apache.commons.jexl3.parser.ASTNullpNode; -import org.apache.commons.jexl3.parser.ASTNumberLiteral; -import org.apache.commons.jexl3.parser.ASTOrNode; -import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier; -import org.apache.commons.jexl3.parser.ASTRangeNode; -import org.apache.commons.jexl3.parser.ASTReference; -import org.apache.commons.jexl3.parser.ASTReferenceExpression; -import org.apache.commons.jexl3.parser.ASTRegexLiteral; -import org.apache.commons.jexl3.parser.ASTReturnStatement; -import org.apache.commons.jexl3.parser.ASTSWNode; -import org.apache.commons.jexl3.parser.ASTSetAddNode; -import org.apache.commons.jexl3.parser.ASTSetAndNode; -import org.apache.commons.jexl3.parser.ASTSetDivNode; -import org.apache.commons.jexl3.parser.ASTSetLiteral; -import org.apache.commons.jexl3.parser.ASTSetModNode; -import org.apache.commons.jexl3.parser.ASTSetMultNode; -import org.apache.commons.jexl3.parser.ASTSetOrNode; -import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode; -import org.apache.commons.jexl3.parser.ASTSetShiftRightNode; -import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode; -import org.apache.commons.jexl3.parser.ASTSetSubNode; -import org.apache.commons.jexl3.parser.ASTSetXorNode; -import org.apache.commons.jexl3.parser.ASTShiftLeftNode; -import org.apache.commons.jexl3.parser.ASTShiftRightNode; -import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode; -import org.apache.commons.jexl3.parser.ASTSizeFunction; -import org.apache.commons.jexl3.parser.ASTStringLiteral; -import org.apache.commons.jexl3.parser.ASTSubNode; -import org.apache.commons.jexl3.parser.ASTTernaryNode; -import org.apache.commons.jexl3.parser.ASTTrueNode; -import org.apache.commons.jexl3.parser.ASTUnaryMinusNode; -import org.apache.commons.jexl3.parser.ASTUnaryPlusNode; -import org.apache.commons.jexl3.parser.ASTVar; -import org.apache.commons.jexl3.parser.ASTWhileStatement; -import org.apache.commons.jexl3.parser.JexlNode; +import org.apache.commons.jexl3.parser.*; /** * An interpreter of JEXL syntax. @@ -700,6 +613,179 @@ public class Interpreter extends InterpreterBase { throw new JexlException.Break(node); } + @Override + protected Object visit(final ASTTryStatement node, final Object data) { + int form = node.getTryForm(); + int nc = 0; + JexlNode tryVar = (form & 1) != 0 ? node.jjtGetChild(nc++) : null; + JexlNode tryExpression = (form & 2) != 0 ? node.jjtGetChild(nc++) : null; + JexlNode tryBody = (form & 4) != 0 ? node.jjtGetChild(nc++) : null; + JexlNode catchVar = (form & 8) != 0 ? node.jjtGetChild(nc++) : null; + JexlNode catchBody = (form & 8) != 0 ? node.jjtGetChild(nc++) : null; + JexlNode finallyBody = (form & 16) != 0 ? node.jjtGetChild(nc) : null; + JexlException caught = null; + JexlException flowControl = null; + Object result = null; + try { + // evaluate the try + result = evalTry(tryVar, tryExpression, tryBody, data); + } catch(JexlException.Return | JexlException.Cancel xflow) { + // flow control exceptions do not trigger the catch clause + flowControl = xflow; + } catch(JexlException xany) { + caught = xany; + result = null; + } + // if we caught an exception and have a catch body, evaluate it + JexlException thrownByCatch = null; + if (caught != null && catchBody != null) { + try { + // evaluate the catch + evalCatch(catchVar, catchBody, caught, data); + } catch(JexlException.Break | JexlException.Continue | + JexlException.Return | JexlException.Cancel alterFlow) { + flowControl = alterFlow; + } catch (JexlException exception) { + // catching an exception thrown from catch body; can be a (re)throw + thrownByCatch = exception; + } + } + // if we have a 'finally' block, no matter what, evaluate it: its control flow will + // take precedence over what the 'catch' block might have thrown. + if (finallyBody != null) { + try { + finallyBody.jjtAccept(this, data); + } catch (JexlException.Break | JexlException.Continue | JexlException.Return flowException) { + // potentially swallow previous, even return but not cancel + if (!(flowControl instanceof JexlException.Cancel)) { + flowControl = flowException; + } + } catch (JexlException.Cancel cancelException) { + // cancel swallows everything + flowControl = cancelException; + } catch (JexlException exception) { + // catching an exception thrown in finally body + if (jexl.logger.isDebugEnabled()) { + jexl.logger.debug("exception thrown in finally", thrownByCatch); + } + // swallow the caught one + thrownByCatch = exception; + } + } + if (flowControl != null) { + if (thrownByCatch != null && jexl.logger.isDebugEnabled()) { + jexl.logger.debug("finally swallowed exception thrown by catch", thrownByCatch); + } + throw flowControl; + } + if (thrownByCatch != null) { + throw thrownByCatch; + } + return result; + } + + /** + * Evaluate the try/try-with resource a try/catch/finally. + * + * @param tryVar the try-with-resource variable (if any) + * @param tryExpression the try-with-resource expression + * @param tryBody the try body + * @param data the data + * @return the result of body evaluation + */ + private Object evalTry(JexlNode tryVar, JexlNode tryExpression, JexlNode tryBody, final Object data) { + boolean lexical = false; + try { + final ASTIdentifier tryVariable; + final int symbol; + Object tryResult = null; + /* Capture try variable if any. */ + if (tryVar instanceof ASTReference) { + tryVariable = (ASTIdentifier) tryVar.jjtGetChild(0); + symbol = tryVariable.getSymbol(); + lexical = tryVariable.isLexical() || options.isLexical(); + if (lexical) { + // create lexical frame + final LexicalFrame locals = new LexicalFrame(frame, block); + // it may be a local previously declared + final boolean trySymbol = symbol >= 0 && tryVariable instanceof ASTVar; + if (trySymbol && !defineVariable((ASTVar) tryVariable, locals)) { + return redefinedVariable(tryVar.jjtGetParent(), tryVariable.getName()); + } + block = locals; + } + } else { + tryVariable = null; + symbol = -1; + lexical = false; + } + // evaluate try expression if any + if (tryExpression != null) { + tryResult = tryExpression.jjtAccept(this, data); + // assign try variable if declared + if (tryVariable != null) { + if (symbol < 0) { + setContextVariable(tryVar.jjtGetParent(), tryVariable.getName(), tryResult); + } else { + frame.set(symbol, tryResult); + } + } + } + // evaluate the body + return tryBody.jjtAccept(this, data); + } finally { + // restore lexical frame + if (lexical) { + block = block.pop(); + } + } + } + + /** + * Evaluate the catch a try/catch/finally. + * + * @param catchVar the variable containing the exception + * @param catchBody the body + * @param caught the caught exception + * @param data the data + * @return the result of body evaluation + */ + private Object evalCatch(JexlNode catchVar, JexlNode catchBody, JexlException caught, Object data) { + final int symbol; + final ASTIdentifier catchVariable; + boolean lexical = false; + try { + // declare catch variable and assign with caught exception + if (catchVar instanceof ASTReference) { + catchVariable = (ASTIdentifier) catchVar.jjtGetChild(0); + symbol = catchVariable.getSymbol(); + lexical = catchVariable.isLexical() || options.isLexical(); + if (lexical) { + // create lexical frame + final LexicalFrame locals = new LexicalFrame(frame, block); + // it may be a local previously declared + final boolean trySymbol = symbol >= 0 && catchVariable instanceof ASTVar; + if (trySymbol && !defineVariable((ASTVar) catchVariable, locals)) { + return redefinedVariable(catchVar.jjtGetParent(), catchVariable.getName()); + } + block = locals; + } + if (symbol < 0) { + setContextVariable(catchVar.jjtGetParent(), catchVariable.getName(), caught); + } else { + frame.set(symbol, caught); + } + } + // evaluate body + return catchBody.jjtAccept(this, data); + } finally { + // restore lexical frame + if (lexical) { + block = block.pop(); + } + } + } + @Override protected Object visit(final ASTForeachStatement node, final Object data) { return node.getLoopForm() == 0 ? forIterator(node, data) : forLoop(node, data); diff --git a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java index f9f225a3..6bd0cf77 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java +++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java @@ -18,93 +18,7 @@ package org.apache.commons.jexl3.internal; import org.apache.commons.jexl3.JexlExpression; import org.apache.commons.jexl3.JexlScript; -import org.apache.commons.jexl3.parser.ASTAddNode; -import org.apache.commons.jexl3.parser.ASTAndNode; -import org.apache.commons.jexl3.parser.ASTAnnotatedStatement; -import org.apache.commons.jexl3.parser.ASTAnnotation; -import org.apache.commons.jexl3.parser.ASTArguments; -import org.apache.commons.jexl3.parser.ASTArrayAccess; -import org.apache.commons.jexl3.parser.ASTArrayLiteral; -import org.apache.commons.jexl3.parser.ASTAssignment; -import org.apache.commons.jexl3.parser.ASTBitwiseAndNode; -import org.apache.commons.jexl3.parser.ASTBitwiseComplNode; -import org.apache.commons.jexl3.parser.ASTBitwiseOrNode; -import org.apache.commons.jexl3.parser.ASTBitwiseXorNode; -import org.apache.commons.jexl3.parser.ASTBlock; -import org.apache.commons.jexl3.parser.ASTBreak; -import org.apache.commons.jexl3.parser.ASTConstructorNode; -import org.apache.commons.jexl3.parser.ASTContinue; -import org.apache.commons.jexl3.parser.ASTDecrementGetNode; -import org.apache.commons.jexl3.parser.ASTDefineVars; -import org.apache.commons.jexl3.parser.ASTDivNode; -import org.apache.commons.jexl3.parser.ASTDoWhileStatement; -import org.apache.commons.jexl3.parser.ASTEQNode; -import org.apache.commons.jexl3.parser.ASTERNode; -import org.apache.commons.jexl3.parser.ASTEWNode; -import org.apache.commons.jexl3.parser.ASTEmptyFunction; -import org.apache.commons.jexl3.parser.ASTExtendedLiteral; -import org.apache.commons.jexl3.parser.ASTFalseNode; -import org.apache.commons.jexl3.parser.ASTForeachStatement; -import org.apache.commons.jexl3.parser.ASTFunctionNode; -import org.apache.commons.jexl3.parser.ASTGENode; -import org.apache.commons.jexl3.parser.ASTGTNode; -import org.apache.commons.jexl3.parser.ASTGetDecrementNode; -import org.apache.commons.jexl3.parser.ASTGetIncrementNode; -import org.apache.commons.jexl3.parser.ASTIdentifier; -import org.apache.commons.jexl3.parser.ASTIdentifierAccess; -import org.apache.commons.jexl3.parser.ASTIfStatement; -import org.apache.commons.jexl3.parser.ASTIncrementGetNode; -import org.apache.commons.jexl3.parser.ASTJexlScript; -import org.apache.commons.jexl3.parser.ASTJxltLiteral; -import org.apache.commons.jexl3.parser.ASTLENode; -import org.apache.commons.jexl3.parser.ASTLTNode; -import org.apache.commons.jexl3.parser.ASTMapEntry; -import org.apache.commons.jexl3.parser.ASTMapLiteral; -import org.apache.commons.jexl3.parser.ASTMethodNode; -import org.apache.commons.jexl3.parser.ASTModNode; -import org.apache.commons.jexl3.parser.ASTMulNode; -import org.apache.commons.jexl3.parser.ASTNENode; -import org.apache.commons.jexl3.parser.ASTNEWNode; -import org.apache.commons.jexl3.parser.ASTNRNode; -import org.apache.commons.jexl3.parser.ASTNSWNode; -import org.apache.commons.jexl3.parser.ASTNotNode; -import org.apache.commons.jexl3.parser.ASTNullLiteral; -import org.apache.commons.jexl3.parser.ASTNullpNode; -import org.apache.commons.jexl3.parser.ASTNumberLiteral; -import org.apache.commons.jexl3.parser.ASTOrNode; -import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier; -import org.apache.commons.jexl3.parser.ASTRangeNode; -import org.apache.commons.jexl3.parser.ASTReference; -import org.apache.commons.jexl3.parser.ASTReferenceExpression; -import org.apache.commons.jexl3.parser.ASTRegexLiteral; -import org.apache.commons.jexl3.parser.ASTReturnStatement; -import org.apache.commons.jexl3.parser.ASTSWNode; -import org.apache.commons.jexl3.parser.ASTSetAddNode; -import org.apache.commons.jexl3.parser.ASTSetAndNode; -import org.apache.commons.jexl3.parser.ASTSetDivNode; -import org.apache.commons.jexl3.parser.ASTSetLiteral; -import org.apache.commons.jexl3.parser.ASTSetModNode; -import org.apache.commons.jexl3.parser.ASTSetMultNode; -import org.apache.commons.jexl3.parser.ASTSetOrNode; -import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode; -import org.apache.commons.jexl3.parser.ASTSetShiftRightNode; -import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode; -import org.apache.commons.jexl3.parser.ASTSetSubNode; -import org.apache.commons.jexl3.parser.ASTSetXorNode; -import org.apache.commons.jexl3.parser.ASTShiftLeftNode; -import org.apache.commons.jexl3.parser.ASTShiftRightNode; -import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode; -import org.apache.commons.jexl3.parser.ASTSizeFunction; -import org.apache.commons.jexl3.parser.ASTStringLiteral; -import org.apache.commons.jexl3.parser.ASTSubNode; -import org.apache.commons.jexl3.parser.ASTTernaryNode; -import org.apache.commons.jexl3.parser.ASTTrueNode; -import org.apache.commons.jexl3.parser.ASTUnaryMinusNode; -import org.apache.commons.jexl3.parser.ASTUnaryPlusNode; -import org.apache.commons.jexl3.parser.ASTVar; -import org.apache.commons.jexl3.parser.ASTWhileStatement; -import org.apache.commons.jexl3.parser.JexlNode; -import org.apache.commons.jexl3.parser.ParserVisitor; +import org.apache.commons.jexl3.parser.*; /** * Concrete visitor base, used for feature and operator controllers. @@ -187,6 +101,11 @@ public class ScriptVisitor extends ParserVisitor { return visitNode(node, data); } + @Override + protected Object visit(final ASTTryStatement node, final Object data) { + return visitNode(node, data); + } + @Override protected Object visit(final ASTReturnStatement node, final Object data) { return visitNode(node, data); diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTTryStatement.java b/src/main/java/org/apache/commons/jexl3/parser/ASTTryStatement.java new file mode 100644 index 00000000..dde63c65 --- /dev/null +++ b/src/main/java/org/apache/commons/jexl3/parser/ASTTryStatement.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.jexl3.parser; + +/** + * Declares a try/catch/finally statement. + */ +public class ASTTryStatement extends JexlLexicalNode { + private static final long serialVersionUID = 1L; + /** try {}=0; try(expr()) &=2; try(let x = expr()); &=1 . */ + /** catch() &= 4, finally &= 8. */ + private int tryForm; + + void setTryForm(final int form) { + tryForm = form; + } + + public int getTryForm() { + return tryForm; + } + + public ASTTryStatement(final int id) { + super(id); + } + + public ASTTryStatement(final Parser p, final int id) { + super(p, id); + } + + @Override + public Object jjtAccept(final ParserVisitor visitor, final Object data) { + return visitor.visit(this, data); + } + +} diff --git a/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java b/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java index 73d1c3b1..2dfa2b0a 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java +++ b/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java @@ -131,6 +131,14 @@ public class FeatureController extends ScriptVisitor { return data; } + @Override + protected Object visit(final ASTTryStatement node, final Object data) { +// if (!features.supportsLoops()) { +// throwFeatureException(JexlFeatures.LOOP, node); +// } + return data; + } + @Override protected Object visit(final ASTConstructorNode node, final Object data) { if (!features.supportsNewInstance()) { diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt index 527f1e30..7dbcebaa 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt +++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt @@ -29,8 +29,8 @@ options TRACK_TOKENS=true; CACHE_TOKENS=true; ERROR_REPORTING=false; - // DEBUG_PARSER=true; - //DEBUG_TOKEN_MANAGER=true; +// DEBUG_PARSER=true; +// DEBUG_TOKEN_MANAGER=true; } PARSER_BEGIN(Parser) @@ -219,6 +219,11 @@ TOKEN_MGR_DECLS : { | < NULL : "null" > | < TRUE : "true" > | < FALSE : "false" > + | < THROW : "throw" > + | < TRY : "try" > + | < CATCH : "catch" > + | < FINALLY : "finally" > + } /*************************************** @@ -379,6 +384,8 @@ void StatementNoVar() #void : {} | LOOKAHEAD(<RETURN>) ReturnStatement() | LOOKAHEAD(<CONTINUE>) Continue() | LOOKAHEAD(<BREAK>) Break() + | LOOKAHEAD(<THROW>) ThrowStatement() + | LOOKAHEAD(<TRY>) TryStatement() | LOOKAHEAD(Expression()) ExpressionStatement() | Block() | LOOKAHEAD(<VAR>, {!getFeatures().isLexical()} ) Var() @@ -407,6 +414,22 @@ void IfStatement() : {} ( LOOKAHEAD(1) <ELSE> (LOOKAHEAD(1) Block() | StatementNoVar()) )? } +void TryStatement() : { + int tryForm = 0;} +{ + { + pushUnit(jjtThis); + } + <TRY> (<LPAREN> (LOOKAHEAD(3) InlineVar() <assign> { tryForm |= 1; })? Expression() <RPAREN> { tryForm |= 2; })? + ((LOOKAHEAD(1) Block() | StatementNoVar()) { tryForm |= 4; }) + (LOOKAHEAD(1) <CATCH> <LPAREN> InlineVar() <RPAREN> (LOOKAHEAD(1) Block() | StatementNoVar()) { tryForm |= 8; })? + (LOOKAHEAD(1) <FINALLY> (LOOKAHEAD(1) Block() | StatementNoVar()) { tryForm |= 16; })? + { + jjtThis.setTryForm(tryForm); + popUnit(jjtThis); + } +} + void WhileStatement() : {} { <WHILE> <LPAREN> Expression() <RPAREN> { loopCount += 1; } (LOOKAHEAD(1) Block() | StatementNoVar()) { loopCount -= 1; } @@ -422,6 +445,11 @@ void ReturnStatement() : {} <RETURN> (LOOKAHEAD(2) ExpressionStatement() )? } +void ThrowStatement() : {} +{ + <THROW> (LOOKAHEAD(2) ExpressionStatement() )? +} + void Continue() #Continue : { Token t; } @@ -445,7 +473,7 @@ void ForeachStatement() : { } <FOR> <LPAREN> ( - LOOKAHEAD(3) ForEachVar() <COLON> Expression() { loopForm = 0; } + LOOKAHEAD(3) InlineVar() <COLON> Expression() { loopForm = 0; } | ((LOOKAHEAD(1) Var() | Expression()) { loopForm = 1; })? <SEMICOL> (Expression() { loopForm |= 2; })? <SEMICOL> @@ -463,7 +491,7 @@ void ForeachStatement() : { } } -void ForEachVar() #Reference : {} +void InlineVar() #Reference : {} { <VAR> DeclareVar(false, false) | diff --git a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java index 66479e23..1851180a 100644 --- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java +++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java @@ -56,6 +56,8 @@ public abstract class ParserVisitor { protected abstract Object visit(ASTForeachStatement node, Object data); + protected abstract Object visit(ASTTryStatement node, Object data); + protected abstract Object visit(ASTReturnStatement node, Object data); protected abstract Object visit(ASTAssignment node, Object data); diff --git a/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java b/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java new file mode 100644 index 00000000..38535f64 --- /dev/null +++ b/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.jexl3; + +import org.junit.Assert; +import org.junit.Test; + +public class TryCatchFinallyTest extends JexlTestCase { + public TryCatchFinallyTest() { + super(TryCatchFinallyTest.class.getSimpleName()); + } + + @Test + public void testForm0x14() { + String src = "try { 42; } finally { 169; }"; + JexlScript script = JEXL.createScript(src); + Assert.assertNotNull(script); + Object result = script.execute(null); + Assert.assertEquals(42, result); + } + @Test + public void testForm0x17() { + String src = "try(let x = 42) { x; } finally { 169; }"; + JexlScript script = JEXL.createScript(src); + Assert.assertNotNull(script); + Object result = script.execute(null); + Assert.assertEquals(42, result); + } + + + @Test + public void testEdgeTry() { + int i = 0; + while(i++ < 5) { + System.out.println("i: " + i); + try { + throw new JexlException.Continue(null); + } finally { + continue; + } + } + System.out.println("iii: " + i); + } +}