diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java index 61117ef5a..37b833f4e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScript2ClassDetector.java @@ -268,427 +268,471 @@ public class ActionScript2ClassDetector { return mnDv.getAsString(); } - public void checkClass(List 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 parts, int partsPos, int commandsStartPos, int commandsEndPos, List commands, List classNamePath, String scriptPath) { - List 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 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 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 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 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 classNamePath = pathToSearchInIfCond; - classNamePath.remove(0); //remove "_global" - - int ppos = 0; - GraphTargetItem item = parts.get(ppos); - GraphTargetItem extendsOp = null; - List 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 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> traits = new ArrayList<>(); - List 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 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 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 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 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 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> traits = new ArrayList<>(); + List 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 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 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 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 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 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 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 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 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 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 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 memPath = getMembersPath(nti.value); + if (memPath == null) { + break check_variant2; + } + if (ifItem.onTrue.size() < memPath.size()) { + break check_variant2; + } + variant2CurrentPath.clear(); + List 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 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 commands, String scriptPath) { + for (int pos = 0; pos < commands.size(); pos++) { + checkIfVariants(commands, pos, scriptPath); + } } }