AS2 class detector - checking other class variant

This commit is contained in:
Jindra Petřík
2018-01-27 09:45:01 +01:00
parent 205b9cda14
commit a904fdb3a3

View File

@@ -268,427 +268,471 @@ public class ActionScript2ClassDetector {
return mnDv.getAsString();
}
public void checkClass(List<GraphTargetItem> commands, String scriptPath) {
/*
class a.b.c.D:
if(!_global.a)
{
_global.a = new Object();
}
§§pop();
if(!_global.a.b)
{
_global.a.b = new Object();
}
§§pop();
if(!_global.a.b.c)
{
_global.a.b.c = new Object();
}
§§pop();
if(!_global.a.b.c.D)
{
...
§§push(ASSetPropFlags(a.b.c.D.prototype,null,1));
}
§§pop();
*/
private boolean checkClassContent(List<GraphTargetItem> parts, int partsPos, int commandsStartPos, int commandsEndPos, List<GraphTargetItem> commands, List<String> classNamePath, String scriptPath) {
List<String> pathToSearchInIfCond = new ArrayList<>();
pathToSearchInIfCond.add("_global");
looppos:
for (int pos = 0; pos < commands.size(); pos++) {
//start with _global.
if (pathToSearchInIfCond.size() > 1) {
pathToSearchInIfCond.clear();
pathToSearchInIfCond.add("_global");
try {
GraphTargetItem item = parts.get(partsPos);
GraphTargetItem extendsOp = null;
List<GraphTargetItem> implementsOp = new ArrayList<>();
if (item instanceof ExtendsActionItem) {
ExtendsActionItem et = (ExtendsActionItem) parts.get(partsPos);
extendsOp = getWithoutGlobal(et.superclass);
partsPos++;
item = parts.get(partsPos);
}
loopcheck:
for (int checkPos = pos; checkPos < commands.size(); checkPos++) {
GraphTargetItem t = commands.get(checkPos);
if (t instanceof IfItem) {
IfItem ifItem = (IfItem) t;
if (ifItem.expression instanceof NotItem) {
NotItem nti = (NotItem) ifItem.expression;
GraphTargetItem condType = nti.value;
Reference<String> newMemberNameRef = new Reference<>("");
if (isMemberOfPath(condType, pathToSearchInIfCond, newMemberNameRef)) {
pathToSearchInIfCond.add(newMemberNameRef.getVal());
//_global.a.b.c = new Object();
if ((ifItem.onTrue.size() == 1) && (ifItem.onTrue.get(0) instanceof SetMemberActionItem) && (((SetMemberActionItem) ifItem.onTrue.get(0)).value instanceof NewObjectActionItem)) {
//skip §§pop item if its there right after if
if (checkPos + 1 < commands.size()) {
GraphTargetItem tnext = commands.get(checkPos + 1);
if (tnext instanceof PopItem) {
checkPos++;
}
}
continue loopcheck;
int instanceReg = -1;
int classReg = -1;
GraphTargetItem classNameTargetPath = null;
GraphTargetItem constructor = null;
if (item instanceof StoreRegisterActionItem) {
StoreRegisterActionItem sr = (StoreRegisterActionItem) item;
instanceReg = sr.register.number;
if (sr.value instanceof GetMemberActionItem) {
GetMemberActionItem gm = (GetMemberActionItem) sr.value;
if (gm.object instanceof TemporaryRegister) {
TemporaryRegister treg = (TemporaryRegister) gm.object;
classReg = treg.getRegId();
if (!"prototype".equals(getAsString(gm.memberName, "memberName"))) {
throw new AssertException("memberName not \"prototype\"");
}
if ((treg.value instanceof SetMemberActionItem) || (treg.value instanceof SetVariableActionItem)) {
List<String> path = getSetMembersPath(treg.value);
if (path == null || path.isEmpty()) {
throw new AssertException("Cannot detect class - tempreg value is not a path");
}
//remove _global if it's there - happens for classes in global package
if ("_global".equals(path.get(0))) {
path.remove(0);
}
if (classNamePath.equals(path)) {
//can start with _global for classes on top level
classNameTargetPath = getWithoutGlobal(setMemberToGetMember(treg.value));
List<GraphTargetItem> parts = ifItem.onTrue;
try {
if (!ifItem.onFalse.isEmpty()) {
//throw new AssertException("else clause of the if is not empty");
//Note: When there is an else block, it may mean
//that the class was too long to fit maximum of
//jump bytes so the jump target is other invalid location
//(usually negative - will probably lead to other SWF tags)
//so we will rather silently ignore onFalse section
//treg.value.value is the value being set - treg.value is setmember ot setvariable
if (!(treg.value.value instanceof StoreRegisterActionItem)) {
throw new AssertException("Constructor expected to be in storeregister");
}
List<String> classNamePath = pathToSearchInIfCond;
classNamePath.remove(0); //remove "_global"
int ppos = 0;
GraphTargetItem item = parts.get(ppos);
GraphTargetItem extendsOp = null;
List<GraphTargetItem> implementsOp = new ArrayList<>();
if (item instanceof ExtendsActionItem) {
ExtendsActionItem et = (ExtendsActionItem) parts.get(ppos);
extendsOp = getWithoutGlobal(et.superclass);
ppos++;
item = parts.get(ppos);
if (!(treg.value.value.value instanceof FunctionActionItem)) {
throw new AssertException("Constructor expected as functionitem");
}
int instanceReg = -1;
int classReg = -1;
GraphTargetItem classNameTargetPath = null;
GraphTargetItem constructor = null;
if (item instanceof StoreRegisterActionItem) {
StoreRegisterActionItem sr = (StoreRegisterActionItem) item;
instanceReg = sr.register.number;
if (sr.value instanceof GetMemberActionItem) {
GetMemberActionItem gm = (GetMemberActionItem) sr.value;
if (gm.object instanceof TemporaryRegister) {
TemporaryRegister treg = (TemporaryRegister) gm.object;
classReg = treg.getRegId();
if (!"prototype".equals(getAsString(gm.memberName, "memberName"))) {
throw new AssertException("memberName not \"prototype\"");
}
if ((treg.value instanceof SetMemberActionItem) || (treg.value instanceof SetVariableActionItem)) {
List<String> path = getSetMembersPath(treg.value);
if (path == null || path.isEmpty()) {
logger.severe("Cannot detect class - tempreg value is not a path");
break loopcheck;
}
//remove _global if it's there - happens for classes in global package
if ("_global".equals(path.get(0))) {
path.remove(0);
}
if (classNamePath.equals(path)) {
//can start with _global for classes on top level
classNameTargetPath = getWithoutGlobal(setMemberToGetMember(treg.value));
//treg.value.value is the value being set - treg.value is setmember ot setvariable
if (!(treg.value.value instanceof StoreRegisterActionItem)) {
throw new AssertException("Constructor expected to be in storeregister");
}
if (!(treg.value.value.value instanceof FunctionActionItem)) {
throw new AssertException("Constructor expected as functionitem");
}
constructor = treg.value.value.value;
} else {
throw new AssertException("temporaryreg value does not match class path");
}
} else {
throw new AssertException("temporaryreg value not setmember/setvariable");
}
} else {
throw new AssertException("Getmember does not have TemporaryRegister as object");
}
} else {
throw new AssertException("Not Getmember in StoreRegister");
}
ppos++;
}
List<MyEntry<GraphTargetItem, GraphTargetItem>> traits = new ArrayList<>();
List<Boolean> traitsStatic = new ArrayList<>();
loopsetmembers:
for (; ppos < parts.size(); ppos++) {
item = parts.get(ppos);
if (item instanceof SetMemberActionItem) {
SetMemberActionItem sm = (SetMemberActionItem) item;
GraphTargetItem regValue;
int currentRegId = -1;
if (sm.object instanceof TemporaryRegister) {
TemporaryRegister tempReg = (TemporaryRegister) sm.object;
currentRegId = tempReg.getRegId();
regValue = tempReg.value;
} else if ((sm.object instanceof DirectValueActionItem) && (((DirectValueActionItem) sm.object).value instanceof RegisterNumber)) {
DirectValueActionItem dv = (DirectValueActionItem) sm.object;
RegisterNumber rn = ((RegisterNumber) dv.value);
currentRegId = rn.number;
regValue = dv.computedRegValue;
} else {
//might be an interface
List<String> path = getSetMembersPath(item);
if (path == null || path.isEmpty()) {
throw new AssertException("invalid setmember");
}
//remove _global if it's there - happens for classes in global package
if ("_global".equals(path.get(0))) {
path.remove(0);
}
if (!path.equals(classNamePath)) {
throw new AssertException("wrong path in setmember");
}
GraphTargetItem interfaceClass = getWithoutGlobal(setMemberToGetMember(item));
if (!(sm.value instanceof FunctionActionItem)) {
logger.severe("Cannot detect class - not a function in setmember");
break loopcheck;
}
FunctionActionItem f = (FunctionActionItem) sm.value;
if (!"".equals(f.functionName)) {
throw new AssertException("not unnamed func in setmember");
}
if (!f.actions.isEmpty()) {
throw new AssertException("not empty function in setmember");
}
if (!f.paramNames.isEmpty()) {
throw new AssertException("not empty params for function in setmember");
}
ppos++;
for (; ppos < parts.size(); ppos++) {
item = parts.get(ppos);
if (item instanceof ImplementsOpActionItem) {
if (!implementsOp.isEmpty()) {
throw new AssertException("multiple implementsAction");
}
ImplementsOpActionItem io = (ImplementsOpActionItem) item;
implementsOp = io.superclasses;
} else {
throw new AssertException("unknown iface item: " + item.getClass().getSimpleName());
}
}
InterfaceActionItem ifsItem = new InterfaceActionItem(interfaceClass, implementsOp);
for (int k = pos; k <= checkPos; k++) {
commands.remove(pos);
}
commands.add(pos, ifsItem);
//remove §§pop after, if it's there
if (pos + 1 < commands.size()) {
if (commands.get(pos + 1) instanceof PopItem) {
commands.remove(pos + 1);
}
}
// goto next line and check next classes
continue looppos;
}
//it was register .. continue class detection
if (currentRegId != instanceReg && currentRegId != classReg) {
if (!(regValue instanceof SetMemberActionItem)) {
throw new AssertException("temp register do not contain setmember");
}
SetMemberActionItem sm2 = (SetMemberActionItem) regValue;
GraphTargetItem pathSource;
boolean isPrototype;
if ("prototype".equals(getAsString(sm2.objectName, "objectName"))) {
pathSource = sm2.object;
isPrototype = true;
} else {
pathSource = setMemberToGetMember(sm2);
isPrototype = false;
}
List<String> memPath = getMembersPath(pathSource);
if (memPath == null) {
throw new AssertException("Invalid pathsource");
}
if (!classNamePath.equals(memPath)) {
throw new AssertException("Invalid path of setmember:" + String.join(".", memPath));
}
classNameTargetPath = pathSource;
if (!(sm2.value instanceof StoreRegisterActionItem)) {
throw new AssertException("Not storeregister");
}
StoreRegisterActionItem sr = (StoreRegisterActionItem) sm2.value;
if (sr.register.number != currentRegId) {
throw new AssertException("Invalid storeregister");
}
if (isPrototype && ((sr.value instanceof NewMethodActionItem) || (sr.value instanceof NewObjectActionItem))) {
extendsOp = newToGetMember(sr.value);
instanceReg = currentRegId;
} else if (!isPrototype && (sr.value instanceof FunctionActionItem)) { //constructor
constructor = sr.value;
classReg = currentRegId;
} else {
throw new AssertException("invalid storeregister value: " + sr.value.getClass().getSimpleName());
}
}
MyEntry<GraphTargetItem, GraphTargetItem> trait = new MyEntry<>(sm.objectName, sm.value);
if (sm.value instanceof FunctionActionItem) {
FunctionActionItem f = (FunctionActionItem) sm.value;
f.calculatedFunctionName = sm.objectName;
}
traits.add(trait);
if (currentRegId == instanceReg) {
traitsStatic.add(false);
} else if (currentRegId == classReg) {
traitsStatic.add(true);
}
} else if (item instanceof ImplementsOpActionItem) {
ImplementsOpActionItem iot = (ImplementsOpActionItem) item;
implementsOp = iot.superclasses;
} else {
break loopsetmembers;
}
}
looppushes:
for (; ppos < parts.size(); ppos++) {
item = parts.get(ppos);
GraphTargetItem curItem = item;
if (item instanceof PushItem) { //push is optional
PushItem pi = (PushItem) item;
curItem = pi.value;
}
if (curItem instanceof CallMethodActionItem) {
CallMethodActionItem cm = (CallMethodActionItem) curItem;
String pushMethodName = getAsString(cm.methodName, "push methodName");
if ("addProperty".equals(pushMethodName)) {
int rnumObject = getAsRegisterNum(cm.scriptObject, "addProperty not on register");
if ((rnumObject != instanceReg) && (rnumObject != classReg)) {
throw new AssertException("unexpected addProperty object register " + rnumObject);
}
if (cm.arguments.size() != 3) {
throw new AssertException("invalid number of arguments to addProperty: " + cm.arguments.size());
}
GraphTargetItem propertyName = cm.arguments.get(0);
GraphTargetItem propertyGetter = cm.arguments.get(1);
GraphTargetItem propertySetter = cm.arguments.get(2);
String propertyNameStr = getAsString(propertyName, "propertyName");
if (propertyGetter instanceof GetMemberActionItem) {
int regId = getAsRegisterNum(((GetMemberActionItem) propertyGetter).object, "getter member not register");
if (rnumObject != regId) {
throw new AssertException("getter register does not match property register " + regId + " <=> " + rnumObject);
}
String getterNameStr = getAsString(((GetMemberActionItem) propertyGetter).memberName, "getter memberName");
if (!(getterNameStr.equals("__get__" + propertyNameStr))) {
throw new AssertException("getter does not match property name");
}
//TODO: handle getter HERE
} else if (propertyGetter instanceof FunctionActionItem) {
FunctionActionItem getterFunc = (FunctionActionItem) propertyGetter;
if (!(getterFunc.actions.isEmpty() && getterFunc.functionName.isEmpty() && ((FunctionActionItem) propertyGetter).paramNames.isEmpty())) {
logger.severe("Cannot detect class - unexpected getter value for property " + propertyNameStr);
break loopcheck;
}
//we got empty getter
} else {
logger.severe("Cannot detect class - unexpected getter value for property " + propertyNameStr + ": " + propertyGetter.getClass().getSimpleName());
break loopcheck;
}
if (propertySetter instanceof GetMemberActionItem) {
int regId = getAsRegisterNum(((GetMemberActionItem) propertySetter).object, "setter member");
if (rnumObject != regId) {
throw new AssertException("setter register does not match property register " + regId + " <=> " + rnumObject);
}
String setterNameStr = getAsString(((GetMemberActionItem) propertySetter).memberName, "setter memberNAme");
if (!(setterNameStr.equals("__set__" + propertyNameStr))) {
throw new AssertException("setter does not match property name");
}
//TODO: handle setter HERE
} else if (propertySetter instanceof FunctionActionItem) {
FunctionActionItem setterFunc = (FunctionActionItem) propertySetter;
if (!(setterFunc.actions.isEmpty() && setterFunc.functionName.isEmpty() && ((FunctionActionItem) propertySetter).paramNames.isEmpty())) {
throw new AssertException("unexpected getter value for property " + propertyNameStr);
}
//we got empty setter
} else {
throw new AssertException("unexpected setter value for property " + propertyNameStr + ": " + propertySetter.getClass().getSimpleName());
}
} else {
throw new AssertException("unknown push method name: " + pushMethodName);
}
} else if (curItem instanceof CallFunctionActionItem) {
CallFunctionActionItem cf = (CallFunctionActionItem) curItem;
String funName = getAsString(cf.functionName, "pushitem function name");
if (funName.equals("ASSetPropFlags")) {
//it should be ASSetPropFlags(a.b.c.D.prototype,null,1) as it sets prototype to hidden
//see http://www.ryanjuckett.com/programming/how-to-use-assetpropflags-in-actionscript-2-0/
if (cf.arguments.size() != 3) {
throw new AssertException("Invalid number of arguments to ASSetPropFlags:" + cf.arguments.size() + ", 3 expected");
}
GraphTargetItem obj = cf.arguments.get(0);
GraphTargetItem props = cf.arguments.get(1);
GraphTargetItem flags = cf.arguments.get(2);
List<String> path = getMembersPath(obj);
if (path != null && !path.isEmpty() && "_global".equals(path.get(0))) { //For classes in toplevel package, there's _global in path
path.remove(0); //remove that _global
}
List<String> classPathWithPrototype = new ArrayList<>();
classPathWithPrototype.addAll(classNamePath);
classPathWithPrototype.add("prototype");
if (!classPathWithPrototype.equals(path)) {
throw new AssertException("ASSetPropFlags not on prototype");
}
if (!((props instanceof DirectValueActionItem) && (((DirectValueActionItem) props).value == Null.INSTANCE))) {
throw new AssertException("ASSetPropFlags properties param not null");
}
if (!((flags instanceof DirectValueActionItem) && (((DirectValueActionItem) flags).value == (Long) 1L))) {
throw new AssertException("ASSetPropFlags flags not set to 1");
}
} else {
throw new AssertException("unknown pushitem function call " + funName);
}
} else {
throw new AssertException("unknown item - " + item.getClass().getSimpleName());
}
}//end loop pushes
if (constructor != null) { //constructor should be there always, but just in calse
//add constructor as trait
traitsStatic.add(0, false);
DirectValueActionItem classBaseName = new DirectValueActionItem(classNamePath.get(classNamePath.size() - 1));
((FunctionActionItem) constructor).calculatedFunctionName = classBaseName;
traits.add(0, new MyEntry<>(classBaseName, constructor));
} else {
throw new AssertException("No constructor found");
}
ClassActionItem clsItem = new ClassActionItem(classNameTargetPath, extendsOp, implementsOp, traits, traitsStatic);
for (int k = pos; k <= checkPos; k++) {
commands.remove(pos);
}
commands.add(pos, clsItem);
//remove §§pop after, if it's there
if (pos + 1 < commands.size()) {
if (commands.get(pos + 1) instanceof PopItem) {
commands.remove(pos + 1);
}
}
// goto next line and check next classes
continue looppos;
} catch (AssertException ex) {
logger.log(Level.WARNING, "{0}: Cannot detect class - {1}", new Object[]{scriptPath, ex.getCondition()});
break loopcheck;
constructor = treg.value.value.value;
} else {
throw new AssertException("temporaryreg value does not match class path");
}
} else {
break loopcheck; //not !_global.a.b or not member of previous
throw new AssertException("temporaryreg value not setmember/setvariable");
}
} else {
break loopcheck; //not an if !
throw new AssertException("Getmember does not have TemporaryRegister as object");
}
} else {
break loopcheck; //not an if
throw new AssertException("Not Getmember in StoreRegister");
}
} //loopcheck
}//looppos
partsPos++;
}
List<MyEntry<GraphTargetItem, GraphTargetItem>> traits = new ArrayList<>();
List<Boolean> traitsStatic = new ArrayList<>();
loopsetmembers:
for (; partsPos < parts.size(); partsPos++) {
item = parts.get(partsPos);
if (item instanceof PushItem) { //push is optional
PushItem pi = (PushItem) item;
item = pi.value;
}
if (item instanceof SetMemberActionItem) {
SetMemberActionItem sm = (SetMemberActionItem) item;
GraphTargetItem regValue;
int currentRegId = -1;
if (sm.object instanceof TemporaryRegister) {
TemporaryRegister tempReg = (TemporaryRegister) sm.object;
currentRegId = tempReg.getRegId();
regValue = tempReg.value;
} else if ((sm.object instanceof DirectValueActionItem) && (((DirectValueActionItem) sm.object).value instanceof RegisterNumber)) {
DirectValueActionItem dv = (DirectValueActionItem) sm.object;
RegisterNumber rn = ((RegisterNumber) dv.value);
currentRegId = rn.number;
regValue = dv.computedRegValue;
} else {
//might be an interface
List<String> path = getSetMembersPath(item);
if (path == null || path.isEmpty()) {
throw new AssertException("invalid setmember");
}
//remove _global if it's there - happens for classes in global package
if ("_global".equals(path.get(0))) {
path.remove(0);
}
if (!path.equals(classNamePath)) {
throw new AssertException("wrong path in setmember");
}
GraphTargetItem interfaceClass = getWithoutGlobal(setMemberToGetMember(item));
if (!(sm.value instanceof FunctionActionItem)) {
throw new AssertException("not a function in setmember");
}
FunctionActionItem f = (FunctionActionItem) sm.value;
if (!"".equals(f.functionName)) {
throw new AssertException("not unnamed func in setmember");
}
if (!f.actions.isEmpty()) {
throw new AssertException("not empty function in setmember");
}
if (!f.paramNames.isEmpty()) {
throw new AssertException("not empty params for function in setmember");
}
partsPos++;
for (; partsPos < parts.size(); partsPos++) {
item = parts.get(partsPos);
if (item instanceof ImplementsOpActionItem) {
if (!implementsOp.isEmpty()) {
throw new AssertException("multiple implementsAction");
}
ImplementsOpActionItem io = (ImplementsOpActionItem) item;
implementsOp = io.superclasses;
} else {
throw new AssertException("unknown iface item: " + item.getClass().getSimpleName());
}
}
InterfaceActionItem ifsItem = new InterfaceActionItem(interfaceClass, implementsOp);
for (int k = commandsStartPos; k <= commandsEndPos; k++) {
commands.remove(commandsStartPos);
}
commands.add(commandsStartPos, ifsItem);
//remove §§pop after, if it's there
if (commandsStartPos + 1 < commands.size()) {
if (commands.get(commandsStartPos + 1) instanceof PopItem) {
commands.remove(commandsStartPos + 1);
}
}
// goto next line and check next classes
return true;
}
//it was register .. continue class detection
if (currentRegId != instanceReg && currentRegId != classReg) {
if (!(regValue instanceof SetMemberActionItem)) {
throw new AssertException("temp register do not contain setmember");
}
SetMemberActionItem sm2 = (SetMemberActionItem) regValue;
GraphTargetItem pathSource;
boolean isPrototype;
if ("prototype".equals(getAsString(sm2.objectName, "objectName"))) {
pathSource = sm2.object;
isPrototype = true;
} else {
pathSource = setMemberToGetMember(sm2);
isPrototype = false;
}
List<String> memPath = getMembersPath(pathSource);
if (memPath == null) {
throw new AssertException("Invalid pathsource");
}
if (!classNamePath.equals(memPath)) {
throw new AssertException("Invalid path of setmember:" + String.join(".", memPath));
}
classNameTargetPath = pathSource;
if (!(sm2.value instanceof StoreRegisterActionItem)) {
throw new AssertException("Not storeregister");
}
StoreRegisterActionItem sr = (StoreRegisterActionItem) sm2.value;
if (sr.register.number != currentRegId) {
throw new AssertException("Invalid storeregister");
}
if (isPrototype && ((sr.value instanceof NewMethodActionItem) || (sr.value instanceof NewObjectActionItem))) {
extendsOp = newToGetMember(sr.value);
instanceReg = currentRegId;
} else if (!isPrototype && (sr.value instanceof FunctionActionItem)) { //constructor
constructor = sr.value;
classReg = currentRegId;
} else {
throw new AssertException("invalid storeregister value: " + sr.value.getClass().getSimpleName());
}
}
MyEntry<GraphTargetItem, GraphTargetItem> trait = new MyEntry<>(sm.objectName, sm.value);
if (sm.value instanceof FunctionActionItem) {
FunctionActionItem f = (FunctionActionItem) sm.value;
f.calculatedFunctionName = sm.objectName;
}
traits.add(trait);
if (currentRegId == instanceReg) {
traitsStatic.add(false);
} else if (currentRegId == classReg) {
traitsStatic.add(true);
}
} else if (item instanceof ImplementsOpActionItem) {
ImplementsOpActionItem iot = (ImplementsOpActionItem) item;
implementsOp = iot.superclasses;
} else if (item instanceof CallMethodActionItem) {
CallMethodActionItem cm = (CallMethodActionItem) item;
String pushMethodName = getAsString(cm.methodName, "push methodName");
if ("addProperty".equals(pushMethodName)) {
int rnumObject = getAsRegisterNum(cm.scriptObject, "addProperty not on register");
if ((rnumObject != instanceReg) && (rnumObject != classReg)) {
throw new AssertException("unexpected addProperty object register " + rnumObject);
}
if (cm.arguments.size() != 3) {
throw new AssertException("invalid number of arguments to addProperty: " + cm.arguments.size());
}
GraphTargetItem propertyName = cm.arguments.get(0);
GraphTargetItem propertyGetter = cm.arguments.get(1);
GraphTargetItem propertySetter = cm.arguments.get(2);
String propertyNameStr = getAsString(propertyName, "propertyName");
if (propertyGetter instanceof GetMemberActionItem) {
int regId = getAsRegisterNum(((GetMemberActionItem) propertyGetter).object, "getter member not register");
if (rnumObject != regId) {
throw new AssertException("getter register does not match property register " + regId + " <=> " + rnumObject);
}
String getterNameStr = getAsString(((GetMemberActionItem) propertyGetter).memberName, "getter memberName");
if (!(getterNameStr.equals("__get__" + propertyNameStr))) {
throw new AssertException("getter does not match property name");
}
//TODO: handle getter HERE
} else if (propertyGetter instanceof FunctionActionItem) {
FunctionActionItem getterFunc = (FunctionActionItem) propertyGetter;
if (!(getterFunc.actions.isEmpty() && getterFunc.functionName.isEmpty() && ((FunctionActionItem) propertyGetter).paramNames.isEmpty())) {
throw new AssertException("unexpected getter value for property " + propertyNameStr);
}
//we got empty getter
} else {
throw new AssertException("unexpected getter value for property " + propertyNameStr + ": " + propertyGetter.getClass().getSimpleName());
}
if (propertySetter instanceof GetMemberActionItem) {
int regId = getAsRegisterNum(((GetMemberActionItem) propertySetter).object, "setter member");
if (rnumObject != regId) {
throw new AssertException("setter register does not match property register " + regId + " <=> " + rnumObject);
}
String setterNameStr = getAsString(((GetMemberActionItem) propertySetter).memberName, "setter memberNAme");
if (!(setterNameStr.equals("__set__" + propertyNameStr))) {
throw new AssertException("setter does not match property name");
}
//TODO: handle setter HERE
} else if (propertySetter instanceof FunctionActionItem) {
FunctionActionItem setterFunc = (FunctionActionItem) propertySetter;
if (!(setterFunc.actions.isEmpty() && setterFunc.functionName.isEmpty() && ((FunctionActionItem) propertySetter).paramNames.isEmpty())) {
throw new AssertException("unexpected getter value for property " + propertyNameStr);
}
//we got empty setter
} else {
throw new AssertException("unexpected setter value for property " + propertyNameStr + ": " + propertySetter.getClass().getSimpleName());
}
} else {
throw new AssertException("unknown push method name: " + pushMethodName);
}
} else if (item instanceof CallFunctionActionItem) {
CallFunctionActionItem cf = (CallFunctionActionItem) item;
String funName = getAsString(cf.functionName, "pushitem function name");
if (funName.equals("ASSetPropFlags")) {
//it should be ASSetPropFlags(a.b.c.D.prototype,null,1) as it sets prototype to hidden
//see http://www.ryanjuckett.com/programming/how-to-use-assetpropflags-in-actionscript-2-0/
if (cf.arguments.size() != 3) {
throw new AssertException("Invalid number of arguments to ASSetPropFlags:" + cf.arguments.size() + ", 3 expected");
}
GraphTargetItem obj = cf.arguments.get(0);
GraphTargetItem props = cf.arguments.get(1);
GraphTargetItem flags = cf.arguments.get(2);
if ((obj instanceof DirectValueActionItem) && (((DirectValueActionItem) obj).value instanceof RegisterNumber)) {
RegisterNumber rn = (RegisterNumber) ((DirectValueActionItem) obj).value;
if (rn.number != instanceReg) {
throw new AssertException("ASSetPropFlags not on instanceReg");
}
} else {
List<String> path = getMembersPath(obj);
if (path != null && !path.isEmpty() && "_global".equals(path.get(0))) { //For classes in toplevel package, there's _global in path
path.remove(0); //remove that _global
}
List<String> classPathWithPrototype = new ArrayList<>();
classPathWithPrototype.addAll(classNamePath);
classPathWithPrototype.add("prototype");
if (!classPathWithPrototype.equals(path)) {
throw new AssertException("ASSetPropFlags not on prototype");
}
}
if (!((props instanceof DirectValueActionItem) && (((DirectValueActionItem) props).value == Null.INSTANCE))) {
throw new AssertException("ASSetPropFlags properties param not null");
}
if (!((flags instanceof DirectValueActionItem) && (((DirectValueActionItem) flags).value == (Long) 1L))) {
throw new AssertException("ASSetPropFlags flags not set to 1");
}
} else {
throw new AssertException("unknown pushitem function call " + funName);
}
} else {
throw new AssertException("unknown item - " + item.getClass().getSimpleName());
}
}
if (constructor != null) { //constructor should be there always, but just in calse
//add constructor as trait
traitsStatic.add(0, false);
DirectValueActionItem classBaseName = new DirectValueActionItem(classNamePath.get(classNamePath.size() - 1));
((FunctionActionItem) constructor).calculatedFunctionName = classBaseName;
traits.add(0, new MyEntry<>(classBaseName, constructor));
} else {
throw new AssertException("No constructor found");
}
ClassActionItem clsItem = new ClassActionItem(classNameTargetPath, extendsOp, implementsOp, traits, traitsStatic);
for (int k = commandsStartPos; k <= commandsEndPos; k++) {
commands.remove(commandsStartPos);
}
commands.add(commandsStartPos, clsItem);
//remove §§pop after, if it's there
if (commandsStartPos + 1 < commands.size()) {
if (commands.get(commandsStartPos + 1) instanceof PopItem) {
commands.remove(commandsStartPos + 1);
}
}
// goto next line and check next classes
return true;
} catch (AssertException ex) {
logger.log(Level.WARNING, "{0}: Cannot detect class - {1}", new Object[]{scriptPath, ex.getCondition()});
}
return false;
}
private boolean checkIfVariants(List<GraphTargetItem> commands, int pos, String scriptPath) {
/*
Variant 1:
if(!_global.a)
{
_global.a = new Object();
}
§§pop();
if(!_global.a.b)
{
_global.a.b = new Object();
}
§§pop();
if(!_global.a.b.c)
{
_global.a.b.c = new Object();
}
§§pop();
if(!_global.a.b.c.D)
{
..class_content...
}
§§pop();
*/
List<String> pathToSearchVariant1 = new ArrayList<>();
pathToSearchVariant1.add("_global");
check_variant1:
for (int checkPos = pos; checkPos < commands.size(); checkPos++) {
GraphTargetItem t = commands.get(checkPos);
if (t instanceof IfItem) {
IfItem ifItem = (IfItem) t;
if (ifItem.expression instanceof NotItem) {
NotItem nti = (NotItem) ifItem.expression;
GraphTargetItem condType = nti.value;
Reference<String> newMemberNameRef = new Reference<>("");
if (isMemberOfPath(condType, pathToSearchVariant1, newMemberNameRef)) {
pathToSearchVariant1.add(newMemberNameRef.getVal());
//_global.a.b.c = new Object();
if ((ifItem.onTrue.size() == 1) && (ifItem.onTrue.get(0) instanceof SetMemberActionItem) && (((SetMemberActionItem) ifItem.onTrue.get(0)).value instanceof NewObjectActionItem)) {
//skip §§pop item if its there right after if
if (checkPos + 1 < commands.size()) {
GraphTargetItem tnext = commands.get(checkPos + 1);
if (tnext instanceof PopItem) {
checkPos++;
}
}
continue check_variant1;
}
List<String> classPath = pathToSearchVariant1;
classPath.remove(0); //remove _global
if (this.checkClassContent(ifItem.onTrue, 0, pos, checkPos, commands, classPath, scriptPath)) {
return true;
} else {
break check_variant1;
}
} else {
break check_variant1;
}
} else {
break check_variant1; //not an if !
}
} else {
break check_variant1; //not an if
}
} //check_variant1
/*
Variant 2:
if(!a.b.c.D)
{
if(!a)
{
_global.a = new Object();
}
if(!a.b)
{
_global.a.b = new Object();
}
if(!a.b.c)
{
_global.a.b.c = new Object();
}
..class_content..
}
*/
List<String> variant2CurrentPath = new ArrayList<>();
check_variant2:
if (commands.get(pos) instanceof IfItem) {
IfItem ifItem = (IfItem) commands.get(pos);
if (ifItem.expression instanceof NotItem) {
NotItem nti = (NotItem) ifItem.expression;
List<String> memPath = getMembersPath(nti.value);
if (memPath == null) {
break check_variant2;
}
if (ifItem.onTrue.size() < memPath.size()) {
break check_variant2;
}
variant2CurrentPath.clear();
List<GraphTargetItem> parts = ifItem.onTrue;
int checkPos = 0;
for (; checkPos < memPath.size() - 1; checkPos++) {
variant2CurrentPath.add(memPath.get(checkPos));
if (!(parts.get(checkPos) instanceof IfItem)) {
break check_variant2;
}
IfItem ifItem2 = (IfItem) parts.get(checkPos);
if (ifItem2.expression instanceof NotItem) {
NotItem nti2 = (NotItem) ifItem2.expression;
List<String> if2Path = getMembersPath(nti2.value);
if (!variant2CurrentPath.equals(if2Path)) {
break check_variant2;
}
}
}
if (checkClassContent(parts, checkPos, pos, pos, commands, memPath, scriptPath)) {
return true;
}
}
}//check_variant2
return false;
}
public void checkClass(List<GraphTargetItem> commands, String scriptPath) {
for (int pos = 0; pos < commands.size(); pos++) {
checkIfVariants(commands, pos, scriptPath);
}
}
}