/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javassist.CannotCompileException;
/**
* ClassFile
represents a Java .class
file, which
* consists of a constant pool, methods, fields, and attributes.
*
*
For example,
*** ClassFile cf = new ClassFile(false, "test.Foo", null); * cf.setInterfaces(new String[] { "java.lang.Cloneable" }); * * FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I"); * f.setAccessFlags(AccessFlag.PUBLIC); * cf.addField(f); * * cf.write(new DataOutputStream(new FileOutputStream("Foo.class"))); *
This code generates a class file Foo.class
for the following class:
* * @see FieldInfo * @see MethodInfo * @see ClassFileWriter * @see javassist.CtClass#getClassFile() * @see javassist.ClassPool#makeClass(ClassFile) */ public final class ClassFile { int major, minor; // version number ConstPool constPool; int thisClass; int accessFlags; int superClass; int[] interfaces; List* package test; * class Foo implements Cloneable { * public int width; * } *
java.lang.StringBuilder
.
* It is 50 (JDK 1.6)
* if the JVM supports java.util.zip.DeflaterInputStream
.
* It is 51 (JDK 1.7)
* if the JVM supports java.lang.invoke.CallSite
.
* It is 52 (JDK 1.8)
* if the JVM supports java.util.function.Function
.
* It is 53 (JDK 1.9)
* if the JVM supports java.lang.reflect.Module
.
* It is 54 (JDK 10)
* if the JVM supports java.util.List.copyOf(Collection)
.
* It is 55 (JDK 11)
* if the JVM supports java.util.Optional.isEmpty()
.
*/
public static final int MAJOR_VERSION;
static {
int ver = JAVA_3;
try {
Class.forName("java.lang.StringBuilder");
ver = JAVA_5;
Class.forName("java.util.zip.DeflaterInputStream");
ver = JAVA_6;
Class.forName("java.lang.invoke.CallSite", false, ClassLoader.getSystemClassLoader());
ver = JAVA_7;
Class.forName("java.util.function.Function");
ver = JAVA_8;
Class.forName("java.lang.Module");
ver = JAVA_9;
List.class.getMethod("copyOf", Collection.class);
ver = JAVA_10;
Class.forName("java.util.Optional").getMethod("isEmpty");
ver = JAVA_11;
}
catch (Throwable t) {}
MAJOR_VERSION = ver;
}
/**
* Constructs a class file from a byte stream.
*/
public ClassFile(DataInputStream in) throws IOException {
read(in);
}
/**
* Constructs a class file including no members.
*
* @param isInterface
* true if this is an interface. false if this is a class.
* @param classname
* a fully-qualified class name
* @param superclass
* a fully-qualified super class name or null.
*/
public ClassFile(boolean isInterface, String classname, String superclass) {
major = MAJOR_VERSION;
minor = 0; // JDK 1.3 or later
constPool = new ConstPool(classname);
thisClass = constPool.getThisClassInfo();
if (isInterface)
accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT;
else
accessFlags = AccessFlag.SUPER;
initSuperclass(superclass);
interfaces = null;
fields = new ArrayListThe returned value is obtained from inner_class_access_flags
* of the entry representing this nested class itself
* in InnerClasses_attribute
.
*/
public int getInnerAccessFlags() {
InnerClassesAttribute ica
= (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag);
if (ica == null)
return -1;
String name = getName();
int n = ica.tableLength();
for (int i = 0; i < n; ++i)
if (name.equals(ica.innerClass(i)))
return ica.accessFlags(i);
return -1;
}
/**
* Returns the class name.
*/
public String getName() {
return thisclassname;
}
/**
* Sets the class name. This method substitutes the new name for all
* occurrences of the old class name in the class file.
*/
public void setName(String name) {
renameClass(thisclassname, name);
}
/**
* Returns the super class name.
*/
public String getSuperclass() {
if (cachedSuperclass == null)
cachedSuperclass = constPool.getClassInfo(superClass);
return cachedSuperclass;
}
/**
* Returns the index of the constant pool entry representing the super
* class.
*/
public int getSuperclassId() {
return superClass;
}
/**
* Sets the super class.
*
*
* The new super class should inherit from the old super class. * This method modifies constructors so that they call constructors declared * in the new super class. */ public void setSuperclass(String superclass) throws CannotCompileException { if (superclass == null) superclass = "java.lang.Object"; try { this.superClass = constPool.addClassInfo(superclass); for (MethodInfo minfo:methods) minfo.setSuperclass(superclass); } catch (BadBytecode e) { throw new CannotCompileException(e); } cachedSuperclass = superclass; } /** * Replaces all occurrences of a class name in the class file. * *
* If class X is substituted for class Y in the class file, X and Y must
* have the same signature. If Y provides a method m(), X must provide it
* even if X inherits m() from the super class. If this fact is not
* guaranteed, the bytecode verifier may cause an error.
*
* @param oldname
* the replaced class name
* @param newname
* the substituted class name
*/
public final void renameClass(String oldname, String newname) {
if (oldname.equals(newname))
return;
if (oldname.equals(thisclassname))
thisclassname = newname;
oldname = Descriptor.toJvmName(oldname);
newname = Descriptor.toJvmName(newname);
constPool.renameClass(oldname, newname);
AttributeInfo.renameClass(attributes, oldname, newname);
for (MethodInfo minfo :methods) {
String desc = minfo.getDescriptor();
minfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname);
}
for (FieldInfo finfo:fields) {
String desc = finfo.getDescriptor();
finfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname);
}
}
/**
* Replaces all occurrences of several class names in the class file.
*
* @param classnames
* specifies which class name is replaced with which new name.
* Class names must be described with the JVM-internal
* representation like An attribute name can be obtained by, for example,
* {@link AnnotationsAttribute#visibleTag} or
* {@link AnnotationsAttribute#invisibleTag}.
* java/lang/Object
.
* @see #renameClass(String,String)
*/
public final void renameClass(MapCtClass.getRefClasses()
calls this method.
*/
public final void getRefClasses(MapFieldInfo
.
* @see FieldInfo
*/
public ListMethodInfo
.
* @see MethodInfo
*/
public ListList
object
* is shared with this object. If you add a new attribute to the list,
* the attribute is also added to the classs file represented by this
* object. If you remove an attribute from the list, it is also removed
* from the class file.
*
* @return a list of AttributeInfo
objects.
* @see AttributeInfo
*/
public List