mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-10 17:25:21 +00:00
Fixed: #270 AS decompilation - switch in loop
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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;
|
||||
|
||||
24
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForInIf.as
vendored
Normal file
24
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForInIf.as
vendored
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForInSwitch.as
vendored
Normal file
27
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForInSwitch.as
vendored
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ package tests
|
||||
{
|
||||
public function run():*
|
||||
{
|
||||
trace("hello");
|
||||
trace("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
28
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestIfTry.as
vendored
Normal file
28
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestIfTry.as
vendored
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user