All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.coffi.ClassFile Maven / Gradle / Ivy

There is a newer version: 1.12.0
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 1997 Clark Verbrugge
 *
 * 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.coffi;
import soot.*;

import java.io.*;

/**
 * A ClassFile object represents the contents of a .class file.
 * 

* A ClassFile contains code for manipulation of its constituents. * @author Clark Verbrugge */ public class ClassFile { /** Magic number. */ static final long MAGIC = 0xCAFEBABEL; /** Access bit flag. */ static final short ACC_PUBLIC = 0x0001; /** Access bit flag. */ static final short ACC_PRIVATE = 0x0002; /** Access bit flag. */ static final short ACC_PROTECTED = 0x0004; /** Access bit flag. */ static final short ACC_STATIC = 0x0008; /** Access bit flag. */ static final short ACC_FINAL = 0x0010; /** Access bit flag. */ static final short ACC_SUPER = 0x0020; /** Access bit flag. */ static final short ACC_VOLATILE = 0x0040; /** Access bit flag. */ static final short ACC_TRANSIENT = 0x0080; /** Access bit flag. */ static final short ACC_INTERFACE = 0x0200; /** Access bit flag. */ static final short ACC_ABSTRACT = 0x0400; /** Access bit flag. */ static final short ACC_STRICT = 0x0800; /** Access bit flag. */ static final short ACC_ANNOTATION = 0x2000; /** Access bit flag. */ static final short ACC_ENUM = 0x4000; /** Remaining bits in the access bit flag. */ static final short ACC_UNKNOWN = 0x7000; /** Descriptor code string. */ static final String DESC_BYTE = "B"; /** Descriptor code string. */ static final String DESC_CHAR = "C"; /** Descriptor code string. */ static final String DESC_DOUBLE = "D"; /** Descriptor code string. */ static final String DESC_FLOAT= "F"; /** Descriptor code string. */ static final String DESC_INT = "I"; /** Descriptor code string. */ static final String DESC_LONG = "J"; /** Descriptor code string. */ static final String DESC_OBJECT = "L"; /** Descriptor code string. */ static final String DESC_SHORT = "S"; /** Descriptor code string. */ static final String DESC_BOOLEAN = "Z"; /** Descriptor code string. */ static final String DESC_VOID = "V"; /** Descriptor code string. */ static final String DESC_ARRAY = "["; /** Debugging flag. */ boolean debug; /** File name of the .class this represents. */ String fn; /* For chaining ClassFiles into a list. ClassFile next;*/ /** Magic number read in. * @see ClassFile#MAGIC */ long magic; /** Minor version. */ int minor_version; /** Major version. */ int major_version; /** Number of items in the constant pool. */ public int constant_pool_count; /** Array of constant pool items. * @see cp_info */ public cp_info constant_pool[]; /** Access flags for this Class. */ public int access_flags; /** Constant pool index of the Class constant describing this. * @see CONSTANT_Class_info */ public int this_class; /** Constant pool index of the Class constant describing super. * @see CONSTANT_Class_info */ public int super_class; /** Count of interfaces implemented. */ public int interfaces_count; /** Array of constant pool indices of Class constants describing each * interace implemented by this class, as given in the source for this * class. * @see CONSTANT_Class_info */ public int interfaces[]; /** Count of fields this Class contains. */ public int fields_count; /** Array of field_info objects describing each field. * @see field_info */ public field_info fields[]; /** Count of methods this Class contains. */ public int methods_count; /** Array of method_info objects describing each field. * @see method_info */ public method_info methods[]; /** Count of attributes this class contains. */ public int attributes_count; /** Array of attribute_info objects for this class. * @see attribute_info */ public attribute_info attributes[]; /** bootstrap-methods attribute (if any) */ public BootstrapMethods_attribute bootstrap_methods_attribute; /** Creates a new ClassFile object given the name of the file. * @param nfn file name which this ClassFile will represent. */ public ClassFile(String nfn) { fn = nfn; } /** Returns the name of this Class. */ public String toString() { return (constant_pool[this_class].toString(constant_pool)); } public boolean loadClassFile(InputStream is) { InputStream f = null; InputStream classFileStream; DataInputStream d; boolean b; classFileStream = is; byte[] data; Timers.v().readTimer.start(); try { data = new byte[classFileStream.available()]; classFileStream.read(data); f = new ByteArrayInputStream(data); } catch(IOException e) { } Timers.v().readTimer.end(); d = new DataInputStream(f); b = readClass(d); try { classFileStream.close(); d.close(); f.close(); } catch(IOException e) { G.v().out.println("IOException with " + fn + ": " + e.getMessage()); return false; } if (!b) return false; //parse(); // parse all methods & builds CFGs //G.v().out.println("-- Read " + cf + " --"); return true; } /** Main entry point for writing a class file. * The file name is given in the constructor; this opens the * file and writes the internal representation. * @return true on success. */ boolean saveClassFile() { FileOutputStream f; DataOutputStream d; boolean b; try { f = new FileOutputStream(fn); } catch(FileNotFoundException e) { if (fn.indexOf(".class")>=0) { G.v().out.println("Can't find " + fn); return false; } fn = fn + ".class"; try { f = new FileOutputStream(fn); } catch(FileNotFoundException ee) { G.v().out.println("Can't find " + fn); return false; } } d = new DataOutputStream(f); if (d==null) { try { f.close(); } catch(IOException e) { } return false; } b = writeClass(d); try { d.close(); f.close(); } catch(IOException e) { G.v().out.println("IOException with " + fn + ": " + e.getMessage()); return false; } return b; } /** Returns a String constructed by parsing the bits in the given * access code (as defined by the ACC_* constants). * @param af access code. * @param separator String used to separate words used for access bits. * @see ClassFile#access_flags * @see method_info#access_flags * @see field_info#access_flags */ static String access_string(int af,String separator) { boolean hasone = false; String s = ""; if ((af & ACC_PUBLIC) != 0) { s = "public"; hasone = true; } if ((af & ACC_PRIVATE) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "private"; } if ((af & ACC_PROTECTED) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "protected"; } if ((af & ACC_STATIC) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "static"; } if ((af & ACC_FINAL) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "final"; } if ((af & ACC_SUPER) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "super"; } if ((af & ACC_VOLATILE) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "volatile"; } if ((af & ACC_TRANSIENT) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "transient"; } if ((af & ACC_INTERFACE) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "interface"; } if ((af & ACC_ABSTRACT) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "abstract"; } if ((af & ACC_STRICT) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "strict"; } if ((af & ACC_ANNOTATION) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "annotation"; } if ((af & ACC_ENUM) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "enum"; } if ((af & ACC_UNKNOWN) != 0) { if (hasone) s = s + separator; else hasone = true; s = s + "unknown"; } return s; } /** Builds the internal representation of this Class by reading in the * given class file. * @param d Stream forming the .class file. * @return true if read was successful, false on some error. */ public boolean readClass(DataInputStream d) { try { // first read in magic number magic = d.readInt() & 0xFFFFFFFFL; if (magic != MAGIC) { G.v().out.println("Wrong magic number in " + fn + ": " + magic); return false; } //G.v().out.println("Magic number ok"); minor_version = d.readUnsignedShort(); major_version = d.readUnsignedShort(); // G.v().out.println("Version: " + major_version + "." + minor_version); constant_pool_count = d.readUnsignedShort(); //G.v().out.println("Constant pool count: " + constant_pool_count); if (!readConstantPool(d)) return false; access_flags = d.readUnsignedShort(); /*if (access_flags!=0) G.v().out.println("Access flags: " + access_flags + " = " + access_string(access_flags,", "));*/ this_class = d.readUnsignedShort(); super_class = d.readUnsignedShort(); interfaces_count = d.readUnsignedShort(); if (interfaces_count>0) { interfaces = new int[interfaces_count]; int j; for (j=0; j0) { attributes = new attribute_info[attributes_count]; readAttributes(d,attributes_count,attributes); } Timers.v().attributeTimer.end(); } catch(IOException e) { throw new RuntimeException("IOException with " + fn + ": " + e.getMessage(), e); } /*inf.fields = fields_count; inf.methods = methods_count; inf.cp = constant_pool_count;*/ return true; } /** Reads in the constant pool from the given stream. * @param d Stream forming the .class file. * @return true if read was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean readConstantPool(DataInputStream d) throws IOException { byte tag; cp_info cp; int i; boolean skipone; // set if next cp entry is to be skipped constant_pool = new cp_info[constant_pool_count]; //Instruction.constant_pool = constant_pool; skipone = false; for (i=1;i.class file. * @param attributes_count number of attributes to read in. * @param ai pre-allocated array of attributes to be filled in. * @return true if read was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean readAttributes(DataInputStream d,int attributes_count, attribute_info[] ai) throws IOException { attribute_info a=null; int i; int j; long len; String s; for (i=0;i0) { int k; ea.exception_index_table = new int[ea.number_of_exceptions]; for (k=0; k0) { ga.info = new byte[(int) len]; d.read(ga.info); } a = (attribute_info)ga; } a.attribute_name = j; a.attribute_length = len; ai[i] = a; } return true; } private element_value [] readElementValues(int count, DataInputStream d, boolean needName, int name_index) throws IOException { element_value [] list = new element_value[count]; for (int x = 0; x < count; x++){ if (needName){ name_index = d.readUnsignedShort(); } int tag = d.readUnsignedByte(); char kind = (char)tag; if (kind == 'B' || kind == 'C' || kind == 'D' || kind == 'F' || kind == 'I' || kind == 'J' || kind == 'S' || kind == 'Z' || kind == 's'){ constant_element_value elem = new constant_element_value(); elem.name_index = name_index; elem.tag = kind; elem.constant_value_index = d.readUnsignedShort(); list[x] = elem; } else if (kind == 'e'){ enum_constant_element_value elem = new enum_constant_element_value(); elem.name_index = name_index; elem.tag = kind; elem.type_name_index = d.readUnsignedShort(); elem.constant_name_index = d.readUnsignedShort(); list[x] = elem; } else if (kind == 'c'){ class_element_value elem = new class_element_value(); elem.name_index = name_index; elem.tag = kind; elem.class_info_index = d.readUnsignedShort(); list[x] = elem; } else if (kind == '['){ array_element_value elem = new array_element_value(); elem.name_index = name_index; elem.tag = kind; elem.num_values = d.readUnsignedShort(); elem.values = readElementValues(elem.num_values, d, false, name_index); list[x] = elem; } else if (kind == '@'){ annotation_element_value elem = new annotation_element_value(); elem.name_index = name_index; elem.tag = kind; annotation annot = new annotation(); annot.type_index = d.readUnsignedShort(); annot.num_element_value_pairs = d.readUnsignedShort(); annot.element_value_pairs = readElementValues(annot.num_element_value_pairs, d, true, 0); elem.annotation_value = annot; list[x] = elem; } else { throw new RuntimeException("Unknown element value pair kind: "+kind); } } return list; } /** Reads in the fields from the given stream. * @param d Stream forming the .class file. * @return true if read was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean readFields(DataInputStream d) throws IOException { field_info fi; int i; fields = new field_info[fields_count]; for (i=0;i0) { fi.attributes = new attribute_info[fi.attributes_count]; readAttributes(d,fi.attributes_count,fi.attributes); } /*CONSTANT_Utf8_info ci; ci = (CONSTANT_Utf8_info)(constant_pool[fi.name_index]); G.v().out.println("Field: " + ci.convert());*/ fields[i] = fi; } return true; } /** Reads in the methods from the given stream. * @param d Stream forming the .class file. * @return true if read was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean readMethods(DataInputStream d) throws IOException { method_info mi; int i; methods = new method_info[methods_count]; for (i=0;i0) { mi.attributes = new attribute_info[mi.attributes_count]; readAttributes(d,mi.attributes_count,mi.attributes); for (int j=0; jtrue if write was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean writeConstantPool(DataOutputStream dd) throws IOException { byte tag; cp_info cp; int i; boolean skipone = false; for (i=1;itrue if write was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean writeAttributes(DataOutputStream dd, int attributes_count, attribute_info[] ai) throws IOException { attribute_info a=null; int i,len; short j; String s; for (i=0;i0) writeAttributes(dd,ca.attributes_count,ca.attributes); } else if(a instanceof Exception_attribute) { Exception_attribute ea = (Exception_attribute)a; dd.writeShort(ea.number_of_exceptions); if (ea.number_of_exceptions>0) { int k; for (k=0; k0) { dd.write(ga.info,0,(int) ga.attribute_length); } } } return true; } /** Writes the fields to the given stream. * @param dd output stream. * @return true if write was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean writeFields(DataOutputStream dd) throws IOException { field_info fi; int i; for (i=0;i0) { writeAttributes(dd,fi.attributes_count,fi.attributes); } } return true; } /** Writes the methods to the given stream. * @param dd output stream. * @return true if write was successful, false on some error. * @exception java.io.IOException on error. */ protected boolean writeMethods(DataOutputStream dd) throws IOException { method_info mi; int i; for (i=0;i0) { writeAttributes(dd,mi.attributes_count,mi.attributes); } } return true; } /** Writes this entire ClassFile object to the given stream. * @param dd output stream. * @return true if write was successful, false on some error. */ boolean writeClass(DataOutputStream dd) { // outputs the .class file from the loaded one try { // first write magic number dd.writeInt((int) magic); dd.writeShort(minor_version); dd.writeShort(major_version); dd.writeShort(constant_pool_count); if (!writeConstantPool(dd)) return false; dd.writeShort(access_flags); dd.writeShort(this_class); dd.writeShort(super_class); dd.writeShort(interfaces_count); if (interfaces_count>0) { int j; for (j=0; j0) { writeAttributes(dd,attributes_count,attributes); } } catch(IOException e) { G.v().out.println("IOException with " + fn + ": " + e.getMessage()); return false; } return true; } /** Parses the given method, converting its bytecode array into a list * of Instruction objects. * @param m method to parse. * @return head of a list of Instructions. * @see Instruction * @see ByteCode * @see ByteCode#disassemble_bytecode */ public Instruction parseMethod(method_info m) { // first task, look through attributes for a code attribute int j; Code_attribute ca; ByteCode bc; Instruction inst,head,tail; exception_table_entry e; head = null; tail = null; bc = new ByteCode(); ca = m.locate_code_attribute(); if (ca==null) return null; j = 0; while(jnull on error. * @see CFG#reconstructInstructions * @see ClassFile#parseMethod * @see ClassFile#relabel * @see Instruction#compile */ byte[] unparseMethod(method_info m) { int codesize; byte bc[]; Instruction i; // Rebuild instruction sequence m.cfg.reconstructInstructions(); // relabel instructions and get size of code array codesize = relabel(m.instructions); // construct a new array for the byte-code bc = new byte[codesize]; if (bc==null) { G.v().out.println("Warning: can't allocate memory for recompile"); return null; } // then recompile the instructions into byte-code i = m.instructions; codesize = 0; while (i!=null) { codesize = i.compile(bc,codesize); i = i.next; } if (codesize != bc.length) G.v().out.println("Warning: code size doesn't match array length!"); return bc; } /** Inversive to parse, this method calls unparseMethod for each * method, storing the resulting bytecode in the method's code * attribute, and recomputing offsets for exception handlers. * @see ClassFile#unparseMethod */ void unparse() { int i,j; Code_attribute ca; byte bc[]; method_info mi; exception_table_entry e; for (i=0;i=0) { return parseDesc(s.substring(j+1),","); } return parseDesc(s,","); } /** Static utility method to parse the given method descriptor string. * @param s descriptor string. * @return comma-separated ordered list of parameter types * @see ClassFile#parseDesc * @see ClassFile#parseMethodDesc_return */ static String parseMethodDesc_params(String s) { int i,j; i = s.indexOf('('); if (i>=0) { j = s.indexOf(')',i+1); if (j>=0) { return parseDesc(s.substring(i+1,j),","); } } return ""; } /** Static utility method to parse the given method descriptor string. * @param desc descriptor string. * @param sep String to use as a separator between types. * @return String of types parsed. * @see ClassFile#parseDesc * @see ClassFile#parseMethodDesc_return */ static String parseDesc(String desc,String sep) { String params = "",param; char c; int i,len,arraylevel=0; boolean didone = false; len = desc.length(); for (i=0;i10 && desc.substring(i+1,i+11).compareTo("java/lang/")==0) i = i+10; param = desc.substring(i+1,j); // replace '/'s with '.'s param = param.replace('/','.'); i = j; } } else { param = "???"; } if (didone) params = params + sep; params = params + param; while (arraylevel>0) { params = params + "[]"; arraylevel--; } didone = true; } return params; } /** Locates a method by name. * @param s name of method. * @return method_info object representing method, or null if not found. * @see method_info#toName */ method_info findMethod(String s) { method_info m; int i; for (i=0;ipos) { for (j=i;j>pos && j>0;j--) methods[j] = methods[j-1]; methods[pos] = mthd; } else if (itrue if it is a parent, false otherwise. * @see ClassFile#descendsFrom(String) */ boolean descendsFrom(ClassFile cf) { return descendsFrom(cf.toString()); } /** Answers whether this class is an immediate descendant (as subclass or * as an implementation of an interface) of the given class. * @param cname name of supposed parent. * @return true if it is a parent, false otherwise. * @see ClassFile#descendsFrom(ClassFile) */ boolean descendsFrom(String cname) { cp_info cf; int i; cf = constant_pool[super_class]; if (cf.toString(constant_pool).compareTo(cname)==0) return true; for (i=0;itrue if it cannot, false if it might. */ boolean isSterile() { if ((access_flags&ACC_PUBLIC)!=0 && (access_flags&ACC_FINAL)==0) return false; return true; } /** Given the name of a class --- possibly with .class after it, * this answers whether the class might refer to this ClassFile object. * @return true if it does, false if it doesn't. */ boolean sameClass(String cfn) { String s = cfn; int i = s.lastIndexOf(".class"); if (i>0) { // has .class after it s = s.substring(0,i); // cut off the .class } if (s.compareTo(toString())==0) return true; return false; } /** Returns the name of a specific field in the field array. * @param i index of field in field array. * @return name of field. */ String fieldName(int i) { return fields[i].toName(constant_pool); } /* DEPRECATED // Locates the given classfile, and extracts it from the list. // It cannot be the first one in the list, and this returns null // or the classfile. static ClassFile removeClassFile(ClassFile cfhead,String cfn) { ClassFile cf,cfprev; cf = cfhead; cfprev = null; while (cf!=null) { if (cf.sameClass(cfn)) { if (cfprev==null) return null; // this shouldn't happen cfprev.next = cf.next; cf.next = null; return cf; } cfprev = cf; cf = cf.next; } return null; } // returns true if this class contains any references to the given // cuClass.cuName, which is of type cuDesc. Searches for methods if // ismethod is true, fields otherwise. boolean refersTo(boolean ismethod,CONSTANT_Utf8_info cuClass, CONSTANT_Utf8_info cuName,CONSTANT_Utf8_info cuDesc) { int i; CONSTANT_Utf8_info cu; // note that we start at 1 in the constant pool if (ismethod) { for (i=1;i=1;largest--) { for (i=0;ia[largest]) { s = a[i]; a[i] = a[largest]; a[largest] = s; } } } return a; } // Given a new constant pool, and a list of redirections // (new index = redirect[old index]), this changes all constant // pool entries, and installs the new constant pool of size size void changeConstantPool(short redirect[],cp_info newCP[],short size) { Debig d = new Debig(this); d.redirectCPRefs(redirect); constant_pool = newCP; constant_pool_count = size; } // the constant pool is typically a few hundred entries in size, and so // is just a bit too big to make use of insertion/selection sort. // However, the variable size of the entries makes using a heapsort // or quicksort rather cumbersome, so since it is quite close to the // limits of efficient insertion/selection sort, we'll use that anyway. void sortConstantPool() { cp_info newcp[] = new cp_info[constant_pool_count]; short redirect[] = new short[constant_pool_count]; newcp[0] = constant_pool[0]; // the 0-entry stays put redirect[0] = (short)0; int smallest,j; for (int i=1;i " + i); if (constant_pool[smallest].tag==cp_info.CONSTANT_Double || constant_pool[smallest].tag==cp_info.CONSTANT_Long) { redirect[++smallest] = (short)(++i); newcp[i] = constant_pool[smallest]; } } // constant pool is now sorted into newcp changeConstantPool(redirect,newcp,constant_pool_count); G.v().out.println("Finished sorting constant pool"); } // just a wrapper for the debigulation, so we can elegantly allocate // a new debigulator, debigualte and then produce some output void debigulate(boolean attribs,boolean privates) { Debig debigulator = new Debig(this); debigulator.debigulate(attribs,privates); debigulator.setCF(null); inf.verboseReport(G.v().out); }*/ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy