AS3 switch compilation reorganized

This commit is contained in:
Jindra Petřík
2025-09-04 19:45:58 +02:00
parent b8f3b49b0e
commit 3f1ed100be
4 changed files with 89 additions and 50 deletions

View File

@@ -1292,7 +1292,7 @@ public class AVM2Graph extends Graph {
AVM2LocalData localData2 = new AVM2LocalData(localData);
//localData2.scopeStack = new ScopeStack();
localData2.localScopeStack = new ScopeStack();
try {
//We are assuming Finally target has only 1 part
translatePart(finallyTargetItems, localData2, finallyTryTargetPart, st2, staticOperation, path);
@@ -2286,14 +2286,14 @@ public class AVM2Graph extends Graph {
}
int firstPushPos = -1;
for (int i = output.size() - 2 /*last is loop*/; i >= 0; i--) {
if (output.get(i) instanceof PushItem) {
firstPushPos = i;
} else {
break;
}
}
}
if (firstPushPos > -1) {
int max = output.size() - 2;
for (int i = firstPushPos; i <= max; i++) {
@@ -2566,7 +2566,7 @@ public class AVM2Graph extends Graph {
if (debugDoNotProcess) {
return;
}
loopi:
for (int i = 1 /*not first*/; i < list.size(); i++) {
GraphTargetItem item = list.get(i);
@@ -2644,7 +2644,6 @@ public class AVM2Graph extends Graph {
}
}
List<Integer> thisExpressions = new ArrayList<>();
int defaultIndex = -1;
for (int k = 0; k < thisSwitch.caseValues.size(); k++) {
if (thisSwitch.caseValues.get(k) instanceof DefaultItem) {
@@ -2654,6 +2653,47 @@ public class AVM2Graph extends Graph {
if (!(thisSwitch.caseValues.get(k) instanceof IntegerValueTypeItem)) {
continue loopi;
}
}
if (defaultIndex > -1 && !thisSwitch.additionalDefaultValues.isEmpty() && thisSwitch.additionalDefaultPosition > -1) {
int defaultBodyIndex = thisSwitch.valuesMapping.get(defaultIndex);
if (thisSwitch.additionalDefaultPosition != defaultIndex) {
int previousBodyIndex = thisSwitch.valuesMapping.get(thisSwitch.additionalDefaultPosition);
List<GraphTargetItem> defaultBody = thisSwitch.caseCommands.remove(defaultBodyIndex);
thisSwitch.caseCommands.add(previousBodyIndex, defaultBody);
for (int p = 0; p < thisSwitch.valuesMapping.size(); p++) {
if (thisSwitch.valuesMapping.get(p) >= previousBodyIndex) {
thisSwitch.valuesMapping.set(p, thisSwitch.valuesMapping.get(p) + 1);
}
}
defaultBodyIndex = previousBodyIndex;
GraphTargetItem lastCmd = defaultBody.isEmpty() ? null : defaultBody.get(defaultBody.size() - 1);
if (lastCmd != null && !(lastCmd instanceof ExitItem) && !(lastCmd instanceof BreakItem) && !(lastCmd instanceof ContinueItem)) {
defaultBody.add(new BreakItem(thisSwitch.dialect, null, null, thisSwitch.loop.id));
}
}
thisSwitch.caseValues.remove(defaultIndex);
thisSwitch.valuesMapping.remove(defaultIndex);
thisSwitch.caseValues.add(thisSwitch.additionalDefaultPosition, new DefaultItem(thisSwitch.dialect));
thisSwitch.valuesMapping.add(thisSwitch.additionalDefaultPosition, defaultBodyIndex);
for (int m = thisSwitch.additionalDefaultValues.size() - 1; m >= 0; m--) {
thisSwitch.caseValues.add(thisSwitch.additionalDefaultPosition, thisSwitch.additionalDefaultValues.get(m));
thisSwitch.valuesMapping.add(thisSwitch.additionalDefaultPosition, defaultBodyIndex);
}
}
List<Integer> thisExpressions = new ArrayList<>();
for (int k = 0; k < thisSwitch.caseValues.size(); k++) {
if (thisSwitch.caseValues.get(k) instanceof DefaultItem) {
continue;
}
if (!(thisSwitch.caseValues.get(k) instanceof IntegerValueTypeItem)) {
continue loopi;
}
int cv = ((IntegerValueTypeItem) thisSwitch.caseValues.get(k)).intValue();
/*if (!expressionsMap.containsKey(cv)) {
continue loopi;
@@ -2674,32 +2714,8 @@ public class AVM2Graph extends Graph {
return o1 - o2;
}
});
/*if (!noCaseExpressions.isEmpty() && defaultIndex == -1) {
thisSwitch.caseValues.add(new DefaultItem(dialect));
thisSwitch.valuesMapping.add(thisSwitch.caseCommands.size());
if (!thisSwitch.caseCommands.isEmpty()) {
List<GraphTargetItem> lastCommands = thisSwitch.caseCommands.get(thisSwitch.caseCommands.size() - 1);
if (
lastCommands.isEmpty()
|| !(
(lastCommands.get(lastCommands.size() - 1) instanceof ExitItem)
|| (lastCommands.get(lastCommands.size() - 1) instanceof ContinueItem)
|| (lastCommands.get(lastCommands.size() - 1) instanceof BreakItem)
)
) {
lastCommands.add(new BreakItem(dialect, null, null, thisSwitch.loop.id));
}
}
thisSwitch.caseCommands.add(new ArrayList<>());
}*/
for (int k = 0; k < thisSwitch.caseValues.size(); k++) {
/*if (thisSwitch.caseValues.get(k) instanceof DefaultItem) {
thisSwitch.removeValue(k);
k--;
continue;
}*/
if (thisSwitch.caseValues.get(k) instanceof DefaultItem) {
int m = thisSwitch.valuesMapping.get(k);
thisSwitch.caseValues.remove(k);
@@ -2730,12 +2746,12 @@ public class AVM2Graph extends Graph {
continue;
}
int cv = ((IntegerValueTypeItem) thisSwitch.caseValues.get(k)).intValue();
if (!expressionsMap.containsKey(cv)) {
thisSwitch.removeValue(k);
k--;
continue;
}
}
thisSwitch.caseValues.set(k, expressionsMap.get(cv));
}

View File

@@ -2909,34 +2909,33 @@ public class AVM2SourceGenerator implements SourceGenerator {
ret.add(forwardJump);
int defIndex = item.caseValues.size();
boolean hasDefault = false;
for (int i = item.caseValues.size() - 1; i >= 0; i--) {
if (item.caseValues.get(i) instanceof DefaultItem) {
hasDefault = true;
defIndex = i;
break;
}
}
int realCasesCount = item.caseValues.size() + (hasDefault ? -1 : 0);
List<AVM2Instruction> cases = new ArrayList<>();
cases.addAll(toInsList(new IntegerValueAVM2Item(null, null, defIndex).toSource(localData, this)));
int cLen = insToBytes(cases).length;
List<AVM2Instruction> caseLast = new ArrayList<>();
caseLast.add(0, ins(AVM2Instructions.Jump, cLen));
caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, null, defIndex).toSource(localData, this)));
int cLastLen = insToBytes(caseLast).length;
caseLast.add(0, ins(AVM2Instructions.Jump, cLastLen));
cases.addAll(0, caseLast);
cases.addAll(0, toInsList(new IntegerValueAVM2Item(null, null, -1).toSource(localData, this)));
List<AVM2Instruction> preCases = new ArrayList<>();
preCases.addAll(toInsList(item.switchedObject.toSource(localData, this)));
preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg)));
int pos = realCasesCount - 1;
for (int i = item.caseValues.size() - 1; i >= 0; i--) {
if (item.caseValues.get(i) instanceof DefaultItem) {
continue;
}
}
List<AVM2Instruction> sub = new ArrayList<>();
sub.addAll(toInsList(new IntegerValueAVM2Item(null, null, i).toSource(localData, this)));
sub.addAll(toInsList(new IntegerValueAVM2Item(null, null, pos--).toSource(localData, this)));
sub.add(ins(AVM2Instructions.Jump, insToBytes(cases).length));
int subLen = insToBytes(sub).length;
@@ -2946,8 +2945,10 @@ public class AVM2SourceGenerator implements SourceGenerator {
cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this)));
}
cases.addAll(0, preCases);
AVM2Instruction lookupOp = new AVM2Instruction(0, AVM2Instructions.LookupSwitch, new int[1 + 1 + item.caseValues.size() + 1]);
AVM2Instruction lookupOp = new AVM2Instruction(0, AVM2Instructions.LookupSwitch, new int[1 + 1 + realCasesCount]);
cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg))));
List<AVM2Instruction> bodies = new ArrayList<>();
List<Integer> bodiesOffsets = new ArrayList<>();
@@ -2956,18 +2957,25 @@ public class AVM2SourceGenerator implements SourceGenerator {
bodies.add(0, ins(AVM2Instructions.Label));
bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares...
defOffset = -(insToBytes(bodies).length + casesLen);
for (int i = item.caseCommands.size() - 1; i >= 0; i--) {
for (int i = item.caseCommands.size() - 1; i >= 0; i--) {
bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i)));
bodies.add(0, ins(AVM2Instructions.Label));
bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen));
}
lookupOp.operands[0] = defOffset;
lookupOp.operands[1] = item.valuesMapping.size();
// as per avm2 spec: "There are case_count+1 case offsets"
for (int i = 0; i < item.valuesMapping.size(); i++) {
lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i));
if (hasDefault) {
defOffset = bodiesOffsets.get(item.valuesMapping.get(defIndex));
}
lookupOp.operands[2 + item.valuesMapping.size()] = defOffset;
lookupOp.operands[0] = defOffset;
lookupOp.operands[1] = realCasesCount - 1;
// as per avm2 spec: "There are case_count+1 case offsets"
pos = 0;
for (int i = 0; i < item.valuesMapping.size(); i++) {
if (item.caseValues.get(i) instanceof DefaultItem) {
continue;
}
lookupOp.operands[2 + pos++] = bodiesOffsets.get(item.valuesMapping.get(i));
}
//lookupOp.operands[2 + item.valuesMapping.size()] = defOffset;
forwardJump.operands[0] = insToBytes(bodies).length;
ret.addAll(bodies);

