/*
* PatternAnalyzerEx.java
* @Author Oleg Gorobets
* Created: 06.09.2007
* CVS-ID: $Id:
*************************************************************************/
package org.swfparser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.springframework.util.Assert;
import org.swfparser.exception.LabelsInitException;
import org.swfparser.exception.PatternAnalyzerException;
import org.swfparser.pattern.BreakPattern;
import org.swfparser.pattern.ContinuePattern;
import org.swfparser.pattern.DoWhilePattern;
import org.swfparser.pattern.DoWhileSkipPattern;
import org.swfparser.pattern.ForInPattern;
import org.swfparser.pattern.IfElsePattern;
import org.swfparser.pattern.IfPattern;
import org.swfparser.pattern.Pattern;
import org.swfparser.pattern.SkipPattern;
import org.swfparser.pattern.SwitchPattern;
import org.swfparser.pattern.SwitchSkipPattern;
import org.swfparser.pattern.TellTargetPattern;
import org.swfparser.pattern.WhilePattern;
import org.swfparser.pattern.WhileSkipPattern;
import org.swfparser.util.PrintfFormat;
import org.apache.log4j.Logger;
import com.jswiff.swfrecords.actions.Action;
import com.jswiff.swfrecords.actions.ActionBlock;
import com.jswiff.swfrecords.actions.ActionConstants;
import com.jswiff.swfrecords.actions.Branch;
import com.jswiff.swfrecords.actions.End;
import com.jswiff.swfrecords.actions.Enumerate;
import com.jswiff.swfrecords.actions.Enumerate2;
import com.jswiff.swfrecords.actions.Equals;
import com.jswiff.swfrecords.actions.Equals2;
import com.jswiff.swfrecords.actions.If;
import com.jswiff.swfrecords.actions.Jump;
import com.jswiff.swfrecords.actions.SetTarget;
import com.jswiff.swfrecords.actions.SetTarget2;
import com.jswiff.swfrecords.actions.StrictEquals;
public class PatternAnalyzerEx {
private static Logger logger = Logger.getLogger(PatternAnalyzerEx.class);
private static PrintfFormat actionFormat = new PrintfFormat("###A %08X: [%s] [%s] 0x%02X (%s) %s");
private static PrintfFormat offsetFormat = new PrintfFormat("0x%08X");
private PatternContext context;
private List<Action> actions;
private boolean enumerateStarted = false;
private int enumeratePointer = -1;
private static int instanceCounter = 0;
private static int labelCounter = 1;
private int startPointer;
private int endPointer;
public PatternAnalyzerEx(PatternContext context, List<Action> actions) throws LabelsInitException {
super();
this.context = context;
this.actions = actions;
this.startPointer = 0;
this.endPointer = actions.size()-1;
logger.debug("startPointer = 0, endPointer (actions.size) = " + this.endPointer);
init();
}
protected PatternAnalyzerEx(PatternContext context, List<Action> actions, int startPointer, int endPointer) throws LabelsInitException {
super();
this.context = context;
this.actions = actions;
this.startPointer = startPointer;
this.endPointer = endPointer;
init();
}
private void init() throws LabelsInitException {
instanceCounter++;
labelCounter = 1;
if (this.actions.isEmpty()) {
return;
}
appendEndActionIfNeeded();
// build labels
if (isRootBlock()) {
initLabels();
checkLabels();
}
}
private void appendEndActionIfNeeded() {
Action lastAction = this.actions.get(this.actions.size()-1);
if (! (lastAction instanceof End) ) {
End end = new End();
end.setLabel(ActionBlock.LABEL_END);
end.setOffset(lastAction.getOffset()+lastAction.getSize());
this.actions.add(end); // add virtual End action
}
}
private void initLabels() {
logger.debug("in `initLabels()`");
int pointer = 0;
for (Action action : actions) {
// logger.debug("#init() action="+action+" label = "+action.getLabel());
if (action.hasLabel()) {
// nothing...
} else if (action instanceof Branch) { // IF and JUMP actions extend `Branch`
action.setLabel(createLabel()); // set labels to all branch actions
} else if ((action instanceof SetTarget) || (action instanceof SetTarget2)) {
action.setLabel(createLabel("TELL_TARGET")); // set labels to all tellTarget() actions
} else if (action instanceof Enumerate || action instanceof Enumerate2) {
action.setLabel(createLabel("ENUMERATE"));
}
if (action.hasLabel()) {
context.getLabels().put(action.getLabel(), action); // Map `label => action`
}
Assert.isTrue(!context.getActionPointerMap().containsKey(action));
context.getActionPointerMap().put(action, pointer);
pointer++;
}
// Only print actions
for (Action action :actions) {
logger.debug(getActionDebugString(action));
}
}
private static String createLabel() {
return "JUMP_LABEL_"+ instanceCounter +"_"+(labelCounter++);
}
private static String createLabel(String prefix) {
return prefix+"_"+ instanceCounter +"_"+(labelCounter++);
}
public void checkLabels() throws LabelsInitException {
Map<String,Action> labels = new HashMap<String, Action>();
Set<String> branchLabels = new HashSet<String>();
for (Action action : actions) {
if (action.hasLabel()) {
labels.put(action.getLabel(), action);
}
if (action instanceof Branch) {
Branch branch = (Branch) action;
boolean hasBranchLabel = branch.getBranchLabel() != null;
if (hasBranchLabel) {
branchLabels.add(branch.getBranchLabel());
}
}
}
logger.debug("++++ Branch labels +++++ ");
for (String branchLabel : branchLabels) {
logger.debug("BL:"+branchLabel);
}
logger.debug("++++ Labels +++++ ");
for (String label : labels.keySet()) {
logger.debug(" L:"+label);
}
for (String branchLabel : branchLabels) {
if(! (labels.containsKey(branchLabel) || ActionBlock.LABEL_END.equals(branchLabel))) {
throw new LabelsInitException("Branch label "+branchLabel+" is not initialized.");
}
}
}
public void analyze() throws PatternAnalyzerException {
int pointer = startPointer;
while (pointer <= endPointer) {
int newPointer = pointer+1;
Action action = actions.get(pointer);
if (action.hasLabel() && !context.getPatterns().containsKey(action.getLabel())) {
if (action instanceof Branch) {
newPointer = analyzeBranch((Branch) action);
}
if (action instanceof Enumerate || action instanceof Enumerate2) {
enumerateStarted = true;
enumeratePointer = pointer;
}
if (action instanceof SetTarget) {
newPointer = analyzeSetTarget(action);
}
if (action instanceof SetTarget2) {
newPointer = analyzeSetTarget(action);
}
}
pointer = newPointer;
}
if (isRootBlock()) {
// Write results
logger.debug("###### Analyze results #########");
for (Action action : actions) {
logger.debug(getActionDebugString(action));
}
}
}
private int analyzeBranch(Branch action) throws PatternAnalyzerException {
if (action instanceof Jump) {
return analyzeJump((Jump) action);
} else if (action instanceof If) {
int ifPointer = context.getActionPointerMap().get( action );
int jumpPointer = context.getActionPointerMap().get( context.getLabels().get(action.getBranchLabel()) );
int ifOffset = actions.get(ifPointer).getOffset();
int jumpOffset = actions.get(jumpPointer).getOffset();
IfHandle ifHandle = new IfHandle((If)action, ifPointer, jumpPointer, ifOffset, jumpOffset);
return analyzeIf(ifHandle);
} else {
throw new IllegalArgumentException("Invalid branch action "+action);
}
}
/**
* @param handle
*/
private int analyzeIf(IfHandle handle) throws PatternAnalyzerException {
If action = handle.getIf();
logger.debug("Analyzing IF: "+offsetFormat.sprintf(handle.getOffset())+" - "+offsetFormat.sprintf(handle.getJumpOffset())+" label:"+action.getLabel());
logger.debug("IF pointer: "+handle.getPointer()+", jump pointer: "+handle.getJumpPointer());
if (handle.getJumpOffset() < handle.getOffset()) {
// PATTERN: DO-WHILE
logger.debug("Setting target to DO-WHILE");
List<Action> doWhileActions = actions.subList(handle.getJumpPointer(),handle.getPointer());
// context.getPatterns().put(actions.get(handle.getJumpPointer()).getLabel(),
// new SkipForDoWhilePattern(doWhileActions)
// );
DoWhilePattern doWhilePattern = new DoWhilePattern(doWhileActions);
// context.getPatterns().put( handle.getIf().getLabel(), doWhilePattern );
context.getPatterns().put( actions.get(handle.getJumpPointer()).getLabel(), doWhilePattern );
context.getPatterns().put( handle.getIf().getLabel(), new DoWhileSkipPattern() );
setBreaksForDoWhile(doWhilePattern.getActions());
// re-analyze block for "break" and "continue"
analyzeNewBlock(handle.getJumpPointer(),handle.getPointer()-1, doWhilePattern);
return handle.getPointer();
} else {
// analyze jumps
PatternJumpsInfo info = getJumpsInfo(handle);
logger.debug("Jump info: Total.jumps before = "+info.getAllJumpsBefore().size());
logger.debug("Jump info: Uncond.jumps after = "+info.getUnconditionalJumpsAfter().size()+",Cond.jump after="+info.getConditionalJumpsAfter().size());
logger.debug("Jump info: Uncond.jumps end = "+info.getUnconditionalEndJumps().size()+",Cond.jump end="+info.getConditionalEndJumps().size());
if (info.getAllJumpsBefore().size() > 0) {
// get latest jump
// if (info.getLatestJumpBefore() == actions.get(handle.getJumpPointer()-1)) {
// there should be no jumps after the block, while jumps right at the end of the block may occur
Assert.isTrue(info.getAllJumpsAfter().isEmpty());
// Evaluate the end of while/enumerate
int whileEndPointer;
String whileSkipLabel = info.getLatestJumpBefore().getLabel();
int trickyWhilePatternSizeAddon = 0;
if (info.getLatestJumpBefore() == actions.get(handle.getJumpPointer()-1)) {
// standard while
whileEndPointer = handle.getJumpPointer()-1;
// whileSkipLabel = info.getLatestJumpBefore().getLabel();
} else {
// tricky while
logger.error("Tricky while");
whileEndPointer = context.getActionPointerMap().get( info.getLatestJumpBefore() );
trickyWhilePatternSizeAddon = handle.getJumpPointer()-whileEndPointer-1;
// whileSkipLabel = info.getLatestJumpBefore().getLabel();
}
if (enumerateStarted) {
// PATTERN: FOR..IN
logger.debug("Setting target to FOR..IN");
int addedSize = handle.getPointer() - enumeratePointer + 1 + trickyWhilePatternSizeAddon;
List<Action> forInActions = actions.subList(handle.getPointer()+1, whileEndPointer);
List<Action> varActions = actions.subList(enumeratePointer+1, handle.getPointer());
ForInPattern forInPattern = new ForInPattern(forInActions,varActions,addedSize);
context.getPatterns().put( actions.get(enumeratePointer).getLabel(), forInPattern );
context.getPatterns().put(whileSkipLabel, new WhileSkipPattern());
// set break's and continues
setBreaksForDoWhile(forInActions);
// re-scan action inside for..in
analyzeNewBlock(handle.getPointer()+1, whileEndPointer-1, forInPattern);
enumerateStarted = false;
return handle.getJumpPointer();
} else {
// PATTERN: WHILE
logger.debug("Setting target to WHILE");
List<Action> whileActions = actions.subList(handle.getPointer()+1, whileEndPointer);
WhilePattern whilePattern = new WhilePattern(whileActions,trickyWhilePatternSizeAddon);
context.getPatterns().put(handle.getIf().getLabel(), whilePattern );
context.getPatterns().put(whileSkipLabel, new WhileSkipPattern());
// set break's and continues
setBreaks(whilePattern,info);
// re-scan actions inside while
analyzeNewBlock(handle.getPointer()+1, whileEndPointer-1, whilePattern);
return handle.getJumpPointer();
}
// } else {
// // skip by now, possibly "continue" inside do-while block
// logger.debug("Skipping "+info.getLatestJumpBefore().getLabel()+" by now... Maybe continue inside do-while");
// return handle.getJumpPointer();
// }
} else if (info.getAllJumpsAfter().size() > 0) {
boolean isIfElse = false;
if (info.getUnconditionalJumpsAfter().size() == 1 && info.getConditionalJumpsAfter().isEmpty()) {
// check if this jump is the last action in if block, that means we're inside if-else
Branch b = info.getUnconditionalJumpsAfter().keySet().iterator().next();
if (b == actions.get(handle.getJumpPointer()-1)) {
isIfElse = true;
// PATTERN: IF-ELSE
logger.debug("If-Else, setting target to IF-ELSE");
List<Action> ifActions = actions.subList(handle.getPointer()+1, handle.getJumpPointer()-1);
int elseEndsPointer = context.getActionPointerMap().get(info.getUnconditionalJumpsAfter().get(b));
List<Action> elseActions = actions.subList(handle.getJumpPointer(), elseEndsPointer);
IfElsePattern ifElsePattern = new IfElsePattern(ifActions, elseActions);
context.getPatterns().put(handle.getIf().getLabel(), ifElsePattern );
// re-scan if and else clause
analyzeNewBlock(handle.getPointer()+1, handle.getJumpPointer()-2, ifElsePattern);
analyzeNewBlock(handle.getJumpPointer(), elseEndsPointer-1, ifElsePattern);
return elseEndsPointer;
} else {
throw new IllegalArgumentException("Tricky if-else...");
}
}
if (!isIfElse) {
// PATTERN: SWITCH
return analyzeSwitch(handle,info);
} else {
throw new IllegalArgumentException("Tricky if-else...");
}
} else {
// PATTERN: IF
logger.debug("No jumps outside the block, setting target to IF.");
List<Action> ifActions = actions.subList(handle.getPointer()+1, handle.getJumpPointer());
IfPattern ifPattern = new IfPattern( ifActions );
context.getPatterns().put(handle.getIf().getLabel(), ifPattern );
// re-scan if
analyzeNewBlock(handle.getPointer()+1, handle.getJumpPointer()-1, ifPattern);
return handle.getJumpPointer();
}
}
}
// private void setBreaksForEnumerate(List<Action> forInActions) {
// if (!forInActions.isEmpty()) {
// Action firstAction = doWhilePattern.getActions().get(0);
// Action lastAction = doWhilePattern.getActions().get( doWhilePattern.getActions().size()-1 );
// }
//
// }
private void setBreaksForDoWhile(List<Action> actions) {
if (!actions.isEmpty()) {
Action firstAction = actions.get(0);
Action lastAction = actions.get( actions.size()-1 );
for (Action action : actions) {
if (action instanceof Jump) {
Action jmpAction = context.getLabels().get( ((Jump)action).getBranchLabel() );
if (jmpAction == firstAction) {
// PATTERN: CONTINUE
logger.debug("Setting "+action.getLabel()+" to CONTINUE");
context.getPatterns().put(action.getLabel(), new ContinuePattern());
}
if (jmpAction.getOffset() > lastAction.getOffset()) {
// PATTERN: BREAK
logger.debug("Setting "+action.getLabel()+" to BREAK");
// context.getPatterns().put(action.getLabel(), new BreakPattern());
context.getPatterns().put(action.getLabel(), new ContinuePattern());
}
}
}
}
}
private void setBreaks(WhilePattern whilePattern, PatternJumpsInfo info) {
for (Branch jumpBefore : info.getAllJumpsBefore().keySet()) {
if (jumpBefore != info.getLatestJumpBefore()) {
// PATTERN: CONTINUE
logger.debug("Setting "+jumpBefore.getLabel()+" to CONTINUE");
context.getPatterns().put(jumpBefore.getLabel(), new ContinuePattern());
}
}
for (Branch jumpEnd : info.getUnconditionalEndJumps().keySet()) {
// PATTERN: BREAK
logger.debug("Setting "+jumpEnd.getLabel()+" to BREAK");
context.getPatterns().put(jumpEnd.getLabel(), new BreakPattern());
}
}
private PatternJumpsInfo getJumpsInfo(IfHandle handle) {
int ifPointer = handle.getPointer();
int jumpPointer = handle.getJumpPointer();
int startOffset = handle.getOffset();
int endOffset = handle.getJumpOffset();
Map<String, Action> labels = context.getLabels();
Map<Branch,Action> jumpsBefore = new LinkedHashMap<Branch,Action>();
Map<Branch,Action> conditionalJumpsAfter = new LinkedHashMap<Branch,Action>();
Map<Branch,Action> unconditionalJumpsAfter = new LinkedHashMap<Branch,Action>();
Map<Branch,Action> allJumpsAfter = new LinkedHashMap<Branch,Action>();
Map<Branch,Action> conditionalEndJumps = new LinkedHashMap<Branch,Action>();
Map<Branch,Action> unconditionalEndJumps= new LinkedHashMap<Branch,Action>();
Action latestJumpBefore = null;
for (int pointer = ifPointer+1; pointer<jumpPointer; pointer++) {
Action action = actions.get(pointer);
// count only unlabeled jumps
if (context.getPatterns().containsKey(action.getLabel())) {
continue;
}
if (action instanceof Jump) {
Jump a = (Jump) action;
Action jumpDestination = labels.get(a.getBranchLabel());
int jOffset = jumpDestination.getOffset();
if (jOffset <= startOffset) {
jumpsBefore.put(a, jumpDestination);
latestJumpBefore = a;
}
if (jOffset == endOffset) {
unconditionalEndJumps.put(a, jumpDestination);
}
if (jOffset > endOffset) {
unconditionalJumpsAfter.put(a,jumpDestination);
allJumpsAfter.put(a,jumpDestination);
}
}
if (action instanceof If) {
If a = (If) action;
Action jumpDestination = labels.get(a.getBranchLabel());
if (ActionBlock.LABEL_END.equals(a.getBranchLabel())) {
jumpDestination = actions.get(jumpPointer);
}
int jOffset = jumpDestination.getOffset();
if (jOffset <= startOffset) {
jumpsBefore.put(a, jumpDestination);
latestJumpBefore = a;
}
if (jOffset == endOffset) {
conditionalEndJumps.put(a, jumpDestination);
}
if (jOffset > endOffset) {
conditionalJumpsAfter.put(a,jumpDestination);
allJumpsAfter.put(a,jumpDestination);
}
}
}
PatternJumpsInfo info = new PatternJumpsInfo();
info.setAllJumpsBefore(jumpsBefore);
info.setConditionalJumpsAfter(conditionalJumpsAfter);
info.setUnconditionalJumpsAfter(unconditionalJumpsAfter);
info.setConditionalEndJumps(conditionalEndJumps);
info.setUnconditionalEndJumps(unconditionalEndJumps);
info.setLatestJumpBefore(latestJumpBefore);
info.setAllJumpsAfter(allJumpsAfter);
return info;
}
private int analyzeJump(Jump action) {
Action jumpAction = context.getLabels().get(action.getBranchLabel());
if (jumpAction.getOffset() > action.getOffset()) {
if (insideWhile()) {
logger.debug("Setting "+action.getLabel()+" to BREAK");
context.getPatterns().put(action.getLabel(), new BreakPattern());
} else {
logger.debug("Setting "+action.getLabel()+" to SkipPattern");
int startPointer = context.getActionPointerMap().get(action) + 1;
int endPointer = context.getActionPointerMap().get(jumpAction);
context.getPatterns().put(action.getLabel(), new SkipPattern(actions.subList(startPointer, endPointer)));
}
}
return context.getActionPointerMap().get( action ) + 1; // next action
}
public void analyze(boolean discardAllPatterns) throws PatternAnalyzerException {
for (int j=startPointer; j<=endPointer; j++) {
Action action = actions.get(j);
// do not remove BreakPattern and ContinuePattern, they are set from outer block and shouldn't be removed
if (context.getPatterns().containsKey(action.getLabel())
&& !(context.getPatterns().get(action.getLabel()) instanceof BreakPattern
|| context.getPatterns().get(action.getLabel()) instanceof ContinuePattern
|| (insideDoWhile() && j==startPointer && context.getPatterns().get(action.getLabel()) instanceof DoWhilePattern) )
) {
context.getPatterns().remove(action.getLabel());
}
}
analyze();
}
// private boolean insideWhile() {
// return !context.getStack().isEmpty() && context.getStack().peek().getClass().equals( WhilePattern.class );
// }
private boolean insideWhile() {
boolean insideWhile = false;
for (int j=context.getStack().size()-1; j>=0; j--) {
Pattern p = context.getStack().get(j);
if (p.getClass().equals( WhilePattern.class )) {
insideWhile = true;
break;
}
}
return insideWhile;
}
private boolean insideDoWhile() {
return !context.getStack().isEmpty() && context.getStack().peek().getClass().equals( DoWhilePattern.class );
}
private boolean insideEnumerate() {
return !context.getStack().isEmpty() && context.getStack().peek().getClass().equals( ForInPattern.class );
}
private boolean insideLoop() {
return insideWhile() || insideDoWhile();
}
private int analyzeSwitch(IfHandle handle, PatternJumpsInfo info) throws PatternAnalyzerException {
If ifAction = handle.getIf();
Map<String, Pattern> patterns = context.getPatterns();
Map<String, Action> labels = context.getLabels();
Map<Action, Integer> actionPointerMap = context.getActionPointerMap();
boolean jmpIsTheLastAction = false;
if (info.getUnconditionalJumpsAfter().size() == 1) {
Action jumpAfterAction = info.getUnconditionalJumpsAfter().keySet().iterator().next();
if (jumpAfterAction == actions.get(handle.getJumpPointer()-1)) {
jmpIsTheLastAction = true;
}
}
if (jmpIsTheLastAction) {
logger.debug("Check 1 passed. 1 jmp at the end of the block...");
// check all conditional jumps, there should be EQUALS action before each
boolean eqTest = true;
for (Map.Entry<Branch, Action> cJmpAfterEntry : info.getConditionalJumpsAfter().entrySet()) {
int conditionalJumpPointer = context.getActionPointerMap().get(cJmpAfterEntry.getKey());
if (conditionalJumpPointer>=1) {
Action prevAction = actions.get(conditionalJumpPointer-1);
if (prevAction instanceof Equals || prevAction instanceof Equals2 || prevAction instanceof StrictEquals) {
// continue
} else {
eqTest = false;
break;
}
} else {
eqTest = false;
break;
}
}
if (eqTest) {
logger.debug("Check 2 passed. EQ... Setting to SWITCH to "+ifAction.getLabel());
Jump unconditionalJump = (Jump) info.getUnconditionalJumpsAfter().keySet().iterator().next();
logger.debug("Setting SWITCH-SKIP pattern to "+unconditionalJump.getLabel());
patterns.put(unconditionalJump.getLabel(), new SwitchSkipPattern());
Action endOfSwitchAction = labels.get( unconditionalJump.getBranchLabel() );
//
// check switch conditions actions
//
List<Branch> jumps = new ArrayList<Branch>();
jumps.add( ifAction );
jumps.addAll( info.getConditionalJumpsAfter().keySet() );
List<List<Action>> conditionBlocks = new ArrayList<List<Action>>();
logger.debug("Condition blocks:");
for (int j=0; j<jumps.size()-1; j++) {
Branch a = jumps.get(j);
int startPointer = actionPointerMap.get(a) + 1;
int endPointer = actionPointerMap.get(jumps.get(j+1)) - 1;
logger.debug(startPointer + "("+offsetFormat.sprintf( actions.get(startPointer).getOffset() )
+") --- "+endPointer+"("+offsetFormat.sprintf( actions.get(endPointer).getOffset() )+")");
conditionBlocks.add(actions.subList(startPointer, endPointer+1));
}
for (Branch brnch : jumps) {
logger.debug("Setting SWITCH-SKIP pattern to "+brnch.getLabel());
patterns.put(brnch.getLabel(), new SwitchSkipPattern());
}
//
// check switch statements
//
List<Action> jumpDestinations = new ArrayList<Action>();
jumpDestinations.add( labels.get( ifAction.getBranchLabel() ) );
jumpDestinations.addAll( info.getConditionalJumpsAfter().values() );
List<List<Action>> switchBlocks = new ArrayList<List<Action>>();
logger.debug("Switch blocks:");
Action realEndOfSwitch = endOfSwitchAction; // if there is a default block, this should be after the endOfSwitchAction
boolean hasDefaultBlock = false;
for (int j=0; j<jumpDestinations.size(); j++) {
Action a = jumpDestinations.get(j);
int startPointer = actionPointerMap.get(a);
int endPointer = (j<jumpDestinations.size()-1) ? actionPointerMap.get(jumpDestinations.get(j+1)) - 1 : actionPointerMap.get( endOfSwitchAction ) - 1;
logger.debug(startPointer + "("+offsetFormat.sprintf( actions.get(startPointer).getOffset() )
+") --- "+endPointer+"("+offsetFormat.sprintf( actions.get(endPointer).getOffset() )+")");
// analyze the last action in switch statement block
if (actions.get(endPointer) instanceof Jump) {
Jump jump = (Jump) actions.get(endPointer);
if (labels.get(jump.getBranchLabel()) == endOfSwitchAction) {
patterns.put(jump.getLabel(), new BreakPattern());
} else {
int jOffset = labels.get(jump.getBranchLabel()).getOffset();
if (jOffset > endOfSwitchAction.getOffset()) {
hasDefaultBlock = true;
if (realEndOfSwitch != null) {
if (jOffset > realEndOfSwitch.getOffset()) {
realEndOfSwitch = labels.get(jump.getBranchLabel());
}
} else {
realEndOfSwitch = labels.get(jump.getBranchLabel());
}
} else {
// jump to the next switch state, NO "break" at the end of this switch state
}
}
}
switchBlocks.add(actions.subList(startPointer, endPointer+1));
}
logger.debug("Has default block: "+hasDefaultBlock);
logger.debug("End 1: "+getActionDebugString(endOfSwitchAction));
logger.debug("End 2: "+(realEndOfSwitch != null ? getActionDebugString(realEndOfSwitch) : "null"));
List<Action> defaultActions = new ArrayList<Action>();
if (hasDefaultBlock && realEndOfSwitch!=null && realEndOfSwitch.getOffset() > endOfSwitchAction.getOffset()) {
defaultActions = actions.subList(actionPointerMap.get(endOfSwitchAction), actionPointerMap.get(realEndOfSwitch));
}
int switchPatternSize = actionPointerMap.get( realEndOfSwitch ) - actionPointerMap.get( ifAction ) - 1;
SwitchPattern switchPattern = new SwitchPattern(conditionBlocks,switchBlocks,defaultActions,switchPatternSize);
patterns.put( ifAction.getLabel(), switchPattern );
logger.debug("Re-scanning switch blocks...");
for (List<Action> switchActionBlock : switchBlocks) {
if (!switchActionBlock.isEmpty()) {
int actionBlockStartPointer = context.getActionPointerMap().get(switchActionBlock.get(0));
int actionBlockEndPointer = context.getActionPointerMap().get(switchActionBlock.get(switchActionBlock.size()-1));
analyzeNewBlock(actionBlockStartPointer, actionBlockEndPointer, switchPattern);
}
}
if (hasDefaultBlock && !defaultActions.isEmpty()) {
logger.debug("Re-scanning default block");
analyzeNewBlock(context.getActionPointerMap().get(endOfSwitchAction), context.getActionPointerMap().get(realEndOfSwitch), switchPattern);
}
// mark all jumps inside switch block as switch-skip pattern
// int startP = actionPointerMap.get( ifAction );
// int endP = actionPointerMap.get( realEndOfSwitch );
// for (int j=startP; j<endP; j++) {
// Action a = actions.get(j);
// if (a instanceof Branch) {
// Branch b = (Branch) a;
// if (!patterns.containsKey(b.getLabel())) {
// logger.debug("Added SWITCH-SKIP pattern for "+b.getLabel());
// patterns.put(b.getLabel(), new SwitchSkipPattern());
// }
// }
// }
// throw new RuntimeException("No handle");
return actionPointerMap.get( realEndOfSwitch );
} // eqTest
} // jmpIsTheLastAction
throw new IllegalArgumentException("Unknown branch type, possibly tricky if-else...");
}
// private void analyzeNewBlock(List<Action> actions, Pattern pattern) {
// context.getStack().push(pattern);
// new PatternAnalyzerEx(context,actions).analyze(true);
// context.getStack().pop();
// }
private void analyzeNewBlock(int startPointer, int endPointer, Pattern pattern) throws PatternAnalyzerException {
String blockDumpString = offsetFormat.sprintf(actions.get(startPointer).getOffset())+" - "+offsetFormat.sprintf(actions.get(endPointer).getOffset());
logger.debug("Re-scanning actions "+blockDumpString);
context.getStack().push(pattern);
try {
new PatternAnalyzerEx(this.context,this.actions,startPointer,endPointer).analyze(true);
} catch (LabelsInitException e) {
throw new PatternAnalyzerException("Error while re-scanning block "+blockDumpString);
}
context.getStack().pop();
}
private String getActionDebugString(Action action) {
String actionInfo = "";
if (action instanceof Branch) {
Branch brnch = (Branch) action;
Action jumpDestination = context.getLabels().get(brnch.getBranchLabel());
actionInfo += brnch.getBranchLabel() +"("+ ( jumpDestination!=null ? offsetFormat.sprintf( jumpDestination.getOffset() ) : "[null]")+")";
actionInfo += ", branch offset = "+brnch.getBranchOffset();
}
return actionFormat.sprintf(new Object[]{
action.getOffset(),
(context.getPatterns().containsKey(action.getLabel())) ? context.getPatterns().get(action.getLabel()).getClass().getSimpleName() : "",
(action.getLabel()==null)?"":action.getLabel(),
action.getCode(),
ActionConstants.getActionName(action.getCode()),
actionInfo});
}
public void clearBranchPattern(String label) {
context.getPatterns().remove(label);
}
public Pattern getPatternByLabel(String label) {
return context.getPatterns().get(label);
}
public boolean isRootBlock() {
return context.getStack().isEmpty();
}
public Map<String, Action> getLabels() {
return context.getLabels();
}
private boolean jumpOutsideLoop(int pointer) {
boolean r = false;
for (int j=context.getStack().size()-1; j>=0; j--) {
Pattern p = context.getStack().get(j);
if (p instanceof WhilePattern) {
WhilePattern whilePattern = (WhilePattern) p;
Action lastAction = whilePattern.getActions().get(whilePattern.getActions().size()-1);
int whileEnd = context.getActionPointerMap().get( lastAction ) + 2;
if (pointer == whileEnd) {
r = true;
break;
} else {
r = false;
break;
}
}
}
return r;
}
private boolean isNestedTarget() {
boolean isNested = false;
for (int j=context.getStack().size()-1; j>=0; j--) {
Pattern p = context.getStack().get(j);
if (p instanceof TellTargetPattern) {
isNested = true;
break;
}
}
return isNested;
}
private int analyzeSetTarget(Action t) throws PatternAnalyzerException {
logger.debug("Analyzing set-target "+t);
Stack<Action> targets = new Stack<Action>();
targets.push(t);
int setTargetPointer = context.getActionPointerMap().get(t);
int setTragetEndPointer = -1;
for (int pointer = setTargetPointer+1; pointer<=endPointer;) {
Action action = actions.get(pointer);
if (action instanceof SetTarget) {
SetTarget setTarget = (SetTarget) action;
if ("".equals(setTarget.getName())) {
// end of target
targets.pop();
boolean endOfNestedTarget = !targets.isEmpty();
if (endOfNestedTarget) {
pointer++;
} else {
// this is our target
logger.debug("Setting target to TELLTARGET");
setTragetEndPointer = pointer;
break;
}
} else {
targets.push(action);
}
} else if (action instanceof SetTarget2) {
targets.push(action);
}
pointer++;
}
if (setTragetEndPointer!=-1) {
TellTargetPattern tellTargetPattern = new TellTargetPattern(actions.subList(setTargetPointer+1, setTragetEndPointer), isNestedTarget());
context.getPatterns().put(t.getLabel(), tellTargetPattern);
analyzeNewBlock(setTargetPointer+1, setTragetEndPointer-1, tellTargetPattern);
return setTragetEndPointer+1;
} else {
logger.error("No end of tellTarget found. Add all rest actions..");
List<Action> tellTargetActions = actions.subList(setTargetPointer+1, endPointer+1);
TellTargetPattern tellTargetPattern = new TellTargetPattern(tellTargetActions,isNestedTarget());
context.getPatterns().put(t.getLabel(), tellTargetPattern);
analyzeNewBlock(setTargetPointer+1, endPointer, tellTargetPattern);
return endPointer+1;
}
}
}