/* * StoreRegisterOperation.java * @Author Oleg Gorobets * Created: 26.07.2007 * CVS-ID: $Id: *************************************************************************/ package org.swfparser.operation; import java.util.Collections; import java.util.List; import java.util.Stack; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.log4j.Logger; import org.swfparser.AssignOperation; import org.swfparser.BooleanOperation; import org.swfparser.CodeUtil; import org.swfparser.ExecutionContext; import org.swfparser.Operation; import org.swfparser.OperationFactory; import org.swfparser.Priority; import org.swfparser.SkipOperation; import org.swfparser.annotations.DoNotWritePop; import com.jswiff.swfrecords.actions.NullStackValue; import com.jswiff.swfrecords.actions.StoreRegister; @DoNotWritePop public class StoreRegisterOperation extends UnaryOperation implements OperationFactory, AssignOperation, SkipOperation { private static Logger logger = Logger.getLogger(StoreRegisterOperation.class); private int registerNumber; private String varName; private Operation returnOperation; private RegisterHandle registerHandle; private boolean skipOperation = false; public StoreRegisterOperation(ExecutionContext context, StoreRegister action) { super(context.getExecStack().peek()); this.registerNumber = action.getNumber(); logger.debug("Register number = " + this.registerNumber); logger.debug("Adding register " + this.registerNumber + " = " + op); List<Operation> registers = context.getRegisters(); Operation registerOperation = null; if (registers.size() > this.registerNumber) { // Obviously `List registers` is like a sparse array, if index 1 is set the size is 2. registerOperation = registers.get(this.registerNumber); } if (registerOperation instanceof FunctionParameterOperation) { // Is the operation a function parameter? String varName = registerOperation.getStringValue(0); this.varName = varName; this.registerHandle = new RegisterHandle(varName, op, this); } else if (registerOperation instanceof RegisterHandle && ((RegisterHandle) registerOperation).hasVarName()) { // Does the registerOperation have a varName set, which means it used to be a function param, the case above. this.varName = ((RegisterHandle) registerOperation).getVarName(); this.registerHandle = new RegisterHandle(varName, op, this); } else { this.registerHandle = new RegisterHandle(this.registerNumber, op, this); } // If we modify the function parameter, keep using it, instead of the entire expression in subsequent uses. // E.g. `trace((t = t / 2) * t);` becomes `t = t / 2; trace(t * t);` // instead of `t = t / 2; trace((t / 2) * t);` which it would without adding this check to the following IF. Boolean storesValueBackInFunctionParameter = this.registerHandle.hasVarName(); if (op instanceof NewMethodOperation || storesValueBackInFunctionParameter) { // Don't duplicate `x = new Class()` but use only the register from now on, it used to render the following: // `__regX = new Class()` and in a subsequent use if would again use `new Class()` instead of `__regX`. // Fixed this hereby. // TODO might also make sense for CallFunc/Method and maybe everything stored in a register ... Stack<Operation> execStack = context.getExecStack(); execStack.pop(); execStack.push(this.registerHandle); } registers.set(this.registerNumber, this.registerHandle); String regInfo="REGS:"; int i = 1; for (Operation op : registers) { regInfo += (i++) + " => " + op + ", "; } logger.debug(regInfo); handleIncrementDecrement(); // If operation under Enumerate/Enumerate2 and NullStackValue is assigned to register, // set this RegisterHandle as variable in for..in statement and skip this statement. if (!context.getOperationStack().isEmpty() && (context.getOperationStack().peek() instanceof ForInOperation) && op instanceof NullStackValue) { ((ForInOperation)context.getOperationStack().peek()).setVariable(registerHandle); // ((ForInOperation)context.getOperationStack().peek()).setVariable(op); skipOperation = true; } } protected void handleIncrementDecrement() { if (op instanceof SimpleIncrementOperation) { Operation underlyingOp = ((UnaryOperation)op).getOp(); if (underlyingOp instanceof RegisterHandle && ((RegisterHandle)underlyingOp).getRegisterNumber() == this.registerNumber) { returnOperation = new PostIncrementOperation(underlyingOp,true); } } if (op instanceof SimpleDecrementOperation) { Operation underlyingOp = ((UnaryOperation)op).getOp(); if (underlyingOp instanceof RegisterHandle && ((RegisterHandle)underlyingOp).getRegisterNumber() == this.registerNumber) { returnOperation = new PostDecrementOperation(underlyingOp, true); } } logger.debug("Return operation is " + returnOperation ); } public String getStringValue(int level) { String val = op.getStringValue(0); // pass 0 as val shouldn't be indented if (varName != null) { return CodeUtil.getIndent(level) + varName + " = " + val; } else { String leftHandVariable = "__reg" + registerNumber; if (leftHandVariable.equals(val)) { // Don't render 'var __reg0 = __reg0;' return ""; // } else if (val.equals("undefined")) { // // Don't render `var __regX = undefined;`, just `var __regX`; // return "var " + leftHandVariable; render anything at all???? } else { return CodeUtil.getIndent(level) + "var " + leftHandVariable + " = " + val; } } } public Operation getObject() { return returnOperation != null ? returnOperation : this; } public Operation getLeftPart() { return registerHandle; } public Operation getRightPart() { return op; } public int getRegisterNumber() { return registerNumber; } public static class RegisterHandle implements Operation, BooleanOperation { private int registerNumber; private String varName; private Operation undelrlyingOp; private StoreRegisterOperation storeRegisterOp; public RegisterHandle(int registerNumber) { this.registerNumber = registerNumber; } public RegisterHandle(int registerNumber, Operation undelrlyingOp) { this.registerNumber = registerNumber; this.undelrlyingOp = undelrlyingOp; } public RegisterHandle(int registerNumber, Operation undelrlyingOp, StoreRegisterOperation storeRegisterOp) { this.registerNumber = registerNumber; this.undelrlyingOp = undelrlyingOp; this.storeRegisterOp = storeRegisterOp; } public RegisterHandle(String varName, Operation undelrlyingOp, StoreRegisterOperation storeRegisterOp) { this.varName = varName; this.undelrlyingOp = undelrlyingOp; this.storeRegisterOp = storeRegisterOp; } public int getArgsNumber() { return 0; } public String getStringValue(int level) { return this.varName==null ? "__reg" + registerNumber : this.varName; } public Boolean hasVarName() { return varName != null; } public String getVarName() { return varName; } public int getPriority() { return Priority.HIGHEST; } public int getRegisterNumber() { return registerNumber; } public void setRegisterNumber(int registerNumber) { this.registerNumber = registerNumber; } public StoreRegisterOperation getStoreRegisterOp() { return storeRegisterOp; } @Override public String toString() { return "RegisterHandle("+registerNumber+")"; } public List<Operation> getOperations() { return Collections.EMPTY_LIST; } public Operation getUndelrlyingOp() { return undelrlyingOp; } @Override public boolean equals(Object obj) { // if (obj instanceof NotOperation) { // return obj.equals(this); // invert comparison // } else { if (!(obj instanceof RegisterHandle)) { return false; } if (obj == this) { return true; } RegisterHandle otherOp = (RegisterHandle) obj; return new EqualsBuilder() .append(this.registerNumber, otherOp.registerNumber) .isEquals(); // } } public Operation getInvertedOperation() { return new SimpleInvertedOperation(this); } } public boolean skip() { return skipOperation; } }