Fixed: AS decompilation - §§push before loop

This commit is contained in:
Jindra Petřík
2021-04-03 19:25:57 +02:00
parent a290ebac0d
commit 0a4dc74c61
10 changed files with 176 additions and 13 deletions

View File

@@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file.
- Rename invalid identifiers renames identifiers with a dollar sign
- [#1676] Messages on movie tags when Flash Player ActiveX not available
- [#1677] DefineFont2/3 - missing codeTableOffset if numGlyphs is zero and font has layout
- AS decompilation - §§push before loop
## [14.3.1] - 2021-03-25
### Fixed

View File

@@ -1611,7 +1611,7 @@ public class AVM2Graph extends Graph {
}
@Override
protected GraphTargetItem checkLoop(List<GraphTargetItem> output, LoopItem loopItem, BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates) {
protected GraphTargetItem checkLoop(List<GraphTargetItem> output, LoopItem loopItem, BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates, TranslateStack stack) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
if (loopItem instanceof WhileItem) {
WhileItem w = (WhileItem) loopItem;
@@ -1620,6 +1620,22 @@ public class AVM2Graph extends Graph {
HasNextAVM2Item hn = (HasNextAVM2Item) w.expression.get(w.expression.size() - 1);
if (hn.obj != null) {
if (hn.obj.getNotCoerced().getThroughRegister().getNotCoerced() instanceof FilteredCheckAVM2Item) {
//All items are moved from stack to output before entering while,
// this code block moves them back to stack
int pushnum = 0;
for (int i = output.size() - 2 /*last is loop*/; i >= 0; i--) {
if (output.get(i) instanceof PushItem) {
pushnum++;
} else {
break;
}
}
int rem = output.size() - 1 - pushnum;
for (int i = output.size() - 1 - pushnum; i <= output.size() - 2; i++) {
stack.push(((PushItem) output.remove(rem)).value);
}
//---------- end moving back to stack
if (w.commands.size() >= 3) {
int pos = 0;
Set<Integer> localRegsToKill = new HashSet<>();

View File

@@ -35,6 +35,7 @@ import com.jpexs.decompiler.graph.GraphSourceItemPos;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.GraphTargetVisitorInterface;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.model.BranchStackResistant;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
@@ -48,7 +49,7 @@ import java.util.Set;
*
* @author JPEXS
*/
public class FunctionActionItem extends ActionItem {
public class FunctionActionItem extends ActionItem implements BranchStackResistant {
public List<GraphTargetItem> actions;

View File

@@ -1350,7 +1350,7 @@ public class Graph {
return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, null, staticOperation, path, 0);
}
protected GraphTargetItem checkLoop(List<GraphTargetItem> output, LoopItem loopItem, BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates) {
protected GraphTargetItem checkLoop(List<GraphTargetItem> output, LoopItem loopItem, BaseLocalData localData, List<Loop> loops, List<ThrowState> throwStates, TranslateStack stack) {
return loopItem;
}
@@ -1901,6 +1901,9 @@ public class Graph {
}
}
if (isLoop) {
makeAllCommands(ret, stack);
}
if (debugPrintGraph) {
System.err.println("loopsize:" + loops.size());
}
@@ -2675,7 +2678,7 @@ public class Graph {
}
currentLoop.phase = 2;
GraphTargetItem replaced = checkLoop(ret, li, localData, loops, throwStates);
GraphTargetItem replaced = checkLoop(ret, li, localData, loops, throwStates, sPreLoop);
if (replaced != li) {
int index = ret.indexOf(li);
ret.remove(index);
@@ -2951,21 +2954,18 @@ public class Graph {
clen--;
}
}
while (stack.size() > 0) {
GraphTargetItem p = stack.pop();
for (int i = stack.size() - 1; i >= 0; i--) {
GraphTargetItem p = stack.get(i);
if (p instanceof BranchStackResistant) {
continue;
}
stack.remove(i);
if (!(p instanceof PopItem)) {
if (p instanceof FunctionActionItem) {
if (isExit) {
//ASC2 leaves some function calls unpopped on stack before returning from a method
commands.add(clen, p);
} else {
if (isExit) {
//ASC2 leaves some function calls unpopped on stack before returning from a method
commands.add(clen, p);
} else {
commands.add(clen, new PushItem(p));
}
commands.add(clen, new PushItem(p));
}
}
}

View File

@@ -131,6 +131,22 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT
false);
}
@Test
public void testPushWhile() {
decompileMethod("assembled", "testPushWhile", "var _loc3_:int = 5;\r\n"
+ "§§push(obfuscated[\"xxx\"] = new [\"ByteArray\"]());\r\n"
+ "§§push(50);\r\n"
+ "while(§§dup(§§pop()))\r\n"
+ "{\r\n"
+ "§§dup(§§pop())[§§dup(§§dup(§§pop())).length] = 41 ^ 111;\r\n"
+ "§§dup(§§pop())[§§dup(§§dup(§§pop())).length] = 9 ^ 84;\r\n"
+ "§§push(§§pop() - 1);\r\n"
+ "}\r\n"
+ "§§pop();\r\n"
+ "§§pop();\r\n",
false);
}
@Test
public void testSetSlotDup() {
decompileMethod("assembled", "testSetSlotDup", "var _loc5_:int = 5;\r\n"

View File

@@ -28,5 +28,6 @@ program
#include "tests/TestDecrementPrecedence.script.asasm"
#include "tests/TestSwitchGoto.script.asasm"
#include "tests/TestTryWhile.script.asasm"
#include "tests/TestPushWhile.script.asasm"
; place to add next
end ; program

View File

@@ -0,0 +1,99 @@
class
refid "tests:TestPushWhile"
instance QName(PackageNamespace("tests"), "TestPushWhile")
extends QName(PackageNamespace(""), "Object")
flag SEALED
flag PROTECTEDNS
protectedns ProtectedNamespace("tests:TestPushWhile")
iinit
refid "tests:TestPushWhile/instance/init"
body
maxstack 1
localcount 1
initscopedepth 4
maxscopedepth 5
code
getlocal0
pushscope
getlocal0
constructsuper 0
returnvoid
end ; code
end ; body
end ; method
trait method QName(PackageNamespace(""), "run")
method
refid "tests:TestPushWhile/instance/run"
returns QName(PackageNamespace(""), "void")
body
maxstack 2
localcount 4
initscopedepth 4
maxscopedepth 5
code
getlocal0
pushscope
pushbyte 5
setlocal 3
pushstring "ByteArray"
dup
findpropstrict MultinameL([PackageNamespace("flash.utils","1")])
swap
constructprop MultinameL([PackageNamespace("flash.utils","1")]), 0
dup
getlex QName(PackageNamespace("","2"),"obfuscated")
swap
pushstring "xxx"
swap
setproperty MultinameL([PackageNamespace("","2")])
pushint 50
ofs0016:
label
dup
iffalse ofs0039
decrement_i
swap
dup
dup
getproperty QName(PackageNamespace("","2"),"length")
pushbyte 41
pushbyte 111
bitxor
setproperty MultinameL([PackageNamespace("","2")])
dup
dup
getproperty QName(PackageNamespace("","2"),"length")
pushbyte 9
pushbyte 84
bitxor
setproperty MultinameL([PackageNamespace("","2")])
swap
jump ofs0016
ofs0039:
pop
pop
returnvoid
end ; code
end ; body
end ; method
end ; trait
end ; instance
cinit
refid "tests:TestPushWhile/class/init"
body
maxstack 1
localcount 1
initscopedepth 3
maxscopedepth 4
code
getlocal0
pushscope
returnvoid
end ; code
end ; body
end ; method
end ; class

View File

@@ -0,0 +1,29 @@
script
sinit
refid "tests:TestPushWhile/init"
body
maxstack 2
localcount 1
initscopedepth 1
maxscopedepth 3
code
getlocal0
pushscope
findpropstrict Multiname("TestPushWhile", [PackageNamespace("tests")])
getlex QName(PackageNamespace(""), "Object")
pushscope
getlex Multiname("Object", [PrivateNamespace(null, "tests:TestPushWhile"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")])
newclass "tests:TestPushWhile"
popscope
initproperty QName(PackageNamespace("tests"), "TestPushWhile")
returnvoid
end ; code
end ; body
end ; method
trait class QName(PackageNamespace("tests"), "TestPushWhile")
#include "TestPushWhile.class.asasm"
end ; trait
end ; script