mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-07 13:35:31 +00:00
Fixed: #2226 Incorrect decompilation of continue statements in some cases
This commit is contained in:
@@ -80,6 +80,7 @@ All notable changes to this project will be documented in this file.
|
||||
classes must be fully qualified
|
||||
- [#2234] AS1/2 postincrement/decrement inside DefineFunction2
|
||||
- AS3 PCode - pushbyte operand docs - signed byte
|
||||
- [#2226] Incorrect decompilation of continue statements in some cases
|
||||
|
||||
### Changed
|
||||
- [#2185] MochiCrypt no longer offered for auto decrypt, user needs to choose variant from "Use unpacker" menu
|
||||
@@ -3476,6 +3477,7 @@ Major version of SWF to XML export changed to 2.
|
||||
[#2239]: https://www.free-decompiler.com/flash/issues/2239
|
||||
[#2237]: https://www.free-decompiler.com/flash/issues/2237
|
||||
[#2234]: https://www.free-decompiler.com/flash/issues/2234
|
||||
[#2226]: https://www.free-decompiler.com/flash/issues/2226
|
||||
[#2206]: https://www.free-decompiler.com/flash/issues/2206
|
||||
[#2228]: https://www.free-decompiler.com/flash/issues/2228
|
||||
[#2100]: https://www.free-decompiler.com/flash/issues/2100
|
||||
|
||||
@@ -2278,6 +2278,7 @@ public class Graph {
|
||||
//List<GraphPart> loopContinues = getLoopsContinues(loops);
|
||||
boolean isLoop = false;
|
||||
Loop currentLoop = null;
|
||||
List<GraphTargetItem> precontinueCommands = new ArrayList<>();
|
||||
|
||||
boolean vCanHandleLoop = canHandleLoop(localData, part, loops, throwStates);
|
||||
Loop ignoredLoop = null;
|
||||
@@ -2426,6 +2427,9 @@ public class Graph {
|
||||
List<GraphTargetItem> currentRet = ret;
|
||||
UniversalLoopItem loopItem = null;
|
||||
TranslateStack sPreLoop = stack;
|
||||
LoopItem li = null;
|
||||
boolean loopTypeFound = false;
|
||||
boolean doWhileCandidate = false;
|
||||
if (isLoop) {
|
||||
//makeAllCommands(currentRet, stack);
|
||||
stack = (TranslateStack) stack.clone();
|
||||
@@ -2440,6 +2444,52 @@ public class Graph {
|
||||
//loopItem.commands=printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops);
|
||||
currentRet.add(loopItem);
|
||||
currentRet = loopItem.commands;
|
||||
li = loopItem;
|
||||
|
||||
if (currentLoop.loopPreContinue != null) {
|
||||
GraphPart backup = currentLoop.loopPreContinue;
|
||||
currentLoop.loopPreContinue = null;
|
||||
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
|
||||
stopPart2.add(currentLoop.loopContinue);
|
||||
List<StopPartKind> stopPartKind2 = new ArrayList<>(stopPartKind);
|
||||
stopPartKind2.add(StopPartKind.OTHER);
|
||||
Set<GraphPart> subVisited = new HashSet<>();
|
||||
precontinueCommands = printGraph(foundGotos, partCodes, partCodePos, subVisited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1);
|
||||
currentLoop.loopPreContinue = backup;
|
||||
checkContinueAtTheEnd(precontinueCommands, currentLoop);
|
||||
|
||||
|
||||
|
||||
if (!precontinueCommands.isEmpty() && precontinueCommands.get(precontinueCommands.size() - 1) instanceof IfItem) {
|
||||
IfItem ifi = (IfItem) precontinueCommands.get(precontinueCommands.size() - 1);
|
||||
boolean ok = false;
|
||||
boolean invert = false;
|
||||
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem) && (((BreakItem) ifi.onTrue.get(0)).loopId == currentLoop.id))
|
||||
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) {
|
||||
ok = true;
|
||||
invert = true;
|
||||
}
|
||||
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onTrue.get(0)).loopId == currentLoop.id))
|
||||
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem) && (((BreakItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) {
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
doWhileCandidate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doWhileCandidate) {
|
||||
for (GraphTargetItem item : precontinueCommands) {
|
||||
if (item instanceof Block) {
|
||||
currentLoop.loopPreContinue = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentLoop.loopPreContinue == null) {
|
||||
precontinueCommands.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
//return ret;
|
||||
}
|
||||
|
||||
@@ -2888,38 +2938,40 @@ public class Graph {
|
||||
|
||||
}
|
||||
if (isLoop && loopItem != null && currentLoop != null) {
|
||||
|
||||
LoopItem li = loopItem;
|
||||
boolean loopTypeFound = false;
|
||||
|
||||
boolean hasContinue = false;
|
||||
|
||||
processIfs(loopItem.commands);
|
||||
processSwitches(loopItem.commands, currentLoop.id);
|
||||
processOther(loopItem.commands, currentLoop.id);
|
||||
|
||||
checkContinueAtTheEnd(loopItem.commands, currentLoop);
|
||||
List<ContinueItem> continues = loopItem.getContinues();
|
||||
for (ContinueItem c : continues) {
|
||||
if (c.loopId == currentLoop.id) {
|
||||
hasContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasContinue) {
|
||||
if (currentLoop.loopPreContinue != null) {
|
||||
List<GraphPart> stopContPart = new ArrayList<>();
|
||||
stopContPart.add(currentLoop.loopContinue);
|
||||
List<StopPartKind> stopContPartKind = new ArrayList<>();
|
||||
stopContPartKind.add(StopPartKind.OTHER);
|
||||
GraphPart precoBackup = currentLoop.loopPreContinue;
|
||||
currentLoop.loopPreContinue = null;
|
||||
printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, null, precoBackup, stopContPart, stopContPartKind, loops, throwStates, loopItem.commands, staticOperation, path, recursionLevel + 1);
|
||||
checkContinueAtTheEnd(loopItem.commands, currentLoop);
|
||||
}
|
||||
}
|
||||
|
||||
//Loop with condition at the beginning (While)
|
||||
|
||||
//DoWhile based on precontinue
|
||||
if (!loopTypeFound && (!loopItem.commands.isEmpty())) {
|
||||
List<List<GraphTargetItem>> continueCommands1 = new ArrayList<>();
|
||||
getContinuesCommands(loopItem.commands, continueCommands1, currentLoop.id);
|
||||
if (!continueCommands1.isEmpty() && doWhileCandidate) {
|
||||
int index = ret.indexOf(loopItem);
|
||||
ret.remove(index);
|
||||
IfItem ifi = (IfItem) precontinueCommands.remove(precontinueCommands.size() - 1);
|
||||
List<GraphTargetItem> exprList = new ArrayList<>(precontinueCommands);
|
||||
boolean invert = false;
|
||||
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem) && (((BreakItem) ifi.onTrue.get(0)).loopId == currentLoop.id))
|
||||
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) {
|
||||
invert = true;
|
||||
}
|
||||
|
||||
GraphTargetItem expr = ifi.expression;
|
||||
if (invert) {
|
||||
expr = expr.invert(null);
|
||||
}
|
||||
exprList.add(expr);
|
||||
ret.add(index, li = new DoWhileItem(null, expr.getLineStartItem(), currentLoop, loopItem.commands, exprList));
|
||||
loopTypeFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Loop with condition at the beginning (While)
|
||||
if (!loopTypeFound && (!loopItem.commands.isEmpty())) {
|
||||
if (loopItem.commands.get(0) instanceof IfItem) {
|
||||
IfItem ifi = (IfItem) loopItem.commands.get(0);
|
||||
|
||||
@@ -2973,49 +3025,26 @@ public class Graph {
|
||||
List<GraphTargetItem> finalComm = new ArrayList<>();
|
||||
|
||||
//findGotoTargets - comment this out:
|
||||
if (currentLoop.loopPreContinue != null) {
|
||||
GraphPart backup = currentLoop.loopPreContinue;
|
||||
currentLoop.loopPreContinue = null;
|
||||
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
|
||||
stopPart2.add(currentLoop.loopContinue);
|
||||
List<StopPartKind> stopPartKind2 = new ArrayList<>(stopPartKind);
|
||||
stopPartKind2.add(StopPartKind.OTHER);
|
||||
List<GraphTargetItem> precoCommands = printGraph(foundGotos, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1);
|
||||
currentLoop.loopPreContinue = backup;
|
||||
checkContinueAtTheEnd(precoCommands, currentLoop);
|
||||
|
||||
if (!precontinueCommands.isEmpty()) {
|
||||
|
||||
List<List<GraphTargetItem>> continueCommands = new ArrayList<>();
|
||||
getContinuesCommands(commands, continueCommands, currentLoop.id);
|
||||
|
||||
if (continueCommands.isEmpty()) {
|
||||
commands.addAll(precoCommands);
|
||||
precoCommands = new ArrayList<>();
|
||||
commands.addAll(precontinueCommands);
|
||||
precontinueCommands = new ArrayList<>();
|
||||
|
||||
//Single continue and there is break/continue/return/throw at end of the commands
|
||||
} else if (!commands.isEmpty() && continueCommands.size() == 1) {
|
||||
GraphTargetItem lastItem = commands.get(commands.size() - 1);
|
||||
if ((lastItem instanceof BreakItem) || (lastItem instanceof ContinueItem) || (lastItem instanceof ExitItem)) {
|
||||
continueCommands.get(0).addAll(continueCommands.get(0).size() - 1, precoCommands);
|
||||
precoCommands = new ArrayList<>();
|
||||
continueCommands.get(0).addAll(continueCommands.get(0).size() - 1, precontinueCommands);
|
||||
precontinueCommands = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAllNotBlock = true;
|
||||
for (GraphTargetItem ti : precoCommands) {
|
||||
if (ti instanceof Block) {
|
||||
isAllNotBlock = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isAllNotBlock) {
|
||||
finalComm.addAll(precoCommands);
|
||||
} else {
|
||||
commands.addAll(precoCommands);
|
||||
}
|
||||
}
|
||||
if (currentLoop.precontinueCommands != null) {
|
||||
finalComm.addAll(currentLoop.precontinueCommands);
|
||||
}
|
||||
finalComm.addAll(precontinueCommands);
|
||||
}
|
||||
if (!finalComm.isEmpty()) {
|
||||
ret.add(index, li = new ForItem(expr.getSrc(), expr.getLineStartItem(), currentLoop, new ArrayList<>(), exprList.get(exprList.size() - 1), finalComm, commands));
|
||||
} else {
|
||||
@@ -3029,6 +3058,10 @@ public class Graph {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!loopTypeFound && !precontinueCommands.isEmpty()) {
|
||||
loopItem.commands.addAll(precontinueCommands);
|
||||
}
|
||||
|
||||
//Loop with condition at the end (Do..While)
|
||||
if (!loopTypeFound && (!loopItem.commands.isEmpty())) {
|
||||
@@ -3078,50 +3111,6 @@ public class Graph {
|
||||
}
|
||||
}
|
||||
|
||||
if (!loopTypeFound) {
|
||||
if (currentLoop.loopPreContinue != null) {
|
||||
loopTypeFound = true;
|
||||
GraphPart backup = currentLoop.loopPreContinue;
|
||||
currentLoop.loopPreContinue = null;
|
||||
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
|
||||
stopPart2.add(currentLoop.loopContinue);
|
||||
List<StopPartKind> stopPartKind2 = new ArrayList<>(stopPartKind);
|
||||
stopPartKind2.add(StopPartKind.OTHER);
|
||||
List<GraphTargetItem> finalComm = printGraph(foundGotos, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, stopPartKind2, loops, throwStates, null, staticOperation, path, recursionLevel + 1);
|
||||
currentLoop.loopPreContinue = backup;
|
||||
checkContinueAtTheEnd(finalComm, currentLoop);
|
||||
|
||||
if (!finalComm.isEmpty()) {
|
||||
if (finalComm.get(finalComm.size() - 1) instanceof IfItem) {
|
||||
IfItem ifi = (IfItem) finalComm.get(finalComm.size() - 1);
|
||||
boolean ok = false;
|
||||
boolean invert = false;
|
||||
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem) && (((BreakItem) ifi.onTrue.get(0)).loopId == currentLoop.id))
|
||||
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) {
|
||||
ok = true;
|
||||
invert = true;
|
||||
}
|
||||
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onTrue.get(0)).loopId == currentLoop.id))
|
||||
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem) && (((BreakItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) {
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
finalComm.remove(finalComm.size() - 1);
|
||||
int index = ret.indexOf(loopItem);
|
||||
ret.remove(index);
|
||||
List<GraphTargetItem> exprList = new ArrayList<>(finalComm);
|
||||
GraphTargetItem expr = ifi.expression;
|
||||
if (invert) {
|
||||
expr = expr.invert(null);
|
||||
}
|
||||
exprList.add(expr);
|
||||
ret.add(index, li = new DoWhileItem(null, expr.getLineStartItem(), currentLoop, loopItem.commands, exprList));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!loopTypeFound) {
|
||||
checkContinueAtTheEnd(loopItem.commands, currentLoop);
|
||||
}
|
||||
|
||||
@@ -371,6 +371,9 @@ public class GraphPrecontinueDetector {
|
||||
return node;
|
||||
}
|
||||
visited.add(node);
|
||||
|
||||
//Note to my future self: Do not make this twoway ifs only since it may break && and || operations in expressions
|
||||
|
||||
/*
|
||||
if(a)
|
||||
{
|
||||
|
||||
@@ -2226,6 +2226,36 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhileSwitch() {
|
||||
decompileMethod("classic_air", "testWhileSwitch", "var a:Boolean = true;\r\n"
|
||||
+ "var d:int = 5;\r\n"
|
||||
+ "var e:Boolean = true;\r\n"
|
||||
+ "var i:int = 0;\r\n"
|
||||
+ "while(i < 100)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "trace(\"start\");\r\n"
|
||||
+ "if(a)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "trace(\"A\");\r\n"
|
||||
+ "}\r\n"
|
||||
+ "else\r\n"
|
||||
+ "{\r\n"
|
||||
+ "switch(d - 1)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "case 0:\r\n"
|
||||
+ "trace(\"D1\");\r\n"
|
||||
+ "}\r\n"
|
||||
+ "}\r\n"
|
||||
+ "if(e)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "trace(\"E\");\r\n"
|
||||
+ "}\r\n"
|
||||
+ "i++;\r\n"
|
||||
+ "}\r\n",
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhileTry() {
|
||||
decompileMethod("classic_air", "testWhileTry", "while(true)\r\n"
|
||||
|
||||
@@ -2219,6 +2219,36 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhileSwitch() {
|
||||
decompileMethod("classic", "testWhileSwitch", "var a:Boolean = true;\r\n"
|
||||
+ "var d:int = 5;\r\n"
|
||||
+ "var e:Boolean = true;\r\n"
|
||||
+ "var i:int = 0;\r\n"
|
||||
+ "while(i < 100)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "trace(\"start\");\r\n"
|
||||
+ "if(a)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "trace(\"A\");\r\n"
|
||||
+ "}\r\n"
|
||||
+ "else\r\n"
|
||||
+ "{\r\n"
|
||||
+ "switch(d)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "case 1:\r\n"
|
||||
+ "trace(\"D1\");\r\n"
|
||||
+ "}\r\n"
|
||||
+ "}\r\n"
|
||||
+ "if(e)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "trace(\"E\");\r\n"
|
||||
+ "}\r\n"
|
||||
+ "i++;\r\n"
|
||||
+ "}\r\n",
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhileTry() {
|
||||
decompileMethod("classic", "testWhileTry", "while(true)\r\n"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -130,6 +130,7 @@ package
|
||||
TestWhileBreak;
|
||||
TestWhileBreak2;
|
||||
TestWhileContinue;
|
||||
TestWhileSwitch;
|
||||
TestWhileTry;
|
||||
TestWhileTry2;
|
||||
TestXml;
|
||||
|
||||
35
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestWhileSwitch.as
vendored
Normal file
35
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestWhileSwitch.as
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package tests
|
||||
{
|
||||
|
||||
public class TestWhileSwitch
|
||||
{
|
||||
public function run():*
|
||||
{
|
||||
var a:Boolean = true;
|
||||
var d:int = 5;
|
||||
var e:Boolean = true;
|
||||
var i:int = 0;
|
||||
while(i < 100)
|
||||
{
|
||||
trace("start");
|
||||
if(a)
|
||||
{
|
||||
trace("A");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(d)
|
||||
{
|
||||
case 1:
|
||||
trace("D1");
|
||||
}
|
||||
}
|
||||
if(e)
|
||||
{
|
||||
trace("E");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user