AS2 classes

- maintain order of variables, maintain order of methods,
variables before methods
- stub for getters/setters detection
This commit is contained in:
Jindra Petřík
2018-01-21 08:20:39 +01:00
parent 16f6fa329c
commit 8009d9e7f6
5 changed files with 198 additions and 122 deletions
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference;
import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscator;
import com.jpexs.decompiler.flash.action.model.ActionItem;
import com.jpexs.decompiler.flash.action.model.CallMethodActionItem;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.ExtendsActionItem;
@@ -79,6 +80,7 @@ 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.PopItem;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.decompiler.graph.model.ScriptEndItem;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
@@ -1217,10 +1219,8 @@ public abstract class Action implements GraphSourceItem {
//return output;
}
List<GraphTargetItem> ret = new ArrayList<>();
List<GraphTargetItem> functions = new ArrayList<>();
List<GraphTargetItem> staticFunctions = new ArrayList<>();
List<MyEntry<GraphTargetItem, GraphTargetItem>> vars = new ArrayList<>();
List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars = new ArrayList<>();
List<MyEntry<GraphTargetItem, GraphTargetItem>> traits = new ArrayList<>();
List<Boolean> traitsStatic = new ArrayList<>();
GraphTargetItem className;
GraphTargetItem extendsOp = null;
List<GraphTargetItem> implementsOp = new ArrayList<>();
@@ -1264,8 +1264,10 @@ public abstract class Action implements GraphSourceItem {
if (sm.value instanceof StoreRegisterActionItem) {
sr = (StoreRegisterActionItem) sm.value;
if (sr.value instanceof FunctionActionItem) {
((FunctionActionItem) (sr.value)).calculatedFunctionName = (className instanceof GetMemberActionItem) ? ((GetMemberActionItem) className).memberName : className;
functions.add((FunctionActionItem) sr.value);
((FunctionActionItem) (sr.value)).calculatedFunctionName = sm.objectName; //(className instanceof GetMemberActionItem) ? ((GetMemberActionItem) className).memberName : className;
//This is probably a constructor
traits.add(new MyEntry<>(((FunctionActionItem) (sr.value)).calculatedFunctionName, ((FunctionActionItem) sr.value)));
traitsStatic.add(false);
}
}
@@ -1296,14 +1298,12 @@ public abstract class Action implements GraphSourceItem {
}
}
GraphTargetItem constructor = null;
for (; ipos < parts.size(); ipos++) {
if (parts.get(ipos) instanceof ImplementsOpActionItem) {
ImplementsOpActionItem io = (ImplementsOpActionItem) parts.get(ipos);
implementsOp = io.superclasses;
continue;
}
if (parts.get(ipos) instanceof SetMemberActionItem) {
} else if (parts.get(ipos) instanceof SetMemberActionItem) {
SetMemberActionItem sm = (SetMemberActionItem) parts.get(ipos);
if (sm.object instanceof TemporaryRegister) {
@@ -1324,8 +1324,8 @@ public abstract class Action implements GraphSourceItem {
val = val.value;
}
if (val instanceof FunctionActionItem) {
constructor = val;
((FunctionActionItem) (constructor)).calculatedFunctionName = (className instanceof GetMemberActionItem) ? ((GetMemberActionItem) className).memberName : className;
//Is this a constructor?
//((FunctionActionItem) (constructor)).calculatedFunctionName = (className instanceof GetMemberActionItem) ? ((GetMemberActionItem) className).memberName : className;
}
}
}
@@ -1343,19 +1343,92 @@ public abstract class Action implements GraphSourceItem {
if (sm.object instanceof TemporaryRegister) {
rnum = ((TemporaryRegister) sm.object).getRegId();
}
if (rnum == instanceReg) {
if (rnum == instanceReg || rnum == classReg) {
if (sm.value instanceof FunctionActionItem) {
((FunctionActionItem) sm.value).calculatedFunctionName = sm.objectName;
functions.add((FunctionActionItem) sm.value);
} else {
vars.add(new MyEntry<>(sm.objectName, sm.value));
}
} else if (rnum == classReg) {
if (sm.value instanceof FunctionActionItem) {
((FunctionActionItem) sm.value).calculatedFunctionName = sm.objectName;
staticFunctions.add((FunctionActionItem) sm.value);
} else {
staticVars.add(new MyEntry<>(sm.objectName, sm.value));
traits.add(new MyEntry<>(sm.objectName, sm.value));
traitsStatic.add(rnum == classReg);
}
} else if (parts.get(ipos) instanceof PushItem) {
if (parts.get(ipos).value instanceof CallMethodActionItem) {
CallMethodActionItem cm = (CallMethodActionItem) parts.get(ipos).value;
if (cm.methodName instanceof DirectValueActionItem) {
if ("addProperty".equals(((DirectValueActionItem) cm.methodName).getAsString())) {
int rnum = -1;
if (cm.scriptObject instanceof DirectValueActionItem) {
DirectValueActionItem dv = (DirectValueActionItem) cm.scriptObject;
if (dv.value instanceof RegisterNumber) {
RegisterNumber rn = (RegisterNumber) dv.value;
rnum = rn.number;
}
}
if (cm.scriptObject instanceof TemporaryRegister) {
rnum = ((TemporaryRegister) cm.scriptObject).getRegId();
}
if (cm.arguments.size() > 1) {
GraphTargetItem propertyName = cm.arguments.get(0); //name
GraphTargetItem propertyGetter = cm.arguments.get(1); //getter
GraphTargetItem propertySetter = cm.arguments.get(2); //setter
if ((propertyName instanceof DirectValueActionItem) && ((DirectValueActionItem) propertyName).isString()) {
String propertyNameStr = ((DirectValueActionItem) propertyName).getAsString();
if (propertyGetter instanceof GetMemberActionItem) {
if (((GetMemberActionItem) propertyGetter).object instanceof TemporaryRegister) {
if (((TemporaryRegister) ((GetMemberActionItem) propertyGetter).object).getRegId() == rnum) {
if (((GetMemberActionItem) propertyGetter).memberName instanceof DirectValueActionItem) {
DirectValueActionItem mNameDv = (DirectValueActionItem) ((GetMemberActionItem) propertyGetter).memberName;
if (mNameDv.isString()) {
String getterNameStr = mNameDv.getAsString();
if (getterNameStr.equals("__get__" + propertyNameStr)) {
//TODO: handle setter
}
}
}
}
}
} else if (propertyGetter instanceof FunctionActionItem) {
FunctionActionItem getterFunc = (FunctionActionItem) propertyGetter;
if (getterFunc.actions.isEmpty() && getterFunc.functionName.isEmpty() && ((FunctionActionItem) propertyGetter).paramNames.isEmpty()) {
//well, no getter then
} else {
logger.severe("unexpected getter value for property " + propertyNameStr);
}
} else {
logger.severe("unexpected getter value for property " + propertyNameStr + ": " + propertyGetter.getClass().getSimpleName());
}
if (propertySetter instanceof GetMemberActionItem) {
if (((GetMemberActionItem) propertySetter).object instanceof TemporaryRegister) {
if (((TemporaryRegister) ((GetMemberActionItem) propertySetter).object).getRegId() == rnum) {
if (((GetMemberActionItem) propertySetter).memberName instanceof DirectValueActionItem) {
DirectValueActionItem mNameDv = (DirectValueActionItem) ((GetMemberActionItem) propertySetter).memberName;
if (mNameDv.isString()) {
String getterNameStr = mNameDv.getAsString();
if (getterNameStr.equals("__set__" + propertyNameStr)) {
//TODO: handle setter
}
}
}
}
}
} else if (propertySetter instanceof FunctionActionItem) {
FunctionActionItem setterFunc = (FunctionActionItem) propertySetter;
if (setterFunc.actions.isEmpty() && setterFunc.functionName.isEmpty() && ((FunctionActionItem) propertySetter).paramNames.isEmpty()) {
//well, no setter then
} else {
logger.severe("unexpected setter value for property " + propertyNameStr);
}
//no setter
} else {
logger.severe("unexpected setter value for property " + propertyNameStr + ": " + propertySetter.getClass().getSimpleName());
}
}
}
}
}
}
}
@@ -1364,7 +1437,7 @@ public abstract class Action implements GraphSourceItem {
for (int i = 0; i < prevCount; i++) {
output2.add(output.get(i));
}
output2.add(new ClassActionItem(className, extendsOp, implementsOp, constructor, functions, vars, staticFunctions, staticVars));
output2.add(new ClassActionItem(className, extendsOp, implementsOp, traits, traitsStatic));
return output2;
}
@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.␍ */
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.model.clauses;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
@@ -44,56 +45,39 @@ import java.util.Set;
*/
public class ClassActionItem extends ActionItem implements Block {
public List<GraphTargetItem> functions;
public List<GraphTargetItem> staticFunctions;
public GraphTargetItem extendsOp;
public List<GraphTargetItem> implementsOp;
public GraphTargetItem className;
public GraphTargetItem constructor;
public List<MyEntry<GraphTargetItem, GraphTargetItem>> vars;
public List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars;
//public GraphTargetItem constructor;
public List<MyEntry<GraphTargetItem, GraphTargetItem>> traits;
public List<Boolean> traitsStatic;
public Set<String> uninitializedVars;
@Override
public List<List<GraphTargetItem>> getSubs() {
List<List<GraphTargetItem>> ret = new ArrayList<>();
if (functions != null) {
ret.add(functions);
}
if (staticFunctions != null) {
ret.add(staticFunctions);
}
//? is this needed for traits ?
return ret;
}
public ClassActionItem(GraphTargetItem className, GraphTargetItem extendsOp, List<GraphTargetItem> implementsOp, GraphTargetItem constructor, List<GraphTargetItem> functions, List<MyEntry<GraphTargetItem, GraphTargetItem>> vars, List<GraphTargetItem> staticFunctions, List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars) {
public ClassActionItem(GraphTargetItem className, GraphTargetItem extendsOp, List<GraphTargetItem> implementsOp, List<MyEntry<GraphTargetItem, GraphTargetItem>> traits, List<Boolean> traitsStatic) {
super(null, null, NOPRECEDENCE);
this.className = className;
this.functions = functions;
this.vars = vars;
this.traits = traits;
this.traitsStatic = traitsStatic;
this.extendsOp = extendsOp;
this.implementsOp = implementsOp;
this.staticFunctions = staticFunctions;
this.staticVars = staticVars;
this.constructor = constructor;
//this.constructor = constructor;
List<GraphTargetItem> allFunc = new ArrayList<>(functions);
if (constructor != null) {
allFunc.add(constructor);
}
this.uninitializedVars = new HashSet<>();
List<GraphTargetItem> allUsages = new ArrayList<>();
for (GraphTargetItem it : allFunc) {
if (it instanceof FunctionActionItem) {
FunctionActionItem f = (FunctionActionItem) it;
for (MyEntry<GraphTargetItem, GraphTargetItem> it : traits) {
if (it.getValue() instanceof FunctionActionItem) {
FunctionActionItem f = (FunctionActionItem) it.getValue();
detectUnitializedVars(f.actions, allUsages);
}
}
@@ -102,7 +86,7 @@ public class ClassActionItem extends ActionItem implements Block {
allMembers.add(it.toStringNoQuotes(LocalData.empty));
}
uninitializedVars.addAll(allMembers);
for (MyEntry<GraphTargetItem, GraphTargetItem> v : vars) {
for (MyEntry<GraphTargetItem, GraphTargetItem> v : traits) {
String s = v.getKey().toStringNoQuotes(LocalData.empty);
if (uninitializedVars.contains(s)) {
uninitializedVars.remove(s);
@@ -176,38 +160,49 @@ public class ClassActionItem extends ActionItem implements Block {
}
writer.startBlock();
if (constructor != null) {
/*if (constructor != null) {
constructor.toString(writer, localData).newLine();
}
}*/
for (int pass = 1; pass <= 2; pass++) {
looptraits:
for (int i = 0; i < traits.size(); i++) {
MyEntry<GraphTargetItem, GraphTargetItem> item = traits.get(i);
for (MyEntry<GraphTargetItem, GraphTargetItem> item : vars) {
writer.append("var ");
item.getKey().toStringNoQuotes(writer, localData);
writer.append(" = ");
item.getValue().toString(writer, localData);
writer.append(";").newLine();
switch (pass) {
//pass 1: add variables
case 1:
if (item.getValue() instanceof FunctionActionItem) { //ignore methods
continue looptraits;
}
break;
//pass 2: add methods
case 2:
if (!(item.getValue() instanceof FunctionActionItem)) { //ignore nonmethods
continue looptraits;
}
break;
}
if (traitsStatic.get(i)) {
writer.append("static ");
}
if (item.getValue() instanceof FunctionActionItem) {
item.getValue().toString(writer, localData).newLine();
} else {
writer.append("var ");
item.getKey().toStringNoQuotes(writer, localData);
writer.append(" = ");
item.getValue().toString(writer, localData);
writer.append(";").newLine();
}
}
}
for (String v : uninitializedVars) {
writer.append("var ");
writer.append(v);
writer.append(";").newLine();
}
for (MyEntry<GraphTargetItem, GraphTargetItem> item : staticVars) {
writer.append("static var ");
item.getKey().toStringNoQuotes(writer, localData);
writer.append(" = ");
item.getValue().toString(writer, localData);
writer.append(";").newLine();
}
for (GraphTargetItem f : functions) {
f.toString(writer, localData).newLine();
}
for (GraphTargetItem f : staticFunctions) {
writer.append("static ");
f.toString(writer, localData).newLine();
}
writer.endBlock();
writer.endClass();
return writer;
@@ -230,7 +225,7 @@ public class ClassActionItem extends ActionItem implements Block {
ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator;
SourceGeneratorLocalData localData2 = Helper.deepCopy(localData);
asGenerator.setInMethod(localData2, true);
ret.addAll(asGenerator.generateTraits(localData2, false, className, extendsOp, implementsOp, constructor, functions, vars, staticFunctions, staticVars));
ret.addAll(asGenerator.generateTraits(localData2, false, className, extendsOp, implementsOp, traits, traitsStatic));
return ret;
}
@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.␍ */
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.model.clauses;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
@@ -20,6 +21,7 @@ import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.model.ActionItem;
import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
@@ -72,7 +74,7 @@ public class InterfaceActionItem extends ActionItem {
public List<GraphSourceItem> toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException {
List<GraphSourceItem> ret = new ArrayList<>();
ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator;
ret.addAll(asGenerator.generateTraits(localData, true, name, null, superInterfaces, null, null, null, null, null));
ret.addAll(asGenerator.generateTraits(localData, true, name, null, superInterfaces, new ArrayList<MyEntry<GraphTargetItem, GraphTargetItem>>(), new ArrayList<>()));
return ret;
}
@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.␍ */
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.parser.script;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
@@ -338,11 +339,8 @@ public class ActionScript2Parser {
globalClassTypeStr.addAll(nameStr);*/
ParsedSymbol s;
FunctionActionItem constr = null;
List<GraphTargetItem> staticFunctions = new ArrayList<>();
List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars = new ArrayList<>();
List<GraphTargetItem> instanceFunctions = new ArrayList<>();
List<MyEntry<GraphTargetItem, GraphTargetItem>> vars = new ArrayList<>();
List<MyEntry<GraphTargetItem, GraphTargetItem>> traits = new ArrayList<>();
List<Boolean> traitsStatic = new ArrayList<>();
String classNameStr = "";
if (nameStr instanceof GetMemberActionItem) {
@@ -373,16 +371,21 @@ public class ActionScript2Parser {
expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC);
String fname = s.value.toString();
if (fname.equals(classNameStr)) { //constructor
constr = (function(!isInterface, "", true, variables, functions));
} else if (!isInterface) {
//actually there's no difference, it's instance trait
}
if (!isInterface) {
if (isStatic) {
FunctionActionItem ft = function(!isInterface, "", true, variables, functions);
ft.calculatedFunctionName = pushConst(fname);
staticFunctions.add(ft);
//staticFunctions.add(ft);
traits.add(new MyEntry<>(ft.calculatedFunctionName, ft));
traitsStatic.add(true);
} else {
FunctionActionItem ft = function(!isInterface, "", true, variables, functions);
ft.calculatedFunctionName = pushConst(fname);
instanceFunctions.add(ft);
//instanceFunctions.add(ft);
traits.add(new MyEntry<>(ft.calculatedFunctionName, ft));
traitsStatic.add(false);
}
}
break;
@@ -396,11 +399,8 @@ public class ActionScript2Parser {
s = lex();
}
if (s.type == SymbolType.ASSIGN) {
if (isStatic) {
staticVars.add(new MyEntry<>(pushConst(ident), expression(false, false, true, variables, functions)));
} else {
vars.add(new MyEntry<>(pushConst(ident), expression(false, false, true, variables, functions)));
}
traits.add(new MyEntry<>(pushConst(ident), expression(false, false, true, variables, functions)));
traitsStatic.add(isStatic);
s = lex();
}
if (s.type != SymbolType.SEMICOLON) {
@@ -417,7 +417,7 @@ public class ActionScript2Parser {
if (isInterface) {
return new InterfaceActionItem(nameStr, implementsStr);
} else {
return new ClassActionItem(nameStr, extendsStr, implementsStr, constr, instanceFunctions, vars, staticFunctions, staticVars);
return new ClassActionItem(nameStr, extendsStr, implementsStr, traits, traitsStatic);
}
}
@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.␍ */
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.parser.script;
import com.jpexs.decompiler.flash.SWF;
@@ -655,7 +656,7 @@ public class ActionSourceGenerator implements SourceGenerator {
return new ActionPush(new ConstantIndex(index));
}
public List<GraphSourceItem> generateTraits(SourceGeneratorLocalData localData, boolean isInterface, GraphTargetItem name, GraphTargetItem extendsVal, List<GraphTargetItem> implementsStr, GraphTargetItem constructor, List<GraphTargetItem> functions, List<MyEntry<GraphTargetItem, GraphTargetItem>> vars, List<GraphTargetItem> staticFunctions, List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars) throws CompilationException {
public List<GraphSourceItem> generateTraits(SourceGeneratorLocalData localData, boolean isInterface, GraphTargetItem name, GraphTargetItem extendsVal, List<GraphTargetItem> implementsStr, List<MyEntry<GraphTargetItem, GraphTargetItem>> traits, List<Boolean> traitsStatic) throws CompilationException {
List<String> extendsStr = getVarParts(extendsVal);
List<GraphSourceItem> ret = new ArrayList<>();
List<String> nameStr = getVarParts(name);
@@ -683,6 +684,19 @@ public class ActionSourceGenerator implements SourceGenerator {
globalClassTypeStr.add("_global");
globalClassTypeStr.addAll(nameStr);
String constructorName = nameStr.get(nameStr.size() - 1); //com.jpexs.MyClass => MyClass
GraphTargetItem constructor = null;
int constructorIndex = -1;
for (int t = 0; t < traits.size(); t++) {
MyEntry<GraphTargetItem, GraphTargetItem> en = traits.get(t);
if (en.getValue() instanceof FunctionActionItem) {
if (constructorName.equals(getName(en.getKey()))) {
constructorIndex = t;
constructor = en.getValue();
break;
}
}
}
ParsedSymbol s = null;
List<Action> constr = new ArrayList<>();
@@ -699,31 +713,23 @@ public class ActionSourceGenerator implements SourceGenerator {
constr = (typeToActions(globalClassTypeStr, constr));
}
if (!isInterface) {
for (GraphTargetItem f : staticFunctions) {
FunctionActionItem fi = (FunctionActionItem) f;
ifbody.add(new ActionPush(new RegisterNumber(1/*static*/)));
ifbody.add(new ActionPush(getName(fi.calculatedFunctionName)));
ifbody.addAll(toActionList(fi.toSource(localData, this)));
ifbody.add(new ActionSetMember());
}
for (GraphTargetItem f : functions) {
FunctionActionItem fi = (FunctionActionItem) f;
ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/)));
ifbody.add(new ActionPush(getName(fi.calculatedFunctionName)));
ifbody.addAll(toActionList(fi.toSource(localData, this)));
ifbody.add(new ActionSetMember());
}
for (MyEntry<GraphTargetItem, GraphTargetItem> en : staticVars) {
ifbody.add(new ActionPush(new RegisterNumber(1/*static*/)));
ifbody.add(new ActionPush(getName(en.getKey())));
ifbody.addAll(toActionList(en.getValue().toSource(localData, this)));
ifbody.add(new ActionSetMember());
}
for (MyEntry<GraphTargetItem, GraphTargetItem> en : vars) {
ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/)));
ifbody.add(new ActionPush(getName(en.getKey())));
ifbody.addAll(toActionList(en.getValue().toSource(localData, this)));
ifbody.add(new ActionSetMember());
for (int t = 0; t < traits.size(); t++) {
if (constructorIndex == t) { //constructor already handled
continue;
}
MyEntry<GraphTargetItem, GraphTargetItem> en = traits.get(t);
if (en.getValue() instanceof FunctionActionItem) {
FunctionActionItem fi = (FunctionActionItem) en.getValue();
ifbody.add(new ActionPush(new RegisterNumber(traitsStatic.get(t) ? 1 : 2)));
ifbody.add(new ActionPush(getName(en.getKey())));
ifbody.addAll(toActionList(fi.toSource(localData, this)));
ifbody.add(new ActionSetMember());
} else {
ifbody.add(new ActionPush(new RegisterNumber(traitsStatic.get(t) ? 1 : 2)));
ifbody.add(new ActionPush(getName(en.getKey())));
ifbody.addAll(toActionList(en.getValue().toSource(localData, this)));
ifbody.add(new ActionSetMember());
}
}
}