/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.abc.types.traits; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.exporters.script.Dependency; import com.jpexs.decompiler.flash.exporters.script.DependencyParser; import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; import java.io.Serializable; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * * @author JPEXS */ public abstract class Trait implements Cloneable, Serializable { public static final String METADATA_CTOR_DEFINITION = "__go_to_ctor_definition_help"; public static final String METADATA_DEFINITION = "__go_to_definition_help"; private static final int[] EMPTY_METADATA_ARRAY = new int[0]; public int name_index; public int kindType; public int kindFlags; public int[] metadata = EMPTY_METADATA_ARRAY; public long fileOffset; public byte[] bytes; public static final int ATTR_Final = 0x1; public static final int ATTR_Override = 0x2; public static final int ATTR_Metadata = 0x4; public static final int ATTR_0x8 = 0x8; //unknown public static final int TRAIT_SLOT = 0; public static final int TRAIT_METHOD = 1; public static final int TRAIT_GETTER = 2; public static final int TRAIT_SETTER = 3; public static final int TRAIT_CLASS = 4; public static final int TRAIT_FUNCTION = 5; public static final int TRAIT_CONST = 6; public abstract void delete(ABC abc, boolean d); public final List<Entry<String, Map<String, String>>> getMetaDataTable(Trait parent, ConvertData convertData, ABC abc) { List<Entry<String, Map<String, String>>> ret = new ArrayList<>(); for (int m : metadata) { if (m >= 0 && m < abc.metadata_info.size()) { String name = abc.constants.getString(abc.metadata_info.get(m).name_index); Map<String, String> data = new HashMap<>(); for (int i = 0; i < abc.metadata_info.get(m).keys.length; i++) { data.put(abc.constants.getString(abc.metadata_info.get(m).keys[i]), abc.constants.getString(abc.metadata_info.get(m).values[i])); } ret.add(new SimpleEntry<>(name, data)); } } if (Configuration.handleSkinPartsAutomatically.get()) { /* private static var _skinParts:Object = {"attr":false,"attr2":true}; => [SkinPart required="false"] public var attr; [SkinPart required="true"] public var attr2; */ if (parent instanceof TraitClass) { String thisName = getName(abc).getName(abc.constants, new ArrayList<>(), true, true); List<Trait> classTraits = abc.class_info.get(((TraitClass) parent).class_info).static_traits.traits; for (Trait t : classTraits) { if (t.kindType == Trait.TRAIT_SLOT) { if ("_skinParts".equals(t.getName(abc).getName(abc.constants, new ArrayList<>(), true, true))) { if (t.getName(abc).getNamespace(abc.constants).kind == Namespace.KIND_PRIVATE) { if (convertData.assignedValues.containsKey(t)) { if (convertData.assignedValues.get(t).value instanceof NewObjectAVM2Item) { NewObjectAVM2Item no = (NewObjectAVM2Item) convertData.assignedValues.get(t).value; for (NameValuePair nvp : no.pairs) { if (nvp.name instanceof StringAVM2Item) { if (thisName.equals(((StringAVM2Item) nvp.name).getValue())) { String newReq = "" + nvp.value.getResult(); boolean found = false; //if already has SkinPart metadata, change required value only for (int i = 0; i < ret.size(); i++) { Entry<String, Map<String, String>> e = ret.get(i); if ("SkinPart".equals(e.getKey())) { e.getValue().put("required", newReq); found = true; break; } } //add new metadata if not found if (!found) { Map<String, String> data = new HashMap<>(); data.put("required", newReq); ret.add(new SimpleEntry<>("SkinPart", data)); } } } } } } } } } } } } return ret; } protected DottedChain getPackage(ABC abc) { return getName(abc).getNamespace(abc.constants).getName(abc.constants); } public void getDependencies(String ignoredCustom, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) { if (ignoredCustom == null) { Namespace n = getName(abc).getNamespace(abc.constants); if (n.kind == Namespace.KIND_NAMESPACE) { ignoredCustom = n.getName(abc.constants).toRawString(); } } DependencyParser.parseUsagesFromMultiname(ignoredCustom, abc, dependencies, uses, getName(abc), ignorePackage, fullyQualifiedNames, DependencyType.NAMESPACE); } 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 static boolean isBuiltInClass(String name) { for (String g : builtInClasses) { if (g.equals(name)) { return true; } } return false; } public void writeImportsUsages(ABC abc, GraphTextWriter writer, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) { List<String> namesInThisPackage = new ArrayList<>(); for (ABCContainerTag tag : abc.getAbcTags()) { for (ScriptInfo si : tag.getABC().script_info) { for (Trait t : si.traits.traits) { ClassPath classPath = t.getPath(tag.getABC()); if (classPath.packageStr.equals(ignorePackage)) { namesInThisPackage.add(classPath.className); } } } } //imports List<Dependency> dependencies = new ArrayList<>(); List<String> uses = new ArrayList<>(); String customNs = null; Namespace ns = getName(abc).getNamespace(abc.constants); if (ns.kind == Namespace.KIND_NAMESPACE) { customNs = ns.getName(abc.constants).toRawString(); } getDependencies(customNs, abc, dependencies, uses, ignorePackage, new ArrayList<>()); List<DottedChain> imports = new ArrayList<>(); for (Dependency d : dependencies) { if (!imports.contains(d.getId())) { imports.add(d.getId()); } } List<String> importnames = new ArrayList<>(); importnames.addAll(namesInThisPackage); importnames.addAll(Arrays.asList(builtInClasses)); for (int i = 0; i < imports.size(); i++) { DottedChain ipath = imports.get(i); if (ipath.getWithoutLast().equals(ignorePackage)) { //do not check classes from same package, they are imported automatically imports.remove(i); i--; continue; } String name = ipath.getLast(); if (importnames.contains(name)) { imports.remove(i); i--; fullyQualifiedNames.add(DottedChain.parseWithSuffix(name)); } else { importnames.add(name); } } for (int i = 0; i < imports.size(); i++) { DottedChain imp = imports.get(i); DottedChain pkg = imp.getWithoutLast(); String name = imp.getLast(); if (name.equals("*")) { continue; } DottedChain dAll = pkg.addWithSuffix("*"); if (imports.contains(dAll)) { imports.remove(i); i--; } } boolean hasImport = false; Collections.sort(imports); for (DottedChain imp : imports) { if (imp.size() > 1) { //No imports from root package writer.appendNoHilight("import " + imp.toPrintableString(true) + ";").newLine(); hasImport = true; } } if (hasImport) { writer.newLine(); } for (String us : uses) { writer.appendNoHilight("use namespace " + us + ";").newLine(); } if (uses.size() > 0) { writer.newLine(); } } public final GraphTextWriter getMetaData(Trait parent, ConvertData convertData, ABC abc, GraphTextWriter writer) { List<Entry<String, Map<String, String>>> md = getMetaDataTable(parent, convertData, abc); for (Entry<String, Map<String, String>> en : md) { String name = en.getKey(); if (METADATA_DEFINITION.equals(name) || METADATA_CTOR_DEFINITION.equals(name)) { continue; } writer.append("[").append(IdentifiersDeobfuscation.printIdentifier(true, name)); if (!en.getValue().isEmpty()) { writer.append("("); boolean first = true; for (String key : en.getValue().keySet()) { if (!first) { writer.append(","); } first = false; if (key != null && !key.isEmpty()) { writer.append(IdentifiersDeobfuscation.printIdentifier(true, key)).append("="); } writer.append("\""); String val = en.getValue().get(key); writer.append(Helper.escapeActionScriptString(val)); writer.append("\""); } writer.append(")"); } writer.append("]"); writer.newLine(); } return writer; } protected final DottedChain findCustomNs(int link_ns_index, ABC abc) { String nsname; if (link_ns_index <= 0) { return null; } Namespace ns = abc.constants.getNamespace(link_ns_index); if (ns.kind != Namespace.KIND_NAMESPACE) { return null; } String name = abc.constants.getString(ns.name_index); for (ABCContainerTag abcTag : abc.getAbcTags()) { DottedChain dc = abcTag.getABC().nsValueToName(name); nsname = dc.getLast(); if (nsname == null) { continue; } if (!nsname.isEmpty()) { return dc; } } return null; } public final GraphTextWriter getModifiers(ABC abc, boolean isStatic, GraphTextWriter writer) { if ((kindFlags & ATTR_Override) > 0) { writer.appendNoHilight("override "); } Multiname m = getName(abc); if (m != null) { DottedChain dc = findCustomNs(m.namespace_index, abc); String nsname = dc != null ? dc.getLast() : null; Namespace ns = m.getNamespace(abc.constants); if (nsname != null) { String identifier = IdentifiersDeobfuscation.printIdentifier(true, nsname); if (identifier != null && !identifier.isEmpty()) { writer.appendNoHilight(identifier).appendNoHilight(" "); } } if (ns != null) { String nsPrefix = ns.getPrefix(abc); if (nsPrefix != null && !nsPrefix.isEmpty()) { writer.appendNoHilight(nsPrefix).appendNoHilight(" "); } } } if (isStatic) { if ((this instanceof TraitSlotConst) && ((TraitSlotConst) this).isNamespace()) { //static is automatic } else { writer.appendNoHilight("static "); } } if ((kindFlags & ATTR_Final) > 0) { if (!isStatic) { writer.appendNoHilight("final "); } } return writer; } @Override public String toString() { return "name_index=" + name_index + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); } public String toString(ABC abc, List<DottedChain> fullyQualifiedNames) { return abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); } public GraphTextWriter toString(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException { writer.appendNoHilight(abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata)); return writer; } public void convert(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException { } public abstract GraphTextWriter convertTraitHeader(ABC abc, GraphTextWriter writer); public GraphTextWriter convertCommonHeaderFlags(String traitType, ABC abc, GraphTextWriter writer) { writer.appendNoHilight("trait "); writer.hilightSpecial(traitType, HighlightSpecialType.TRAIT_TYPE); writer.appendNoHilight(" "); writer.hilightSpecial(abc.constants.multinameToString(name_index), HighlightSpecialType.TRAIT_NAME); if ((kindFlags & ATTR_Final) > 0) { writer.newLine(); writer.append("flag "); writer.hilightSpecial("FINAL", HighlightSpecialType.ATTR_FINAL); } if ((kindFlags & ATTR_Override) > 0) { writer.newLine(); writer.append("flag "); writer.hilightSpecial("OVERRIDE", HighlightSpecialType.ATTR_OVERRIDE); } if ((kindFlags & ATTR_Metadata) > 0) { writer.newLine(); writer.append("flag "); writer.hilightSpecial("METADATA", HighlightSpecialType.ATTR_METADATA); } if ((kindFlags & ATTR_0x8) > 0) { writer.newLine(); writer.append("flag "); writer.hilightSpecial("0x8", HighlightSpecialType.ATTR_0x8); } if ((kindFlags & ATTR_Metadata) > 0) { writer.newLine(); for (int m : metadata) { writer.append("metadata"); writer.append("\""); writer.append(Helper.escapeActionScriptString(abc.constants.getString(abc.metadata_info.get(m).name_index))); writer.append("\""); writer.newLine(); if (m >= 0 && m < abc.metadata_info.size()) { for (int i = 0; i < abc.metadata_info.get(m).keys.length; i++) { int key = abc.metadata_info.get(m).keys[i]; int val = abc.metadata_info.get(m).values[i]; writer.append("item "); writer.append("\""); writer.append(Helper.escapeActionScriptString(abc.constants.getString(key))); writer.append("\""); writer.append(" "); writer.append("\""); writer.append(Helper.escapeActionScriptString(abc.constants.getString(val))); writer.append("\""); writer.newLine(); } } writer.append("end ; metadata"); } } return writer; } public GraphTextWriter toStringPackaged(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException { Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { String nsname = ns.getName(abc.constants).toPrintableString(true); writer.appendNoHilight("package"); if (!nsname.isEmpty()) { writer.appendNoHilight(" " + nsname); //assume not null name } writer.startBlock(); toString(parent, convertData, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); writer.endBlock(); writer.newLine(); } return writer; } public void convertPackaged(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException { Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { String nsname = ns.getName(abc.constants).toPrintableString(true); convert(parent, convertData, path + nsname, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); } } public GraphTextWriter toStringHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException { toString(parent, convertData, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); return writer; } public void convertHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) throws InterruptedException { convert(parent, convertData, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); } public final Multiname getName(ABC abc) { if (name_index == 0) { return null; } else { return abc.constants.getMultiname(name_index); } } public abstract int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException; public final ClassPath getPath(ABC abc) { Multiname name = getName(abc); Namespace ns = name.getNamespace(abc.constants); DottedChain packageName = ns == null ? DottedChain.EMPTY : ns.getName(abc.constants); String objectName = name.getName(abc.constants, null, true, false); String namespaceSuffix = name.getNamespaceSuffix(); return new ClassPath(packageName, objectName, namespaceSuffix); //assume not null name } @Override public Trait clone() { try { Trait ret = (Trait) super.clone(); return ret; } catch (CloneNotSupportedException ex) { throw new RuntimeException(); } } public boolean isVisible(boolean isStatic, ABC abc) { return true; } }