mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-05 00:45:50 +00:00
AS2 class detector - checking other class variant
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user