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

com.tangosol.dev.assembler.ClassFile Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.dev.assembler;

import java.io.IOException;
import java.io.EOFException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;

import java.util.Arrays;
import java.util.Enumeration;

import com.tangosol.io.ByteArrayReadBuffer;
import com.tangosol.io.ReadBuffer;

import com.tangosol.util.StringTable;

/**
* Represents a Java .class structure (the JVM Class file format).  For
* reference purposes, download the JVM specification from
* http://java.sun.com/docs/index.html.
* 

* Description: *

* The .class structure is hierachical in nature with a single internally * shared resource, the constant pool. In other words, with the exception * of the constant pool, all portions of the .class structure only refer * to themselves or structures which are contained within themselves. The * hierarchy is as follows: *

*

*   1) ClassFile
*       1)  header
*           1)  magic cookie
*           2)  expected JVM version
*       2)  Constant[] (the ConstantPool)
*       3)  class information
*           1)  AccessFlags
*           2)  this class/interface identity
*           3)  super class/interface identity
*           4)  implemented interface identity[]
*       4)  Field[]
*           1)  AccessFlags
*           2)  field identity
*           3)  field data type
*           4)  Attribute[]
*               1)  JVM supports a single "ConstantValue" attribute
*       5)  Method[]
*           1)  AccessFlags
*           2)  method identity
*           3)  method parameter/return signature
*           4)  Attribute[]
*               1)  JVM supports a single "Code" attribute
*                   1)  frame stack area size
*                   2)  frame local variable area size
*                   3)  Op[]
*                   4)  TryCatch[]
*                       1)  Op "try" range [pc,pc)
*                       2)  Handler pc
*                       3)  catchable class identity
*                   5)  Attribute[] (yes, the "Code" attribute has attributes!)
*                       1)  JVM supports a single "LineNumberTable" attribute
*                           - cross-references pc and source line number
*                             values
*                       2)  JVM supports a single "LocalVariableTable" attribute
*                           - cross-references local variable index and local
*                             variable name/data type values
*                       3)  JVM supports a single "LocalVariableTypeTable" attribute
*                           - cross-references local variable index and local
*                             variable name/data signature values
*               2)  JVM supports a single "Exceptions" attribute
*                   1)  declared exception identity[]
*       6)  Attribute[]
*           1)  JVM specification defines (but JVM does not support) a single
*               "SourceFile" attribute
*               - specifies the OS source file name (without any path)
* 
*

* WARNING!!! Other attributes have been introduced since the specification * was last updated. These attributes are similar in structure and naming * conventions to the current attributes and appear to all be related to the * JDK1.1 "inner class" feature. *

* Note: Other attributes than those defined above are legal but ignored * by both the JVM and any other process examining/manipulating/using a * .class structure. Custom-defined attributes must follow the package * and class naming conventions, e.g. "com.tangosol.examples.BreakPointTable". *

* Requirements: *

*

*   1)  The role of the class is to support the contruction, reflection,
*       modification, and transport (i.e. transient storage) of Java
*       ClassFile (aka ".class file") structures
*
*       1)  An assembler will construct ClassFile structures
*
*       2)  The Component Definition will create JCS structures and a
*           disassembler will display detailed information using the
*           reflection information provided by ClassFile structures
*
*       3)  A deployment tool or the class loader will patch up package
*           names by modifying the ClassFile structure
*
*       4)  ClassFile structures provide a type-safe reference type for
*           compiled classes (typically used for passing a compiled class
*           as a parameter or as the type of a return value)
*
*   2)  Efficiency, especially when the ClassFile structure is used only
*       for transport, means that the disassembly of .class structures and
*       the final assembly and linking of accessor-supplied information must
*       be deferred until necessary
*
*   3)  Production of dependency and relationship information which would
*       enable partial compilation, selective re-compilation, BOM/where-used
*       and impact analysis
*
*   4)  Support for partial, incremental, and method (re-)compilation
* 
*

* WARNING!!! This implementation is not intended to be thread-safe. Do * not access a single instance of this class (or any of the other classes * in this package) from more than one thread unless synchronization is * handled by the caller. *

* WARNING!!! This implementation is not intended to be idiot-safe. It is * the responsibility of the caller to provide legal ClassFile information, * and not the responsibility of this implementation to detect it. * * @version 0.10, 02/05/98, prototype dis-assembler * @version 0.50, 05/07/98, assembler/dis-assembler * @author Cameron Purdy */ public class ClassFile extends VMStructure implements Constants { // ----- construction --------------------------------------------------- /** * Construct a new ClassFile object. * * @param sThis fully qualified name of this class * @param sSuper name of the super class, or null if there is no * super class (e.g. java.lang.Object or an interface) * @param fInterface true means that this is an interface */ public ClassFile(String sThis, String sSuper, boolean fInterface) { init(); setLoaded(true); setName(sThis); setSuper(sSuper); setInterface(fInterface); setSynchronized(true); } /** * Constructs a ClassFile from a .class structure stored in a byte array. * * This constructor is preferred to the DataInput-based constructor. * * @param abClazz the byte array containing the .class structure */ public ClassFile(byte[] abClazz) { setBytes(abClazz); } /** * Constructs a ClassFile from a stream containing a .class structure. * * @param stream the java.io.DataInput stream containing the .class * structure */ public ClassFile(DataInput stream) throws IOException { load(stream); } // ----- persistence ---------------------------------------------------- /** * Read the .class structure out of the passed stream. * * @param stream the object implementing java.io.DataInput and containing * the .class structure * * @throws java.io.IOException if an error (besides EOF) occurs reading * from the stream */ public void load(DataInput stream) throws IOException { final int MAX = 8192; byte[] abBuf = new byte[MAX]; ByteArrayOutputStream streamRaw = new ByteArrayOutputStream(MAX); int of = 0; try { while (true) { byte b = stream.readByte(); abBuf[of++] = b; if (of == MAX) { streamRaw.write(abBuf, 0, MAX); of = 0; } } } catch (EOFException e) { if (of > 0) { streamRaw.write(abBuf, 0, of); } } setBytes(streamRaw.toByteArray()); } /** * Write the .class structure into a stream. * * @param stream the object implementing java.io.DataOutput to which * the .class structure will be written * * @throws java.io.IOException if an error occurs writing the stream */ public void save(DataOutput stream) throws IOException { stream.write(getBytes()); } // ----- VMStructure operations ----------------------------------------- /** * The disassembly process reads the structure from the passed input * stream and uses the constant pool to dereference any constant * references. *

* The structure is defined by the Java Virtual Machine Specification as: *

    *   ClassFile
    *       {
    *       u4 magic;
    *       u2 minor_version;
    *       u2 major_version;
    *       u2 constant_pool_count;
    *       cp_info constant_pool[constant_pool_count-1];
    *       u2 access_flags;
    *       u2 this_class;
    *       u2 super_class;
    *       u2 interfaces_count;
    *       u2 interfaces[interfaces_count];
    *       u2 fields_count;
    *       field_info fields[fields_count];
    *       u2 methods_count;
    *       method_info methods[methods_count];
    *       u2 attributes_count;
    *       attribute_info attributes[attributes_count];
    *       }
    * 
* * @param stream the stream implementing java.io.DataInput from which * to read the assembled VM structure * @param pool the constant pool for the class which contains any * constants referenced by this VM structure */ protected void disassemble(DataInput stream, ConstantPool pool) throws IOException { // check header int nCookie = stream.readInt(); if (nCookie != CLASS_COOKIE) { throw new IOException(CLASS + ".disassemble: Class cookie not found!"); } m_nVersionMinor = stream.readUnsignedShort(); m_nVersionMajor = stream.readUnsignedShort(); if ( m_nVersionMajor < VERSION_MAJOR_MIN || (m_nVersionMajor == VERSION_MAJOR_MIN && m_nVersionMinor < VERSION_MINOR_MIN) || m_nVersionMajor > VERSION_MAJOR_MAX || (m_nVersionMajor == VERSION_MAJOR_MAX && m_nVersionMinor > VERSION_MINOR_MAX)) { throw new IOException(CLASS + ".disassemble: Version (" + m_nVersionMajor + '.' + m_nVersionMinor+ ") not supported!"); } // constant pool pool.disassemble(stream, pool); m_pool = pool; // access flags AccessFlags flags = m_flags; flags.disassemble(stream, pool); // identity (this/super) m_clzName = (ClassConstant) pool.getConstant(stream.readUnsignedShort()); m_clzSuper = (ClassConstant) pool.getConstant(stream.readUnsignedShort()); // interfaces m_tblInterface.clear(); int c = stream.readUnsignedShort(); for (int i = 0; i < c; ++i) { ClassConstant clz = (ClassConstant) pool.getConstant(stream.readUnsignedShort()); m_tblInterface.put(clz.getValue(), clz); } // fields m_tblField.clear(); c = stream.readUnsignedShort(); for (int i = 0; i < c; ++i) { Field field = new Field(); field.disassemble(stream, pool); m_tblField.put(field.getIdentity(), field); } // methods m_tblMethod.clear(); c = stream.readUnsignedShort(); String sClass = m_clzName.getValue(); for (int i = 0; i < c; ++i) { Method method = new Method(sClass, flags.isInterface()); method.disassemble(stream, pool); m_tblMethod.put(method.getIdentity(), method); } // attributes m_tblAttribute.clear(); c = stream.readUnsignedShort(); for (int i = 0; i < c; ++i) { Attribute attribute = Attribute.loadAttribute(this, stream, pool); m_tblAttribute.put(attribute.getIdentity(), attribute); } resetModified(); } /** * The pre-assembly step collects the necessary entries for the constant * pool. During this step, all constants used by this VM structure and * any sub-structures are registered with (but not yet bound by position * in) the constant pool. * * @param pool the constant pool for the class which needs to be * populated with the constants required to build this * VM structure */ protected void preassemble(ConstantPool pool) { // register constants used by the class pool.registerConstant(m_clzName); if (m_clzSuper != null) { pool.registerConstant(m_clzSuper); } Enumeration enmr = m_tblInterface.elements(); while (enmr.hasMoreElements()) { pool.registerConstant((ClassConstant) enmr.nextElement()); } // the constant pool doesn't have to register itself // m_pool.preassemble(pool); // register constants used by the fields, methods, and attributes StringTable[] atbl = CONTAINED_TABLE; for (int i = 0; i < atbl.length; ++i) { StringTable tbl = atbl[i]; enmr = tbl.elements(); while (enmr.hasMoreElements()) { ((VMStructure) enmr.nextElement()).preassemble(pool); } } } /** * The assembly process assembles and writes the structure to the passed * output stream, resolving any dependencies using the passed constant * pool. *

* The structure is defined by the Java Virtual Machine Specification as: *

    *   ClassFile
    *       {
    *       u4 magic;
    *       u2 minor_version;
    *       u2 major_version;
    *       u2 constant_pool_count;
    *       cp_info constant_pool[constant_pool_count-1];
    *       u2 access_flags;
    *       u2 this_class;
    *       u2 super_class;
    *       u2 interfaces_count;
    *       u2 interfaces[interfaces_count];
    *       u2 fields_count;
    *       field_info fields[fields_count];
    *       u2 methods_count;
    *       method_info methods[methods_count];
    *       u2 attributes_count;
    *       attribute_info attributes[attributes_count];
    *       }
    * 
* * @param stream the stream implementing java.io.DataOutput to which to * write the assembled VM structure * @param pool the constant pool for the class which by this point * contains the entire set of constants required to build * this VM structure */ protected void assemble(DataOutput stream, ConstantPool pool) throws IOException { // header int nVersionMajor = m_nVersionMajor; stream.writeInt(CLASS_COOKIE); stream.writeShort(m_nVersionMinor); stream.writeShort(m_nVersionMajor); // constant pool m_pool.assemble(stream, pool); // access flags if (nVersionMajor >= 52) { // as of 52.0 ACC_SUPER should always be set m_flags.setSynchronized(true); } m_flags.assemble(stream, pool); // identity (this/super) stream.writeShort(pool.findConstant(m_clzName)); stream.writeShort(m_clzSuper == null ? 0 : pool.findConstant(m_clzSuper)); // interfaces stream.writeShort(m_tblInterface.getSize()); Enumeration enmr = m_tblInterface.elements(); while (enmr.hasMoreElements()) { ClassConstant clz = (ClassConstant) enmr.nextElement(); stream.writeShort(pool.findConstant(clz)); } // fields, methods, attributes StringTable[] atbl = CONTAINED_TABLE; for (int i = 0; i < atbl.length; ++i) { StringTable tbl = atbl[i]; stream.writeShort(tbl.getSize()); enmr = tbl.elements(); while (enmr.hasMoreElements()) { ((VMStructure) enmr.nextElement()).assemble(stream, pool); } } } // ----- Object operations ---------------------------------------------- /** * Produce a human-readable string describing the ClassFile. * * @return a string describing the ClassFile */ public String toString() { StringBuffer sb = new StringBuffer(); boolean fInterface = isInterface(); String sMod = m_flags.toString(fInterface ? ACC_INTERFACE : ACC_CLASS); if (sMod.length() > 0) { sb.append(sMod) .append(' '); } sb.append(fInterface ? "interface " : "class ") .append(m_clzName.getJavaName()); ClassConstant clzSuper = m_clzSuper; if (clzSuper != null) { String sSuper = clzSuper.getValue(); if (!sSuper.equals(DEFAULT_SUPER)) { sb.append(" extends ") .append(clzSuper.getJavaName()); } } if (!m_tblInterface.isEmpty()) { sb.append(fInterface? " extends " : " implements "); Enumeration enmr = m_tblInterface.elements(); boolean fTrailing = false; while (enmr.hasMoreElements()) { if (fTrailing) { sb.append(", "); } sb.append(((ClassConstant) enmr.nextElement()).getJavaName()); fTrailing = true; } } return sb.toString(); } /** * Compare this object to another object for equality. * * @param obj the other object to compare to this * * @return true if this object equals that object */ public boolean equals(Object obj) { if (obj instanceof ClassFile) { ClassFile that = (ClassFile) obj; if (this == that) { return true; } // equal iff .class structure is identical byte[] abThis = this.getBytes(); byte[] abThat = that.getBytes(); return Arrays.equals(abThis, abThat); } return false; } // ----- ClassFile operations ------------------------------------------- /** * Internal initialization. */ protected void init() { m_fModified = false; m_fLoaded = false; m_abClazz = null; m_clzName = null; m_clzSuper = null; m_pool = new ConstantPool(this); m_flags = new AccessFlags(); m_tblInterface = new StringTable(); m_tblField = new StringTable(); m_tblMethod = new StringTable(); m_tblAttribute = new StringTable(); CONTAINED_TABLE[0] = m_tblField; CONTAINED_TABLE[1] = m_tblMethod; CONTAINED_TABLE[2] = m_tblAttribute; } /** * Using the constant pool resolution feature, change all references to * the specified package. * * @param sPkg dot-delimited package name, not null */ public void relocate(String sPkg) { if (!sPkg.replace('.', '/').equals(Relocator.PACKAGE)) { resolve(new Relocator(sPkg)); } } /** * Allow constants in the pool to be replaced by a callback. * * @param resolver the callback */ public void resolve(Resolver resolver) { // get the .class structure byte[] ab = getBytes(); // wipe any disassembled information -- we are going to go directly // after the constant pool portion of the binary .class structure if (isLoaded()) { init(); } try { ByteArrayReadBuffer in = new ByteArrayReadBuffer(ab); ByteArrayOutputStream out = new ByteArrayOutputStream((int) (ab.length * 1.25)); ReadBuffer.BufferInput streamIn = in.getBufferInput(); DataOutput streamOut = new DataOutputStream(out); // the portion of the .class structure before the pool int ofStart = 8; streamIn.setOffset(ofStart); out.write(ab, 0, ofStart); // the constant pool is in the middle (offset 8 bytes) of the .class int cConst = streamIn.readUnsignedShort(); streamOut.writeShort(cConst); for (int i = 1; i < cConst; ++i) { Constant constant = null; int nTag = streamIn.readUnsignedByte(); switch (nTag) { case CONSTANT_UTF8: constant = new UtfConstant(); break; case CONSTANT_INTEGER: constant = new IntConstant(); break; case CONSTANT_FLOAT: constant = new FloatConstant(); break; case CONSTANT_LONG: constant = new LongConstant(); ++i; // uses two constant slots break; case CONSTANT_DOUBLE: constant = new DoubleConstant(); ++i; // uses two constant slots break; case CONSTANT_CLASS: case CONSTANT_STRING: case CONSTANT_METHODTYPE: case CONSTANT_MODULE: case CONSTANT_PACKAGE: streamOut.writeByte(nTag); streamOut.writeShort(streamIn.readUnsignedShort()); break; case CONSTANT_FIELDREF: case CONSTANT_METHODREF: case CONSTANT_INTERFACEMETHODREF: case CONSTANT_NAMEANDTYPE: case CONSTANT_INVOKEDYNAMIC: streamOut.writeByte(nTag); streamOut.writeShort(streamIn.readUnsignedShort()); streamOut.writeShort(streamIn.readUnsignedShort()); break; case CONSTANT_METHODHANDLE: streamOut.writeByte(nTag); streamOut.writeByte(streamIn.readUnsignedByte()); streamOut.writeShort(streamIn.readUnsignedShort()); break; default: throw new IOException("Invalid constant tag " + nTag); } if (constant != null) { constant.disassemble(streamIn, null); constant = resolver.resolve(constant); constant.assemble(streamOut, null); } } // the portion of the .class structure after the pool int ofStop = streamIn.getOffset(); out.write(ab, ofStop, ab.length - ofStop); ab = out.toByteArray(); } catch (IOException e) { throw new RuntimeException("Illegal .class structure!\n" + e.toString()); } // store the new .class structure setBytes(ab); } /** * Write java like descriptions of the byte codes for this class file not * including attributes for each info structure (class | field | method). * * @param out the {@link PrintWriter} to write the text to */ public void dump(PrintWriter out) { // class header boolean fInterface = isInterface(); String sMod = m_flags.toString(fInterface ? ACC_INTERFACE : ACC_CLASS); if (sMod.length() > 0) { out.write(sMod); out.write(' '); } out.write(fInterface ? "interface " : "class "); out.write(m_clzName.getJavaName()); ClassConstant clzSuper = m_clzSuper; if (clzSuper != null) { String sSuper = clzSuper.getValue(); if (!sSuper.equals(DEFAULT_SUPER)) { out.write(" extends "); out.write(clzSuper.getJavaName()); } } if (!m_tblInterface.isEmpty()) { out.write(fInterface? " extends " : " implements "); Enumeration enmr = m_tblInterface.elements(); boolean fTrailing = false; while (enmr.hasMoreElements()) { if (fTrailing) { out.write(", "); } out.write(((ClassConstant) enmr.nextElement()).getJavaName()); fTrailing = true; } } String sIndent = " "; out.println(toString()); out.println(sIndent + '{'); String sBreak = ""; // fields Enumeration enmr = m_tblField.elements(); while (enmr.hasMoreElements()) { Field field = (Field) enmr.nextElement(); out.print(sIndent + field.toString()); if (field.isConstant()) { out.println(" = " + field.getConstantValue() + ';'); } else { out.println(';'); } sBreak = "\n"; } out.print(sBreak); // methods enmr = m_tblMethod.elements(); while (enmr.hasMoreElements()) { //((Method) enmr.nextElement()).dump(out, sIndent); out.print(sIndent); out.println(enmr.nextElement()); } out.println(sIndent + '}'); } // ----- accessor: classfile version ----------------------------------- /** * Get the Classfile format major version number of this class. * * @return the major version number */ public int getMajorVersion() { return m_nVersionMajor; } /** * Set the Classfile format major version number of this class. * * @param nVersionMajor the major version number */ public void setMajorVersion(int nVersionMajor) { m_nVersionMajor = nVersionMajor; setModified(true); } /** * Get the Classfile format minor version number of this class. * * @return the minor version number */ public int getMinorVersion() { return m_nVersionMinor; } /** * Set the Classfile format minor version number of this class. * * @param nVersionMinor the minor version number */ public void setMinorVersion(int nVersionMinor) { m_nVersionMinor = nVersionMinor; setModified(true); } // ----- accessor: loaded ---------------------------------------------- /** * The loaded property refers to the .class structure stored internally * as a byte array with respect to the information stored in the ClassFile * object. If the ClassFile is constructed from a byte array or an input * stream, the information is not expanded immediately in case the reason * for the creation of the ClassFile object is as a type-safe mechanism * for passing compiled .class structures. Before any operations can take * place agains the ClassFile, the .class structure stored in the byte * array must be expanded. * * @return false if the .class structure must be expanded before * queries/modifications against the ClassFile can commence * * @see #ensureLoaded() */ protected boolean isLoaded() { return m_fLoaded; } /** * Set or reset the loaded flag on the ClassFile. * * @param fLoaded true to set the loaded flag, false to reset it */ protected void setLoaded(boolean fLoaded) { m_fLoaded = fLoaded; } /** * Ensure that the ClassFile object is ready to be queried/modified. */ protected void ensureLoaded() { if (!m_fLoaded) { if (m_abClazz != null) { ByteArrayInputStream streamRaw = new ByteArrayInputStream(m_abClazz); DataInput stream = new DataInputStream(streamRaw); try { disassemble(stream, m_pool); } catch (IOException e) { throw ensureRuntimeException(e); } } m_fLoaded = true; } } // ----- accessor: modified -------------------------------------------- /** * The modified property refers to the information stored in the ClassFile * object with respect to the potentially cached .class structure stored * internally as a byte array. If a modification has occurred to the * ClassFile information such that the .class structure (the byte array) * must be re-built, the ClassFile is considered to be modified. When the * .class structure is re-built, the modified flag is reset. * * @return true if the ClassFile information has been modified */ public boolean isModified() { // a class is only modifiable after it is loaded if (!m_fLoaded) { return m_fModified; } if (m_fModified || m_abClazz == null || m_pool.isModified()) { return true; } // check all other VM sub-structures StringTable[] atbl = CONTAINED_TABLE; for (int i = 0; i < atbl.length; ++i) { Enumeration enmr = atbl[i].elements(); while (enmr.hasMoreElements()) { if (((VMStructure) enmr.nextElement()).isModified()) { return true; } } } return false; } /** * Set or reset the modified flag on the ClassFile. * * @param fModified true to set the modified flag, false to reset it */ public void setModified(boolean fModified) { if (fModified) { m_fModified = fModified; } else { resetModified(); } } /** * Reset the modified flag on the ClassFile. */ protected void resetModified() { m_pool.resetModified(); // reset all other VM sub-structures StringTable[] atbl = CONTAINED_TABLE; for (int i = 0; i < atbl.length; ++i) { Enumeration enmr = atbl[i].elements(); while (enmr.hasMoreElements()) { ((VMStructure) enmr.nextElement()).resetModified(); } } m_fModified = false; } // ----- accessor: .class structure ------------------------------------ /** * Access the .class structure as a byte array. Do not modify the return * value from this accessor method. * * @return the .class structure in the form of a byte array */ public byte[] getBytes() { if (isModified()) { // collect all constants ConstantPool pool = m_pool; boolean fOptimize = !pool.isOrderImportant(); if (fOptimize) { if (pool != null) { // detach the previous pool from the ClassFile pool.setClassFile(null); } pool = new ConstantPool(this); } preassemble(pool); if (fOptimize) { pool.sort(); } m_pool = pool; // assemble the class into bytes ByteArrayOutputStream streamRaw = new ByteArrayOutputStream(8192); DataOutputStream stream = new DataOutputStream(streamRaw); try { assemble(stream, pool); } catch (IOException e) { throw ensureRuntimeException(e); } m_abClazz = streamRaw.toByteArray(); resetModified(); } return m_abClazz; } /** * Supply the class structure as a byte array. * * @param abClazz the .class structure in the form of a byte array */ public void setBytes(byte[] abClazz) { if (abClazz == null) { throw new IllegalArgumentException(CLASS + ".setBytes: byte array is required"); } init(); m_abClazz = abClazz; setLoaded(false); } // ----- accessor: .class identity ------------------------------------- /** * Determine the fully qualified name of this class. * * @return this class name */ public String getName() { ensureLoaded(); return m_clzName.getValue(); } /** * Set the fully qualified name of this class. * * @param sName the new class name */ public void setName(String sName) { ensureLoaded(); m_clzName = new ClassConstant(sName); setModified(true); } /** * Determine the class constant for this class. * * @return this class's class constant */ public ClassConstant getClassConstant() { ensureLoaded(); return m_clzName; } // ----- accessor: .class derivation ----------------------------------- /** * Determine the fully qualified name of the super class. * * @return the super class name */ public String getSuper() { ensureLoaded(); return (m_clzSuper == null ? (String) null : m_clzSuper.getValue()); } /** * Set the fully qualified name of the super class. * * @param sSuper the new super class name */ public void setSuper(String sSuper) { ensureLoaded(); m_clzSuper = (sSuper == null ? (ClassConstant) null : new ClassConstant(sSuper)); setModified(true); } /** * Determine the class constant for the super class. * * @return the super class's class constant */ public ClassConstant getSuperClassConstant() { ensureLoaded(); return m_clzSuper; } // ----- accessor: implements ------------------------------------------ /** * Add an implemented interface. (For interfaces, this corresponds to the * Java language "extends" keyword.) * * @param sInterface the interface name */ public void addImplements(String sInterface) { ensureLoaded(); m_tblInterface.put(sInterface.replace('.', '/'), new ClassConstant(sInterface)); setModified(true); } /** * Remove an implemented interface. * * @param sInterface the interface name */ public void removeImplements(String sInterface) { ensureLoaded(); m_tblInterface.remove(sInterface.replace('.', '/')); setModified(true); } /** * Access the set of implemented interfaces. * * @return an enumeration of interface names */ public Enumeration getImplements() { ensureLoaded(); return m_tblInterface.keys(); } // ----- accessor: interface ------------------------------------------- /** * Determine if the interface attribute is set. * * @return true if interface */ public boolean isInterface() { ensureLoaded(); return m_flags.isInterface(); } /** * Set the interface attribute. * * @param fInterface true to set to interface, false to set to class */ public void setInterface(boolean fInterface) { ensureLoaded(); m_flags.setInterface(fInterface); setModified(true); } // ----- accessor: access ---------------------------------------------- /** * Get the class/method/field accessibility value. * * @return one of ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE, or ACC_PACKAGE */ public int getAccess() { ensureLoaded(); return m_flags.getAccess(); } /** * Set the class/method/field accessibility value. * * @param nAccess should be one of ACC_PUBLIC, ACC_PROTECTED, * ACC_PRIVATE, or ACC_PACKAGE */ public void setAccess(int nAccess) { ensureLoaded(); m_flags.setAccess(nAccess); setModified(true); } /** * Determine if the accessibility is public. * * @return true if the accessibility is public */ public boolean isPublic() { ensureLoaded(); return m_flags.isPublic(); } /** * Set the accessibility to public. */ public void setPublic() { ensureLoaded(); m_flags.setPublic(); setModified(true); } /** * Determine if the accessibility is protected. * * @return true if the accessibility is protected */ public boolean isProtected() { ensureLoaded(); return m_flags.isProtected(); } /** * Set the accessibility to protected. */ public void setProtected() { ensureLoaded(); m_flags.setProtected(); setModified(true); } /** * Determine if the accessibility is package private. * * @return true if the accessibility is package private */ public boolean isPackage() { ensureLoaded(); return m_flags.isPackage(); } /** * Set the accessibility to package private. */ public void setPackage() { ensureLoaded(); m_flags.setPackage(); setModified(true); } /** * Determine if the accessibility is private. * * @return true if the accessibility is private */ public boolean isPrivate() { ensureLoaded(); return m_flags.isPrivate(); } /** * Set the accessibility to private. */ public void setPrivate() { ensureLoaded(); m_flags.setPrivate(); setModified(true); } // ----- accessor: Static ------------------------------------------- /** * Determine if the Static attribute is set. * * @return true if Static */ public boolean isStatic() { ensureLoaded(); return m_flags.isStatic(); } /** * Set the Static attribute. * * @param fStatic true to set to Static, false otherwise */ public void setStatic(boolean fStatic) { ensureLoaded(); m_flags.setStatic(fStatic); setModified(true); } // ----- accessor: abstract ------------------------------------------- /** * Determine if the Abstract attribute is set. * * @return true if Abstract */ public boolean isAbstract() { ensureLoaded(); return m_flags.isAbstract(); } /** * Set the Abstract attribute. * * @param fAbstract true to set to Abstract, false otherwise */ public void setAbstract(boolean fAbstract) { ensureLoaded(); m_flags.setAbstract(fAbstract); setModified(true); } // ----- accessor: final ------------------------------------------- /** * Determine if the Final attribute is set. * * @return true if Final */ public boolean isFinal() { ensureLoaded(); return m_flags.isFinal(); } /** * Set the Final attribute. * * @param fFinal true to set to Final, false otherwise */ public void setFinal(boolean fFinal) { ensureLoaded(); m_flags.setFinal(fFinal); setModified(true); } // ----- accessor: synchronized ------------------------------------------- /** * Determine if the synchronized attribute is set. * * @return true if synchronized */ public boolean isSynchronized() { ensureLoaded(); return m_flags.isSynchronized(); } /** * Set the synchronized attribute. * * @param fSynchronized true to set to synchronized, false otherwise */ public void setSynchronized(boolean fSynchronized) { ensureLoaded(); m_flags.setSynchronized(fSynchronized); setModified(true); } // ----- accessor: field ----------------------------------------------- /** * Access a Java .class field structure. * * @param sName the field name * * @return the specified field or null if the field does not exist */ public Field getField(String sName) { ensureLoaded(); return (Field) m_tblField.get(sName); } /** * Add a Java .class field structure. * * @param sName the field name * @param sType the field type * * @return the new field */ public Field addField(String sName, String sType) { ensureLoaded(); Field field = new Field(sName, sType); m_tblField.put(field.getIdentity(), field); setModified(true); return field; } /** * Remove a field. * * @param sName the field name */ public void removeField(String sName) { ensureLoaded(); m_tblField.remove(sName); setModified(true); } /** * Access the set of fields. * * @return an enumeration of fields (not field names) */ public Enumeration getFields() { ensureLoaded(); return m_tblField.elements(); } /** * Get a field constant for the specified class/interface field. * * @param sName the field name * * @return the specified field constant or null if the field does not exist */ public FieldConstant getFieldConstant(String sName) { Field field = getField(sName); return field == null ? null : new FieldConstant(getName(), sName, field.getType()); } // ----- accessor: method ----------------------------------------------- /** * Access a Java .class method structure. * * @param sName the method name * @param sSig the method signature * * @return the specified method or null if the method does not exist */ public Method getMethod(String sName, String sSig) { return getMethod(sName + sSig.replace('.', '/')); } /** * Access a Java .class method structure. * * @param sSig the complete JVM method signature, including the * method name * * @return the specified method or null if the method does not exist */ public Method getMethod(String sSig) { ensureLoaded(); return (Method) m_tblMethod.get(sSig); } /** * Add a Java .class method structure. * * @param sSig the complete JVM method signature, including the * method name * * @return the new method */ public Method addMethod(String sSig) { int of = sSig.indexOf('('); return addMethod(sSig.substring(0, of), sSig.substring(of)); } /** * Add a Java .class method structure. * * @param sName the method name * @param sSig the method signature * * @return the new method */ public Method addMethod(String sName, String sSig) { ensureLoaded(); Method method = new Method(sName, sSig, m_flags.isInterface()); m_tblMethod.put(method.getIdentity(), method); setModified(true); return method; } /** * Remove a method. * * @param sName the method name * @param sSig the method signature */ public void removeMethod(String sName, String sSig) { removeMethod(sName + sSig); } /** * Remove a method. * * @param sSig the complete JVM method signature, including the * method name */ public void removeMethod(String sSig) { ensureLoaded(); m_tblMethod.remove(sSig); setModified(true); } /** * Access the set of methods. * * @return an enumeration of methods (not method names) */ public Enumeration getMethods() { ensureLoaded(); return m_tblMethod.elements(); } /** * Get a method constant for the specified method * * @param sSig the complete JVM method signature, including the * method name * * @return the specified method constant or null if the method does not exist */ public MethodConstant getMethodConstant(String sSig) { Method method = getMethod(sSig); return method == null ? null : new MethodConstant(getName(), method.getName(), method.getType()); } // ----- accessor: attribute ------------------------------------------- /** * Access a Java .class attribute structure. * * @param sName the attribute name * * @return the specified attribute or null if the attribute does not exist */ public Attribute getAttribute(String sName) { ensureLoaded(); return (Attribute) m_tblAttribute.get(sName); } /** * Add a Java .class attribute structure. * * @param sName the attribute name * * @return the new attribute */ public Attribute addAttribute(String sName) { ensureLoaded(); Attribute attribute; if (sName.equals(ATTR_FILENAME)) { attribute = new SourceFileAttribute(this); } else if (sName.equals(ATTR_DEPRECATED)) { attribute = new DeprecatedAttribute(this); } else if (sName.equals(ATTR_SYNTHETIC)) { attribute = new SyntheticAttribute(this); } else if (sName.equals(ATTR_INNERCLASSES)) { attribute = new InnerClassesAttribute(this); } else if (sName.equals(ATTR_ENCMETHOD)) { attribute = new EnclosingMethodAttribute(this); } else if (sName.equals(ATTR_SIGNATURE)) { attribute = new SignatureAttribute(this); } else if (sName.equals(ATTR_RTVISANNOT)) { attribute = new RuntimeVisibleAnnotationsAttribute(this); } else if (sName.equals(ATTR_RTINVISANNOT)) { attribute = new RuntimeInvisibleAnnotationsAttribute(this); } else if (sName.equals(ATTR_RTVISTANNOT)) { attribute = new RuntimeVisibleTypeAnnotationsAttribute(this); } else if (sName.equals(ATTR_RTINVISTANNOT)) { attribute = new RuntimeInvisibleTypeAnnotationsAttribute(this); } else if (sName.equals(ATTR_BOOTSTRAPMETHODS)) { attribute = new BootstrapMethodsAttribute(this); } else { attribute = new Attribute(this, sName); } m_tblAttribute.put(attribute.getIdentity(), attribute); setModified(true); return attribute; } /** * Remove a attribute. * * @param sName the attribute name */ public void removeAttribute(String sName) { ensureLoaded(); m_tblAttribute.remove(sName); setModified(true); } /** * Access the set of attributes. * * @return an enumeration of attributes (not attribute names) */ public Enumeration getAttributes() { ensureLoaded(); return m_tblAttribute.elements(); } // ----- accessor: attribute helpers ----------------------------------- /** * Determine if the class is deprecated. * * @return true if deprecated, false otherwise */ public boolean isDeprecated() { ensureLoaded(); return m_tblAttribute.contains(ATTR_DEPRECATED); } /** * Toggle if the class is deprecated. * * @param fDeprecated pass true to deprecate, false otherwise */ public void setDeprecated(boolean fDeprecated) { if (fDeprecated) { addAttribute(ATTR_DEPRECATED); } else { removeAttribute(ATTR_DEPRECATED); } } /** * Determine if the class is synthetic. * * @return true if synthetic, false otherwise */ public boolean isSynthetic() { ensureLoaded(); return m_tblAttribute.contains(ATTR_SYNTHETIC); } /** * Toggle if the class is synthetic. * * @param fSynthetic pass true to set synthetic, false otherwise */ public void setSynthetic(boolean fSynthetic) { if (fSynthetic) { addAttribute(ATTR_SYNTHETIC); } else { removeAttribute(ATTR_SYNTHETIC); } } // ----- data members --------------------------------------------------- /** * The name of this class. */ private static final String CLASS = "ClassFile"; /** * Access flags applicable to a class. */ private static final int ACC_CLASS = AccessFlags.ACC_PUBLIC | AccessFlags.ACC_PRIVATE | AccessFlags.ACC_PROTECTED | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL | AccessFlags.ACC_ABSTRACT; /** * Access flags applicable to an interface. */ private static final int ACC_INTERFACE = AccessFlags.ACC_PUBLIC; /** * Major version of this class. */ private int m_nVersionMajor = VERSION_MAJOR; /** * Minor version of this class. */ private int m_nVersionMinor = VERSION_MINOR; /** * Has the class information been modified? */ private boolean m_fModified; /** * Is the class in a "loaded" state? */ private boolean m_fLoaded; /** * The .class structure stored (cached) as a byte array. */ private byte[] m_abClazz; /** * The constant pool for the class. */ private ConstantPool m_pool; /** * The fully qualified name of the Java .class being managed by this data * structure. */ private ClassConstant m_clzName; /** * The fully qualified name of the Java .class from which the Java .class * being managed by this data structure derives. */ private ClassConstant m_clzSuper; /** * The class/interface access flags. */ private AccessFlags m_flags; /** * The interface names implemented by the Java .class. */ private StringTable m_tblInterface; /** * The fields of the Java .class. */ private StringTable m_tblField; /** * The methods of the Java .class. */ private StringTable m_tblMethod; /** * The attributes of the Java .class. */ private StringTable m_tblAttribute; /** * Three tables contained by the ClassFile storing VM structures which * are often manipulated iteratively and identically by the ClassFile. */ private final StringTable[] CONTAINED_TABLE = new StringTable[3]; // ----- inner classes -------------------------------------------------- /** * A callback interface for swapping out constants in the constant pool. * * @see ClassFile#resolve(com.tangosol.dev.assembler.ClassFile.Resolver) */ public static interface Resolver { Constant resolve(Constant constOrig); } /** * An implementation of Resolver which relocates a package. * * @see ClassFile#resolve(com.tangosol.dev.assembler.ClassFile.Resolver) */ public static class Relocator implements Resolver { public static final String PACKAGE = "_package/"; private String sPkg; public Relocator(String sPkg) { if (sPkg.length() > 0) { sPkg = sPkg.replace('.', '/'); if (!sPkg.endsWith("/")) { sPkg += '/'; } } this.sPkg = sPkg; } public Constant resolve(Constant constOrig) { if (constOrig instanceof UtfConstant) { UtfConstant utf = (UtfConstant) constOrig; String s = utf.getValue(); int of = s.indexOf(PACKAGE); if (of >= 0) { do { s = s.substring(0, of) + sPkg + s.substring(of + PACKAGE.length()); of = s.indexOf(PACKAGE); } while (of >= 0); return new UtfConstant(s); } } return constOrig; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy