From c8951d9522c33b16711b2275b34c757ba5388ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 31 Jul 2016 15:15:36 +0200 Subject: [PATCH] Import/Usage parsing separated to one class Class for generating linker reports from SWF files (similar to -link-report option in Flex) Generating linker report from commandline --- .../flash/abc/types/traits/Trait.java | 172 +------------- .../flash/abc/types/traits/TraitClass.java | 11 +- .../flash/abc/types/traits/TraitFunction.java | 3 +- .../types/traits/TraitMethodGetterSetter.java | 3 +- .../abc/types/traits/TraitSlotConst.java | 3 +- .../exporters/script/ImportsUsagesParser.java | 211 ++++++++++++++++++ .../exporters/script/LinkReportExporter.java | 206 +++++++++++++++++ .../console/CommandLineArgumentParser.java | 56 ++++- 8 files changed, 486 insertions(+), 179 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java index c161303b6..35b66c090 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java @@ -42,6 +42,7 @@ import com.jpexs.decompiler.flash.abc.types.NamespaceSet; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; @@ -169,117 +170,6 @@ public abstract class Trait implements Cloneable, Serializable { return ret; } - protected void parseImportsUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames, true); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - for (int n : nss.namespaces) { - parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, nss.namespaces.length > 1 ? "" : name); - } - } - } - } - - private boolean parseUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - - if (ns.kind == Namespace.KIND_NAMESPACE) { - String nsVal = ns.getName(abc.constants).toRawString(); - for (ABCContainerTag abcTag : abc.getAbcTags()) { - DottedChain nsimport = abcTag.getABC().nsValueToName(nsVal); - if (nsimport.equals(AVM2Deobfuscation.BUILTIN)) { - return true; //handled, but import/use not added - } - if (!nsimport.isEmpty()) { - - if (!nsimport.getWithoutLast().equals(ignorePackage) && !imports.contains(nsimport)) { - imports.add(nsimport); - } - if (ignoredCustom != null && nsVal.equals(ignoredCustom)) { - return true; - } - if (!uses.contains(nsimport.getLast())) { - uses.add(nsimport.getLast()); - } - return true; - } - } - } - return false; - } - - protected void parseImportsUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - if (name.isEmpty()) { - name = "*"; - } - DottedChain newimport = ns.getName(abc.constants); - - if (parseUsagesFromNS(ignoredCustom, abc, imports, uses, namespace_index, ignorePackage, name)) { - return; - } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { - return; - } - newimport = newimport.add(name); - if (!imports.contains(newimport)) { - DottedChain pkg = newimport.getWithoutLast(); //.substring(0, newimport.lastIndexOf('.')); - if (pkg.equals(InitVectorAVM2Item.VECTOR_PACKAGE)) { //special case - is imported always - return; - } - if (!pkg.equals(ignorePackage)) { - imports.add(newimport); - } - } - //} - } - - protected void parseUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames, false); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - if (nss.namespaces.length == 1) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, nss.namespaces[0], ignorePackage, name); - } else { - for (int n : nss.namespaces) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, ""); - } - } - } - } - } - protected DottedChain getPackage(ABC abc) { return getName(abc).getNamespace(abc.constants).getName(abc.constants); } @@ -291,7 +181,7 @@ public abstract class Trait implements Cloneable, Serializable { ignoredCustom = n.getName(abc.constants).toRawString(); } } - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, getName(abc), ignorePackage, fullyQualifiedNames); + ImportsUsagesParser.parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, getName(abc), ignorePackage, fullyQualifiedNames); } private static final String[] builtInClasses = {"ArgumentError", "arguments", "Array", "Boolean", "Class", "Date", "DefinitionError", "Error", "EvalError", "Function", "int", "JSON", "Math", "Namespace", "Number", "Object", "QName", "RangeError", "ReferenceError", "RegExp", "SecurityError", "String", "SyntaxError", "TypeError", "uint", "URIError", "VerifyError", "XML", "XMLList"}; @@ -375,64 +265,6 @@ public abstract class Trait implements Cloneable, Serializable { } } - protected void parseImportsUsagesFromMethodInfo(String ignoredCustom, ABC abc, int method_index, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames, List visitedMethods) { - if ((method_index < 0) || (method_index >= abc.method_info.size())) { - return; - } - visitedMethods.add(method_index); - if (abc.method_info.get(method_index).ret_type != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames); - } - for (int t : abc.method_info.get(method_index).param_types) { - if (t != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); - } - } - MethodBody body = abc.findBody(method_index); - if (body != null) { - body.traits.getImportsUsages(ignoredCustom, abc, imports, uses, ignorePackage, fullyQualifiedNames); - for (ABCException ex : body.exceptions) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); - } - for (AVM2Instruction ins : body.getCode().code) { - if (ins.definition instanceof AlchemyTypeIns) { - DottedChain nimport = AlchemyTypeIns.ALCHEMY_PACKAGE.add(ins.definition.instructionName); - if (!imports.contains(nimport)) { - imports.add(nimport); - } - } - if (ins.definition instanceof NewFunctionIns) { - if (ins.operands[0] != method_index) { - if (!visitedMethods.contains(ins.operands[0])) { - parseImportsUsagesFromMethodInfo(ignoredCustom, abc, ins.operands[0], imports, uses, ignorePackage, fullyQualifiedNames, visitedMethods); - } - } - } - if ((ins.definition instanceof FindPropertyStrictIns) - || (ins.definition instanceof FindPropertyIns) - || (ins.definition instanceof GetLexIns) - || (ins.definition instanceof CoerceIns) - || (ins.definition instanceof AsTypeIns)) { - int m = ins.operands[0]; - if (m != 0) { - if (m < abc.constants.getMultinameCount()) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames); - } - } - } else { - for (int k = 0; k < ins.definition.operands.length; k++) { - if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) { - int multinameIndex = ins.operands[k]; - if (multinameIndex < abc.constants.getMultinameCount()) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); - } - } - } - } - } - } - } - public final GraphTextWriter getMetaData(Trait parent, ConvertData convertData, ABC abc, GraphTextWriter writer) { List>> md = getMetaDataTable(parent, convertData, abc); for (Entry> en : md) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index d21b8a12d..d92552773 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -25,6 +25,7 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -84,26 +85,26 @@ public class TraitClass extends Trait implements TraitWithSlot { InstanceInfo instanceInfo = abc.instance_info.get(class_info); DottedChain packageName = instanceInfo.getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames); + ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames); if (instanceInfo.super_index > 0) { - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames); + ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames); } for (int i : instanceInfo.interfaces) { - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); + ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); } //static classInfo.static_traits.getImportsUsages(customNs, abc, imports, uses, packageName, fullyQualifiedNames); //static initializer - parseImportsUsagesFromMethodInfo(customNs, abc, classInfo.cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); + ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, classInfo.cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); //instance instanceInfo.instance_traits.getImportsUsages(customNs, abc, imports, uses, packageName, fullyQualifiedNames); //instance initializer - parseImportsUsagesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); + ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java index 887971236..b18f5f1f1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -130,7 +131,7 @@ public class TraitFunction extends Trait implements TraitWithSlot { super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); //if (method_info != 0) { - parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); + ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java index cc836a0ae..7487d22f2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -72,7 +73,7 @@ public class TraitMethodGetterSetter extends Trait { } //if (method_info != 0) { - parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); + ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java index 6971590a8..d46ec1c8b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java @@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.ValueKind; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -201,7 +202,7 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { ignorePackage = getPackage(abc); } super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); - parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(type_index), getPackage(abc), fullyQualifiedNames); + ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(type_index), getPackage(abc), fullyQualifiedNames); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java new file mode 100644 index 000000000..79637ff6d --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.exporters.script; + +import com.jpexs.decompiler.flash.abc.ABC; +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.NewFunctionIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; +import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.DottedChain; +import java.util.List; + +public class ImportsUsagesParser { + + public static void parseImportsUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { + Namespace ns = abc.constants.getNamespace(namespace_index); + if (name.isEmpty()) { + name = "*"; + } + DottedChain newimport = ns.getName(abc.constants); + + if (parseUsagesFromNS(ignoredCustom, abc, imports, uses, namespace_index, ignorePackage, name)) { + return; + } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { + return; + } + newimport = newimport.add(name); + if (!imports.contains(newimport)) { + DottedChain pkg = newimport.getWithoutLast(); //.substring(0, newimport.lastIndexOf('.')); + if (pkg.equals(InitVectorAVM2Item.VECTOR_PACKAGE)) { //special case - is imported always + return; + } + if (!pkg.equals(ignorePackage)) { + imports.add(newimport); + } + } + //} + } + + public static void parseImportsUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { + if (m != null) { + if (m.kind == Multiname.TYPENAME) { + if (m.qname_index != 0) { + parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + } + for (Integer i : m.params) { + if (i != 0) { + parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + } + } + return; + } + Namespace ns = m.getNamespace(abc.constants); + String name = m.getName(abc.constants, fullyQualifiedNames, true); + NamespaceSet nss = m.getNamespaceSet(abc.constants); + if (ns != null) { + parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); + } + if (nss != null) { + for (int n : nss.namespaces) { + parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, nss.namespaces.length > 1 ? "" : name); + } + } + } + } + + public static void parseImportsUsagesFromMethodInfo(String ignoredCustom, ABC abc, int method_index, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames, List visitedMethods) { + if ((method_index < 0) || (method_index >= abc.method_info.size())) { + return; + } + visitedMethods.add(method_index); + if (abc.method_info.get(method_index).ret_type != 0) { + parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames); + } + for (int t : abc.method_info.get(method_index).param_types) { + if (t != 0) { + parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); + } + } + MethodBody body = abc.findBody(method_index); + if (body != null) { + body.traits.getImportsUsages(ignoredCustom, abc, imports, uses, ignorePackage, fullyQualifiedNames); + for (ABCException ex : body.exceptions) { + parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); + } + for (AVM2Instruction ins : body.getCode().code) { + if (ins.definition instanceof AlchemyTypeIns) { + DottedChain nimport = AlchemyTypeIns.ALCHEMY_PACKAGE.add(ins.definition.instructionName); + if (!imports.contains(nimport)) { + imports.add(nimport); + } + } + if (ins.definition instanceof NewFunctionIns) { + if (ins.operands[0] != method_index) { + if (!visitedMethods.contains(ins.operands[0])) { + parseImportsUsagesFromMethodInfo(ignoredCustom, abc, ins.operands[0], imports, uses, ignorePackage, fullyQualifiedNames, visitedMethods); + } + } + } + if ((ins.definition instanceof FindPropertyStrictIns) + || (ins.definition instanceof FindPropertyIns) + || (ins.definition instanceof GetLexIns) + || (ins.definition instanceof CoerceIns) + || (ins.definition instanceof AsTypeIns)) { + int m = ins.operands[0]; + if (m != 0) { + if (m < abc.constants.getMultinameCount()) { + parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames); + } + } + } else { + for (int k = 0; k < ins.definition.operands.length; k++) { + if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) { + int multinameIndex = ins.operands[k]; + if (multinameIndex < abc.constants.getMultinameCount()) { + parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); + } + } + } + } + } + } + } + + public static void parseUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { + if (m != null) { + if (m.kind == Multiname.TYPENAME) { + if (m.qname_index != 0) { + parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + } + for (Integer i : m.params) { + if (i != 0) { + parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + } + } + return; + } + Namespace ns = m.getNamespace(abc.constants); + String name = m.getName(abc.constants, fullyQualifiedNames, false); + NamespaceSet nss = m.getNamespaceSet(abc.constants); + if (ns != null) { + parseUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); + } + if (nss != null) { + if (nss.namespaces.length == 1) { + parseUsagesFromNS(ignoredCustom, abc, imports, uses, nss.namespaces[0], ignorePackage, name); + } else { + for (int n : nss.namespaces) { + parseUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, ""); + } + } + } + } + } + + private static boolean parseUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { + Namespace ns = abc.constants.getNamespace(namespace_index); + + if (ns.kind == Namespace.KIND_NAMESPACE) { + String nsVal = ns.getName(abc.constants).toRawString(); + for (ABCContainerTag abcTag : abc.getAbcTags()) { + DottedChain nsimport = abcTag.getABC().nsValueToName(nsVal); + if (nsimport.equals(AVM2Deobfuscation.BUILTIN)) { + return true; //handled, but import/use not added + } + if (!nsimport.isEmpty()) { + + if (!nsimport.getWithoutLast().equals(ignorePackage) && !imports.contains(nsimport)) { + imports.add(nsimport); + } + if (ignoredCustom != null && nsVal.equals(ignoredCustom)) { + return true; + } + if (!uses.contains(nsimport.getLast())) { + uses.add(nsimport.getLast()); + } + return true; + } + } + } + return false; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java new file mode 100644 index 000000000..b59db7d8a --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.exporters.script; + +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.graph.DottedChain; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Class LinkReportExporter - generates Linker reports similar to Flex + * -link-report, but for SWF files + * + * @author JPEXS + */ +public class LinkReportExporter { + + private String newLineChar = "\n"; + private String indentStr = " "; + + /** + * Constructs reporter with LF as newline, two spaces as indent + */ + public LinkReportExporter() { + + } + + /** + * Constructs reporter with custom newline char, two spaces as indent + * + * @param newLineChar + */ + public LinkReportExporter(String newLineChar) { + this.newLineChar = newLineChar; + } + + /** + * Constructs reporter with custom newline char and indent string + * + * @param newLineChar + * @param indentStr + */ + public LinkReportExporter(String newLineChar, String indentStr) { + this.newLineChar = newLineChar; + this.indentStr = indentStr; + + } + + private String indent(int cnt) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < cnt; i++) { + sb.append(indentStr); + } + return sb.toString(); + } + + public String generateReport(SWF swf, List as3scripts, EventListener evl) { + StringBuilder sb = new StringBuilder(); + Set extDeps = new HashSet<>(); + sb.append("").append(newLineChar); + sb.append(indent(1)).append("").append(newLineChar); + List revList = new ArrayList<>(as3scripts); + Collections.reverse(revList); + + List existingObjects = new ArrayList<>(); + for (ScriptPack sp : as3scripts) { + existingObjects.add(sp.getClassPath().packageStr.add(sp.getClassPath().className)); + } + + for (ScriptPack sp : revList) { + String scriptName = "script" + sp.scriptIndex; + sb.append(indent(2)).append("").append(newLineChar); + } + sb.append(indent(1)).append("").append(newLineChar); + sb.append("").append(newLineChar); + return sb.toString(); + } + + private String multiNameToId(ABC abc, Multiname multiName) { + Namespace ns = multiName.getNamespace(abc.constants); + NamespaceSet nss = multiName.getNamespaceSet(abc.constants); + if (nss != null && nss.namespaces.length == 1) { + ns = abc.constants.getNamespace(nss.namespaces[0]); + } + String pkgName = ns == null ? "" : ns.getName(abc.constants).toRawString(); + String clsName = multiName.getName(abc.constants, new ArrayList<>(), true); + return pkgName.isEmpty() ? clsName : pkgName + ":" + clsName; + } + + private String dottedChainToId(DottedChain dc) { + if (dc.getWithoutLast().isEmpty()) { + return dc.getLast(); + } + return dc.getWithoutLast().toRawString() + ":" + dc.getLast(); + } + + private String reportTrait(Set externalDefs, List existingObjects, SWF swf, ABC abc, Trait t) { + //TODO: handle externalDefs - + StringBuilder sb = new StringBuilder(); + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + sb.append(indent(3)).append("").append(newLineChar); + ClassInfo ci = abc.class_info.get(tc.class_info); + InstanceInfo ii = abc.instance_info.get(tc.class_info); + + Set allDeps = new HashSet<>(); + + String superPre; + if (ii.super_index != 0) { + superPre = multiNameToId(abc, abc.constants.getMultiname(ii.super_index)); + } else { + superPre = "Object"; + } + allDeps.add(superPre); + sb.append(indent(3)).append("
").append(newLineChar);
+
+            for (int iface : ii.interfaces) {
+                String ifacePre = multiNameToId(abc, abc.constants.getMultiname(iface));
+                allDeps.add(ifacePre);
+                sb.append(indent(3)).append("
").append(newLineChar);
+            }
+
+            for (Trait ct : ci.static_traits.traits) {
+                reportTrait(externalDefs, existingObjects, swf, abc, ct);
+            }
+            for (Trait it : ii.instance_traits.traits) {
+                reportTrait(externalDefs, existingObjects, swf, abc, it);
+            }
+            List imports = new ArrayList<>();
+            List uses = new ArrayList<>();
+
+            sb.append(indent(3)).append("").append(newLineChar); //Automatic
+
+            getImportsUsagesFromClass(tc.class_info, null, abc, imports, uses, new DottedChain("FAKE!PACKAGE") /*do not skip any package*/, new ArrayList<>());
+            for (DottedChain dc : imports) {
+                if (!"*".equals(dc.getLast())) {
+                    //some toplevel "imports" can be only method calls
+                    if (dc.getWithoutLast().isEmpty() && !existingObjects.contains(dc)) {
+                        continue;
+                    }
+                    String dep = dottedChainToId(dc);
+                    if (!allDeps.contains(dep)) {
+                        sb.append(indent(3)).append("").append(newLineChar);
+                        allDeps.add(dep);
+                    }
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    private void getImportsUsagesFromClass(int class_info, String customNs, ABC abc, List imports, List uses, DottedChain ignoredPackage, List fullyQualifiedNames) {
+        ClassInfo classInfo = abc.class_info.get(class_info);
+        InstanceInfo instanceInfo = abc.instance_info.get(class_info);
+
+        //static initializer
+        ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, classInfo.cinit_index, imports, uses, ignoredPackage, fullyQualifiedNames, new ArrayList<>());
+
+        //instance initializer
+        ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, imports, uses, ignoredPackage, fullyQualifiedNames, new ArrayList<>());
+
+        //static
+        classInfo.static_traits.getImportsUsages(customNs, abc, imports, uses, ignoredPackage, fullyQualifiedNames);
+
+        //instance
+        instanceInfo.instance_traits.getImportsUsages(customNs, abc, imports, uses, ignoredPackage, fullyQualifiedNames);
+
+    }
+
+}
diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java
index 906c93de2..b1abaff39 100644
--- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java
+++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java
@@ -75,6 +75,7 @@ import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
 import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
 import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode;
 import com.jpexs.decompiler.flash.exporters.modes.TextExportMode;
+import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter;
 import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings;
 import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings;
 import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings;
@@ -577,6 +578,13 @@ public class CommandLineArgumentParser {
             out.println("  ...: SWF file to search instance in");
         }
 
+        if (filter == null || filter.equals("linkreport")) {
+            out.println(" " + (cnt++) + ") -linkReport [-outfile ] ");
+            out.println("  ...generates linker report for the swffile");
+            out.println("  ...-outfile  (optional): Saves XML report to . When ommited, the report is printed to stdout.");
+            out.println("  ...: SWF file to search instance in");
+        }
+
         printCmdLineUsageExamples(out, filter);
     }
 
@@ -792,7 +800,9 @@ public class CommandLineArgumentParser {
             command = nextParam.substring(1);
         }
 
-        if (command.equals("getinstancemetadata")) {
+        if (command.equals("linkreport")) {
+            parseLinkReport(selectionClasses, args);
+        } else if (command.equals("getinstancemetadata")) {
             parseGetInstanceMetadata(args);
         } else if (command.equals("setinstancemetadata")) {
             parseSetInstanceMetadata(args);
@@ -987,6 +997,50 @@ public class CommandLineArgumentParser {
         setConfigurations(args.pop());
     }
 
+    private static void parseLinkReport(List selectionClasses, Stack args) {
+        if (args.isEmpty()) {
+            badArguments("linkreport");
+        }
+        File stdOutFile = null;
+        File swfFile = null;
+        while (!args.isEmpty()) {
+            String paramName = args.pop().toLowerCase();
+            switch (paramName) {
+                case "-outfile":
+                    if (args.empty()) {
+                        System.err.println("Missing output file");
+                        badArguments("linkreport");
+                    }
+                    stdOutFile = new File(args.pop());
+                    break;
+                default:
+                    if (!args.isEmpty()) {
+                        badArguments("linkreport");
+                    }
+                    swfFile = new File(paramName);
+            }
+        }
+        if (swfFile == null) {
+            System.err.println("No SWF file specified");
+            badArguments("getinstancemetadata");
+        }
+        processReadSWF(swfFile, stdOutFile, (SWF swf, OutputStream stdout) -> {
+            LinkReportExporter lre = new LinkReportExporter();
+
+            List reportPacks;
+            try {
+                reportPacks = selectionClasses != null ? swf.getScriptPacksByClassNames(selectionClasses) : swf.getAS3Packs();
+            } catch (Exception ex) {
+                System.err.println("Error while getting packs");
+                System.exit(1);
+                return;
+            }
+
+            String reportStr = lre.generateReport(swf, reportPacks, null);
+            stdout.write(reportStr.getBytes("UTF-8"));
+        });
+    }
+
     private static void parseGetInstanceMetadata(Stack args) {
         if (args.size() < 3) {
             badArguments("getinstancemetadata");