blob: 4c860606b1de1ec3d5d667cddbaca0eb443de386 [file] [log] [blame]
/*
*
* 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.royale.abc.print;
import org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.ABCParser;
import org.apache.royale.abc.PoolingABCVisitor;
import org.apache.royale.abc.graph.IBasicBlock;
import org.apache.royale.abc.graph.IFlowgraph;
import org.apache.royale.abc.semantics.ClassInfo;
import org.apache.royale.abc.semantics.ExceptionInfo;
import org.apache.royale.abc.semantics.InstanceInfo;
import org.apache.royale.abc.semantics.Instruction;
import org.apache.royale.abc.semantics.Label;
import org.apache.royale.abc.semantics.Metadata;
import org.apache.royale.abc.semantics.MethodBodyInfo;
import org.apache.royale.abc.semantics.MethodInfo;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.semantics.Namespace;
import org.apache.royale.abc.semantics.Nsset;
import org.apache.royale.abc.semantics.ScriptInfo;
import org.apache.royale.abc.semantics.Trait;
import org.apache.royale.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.royale.abc.ABCConstants.CONSTANT_Multiname;
import static org.apache.royale.abc.ABCConstants.CONSTANT_MultinameA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_MultinameL;
import static org.apache.royale.abc.ABCConstants.CONSTANT_Namespace;
import static org.apache.royale.abc.ABCConstants.CONSTANT_PackageInternalNs;
import static org.apache.royale.abc.ABCConstants.CONSTANT_PackageNs;
import static org.apache.royale.abc.ABCConstants.CONSTANT_PrivateNs;
import static org.apache.royale.abc.ABCConstants.CONSTANT_ProtectedNs;
import static org.apache.royale.abc.ABCConstants.CONSTANT_Qname;
import static org.apache.royale.abc.ABCConstants.CONSTANT_QnameA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_RTQname;
import static org.apache.royale.abc.ABCConstants.CONSTANT_RTQnameA;
import static org.apache.royale.abc.ABCConstants.CONSTANT_StaticProtectedNs;
import static org.apache.royale.abc.ABCConstants.CONSTANT_TypeName;
import static org.apache.royale.abc.ABCConstants.OP_debugfile;
import static org.apache.royale.abc.ABCConstants.OP_lookupswitch;
import static org.apache.royale.abc.ABCConstants.OP_pushstring;
import static org.apache.royale.abc.ABCConstants.TRAIT_Class;
import static org.apache.royale.abc.ABCConstants.TRAIT_Const;
import static org.apache.royale.abc.ABCConstants.TRAIT_Function;
import static org.apache.royale.abc.ABCConstants.TRAIT_Getter;
import static org.apache.royale.abc.ABCConstants.TRAIT_Method;
import static org.apache.royale.abc.ABCConstants.TRAIT_Setter;
import static org.apache.royale.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();
}
}