/*
* @(#)ClassInfo.java 1.48 06/10/22
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* 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 Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package components;
import jcc.Util;
import jcc.Str2ID;
import consts.Const;
import util.*;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;
/**
* ClassInfo is the root of all information about the class
* which comes out of the .class file, including its methods,
* fields, interfaces, superclass, and any other attributes we
* read in.
* ClassInfo does not contain (in theory) any VM-specific data.
* All the target-VM-specific information is kept in the corresponding
* ClassClass: see field vmClass. ClassClass is abstract, so what is
* really present is a target-VM-specific subclass of it, such as CVMClass.
*/
public
class ClassInfo
{
public int majorVersion;
public int minorVersion;
public String className;
public int access;
public ClassConstant thisClass;
public ClassConstant superClass;
public ClassInfo superClassInfo;
// Tables for all fields, methods and constants of this class
public FieldInfo fields[];
public MethodInfo methods[];
private static ConstantPool nullCP =
new ConstantPool(new ConstantObject[0]);
private int sharedCPIdx = -1;
private ConstantPool cp = nullCP;
private ConstantObject oldConstants[];
public ClassConstant interfaces[];
// These are the tables of instance fields and methods.
// The methodtable is turned into the virtual method lookup table
// of the virtual machine.
public FieldInfo fieldtable[];
public MethodInfo methodtable[];
/*
* Info on each slot of an object, indexed by offset
* instead of on its field #
* The difference between fieldtable and slottable is slight
* -- its just that double-word fields (types J and D) take
* up 2 entries in the slottable.
*/
public FieldInfo slottable[];
// Class attributes that we do not interpret
public Attribute[] classAttributes;
public SourceFileAttribute sourceFileAttr;
// Innerclass attribute accessed.
public InnerClassAttribute innerClassAttr;
// Class generics signature info:
public SignatureAttribute signatureAttr;
public ClassLoader loader;
public vm.ClassClass vmClass; // used by in-core output writers
public Vector allInterfaces;
protected boolean verbose;
protected static PrintStream log = System.out;
public static boolean classDebug = false;
public int flags;
public static final int INCLUDE_ALL = 1;
public int group;
public static int finalizerID = Str2ID.sigHash.getID(
/*NOI18N*/"finalize", /*NOI18N*/"()V" );
public boolean hasNonTrivialFinalizer = false;
private int pkgNameLength;
private final char SIGC_PACKAGE = util.ClassFileConst.SIGC_PACKAGE;
public ClassInfo( boolean v ) {
Assert.assertClassloadingIsAllowed();
verbose = v;
flags = INCLUDE_ALL; // by default, we want all members.
// what else should be here?
}
private String genericNativeName;
public void setSharedCPIdx(int i) {
sharedCPIdx = i;
}
public final String getGenericNativeName() {
if (genericNativeName == null)
genericNativeName = createGenericNativeName();
return genericNativeName;
}
public boolean isFinal() {
return ((access & Const.ACC_FINAL) != 0);
}
// Is this class a subclass of java.lang.ref.Reference?
public boolean isReference() {
ClassInfo cinfo = this;
while (cinfo != null) {
if (cinfo.className.equals("java/lang/ref/Reference")) {
return true;
}
cinfo = cinfo.superClassInfo;
}
return false;
}
// Is this class an interface?
public boolean isInterface() {
return ((this.access & Const.ACC_INTERFACE) != 0);
}
// Is this class the java.lang.Object class?
public boolean isJavaLangObject() {
ClassInfo cinfo = this;
if ((cinfo != null) && cinfo.className.equals("java/lang/Object")) {
return true;
}
return false;
}
// This will be overridden by subclasses
protected String createGenericNativeName() {
return Util.convertToClassName(className );
}
/**
* Reads in the constant pool from a classfile, and fills in the cp
* (i.e. constantpool) field of this classinfo instance. The filled in
* constants are not yet in a resolved state.
*/
private void readConstantPool(DataInput in) throws IOException {
int num = in.readUnsignedShort();
if(verbose){
log.println(Localizer.getString(
"classinfo.reading_entries_in_constant_pool",
Integer.toString(num)));
}
ConstantObject[] constants = new ConstantObject[num];
for (int i = 1; i < num; i+=constants[i].nSlots) {
constants[i] = ConstantObject.readObject(in);
constants[i].index = i;
constants[i].containingClass = this;
}
cp = new ConstantPool(constants);
}
/**
* Flatten all CP entries so that they need not indirect through the
* constant pool to get to the symbollic info. For example, a class
* constant is defined by a CP index which point to a UTF8 string. A
* flattened class constant will refer to the UTF8 string directly instead
* of needing to index into the constant pool to get to it.
*/
private void flattenConstants() {
if (verbose) {
log.println(Localizer.getString(
"classinfo.>>>resolving_constants"));
}
ConstantObject constants[] = cp.getConstants();
for (int i = 1; i < constants.length; i+=constants[i].nSlots) {
constants[i].flatten(cp);
}
}
/*
* Iterate through the constant pool.
* Look for class constants that don't point to anything, yet.
* Put those String names in the undefClasses set.
*/
public void
findUndefinedClasses(Set undefClasses) {
ConstantObject constants[] = cp.getConstants();
for (int i = 1; i < constants.length; i+=constants[i].nSlots) {
if (constants[i].tag == Const.CONSTANT_CLASS) {
ClassConstant c = (ClassConstant) constants[i];
if (c.find() == null){
String stringname = c.name.string;
c.forget();
if (stringname.charAt(0) == '['){
// this is an array.
// DO NOT put the array in the table.
// but we may need to put an underlying
// object type in, so take a closer look.
int lindex = stringname.indexOf('L');
if (lindex == -1){
// primitive array type.
// not interesting.
continue;
}
// capture the name between the L and the ;
stringname = stringname.substring(
lindex+1, stringname.length()-1);
if (loader.lookupClass(stringname) != null){
continue; // found it.
}
}
undefClasses.add(stringname);
}
}
}
}
/*
* we can make our own table smaller by deleting unreferenced elements.
* At this point,
* all non-code references into it are by object reference, NEVER
* by index -- everything has been resolved! Thus we can
* compact our table, deleting all the UnicodeConstants.
* We adjust each constant's index entry accordingly.
* Naturally, we preserve the null entries.
*/
public void smashConstantPool() {
ConstantObject constants[] = cp.getConstants();
int nOld = constants.length;
int nNew = 1;
ConstantObject o;
// first, count and index.
for (int i = 1; i < nOld; i += o.nSlots) {
o = constants[i];
if (!o.shared) {
if (o.getReferences() == 0) {
o.index = -1; // trouble.
} else {
// we're keeping it.
o.index = nNew;
nNew += o.nSlots;
}
}
}
// now reallocate and copy.
ConstantObject newConstants[] = new ConstantObject[ nNew ];
int j = 1;
for (int i = 1; i < nOld; i += o.nSlots) {
o = constants[i];
if (!o.shared && (o.getReferences() != 0)) {
// we're keeping it.
newConstants[j] = o;
j += o.nSlots;
}
}
oldConstants = constants;
cp = new ConstantPool(newConstants);
}
public ConstantPool getConstantPool() {
return cp;
}
public void setConstantPool(Vector cps) {
if (sharedCPIdx != -1) {
cp = (ConstantPool)cps.get(sharedCPIdx);
}
}
// Read the list of interfaces this class supports
void readInterfaces( DataInput in ) throws IOException {
int count = in.readUnsignedShort();
if(verbose){
log.println(Localizer.getString(
"classinfo.reading_interfaces_implemented", Integer.toString(count)));
}
interfaces = new ClassConstant[count];
ConstantObject constants[] = cp.getConstants();
for (int i = 0; i < count; i++) {
interfaces[i] = (ClassConstant) constants[in.readUnsignedShort()];
}
}
// Read the list of fields
void readFields( DataInput in ) throws IOException {
int count = in.readUnsignedShort();
fields = new FieldInfo[count];
if(verbose){
log.println(Localizer.getString("classinfo.reading_field_members", Integer.toString(count)));
}
int i, j;
for (i = j = 0; i < count; i++) {
FieldInfo fi = FieldInfo.readField(in, this);
if (fi != null) {
fields[j] = fi;
fields[j].index = j;
fields[j++].captureValueAttribute();
}
}
// If we have discarded any fields, re-create the table.
if (j != i) {
FieldInfo[] smaller = new FieldInfo[j];
System.arraycopy(fields, 0, smaller, 0, j);
fields = smaller;
}
}
// Read the list of methods from classfile
void readMethods( DataInput in, boolean readCode ) throws IOException {
int count = in.readUnsignedShort();
methods = new MethodInfo[count];
if(verbose){
log.println(Localizer.getString(
"classinfo.reading_methods", Integer.toString(count)));
}
int i, j;
for (i = j = 0; i < count; i++) {
MethodInfo mi = MethodInfo.readMethod( in, this, readCode );
if (mi != null) {
methods[j] = mi;
methods[j].index = j++;
}
}
// If we have discarded any methods, re-create the table.
if ( j != i ) {
MethodInfo[] smaller = new MethodInfo[j];
System.arraycopy(methods, 0, smaller, 0, j);
methods = smaller;
}
}
void readAttributes( DataInput in ) throws IOException {
int count = in.readUnsignedShort();
Vector clssAttr = new Vector();
if(verbose){
log.println(Localizer.getString("classinfo.reading_attributes", Integer.toString(count)));
}
ConstantObject constants[] = cp.getConstants();
for (int i = 0; i < count; i++) {
int nameIndex = in.readUnsignedShort();
int bytes = in.readInt();
UnicodeConstant name = (UnicodeConstant)constants[nameIndex];
if (name.string.equals(/*NOI18N*/"SourceFile")) {
if (ClassInfo.classDebug) {
UnicodeConstant srcName =
(UnicodeConstant)constants[in.readUnsignedShort()];
sourceFileAttr =
new SourceFileAttribute(name, bytes, srcName);
clssAttr.addElement(sourceFileAttr);
} else {
byte[] b = new byte[bytes];
in.readFully(b);
clssAttr.addElement
(new UninterpretedAttribute(name, bytes, b));
}
} else if (name.string.equals("InnerClasses")) {
//Added to support the InnerClasses Attribute defined in the
//1.2 JVM Spec.
/* this need not be done if reflection isn't supported */
innerClassAttr = (InnerClassAttribute)InnerClassAttribute.readAttribute(in, bytes, name, constants);
clssAttr.addElement(innerClassAttr);
/* TODO :: BEGIN experimental code for future signature support.
} else if (name.string.equals("Signature")) {
// Added to support the Signature Attribute defined in the
// 1.5 VM spec.
// this need not be done if reflection isn't supported.
int idx = in.readUnsignedShort();
UnicodeConstant signature = (UnicodeConstant)constants[idx];
//StringConstant sigStr = StringConstant.utfToString(signature);
//sigStr = (StringConstant)cp.add(sigStr);
//sigStr.resolve(cp);
//constants = cp.getConstants();
signatureAttr =
//new SignatureAttribute(name, bytes, sigStr);
new SignatureAttribute(name, bytes, signature);
clssAttr.addElement(signatureAttr);
* TODO :: END */
} else {
byte[] b = new byte[bytes];
in.readFully(b);
clssAttr.addElement
(new UninterpretedAttribute(name, bytes, b));
}
}
int nattr = clssAttr.size();
if (nattr > 0) {
this.classAttributes = new Attribute[nattr];
clssAttr.copyInto(classAttributes);
}
}
// Read in the entire classfile.
// Assumes that the file is open, and the magic numbers are o.k.
//
public void
read(DataInput in, boolean readCode)
throws IOException {
readConstantPool(in);
flattenConstants();
Assert.disallowClassloading();
ConstantObject constants[] = cp.getConstants();
access = in.readUnsignedShort();
thisClass = (ClassConstant) constants[in.readUnsignedShort()];
int sup = in.readUnsignedShort();
if (sup != 0) {
superClass = (ClassConstant) constants[sup];
}
className = thisClass.name.string;
pkgNameLength = className.lastIndexOf(SIGC_PACKAGE);
// Read the various parts of the class file
readInterfaces( in );
readFields( in );
readMethods( in, readCode );
readAttributes( in );
/* DONT DO THIS HERE enterClass(); */
Assert.allowClassloading();
}
// Sets the classfile version numner:
public void setVersionInfo(int majorVersion, int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
// Compute the fieldtable for a class. This requires laying
// out the fieldtable for our parent, then adding any fields
// that are not inherited.
public
void buildFieldtable(){
if (this.fieldtable != null) return; // already done.
FieldInfo fieldtable[];
int n;
int fieldoff;
int fieldtableLength = 0;
FieldInfo candidate[] = this.fields;
for( int i =0; i < candidate.length; i++ ){
if ((candidate[i].access & Const.ACC_STATIC) == 0){
fieldtableLength++;
}
}
if ( superClassInfo != null ){
superClassInfo.buildFieldtable();
n = superClassInfo.fieldtable.length;
fieldtableLength += n;
fieldoff = (n==0)? 0
: (superClassInfo.fieldtable[n-1].instanceOffset +
superClassInfo.fieldtable[n-1].nSlots);
fieldtable = new FieldInfo[ fieldtableLength ];
System.arraycopy( superClassInfo.fieldtable, 0, fieldtable, 0, n );
} else {
fieldtable = new FieldInfo[ fieldtableLength ];
n = 0;
fieldoff = 0;
}
for( int i =0; i < candidate.length; i++ ){
if ((candidate[i].access & Const.ACC_STATIC) == 0){
fieldtable[n++] = candidate[i];
candidate[i].instanceOffset = fieldoff;
fieldoff += candidate[i].nSlots;
}
}
this.fieldtable = fieldtable;
//
// Build a mapping of slot id to FieldInfo structure.
// This makes offset based calculations much easier -- for
// example stackmap computation
//
this.slottable = buildSlotTable(fieldtable, fieldoff);
}
//
// Build a mapping of slot id to FieldInfo structure.
// This makes offset based calculations much easier -- for
// example stackmap computation
//
private FieldInfo[] buildSlotTable(FieldInfo[] fieldtable,
int slotTableSize) {
FieldInfo[] slotTable = new FieldInfo[slotTableSize];
int sIdx = 0;
for (int i = 0; i < fieldtable.length; i++) {
FieldInfo fld = fieldtable[i];
if (fld.nSlots == 1) {
slotTable[sIdx] = fld;
} else {
slotTable[sIdx] = fld;
slotTable[sIdx + 1] = fld;
}
sIdx += fld.nSlots;
}
return slotTable;
}
// Compute the method table for a class. This requires laying
// out the method table for our parent, then adding any methods
// that are not inherited.
//
// Jdk 1.4 javac does not insert miranda methods
// into a class if the class is abstract and does not
// declare all the methods of its superinterfaces,
// like older versions of javac. So we are doing it.
public
void buildMethodtable() {
if ( this.methodtable != null ) return; // already done.
MethodInfo table[];
MethodInfo methods0[] = this.methods;
ClassInfo sup = superClassInfo;
int myPkgNameLength = this.pkgNameLength;
if ((sup != null) && ( (sup.access&Const.ACC_INTERFACE)==(this.access&Const.ACC_INTERFACE) ) ) {
sup.buildMethodtable();
table = sup.methodtable;
} else {
table = new MethodInfo[0];
}
//
// This might change soon, if we try to override the non-trivial
// finalizer of the superclass with a trivial one. Otherwise
// it stands.
//
if (sup != null) {
this.hasNonTrivialFinalizer = sup.hasNonTrivialFinalizer;
}
/*
* allocate a temporary table that is certainly large enough.
* It starts as a copy of our parent's table
*/
MethodInfo newTable[] = new MethodInfo[table.length + methods0.length];
int index = table.length;
System.arraycopy( table, 0, newTable, 0, index );
if (sup == null) {
// finalize() goes into slot 0 of java.lang.Object
index++;
}
method_loop:
for (int i = 0; i < methods0.length; i++) {
if ((methods0[i].access & (Const.ACC_STATIC|Const.ACC_PRIVATE)) != 0) {
/* static and private methods don't go in the table */
continue method_loop;
} else if (methods0[i].name.string.equals(/*NOI18N*/"<init>")) {
/* <init> methods don't go in the table */
continue method_loop;
} else if (sup == null && methods0[i].name.string.equals(/*NOI18N*/"finalize")
&& methods0[i].type.string.equals(/*NOI18N*/"()V")) {
/* java.lang.Object.finalize()V always goes at the top of the table*/
newTable[0] = methods0[i];
newTable[0].methodTableIndex = 0; // 1=>0 EJVM
continue method_loop;
}
int j;
int thisID = methods0[i].getID();
if (thisID == finalizerID) {
/* Note that this class has its own finalizer */
this.hasNonTrivialFinalizer = methods0[i].isNonTrivial();
}
/*
* Now search the table (initialized with our parent's table)
* for a name/type match.
* Since private methods aren't even in the table, we don't have
* to worry about those. But we do need to consider
* package scoping.
*/
match_loop:
for ( j = 0; j < table.length; j++) {
if (thisID == table[j].getID()) {
MethodInfo parentMethod = table[j];
if ((parentMethod.access & (Const.ACC_PUBLIC|Const.ACC_PROTECTED))
== 0)
{
/*
* we must be in the same package to override this one
* sadly, the only way to compare packages is
* to compare names
*/
int parentPkgNameLength = parentMethod.parent.pkgNameLength;
boolean equalPkgNames;
if (parentPkgNameLength != myPkgNameLength){
/* package names of unequal length. Quick failure */
equalPkgNames = false;
} else if (myPkgNameLength == -1){
equalPkgNames = true; // neither is in a package.
} else {
equalPkgNames = parentMethod.parent.className.regionMatches(
0, className, 0, myPkgNameLength);
}
if (! equalPkgNames){
/* package names of unequal value. Failure */
continue match_loop;
}
}
newTable[j] = methods0[i];
newTable[j].methodTableIndex = j + 0; // 1=>0 EJVM
continue method_loop;
}
}
// If we're not overriding our parent's method we do add
// a new entry to the method table.
newTable[index] = methods0[i];
newTable[index].methodTableIndex = index + 0; // 1=>0 EJVM
index++;
}
// now allocate a table of the correct size.
MethodInfo methodTable0[] = new MethodInfo[index];
System.arraycopy( newTable, 0, methodTable0, 0, index );
this.methodtable = methodTable0;
// now try to resolve miranda methods.
MethodInfo thisMethodtable[] = null;
MethodInfo thisMethods[] = null;
if ((this.access&Const.ACC_ABSTRACT) != 0 &&
(this.access&Const.ACC_INTERFACE) == 0) {
int i;
int methodsnumber = methods.length;
if (allInterfaces == null) {
findAllInterfaces();
}
for (i = 0; i < allInterfaces.size(); i++) {
ClassInfo superinterface = (ClassInfo)allInterfaces.get(i);
superinterface.buildMethodtable();
MethodInfo interfaceMethodtable[] = superinterface.methodtable;
MethodInfo tmpTable[] = new MethodInfo[interfaceMethodtable.length];
int idx = 0;
interface_method_loop:
for (int j = 0; j < interfaceMethodtable.length; j++) {
int imID = interfaceMethodtable[j].getID();
for (int k = 0; k < methodtable.length; k++) {
if (imID == methodtable[k].getID()) {
continue interface_method_loop;
}
}
// We need to get a copy of the methodInfo, because
// we are going to change its states.
try {
tmpTable[idx] =
(MethodInfo)(interfaceMethodtable[j].clone());
tmpTable[idx].parent = this;
tmpTable[idx].index = methodsnumber;
methodsnumber++;
} catch (CloneNotSupportedException cnse) {
cnse.printStackTrace();
}
idx++;
}
// We need to insert these miranda methods into
// both this.methods[] and this.methodtable[].
// The reason is methods[] data structure is
// used to resolve MethodRef constant in quicken
// process, while methodtable[] is used in
// InterfaceMethodTable.generateInterfaceVector()
// to verify if the class implements all the
// interface methods.
if (idx > 0) {
int newMethodsNum = methods.length + idx;
thisMethods = new MethodInfo[newMethodsNum];
System.arraycopy(methods, 0, thisMethods, 0, methods.length);
System.arraycopy(tmpTable, 0, thisMethods, methods.length, idx);
this.methods = thisMethods;
int newTableLen = methodtable.length + idx;
thisMethodtable = new MethodInfo[newTableLen];
System.arraycopy(methodtable, 0,
thisMethodtable, 0, methodtable.length);
System.arraycopy(tmpTable, 0,
thisMethodtable, methodtable.length, idx);
for (int mtidx = methodtable.length; mtidx < newTableLen; mtidx++) {
thisMethodtable[mtidx].methodTableIndex = mtidx + 0;
}
this.methodtable = thisMethodtable;
}
}
}
}
private static boolean
conditionalAdd( Vector v, Object o ){
if ( v.contains( o ) )
return false;
v.addElement( o );
return true;
}
/*
* Compute the vector of all interfaces this class implements (or
* this interface extends). Not only the interfaced declared in
* the implements clause, which is what the interfaces[] field
* represents, but all interfaces, including those of our superclasses
* and those extended/implemented by any interfaces we implement.
*
*/
public void
findAllInterfaces(){
/*
* This works recursively, by computing parent's interface
* set first. THIS ASSUMES NON-CIRCULARITY, as does the rest
* of the Java system. This assumption will fail loudly, if
* at all.
*/
if ( allInterfaces != null )
return; // already done
if ( superClassInfo == null ){
// we must be java.lang.Object!
allInterfaces = new Vector(5); // a generous size.
} else {
if ( superClassInfo.allInterfaces == null )
superClassInfo.findAllInterfaces();
allInterfaces = (Vector)(superClassInfo.allInterfaces.clone());
}
if ( interfaces == null ) return; // all done!
for( int i = 0; i < interfaces.length; i++ ){
ClassInfo interf = interfaces[i].find();
if ((interf == null) ||
((interf.access & Const.ACC_INTERFACE) == 0)) {
System.err.println(Localizer.getString(
"classinfo.class_which_should_be_an_interface_but_is_not",
className, interfaces[i]));
continue;
}
if ( interf.allInterfaces == null )
interf.findAllInterfaces();
if ( ! conditionalAdd( allInterfaces, interf ) ){
// if this interface was already in the set,
// then all the interfaces that it extend/implement
// will be, too.
continue;
}
Enumeration interfInterf = interf.allInterfaces.elements();
while( interfInterf.hasMoreElements() ){
conditionalAdd( allInterfaces, interfInterf.nextElement() );
}
}
}
public boolean
countReferences(boolean isRelocatable) {
if (isRelocatable) {
thisClass.incReference();
if (superClass != null) superClass.incReference();
}
// count interface references
if (interfaces != null) {
for (int i = 0; i < interfaces.length ; i++) {
interfaces[i].incReference();
}
}
// then count references from fields.
if (fields != null) {
for (int i = 0; i < fields.length; i++) {
fields[i].countConstantReferences(isRelocatable);
}
}
// then count references from code
if (methods != null) {
for (int i = 0; i < methods.length; i++) {
methods[i].countConstantReferences(cp, isRelocatable);
}
}
Attribute.countConstantReferences(classAttributes, isRelocatable);
return true;
}
/*
* sweep over own constant pool. For each constant in it,
* ensure that
* (a) the index is correct.
* (b) two-word constants are followed by null entries,
* one-word entries are not.
* (c) there are no UnicodeConstants.
* (d) no constant is flagged as 'shared'
* (e) no constant is unreferenced.
*/
public void
validateConstantPool(){
ConstantObject[] constantPool = cp.getConstants();
if (constantPool == null)
return; // it can happen!
int nConsts = constantPool.length;
if (constantPool[0] != null){
throw new ValidationException(
"Constant pool entry 0 must be zero in" +className);
}
for (int i = 1; i < nConsts; i++ ){
ConstantObject c = constantPool[i];
if (c == null){
throw new ValidationException(
"Unexpected null constant pool entry in "+className);
}
if (c.index != i){
throw new ValidationException(
"Bad constant self-index in "+className, c);
}
if (c.shared){
throw new ValidationException(
"Shared constant in private pool in "+className, c);
}
if (c instanceof DoubleValueConstant){
// following element must be zero.
i += 1;
if ((i >= nConsts) || (constantPool[i] != null)){
throw new ValidationException(
"Bad double value constant (no null) in "+className, c);
}
}else if (c instanceof UnicodeConstant){
throw new ValidationException(
"Constant pool contains Unicode constant in "+className, c);
}
if (c.getReferences() + c.getLdcReferences() == 0) {
throw new ValidationException(
"Constant pool contains unreferenced constant in "+className, c);
}
}
}
/*
* This method can only be called at the very end, just before output
* writing. It does a sanity check of data structures. Notably,
* it makes sure that all constants referenced are or are not in constant pools.
* If any assumptions are violated a ValidationException will be thrown.
*/
public void
validate( ConstantPool sharedPool ){
ConstantObject constantPool[];
if (superClassInfo == null){
if (!className.equals("java/lang/Object")){
throw new ValidationException("Parentless class",className);
}
}
for ( int i = 0; i < fields.length; i++ ){
if (fields[i].parent != this){
throw new ValidationException(
"Class containg non-member field", className);
}
fields[i].validate();
}
if (sourceFileAttr != null)
sourceFileAttr.validate();
if (innerClassAttr != null)
innerClassAttr.validate();
if (signatureAttr != null) {
signatureAttr.validate();
}
if (sharedPool == null){
// not sharing. Use our own pool.
constantPool = cp.getConstants();
// sweep our pool
validateConstantPool();
}else{
constantPool = sharedPool.getConstants();
}
for ( int i = 0; i < methods.length; i++ ){
if (methods[i].parent != this){
throw new ValidationException(
"Class containg non-member method", className);
}
methods[i].validate(constantPool);
}
}
public boolean
relocateReferences(){
for ( int i = 0; i < methods.length; i++ ){
methods[i].relocateConstantReferences( oldConstants );
}
return true;
}
public void
clearMemberFlags( int flagsToClear ){
int mask = ~ flagsToClear;
int n;
ClassMemberInfo members[];
if ( fields != null ){
members = fields;
n = members.length;
for ( int i = 0; i < n; i++ ){
members[i].flags &= mask;
}
}
if ( fields != null ){
members = methods;
n = members.length;
for ( int i = 0; i < n; i++ ){
members[i].flags &= mask;
}
}
}
private static void
dumpComponentTable( PrintStream o, String title, ClassComponent t[] ){
int n;
if ( (t == null) || ((n=t.length) == 0) ) return;
o.print( title ); o.println(/*NOI18N*/"["+n+"]:");
for( int i = 0; i < n; i++ ){
if ( t[i] != null )
o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i]);
}
}
private static void
dumpConstantTable( PrintStream o, String title, ConstantPool cp ){
ConstantObject t[] = cp.getConstants();
int n;
if ( (t == null) || ((n=t.length) == 0) ) return;
o.print( title ); o.println(/*NOI18N*/"["+n+/*NOI18N*/"]:");
o.println(/*NOI18N*/"\tPosition Index\tNrefs");
for (int i = 0; i < n; i++) {
if (t[i] != null) {
o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i].index+
/*NOI18N*/"\t"+t[i].getReferences() +
/*NOI18N*/"\t"+t[i]);
}
}
}
private static void
dumpMemberTable( PrintStream o, String title, ClassMemberInfo t[] ){
int n;
if ( (t == null) || ((n=t.length) == 0) ) return;
o.print( title ); o.println(/*NOI18N*/":");
for( int i = 0; i < n; i++ ){
if ( t[i] != null )
o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i].qualifiedName() );
}
}
public void
dump( PrintStream o ){
o.print(Util.accessToString(access)+/*NOI18N*/"Class "+thisClass);
if ( superClass != null )
o.print(/*NOI18N*/" extends "+superClass);
if ( interfaces!=null && interfaces.length != 0 ){
o.print(/*NOI18N*/" implements");
for( int i = 0; i < interfaces.length ; i++ ){
o.print(" "+interfaces[i]);
}
}
o.println();
dumpComponentTable( o, /*NOI18N*/"Methods", methods );
dumpComponentTable( o, /*NOI18N*/"Fields", fields );
if ( fieldtable != null )
dumpMemberTable( o, /*NOI18N*/"Fieldtable", fieldtable );
if ( methodtable != null )
dumpMemberTable( o, /*NOI18N*/"Methodtable", methodtable );
dumpConstantTable( o, /*NOI18N*/"Constants", cp );
}
public static boolean resolveSupers(){
Enumeration allclasses = ClassTable.elements();
boolean ok = true;
while( allclasses.hasMoreElements() ){
ClassInfo cinfo = (ClassInfo)allclasses.nextElement();
if ( cinfo.superClass==null ){
// only java.lang.Object can be parentless
if ( ! cinfo.className.equals( /*NOI18N*/"java/lang/Object" ) ){
log.println(Localizer.getString(
"classinfo.class_is_parent-less", cinfo.className));
ok = false;
}
} else {
ClassInfo superClassInfo =
cinfo.loader.lookupClass(cinfo.superClass.name.string);
if ( superClassInfo == null ){
log.println(Localizer.getString(
"classinfo.class_is_missing_parent", cinfo.className,
cinfo.superClass.name.string ));
ok = false;
} else {
cinfo.superClassInfo = superClassInfo;
}
}
if (cinfo.interfaces != null ) {
for (int i = 0; i < cinfo.interfaces.length; ++i) {
ClassConstant cc = cinfo.interfaces[i];
if (cc.find() == null) {
log.println(Localizer.getString("classinfo.class_is_missing_parent", cinfo.className, cc.name.string));
ok = false;
}
}
}
}
return ok;
}
//
// write still used by Strip
//
// write constants back out, just like we read them in.
void writeConstantPool( DataOutput out ) throws IOException {
ConstantObject constants[] = cp.getConstants();
int num = constants==null ? 0 : constants.length;
if(verbose){
log.println(Localizer.getString("classinfo.writing_constant_pool_entries", Integer.toString(num)));
}
out.writeShort( num );
for (int i = 1; i < num; i+=constants[i].nSlots) {
constants[i].write( out );
}
}
void writeInterfaces( DataOutput out ) throws IOException {
int count = interfaces==null ? 0 : interfaces.length;
if(verbose){
log.println(Localizer.getString("classinfo.writing_interfaces_implemented", Integer.toString(count)));
}
out.writeShort( count );
for (int i = 0; i < count; i++) {
out.writeShort( interfaces[i].index );
}
}
void writeFields( DataOutput out ) throws IOException {
int count = fields==null ? 0 : fields.length;
if(verbose){
log.println(Localizer.getString("classinfo.writing_field_members", Integer.toString(count)));
}
out.writeShort( count );
for (int i = 0; i < count; i++) {
fields[i].write( out );
}
}
void writeMethods( DataOutput out) throws IOException {
int count = methods==null ? 0 : methods.length;
if(verbose){
log.println(Localizer.getString("classinfo.writing_methods", Integer.toString(count)));
}
out.writeShort(count);
for (int i = 0; i < count; i++) {
methods[i].write( out );
}
}
void writeTableAttribute( DataOutput out, UnicodeConstant name, FMIrefConstant table[] ) throws IOException {
if (verbose){
log.println(Localizer.getString("classinfo.writing_name", name.string));
}
out.writeShort( name.index );
int n = table.length;
out.writeInt( 2*n );
for ( int i = 0; i < n; i++ ){
out.writeShort( table[i].index );
}
}
void writeAttributes( DataOutput out ) throws IOException {
int count = 0;
if (classAttributes != null) {
count += classAttributes.length;
}
out.writeShort(count);
if (classAttributes != null) {
for (int k = 0; k < classAttributes.length; k++) {
classAttributes[k].write( out );
}
}
}
public void
write( DataOutput o ) throws IOException {
writeConstantPool( o );
o.writeShort( access );
o.writeShort( thisClass.index );
o.writeShort( superClass==null? 0 : superClass.index );
writeInterfaces( o );
writeFields( o );
writeMethods( o );
writeAttributes( o );
}
public String
toString(){
return /*NOI18N*/"ClassInfo-\""+className+/*NOI18N*/"\"";
}
// Convert ldc to ldc2
public void relocateAndPackCode(boolean noCodeCompaction) {
for ( int i = 0; i < methods.length; i++ )
methods[i].relocateAndPackCode(cp, noCodeCompaction);
}
// Pass lists of excluded methods & fields to the MethodInfo
// and FieldInfo classes.
static public void setExcludeLists( Vector methodExcludeList,
Vector fieldExcludeList ) {
MethodInfo.setExcludeList(methodExcludeList);
FieldInfo.setExcludeList(fieldExcludeList);
return;
}
}