/* 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 <tt>.class</tt> file.
* <p>
* 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 <tt>.class</tt> 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 <i>this</i>.
* @see CONSTANT_Class_info
*/
public int this_class;
/** Constant pool index of the Class constant describing <i>super</i>.
* @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 <i>true</i> 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 <tt>.class</tt> file.
* @return <i>true</i> if read was successful, <i>false</i> 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; j<interfaces_count; j++)
interfaces[j] = d.readUnsignedShort();
}
//G.v().out.println("Implements " + interfaces_count + " interface(s)");
Timers.v().fieldTimer.start();
fields_count = d.readUnsignedShort();
//G.v().out.println("Has " + fields_count + " field(s)");
readFields(d);
Timers.v().fieldTimer.end();
Timers.v().methodTimer.start();
methods_count = d.readUnsignedShort();
//G.v().out.println("Has " + methods_count + " method(s)");
readMethods(d);
Timers.v().methodTimer.end();
Timers.v().attributeTimer.start();
attributes_count = d.readUnsignedShort();
//G.v().out.println("Has " + attributes_count + " attribute(s)");
if (attributes_count>0) {
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 <tt>.class</tt> file.
* @return <i>true</i> if read was successful, <i>false</i> 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<constant_pool_count;i++) {
if (skipone) {
skipone = false;
continue;
}
tag = (byte)d.readUnsignedByte();
switch(tag) {
case cp_info.CONSTANT_Class:
cp = new CONSTANT_Class_info();
((CONSTANT_Class_info)cp).name_index = d.readUnsignedShort();
if (debug) G.v().out.println("Constant pool[" + i + "]: Class");
break;
case cp_info.CONSTANT_Fieldref:
cp = new CONSTANT_Fieldref_info();
((CONSTANT_Fieldref_info)cp).class_index = d.readUnsignedShort();
((CONSTANT_Fieldref_info)cp).name_and_type_index =
d.readUnsignedShort();
if (debug) G.v().out.println("Constant pool[" + i + "]: Fieldref");
break;
case cp_info.CONSTANT_Methodref:
cp = new CONSTANT_Methodref_info();
((CONSTANT_Methodref_info)cp).class_index = d.readUnsignedShort();
((CONSTANT_Methodref_info)cp).name_and_type_index =
d.readUnsignedShort();
if (debug) G.v().out.println("Constant pool[" + i + "]: Methodref");
break;
case cp_info.CONSTANT_InterfaceMethodref:
cp = new CONSTANT_InterfaceMethodref_info();
((CONSTANT_InterfaceMethodref_info)cp).class_index =
d.readUnsignedShort();
((CONSTANT_InterfaceMethodref_info)cp).name_and_type_index =
d.readUnsignedShort();
if (debug)
G.v().out.println("Constant pool[" + i + "]: MethodHandle");
break;
case cp_info.CONSTANT_String:
cp = new CONSTANT_String_info();
((CONSTANT_String_info)cp).string_index =
d.readUnsignedShort();
if (debug) G.v().out.println("Constant pool[" + i + "]: String");
break;
case cp_info.CONSTANT_Integer:
cp = new CONSTANT_Integer_info();
((CONSTANT_Integer_info)cp).bytes = d.readInt();
if (debug) G.v().out.println("Constant pool[" + i + "]: Integer = " +
((CONSTANT_Integer_info)cp).bytes);
break;
case cp_info.CONSTANT_Float:
cp = new CONSTANT_Float_info();
((CONSTANT_Float_info)cp).bytes = d.readInt();
if (debug) G.v().out.println("Constant pool[" + i + "]: Float = " +
((CONSTANT_Float_info)cp).convert());
break;
case cp_info.CONSTANT_Long:
cp = new CONSTANT_Long_info();
((CONSTANT_Long_info)cp).high = d.readInt() & 0xFFFFFFFFL;
((CONSTANT_Long_info)cp).low = d.readInt() & 0xFFFFFFFFL;
if (debug) {
String temp = cp.toString(constant_pool);
G.v().out.println("Constant pool[" + i + "]: Long = " + temp);
/*G.v().out.println("Constant pool[" + i + "]: that's " +
cp.printBits(((CONSTANT_Long_info)cp).high) + " <<32 + " +
cp.printBits(((CONSTANT_Long_info)cp).low) + " = " +
cp.printBits(((CONSTANT_Long_info)cp).convert()));*/
}
skipone = true; // next entry needs to be skipped
break;
case cp_info.CONSTANT_Double:
cp = new CONSTANT_Double_info();
((CONSTANT_Double_info)cp).high = d.readInt() & 0xFFFFFFFFL;
((CONSTANT_Double_info)cp).low = d.readInt() & 0xFFFFFFFFL;
if (debug) G.v().out.println("Constant pool[" + i + "]: Double = " +
((CONSTANT_Double_info)cp).convert());
skipone = true; // next entry needs to be skipped
break;
case cp_info.CONSTANT_NameAndType:
cp = new CONSTANT_NameAndType_info();
((CONSTANT_NameAndType_info)cp).name_index =
d.readUnsignedShort();
((CONSTANT_NameAndType_info)cp).descriptor_index =
d.readUnsignedShort();
if (debug) G.v().out.println("Constant pool[" + i + "]: Name and Type");
break;
case cp_info.CONSTANT_Utf8:
CONSTANT_Utf8_info cputf8 = new CONSTANT_Utf8_info(d);
// If an equivalent CONSTANT_Utf8 already exists, we return
// the pre-existing one and allow cputf8 to be GC'd.
cp = (cp_info) CONSTANT_Utf8_collector.v().add(cputf8);
if (debug)
G.v().out.println("Constant pool[" + i + "]: Utf8 = \"" +
cputf8.convert() + "\"");
break;
case cp_info.CONSTANT_MethodHandle:
cp = new CONSTANT_MethodHandle_info();
((CONSTANT_MethodHandle_info)cp).kind = d.readByte();
((CONSTANT_MethodHandle_info)cp).target_index = d.readUnsignedShort();
break;
case cp_info.CONSTANT_MethodType:
// RoboVM note: Handle CONSTANT_MethodType_info structures
cp = new CONSTANT_MethodType_info();
((CONSTANT_MethodType_info)cp).descriptor_index = d.readUnsignedShort();
break;
case cp_info.CONSTANT_InvokeDynamic:
cp = new CONSTANT_InvokeDynamic_info();
((CONSTANT_InvokeDynamic_info)cp).bootstrap_method_index = d.readUnsignedShort();
((CONSTANT_InvokeDynamic_info)cp).name_and_type_index = d.readUnsignedShort();
break;
default:
G.v().out.println("Unknown tag in constant pool: " +
tag + " at entry " + i);
return false;
}
cp.tag = tag;
constant_pool[i] = cp;
}
return true;
}
/** Reads in the given number of attributes from the given stream.
* @param d Stream forming the <tt>.class</tt> file.
* @param attributes_count number of attributes to read in.
* @param ai pre-allocated array of attributes to be filled in.
* @return <i>true</i> if read was successful, <i>false</i> 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;i<attributes_count;i++) {
j = d.readUnsignedShort(); // read attribute name before allocating
len = d.readInt() & 0xFFFFFFFFL;
s = ((CONSTANT_Utf8_info)(constant_pool[j])).convert();
if (s.compareTo(attribute_info.SourceFile)==0)
{
SourceFile_attribute sa = new SourceFile_attribute();
sa.sourcefile_index = d.readUnsignedShort();
a = (attribute_info)sa;
} else if(s.compareTo(attribute_info.ConstantValue)==0)
{
ConstantValue_attribute ca = new ConstantValue_attribute();
ca.constantvalue_index = d.readUnsignedShort();
a = (attribute_info)ca;
} else if(s.compareTo(attribute_info.Code)==0)
{
Code_attribute ca = new Code_attribute();
ca.max_stack = d.readUnsignedShort();
ca.max_locals = d.readUnsignedShort();
ca.code_length = d.readInt() & 0xFFFFFFFFL;
ca.code = new byte[(int) ca.code_length];
d.read(ca.code);
ca.exception_table_length = d.readUnsignedShort();
ca.exception_table = new exception_table_entry[ca.exception_table_length];
int k;
exception_table_entry e;
for (k=0; k<ca.exception_table_length; k++) {
e = new exception_table_entry();
e.start_pc = d.readUnsignedShort();
e.end_pc = d.readUnsignedShort();
e.handler_pc = d.readUnsignedShort();
e.catch_type = d.readUnsignedShort();
ca.exception_table[k] = e;
}
ca.attributes_count = d.readUnsignedShort();
ca.attributes = new attribute_info[ca.attributes_count];
readAttributes(d,ca.attributes_count,ca.attributes);
a = (attribute_info)ca;
} else if(s.compareTo(attribute_info.Exceptions)==0)
{
Exception_attribute ea = new Exception_attribute();
ea.number_of_exceptions = d.readUnsignedShort();
if (ea.number_of_exceptions>0) {
int k;
ea.exception_index_table = new int[ea.number_of_exceptions];
for (k=0; k<ea.number_of_exceptions; k++)
ea.exception_index_table[k] = d.readUnsignedShort();
}
a = (attribute_info)ea;
} else if(s.compareTo(attribute_info.LineNumberTable)==0)
{
LineNumberTable_attribute la = new LineNumberTable_attribute();
la.line_number_table_length = d.readUnsignedShort();
int k;
line_number_table_entry e;
la.line_number_table = new
line_number_table_entry[la.line_number_table_length];
for (k=0; k<la.line_number_table_length; k++) {
e = new line_number_table_entry();
e.start_pc = d.readUnsignedShort();
e.line_number = d.readUnsignedShort();
la.line_number_table[k] = e;
}
a = (attribute_info)la;
}
else if(s.compareTo(attribute_info.LocalVariableTable)==0)
{
LocalVariableTable_attribute la = new LocalVariableTable_attribute();
la.local_variable_table_length = d.readUnsignedShort();
int k;
local_variable_table_entry e;
la.local_variable_table =
new local_variable_table_entry[la.local_variable_table_length];
for (k=0; k<la.local_variable_table_length; k++) {
e = new local_variable_table_entry();
e.start_pc = d.readUnsignedShort();
e.length = d.readUnsignedShort();
e.name_index = d.readUnsignedShort();
e.descriptor_index = d.readUnsignedShort();
e.index = d.readUnsignedShort();
la.local_variable_table[k] = e;
}
a = (attribute_info)la;
}
else if(s.compareTo(attribute_info.LocalVariableTypeTable)==0)
{
LocalVariableTypeTable_attribute la = new LocalVariableTypeTable_attribute();
la.local_variable_type_table_length = d.readUnsignedShort();
int k;
local_variable_type_table_entry e;
la.local_variable_type_table =
new local_variable_type_table_entry[la.local_variable_type_table_length];
for (k=0; k<la.local_variable_type_table_length; k++) {
e = new local_variable_type_table_entry();
e.start_pc = d.readUnsignedShort();
e.length = d.readUnsignedShort();
e.name_index = d.readUnsignedShort();
e.signature_index = d.readUnsignedShort();
e.index = d.readUnsignedShort();
la.local_variable_type_table[k] = e;
}
a = (attribute_info)la;
}
else if (s.compareTo(attribute_info.Synthetic)==0){
Synthetic_attribute ia = new Synthetic_attribute();
a = (attribute_info)ia;
}
else if (s.compareTo(attribute_info.Signature)==0){
Signature_attribute ia = new Signature_attribute();
ia.signature_index = d.readUnsignedShort();
a = (attribute_info)ia;
}
else if (s.compareTo(attribute_info.Deprecated)==0){
Deprecated_attribute da = new Deprecated_attribute();
a = (attribute_info)da;
}
else if (s.compareTo(attribute_info.EnclosingMethod)==0){
EnclosingMethod_attribute ea = new EnclosingMethod_attribute();
ea.class_index = d.readUnsignedShort();
ea.method_index = d.readUnsignedShort();
a = (attribute_info)ea;
}
else if(s.compareTo(attribute_info.InnerClasses)==0)
{
InnerClasses_attribute ia = new InnerClasses_attribute();
ia.inner_classes_length = d.readUnsignedShort();
ia.inner_classes = new inner_class_entry[ia.inner_classes_length];
for (int k = 0; k < ia.inner_classes_length; k++) {
inner_class_entry e = new inner_class_entry();
e.inner_class_index = d.readUnsignedShort();
e.outer_class_index = d.readUnsignedShort();
e.name_index = d.readUnsignedShort();
e.access_flags = d.readUnsignedShort();
ia.inner_classes[k] = e;
}
a = (attribute_info)ia;
}
else if (s.compareTo(attribute_info.RuntimeVisibleAnnotations)==0)
{
RuntimeVisibleAnnotations_attribute ra = new RuntimeVisibleAnnotations_attribute();
ra.number_of_annotations = d.readUnsignedShort();
ra.annotations = new annotation[ra.number_of_annotations];
for (int k = 0; k < ra.number_of_annotations; k++){
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);
ra.annotations[k] = annot;
}
a = (attribute_info)ra;
}
else if (s.compareTo(attribute_info.RuntimeInvisibleAnnotations)==0)
{
RuntimeInvisibleAnnotations_attribute ra = new RuntimeInvisibleAnnotations_attribute();
ra.number_of_annotations = d.readUnsignedShort();
ra.annotations = new annotation[ra.number_of_annotations];
for (int k = 0; k < ra.number_of_annotations; k++){
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);
ra.annotations[k] = annot;
}
a = (attribute_info)ra;
}
else if (s.compareTo(attribute_info.RuntimeVisibleParameterAnnotations)==0)
{
RuntimeVisibleParameterAnnotations_attribute ra = new RuntimeVisibleParameterAnnotations_attribute();
ra.num_parameters = d.readUnsignedByte();
ra.parameter_annotations = new parameter_annotation[ra.num_parameters];
for (int x = 0; x < ra.num_parameters; x++){
parameter_annotation pAnnot = new parameter_annotation();
pAnnot.num_annotations = d.readUnsignedShort();
pAnnot.annotations = new annotation[pAnnot.num_annotations];
for (int k = 0; k < pAnnot.num_annotations; k++){
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);
pAnnot.annotations[k] = annot;
}
ra.parameter_annotations[x] = pAnnot;
}
a = (attribute_info)ra;
}
else if (s.compareTo(attribute_info.RuntimeInvisibleParameterAnnotations)==0)
{
RuntimeInvisibleParameterAnnotations_attribute ra = new RuntimeInvisibleParameterAnnotations_attribute();
ra.num_parameters = d.readUnsignedByte();
ra.parameter_annotations = new parameter_annotation[ra.num_parameters];
for (int x = 0; x < ra.num_parameters; x++){
parameter_annotation pAnnot = new parameter_annotation();
pAnnot.num_annotations = d.readUnsignedShort();
pAnnot.annotations = new annotation[pAnnot.num_annotations];
for (int k = 0; k < pAnnot.num_annotations; k++){
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);
pAnnot.annotations[k] = annot;
}
ra.parameter_annotations[x] = pAnnot;
}
a = (attribute_info)ra;
}
else if (s.compareTo(attribute_info.AnnotationDefault)==0){
AnnotationDefault_attribute da = new AnnotationDefault_attribute();
element_value [] result = readElementValues(1, d, false, 0);
da.default_value = result[0];
a = (attribute_info)da;
} else if (s.equals(attribute_info.BootstrapMethods)){
BootstrapMethods_attribute bsma = new BootstrapMethods_attribute();
int count = d.readUnsignedShort();
bsma.method_handles = new short[count];
bsma.arg_indices = new short[count][];
for(int num=0;num<count;num++) {
short index = (short) d.readUnsignedShort();
bsma.method_handles[num] = index;
int argCount = d.readUnsignedShort();
bsma.arg_indices[num] = new short[argCount];
for(int numArg=0;numArg<argCount;numArg++) {
short indexArg = (short) d.readUnsignedShort();
bsma.arg_indices[num][numArg] = indexArg;
}
}
assert bootstrap_methods_attribute==null : "More than one bootstrap methods attribute!";
a = bootstrap_methods_attribute = bsma;
} else {
// unknown attribute
// G.v().out.println("Generic/Unknown Attribute: " + s);
Generic_attribute ga = new Generic_attribute();
if (len>0) {
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 <tt>.class</tt> file.
* @return <i>true</i> if read was successful, <i>false</i> 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;i<fields_count;i++) {
fi = new field_info();
fi.access_flags = d.readUnsignedShort();
fi.name_index = d.readUnsignedShort();
fi.descriptor_index = d.readUnsignedShort();
fi.attributes_count = d.readUnsignedShort();
if (fi.attributes_count>0) {
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 <tt>.class</tt> file.
* @return <i>true</i> if read was successful, <i>false</i> 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;i<methods_count;i++) {
mi = new method_info();
mi.access_flags = d.readUnsignedShort();
mi.name_index = d.readUnsignedShort();
mi.descriptor_index = d.readUnsignedShort();
mi.attributes_count = d.readUnsignedShort();
CONSTANT_Utf8_info ci;
ci = (CONSTANT_Utf8_info)(constant_pool[mi.name_index]);
//G.v().out.println("Has " + mi.attributes_count + " attribute(s)");
if (mi.attributes_count>0) {
mi.attributes = new attribute_info[mi.attributes_count];
readAttributes(d,mi.attributes_count,mi.attributes);
for (int j=0; j<mi.attributes_count; j++)
{
if (mi.attributes[j] instanceof Code_attribute)
{
mi.code_attr = (Code_attribute)mi.attributes[j];
break;
}
}
}
/*if ("main".compareTo(ci.convert())==0) {
decompile(mi);
}*/
methods[i] = mi;
}
return true;
}
/* DEPRECATED
public void showByteCode(Code_attribute ca) {
int i=0,j;
G.v().out.println("Code bytes follow...");
while(i<ca.code_length) {
j = (int)(ca.code[i]);
j &= 0xff;
G.v().out.print(Integer.toString(j) + " ");
i++;
}
G.v().out.println("");
}*/
/** Writes the current constant pool to the given stream.
* @param dd output stream.
* @return <i>true</i> if write was successful, <i>false</i> 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;i<constant_pool_count;i++) {
if (skipone) {
skipone = false;
continue;
}
cp = constant_pool[i];
dd.writeByte(cp.tag);
switch(cp.tag) {
case cp_info.CONSTANT_Class:
dd.writeShort(((CONSTANT_Class_info)cp).name_index);
break;
case cp_info.CONSTANT_Fieldref:
dd.writeShort(((CONSTANT_Fieldref_info)cp).class_index);
dd.writeShort(((CONSTANT_Fieldref_info)cp).name_and_type_index);
break;
case cp_info.CONSTANT_Methodref:
dd.writeShort(((CONSTANT_Methodref_info)cp).class_index);
dd.writeShort(((CONSTANT_Methodref_info)cp).name_and_type_index);
break;
case cp_info.CONSTANT_InterfaceMethodref:
dd.writeShort(((CONSTANT_InterfaceMethodref_info)cp).class_index);
dd.writeShort(((CONSTANT_InterfaceMethodref_info)cp).name_and_type_index);
break;
case cp_info.CONSTANT_String:
dd.writeShort(((CONSTANT_String_info)cp).string_index);
break;
case cp_info.CONSTANT_Integer:
dd.writeInt((int) ((CONSTANT_Integer_info)cp).bytes);
break;
case cp_info.CONSTANT_Float:
dd.writeInt((int) ((CONSTANT_Float_info)cp).bytes);
break;
case cp_info.CONSTANT_Long:
dd.writeInt((int) ((CONSTANT_Long_info)cp).high);
dd.writeInt((int) ((CONSTANT_Long_info)cp).low);
skipone = true;
break;
case cp_info.CONSTANT_Double:
dd.writeInt((int) ((CONSTANT_Double_info)cp).high);
dd.writeInt((int) ((CONSTANT_Double_info)cp).low);
skipone = true;
break;
case cp_info.CONSTANT_NameAndType:
dd.writeShort(((CONSTANT_NameAndType_info)cp).name_index);
dd.writeShort(((CONSTANT_NameAndType_info)cp).descriptor_index);
break;
case cp_info.CONSTANT_Utf8:
((CONSTANT_Utf8_info)cp).writeBytes(dd);
break;
default:
G.v().out.println("Unknown tag in constant pool: " + cp.tag);
return false;
}
}
return true;
}
/** Writes the given array of attributes to the given stream.
* @param dd output stream.
* @param attributes_count number of attributes to write.
* @param ai array of attributes to write.
* @return <i>true</i> if write was successful, <i>false</i> 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;i<attributes_count;i++) {
a = ai[i];
dd.writeShort(a.attribute_name);
dd.writeInt((int) a.attribute_length);
if (a instanceof SourceFile_attribute) {
SourceFile_attribute sa = (SourceFile_attribute)a;
dd.writeShort(sa.sourcefile_index);
} else if(a instanceof ConstantValue_attribute) {
ConstantValue_attribute ca = (ConstantValue_attribute)a;
dd.writeShort(ca.constantvalue_index);
} else if(a instanceof Code_attribute) {
Code_attribute ca = (Code_attribute)a;
dd.writeShort(ca.max_stack);
dd.writeShort(ca.max_locals);
dd.writeInt((int) ca.code_length);
dd.write(ca.code,0, (int) ca.code_length);
dd.writeShort(ca.exception_table_length);
int k;
exception_table_entry e;
for (k=0; k<ca.exception_table_length; k++) {
e = ca.exception_table[k];
dd.writeShort(e.start_pc);
dd.writeShort(e.end_pc);
dd.writeShort(e.handler_pc);
dd.writeShort(e.catch_type);
}
dd.writeShort(ca.attributes_count);
if (ca.attributes_count>0)
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; k<ea.number_of_exceptions; k++)
dd.writeShort(ea.exception_index_table[k]);
}
} else if(a instanceof LineNumberTable_attribute) {
LineNumberTable_attribute la = (LineNumberTable_attribute)a;
dd.writeShort(la.line_number_table_length);
int k;
line_number_table_entry e;
for (k=0; k<la.line_number_table_length; k++) {
e = la.line_number_table[k];
dd.writeShort(e.start_pc);
dd.writeShort(e.line_number);
}
} else if(a instanceof LocalVariableTable_attribute) {
LocalVariableTable_attribute la = (LocalVariableTable_attribute)a;
dd.writeShort(la.local_variable_table_length);
int k;
local_variable_table_entry e;
for (k=0; k<la.local_variable_table_length; k++) {
e = la.local_variable_table[k];
dd.writeShort(e.start_pc);
dd.writeShort(e.length);
dd.writeShort(e.name_index);
dd.writeShort(e.descriptor_index);
dd.writeShort(e.index);
}
} else {
// unknown attribute
G.v().out.println("Generic/Unknown Attribute in output");
Generic_attribute ga = (Generic_attribute)a;
if (ga.attribute_length>0) {
dd.write(ga.info,0,(int) ga.attribute_length);
}
}
}
return true;
}
/** Writes the fields to the given stream.
* @param dd output stream.
* @return <i>true</i> if write was successful, <i>false</i> on some error.
* @exception java.io.IOException on error.
*/
protected boolean writeFields(DataOutputStream dd) throws IOException {
field_info fi;
int i;
for (i=0;i<fields_count;i++) {
fi = fields[i];
dd.writeShort(fi.access_flags);
dd.writeShort(fi.name_index);
dd.writeShort(fi.descriptor_index);
dd.writeShort(fi.attributes_count);
if (fi.attributes_count>0) {
writeAttributes(dd,fi.attributes_count,fi.attributes);
}
}
return true;
}
/** Writes the methods to the given stream.
* @param dd output stream.
* @return <i>true</i> if write was successful, <i>false</i> on some error.
* @exception java.io.IOException on error.
*/
protected boolean writeMethods(DataOutputStream dd) throws IOException {
method_info mi;
int i;
for (i=0;i<methods_count;i++) {
mi = methods[i];
dd.writeShort(mi.access_flags);
dd.writeShort(mi.name_index);
dd.writeShort(mi.descriptor_index);
dd.writeShort(mi.attributes_count);
if (mi.attributes_count>0) {
writeAttributes(dd,mi.attributes_count,mi.attributes);
}
}
return true;
}
/** Writes this entire ClassFile object to the given stream.
* @param dd output stream.
* @return <i>true</i> if write was successful, <i>false</i> 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; j<interfaces_count; j++)
dd.writeShort(interfaces[j]);
}
dd.writeShort(fields_count);
writeFields(dd);
dd.writeShort(methods_count);
writeMethods(dd);
dd.writeShort(attributes_count);
if (attributes_count>0) {
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(j<ca.code_length) {
inst = bc.disassemble_bytecode(ca.code,j);
inst.originalIndex = j;
// G.v().out.println(inst + ": " + (((int)(inst.code))&0xff));
// G.v().out.println(j + " : " + inst);
if (inst instanceof Instruction_Unknown) {
G.v().out.println("Unknown instruction in \"" + m.toName(constant_pool) +
"\" at offset " + j);
G.v().out.println(" bytecode = " + (((int)(inst.code))&0xff));
}
// G.v().out.println("before: " + j);
j = inst.nextOffset(j);
// G.v().out.println("after: " + j);
if ( head==null )
head = inst;
else
{
tail.next = inst;
inst.prev = tail;
}
tail = inst;
}
// bytecode converted into instructions, now build pointers
bc.build(head);
// also change exception table to use pointers instead of absolute addresses
for (j=0;j<ca.exception_table_length;j++) {
e = ca.exception_table[j];
e.start_inst = bc.locateInst(e.start_pc);
if (e.end_pc == ca.code_length)
e.end_inst = null;
else
e.end_inst = bc.locateInst(e.end_pc);
e.handler_inst = bc.locateInst(e.handler_pc);
if (e.handler_inst!=null)
e.handler_inst.labelled = true;
}
m.instructions = head;
for (attribute_info element : ca.attributes) {
if (element instanceof LineNumberTable_attribute)
{
LineNumberTable_attribute lntattr =
(LineNumberTable_attribute)element;
for (line_number_table_entry element0 : lntattr.line_number_table) {
element0.start_inst =
bc.locateInst(element0.start_pc);
}
}
}
// RoboVM note: Start change. Keep track of the start and end instructions for the scope of local vars.
LocalVariableTable_attribute la = ca.findLocalVariableTable();
if (la != null) {
for (local_variable_table_entry entry : la.local_variable_table) {
entry.start_inst = bc.locateInst(entry.start_pc);
if (entry.start_pc + entry.length < ca.code_length) {
entry.end_inst = bc.locateInst(entry.start_pc + entry.length);
}
}
}
// RoboVM note: End change.
return head;
}
/** For every method, this calls parseMethod, storing the list of Instructions
* in the method_info object, and also constructs the corresponding CFG.
* @see ClassFile#parseMethod
* @see CFG
*/
public void parse() {
method_info mi;
int i;
for (i=0;i<methods_count;i++) {
mi = methods[i];
mi.instructions = parseMethod(mi);
//new CFG(mi);
// don't build it right away for now
}
}
/** Recomputes the offset of each Instruction starting from 0;
* used when converting references back to offsets.
* @param i list of Instructions to process.
* @return length of corresponding bytecode.
* @see Instruction#nextOffset
*/
int relabel(Instruction i) {
int index = 0;
while (i!=null) {
i.label = index;
index = i.nextOffset(index);
i = i.next;
}
return index;
}
/** Inversive to parseMethod, this converts the list of
* Instructions stored in a method_info object back to an
* array of bytecode.
* @param m method to unparse.
* @return array of bytecode, or <i>null</i> 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<methods_count;i++) {
mi = methods[i];
// locate code attribute
ca = mi.locate_code_attribute();
if (ca==null) continue;
bc = unparseMethod(mi);
if (bc==null) {
G.v().out.println("Recompile of " + mi.toName(constant_pool) + " failed!");
} else {
ca.code_length = bc.length;
ca.code = bc;
// also recompile exception table
for (j=0;j<ca.exception_table_length;j++) {
e = ca.exception_table[j];
e.start_pc = (e.start_inst.label);
if (e.end_inst!=null)
e.end_pc = (e.end_inst.label);
else
e.end_pc = (int) (ca.code_length);
e.handler_pc = (e.handler_inst.label);
}
}
}
}
/** Static utility method to parse the given method descriptor string.
* @param s descriptor string.
* @return return type of method.
* @see ClassFile#parseDesc
* @see ClassFile#parseMethodDesc_params
*/
static String parseMethodDesc_return(String s) {
int j;
j = s.lastIndexOf(')');
if (j>=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 "<parse error>";
}
/** 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;i<len;i++) {
c = desc.charAt(i);
if (c==DESC_BYTE.charAt(0)) {
param = "byte";
} else if (c==DESC_CHAR.charAt(0)) {
param = "char";
} else if (c==DESC_DOUBLE.charAt(0)) {
param = "double";
} else if (c==DESC_FLOAT.charAt(0)) {
param = "float";
} else if (c==DESC_INT.charAt(0)) {
param = "int";
} else if (c==DESC_LONG.charAt(0)) {
param = "long";
} else if (c==DESC_SHORT.charAt(0)) {
param = "short";
} else if (c==DESC_BOOLEAN.charAt(0)) {
param = "boolean";
} else if (c==DESC_VOID.charAt(0)) {
param = "void";
} else if (c==DESC_ARRAY.charAt(0)) {
arraylevel++;
continue;
} else if (c==DESC_OBJECT.charAt(0)) {
int j;
j = desc.indexOf(';',i+1);
if (j<0) {
G.v().out.println("Warning: Parse error -- can't find a ; in " +
desc.substring(i+1));
param = "<error>";
} else {
if (j-i>10 && 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 <i>null</i> if not found.
* @see method_info#toName
*/
method_info findMethod(String s) {
method_info m;
int i;
for (i=0;i<methods_count;i++) {
m = methods[i];
if (s.equals(m.toName(constant_pool))) {
return m;
}
}
return null;
}
/** Displays a the prototypes for all the methods defined in this ClassFile.
* @see ClassFile#methods
* @see ClassFile#methods_count
* @see method_info#prototype
*/
void listMethods() {
int i;
for (i=0;i<methods_count;i++) {
G.v().out.println(methods[i].prototype(constant_pool));
}
}
/** Displays the entire constant pool.
* @see ClassFile#constant_pool
* @see ClassFile#constant_pool_count
* @see cp_info#toString
*/
void listConstantPool() {
cp_info c;
int i;
// note that we start at 1 in the constant pool
for (i=1;i<constant_pool_count;i++) {
c = constant_pool[i];
G.v().out.println("[" + i + "] " + c.typeName() +
"=" + c.toString(constant_pool));
if ((constant_pool[i]).tag==cp_info.CONSTANT_Long ||
(constant_pool[i]).tag==cp_info.CONSTANT_Double) {
// must skip an entry after a long or double constant
i++;
}
}
}
/** Displays the list of fields defined in this ClassFile, including
* any static initializers (constants).
* @see ClassFile#fields
* @see ClassFile#fields_count
* @see field_info#prototype
* @see ConstantValue_attribute
*/
void listFields() {
field_info fi;
ConstantValue_attribute cva;
CONSTANT_Utf8_info cm;
int i,j;
for (i=0;i<fields_count;i++) {
fi = fields[i];
G.v().out.print(fi.prototype(constant_pool));
// see if has a constant value attribute
for (j=0;j<fi.attributes_count;j++) {
cm = (CONSTANT_Utf8_info)(constant_pool[fi.attributes[j].attribute_name]);
if (cm.convert().compareTo(attribute_info.ConstantValue)==0) {
cva = (ConstantValue_attribute)(fi.attributes[j]);
//dm = (CONSTANT_Utf8_info)(constant_pool[cva.constantvalue_index]);
G.v().out.print(" = " +
constant_pool[cva.constantvalue_index].
toString(constant_pool));
break;
}
}
G.v().out.println(";");
}
}
/** Moves a method to a different index in the methods array.
* @param m name of method to move.
* @param pos desired index.
* @see ClassFile#methods
*/
void moveMethod(String m,int pos) {
int i,j;
method_info mthd;
G.v().out.println("Moving " + m + " to position " + pos +
" of " + methods_count);
for (i=0;i<methods_count;i++) {
if (m.compareTo(methods[i].toName(constant_pool))==0) {
mthd = methods[i];
if (i>pos) {
for (j=i;j>pos && j>0;j--)
methods[j] = methods[j-1];
methods[pos] = mthd;
} else if (i<pos) {
for (j=i;j<pos && j<methods_count-1;j++)
methods[j] = methods[j+1];
methods[pos] = mthd;
}
return;
}
}
}
/** Answers whether this class is an immediate descendant (as subclass or
* as an implementation of an interface) of the given class.
* @param cf ClassFile of supposed parent.
* @return <i>true</i> if it is a parent, <i>false</i> 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 <i>true</i> if it is a parent, <i>false</i> 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;i<interfaces_count;i++) {
cf = constant_pool[interfaces[i]];
if (cf.toString(constant_pool).compareTo(cname)==0) return true;
}
return false;
}
/** Answers whether this class can have subclasses outside its package.
* @return <i>true</i> if it cannot, <i>false</i> 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 <tt>.class</tt> after it,
* this answers whether the class might refer to this ClassFile object.
* @return <i>true</i> if it does, <i>false</i> 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<constant_pool_count;i++) {
if ((constant_pool[i]).tag==cp_info.CONSTANT_Methodref) {
CONSTANT_Methodref_info cf = (CONSTANT_Methodref_info)(constant_pool[i]);
CONSTANT_Class_info cc = (CONSTANT_Class_info)
(constant_pool[cf.class_index]);
if (cuClass.equals((CONSTANT_Utf8_info)
(constant_pool[cc.name_index]))) {
CONSTANT_NameAndType_info cn = (CONSTANT_NameAndType_info)
(constant_pool[cf.name_and_type_index]);
if (cuName.equals((CONSTANT_Utf8_info)
(constant_pool[cn.name_index])) &&
cuDesc.equals((CONSTANT_Utf8_info)
(constant_pool[cn.descriptor_index])))
return true;
}
} else if ((constant_pool[i]).tag==
cp_info.CONSTANT_InterfaceMethodref) {
CONSTANT_InterfaceMethodref_info cf =
(CONSTANT_InterfaceMethodref_info)(constant_pool[i]);
CONSTANT_Class_info cc = (CONSTANT_Class_info)
(constant_pool[cf.class_index]);
if (cuClass.equals((CONSTANT_Utf8_info)
(constant_pool[cc.name_index]))) {
CONSTANT_NameAndType_info cn = (CONSTANT_NameAndType_info)
(constant_pool[cf.name_and_type_index]);
if (cuName.equals((CONSTANT_Utf8_info)
(constant_pool[cn.name_index])) &&
cuDesc.equals((CONSTANT_Utf8_info)
(constant_pool[cn.descriptor_index])))
return true;
}
} else if ((constant_pool[i]).tag==cp_info.CONSTANT_Long ||
(constant_pool[i]).tag==cp_info.CONSTANT_Double) {
// must skip an entry after a long or double constant
i++;
}
}
} else {
for (i=1;i<constant_pool_count;i++) {
if ((constant_pool[i]).tag==cp_info.CONSTANT_Fieldref) {
CONSTANT_Fieldref_info cf = (CONSTANT_Fieldref_info)(constant_pool[i]);
CONSTANT_Class_info cc = (CONSTANT_Class_info)
(constant_pool[cf.class_index]);
if (cuClass.equals((CONSTANT_Utf8_info)
(constant_pool[cc.name_index]))) {
CONSTANT_NameAndType_info cn = (CONSTANT_NameAndType_info)
(constant_pool[cf.name_and_type_index]);
if (cuName.equals((CONSTANT_Utf8_info)
(constant_pool[cn.name_index])) &&
cuDesc.equals((CONSTANT_Utf8_info)
(constant_pool[cn.descriptor_index])))
return true;
}
} else if ((constant_pool[i]).tag==cp_info.CONSTANT_Long ||
(constant_pool[i]).tag==cp_info.CONSTANT_Double) {
// must skip an entry after a long or double constant
i++;
}
}
}
return false;
}
// produces a sorted array of constant pool indices, one for each Utf8 entry used
// by any field
short[] forbiddenFields() {
short fFields[] = new short[fields_count];
for (int i=0;i<fields_count;i++) {
fFields[i] = fields[i].name_index;
}
// now to sort the array
return sortShorts(fFields);
}
// sorts an array of shorts using selection sort. It's assumed no valid
// entry is 0.
static short[] sortShorts(short a[]) {
int i,largest;
short s;
for(largest = a.length-1;largest>=1;largest--) {
for (i=0;i<largest;i++) {
if (a[i]>a[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<constant_pool_count;i++) redirect[i] = (short)0;
for (int i=1;i<constant_pool_count;i++) {
for (smallest = 1;smallest<constant_pool_count;smallest++)
if (redirect[smallest]==(short)0) break;
//G.v().out.println(" smallest = " + smallest);
j = (constant_pool[smallest].tag==cp_info.CONSTANT_Double ||
constant_pool[smallest].tag==cp_info.CONSTANT_Long) ? smallest+2 : smallest+1;
for (;j<constant_pool_count;j++) {
if ((redirect[j]==(short)0) && constant_pool[j].
compareTo(constant_pool,constant_pool[smallest],constant_pool)<0) {
smallest = j;
}
if (constant_pool[j].tag==cp_info.CONSTANT_Double ||
constant_pool[j].tag==cp_info.CONSTANT_Long) j++;
}
redirect[smallest] = (short)i;
newcp[i] = constant_pool[smallest];
//G.v().out.println(" Smallest cp entry is [" + smallest + "] = " + constant_pool[smallest]
// + " -> " + 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);
}*/
}