package org.mvel2.ast;
import org.mvel2.CompileException;
import org.mvel2.MVEL;
import org.mvel2.Operator;
import org.mvel2.ParserContext;
import org.mvel2.integration.VariableResolver;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.util.ExecutionStack;
import org.mvel2.util.ParseTools;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Mike Brock <cbrock@redhat.com>
*/
public class Stacklang extends BlockNode {
List<Instruction> instructionList;
ParserContext pCtx;
public Stacklang(char[] expr, int blockStart, int blockOffset, int fields, ParserContext pCtx) {
super(pCtx);
this.expr = expr;
this.blockStart = blockStart;
this.blockOffset = blockOffset;
this.fields = fields | ASTNode.STACKLANG;
String[] instructions = new String(expr, blockStart, blockOffset).split(";");
instructionList = new ArrayList<Instruction>(instructions.length);
for (String s : instructions) {
instructionList.add(parseInstruction(s.trim()));
}
this.pCtx = pCtx;
}
@Override
public Object getReducedValueAccelerated(Object ctx, Object thisValue, VariableResolverFactory factory) {
ExecutionStack stk = new ExecutionStack();
stk.push(getReducedValue(stk, thisValue, factory));
if (stk.isReduceable()) {
while (true) {
stk.op();
if (stk.isReduceable()) {
stk.xswap();
}
else {
break;
}
}
}
return stk.peek();
}
@Override
public Object getReducedValue(Object ctx, Object thisValue, VariableResolverFactory factory) {
ExecutionStack stack = (ExecutionStack) ctx;
for (int i1 = 0, instructionListSize = instructionList.size(); i1 < instructionListSize; i1++) {
Instruction instruction = instructionList.get(i1);
System.out.println(stack.toString() + " >> " + instruction.opcode + ":" + instruction.expr);
switch (instruction.opcode) {
case Operator.STORE:
if (instruction.cache == null) {
instruction.cache = factory.createVariable(instruction.expr, stack.peek());
}
else {
((VariableResolver) instruction.cache).setValue(stack.peek());
}
break;
case Operator.LOAD:
if (instruction.cache == null) {
instruction.cache = factory.getVariableResolver(instruction.expr);
}
stack.push(((VariableResolver) instruction.cache).getValue());
break;
case Operator.GETFIELD:
try {
if (stack.isEmpty() || !(stack.peek() instanceof Class)) {
throw new CompileException("getfield without class", expr, blockStart);
}
Field field;
if (instruction.cache == null) {
instruction.cache = field = ((Class) stack.pop()).getField(instruction.expr);
}
else {
stack.discard();
field = (Field) instruction.cache;
}
stack.push(field.get(stack.pop()));
}
catch (Exception e) {
throw new CompileException("field access error", expr, blockStart, e);
}
break;
case Operator.STOREFIELD:
try {
if (stack.isEmpty() || !(stack.peek() instanceof Class)) {
throw new CompileException("storefield without class", expr, blockStart);
}
Class cls = (Class) stack.pop();
Object val = stack.pop();
cls.getField(instruction.expr).set(stack.pop(), val);
stack.push(val);
}
catch (Exception e) {
throw new CompileException("field access error", expr, blockStart, e);
}
break;
case Operator.LDTYPE:
try {
if (instruction.cache == null) {
instruction.cache = ParseTools.createClass(instruction.expr, pCtx);
}
stack.push(instruction.cache);
}
catch (ClassNotFoundException e) {
throw new CompileException("error", expr, blockStart, e);
}
break;
case Operator.INVOKE:
Object[] parms;
ExecutionStack call = new ExecutionStack();
while (!stack.isEmpty() && !(stack.peek() instanceof Class)) {
call.push(stack.pop());
}
if (stack.isEmpty()) {
throw new CompileException("invoke without class", expr, blockStart);
}
parms = new Object[call.size()];
for (int i = 0; !call.isEmpty(); i++) parms[i] = call.pop();
if ("<init>".equals(instruction.expr)) {
Constructor c;
if (instruction.cache == null) {
instruction.cache = c = ParseTools.getBestConstructorCandidate(parms, (Class) stack.pop(), false);
}
else {
c = (Constructor) instruction.cache;
}
try {
stack.push(c.newInstance(parms));
}
catch (Exception e) {
throw new CompileException("instantiation error", expr, blockStart, e);
}
}
else {
Method m;
if (instruction.cache == null) {
Class cls = (Class) stack.pop();
instruction.cache = m = ParseTools.getBestCandidate(parms, instruction.expr, cls,
cls.getDeclaredMethods(), false);
}
else {
stack.discard();
m = (Method) instruction.cache;
}
try {
stack.push(m.invoke(stack.isEmpty() ? null : stack.pop(), parms));
}
catch (Exception e) {
throw new CompileException("invokation error", expr, blockStart, e);
}
}
break;
case Operator.PUSH:
if (instruction.cache == null) {
instruction.cache = MVEL.eval(instruction.expr, ctx, factory);
}
stack.push(instruction.cache);
break;
case Operator.POP:
stack.pop();
break;
case Operator.DUP:
stack.dup();
break;
case Operator.LABEL:
break;
case Operator.JUMPIF:
if (!stack.popBoolean()) continue;
case Operator.JUMP:
if (instruction.cache != null) {
i1 = (Integer) instruction.cache;
}
else {
for (int i2 = 0; i2 < instructionList.size(); i2++) {
Instruction ins = instructionList.get(i2);
if (ins.opcode == Operator.LABEL &&
instruction.expr.equals(ins.expr)) {
instruction.cache = i1 = i2;
break;
}
}
}
break;
case Operator.EQUAL:
stack.push(stack.pop().equals(stack.pop()));
break;
case Operator.NEQUAL:
stack.push(!stack.pop().equals(stack.pop()));
break;
case Operator.REDUCE:
stack.op();
break;
case Operator.XSWAP:
stack.xswap2();
break;
case Operator.SWAP:
Object o = stack.pop();
Object o2 = stack.pop();
stack.push(o);
stack.push(o2);
break;
}
}
return stack.pop();
}
private static class Instruction {
int opcode;
String expr;
Object cache;
}
private static Instruction parseInstruction(String s) {
int split = s.indexOf(' ');
Instruction instruction = new Instruction();
String keyword = split == -1 ? s : s.substring(0, split);
if (opcodes.containsKey(keyword)) {
instruction.opcode = opcodes.get(keyword);
}
//noinspection StringEquality
if (keyword != s) {
instruction.expr = s.substring(split + 1);
}
return instruction;
}
static final Map<String, Integer> opcodes = new HashMap<String, Integer>();
static {
opcodes.put("push", Operator.PUSH);
opcodes.put("pop", Operator.POP);
opcodes.put("load", Operator.LOAD);
opcodes.put("ldtype", Operator.LDTYPE);
opcodes.put("invoke", Operator.INVOKE);
opcodes.put("store", Operator.STORE);
opcodes.put("getfield", Operator.GETFIELD);
opcodes.put("storefield", Operator.STOREFIELD);
opcodes.put("dup", Operator.DUP);
opcodes.put("jump", Operator.JUMP);
opcodes.put("jumpif", Operator.JUMPIF);
opcodes.put("label", Operator.LABEL);
opcodes.put("eq", Operator.EQUAL);
opcodes.put("ne", Operator.NEQUAL);
opcodes.put("reduce", Operator.REDUCE);
opcodes.put("xswap", Operator.XSWAP);
opcodes.put("swap", Operator.SWAP);
}
}