From bfd45b1441c3476c7fc7a9ad8608b0217773aed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 24 Sep 2023 22:48:37 +0200 Subject: [PATCH] Fixed #2086 AS3 direct editation - Correct class order (instanceinfo,classinfo) respecting extends/implements --- CHANGELOG.md | 2 + .../com/jpexs/decompiler/flash/abc/ABC.java | 101 ++++++++++++++---- .../parser/script/AVM2SourceGenerator.java | 34 +++++- 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d81d84109..9b8b933a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ All notable changes to this project will be documented in this file. - Not working "Show in Projector" button for DefineFont4 (hide) - Proper error message when there is no room for new characters in the font (DefineFont1) - Synchronization problems when adding characters to the font vs its display +- [#2086] AS3 direct editation - Correct class order (instanceinfo,classinfo) respecting extends/implements ### Changed - [#2070] String values inside SWF to XML export are backslash escaped @@ -3103,6 +3104,7 @@ Major version of SWF to XML export changed to 2. [#2029]: https://www.free-decompiler.com/flash/issues/2029 [#2077]: https://www.free-decompiler.com/flash/issues/2077 [#2076]: https://www.free-decompiler.com/flash/issues/2076 +[#2086]: https://www.free-decompiler.com/flash/issues/2086 [#1998]: https://www.free-decompiler.com/flash/issues/1998 [#2038]: https://www.free-decompiler.com/flash/issues/2038 [#2028]: https://www.free-decompiler.com/flash/issues/2028 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index 59a9cd829..7f6119ec0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -71,6 +71,7 @@ import com.jpexs.decompiler.flash.treeitems.Openable; import com.jpexs.decompiler.flash.treeitems.OpenableList; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.helpers.Reference; import com.jpexs.helpers.utf8.Utf8PrintWriter; import java.io.File; import java.io.IOException; @@ -290,32 +291,32 @@ public class ABC implements Openable { } return ret; } - + public void getTraitStringUsages(Set ret, Traits traits) { - for (Trait t:traits.traits) { + for (Trait t : traits.traits) { if (t instanceof TraitClass) { - int ci = ((TraitClass)t).class_info; + int ci = ((TraitClass) t).class_info; getTraitStringUsages(ret, instance_info.get(ci).instance_traits); getTraitStringUsages(ret, class_info.get(ci).static_traits); } if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst)t; + TraitSlotConst tsc = (TraitSlotConst) t; if (tsc.value_kind == ValueKind.CONSTANT_Utf8) { ret.add(tsc.value_index); } } } } - + public Set getStringUsages() { Set ret = new HashSet<>(); - - for (ScriptInfo si:script_info) { + + for (ScriptInfo si : script_info) { getTraitStringUsages(ret, si.traits); } - - for (MethodInfo mi: method_info) { - if((mi.flags & MethodInfo.FLAG_HAS_OPTIONAL) == MethodInfo.FLAG_HAS_OPTIONAL) { + + for (MethodInfo mi : method_info) { + if ((mi.flags & MethodInfo.FLAG_HAS_OPTIONAL) == MethodInfo.FLAG_HAS_OPTIONAL) { for (ValueKind vk : mi.optional) { if (vk.value_kind == ValueKind.CONSTANT_Utf8) { ret.add(vk.value_index); @@ -423,7 +424,7 @@ public class ABC implements Openable { } } - public void deobfuscateIdentifiers(Map stringUsageTypes, Set stringUsages, HashMap namesMap, RenameType renameType, boolean doClasses) { + public void deobfuscateIdentifiers(Map stringUsageTypes, Set stringUsages, HashMap namesMap, RenameType renameType, boolean doClasses) { Set namespaceUsages = getNsStringUsages(); AVM2Deobfuscation deobfuscation = getDeobfuscation(); @@ -448,13 +449,13 @@ public class ABC implements Openable { } else { return; } - + for (int i = 1; i < constants.getMultinameCount(); i++) { informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); - + Multiname m = constants.getMultiname(i); int strIndex = m.name_index; - if (m.kind == Multiname.MULTINAME && strIndex > 0 && "*".equals(constants.getString(strIndex))) { + if (m.kind == Multiname.MULTINAME && strIndex > 0 && "*".equals(constants.getString(strIndex))) { continue; } constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); @@ -1218,11 +1219,11 @@ public class ABC implements Openable { } } } - + private boolean isSameName(int expectedQNameIndex, int checkedNameIndex, boolean exactMatch) { if (expectedQNameIndex == checkedNameIndex) { return true; - } + } if (exactMatch) { return false; } @@ -1231,7 +1232,7 @@ public class ABC implements Openable { if (checkedName == null) { return false; } - + if (expectedQName.name_index != checkedName.name_index) { return false; } @@ -1241,11 +1242,11 @@ public class ABC implements Openable { if (checkedName.kind != Multiname.MULTINAME) { return false; } - for (int ns:constants.getNamespaceSet(checkedName.namespace_set_index).namespaces) { + for (int ns : constants.getNamespaceSet(checkedName.namespace_set_index).namespaces) { if (ns == expectedQName.namespace_index) { return true; } - } + } return false; } @@ -1451,7 +1452,7 @@ public class ABC implements Openable { } for (int s = 0; s < script_info.size(); s++) { - checkAllMultinameUsedInMethod(script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1); + checkAllMultinameUsedInMethod(script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1); findAllMultinameUsageInTraits(script_info.get(s).traits, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1); } @@ -1602,6 +1603,60 @@ public class ABC implements Openable { } } + private void moveClassInTraits(Traits traits, int oldIndex, int newIndex) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + moveClassInTraits(instance_info.get(tc.class_info).instance_traits, oldIndex, newIndex); + moveClassInTraits(class_info.get(tc.class_info).static_traits, oldIndex, newIndex); + tc.class_info = moveClassIndex(oldIndex, newIndex, tc.class_info); + } + } + } + + private int moveClassIndex(int oldIndex, int newIndex, int currentIndex) { + if (newIndex > oldIndex) { + newIndex--; + } + if (currentIndex == oldIndex) { + return newIndex; + } + if (currentIndex > oldIndex) { + currentIndex = currentIndex - 1; + } + if (currentIndex >= newIndex) { + currentIndex = currentIndex + 1; + } + return currentIndex; + } + + public void moveClass(int oldIndex, int newIndex) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.getCode().code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + ins.setOperand(i, moveClassIndex(oldIndex, newIndex, ins.operands[0]), b.getCode(), b); + } + } + } + } + + for (ScriptInfo si : script_info) { + moveClassInTraits(si.traits, oldIndex, newIndex); + } + for (MethodBody b : bodies) { + moveClassInTraits(b.traits, oldIndex, newIndex); + } + + if (newIndex > oldIndex) { + newIndex--; + } + InstanceInfo ii = instance_info.remove(oldIndex); + ClassInfo ci = class_info.remove(oldIndex); + instance_info.add(newIndex, ii); + class_info.add(newIndex, ci); + } + public void addClass(ClassInfo ci, InstanceInfo ii, int index) { for (MethodBody b : bodies) { for (AVM2Instruction ins : b.getCode().code) { @@ -2222,9 +2277,9 @@ public class ABC implements Openable { String name = constants.getString(ns.name_index); if (name.equals("http://adobe.com/AS3/2006/builtin")) { //TODO: This should really be resolved using ABC indexing, not hardcoded constant return DottedChain.parseNoSuffix("AS3"); - } - - return getSwf().getAbcIndex().nsValueToName(name); + } + + return getSwf().getAbcIndex().nsValueToName(name); /* for (ABCContainerTag abcTag : getAbcTags()) { DottedChain dc = abcTag.getABC().nsValueToName(name); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index 9b63aed9d..c937827f0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -2180,20 +2180,41 @@ public class AVM2SourceGenerator implements SourceGenerator { continue; } if (item instanceof ClassAVM2Item) { + int minClassIndex = 0; localData.currentClass = ((ClassAVM2Item) item).pkg.name.addWithSuffix(((ClassAVM2Item) item).className).toRawString(); InstanceInfo instanceInfo = abcIndex.getSelectedAbc().instance_info.get(((TraitClass) traits[k]).class_info); + ABC abc = abcIndex.getSelectedAbc(); if (((ClassAVM2Item) item).extendsOp != null) { instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); } else { instanceInfo.super_index = abcIndex.getSelectedAbc().constants.getMultinameId(Multiname.createQName(false, str("Object"), namespace(Namespace.KIND_PACKAGE, "")), true); } + if (instanceInfo.super_index != 0) { + int foundClass = abc.findClassByName(abc.constants.getMultiname(instanceInfo.super_index).getNameWithNamespace(abc.constants, true)); + if (foundClass > -1) { + if (foundClass > minClassIndex) { + minClassIndex = foundClass; + } + } + } instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); + instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); + int foundIface = abc.findClassByName(abc.constants.getMultiname(instanceInfo.interfaces[i]).getNameWithNamespace(abc.constants, true)); + if (foundIface > -1) { + if (foundIface > minClassIndex) { + minClassIndex = foundIface; + } + } + } + int oldClassIndex = ((TraitClass) traits[k]).class_info; + if (oldClassIndex <= minClassIndex) { + abc.moveClass(oldClassIndex, minClassIndex + 1); } localData.currentClass = null; } if (item instanceof InterfaceAVM2Item) { + int minClassIndex = 0; localData.currentClass = ((InterfaceAVM2Item) item).pkg.name.addWithSuffix(((InterfaceAVM2Item) item).name).toRawString(); ABC abc = abcIndex.getSelectedAbc(); InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); @@ -2201,6 +2222,17 @@ public class AVM2SourceGenerator implements SourceGenerator { for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); instanceInfo.interfaces[i] = superIntName(localData, un); + + int foundIface = abc.findClassByName(abc.constants.getMultiname(instanceInfo.interfaces[i]).getNameWithNamespace(abc.constants, true)); + if (foundIface > -1) { + if (foundIface > minClassIndex) { + minClassIndex = foundIface; + } + } + } + int oldClassIndex = ((TraitClass) traits[k]).class_info; + if (oldClassIndex <= minClassIndex) { + abc.moveClass(oldClassIndex, minClassIndex + 1); } localData.currentClass = null; }