AS3 parser: for in, for each in, better variable declarations

This commit is contained in:
Jindra Pet��k
2014-04-05 16:48:23 +02:00
parent 9182b15286
commit 8deb0a81b7
10 changed files with 164 additions and 72 deletions
@@ -43,4 +43,11 @@ public class HasNext2Ins extends InstructionDefinition {
//stack.push("_loc_" + objectReg + ".hasNext(cnt=_loc_" + indexReg + ")");
stack.push(new HasNextAVM2Item(ins, new LocalRegAVM2Item(ins, indexReg, localRegs.get(indexReg)), localRegs.get(objectReg)));
}
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return 1;
}
}
@@ -46,5 +46,5 @@ public class HasNextIns extends InstructionDefinition {
@Override
public int getStackDelta(AVM2Instruction ins, ABC abc) {
return -2 + 1;
}
}
}
@@ -111,16 +111,16 @@ public abstract class AVM2Item extends GraphTargetItem {
return ret;
}
protected AVM2Instruction ins(InstructionDefinition def, int... operands) {
public static AVM2Instruction ins(InstructionDefinition def, int... operands) {
return new AVM2Instruction(0, def, operands, new byte[0]);
}
protected int getFreeRegister(SourceGeneratorLocalData localData, SourceGenerator generator) {
public static int getFreeRegister(SourceGeneratorLocalData localData, SourceGenerator generator) {
AVM2SourceGenerator g = (AVM2SourceGenerator) generator;
return g.getFreeRegister(localData);
}
protected void killRegister(SourceGeneratorLocalData localData, SourceGenerator generator, int regNumber) {
public static void killRegister(SourceGeneratorLocalData localData, SourceGenerator generator, int regNumber) {
AVM2SourceGenerator g = (AVM2SourceGenerator) generator;
g.killRegister(localData, regNumber);
}
@@ -16,9 +16,11 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model.clauses;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.LoopWithType;
import com.jpexs.decompiler.flash.helpers.NulWriter;
@@ -26,6 +28,7 @@ import com.jpexs.decompiler.graph.Block;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
@@ -128,4 +131,11 @@ public class ForEachInAVM2Item extends LoopItem implements Block {
public GraphTargetItem returnType() {
return TypeItem.UNBOUNDED;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) {
return ((AVM2SourceGenerator)generator).generate(localData, this);
}
}
@@ -16,9 +16,11 @@
*/
package com.jpexs.decompiler.flash.abc.avm2.model.clauses;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.LoopWithType;
import com.jpexs.decompiler.flash.helpers.NulWriter;
@@ -26,6 +28,7 @@ import com.jpexs.decompiler.graph.Block;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.Loop;
import com.jpexs.decompiler.graph.SourceGenerator;
import com.jpexs.decompiler.graph.TypeItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.LocalData;
@@ -128,4 +131,9 @@ public class ForInAVM2Item extends LoopItem implements Block {
public GraphTargetItem returnType() {
return TypeItem.UNBOUNDED;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) {
return ((AVM2SourceGenerator)generator).generate(localData, this);
}
}
@@ -34,13 +34,17 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns;
import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item;
@@ -50,6 +54,8 @@ import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.operations.IfCondition;
import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException;
@@ -70,6 +76,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate;
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;
@@ -82,6 +89,7 @@ import com.jpexs.decompiler.graph.model.DoWhileItem;
import com.jpexs.decompiler.graph.model.DuplicateItem;
import com.jpexs.decompiler.graph.model.ForItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.LocalData;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.OrItem;
import com.jpexs.decompiler.graph.model.SwitchItem;
@@ -91,6 +99,7 @@ import com.jpexs.decompiler.graph.model.WhileItem;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
@@ -298,6 +307,81 @@ public class AVM2SourceGenerator implements SourceGenerator {
fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id);
return ret;
}
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) {
return generateForIn(localData,item.expression.collection,(AssignableAVM2Item)item.expression.object,item.commands,true);
}
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, ForInAVM2Item item) {
return generateForIn(localData,item.expression.collection,(AssignableAVM2Item)item.expression.object,item.commands,false);
}
public List<GraphSourceItem> generateForIn(SourceGeneratorLocalData localData,GraphTargetItem collection, AssignableAVM2Item assignable,List<GraphTargetItem> commands, final boolean each) {
List<GraphSourceItem> ret = new ArrayList<>();
final Reference<Integer> counterReg = new Reference<>(0);
final Reference<Integer> collectionReg = new Reference<>(0);
ret.addAll(GraphTargetItem.toSourceMerge(localData, this,
ins(new PushByteIns(),0),
AssignableAVM2Item.setTemp(localData, this, counterReg),
collection,
NameAVM2Item.generateCoerce(this, "*"),
AssignableAVM2Item.setTemp(localData, this, collectionReg)
));
GraphTargetItem assigned=new GraphTargetItem() {
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
return null;
}
@Override
public boolean hasReturnValue() {
return true;
}
@Override
public GraphTargetItem returnType() {
return TypeItem.UNBOUNDED;
}
@Override
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) {
return toSourceMerge(localData, generator,
AssignableAVM2Item.getTemp(localData, generator, collectionReg),
AssignableAVM2Item.getTemp(localData, generator, counterReg),
ins(each?new NextValueIns():new NextNameIns())
);
}
};
assignable.setAssignedValue(assigned);
List<AVM2Instruction> forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this,
ins(new LabelIns()),
assignable.toSourceIgnoreReturnValue(localData, this)
));
forBody.addAll(generateToActionList(localData, commands));
int forBodyLen = insToBytes(forBody).length;
AVM2Instruction forwardJump = ins(new JumpIns(),forBodyLen);
ret.add(forwardJump);
List<AVM2Instruction> expr = new ArrayList<>();
expr.add(ins(new HasNext2Ins(),collectionReg.getVal(),counterReg.getVal()));
AVM2Instruction backIf = ins(new IfTrueIns(),0);
expr.add(backIf);
int exprLen = insToBytes(expr).length;
backIf.operands[0] = -(exprLen + forBodyLen);
ret.addAll(forBody);
ret.addAll(expr);
ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg,counterReg)));
return ret;
}
@Override
public List<GraphSourceItem> generate(SourceGeneratorLocalData localData, DoWhileItem item) {
@@ -1335,6 +1419,15 @@ public class AVM2SourceGenerator implements SourceGenerator {
for (int i = 0; i < registerNames.size(); i++) {
localData.registerVars.put(registerNames.get(i), i);
}
List<NameAVM2Item> declarations=new ArrayList<>();
for (NameAVM2Item n : fun.subvariables) {
if(n.isDefinition() && n.getAssignedValue() == null){
NameAVM2Item d=new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue(""+n.type), true, n.openedNamespaces);
d.setRegNumber(n.getRegNumber());
declarations.add(d);
}
}
fun.body.addAll(0,declarations);
}
public int resolveType(String objType) {
@@ -987,62 +987,34 @@ public class ActionScriptParser {
expectedType(SymbolType.PARENT_CLOSE);
ret = new DoWhileItem(null, dloop, doBody, doExpr);
break;
case FOR:
expectedType(SymbolType.PARENT_OPEN);
case FOR:
s = lex();
boolean forin = false;
boolean each = false;
GraphTargetItem collection = null;
String objIdent = null;
int innerExprReg = 0;
GraphTargetItem collection = null;
if (s.type == SymbolType.EACH) {
each = true;
forin = true;
s = lex();
}
if (s.type == SymbolType.VAR || s.type == SymbolType.IDENTIFIER || each) {
ParsedSymbol s2 = null;
ParsedSymbol ssel = s;
if (s.type == SymbolType.VAR) {
s2 = lex();
ssel = s2;
expected(s, lexer.yyline(), SymbolType.PARENT_OPEN);
GraphTargetItem firstCommand=command(importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, false, variables);
if(firstCommand instanceof NameAVM2Item){
NameAVM2Item nai=(NameAVM2Item)firstCommand;
if(nai.isDefinition() && nai.getAssignedValue() == null){
firstCommand = expressionRemainder(openedNamespaces, firstCommand, registerVars, inFunction, inMethod, true, variables, importedClasses);
}
if (forin) {
expected(ssel, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.VAR);
}
if (ssel.type == SymbolType.IDENTIFIER) {
objIdent = ssel.value.toString();
ParsedSymbol s3 = lex();
if (s3.type == SymbolType.IN) {
if (inFunction) {
for (int i = 0; i < 256; i++) {
if (!registerVars.containsValue(i)) {
registerVars.put(objIdent, i);
innerExprReg = i;
break;
}
}
}
collection = expression(importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);
forin = true;
} else {
lexer.pushback(s3);
if (s2 != null) {
lexer.pushback(s2);
}
lexer.pushback(s);
}
} else {
if (s2 != null) {
lexer.pushback(s2);
}
lexer.pushback(s);
}
} else {
lexer.pushback(s);
}
InAVM2Item inexpr = null;
if(firstCommand instanceof InAVM2Item){
forin = true;
inexpr = (InAVM2Item)firstCommand;
}else{
if(forin){
throw new ParseException("In expression required", lexer.yyline());
}
}
Loop floop = new Loop(uniqId(), null, null);
loops.push(floop);
if (loopLabel != null) {
@@ -1052,9 +1024,9 @@ public class ActionScriptParser {
GraphTargetItem forExpr = null;
List<GraphTargetItem> forFirstCommands = new ArrayList<>();
if (!forin) {
GraphTargetItem fc = command(importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables);
if (fc != null) { //can be empty command
forFirstCommands.add(fc);
//GraphTargetItem firstCommand = command(importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables);
if (firstCommand != null) { //can be empty command
forFirstCommands.add(firstCommand);
}
forExpr = (expression(importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables));
expectedType(SymbolType.SEMICOLON);
@@ -1063,15 +1035,12 @@ public class ActionScriptParser {
expectedType(SymbolType.PARENT_CLOSE);
List<GraphTargetItem> forBody = new ArrayList<>();
forBody.add(command(importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables));
if (forin) {
NameAVM2Item obj = new NameAVM2Item(new UnboundedTypeItem(), lexer.yyline(), objIdent, null, false, openedNamespaces);
variables.add(obj);
if (forin) {
if (each) {
ret = new ForEachInAVM2Item(null, floop, new InAVM2Item(null, obj, collection), forBody);
ret = new ForEachInAVM2Item(null, floop, inexpr, forBody);
} else {
ret = new ForInAVM2Item(null, floop, new InAVM2Item(null, obj, collection), forBody);
ret = new ForInAVM2Item(null, floop, inexpr, forBody);
}
} else {
ret = new ForItem(null, floop, forFirstCommands, forExpr, forFinalCommands, forBody);
@@ -1315,6 +1284,9 @@ public class ActionScriptParser {
ret = name;
}
break;
case IN:
ret = new InAVM2Item(null, expr, expression(importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables));
break;
case TERNAR:
GraphTargetItem terOnTrue = expression(importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);
expectedType(SymbolType.COLON);
@@ -66,7 +66,7 @@ public abstract class AssignableAVM2Item extends AVM2Item {
this.assignedValue = storeValue;
}
protected List<GraphSourceItem> dupSetTemp(SourceGeneratorLocalData localData, SourceGenerator generator, Reference<Integer> register) {
public static List<GraphSourceItem> dupSetTemp(SourceGeneratorLocalData localData, SourceGenerator generator, Reference<Integer> register) {
register.setVal(getFreeRegister(localData, generator));
List<GraphSourceItem> ret = new ArrayList<>();
ret.add(ins(new DupIns()));
@@ -74,14 +74,14 @@ public abstract class AssignableAVM2Item extends AVM2Item {
return ret;
}
protected List<GraphSourceItem> setTemp(SourceGeneratorLocalData localData, SourceGenerator generator, Reference<Integer> register) {
public static List<GraphSourceItem> setTemp(SourceGeneratorLocalData localData, SourceGenerator generator, Reference<Integer> register) {
register.setVal(getFreeRegister(localData, generator));
List<GraphSourceItem> ret = new ArrayList<>();
ret.add(generateSetLoc(register.getVal()));
return ret;
}
protected List<GraphSourceItem> getTemp(SourceGeneratorLocalData localData, SourceGenerator generator, Reference<Integer> register) {
public static List<GraphSourceItem> getTemp(SourceGeneratorLocalData localData, SourceGenerator generator, Reference<Integer> register) {
if (register.getVal() < 0) {
return new ArrayList<>();
}
@@ -98,7 +98,7 @@ public abstract class AssignableAVM2Item extends AVM2Item {
return ret;
}*/
@SuppressWarnings("unchecked")
protected List<GraphSourceItem> killTemp(SourceGeneratorLocalData localData, SourceGenerator generator, List<Reference<Integer>> registers) {
public static List<GraphSourceItem> killTemp(SourceGeneratorLocalData localData, SourceGenerator generator, List<Reference<Integer>> registers) {
List<GraphSourceItem> ret = new ArrayList<>();
for (Reference<Integer> register : registers) {
if (register.getVal() < 0) {
@@ -111,7 +111,7 @@ public abstract class AssignableAVM2Item extends AVM2Item {
return ret;
}
protected AVM2Instruction generateSetLoc(int regNumber) {
public static AVM2Instruction generateSetLoc(int regNumber) {
switch (regNumber) {
case 0:
return ins(new SetLocal0Ins());
@@ -126,7 +126,7 @@ public abstract class AssignableAVM2Item extends AVM2Item {
}
}
protected AVM2Instruction generateGetLoc(int regNumber) {
public static AVM2Instruction generateGetLoc(int regNumber) {
switch (regNumber) {
case 0:
return ins(new GetLocal0Ins());
@@ -39,6 +39,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertSIns;
import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item;
import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.NamespaceSet;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
@@ -164,8 +165,10 @@ public class NameAVM2Item extends AssignableAVM2Item {
return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true);
}
private GraphTargetItem getDefaultValue(String type){
public static GraphTargetItem getDefaultValue(String type){
switch(type){
case "*":
return new UndefinedAVM2Item(null);
case "int":
return new IntegerValueAVM2Item(null, 0L);
case "Number":
@@ -175,7 +178,7 @@ public class NameAVM2Item extends AssignableAVM2Item {
}
}
private AVM2Instruction generateCoerce(SourceGenerator generator, String type) {
public static AVM2Instruction generateCoerce(SourceGenerator generator, String type) {
AVM2Instruction ins;
switch (type) {
case "int":
@@ -199,8 +202,8 @@ public class NameAVM2Item extends AssignableAVM2Item {
if (variableName != null && regNumber == -1 && ns == null) {
throw new RuntimeException("No register set for " + variableName);
}
if (definition && assignedValue == null) {
assignedValue = getDefaultValue(type==null?"*":type.toString());
if (definition && assignedValue == null) {
return new ArrayList<GraphSourceItem>();
}
AVM2SourceGenerator g = (AVM2SourceGenerator) generator;
Reference<Integer> ns_temp = new Reference<>(-1);
@@ -3,10 +3,9 @@ TODO List for AS3 parser/compiler:
- exceptions (try/catch)
- finally clause
- E4X (XML)
- for in/ for each
- typenames (Vectors)
- inner functions (activation, slots, etc.)
- with
- delete property
- default xml namespace
- custom namespace modifiers
- custom namespace modifiers