/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.exporters.script; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.types.ClassInfo; import com.jpexs.decompiler.flash.abc.types.InstanceInfo; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.NamespaceSet; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; import com.jpexs.decompiler.graph.DottedChain; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Class LinkReportExporter - generates Linker reports similar to Flex * -link-report, but for SWF files * * @author JPEXS */ public class LinkReportExporter { private String newLineChar = "\n"; private String indentStr = " "; /** * Constructs reporter with LF as newline, two spaces as indent */ public LinkReportExporter() { } /** * Constructs reporter with custom newline char, two spaces as indent * * @param newLineChar */ public LinkReportExporter(String newLineChar) { this.newLineChar = newLineChar; } /** * Constructs reporter with custom newline char and indent string * * @param newLineChar * @param indentStr */ public LinkReportExporter(String newLineChar, String indentStr) { this.newLineChar = newLineChar; this.indentStr = indentStr; } private String indent(int cnt) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < cnt; i++) { sb.append(indentStr); } return sb.toString(); } public String generateReport(SWF swf, List<ScriptPack> as3scripts, EventListener evl) { StringBuilder sb = new StringBuilder(); Set<String> extDeps = new HashSet<>(); sb.append("<report>").append(newLineChar); sb.append(indent(1)).append("<scripts>").append(newLineChar); List<ScriptPack> revList = new ArrayList<>(as3scripts); Collections.reverse(revList); List<DottedChain> existingObjects = new ArrayList<>(); for (ScriptPack sp : as3scripts) { existingObjects.add(sp.getClassPath().packageStr.add(sp.getClassPath().className, sp.getClassPath().namespaceSuffix)); } for (ScriptPack sp : revList) { String scriptName = "script" + sp.scriptIndex; sb.append(indent(2)).append("<script name=\"").append(scriptName).append("\">").append(newLineChar); //TODO: additional atributes - mod="1469951734131" size="525" optimizedsize="508 ScriptInfo script = sp.abc.script_info.get(sp.scriptIndex); for (int traitIndex : sp.traitIndices) { Trait trait = script.traits.traits.get(traitIndex); sb.append(reportTrait(extDeps, existingObjects, swf, sp.abc, trait)); } //how about script_init method(?) sb.append(indent(2)).append("</script>").append(newLineChar); } sb.append(indent(1)).append("</scripts>").append(newLineChar); sb.append("</report>").append(newLineChar); return sb.toString(); } private String multiNameToId(ABC abc, Multiname multiName) { Namespace ns = multiName.getNamespace(abc.constants); NamespaceSet nss = multiName.getNamespaceSet(abc.constants); if (nss != null && nss.namespaces.length == 1) { ns = abc.constants.getNamespace(nss.namespaces[0]); } String pkgName = ns == null ? "" : ns.getName(abc.constants).toRawString(); String clsName = multiName.getName(abc.constants, new ArrayList<>(), true, true); return pkgName.isEmpty() ? clsName : pkgName + ":" + clsName; } private String dottedChainToId(DottedChain dc) { if (dc.getWithoutLast().isEmpty()) { return dc.getLast(); } return dc.getWithoutLast().toRawString() + ":" + dc.getLast(); } private String reportTrait(Set<String> externalDefs, List<DottedChain> existingObjects, SWF swf, ABC abc, Trait t) { //TODO: handle externalDefs - <external-defs> <ext id="..." /> </external-defs> StringBuilder sb = new StringBuilder(); if (t instanceof TraitClass) { TraitClass tc = (TraitClass) t; sb.append(indent(3)).append("<def id=\"").append(multiNameToId(abc, tc.getName(abc))).append("\" />").append(newLineChar); ClassInfo ci = abc.class_info.get(tc.class_info); InstanceInfo ii = abc.instance_info.get(tc.class_info); Set<String> allDeps = new HashSet<>(); String superPre; if (ii.super_index != 0) { superPre = multiNameToId(abc, abc.constants.getMultiname(ii.super_index)); } else { superPre = "Object"; } allDeps.add(superPre); sb.append(indent(3)).append("<pre id=\"").append(superPre).append("\" />").append(newLineChar); for (int iface : ii.interfaces) { String ifacePre = multiNameToId(abc, abc.constants.getMultiname(iface)); allDeps.add(ifacePre); sb.append(indent(3)).append("<pre id=\"").append(ifacePre).append("\" />").append(newLineChar); } for (Trait ct : ci.static_traits.traits) { reportTrait(externalDefs, existingObjects, swf, abc, ct); } for (Trait it : ii.instance_traits.traits) { reportTrait(externalDefs, existingObjects, swf, abc, it); } List<Dependency> dependencies = new ArrayList<>(); List<String> uses = new ArrayList<>(); sb.append(indent(3)).append("<dep id=\"AS3\" />").append(newLineChar); //Automatic tc.getDependencies(null, abc, dependencies, uses, new DottedChain(new String[]{"FAKE!PACKAGE"}, ""), new ArrayList<>()); for (Dependency dependency : dependencies) { DottedChain dc = dependency.getId(); if (!"*".equals(dc.getLast())) { //some toplevel "imports" can be only method calls if (dc.getWithoutLast().isEmpty() && !existingObjects.contains(dc)) { continue; } String reportDepId = dottedChainToId(dc); if (!allDeps.contains(reportDepId)) { sb.append(indent(3)).append("<dep id=\"").append(reportDepId).append("\" />").append(newLineChar); allDeps.add(reportDepId); } } } } return sb.toString(); } }