Fixed AS2 - getters and setters decompilation and editing

Fixed AS1/2 - definefunction2 suppresssuper parameter
This commit is contained in:
Jindra Petřík
2021-11-24 23:04:26 +01:00
parent b419687630
commit 197e202aae
5 changed files with 184 additions and 11 deletions

View File

@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.action.model.GetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.ImplementsOpActionItem;
import com.jpexs.decompiler.flash.action.model.NewMethodActionItem;
import com.jpexs.decompiler.flash.action.model.NewObjectActionItem;
import com.jpexs.decompiler.flash.action.model.ReturnActionItem;
import com.jpexs.decompiler.flash.action.model.SetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.SetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem;
@@ -40,6 +41,7 @@ import com.jpexs.decompiler.graph.model.IfItem;
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.Reference;
import java.util.ArrayList;
import java.util.List;
@@ -504,7 +506,19 @@ public class ActionScript2ClassDetector {
if (!(getterNameStr.equals("__get__" + propertyNameStr))) {
throw new AssertException("getter does not match property name");
}
//TODO: handle getter HERE
for (MyEntry<GraphTargetItem, GraphTargetItem> trait : traits) {
if (trait.getKey() instanceof DirectValueActionItem) {
if (((DirectValueActionItem) trait.getKey()).isString()) {
if (((DirectValueActionItem) trait.getKey()).toString().equals(getterNameStr)) {
if (trait.getValue() instanceof FunctionActionItem) {
FunctionActionItem func = (FunctionActionItem) trait.getValue();
func.isGetter = true;
}
}
}
}
}
} else if (propertyGetter instanceof FunctionActionItem) {
FunctionActionItem getterFunc = (FunctionActionItem) propertyGetter;
@@ -525,7 +539,38 @@ public class ActionScript2ClassDetector {
if (!(setterNameStr.equals("__set__" + propertyNameStr))) {
throw new AssertException("setter does not match property name");
}
//TODO: handle setter HERE
for (MyEntry<GraphTargetItem, GraphTargetItem> trait : traits) {
if (trait.getKey() instanceof DirectValueActionItem) {
if (((DirectValueActionItem) trait.getKey()).isString()) {
if (((DirectValueActionItem) trait.getKey()).toString().equals(setterNameStr)) {
if (trait.getValue() instanceof FunctionActionItem) {
FunctionActionItem func = (FunctionActionItem) trait.getValue();
func.isSetter = true;
//There is return getter added at the end of every setter, gotta remove it, since it won't compile
//as setter must not return a value
if (!func.actions.isEmpty()) {
int pos = func.actions.size() - 1;
if (func.actions.get(pos) instanceof ScriptEndItem) {
pos--;
}
if (pos >= 0 && func.actions.get(pos) instanceof ReturnActionItem) {
GraphTargetItem val = func.actions.get(pos);
if (val.value instanceof CallMethodActionItem) {
if (((CallMethodActionItem) val.value).methodName instanceof DirectValueActionItem) {
if (((CallMethodActionItem) val.value).methodName.toString().startsWith("__get__")) {
func.actions.remove(pos);
}
}
}
}
}
}
}
}
}
}
} else if (propertySetter instanceof FunctionActionItem) {
FunctionActionItem setterFunc = (FunctionActionItem) propertySetter;
if (!(setterFunc.actions.isEmpty() && setterFunc.functionName.isEmpty() && ((FunctionActionItem) propertySetter).paramNames.isEmpty())) {

View File

@@ -51,6 +51,10 @@ import java.util.Set;
*/
public class FunctionActionItem extends ActionItem implements BranchStackResistant {
public boolean isGetter = false;
public boolean isSetter = false;
public List<GraphTargetItem> actions;
public List<String> constants;
@@ -92,6 +96,10 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
}
public void addVariable(VariableActionItem variable) {
variables.add(variable);
}
public FunctionActionItem() {
super(null, null, PRECEDENCE_PRIMARY);
@@ -119,21 +127,43 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
srcData.localName = n;
srcData.declaration = true;
}
writer.append("function");
if (isGetter) {
writer.append(" get");
}
if (isSetter) {
writer.append(" set");
}
if (calculatedFunctionName != null) {
writer.append(" ");
String fname = calculatedFunctionName.toStringNoQuotes(localData);
if (isGetter && fname.startsWith("__get__")) {
fname = fname.substring(7);
}
if (isSetter && fname.startsWith("__set__")) {
fname = fname.substring(7);
}
if (!IdentifiersDeobfuscation.isValidName(false, fname)) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(fname, writer);
} else {
calculatedFunctionName.appendToNoQuotes(writer, localData);
writer.append(fname);
//calculatedFunctionName.appendToNoQuotes(writer, localData);
}
} else if (!functionName.isEmpty()) {
String fname = functionName;
if (isGetter && fname.startsWith("__get__")) {
fname = fname.substring(7);
}
if (isSetter && fname.startsWith("__set__")) {
fname = fname.substring(7);
}
writer.append(" ");
if (!IdentifiersDeobfuscation.isValidName(false, functionName)) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(functionName, writer);
if (!IdentifiersDeobfuscation.isValidName(false, fname)) {
IdentifiersDeobfuscation.appendObfuscatedIdentifier(fname, writer);
} else {
writer.append(functionName);
writer.append(fname);
}
}
writer.spaceBeforeCallParenthesies(paramNames.size());
@@ -262,7 +292,7 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
boolean preloadThisFlag = false;
boolean preloadGlobalFlag = false;
boolean suppressParentFlag = false;
boolean suppressSuperFlag = false;
boolean suppressArgumentsFlag = false;
boolean suppressThisFlag = false;
@@ -288,7 +318,10 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
preloadSuperFlag = true;
needsFun2 = true;
registerNames.add("super");
} else {
suppressSuperFlag = true;
}
if (usedNames.contains("_root")) {
preloadRootFlag = true;
needsFun2 = true;
@@ -298,8 +331,6 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
preloadParentFlag = true;
needsFun2 = true;
registerNames.add("_parent");
} else {
suppressParentFlag = true;
}
if (usedNames.contains("_global")) {
needsFun2 = true;
@@ -404,7 +435,7 @@ public class FunctionActionItem extends ActionItem implements BranchStackResista
ret.add(0, new ActionDefineFunction2(functionName,
preloadParentFlag,
preloadRootFlag,
suppressParentFlag,
suppressSuperFlag,
preloadSuperFlag,
suppressArgumentsFlag,
preloadArgumentsFlag,

View File

@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action.parser.script;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.model.CallMethodAVM2Item;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem;
import com.jpexs.decompiler.flash.action.model.CallActionItem;
@@ -447,6 +448,8 @@ public class ActionScript2Parser {
looptrait:
while (true) {
s = lex();
boolean isGetter = false;
boolean isSetter = false;
boolean isStatic = false;
while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE)) {
if (s.type == SymbolType.STATIC) {
@@ -457,6 +460,15 @@ public class ActionScript2Parser {
switch (s.type) {
case FUNCTION:
s = lex();
if (s.type == SymbolType.SET) {
isSetter = true;
s = lex();
} else if (s.type == SymbolType.GET) {
isGetter = true;
s = lex();
}
expectedIdentifier(s, lexer.yyline());
String fname = s.value.toString();
if (fname.equals(classNameStr)) { //constructor
@@ -466,16 +478,37 @@ public class ActionScript2Parser {
if (isStatic) {
FunctionActionItem ft = function(!isInterface, "", true, variables, functions, inTellTarget, hasEval);
ft.calculatedFunctionName = pushConst(fname);
ft.isSetter = isSetter;
ft.isGetter = isGetter;
//staticFunctions.add(ft);
traits.add(new MyEntry<>(ft.calculatedFunctionName, ft));
traitsStatic.add(true);
if (isSetter) {
//add return getter automatically
GraphTargetItem callM = new CallMethodActionItem(null, null, nameStr, pushConst("__get__" + fname), new ArrayList<>());
GraphTargetItem retV = new ReturnActionItem(null, null, callM);
ft.actions.add(retV);
}
} else {
FunctionActionItem ft = function(!isInterface, "", true, variables, functions, inTellTarget, hasEval);
ft.calculatedFunctionName = pushConst(fname);
ft.isSetter = isSetter;
ft.isGetter = isGetter;
//instanceFunctions.add(ft);
traits.add(new MyEntry<>(ft.calculatedFunctionName, ft));
traitsStatic.add(false);
if (isSetter) {
//add return getter automatically
GraphTargetItem thisVar = new VariableActionItem("this", null, false);
ft.addVariable((VariableActionItem) thisVar);
GraphTargetItem callM = new CallMethodActionItem(null, null, thisVar, pushConst("__get__" + fname), new ArrayList<>());
GraphTargetItem retV = new ReturnActionItem(null, null, callM);
ft.actions.add(retV);
}
}
}
break;
case VAR:
@@ -651,6 +684,7 @@ public class ActionScript2Parser {
SymbolType.EACH, SymbolGroup.GLOBALFUNC,
SymbolType.NUMBER_OP, SymbolType.STRING_OP);
}
private void expectedIdentifier(ParsedSymbol s, int line, Object... exceptions) throws IOException, ActionParseException {
for (Object ex : exceptions) {
if (s.isType(ex)) {

View File

@@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable;
import com.jpexs.decompiler.flash.action.swf4.ConstantIndex;
import com.jpexs.decompiler.flash.action.swf4.RegisterNumber;
import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction;
import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod;
import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction;
import com.jpexs.decompiler.flash.action.swf5.ActionGetMember;
import com.jpexs.decompiler.flash.action.swf5.ActionNewObject;
@@ -70,7 +71,10 @@ import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
*
@@ -711,6 +715,16 @@ public class ActionSourceGenerator implements SourceGenerator {
constr.add(new ActionStoreRegister(1));
constr = (typeToActions(globalClassTypeStr, constr));
}
Set<String> properties = new LinkedHashSet<>();
Set<String> setters = new HashSet<>();
Set<String> getters = new HashSet<>();
Set<String> staticProperties = new LinkedHashSet<>();
Set<String> staticSetters = new HashSet<>();
Set<String> staticGetters = new HashSet<>();
if (!isInterface) {
for (int pass = 1; pass <= 2; pass++) { //two passes, methods first, then variables
for (int t = 0; t < traits.size(); t++) {
@@ -721,8 +735,20 @@ public class ActionSourceGenerator implements SourceGenerator {
boolean isFunc = (en.getValue() instanceof FunctionActionItem);
if (pass == 1 && isFunc) { //Add methods in first pass
FunctionActionItem fi = (FunctionActionItem) en.getValue();
if (fi.isGetter || fi.isSetter) {
(traitsStatic.get(t) ? staticProperties : properties).add(fi.calculatedFunctionName.toString());
}
String prefix = "";
if (fi.isGetter) {
(traitsStatic.get(t) ? staticGetters : getters).add(fi.calculatedFunctionName.toString());
prefix = "__get__";
}
if (fi.isSetter) {
(traitsStatic.get(t) ? staticSetters : setters).add(fi.calculatedFunctionName.toString());
prefix = "__set__";
}
ifbody.add(new ActionPush(new RegisterNumber(traitsStatic.get(t) ? 1 : 2)));
ifbody.add(pushConst(getName(en.getKey())));
ifbody.add(pushConst(prefix + getName(en.getKey())));
ifbody.addAll(toActionList(fi.toSource(localData, this)));
ifbody.add(new ActionSetMember());
} else if (pass == 2 && !isFunc) { //add variables in second pass
@@ -735,6 +761,41 @@ public class ActionSourceGenerator implements SourceGenerator {
}
}
for (String prop : staticProperties) {
if (staticSetters.contains(prop)) {
ifbody.add(new ActionPush(new Object[]{new RegisterNumber(1), "__set__" + prop}));
ifbody.add(new ActionGetMember());
} else {
ifbody.add(new ActionDefineFunction("", new ArrayList<String>(), 0, swfVersion));
}
if (staticGetters.contains(prop)) {
ifbody.add(new ActionPush(new Object[]{new RegisterNumber(1), "__get__" + prop}));
ifbody.add(new ActionGetMember());
} else {
ifbody.add(new ActionDefineFunction("", new ArrayList<String>(), 0, swfVersion));
}
ifbody.add(new ActionPush(new Object[]{prop, 3, new RegisterNumber(1), "addProperty"}));
ifbody.add(new ActionCallMethod());
}
for (String prop : properties) {
if (setters.contains(prop)) {
ifbody.add(new ActionPush(new Object[]{new RegisterNumber(2), "__set__" + prop}));
ifbody.add(new ActionGetMember());
} else {
ifbody.add(new ActionDefineFunction("", new ArrayList<String>(), 0, swfVersion));
}
if (getters.contains(prop)) {
ifbody.add(new ActionPush(new Object[]{new RegisterNumber(2), "__get__" + prop}));
ifbody.add(new ActionGetMember());
} else {
ifbody.add(new ActionDefineFunction("", new ArrayList<String>(), 0, swfVersion));
}
ifbody.add(new ActionPush(new Object[]{prop, 3, new RegisterNumber(2), "addProperty"}));
ifbody.add(new ActionCallMethod());
}
if (!isInterface) {
ifbody.add(new ActionPush((Long) 1L));
ifbody.add(new ActionPush(Null.INSTANCE));