/* * 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; } }