Fixed: #270 AS decompilation - switch in loop

This commit is contained in:
Jindra Petřík
2021-03-13 18:30:05 +01:00
parent 9b13ca6504
commit 09b68c1f9c
11 changed files with 260 additions and 7 deletions

View File

@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- [#1488] SVG Export - EmptyStackException when clipping used
- [#1584] SVG Import - paths with horizontal/vertical lines and rotation
- [#1572] SVG Export - clipping must not use groups
- [#270] AS decompilation - switch in loop
## [14.2.0] - 2021-03-12
### Added
@@ -2116,6 +2117,7 @@ All notable changes to this project will be documented in this file.
[#1488]: https://www.free-decompiler.com/flash/issues/1488
[#1584]: https://www.free-decompiler.com/flash/issues/1584
[#1572]: https://www.free-decompiler.com/flash/issues/1572
[#270]: https://www.free-decompiler.com/flash/issues/270
[#1645]: https://www.free-decompiler.com/flash/issues/1645
[#1639]: https://www.free-decompiler.com/flash/issues/1639
[#1371]: https://www.free-decompiler.com/flash/issues/1371

View File

@@ -556,6 +556,7 @@ public class Graph {
}
}
expandGotos(ret);
//processSwitches(ret);
processIfs(ret);
finalProcessStack(stack, ret, path);
makeAllCommands(ret, stack);
@@ -563,6 +564,139 @@ public class Graph {
return ret;
}
protected void processSwitches(List<GraphTargetItem> list) {
processSwitches(list, -1);
}
/*
while(something){
switch(xx){
case 1:
trace("1");
continue;
case 2:
trace("2");
continue;
case 3:
break;
default:
continue;
}
item
}
=>
while(something){
switch(xx){
case 1:
trace("1");
break;
case 2:
trace("2");
break;
case 3:
item
break;
default:
break;
}
}
This will fix precontinue handler which detects multiple continues
*/
protected void processSwitches(List<GraphTargetItem> list, long lastLoopId) {
loopi:
for (int i = 0; i < list.size(); i++) {
GraphTargetItem item = list.get(i);
if (item instanceof SwitchItem) {
SwitchItem swi = (SwitchItem) item;
Set<GraphTargetItem> allItems = swi.getAllSubItemsRecursively();
int breakCount = 0;
for (GraphTargetItem it : allItems) {
if (it instanceof BreakItem) {
BreakItem br = (BreakItem) it;
if (br.loopId == swi.loop.id) {
breakCount++;
if (breakCount > 1) {
continue loopi;
}
}
}
}
if (!swi.caseCommands.isEmpty()) {
List<GraphTargetItem> lastCommands = swi.caseCommands.get(swi.caseCommands.size() - 1);
if (lastCommands.isEmpty() && breakCount == 1) {
continue loopi;
}
if (breakCount == 1 && !(lastCommands.get(lastCommands.size() - 1) instanceof ContinueItem)
&& !(lastCommands.get(lastCommands.size() - 1) instanceof ExitItem)) {
continue loopi;
}
}
int breakCaseIndex = -1;
for (int c = 0; c < swi.caseCommands.size(); c++) {
List<GraphTargetItem> commands = swi.caseCommands.get(c);
if (!commands.isEmpty()) {
if (commands.get(commands.size() - 1) instanceof BreakItem) {
BreakItem br = (BreakItem) commands.get(commands.size() - 1);
if (br.loopId == swi.loop.id) {
breakCaseIndex = c;
break;
}
}
}
if (c == swi.caseCommands.size() - 1) {
if (commands.isEmpty()) {
breakCaseIndex = c;
break;
}
if (!(commands.get(commands.size() - 1) instanceof ContinueItem)
&& !(commands.get(commands.size() - 1) instanceof ExitItem)) {
breakCaseIndex = c;
break;
}
}
}
boolean hasContinues = false;
for (int c = 0; c < swi.caseCommands.size(); c++) {
List<GraphTargetItem> commands = swi.caseCommands.get(c);
if (!commands.isEmpty()) {
if (commands.get(commands.size() - 1) instanceof ContinueItem) {
ContinueItem cnt = (ContinueItem) commands.get(commands.size() - 1);
if (cnt.loopId == lastLoopId) {
hasContinues = true;
commands.set(commands.size() - 1, new BreakItem(null, null, swi.loop.id));
if (c == swi.caseCommands.size() - 1) {
if (commands.size() == 1) {
commands.remove(0);
}
}
}
}
}
}
if (hasContinues && breakCaseIndex > -1 && i + 1 < list.size() && (list.get(list.size() - 1) instanceof ContinueItem)) {
List<GraphTargetItem> toAdd = new ArrayList<>();
for (int j = i + 1; j < list.size() - 1; j++) {
toAdd.add(list.remove(i + 1));
}
List<GraphTargetItem> targetCommands = swi.caseCommands.get(breakCaseIndex);
if (!targetCommands.isEmpty() && (targetCommands.get(targetCommands.size() - 1) instanceof BreakItem)) {
targetCommands.remove(targetCommands.size() - 1);
}
targetCommands.addAll(toAdd);
targetCommands.add(new BreakItem(null, null, swi.loop.id));
}
}
}
}
protected FinalProcessLocalData getFinalData(BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates) {
return new FinalProcessLocalData(loops);
}
@@ -844,6 +978,7 @@ public class Graph {
}
alreadyProcessedBlocks.add(list);
}
/**
* if (xxx) { y ; goto a } else { z ; goto a }
*
@@ -1005,6 +1140,21 @@ public class Graph {
}
}
}
if (i < list.size() - 1) {
if ((list.get(list.size() - 1) instanceof ExitItem) || (list.get(list.size() - 1) instanceof BreakItem)) {
if (onFalse.isEmpty() && !onTrue.isEmpty() && (onTrue.get(onTrue.size() - 1) instanceof ContinueItem)) {
ifi.expression = ifi.expression.invert(null);
List<GraphTargetItem> onTrueItems = new ArrayList<>();
for (int j = i + 1; j < list.size(); j++) {
onTrueItems.add(list.remove(i + 1));
}
list.addAll(i + 1, ifi.onTrue);
ifi.onTrue.clear();
ifi.onTrue.addAll(onTrueItems);
}
}
}
}
}
@@ -2263,6 +2413,8 @@ public class Graph {
boolean hasContinue = false;
processIfs(loopItem.commands);
processSwitches(loopItem.commands, currentLoop.id);
checkContinueAtTheEnd(loopItem.commands, currentLoop);
List<ContinueItem> continues = loopItem.getContinues();
for (ContinueItem c : continues) {

View File

@@ -99,6 +99,26 @@ public class SwitchItem extends LoopItem implements Block {
switchedObject.toString(writer, localData);
writer.append(")").startBlock();
for (int i = 0; i < caseCommands.size(); i++) {
//if last is default and is empty, ignore it
if (i == caseCommands.size() - 1) {
if (caseCommands.get(i).isEmpty()) {
boolean hasDefault = false;
boolean hasNonDefault = false;
for (int k = 0; k < valuesMapping.size(); k++) {
if (valuesMapping.get(k) == i) {
if (caseValues.get(k) instanceof DefaultItem) {
hasDefault = true;
} else {
hasNonDefault = true;
}
}
}
if (hasDefault && !hasNonDefault) {
continue;
}
}
}
for (int k = 0; k < valuesMapping.size(); k++) {
if (valuesMapping.get(k) == i) {
if (!(caseValues.get(k) instanceof DefaultItem)) {

View File

@@ -644,16 +644,15 @@ public class ActionScript2Test extends ActionScript2TestBase {
+ "var _loc1_ = 5;\r\n"
+ "while(_loc1_ < 10)\r\n"
+ "{\r\n"
+ "if(_loc1_ == 5)\r\n"
+ "if(_loc1_ != 5)\r\n"
+ "{\r\n"
+ "return false;\r\n"
+ "}\r\n"
+ "if(_loc1_ == 6)\r\n"
+ "{\r\n"
+ "return true;\r\n"
+ "}\r\n"
+ "_loc1_ = _loc1_ + 1;\r\n"
+ "continue;\r\n"
+ "}\r\n"
+ "return false;\r\n"
+ "}\r\n"
+ "}\r\n"
+ "trace(\"function4Test\");\r\n"
@@ -2002,8 +2001,6 @@ public class ActionScript2Test extends ActionScript2TestBase {
+ "case \"C\":\r\n"
+ "trace(\"Ret 5\");\r\n"
+ "return 5;\r\n"
+ "default:\r\n"
+ "continue;\r\n"
+ "}\r\n"
+ "}\r\n"
+ "trace(\"Final\");\r\n"

View File

@@ -43,7 +43,9 @@ package
TestForEachReturn;
TestForGoto;
TestForIn;
TestForInIf;
TestForInReturn;
TestForInSwitch;
TestForXml;
TestGotos;
TestGotos2;
@@ -56,6 +58,7 @@ package
TestIf;
TestIfElse;
TestIfInIf;
TestIfTry;
TestIgnoreAndOr;
TestImportedVar;
TestInc2;

View File

@@ -0,0 +1,24 @@
package tests
{
public class TestForInIf
{
public function run():*
{
var arr:Array = ["a", "b", "c"];
var b:int = 5;
for (var a:String in arr){
if (b == 5){
if (b > 7){
trace("b>7");
}else{
return;
}
}
trace("forend");
}
}
}
}

View File

@@ -0,0 +1,27 @@
package tests
{
public class TestForInSwitch
{
public function run():*
{
var arr:Array = ["a", "b", "c"];
for (var a:String in arr){
switch(a){
case "a":
trace("val a");
break;
case "b":
trace("val b");
break;
case "c":
trace("val c");
//break;
}
trace("final");
}
}
}
}

View File

@@ -5,7 +5,7 @@ package tests
{
public function run():*
{
trace("hello");
trace("hello");
}
}
}

View File

@@ -0,0 +1,28 @@
package tests
{
public class TestIfTry
{
public function run():*
{
var b:Boolean = true;
if (b)
{
var c:int = 5;
for (var i:int = 0; i < c; i++)
{
trace("xx");
}
}
try
{
trace("in try");
}
catch (e:Error)
{
trace("in catch");
}
}
}
}