/* Soot - a J*va Optimization Framework
*
*
* Copyright (C) 1999 Patrick Lam, Patrick Pominville and Raja Vallee-Rai
* Copyright (C) 2004 Jennifer Lhotak, Ondrej Lhotak
*
* 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 2.1 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; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the Sable Research Group and others 1997-1999.
* See the 'credits' file distributed with Soot for the complete list of
* contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
*/
package soot;
import soot.options.*;
import soot.tagkit.*;
import soot.jimple.*;
import soot.toolkits.graph.*;
import java.util.*;
import java.io.*;
import soot.baf.*;
public abstract class AbstractJasminClass
{
protected Map unitToLabel;
protected Map<Local, Integer> localToSlot;
protected Map<Unit, Integer> subroutineToReturnAddressSlot;
protected List<String> code;
protected boolean isEmittingMethodCode;
protected int labelCount;
protected boolean isNextGotoAJsr;
protected int returnAddressSlot;
protected int currentStackHeight = 0;
protected int maxStackHeight = 0;
protected Map<Local, Object> localToGroup;
protected Map<Object, Integer> groupToColorCount;
protected Map<Local, Integer> localToColor;
protected Map<Block, Integer> blockToStackHeight = new HashMap<Block, Integer>(); // maps a block to the stack height upon entering it
protected Map<Block, Integer> blockToLogicalStackHeight = new HashMap<Block, Integer>(); // maps a block to the logical stack height upon entering it
public static String slashify(String s)
{
return s.replace('.', '/');
}
public static int sizeOfType(Type t)
{
if(t instanceof DoubleWordType || t instanceof LongType || t instanceof DoubleType)
return 2;
else if(t instanceof VoidType)
return 0;
else
return 1;
}
public static int argCountOf(SootMethodRef m)
{
int argCount = 0;
Iterator typeIt = m.parameterTypes().iterator();
while(typeIt.hasNext())
{
Type t = (Type) typeIt.next();
argCount += sizeOfType(t);
}
return argCount;
}
public static String jasminDescriptorOf(Type type)
{
TypeSwitch sw;
type.apply(sw = new TypeSwitch()
{
public void caseBooleanType(BooleanType t)
{
setResult("Z");
}
public void caseByteType(ByteType t)
{
setResult("B");
}
public void caseCharType(CharType t)
{
setResult("C");
}
public void caseDoubleType(DoubleType t)
{
setResult("D");
}
public void caseFloatType(FloatType t)
{
setResult("F");
}
public void caseIntType(IntType t)
{
setResult("I");
}
public void caseLongType(LongType t)
{
setResult("J");
}
public void caseShortType(ShortType t)
{
setResult("S");
}
public void defaultCase(Type t)
{
throw new RuntimeException("Invalid type: " + t);
}
public void caseArrayType(ArrayType t)
{
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < t.numDimensions; i++)
buffer.append("[");
setResult(buffer.toString() + jasminDescriptorOf(t.baseType));
}
public void caseRefType(RefType t)
{
setResult("L" + t.getClassName().replace('.', '/') + ";");
}
public void caseVoidType(VoidType t)
{
setResult("V");
}
});
return (String) sw.getResult();
}
public static String jasminDescriptorOf(SootMethodRef m)
{
StringBuffer buffer = new StringBuffer();
buffer.append("(");
// Add methods parameters
{
Iterator typeIt = m.parameterTypes().iterator();
while(typeIt.hasNext())
{
Type t = (Type) typeIt.next();
buffer.append(jasminDescriptorOf(t));
}
}
buffer.append(")");
buffer.append(jasminDescriptorOf(m.returnType()));
return buffer.toString();
}
protected void emit(String s)
{
okayEmit(s);
}
protected void okayEmit(String s)
{
if(isEmittingMethodCode && !s.endsWith(":"))
code.add(" " + s);
else
code.add(s);
}
private String getVisibilityAnnotationAttr(VisibilityAnnotationTag tag){
StringBuffer sb = new StringBuffer();
if (tag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE){
sb.append(".runtime_visible_annotation\n");
}
else if(tag.getVisibility() == AnnotationConstants.RUNTIME_INVISIBLE){
sb.append(".runtime_invisible_annotation\n");
} else {
//source level annotation
return "";
}
if (tag.hasAnnotations()){
Iterator<AnnotationTag> it = tag.getAnnotations().iterator();
while (it.hasNext()){
AnnotationTag annot = it.next();
sb.append(".annotation ");
sb.append(soot.util.StringTools.getQuotedStringOf(annot.getType())+"\n");
for (int i = 0; i < annot.getNumElems(); i++){
sb.append(getElemAttr(annot.getElemAt(i)));
}
sb.append(".end .annotation\n");
}
}
sb.append(".end .annotation_attr\n");
return sb.toString();
}
private String getVisibilityParameterAnnotationAttr(VisibilityParameterAnnotationTag tag){
StringBuffer sb = new StringBuffer();
sb.append(".param ");
if (tag.getKind() == AnnotationConstants.RUNTIME_VISIBLE){
sb.append(".runtime_visible_annotation\n");
}
else {
sb.append(".runtime_invisible_annotation\n");
}
ArrayList<VisibilityAnnotationTag> vis_list = tag.getVisibilityAnnotations();
if (vis_list != null){
Iterator<VisibilityAnnotationTag> it = vis_list.iterator();
while (it.hasNext()){
sb.append(getVisibilityAnnotationAttr(it.next()));
}
}
sb.append(".end .param\n");
return sb.toString();
}
private String getElemAttr(AnnotationElem elem){
StringBuffer result = new StringBuffer(".elem ");
switch (elem.getKind()){
case 'Z': {
result.append(".bool_kind ");
result.append("\"" + elem.getName() + "\" ");
if (elem instanceof AnnotationIntElem) {
result.append(((AnnotationIntElem) elem).getValue());
} else {
if (((AnnotationBooleanElem) elem).getValue()) {
result.append(1);
} else {
result.append(0);
}
}
result.append("\n");
break;
}
case 'S': {
result.append(".short_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(((AnnotationIntElem)elem).getValue());
result.append("\n");
break;
}
case 'B': {
result.append(".byte_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(((AnnotationIntElem)elem).getValue());
result.append("\n");
break;
}
case 'C': {
result.append(".char_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(((AnnotationIntElem)elem).getValue());
result.append("\n");
break;
}
case 'I': {
result.append(".int_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(((AnnotationIntElem)elem).getValue());
result.append("\n");
break;
}
case 'J': {
result.append(".long_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(((AnnotationLongElem)elem).getValue());
result.append("\n");
break;
}
case 'F': {
result.append(".float_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(Float.floatToRawIntBits(((AnnotationFloatElem)elem).getValue()));
result.append("\n");
break;
}
case 'D': {
result.append(".doub_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(Double.doubleToRawLongBits(((AnnotationDoubleElem)elem).getValue()));
result.append("\n");
break;
}
case 's': {
result.append(".str_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(soot.util.StringTools.getQuotedStringOf(((AnnotationStringElem)elem).getValue()));
result.append("\n");
break;
}
case 'e': {
result.append(".enum_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(soot.util.StringTools.getQuotedStringOf(((AnnotationEnumElem)elem).getTypeName()));
result.append(" ");
result.append(soot.util.StringTools.getQuotedStringOf(((AnnotationEnumElem)elem).getConstantName()));
result.append("\n");
break;
}
case 'c': {
result.append(".cls_kind ");
result.append("\""+elem.getName()+"\" ");
result.append(soot.util.StringTools.getQuotedStringOf(((AnnotationClassElem)elem).getDesc()));
result.append("\n");
break;
}
case '[': {
result.append(".arr_kind ");
result.append("\""+elem.getName()+"\" ");
AnnotationArrayElem arrayElem = (AnnotationArrayElem)elem;
result.append("\n");
for (int i = 0; i < arrayElem.getNumValues(); i++){
//result.append("\n");
result.append(getElemAttr(arrayElem.getValueAt(i)));
}
result.append(".end .arr_elem\n");
break;
}
case '@': {
result.append(".ann_kind ");
result.append("\""+elem.getName()+"\"\n");
AnnotationTag annot = ((AnnotationAnnotationElem)elem).getValue();
result.append(".annotation ");
result.append(soot.util.StringTools.getQuotedStringOf(annot.getType())+"\n");
for (int i = 0; i < annot.getNumElems(); i++){
result.append(getElemAttr(annot.getElemAt(i)));
}
result.append(".end .annotation\n");
result.append(".end .annot_elem\n");
break;
}
default : {
throw new RuntimeException("Unknown Elem Attr Kind: "+elem.getKind());
}
}
return result.toString();
}
public AbstractJasminClass(SootClass sootClass)
{
if(Options.v().time())
Timers.v().buildJasminTimer.start();
if(Options.v().verbose())
G.v().out.println("[" + sootClass.getName() + "] Constructing baf.JasminClass...");
code = new LinkedList<String>();
// Emit the header
{
int modifiers = sootClass.getModifiers();
if ((sootClass.getTag("SourceFileTag") != null) && (!Options.v().no_output_source_file_attribute())){
String srcName = ((SourceFileTag)sootClass.getTag("SourceFileTag")).getSourceFile();
emit(".source "+soot.util.StringTools.getEscapedStringOf(srcName));
}
if(Modifier.isInterface(modifiers))
{
modifiers -= Modifier.INTERFACE;
emit(".interface " + Modifier.toString(modifiers) + " " + slashify(sootClass.getName()));
}
else
emit(".class " + Modifier.toString(modifiers) + " " + slashify(sootClass.getName()));
if(sootClass.hasSuperclass())
emit(".super " + slashify(sootClass.getSuperclass().getName()));
else
emit(".no_super");
emit("");
}
// Emit the interfaces
{
Iterator interfaceIt = sootClass.getInterfaces().iterator();
while(interfaceIt.hasNext())
{
SootClass inter = (SootClass) interfaceIt.next();
emit(".implements " + slashify(inter.getName()));
}
/* why do this????
if(sootClass.getInterfaceCount() != 0)
emit("");*/
}
// emit class attributes.
Iterator it = sootClass.getTags().iterator();
while(it.hasNext()) {
Tag tag = (Tag) it.next();
if(tag instanceof Attribute)
emit(".class_attribute " + tag.getName() + " \"" + new String(Base64.encode(((Attribute)tag).getValue()))+"\"");
/*else {
emit("");
}*/
}
// emit synthetic attributes
if (sootClass.hasTag("SyntheticTag")){
emit(".synthetic\n");
}
// emit inner class attributes
if (sootClass.hasTag("InnerClassAttribute")){
if (!Options.v().no_output_inner_classes_attribute()){
emit(".inner_class_attr ");
Iterator<Tag> innersIt = ((InnerClassAttribute)sootClass.getTag("InnerClassAttribute")).getSpecs().iterator();
while (innersIt.hasNext()){
InnerClassTag ict = (InnerClassTag)innersIt.next();
//System.out.println("inner class tag: "+ict);
emit(".inner_class_spec_attr "+
"\""+ict.getInnerClass()+"\" "+
"\""+ict.getOuterClass()+"\" "+
"\""+ict.getShortName()+"\" "+
Modifier.toString(ict.getAccessFlags())+" "+
".end .inner_class_spec_attr");
}
emit(".end .inner_class_attr\n");
}
}
if (sootClass.hasTag("EnclosingMethodTag")){
String encMeth = ".enclosing_method_attr ";
EnclosingMethodTag eMethTag = (EnclosingMethodTag)sootClass.getTag("EnclosingMethodTag");
encMeth += "\""+eMethTag.getEnclosingClass()+"\" ";
encMeth += "\""+eMethTag.getEnclosingMethod()+"\" ";
encMeth += "\""+eMethTag.getEnclosingMethodSig()+"\"\n";
emit(encMeth);
}
// emit deprecated attributes
if (sootClass.hasTag("DeprecatedTag")){
emit(".deprecated\n");
}
if (sootClass.hasTag("SignatureTag")){
String sigAttr = ".signature_attr ";
SignatureTag sigTag = (SignatureTag)sootClass.getTag("SignatureTag");
sigAttr += "\""+sigTag.getSignature()+"\"\n";
emit(sigAttr);
}
Iterator vit = sootClass.getTags().iterator();
while (vit.hasNext()){
Tag t = (Tag)vit.next();
if (t.getName().equals("VisibilityAnnotationTag")){
emit(getVisibilityAnnotationAttr((VisibilityAnnotationTag)t));
}
}
// Emit the fields
{
Iterator fieldIt = sootClass.getFields().iterator();
while(fieldIt.hasNext())
{
SootField field = (SootField) fieldIt.next();
String fieldString = ".field " + Modifier.toString(field.getModifiers()) + " " + "\"" + field.getName() + "\"" + " " + jasminDescriptorOf(field.getType());
if (field.hasTag("StringConstantValueTag")){
fieldString += " = ";
fieldString += soot.util.StringTools.getQuotedStringOf(((StringConstantValueTag)field.getTag("StringConstantValueTag")).getStringValue());
}
else if (field.hasTag("IntegerConstantValueTag")){
fieldString += " = ";
fieldString += ((IntegerConstantValueTag)field.getTag("IntegerConstantValueTag")).getIntValue();
}
else if (field.hasTag("LongConstantValueTag")){
fieldString += " = ";
fieldString += ((LongConstantValueTag)field.getTag("LongConstantValueTag")).getLongValue();
}
else if (field.hasTag("FloatConstantValueTag")){
fieldString += " = ";
float val = ((FloatConstantValueTag)field.getTag("FloatConstantValueTag")).getFloatValue();
fieldString += Float.floatToRawIntBits(val);
}
else if (field.hasTag("DoubleConstantValueTag")){
fieldString += " = ";
double val = ((DoubleConstantValueTag)field.getTag("DoubleConstantValueTag")).getDoubleValue();
fieldString += Double.doubleToRawLongBits(val);
}
if (field.hasTag("SyntheticTag")){
fieldString +=" .synthetic";
}
fieldString +="\n";
if (field.hasTag("DeprecatedTag")){
fieldString +=".deprecated\n";
}
if (field.hasTag("SignatureTag")){
fieldString += ".signature_attr ";
SignatureTag sigTag = (SignatureTag)field.getTag("SignatureTag");
fieldString += "\""+sigTag.getSignature()+"\"\n";
}
Iterator vfit = field.getTags().iterator();
while (vfit.hasNext()){
Tag t = (Tag)vfit.next();
if (t.getName().equals("VisibilityAnnotationTag")){
fieldString += getVisibilityAnnotationAttr((VisibilityAnnotationTag)t);
}
}
emit(fieldString);
Iterator attributeIt = field.getTags().iterator();
while(attributeIt.hasNext()) {
Tag tag = (Tag) attributeIt.next();
if(tag instanceof Attribute)
emit(".field_attribute " + tag.getName() + " \"" + new String(Base64.encode(((Attribute)tag).getValue())) +"\"");
}
}
if(sootClass.getFieldCount() != 0)
emit("");
}
// Emit the methods
{
Iterator methodIt = sootClass.methodIterator();
while(methodIt.hasNext())
{
emitMethod((SootMethod) methodIt.next());
emit("");
}
}
if(Options.v().time())
Timers.v().buildJasminTimer.end();
}
protected void assignColorsToLocals(Body body)
{
if(Options.v().verbose())
G.v().out.println("[" + body.getMethod().getName() +
"] Assigning colors to locals...");
if(Options.v().time())
Timers.v().packTimer.start();
localToGroup = new HashMap<Local, Object>(body.getLocalCount() * 2 + 1, 0.7f);
groupToColorCount = new HashMap<Object, Integer>(body.getLocalCount() * 2 + 1, 0.7f);
localToColor = new HashMap<Local, Integer>(body.getLocalCount() * 2 + 1, 0.7f);
// Assign each local to a group, and set that group's color count to 0.
{
Iterator localIt = body.getLocals().iterator();
while(localIt.hasNext())
{
Local l = (Local) localIt.next();
Object g;
if(sizeOfType(l.getType()) == 1)
g = IntType.v();
else
g = LongType.v();
localToGroup.put(l, g);
if(!groupToColorCount.containsKey(g))
{
groupToColorCount.put(g, new Integer(0));
}
}
}
// Assign colors to the parameter locals.
{
Iterator codeIt = body.getUnits().iterator();
while(codeIt.hasNext())
{
Stmt s = (Stmt) codeIt.next();
if(s instanceof IdentityStmt &&
((IdentityStmt) s).getLeftOp() instanceof Local)
{
Local l = (Local) ((IdentityStmt) s).getLeftOp();
Object group = localToGroup.get(l);
int count = groupToColorCount.get(group).intValue();
localToColor.put(l, new Integer(count));
count++;
groupToColorCount.put(group, new Integer(count));
}
}
}
}
protected void emitMethod(SootMethod method)
{
if (method.isPhantom())
return;
// Emit prologue
emit(".method " + Modifier.toString(method.getModifiers()) + " " +
method.getName() + jasminDescriptorOf(method.makeRef()));
Iterator<SootClass> throwsIt = method.getExceptions().iterator();
while (throwsIt.hasNext()){
SootClass exceptClass = throwsIt.next();
emit(".throws "+exceptClass.getName());
}
if (method.hasTag("SyntheticTag")){
emit(".synthetic");
}
if (method.hasTag("DeprecatedTag")){
emit(".deprecated");
}
if (method.hasTag("SignatureTag")){
String sigAttr = ".signature_attr ";
SignatureTag sigTag = (SignatureTag)method.getTag("SignatureTag");
sigAttr += "\""+sigTag.getSignature()+"\"";
emit(sigAttr);
}
if (method.hasTag("AnnotationDefaultTag")){
String annotDefAttr = ".annotation_default ";
AnnotationDefaultTag annotDefTag = (AnnotationDefaultTag)method.getTag("AnnotationDefaultTag");
annotDefAttr += getElemAttr(annotDefTag.getDefaultVal());
annotDefAttr += ".end .annotation_default";
emit(annotDefAttr);
}
Iterator vit = method.getTags().iterator();
while (vit.hasNext()){
Tag t = (Tag)vit.next();
if (t.getName().equals("VisibilityAnnotationTag")){
emit(getVisibilityAnnotationAttr((VisibilityAnnotationTag)t));
}
if (t.getName().equals("VisibilityParameterAnnotationTag")){
emit(getVisibilityParameterAnnotationAttr((VisibilityParameterAnnotationTag)t));
}
}
if(method.isConcrete())
{
if(!method.hasActiveBody())
throw new RuntimeException("method: " + method.getName() + " has no active body!");
else
emitMethodBody(method);
}
// Emit epilogue
emit(".end method");
Iterator it = method.getTags().iterator();
while(it.hasNext()) {
Tag tag = (Tag) it.next();
if(tag instanceof Attribute)
emit(".method_attribute " + tag.getName() + " \"" + new String(Base64.encode(tag.getValue())) +"\"");
}
}
protected abstract void emitMethodBody(SootMethod method);
public void print(PrintWriter out)
{
Iterator<String> it = code.iterator();
while(it.hasNext())
out.println(it.next());
}
}