View File

@@ -3753,10 +3753,13 @@ public class Graph {
GraphPart defaultPart = hasExpr ? getNextParts(localData, part).get(1 + defaultBranch) : getNextParts(localData, part).get(0);
List<GraphPart> caseBodyParts = new ArrayList<>();
List<GraphTargetItem> additionalDefaultValues = new ArrayList<>();
int additionalDefaultPosition = -1;
for (int i = 1; i < getNextParts(localData, part).size(); i++) {
if (!hasExpr) {
//This if probably should be removed, but it fails some tests...
if (getNextParts(localData, part).get(i) == defaultPart) {
additionalDefaultPosition = caseValues.size();
additionalDefaultValues.add(new IntegerValueItem(dialect, null, localData.lineStartInstruction, pos));
pos++;
continue;
}
@@ -3781,6 +3784,8 @@ public class Graph {
makeAllCommands(currentRet, stack);
SwitchItem sw = handleSwitch(switchedItem, originalSwitchedItem.getSrc(), foundGotos, partCodes, partCodePos, visited, allParts, stack, stopPart, stopPartKind, loops, throwStates, localData, staticOperation, path,
caseValues, defaultPart, caseBodyParts, nextRef, tiRef);
sw.additionalDefaultPosition = additionalDefaultPosition;
sw.additionalDefaultValues = additionalDefaultValues;
GraphPart next = nextRef.getVal();
checkSwitch(localData, sw, caseExpressionOtherSides.values(), currentRet);
currentRet.add(sw);

View File

@@ -58,7 +58,17 @@ public class SwitchItem extends LoopItem implements Block {
* Values mapping
*/
public List<Integer> valuesMapping;
/**
* Additional default position. Mark for additional processing...
*/
public int additionalDefaultPosition = -1;
/**
* Additional default values - they are not printed. This is just mark for additional processing...
*/
public List<GraphTargetItem> additionalDefaultValues = new ArrayList<>();
/**
* Label used
*/