/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.abc.print; import org.apache.flex.abc.ABCConstants; import org.apache.flex.abc.ABCParser; import org.apache.flex.abc.PoolingABCVisitor; import org.apache.flex.abc.graph.IBasicBlock; import org.apache.flex.abc.graph.IFlowgraph; import org.apache.flex.abc.semantics.ClassInfo; import org.apache.flex.abc.semantics.ExceptionInfo; import org.apache.flex.abc.semantics.InstanceInfo; import org.apache.flex.abc.semantics.Instruction; import org.apache.flex.abc.semantics.Label; import org.apache.flex.abc.semantics.Metadata; import org.apache.flex.abc.semantics.MethodBodyInfo; import org.apache.flex.abc.semantics.MethodInfo; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.semantics.Namespace; import org.apache.flex.abc.semantics.Nsset; import org.apache.flex.abc.semantics.ScriptInfo; import org.apache.flex.abc.semantics.Trait; import org.apache.flex.abc.semantics.Traits; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import static org.apache.flex.abc.ABCConstants.CONSTANT_Multiname; import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameA; import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameL; import static org.apache.flex.abc.ABCConstants.CONSTANT_Namespace; import static org.apache.flex.abc.ABCConstants.CONSTANT_PackageInternalNs; import static org.apache.flex.abc.ABCConstants.CONSTANT_PackageNs; import static org.apache.flex.abc.ABCConstants.CONSTANT_PrivateNs; import static org.apache.flex.abc.ABCConstants.CONSTANT_ProtectedNs; import static org.apache.flex.abc.ABCConstants.CONSTANT_Qname; import static org.apache.flex.abc.ABCConstants.CONSTANT_QnameA; import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQname; import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQnameA; import static org.apache.flex.abc.ABCConstants.CONSTANT_StaticProtectedNs; import static org.apache.flex.abc.ABCConstants.CONSTANT_TypeName; import static org.apache.flex.abc.ABCConstants.OP_debugfile; import static org.apache.flex.abc.ABCConstants.OP_lookupswitch; import static org.apache.flex.abc.ABCConstants.OP_pushstring; import static org.apache.flex.abc.ABCConstants.TRAIT_Class; import static org.apache.flex.abc.ABCConstants.TRAIT_Const; import static org.apache.flex.abc.ABCConstants.TRAIT_Function; import static org.apache.flex.abc.ABCConstants.TRAIT_Getter; import static org.apache.flex.abc.ABCConstants.TRAIT_Method; import static org.apache.flex.abc.ABCConstants.TRAIT_Setter; import static org.apache.flex.abc.ABCConstants.TRAIT_Var; /** * ABC Visitor implementation that can take an ABC and * dump out a textual representation of it. Similar to * abcdump/abcdis in the tamarin project. * <p/> * To use, construct one of these and pass it ABCParser, * or whatever is driving the ABC events. */ public class ABCDumpVisitor extends PoolingABCVisitor { /** * Constructor * * @param p The PrintWriter to write the textual represention of the ABC to */ public ABCDumpVisitor (PrintWriter p, boolean sortOption) { super(); printer = new IndentingPrinter(p, 0, 2); dumpedMethods = new HashSet<MethodInfo>(); this.sortOption = sortOption; } private boolean sortOption; private IndentingPrinter printer; private Set<MethodInfo> dumpedMethods; /** * Turn a namespace, name pair into a user readable String */ private String qnameToString (Namespace ns, String n) { if (ns == null) return n; if ((ns.getKind() == CONSTANT_PackageNs) && (ns.getName().length() > 0)) return ns.getName() + "::" + n; String qual = nsQualifierForNamespace(ns); if (qual.length() > 0) return n; if (ns.getName().length() == 0) return n; return ns.getName() + "::" + n; } /** * Turn a namespace set into a user readable String */ private String nssetToString (Nsset nsSet) { String s = ""; for (Namespace ns : nsSet) { if (ns.getKind() != CONSTANT_PrivateNs) s += (ns.getName() + ", "); else s += "private, "; } return "{" + s + "}"; } /** * Turn a Name into a user readable String */ private String nameToString (Name n) { if (n == null || n.couldBeAnyType()) return "*"; Nsset nsset; switch (n.getKind()) { case CONSTANT_Qname: case CONSTANT_QnameA: return qnameToString(n.getSingleQualifier(), n.getBaseName()); case CONSTANT_Multiname: case CONSTANT_MultinameA: nsset = n.getQualifiers(); if (nsset.length() == 1) return qnameToString(nsset.iterator().next(), n.getBaseName()); else return (nssetToString(nsset) + "::") + n.getBaseName(); case CONSTANT_RTQname: case CONSTANT_RTQnameA: return "<error> " + n.toString(); case CONSTANT_MultinameL: return "<error> " + n.toString(); case CONSTANT_TypeName: Name typeName = n.getTypeNameParameter(); return nameToString(n.getTypeNameBase()) + ".<" + nameToString(typeName) + ">"; } return "<error> " + n.toString(); } /** * Get a String that can be used as the qualifier for a given Name */ private String nsQualifierForName (Name n) { Nsset nsset; switch (n.getKind()) { case CONSTANT_Qname: case CONSTANT_QnameA: return nsQualifierForNamespace(n.getSingleQualifier()); case CONSTANT_Multiname: case CONSTANT_MultinameA: nsset = n.getQualifiers(); if (nsset.length() == 1) return nsQualifierForNamespace(nsset.iterator().next()); break; case CONSTANT_RTQname: case CONSTANT_RTQnameA: break; case CONSTANT_MultinameL: break; case CONSTANT_TypeName: break; } return ""; } /** * Get a String representing the access modifier(public, private, etc) based on a namespace value */ private String nsQualifierForNamespace (Namespace ns) { switch (ns.getKind()) { case CONSTANT_PackageNs: return "public "; case CONSTANT_ProtectedNs: case CONSTANT_StaticProtectedNs: //case CONSTANT_StaticProtectedNs2: return "protected "; case CONSTANT_PackageInternalNs: return "internal "; case CONSTANT_PrivateNs: return "private "; } if (ns.getKind() == CONSTANT_Namespace) { if (ns.getName().equals("http://adobe.com/AS3/2006/builtin")) return "AS3 "; } return ns.getName() + " "; } /** * Escape a string for displaying better */ private String stringToEscapedString (String s) { String charsToEscape = "\b\t\n\f\r\"\'\\"; String escapeChars = "btnfr\"\'\\"; int escapeIndex; char currChar; String result = ""; for (int i = 0; i < s.length(); ++i) { currChar = s.charAt(i); escapeIndex = charsToEscape.indexOf(currChar); if (escapeIndex != -1) result += "\\" + escapeChars.charAt(escapeIndex); else result += currChar; } return result; } /** * Print the ABC */ public void write () { traverse(); writeIterable(getMethodInfos(), new IWriteFunction() { public void write (Object v, int index) { writeAnonMethodInfo((MethodInfo) v, index); } } ); } /** * Walk over the elements of the ABC * Starts with the Scripts and walks down from there */ public void traverse () { int nScripts = getScriptInfos().size(); ScriptInfo si; if (sortOption) { HashMap<String, ScriptInfo> scripts = new HashMap<String, ScriptInfo>(); for (int i = 0; i < nScripts; ++i) { si = getScriptInfos().get(i); Iterator<Trait> traits = si.getTraits().iterator(); Name name = traits.hasNext() ? traits.next().getName() : null; String scriptName = name != null ? name.getSingleQualifier().getName() + "." + name.getBaseName() : ""; scripts.put(scriptName, si); } ArrayList<String> nameList = new ArrayList<String>(); nameList.addAll(scripts.keySet()); Collections.sort(nameList); for (int i = 0; i < nScripts; ++i) { si = scripts.get(nameList.get(i)); traverseScript(i, si); } } else { for (int i = 0; i < nScripts; ++i) { si = getScriptInfos().get(i); traverseScript(i, si); } } } /** * Traverse a Script, and its traits */ protected void traverseScript (int id, ScriptInfo scriptInfo) { printer.println("// script " + id); traverseScriptTraits(scriptInfo.getTraits(), scriptInfo); MethodInfo initMethodInfo = scriptInfo.getInit(); traverseScriptInit(initMethodInfo, scriptInfo, id); printer.println(""); } /** * Traverse the traits of a script */ protected void traverseScriptTraits (Traits traits, ScriptInfo si) { for (Trait t : traits) { switch (t.getKind()) { case TRAIT_Const: traverseScriptConstTrait(t, si); break; case TRAIT_Var: traverseScriptSlotTrait(t, si); break; case TRAIT_Method: traverseScriptMethodTrait(t, si); break; case TRAIT_Getter: traverseScriptGetterTrait(t, si); break; case TRAIT_Setter: traverseScriptSetterTrait(t, si); break; case TRAIT_Function: traverseScriptFunctionTrait(t, si); break; case TRAIT_Class: traverseScriptClassTrait(t, si); break; } } } /** * traverse the traits of an Instance Info */ protected void traverseInstanceTraits (Traits traits) { for (Trait t : traits) { switch (t.getKind()) { case TRAIT_Const: traverseInstanceConstTrait(t); break; case TRAIT_Var: traverseInstanceSlotTrait(t); break; case TRAIT_Method: traverseInstanceMethodTrait(t); break; case TRAIT_Getter: traverseInstanceGetterTrait(t); break; case TRAIT_Setter: traverseInstanceSetterTrait(t); break; case TRAIT_Function: traverseInstanceFunctionTrait(t); break; } } } /** * Traverse the traits of a Class Info */ protected void traverseClassTraits (Traits traits) { for (Trait t : traits) { switch (t.getKind()) { case TRAIT_Const: traverseClassConstTrait(t); break; case TRAIT_Var: traverseClassSlotTrait(t); break; case TRAIT_Method: traverseClassMethodTrait(t); break; case TRAIT_Getter: traverseClassGetterTrait(t); break; case TRAIT_Setter: traverseClassSetterTrait(t); break; case TRAIT_Function: traverseClassFunctionTrait(t); break; } } } /** * Traverse a slot trait of a script */ protected void traverseScriptSlotTrait (Trait trait, ScriptInfo scriptInfo) { writeSlotTrait("var", trait, false); } /** * Traverse a const trait of a script */ protected void traverseScriptConstTrait (Trait trait, ScriptInfo scriptInfo) { writeSlotTrait("const", trait, false); } /** * Traverse a method trait of a script */ protected void traverseScriptMethodTrait (Trait trait, ScriptInfo scriptInfo) { writeMethodTrait("function", trait, false); } /** * Traverse a getter trait of a script */ protected void traverseScriptGetterTrait (Trait trait, ScriptInfo scriptInfo) { writeMethodTrait("function get", trait, false); } /** * Traverse a setter trait of a script */ protected void traverseScriptSetterTrait (Trait trait, ScriptInfo scriptInfo) { writeMethodTrait("function set", trait, false); } /** * Traverse a function trait of a script */ protected void traverseScriptFunctionTrait (Trait trait, ScriptInfo scriptInfo) { writeMethodTrait("function", trait, false); } /** * Traverse a class trait of a script */ protected void traverseScriptClassTrait (Trait trait, ScriptInfo scriptInfo) { ClassInfo ci = (ClassInfo) trait.getAttr(Trait.TRAIT_CLASS); int classIndex = getClassId(ci); ClassVisitor cv = getDefinedClasses().get(classIndex); InstanceInfo iinfo = cv.getInstanceInfo(); traverseScriptClassTrait(classIndex, iinfo, ci, trait, scriptInfo); } /** * Traverse a class trait of a script */ protected void traverseScriptClassTrait (int classId, InstanceInfo instanceInfo, ClassInfo classInfo, Trait trait, ScriptInfo scriptInfo) { printer.println(""); int slotId = 0; if( trait.hasAttr(Trait.TRAIT_SLOT )) slotId = trait.getIntAttr(Trait.TRAIT_SLOT); printer.println("// class_id=" + classId + " slot_id=" + String.valueOf(slotId)); String def; if (instanceInfo.isInterface()) { def = "interface"; } else { def = "class"; if (!instanceInfo.isSealed()) def = "dynamic " + def; if (instanceInfo.isFinal()) def = "final " + def; } writeMetaData(trait); printer.println(nsQualifierForName(trait.getName()) + def + " " + nameToString(trait.getName()) + " extends " + nameToString(instanceInfo.superName)); if (instanceInfo.interfaceNames.length > 0) { printer.indent(); List<String> interfaceNames = new ArrayList<String>(); for (Name interfaceName : instanceInfo.interfaceNames) { interfaceNames.add(nameToString(interfaceName)); } printer.println(joinOn(",",interfaceNames)); printer.unindent(); } printer.println("{"); printer.indent(); traverseInstanceInit(instanceInfo.iInit, instanceInfo, trait, scriptInfo); traverseInstanceTraits(instanceInfo.traits); traverseClassInit(classInfo.cInit, classInfo, trait, scriptInfo); traverseClassTraits(classInfo.classTraits); printer.unindent(); printer.println("}"); } /** * Traverse the Script init method */ protected void traverseScriptInit (MethodInfo init, ScriptInfo scriptInfo, int scriptId) { printer.println(""); writeMethodInfo("", "script" + scriptId + "$init", "function", init, false, false, false); } /** * Traverse an instance init method */ protected void traverseInstanceInit (MethodInfo init, InstanceInfo instanceInfo, Trait classTrait, ScriptInfo scriptInfo) { printer.println(""); printer.println("// method_id=" + getMethodInfos().getId(instanceInfo.iInit)); writeMethodInfo("public ", nameToString(classTrait.getName()), "function", init, false, false, false); } /** * Traverse a slot trait of an instance info */ protected void traverseInstanceSlotTrait (Trait trait) { writeSlotTrait("var", trait, false); } /** * Traverse a const trait of an instance info */ protected void traverseInstanceConstTrait (Trait trait) { writeSlotTrait("const", trait, false); } /** * Traverse a method trait of an instance info */ protected void traverseInstanceMethodTrait (Trait trait) { writeMethodTrait("function", trait, false); } /** * Traverse a getter trait of an instance info */ protected void traverseInstanceGetterTrait (Trait trait) { writeMethodTrait("function get", trait, false); } /** * Traverse a setter trait of an instance info */ protected void traverseInstanceSetterTrait (Trait trait) { writeMethodTrait("function set", trait, false); } /** * Traverse a function trait of an instance info */ protected void traverseInstanceFunctionTrait (Trait trait) { writeMethodTrait("function", trait, false); } /** * Traverse a class init method */ protected void traverseClassInit (MethodInfo init, ClassInfo classInfo, Trait classTrait, ScriptInfo scriptInfo) { printer.println(""); //printer.println("// method_id=" + classInfo.init_index) writeMethodInfo("public ", nameToString(classTrait.getName()) + "$", "function", init, true, false, false); } /** * Traverse a slot trait of a class info */ protected void traverseClassSlotTrait (Trait trait) { writeSlotTrait("var", trait, true); } /** * Traverse a const trait of a class info */ protected void traverseClassConstTrait (Trait trait) { writeSlotTrait("const", trait, true); } /** * Traverse a method trait of a class info */ protected void traverseClassMethodTrait (Trait trait) { writeMethodTrait("function", trait, true); } /** * Traverse a getter trait of a class info */ protected void traverseClassGetterTrait (Trait trait) { writeMethodTrait("function get", trait, true); } /** * Traverse a setter trait of a class info */ protected void traverseClassSetterTrait (Trait trait) { writeMethodTrait("function set", trait, true); } /** * Traverse a function trait of a class info */ protected void traverseClassFunctionTrait (Trait trait) { writeMethodTrait("function", trait, true); } /** * Write out the metadata for a given Trait */ private void writeMetaData (Trait t) { if (!t.hasMetadata()) return; for (Metadata mid : t.getMetadata()) { List<String> entries = new Vector<String>(); String[] keys = mid.getKeys(); for (int i = 0; i < keys.length; ++i) { String key = keys[i]; String value = mid.getValues()[i]; if (key == null || key.length() == 0) entries.add("\"" + value + "\""); else entries.add(key + "=\"" + value + "\""); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < entries.size(); ++i) { sb.append(entries.get(i)); if (i < entries.size() - 1) sb.append(", "); } if (sortOption && mid.getName().contains("_definition_help")) printer.println("[" + mid.getName() + "(xxxx)]"); else printer.println("[" + mid.getName() + "(" + sb.toString() + ")]"); } } /** * Write out the method info for an anonymous method */ private void writeAnonMethodInfo (MethodInfo mi, int id) { if (dumpedMethods.contains(mi)) return; printer.println(""); printer.println("// " + id + " " + mi.getMethodName()); writeMethodInfo("", "", "function ", mi, false, false, false); } /** * Write out a slot trait * * @param kindStr the kind of trait (var or const) * @param t the trait * @param isStatic whether the trait is static or not */ private void writeSlotTrait (String kindStr, Trait t, boolean isStatic) { printer.println(""); String qual = nsQualifierForName(t.getName()); String nameStr = nameToString(t.getName()); Object value = null; if( t.hasAttr(Trait.SLOT_VALUE) ) value = t.getAttr(Trait.SLOT_VALUE); String valueStr = ""; if (value instanceof String) valueStr = " = \"" + value + "\""; else if (value instanceof Namespace) valueStr = " = " + ((Namespace) value).getName(); else if (value == ABCConstants.NULL_VALUE) valueStr = " = null"; else if (value == ABCConstants.UNDEFINED_VALUE) valueStr = ""; else if (value != null) valueStr = " = " + value.toString(); String staticStr = isStatic ? "static " : ""; writeMetaData(t); //printer.println("// name_id=" + t.name_index + " slot_id=" + t.slot_id) printer.println(qual + staticStr + kindStr + " " + nameStr + ":" + nameToString((Name) t.getAttr(Trait.TRAIT_TYPE)) + valueStr); } /** * Generate a string for displaying a lookupswitch instruction * * @param inst the lookupswitch instruction * @param mb the method body * @param blockNames a map of IBasicBlock to display name of block * @param cfg the control flow graph for the current method * @return a string to use to display the lookupswitch istruction */ private String stringForLookupSwitch (Instruction inst, MethodBodyInfo mb, Map<IBasicBlock, String> blockNames, IFlowgraph cfg) { int case_size = inst.getOperandCount() - 1; // Last label is the default String defaultStr = "default: " + blockNames.get(cfg.getBlock((Label) inst.getOperand(case_size))); String maxCaseStr = "maxcase: " + case_size; List<String> result = new Vector<String>(); result.add(defaultStr); result.add(maxCaseStr); for (int i = 0; i < case_size; ++i) { result.add(blockNames.get(cfg.getBlock((Label) inst.getOperand(i)))); } return joinOn(" ", result); } /** * Write out a method info, and it's corresponding body */ private void writeMethodInfo (String qualStr, String nameStr, String kindStr, MethodInfo methodInfo, boolean isStatic, boolean isOverride, boolean isFinal) { dumpedMethods.add(methodInfo); List<String> paramTypeStrings = new Vector<String>(); for (Name paramTypeName : methodInfo.getParamTypes()) paramTypeStrings.add(nameToString(paramTypeName)); String staticStr = isStatic ? "static " : ""; String overrideStr = isOverride ? "override " : ""; String nativeStr = methodInfo.isNative() ? "native " : ""; String finalStr = isFinal ? "final " : ""; printer.println(qualStr + staticStr + nativeStr + finalStr + overrideStr + kindStr + " " + nameStr + "(" + joinOn(",", paramTypeStrings) + "):" + nameToString(methodInfo.getReturnType())); MethodBodyInfo mb = getMethodBodyForMethodInfo(methodInfo); if (mb != null) { printer.println("{"); printer.indent(); TablePrinter tablePrinter = new TablePrinter(3, 2); tablePrinter.addRow(new String[]{"//", "derivedName", methodInfo.getMethodName()}); tablePrinter.addRow(new String[]{"//", "method_info", String.valueOf(getMethodInfos().getId(mb.getMethodInfo()))}); tablePrinter.addRow(new String[]{"//", "max_stack", String.valueOf(mb.max_stack)}); tablePrinter.addRow(new String[]{"//", "max_regs", String.valueOf(mb.max_local)}); tablePrinter.addRow(new String[]{"//", "scope_depth", String.valueOf(mb.initial_scope)}); tablePrinter.addRow(new String[]{"//", "max_scope", String.valueOf(mb.max_scope)}); tablePrinter.addRow(new String[]{"//", "code_length", String.valueOf(mb.code_len)}); //tablePrinter.addRow(["//", "code_offset", mb.code_offset]); tablePrinter.print(printer); if (mb.getTraits() != null && mb.getTraits().getTraitCount() > 0) { printer.println("activation_traits {"); printer.indent(); for (Trait trait : mb.getTraits()) { //var kindStr : String; switch (trait.getKind()) { case TRAIT_Var: kindStr = "var"; break; case TRAIT_Const: kindStr = "const"; break; default: throw new Error("Illegal activation trait in " + methodInfo.getMethodName()); } writeSlotTrait(kindStr, trait, false); } printer.unindent(); printer.println("}"); } IFlowgraph cfg = mb.getCfg(); Map<IBasicBlock, String> blockNames = new HashMap<IBasicBlock, String>(); int i = 0; for (IBasicBlock block : cfg.getBlocksInEntryOrder()) { blockNames.put(block, "bb" + i++); } int offset = 0; for (IBasicBlock block : cfg.getBlocksInEntryOrder()) { printer.println(blockNames.get(block)); printer.indent(); // TODO: preds //printer.println("preds=[" + block.getPredeccessor()mb.blocks[i].getPredIds(mb.blocks).join(", ") + "]"); Collection<? extends IBasicBlock> succs = block.getSuccessors(); List<String> succNames = new ArrayList<String>(); for (IBasicBlock s : succs) succNames.add(blockNames.get(s)); if (!sortOption) printer.println("succs=[" + joinOn(",", succNames) + "]"); /* // TODO: implement this with FrameModelEncoder if(mb.blocks[i].state != null) { printer.println("verification = " + (mb.blocks[i].state.verifyError == null ? "ok" : "failed: " + mb.blocks[i].state.verifyError)); } */ tablePrinter = new TablePrinter(4, 2); for (int j = 0; j < block.size(); j++) { Instruction inst = block.get(j); String constantStr = ""; if (inst.hasOperands() && inst.getOperand(0) instanceof Name) { constantStr = nameToString((Name) inst.getOperand(0)); } else if (inst.isBranch() && inst.getOpcode() != OP_lookupswitch) { constantStr = blockNames.get(cfg.getBlock((Label) inst.getOperand(0))); } else { switch (inst.getOpcode()) { case OP_debugfile: if (sortOption) { String fileName = (String) inst.getOperand(0); fileName = fileName.substring(fileName.indexOf(";")); fileName = fileName.replace("\\", "/"); constantStr = "\"" + stringToEscapedString(fileName) + "\""; } else constantStr = "\"" + stringToEscapedString((String) inst.getOperand(0)) + "\""; break; case OP_pushstring: constantStr = "\"" + stringToEscapedString((String) inst.getOperand(0)) + "\""; break; case OP_lookupswitch: constantStr = stringForLookupSwitch(inst, mb, blockNames, cfg); break; } } tablePrinter.addRow(new String[]{offset + " ", Instruction.decodeOp(inst.getOpcode()), constantStr, inst.isImmediate() ? String.valueOf(inst.getImmediate()) : "" // TODO : Use FrameModelEncoder to keep track // TODO: of stack/local values //(inst.getStackDepth() == null ? "" : "// stack: " + inst.getStackDepth()), //(inst.getState() == null ? "" : "// stack["+inst.getState().stackDepth+"]: " + inst.getState().stackTypeString()), //(inst.getState() == null ? "" : "// locals: " + inst.getState().localsTypeString()), //inst.getVerifyError() }); offset++; } tablePrinter.print(printer); printer.unindent(); } printer.unindent(); printer.println("}"); if (mb.getExceptions().size() > 0) { tablePrinter = new TablePrinter(7, 2); tablePrinter.addRow(new String[]{"//", "exception", "start", "end", "target", "type string", "name string"}); for (i = 0; i < mb.getExceptions().size(); i++) { ExceptionInfo exception = mb.getExceptions().get(i); tablePrinter.addRow(new String[]{"//", String.valueOf(i), String.valueOf(exception.getFrom().getPosition()), String.valueOf(exception.getTo().getPosition()), String.valueOf(exception.getTarget().getPosition()), nameToString(exception.getExceptionType()), nameToString(exception.getCatchVar())}); } tablePrinter.print(printer); printer.println(""); } } } /** * Write out a method trait */ private void writeMethodTrait (String kindStr, Trait t, boolean isStatic) { String qual = nsQualifierForName(t.getName()); String nameStr = nameToString(t.getName()); MethodInfo methodInfo = (MethodInfo) t.getAttr(Trait.TRAIT_METHOD); printer.println(""); writeMetaData(t); //printer.println("// name_id=" + t.name_index + " method_id=" + t.method_info + " disp_id=" + t.disp_id) writeMethodInfo(qual, nameStr, kindStr, methodInfo, isStatic, t.getBooleanAttr(Trait.TRAIT_OVERRIDE), t.getBooleanAttr(Trait.TRAIT_FINAL)); } /** * Helper interface to pass around writers */ interface IWriteFunction { void write (Object v, int index); } /** * Write an array using the given IWriteFunction */ private <T> void writeIterable (Iterable<T> p, IWriteFunction writefunc) { int i = 0; for (Object v : p) { writefunc.write(v, i++); } } /** * Helper class to manage indenting */ private static class IndentingPrinter { private PrintWriter delegate; private String currentIndent; private int indentIncrement; public IndentingPrinter (PrintWriter delegate, int initialIndent, int indentIncrement) { this.delegate = delegate; this.currentIndent = makeIndentStr(initialIndent); this.indentIncrement = indentIncrement; } private static String makeIndentStr (int indent) { String result; StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; ++i) sb.append(" "); result = sb.toString(); return result; } public void println (String s) { if (s.length() > 0) s = currentIndent + s; delegate.println(s); } public void indent () { int newIndent = currentIndent.length() + indentIncrement; currentIndent = makeIndentStr(newIndent); } public void unindent () { int newIndent = currentIndent.length() - indentIncrement; currentIndent = makeIndentStr(newIndent); } public void flush () { delegate.flush(); } } /** * Helper class to display nicely formatted tables of data */ public static class TablePrinter { public TablePrinter (int nCols, int minPadding) { cols = nCols; this.minPadding = minPadding; m_rows = new Vector<Row>(); } public void addRow (String[] r) { if (r.length != cols) throw new Error("Invalid row"); m_rows.add(new Row(r)); } public void print (IndentingPrinter p) { int[] colWidths = new int[cols]; int i; for (i = 0; i < cols; ++i) colWidths[i] = 0; for (Row r : m_rows) r.measure(colWidths, minPadding); for (Row r : m_rows) r.print(p, colWidths); } private int cols; private int minPadding; private Vector<Row> m_rows; private class Row { private String[] cells; public Row (String[] cells) { this.cells = cells; } public void measure (int[] colWidths, int minPadding) { for (int i = 0; i < cells.length; ++i) colWidths[i] = Math.max(colWidths[i], getRowItemStr(i).length() + minPadding); } public void print (IndentingPrinter p, int[] colWidths) { String rowStr = ""; for (int i = 0; i < cells.length; ++i) rowStr += padString(getRowItemStr(i), colWidths[i]); p.println(rowStr); } private String getRowItemStr (int i) { if (cells[i] == null) return "null"; if (i < cells.length) return cells[i]; return "error - out of range " + i; } private String padString (String s, int minLength) { while (s.length() < minLength) s += " "; return s; } } } /** * Entry point for testing * <p/> * Spits out the dump to System.out */ public static void main (String[] args) throws Exception { for (String arg : args) { File f = new File(arg); if (f.exists()) { ABCParser parser = new ABCParser(new BufferedInputStream(new FileInputStream(f))); PoolingABCVisitor printer = new ABCDumpVisitor(new PrintWriter(System.out), false); parser.parseABC(printer); } } } /** * This implementation will dump out a text representation of the ABC to * the PrintWriter that was passed in on construction. */ public void visitEnd () { write(); printer.flush(); } /** * Stringify a collection of items. * @param separator the separator string. * @param items the items to stringify. * @return the items, listed out with a separator in between. */ private static String joinOn(String separator, Collection<? extends Object> items) { StringBuilder result = new StringBuilder(); for ( Object item: items ) { if ( result.length() > 0 ) result.append(separator); result.append(item); } return result.toString(); } }