/* * Copyright 2004-2010 Brian S O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cojen.classfile; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Set; import java.util.HashSet; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import org.cojen.classfile.attribute.Annotation; import org.cojen.classfile.attribute.AnnotationsAttr; import org.cojen.classfile.attribute.DeprecatedAttr; import org.cojen.classfile.attribute.InnerClassesAttr; import org.cojen.classfile.attribute.RuntimeInvisibleAnnotationsAttr; import org.cojen.classfile.attribute.RuntimeVisibleAnnotationsAttr; import org.cojen.classfile.attribute.SignatureAttr; import org.cojen.classfile.attribute.SourceFileAttr; import org.cojen.classfile.attribute.SyntheticAttr; import org.cojen.classfile.constant.ConstantClassInfo; /** * A class used to create Java class files. Call the writeTo method * to produce a class file. * * <p>See <i>The Java Virtual Machine Specification</i> (ISBN 0-201-63452-X) * for information on how class files are structured. Section 4.1 describes * the ClassFile structure. * * @author Brian S O'Neill */ public class ClassFile { private static final int MAGIC = 0xCAFEBABE; /** * Reads a ClassFile from the given InputStream. With this method, inner * classes cannot be loaded, and custom attributes cannot be defined. * * @param in source of class file data * @throws IOException for I/O error or if classfile is invalid. * @throws ArrayIndexOutOfBoundsException if a constant pool index is out * of range. * @throws ClassCastException if a constant pool index references the * wrong type. */ public static ClassFile readFrom(InputStream in) throws IOException { return readFrom(in, null, null); } /** * Reads a ClassFile from the given DataInput. With this method, inner * classes cannot be loaded, and custom attributes cannot be defined. * * @param din source of class file data * @throws IOException for I/O error or if classfile is invalid. * @throws ArrayIndexOutOfBoundsException if a constant pool index is out * of range. * @throws ClassCastException if a constant pool index references the * wrong type. */ public static ClassFile readFrom(DataInput din) throws IOException { return readFrom(din, null, null); } /** * Reads a ClassFile from the given InputStream. A * {@link ClassFileDataLoader} may be provided, which allows inner class * definitions to be loaded. Also, an {@link AttributeFactory} may be * provided, which allows non-standard attributes to be read. All * remaining unknown attribute types are captured, but are not decoded. * * @param in source of class file data * @param loader optional loader for reading inner class definitions * @param attrFactory optional factory for reading custom attributes * @throws IOException for I/O error or if classfile is invalid. * @throws ArrayIndexOutOfBoundsException if a constant pool index is out * of range. * @throws ClassCastException if a constant pool index references the * wrong type. */ public static ClassFile readFrom(InputStream in, ClassFileDataLoader loader, AttributeFactory attrFactory) throws IOException { if (!(in instanceof DataInput)) { in = new DataInputStream(in); } return readFrom((DataInput)in, loader, attrFactory); } /** * Reads a ClassFile from the given DataInput. A * {@link ClassFileDataLoader} may be provided, which allows inner class * definitions to be loaded. Also, an {@link AttributeFactory} may be * provided, which allows non-standard attributes to be read. All * remaining unknown attribute types are captured, but are not decoded. * * @param din source of class file data * @param loader optional loader for reading inner class definitions * @param attrFactory optional factory for reading custom attributes * @throws IOException for I/O error or if classfile is invalid. * @throws ArrayIndexOutOfBoundsException if a constant pool index is out * of range. * @throws ClassCastException if a constant pool index references the * wrong type. */ public static ClassFile readFrom(DataInput din, ClassFileDataLoader loader, AttributeFactory attrFactory) throws IOException { return readFrom(din, loader, attrFactory, new HashMap<String, ClassFile>(11), null); } /** * @param loadedClassFiles Maps name to ClassFiles for classes already * loaded. This prevents infinite loop: inner loads outer loads inner... */ private static ClassFile readFrom(DataInput din, ClassFileDataLoader loader, AttributeFactory attrFactory, Map<String, ClassFile> loadedClassFiles, ClassFile outerClass) throws IOException { int magic = din.readInt(); if (magic != MAGIC) { throw new IOException("Incorrect magic number: 0x" + Integer.toHexString(magic)); } short minor = din.readShort(); short major = din.readShort(); ConstantPool cp = ConstantPool.readFrom(din); Modifiers modifiers = Modifiers.getInstance(din.readUnsignedShort()) .toSynchronized(false); int index = din.readUnsignedShort(); ConstantClassInfo thisClass = (ConstantClassInfo)cp.getConstant(index); index = din.readUnsignedShort(); ConstantClassInfo superClass = null; if (index > 0) { superClass = (ConstantClassInfo)cp.getConstant(index); } ClassFile cf = new ClassFile(cp, modifiers, thisClass, superClass, outerClass); cf.setVersion(major, minor); loadedClassFiles.put(cf.getClassName(), cf); // Read interfaces. int size = din.readUnsignedShort(); for (int i=0; i<size; i++) { index = din.readUnsignedShort(); ConstantClassInfo info = (ConstantClassInfo)cp.getConstant(index); cf.addInterface(info.getType().getRootName()); } // Read fields. size = din.readUnsignedShort(); for (int i=0; i<size; i++) { cf.mFields.add(FieldInfo.readFrom(cf, din, attrFactory)); } // Read methods. size = din.readUnsignedShort(); for (int i=0; i<size; i++) { cf.mMethods.add(MethodInfo.readFrom(cf, din, attrFactory)); } // Read attributes. size = din.readUnsignedShort(); for (int i=0; i<size; i++) { Attribute attr = Attribute.readFrom(cp, din, attrFactory); cf.addAttribute(attr); if (attr instanceof InnerClassesAttr) { cf.mInnerClassesAttr = (InnerClassesAttr)attr; } } // Load inner and outer classes. if (cf.mInnerClassesAttr != null && loader != null) { InnerClassesAttr.Info[] infos = cf.mInnerClassesAttr.getInnerClassesInfo(); for (int i=0; i<infos.length; i++) { InnerClassesAttr.Info info = infos[i]; if (thisClass.equals(info.getInnerClass())) { // This class is an inner class. if (info.getInnerClassName() != null) { cf.mInnerClassName = info.getInnerClassName().getValue(); } ConstantClassInfo outer = info.getOuterClass(); if (cf.mOuterClass == null && outer != null) { cf.mOuterClass = readOuterClass (outer, loader, attrFactory, loadedClassFiles); } Modifiers innerFlags = info.getModifiers(); cf.mModifiers = cf.mModifiers .toStatic(innerFlags.isStatic()) .toPrivate(innerFlags.isPrivate()) .toProtected(innerFlags.isProtected()) .toPublic(innerFlags.isPublic()); } else if (info.getOuterClass() == null || thisClass.equals(info.getOuterClass())) { // This class is an outer class. ConstantClassInfo inner = info.getInnerClass(); if (inner != null) { ClassFile innerClass = readInnerClass (inner, loader, attrFactory, loadedClassFiles, cf); if (innerClass != null) { if (innerClass.getInnerClassName() != null) { innerClass.mInnerClassName = info.getInnerClassName().getValue(); } if (cf.mInnerClasses == null) { cf.mInnerClasses = new ArrayList<ClassFile>(); } cf.mInnerClasses.add(innerClass); } } } } } return cf; } private static ClassFile readOuterClass(ConstantClassInfo outer, ClassFileDataLoader loader, AttributeFactory attrFactory, Map<String, ClassFile> loadedClassFiles) throws IOException { String name = outer.getType().getRootName(); ClassFile outerClass = loadedClassFiles.get(name); if (outerClass != null) { return outerClass; } InputStream in = loader.getClassData(name); if (in == null) { return null; } if (!(in instanceof DataInput)) { in = new DataInputStream(in); } return readFrom((DataInput)in, loader, attrFactory, loadedClassFiles, null); } private static ClassFile readInnerClass(ConstantClassInfo inner, ClassFileDataLoader loader, AttributeFactory attrFactory, Map<String, ClassFile> loadedClassFiles, ClassFile outerClass) throws IOException { String name = inner.getType().getRootName(); // Prevent cycles in inner class structure. for (ClassFile outer = outerClass; outer != null; outer = outer.getOuterClass()) { if (name.equals(outer.getClassName())) { // Cycle prevented. return null; } } // Prevent classes from being loaded multiple times. ClassFile innerClass = loadedClassFiles.get(name); if (innerClass != null) { return innerClass; } InputStream in = loader.getClassData(name); if (in == null) { return null; } if (!(in instanceof DataInput)) { in = new DataInputStream(in); } return readFrom((DataInput)in, loader, attrFactory, loadedClassFiles, outerClass); } private int mVersion; private String mTarget; { setTarget(null); } private final String mClassName; private final String mSuperClassName; private String mInnerClassName; private TypeDesc mType; private ConstantPool mCp; private Modifiers mModifiers; private ConstantClassInfo mThisClass; private ConstantClassInfo mSuperClass; private List<ConstantClassInfo> mInterfaces = new ArrayList<ConstantClassInfo>(2); private Set<String> mInterfaceSet = new HashSet<String>(7); private List<FieldInfo> mFields = new ArrayList<FieldInfo>(); private List<MethodInfo> mMethods = new ArrayList<MethodInfo>(); private List<Attribute> mAttributes = new ArrayList<Attribute>(); private SourceFileAttr mSource; private List<ClassFile> mInnerClasses; private int mAnonymousInnerClassCount = 0; private InnerClassesAttr mInnerClassesAttr; // Is non-null for inner classes. private ClassFile mOuterClass; /** * By default, the ClassFile defines public, non-final, concrete classes. * This constructor creates a ClassFile for a class that extends * java.lang.Object. * <p> * Use the {@link #setModifiers} method to change the access modifiers of * this class or to turn it into an interface. * * @param className Full class name of the form ex: "java.lang.String". */ public ClassFile(String className) { this(className, (String)null); } /** * By default, the ClassFile defines public, non-final, concrete classes. * <p> * Use the {@link #setModifiers} method to change the access modifiers of * this class or to turn it into an interface. * * @param className Full class name of the form ex: "java.lang.String". * @param superClass Super class or interface. */ public ClassFile(String className, Class superClass) { this(className, superClass.isInterface() ? null : superClass.getName()); if (superClass.isInterface()) { addInterface(superClass); } } /** * By default, the ClassFile defines public, non-final, concrete classes. * <p> * Use the {@link #setModifiers} method to change the access modifiers of * this class or to turn it into an interface. * * @param className Full class name of the form ex: "java.lang.String". * @param superClassName Full super class name. */ public ClassFile(String className, String superClassName) { if (superClassName == null) { if (!className.equals(Object.class.getName())) { superClassName = Object.class.getName(); } } mCp = new ConstantPool(); // public, non-final, concrete class mModifiers = Modifiers.PUBLIC; mThisClass = mCp.addConstantClass(className); mSuperClass = mCp.addConstantClass(superClassName); mClassName = className; mSuperClassName = superClassName; } /** * Used to construct a ClassFile when read from a stream. */ private ClassFile(ConstantPool cp, Modifiers modifiers, ConstantClassInfo thisClass, ConstantClassInfo superClass, ClassFile outerClass) { mCp = cp; mModifiers = modifiers; mThisClass = thisClass; mSuperClass = superClass; mClassName = thisClass.getType().getRootName(); if (superClass == null) { mSuperClassName = null; } else { mSuperClassName = superClass.getType().getRootName(); } mOuterClass = outerClass; } public String getClassName() { return mClassName; } public String getSuperClassName() { return mSuperClassName; } /** * Returns a TypeDesc for the type of this ClassFile. */ public TypeDesc getType() { if (mType == null) { mType = TypeDesc.forClass(mClassName); } return mType; } public Modifiers getModifiers() { return mModifiers; } /** * Returns the names of all the interfaces that this class implements. */ public String[] getInterfaces() { int size = mInterfaces.size(); String[] names = new String[size]; for (int i=0; i<size; i++) { names[i] = mInterfaces.get(i).getType().getRootName(); } return names; } /** * Returns all the fields defined in this class. */ public FieldInfo[] getFields() { return mFields.toArray(new FieldInfo[mFields.size()]); } /** * Returns all the methods defined in this class, not including * constructors and static initializers. */ public MethodInfo[] getMethods() { int size = mMethods.size(); List<MethodInfo> methodsOnly = new ArrayList<MethodInfo>(size); for (int i=0; i<size; i++) { MethodInfo method = mMethods.get(i); String name = method.getName(); if (!"<init>".equals(name) && !"<clinit>".equals(name)) { methodsOnly.add(method); } } return methodsOnly.toArray(new MethodInfo[methodsOnly.size()]); } /** * Returns all the constructors defined in this class. */ public MethodInfo[] getConstructors() { int size = mMethods.size(); List<MethodInfo> ctorsOnly = new ArrayList<MethodInfo>(size); for (int i=0; i<size; i++) { MethodInfo method = mMethods.get(i); if ("<init>".equals(method.getName())) { ctorsOnly.add(method); } } return ctorsOnly.toArray(new MethodInfo[ctorsOnly.size()]); } /** * Returns the static initializer defined in this class or null if there * isn't one. */ public MethodInfo getInitializer() { int size = mMethods.size(); for (int i=0; i<size; i++) { MethodInfo method = mMethods.get(i); if ("<clinit>".equals(method.getName())) { return method; } } return null; } /** * Returns all the inner classes defined in this class. If no inner classes * are defined, then an array of length zero is returned. */ public ClassFile[] getInnerClasses() { if (mInnerClasses == null) { return new ClassFile[0]; } return mInnerClasses.toArray(new ClassFile[mInnerClasses.size()]); } /** * Returns true if this ClassFile represents an inner class. */ public boolean isInnerClass() { return mOuterClass != null; } /** * If this ClassFile represents a non-anonymous inner class, returns its * short inner class name. */ public String getInnerClassName() { return mInnerClassName; } /** * Returns null if this ClassFile does not represent an inner class. * * @see #isInnerClass() */ public ClassFile getOuterClass() { return mOuterClass; } /** * Returns a value indicating how deeply nested an inner class is with * respect to its outermost enclosing class. For top level classes, 0 * is returned. For first level inner classes, 1 is returned, etc. */ public int getClassDepth() { int depth = 0; ClassFile outer = mOuterClass; while (outer != null) { depth++; outer = outer.mOuterClass; } return depth; } /** * Returns the source file of this class file or null if not set. */ public String getSourceFile() { if (mSource == null) { return null; } else { return mSource.getFileName().getValue(); } } public boolean isSynthetic() { for (int i = mAttributes.size(); --i >= 0; ) { Attribute attr = mAttributes.get(i); if (attr instanceof SyntheticAttr) { return true; } } return false; } public boolean isDeprecated() { for (int i = mAttributes.size(); --i >= 0; ) { Attribute attr = mAttributes.get(i); if (attr instanceof DeprecatedAttr) { return true; } } return false; } /** * Returns all the runtime invisible annotations defined for this class * file, or an empty array if none. */ public Annotation[] getRuntimeInvisibleAnnotations() { for (int i = mAttributes.size(); --i >= 0; ) { Attribute attr = mAttributes.get(i); if (attr instanceof RuntimeInvisibleAnnotationsAttr) { return ((AnnotationsAttr) attr).getAnnotations(); } } return new Annotation[0]; } /** * Returns all the runtime visible annotations defined for this class file, * or an empty array if none. */ public Annotation[] getRuntimeVisibleAnnotations() { for (int i = mAttributes.size(); --i >= 0; ) { Attribute attr = mAttributes.get(i); if (attr instanceof RuntimeVisibleAnnotationsAttr) { return ((AnnotationsAttr) attr).getAnnotations(); } } return new Annotation[0]; } /** * Add a runtime invisible annotation. */ public Annotation addRuntimeInvisibleAnnotation(TypeDesc type) { AnnotationsAttr attr = null; for (int i = mAttributes.size(); --i >= 0; ) { Attribute a = mAttributes.get(i); if (a instanceof RuntimeInvisibleAnnotationsAttr) { attr = (AnnotationsAttr) a; } } if (attr == null) { attr = new RuntimeInvisibleAnnotationsAttr(mCp); addAttribute(attr); } Annotation ann = new Annotation(mCp); ann.setType(type); attr.addAnnotation(ann); return ann; } /** * Add a runtime visible annotation. */ public Annotation addRuntimeVisibleAnnotation(TypeDesc type) { AnnotationsAttr attr = null; for (int i = mAttributes.size(); --i >= 0; ) { Attribute a = mAttributes.get(i); if (a instanceof RuntimeVisibleAnnotationsAttr) { attr = (AnnotationsAttr) a; } } if (attr == null) { attr = new RuntimeVisibleAnnotationsAttr(mCp); addAttribute(attr); } Annotation ann = new Annotation(mCp); ann.setType(type); attr.addAnnotation(ann); return ann; } /** * Returns the signature attribute of this classfile, or null if none is * defined. */ // TODO: Eventually remove this method public SignatureAttr getSignatureAttr() { for (int i = mAttributes.size(); --i >= 0; ) { Attribute attr = mAttributes.get(i); if (attr instanceof SignatureAttr) { return (SignatureAttr) attr; } } return null; } /** * Provides access to the ClassFile's ContantPool. * * @return The constant pool for this class file. */ public ConstantPool getConstantPool() { return mCp; } public void setModifiers(Modifiers modifiers) { mModifiers = modifiers; } /** * Add an interface that this class implements. * * @param interfaceName Full interface name. */ public void addInterface(String interfaceName) { if (!mInterfaceSet.contains(interfaceName)) { mInterfaces.add(mCp.addConstantClass(interfaceName)); mInterfaceSet.add(interfaceName); } } /** * Add an interface that this class implements. */ public void addInterface(Class i) { addInterface(i.getName()); } /** * Add a field to this class. */ public FieldInfo addField(Modifiers modifiers, String fieldName, TypeDesc type) { FieldInfo fi = new FieldInfo(this, modifiers, fieldName, type); mFields.add(fi); return fi; } /** * Add a method to this class. * * @param ret Is null if method returns void. * @param params May be null if method accepts no parameters. */ public MethodInfo addMethod(Modifiers modifiers, String methodName, TypeDesc ret, TypeDesc[] params) { MethodDesc md = MethodDesc.forArguments(ret, params); return addMethod(modifiers, methodName, md); } /** * Add a method to this class. */ public MethodInfo addMethod(Modifiers modifiers, String methodName, MethodDesc md) { MethodInfo mi = new MethodInfo(this, modifiers, methodName, md); mMethods.add(mi); return mi; } /** * Add a method to this class. This method is handy for implementing * methods defined by a pre-existing interface. */ public MethodInfo addMethod(Method method) { Modifiers modifiers = Modifiers.getInstance(method.getModifiers()).toAbstract(false); MethodInfo mi = addMethod(modifiers, method.getName(), MethodDesc.forMethod(method)); // exception stuff... Class[] exceptions = method.getExceptionTypes(); for (int i=0; i<exceptions.length; i++) { mi.addException(TypeDesc.forClass(exceptions[i])); } return mi; } /** * Add a method to this class by declaration. * * @throws IllegalArgumentException if declaration syntax is wrong * @see MethodDeclarationParser */ public MethodInfo addMethod(String declaration) { MethodDeclarationParser p = new MethodDeclarationParser(declaration); return addMethod(p.getModifiers(), p.getMethodName(), p.getReturnType(), p.getParameters()); } /** * Add a constructor to this class. * * @param params May be null if constructor accepts no parameters. */ public MethodInfo addConstructor(Modifiers modifiers, TypeDesc[] params) { MethodDesc md = MethodDesc.forArguments(null, params); MethodInfo mi = new MethodInfo(this, modifiers, "<init>", md); mMethods.add(mi); return mi; } /** * Adds a public, no-arg constructor with the code buffer properly defined. */ public MethodInfo addDefaultConstructor() { MethodInfo mi = addConstructor(Modifiers.PUBLIC, null); CodeBuilder builder = new CodeBuilder(mi); builder.loadThis(); builder.invokeSuperConstructor(null); builder.returnVoid(); return mi; } /** * Add a static initializer to this class. */ public MethodInfo addInitializer() { MethodDesc md = MethodDesc.forArguments(null, null); Modifiers af = Modifiers.NONE.toStatic(true); MethodInfo mi = new MethodInfo(this, af, "<clinit>", md); mMethods.add(mi); return mi; } /** * Add an inner class to this class. By default, inner classes are private * static. * * @param fullInnerClassName Optional full inner class name. * @param innerClassName Optional short inner class name. */ public ClassFile addInnerClass(String fullInnerClassName, String innerClassName) { return addInnerClass(fullInnerClassName, innerClassName, (String)null); } /** * Add an inner class to this class. By default, inner classes are private * static. * * @param fullInnerClassName Optional full inner class name. * @param innerClassName Optional short inner class name. * @param superClass Super class. */ public ClassFile addInnerClass(String fullInnerClassName, String innerClassName, Class superClass) { return addInnerClass(fullInnerClassName, innerClassName, superClass.getName()); } /** * Add an inner class to this class. By default, inner classes are private * static. * * @param fullInnerClassName Optional full inner class name. * @param innerClassName Optional short inner class name. * @param superClassName Full super class name. */ public ClassFile addInnerClass(String fullInnerClassName, String innerClassName, String superClassName) { if (fullInnerClassName == null) { if (innerClassName == null) { fullInnerClassName = mClassName + '$' + (++mAnonymousInnerClassCount); } else { fullInnerClassName = mClassName + '$' + innerClassName; } } ClassFile inner = new ClassFile(fullInnerClassName, superClassName); Modifiers modifiers = inner.getModifiers().toPrivate(true).toStatic(true); inner.setModifiers(modifiers); inner.mInnerClassName = innerClassName; inner.mOuterClass = this; if (mInnerClasses == null) { mInnerClasses = new ArrayList<ClassFile>(); } mInnerClasses.add(inner); // Record the inner class in this, the outer class. if (mInnerClassesAttr == null) { addAttribute(new InnerClassesAttr(mCp)); } // TODO: Anonymous inner classes and method scoped classes do not have // an outer class listed. mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName, innerClassName, modifiers); // Record the inner class in itself. inner.addAttribute(new InnerClassesAttr(inner.getConstantPool())); inner.mInnerClassesAttr.addInnerClass(fullInnerClassName, mClassName, innerClassName, modifiers); return inner; } /** * Set the source file of this class file by adding a source file * attribute. The source doesn't actually have to be a file, * but the virtual machine spec names the attribute "SourceFile_attribute". */ public void setSourceFile(String fileName) { addAttribute(new SourceFileAttr(mCp, fileName)); } /** * Mark this class as being synthetic by adding a special attribute. */ public void markSynthetic() { addAttribute(new SyntheticAttr(mCp)); } /** * Mark this class as being deprecated by adding a special attribute. */ public void markDeprecated() { addAttribute(new DeprecatedAttr(mCp)); } /** * Add an attribute to this class. */ public void addAttribute(Attribute attr) { if (attr instanceof SourceFileAttr) { if (mSource != null) { mAttributes.remove(mSource); } mSource = (SourceFileAttr)attr; } else if (attr instanceof InnerClassesAttr) { if (mInnerClassesAttr != null) { mAttributes.remove(mInnerClassesAttr); } mInnerClassesAttr = (InnerClassesAttr)attr; } mAttributes.add(attr); } public Attribute[] getAttributes() { return mAttributes.toArray(new Attribute[mAttributes.size()]); } /** * Specify what target virtual machine version classfile should generate * for. Calling this method changes the major and minor version of the * classfile format. * * @param target VM version, 1.0, 1.1, etc. * @throws IllegalArgumentException if target is not supported */ public void setTarget(String target) throws IllegalArgumentException { int major, minor; if (target == null || "1.0".equals(target) || "1.1".equals(target)) { major = 45; minor = 3; if (target == null) { target = "1.0"; } } else if ("1.2".equals(target)) { major = 46; minor = 0; } else if ("1.3".equals(target)) { major = 47; minor = 0; } else if ("1.4".equals(target)) { major = 48; minor = 0; } else if ("1.5".equals(target)) { major = 49; minor = 0; } else if ("1.6".equals(target)) { major = 50; minor = 0; } else if ("1.7".equals(target)) { major = 51; minor = 0; } else { throw new IllegalArgumentException ("Unsupported target version: " + target); } mVersion = (minor << 16) | (major & 0xffff); mTarget = target.intern(); } /** * Returns the target virtual machine version, or null if unknown. */ public String getTarget() { return mTarget; } /** * Sets the version to use when writing the generated classfile, overriding * the target. */ public void setVersion(int major, int minor) { if (major > 65535 || minor > 65535) { throw new IllegalArgumentException("Version number element cannot exceed 65535"); } mVersion = (minor << 16) | (major & 0xffff); String target; switch (major) { default: target = null; break; case 45: target = minor == 3 ? "1.0" : null; break; case 46: target = minor == 0 ? "1.2" : null; break; case 47: target = minor == 0 ? "1.3" : null; break; case 48: target = minor == 0 ? "1.4" : null; break; case 49: target = minor == 0 ? "1.5" : null; break; case 50: target = minor == 0 ? "1.6" : null; break; case 51: target = minor == 0 ? "1.7" : null; break; } mTarget = target; } /** * Returns the major version number of the classfile format. */ public int getMajorVersion() { return mVersion & 0xffff; } /** * Returns the minor version number of the classfile format. */ public int getMinorVersion() { return (mVersion >> 16) & 0xffff; } /** * Writes the ClassFile to the given OutputStream. */ public void writeTo(OutputStream out) throws IOException { if (!(out instanceof DataOutput)) { out = new DataOutputStream(out); } writeTo((DataOutput)out); } /** * Writes the ClassFile to the given DataOutput. */ public void writeTo(DataOutput dout) throws IOException { dout.writeInt(MAGIC); dout.writeInt(mVersion); mCp.writeTo(dout); { int flags = mModifiers.getBitmask(); if (!mModifiers.isInterface()) { // Set the ACC_SUPER flag for classes only. flags |= Modifier.SYNCHRONIZED; } dout.writeShort(flags); } dout.writeShort(mThisClass.getIndex()); if (mSuperClass != null) { dout.writeShort(mSuperClass.getIndex()); } else { dout.writeShort(0); } int size = mInterfaces.size(); if (size > 65535) { throw new IllegalStateException("Interfaces count cannot exceed 65535: " + size); } dout.writeShort(size); for (int i=0; i<size; i++) { int index = mInterfaces.get(i).getIndex(); dout.writeShort(index); } size = mFields.size(); if (size > 65535) { throw new IllegalStateException("Field count cannot exceed 65535: " + size); } dout.writeShort(size); for (int i=0; i<size; i++) { FieldInfo field = mFields.get(i); field.writeTo(dout); } size = mMethods.size(); if (size > 65535) { throw new IllegalStateException("Method count cannot exceed 65535: " + size); } dout.writeShort(size); for (int i=0; i<size; i++) { MethodInfo method = mMethods.get(i); method.writeTo(dout); } size = mAttributes.size(); if (size > 65535) { throw new IllegalStateException("Attribute count cannot exceed 65535: " + size); } dout.writeShort(size); for (int i=0; i<size; i++) { Attribute attr = mAttributes.get(i); attr.writeTo(dout); } } public String toString() { StringBuffer buf = new StringBuffer(); String modStr = mModifiers.toString(); if (modStr.length() > 0) { buf.append(modStr); buf.append(' '); } if (getModifiers().isInterface()) { buf.append("interface"); } else { buf.append("class"); } buf.append(' '); buf.append(getClassName()); return buf.toString(); } }