/** * 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.ast; import org.mvel2.CompileException; import org.mvel2.ParserConfiguration; import org.mvel2.ParserContext; import org.mvel2.compiler.Accessor; import org.mvel2.debug.DebugTools; import org.mvel2.integration.VariableResolverFactory; import org.mvel2.optimizers.AccessorOptimizer; import org.mvel2.optimizers.OptimizationNotSupported; import java.io.Serializable; import static java.lang.Thread.currentThread; import static org.mvel2.Operator.NOOP; import static org.mvel2.PropertyAccessor.get; import static org.mvel2.optimizers.OptimizerFactory.*; import static org.mvel2.util.CompilerTools.getInjectedImports; import static org.mvel2.util.ParseTools.*; @SuppressWarnings({"ManualArrayCopy", "CaughtExceptionImmediatelyRethrown"}) public class ASTNode implements Cloneable, Serializable { public static final int LITERAL = 1; public static final int DEEP_PROPERTY = 1 << 1; public static final int OPERATOR = 1 << 2; public static final int IDENTIFIER = 1 << 3; public static final int COMPILE_IMMEDIATE = 1 << 4; public static final int NUMERIC = 1 << 5; public static final int INVERT = 1 << 6; public static final int ASSIGN = 1 << 7; public static final int COLLECTION = 1 << 8; public static final int THISREF = 1 << 9; public static final int INLINE_COLLECTION = 1 << 10; public static final int BLOCK_IF = 1 << 11; public static final int BLOCK_FOREACH = 1 << 12; public static final int BLOCK_WITH = 1 << 13; public static final int BLOCK_UNTIL = 1 << 14; public static final int BLOCK_WHILE = 1 << 15; public static final int BLOCK_DO = 1 << 16; public static final int BLOCK_DO_UNTIL = 1 << 17; public static final int BLOCK_FOR = 1 << 18; public static final int OPT_SUBTR = 1 << 19; public static final int FQCN = 1 << 20; public static final int STACKLANG = 1 << 22; public static final int DEFERRED_TYPE_RES = 1 << 23; public static final int STRONG_TYPING = 1 << 24; public static final int PCTX_STORED = 1 << 25; public static final int ARRAY_TYPE_LITERAL = 1 << 26; public static final int NOJIT = 1 << 27; public static final int DEOP = 1 << 28; public static final int DISCARD = 1 << 29; // *** // protected int firstUnion; protected int endOfName; public int fields = 0; protected Class egressType; protected char[] expr; protected int start; protected int offset; protected String nameCache; protected Object literal; protected transient volatile Accessor accessor; protected volatile Accessor safeAccessor; protected int cursorPosition; public ASTNode nextASTNode; protected ParserContext pCtx; public Object getReducedValueAccelerated(Object ctx, Object thisValue, VariableResolverFactory factory) { if (accessor != null) { try { return accessor.getValue(ctx, thisValue, factory); } catch (ClassCastException ce) { return deop(ctx, thisValue, factory, ce); } } else { return optimize(ctx, thisValue, factory); } } private Object deop(Object ctx, Object thisValue, VariableResolverFactory factory, RuntimeException e) { if ((fields & DEOP) == 0) { accessor = null; fields |= DEOP | NOJIT; synchronized (this) { return getReducedValueAccelerated(ctx, thisValue, factory); } } else { throw e; } } private Object optimize(Object ctx, Object thisValue, VariableResolverFactory factory) { if ((fields & DEOP) != 0) { fields ^= DEOP; } AccessorOptimizer optimizer; Object retVal = null; if ((fields & NOJIT) != 0 || factory != null && factory.isResolveable(nameCache)) { optimizer = getAccessorCompiler(SAFE_REFLECTIVE); } else { optimizer = getDefaultAccessorCompiler(); } ParserContext pCtx; if ((fields & PCTX_STORED) != 0) { pCtx = (ParserContext) literal; } else { pCtx = new ParserContext(new ParserConfiguration(getInjectedImports(factory), null)); } try { pCtx.optimizationNotify(); setAccessor(optimizer.optimizeAccessor(pCtx, expr, start, offset, ctx, thisValue, factory, true, egressType)); } catch (OptimizationNotSupported ne) { setAccessor((optimizer = getAccessorCompiler(SAFE_REFLECTIVE)) .optimizeAccessor(pCtx, expr, start, offset, ctx, thisValue, factory, true, null)); } if (accessor == null) { return get(expr, start, offset, ctx, factory, thisValue, pCtx); } if (retVal == null) { retVal = optimizer.getResultOptPass(); } if (egressType == null) { egressType = optimizer.getEgressType(); } return retVal; } public Object getReducedValue(Object ctx, Object thisValue, VariableResolverFactory factory) { if ((fields & (LITERAL)) != 0) { return literal; } else { return get(expr, start, offset, ctx, factory, thisValue, pCtx); } } protected String getAbsoluteRootElement() { if ((fields & (DEEP_PROPERTY | COLLECTION)) != 0) { return new String(expr, start, getAbsoluteFirstPart()); } return nameCache; } public Class getEgressType() { return egressType; } public void setEgressType(Class egressType) { this.egressType = egressType; } public char[] getNameAsArray() { return subArray(expr, start, start + offset); } private int getAbsoluteFirstPart() { if ((fields & COLLECTION) != 0) { if (firstUnion < 0 || endOfName < firstUnion) return endOfName; else return firstUnion; } else if ((fields & DEEP_PROPERTY) != 0) { return firstUnion; } else { return -1; } } public String getAbsoluteName() { if (firstUnion > start) { return new String(expr, start, getAbsoluteFirstPart() - start); } else { return getName(); } } public String getName() { if (nameCache != null) { return nameCache; } else if (expr != null) { return nameCache = new String(expr, start, offset); } return ""; } public Object getLiteralValue() { return literal; } public void storeInLiteralRegister(Object o) { this.literal = o; } public void setLiteralValue(Object literal) { this.literal = literal; this.fields |= LITERAL; } @SuppressWarnings({"SuspiciousMethodCalls"}) protected void setName(char[] name) { if (isNumber(name, start, offset)) { egressType = (literal = handleNumericConversion(name, start, offset)).getClass(); if (((fields |= NUMERIC | LITERAL | IDENTIFIER) & INVERT) != 0) { try { literal = ~((Integer) literal); } catch (ClassCastException e) { throw new CompileException("bitwise (~) operator can only be applied to integers", expr, start); } } return; } this.literal = new String(name, start, offset); int end = start + offset; Scan: for (int i = start; i < end; i++) { switch (name[i]) { case '.': if (firstUnion == 0) { firstUnion = i; } break; case '[': case '(': if (firstUnion == 0) { firstUnion = i; } if (endOfName == 0) { endOfName = i; if (i < name.length && name[i + 1] == ']') fields |= ARRAY_TYPE_LITERAL; break Scan; } } } if ((fields & INLINE_COLLECTION) != 0) { return; } if (firstUnion > start) { fields |= DEEP_PROPERTY | IDENTIFIER; } else { fields |= IDENTIFIER; } } public Accessor setAccessor(Accessor accessor) { return this.accessor = accessor; } public boolean isIdentifier() { return (fields & IDENTIFIER) != 0; } public boolean isLiteral() { return (fields & LITERAL) != 0; } public boolean isThisVal() { return (fields & THISREF) != 0; } public boolean isOperator() { return (fields & OPERATOR) != 0; } public boolean isOperator(Integer operator) { return (fields & OPERATOR) != 0 && operator.equals(literal); } public Integer getOperator() { return NOOP; } protected boolean isCollection() { return (fields & COLLECTION) != 0; } public boolean isAssignment() { return ((fields & ASSIGN) != 0); } public boolean isDeepProperty() { return ((fields & DEEP_PROPERTY) != 0); } public boolean isFQCN() { return ((fields & FQCN) != 0); } public void setAsLiteral() { fields |= LITERAL; } public void setAsFQCNReference() { fields |= FQCN; } public int getCursorPosition() { return cursorPosition; } public void setCursorPosition(int cursorPosition) { this.cursorPosition = cursorPosition; } public boolean isDiscard() { return fields != -1 && (fields & DISCARD) != 0; } public void discard() { this.fields |= DISCARD; } public void strongTyping() { this.fields |= STRONG_TYPING; } public void storePctx() { this.fields |= PCTX_STORED; } public boolean isDebuggingSymbol() { return this.fields == -1; } public int getFields() { return fields; } public Accessor getAccessor() { return accessor; } public boolean canSerializeAccessor() { return safeAccessor != null; } public int getStart() { return start; } public int getOffset() { return offset; } public char[] getExpr() { return expr; } protected ASTNode(ParserContext pCtx) { this.pCtx = pCtx; } public ASTNode(char[] expr, int start, int offset, int fields, ParserContext pCtx) { this(pCtx); this.fields = fields; this.expr = expr; this.start = start; this.offset = offset; setName(expr); } public String toString() { return isOperator() ? "<<" + DebugTools.getOperatorName(getOperator()) + ">>" : (PCTX_STORED & fields) != 0 ? nameCache : new String(expr, start, offset); } protected ClassLoader getClassLoader() { return pCtx != null ? pCtx.getClassLoader() : currentThread().getContextClassLoader(); } }