Fixed: #2477 AS1/2 Switch in last statement of switch break labels

This commit is contained in:
Jindra Petřík
2025-07-11 21:08:49 +02:00
parent 2adef5f600
commit 0fc10bde99
25 changed files with 419 additions and 19 deletions
@@ -31,6 +31,7 @@ import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.LoopItem;
@@ -168,6 +169,20 @@ public class ForEachInAVM2Item extends LoopItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public boolean hasReturnValue() {
@@ -31,6 +31,7 @@ import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.LoopItem;
@@ -152,6 +153,20 @@ public class ForInAVM2Item extends LoopItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public boolean hasReturnValue() {
@@ -33,6 +33,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
@@ -201,6 +202,40 @@ public class TryAVM2Item extends AVM2Item implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : tryCommands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
if (finallyCommands != null) {
for (GraphTargetItem ti : finallyCommands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
}
for (List<GraphTargetItem> commands : catchCommands) {
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
}
return ret;
}
@Override
public boolean needsSemicolon() {
@@ -23,6 +23,7 @@ import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
@@ -186,6 +187,13 @@ public class ClassAVM2Item extends AVM2Item implements Block {
List<ContinueItem> ret = new ArrayList<>();
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
return ret;
}
@Override
public boolean needsSemicolon() {
@@ -34,6 +34,7 @@ import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Helper;
@@ -273,6 +274,12 @@ public class ClassActionItem extends ActionItem implements Block {
List<ContinueItem> ret = new ArrayList<>();
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
return ret;
}
@Override
public boolean needsSemicolon() {
@@ -43,6 +43,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
@@ -180,6 +181,20 @@ public class ForInActionItem extends LoopActionItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
@@ -28,6 +28,7 @@ import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
@@ -96,6 +97,11 @@ public class IfFrameLoadedActionItem extends ActionItem implements Block {
return new ArrayList<>();
}
@Override
public List<BreakItem> getBreaks() {
return new ArrayList<>();
}
@Override
public List<List<GraphTargetItem>> getSubs() {
List<List<GraphTargetItem>> ret = new ArrayList<>();
@@ -32,6 +32,7 @@ import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphSourceItemPos;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
@@ -144,6 +145,20 @@ public class TellTargetActionItem extends ActionItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public List<List<GraphTargetItem>> getSubs() {
@@ -42,6 +42,7 @@ import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
@@ -191,6 +192,40 @@ public class TryActionItem extends ActionItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : tryCommands) {
if (ti instanceof ContinueItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
if (finallyCommands != null) {
for (GraphTargetItem ti : finallyCommands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
}
for (List<GraphTargetItem> commands : catchCommands) {
for (GraphTargetItem ti : commands) {
if (ti instanceof ContinueItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
}
return ret;
}
@Override
public boolean needsSemicolon() {
@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import java.util.List;
@@ -33,6 +34,13 @@ public interface Block {
* @return List of continues
*/
public List<ContinueItem> getContinues();
/**
* Gets all sub breaks.
*
* @return List of breaks
*/
public List<BreakItem> getBreaks();
/**
* Gets all sub blocks.
@@ -1016,12 +1016,91 @@ public class Graph {
}
expandGotos(ret);
processIfs(ret);
processSwitches2(ret);
finalProcessStack(stack, ret, path);
makeAllCommands(ret, stack);
finalProcessAll(null, ret, 0, getFinalData(localData, loops, throwStates), path);
return ret;
}
/**
* This is needed to avoid loop identifiers in AS1/2. AS3 supports them, but AS1/2 not.
*
* loop1: switch(a) { //has loop identifier
* case 1:
* trace("1");
* break;
* case 2: //last case
* trace("2");
* switch(b) { //last command is switch
* case 3:
* case 4:
* trace("4");
* break loop1; //breaks parent loop
* case 5:
* trace("5");
* break loop1;
* case 6:
* trace("6");
* }
* }
*
* ==>
*
* switch(a) {
* case 1:
* trace("1");
* break;
* case 2:
* trace("2");
* switch(b) {
* case 3:
* case 4:
* trace("4");
* break;
* case 5:
* trace("5");
* break;
* case 6:
* trace("6");
* }
* }
* @param list Items
*/
protected void processSwitches2(List<GraphTargetItem> list) {
for (int i = 0; i < list.size(); i++) {
GraphTargetItem item = list.get(i);
if (item instanceof Block) {
Block bl = (Block) item;
for (List<GraphTargetItem> subList : bl.getSubs()) {
processSwitches2(subList);
}
}
if (item instanceof SwitchItem) {
SwitchItem sw = (SwitchItem) item;
if (sw.caseCommands.isEmpty()) {
continue;
}
List<GraphTargetItem> lastCase = sw.caseCommands.get(sw.caseCommands.size() - 1);
if (lastCase.isEmpty()) {
continue;
}
if (!(lastCase.get(lastCase.size() - 1) instanceof SwitchItem)) {
continue;
}
SwitchItem swInner = (SwitchItem) lastCase.get(lastCase.size() - 1);
List<BreakItem> breaks = swInner.getBreaks();
for (BreakItem br : breaks) {
if (br.loopId == sw.loop.id) {
br.loopId = swInner.loop.id;
}
}
}
}
}
/**
* Prepares second pass data. Can return null when no second pass will
* happen. Override this method to prepare second pass data.
@@ -1051,7 +1130,7 @@ public class Graph {
protected final void processSwitches(List<GraphTargetItem> list) {
processSwitches(list, -1);
}
/*
while(something){
@@ -158,6 +158,20 @@ public class DoWhileItem extends LoopItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
@@ -185,6 +185,20 @@ public class ForItem extends LoopItem implements Block {
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
return generator.generate(localData, this);
@@ -115,6 +115,20 @@ public class GotoItem extends GraphTargetItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
if (targetCommands == null) {
return ret;
}
for (GraphTargetItem c : targetCommands) {
if (c instanceof BreakItem) {
ret.add((BreakItem) c);
}
}
return ret;
}
@Override
public List<List<GraphTargetItem>> getSubs() {
@@ -194,6 +194,28 @@ public class IfItem extends GraphTargetItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : onTrue) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
for (GraphTargetItem ti : onFalse) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
@@ -187,6 +187,23 @@ public class SwitchItem extends LoopItem implements Block {
}
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (List<GraphTargetItem> onecase : caseCommands) {
for (GraphTargetItem ti : onecase) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
}
return ret;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
@@ -151,6 +151,20 @@ public class WhileItem extends LoopItem implements Block {
return ret;
}
@Override
public List<BreakItem> getBreaks() {
List<BreakItem> ret = new ArrayList<>();
for (GraphTargetItem ti : commands) {
if (ti instanceof BreakItem) {
ret.add((BreakItem) ti);
}
if (ti instanceof Block) {
ret.addAll(((Block) ti).getBreaks());
}
}
return ret;
}
@Override
public boolean needsSemicolon() {
return false;