/*
* 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;
import java.lang.ref.
WeakReference;
import java.io.
BufferedInputStream;
import java.io.
ByteArrayOutputStream;
import java.io.
ByteArrayInputStream;
import java.io.
DataInputStream;
import java.io.
DataOutputStream;
import java.io.
IOException;
import java.io.
InputStream;
import java.net.
URL;
import java.util.
ArrayList;
import java.util.
HashMap;
import java.util.
Hashtable;
import java.util.
List;
import java.util.
Set;
import javassist.
bytecode.
AccessFlag;
import javassist.
bytecode.
AttributeInfo;
import javassist.
bytecode.
AnnotationsAttribute;
import javassist.
bytecode.
BadBytecode;
import javassist.
bytecode.
Bytecode;
import javassist.
bytecode.
ClassFile;
import javassist.
bytecode.
CodeAttribute;
import javassist.
bytecode.
ConstantAttribute;
import javassist.
bytecode.
CodeIterator;
import javassist.
bytecode.
ConstPool;
import javassist.
bytecode.
Descriptor;
import javassist.
bytecode.
EnclosingMethodAttribute;
import javassist.
bytecode.
FieldInfo;
import javassist.
bytecode.
InnerClassesAttribute;
import javassist.
bytecode.
MethodInfo;
import javassist.
bytecode.
ParameterAnnotationsAttribute;
import javassist.
bytecode.
SignatureAttribute;
import javassist.
bytecode.
annotation.
Annotation;
import javassist.
compiler.
AccessorMaker;
import javassist.
compiler.
CompileError;
import javassist.
compiler.
Javac;
import javassist.
expr.
ExprEditor;
/**
* Class types.
*/
class
CtClassType extends
CtClass {
ClassPool classPool;
boolean
wasChanged;
private boolean
wasFrozen;
boolean
wasPruned;
boolean
gcConstPool; // if true, the constant pool entries will be garbage collected.
ClassFile classfile;
byte[]
rawClassfile; // backup storage
private
WeakReference memberCache;
private
AccessorMaker accessors;
private
FieldInitLink fieldInitializers;
private
Hashtable hiddenMethods; // must be synchronous
private int
uniqueNumberSeed;
private boolean
doPruning =
ClassPool.
doPruning;
private int
getCount;
private static final int
GET_THRESHOLD = 2; // see compress()
CtClassType(
String name,
ClassPool cp) {
super(
name);
classPool =
cp;
wasChanged =
wasFrozen =
wasPruned =
gcConstPool = false;
classfile = null;
rawClassfile = null;
memberCache = null;
accessors = null;
fieldInitializers = null;
hiddenMethods = null;
uniqueNumberSeed = 0;
getCount = 0;
}
CtClassType(
InputStream ins,
ClassPool cp) throws
IOException {
this((
String)null,
cp);
classfile = new
ClassFile(new
DataInputStream(
ins));
qualifiedName =
classfile.
getName();
}
protected void
extendToString(
StringBuffer buffer) {
if (
wasChanged)
buffer.
append("changed ");
if (
wasFrozen)
buffer.
append("frozen ");
if (
wasPruned)
buffer.
append("pruned ");
buffer.
append(
Modifier.
toString(
getModifiers()));
buffer.
append(" class ");
buffer.
append(
getName());
try {
CtClass ext =
getSuperclass();
if (
ext != null) {
String name =
ext.
getName();
if (!
name.
equals("java.lang.Object"))
buffer.
append(" extends " +
ext.
getName());
}
}
catch (
NotFoundException e) {
buffer.
append(" extends ??");
}
try {
CtClass[]
intf =
getInterfaces();
if (
intf.
length > 0)
buffer.
append(" implements ");
for (int
i = 0;
i <
intf.
length; ++
i) {
buffer.
append(
intf[
i].
getName());
buffer.
append(", ");
}
}
catch (
NotFoundException e) {
buffer.
append(" extends ??");
}
CtMember.
Cache memCache =
getMembers();
exToString(
buffer, " fields=",
memCache.
fieldHead(),
memCache.
lastField());
exToString(
buffer, " constructors=",
memCache.
consHead(),
memCache.
lastCons());
exToString(
buffer, " methods=",
memCache.
methodHead(),
memCache.
lastMethod());
}
private void exToString(
StringBuffer buffer,
String msg,
CtMember head,
CtMember tail) {
buffer.
append(
msg);
while (
head !=
tail) {
head =
head.
next();
buffer.
append(
head);
buffer.
append(", ");
}
}
public
AccessorMaker getAccessorMaker() {
if (
accessors == null)
accessors = new
AccessorMaker(this);
return
accessors;
}
public
ClassFile getClassFile2() {
ClassFile cfile =
classfile;
if (
cfile != null)
return
cfile;
classPool.
compress();
if (
rawClassfile != null) {
try {
classfile = new
ClassFile(new
DataInputStream(
new
ByteArrayInputStream(
rawClassfile)));
rawClassfile = null;
getCount =
GET_THRESHOLD;
return
classfile;
}
catch (
IOException e) {
throw new
RuntimeException(
e.
toString(),
e);
}
}
InputStream fin = null;
try {
fin =
classPool.
openClassfile(
getName());
if (
fin == null)
throw new
NotFoundException(
getName());
fin = new
BufferedInputStream(
fin);
ClassFile cf = new
ClassFile(new
DataInputStream(
fin));
if (!
cf.
getName().
equals(
qualifiedName))
throw new
RuntimeException("cannot find " +
qualifiedName + ": "
+
cf.
getName() + " found in "
+
qualifiedName.
replace('.', '/') + ".class");
classfile =
cf;
return
cf;
}
catch (
NotFoundException e) {
throw new
RuntimeException(
e.
toString(),
e);
}
catch (
IOException e) {
throw new
RuntimeException(
e.
toString(),
e);
}
finally {
if (
fin != null)
try {
fin.
close();
}
catch (
IOException e) {}
}
}
/* Inherited from CtClass. Called by get() in ClassPool.
*
* @see javassist.CtClass#incGetCounter()
* @see #toBytecode(DataOutputStream)
*/
final void
incGetCounter() { ++
getCount; }
/**
* Invoked from ClassPool#compress().
* It releases the class files that have not been recently used
* if they are unmodified.
*/
void
compress() {
if (
getCount <
GET_THRESHOLD)
if (!
isModified() &&
ClassPool.
releaseUnmodifiedClassFile)
removeClassFile();
else if (
isFrozen() && !
wasPruned)
saveClassFile();
getCount = 0;
}
/**
* Converts a ClassFile object into a byte array
* for saving memory space.
*/
private synchronized void
saveClassFile() {
/* getMembers() and releaseClassFile() are also synchronized.
*/
if (
classfile == null ||
hasMemberCache() != null)
return;
ByteArrayOutputStream barray = new
ByteArrayOutputStream();
DataOutputStream out = new
DataOutputStream(
barray);
try {
classfile.
write(
out);
barray.
close();
rawClassfile =
barray.
toByteArray();
classfile = null;
}
catch (
IOException e) {}
}
private synchronized void
removeClassFile() {
if (
classfile != null && !
isModified() &&
hasMemberCache() == null)
classfile = null;
}
public
ClassPool getClassPool() { return
classPool; }
void setClassPool(
ClassPool cp) {
classPool =
cp; }
public
URL getURL() throws
NotFoundException {
URL url =
classPool.
find(
getName());
if (
url == null)
throw new
NotFoundException(
getName());
else
return
url;
}
public boolean
isModified() { return
wasChanged; }
public boolean
isFrozen() { return
wasFrozen; }
public void
freeze() {
wasFrozen = true; }
void
checkModify() throws
RuntimeException {
if (
isFrozen()) {
String msg =
getName() + " class is frozen";
if (
wasPruned)
msg += " and pruned";
throw new
RuntimeException(
msg);
}
wasChanged = true;
}
public void
defrost() {
checkPruned("defrost");
wasFrozen = false;
}
public boolean subtypeOf(
CtClass clazz) throws
NotFoundException {
int
i;
String cname =
clazz.
getName();
if (this ==
clazz ||
getName().
equals(
cname))
return true;
ClassFile file =
getClassFile2();
String supername =
file.
getSuperclass();
if (
supername != null &&
supername.
equals(
cname))
return true;
String[]
ifs =
file.
getInterfaces();
int
num =
ifs.length;
for (
i = 0;
i <
num; ++
i)
if (
ifs[
i].
equals(
cname))
return true;
if (
supername != null &&
classPool.
get(
supername).
subtypeOf(
clazz))
return true;
for (
i = 0;
i <
num; ++
i)
if (
classPool.
get(
ifs[
i]).
subtypeOf(
clazz))
return true;
return false;
}
public void
setName(
String name) throws
RuntimeException {
String oldname =
getName();
if (
name.
equals(
oldname))
return;
// check this in advance although classNameChanged() below does.
classPool.
checkNotFrozen(
name);
ClassFile cf =
getClassFile2();
super.
setName(
name);
cf.
setName(
name);
nameReplaced();
classPool.
classNameChanged(
oldname, this);
}
public
String getGenericSignature() {
SignatureAttribute sa
= (
SignatureAttribute)
getClassFile2().
getAttribute(
SignatureAttribute.
tag);
return
sa == null ? null :
sa.
getSignature();
}
public void
setGenericSignature(
String sig) {
ClassFile cf =
getClassFile();
SignatureAttribute sa = new
SignatureAttribute(
cf.
getConstPool(),
sig);
cf.
addAttribute(
sa);
}
public void replaceClassName(
ClassMap classnames)
throws
RuntimeException
{
String oldClassName =
getName();
String newClassName
= (
String)
classnames.
get(
Descriptor.
toJvmName(
oldClassName));
if (
newClassName != null) {
newClassName =
Descriptor.
toJavaName(
newClassName);
// check this in advance although classNameChanged() below does.
classPool.
checkNotFrozen(
newClassName);
}
super.
replaceClassName(
classnames);
ClassFile cf =
getClassFile2();
cf.
renameClass(
classnames);
nameReplaced();
if (
newClassName != null) {
super.
setName(
newClassName);
classPool.
classNameChanged(
oldClassName, this);
}
}
public void
replaceClassName(
String oldname,
String newname)
throws
RuntimeException
{
String thisname =
getName();
if (
thisname.
equals(
oldname))
setName(
newname);
else {
super.
replaceClassName(
oldname,
newname);
getClassFile2().
renameClass(
oldname,
newname);
nameReplaced();
}
}
public boolean
isInterface() {
return
Modifier.
isInterface(
getModifiers());
}
public boolean
isAnnotation() {
return
Modifier.
isAnnotation(
getModifiers());
}
public boolean
isEnum() {
return
Modifier.
isEnum(
getModifiers());
}
public int
getModifiers() {
ClassFile cf =
getClassFile2();
int
acc =
cf.
getAccessFlags();
acc =
AccessFlag.
clear(
acc,
AccessFlag.
SUPER);
int
inner =
cf.
getInnerAccessFlags();
if (
inner != -1 && (
inner &
AccessFlag.
STATIC) != 0)
acc |=
AccessFlag.
STATIC;
return
AccessFlag.
toModifier(
acc);
}
public
CtClass[] getNestedClasses() throws
NotFoundException {
ClassFile cf =
getClassFile2();
InnerClassesAttribute ica
= (
InnerClassesAttribute)
cf.
getAttribute(
InnerClassesAttribute.
tag);
if (
ica == null)
return new
CtClass[0];
String thisName =
cf.
getName() + "$";
int
n =
ica.
tableLength();
ArrayList list = new
ArrayList(
n);
for (int
i = 0;
i <
n;
i++) {
String name =
ica.
innerClass(
i);
if (
name != null)
if (
name.
startsWith(
thisName)) {
// if it is an immediate nested class
if (
name.
lastIndexOf('$') <
thisName.
length())
list.
add(
classPool.
get(
name));
}
}
return (
CtClass[])
list.
toArray(new
CtClass[
list.
size()]);
}
public void
setModifiers(int
mod) {
ClassFile cf =
getClassFile2();
if (
Modifier.
isStatic(
mod)) {
int
flags =
cf.
getInnerAccessFlags();
if (
flags != -1 && (
flags &
AccessFlag.
STATIC) != 0)
mod =
mod & ~
Modifier.
STATIC;
else
throw new
RuntimeException("cannot change " +
getName() + " into a static class");
}
checkModify();
cf.
setAccessFlags(
AccessFlag.
of(
mod));
}
public boolean
hasAnnotation(
Class clz) {
ClassFile cf =
getClassFile2();
AnnotationsAttribute ainfo = (
AnnotationsAttribute)
cf.
getAttribute(
AnnotationsAttribute.
invisibleTag);
AnnotationsAttribute ainfo2 = (
AnnotationsAttribute)
cf.
getAttribute(
AnnotationsAttribute.
visibleTag);
return
hasAnnotationType(
clz,
getClassPool(),
ainfo,
ainfo2);
}
static boolean hasAnnotationType(
Class clz,
ClassPool cp,
AnnotationsAttribute a1,
AnnotationsAttribute a2)
{
Annotation[]
anno1,
anno2;
if (
a1 == null)
anno1 = null;
else
anno1 =
a1.
getAnnotations();
if (
a2 == null)
anno2 = null;
else
anno2 =
a2.
getAnnotations();
String typeName =
clz.
getName();
if (
anno1 != null)
for (int
i = 0;
i <
anno1.
length;
i++)
if (
anno1[
i].
getTypeName().
equals(
typeName))
return true;
if (
anno2 != null)
for (int
i = 0;
i <
anno2.
length;
i++)
if (
anno2[
i].
getTypeName().
equals(
typeName))
return true;
return false;
}
public
Object getAnnotation(
Class clz) throws
ClassNotFoundException {
ClassFile cf =
getClassFile2();
AnnotationsAttribute ainfo = (
AnnotationsAttribute)
cf.
getAttribute(
AnnotationsAttribute.
invisibleTag);
AnnotationsAttribute ainfo2 = (
AnnotationsAttribute)
cf.
getAttribute(
AnnotationsAttribute.
visibleTag);
return
getAnnotationType(
clz,
getClassPool(),
ainfo,
ainfo2);
}
static
Object getAnnotationType(
Class clz,
ClassPool cp,
AnnotationsAttribute a1,
AnnotationsAttribute a2)
throws
ClassNotFoundException
{
Annotation[]
anno1,
anno2;
if (
a1 == null)
anno1 = null;
else
anno1 =
a1.
getAnnotations();
if (
a2 == null)
anno2 = null;
else
anno2 =
a2.
getAnnotations();
String typeName =
clz.
getName();
if (
anno1 != null)
for (int
i = 0;
i <
anno1.
length;
i++)
if (
anno1[
i].
getTypeName().
equals(
typeName))
return
toAnnoType(
anno1[
i],
cp);
if (
anno2 != null)
for (int
i = 0;
i <
anno2.
length;
i++)
if (
anno2[
i].
getTypeName().
equals(
typeName))
return
toAnnoType(
anno2[
i],
cp);
return null;
}
public
Object[]
getAnnotations() throws
ClassNotFoundException {
return
getAnnotations(false);
}
public
Object[]
getAvailableAnnotations(){
try {
return
getAnnotations(true);
}
catch (
ClassNotFoundException e) {
throw new
RuntimeException("Unexpected exception ",
e);
}
}
private
Object[]
getAnnotations(boolean
ignoreNotFound)
throws
ClassNotFoundException
{
ClassFile cf =
getClassFile2();
AnnotationsAttribute ainfo = (
AnnotationsAttribute)
cf.
getAttribute(
AnnotationsAttribute.
invisibleTag);
AnnotationsAttribute ainfo2 = (
AnnotationsAttribute)
cf.
getAttribute(
AnnotationsAttribute.
visibleTag);
return
toAnnotationType(
ignoreNotFound,
getClassPool(),
ainfo,
ainfo2);
}
static
Object[] toAnnotationType(boolean
ignoreNotFound,
ClassPool cp,
AnnotationsAttribute a1,
AnnotationsAttribute a2)
throws
ClassNotFoundException
{
Annotation[]
anno1,
anno2;
int
size1,
size2;
if (
a1 == null) {
anno1 = null;
size1 = 0;
}
else {
anno1 =
a1.
getAnnotations();
size1 =
anno1.
length;
}
if (
a2 == null) {
anno2 = null;
size2 = 0;
}
else {
anno2 =
a2.
getAnnotations();
size2 =
anno2.
length;
}
if (!
ignoreNotFound){
Object[]
result = new
Object[
size1 +
size2];
for (int
i = 0;
i <
size1;
i++)
result[
i] =
toAnnoType(
anno1[
i],
cp);
for (int
j = 0;
j <
size2;
j++)
result[
j +
size1] =
toAnnoType(
anno2[
j],
cp);
return
result;
}
else{
ArrayList annotations = new
ArrayList();
for (int
i = 0 ;
i <
size1 ;
i++){
try{
annotations.
add(
toAnnoType(
anno1[
i],
cp));
}
catch(
ClassNotFoundException e){}
}
for (int
j = 0;
j <
size2;
j++) {
try{
annotations.
add(
toAnnoType(
anno2[
j],
cp));
}
catch(
ClassNotFoundException e){}
}
return
annotations.
toArray();
}
}
static
Object[][] toAnnotationType(boolean
ignoreNotFound,
ClassPool cp,
ParameterAnnotationsAttribute a1,
ParameterAnnotationsAttribute a2,
MethodInfo minfo)
throws
ClassNotFoundException
{
int
numParameters = 0;
if (
a1 != null)
numParameters =
a1.
numParameters();
else if (
a2 != null)
numParameters =
a2.
numParameters();
else
numParameters =
Descriptor.
numOfParameters(
minfo.
getDescriptor());
Object[][]
result = new
Object[
numParameters][];
for (int
i = 0;
i <
numParameters;
i++) {
Annotation[]
anno1,
anno2;
int
size1,
size2;
if (
a1 == null) {
anno1 = null;
size1 = 0;
}
else {
anno1 =
a1.
getAnnotations()[
i];
size1 =
anno1.
length;
}
if (
a2 == null) {
anno2 = null;
size2 = 0;
}
else {
anno2 =
a2.
getAnnotations()[
i];
size2 =
anno2.
length;
}
if (!
ignoreNotFound){
result[
i] = new
Object[
size1 +
size2];
for (int
j = 0;
j <
size1; ++
j)
result[
i][
j] =
toAnnoType(
anno1[
j],
cp);
for (int
j = 0;
j <
size2; ++
j)
result[
i][
j +
size1] =
toAnnoType(
anno2[
j],
cp);
}
else{
ArrayList annotations = new
ArrayList();
for (int
j = 0 ;
j <
size1 ;
j++){
try{
annotations.
add(
toAnnoType(
anno1[
j],
cp));
}
catch(
ClassNotFoundException e){}
}
for (int
j = 0;
j <
size2;
j++){
try{
annotations.
add(
toAnnoType(
anno2[
j],
cp));
}
catch(
ClassNotFoundException e){}
}
result[
i] =
annotations.
toArray();
}
}
return
result;
}
private static
Object toAnnoType(
Annotation anno,
ClassPool cp)
throws
ClassNotFoundException
{
try {
ClassLoader cl =
cp.
getClassLoader();
return
anno.
toAnnotationType(
cl,
cp);
}
catch (
ClassNotFoundException e) {
ClassLoader cl2 =
cp.
getClass().
getClassLoader();
return
anno.
toAnnotationType(
cl2,
cp);
}
}
public boolean subclassOf(
CtClass superclass) {
if (
superclass == null)
return false;
String superName =
superclass.
getName();
CtClass curr = this;
try {
while (
curr != null) {
if (
curr.
getName().
equals(
superName))
return true;
curr =
curr.
getSuperclass();
}
}
catch (
Exception ignored) {}
return false;
}
public
CtClass getSuperclass() throws
NotFoundException {
String supername =
getClassFile2().
getSuperclass();
if (
supername == null)
return null;
else
return
classPool.
get(
supername);
}
public void setSuperclass(
CtClass clazz) throws
CannotCompileException {
checkModify();
if (
isInterface())
addInterface(
clazz);
else
getClassFile2().
setSuperclass(
clazz.
getName());
}
public
CtClass[] getInterfaces() throws
NotFoundException {
String[]
ifs =
getClassFile2().
getInterfaces();
int
num =
ifs.length;
CtClass[]
ifc = new
CtClass[
num];
for (int
i = 0;
i <
num; ++
i)
ifc[
i] =
classPool.
get(
ifs[
i]);
return
ifc;
}
public void setInterfaces(
CtClass[]
list) {
checkModify();
String[]
ifs;
if (
list == null)
ifs = new
String[0];
else {
int
num =
list.
length;
ifs = new
String[
num];
for (int
i = 0;
i <
num; ++
i)
ifs[
i] =
list[
i].
getName();
}
getClassFile2().
setInterfaces(
ifs);
}
public void addInterface(
CtClass anInterface) {
checkModify();
if (
anInterface != null)
getClassFile2().
addInterface(
anInterface.
getName());
}
public
CtClass getDeclaringClass() throws
NotFoundException {
ClassFile cf =
getClassFile2();
InnerClassesAttribute ica = (
InnerClassesAttribute)
cf.
getAttribute(
InnerClassesAttribute.
tag);
if (
ica == null)
return null;
String name =
getName();
int
n =
ica.
tableLength();
for (int
i = 0;
i <
n; ++
i)
if (
name.
equals(
ica.
innerClass(
i))) {
String outName =
ica.
outerClass(
i);
if (
outName != null)
return
classPool.
get(
outName);
else {
// maybe anonymous or local class.
EnclosingMethodAttribute ema
= (
EnclosingMethodAttribute)
cf.
getAttribute(
EnclosingMethodAttribute.
tag);
if (
ema != null)
return
classPool.
get(
ema.
className());
}
}
return null;
}
public
CtMethod getEnclosingMethod() throws
NotFoundException {
ClassFile cf =
getClassFile2();
EnclosingMethodAttribute ema
= (
EnclosingMethodAttribute)
cf.
getAttribute(
EnclosingMethodAttribute.
tag);
if (
ema != null) {
CtClass enc =
classPool.
get(
ema.
className());
return
enc.
getMethod(
ema.
methodName(),
ema.
methodDescriptor());
}
return null;
}
public
CtClass makeNestedClass(
String name, boolean
isStatic) {
if (!
isStatic)
throw new
RuntimeException(
"sorry, only nested static class is supported");
checkModify();
CtClass c =
classPool.
makeNestedClass(
getName() + "$" +
name);
ClassFile cf =
getClassFile2();
ClassFile cf2 =
c.
getClassFile2();
InnerClassesAttribute ica = (
InnerClassesAttribute)
cf.
getAttribute(
InnerClassesAttribute.
tag);
if (
ica == null) {
ica = new
InnerClassesAttribute(
cf.
getConstPool());
cf.
addAttribute(
ica);
}
ica.
append(
c.
getName(), this.
getName(),
name,
(
cf2.
getAccessFlags() & ~
AccessFlag.
SUPER) |
AccessFlag.
STATIC);
cf2.
addAttribute(
ica.
copy(
cf2.
getConstPool(), null));
return
c;
}
/* flush cached names.
*/
private void
nameReplaced() {
CtMember.
Cache cache =
hasMemberCache();
if (
cache != null) {
CtMember mth =
cache.
methodHead();
CtMember tail =
cache.
lastMethod();
while (
mth !=
tail) {
mth =
mth.
next();
mth.
nameReplaced();
}
}
}
/**
* Returns null if members are not cached.
*/
protected
CtMember.
Cache hasMemberCache() {
if (
memberCache != null)
return (
CtMember.
Cache)
memberCache.
get();
else
return null;
}
protected synchronized
CtMember.
Cache getMembers() {
CtMember.
Cache cache = null;
if (
memberCache == null
|| (
cache = (
CtMember.
Cache)
memberCache.
get()) == null) {
cache = new
CtMember.
Cache(this);
makeFieldCache(
cache);
makeBehaviorCache(
cache);
memberCache = new
WeakReference(
cache);
}
return
cache;
}
private void makeFieldCache(
CtMember.
Cache cache) {
List list =
getClassFile2().
getFields();
int
n =
list.
size();
for (int
i = 0;
i <
n; ++
i) {
FieldInfo finfo = (
FieldInfo)
list.
get(
i);
CtField newField = new
CtField(
finfo, this);
cache.
addField(
newField);
}
}
private void makeBehaviorCache(
CtMember.
Cache cache) {
List list =
getClassFile2().
getMethods();
int
n =
list.
size();
for (int
i = 0;
i <
n; ++
i) {
MethodInfo minfo = (
MethodInfo)
list.
get(
i);
if (
minfo.
isMethod()) {
CtMethod newMethod = new
CtMethod(
minfo, this);
cache.
addMethod(
newMethod);
}
else {
CtConstructor newCons = new
CtConstructor(
minfo, this);
cache.
addConstructor(
newCons);
}
}
}
public
CtField[] getFields() {
ArrayList alist = new
ArrayList();
getFields(
alist, this);
return (
CtField[])
alist.
toArray(new
CtField[
alist.
size()]);
}
private static void getFields(
ArrayList alist,
CtClass cc) {
int
i,
num;
if (
cc == null)
return;
try {
getFields(
alist,
cc.
getSuperclass());
}
catch (
NotFoundException e) {}
try {
CtClass[]
ifs =
cc.
getInterfaces();
num =
ifs.
length;
for (
i = 0;
i <
num; ++
i)
getFields(
alist,
ifs[
i]);
}
catch (
NotFoundException e) {}
CtMember.
Cache memCache = ((
CtClassType)
cc).
getMembers();
CtMember field =
memCache.
fieldHead();
CtMember tail =
memCache.
lastField();
while (
field !=
tail) {
field =
field.
next();
if (!
Modifier.
isPrivate(
field.
getModifiers()))
alist.
add(
field);
}
}
public
CtField getField(
String name,
String desc) throws
NotFoundException {
CtField f =
getField2(
name,
desc);
return
checkGetField(
f,
name,
desc);
}
private
CtField checkGetField(
CtField f,
String name,
String desc)
throws
NotFoundException
{
if (
f == null) {
String msg = "field: " +
name;
if (
desc != null)
msg += " type " +
desc;
throw new
NotFoundException(
msg + " in " +
getName());
}
else
return
f;
}
CtField getField2(
String name,
String desc) {
CtField df =
getDeclaredField2(
name,
desc);
if (
df != null)
return
df;
try {
CtClass[]
ifs =
getInterfaces();
int
num =
ifs.
length;
for (int
i = 0;
i <
num; ++
i) {
CtField f =
ifs[
i].
getField2(
name,
desc);
if (
f != null)
return
f;
}
CtClass s =
getSuperclass();
if (
s != null)
return
s.
getField2(
name,
desc);
}
catch (
NotFoundException e) {}
return null;
}
public
CtField[] getDeclaredFields() {
CtMember.
Cache memCache =
getMembers();
CtMember field =
memCache.
fieldHead();
CtMember tail =
memCache.
lastField();
int
num =
CtMember.
Cache.
count(
field,
tail);
CtField[]
cfs = new
CtField[
num];
int
i = 0;
while (
field !=
tail) {
field =
field.
next();
cfs[
i++] = (
CtField)
field;
}
return
cfs;
}
public
CtField getDeclaredField(
String name) throws
NotFoundException {
return
getDeclaredField(
name, null);
}
public
CtField getDeclaredField(
String name,
String desc) throws
NotFoundException {
CtField f =
getDeclaredField2(
name,
desc);
return
checkGetField(
f,
name,
desc);
}
private
CtField getDeclaredField2(
String name,
String desc) {
CtMember.
Cache memCache =
getMembers();
CtMember field =
memCache.
fieldHead();
CtMember tail =
memCache.
lastField();
while (
field !=
tail) {
field =
field.
next();
if (
field.
getName().
equals(
name)
&& (
desc == null ||
desc.
equals(
field.
getSignature())))
return (
CtField)
field;
}
return null;
}
public
CtBehavior[] getDeclaredBehaviors() {
CtMember.
Cache memCache =
getMembers();
CtMember cons =
memCache.
consHead();
CtMember consTail =
memCache.
lastCons();
int
cnum =
CtMember.
Cache.
count(
cons,
consTail);
CtMember mth =
memCache.
methodHead();
CtMember mthTail =
memCache.
lastMethod();
int
mnum =
CtMember.
Cache.
count(
mth,
mthTail);
CtBehavior[]
cb = new
CtBehavior[
cnum +
mnum];
int
i = 0;
while (
cons !=
consTail) {
cons =
cons.
next();
cb[
i++] = (
CtBehavior)
cons;
}
while (
mth !=
mthTail) {
mth =
mth.
next();
cb[
i++] = (
CtBehavior)
mth;
}
return
cb;
}
public
CtConstructor[] getConstructors() {
CtMember.
Cache memCache =
getMembers();
CtMember cons =
memCache.
consHead();
CtMember consTail =
memCache.
lastCons();
int
n = 0;
CtMember mem =
cons;
while (
mem !=
consTail) {
mem =
mem.
next();
if (
isPubCons((
CtConstructor)
mem))
n++;
}
CtConstructor[]
result = new
CtConstructor[
n];
int
i = 0;
mem =
cons;
while (
mem !=
consTail) {
mem =
mem.
next();
CtConstructor cc = (
CtConstructor)
mem;
if (
isPubCons(
cc))
result[
i++] =
cc;
}
return
result;
}
private static boolean isPubCons(
CtConstructor cons) {
return !
Modifier.
isPrivate(
cons.
getModifiers())
&&
cons.
isConstructor();
}
public
CtConstructor getConstructor(
String desc)
throws
NotFoundException
{
CtMember.
Cache memCache =
getMembers();
CtMember cons =
memCache.
consHead();
CtMember consTail =
memCache.
lastCons();
while (
cons !=
consTail) {
cons =
cons.
next();
CtConstructor cc = (
CtConstructor)
cons;
if (
cc.
getMethodInfo2().
getDescriptor().
equals(
desc)
&&
cc.
isConstructor())
return
cc;
}
return super.
getConstructor(
desc);
}
public
CtConstructor[] getDeclaredConstructors() {
CtMember.
Cache memCache =
getMembers();
CtMember cons =
memCache.
consHead();
CtMember consTail =
memCache.
lastCons();
int
n = 0;
CtMember mem =
cons;
while (
mem !=
consTail) {
mem =
mem.
next();
CtConstructor cc = (
CtConstructor)
mem;
if (
cc.
isConstructor())
n++;
}
CtConstructor[]
result = new
CtConstructor[
n];
int
i = 0;
mem =
cons;
while (
mem !=
consTail) {
mem =
mem.
next();
CtConstructor cc = (
CtConstructor)
mem;
if (
cc.
isConstructor())
result[
i++] =
cc;
}
return
result;
}
public
CtConstructor getClassInitializer() {
CtMember.
Cache memCache =
getMembers();
CtMember cons =
memCache.
consHead();
CtMember consTail =
memCache.
lastCons();
while (
cons !=
consTail) {
cons =
cons.
next();
CtConstructor cc = (
CtConstructor)
cons;
if (
cc.
isClassInitializer())
return
cc;
}
return null;
}
public
CtMethod[] getMethods() {
HashMap h = new
HashMap();
getMethods0(
h, this);
return (
CtMethod[])
h.
values().
toArray(new
CtMethod[
h.
size()]);
}
private static void getMethods0(
HashMap h,
CtClass cc) {
try {
CtClass[]
ifs =
cc.
getInterfaces();
int
size =
ifs.
length;
for (int
i = 0;
i <
size; ++
i)
getMethods0(
h,
ifs[
i]);
}
catch (
NotFoundException e) {}
try {
CtClass s =
cc.
getSuperclass();
if (
s != null)
getMethods0(
h,
s);
}
catch (
NotFoundException e) {}
if (
cc instanceof
CtClassType) {
CtMember.
Cache memCache = ((
CtClassType)
cc).
getMembers();
CtMember mth =
memCache.
methodHead();
CtMember mthTail =
memCache.
lastMethod();
while (
mth !=
mthTail) {
mth =
mth.
next();
if (!
Modifier.
isPrivate(
mth.
getModifiers()))
h.
put(((
CtMethod)
mth).
getStringRep(),
mth);
}
}
}
public
CtMethod getMethod(
String name,
String desc)
throws
NotFoundException
{
CtMethod m =
getMethod0(this,
name,
desc);
if (
m != null)
return
m;
else
throw new
NotFoundException(
name + "(..) is not found in "
+
getName());
}
private static
CtMethod getMethod0(
CtClass cc,
String name,
String desc) {
if (
cc instanceof
CtClassType) {
CtMember.
Cache memCache = ((
CtClassType)
cc).
getMembers();
CtMember mth =
memCache.
methodHead();
CtMember mthTail =
memCache.
lastMethod();
while (
mth !=
mthTail) {
mth =
mth.
next();
if (
mth.
getName().
equals(
name)
&& ((
CtMethod)
mth).
getMethodInfo2().
getDescriptor().
equals(
desc))
return (
CtMethod)
mth;
}
}
try {
CtClass s =
cc.
getSuperclass();
if (
s != null) {
CtMethod m =
getMethod0(
s,
name,
desc);
if (
m != null)
return
m;
}
}
catch (
NotFoundException e) {}
try {
CtClass[]
ifs =
cc.
getInterfaces();
int
size =
ifs.
length;
for (int
i = 0;
i <
size; ++
i) {
CtMethod m =
getMethod0(
ifs[
i],
name,
desc);
if (
m != null)
return
m;
}
}
catch (
NotFoundException e) {}
return null;
}
public
CtMethod[] getDeclaredMethods() {
CtMember.
Cache memCache =
getMembers();
CtMember mth =
memCache.
methodHead();
CtMember mthTail =
memCache.
lastMethod();
int
num =
CtMember.
Cache.
count(
mth,
mthTail);
CtMethod[]
cms = new
CtMethod[
num];
int
i = 0;
while (
mth !=
mthTail) {
mth =
mth.
next();
cms[
i++] = (
CtMethod)
mth;
}
return
cms;
}
public
CtMethod getDeclaredMethod(
String name) throws
NotFoundException {
CtMember.
Cache memCache =
getMembers();
CtMember mth =
memCache.
methodHead();
CtMember mthTail =
memCache.
lastMethod();
while (
mth !=
mthTail) {
mth =
mth.
next();
if (
mth.
getName().
equals(
name))
return (
CtMethod)
mth;
}
throw new
NotFoundException(
name + "(..) is not found in "
+
getName());
}
public
CtMethod getDeclaredMethod(
String name,
CtClass[]
params)
throws
NotFoundException
{
String desc =
Descriptor.
ofParameters(
params);
CtMember.
Cache memCache =
getMembers();
CtMember mth =
memCache.
methodHead();
CtMember mthTail =
memCache.
lastMethod();
while (
mth !=
mthTail) {
mth =
mth.
next();
if (
mth.
getName().
equals(
name)
&& ((
CtMethod)
mth).
getMethodInfo2().
getDescriptor().
startsWith(
desc))
return (
CtMethod)
mth;
}
throw new
NotFoundException(
name + "(..) is not found in "
+
getName());
}
public void addField(
CtField f,
String init)
throws
CannotCompileException
{
addField(
f,
CtField.
Initializer.
byExpr(
init));
}
public void addField(
CtField f,
CtField.
Initializer init)
throws
CannotCompileException
{
checkModify();
if (
f.
getDeclaringClass() != this)
throw new
CannotCompileException("cannot add");
if (
init == null)
init =
f.
getInit();
if (
init != null) {
init.
check(
f.
getSignature());
int
mod =
f.
getModifiers();
if (
Modifier.
isStatic(
mod) &&
Modifier.
isFinal(
mod))
try {
ConstPool cp =
getClassFile2().
getConstPool();
int
index =
init.
getConstantValue(
cp,
f.
getType());
if (
index != 0) {
f.
getFieldInfo2().
addAttribute(new
ConstantAttribute(
cp,
index));
init = null;
}
}
catch (
NotFoundException e) {}
}
getMembers().
addField(
f);
getClassFile2().
addField(
f.
getFieldInfo2());
if (
init != null) {
FieldInitLink fil = new
FieldInitLink(
f,
init);
FieldInitLink link =
fieldInitializers;
if (
link == null)
fieldInitializers =
fil;
else {
while (
link.
next != null)
link =
link.
next;
link.
next =
fil;
}
}
}
public void removeField(
CtField f) throws
NotFoundException {
checkModify();
FieldInfo fi =
f.
getFieldInfo2();
ClassFile cf =
getClassFile2();
if (
cf.
getFields().
remove(
fi)) {
getMembers().
remove(
f);
gcConstPool = true;
}
else
throw new
NotFoundException(
f.
toString());
}
public
CtConstructor makeClassInitializer()
throws
CannotCompileException
{
CtConstructor clinit =
getClassInitializer();
if (
clinit != null)
return
clinit;
checkModify();
ClassFile cf =
getClassFile2();
Bytecode code = new
Bytecode(
cf.
getConstPool(), 0, 0);
modifyClassConstructor(
cf,
code, 0, 0);
return
getClassInitializer();
}
public void addConstructor(
CtConstructor c)
throws
CannotCompileException
{
checkModify();
if (
c.
getDeclaringClass() != this)
throw new
CannotCompileException("cannot add");
getMembers().
addConstructor(
c);
getClassFile2().
addMethod(
c.
getMethodInfo2());
}
public void removeConstructor(
CtConstructor m) throws
NotFoundException {
checkModify();
MethodInfo mi =
m.
getMethodInfo2();
ClassFile cf =
getClassFile2();
if (
cf.
getMethods().
remove(
mi)) {
getMembers().
remove(
m);
gcConstPool = true;
}
else
throw new
NotFoundException(
m.
toString());
}
public void addMethod(
CtMethod m) throws
CannotCompileException {
checkModify();
if (
m.
getDeclaringClass() != this)
throw new
CannotCompileException("bad declaring class");
int
mod =
m.
getModifiers();
if ((
getModifiers() &
Modifier.
INTERFACE) != 0) {
m.
setModifiers(
mod |
Modifier.
PUBLIC);
if ((
mod &
Modifier.
ABSTRACT) == 0)
throw new
CannotCompileException(
"an interface method must be abstract: " +
m.
toString());
}
getMembers().
addMethod(
m);
getClassFile2().
addMethod(
m.
getMethodInfo2());
if ((
mod &
Modifier.
ABSTRACT) != 0)
setModifiers(
getModifiers() |
Modifier.
ABSTRACT);
}
public void removeMethod(
CtMethod m) throws
NotFoundException {
checkModify();
MethodInfo mi =
m.
getMethodInfo2();
ClassFile cf =
getClassFile2();
if (
cf.
getMethods().
remove(
mi)) {
getMembers().
remove(
m);
gcConstPool = true;
}
else
throw new
NotFoundException(
m.
toString());
}
public byte[]
getAttribute(
String name) {
AttributeInfo ai =
getClassFile2().
getAttribute(
name);
if (
ai == null)
return null;
else
return
ai.
get();
}
public void
setAttribute(
String name, byte[]
data) {
checkModify();
ClassFile cf =
getClassFile2();
cf.
addAttribute(new
AttributeInfo(
cf.
getConstPool(),
name,
data));
}
public void instrument(
CodeConverter converter)
throws
CannotCompileException
{
checkModify();
ClassFile cf =
getClassFile2();
ConstPool cp =
cf.
getConstPool();
List list =
cf.
getMethods();
int
n =
list.
size();
for (int
i = 0;
i <
n; ++
i) {
MethodInfo minfo = (
MethodInfo)
list.
get(
i);
converter.
doit(this,
minfo,
cp);
}
}
public void instrument(
ExprEditor editor)
throws
CannotCompileException
{
checkModify();
ClassFile cf =
getClassFile2();
List list =
cf.
getMethods();
int
n =
list.
size();
for (int
i = 0;
i <
n; ++
i) {
MethodInfo minfo = (
MethodInfo)
list.
get(
i);
editor.
doit(this,
minfo);
}
}
/**
* @see javassist.CtClass#prune()
* @see javassist.CtClass#stopPruning(boolean)
*/
public void
prune() {
if (
wasPruned)
return;
wasPruned =
wasFrozen = true;
getClassFile2().
prune();
}
public void
rebuildClassFile() {
gcConstPool = true; }
public void toBytecode(
DataOutputStream out)
throws
CannotCompileException,
IOException
{
try {
if (
isModified()) {
checkPruned("toBytecode");
ClassFile cf =
getClassFile2();
if (
gcConstPool) {
cf.
compact();
gcConstPool = false;
}
modifyClassConstructor(
cf);
modifyConstructors(
cf);
if (
debugDump != null)
dumpClassFile(
cf);
cf.
write(
out);
out.
flush();
fieldInitializers = null;
if (
doPruning) {
// to save memory
cf.
prune();
wasPruned = true;
}
}
else {
classPool.
writeClassfile(
getName(),
out);
// to save memory
// classfile = null;
}
getCount = 0;
wasFrozen = true;
}
catch (
NotFoundException e) {
throw new
CannotCompileException(
e);
}
catch (
IOException e) {
throw new
CannotCompileException(
e);
}
}
private void dumpClassFile(
ClassFile cf) throws
IOException {
DataOutputStream dump =
makeFileOutput(
debugDump);
try {
cf.
write(
dump);
}
finally {
dump.
close();
}
}
/* See also checkModified()
*/
private void
checkPruned(
String method) {
if (
wasPruned)
throw new
RuntimeException(
method + "(): " +
getName()
+ " was pruned.");
}
public boolean
stopPruning(boolean
stop) {
boolean
prev = !
doPruning;
doPruning = !
stop;
return
prev;
}
private void modifyClassConstructor(
ClassFile cf)
throws
CannotCompileException,
NotFoundException
{
if (
fieldInitializers == null)
return;
Bytecode code = new
Bytecode(
cf.
getConstPool(), 0, 0);
Javac jv = new
Javac(
code, this);
int
stacksize = 0;
boolean
doInit = false;
for (
FieldInitLink fi =
fieldInitializers;
fi != null;
fi =
fi.
next) {
CtField f =
fi.
field;
if (
Modifier.
isStatic(
f.
getModifiers())) {
doInit = true;
int
s =
fi.
init.
compileIfStatic(
f.
getType(),
f.
getName(),
code,
jv);
if (
stacksize <
s)
stacksize =
s;
}
}
if (
doInit) // need an initializer for static fileds.
modifyClassConstructor(
cf,
code,
stacksize, 0);
}
private void modifyClassConstructor(
ClassFile cf,
Bytecode code,
int
stacksize, int
localsize)
throws
CannotCompileException
{
MethodInfo m =
cf.
getStaticInitializer();
if (
m == null) {
code.
add(
Bytecode.
RETURN);
code.
setMaxStack(
stacksize);
code.
setMaxLocals(
localsize);
m = new
MethodInfo(
cf.
getConstPool(), "<clinit>", "()V");
m.
setAccessFlags(
AccessFlag.
STATIC);
m.
setCodeAttribute(
code.
toCodeAttribute());
cf.
addMethod(
m);
CtMember.
Cache cache =
hasMemberCache();
if (
cache != null)
cache.
addConstructor(new
CtConstructor(
m, this));
}
else {
CodeAttribute codeAttr =
m.
getCodeAttribute();
if (
codeAttr == null)
throw new
CannotCompileException("empty <clinit>");
try {
CodeIterator it =
codeAttr.
iterator();
int
pos =
it.
insertEx(
code.
get());
it.
insert(
code.
getExceptionTable(),
pos);
int
maxstack =
codeAttr.
getMaxStack();
if (
maxstack <
stacksize)
codeAttr.
setMaxStack(
stacksize);
int
maxlocals =
codeAttr.
getMaxLocals();
if (
maxlocals <
localsize)
codeAttr.
setMaxLocals(
localsize);
}
catch (
BadBytecode e) {
throw new
CannotCompileException(
e);
}
}
try {
m.
rebuildStackMapIf6(
classPool,
cf);
}
catch (
BadBytecode e) {
throw new
CannotCompileException(
e);
}
}
private void modifyConstructors(
ClassFile cf)
throws
CannotCompileException,
NotFoundException
{
if (
fieldInitializers == null)
return;
ConstPool cp =
cf.
getConstPool();
List list =
cf.
getMethods();
int
n =
list.
size();
for (int
i = 0;
i <
n; ++
i) {
MethodInfo minfo = (
MethodInfo)
list.
get(
i);
if (
minfo.
isConstructor()) {
CodeAttribute codeAttr =
minfo.
getCodeAttribute();
if (
codeAttr != null)
try {
Bytecode init = new
Bytecode(
cp, 0,
codeAttr.
getMaxLocals());
CtClass[]
params
=
Descriptor.
getParameterTypes(
minfo.
getDescriptor(),
classPool);
int
stacksize =
makeFieldInitializer(
init,
params);
insertAuxInitializer(
codeAttr,
init,
stacksize);
minfo.
rebuildStackMapIf6(
classPool,
cf);
}
catch (
BadBytecode e) {
throw new
CannotCompileException(
e);
}
}
}
}
private static void insertAuxInitializer(
CodeAttribute codeAttr,
Bytecode initializer,
int
stacksize)
throws
BadBytecode
{
CodeIterator it =
codeAttr.
iterator();
int
index =
it.
skipSuperConstructor();
if (
index < 0) {
index =
it.
skipThisConstructor();
if (
index >= 0)
return; // this() is called.
// Neither this() or super() is called.
}
int
pos =
it.
insertEx(
initializer.
get());
it.
insert(
initializer.
getExceptionTable(),
pos);
int
maxstack =
codeAttr.
getMaxStack();
if (
maxstack <
stacksize)
codeAttr.
setMaxStack(
stacksize);
}
private int makeFieldInitializer(
Bytecode code,
CtClass[]
parameters)
throws
CannotCompileException,
NotFoundException
{
int
stacksize = 0;
Javac jv = new
Javac(
code, this);
try {
jv.
recordParams(
parameters, false);
}
catch (
CompileError e) {
throw new
CannotCompileException(
e);
}
for (
FieldInitLink fi =
fieldInitializers;
fi != null;
fi =
fi.
next) {
CtField f =
fi.
field;
if (!
Modifier.
isStatic(
f.
getModifiers())) {
int
s =
fi.
init.
compile(
f.
getType(),
f.
getName(),
code,
parameters,
jv);
if (
stacksize <
s)
stacksize =
s;
}
}
return
stacksize;
}
// Methods used by CtNewWrappedMethod
Hashtable getHiddenMethods() {
if (
hiddenMethods == null)
hiddenMethods = new
Hashtable();
return
hiddenMethods;
}
int
getUniqueNumber() { return
uniqueNumberSeed++; }
public
String makeUniqueName(
String prefix) {
HashMap table = new
HashMap();
makeMemberList(
table);
Set keys =
table.
keySet();
String[]
methods = new
String[
keys.
size()];
keys.
toArray(
methods);
if (
notFindInArray(
prefix,
methods))
return
prefix;
int
i = 100;
String name;
do {
if (
i > 999)
throw new
RuntimeException("too many unique name");
name =
prefix +
i++;
} while (!
notFindInArray(
name,
methods));
return
name;
}
private static boolean
notFindInArray(
String prefix,
String[]
values) {
int
len =
values.length;
for (int
i = 0;
i <
len;
i++)
if (
values[
i].
startsWith(
prefix))
return false;
return true;
}
private void
makeMemberList(
HashMap table) {
int
mod =
getModifiers();
if (
Modifier.
isAbstract(
mod) ||
Modifier.
isInterface(
mod))
try {
CtClass[]
ifs =
getInterfaces();
int
size =
ifs.
length;
for (int
i = 0;
i <
size;
i++) {
CtClass ic =
ifs[
i];
if (
ic != null &&
ic instanceof
CtClassType)
((
CtClassType)
ic).
makeMemberList(
table);
}
}
catch (
NotFoundException e) {}
try {
CtClass s =
getSuperclass();
if (
s != null &&
s instanceof
CtClassType)
((
CtClassType)
s).
makeMemberList(
table);
}
catch (
NotFoundException e) {}
List list =
getClassFile2().
getMethods();
int
n =
list.
size();
for (int
i = 0;
i <
n;
i++) {
MethodInfo minfo = (
MethodInfo)
list.
get(
i);
table.
put(
minfo.
getName(), this);
}
list =
getClassFile2().
getFields();
n =
list.
size();
for (int
i = 0;
i <
n;
i++) {
FieldInfo finfo = (
FieldInfo)
list.
get(
i);
table.
put(
finfo.
getName(), this);
}
}
}
class
FieldInitLink {
FieldInitLink next;
CtField field;
CtField.
Initializer init;
FieldInitLink(
CtField f,
CtField.
Initializer i) {
next = null;
field =
f;
init =
i;
}
}