AS3 parser: try..finally

This commit is contained in:
Jindra Pet��k
2014-04-06 20:08:20 +02:00
parent 7e19be36c8
commit e5a9b6d28c
10 changed files with 663 additions and 299 deletions
@@ -16,11 +16,15 @@
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference;
import com.jpexs.decompiler.flash.abc.types.ABCException;
import java.io.IOException;
import java.io.Serializable;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
@@ -33,6 +37,9 @@ public class SourceGeneratorLocalData implements Serializable {
public Boolean inMethod;
public Integer forInLevel;
public List<ABCException> exceptions = new ArrayList<>();
public List<Integer> finallyCatches = new ArrayList<Integer>();
public Map<Integer,Integer> finallyCounter = new HashMap<>();
public int finallyRegister = -1;
public String currentClass;
public int activationReg = 0;
@@ -1848,14 +1848,15 @@ public class AVM2Code implements Serializable {
private boolean walkCode(CodeStats stats, int pos, int stack, int scope, ABC abc) {
while (pos < code.size()) {
AVM2Instruction ins = code.get(pos);
if (stats.instructionStats[pos].seen) {
//check stack mismatch here
return true;
return true;
}
stats.instructionStats[pos].seen = true;
stats.instructionStats[pos].stackpos = stack;
stats.instructionStats[pos].scopepos = scope;
AVM2Instruction ins = code.get(pos);
int stackDelta = ins.definition.getStackDelta(ins, abc);
int scopeDelta = ins.definition.getScopeStackDelta(ins, abc);
int oldStack = stack;
@@ -1864,6 +1865,9 @@ public class AVM2Code implements Serializable {
stack += stackDelta;
scope += scopeDelta;
stats.instructionStats[pos].stackpos_after = stack;
stats.instructionStats[pos].scopepos_after = scope;
if (stack > stats.maxstack) {
stats.maxstack = stack;
}
@@ -1914,6 +1918,9 @@ public class AVM2Code implements Serializable {
}
if (ins.definition instanceof LookupSwitchIns) {
for (int i = 0; i < ins.operands.length; i++) {
if (i == 1) {
continue;
}
try {
int newpos = adr2pos(pos2adr(pos) + ins.operands[i]);
if (!walkCode(stats, newpos, stack, scope, abc)) {
@@ -1935,29 +1942,51 @@ public class AVM2Code implements Serializable {
return null;
}
int scopePos = -1;
for (ABCException ex : body.exceptions) {
int prevStart = 0;
for (int e=0;e<body.exceptions.length;e++) {
ABCException ex = body.exceptions[e];
try {
if(scopePos==-1){
scopePos=stats.instructionStats[adr2pos(ex.end)-1].scopepos;
if (scopePos == -1) {
scopePos = stats.instructionStats[adr2pos(ex.end) - 1].scopepos_after;
}
List<Integer> visited = new ArrayList<>();
for(int i=0;i<stats.instructionStats.length;i++){
if(stats.instructionStats[i].seen){
for (int i = 0; i < stats.instructionStats.length; i++) {
if (stats.instructionStats[i].seen) {
visited.add(i);
}
}
if (!walkCode(stats, adr2pos(ex.target), 1/*exception*/, scopePos, abc)) {
if (!walkCode(stats, adr2pos(ex.target), 1 + (ex.isFinally() ? 1 : 0), scopePos, abc)) {
return null;
}
int maxIp = 0;
//searching for visited instruction in second run which has maximum position
for(int i=0;i<stats.instructionStats.length;i++){
if(stats.instructionStats[i].seen && !visited.contains(i)){
for (int i = 0; i < stats.instructionStats.length; i++) {
if (stats.instructionStats[i].seen && !visited.contains(i)) {
maxIp = i;
}
}
scopePos = stats.instructionStats[maxIp].scopepos;
scopePos = stats.instructionStats[maxIp].scopepos_after;
int stackPos = stats.instructionStats[maxIp].stackpos_after;
int nextIp = maxIp + 1;
if(code.get(maxIp).definition instanceof JumpIns){
nextIp = adr2pos(pos2adr(nextIp)+code.get(maxIp).operands[0]);
}
int origScopePos = stats.instructionStats[nextIp].scopepos;
int origStackPos = stats.instructionStats[nextIp].stackpos;
if (prevStart==ex.start && ex.isFinally() && !code.get(nextIp).isExit() && stats.instructionStats[nextIp].seen) {
for (int i = 0; i < stats.instructionStats.length; i++) {
stats.instructionStats[i].seen = false;
}
//Rerun rest with new scopePos, stackPos
if (!walkCode(stats, nextIp, origStackPos+1/*magic!*/, scopePos - 1 /*magic!*/, abc)) {
return null;
}
scopePos--;
}
prevStart = ex.start;
} catch (ConvertException ex1) {
//ignore
}
}
//stats.maxscope+=initScope;
@@ -27,6 +27,8 @@ public class InstructionStats {
public boolean seen = false;
public int stackpos = 0;
public int scopepos = 0;
public int stackpos_after = 0;
public int scopepos_after = 0;
public AVM2Instruction ins;
public InstructionStats(AVM2Instruction ins) {
@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
@@ -26,6 +27,7 @@ import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.ExitItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.ArrayList;
import java.util.List;
public class ReturnValueAVM2Item extends AVM2Item implements ExitItem {
@@ -44,9 +46,7 @@ public class ReturnValueAVM2Item extends AVM2Item implements ExitItem {
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) {
return toSourceMerge(localData, generator, value,
new AVM2Instruction(0, new ReturnValueIns(), new int[]{}, new byte[0])
);
return ((AVM2SourceGenerator)generator).generate(localData, this);
}
@Override
@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
@@ -41,8 +42,7 @@ public class ReturnVoidAVM2Item extends AVM2Item implements ExitItem {
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) {
return toSourceMerge(localData, generator, new AVM2Instruction(0, new ReturnVoidIns(), new int[]{}, new byte[0])
);
return ((AVM2SourceGenerator)generator).generate(localData, this);
}
@Override
@@ -16,11 +16,16 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.LocalData;
import java.util.List;
public class ThrowAVM2Item extends AVM2Item {
@@ -45,4 +50,11 @@ public class ThrowAVM2Item extends AVM2Item {
public boolean hasReturnValue() {
return false;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) {
return ((AVM2SourceGenerator)generator).generate(localData, this);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.abc.avm2.parser.script;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
/**
*
* @author JPEXS
*/
public class FinallyJumpIns extends JumpIns {
private long finallyClauseId;
public FinallyJumpIns(long finallyClauseId) {
this.finallyClauseId = finallyClauseId;
}
public long getClauseId() {
return finallyClauseId;
}
}
@@ -95,11 +95,6 @@ public class NameAVM2Item extends AssignableAVM2Item {
public int getSlotScope() {
return slotScope;
}
public void setNs(GraphTargetItem ns) {
this.ns = ns;
@@ -111,12 +106,12 @@ public class NameAVM2Item extends AssignableAVM2Item {
public int getSlotNumber() {
return slotNumber;
}
}
public void setSlotNumber(int slotNumber) {
this.slotNumber = slotNumber;
}
}
public int getRegNumber() {
return regNumber;
}
@@ -189,8 +184,8 @@ public class NameAVM2Item extends AssignableAVM2Item {
return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true);
}
public static GraphTargetItem getDefaultValue(String type){
switch(type){
public static GraphTargetItem getDefaultValue(String type) {
switch (type) {
case "*":
return new UndefinedAVM2Item(null);
case "int":
@@ -201,7 +196,7 @@ public class NameAVM2Item extends AssignableAVM2Item {
return new NullAVM2Item(null);
}
}
public static AVM2Instruction generateCoerce(SourceGenerator generator, String type) {
AVM2Instruction ins;
switch (type) {
@@ -223,10 +218,10 @@ public class NameAVM2Item extends AssignableAVM2Item {
}
private List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) {
if (variableName != null && regNumber == -1 && slotNumber == -1 && ns == null) {
if (variableName != null && regNumber == -1 && slotNumber == -1 && ns == null) {
throw new RuntimeException("No register or slot set for " + variableName);
}
if (definition && assignedValue == null) {
if (definition && assignedValue == null) {
return new ArrayList<GraphSourceItem>();
}
AVM2SourceGenerator g = (AVM2SourceGenerator) generator;
@@ -276,7 +271,7 @@ public class NameAVM2Item extends AssignableAVM2Item {
if (index != null) {
if (assignedValue != null) {
return toSourceMerge(localData, generator,
generateGetLoc(regNumber),generateGetSlot(slotScope,slotNumber), index, assignedValue,
generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber), index, assignedValue,
needsReturn ? dupSetTemp(localData, generator, ret_temp) : null,
ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList<Integer>()), true)),
needsReturn ? getTemp(localData, generator, ret_temp) : null,
@@ -292,25 +287,23 @@ public class NameAVM2Item extends AssignableAVM2Item {
}
if (assignedValue != null) {
List<String> basicTypes = Arrays.asList("int","Number");
if(slotNumber>-1)
{
return toSourceMerge(localData, generator,
ins(new GetScopeObjectIns(),slotScope),
assignedValue, !((""+assignedValue.returnType()).equals(""+type)&&(basicTypes.contains(""+type))) ? generateCoerce(generator, "" + type) : null, needsReturn
? dupSetTemp(localData, generator, ret_temp) : null, generateSetLoc(regNumber),slotNumber>-1?
ins(new SetSlotIns(),slotNumber)
:null,
needsReturn?getTemp(localData, generator, ret_temp):null,
killTemp(localData, generator, Arrays.asList(ret_temp)));
}
else{
return toSourceMerge(localData, generator, assignedValue, !((""+assignedValue.returnType()).equals(""+type)&&(basicTypes.contains(""+type))) ? generateCoerce(generator, "" + type) : null, needsReturn
? ins(new DupIns()) : null, generateSetLoc(regNumber));
List<String> basicTypes = Arrays.asList("int", "Number");
if (slotNumber > -1) {
return toSourceMerge(localData, generator,
ins(new GetScopeObjectIns(), slotScope),
assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(generator, "" + type) : null, needsReturn
? dupSetTemp(localData, generator, ret_temp) : null, generateSetLoc(regNumber), slotNumber > -1
? ins(new SetSlotIns(), slotNumber)
: null,
needsReturn ? getTemp(localData, generator, ret_temp) : null,
killTemp(localData, generator, Arrays.asList(ret_temp)));
} else {
return toSourceMerge(localData, generator, assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(generator, "" + type) : null, needsReturn
? ins(new DupIns()) : null, generateSetLoc(regNumber));
}
} else {
return toSourceMerge(localData, generator, generateGetLoc(regNumber),generateGetSlot(slotScope,slotNumber),
return toSourceMerge(localData, generator, generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber),
needsReturn ? null : ins(new PopIns()));
}
}
@@ -431,7 +424,7 @@ public class NameAVM2Item extends AssignableAVM2Item {
if (index != null) {
return toSourceMerge(localData, generator,
generateGetLoc(regNumber),generateGetSlot(slotScope,slotNumber), dupSetTemp(localData, generator, name_temp), index, dupSetTemp(localData, generator, index_temp),
generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber), dupSetTemp(localData, generator, name_temp), index, dupSetTemp(localData, generator, index_temp),
//Start get original
//generateGetLoc(regNumber), getTemp(localData, generator, index_temp),
ins(new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList<Integer>()), true)),
@@ -449,23 +442,23 @@ public class NameAVM2Item extends AssignableAVM2Item {
);
}
if(!needsReturn){
if(slotNumber>-1){
if (!needsReturn) {
if (slotNumber > -1) {
return toSourceMerge(localData, generator,
ins(new GetScopeObjectIns(),slotScope),
generateGetSlot(slotScope,slotNumber),
ins(new GetScopeObjectIns(), slotScope),
generateGetSlot(slotScope, slotNumber),
(decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())),
ins(new SetSlotIns(),slotNumber)
ins(new SetSlotIns(), slotNumber)
);
}else{
} else {
return toSourceMerge(localData, generator,
ins(isInteger?new IncLocalIIns():new IncLocalIns(),regNumber));
ins(isInteger ? new IncLocalIIns() : new IncLocalIns(), regNumber));
}
}
return toSourceMerge(localData, generator,
slotNumber>-1?ins(new GetScopeObjectIns(),slotScope):null,
slotNumber > -1 ? ins(new GetScopeObjectIns(), slotScope) : null,
//Start get original
generateGetLoc(regNumber),generateGetSlot(slotScope,slotNumber),
generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber),
//End get original
!isInteger ? ins(new ConvertDIns()) : null,
//End get original
@@ -473,7 +466,7 @@ public class NameAVM2Item extends AssignableAVM2Item {
needsReturn ? ins(new DupIns()) : null,
(post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null,
generateSetLoc(regNumber),
slotNumber>-1?ins(new SetSlotIns(),slotNumber):null
slotNumber > -1 ? ins(new SetSlotIns(), slotNumber) : null
);
}
@@ -1,6 +1,5 @@
TODO List for AS3 parser/compiler:
------------------------------
- finally clause
- E4X (XML)
- typenames (Vectors)
- inner functions (activation, slots, etc.)