/*
* AbstractCompoundOperation.java
* @Author Oleg Gorobets
* Created: 25.07.2007
* CVS-ID: $Id:
*************************************************************************/
package org.swfparser.operation;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import org.springframework.util.Assert;
import org.apache.log4j.Logger;
import org.swfparser.BooleanOperation;
import org.swfparser.CodeUtil;
import org.swfparser.DualUse;
import org.swfparser.ExecutionContext;
import org.swfparser.ExecutionStack;
import org.swfparser.Operation;
import org.swfparser.StatementBlock;
import org.swfparser.exception.StatementBlockException;
import com.jswiff.swfrecords.actions.Action;
// TODO: Refactor to accept only context
public abstract class AbstractCompoundOperation extends AbstractOperation {
private static Logger logger = Logger.getLogger(AbstractCompoundOperation.class);
protected StatementBlock statementBlock;
protected ExecutionContext context;
public AbstractCompoundOperation(ExecutionContext context) {
super(context.getExecStack());
this.context = context;
this.statementBlock = CodeUtil.getStatementBlockReader();
}
public int getArgsNumber() {
// TODO Auto-generated method stub
return 0;
}
public int getPrintOffset() {
return (context.getActionStack() != null ) ? context.getActionStack().size() : 0;
}
protected List<Operation> executeWithSameStack(ExecutionContext context, List<Action> actions, Operation parentOperation) throws StatementBlockException {
StatementBlock block = CodeUtil.getStatementBlockReader();
context.getOperationStack().push(parentOperation); // save parent operation in operation stack
block.setExecutionContext(context);
block.read(actions);
context.getOperationStack().pop(); // restore operation stack
return block.getOperations();
}
protected BlockExecutionResult executeWithCopiedStack(ExecutionContext context, List<Action> actions, Operation parentOperation) throws StatementBlockException {
StatementBlock block = CodeUtil.getStatementBlockReader();
context.getOperationStack().push(parentOperation); // save parent operation in operation stack
Stack<Operation> currentExecutionStack = context.getExecStack(); // save current execution stack
context.setExecStack(copyExecutionStack(currentExecutionStack)); // set a copy of current stack
block.setExecutionContext(context);
block.read(actions);
BlockExecutionResult blockExecutionResult = new BlockExecutionResult(block.getOperations(),context.getExecStack());
context.setExecStack(currentExecutionStack); // restore previous execution stack
context.getOperationStack().pop(); // restore operation stack
return blockExecutionResult;
}
protected Stack<Operation> copyExecutionStack(Stack<Operation> currentExecutionStack) {
Stack<Operation> newExecutionStack = new ExecutionStack<Operation>();
for (Operation op : currentExecutionStack) {
newExecutionStack.push(op);
}
// newExecutionStack.setSize(currentExecutionStack.size());
// Collections.copy(newExecutionStack, currentExecutionStack);
return newExecutionStack;
}
protected Stack<Operation> createEmptyExecutionStack() {
return new ExecutionStack<Operation>();
}
/**
* -- before stack
* 00: jump 04 (jump to 04 on jumpCondition)
* 01:
* 02:
* 03:
* -- after stack (if jump not succeeded)
* 04:
*
* @param jumpCondition
* @param beforeStack = jump executed stack
* @param afterStack = jump NOT executed stack
*
* @return a new copy of stack
*/
protected Stack<Operation> handleUnequalStack(Operation jumpCondition, Stack<Operation> beforeStack, Stack<Operation> afterStack) {
boolean equalStacks = Arrays.equals(beforeStack.toArray(), afterStack.toArray());
if (equalStacks) {
return copyExecutionStack(afterStack);
}
logger.debug("Unequal stacks");
// Handle only equal size stacks
if (beforeStack.size() == afterStack.size()) {
return handleEqualSizeStack(jumpCondition, beforeStack, afterStack);
} else {
return handleUnEqualSizeStack(jumpCondition, beforeStack, afterStack);
}
}
protected Stack<Operation> handleUnEqualSizeStack(Operation jumpCondition, Stack<Operation> beforeStack, Stack<Operation> afterStack) {
logger.debug("Unequal stack size, beforeStack = "+beforeStack.size()+", afterStack = "+afterStack.size());
// Assert.isTrue(afterStack.size() > beforeStack.size());
// while (!afterStack.isEmpty() && (afterStack.peek() instanceof DualUse)) {
// context.getafterStack.pop();
// }
return beforeStack;
}
protected Stack<Operation> handleEqualSizeStack(Operation jumpCondition, Stack<Operation> beforeStack, Stack<Operation> afterStack) {
int size = beforeStack.size();
int unequalItems = 0;
for (int j=size-1; j>=0; j--) {
logger.debug("CHK(PREV<->NEXT): "+beforeStack.get(j)+" <-> "+afterStack.get(j));
if (!beforeStack.get(j).equals(afterStack.get(j))) {
Assert.isTrue(j == size-1); // allow only stack-top items to be unequal
unequalItems++;
}
}
logger.debug("Unequal items = "+unequalItems);
// Handle only 1 unequal item
Assert.isTrue(unequalItems == 1);
// Handle only boolean operations
if (! (beforeStack.peek() instanceof BooleanOperation)) {
// throw new IllegalArgumentException("Not boolean: "+beforeStack.peek());
logger.error("Not boolean: "+beforeStack.peek());
}
if (! (afterStack.peek() instanceof BooleanOperation)) {
// throw new IllegalArgumentException("Not boolean: "+afterStack.peek());
logger.error("Not boolean: "+afterStack.peek());
}
logger.debug("Try to simplify if condition...");
/*
condition = a
before stack = b
after stack = c
a&&b || !a&&c
*/
Stack<Operation> newExecutionStack = copyExecutionStack(afterStack);
if (jumpCondition.equals(beforeStack.peek())) {
// a&&a || !a&&c = a || !a&&c = a||c;
logger.debug("Simplifying to OR");
Operation nextStackValue = newExecutionStack.pop();
newExecutionStack.push(new OrOperation(jumpCondition,nextStackValue));
} else if (jumpCondition.equals(new NotOperation(beforeStack.peek()))) {
// a&&!a || !a&&c = 0 || !a&&c = !a&&c
logger.debug("Simplifying to AND");
Operation nextStackValue = newExecutionStack.pop();
newExecutionStack.push(AndOperation.createInstance(new NotOperation(jumpCondition),nextStackValue));
} else {
logger.debug("Simplifying to TERNARY");
// Operation op1 = AndOperation.createInstance(jumpCondition,beforeStack.peek());
// Operation op2 = AndOperation.createInstance(new NotOperation(jumpCondition),newExecutionStack.pop());
// newExecutionStack.push(BranchOperation.createInstance(op1, op2));
newExecutionStack.push(
new TernaryOperation(jumpCondition,beforeStack.peek(),newExecutionStack.pop())
);
}
return newExecutionStack;
}
protected static class BlockExecutionResult {
private List<Operation> operations;
private Stack<Operation> stack;
public BlockExecutionResult(List<Operation> operations, Stack<Operation> stack) {
super();
this.operations = operations;
this.stack = stack;
}
public List<Operation> getOperations() {
return operations;
}
public void setOperations(List<Operation> operations) {
this.operations = operations;
}
public Stack<Operation> getStack() {
return stack;
}
public void setStack(Stack<Operation> stack) {
this.stack = stack;
}
}
protected boolean equalStacks(List<Stack<Operation>> stacks) {
boolean eq = true;
for (int j = 0; j<stacks.size()-1; j++) {
if (! Arrays.equals(stacks.get(j).toArray(), stacks.get(j+1).toArray()) ) {
eq = false;
break;
}
}
return eq;
}
/* (non-Javadoc)
* @see org.swfparser.Operation#getOperations()
*
* Return empty list for compound operations as they are usually statements
*
*/
public List<Operation> getOperations() {
return Collections.EMPTY_LIST;
}
}