diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index ba0a7769e..4c259920b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -1770,193 +1770,191 @@ public class AVM2Code implements Cloneable { return list; } + public void updateOffsets(OffsetUpdater updater, MethodBody body) { + for (AVM2Instruction ins : code) { + if (ins.definition instanceof LookupSwitchIns) { + long target = ins.offset + ins.operands[0]; + ins.operands[0] = updater.updateOperandOffset(target, ins.operands[0]); + for (int k = 2; k < ins.operands.length; k++) { + target = ins.offset + ins.operands[k]; + ins.operands[k] = updater.updateOperandOffset(target, ins.operands[k]); + } + } else { + /*for (int j = 0; j < ins.definition.operands.length; j++) { + if (ins.definition.operands[j] == AVM2Code.DAT_OFFSET) { + long target = ins.offset + ins.getBytes().length + ins.operands[j]; + ins.operands[j] = updater.updateOperandOffset(target, ins.operands[j]); + } + }*/ + //Faster, but not so universal + if ((ins.definition instanceof JumpIns) || (ins.definition instanceof IfTypeIns)) { + long target = ins.offset + ins.getBytes().length + ins.operands[0]; + ins.operands[0] = updater.updateOperandOffset(target, ins.operands[0]); + } + } + } + + for (ABCException ex : body.exceptions) { + ex.start = updater.updateOperandOffset(ex.start, ex.start); + ex.end = updater.updateOperandOffset(ex.end, ex.end); + ex.target = updater.updateOperandOffset(ex.target, ex.target); + } + for (AVM2Instruction ins : code) { + ins.offset = updater.updateInstructionOffset(ins.offset); + } + } + + private void checkValidOffsets(MethodBody body) { + updateOffsets(new OffsetUpdater() { + + @Override + public long updateInstructionOffset(long offset) { + adr2pos(offset); + return offset; + } + + @Override + public int updateOperandOffset(long targetAddress, int offset) { + adr2pos(targetAddress); + return offset; + } + + }, body); + } + public void removeInstruction(int pos, MethodBody body) { if ((pos < 0) || (pos >= code.size())) { throw new IndexOutOfBoundsException(); } - int byteCount = code.get(pos).getBytes().length; - long remOffset = code.get(pos).offset; - for (int i = pos + 1; i < code.size(); i++) { - code.get(i).offset -= byteCount; - } + //checkValidOffsets(body); + final long remOffset = code.get(pos).offset; + final int byteCount = code.get(pos).getBytes().length; + updateOffsets(new OffsetUpdater() { + @Override + public long updateInstructionOffset(long address) { + if (address > remOffset) { + return address - byteCount; + } + return address; + } - for (ABCException ex : body.exceptions) { - if (ex.start > remOffset) { - ex.start -= byteCount; + @Override + public int updateOperandOffset(long targetAddress, int offset) { + if (targetAddress > remOffset) { + return offset - byteCount; + } + return offset; } - if (ex.end > remOffset) { - ex.end -= byteCount; - } - if (ex.target > remOffset) { - ex.target -= byteCount; - } - } - - for (int i = 0; i < pos; i++) { - if (code.get(i).definition instanceof LookupSwitchIns) { - long target = code.get(i).offset + code.get(i).operands[0]; - if (target > remOffset) { - code.get(i).operands[0] -= byteCount; - } - for (int k = 2; k < code.get(i).operands.length; k++) { - target = code.get(i).offset + code.get(i).operands[k]; - if (target > remOffset) { - code.get(i).operands[k] -= byteCount; - } - } - } else { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target > remOffset) { - code.get(i).operands[j] -= byteCount; - } - } - } - } - } - for (int i = pos + 1; i < code.size(); i++) { - if (code.get(i).definition instanceof LookupSwitchIns) { - long target = code.get(i).offset + code.get(i).operands[0]; - if (target < remOffset) { - code.get(i).operands[0] += byteCount; - } - for (int k = 2; k < code.get(i).operands.length; k++) { - target = code.get(i).offset + code.get(i).operands[k]; - if (target < remOffset) { - code.get(i).operands[k] += byteCount; - } - } - } else { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target < remOffset) { - code.get(i).operands[j] += byteCount; - } - } - } - } - } - + }, body); code.remove(pos); invalidateCache(); + //checkValidOffsets(body); + //System.exit(0); + } /** - * @param pos - * @param instruction + * Inserts instuction at specified point. Handles offsets properly. Note: If + * newinstruction is jump, the offset operand must be handled properly by + * caller. All old jump offsets to pos are targeted before new instruction. + * + * @param pos Position in the list + * @param instruction Instruction False means before new instruction + * @param body Method body (used for try handling) */ public void insertInstruction(int pos, AVM2Instruction instruction, MethodBody body) { - insertInstruction(pos, instruction, true, false, body); + insertInstruction(pos, instruction, false, body); } - public void replaceInstruction(int idx, AVM2Instruction ins, MethodBody body) { - insertInstruction(idx, ins, true, true, body); - removeInstruction(idx + 1, body); - } - - public void insertInstruction(int pos, AVM2Instruction instruction, boolean preRefsToThis, boolean postRefsToThis, MethodBody body) { + /** + * Replaces instrunction by another. Properly handles offsets. Note: If + * newinstruction is jump, the offset operand must be handled properly by + * caller. + * + * @param pos + * @param instruction + * @param body + */ + public void replaceInstruction(int pos, AVM2Instruction instruction, MethodBody body) { if (pos < 0) { pos = 0; } if (pos > code.size()) { pos = code.size(); } - int byteCount = instruction.getBytes().length; + instruction.offset = code.get(pos).offset; + int oldByteCount = code.get(pos).getBytes().length; + int newByteCount = instruction.getBytes().length; + int byteDelta = newByteCount - oldByteCount; + + if (byteDelta != 0) { + updateOffsets(new OffsetUpdater() { + + @Override + public long updateInstructionOffset(long offset) { + if (offset > instruction.offset) { + return offset + byteDelta; + } + return offset; + } + + @Override + public int updateOperandOffset(long targetAddress, int offset) { + if (targetAddress > instruction.offset) { + return offset + byteDelta; + } + return offset; + } + }, body); + } + code.set(pos, instruction); + } + + /** + * Inserts instuction at specified point. Handles offsets properly. Note: If + * newinstruction is jump, the offset operand must be handled properly by + * caller. + * + * @param pos Position in the list + * @param instruction Instruction + * @param mapOffsetsAfterIns Map all jumps to the pos after new instruction? + * False means before new instruction + * @param body Method body (used for try handling) + */ + public void insertInstruction(int pos, AVM2Instruction instruction, boolean mapOffsetsAfterIns, MethodBody body) { + if (pos < 0) { + pos = 0; + } + if (pos > code.size()) { + pos = code.size(); + } + final int byteCount = instruction.getBytes().length; if (pos == code.size()) { instruction.offset = code.get(pos - 1).offset + code.get(pos - 1).getBytes().length; } else { instruction.offset = code.get(pos).offset; } + updateOffsets(new OffsetUpdater() { - for (ABCException ex : body.exceptions) { - if (ex.start > instruction.offset) { - ex.start += byteCount; - } - if (ex.end > instruction.offset) { - ex.end += byteCount; - } - if (ex.target > instruction.offset) { - ex.target += byteCount; - } - } - - { - for (int i = 0; i < pos; i++) { - if (code.get(i).definition instanceof LookupSwitchIns) { - long target = code.get(i).offset + code.get(i).operands[0]; - if (target > instruction.offset) { - code.get(i).operands[0] += byteCount; - } - if (target == instruction.offset && !preRefsToThis) { - code.get(i).operands[0] += byteCount; - } - for (int k = 2; k < code.get(i).operands.length; k++) { - target = code.get(i).offset + code.get(i).operands[k]; - if (target > instruction.offset) { - code.get(i).operands[k] += byteCount; - } - if (target == instruction.offset && !preRefsToThis) { - code.get(i).operands[k] += byteCount; - } - } - } else { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target > instruction.offset) { - code.get(i).operands[j] += byteCount; - } - if (target == instruction.offset && !preRefsToThis) { - code.get(i).operands[j] += byteCount; - } - - } - } + @Override + public long updateInstructionOffset(long offset) { + if (offset >= instruction.offset) { + return offset + byteCount; } + return offset; } - } - { - for (int i = pos; i < code.size(); i++) { - if (code.get(i).definition instanceof LookupSwitchIns) { - long target = code.get(i).offset + code.get(i).operands[0]; - if (target < instruction.offset) { - code.get(i).operands[0] -= byteCount; - } - if (target == instruction.offset && postRefsToThis) { - code.get(i).operands[0] -= byteCount; - } - for (int k = 2; k < code.get(i).operands.length; k++) { - target = code.get(i).offset + code.get(i).operands[k]; - if (target < instruction.offset) { - code.get(i).operands[k] -= byteCount; - } - if (target == instruction.offset && postRefsToThis) { - code.get(i).operands[k] -= byteCount; - } - } - } else { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target < instruction.offset) { - code.get(i).operands[j] -= byteCount; - } - if (target == instruction.offset && postRefsToThis) { - code.get(i).operands[j] -= byteCount; - } - } - } - } - } - } - for (int i = pos; i < code.size(); i++) { - code.get(i).offset += byteCount; - } + @Override + public int updateOperandOffset(long targetAddress, int offset) { + if ((targetAddress > instruction.offset) || (mapOffsetsAfterIns && (targetAddress == instruction.offset))) { + return offset + byteCount; + } + return offset; + } + }, body); code.add(pos, instruction); invalidateCache(); + checkValidOffsets(body); } @SuppressWarnings("unchecked") @@ -2524,6 +2522,30 @@ public class AVM2Code implements Cloneable { restoreControlFlowPass(constants, trait, info, body, false); //restoreControlFlowPass(constants, body, true); } + /* + public void removeIgnored(AVM2ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { + try { + List outputMap = new ArrayList<>(); + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false); + toASMSource(constants, trait, info, body, outputMap, ScriptExportMode.PCODE, writer); + String src = writer.toString(); + AVM2Code acode = ASM3Parser.parse(new StringReader(src), constants, trait, body, info); + for (int i = 0; i < acode.code.size(); i++) { + if (outputMap.size() > i) { + int tpos = outputMap.get(i); + if (tpos == -1) { + } else if (code.get(tpos).mappedOffset >= 0) { + acode.code.get(i).mappedOffset = code.get(tpos).mappedOffset; + } else { + acode.code.get(i).mappedOffset = pos2adr(tpos); + } + } + } + this.code = acode.code; + } catch (IOException | AVM2ParseException ex) { + } + invalidateCache(); + }*/ public void removeIgnored(AVM2ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { for (int i = 0; i < code.size(); i++) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java new file mode 100644 index 000000000..66d4ecbe9 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/OffsetUpdater.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc.avm2; + +/** + * + * @author JPEXS + */ +public interface OffsetUpdater { + + public long updateInstructionOffset(long offset); + + public int updateOperandOffset(long targetAddress, int offset); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java index 397c659a1..8c0bfea1c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/deobfuscation/AVM2DeobfuscatorSimple.java @@ -164,67 +164,50 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { ExecutionResult result = new ExecutionResult(); executeActions(classIndex, isStatic, body, scriptIndex, abc, code, i, code.code.size() - 1, result); - if (result.idx != -1) { - int newIstructionCount = 1; // jump - if (!result.stack.isEmpty()) { - newIstructionCount += result.stack.size(); - } + /*if (result.idx != -1) { + int newIstructionCount = 1; // jump + if (!result.stack.isEmpty()) { + newIstructionCount += result.stack.size(); + } - if (newIstructionCount < result.instructionsProcessed) //if (result.isIf) - { - AVM2Instruction target = code.code.get(result.idx); - AVM2Instruction prevAction = code.code.get(i); - int idelta = 0; + if (newIstructionCount < result.instructionsProcessed) //if (result.isIf) + { + AVM2Instruction target = code.code.get(result.idx); + AVM2Instruction prevAction = code.code.get(i); + int idelta = 0; - if (result.stack.isEmpty() && prevAction.definition instanceof JumpIns) { - prevAction.operands[0] = ((int) (target.offset - prevAction.offset - prevAction.getBytes().length)); - } else { - if (!result.stack.isEmpty()) { - for (GraphTargetItem graphTargetItem : result.stack) { - if (graphTargetItem instanceof PopItem) { - continue; - } - AVM2Instruction ins = makePush(graphTargetItem.getResult(), cpool); - if (ins != null) { - code.insertInstruction(i + (idelta++), ins, body); - //prevAction = ins; - } else { - throw new TranslateException("Cannot push: " + graphTargetItem); - } + if (result.stack.isEmpty() && prevAction.definition instanceof JumpIns) { + prevAction.operands[0] = ((int) (target.offset - prevAction.offset - prevAction.getBytes().length)); + } else { + if (!result.stack.isEmpty()) { + for (GraphTargetItem graphTargetItem : result.stack) { + if (graphTargetItem instanceof PopItem) { + continue; + } + AVM2Instruction ins = makePush(graphTargetItem.getResult(), cpool); + if (ins != null) { + code.insertInstruction(i + (idelta++), ins, body); + //prevAction = ins; + } else { + throw new TranslateException("Cannot push: " + graphTargetItem); + } - } - } + } + } - AVM2Instruction jump = new AVM2Instruction(0, new JumpIns(), new int[]{0}); - code.insertInstruction(i + (idelta++), jump, body); + AVM2Instruction jump = new AVM2Instruction(0, new JumpIns(), new int[]{0}); + code.insertInstruction(i + (idelta++), jump, body); - jump.operands[0] = ((int) (target.offset - jump.offset - jump.getBytes().length)); + jump.operands[0] = ((int) (target.offset - jump.offset - jump.getBytes().length)); - } + } - removeUnreachableActions(code, cpool, trait, minfo, body); - removeZeroJumps(code, body); + removeUnreachableActions(code, cpool, trait, minfo, body); + removeZeroJumps(code, body); - i = -1; - /*if (nextAction != null) { - long mapped = nextAction.mappedOffset; - int nextIdx = -1; - for (int p = 0; p < code.code.size(); p++) { - if (code.code.get(p).mappedOffset == mapped) { - nextIdx = p; - break; - } - } - if (nextIdx == -1) { - //? - break; - } else { - i = nextIdx - 1; - } - - }*/ - } - } + i = -1; + } + }*/ } return false; @@ -340,7 +323,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { break; } - //boolean ifed = false; + boolean ifed = false; if (def instanceof JumpIns) { //ActionJump jump = (ActionJump) action; long address = action.offset + action.getBytes().length + action.operands[0]; @@ -353,18 +336,29 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { //ActionIf aif = (ActionIf) action; GraphTargetItem top = stack.pop(); Object res = top.getResult(); + long address = action.offset + action.getBytes().length + action.operands[0]; + int nidx = code.adr2pos(address);//code.indexOf(code.getByAddress(address)); + AVM2Instruction tarIns = code.code.get(nidx); + if (EcmaScript.toBoolean(res)) { - long address = action.offset + action.getBytes().length + action.operands[0]; - idx = code.adr2pos(address);//code.indexOf(code.getByAddress(address)); - if (idx == -1) { - throw new TranslateException("If target not found: " + address); - } - //ifed = true; + /*if (nidx == -1) { + throw new TranslateException("If target not found: " + address); + }*/ + AVM2Instruction jumpIns = new AVM2Instruction(0, new JumpIns(), new int[]{0}); + //jumpIns.operands[0] = action.operands[0] /*- action.getBytes().length*/ + jumpIns.getBytes().length; + code.replaceInstruction(idx, jumpIns, body); + jumpIns.operands[0] = (int) (tarIns.offset - jumpIns.offset - jumpIns.getBytes().length); + + code.insertInstruction(idx, new AVM2Instruction(action.offset, new DeobfuscatePopIns(), new int[]{}), true, body); + + idx = code.adr2pos(jumpIns.offset + jumpIns.getBytes().length + jumpIns.operands[0]); } else { - //action.definition = new DeobfuscatePopIns(); code.replaceInstruction(idx, new AVM2Instruction(action.offset, new DeobfuscatePopIns(), new int[]{}), body); + //action.definition = new DeobfuscatePopIns(); idx++; } + ifed = true; + //break; } else { idx++; } @@ -377,7 +371,9 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener { result.stack.clear(); result.stack.addAll(stack); } - + if (ifed) { + break; + } } } catch (EmptyStackException | TranslateException | InterruptedException ex) { //result.idx = -1; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java index eff50d8c5..2d9a0c447 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java @@ -167,7 +167,7 @@ public class AVM2Graph extends Graph { } @Override - protected List check(GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { + protected List check(Map> partCodes, Map partCodePos, GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { List ret = null; AVM2LocalData aLocalData = (AVM2LocalData) localData; @@ -277,7 +277,7 @@ public class AVM2Graph extends Graph { finallyJumps.clear(); ignoredSwitches.put(e, swPos); st.push(new PopItem(null)); - finallyCommands = printGraph(localData, st, allParts, parent, fpart, null, loops, staticOperation, path); + finallyCommands = printGraph(partCodes, partCodePos, localData, st, allParts, parent, fpart, null, loops, staticOperation, path); //ignoredSwitches.remove(igs_size-1); finallyJumps.putAll(oldFinallyJumps); if (!finallyJumps.containsKey(e)) { @@ -336,7 +336,7 @@ public class AVM2Graph extends Graph { stopPart2.add(retPart); } - List ncatchedCommands = printGraph(localData2, st2, allParts, parent, npart, stopPart2, loops, staticOperation, path); + List ncatchedCommands = printGraph(partCodes, partCodePos, localData2, st2, allParts, parent, npart, stopPart2, loops, staticOperation, path); if (catchedExceptions.get(e).isFinally() && (catchedExceptions.size() > 1 || hasFinally)) { catchedExceptions.remove(e); e--; @@ -376,7 +376,7 @@ public class AVM2Graph extends Graph { } TranslateStack st = (TranslateStack) stack.clone(); st.clear(); - List tryCommands = printGraph(localData, st, allParts, parent, part, stopPart2, loops, staticOperation, path); + List tryCommands = printGraph(partCodes, partCodePos, localData, st, allParts, parent, part, stopPart2, loops, staticOperation, path); if (retPart != null && avm2code.code.get(retPart.start).isExit() && !(!tryCommands.isEmpty() && (tryCommands.get(tryCommands.size() - 1) instanceof ExitItem))) { avm2code.code.get(retPart.start).translate(localData, st, tryCommands, staticOperation, path); } @@ -413,7 +413,7 @@ public class AVM2Graph extends Graph { TranslateStack st = (TranslateStack) stack.clone(); st.clear(); - ret.addAll(printGraph(localData, st, allParts, null, part, stopPart, loops, staticOperation, path)); + ret.addAll(printGraph(partCodes, partCodePos, localData, st, allParts, null, part, stopPart, loops, staticOperation, path)); } else { ret.add(lop); } @@ -603,7 +603,7 @@ public class AVM2Graph extends Graph { defaultPart = switchLoc.nextParts.get(switchLoc.nextParts.size() - 1); List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(next); - defaultCommands = printGraph(localData, stack, allParts, switchLoc, defaultPart, stopPart2, loops, staticOperation, path); + defaultCommands = printGraph(partCodes, partCodePos, localData, stack, allParts, switchLoc, defaultPart, stopPart2, loops, staticOperation, path); if (!defaultCommands.isEmpty()) { if (defaultCommands.get(defaultCommands.size() - 1) instanceof BreakItem) { if (((BreakItem) defaultCommands.get(defaultCommands.size() - 1)).loopId == currentLoop.id) { @@ -630,7 +630,7 @@ public class AVM2Graph extends Graph { stopPart2.add(defaultPart); } - cc.addAll(0, printGraph(localData, stack, allParts, switchLoc, caseBodies.get(i), stopPart2, loops, staticOperation, path)); + cc.addAll(0, printGraph(partCodes, partCodePos, localData, stack, allParts, switchLoc, caseBodies.get(i), stopPart2, loops, staticOperation, path)); caseCommands.add(cc); } @@ -642,7 +642,7 @@ public class AVM2Graph extends Graph { ret.add(ti); } else {*/ currentLoop.phase = 2; - ret.addAll(printGraph(localData, stack, allParts, null, next, stopPart, loops, staticOperation, path)); + ret.addAll(printGraph(partCodes, partCodePos, localData, stack, allParts, null, next, stopPart, loops, staticOperation, path)); //} } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index 956c30efd..5d4b0e288 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -204,7 +204,7 @@ public final class MethodBody implements Cloneable { if (exportMode != ScriptExportMode.AS) { getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); } else { - //if (!path.contains("RemoveAllPopup")) { + //if (!path.contains("_addGarbageLineMulti")) { if (!Configuration.decompile.get()) { writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); return; @@ -248,7 +248,7 @@ public final class MethodBody implements Cloneable { if (exportMode != ScriptExportMode.AS) { getCode().toASMSource(constants, trait, method_info.get(this.method_info), this, exportMode, writer); } else { - //if (!path.contains("RemoveAllPopup")) { + //if (!path.contains("_addGarbageLineMulti")) { if (!Configuration.decompile.get()) { //writer.startMethod(this.method_info); writer.appendNoHilight(Helper.getDecompilationSkippedComment()).newLine(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java index 599018dc0..ffd4ef06f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java @@ -53,6 +53,7 @@ import com.jpexs.decompiler.graph.model.WhileItem; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @@ -247,7 +248,7 @@ public class ActionGraph extends Graph { } @Override - protected List check(GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { + protected List check(Map> partCodes, Map partCodePos, GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { if (!output.isEmpty()) { if (output.get(output.size() - 1) instanceof StoreRegisterActionItem) { StoreRegisterActionItem str = (StoreRegisterActionItem) output.get(output.size() - 1); @@ -310,7 +311,7 @@ public class ActionGraph extends Graph { List defaultCommands = new ArrayList<>(); List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(defaultPart2); - defaultCommands = printGraph(localData, stack, allParts, null, defaultPart, stopPart2, loops, staticOperation, path); + defaultCommands = printGraph(partCodes, partCodePos, localData, stack, allParts, null, defaultPart, stopPart2, loops, staticOperation, path); List loopContinues = new ArrayList<>(); for (Loop l : loops) { @@ -384,7 +385,7 @@ public class ActionGraph extends Graph { if ((defaultPart != null) && (defaultCommands.isEmpty())) { List stopPart2x = new ArrayList<>(stopPart); stopPart2x.add(next); - defaultCommands = printGraph(localData, stack, allParts, null, defaultPart, stopPart2x, loops, staticOperation, path); + defaultCommands = printGraph(partCodes, partCodePos, localData, stack, allParts, null, defaultPart, stopPart2x, loops, staticOperation, path); } if (!defaultCommands.isEmpty()) { @@ -433,7 +434,7 @@ public class ActionGraph extends Graph { if (breakPart != null) { stopPart2x.add(breakPart); } - cc.addAll(0, printGraph(localData, stack, allParts, null, caseBodies.get(i), stopPart2x, loops, staticOperation, path)); + cc.addAll(0, printGraph(partCodes, partCodePos, localData, stack, allParts, null, caseBodies.get(i), stopPart2x, loops, staticOperation, path)); if (cc.size() >= 2) { if (cc.get(cc.size() - 1) instanceof BreakItem) { if ((cc.get(cc.size() - 2) instanceof ContinueItem) || (cc.get(cc.size() - 2) instanceof BreakItem)) { @@ -452,7 +453,7 @@ public class ActionGraph extends Graph { if (ti != null) { ret.add(ti); } else { - ret.addAll(printGraph(localData, stack, allParts, null, next, stopPart, loops, staticOperation, path)); + ret.addAll(printGraph(partCodes, partCodePos, localData, stack, allParts, null, next, stopPart, loops, staticOperation, path)); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java index f0167585b..7b255fe6a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -31,8 +31,10 @@ import com.jpexs.decompiler.graph.model.DuplicateItem; import com.jpexs.decompiler.graph.model.ExitItem; import com.jpexs.decompiler.graph.model.FalseItem; import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.GotoItem; import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.IntegerValueItem; +import com.jpexs.decompiler.graph.model.LabelItem; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.decompiler.graph.model.LogicalOpItem; import com.jpexs.decompiler.graph.model.LoopItem; @@ -434,7 +436,7 @@ public class Graph { /*1 System.err.println(""); for (Loop el : loops) { - System..println(el); + System.err.println(el); } System.err.println("");*/ getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); @@ -444,7 +446,7 @@ public class Graph { } System.err.println("");//*/ - List ret = printGraph(localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); + List ret = printGraph(new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); processIfs(ret); finalProcessStack(stack, ret); finalProcessAll(ret, 0, new FinalProcessLocalData()); @@ -731,7 +733,7 @@ public class Graph { return false; } - protected List check(GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { + protected List check(Map> partCodes, Map partCodePos, GraphSource code, BaseLocalData localData, List allParts, TranslateStack stack, GraphPart parent, GraphPart part, List stopPart, List loops, List output, Loop currentLoop, int staticOperation, String path) throws InterruptedException { return null; } @@ -807,9 +809,9 @@ public class Graph { list.remove(list.size() - 1); } - protected List printGraph(BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, int staticOperation, String path) throws InterruptedException { + protected List printGraph(Map> partCodes, Map partCodePos, BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, int staticOperation, String path) throws InterruptedException { List visited = new ArrayList<>(); - return printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops, null, staticOperation, path, 0); + return printGraph(partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, loops, null, staticOperation, path, 0); } protected GraphTargetItem checkLoop(LoopItem loopItem, BaseLocalData localData, List loops) { @@ -1305,7 +1307,7 @@ public class Graph { } } - protected List printGraph(List visited, BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { + protected List printGraph(Map> partCodes, Map partCodePos, List visited, BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } @@ -1315,11 +1317,7 @@ public class Graph { if (recursionLevel > allParts.size() + 1) { throw new TranslateException("printGraph max recursion level reached."); } - if (visited.contains(part)) { - //return new ArrayList(); - } else { - visited.add(part); - } + if (ret == null) { ret = new ArrayList<>(); } @@ -1429,6 +1427,23 @@ public class Graph { ret.add(new ScriptEndItem()); return ret; } + + if (visited.contains(part)) { + String labelName = "addr" + part.start; + List firstCode = partCodes.get(part); + int firstCodePos = partCodePos.get(part); + if (firstCode.size() > firstCodePos && (firstCode.get(firstCodePos) instanceof LabelItem)) { + labelName = ((LabelItem) firstCode.get(firstCodePos)).labelName; + } else { + firstCode.add(firstCodePos, new LabelItem(null, labelName)); + } + ret.add(new GotoItem(null, labelName)); + return ret; + } else { + visited.add(part); + partCodes.put(part, ret); + partCodePos.put(part, ret.size()); + } List currentRet = ret; UniversalLoopItem loopItem = null; TranslateStack sPreLoop = stack; @@ -1466,7 +1481,7 @@ public class Graph { } if (parseNext) { - List retCheck = check(code, localData, allParts, stack, parent, part, stopPart, loops, output, currentLoop, staticOperation, path); + List retCheck = check(partCodes, partCodePos, code, localData, allParts, stack, parent, part, stopPart, loops, output, currentLoop, staticOperation, path); if (retCheck != null) { if (!retCheck.isEmpty()) { currentRet.addAll(retCheck); @@ -1524,7 +1539,7 @@ public class Graph { //int stackLenBefore = stack.size(); TranslateStack s2 = (TranslateStack) stack.clone(); s2.clear(); - List nextCommands = printGraph(visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + List nextCommands = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); makeAllCommands(nextCommands, s2); if (first) { defaultCommands = nextCommands; @@ -1539,7 +1554,7 @@ public class Graph { currentRet.add(sw); swLoop.phase = 2; if (next != null) { - currentRet.addAll(printGraph(visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + currentRet.addAll(printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); } } //else GraphPart nextOnePart = null; @@ -1576,12 +1591,12 @@ public class Graph { List onTrue = new ArrayList<>(); if (!isEmpty && hasOntrue) { - onTrue = printGraph(visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + onTrue = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); } List onFalse = new ArrayList<>(); if (!isEmpty && hasOnFalse) { - onFalse = printGraph(visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + onFalse = printGraph(partCodes, partCodePos, visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1); } //List out2 = new ArrayList<>(); //makeAllCommands(out2, stack); @@ -1625,7 +1640,7 @@ public class Graph { } //currentRet.addAll(out2); if (next != null) { - printGraph(visited, localData, stack, allParts, part, next, stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); + printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); //currentRet.addAll(); } } @@ -1634,7 +1649,7 @@ public class Graph { nextOnePart = part.nextParts.get(0); } if (nextOnePart != null) { - printGraph(visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); + printGraph(partCodes, partCodePos, visited, localData, stack, allParts, part, part.nextParts.get(0), stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1); } } @@ -1659,7 +1674,7 @@ public class Graph { stopContPart.add(currentLoop.loopContinue); GraphPart precoBackup = currentLoop.loopPreContinue; currentLoop.loopPreContinue = null; - loopItem.commands.addAll(printGraph(visited, localData, new TranslateStack(path), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1)); + loopItem.commands.addAll(printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1)); } } @@ -1716,7 +1731,7 @@ public class Graph { currentLoop.loopPreContinue = null; List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(currentLoop.loopContinue); - finalComm = printGraph(visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + finalComm = printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); currentLoop.loopPreContinue = backup; checkContinueAtTheEnd(finalComm, currentLoop); } @@ -1793,7 +1808,7 @@ public class Graph { currentLoop.loopPreContinue = null; List stopPart2 = new ArrayList<>(stopPart); stopPart2.add(currentLoop.loopContinue); - List finalComm = printGraph(visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); + List finalComm = printGraph(partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); currentLoop.loopPreContinue = backup; checkContinueAtTheEnd(finalComm, currentLoop); @@ -1843,7 +1858,7 @@ public class Graph { } if (currentLoop.loopBreak != null) { - ret.addAll(printGraph(visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); + ret.addAll(printGraph(partCodes, partCodePos, visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, loops, null, staticOperation, path, recursionLevel + 1)); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/GotoItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/GotoItem.java new file mode 100644 index 000000000..d2c0a312c --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/GotoItem.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.graph.model; + +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TypeItem; + +/** + * + * @author JPEXS + */ +public class GotoItem extends GraphTargetItem { + + public String labelName; + + public GotoItem(GraphSourceItem src, String labelName) { + super(src, PRECEDENCE_PRIMARY); + this.labelName = labelName; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("§§goto(" + labelName + ")"); + return writer; + } + + @Override + public boolean hasReturnValue() { + return false; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public Object getResult() { + return null; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/LabelItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/LabelItem.java new file mode 100644 index 000000000..fa61ceafb --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/LabelItem.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.graph.model; + +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TypeItem; + +/** + * + * @author JPEXS + */ +public class LabelItem extends GraphTargetItem { + + public String labelName; + + public LabelItem(GraphSourceItem src, String labelName) { + super(src, PRECEDENCE_PRIMARY); + this.labelName = labelName; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append(labelName + ":"); + return writer; + } + + @Override + public boolean needsNewLine() { + return false; + } + + @Override + public boolean needsSemicolon() { + return false; + } + + @Override + public boolean hasReturnValue() { + return false; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public Object getResult() { + return null; + } + +}