diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d258fc33..1f4d1a8b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ All notable changes to this project will be documented in this file. - [#2493] Incorrect placement of §§push instructions - AS3 direct editation/highlighter §§pop in class traits - AS direct editation/highlighter §§pop and §§dup can have getmember or call +- [#1295], [#116] AS3 dynamic construction via MultinameL ### Changed - Icon of "Deobfuscation options" menu from pile of pills to medkit @@ -3937,6 +3938,8 @@ Major version of SWF to XML export changed to 2. [#2483]: https://www.free-decompiler.com/flash/issues/2483 [#2486]: https://www.free-decompiler.com/flash/issues/2486 [#2493]: https://www.free-decompiler.com/flash/issues/2493 +[#1295]: https://www.free-decompiler.com/flash/issues/1295 +[#116]: https://www.free-decompiler.com/flash/issues/116 [#2476]: https://www.free-decompiler.com/flash/issues/2476 [#2404]: https://www.free-decompiler.com/flash/issues/2404 [#1418]: https://www.free-decompiler.com/flash/issues/1418 @@ -4178,7 +4181,6 @@ Major version of SWF to XML export changed to 2. [#2099]: https://www.free-decompiler.com/flash/issues/2099 [#2090]: https://www.free-decompiler.com/flash/issues/2090 [#2079]: https://www.free-decompiler.com/flash/issues/2079 -[#116]: https://www.free-decompiler.com/flash/issues/116 [#2097]: https://www.free-decompiler.com/flash/issues/2097 [#2098]: https://www.free-decompiler.com/flash/issues/2098 [#2093]: https://www.free-decompiler.com/flash/issues/2093 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructPropIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructPropIns.java index 2b0c1f7e2..18b293243 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructPropIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructPropIns.java @@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.RegExpAvm2Item; import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.XMLAVM2Item; +import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.helpers.Reference; @@ -108,7 +109,7 @@ public class ConstructPropIns extends InstructionDefinition { if (obj instanceof FindPropertyAVM2Item) { multiname.property = false; //can be type - } + } stack.push(new ConstructPropAVM2Item(ins, localData.lineStartInstruction, obj, multiname, args, type.getVal(), isStatic.getVal())); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java index c6738cb81..55db0972d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java @@ -16,11 +16,14 @@ */ package com.jpexs.decompiler.flash.abc.avm2.model; +import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Helper; import java.util.List; import java.util.Objects; @@ -75,12 +78,45 @@ public class ConstructAVM2Item extends AVM2Item { || (object instanceof CallStaticAVM2Item) || (object instanceof CallSuperAVM2Item); - if (object.getPrecedence() > getPrecedence() || objectIsCall) { - writer.append("("); + boolean isGetDefinition = false; + if (object instanceof GetPropertyAVM2Item) { + GetPropertyAVM2Item getProperty = (GetPropertyAVM2Item) object; + if (getProperty.propertyName instanceof FullMultinameAVM2Item) { + FullMultinameAVM2Item fm = (FullMultinameAVM2Item) getProperty.propertyName; + if (fm.multinameIndex > 0) { + Multiname m = localData.abc.constants.getMultiname(fm.multinameIndex); + if (m.kind == Multiname.MULTINAMEL) { + if (m.getNamespaceSet(localData.abc.constants).namespaces.length == 1) { + isGetDefinition = true; + writer.append("("); + if (localData.fullyQualifiedNames.contains(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"))) { + writer.append("flash.utils.getDefinitionByName"); + } else { + writer.append("getDefinitionByName"); + } + writer.append("("); + String nname = m.getSingleNamespace(localData.abc.constants).getName(localData.abc.constants).toRawString(); + if (!nname.isEmpty()) { + writer.append("\""); + writer.append(Helper.escapeActionScriptString(nname)); + writer.append("\"+\".\"+"); + } + fm.name.appendTo(writer, localData); + writer.append("))"); + } + } + } + } } - object.toString(writer, localData); - if (object.getPrecedence() > getPrecedence() || objectIsCall) { - writer.append(")"); + + if (!isGetDefinition) { + if (object.getPrecedence() > getPrecedence() || objectIsCall) { + writer.append("("); + } + object.toString(writer, localData); + if (object.getPrecedence() > getPrecedence() || objectIsCall) { + writer.append(")"); + } } writer.spaceBeforeCallParenthesis(args.size()); if (object instanceof InitVectorAVM2Item) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructPropAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructPropAVM2Item.java index 85e793db4..0aa69d86f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructPropAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructPropAVM2Item.java @@ -16,11 +16,14 @@ */ package com.jpexs.decompiler.flash.abc.avm2.model; +import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Helper; import java.util.List; import java.util.Objects; @@ -86,7 +89,36 @@ public class ConstructPropAVM2Item extends AVM2Item { @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { writer.append("new "); - formatProperty(writer, object, propertyName, localData, isStatic, false); + boolean isGetDefinition = false; + if (propertyName instanceof FullMultinameAVM2Item) { + FullMultinameAVM2Item fm = (FullMultinameAVM2Item) propertyName; + if (fm.multinameIndex > 0) { + Multiname m = localData.abc.constants.getMultiname(fm.multinameIndex); + if (m.kind == Multiname.MULTINAMEL) { + if (m.getNamespaceSet(localData.abc.constants).namespaces.length == 1) { + isGetDefinition = true; + writer.append("("); + if (localData.fullyQualifiedNames.contains(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"))) { + writer.append("flash.utils.getDefinitionByName"); + } else { + writer.append("getDefinitionByName"); + } + writer.append("("); + String nname = m.getSingleNamespace(localData.abc.constants).getName(localData.abc.constants).toRawString(); + if (!nname.isEmpty()) { + writer.append("\""); + writer.append(Helper.escapeActionScriptString(nname)); + writer.append("\"+\".\"+"); + } + fm.name.appendTo(writer, localData); + writer.append("))"); + } + } + } + } + if (!isGetDefinition) { + formatProperty(writer, object, propertyName, localData, isStatic, false); + } writer.spaceBeforeCallParenthesis(args.size()); writer.append("("); for (int a = 0; a < args.size(); a++) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java index c553be4d5..5b38cb60c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java @@ -989,7 +989,7 @@ public class Multiname { * @return True if this MULTINAME kind with only one namespace */ public boolean isMULTINAMEwithOneNs(AVM2ConstantPool pool) { - return kind == MULTINAME && pool.getNamespaceSet(namespace_set_index).namespaces.length == 1; + return (kind == MULTINAME || kind == MULTINAMEL || kind == MULTINAMELA) && pool.getNamespaceSet(namespace_set_index).namespaces.length == 1; } /** diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java index d4bfb026c..f9678b4fc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java @@ -21,10 +21,13 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.AlchemyTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetOuterScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.parser.script.AbcIndexing; @@ -196,6 +199,7 @@ public class DependencyParser { } } boolean wasNewClass = false; + AVM2Instruction prevIns = null; for (int i = 0; i < body.getCode().code.size(); i++) { AVM2Instruction ins = body.getCode().code.get(i); @@ -245,6 +249,26 @@ public class DependencyParser { } } } + if (ins.definition instanceof ConstructPropIns) { + Multiname m = abc.constants.getMultiname(ins.operands[0]); + if (m != null) { + if (m.kind == Multiname.MULTINAMEL) { + Dependency dep = new Dependency(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"), DependencyType.NAMESPACE); + dependencies.add(dep); + } + } + } + + //TODO: what if there's jump between getproperty and construct? + if (ins.definition instanceof ConstructIns && prevIns != null && prevIns.definition instanceof GetPropertyIns) { + Multiname m = abc.constants.getMultiname(prevIns.operands[0]); + if (m != null) { + if (m.kind == Multiname.MULTINAMEL) { + Dependency dep = new Dependency(DottedChain.parseNoSuffix("flash.utils.getDefinitionByName"), DependencyType.NAMESPACE); + dependencies.add(dep); + } + } + } if (classIndex > -1 && ins.definition instanceof GetOuterScopeIns) { if (ins.operands[0] > 0) { //first is global DottedChain type = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, true); @@ -282,6 +306,7 @@ public class DependencyParser { numberContextRef.setVal(ins.operands[k]); } } + prevIns = ins; } } } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassTest.java index 743e18a40..2d009a5cd 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassTest.java @@ -748,4 +748,25 @@ public class ActionScript3ClassTest extends ActionScript3DecompileTestBase { + "}\n" ); } + + @Test + public void testConstructDynamically() { + decompileScriptPack("assembled", "tests.TestConstructDynamically", "package tests\n" + + "{\n" + + "import flash.display.*;\n" + + "import flash.utils.getDefinitionByName;\n" + + "public class TestConstructDynamically\n" + + "{\n" + + "public function TestConstructDynamically()\n" + + "{\n" + + "super();\n" + + "}\n" + + "public function test() : void\n" + + "{\n" + + "var _loc1_:* = new (getDefinitionByName(\"flash.display\"+\".\"+\"Sprite\"))();\n" + + "_loc1_ = new (getDefinitionByName(\"Object\"))();\n" + + "}\n" + + "}\n" + + "}"); + } } diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc index f5ec7afc1..b6b6498f0 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc and b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc differ diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm index 96276599b..61760f42a 100644 --- a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm @@ -40,5 +40,6 @@ program #include "tests/TestAlwaysBreak.script.asasm" #include "tests/TestAlwaysBreak2.script.asasm" #include "tests/TestPushPlacement.script.asasm" + #include "tests/TestConstructDynamically.script.asasm" ; place to add next end ; program diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestConstructDynamically.class.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestConstructDynamically.class.asasm new file mode 100644 index 000000000..9ffb3dc51 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestConstructDynamically.class.asasm @@ -0,0 +1,70 @@ +class + refid "tests:TestConstructDynamically" + instance QName(PackageNamespace("tests"), "TestConstructDynamically") + extends QName(PackageNamespace(""), "Object") + flag SEALED + flag PROTECTEDNS + protectedns ProtectedNamespace("tests:TestConstructDynamically") + iinit + refid "tests:TestConstructDynamically/instance/init" + body + maxstack 1 + localcount 1 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + + getlocal0 + constructsuper 0 + + returnvoid + end ; code + end ; body + end ; method + trait method QName(PackageNamespace(""), "test") + method + refid "tests:TestConstructDynamically/instance/test" + returns QName(PackageNamespace(""), "void") + body + maxstack 2 + localcount 4 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + pushstring "Sprite" + findpropstrict MultinameL([PackageNamespace("flash.display")]) + pushstring "Sprite" + constructprop MultinameL([PackageNamespace("flash.display")]), 0 + setlocal1 + pushstring "Object" + findpropstrict MultinameL([PackageNamespace("")]) + pushstring "Object" + getproperty MultinameL([PackageNamespace("")]) + construct 0 + setlocal1 + returnvoid + end ; code + end ; body + end ; method + end ; trait + end ; instance + cinit + refid "tests:TestConstructDynamically/class/init" + body + maxstack 1 + localcount 1 + initscopedepth 3 + maxscopedepth 4 + code + getlocal0 + pushscope + + returnvoid + end ; code + end ; body + end ; method +end ; class diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestConstructDynamically.script.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestConstructDynamically.script.asasm new file mode 100644 index 000000000..1e165ee75 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestConstructDynamically.script.asasm @@ -0,0 +1,29 @@ +script + sinit + refid "tests:TestConstructDynamically/init" + body + maxstack 2 + localcount 1 + initscopedepth 1 + maxscopedepth 3 + code + getlocal0 + pushscope + + findpropstrict Multiname("TestConstructDynamically", [PackageNamespace("tests")]) + getlex QName(PackageNamespace(""), "Object") + pushscope + + getlex Multiname("Object", [PrivateNamespace(null, "tests:TestConstructDynamically"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")]) + newclass "tests:TestConstructDynamically" + popscope + initproperty QName(PackageNamespace("tests"), "TestConstructDynamically") + + returnvoid + end ; code + end ; body + end ; method + trait class QName(PackageNamespace("tests"), "TestConstructDynamically") + #include "TestConstructDynamically.class.asasm" + end ; trait +end ; script diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf index 7e05cf57c..1f3441cb9 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf and b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf differ