soot.coffi.Util Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of soot Show documentation
Show all versions of soot Show documentation
A Java Optimization Framework
package soot.coffi;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 1997 Clark Verbrugge
* %%
* This program 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 program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.ArrayType;
import soot.Body;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.G;
import soot.IntType;
import soot.Local;
import soot.LongType;
import soot.Modifier;
import soot.RefType;
import soot.Scene;
import soot.ShortType;
import soot.Singletons;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.SootResolver;
import soot.Type;
import soot.UnknownType;
import soot.VoidType;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.tagkit.AnnotationAnnotationElem;
import soot.tagkit.AnnotationArrayElem;
import soot.tagkit.AnnotationClassElem;
import soot.tagkit.AnnotationConstants;
import soot.tagkit.AnnotationDefaultTag;
import soot.tagkit.AnnotationDoubleElem;
import soot.tagkit.AnnotationElem;
import soot.tagkit.AnnotationEnumElem;
import soot.tagkit.AnnotationFloatElem;
import soot.tagkit.AnnotationIntElem;
import soot.tagkit.AnnotationLongElem;
import soot.tagkit.AnnotationStringElem;
import soot.tagkit.AnnotationTag;
import soot.tagkit.ConstantValueTag;
import soot.tagkit.DeprecatedTag;
import soot.tagkit.DoubleConstantValueTag;
import soot.tagkit.EnclosingMethodTag;
import soot.tagkit.FloatConstantValueTag;
import soot.tagkit.GenericAttribute;
import soot.tagkit.Host;
import soot.tagkit.InnerClassTag;
import soot.tagkit.IntegerConstantValueTag;
import soot.tagkit.LongConstantValueTag;
import soot.tagkit.SignatureTag;
import soot.tagkit.SourceFileTag;
import soot.tagkit.StringConstantValueTag;
import soot.tagkit.SyntheticTag;
import soot.tagkit.VisibilityAnnotationTag;
import soot.tagkit.VisibilityParameterAnnotationTag;
public class Util {
private static final Logger logger = LoggerFactory.getLogger(Util.class);
public Util(Singletons.Global g) {
}
public static Util v() {
return G.v().soot_coffi_Util();
}
private cp_info[] activeConstantPool = null;
private LocalVariableTable_attribute activeVariableTable;
/*
* maps from variable names to local variable slot indexes to soot Locals
*/
private Map> nameToIndexToLocal;
private boolean useFaithfulNaming = false;
/**
* Set the informations relative to the current method body. This method must be called before using getLocalForIndex(...)
* and getLocalForStackOp(...) each time a different current method body is considered.
*/
public void bodySetup(LocalVariableTable_attribute la, LocalVariableTypeTable_attribute lt, cp_info[] ca) {
activeVariableTable = la;
activeConstantPool = ca;
nameToIndexToLocal = null;
}
public void setFaithfulNaming(boolean v) {
useFaithfulNaming = v;
}
public boolean isUsingFaithfulNaming() {
return useFaithfulNaming;
}
public void resolveFromClassFile(SootClass aClass, InputStream is, String filePath, Collection references) {
SootClass bclass = aClass;
String className = bclass.getName();
ClassFile coffiClass = new ClassFile(className);
// Load up class file, and retrieve
// bclass from class manager.
{
boolean success = coffiClass.loadClassFile(is);
if (!success) {
if (!Scene.v().allowsPhantomRefs()) {
throw new RuntimeException("Could not load classfile: " + bclass.getName());
} else {
logger.warn("" + className + " is a phantom class!");
bclass.setPhantomClass();
return;
}
}
CONSTANT_Class_info c = (CONSTANT_Class_info) coffiClass.constant_pool[coffiClass.this_class];
String name = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[c.name_index])).convert();
name = name.replace('/', '.');
if (!name.equals(bclass.getName())) {
throw new RuntimeException(
"Error: class " + name + " read in from a classfile in which " + bclass.getName() + " was expected.");
}
}
// Set modifier
bclass.setModifiers(coffiClass.access_flags & (~0x0020));
// don't want the ACC_SUPER flag, it is
// always supposed to be set
// anyways
// Set superclass
{
if (coffiClass.super_class != 0) {
// This object is not java.lang.Object,
// so must have a super
// class
CONSTANT_Class_info c = (CONSTANT_Class_info) coffiClass.constant_pool[coffiClass.super_class];
String superName = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[c.name_index])).convert();
superName = superName.replace('/', '.');
references.add(RefType.v(superName));
bclass.setSuperclass(SootResolver.v().makeClassRef(superName));
}
}
// Add interfaces to the bclass
{
for (int i = 0; i < coffiClass.interfaces_count; i++) {
CONSTANT_Class_info c = (CONSTANT_Class_info) coffiClass.constant_pool[coffiClass.interfaces[i]];
String interfaceName = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[c.name_index])).convert();
interfaceName = interfaceName.replace('/', '.');
references.add(RefType.v(interfaceName));
SootClass interfaceClass = SootResolver.v().makeClassRef(interfaceName);
interfaceClass.setModifiers(interfaceClass.getModifiers() | Modifier.INTERFACE);
bclass.addInterface(interfaceClass);
}
}
// Add every field to the bclass
for (int i = 0; i < coffiClass.fields_count; i++) {
field_info fieldInfo = coffiClass.fields[i];
String fieldName = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[fieldInfo.name_index])).convert();
String fieldDescriptor = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[fieldInfo.descriptor_index])).convert();
int modifiers = fieldInfo.access_flags;
Type fieldType = jimpleTypeOfFieldDescriptor(fieldDescriptor);
SootField field = Scene.v().makeSootField(fieldName, fieldType, modifiers);
bclass.addField(field);
references.add(fieldType);
// add initialization constant, if any
for (int j = 0; j < fieldInfo.attributes_count; j++) {
// add constant value attributes
if (fieldInfo.attributes[j] instanceof ConstantValue_attribute) {
ConstantValue_attribute attr = (ConstantValue_attribute) fieldInfo.attributes[j];
cp_info cval = coffiClass.constant_pool[attr.constantvalue_index];
ConstantValueTag tag;
switch (cval.tag) {
case cp_info.CONSTANT_Integer:
tag = new IntegerConstantValueTag((int) ((CONSTANT_Integer_info) cval).bytes);
break;
case cp_info.CONSTANT_Float:
// tag = new
// FloatConstantValueTag((int)((CONSTANT_Float_info)cval).bytes);
tag = new FloatConstantValueTag(((CONSTANT_Float_info) cval).convert());
break;
case cp_info.CONSTANT_Long: {
CONSTANT_Long_info lcval = (CONSTANT_Long_info) cval;
tag = new LongConstantValueTag((lcval.high << 32) + lcval.low);
break;
}
case cp_info.CONSTANT_Double: {
CONSTANT_Double_info dcval = (CONSTANT_Double_info) cval;
// tag = new
// DoubleConstantValueTag((dcval.high <<
// 32) +
// dcval.low);
tag = new DoubleConstantValueTag(dcval.convert());
break;
}
case cp_info.CONSTANT_String: {
CONSTANT_String_info scval = (CONSTANT_String_info) cval;
CONSTANT_Utf8_info ucval = (CONSTANT_Utf8_info) coffiClass.constant_pool[scval.string_index];
tag = new StringConstantValueTag(ucval.convert());
break;
}
default:
throw new RuntimeException("unexpected ConstantValue: " + cval);
}
field.addTag(tag);
}
// add synthetic tag
else if (fieldInfo.attributes[j] instanceof Synthetic_attribute) {
field.addTag(new SyntheticTag());
}
// add deprecated tag
else if (fieldInfo.attributes[j] instanceof Deprecated_attribute) {
field.addTag(new DeprecatedTag());
}
// add signature tag
else if (fieldInfo.attributes[j] instanceof Signature_attribute) {
int signature_index = ((Signature_attribute) fieldInfo.attributes[j]).signature_index;
String generic_sig = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[signature_index])).convert();
field.addTag(new SignatureTag(generic_sig));
} else if (fieldInfo.attributes[j] instanceof RuntimeVisibleAnnotations_attribute
|| fieldInfo.attributes[j] instanceof RuntimeInvisibleAnnotations_attribute) {
addAnnotationVisibilityAttribute(field, fieldInfo.attributes[j], coffiClass, references);
} else if (fieldInfo.attributes[j] instanceof Generic_attribute) {
Generic_attribute attr = (Generic_attribute) fieldInfo.attributes[j];
String name = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[attr.attribute_name])).convert();
field.addTag(new GenericAttribute(name, attr.info));
}
}
}
// Add every method to the bclass
for (int i = 0; i < coffiClass.methods_count; i++) {
method_info methodInfo = coffiClass.methods[i];
if ((coffiClass.constant_pool[methodInfo.name_index]) == null) {
logger.debug("method index: " + methodInfo.toName(coffiClass.constant_pool));
throw new RuntimeException("method has no name");
}
String methodName = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[methodInfo.name_index])).convert();
String methodDescriptor = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[methodInfo.descriptor_index])).convert();
List parameterTypes;
Type returnType;
// Generate parameterTypes & returnType
{
Type[] types = jimpleTypesOfFieldOrMethodDescriptor(methodDescriptor);
parameterTypes = new ArrayList();
for (int j = 0; j < types.length - 1; j++) {
references.add(types[j]);
parameterTypes.add(types[j]);
}
returnType = types[types.length - 1];
references.add(returnType);
}
int modifiers = methodInfo.access_flags;
SootMethod method;
method = Scene.v().makeSootMethod(methodName, parameterTypes, returnType, modifiers);
bclass.addMethod(method);
methodInfo.jmethod = method;
// add exceptions to method
{
for (int j = 0; j < methodInfo.attributes_count; j++) {
if (methodInfo.attributes[j] instanceof Exception_attribute) {
Exception_attribute exceptions = (Exception_attribute) methodInfo.attributes[j];
for (int k = 0; k < exceptions.number_of_exceptions; k++) {
CONSTANT_Class_info c = (CONSTANT_Class_info) coffiClass.constant_pool[exceptions.exception_index_table[k]];
String exceptionName = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[c.name_index])).convert();
exceptionName = exceptionName.replace('/', '.');
references.add(RefType.v(exceptionName));
method.addExceptionIfAbsent(SootResolver.v().makeClassRef(exceptionName));
}
} else if (methodInfo.attributes[j] instanceof Synthetic_attribute) {
method.addTag(new SyntheticTag());
} else if (methodInfo.attributes[j] instanceof Deprecated_attribute) {
method.addTag(new DeprecatedTag());
} else if (methodInfo.attributes[j] instanceof Signature_attribute) {
int signature_index = ((Signature_attribute) methodInfo.attributes[j]).signature_index;
String generic_sig = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[signature_index])).convert();
method.addTag(new SignatureTag(generic_sig));
} else if (methodInfo.attributes[j] instanceof RuntimeVisibleAnnotations_attribute
|| methodInfo.attributes[j] instanceof RuntimeInvisibleAnnotations_attribute) {
addAnnotationVisibilityAttribute(method, methodInfo.attributes[j], coffiClass, references);
} else if (methodInfo.attributes[j] instanceof RuntimeVisibleParameterAnnotations_attribute
|| methodInfo.attributes[j] instanceof RuntimeInvisibleParameterAnnotations_attribute) {
addAnnotationVisibilityParameterAttribute(method, methodInfo.attributes[j], coffiClass, references);
} else if (methodInfo.attributes[j] instanceof AnnotationDefault_attribute) {
AnnotationDefault_attribute attr = (AnnotationDefault_attribute) methodInfo.attributes[j];
element_value[] input = new element_value[1];
input[0] = attr.default_value;
ArrayList list = createElementTags(1, coffiClass, input);
method.addTag(new AnnotationDefaultTag(list.get(0)));
} else if (methodInfo.attributes[j] instanceof Generic_attribute) {
Generic_attribute attr = (Generic_attribute) methodInfo.attributes[j];
String name = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[attr.attribute_name])).convert();
method.addTag(new GenericAttribute(name, attr.info));
}
}
}
// Go through the constant pool, forcing
// all mentioned classes to be
// resolved.
{
for (int k = 0; k < coffiClass.constant_pool_count; k++) {
if (coffiClass.constant_pool[k] instanceof CONSTANT_Class_info) {
CONSTANT_Class_info c = (CONSTANT_Class_info) coffiClass.constant_pool[k];
String desc = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[c.name_index])).convert();
String name = desc.replace('/', '.');
if (name.startsWith("[")) {
references.add(jimpleTypeOfFieldDescriptor(desc));
} else {
references.add(RefType.v(name));
}
}
if (coffiClass.constant_pool[k] instanceof CONSTANT_Fieldref_info
|| coffiClass.constant_pool[k] instanceof CONSTANT_Methodref_info
|| coffiClass.constant_pool[k] instanceof CONSTANT_InterfaceMethodref_info) {
Type[] types = jimpleTypesOfFieldOrMethodDescriptor(cp_info.getTypeDescr(coffiClass.constant_pool, k));
for (Type element : types) {
references.add(element);
}
}
}
}
}
// Set coffi source of method
for (int i = 0; i < coffiClass.methods_count; i++) {
method_info methodInfo = coffiClass.methods[i];
// methodInfo.jmethod.setSource(coffiClass,
// methodInfo);
methodInfo.jmethod.setSource(new CoffiMethodSource(coffiClass, methodInfo));
}
// Set "SourceFile" attribute tag
for (int i = 0; i < coffiClass.attributes_count; i++) {
if (coffiClass.attributes[i] instanceof SourceFile_attribute) {
SourceFile_attribute attr = (SourceFile_attribute) coffiClass.attributes[i];
String sourceFile = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[attr.sourcefile_index])).convert();
if (sourceFile.indexOf(' ') >= 0) {
logger.debug("" + "Warning: Class " + className + " has invalid SourceFile attribute (will be ignored).");
} else {
bclass.addTag(new SourceFileTag(sourceFile, filePath));
}
}
// Set "InnerClass" attribute tag
else if (coffiClass.attributes[i] instanceof InnerClasses_attribute) {
InnerClasses_attribute attr = (InnerClasses_attribute) coffiClass.attributes[i];
for (int j = 0; j < attr.inner_classes_length; j++) {
inner_class_entry e = attr.inner_classes[j];
String inner = null;
String outer = null;
String name = null;
if (e.inner_class_index != 0) {
int name_index = ((CONSTANT_Class_info) coffiClass.constant_pool[e.inner_class_index]).name_index;
inner = ((CONSTANT_Utf8_info) coffiClass.constant_pool[name_index]).convert();
}
if (e.outer_class_index != 0) {
int name_index = ((CONSTANT_Class_info) coffiClass.constant_pool[e.outer_class_index]).name_index;
outer = ((CONSTANT_Utf8_info) coffiClass.constant_pool[name_index]).convert();
}
if (e.name_index != 0) {
name = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[e.name_index])).convert();
}
bclass.addTag(new InnerClassTag(inner, outer, name, e.access_flags));
}
}
// set synthetic tags
else if (coffiClass.attributes[i] instanceof Synthetic_attribute) {
bclass.addTag(new SyntheticTag());
}
// set deprectaed tags
else if (coffiClass.attributes[i] instanceof Deprecated_attribute) {
bclass.addTag(new DeprecatedTag());
} else if (coffiClass.attributes[i] instanceof Signature_attribute) {
int signature_index = ((Signature_attribute) coffiClass.attributes[i]).signature_index;
String generic_sig = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[signature_index])).convert();
bclass.addTag(new SignatureTag(generic_sig));
} else if (coffiClass.attributes[i] instanceof EnclosingMethod_attribute) {
EnclosingMethod_attribute attr = (EnclosingMethod_attribute) coffiClass.attributes[i];
int name_index = ((CONSTANT_Class_info) coffiClass.constant_pool[attr.class_index]).name_index;
String class_name = ((CONSTANT_Utf8_info) coffiClass.constant_pool[name_index]).convert();
CONSTANT_NameAndType_info info = (CONSTANT_NameAndType_info) coffiClass.constant_pool[attr.method_index];
String method_name = "";
String method_sig = "";
if (info != null) {
method_name = ((CONSTANT_Utf8_info) coffiClass.constant_pool[info.name_index]).convert();
method_sig = ((CONSTANT_Utf8_info) coffiClass.constant_pool[info.descriptor_index]).convert();
}
bclass.addTag(new EnclosingMethodTag(class_name, method_name, method_sig));
} else if (coffiClass.attributes[i] instanceof RuntimeVisibleAnnotations_attribute
|| coffiClass.attributes[i] instanceof RuntimeInvisibleAnnotations_attribute) {
addAnnotationVisibilityAttribute(bclass, coffiClass.attributes[i], coffiClass, references);
} else if (coffiClass.attributes[i] instanceof Generic_attribute) {
Generic_attribute attr = (Generic_attribute) coffiClass.attributes[i];
String name = ((CONSTANT_Utf8_info) (coffiClass.constant_pool[attr.attribute_name])).convert();
bclass.addTag(new GenericAttribute(name, attr.info));
}
}
}
Type jimpleReturnTypeOfMethodDescriptor(String descriptor) {
Type[] types = jimpleTypesOfFieldOrMethodDescriptor(descriptor);
return types[types.length - 1];
}
private final ArrayList conversionTypes = new ArrayList();
/*
* private Map cache = new HashMap(); public Type[] jimpleTypesOfFieldOrMethodDescriptor( String descriptor) { Type[] ret =
* (Type[]) cache.get(descriptor); if( ret != null ) return ret; conversionTypes.clear();
*
* while(descriptor.length() != 0) { boolean isArray = false; int numDimensions = 0; Type baseType;
*
* // Skip parenthesis if(descriptor.startsWith("(") || descriptor.startsWith(")")) { descriptor = descriptor.substring(1);
* continue; }
*
* // Handle array case while(descriptor.startsWith("[")) { isArray = true; numDimensions++; descriptor =
* descriptor.substring(1); }
*
* // Determine base type if(descriptor.startsWith("B")) { baseType = ByteType.v(); descriptor = descriptor.substring(1); }
* else if(descriptor.startsWith("C")) { baseType = CharType.v(); descriptor = descriptor.substring(1); } else
* if(descriptor.startsWith("D")) { baseType = DoubleType.v(); descriptor = descriptor.substring(1); } else
* if(descriptor.startsWith("F")) { baseType = FloatType.v(); descriptor = descriptor.substring(1); } else
* if(descriptor.startsWith("I")) { baseType = IntType.v(); descriptor = descriptor.substring(1); } else
* if(descriptor.startsWith("J")) { baseType = LongType.v(); descriptor = descriptor.substring(1); } else
* if(descriptor.startsWith("L")) { int index = descriptor.indexOf(';');
*
* if(index == -1) throw new RuntimeException("Class reference has no ending ;" );
*
* String className = descriptor.substring(1, index);
*
* baseType = RefType.v(className.replace('/', '.'));
*
* descriptor = descriptor.substring(index + 1); } else if(descriptor.startsWith("S")) { baseType = ShortType.v();
* descriptor = descriptor.substring(1); } else if(descriptor.startsWith("Z")) { baseType = BooleanType.v(); descriptor =
* descriptor.substring(1); } else if(descriptor.startsWith("V")) { baseType = VoidType.v(); descriptor =
* descriptor.substring(1); } else throw new RuntimeException("Unknown field type!" );
*
* Type t;
*
* // Determine type if(isArray) t = ArrayType.v(baseType, numDimensions); else t = baseType;
*
* conversionTypes.add(t); }
*
* ret = (Type[]) conversionTypes.toArray(new Type[0]); cache.put(descriptor, ret); return ret; }
*/
private final Map cache = new HashMap();
public Type[] jimpleTypesOfFieldOrMethodDescriptor(String descriptor) {
Type[] ret = null;
synchronized (cache) {
ret = cache.get(descriptor);
}
if (ret != null) {
return ret;
}
char[] d = descriptor.toCharArray();
int p = 0;
List conversionTypes = new ArrayList();
outer: while (p < d.length) {
boolean isArray = false;
int numDimensions = 0;
Type baseType = null;
swtch: while (p < d.length) {
switch (d[p]) {
// Skip parenthesis
case '(':
case ')':
p++;
continue outer;
case '[':
isArray = true;
numDimensions++;
p++;
continue swtch;
case 'B':
baseType = ByteType.v();
p++;
break swtch;
case 'C':
baseType = CharType.v();
p++;
break swtch;
case 'D':
baseType = DoubleType.v();
p++;
break swtch;
case 'F':
baseType = FloatType.v();
p++;
break swtch;
case 'I':
baseType = IntType.v();
p++;
break swtch;
case 'J':
baseType = LongType.v();
p++;
break swtch;
case 'L':
int index = p + 1;
while (index < d.length && d[index] != ';') {
if (d[index] == '/') {
d[index] = '.';
}
index++;
}
if (index >= d.length) {
throw new RuntimeException("Class reference has no ending ;");
}
String className = new String(d, p + 1, index - p - 1);
baseType = RefType.v(className);
p = index + 1;
break swtch;
case 'S':
baseType = ShortType.v();
p++;
break swtch;
case 'Z':
baseType = BooleanType.v();
p++;
break swtch;
case 'V':
baseType = VoidType.v();
p++;
break swtch;
default:
throw new RuntimeException("Unknown field type!");
}
}
if (baseType == null) {
continue;
}
// Determine type
Type t;
if (isArray) {
t = ArrayType.v(baseType, numDimensions);
} else {
t = baseType;
}
conversionTypes.add(t);
}
ret = conversionTypes.toArray(new Type[0]);
synchronized (cache) {
cache.put(descriptor, ret);
}
return ret;
}
public Type jimpleTypeOfFieldDescriptor(String descriptor) {
// Handle array case
int numDimensions = 0;
while (descriptor.startsWith("[")) {
numDimensions++;
descriptor = descriptor.substring(1);
}
// Determine base type
Type baseType;
if (descriptor.equals("B")) {
baseType = ByteType.v();
} else if (descriptor.equals("C")) {
baseType = CharType.v();
} else if (descriptor.equals("D")) {
baseType = DoubleType.v();
} else if (descriptor.equals("F")) {
baseType = FloatType.v();
} else if (descriptor.equals("I")) {
baseType = IntType.v();
} else if (descriptor.equals("J")) {
baseType = LongType.v();
} else if (descriptor.equals("V")) {
baseType = VoidType.v();
} else if (descriptor.startsWith("L")) {
if (!descriptor.endsWith(";")) {
throw new RuntimeException("Class reference does not end with ;");
}
String className = descriptor.substring(1, descriptor.length() - 1);
baseType = RefType.v(className.replace('/', '.'));
} else if (descriptor.equals("S")) {
baseType = ShortType.v();
} else if (descriptor.equals("Z")) {
baseType = BooleanType.v();
} else {
throw new RuntimeException("Unknown field type: " + descriptor);
}
// Return type
return (numDimensions > 0) ? ArrayType.v(baseType, numDimensions) : baseType;
}
int nextEasyNameIndex;
void resetEasyNames() {
nextEasyNameIndex = 0;
}
String getNextEasyName() {
final String[] easyNames = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
"s", "t", "u", "v", "w", "x", "y", "z" };
int justifiedIndex = nextEasyNameIndex++;
if (justifiedIndex >= easyNames.length) {
return "local" + (justifiedIndex - easyNames.length);
} else {
return easyNames[justifiedIndex];
}
}
Local getLocalForStackOp(JimpleBody listBody, TypeStack typeStack, int index) {
if (typeStack.get(index).equals(Double2ndHalfType.v()) || typeStack.get(index).equals(Long2ndHalfType.v())) {
index--;
}
return getLocalCreatingIfNecessary(listBody, "$stack" + index, UnknownType.v());
}
String getAbbreviationOfClassName(String className) {
StringBuffer buffer = new StringBuffer(new Character(className.charAt(0)).toString());
int periodIndex = 0;
for (;;) {
periodIndex = className.indexOf('.', periodIndex + 1);
if (periodIndex == -1) {
break;
}
buffer.append(Character.toLowerCase(className.charAt(periodIndex + 1)));
}
return buffer.toString();
}
String getNormalizedClassName(String className) {
className = className.replace('/', '.');
if (className.endsWith(";")) {
className = className.substring(0, className.length() - 1);
}
// Handle array case
{
int numDimensions = 0;
while (className.startsWith("[")) {
numDimensions++;
className = className.substring(1, className.length());
className = className + "[]";
}
if (numDimensions != 0) {
if (!className.startsWith("L")) {
throw new RuntimeException("For some reason an array reference does not start with L");
}
className = className.substring(1, className.length());
}
}
return className;
}
private Local getLocalUnsafe(Body b, String name) {
for (Local local : b.getLocals()) {
if (local.getName().equals(name)) {
return local;
}
}
return null;
}
Local getLocalCreatingIfNecessary(JimpleBody listBody, String name, Type type) {
Local l = getLocalUnsafe(listBody, name);
if (l != null) {
if (!l.getType().equals(type)) {
throw new RuntimeException("The body already declares this local name with a different type.");
}
} else {
l = Jimple.v().newLocal(name, type);
listBody.getLocals().add(l);
}
return l;
}
/**
* Get a Local for the parameter at the given local variable index.
*
* @listBody the method body.
* @index the parameter's local variable index.
* @return the Local for the given local variable index.
*/
Local getLocalForParameter(JimpleBody listBody, int index) {
return getLocalForIndex(listBody, index, 0, 0, false);
}
/**
* Get a Local for the local variable at the given index in the context of the given instruction.
*
* @listBody the method body.
* @index the local variable index.
* @context the instruction context.
* @return the Local for the given local variable index.
*/
Local getLocalForIndex(JimpleBody listBody, int index, Instruction context) {
return getLocalForIndex(listBody, index, context.originalIndex, context.nextOffset(context.originalIndex),
ByteCode.isLocalStore(context.code));
}
private Local getLocalForIndex(JimpleBody listBody, int index, int bcIndex, int nextBcIndex, boolean isLocalStore) {
String name = null;
Local local;
if (useFaithfulNaming && activeVariableTable != null) {
/*
* [use-original-names]
*
* Use original variables names. Generally speeking, the current way of handling original variables names is sound if
* the local variable table is consistant with the source code.
*
* consistant(table) ==> sound(local for index)
*
* In the table, variables are tuples ((s,l),i,N) where (s,l) encodes the half-open bytecode index interval [s,s+l[, i
* is the local variable index and N is the original variable name. ((s,l),i,N) means "from bytecode index s included
* to bytecode index s+l excluded, the variable at index i is named N is the source code".
*
* However the content of the table is for informational and debugging purpose only, the compiler can insert whatever
* inconsistancies it may want, meaning we cannot trust the table.
*
* The most common inconsistancy we found so far is where variables with different indexes and overlapping bytecode
* ranges are given the same name in the table. Although it won't happen with user-defined variables since Java doesn't
* allow the same variable name to be declared in nested scopes. But this situation can arise for compiler-generated
* local variables if they are assigned the same name (`i$` is common in OpenJDK). Notable example are local variables
* generated to implement the Java for-each statement (for(Foo foo : bar){}). If the source code contains nested
* for-each statements, then it becomes ugly because all the generated iterators or counters (for-each on arrays) are
* assigned the same name in the table.
*
* This inconsistancy is now handled correctly by the following code. The idea is based on the observation that the
* Local allocation works well if we simply allocate a different Local object for each local variable index (it is the
* default allocation policy, when the original names are not kept). Therefore, local variables with the same name and
* same index should have the same Local object. And local variables with the same name but different index should have
* a different Local object. We maintain the map nameToIndexToLocal :: Name -> (Index -> Local Object) from variable
* names to maps from variable indexes to Local objects. So we allocate a new Local object for each pair of a variable
* name and a variable index.
*
* Unfortunately, this is still unsound if the table gives several names for the same variable of the source code. Or
* if it reports wrong bytecode ranges, or if it doesn't report the name of a variable everywhere it is used in the
* bytecode...
*
* In order to obtain sound Local allocation when taking original names into account... we should not allocate Local
* according to these names. Instead we must keep the default allocation policy (one Local object for each local
* variable index), and then annotate each statement with "most probable name(s)" for each use or def Local.
*
*/
if (bcIndex != -1) {
int lookupBcIndex = bcIndex;
/*
* For local store bytecode, the local actually takes its new value after the bytecode is executed, so we must look
* at the next bytecode index. This is the behavior observed at least with OpenJDK javac.
*/
if (isLocalStore) {
lookupBcIndex = nextBcIndex;
}
name = activeVariableTable.getLocalVariableName(activeConstantPool, index, lookupBcIndex);
/*
* // for debug purpose String desc = activeVariableTable. getLocalVariableDescriptor( activeConstantPool, index,
* activeOriginalIndex); if (activeVariableTypeTable != null){ String debug_type = activeVariableTypeTable.
* getLocalVariableType( activeConstantPool, index, activeOriginalIndex); }
*/
}
}
if (name == null) {
name = "l" + index; // generate a default name for the local
}
if (nameToIndexToLocal == null) {
nameToIndexToLocal = new HashMap>();
}
Map indexToLocal;
if (!nameToIndexToLocal.containsKey(name)) {
indexToLocal = new HashMap();
nameToIndexToLocal.put(name, indexToLocal);
} else {
indexToLocal = nameToIndexToLocal.get(name);
}
if (indexToLocal.containsKey(index)) {
local = indexToLocal.get(index);
} else {
local = Jimple.v().newLocal(name, UnknownType.v());
listBody.getLocals().add(local);
indexToLocal.put(index, local);
}
return local;
}
/*
* void setLocalType(Local local, List locals, int localIndex, Type type) { if(local.getType().equals(UnknownType .v()) ||
* local.getType().equals(type)) { local.setType(type);
*
* if(local.getType().equals(DoubleType. v()) || local.getType().equals(LongType.v())) { // This means the next local
* becomes voided, since these types occupy two // words.
*
* Local secondHalf = (Local) locals.get(localIndex + 1);
*
* secondHalf.setType(VoidType.v()); }
*
* return; }
*
* if(type.equals(IntType.v())) { if(local.getType().equals(BooleanType .v()) || local.getType().equals(CharType.v()) ||
* local.getType().equals(ShortType.v()) || local.getType().equals(ByteType.v())) { // Even though it's not the same, it's
* ok, because booleans, chars, shorts, and // bytes are all sort of treated like integers by the JVM. return; }
*
* }
*
* throw new RuntimeException("required and actual types do not match: " + type.toString() + " with " +
* local.getType().toString()); }
*/
/**
* Verifies the prospective name for validity as a Jimple name. In particular, first-char is alpha | _ | $,
* subsequent-chars are alphanum | _ | $.
*
* We could use isJavaIdentifier, except that Jimple's grammar doesn't support all of those, just ASCII.
*
* I'd put this in soot.Local, but that's an interface.
*
* @author Patrick Lam
*/
boolean isValidJimpleName(String prospectiveName) {
if (prospectiveName == null) {
return false;
}
for (int i = 0; i < prospectiveName.length(); i++) {
char c = prospectiveName.charAt(i);
if ((i == 0 && c >= '0' && c <= '9')
|| !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_' || c == '$'))) {
return false;
}
}
return true;
}
private void addAnnotationVisibilityAttribute(Host host, attribute_info attribute, ClassFile coffiClass,
Collection references) {
VisibilityAnnotationTag tag;
if (attribute instanceof RuntimeVisibleAnnotations_attribute) {
tag = new VisibilityAnnotationTag(AnnotationConstants.RUNTIME_VISIBLE);
RuntimeVisibleAnnotations_attribute attr = (RuntimeVisibleAnnotations_attribute) attribute;
addAnnotations(attr.number_of_annotations, attr.annotations, coffiClass, tag, references);
} else {
tag = new VisibilityAnnotationTag(AnnotationConstants.RUNTIME_INVISIBLE);
RuntimeInvisibleAnnotations_attribute attr = (RuntimeInvisibleAnnotations_attribute) attribute;
addAnnotations(attr.number_of_annotations, attr.annotations, coffiClass, tag, references);
}
host.addTag(tag);
}
private void addAnnotationVisibilityParameterAttribute(Host host, attribute_info attribute, ClassFile coffiClass,
Collection references) {
VisibilityParameterAnnotationTag tag;
if (attribute instanceof RuntimeVisibleParameterAnnotations_attribute) {
RuntimeVisibleParameterAnnotations_attribute attr = (RuntimeVisibleParameterAnnotations_attribute) attribute;
tag = new VisibilityParameterAnnotationTag(attr.num_parameters, AnnotationConstants.RUNTIME_VISIBLE);
for (int i = 0; i < attr.num_parameters; i++) {
parameter_annotation pAnnot = attr.parameter_annotations[i];
VisibilityAnnotationTag vTag = new VisibilityAnnotationTag(AnnotationConstants.RUNTIME_VISIBLE);
addAnnotations(pAnnot.num_annotations, pAnnot.annotations, coffiClass, vTag, references);
tag.addVisibilityAnnotation(vTag);
}
} else {
RuntimeInvisibleParameterAnnotations_attribute attr = (RuntimeInvisibleParameterAnnotations_attribute) attribute;
tag = new VisibilityParameterAnnotationTag(attr.num_parameters, AnnotationConstants.RUNTIME_INVISIBLE);
for (int i = 0; i < attr.num_parameters; i++) {
parameter_annotation pAnnot = attr.parameter_annotations[i];
VisibilityAnnotationTag vTag = new VisibilityAnnotationTag(AnnotationConstants.RUNTIME_INVISIBLE);
addAnnotations(pAnnot.num_annotations, pAnnot.annotations, coffiClass, vTag, references);
tag.addVisibilityAnnotation(vTag);
}
}
host.addTag(tag);
}
private void addAnnotations(int numAnnots, annotation[] annotations, ClassFile coffiClass, VisibilityAnnotationTag tag,
Collection references) {
for (int i = 0; i < numAnnots; i++) {
annotation annot = annotations[i];
String annotType = ((CONSTANT_Utf8_info) coffiClass.constant_pool[annot.type_index]).convert();
String ref = annotType.substring(1, annotType.length() - 1);
ref = ref.replace('/', '.');
references.add(RefType.v(ref));
AnnotationTag annotTag = new AnnotationTag(annotType,
createElementTags(annot.num_element_value_pairs, coffiClass, annot.element_value_pairs));
tag.addAnnotation(annotTag);
}
}
private ArrayList createElementTags(int count, ClassFile coffiClass, element_value[] elems) {
ArrayList list = new ArrayList();
for (int j = 0; j < count; j++) {
element_value ev = elems[j];
char kind = ev.tag;
String elemName = "default";
if (ev.name_index != 0) {
elemName = ((CONSTANT_Utf8_info) coffiClass.constant_pool[ev.name_index]).convert();
}
if (kind == 'B' || kind == 'C' || kind == 'I' || kind == 'S' || kind == 'Z' || kind == 'D' || kind == 'F'
|| kind == 'J' || kind == 's') {
constant_element_value cev = (constant_element_value) ev;
if (kind == 'B' || kind == 'C' || kind == 'I' || kind == 'S' || kind == 'Z') {
cp_info cval = coffiClass.constant_pool[cev.constant_value_index];
int constant_val = (int) ((CONSTANT_Integer_info) cval).bytes;
AnnotationIntElem elem = new AnnotationIntElem(constant_val, kind, elemName);
list.add(elem);
} else if (kind == 'D') {
cp_info cval = coffiClass.constant_pool[cev.constant_value_index];
double constant_val = ((CONSTANT_Double_info) cval).convert();
AnnotationDoubleElem elem = new AnnotationDoubleElem(constant_val, kind, elemName);
list.add(elem);
} else if (kind == 'F') {
cp_info cval = coffiClass.constant_pool[cev.constant_value_index];
float constant_val = ((CONSTANT_Float_info) cval).convert();
AnnotationFloatElem elem = new AnnotationFloatElem(constant_val, kind, elemName);
list.add(elem);
} else if (kind == 'J') {
cp_info cval = coffiClass.constant_pool[cev.constant_value_index];
CONSTANT_Long_info lcval = (CONSTANT_Long_info) cval;
long constant_val = (lcval.high << 32) + lcval.low;
AnnotationLongElem elem = new AnnotationLongElem(constant_val, kind, elemName);
list.add(elem);
} else if (kind == 's') {
cp_info cval = coffiClass.constant_pool[cev.constant_value_index];
String constant_val = ((CONSTANT_Utf8_info) cval).convert();
AnnotationStringElem elem = new AnnotationStringElem(constant_val, kind, elemName);
list.add(elem);
}
} else if (kind == 'e') {
enum_constant_element_value ecev = (enum_constant_element_value) ev;
cp_info type_val = coffiClass.constant_pool[ecev.type_name_index];
String type_name = ((CONSTANT_Utf8_info) type_val).convert();
cp_info name_val = coffiClass.constant_pool[ecev.constant_name_index];
String constant_name = ((CONSTANT_Utf8_info) name_val).convert();
AnnotationEnumElem elem = new AnnotationEnumElem(type_name, constant_name, kind, elemName);
list.add(elem);
} else if (kind == 'c') {
class_element_value cev = (class_element_value) ev;
cp_info cval = coffiClass.constant_pool[cev.class_info_index];
CONSTANT_Utf8_info sval = (CONSTANT_Utf8_info) cval;
String desc = sval.convert();
AnnotationClassElem elem = new AnnotationClassElem(desc, kind, elemName);
list.add(elem);
} else if (kind == '[') {
array_element_value aev = (array_element_value) ev;
int num_vals = aev.num_values;
ArrayList elemVals = createElementTags(num_vals, coffiClass, aev.values);
AnnotationArrayElem elem = new AnnotationArrayElem(elemVals, kind, elemName);
list.add(elem);
} else if (kind == '@') {
annotation_element_value aev = (annotation_element_value) ev;
annotation annot = aev.annotation_value;
String annotType = ((CONSTANT_Utf8_info) coffiClass.constant_pool[annot.type_index]).convert();
AnnotationTag annotTag = new AnnotationTag(annotType,
createElementTags(annot.num_element_value_pairs, coffiClass, annot.element_value_pairs));
AnnotationAnnotationElem elem = new AnnotationAnnotationElem(annotTag, kind, elemName);
list.add(elem);
}
}
return list;
}
}