blob: bf2625797d5ce5de55844a72b85c0a8fec887da4 [file] [log] [blame]
/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
*
* This program and the accompanying materials are made available under
* the terms of the Common Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/cpl-v10.html
*
* $Id: ClassDef.java,v 1.1.1.1.2.1 2004/07/16 23:32:30 vlad_r Exp $
*/
package com.vladium.jcd.cls;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import com.vladium.jcd.cls.attribute.AttributeElementFactory;
import com.vladium.jcd.cls.attribute.Attribute_info;
import com.vladium.jcd.cls.attribute.CodeAttribute_info;
import com.vladium.jcd.cls.attribute.InnerClassesAttribute_info;
import com.vladium.jcd.cls.constant.CONSTANT_Class_info;
import com.vladium.jcd.cls.constant.CONSTANT_Fieldref_info;
import com.vladium.jcd.cls.constant.CONSTANT_NameAndType_info;
import com.vladium.jcd.cls.constant.CONSTANT_String_info;
import com.vladium.jcd.cls.constant.CONSTANT_Utf8_info;
import com.vladium.jcd.compiler.IClassFormatOutput;
import com.vladium.jcd.lib.Types;
import com.vladium.jcd.lib.UDataOutputStream;
import com.vladium.util.ByteArrayOStream;
// ----------------------------------------------------------------------------
/**
* This class represents the abstract syntax table (AST) that {@link com.vladium.jcd.parser.ClassDefParser}
* produces from bytecode. Most elements are either settable or extendible.
* This class also implements {@link com.vladium.jcd.compiler.IClassFormatOutput}
* and works with {@link com.vladium.jcd.compiler.ClassWriter} to produce
* bytecode without an external compiler.<P>
*
* MT-safety: this class and all interfaces used by it are not safe for
* access from multiple concurrent threads.
*
* @author (C) 2001, Vlad Roubtsov
*/
public
final class ClassDef implements Cloneable, IAccessFlags, IClassFormatOutput
{
// public: ................................................................
public ClassDef ()
{
m_version = new int [2];
m_constants = ElementFactory.newConstantCollection (-1);
m_interfaces = ElementFactory.newInterfaceCollection (-1);
m_fields = ElementFactory.newFieldCollection (-1);
m_methods = ElementFactory.newMethodCollection (-1);
m_attributes = ElementFactory.newAttributeCollection (-1);
}
// Visitor:
public void accept (final IClassDefVisitor visitor, final Object ctx)
{
visitor.visit (this, ctx);
}
public long getMagic ()
{
return m_magic;
}
public void setMagic (final long magic)
{
m_magic = magic;
}
public int [] getVersion ()
{
return m_version;
}
public void setVersion (final int [] version)
{
m_version [0] = version [0];
m_version [1] = version [1];
}
public final void setDeclaredSUID (final long suid)
{
m_declaredSUID = suid;
}
public int getThisClassIndex ()
{
return m_this_class_index;
}
public void setThisClassIndex (final int this_class_index)
{
m_this_class_index = this_class_index;
}
public CONSTANT_Class_info getThisClass ()
{
return (CONSTANT_Class_info) m_constants.get (m_this_class_index);
}
public CONSTANT_Class_info getSuperClass ()
{
return (CONSTANT_Class_info) m_constants.get (m_super_class_index);
}
public String getName ()
{
return getThisClass ().getName (this);
}
public int getSuperClassIndex ()
{
return m_super_class_index;
}
public void setSuperClassIndex (final int super_class_index)
{
m_super_class_index = super_class_index;
}
// IAccessFlags:
public final int getAccessFlags ()
{
return m_access_flags;
}
public final void setAccessFlags (final int flags)
{
m_access_flags = flags;
}
public boolean isInterface ()
{
return (m_access_flags & ACC_INTERFACE) != 0;
}
public boolean isSynthetic ()
{
return m_attributes.hasSynthetic ();
}
public boolean isNested (final int [] nestedAccessFlags)
{
final InnerClassesAttribute_info innerClassesAttribute = m_attributes.getInnerClassesAttribute ();
if (innerClassesAttribute == null)
return false;
else
return innerClassesAttribute.makesClassNested (m_this_class_index, nestedAccessFlags);
}
// methods for getting various nested tables:
public IConstantCollection getConstants ()
{
return m_constants;
}
public IInterfaceCollection getInterfaces ()
{
return m_interfaces;
}
public IFieldCollection getFields ()
{
return m_fields;
}
public IMethodCollection getMethods ()
{
return m_methods;
}
public IAttributeCollection getAttributes ()
{
return m_attributes;
}
public int [] getFields (final String name)
{
return m_fields.get (this, name);
}
public int [] getMethods (final String name)
{
return m_methods.get (this, name);
}
// Cloneable:
/**
* Performs a deep copy.
*/
public Object clone ()
{
try
{
final ClassDef _clone = (ClassDef) super.clone ();
// do deep copy:
_clone.m_version = (int []) m_version.clone ();
_clone.m_constants = (IConstantCollection) m_constants.clone ();
_clone.m_interfaces = (IInterfaceCollection) m_interfaces.clone ();
_clone.m_fields = (IFieldCollection) m_fields.clone ();
_clone.m_methods = (IMethodCollection) m_methods.clone ();
_clone.m_attributes = (IAttributeCollection) m_attributes.clone ();
return _clone;
}
catch (CloneNotSupportedException e)
{
throw new InternalError (e.toString ());
}
}
// IClassFormatOutput:
public void writeInClassFormat (final UDataOutputStream out) throws IOException
{
if (out == null) throw new IllegalArgumentException ("null input: out");
out.writeU4 (m_magic);
out.writeU2 (m_version [1]);
out.writeU2 (m_version [0]);
m_constants.writeInClassFormat (out);
out.writeU2 (m_access_flags);
out.writeU2 (m_this_class_index);
out.writeU2 (m_super_class_index);
m_interfaces.writeInClassFormat (out);
m_fields.writeInClassFormat (out);
m_methods.writeInClassFormat (out);
m_attributes.writeInClassFormat (out);
}
public final long getDeclaredSUID ()
{
return m_declaredSUID;
}
/**
* This follows the spec at http://java.sun.com/j2se/1.4.1/docs/guide/serialization/spec/class.doc6.html#4100
* as well as undocumented hacks used by Sun's 1.4.2 J2SDK
*/
public final long computeSUID (final boolean skipCLINIT)
{
long result = m_declaredSUID;
if (result != 0L)
return result;
else
{
try
{
final ByteArrayOStream bout = new ByteArrayOStream (1024); // TODO: reuse these
final DataOutputStream dout = new DataOutputStream (bout);
// (1) The class name written using UTF encoding:
dout.writeUTF (Types.vmNameToJavaName (getName ())); // [in Java format]
// (2) The class modifiers written as a 32-bit integer:
// ACC_STATIC is never written for nested classes/interfaces;
// however, ACC_SUPER must be ignored:
{
// this is tricky: for static/non-static nested classes that
// were declared protected in the source the usual access flags
// will have ACC_PUBLIC set; the only way to achieve J2SDK
// compatibility is to recover the source access flags
// from the InnerClasses attribute:
final int [] nestedAccessFlags = new int [1];
final int modifiers = (isNested (nestedAccessFlags)
? nestedAccessFlags [0]
: getAccessFlags ())
& (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT);
// if/when emma decides to instrument interfaces for <clinit>
// coverage, compensate for javac bug in which ABSTRACT bit
// was set for an interface only if the interface declared methods
// [Sun's J2SDK]:
dout.writeInt (modifiers);
}
// not doing another J2SDK compensation for arrays, because
// we never load/instrument those
// (3) The name of each interface sorted by name written using UTF encoding
{
final IInterfaceCollection interfaces = getInterfaces ();
final String [] ifcs = new String [interfaces.size ()];
final int iLimit = ifcs.length;
for (int i = 0; i < iLimit; ++ i)
{
// [in Java format]
ifcs [i] = Types.vmNameToJavaName (((CONSTANT_Class_info) m_constants.get (interfaces.get (i))).getName (this));
}
Arrays.sort (ifcs);
for (int i = 0; i < iLimit; ++ i)
{
dout.writeUTF (ifcs [i]);
}
}
// (4) For each field of the class sorted by field name (except
// private static and private transient fields):
// a. The name of the field in UTF encoding.
// b. The modifiers of the field written as a 32-bit integer.
// c. The descriptor of the field in UTF encoding
{
final IFieldCollection fields = getFields ();
final FieldDescriptor [] fds = new FieldDescriptor [fields.size ()];
int fcount = 0;
for (int f = 0, fLimit = fds.length; f < fLimit; ++ f)
{
final Field_info field = fields.get (f);
final int modifiers = field.getAccessFlags ();
if (((modifiers & ACC_PRIVATE) == 0) ||
((modifiers & (ACC_STATIC | ACC_TRANSIENT)) == 0))
fds [fcount ++] = new FieldDescriptor (field.getName (this), modifiers, field.getDescriptor (this));
}
if (fcount > 0)
{
Arrays.sort (fds, 0, fcount);
for (int i = 0; i < fcount; ++ i)
{
final FieldDescriptor fd = fds [i];
dout.writeUTF (fd.m_name);
dout.writeInt (fd.m_modifiers);
dout.writeUTF (fd.m_descriptor);
}
}
}
// (5) If a class initializer exists, write out the following:
// a. The name of the method, <clinit>, in UTF encoding.
// b. The modifier of the method, ACC_STATIC, written as a 32-bit integer.
// c. The descriptor of the method, ()V, in UTF encoding.
// (6) For each non-private constructor sorted by method name and signature:
// a. The name of the method, <init>, in UTF encoding.
// b. The modifiers of the method written as a 32-bit integer.
// c. The descriptor of the method in UTF encoding.
// (7) For each non-private method sorted by method name and signature:
// a. The name of the method in UTF encoding.
// b. The modifiers of the method written as a 32-bit integer.
// c. The descriptor of the method in UTF encoding.
// note: although this is not documented, J2SDK code uses '.''s as
// descriptor separators (this is done for methods only, not for fields)
{
final IMethodCollection methods = getMethods ();
boolean hasCLINIT = false;
final ConstructorDescriptor [] cds = new ConstructorDescriptor [methods.size ()];
final MethodDescriptor [] mds = new MethodDescriptor [cds.length];
int ccount = 0, mcount = 0;
for (int i = 0, iLimit = cds.length; i < iLimit; ++ i)
{
final Method_info method = methods.get (i);
final String name = method.getName (this);
if (! hasCLINIT && IClassDefConstants.CLINIT_NAME.equals (name))
{
hasCLINIT = true;
continue;
}
else
{
final int modifiers = method.getAccessFlags ();
if ((modifiers & ACC_PRIVATE) == 0)
{
if (IClassDefConstants.INIT_NAME.equals (name))
cds [ccount ++] = new ConstructorDescriptor (modifiers, method.getDescriptor (this));
else
mds [mcount ++] = new MethodDescriptor (name, modifiers, method.getDescriptor (this));
}
}
}
if (hasCLINIT && ! skipCLINIT)
{
dout.writeUTF (IClassDefConstants.CLINIT_NAME);
dout.writeInt (ACC_STATIC);
dout.writeUTF (IClassDefConstants.CLINIT_DESCRIPTOR);
}
if (ccount > 0)
{
Arrays.sort (cds, 0, ccount);
for (int i = 0; i < ccount; ++ i)
{
final ConstructorDescriptor cd = cds [i];
dout.writeUTF (IClassDefConstants.INIT_NAME);
dout.writeInt (cd.m_modifiers);
dout.writeUTF (cd.m_descriptor.replace ('/', '.'));
}
}
if (mcount > 0)
{
Arrays.sort (mds, 0, mcount);
for (int i = 0; i < mcount; ++ i)
{
final MethodDescriptor md = mds [i];
dout.writeUTF (md.m_name);
dout.writeInt (md.m_modifiers);
dout.writeUTF (md.m_descriptor.replace ('/', '.'));
}
}
}
dout.flush();
if (DEBUG_SUID)
{
byte [] dump = bout.copyByteArray ();
for (int x = 0; x < dump.length; ++ x)
{
System.out.println ("DUMP[" + x + "] = " + dump [x] + "\t" + (char) dump[x]);
}
}
final MessageDigest md = MessageDigest.getInstance ("SHA");
md.update (bout.getByteArray (), 0, bout.size ());
final byte [] hash = md.digest ();
if (DEBUG_SUID)
{
for (int x = 0; x < hash.length; ++ x)
{
System.out.println ("HASH[" + x + "] = " + hash [x]);
}
}
// final int hash0 = hash [0];
// final int hash1 = hash [1];
// result = ((hash0 >>> 24) & 0xFF) | ((hash0 >>> 16) & 0xFF) << 8 | ((hash0 >>> 8) & 0xFF) << 16 | ((hash0 >>> 0) & 0xFF) << 24 |
// ((hash1 >>> 24) & 0xFF) << 32 | ((hash1 >>> 16) & 0xFF) << 40 | ((hash1 >>> 8) & 0xFF) << 48 | ((hash1 >>> 0) & 0xFF) << 56;
for (int i = Math.min (hash.length, 8) - 1; i >= 0; -- i)
{
result = (result << 8) | (hash [i] & 0xFF);
}
return result;
}
catch (IOException ioe)
{
throw new Error (ioe.getMessage ());
}
catch (NoSuchAlgorithmException nsae)
{
throw new SecurityException (nsae.getMessage());
}
}
}
public int addCONSTANT_Utf8 (final String value, final boolean keepUnique)
{
if (keepUnique)
{
final int existing = m_constants.findCONSTANT_Utf8 (value);
if (existing > 0)
{
return existing;
}
// [else fall through]
}
return m_constants.add (new CONSTANT_Utf8_info (value));
}
public int addStringConstant (final String value)
{
final int value_index = addCONSTANT_Utf8 (value, true);
// TODO: const uniqueness
return m_constants.add (new CONSTANT_String_info (value_index));
}
public int addNameType (final String name, final String typeDescriptor)
{
final int name_index = addCONSTANT_Utf8 (name, true);
final int descriptor_index = addCONSTANT_Utf8 (typeDescriptor, true);
return m_constants.add (new CONSTANT_NameAndType_info (name_index, descriptor_index));
}
public int addClassref (final String classJVMName)
{
final int name_index = addCONSTANT_Utf8 (classJVMName, true);
// TODO: this should do uniqueness checking:
return m_constants.add (new CONSTANT_Class_info (name_index));
}
/**
* Adds a new declared field to this class [with no attributes]
*/
public int addField (final String name, final String descriptor, final int access_flags)
{
// TODO: support Fields with initializer attributes?
// TODO: no "already exists" check done here
final int name_index = addCONSTANT_Utf8 (name, true);
final int descriptor_index = addCONSTANT_Utf8 (descriptor, true);
final Field_info field = new Field_info (access_flags, name_index, descriptor_index,
ElementFactory.newAttributeCollection (0));
return m_fields.add (field);
}
/**
* Adds a new declared field to this class [with given attributes]
*/
public int addField (final String name, final String descriptor, final int access_flags,
final IAttributeCollection attributes)
{
// TODO: support Fields with initializer attributes?
// TODO: no "already exists" check done here
final int name_index = addCONSTANT_Utf8 (name, true);
final int descriptor_index = addCONSTANT_Utf8 (descriptor, true);
final Field_info field = new Field_info (access_flags, name_index, descriptor_index, attributes);
return m_fields.add (field);
}
// TODO: rework this API
public Method_info newEmptyMethod (final String name, final String descriptor, final int access_flags)
{
// TODO: flag for making synthetic etc
final int attribute_name_index = addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_CODE, true);
final int name_index = addCONSTANT_Utf8 (name, true);
final int descriptor_index = addCONSTANT_Utf8 (descriptor, true);
final IAttributeCollection attributes = ElementFactory.newAttributeCollection (0);
final CodeAttribute_info code = new CodeAttribute_info (attribute_name_index, 0, 0,
CodeAttribute_info.EMPTY_BYTE_ARRAY,
AttributeElementFactory.newExceptionHandlerTable (0),
ElementFactory.newAttributeCollection (0));
attributes.add (code);
final Method_info method = new Method_info (access_flags, name_index, descriptor_index, attributes);
return method;
}
public int addMethod (final Method_info method)
{
return m_methods.add (method);
}
/**
* Adds a reference to a field declared by this class.
*
* @return constant pool index of the reference
*/
public int addFieldref (final Field_info field)
{
// TODO: keepUnique flag
final CONSTANT_NameAndType_info nametype = new CONSTANT_NameAndType_info (field.m_name_index, field.m_descriptor_index);
final int nametype_index = m_constants.add (nametype); // TODO: unique logic
return m_constants.add (new CONSTANT_Fieldref_info (m_this_class_index, nametype_index));
}
/**
* Adds a reference to a field declared by this class.
*
* @return constant pool index of the reference
*/
public int addFieldref (final int offset)
{
// TODO: keepUnique flag
final Field_info field = m_fields.get (offset);
final CONSTANT_NameAndType_info nametype = new CONSTANT_NameAndType_info (field.m_name_index, field.m_descriptor_index);
final int nametype_index = m_constants.add (nametype); // TODO: unique logic
return m_constants.add (new CONSTANT_Fieldref_info (m_this_class_index, nametype_index));
}
// protected: .............................................................
// package: ...............................................................
// private: ...............................................................
private static final class FieldDescriptor implements Comparable
{
// Comparable:
public final int compareTo (final Object obj)
{
return m_name.compareTo (((FieldDescriptor) obj).m_name);
}
FieldDescriptor (final String name, final int modifiers, final String descriptor)
{
m_name = name;
m_modifiers = modifiers;
m_descriptor = descriptor;
}
final String m_name;
final int m_modifiers;
final String m_descriptor;
} // end of nested class
private static final class ConstructorDescriptor implements Comparable
{
// Comparable:
public final int compareTo (final Object obj)
{
return m_descriptor.compareTo (((ConstructorDescriptor) obj).m_descriptor);
}
ConstructorDescriptor (final int modifiers, final String descriptor)
{
m_modifiers = modifiers;
m_descriptor = descriptor;
}
final int m_modifiers;
final String m_descriptor;
} // end of nested class
private static final class MethodDescriptor implements Comparable
{
// Comparable:
public final int compareTo (final Object obj)
{
final MethodDescriptor rhs = (MethodDescriptor) obj;
int result = m_name.compareTo (rhs.m_name);
if (result == 0)
result = m_descriptor.compareTo (rhs.m_descriptor);
return result;
}
MethodDescriptor (final String name, final int modifiers, final String descriptor)
{
m_name = name;
m_modifiers = modifiers;
m_descriptor = descriptor;
}
final String m_name;
final int m_modifiers;
final String m_descriptor;
} // end of nested class
// TODO: final fields
private long m_magic;
private int [] /* major, minor */ m_version;
private int m_access_flags;
private int m_this_class_index, m_super_class_index;
private IConstantCollection m_constants;
private IInterfaceCollection m_interfaces;
private IFieldCollection m_fields;
private IMethodCollection m_methods;
private IAttributeCollection m_attributes;
private long m_declaredSUID;
private static final boolean DEBUG_SUID = false;
} // end of class
// ----------------------------------------------------------------------------