From aeba926b5417321cc6c31d55d056789be02397d0 Mon Sep 17 00:00:00 2001 From: honfika Date: Sat, 19 Jul 2014 20:30:39 +0200 Subject: [PATCH] Issue #594: method start curly brace position fixed --- .../flash/abc/types/traits/TraitClass.java | 1127 +++++++------ .../flash/configuration/Configuration.java | 1396 +++++++++-------- .../flash/helpers/GraphTextWriter.java | 4 +- .../flash/helpers/HilightedTextWriter.java | 18 +- 4 files changed, 1276 insertions(+), 1269 deletions(-) diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index e202fb06c..ea69ef907 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -1,565 +1,562 @@ -/* - * Copyright (C) 2010-2014 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -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.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.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -public class TraitClass extends Trait implements TraitWithSlot { - - public int slot_id; - public int class_info; - 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"}; - - private boolean classInitializerIsEmpty; - - @Override - public void delete(ABC abc, boolean d) { - abc.class_info.get(class_info).deleted = d; - abc.instance_info.get(class_info).deleted = d; - - abc.class_info.get(class_info).static_traits.delete(abc, d); - abc.method_info.get(abc.class_info.get(class_info).cinit_index).delete(abc, d); - - abc.instance_info.get(class_info).instance_traits.delete(abc, d); - abc.method_info.get(abc.instance_info.get(class_info).iinit_index).delete(abc, d); - - abc.constants.constant_namespace.get(abc.instance_info.get(class_info).protectedNS).deleted = d; - - abc.constants.constant_multiname.get(name_index).deleted = d; - } - - @Override - public int getSlotIndex() { - return slot_id; - } - - private static boolean isBuiltInClass(String name) { - for (String g : builtInClasses) { - if (g.equals(name)) { - return true; - } - } - return false; - } - - @Override - public String toString(ABC abc, List fullyQualifiedNames) { - return "Class " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " class_info=" + class_info + " metadata=" + Helper.intArrToString(metadata); - } - - private boolean parseUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - if (name.isEmpty()) { - name = "*"; - } - String newimport = ns.getName(abc.constants); - /*if ((ns.kind != Namespace.KIND_PACKAGE) - && (ns.kind != Namespace.KIND_NAMESPACE) - && (ns.kind != Namespace.KIND_STATIC_PROTECTED)) { - return false; - }*/ - /*if (ns.kind == Namespace.KIND_NAMESPACE)*/ { - String oldimport = newimport; - newimport = null; - for (ABCContainerTag abcTag : abcTags) { - String newname = abcTag.getABC().nsValueToName(oldimport); - if (newname.equals("-")) { - return true; - } - if (!newname.isEmpty()) { - newimport = newname; - break; - } - } - if (newimport == null) { - newimport = oldimport; - newimport += "." + name; - } - if (newimport != null && newimport.isEmpty()) { - newimport = null; - } - if (newimport != null) { - /* if(ns.kind==Namespace.KIND_PACKAGE){ - newimport+=".*"; - }*/ - - if (!imports.contains(newimport)) { - if (newimport.contains(":")) { - return true; - } - String pkg = ""; - if (newimport.contains(".")) { - pkg = newimport.substring(0, newimport.lastIndexOf('.')); - } - String usname = newimport; - if (usname.contains(".")) { - usname = usname.substring(usname.lastIndexOf('.') + 1); - } - if (ns.kind == Namespace.KIND_PACKAGE) { - if (!pkg.equals(ignorePackage)) { - if (!pkg.equals("__AS3__.vec")) { //Automatic import - imports.add(newimport); - } - } - } - if (ns.kind == Namespace.KIND_NAMESPACE) { - if (!usname.equals("*")) { - /*if (!uses.contains(usname)) { - uses.add(usname); - }*/ - if (!pkg.equals(ignorePackage)) { - imports.add(newimport); - } - } - } - } - return true; - } - } - return false; - } - - private void parseImportsUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - if (name.isEmpty()) { - name = "*"; - } - String newimport = ns.getName(abc.constants); - if (parseUsagesFromNS(abcTags, abc, imports, uses, namespace_index, ignorePackage, name)) { - return; - } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { - return; - } - if (newimport == null) { - newimport = ""; - } - //if (!newimport.equals("")) { - newimport += "." + name; - if (newimport.contains(":")) { - return; - } - if (!imports.contains(newimport)) { - String pkg = newimport.substring(0, newimport.lastIndexOf('.')); - if (pkg.equals("__AS3__.vec")) { //special case - is imported always - return; - } - if (!pkg.equals(ignorePackage)) { - imports.add(newimport); - } - } - //} - } - - private void parseUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - if (nss.namespaces.length == 1) { - parseUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); - } else { - for (int n : nss.namespaces) { - parseUsagesFromNS(abcTags, abc, imports, uses, n, ignorePackage, ""); - } - } - } - } - } - - private void parseImportsUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseImportsUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - if (nss.namespaces.length == 1) { - parseImportsUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); - } - } - } - } - - private void parseImportsUsagesFromMethodInfo(List abcTags, ABC abc, int method_index, List imports, List uses, String 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(abcTags, 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(abcTags, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); - } - } - MethodBody body = abc.findBody(method_index); - if (body != null) { - parseImportsUsagesFromTraits(abcTags, abc, body.traits, imports, uses, ignorePackage, fullyQualifiedNames); - for (ABCException ex : body.exceptions) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); - } - for (AVM2Instruction ins : body.code.code) { - if (ins.definition instanceof NewFunctionIns) { - if (ins.operands[0] != method_index) { - if (!visitedMethods.contains(ins.operands[0])) { - parseImportsUsagesFromMethodInfo(abcTags, 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) { - parseImportsUsagesFromMultiname(abcTags, 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]; - parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); - } - } - } - } - } - } - - private void parseImportsUsagesFromTraits(List abcTags, ABC abc, Traits ts, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { - for (Trait t : ts.traits) { - parseImportsUsagesFromTrait(abcTags, abc, t, imports, uses, ignorePackage, fullyQualifiedNames); - } - } - - private void parseImportsUsagesFromTrait(List abcTags, ABC abc, Trait t, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(tm.name_index), ignorePackage, fullyQualifiedNames); - if (tm.method_info != 0) { - parseImportsUsagesFromMethodInfo(abcTags, abc, tm.method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList()); - } - } - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, t.getName(abc), ignorePackage, fullyQualifiedNames); - if (t instanceof TraitSlotConst) { - TraitSlotConst ts = (TraitSlotConst) t; - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.name_index), ignorePackage, fullyQualifiedNames); - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.type_index), ignorePackage, fullyQualifiedNames); - } - } - - private List getImportsUsages(List abcTags, ABC abc, List imports, List uses, List fullyQualifiedNames) { - //constructor - - String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).name_index), packageName, fullyQualifiedNames); - - if (abc.instance_info.get(class_info).super_index > 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).super_index), packageName, fullyQualifiedNames); - } - for (int i : abc.instance_info.get(class_info).interfaces) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); - } - - //static - parseImportsUsagesFromTraits(abcTags, abc, abc.class_info.get(class_info).static_traits, imports, uses, packageName, fullyQualifiedNames); - - //static initializer - parseImportsUsagesFromMethodInfo(abcTags, abc, abc.class_info.get(class_info).cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); - - //instance - parseImportsUsagesFromTraits(abcTags, abc, abc.instance_info.get(class_info).instance_traits, imports, uses, packageName, fullyQualifiedNames); - - //instance initializer - parseImportsUsagesFromMethodInfo(abcTags, abc, abc.instance_info.get(class_info).iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); - return imports; - } - - @Override - public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { - String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); - return writer.appendNoHilight(classHeader); - } - - @Override - public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { - } - - @Override - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - - writer.startClass(class_info); - String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - List namesInThisPackage = new ArrayList<>(); - for (ABCContainerTag tag : abcTags) { - for (ScriptInfo si : tag.getABC().script_info) { - for (Trait t : si.traits.traits) { - String spath = t.getPath(tag.getABC()); - String pkg = ""; - String name = spath; - if (spath.contains(".")) { - pkg = spath.substring(0, spath.lastIndexOf('.')); - name = spath.substring(spath.lastIndexOf('.') + 1); - } - if (pkg.equals(packageName)) { - namesInThisPackage.add(name); - } - } - - } - } - //imports - List imports = new ArrayList<>(); - List uses = new ArrayList<>(); - getImportsUsages(abcTags, abc, imports, uses, new ArrayList()); - - fullyQualifiedNames = new ArrayList<>(); - - List importnames = new ArrayList<>(); - importnames.addAll(namesInThisPackage); - for (String ipath : imports) { - String name = ipath; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - if (importnames.contains(name) || ((!pkg.isEmpty()) && isBuiltInClass(name))) { - fullyQualifiedNames.add(name); - } else { - importnames.add(name); - } - } - /*List imports2 = new ArrayList(); - for (String path : imports) { - String name = path; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf(".")); - name = name.substring(name.lastIndexOf(".") + 1); - } - - if ((!packageName.equals(pkg)) && (!fullyQualifiedNames.contains(name))) { - imports2.add(path); - } - } - imports = imports2;*/ - - for (int i = 0; i < imports.size(); i++) { - String imp = imports.get(i); - String pkg = imp.substring(0, imp.lastIndexOf('.')); - String name = imp.substring(imp.lastIndexOf('.') + 1); - if (name.equals("*")) { - continue; - } - if (imports.contains(pkg + ".*")) { - imports.remove(i); - i--; - } - } - - boolean hasImport = false; - for (String imp : imports) { - if (!imp.startsWith(".")) { - writer.appendNoHilight("import " + imp + ";").newLine(); - hasImport = true; - } - } - if (hasImport) { - writer.newLine(); - } - for (String us : uses) { - writer.appendNoHilight("use namespace " + us + ";").newLine(); - } - if (uses.size() > 0) { - writer.newLine(); - } - - //class header - String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); - if (classHeader.startsWith("private ")) { - classHeader = classHeader.substring("private ".length()); - } - writer.appendNoHilight(classHeader).newLine(); - writer.appendNoHilight("{").newLine(); - writer.indent(); - - int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); - if (bodyIndex != -1) { - if (!classInitializerIsEmpty) { - writer.newLine(); - writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size() + 1); - writer.appendNoHilight("{").newLine(); - abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits); - writer.appendNoHilight("}").newLine(); - writer.endTrait(); - } - } else { - //"/*classInitializer*/"; - } - - //constructor - if (!abc.instance_info.get(class_info).isInterface()) { - String modifier = ""; - Multiname m = abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); - if (m != null) { - Namespace ns = m.getNamespace(abc.constants); - if (ns != null) { - modifier = ns.getPrefix(abc) + " "; - if (modifier.equals(" ")) { - modifier = ""; - } - if (modifier.startsWith("private")) { //cannot have private constuctor - modifier = ""; - } - } - } - - writer.newLine(); - writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size()); - writer.appendNoHilight(modifier); - writer.appendNoHilight("function "); - writer.appendNoHilight(abc.constants.getMultiname(abc.instance_info.get(class_info).name_index).getName(abc.constants, new ArrayList()/*do not want full names here*/)); - writer.appendNoHilight("("); - bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); - if (bodyIndex != -1) { - abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, abc.bodies.get(bodyIndex), abc, fullyQualifiedNames); - } else { - abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, null, abc, fullyQualifiedNames); - } - writer.appendNoHilight(") {").newLine(); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits); - } - writer.appendNoHilight("}").newLine(); - writer.endTrait(); - } - - //static variables,constants & methods - abc.class_info.get(class_info).static_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - - abc.instance_info.get(class_info).instance_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - - writer.unindent(); - writer.appendNoHilight("}"); // class - writer.endClass(); - writer.newLine(); - return writer; - } - - @Override - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - - fullyQualifiedNames = new ArrayList<>(); - - int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); - if (bodyIndex != -1) { - writer.mark(); - abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits, true); - classInitializerIsEmpty = !writer.getMark(); - } - - //constructor - if (!abc.instance_info.get(class_info).isInterface()) { - bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits, true); - } - } - - //static variables,constants & methods - abc.class_info.get(class_info).static_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - - abc.instance_info.get(class_info).instance_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - } - - @Override - public Multiname getName(ABC abc) { - return abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); - } - - @Override - public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { - int iInitializer = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); - int ret = 0; - if (iInitializer != -1) { - ret += abc.bodies.get(iInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, false, path); - } - int sInitializer = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); - if (sInitializer != -1) { - ret += abc.bodies.get(sInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, true, path); - } - ret += abc.instance_info.get(class_info).instance_traits.removeTraps(scriptIndex, class_info, false, abc, path); - ret += abc.class_info.get(class_info).static_traits.removeTraps(scriptIndex, class_info, true, abc, path); - return ret; - } -} +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +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.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.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class TraitClass extends Trait implements TraitWithSlot { + + public int slot_id; + public int class_info; + 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"}; + + private boolean classInitializerIsEmpty; + + @Override + public void delete(ABC abc, boolean d) { + abc.class_info.get(class_info).deleted = d; + abc.instance_info.get(class_info).deleted = d; + + abc.class_info.get(class_info).static_traits.delete(abc, d); + abc.method_info.get(abc.class_info.get(class_info).cinit_index).delete(abc, d); + + abc.instance_info.get(class_info).instance_traits.delete(abc, d); + abc.method_info.get(abc.instance_info.get(class_info).iinit_index).delete(abc, d); + + abc.constants.constant_namespace.get(abc.instance_info.get(class_info).protectedNS).deleted = d; + + abc.constants.constant_multiname.get(name_index).deleted = d; + } + + @Override + public int getSlotIndex() { + return slot_id; + } + + private static boolean isBuiltInClass(String name) { + for (String g : builtInClasses) { + if (g.equals(name)) { + return true; + } + } + return false; + } + + @Override + public String toString(ABC abc, List fullyQualifiedNames) { + return "Class " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " class_info=" + class_info + " metadata=" + Helper.intArrToString(metadata); + } + + private boolean parseUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { + Namespace ns = abc.constants.getNamespace(namespace_index); + if (name.isEmpty()) { + name = "*"; + } + String newimport = ns.getName(abc.constants); + /*if ((ns.kind != Namespace.KIND_PACKAGE) + && (ns.kind != Namespace.KIND_NAMESPACE) + && (ns.kind != Namespace.KIND_STATIC_PROTECTED)) { + return false; + }*/ + /*if (ns.kind == Namespace.KIND_NAMESPACE)*/ { + String oldimport = newimport; + newimport = null; + for (ABCContainerTag abcTag : abcTags) { + String newname = abcTag.getABC().nsValueToName(oldimport); + if (newname.equals("-")) { + return true; + } + if (!newname.isEmpty()) { + newimport = newname; + break; + } + } + if (newimport == null) { + newimport = oldimport; + newimport += "." + name; + } + if (newimport != null && newimport.isEmpty()) { + newimport = null; + } + if (newimport != null) { + /* if(ns.kind==Namespace.KIND_PACKAGE){ + newimport+=".*"; + }*/ + + if (!imports.contains(newimport)) { + if (newimport.contains(":")) { + return true; + } + String pkg = ""; + if (newimport.contains(".")) { + pkg = newimport.substring(0, newimport.lastIndexOf('.')); + } + String usname = newimport; + if (usname.contains(".")) { + usname = usname.substring(usname.lastIndexOf('.') + 1); + } + if (ns.kind == Namespace.KIND_PACKAGE) { + if (!pkg.equals(ignorePackage)) { + if (!pkg.equals("__AS3__.vec")) { //Automatic import + imports.add(newimport); + } + } + } + if (ns.kind == Namespace.KIND_NAMESPACE) { + if (!usname.equals("*")) { + /*if (!uses.contains(usname)) { + uses.add(usname); + }*/ + if (!pkg.equals(ignorePackage)) { + imports.add(newimport); + } + } + } + } + return true; + } + } + return false; + } + + private void parseImportsUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { + Namespace ns = abc.constants.getNamespace(namespace_index); + if (name.isEmpty()) { + name = "*"; + } + String newimport = ns.getName(abc.constants); + if (parseUsagesFromNS(abcTags, abc, imports, uses, namespace_index, ignorePackage, name)) { + return; + } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { + return; + } + if (newimport == null) { + newimport = ""; + } + //if (!newimport.equals("")) { + newimport += "." + name; + if (newimport.contains(":")) { + return; + } + if (!imports.contains(newimport)) { + String pkg = newimport.substring(0, newimport.lastIndexOf('.')); + if (pkg.equals("__AS3__.vec")) { //special case - is imported always + return; + } + if (!pkg.equals(ignorePackage)) { + imports.add(newimport); + } + } + //} + } + + private void parseUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { + if (m != null) { + if (m.kind == Multiname.TYPENAME) { + if (m.qname_index != 0) { + parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + } + for (Integer i : m.params) { + if (i != 0) { + parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + } + } + return; + } + Namespace ns = m.getNamespace(abc.constants); + String name = m.getName(abc.constants, fullyQualifiedNames); + NamespaceSet nss = m.getNamespaceSet(abc.constants); + if (ns != null) { + parseUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); + } + if (nss != null) { + if (nss.namespaces.length == 1) { + parseUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); + } else { + for (int n : nss.namespaces) { + parseUsagesFromNS(abcTags, abc, imports, uses, n, ignorePackage, ""); + } + } + } + } + } + + private void parseImportsUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { + if (m != null) { + if (m.kind == Multiname.TYPENAME) { + if (m.qname_index != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + } + for (Integer i : m.params) { + if (i != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + } + } + return; + } + Namespace ns = m.getNamespace(abc.constants); + String name = m.getName(abc.constants, fullyQualifiedNames); + NamespaceSet nss = m.getNamespaceSet(abc.constants); + if (ns != null) { + parseImportsUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); + } + if (nss != null) { + if (nss.namespaces.length == 1) { + parseImportsUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); + } + } + } + } + + private void parseImportsUsagesFromMethodInfo(List abcTags, ABC abc, int method_index, List imports, List uses, String 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(abcTags, 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(abcTags, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); + } + } + MethodBody body = abc.findBody(method_index); + if (body != null) { + parseImportsUsagesFromTraits(abcTags, abc, body.traits, imports, uses, ignorePackage, fullyQualifiedNames); + for (ABCException ex : body.exceptions) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); + } + for (AVM2Instruction ins : body.code.code) { + if (ins.definition instanceof NewFunctionIns) { + if (ins.operands[0] != method_index) { + if (!visitedMethods.contains(ins.operands[0])) { + parseImportsUsagesFromMethodInfo(abcTags, 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) { + parseImportsUsagesFromMultiname(abcTags, 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]; + parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); + } + } + } + } + } + } + + private void parseImportsUsagesFromTraits(List abcTags, ABC abc, Traits ts, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { + for (Trait t : ts.traits) { + parseImportsUsagesFromTrait(abcTags, abc, t, imports, uses, ignorePackage, fullyQualifiedNames); + } + } + + private void parseImportsUsagesFromTrait(List abcTags, ABC abc, Trait t, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(tm.name_index), ignorePackage, fullyQualifiedNames); + if (tm.method_info != 0) { + parseImportsUsagesFromMethodInfo(abcTags, abc, tm.method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList()); + } + } + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, t.getName(abc), ignorePackage, fullyQualifiedNames); + if (t instanceof TraitSlotConst) { + TraitSlotConst ts = (TraitSlotConst) t; + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.name_index), ignorePackage, fullyQualifiedNames); + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.type_index), ignorePackage, fullyQualifiedNames); + } + } + + private List getImportsUsages(List abcTags, ABC abc, List imports, List uses, List fullyQualifiedNames) { + //constructor + + String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name + + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).name_index), packageName, fullyQualifiedNames); + + if (abc.instance_info.get(class_info).super_index > 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).super_index), packageName, fullyQualifiedNames); + } + for (int i : abc.instance_info.get(class_info).interfaces) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); + } + + //static + parseImportsUsagesFromTraits(abcTags, abc, abc.class_info.get(class_info).static_traits, imports, uses, packageName, fullyQualifiedNames); + + //static initializer + parseImportsUsagesFromMethodInfo(abcTags, abc, abc.class_info.get(class_info).cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); + + //instance + parseImportsUsagesFromTraits(abcTags, abc, abc.instance_info.get(class_info).instance_traits, imports, uses, packageName, fullyQualifiedNames); + + //instance initializer + parseImportsUsagesFromMethodInfo(abcTags, abc, abc.instance_info.get(class_info).iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); + return imports; + } + + @Override + public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { + String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); + return writer.appendNoHilight(classHeader); + } + + @Override + public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { + } + + @Override + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + + writer.startClass(class_info); + String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name + List namesInThisPackage = new ArrayList<>(); + for (ABCContainerTag tag : abcTags) { + for (ScriptInfo si : tag.getABC().script_info) { + for (Trait t : si.traits.traits) { + String spath = t.getPath(tag.getABC()); + String pkg = ""; + String name = spath; + if (spath.contains(".")) { + pkg = spath.substring(0, spath.lastIndexOf('.')); + name = spath.substring(spath.lastIndexOf('.') + 1); + } + if (pkg.equals(packageName)) { + namesInThisPackage.add(name); + } + } + + } + } + //imports + List imports = new ArrayList<>(); + List uses = new ArrayList<>(); + getImportsUsages(abcTags, abc, imports, uses, new ArrayList()); + + fullyQualifiedNames = new ArrayList<>(); + + List importnames = new ArrayList<>(); + importnames.addAll(namesInThisPackage); + for (String ipath : imports) { + String name = ipath; + String pkg = ""; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + if (importnames.contains(name) || ((!pkg.isEmpty()) && isBuiltInClass(name))) { + fullyQualifiedNames.add(name); + } else { + importnames.add(name); + } + } + /*List imports2 = new ArrayList(); + for (String path : imports) { + String name = path; + String pkg = ""; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf(".")); + name = name.substring(name.lastIndexOf(".") + 1); + } + + if ((!packageName.equals(pkg)) && (!fullyQualifiedNames.contains(name))) { + imports2.add(path); + } + } + imports = imports2;*/ + + for (int i = 0; i < imports.size(); i++) { + String imp = imports.get(i); + String pkg = imp.substring(0, imp.lastIndexOf('.')); + String name = imp.substring(imp.lastIndexOf('.') + 1); + if (name.equals("*")) { + continue; + } + if (imports.contains(pkg + ".*")) { + imports.remove(i); + i--; + } + } + + boolean hasImport = false; + for (String imp : imports) { + if (!imp.startsWith(".")) { + writer.appendNoHilight("import " + imp + ";").newLine(); + hasImport = true; + } + } + if (hasImport) { + writer.newLine(); + } + for (String us : uses) { + writer.appendNoHilight("use namespace " + us + ";").newLine(); + } + if (uses.size() > 0) { + writer.newLine(); + } + + //class header + String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); + if (classHeader.startsWith("private ")) { + classHeader = classHeader.substring("private ".length()); + } + writer.appendNoHilight(classHeader).startBlock(); + + int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); + if (bodyIndex != -1) { + if (!classInitializerIsEmpty) { + writer.newLine(); + writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size() + 1); + writer.appendNoHilight("{").newLine(); + abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits); + writer.appendNoHilight("}").newLine(); + writer.endTrait(); + } + } else { + //"/*classInitializer*/"; + } + + //constructor + if (!abc.instance_info.get(class_info).isInterface()) { + String modifier = ""; + Multiname m = abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); + if (m != null) { + Namespace ns = m.getNamespace(abc.constants); + if (ns != null) { + modifier = ns.getPrefix(abc) + " "; + if (modifier.equals(" ")) { + modifier = ""; + } + if (modifier.startsWith("private")) { //cannot have private constuctor + modifier = ""; + } + } + } + + writer.newLine(); + writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size()); + writer.appendNoHilight(modifier); + writer.appendNoHilight("function "); + writer.appendNoHilight(abc.constants.getMultiname(abc.instance_info.get(class_info).name_index).getName(abc.constants, new ArrayList()/*do not want full names here*/)); + writer.appendNoHilight("("); + bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); + if (bodyIndex != -1) { + abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, abc.bodies.get(bodyIndex), abc, fullyQualifiedNames); + } else { + abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, null, abc, fullyQualifiedNames); + } + writer.appendNoHilight(")").startBlock(); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits); + } + writer.endBlock().newLine(); + writer.endTrait(); + } + + //static variables,constants & methods + abc.class_info.get(class_info).static_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + + abc.instance_info.get(class_info).instance_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + + writer.endBlock(); // class + writer.endClass(); + writer.newLine(); + return writer; + } + + @Override + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + + fullyQualifiedNames = new ArrayList<>(); + + int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); + if (bodyIndex != -1) { + writer.mark(); + abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits, true); + classInitializerIsEmpty = !writer.getMark(); + } + + //constructor + if (!abc.instance_info.get(class_info).isInterface()) { + bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits, true); + } + } + + //static variables,constants & methods + abc.class_info.get(class_info).static_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + + abc.instance_info.get(class_info).instance_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + } + + @Override + public Multiname getName(ABC abc) { + return abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); + } + + @Override + public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { + int iInitializer = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); + int ret = 0; + if (iInitializer != -1) { + ret += abc.bodies.get(iInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, false, path); + } + int sInitializer = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); + if (sInitializer != -1) { + ret += abc.bodies.get(sInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, true, path); + } + ret += abc.instance_info.get(class_info).instance_traits.removeTraps(scriptIndex, class_info, false, abc, path); + ret += abc.class_info.get(class_info).static_traits.removeTraps(scriptIndex, class_info, true, abc, path); + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/src/com/jpexs/decompiler/flash/configuration/Configuration.java index ce5397bf4..ce6e9a49c 100644 --- a/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -1,694 +1,702 @@ -/* - * Copyright (C) 2010-2014 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.configuration; - -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.helpers.CodeFormatting; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.utf8.Utf8InputStreamReader; -import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; -import com.jpexs.proxy.Replacement; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.PrintWriter; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JOptionPane; - -public class Configuration { - - private static final String CONFIG_NAME = "config.bin"; - private static final String REPLACEMENTS_NAME = "replacements.cfg"; - private static final File unspecifiedFile = new File("unspecified"); - private static File directory = unspecifiedFile; - - /** - * List of replacements - */ - private static List replacements = new ArrayList<>(); - - public static final Level logLevel; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("ui") - public static final ConfigurationItem openMultipleFiles = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("script") - public static final ConfigurationItem decompile = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("decompilation") - public static final ConfigurationItem parallelSpeedUp = null; - - @ConfigurationDefaultInt(20) - @ConfigurationCategory("decompilation") - public static final ConfigurationItem parallelThreadCount = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem autoDeobfuscate = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("decompilation") - public static final ConfigurationItem cacheOnDisk = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("display") - public static final ConfigurationItem internalFlashViewer = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("display") - public static final ConfigurationItem dumpView = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("display") - public static final ConfigurationItem dumpInfoCollecting = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("ui") - public static final ConfigurationItem gotoMainClassOnStartup = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem autoRenameIdentifiers = null; - - @ConfigurationDefaultBoolean(false) - public static final ConfigurationItem offeredAssociation = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem decimalAddress = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem showAllAddresses = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("display") - public static final ConfigurationItem useFrameCache = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("ui") - public static final ConfigurationItem useRibbonInterface = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("export") - public static final ConfigurationItem openFolderAfterFlaExport = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem useDetailedLogging = null; - - /** - * Debug mode = throwing an error when comparing original file and - * recompiled - */ - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem debugMode = null; - /** - * Turn off resolving constants in ActionScript 2 - */ - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("script") - public static final ConfigurationItem resolveConstants = null; - /** - * Limit of code subs (for obfuscated code) - */ - @ConfigurationDefaultInt(500) - @ConfigurationCategory("limit") - public static final ConfigurationItem sublimiter = null; - /** - * Total export timeout in seconds - */ - @ConfigurationDefaultInt(30 * 60) - @ConfigurationCategory("limit") - public static final ConfigurationItem exportTimeout = null; - /** - * Decompilation timeout in seconds for a single file - */ - @ConfigurationDefaultInt(5 * 60) - @ConfigurationCategory("limit") - public static final ConfigurationItem decompilationTimeoutFile = null; - /** - * Using parameter names in decompiling may cause problems because official - * programs like Flash CS 5.5 inserts wrong parameter names indices - */ - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem paramNamesEnable = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("ui") - public static final ConfigurationItem displayFileName = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem debugCopy = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem dumpTags = null; - - @ConfigurationDefaultInt(60) - @ConfigurationCategory("limit") - public static final ConfigurationItem decompilationTimeoutSingleMethod = null; - @ConfigurationDefaultInt(1) - public static final ConfigurationItem lastRenameType = null; - - @ConfigurationDefaultString(".") - public static final ConfigurationItem lastSaveDir = null; - - @ConfigurationDefaultString(".") - public static final ConfigurationItem lastOpenDir = null; - - @ConfigurationDefaultString(".") - public static final ConfigurationItem lastExportDir = null; - - @ConfigurationDefaultString("en") - @ConfigurationCategory("ui") - public static final ConfigurationItem locale = null; - - @ConfigurationDefaultString("_loc%d_") - @ConfigurationCategory("script") - public static final ConfigurationItem registerNameFormat = null; - - @ConfigurationDefaultInt(10) - public static final ConfigurationItem maxRecentFileCount = null; - - public static final ConfigurationItem recentFiles = null; - - public static final ConfigurationItem fontPairing = null; - - public static final ConfigurationItem lastUpdatesCheckDate = null; - - @ConfigurationDefaultInt(1000) - @ConfigurationName("gui.window.width") - public static final ConfigurationItem guiWindowWidth = null; - @ConfigurationDefaultInt(700) - @ConfigurationName("gui.window.height") - public static final ConfigurationItem guiWindowHeight = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationName("gui.window.maximized.horizontal") - public static final ConfigurationItem guiWindowMaximizedHorizontal = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationName("gui.window.maximized.vertical") - public static final ConfigurationItem guiWindowMaximizedVertical = null; - @ConfigurationName("gui.avm2.splitPane.dividerLocation") - public static final ConfigurationItem guiAvm2SplitPaneDividerLocation = null; - @ConfigurationName("guiActionSplitPaneDividerLocation") - public static final ConfigurationItem guiActionSplitPaneDividerLocation = null; - @ConfigurationName("guiPreviewSplitPaneDividerLocation") - public static final ConfigurationItem guiPreviewSplitPaneDividerLocation = null; - @ConfigurationName("gui.splitPane1.dividerLocation") - public static final ConfigurationItem guiSplitPane1DividerLocation = null; - @ConfigurationName("gui.splitPane2.dividerLocation") - public static final ConfigurationItem guiSplitPane2DividerLocation = null; - @ConfigurationDefaultInt(3) - @ConfigurationCategory("export") - public static final ConfigurationItem saveAsExeScaleMode = null; - - @ConfigurationDefaultInt(1024 * 100/*100KB*/) - @ConfigurationCategory("limit") - public static final ConfigurationItem syntaxHighlightLimit = null; - public static final ConfigurationItem guiFontPreviewSampleText = null; - @ConfigurationName("gui.fontPreviewWindow.width") - public static final ConfigurationItem guiFontPreviewWidth = null; - @ConfigurationName("gui.fontPreviewWindow.height") - public static final ConfigurationItem guiFontPreviewHeight = null; - @ConfigurationName("gui.fontPreviewWindow.posX") - public static final ConfigurationItem guiFontPreviewPosX = null; - @ConfigurationName("gui.fontPreviewWindow.posY") - public static final ConfigurationItem guiFontPreviewPosY = null; - - @ConfigurationDefaultInt(3) - @ConfigurationName("formatting.indent.size") - @ConfigurationCategory("format") - public static final ConfigurationItem indentSize = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationName("formatting.indent.useTabs") - @ConfigurationCategory("format") - public static final ConfigurationItem indentUseTabs = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("format") - public static final ConfigurationItem beginBlockOnNewLine = null; - - @ConfigurationDefaultInt(1000 * 60 * 60 * 24) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.delay") - public static final ConfigurationItem checkForUpdatesDelay = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.stable") - public static final ConfigurationItem checkForUpdatesStable = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.nightly") - public static final ConfigurationItem checkForUpdatesNightly = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.enabled") - public static final ConfigurationItem checkForUpdatesAuto = null; - - @ConfigurationDefaultString("") - @ConfigurationName("export.formats") - public static final ConfigurationItem lastSelectedExportFormats = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("export") - public static final ConfigurationItem textExportSingleFile = null; - - @ConfigurationDefaultString("--- SEPARATOR ---") - @ConfigurationCategory("export") - public static final ConfigurationItem textExportSingleFileSeparator = null; - - @ConfigurationDefaultString("--- RECORDSEPARATOR ---") - @ConfigurationCategory("export") - public static final ConfigurationItem textExportSingleFileRecordSeparator = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationName("warning.experimental.as12edit") - @ConfigurationCategory("script") - public static final ConfigurationItem warningExperimentalAS12Edit = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationName("warning.experimental.as3edit") - @ConfigurationCategory("script") - public static final ConfigurationItem warningExperimentalAS3Edit = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("export") - public static final ConfigurationItem packJavaScripts = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("export") - public static final ConfigurationItem textExportExportFontFace = null; - - private enum OSId { - - WINDOWS, OSX, UNIX - } - - private static OSId getOSId() { - PrivilegedAction doGetOSName = new PrivilegedAction() { - @Override - public String run() { - return System.getProperty("os.name"); - } - }; - OSId id = OSId.UNIX; - String osName = AccessController.doPrivileged(doGetOSName); - if (osName != null) { - if (osName.toLowerCase().startsWith("mac os x")) { - id = OSId.OSX; - } else if (osName.contains("Windows")) { - id = OSId.WINDOWS; - } - } - return id; - } - - public static String getFFDecHome() { - if (directory == unspecifiedFile) { - directory = null; - String userHome = null; - try { - userHome = System.getProperty("user.home"); - } catch (SecurityException ignore) { - } - if (userHome != null) { - String applicationId = ApplicationInfo.SHORT_APPLICATION_NAME; - OSId osId = getOSId(); - if (osId == OSId.WINDOWS) { - File appDataDir = null; - try { - String appDataEV = System.getenv("APPDATA"); - if ((appDataEV != null) && (appDataEV.length() > 0)) { - appDataDir = new File(appDataEV); - } - } catch (SecurityException ignore) { - } - String vendorId = ApplicationInfo.VENDOR; - if ((appDataDir != null) && appDataDir.isDirectory()) { - // ${APPDATA}\{vendorId}\${applicationId} - String path = vendorId + "\\" + applicationId + "\\"; - directory = new File(appDataDir, path); - } else { - // ${userHome}\Application Data\${vendorId}\${applicationId} - String path = "Application Data\\" + vendorId + "\\" + applicationId + "\\"; - directory = new File(userHome, path); - } - } else if (osId == OSId.OSX) { - // ${userHome}/Library/Application Support/${applicationId} - String path = "Library/Application Support/" + applicationId + "/"; - directory = new File(userHome, path); - } else { - // ${userHome}/.${applicationId}/ - String path = "." + applicationId + "/"; - directory = new File(userHome, path); - } - } else { - //no home, then use application directory - directory = new File("."); - } - } - if (!directory.exists()) { - if (!directory.mkdirs()) { - if (!directory.exists()) { - directory = new File("."); //fallback to current directory - } - } - } - String ret = directory.getAbsolutePath(); - if (!ret.endsWith(File.separator)) { - ret += File.separator; - } - return ret; - } - - public static List getRecentFiles() { - String files = recentFiles.get(); - if (files == null || files.isEmpty()) { - return new ArrayList<>(); - } - return Arrays.asList(files.split("::")); - } - - public static void addRecentFile(String path) { - List recentFilesArray = new ArrayList<>(getRecentFiles()); - int idx = recentFilesArray.indexOf(path); - if (idx != -1) { - recentFilesArray.remove(idx); - } - recentFilesArray.add(path); - while (recentFilesArray.size() > maxRecentFileCount.get()) { - recentFilesArray.remove(0); - } - recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); - } - - public static void removeRecentFile(String path) { - List recentFilesArray = new ArrayList<>(getRecentFiles()); - int idx = recentFilesArray.indexOf(path); - if (idx != -1) { - recentFilesArray.remove(idx); - } - recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); - } - - public static Map getFontPairs() { - String fonts = fontPairing.get(); - if (fonts == null) { - return new HashMap<>(); - } - - Map result = new HashMap<>(); - for (String pair : fonts.split("::")) { - String[] splittedPair = pair.split("="); - result.put(splittedPair[0], splittedPair[1]); - } - return result; - } - - public static void addFontPair(String fileName, int fontId, String fontName, String systemFontName) { - String key = fileName + "_" + fontId + "_" + fontName; - Map fontPairs = getFontPairs(); - fontPairs.put(key, systemFontName); - fontPairs.put(fontName, systemFontName); - StringBuilder sb = new StringBuilder(); - int i = 0; - for (Entry pair : fontPairs.entrySet()) { - if (i != 0) { - sb.append("::"); - } - sb.append(pair.getKey()).append("=").append(pair.getValue()); - i++; - } - fontPairing.set(sb.toString()); - } - - /** - * Saves replacements to file for future use - */ - private static void saveReplacements(String replacementsFile) { - if (replacements.isEmpty()) { - File rf = new File(replacementsFile); - if (rf.exists()) { - if (!rf.delete()) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Cannot delete replacements file"); - } - } - } else { - try (PrintWriter pw = new PrintWriter(new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(replacementsFile))))) { - for (Replacement r : replacements) { - pw.println(r.urlPattern); - pw.println(r.targetFile); - } - } catch (IOException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Exception during saving replacements", ex); - } - } - } - - /** - * Load replacements from file - */ - private static void loadReplacements(String replacementsFile) { - if (!(new File(replacementsFile)).exists()) { - return; - } - replacements = new ArrayList<>(); - try (BufferedReader br = new BufferedReader(new Utf8InputStreamReader(new FileInputStream(replacementsFile)))) { - String s; - while ((s = br.readLine()) != null) { - Replacement r = new Replacement(s, br.readLine()); - replacements.add(r); - } - } catch (IOException e) { - //ignore - } - } - - private static String getReplacementsFile() throws IOException { - return getFFDecHome() + REPLACEMENTS_NAME; - } - - private static String getConfigFile() throws IOException { - return getFFDecHome() + CONFIG_NAME; - } - - private static HashMap loadFromFile(String file, String replacementsFile) { - HashMap config = new HashMap<>(); - try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { - - @SuppressWarnings("unchecked") - HashMap cfg = (HashMap) ois.readObject(); - config = cfg; - } catch (ClassNotFoundException | IOException ex) { - //ignore - } - if (replacementsFile != null) { - loadReplacements(replacementsFile); - } - if (config.containsKey("paralelSpeedUp")) { - config.put("parallelSpeedUp", config.get("paralelSpeedUp")); - config.remove("paralelSpeedUp"); - } - return config; - } - - private static void saveToFile(String file, String replacementsFile) { - HashMap config = new HashMap<>(); - for (Entry entry : getConfigurationFields().entrySet()) { - try { - String name = entry.getKey(); - Field field = entry.getValue(); - ConfigurationItem item = (ConfigurationItem) field.get(null); - if (item.hasValue) { - config.put(name, item.get()); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } - } - try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { - oos.writeObject(config); - } catch (IOException ex) { - //TODO: move this to GUI - JOptionPane.showMessageDialog(null, "Cannot save configuration.", "Error", JOptionPane.ERROR_MESSAGE); - Logger.getLogger(Configuration.class.getName()).severe("Configuration directory is read only."); - } - if (replacementsFile != null) { - saveReplacements(replacementsFile); - } - } - - public static List getReplacements() { - return replacements; - } - - public static void saveConfig() { - try { - saveToFile(getConfigFile(), getReplacementsFile()); - } catch (IOException ex) { - //ignore - } - } - - static { - setConfigurationFields(); - if (useDetailedLogging.get() || debugMode.get()) { - logLevel = Level.CONFIG; - } else { - logLevel = Level.WARNING; - } - int processorCount = Runtime.getRuntime().availableProcessors(); - if (parallelThreadCount.get() > processorCount) { - parallelThreadCount.set(processorCount); - } - } - - @SuppressWarnings("unchecked") - public static void setConfigurationFields() { - try { - HashMap config = loadFromFile(getConfigFile(), getReplacementsFile()); - for (Entry entry : getConfigurationFields().entrySet()) { - String name = entry.getKey(); - Field field = entry.getValue(); - // remove final modifier from field - Field modifiersField = field.getClass().getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - - Object defaultValue = getDefaultValue(field); - Object value = null; - if (config.containsKey(name)) { - value = config.get(name); - } - - if (value != null) { - field.set(null, new ConfigurationItem(name, defaultValue, value)); - } else { - field.set(null, new ConfigurationItem(name, defaultValue)); - } - } - } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } catch (IOException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public static Object getDefaultValue(Field field) { - Object defaultValue = null; - ConfigurationDefaultBoolean aBool = field.getAnnotation(ConfigurationDefaultBoolean.class); - if (aBool != null) { - defaultValue = aBool.value(); - } - ConfigurationDefaultInt aInt = field.getAnnotation(ConfigurationDefaultInt.class); - if (aInt != null) { - defaultValue = aInt.value(); - } - ConfigurationDefaultString aString = field.getAnnotation(ConfigurationDefaultString.class); - if (aString != null) { - defaultValue = aString.value(); - } - return defaultValue; - } - - public static Map getConfigurationFields() { - Field[] fields = Configuration.class.getFields(); - Map result = new HashMap<>(); - for (Field field : fields) { - if (ConfigurationItem.class.isAssignableFrom(field.getType())) { - ConfigurationName annotation = field.getAnnotation(ConfigurationName.class); - String name = annotation == null ? field.getName() : annotation.value(); - result.put(name, field); - } - } - return result; - } - - public static CodeFormatting getCodeFormatting() { - CodeFormatting ret = new CodeFormatting(); - String indentString = ""; - for (int i = 0; i < indentSize.get(); i++) { - indentString += indentUseTabs.get() ? "\t" : " "; - } - ret.indentString = indentString; - ret.beginBlockOnNewLine = beginBlockOnNewLine.get(); - return ret; - } - - public static File getFlashLibPath() { - String home = getFFDecHome(); - File libsdir = new File(home + "flashlib"); - if (!libsdir.exists()) { - libsdir.mkdirs(); - } - return libsdir; - } - - public static File getPlayerSWC() { - File libsdir = getFlashLibPath(); - if (libsdir != null && libsdir.exists()) { - File libs[] = libsdir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - return name.toLowerCase().startsWith("playerglobal"); - } - }); - List libnames = new ArrayList<>(); - for (File f : libs) { - libnames.add(f.getName()); - } - Collections.sort(libnames); - if (!libnames.isEmpty()) { - return new File(libsdir.getAbsolutePath() + File.separator + libnames.get(libnames.size() - 1)); - } else { - return null; - } - } - - return null; - } -} +/* + * Copyright (C) 2010-2014 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.configuration; + +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.utf8.Utf8InputStreamReader; +import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; +import com.jpexs.proxy.Replacement; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; + +public class Configuration { + + private static final String CONFIG_NAME = "config.bin"; + private static final String REPLACEMENTS_NAME = "replacements.cfg"; + private static final File unspecifiedFile = new File("unspecified"); + private static File directory = unspecifiedFile; + + /** + * List of replacements + */ + private static List replacements = new ArrayList<>(); + + public static final Level logLevel; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("ui") + public static final ConfigurationItem openMultipleFiles = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("script") + public static final ConfigurationItem decompile = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("decompilation") + public static final ConfigurationItem parallelSpeedUp = null; + + @ConfigurationDefaultInt(20) + @ConfigurationCategory("decompilation") + public static final ConfigurationItem parallelThreadCount = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem autoDeobfuscate = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("decompilation") + public static final ConfigurationItem cacheOnDisk = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("display") + public static final ConfigurationItem internalFlashViewer = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("display") + public static final ConfigurationItem dumpView = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("display") + public static final ConfigurationItem dumpInfoCollecting = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("ui") + public static final ConfigurationItem gotoMainClassOnStartup = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem autoRenameIdentifiers = null; + + @ConfigurationDefaultBoolean(false) + public static final ConfigurationItem offeredAssociation = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem decimalAddress = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem showAllAddresses = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("display") + public static final ConfigurationItem useFrameCache = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("ui") + public static final ConfigurationItem useRibbonInterface = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static final ConfigurationItem openFolderAfterFlaExport = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem useDetailedLogging = null; + + /** + * Debug mode = throwing an error when comparing original file and + * recompiled + */ + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem debugMode = null; + /** + * Turn off resolving constants in ActionScript 2 + */ + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("script") + public static final ConfigurationItem resolveConstants = null; + /** + * Limit of code subs (for obfuscated code) + */ + @ConfigurationDefaultInt(500) + @ConfigurationCategory("limit") + public static final ConfigurationItem sublimiter = null; + /** + * Total export timeout in seconds + */ + @ConfigurationDefaultInt(30 * 60) + @ConfigurationCategory("limit") + public static final ConfigurationItem exportTimeout = null; + /** + * Decompilation timeout in seconds for a single file + */ + @ConfigurationDefaultInt(5 * 60) + @ConfigurationCategory("limit") + public static final ConfigurationItem decompilationTimeoutFile = null; + /** + * Using parameter names in decompiling may cause problems because official + * programs like Flash CS 5.5 inserts wrong parameter names indices + */ + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem paramNamesEnable = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("ui") + public static final ConfigurationItem displayFileName = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem debugCopy = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem dumpTags = null; + + @ConfigurationDefaultInt(60) + @ConfigurationCategory("limit") + public static final ConfigurationItem decompilationTimeoutSingleMethod = null; + @ConfigurationDefaultInt(1) + public static final ConfigurationItem lastRenameType = null; + + @ConfigurationDefaultString(".") + public static final ConfigurationItem lastSaveDir = null; + + @ConfigurationDefaultString(".") + public static final ConfigurationItem lastOpenDir = null; + + @ConfigurationDefaultString(".") + public static final ConfigurationItem lastExportDir = null; + + @ConfigurationDefaultString("en") + @ConfigurationCategory("ui") + public static final ConfigurationItem locale = null; + + @ConfigurationDefaultString("_loc%d_") + @ConfigurationCategory("script") + public static final ConfigurationItem registerNameFormat = null; + + @ConfigurationDefaultInt(10) + public static final ConfigurationItem maxRecentFileCount = null; + + public static final ConfigurationItem recentFiles = null; + + public static final ConfigurationItem fontPairing = null; + + public static final ConfigurationItem lastUpdatesCheckDate = null; + + @ConfigurationDefaultInt(1000) + @ConfigurationName("gui.window.width") + public static final ConfigurationItem guiWindowWidth = null; + @ConfigurationDefaultInt(700) + @ConfigurationName("gui.window.height") + public static final ConfigurationItem guiWindowHeight = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationName("gui.window.maximized.horizontal") + public static final ConfigurationItem guiWindowMaximizedHorizontal = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationName("gui.window.maximized.vertical") + public static final ConfigurationItem guiWindowMaximizedVertical = null; + @ConfigurationName("gui.avm2.splitPane.dividerLocation") + public static final ConfigurationItem guiAvm2SplitPaneDividerLocation = null; + @ConfigurationName("guiActionSplitPaneDividerLocation") + public static final ConfigurationItem guiActionSplitPaneDividerLocation = null; + @ConfigurationName("guiPreviewSplitPaneDividerLocation") + public static final ConfigurationItem guiPreviewSplitPaneDividerLocation = null; + @ConfigurationName("gui.splitPane1.dividerLocation") + public static final ConfigurationItem guiSplitPane1DividerLocation = null; + @ConfigurationName("gui.splitPane2.dividerLocation") + public static final ConfigurationItem guiSplitPane2DividerLocation = null; + @ConfigurationDefaultInt(3) + @ConfigurationCategory("export") + public static final ConfigurationItem saveAsExeScaleMode = null; + + @ConfigurationDefaultInt(1024 * 100/*100KB*/) + @ConfigurationCategory("limit") + public static final ConfigurationItem syntaxHighlightLimit = null; + public static final ConfigurationItem guiFontPreviewSampleText = null; + @ConfigurationName("gui.fontPreviewWindow.width") + public static final ConfigurationItem guiFontPreviewWidth = null; + @ConfigurationName("gui.fontPreviewWindow.height") + public static final ConfigurationItem guiFontPreviewHeight = null; + @ConfigurationName("gui.fontPreviewWindow.posX") + public static final ConfigurationItem guiFontPreviewPosX = null; + @ConfigurationName("gui.fontPreviewWindow.posY") + public static final ConfigurationItem guiFontPreviewPosY = null; + + @ConfigurationDefaultInt(3) + @ConfigurationName("formatting.indent.size") + @ConfigurationCategory("format") + public static final ConfigurationItem indentSize = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationName("formatting.indent.useTabs") + @ConfigurationCategory("format") + public static final ConfigurationItem indentUseTabs = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("format") + public static final ConfigurationItem beginBlockOnNewLine = null; + + @ConfigurationDefaultInt(1000 * 60 * 60 * 24) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.delay") + public static final ConfigurationItem checkForUpdatesDelay = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.stable") + public static final ConfigurationItem checkForUpdatesStable = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.nightly") + public static final ConfigurationItem checkForUpdatesNightly = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.enabled") + public static final ConfigurationItem checkForUpdatesAuto = null; + + @ConfigurationDefaultString("") + @ConfigurationName("export.formats") + public static final ConfigurationItem lastSelectedExportFormats = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static final ConfigurationItem textExportSingleFile = null; + + @ConfigurationDefaultString("--- SEPARATOR ---") + @ConfigurationCategory("export") + public static final ConfigurationItem textExportSingleFileSeparator = null; + + @ConfigurationDefaultString("--- RECORDSEPARATOR ---") + @ConfigurationCategory("export") + public static final ConfigurationItem textExportSingleFileRecordSeparator = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationName("warning.experimental.as12edit") + @ConfigurationCategory("script") + public static final ConfigurationItem warningExperimentalAS12Edit = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationName("warning.experimental.as3edit") + @ConfigurationCategory("script") + public static final ConfigurationItem warningExperimentalAS3Edit = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("export") + public static final ConfigurationItem packJavaScripts = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static final ConfigurationItem textExportExportFontFace = null; + + private enum OSId { + + WINDOWS, OSX, UNIX + } + + private static OSId getOSId() { + PrivilegedAction doGetOSName = new PrivilegedAction() { + @Override + public String run() { + return System.getProperty("os.name"); + } + }; + OSId id = OSId.UNIX; + String osName = AccessController.doPrivileged(doGetOSName); + if (osName != null) { + if (osName.toLowerCase().startsWith("mac os x")) { + id = OSId.OSX; + } else if (osName.contains("Windows")) { + id = OSId.WINDOWS; + } + } + return id; + } + + public static String getFFDecHome() { + if (directory == unspecifiedFile) { + directory = null; + String userHome = null; + try { + userHome = System.getProperty("user.home"); + } catch (SecurityException ignore) { + } + if (userHome != null) { + String applicationId = ApplicationInfo.SHORT_APPLICATION_NAME; + OSId osId = getOSId(); + if (osId == OSId.WINDOWS) { + File appDataDir = null; + try { + String appDataEV = System.getenv("APPDATA"); + if ((appDataEV != null) && (appDataEV.length() > 0)) { + appDataDir = new File(appDataEV); + } + } catch (SecurityException ignore) { + } + String vendorId = ApplicationInfo.VENDOR; + if ((appDataDir != null) && appDataDir.isDirectory()) { + // ${APPDATA}\{vendorId}\${applicationId} + String path = vendorId + "\\" + applicationId + "\\"; + directory = new File(appDataDir, path); + } else { + // ${userHome}\Application Data\${vendorId}\${applicationId} + String path = "Application Data\\" + vendorId + "\\" + applicationId + "\\"; + directory = new File(userHome, path); + } + } else if (osId == OSId.OSX) { + // ${userHome}/Library/Application Support/${applicationId} + String path = "Library/Application Support/" + applicationId + "/"; + directory = new File(userHome, path); + } else { + // ${userHome}/.${applicationId}/ + String path = "." + applicationId + "/"; + directory = new File(userHome, path); + } + } else { + //no home, then use application directory + directory = new File("."); + } + } + if (!directory.exists()) { + if (!directory.mkdirs()) { + if (!directory.exists()) { + directory = new File("."); //fallback to current directory + } + } + } + String ret = directory.getAbsolutePath(); + if (!ret.endsWith(File.separator)) { + ret += File.separator; + } + return ret; + } + + public static List getRecentFiles() { + String files = recentFiles.get(); + if (files == null || files.isEmpty()) { + return new ArrayList<>(); + } + return Arrays.asList(files.split("::")); + } + + public static void addRecentFile(String path) { + List recentFilesArray = new ArrayList<>(getRecentFiles()); + int idx = recentFilesArray.indexOf(path); + if (idx != -1) { + recentFilesArray.remove(idx); + } + recentFilesArray.add(path); + while (recentFilesArray.size() > maxRecentFileCount.get()) { + recentFilesArray.remove(0); + } + recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); + } + + public static void removeRecentFile(String path) { + List recentFilesArray = new ArrayList<>(getRecentFiles()); + int idx = recentFilesArray.indexOf(path); + if (idx != -1) { + recentFilesArray.remove(idx); + } + recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); + } + + public static Map getFontPairs() { + String fonts = fontPairing.get(); + if (fonts == null) { + return new HashMap<>(); + } + + Map result = new HashMap<>(); + for (String pair : fonts.split("::")) { + String[] splittedPair = pair.split("="); + result.put(splittedPair[0], splittedPair[1]); + } + return result; + } + + public static void addFontPair(String fileName, int fontId, String fontName, String systemFontName) { + String key = fileName + "_" + fontId + "_" + fontName; + Map fontPairs = getFontPairs(); + fontPairs.put(key, systemFontName); + fontPairs.put(fontName, systemFontName); + StringBuilder sb = new StringBuilder(); + int i = 0; + for (Entry pair : fontPairs.entrySet()) { + if (i != 0) { + sb.append("::"); + } + sb.append(pair.getKey()).append("=").append(pair.getValue()); + i++; + } + fontPairing.set(sb.toString()); + } + + /** + * Saves replacements to file for future use + */ + private static void saveReplacements(String replacementsFile) { + if (replacements.isEmpty()) { + File rf = new File(replacementsFile); + if (rf.exists()) { + if (!rf.delete()) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Cannot delete replacements file"); + } + } + } else { + try (PrintWriter pw = new PrintWriter(new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(replacementsFile))))) { + for (Replacement r : replacements) { + pw.println(r.urlPattern); + pw.println(r.targetFile); + } + } catch (IOException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Exception during saving replacements", ex); + } + } + } + + /** + * Load replacements from file + */ + private static void loadReplacements(String replacementsFile) { + if (!(new File(replacementsFile)).exists()) { + return; + } + replacements = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new Utf8InputStreamReader(new FileInputStream(replacementsFile)))) { + String s; + while ((s = br.readLine()) != null) { + Replacement r = new Replacement(s, br.readLine()); + replacements.add(r); + } + } catch (IOException e) { + //ignore + } + } + + private static String getReplacementsFile() throws IOException { + return getFFDecHome() + REPLACEMENTS_NAME; + } + + private static String getConfigFile() throws IOException { + return getFFDecHome() + CONFIG_NAME; + } + + private static HashMap loadFromFile(String file, String replacementsFile) { + HashMap config = new HashMap<>(); + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { + + @SuppressWarnings("unchecked") + HashMap cfg = (HashMap) ois.readObject(); + config = cfg; + } catch (ClassNotFoundException | IOException ex) { + //ignore + } + if (replacementsFile != null) { + loadReplacements(replacementsFile); + } + if (config.containsKey("paralelSpeedUp")) { + config.put("parallelSpeedUp", config.get("paralelSpeedUp")); + config.remove("paralelSpeedUp"); + } + return config; + } + + private static void saveToFile(String file, String replacementsFile) { + HashMap config = new HashMap<>(); + for (Entry entry : getConfigurationFields().entrySet()) { + try { + String name = entry.getKey(); + Field field = entry.getValue(); + ConfigurationItem item = (ConfigurationItem) field.get(null); + if (item.hasValue) { + config.put(name, item.get()); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); + } + } + try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + oos.writeObject(config); + } catch (IOException ex) { + //TODO: move this to GUI + JOptionPane.showMessageDialog(null, "Cannot save configuration.", "Error", JOptionPane.ERROR_MESSAGE); + Logger.getLogger(Configuration.class.getName()).severe("Configuration directory is read only."); + } + if (replacementsFile != null) { + saveReplacements(replacementsFile); + } + } + + public static List getReplacements() { + return replacements; + } + + public static void saveConfig() { + try { + saveToFile(getConfigFile(), getReplacementsFile()); + } catch (IOException ex) { + //ignore + } + } + + static { + setConfigurationFields(); + if (useDetailedLogging.get() || debugMode.get()) { + logLevel = Level.CONFIG; + } else { + logLevel = Level.WARNING; + } + int processorCount = Runtime.getRuntime().availableProcessors(); + if (parallelThreadCount.get() > processorCount) { + parallelThreadCount.set(processorCount); + } + + if (lastUpdatesCheckDate.get() == null) { + GregorianCalendar mingc = new GregorianCalendar(); + mingc.setTime(new Date(Long.MIN_VALUE)); + lastUpdatesCheckDate.set(mingc); + } + } + + @SuppressWarnings("unchecked") + public static void setConfigurationFields() { + try { + HashMap config = loadFromFile(getConfigFile(), getReplacementsFile()); + for (Entry entry : getConfigurationFields().entrySet()) { + String name = entry.getKey(); + Field field = entry.getValue(); + // remove final modifier from field + Field modifiersField = field.getClass().getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + + Object defaultValue = getDefaultValue(field); + Object value = null; + if (config.containsKey(name)) { + value = config.get(name); + } + + if (value != null) { + field.set(null, new ConfigurationItem(name, defaultValue, value)); + } else { + field.set(null, new ConfigurationItem(name, defaultValue)); + } + } + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } catch (IOException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static Object getDefaultValue(Field field) { + Object defaultValue = null; + ConfigurationDefaultBoolean aBool = field.getAnnotation(ConfigurationDefaultBoolean.class); + if (aBool != null) { + defaultValue = aBool.value(); + } + ConfigurationDefaultInt aInt = field.getAnnotation(ConfigurationDefaultInt.class); + if (aInt != null) { + defaultValue = aInt.value(); + } + ConfigurationDefaultString aString = field.getAnnotation(ConfigurationDefaultString.class); + if (aString != null) { + defaultValue = aString.value(); + } + return defaultValue; + } + + public static Map getConfigurationFields() { + Field[] fields = Configuration.class.getFields(); + Map result = new HashMap<>(); + for (Field field : fields) { + if (ConfigurationItem.class.isAssignableFrom(field.getType())) { + ConfigurationName annotation = field.getAnnotation(ConfigurationName.class); + String name = annotation == null ? field.getName() : annotation.value(); + result.put(name, field); + } + } + return result; + } + + public static CodeFormatting getCodeFormatting() { + CodeFormatting ret = new CodeFormatting(); + String indentString = ""; + for (int i = 0; i < indentSize.get(); i++) { + indentString += indentUseTabs.get() ? "\t" : " "; + } + ret.indentString = indentString; + ret.beginBlockOnNewLine = beginBlockOnNewLine.get(); + return ret; + } + + public static File getFlashLibPath() { + String home = getFFDecHome(); + File libsdir = new File(home + "flashlib"); + if (!libsdir.exists()) { + libsdir.mkdirs(); + } + return libsdir; + } + + public static File getPlayerSWC() { + File libsdir = getFlashLibPath(); + if (libsdir != null && libsdir.exists()) { + File libs[] = libsdir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().startsWith("playerglobal"); + } + }); + List libnames = new ArrayList<>(); + for (File f : libs) { + libnames.add(f.getName()); + } + Collections.sort(libnames); + if (!libnames.isEmpty()) { + return new File(libsdir.getAbsolutePath() + File.separator + libnames.get(libnames.size() - 1)); + } else { + return null; + } + } + + return null; + } +} diff --git a/src/com/jpexs/decompiler/flash/helpers/GraphTextWriter.java b/src/com/jpexs/decompiler/flash/helpers/GraphTextWriter.java index 454979384..542105a50 100644 --- a/src/com/jpexs/decompiler/flash/helpers/GraphTextWriter.java +++ b/src/com/jpexs/decompiler/flash/helpers/GraphTextWriter.java @@ -153,7 +153,7 @@ public abstract class GraphTextWriter { return ""; } - public GraphTextWriter startBlock(String opening) { + private GraphTextWriter startBlock(String opening) { if (formatting.beginBlockOnNewLine) { newLine(); } else { @@ -166,7 +166,7 @@ public abstract class GraphTextWriter { return startBlock("{"); } - public GraphTextWriter endBlock(String closing) { + private GraphTextWriter endBlock(String closing) { return unindent().append(closing); } diff --git a/src/com/jpexs/decompiler/flash/helpers/HilightedTextWriter.java b/src/com/jpexs/decompiler/flash/helpers/HilightedTextWriter.java index 3228acbeb..e0ed9f94c 100644 --- a/src/com/jpexs/decompiler/flash/helpers/HilightedTextWriter.java +++ b/src/com/jpexs/decompiler/flash/helpers/HilightedTextWriter.java @@ -157,15 +157,17 @@ public class HilightedTextWriter extends GraphTextWriter { @Override public HilightedTextWriter append(String str) { - GraphSourceItemPosition itemPos = offsets.peek(); - GraphSourceItem src = itemPos.graphSourceItem; - int pos = itemPos.position; Highlighting h = null; - if (src != null && hilight) { - Map data = new HashMap<>(); - data.put("offset", Long.toString(src.getOffset() + pos + 1)); - h = new Highlighting(sb.length() - newLineCount, data, HilightType.OFFSET, str); - instructionHilights.add(h); + if (!offsets.empty()) { + GraphSourceItemPosition itemPos = offsets.peek(); + GraphSourceItem src = itemPos.graphSourceItem; + int pos = itemPos.position; + if (src != null && hilight) { + Map data = new HashMap<>(); + data.put("offset", Long.toString(src.getOffset() + pos + 1)); + h = new Highlighting(sb.length() - newLineCount, data, HilightType.OFFSET, str); + instructionHilights.add(h); + } } appendToSb(str); if (h != null) {