/** * MVEL 2.0 * Copyright (C) 2007 The Codehaus * Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor * * Licensed 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.mvel2; import org.mvel2.ast.ASTNode; import org.mvel2.ast.Substatement; import org.mvel2.compiler.AbstractParser; import org.mvel2.compiler.BlankLiteral; import org.mvel2.integration.VariableResolverFactory; import org.mvel2.integration.impl.ImmutableDefaultFactory; import org.mvel2.integration.impl.MapVariableResolverFactory; import org.mvel2.util.ErrorUtil; import org.mvel2.util.ExecutionStack; import java.util.Map; import static org.mvel2.Operator.*; /** * The MVEL interpreted runtime, used for fast parse and execution of scripts. */ @SuppressWarnings({"CaughtExceptionImmediatelyRethrown"}) public class MVELInterpretedRuntime extends AbstractParser { public Object parse() { try { stk = new ExecutionStack(); dStack = new ExecutionStack(); variableFactory.setTiltFlag(false); cursor = start; return parseAndExecuteInterpreted(); } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); throw new CompileException("unexpected end of statement", expr, length); } catch (NullPointerException e) { e.printStackTrace(); if (cursor >= length) { throw new CompileException("unexpected end of statement", expr, length); } else { throw e; } } catch (CompileException e) { throw ErrorUtil.rewriteIfNeeded(e, expr, cursor); } } private Object holdOverRegister; /** * Main interpreter loop. * * @return value */ private Object parseAndExecuteInterpreted() { ASTNode tk = null; int operator; lastWasIdentifier = false; try { while ((tk = nextToken()) != null) { holdOverRegister = null; if (lastWasIdentifier && lastNode.isDiscard()) { stk.discard(); } /** * If we are at the beginning of a statement, then we immediately push the first token * onto the stack. */ if (stk.isEmpty()) { if ((tk.fields & ASTNode.STACKLANG) != 0) { stk.push(tk.getReducedValue(stk, ctx, variableFactory)); Object o = stk.peek(); if (o instanceof Integer) { arithmeticFunctionReduction((Integer) o); } } else { stk.push(tk.getReducedValue(ctx, ctx, variableFactory)); } /** * If this is a substatement, we need to move the result into the d-stack to preserve * proper execution order. */ if (tk instanceof Substatement && (tk = nextToken()) != null) { if (isArithmeticOperator(operator = tk.getOperator())) { stk.push(nextToken().getReducedValue(ctx, ctx, variableFactory), operator); if (procBooleanOperator(arithmeticFunctionReduction(operator)) == -1) return stk.peek(); else continue; } } else { continue; } } if (variableFactory.tiltFlag()) { return stk.pop(); } switch (procBooleanOperator(operator = tk.getOperator())) { case RETURN: variableFactory.setTiltFlag(true); return stk.pop(); case OP_TERMINATE: return stk.peek(); case OP_RESET_FRAME: continue; case OP_OVERFLOW: if (!tk.isOperator()) { if (!(stk.peek() instanceof Class)) { throw new CompileException("unexpected token or unknown identifier:" + tk.getName(), expr, st); } variableFactory.createVariable(tk.getName(), null, (Class) stk.peek()); } continue; } stk.push(nextToken().getReducedValue(ctx, ctx, variableFactory), operator); switch ((operator = arithmeticFunctionReduction(operator))) { case OP_TERMINATE: return stk.peek(); case OP_RESET_FRAME: continue; } if (procBooleanOperator(operator) == OP_TERMINATE) return stk.peek(); } if (holdOverRegister != null) { return holdOverRegister; } } catch (CompileException e) { throw ErrorUtil.rewriteIfNeeded(e, expr, start); } catch (NullPointerException e) { if (tk != null && tk.isOperator()) { CompileException ce = new CompileException("incomplete statement: " + tk.getName() + " (possible use of reserved keyword as identifier: " + tk.getName() + ")" , expr, st, e); ce.setExpr(expr); ce.setLineNumber(line); ce.setCursor(cursor); throw ce; } else { throw e; } } return stk.peek(); } private int procBooleanOperator(int operator) { switch (operator) { case RETURN: return RETURN; case NOOP: return -2; case AND: reduceRight(); if (!stk.peekBoolean()) { if (unwindStatement(operator)) { return -1; } else { stk.clear(); return OP_RESET_FRAME; } } else { stk.discard(); return OP_RESET_FRAME; } case OR: reduceRight(); if (stk.peekBoolean()) { if (unwindStatement(operator)) { return OP_TERMINATE; } else { stk.clear(); return OP_RESET_FRAME; } } else { stk.discard(); return OP_RESET_FRAME; } case CHOR: if (!BlankLiteral.INSTANCE.equals(stk.peek())) { return OP_TERMINATE; } break; case TERNARY: if (!stk.popBoolean()) { stk.clear(); ASTNode tk; for (; ; ) { if ((tk = nextToken()) == null || tk.isOperator(Operator.TERNARY_ELSE)) break; } } return OP_RESET_FRAME; case TERNARY_ELSE: captureToEOS(); return OP_RESET_FRAME; case END_OF_STMT: /** * Assignments are a special scenario for dealing with the stack. Assignments are basically like * held-over failures that basically kickstart the parser when an assignment operator is is * encountered. The originating token is captured, and the the parser is told to march on. The * resultant value on the stack is then used to populate the target variable. * * The other scenario in which we don't want to wipe the stack, is when we hit the end of the * statement, because that top stack value is the value we want back from the parser. */ if (hasMore()) { holdOverRegister = stk.pop(); stk.clear(); } return OP_RESET_FRAME; } return OP_CONTINUE; } /** * This method peforms the equivilent of an XSWAP operation to flip the operator * over to the top of the stack, and loads the stored values on the d-stack onto * the main program stack. */ private void reduceRight() { if (dStack.isEmpty()) return; Object o = stk.pop(); stk.push(dStack.pop(), o, dStack.pop()); reduce(); } private boolean hasMore() { return cursor <= end; } /** * This method is called to unwind the current statement without any reduction or further parsing. * * @param operator - * @return - */ private boolean unwindStatement(int operator) { ASTNode tk; switch (operator) { case AND: while ((tk = nextToken()) != null && !tk.isOperator(Operator.END_OF_STMT) && !tk.isOperator(Operator.OR)) { //nothing } break; default: while ((tk = nextToken()) != null && !tk.isOperator(Operator.END_OF_STMT)) { //nothing } } return tk == null; } MVELInterpretedRuntime(char[] expression, Object ctx, Map<String, Object> variables) { this.expr = expression; this.length = expr.length; this.ctx = ctx; this.variableFactory = new MapVariableResolverFactory(variables); } MVELInterpretedRuntime(char[] expression, Object ctx) { this.expr = expression; this.length = expr.length; this.ctx = ctx; this.variableFactory = new ImmutableDefaultFactory(); } MVELInterpretedRuntime(String expression) { setExpression(expression); this.variableFactory = new ImmutableDefaultFactory(); } MVELInterpretedRuntime(char[] expression) { this.length = end = (this.expr = expression).length; } public MVELInterpretedRuntime(char[] expr, Object ctx, VariableResolverFactory resolverFactory) { this.length = end = (this.expr = expr).length; this.ctx = ctx; this.variableFactory = resolverFactory; } public MVELInterpretedRuntime(char[] expr, int start, int offset, Object ctx, VariableResolverFactory resolverFactory) { this.expr = expr; this.start = start; this.end = start + offset; this.length = end - start; this.ctx = ctx; this.variableFactory = resolverFactory; } public MVELInterpretedRuntime(char[] expr, int start, int offset, Object ctx, VariableResolverFactory resolverFactory, ParserContext pCtx) { super(pCtx); this.expr = expr; this.start = start; this.end = start + offset; this.length = end - start; this.ctx = ctx; this.variableFactory = resolverFactory; } public MVELInterpretedRuntime(String expression, Object ctx, VariableResolverFactory resolverFactory) { setExpression(expression); this.ctx = ctx; this.variableFactory = resolverFactory; } public MVELInterpretedRuntime(String expression, Object ctx, VariableResolverFactory resolverFactory, ParserContext pCtx) { super(pCtx); setExpression(expression); this.ctx = ctx; this.variableFactory = resolverFactory; } MVELInterpretedRuntime(String expression, VariableResolverFactory resolverFactory) { setExpression(expression); this.variableFactory = resolverFactory; } MVELInterpretedRuntime(String expression, Object ctx) { setExpression(expression); this.ctx = ctx; this.variableFactory = new ImmutableDefaultFactory(); } }