0001: /*
0002: * Javassist, a Java-bytecode translator toolkit.
0003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
0004: *
0005: * The contents of this file are subject to the Mozilla Public License Version
0006: * 1.1 (the "License"); you may not use this file except in compliance with
0007: * the License. Alternatively, the contents of this file may be used under
0008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
0009: *
0010: * Software distributed under the License is distributed on an "AS IS" basis,
0011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0012: * for the specific language governing rights and limitations under the
0013: * License.
0014: */
0015:
0016: package javassist;
0017:
0018: import java.io.BufferedInputStream;
0019: import java.io.DataInputStream;
0020: import java.io.DataOutputStream;
0021: import java.io.IOException;
0022: import java.io.InputStream;
0023: import java.net.URL;
0024: import java.util.ArrayList;
0025: import java.util.Enumeration;
0026: import java.util.HashMap;
0027: import java.util.Hashtable;
0028: import java.util.List;
0029: import java.util.Set;
0030:
0031: import javassist.bytecode.AccessFlag;
0032: import javassist.bytecode.AttributeInfo;
0033: import javassist.bytecode.AnnotationsAttribute;
0034: import javassist.bytecode.BadBytecode;
0035: import javassist.bytecode.Bytecode;
0036: import javassist.bytecode.ClassFile;
0037: import javassist.bytecode.CodeAttribute;
0038: import javassist.bytecode.ConstantAttribute;
0039: import javassist.bytecode.CodeIterator;
0040: import javassist.bytecode.ConstPool;
0041: import javassist.bytecode.Descriptor;
0042: import javassist.bytecode.EnclosingMethodAttribute;
0043: import javassist.bytecode.FieldInfo;
0044: import javassist.bytecode.InnerClassesAttribute;
0045: import javassist.bytecode.MethodInfo;
0046: import javassist.bytecode.ParameterAnnotationsAttribute;
0047: import javassist.bytecode.annotation.Annotation;
0048: import javassist.compiler.AccessorMaker;
0049: import javassist.compiler.CompileError;
0050: import javassist.compiler.Javac;
0051: import javassist.expr.ExprEditor;
0052:
0053: /**
0054: * Class types.
0055: */
0056: class CtClassType extends CtClass {
0057: ClassPool classPool;
0058: boolean wasChanged;
0059: private boolean wasFrozen;
0060: boolean wasPruned;
0061: boolean memberRemoved;
0062: ClassFile classfile;
0063:
0064: private CtMember fieldsCache;
0065: private CtMember methodsCache;
0066: private CtMember constructorsCache;
0067: private CtConstructor classInitializerCache;
0068:
0069: private AccessorMaker accessors;
0070:
0071: private FieldInitLink fieldInitializers;
0072: private Hashtable hiddenMethods; // must be synchronous
0073: private int uniqueNumberSeed;
0074:
0075: private boolean doPruning = ClassPool.doPruning;
0076: int getCounter;
0077: private static int readCounter = 0;
0078: private static final int READ_THRESHOLD = 100; // see getClassFile2()
0079:
0080: CtClassType(String name, ClassPool cp) {
0081: super (name);
0082: classPool = cp;
0083: wasChanged = wasFrozen = wasPruned = memberRemoved = false;
0084: classfile = null;
0085: accessors = null;
0086: fieldInitializers = null;
0087: hiddenMethods = null;
0088: uniqueNumberSeed = 0;
0089: eraseCache();
0090: getCounter = 0;
0091: }
0092:
0093: CtClassType(InputStream ins, ClassPool cp) throws IOException {
0094: this ((String) null, cp);
0095: classfile = new ClassFile(new DataInputStream(ins));
0096: qualifiedName = classfile.getName();
0097: }
0098:
0099: protected void extendToString(StringBuffer buffer) {
0100: if (wasChanged)
0101: buffer.append("changed ");
0102:
0103: if (wasFrozen)
0104: buffer.append("frozen ");
0105:
0106: if (wasPruned)
0107: buffer.append("pruned ");
0108:
0109: buffer.append(Modifier.toString(getModifiers()));
0110: buffer.append(" class ");
0111: buffer.append(getName());
0112:
0113: try {
0114: CtClass ext = getSuperclass();
0115: if (ext != null) {
0116: String name = ext.getName();
0117: if (!name.equals("java.lang.Object"))
0118: buffer.append(" extends " + ext.getName());
0119: }
0120: } catch (NotFoundException e) {
0121: buffer.append(" extends ??");
0122: }
0123:
0124: try {
0125: CtClass[] intf = getInterfaces();
0126: if (intf.length > 0)
0127: buffer.append(" implements ");
0128:
0129: for (int i = 0; i < intf.length; ++i) {
0130: buffer.append(intf[i].getName());
0131: buffer.append(", ");
0132: }
0133: } catch (NotFoundException e) {
0134: buffer.append(" extends ??");
0135: }
0136:
0137: CtMember field = getFieldsCache();
0138: buffer.append(" fields=");
0139: while (field != null) {
0140: buffer.append(field);
0141: buffer.append(", ");
0142: field = field.next;
0143: }
0144:
0145: CtMember c = getConstructorsCache();
0146: buffer.append(" constructors=");
0147: while (c != null) {
0148: buffer.append(c);
0149: buffer.append(", ");
0150: c = c.next;
0151: }
0152:
0153: CtMember m = getMethodsCache();
0154: buffer.append(" methods=");
0155: while (m != null) {
0156: buffer.append(m);
0157: buffer.append(", ");
0158: m = m.next;
0159: }
0160: }
0161:
0162: protected void eraseCache() {
0163: fieldsCache = null;
0164: constructorsCache = null;
0165: classInitializerCache = null;
0166: methodsCache = null;
0167: }
0168:
0169: public AccessorMaker getAccessorMaker() {
0170: if (accessors == null)
0171: accessors = new AccessorMaker(this );
0172:
0173: return accessors;
0174: }
0175:
0176: public ClassFile getClassFile2() {
0177: if (classfile != null)
0178: return classfile;
0179:
0180: if (readCounter++ > READ_THRESHOLD
0181: && ClassPool.releaseUnmodifiedClassFile) {
0182: releaseClassFiles();
0183: readCounter = 0;
0184: }
0185:
0186: InputStream fin = null;
0187: try {
0188: fin = classPool.openClassfile(getName());
0189: if (fin == null)
0190: throw new NotFoundException(getName());
0191:
0192: fin = new BufferedInputStream(fin);
0193: classfile = new ClassFile(new DataInputStream(fin));
0194: if (!classfile.getName().equals(qualifiedName))
0195: throw new RuntimeException("cannot find "
0196: + qualifiedName + ": " + classfile.getName()
0197: + " found in "
0198: + qualifiedName.replace('.', '/') + ".class");
0199:
0200: return classfile;
0201: } catch (NotFoundException e) {
0202: throw new RuntimeException(e.toString());
0203: } catch (IOException e) {
0204: throw new RuntimeException(e.toString());
0205: } finally {
0206: if (fin != null)
0207: try {
0208: fin.close();
0209: } catch (IOException e) {
0210: }
0211: }
0212: }
0213:
0214: /* Inherited from CtClass. Called by get() in ClassPool.
0215: *
0216: * @see javassist.CtClass#incGetCounter()
0217: */
0218: void incGetCounter() {
0219: ++getCounter;
0220: }
0221:
0222: /**
0223: * Releases the class files and cached CtBehaviors
0224: * of the CtClasses that have not been recently used
0225: * if they are unmodified.
0226: */
0227: private void releaseClassFiles() {
0228: Enumeration e = classPool.classes.elements();
0229: while (e.hasMoreElements()) {
0230: Object obj = e.nextElement();
0231: if (obj instanceof CtClassType) {
0232: CtClassType cct = (CtClassType) obj;
0233: if (cct.getCounter < 2 && !cct.isModified()) {
0234: cct.eraseCache();
0235: cct.classfile = null;
0236: }
0237:
0238: cct.getCounter = 0;
0239: }
0240: }
0241: }
0242:
0243: public ClassPool getClassPool() {
0244: return classPool;
0245: }
0246:
0247: void setClassPool(ClassPool cp) {
0248: classPool = cp;
0249: }
0250:
0251: public URL getURL() throws NotFoundException {
0252: URL url = classPool.find(getName());
0253: if (url == null)
0254: throw new NotFoundException(getName());
0255: else
0256: return url;
0257: }
0258:
0259: public boolean isModified() {
0260: return wasChanged;
0261: }
0262:
0263: public boolean isFrozen() {
0264: return wasFrozen;
0265: }
0266:
0267: void freeze() {
0268: wasFrozen = true;
0269: }
0270:
0271: void checkModify() throws RuntimeException {
0272: if (isFrozen()) {
0273: String msg = getName() + " class is frozen";
0274: if (wasPruned)
0275: msg += " and pruned";
0276:
0277: throw new RuntimeException(msg);
0278: }
0279:
0280: wasChanged = true;
0281: }
0282:
0283: public void defrost() {
0284: checkPruned("defrost");
0285: wasFrozen = false;
0286: }
0287:
0288: public boolean subtypeOf(CtClass clazz) throws NotFoundException {
0289: int i;
0290: String cname = clazz.getName();
0291: if (this == clazz || getName().equals(cname))
0292: return true;
0293:
0294: ClassFile file = getClassFile2();
0295: String super name = file.getSuperclass();
0296: if (super name != null && super name.equals(cname))
0297: return true;
0298:
0299: String[] ifs = file.getInterfaces();
0300: int num = ifs.length;
0301: for (i = 0; i < num; ++i)
0302: if (ifs[i].equals(cname))
0303: return true;
0304:
0305: if (super name != null
0306: && classPool.get(super name).subtypeOf(clazz))
0307: return true;
0308:
0309: for (i = 0; i < num; ++i)
0310: if (classPool.get(ifs[i]).subtypeOf(clazz))
0311: return true;
0312:
0313: return false;
0314: }
0315:
0316: public void setName(String name) throws RuntimeException {
0317: String oldname = getName();
0318: if (name.equals(oldname))
0319: return;
0320:
0321: // check this in advance although classNameChanged() below does.
0322: classPool.checkNotFrozen(name);
0323: ClassFile cf = getClassFile2();
0324: super .setName(name);
0325: cf.setName(name);
0326: eraseCache();
0327: classPool.classNameChanged(oldname, this );
0328: }
0329:
0330: public void replaceClassName(ClassMap classnames)
0331: throws RuntimeException {
0332: String oldClassName = getName();
0333: String newClassName = (String) classnames.get(Descriptor
0334: .toJvmName(oldClassName));
0335: if (newClassName != null) {
0336: newClassName = Descriptor.toJavaName(newClassName);
0337: // check this in advance although classNameChanged() below does.
0338: classPool.checkNotFrozen(newClassName);
0339: }
0340:
0341: super .replaceClassName(classnames);
0342: ClassFile cf = getClassFile2();
0343: cf.renameClass(classnames);
0344: eraseCache();
0345:
0346: if (newClassName != null) {
0347: super .setName(newClassName);
0348: classPool.classNameChanged(oldClassName, this );
0349: }
0350: }
0351:
0352: public void replaceClassName(String oldname, String newname)
0353: throws RuntimeException {
0354: String this name = getName();
0355: if (this name.equals(oldname))
0356: setName(newname);
0357: else {
0358: super .replaceClassName(oldname, newname);
0359: getClassFile2().renameClass(oldname, newname);
0360: eraseCache();
0361: }
0362: }
0363:
0364: public boolean isInterface() {
0365: return Modifier.isInterface(getModifiers());
0366: }
0367:
0368: public boolean isAnnotation() {
0369: return Modifier.isAnnotation(getModifiers());
0370: }
0371:
0372: public boolean isEnum() {
0373: return Modifier.isEnum(getModifiers());
0374: }
0375:
0376: public int getModifiers() {
0377: ClassFile cf = getClassFile2();
0378: int acc = cf.getAccessFlags();
0379: acc = AccessFlag.clear(acc, AccessFlag.SUPER);
0380: int inner = cf.getInnerAccessFlags();
0381: if (inner != -1 && (inner & AccessFlag.STATIC) != 0)
0382: acc |= AccessFlag.STATIC;
0383:
0384: return AccessFlag.toModifier(acc);
0385: }
0386:
0387: public CtClass[] getNestedClasses() throws NotFoundException {
0388: ClassFile cf = getClassFile2();
0389: InnerClassesAttribute ica = (InnerClassesAttribute) cf
0390: .getAttribute(InnerClassesAttribute.tag);
0391: if (ica == null)
0392: return new CtClass[0];
0393:
0394: String this Name = cf.getName();
0395: int n = ica.tableLength();
0396: ArrayList list = new ArrayList(n);
0397: for (int i = 0; i < n; i++) {
0398: String outer = ica.outerClass(i);
0399: /*
0400: * If a nested class is local or anonymous,
0401: * the outer_class_info_index is 0.
0402: */
0403: if (outer == null || outer.equals(this Name)) {
0404: String inner = ica.innerClass(i);
0405: if (inner != null)
0406: list.add(classPool.get(inner));
0407: }
0408: }
0409:
0410: return (CtClass[]) list.toArray(new CtClass[list.size()]);
0411: }
0412:
0413: public void setModifiers(int mod) {
0414: ClassFile cf = getClassFile2();
0415: if (Modifier.isStatic(mod)) {
0416: int flags = cf.getInnerAccessFlags();
0417: if (flags != -1 && (flags & AccessFlag.STATIC) != 0)
0418: mod = mod & ~Modifier.STATIC;
0419: else
0420: throw new RuntimeException("cannot change " + getName()
0421: + " into a static class");
0422: }
0423:
0424: checkModify();
0425: int acc = AccessFlag.of(mod) | AccessFlag.SUPER;
0426: cf.setAccessFlags(acc);
0427: }
0428:
0429: public Object[] getAnnotations() throws ClassNotFoundException {
0430: return getAnnotations(false);
0431: }
0432:
0433: public Object[] getAvailableAnnotations() {
0434: try {
0435: return getAnnotations(true);
0436: } catch (ClassNotFoundException e) {
0437: throw new RuntimeException("Unexpected exception ", e);
0438: }
0439: }
0440:
0441: private Object[] getAnnotations(boolean ignoreNotFound)
0442: throws ClassNotFoundException {
0443: ClassFile cf = getClassFile2();
0444: AnnotationsAttribute ainfo = (AnnotationsAttribute) cf
0445: .getAttribute(AnnotationsAttribute.invisibleTag);
0446: AnnotationsAttribute ainfo2 = (AnnotationsAttribute) cf
0447: .getAttribute(AnnotationsAttribute.visibleTag);
0448: return toAnnotationType(ignoreNotFound, getClassPool(), ainfo,
0449: ainfo2);
0450: }
0451:
0452: static Object[] toAnnotationType(boolean ignoreNotFound,
0453: ClassPool cp, AnnotationsAttribute a1,
0454: AnnotationsAttribute a2) throws ClassNotFoundException {
0455: Annotation[] anno1, anno2;
0456: int size1, size2;
0457:
0458: if (a1 == null) {
0459: anno1 = null;
0460: size1 = 0;
0461: } else {
0462: anno1 = a1.getAnnotations();
0463: size1 = anno1.length;
0464: }
0465:
0466: if (a2 == null) {
0467: anno2 = null;
0468: size2 = 0;
0469: } else {
0470: anno2 = a2.getAnnotations();
0471: size2 = anno2.length;
0472: }
0473:
0474: if (!ignoreNotFound) {
0475: Object[] result = new Object[size1 + size2];
0476: for (int i = 0; i < size1; i++)
0477: result[i] = toAnnoType(anno1[i], cp);
0478:
0479: for (int j = 0; j < size2; j++)
0480: result[j + size1] = toAnnoType(anno2[j], cp);
0481:
0482: return result;
0483: } else {
0484: ArrayList annotations = new ArrayList();
0485: for (int i = 0; i < size1; i++) {
0486: try {
0487: annotations.add(toAnnoType(anno1[i], cp));
0488: } catch (ClassNotFoundException e) {
0489: }
0490: }
0491: for (int j = 0; j < size2; j++) {
0492: try {
0493: annotations.add(toAnnoType(anno2[j], cp));
0494: } catch (ClassNotFoundException e) {
0495: }
0496: }
0497:
0498: return annotations.toArray();
0499: }
0500: }
0501:
0502: static Object[][] toAnnotationType(boolean ignoreNotFound,
0503: ClassPool cp, ParameterAnnotationsAttribute a1,
0504: ParameterAnnotationsAttribute a2, MethodInfo minfo)
0505: throws ClassNotFoundException {
0506: int numParameters = 0;
0507: if (a1 != null)
0508: numParameters = a1.numParameters();
0509: else if (a2 != null)
0510: numParameters = a2.numParameters();
0511: else
0512: numParameters = Descriptor.numOfParameters(minfo
0513: .getDescriptor());
0514:
0515: Object[][] result = new Object[numParameters][];
0516: for (int i = 0; i < numParameters; i++) {
0517: Annotation[] anno1, anno2;
0518: int size1, size2;
0519:
0520: if (a1 == null) {
0521: anno1 = null;
0522: size1 = 0;
0523: } else {
0524: anno1 = a1.getAnnotations()[i];
0525: size1 = anno1.length;
0526: }
0527:
0528: if (a2 == null) {
0529: anno2 = null;
0530: size2 = 0;
0531: } else {
0532: anno2 = a2.getAnnotations()[i];
0533: size2 = anno2.length;
0534: }
0535:
0536: if (!ignoreNotFound) {
0537: result[i] = new Object[size1 + size2];
0538: for (int j = 0; j < size1; ++j)
0539: result[i][j] = toAnnoType(anno1[j], cp);
0540:
0541: for (int j = 0; j < size2; ++j)
0542: result[i][j + size1] = toAnnoType(anno2[j], cp);
0543: } else {
0544: ArrayList annotations = new ArrayList();
0545: for (int j = 0; j < size1; j++) {
0546: try {
0547: annotations.add(toAnnoType(anno1[j], cp));
0548: } catch (ClassNotFoundException e) {
0549: }
0550: }
0551: for (int j = 0; j < size2; j++) {
0552: try {
0553: annotations.add(toAnnoType(anno2[j], cp));
0554: } catch (ClassNotFoundException e) {
0555: }
0556: }
0557:
0558: result[i] = annotations.toArray();
0559: }
0560: }
0561:
0562: return result;
0563: }
0564:
0565: private static Object toAnnoType(Annotation anno, ClassPool cp)
0566: throws ClassNotFoundException {
0567: try {
0568: ClassLoader cl = cp.getClassLoader();
0569: return anno.toAnnotationType(cl, cp);
0570: } catch (ClassNotFoundException e) {
0571: ClassLoader cl2 = cp.getClass().getClassLoader();
0572: return anno.toAnnotationType(cl2, cp);
0573: }
0574: }
0575:
0576: public boolean subclassOf(CtClass super class) {
0577: if (super class == null)
0578: return false;
0579:
0580: String super Name = super class.getName();
0581: CtClass curr = this ;
0582: try {
0583: while (curr != null) {
0584: if (curr.getName().equals(super Name))
0585: return true;
0586:
0587: curr = curr.getSuperclass();
0588: }
0589: } catch (Exception ignored) {
0590: }
0591: return false;
0592: }
0593:
0594: public CtClass getSuperclass() throws NotFoundException {
0595: String super name = getClassFile2().getSuperclass();
0596: if (super name == null)
0597: return null;
0598: else
0599: return classPool.get(super name);
0600: }
0601:
0602: public void setSuperclass(CtClass clazz)
0603: throws CannotCompileException {
0604: checkModify();
0605: if (isInterface())
0606: addInterface(clazz);
0607: else
0608: getClassFile2().setSuperclass(clazz.getName());
0609: }
0610:
0611: public CtClass[] getInterfaces() throws NotFoundException {
0612: String[] ifs = getClassFile2().getInterfaces();
0613: int num = ifs.length;
0614: CtClass[] ifc = new CtClass[num];
0615: for (int i = 0; i < num; ++i)
0616: ifc[i] = classPool.get(ifs[i]);
0617:
0618: return ifc;
0619: }
0620:
0621: public void setInterfaces(CtClass[] list) {
0622: checkModify();
0623: String[] ifs;
0624: if (list == null)
0625: ifs = new String[0];
0626: else {
0627: int num = list.length;
0628: ifs = new String[num];
0629: for (int i = 0; i < num; ++i)
0630: ifs[i] = list[i].getName();
0631: }
0632:
0633: getClassFile2().setInterfaces(ifs);
0634: }
0635:
0636: public void addInterface(CtClass anInterface) {
0637: checkModify();
0638: if (anInterface != null)
0639: getClassFile2().addInterface(anInterface.getName());
0640: }
0641:
0642: public CtClass getDeclaringClass() throws NotFoundException {
0643: ClassFile cf = getClassFile2();
0644: InnerClassesAttribute ica = (InnerClassesAttribute) cf
0645: .getAttribute(InnerClassesAttribute.tag);
0646: if (ica == null)
0647: return null;
0648:
0649: String name = getName();
0650: int n = ica.tableLength();
0651: for (int i = 0; i < n; ++i)
0652: if (name.equals(ica.innerClass(i))) {
0653: String outName = ica.outerClass(i);
0654: if (outName != null)
0655: return classPool.get(outName);
0656: else {
0657: // maybe anonymous or local class.
0658: EnclosingMethodAttribute ema = (EnclosingMethodAttribute) cf
0659: .getAttribute(EnclosingMethodAttribute.tag);
0660: if (ema != null)
0661: return classPool.get(ema.className());
0662: }
0663: }
0664:
0665: return null;
0666: }
0667:
0668: public CtMethod getEnclosingMethod() throws NotFoundException {
0669: ClassFile cf = getClassFile2();
0670: EnclosingMethodAttribute ema = (EnclosingMethodAttribute) cf
0671: .getAttribute(EnclosingMethodAttribute.tag);
0672: if (ema != null) {
0673: CtClass enc = classPool.get(ema.className());
0674: return enc.getMethod(ema.methodName(), ema
0675: .methodDescriptor());
0676: }
0677:
0678: return null;
0679: }
0680:
0681: public CtClass makeNestedClass(String name, boolean isStatic) {
0682: if (!isStatic)
0683: throw new RuntimeException(
0684: "sorry, only nested static class is supported");
0685:
0686: checkModify();
0687: CtClass c = classPool.makeNestedClass(getName() + "$" + name);
0688: ClassFile cf = getClassFile2();
0689: ClassFile cf2 = c.getClassFile2();
0690: InnerClassesAttribute ica = (InnerClassesAttribute) cf
0691: .getAttribute(InnerClassesAttribute.tag);
0692: if (ica == null) {
0693: ica = new InnerClassesAttribute(cf.getConstPool());
0694: cf.addAttribute(ica);
0695: }
0696:
0697: ica.append(c.getName(), this .getName(), name, (cf2
0698: .getAccessFlags() & ~AccessFlag.SUPER)
0699: | AccessFlag.STATIC);
0700: cf2.addAttribute(ica.copy(cf2.getConstPool(), null));
0701: return c;
0702: }
0703:
0704: public CtField[] getFields() {
0705: ArrayList alist = new ArrayList();
0706: getFields(alist, this );
0707: return (CtField[]) alist.toArray(new CtField[alist.size()]);
0708: }
0709:
0710: private static void getFields(ArrayList alist, CtClass cc) {
0711: int i, num;
0712: if (cc == null)
0713: return;
0714:
0715: try {
0716: getFields(alist, cc.getSuperclass());
0717: } catch (NotFoundException e) {
0718: }
0719:
0720: try {
0721: CtClass[] ifs = cc.getInterfaces();
0722: num = ifs.length;
0723: for (i = 0; i < num; ++i)
0724: getFields(alist, ifs[i]);
0725: } catch (NotFoundException e) {
0726: }
0727:
0728: CtMember cf = ((CtClassType) cc).getFieldsCache();
0729: while (cf != null) {
0730: if (!Modifier.isPrivate(cf.getModifiers()))
0731: alist.add(cf);
0732:
0733: cf = cf.next;
0734: }
0735: }
0736:
0737: public CtField getField(String name) throws NotFoundException {
0738: CtField f = getField2(name);
0739: if (f == null)
0740: throw new NotFoundException("field: " + name + " in "
0741: + getName());
0742: else
0743: return f;
0744: }
0745:
0746: CtField getField2(String name) {
0747: CtField df = getDeclaredField2(name);
0748: if (df != null)
0749: return df;
0750:
0751: try {
0752: CtClass[] ifs = getInterfaces();
0753: int num = ifs.length;
0754: for (int i = 0; i < num; ++i) {
0755: CtField f = ifs[i].getField2(name);
0756: if (f != null)
0757: return f;
0758: }
0759:
0760: CtClass s = getSuperclass();
0761: if (s != null)
0762: return s.getField2(name);
0763: } catch (NotFoundException e) {
0764: }
0765: return null;
0766: }
0767:
0768: public CtField[] getDeclaredFields() {
0769: CtMember cf = getFieldsCache();
0770: int num = CtField.count(cf);
0771: CtField[] cfs = new CtField[num];
0772: int i = 0;
0773: while (cf != null) {
0774: cfs[i++] = (CtField) cf;
0775: cf = cf.next;
0776: }
0777:
0778: return cfs;
0779: }
0780:
0781: protected CtMember getFieldsCache() {
0782: if (fieldsCache == null) {
0783: List list = getClassFile2().getFields();
0784: int n = list.size();
0785: CtMember allFields = null;
0786: CtField tail = null;
0787: for (int i = 0; i < n; ++i) {
0788: FieldInfo finfo = (FieldInfo) list.get(i);
0789: CtField newTail = new CtField(finfo, this );
0790: allFields = CtMember.append(allFields, tail, newTail);
0791: tail = newTail;
0792: }
0793:
0794: fieldsCache = allFields;
0795: }
0796:
0797: return fieldsCache;
0798: }
0799:
0800: public CtField getDeclaredField(String name)
0801: throws NotFoundException {
0802: CtField f = getDeclaredField2(name);
0803: if (f == null)
0804: throw new NotFoundException("field: " + name + " in "
0805: + getName());
0806: else
0807: return f;
0808: }
0809:
0810: private CtField getDeclaredField2(String name) {
0811: CtMember cf = getFieldsCache();
0812: while (cf != null) {
0813: if (cf.getName().equals(name))
0814: return (CtField) cf;
0815:
0816: cf = cf.next;
0817: }
0818:
0819: return null;
0820: }
0821:
0822: public CtBehavior[] getDeclaredBehaviors() {
0823: CtMember cc = getConstructorsCache();
0824: CtMember cm = getMethodsCache();
0825: int num = CtMember.count(cm) + CtMember.count(cc);
0826: CtBehavior[] cb = new CtBehavior[num];
0827: int i = 0;
0828: while (cc != null) {
0829: cb[i++] = (CtBehavior) cc;
0830: cc = cc.next;
0831: }
0832:
0833: while (cm != null) {
0834: cb[i++] = (CtBehavior) cm;
0835: cm = cm.next;
0836: }
0837:
0838: return cb;
0839: }
0840:
0841: public CtConstructor[] getConstructors() {
0842: CtConstructor[] cons = getDeclaredConstructors();
0843: if (cons.length == 0)
0844: return cons;
0845:
0846: int n = 0;
0847: int i = cons.length;
0848: while (--i >= 0)
0849: if (!Modifier.isPrivate(cons[i].getModifiers()))
0850: ++n;
0851:
0852: CtConstructor[] result = new CtConstructor[n];
0853: n = 0;
0854: i = cons.length;
0855: while (--i >= 0) {
0856: CtConstructor c = cons[i];
0857: if (!Modifier.isPrivate(c.getModifiers()))
0858: result[n++] = c;
0859: }
0860:
0861: return result;
0862: }
0863:
0864: public CtConstructor getConstructor(String desc)
0865: throws NotFoundException {
0866: CtConstructor cc = (CtConstructor) getConstructorsCache();
0867: while (cc != null) {
0868: if (cc.getMethodInfo2().getDescriptor().equals(desc))
0869: return cc;
0870:
0871: cc = (CtConstructor) cc.next;
0872: }
0873:
0874: return super .getConstructor(desc);
0875: }
0876:
0877: public CtConstructor[] getDeclaredConstructors() {
0878: CtMember cc = getConstructorsCache();
0879: int num = CtMember.count(cc);
0880: CtConstructor[] ccs = new CtConstructor[num];
0881: int i = 0;
0882: while (cc != null) {
0883: ccs[i++] = (CtConstructor) cc;
0884: cc = cc.next;
0885: }
0886:
0887: return ccs;
0888: }
0889:
0890: protected CtMember getConstructorsCache() {
0891: if (constructorsCache == null) {
0892: List list = getClassFile2().getMethods();
0893: int n = list.size();
0894: CtMember allConstructors = null;
0895: CtConstructor tail = null;
0896: for (int i = 0; i < n; ++i) {
0897: MethodInfo minfo = (MethodInfo) list.get(i);
0898: if (minfo.isConstructor()) {
0899: CtConstructor newTail = new CtConstructor(minfo,
0900: this );
0901: allConstructors = CtMember.append(allConstructors,
0902: tail, newTail);
0903: tail = newTail;
0904: }
0905: }
0906:
0907: constructorsCache = allConstructors;
0908: }
0909:
0910: return constructorsCache;
0911: }
0912:
0913: public CtConstructor getClassInitializer() {
0914: if (classInitializerCache == null) {
0915: MethodInfo minfo = getClassFile2().getStaticInitializer();
0916: if (minfo != null)
0917: classInitializerCache = new CtConstructor(minfo, this );
0918: }
0919:
0920: return classInitializerCache;
0921: }
0922:
0923: public CtMethod[] getMethods() {
0924: HashMap h = new HashMap();
0925: getMethods0(h, this );
0926: return (CtMethod[]) h.values().toArray(new CtMethod[h.size()]);
0927: }
0928:
0929: private static void getMethods0(HashMap h, CtClass cc) {
0930: try {
0931: CtClass[] ifs = cc.getInterfaces();
0932: int size = ifs.length;
0933: for (int i = 0; i < size; ++i)
0934: getMethods0(h, ifs[i]);
0935: } catch (NotFoundException e) {
0936: }
0937:
0938: try {
0939: CtClass s = cc.getSuperclass();
0940: if (s != null)
0941: getMethods0(h, s);
0942: } catch (NotFoundException e) {
0943: }
0944:
0945: if (cc instanceof CtClassType) {
0946: CtMember cm = ((CtClassType) cc).getMethodsCache();
0947: while (cm != null) {
0948: if (!Modifier.isPrivate(cm.getModifiers()))
0949: h.put(((CtMethod) cm).getStringRep(), cm);
0950:
0951: cm = cm.next;
0952: }
0953: }
0954: }
0955:
0956: public CtMethod getMethod(String name, String desc)
0957: throws NotFoundException {
0958: CtMethod m = getMethod0(this , name, desc);
0959: if (m != null)
0960: return m;
0961: else
0962: throw new NotFoundException(name + "(..) is not found in "
0963: + getName());
0964: }
0965:
0966: private static CtMethod getMethod0(CtClass cc, String name,
0967: String desc) {
0968: if (cc instanceof CtClassType) {
0969: CtMethod cm = (CtMethod) ((CtClassType) cc)
0970: .getMethodsCache();
0971: while (cm != null) {
0972: if (cm.getName().equals(name)
0973: && cm.getMethodInfo2().getDescriptor().equals(
0974: desc))
0975: return cm;
0976:
0977: cm = (CtMethod) cm.next;
0978: }
0979: }
0980:
0981: try {
0982: CtClass s = cc.getSuperclass();
0983: if (s != null) {
0984: CtMethod m = getMethod0(s, name, desc);
0985: if (m != null)
0986: return m;
0987: }
0988: } catch (NotFoundException e) {
0989: }
0990:
0991: try {
0992: CtClass[] ifs = cc.getInterfaces();
0993: int size = ifs.length;
0994: for (int i = 0; i < size; ++i) {
0995: CtMethod m = getMethod0(ifs[i], name, desc);
0996: if (m != null)
0997: return m;
0998: }
0999: } catch (NotFoundException e) {
1000: }
1001: return null;
1002: }
1003:
1004: public CtMethod[] getDeclaredMethods() {
1005: CtMember cm = getMethodsCache();
1006: int num = CtMember.count(cm);
1007: CtMethod[] cms = new CtMethod[num];
1008: int i = 0;
1009: while (cm != null) {
1010: cms[i++] = (CtMethod) cm;
1011: cm = cm.next;
1012: }
1013:
1014: return cms;
1015: }
1016:
1017: public CtMethod getDeclaredMethod(String name)
1018: throws NotFoundException {
1019: CtMember m = getMethodsCache();
1020: while (m != null) {
1021: if (m.getName().equals(name))
1022: return (CtMethod) m;
1023:
1024: m = m.next;
1025: }
1026:
1027: throw new NotFoundException(name + "(..) is not found in "
1028: + getName());
1029: }
1030:
1031: public CtMethod getDeclaredMethod(String name, CtClass[] params)
1032: throws NotFoundException {
1033: String desc = Descriptor.ofParameters(params);
1034: CtMethod m = (CtMethod) getMethodsCache();
1035: while (m != null) {
1036: if (m.getName().equals(name)
1037: && m.getMethodInfo2().getDescriptor().startsWith(
1038: desc))
1039: return m;
1040:
1041: m = (CtMethod) m.next;
1042: }
1043:
1044: throw new NotFoundException(name + "(..) is not found in "
1045: + getName());
1046: }
1047:
1048: protected CtMember getMethodsCache() {
1049: if (methodsCache == null) {
1050: List list = getClassFile2().getMethods();
1051: int n = list.size();
1052: CtMember allMethods = null;
1053: CtMethod tail = null;
1054: for (int i = 0; i < n; ++i) {
1055: MethodInfo minfo = (MethodInfo) list.get(i);
1056: if (minfo.isMethod()) {
1057: CtMethod newTail = new CtMethod(minfo, this );
1058: allMethods = CtMember.append(allMethods, tail,
1059: newTail);
1060: tail = newTail;
1061: }
1062: }
1063:
1064: methodsCache = allMethods;
1065: }
1066:
1067: return methodsCache;
1068: }
1069:
1070: public void addField(CtField f, String init)
1071: throws CannotCompileException {
1072: addField(f, CtField.Initializer.byExpr(init));
1073: }
1074:
1075: public void addField(CtField f, CtField.Initializer init)
1076: throws CannotCompileException {
1077: checkModify();
1078: if (f.getDeclaringClass() != this )
1079: throw new CannotCompileException("cannot add");
1080:
1081: if (init == null)
1082: init = f.getInit();
1083:
1084: if (init != null) {
1085: int mod = f.getModifiers();
1086: if (Modifier.isStatic(mod) && Modifier.isFinal(mod))
1087: try {
1088: ConstPool cp = getClassFile2().getConstPool();
1089: int index = init.getConstantValue(cp, f.getType());
1090: if (index != 0) {
1091: f.getFieldInfo2().addAttribute(
1092: new ConstantAttribute(cp, index));
1093: init = null;
1094: }
1095: } catch (NotFoundException e) {
1096: }
1097: }
1098:
1099: getFieldsCache();
1100: fieldsCache = CtField.append(fieldsCache, f);
1101: getClassFile2().addField(f.getFieldInfo2());
1102:
1103: if (init != null) {
1104: FieldInitLink fil = new FieldInitLink(f, init);
1105: FieldInitLink link = fieldInitializers;
1106: if (link == null)
1107: fieldInitializers = fil;
1108: else {
1109: while (link.next != null)
1110: link = link.next;
1111:
1112: link.next = fil;
1113: }
1114: }
1115: }
1116:
1117: public void removeField(CtField f) throws NotFoundException {
1118: checkModify();
1119: FieldInfo fi = f.getFieldInfo2();
1120: ClassFile cf = getClassFile2();
1121: if (cf.getFields().remove(fi)) {
1122: fieldsCache = CtMember.remove(fieldsCache, f);
1123: memberRemoved = true;
1124: } else
1125: throw new NotFoundException(f.toString());
1126: }
1127:
1128: public CtConstructor makeClassInitializer()
1129: throws CannotCompileException {
1130: CtConstructor clinit = getClassInitializer();
1131: if (clinit != null)
1132: return clinit;
1133:
1134: checkModify();
1135: ClassFile cf = getClassFile2();
1136: Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
1137: modifyClassConstructor(cf, code, 0, 0);
1138: return getClassInitializer();
1139: }
1140:
1141: public void addConstructor(CtConstructor c)
1142: throws CannotCompileException {
1143: checkModify();
1144: if (c.getDeclaringClass() != this )
1145: throw new CannotCompileException("cannot add");
1146:
1147: getConstructorsCache();
1148: constructorsCache = (CtConstructor) CtMember.append(
1149: constructorsCache, c);
1150: getClassFile2().addMethod(c.getMethodInfo2());
1151: }
1152:
1153: public void removeConstructor(CtConstructor m)
1154: throws NotFoundException {
1155: checkModify();
1156: MethodInfo mi = m.getMethodInfo2();
1157: ClassFile cf = getClassFile2();
1158: if (cf.getMethods().remove(mi)) {
1159: constructorsCache = CtMember.remove(constructorsCache, m);
1160: memberRemoved = true;
1161: } else
1162: throw new NotFoundException(m.toString());
1163: }
1164:
1165: public void addMethod(CtMethod m) throws CannotCompileException {
1166: checkModify();
1167: if (m.getDeclaringClass() != this )
1168: throw new CannotCompileException("cannot add");
1169:
1170: getMethodsCache();
1171: methodsCache = CtMember.append(methodsCache, m);
1172: getClassFile2().addMethod(m.getMethodInfo2());
1173: if ((m.getModifiers() & Modifier.ABSTRACT) != 0)
1174: setModifiers(getModifiers() | Modifier.ABSTRACT);
1175: }
1176:
1177: public void removeMethod(CtMethod m) throws NotFoundException {
1178: checkModify();
1179: MethodInfo mi = m.getMethodInfo2();
1180: ClassFile cf = getClassFile2();
1181: if (cf.getMethods().remove(mi)) {
1182: methodsCache = CtMember.remove(methodsCache, m);
1183: memberRemoved = true;
1184: } else
1185: throw new NotFoundException(m.toString());
1186: }
1187:
1188: public byte[] getAttribute(String name) {
1189: AttributeInfo ai = getClassFile2().getAttribute(name);
1190: if (ai == null)
1191: return null;
1192: else
1193: return ai.get();
1194: }
1195:
1196: public void setAttribute(String name, byte[] data) {
1197: checkModify();
1198: ClassFile cf = getClassFile2();
1199: cf
1200: .addAttribute(new AttributeInfo(cf.getConstPool(),
1201: name, data));
1202: }
1203:
1204: public void instrument(CodeConverter converter)
1205: throws CannotCompileException {
1206: checkModify();
1207: ClassFile cf = getClassFile2();
1208: ConstPool cp = cf.getConstPool();
1209: List list = cf.getMethods();
1210: int n = list.size();
1211: for (int i = 0; i < n; ++i) {
1212: MethodInfo minfo = (MethodInfo) list.get(i);
1213: converter.doit(this , minfo, cp);
1214: }
1215: }
1216:
1217: public void instrument(ExprEditor editor)
1218: throws CannotCompileException {
1219: checkModify();
1220: ClassFile cf = getClassFile2();
1221: List list = cf.getMethods();
1222: int n = list.size();
1223: for (int i = 0; i < n; ++i) {
1224: MethodInfo minfo = (MethodInfo) list.get(i);
1225: editor.doit(this , minfo);
1226: }
1227: }
1228:
1229: /**
1230: * @see javassist.CtClass#prune()
1231: * @see javassist.CtClass#stopPruning(boolean)
1232: */
1233: public void prune() {
1234: if (wasPruned)
1235: return;
1236:
1237: wasPruned = wasFrozen = true;
1238: getClassFile2().prune();
1239: }
1240:
1241: public void toBytecode(DataOutputStream out)
1242: throws CannotCompileException, IOException {
1243: try {
1244: if (isModified()) {
1245: checkPruned("toBytecode");
1246: ClassFile cf = getClassFile2();
1247: if (memberRemoved) {
1248: cf.compact();
1249: memberRemoved = false;
1250: }
1251:
1252: modifyClassConstructor(cf);
1253: modifyConstructors(cf);
1254: cf.write(out);
1255: out.flush();
1256: fieldInitializers = null;
1257: if (doPruning) {
1258: // to save memory
1259: cf.prune();
1260: wasPruned = true;
1261: }
1262: } else {
1263: classPool.writeClassfile(getName(), out);
1264: // to save memory
1265: eraseCache();
1266: classfile = null;
1267: }
1268:
1269: wasFrozen = true;
1270: } catch (NotFoundException e) {
1271: throw new CannotCompileException(e);
1272: } catch (IOException e) {
1273: throw new CannotCompileException(e);
1274: }
1275: }
1276:
1277: /* See also checkModified()
1278: */
1279: private void checkPruned(String method) {
1280: if (wasPruned)
1281: throw new RuntimeException(method + "(): " + getName()
1282: + " was pruned.");
1283: }
1284:
1285: public boolean stopPruning(boolean stop) {
1286: boolean prev = !doPruning;
1287: doPruning = !stop;
1288: return prev;
1289: }
1290:
1291: private void modifyClassConstructor(ClassFile cf)
1292: throws CannotCompileException, NotFoundException {
1293: if (fieldInitializers == null)
1294: return;
1295:
1296: Bytecode code = new Bytecode(cf.getConstPool(), 0, 0);
1297: Javac jv = new Javac(code, this );
1298: int stacksize = 0;
1299: boolean doInit = false;
1300: for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
1301: CtField f = fi.field;
1302: if (Modifier.isStatic(f.getModifiers())) {
1303: doInit = true;
1304: int s = fi.init.compileIfStatic(f.getType(), f
1305: .getName(), code, jv);
1306: if (stacksize < s)
1307: stacksize = s;
1308: }
1309: }
1310:
1311: if (doInit) // need an initializer for static fileds.
1312: modifyClassConstructor(cf, code, stacksize, 0);
1313: }
1314:
1315: private void modifyClassConstructor(ClassFile cf, Bytecode code,
1316: int stacksize, int localsize) throws CannotCompileException {
1317: MethodInfo m = cf.getStaticInitializer();
1318: if (m == null) {
1319: code.add(Bytecode.RETURN);
1320: code.setMaxStack(stacksize);
1321: code.setMaxLocals(localsize);
1322: m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V");
1323: m.setAccessFlags(AccessFlag.STATIC);
1324: m.setCodeAttribute(code.toCodeAttribute());
1325: cf.addMethod(m);
1326: } else {
1327: CodeAttribute codeAttr = m.getCodeAttribute();
1328: if (codeAttr == null)
1329: throw new CannotCompileException("empty <clinit>");
1330:
1331: try {
1332: CodeIterator it = codeAttr.iterator();
1333: int pos = it.insertEx(code.get());
1334: it.insert(code.getExceptionTable(), pos);
1335: int maxstack = codeAttr.getMaxStack();
1336: if (maxstack < stacksize)
1337: codeAttr.setMaxStack(stacksize);
1338:
1339: int maxlocals = codeAttr.getMaxLocals();
1340: if (maxlocals < localsize)
1341: codeAttr.setMaxLocals(localsize);
1342: } catch (BadBytecode e) {
1343: throw new CannotCompileException(e);
1344: }
1345: }
1346: }
1347:
1348: private void modifyConstructors(ClassFile cf)
1349: throws CannotCompileException, NotFoundException {
1350: if (fieldInitializers == null)
1351: return;
1352:
1353: ConstPool cp = cf.getConstPool();
1354: List list = cf.getMethods();
1355: int n = list.size();
1356: for (int i = 0; i < n; ++i) {
1357: MethodInfo minfo = (MethodInfo) list.get(i);
1358: if (minfo.isConstructor()) {
1359: CodeAttribute codeAttr = minfo.getCodeAttribute();
1360: if (codeAttr != null)
1361: try {
1362: Bytecode init = new Bytecode(cp, 0, codeAttr
1363: .getMaxLocals());
1364: CtClass[] params = Descriptor
1365: .getParameterTypes(minfo
1366: .getDescriptor(), classPool);
1367: int stacksize = makeFieldInitializer(init,
1368: params);
1369: insertAuxInitializer(codeAttr, init, stacksize);
1370: } catch (BadBytecode e) {
1371: throw new CannotCompileException(e);
1372: }
1373: }
1374: }
1375: }
1376:
1377: private static void insertAuxInitializer(CodeAttribute codeAttr,
1378: Bytecode initializer, int stacksize) throws BadBytecode {
1379: CodeIterator it = codeAttr.iterator();
1380: int index = it.skipSuperConstructor();
1381: if (index < 0) {
1382: index = it.skipThisConstructor();
1383: if (index >= 0)
1384: return; // this() is called.
1385:
1386: // Neither this() or super() is called.
1387: }
1388:
1389: int pos = it.insertEx(initializer.get());
1390: it.insert(initializer.getExceptionTable(), pos);
1391: int maxstack = codeAttr.getMaxStack();
1392: if (maxstack < stacksize)
1393: codeAttr.setMaxStack(stacksize);
1394: }
1395:
1396: private int makeFieldInitializer(Bytecode code, CtClass[] parameters)
1397: throws CannotCompileException, NotFoundException {
1398: int stacksize = 0;
1399: Javac jv = new Javac(code, this );
1400: try {
1401: jv.recordParams(parameters, false);
1402: } catch (CompileError e) {
1403: throw new CannotCompileException(e);
1404: }
1405:
1406: for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) {
1407: CtField f = fi.field;
1408: if (!Modifier.isStatic(f.getModifiers())) {
1409: int s = fi.init.compile(f.getType(), f.getName(), code,
1410: parameters, jv);
1411: if (stacksize < s)
1412: stacksize = s;
1413: }
1414: }
1415:
1416: return stacksize;
1417: }
1418:
1419: // Methods used by CtNewWrappedMethod
1420:
1421: Hashtable getHiddenMethods() {
1422: if (hiddenMethods == null)
1423: hiddenMethods = new Hashtable();
1424:
1425: return hiddenMethods;
1426: }
1427:
1428: int getUniqueNumber() {
1429: return uniqueNumberSeed++;
1430: }
1431:
1432: public String makeUniqueName(String prefix) {
1433: HashMap table = new HashMap();
1434: makeMemberList(table);
1435: Set keys = table.keySet();
1436: String[] methods = new String[keys.size()];
1437: keys.toArray(methods);
1438:
1439: if (notFindInArray(prefix, methods))
1440: return prefix;
1441:
1442: int i = 100;
1443: String name;
1444: do {
1445: if (i > 999)
1446: throw new RuntimeException("too many unique name");
1447:
1448: name = prefix + i++;
1449: } while (!notFindInArray(name, methods));
1450: return name;
1451: }
1452:
1453: private static boolean notFindInArray(String prefix, String[] values) {
1454: int len = values.length;
1455: for (int i = 0; i < len; i++)
1456: if (values[i].startsWith(prefix))
1457: return false;
1458:
1459: return true;
1460: }
1461:
1462: private void makeMemberList(HashMap table) {
1463: int mod = getModifiers();
1464: if (Modifier.isAbstract(mod) || Modifier.isInterface(mod))
1465: try {
1466: CtClass[] ifs = getInterfaces();
1467: int size = ifs.length;
1468: for (int i = 0; i < size; i++) {
1469: CtClass ic = ifs[i];
1470: if (ic != null && ic instanceof CtClassType)
1471: ((CtClassType) ic).makeMemberList(table);
1472: }
1473: } catch (NotFoundException e) {
1474: }
1475:
1476: try {
1477: CtClass s = getSuperclass();
1478: if (s != null && s instanceof CtClassType)
1479: ((CtClassType) s).makeMemberList(table);
1480: } catch (NotFoundException e) {
1481: }
1482:
1483: List list = getClassFile2().getMethods();
1484: int n = list.size();
1485: for (int i = 0; i < n; i++) {
1486: MethodInfo minfo = (MethodInfo) list.get(i);
1487: table.put(minfo.getName(), this );
1488: }
1489:
1490: list = getClassFile2().getFields();
1491: n = list.size();
1492: for (int i = 0; i < n; i++) {
1493: FieldInfo finfo = (FieldInfo) list.get(i);
1494: table.put(finfo.getName(), this );
1495: }
1496: }
1497: }
1498:
1499: class FieldInitLink {
1500: FieldInitLink next;
1501: CtField field;
1502: CtField.Initializer init;
1503:
1504: FieldInitLink(CtField f, CtField.Initializer i) {
1505: next = null;
1506: field = f;
1507: init = i;
1508: }
1509: }
|