com.feilong.lib.javassist.CtClassType Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
/*
* 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 com.feilong.lib.javassist;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.feilong.lib.javassist.bytecode.AccessFlag;
import com.feilong.lib.javassist.bytecode.AnnotationsAttribute;
import com.feilong.lib.javassist.bytecode.AttributeInfo;
import com.feilong.lib.javassist.bytecode.BadBytecode;
import com.feilong.lib.javassist.bytecode.Bytecode;
import com.feilong.lib.javassist.bytecode.ClassFile;
import com.feilong.lib.javassist.bytecode.CodeAttribute;
import com.feilong.lib.javassist.bytecode.CodeIterator;
import com.feilong.lib.javassist.bytecode.ConstPool;
import com.feilong.lib.javassist.bytecode.ConstantAttribute;
import com.feilong.lib.javassist.bytecode.Descriptor;
import com.feilong.lib.javassist.bytecode.EnclosingMethodAttribute;
import com.feilong.lib.javassist.bytecode.FieldInfo;
import com.feilong.lib.javassist.bytecode.InnerClassesAttribute;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.bytecode.Opcode;
import com.feilong.lib.javassist.bytecode.ParameterAnnotationsAttribute;
import com.feilong.lib.javassist.bytecode.SignatureAttribute;
import com.feilong.lib.javassist.bytecode.annotation.Annotation;
import com.feilong.lib.javassist.compiler.AccessorMaker;
import com.feilong.lib.javassist.compiler.CompileError;
import com.feilong.lib.javassist.compiler.Javac;
import com.feilong.lib.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 Reference memberCache;
private AccessorMaker accessors;
private FieldInitLink fieldInitializers;
private Map 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();
}
CtClassType(ClassFile cf, ClassPool cp){
this((String) null, cp);
classfile = cf;
qualifiedName = classfile.getName();
}
@Override
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 (CtClass element : intf){
buffer.append(element.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(", ");
}
}
@Override
public AccessorMaker getAccessorMaker(){
if (accessors == null){
accessors = new AccessorMaker(this);
}
return accessors;
}
@Override
public ClassFile getClassFile2(){
return getClassFile3(true);
}
public ClassFile getClassFile3(boolean doCompress){
ClassFile cfile = classfile;
if (cfile != null){
return cfile;
}
if (doCompress){
classPool.compress();
}
if (rawClassfile != null){
try{
ClassFile cf = new ClassFile(new DataInputStream(new ByteArrayInputStream(rawClassfile)));
rawClassfile = null;
getCount = GET_THRESHOLD;
return setClassFile(cf);
}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");
}
return setClassFile(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)
*/
@Override
final void incGetCounter(){
++getCount;
}
/**
* Invoked from ClassPool#compress().
* It releases the class files that have not been recently used
* if they are unmodified.
*/
@Override
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 removeClassFile() 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;
}
}
/**
* Updates {@code classfile} if it is null.
*/
private synchronized ClassFile setClassFile(ClassFile cf){
if (classfile == null){
classfile = cf;
}
return classfile;
}
@Override
public ClassPool getClassPool(){
return classPool;
}
void setClassPool(ClassPool cp){
classPool = cp;
}
@Override
public URL getURL() throws NotFoundException{
URL url = classPool.find(getName());
if (url == null){
throw new NotFoundException(getName());
}
return url;
}
@Override
public boolean isModified(){
return wasChanged;
}
@Override
public boolean isFrozen(){
return wasFrozen;
}
@Override
public void freeze(){
wasFrozen = true;
}
@Override
void checkModify() throws RuntimeException{
if (isFrozen()){
String msg = getName() + " class is frozen";
if (wasPruned){
msg += " and pruned";
}
throw new RuntimeException(msg);
}
wasChanged = true;
}
@Override
public void defrost(){
checkPruned("defrost");
wasFrozen = false;
}
@Override
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;
}
@Override
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);
}
@Override
public String getGenericSignature(){
SignatureAttribute sa = (SignatureAttribute) getClassFile2().getAttribute(SignatureAttribute.tag);
return sa == null ? null : sa.getSignature();
}
@Override
public void setGenericSignature(String sig){
ClassFile cf = getClassFile();
SignatureAttribute sa = new SignatureAttribute(cf.getConstPool(), sig);
cf.addAttribute(sa);
}
@Override
public void replaceClassName(ClassMap classnames) throws RuntimeException{
String oldClassName = getName();
String newClassName = 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);
}
}
@Override
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();
}
}
@Override
public boolean isInterface(){
return Modifier.isInterface(getModifiers());
}
@Override
public boolean isAnnotation(){
return Modifier.isAnnotation(getModifiers());
}
@Override
public boolean isEnum(){
return Modifier.isEnum(getModifiers());
}
@Override
public int getModifiers(){
ClassFile cf = getClassFile2();
int acc = cf.getAccessFlags();
acc = AccessFlag.clear(acc, AccessFlag.SUPER);
int inner = cf.getInnerAccessFlags();
if (inner != -1){
if ((inner & AccessFlag.STATIC) != 0){
acc |= AccessFlag.STATIC;
}
if ((inner & AccessFlag.PUBLIC) != 0){
acc |= AccessFlag.PUBLIC;
}else{
acc &= ~AccessFlag.PUBLIC; //clear PUBLIC
if ((inner & AccessFlag.PROTECTED) != 0){
acc |= AccessFlag.PROTECTED;
}else if ((inner & AccessFlag.PRIVATE) != 0){
acc |= AccessFlag.PRIVATE;
}
}
}
return AccessFlag.toModifier(acc);
}
@Override
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();
List 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 list.toArray(new CtClass[list.size()]);
}
@Override
public void setModifiers(int mod){
checkModify();
updateInnerEntry(mod, getName(), this, true);
ClassFile cf = getClassFile2();
cf.setAccessFlags(AccessFlag.of(mod & ~Modifier.STATIC));
}
private static void updateInnerEntry(int newMod,String name,CtClass clazz,boolean outer){
ClassFile cf = clazz.getClassFile2();
InnerClassesAttribute ica = (InnerClassesAttribute) cf.getAttribute(InnerClassesAttribute.tag);
if (ica != null){
// If the class is a static inner class, its modifier
// does not contain the static bit. Its inner class attribute
// contains the static bit.
int mod = newMod & ~Modifier.STATIC;
int i = ica.find(name);
if (i >= 0){
int isStatic = ica.accessFlags(i) & AccessFlag.STATIC;
if (isStatic != 0 || !Modifier.isStatic(newMod)){
clazz.checkModify();
ica.setAccessFlags(i, AccessFlag.of(mod) | isStatic);
String outName = ica.outerClass(i);
if (outName != null && outer){
try{
CtClass parent = clazz.getClassPool().get(outName);
updateInnerEntry(mod, name, parent, false);
}catch (NotFoundException e){
throw new RuntimeException("cannot find the declaring class: " + outName);
}
}
return;
}
}
}
if (Modifier.isStatic(newMod)){
throw new RuntimeException("cannot change " + Descriptor.toJavaName(name) + " into a static class");
}
}
@Override
public boolean hasAnnotation(String annotationName){
ClassFile cf = getClassFile2();
AnnotationsAttribute ainfo = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag);
AnnotationsAttribute ainfo2 = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag);
return hasAnnotationType(annotationName, getClassPool(), ainfo, ainfo2);
}
/**
* @deprecated
*/
@Deprecated
static boolean hasAnnotationType(Class clz,ClassPool cp,AnnotationsAttribute a1,AnnotationsAttribute a2){
return hasAnnotationType(clz.getName(), cp, a1, a2);
}
static boolean hasAnnotationType(String annotationTypeName,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();
}
if (anno1 != null){
for (Annotation element : anno1){
if (element.getTypeName().equals(annotationTypeName)){
return true;
}
}
}
if (anno2 != null){
for (Annotation element : anno2){
if (element.getTypeName().equals(annotationTypeName)){
return true;
}
}
}
return false;
}
@Override
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 (Annotation element : anno1){
if (element.getTypeName().equals(typeName)){
return toAnnoType(element, cp);
}
}
}
if (anno2 != null){
for (Annotation element : anno2){
if (element.getTypeName().equals(typeName)){
return toAnnoType(element, cp);
}
}
}
return null;
}
@Override
public Object[] getAnnotations() throws ClassNotFoundException{
return getAnnotations(false);
}
@Override
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;
}
List