0001: //
0002: // This file is part of the prose package.
0003: //
0004: // The contents of this file are subject to the Mozilla Public License
0005: // Version 1.1 (the "License"); you may not use this file except in
0006: // compliance with the License. You may obtain a copy of the License at
0007: // http://www.mozilla.org/MPL/
0008: //
0009: // Software distributed under the License is distributed on an "AS IS" basis,
0010: // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0011: // for the specific language governing rights and limitations under the
0012: // License.
0013: //
0014: // Contributor(s):
0015: // $Id$
0016: // =====================================================================
0017: //
0018: // (history at end)
0019: //
0020:
0021: package ch.ethz.inf.iks.jvmai.jvmdi;
0022:
0023: import java.lang.reflect.Method;
0024: import java.lang.reflect.Constructor;
0025: import java.lang.reflect.Member;
0026: import java.lang.reflect.Modifier;
0027:
0028: import java.util.Collections;
0029: import java.util.Collection;
0030: import java.util.Map;
0031: import java.util.HashMap;
0032: import java.util.LinkedHashMap;
0033: import java.util.ArrayList;
0034: import java.util.Iterator;
0035: import java.util.LinkedList;
0036:
0037: import java.io.ByteArrayInputStream;
0038: import java.io.IOException;
0039:
0040: import org.apache.bcel.Constants;
0041: import org.apache.bcel.Repository;
0042: import org.apache.bcel.classfile.Attribute;
0043: import org.apache.bcel.classfile.ClassParser;
0044: import org.apache.bcel.classfile.ConstantValue;
0045: import org.apache.bcel.classfile.JavaClass;
0046: import org.apache.bcel.generic.*;
0047: import org.apache.bcel.verifier.Verifier;
0048: import org.apache.bcel.verifier.VerifierFactory;
0049: import org.apache.bcel.verifier.VerificationResult;
0050:
0051: import ch.ethz.jvmai.*;
0052:
0053: /*
0054: * Modifies a given class to support advice execution.
0055: * Container for MethodWeavers, which does the code
0056: * manipulations. Use the static method {@link
0057: * #getWeaver(Method)} to get an instance of an
0058: * {@link ch.ethz.jvmai.MethodWeaver}.
0059: * <P>
0060: * Optimized class weaver implementation. Holds a list with all
0061: * ClassWeavers that contains changes which are not yet commited.
0062: * On commit it iterates only over the ClassWeavers in this list.
0063: *
0064: * The SimpleClassWeaver iterates over classweaver list.
0065: * The AdvancedClassWeaver iterates only over the last modified classweavers.
0066: * The advantage is that doesn't have to check for each class weaver if has been modified.
0067: *
0068: * @author Angela Nicoara
0069: * @author Gerald Linhofer
0070: * @version $Revision: 1.1.2.2 $
0071: */
0072: public class HotSwapAdvancedClassWeaver {
0073:
0074: /**
0075: * String representation of the JVMAI class.
0076: */
0077: public static final String JVMAI_CLASS = "ch.ethz.inf.iks.jvmai.jvmdi.HotSwapAspectInterfaceImpl";
0078:
0079: /**
0080: * Set holding all class weavers, which hold uncommited modifications.
0081: */
0082: private static Collection modifiedClassWeavers = new LinkedList();
0083: // private static Collection<HotSwapClassWeaver> modifiedClassWeavers = new LinkedList<HotSwapClassWeaver>(); // only JDK 1.5 or above
0084:
0085: /**
0086: * Map with all class weaver. For each Class there
0087: * exists exactly one class weaver in the system.
0088: */
0089: private static Map classWeavers = new LinkedHashMap(1024);
0090: // private static Map<Class,HotSwapClassWeaver> classWeavers = new LinkedHashMap<Class,HotSwapClassWeaver>(1024); // only JDK 1.5 or above
0091:
0092: /**
0093: * Map with all method weavers. For each Method there
0094: * exists exactly one method weaver in the system.
0095: */
0096: private static Map methodWeavers = new LinkedHashMap(1024);
0097: // private static Map<Member,MethodWeaver> methodWeavers = new LinkedHashMap<Member,MethodWeaver>(1024); // only JDK 1.5 or above
0098:
0099: /**
0100: * Maps unique method ids to the method objects.
0101: */
0102: private static Member methodMap[] = new Member[1024];
0103:
0104: /**
0105: * Manages unique method ids.
0106: */
0107: private static UniqueIdArrayManager idMgr = new UniqueIdArrayManager();
0108:
0109: /**
0110: * The JVMAspectInterface.
0111: */
0112: private static HotSwapAspectInterfaceImpl aspectInterface = null;
0113:
0114: /**
0115: * Is there anything to do for {@link #resetAll}
0116: */
0117: private static boolean anyWoven = false;
0118:
0119: //----------------------------------------------------------------------
0120:
0121: /**
0122: * Get a unique method weaver for 'target'.
0123: *
0124: * @param target method for which a weaver will be returned.
0125: * @return MethodWeaver associated to <CODE>target</CODE>.
0126: */
0127: static public MethodWeaver getWeaver(Member target) /*throws ClassNotFoundException*/{
0128: if (null == target)
0129: throw new NullPointerException(
0130: "Parameter 'target' must not be null");
0131:
0132: MethodWeaver result;
0133: synchronized (methodWeavers) {
0134: result = (MethodWeaver) methodWeavers.get(target);
0135: if (null == result) {
0136: try {
0137: // get or create the ClassWeaver
0138: HotSwapAdvancedClassWeaver cw = getClassWeaver(target
0139: .getDeclaringClass());
0140: // create a new MethodWeaver
0141: result = cw.getNewMethodWeaver(target);
0142: } catch (ClassNotFoundException e) {
0143: throw new JVMAIRuntimeException(
0144: "Class weaver could not get the definition for "
0145: + target.getDeclaringClass());
0146: }
0147: }
0148: }
0149: return result;
0150: }
0151:
0152: /**
0153: * Get a unique class weaver for 'target'.
0154: */
0155: static protected HotSwapAdvancedClassWeaver getClassWeaver(
0156: Class target) throws ClassNotFoundException {
0157: if (null == target)
0158: throw new NullPointerException(
0159: "Parameter 'target' must not be null");
0160:
0161: HotSwapAdvancedClassWeaver result;
0162: synchronized (classWeavers) {
0163: result = (HotSwapAdvancedClassWeaver) classWeavers
0164: .get(target);
0165: if (null == result) {
0166: // create a new ClassWeaver
0167: result = new HotSwapAdvancedClassWeaver(target);
0168: classWeavers.put(target, result);
0169: }
0170: }
0171: return result;
0172: }
0173:
0174: /**
0175: * Re-Weaves all modified classes and activates them.
0176: */
0177: static public void commit() {
0178: if (modifiedClassWeavers.isEmpty())
0179: return;
0180:
0181: // synchronize commit and reset
0182: synchronized (classWeavers) {
0183: Collection clazzes = new ArrayList();
0184: Collection definitions = new ArrayList();
0185:
0186: Iterator iter = modifiedClassWeavers.iterator();
0187: while (iter.hasNext()) {
0188: HotSwapAdvancedClassWeaver weaver = (HotSwapAdvancedClassWeaver) iter
0189: .next();
0190: try {
0191: weaver.prepareClassDefinition();
0192: definitions.add(weaver.getClassDefinition());
0193: clazzes.add(weaver.targetClass);
0194: // remove weaver if its not associated with an modified method.
0195: if (weaver.hasNoModifiedMethods())
0196: classWeavers.remove(weaver.targetClass);
0197: else {
0198: weaver.setWoven();
0199: }
0200: } catch (ClassNotFoundException e) {
0201: System.err
0202: .println("ERROR HotSwapClassWeaver: Class redefinition failed: "
0203: + e);
0204: } catch (IOException e) {
0205: System.err
0206: .println("ERROR HotSwapClassWeaver: Class redefinition failed: "
0207: + e);
0208: }
0209: }
0210: modifiedClassWeavers.clear();
0211:
0212: if (!clazzes.isEmpty()) {
0213: anyWoven = true;
0214: // redefine classes
0215: Class cls[] = new Class[clazzes.size()];
0216: clazzes.toArray(cls);
0217: byte defs[][] = new byte[definitions.size()][];
0218: definitions.toArray(defs);
0219: redefineClasses(cls, defs);
0220: }
0221: }
0222: }
0223:
0224: /**
0225: * Resets all woven classes.
0226: */
0227: static public void resetAll() {
0228: // synchronize commit() and resetAll()
0229: synchronized (classWeavers) {
0230: if (anyWoven) {
0231: Collection clazzes = new ArrayList();
0232: Collection definitions = new ArrayList();
0233: Iterator it = classWeavers.values().iterator();
0234:
0235: // get classes and old class definitions
0236: while (it.hasNext()) {
0237: HotSwapAdvancedClassWeaver cw = (HotSwapAdvancedClassWeaver) it
0238: .next();
0239: if (cw.isWoven() && null != cw.originalCode) {
0240: clazzes.add(cw.targetClass);
0241: definitions.add(cw.originalCode);
0242: }
0243: }
0244:
0245: if (!clazzes.isEmpty()) {
0246: // redefine classes
0247: Class cls[] = new Class[clazzes.size()];
0248: clazzes.toArray(cls);
0249: byte defs[][] = new byte[definitions.size()][];
0250: definitions.toArray(defs);
0251: redefineClasses(cls, defs);
0252: }
0253: }
0254:
0255: classWeavers.clear(); // = new LinkedHashMap();
0256: methodWeavers.clear(); // = new LinkedHashMap();
0257: modifiedClassWeavers.clear(); // = new LinkedList();
0258: methodMap = new Member[1024];
0259: idMgr.reset();
0260: anyWoven = false;
0261: }
0262: }
0263:
0264: /**
0265: * Returns an identifier for <CODE>method</CODE> and
0266: * stores the association, so that {@link #idToMethod(int) idToMethod}
0267: * may be used to get the <CODE>method</CODE> using the id.
0268: *
0269: * Note: only the id is unique, if <CODE>createNewMethodId(Method)
0270: * </CODE> is called more than one time with the same argument,
0271: * each call will create a new id and a new map entry.
0272: *
0273: * @param method that will be associated with a new identifier.
0274: * @return new identifier for <CODE>method</CODE>.
0275: */
0276: private static int createNewMethodId(Member method) {
0277: if (null == method)
0278: throw new NullPointerException();
0279:
0280: int result;
0281:
0282: synchronized (methodMap) {
0283: result = idMgr.newId();
0284: // check if map must be expanded
0285: int mapLength = methodMap.length;
0286: if (mapLength <= result) {
0287: Member newMap[] = new Member[2 * mapLength];
0288: System.arraycopy(methodMap, 0, newMap, 0, mapLength);
0289: methodMap = newMap;
0290: }
0291: // add method to the map
0292: methodMap[result] = method;
0293: }
0294: return result;
0295: }
0296:
0297: /**
0298: * Returns the Member object associated to <CODE>methodId</CODE>
0299: * or <CODE>null</CODE>, if <CODE>methodID</CODE> is not a valid
0300: * id.
0301: *
0302: * @param methodId id for the Method that will be returned.
0303: * @return Method associated to <CODE>methodID<CODE> or <CODE>null</CODE>.
0304: * @throws ArrayOutOfBoundException
0305: */
0306: public static Member idToMethod(int methodId) {
0307: return methodMap[methodId];
0308: }
0309:
0310: /**
0311: * Sets the aspect interface, must be called before any call to
0312: * {@link #commit commit}.
0313: *
0314: * @param ai
0315: */
0316: public synchronized static void setAspectInterface(
0317: HotSwapAspectInterfaceImpl ai) {
0318: aspectInterface = ai;
0319: }
0320:
0321: //---------------------------------------------------------------------
0322:
0323: private boolean initialized;
0324: private boolean modified;
0325: private boolean woven;
0326:
0327: /**
0328: * Class bound to this weaver
0329: */
0330: public final Class targetClass;
0331:
0332: /**
0333: * Cached original (/unmodified) class file of {@link #targetClass}.
0334: */
0335: public final byte[] originalCode;
0336:
0337: /**
0338: * BCEL class generator used during weaving processs.
0339: * holds the actual class definition during weaving process.
0340: */
0341: private ClassGen clGen;
0342:
0343: /**
0344: * BCEL constant pool generator used during weaving process.
0345: */
0346: private ConstantPoolGen cpGen;
0347:
0348: /**
0349: * BCEL instruction factory used during the weaving process.
0350: */
0351: private InstructionFactory instructionFactory;
0352:
0353: /**
0354: * Collection of member MethodWeavers (of {@link #targetClass})
0355: * that will be redefined.
0356: */
0357: private Map methods;
0358:
0359: // Used during weaving (introduced for verfyClassSchema)
0360: private JavaClass bcelClass;
0361:
0362: //-----------------------------------------------------------------------
0363:
0364: /**
0365: * Creates a new class weaver. Use the static method
0366: * {@link #getClassWeaver(java.lang.Class) getClassWeaver} to obtain a weaver.
0367: */
0368: protected HotSwapAdvancedClassWeaver(Class target)
0369: throws ClassNotFoundException {
0370: targetClass = target;
0371: methods = new HashMap();
0372:
0373: bcelClass = HotSwapAspectInterfaceImpl
0374: .getBCELClassDefinition(targetClass);
0375: originalCode = bcelClass.getBytes();
0376:
0377: initialized = false;
0378: woven = false;
0379: modified = false;
0380:
0381: clGen = null;
0382: cpGen = null;
0383: }
0384:
0385: /**
0386: * Indicates if this class weaver holds any
0387: * method weavers.
0388: *
0389: * @return <CODE>true</CODE> if no method weavers are associated.
0390: */
0391: public boolean hasNoModifiedMethods() {
0392: return methods.isEmpty();
0393: }
0394:
0395: /**
0396: * Returns the class bound to this weaver.
0397: * @return Class that is bound to this weaver
0398: */
0399: public Class getTargetClass() {
0400: return targetClass;
0401: }
0402:
0403: public Collection getMethods() {
0404: return methods.keySet();
0405: }
0406:
0407: /**
0408: * Returns a collection of all method weavers for
0409: * this class weaver.
0410: *
0411: * @return Collection of <CODE>MethodWeaver</CODE>s associated to this
0412: * <CODE>ClassWeaver</CODE>
0413: */
0414: public Collection getMethodWeavers() {
0415: return methods.values();
0416: }
0417:
0418: /**
0419: * Sets some internal variables to indicate
0420: * that the target class was woven.
0421: */
0422: protected void setWoven() {
0423: modified = false;
0424: woven = true;
0425: clGen = null;
0426: }
0427:
0428: /**
0429: * Indicates if not yet woven modifications are registered
0430: * for the target class.
0431: *
0432: * @return <CODE>true</CODE> if weaver holds unwoven
0433: * modifications for its target class.
0434: */
0435: public boolean isModified() {
0436: return modified;
0437: }
0438:
0439: /**
0440: * Indicates if the target class was woven (redefined).
0441: *
0442: * @return <CODE>true</CODE> if the target class is redefined.
0443: */
0444: public boolean isWoven() {
0445: return woven;
0446: }
0447:
0448: /**
0449: * Creates a new <CODE>MethodWeaver</CODE> and adds it to
0450: * this weaver.
0451: * This adds the new weaver also to the global methodWeavers
0452: * table.
0453: * Use {@link HotSwapAdvancedClassWeaver#getWeaver(java.lang.reflect.Member) getWeaver} to create a new MethodWeaver.
0454: */
0455: protected MethodWeaver getNewMethodWeaver(Member target) {
0456: MethodWeaver result = new MethodWeaver(target);
0457: methodWeavers.put(target, result);
0458: methods.put(target, result);
0459:
0460: return result;
0461: }
0462:
0463: /**
0464: * Prepares a class for weaving. This mean mainly
0465: * instrumenting the class file representation,
0466: * so that the new definition may be fetched with
0467: * {@link #getClassDefinition()}.
0468: */
0469: protected void prepareClassDefinition() throws java.io.IOException,
0470: ClassNotFoundException {
0471: initPreparation();
0472: weaveMethods();
0473: // verify();
0474: // verifyClassSchema();
0475: finishWeaving();
0476: }
0477:
0478: /**
0479: * Prepare for weaving. Initializes some member field.
0480: * This method must only be called once (for each instance
0481: * of this class).
0482: * called by {@link #prepareClassDefinition()}.
0483: */
0484: private void initPreparation() throws java.io.IOException,
0485: ClassNotFoundException {
0486: if (initialized) {
0487: // weaver was already used, so reset bcelClass to its original state
0488: ClassParser classParser = new ClassParser(
0489: new ByteArrayInputStream(originalCode), bcelClass
0490: .getFileName());
0491: bcelClass = classParser.parse();
0492: } else
0493: initialized = true;
0494:
0495: // create bcel generator objects
0496: clGen = new ClassGen(bcelClass);
0497: cpGen = clGen.getConstantPool();
0498: instructionFactory = new InstructionFactory(clGen, cpGen);
0499: }
0500:
0501: /**
0502: * Resets some member fields to the values they should have
0503: * after weaving.
0504: * Called by {@link #prepareClassDefinition()}-
0505: */
0506: private void finishWeaving() {
0507: woven = true;
0508: cpGen = null;
0509: instructionFactory = null;
0510: }
0511:
0512: /**
0513: * Generate instrumented versions of the member methods
0514: * of {@link #targetClass}.
0515: * called by {@link #prepareClassDefinition()}.
0516: */
0517: private void weaveMethods() {
0518: // Copy the methods to another collection to allow removal
0519: // from the entire collection.
0520: int numOfMethods = methods.size();
0521: MethodWeaver[] mws = new MethodWeaver[numOfMethods];
0522: methods.values().toArray(mws);
0523: for (int i = 0; i < numOfMethods; i++) {
0524: mws[i].weaveMethod();
0525: }
0526: }
0527:
0528: /**
0529: * Returns the instrumented class file.
0530: * {@link #prepareClassDefinition()} must be called first.
0531: */
0532: protected byte[] getClassDefinition() {
0533: if (!initialized)
0534: throw new JVMAIRuntimeException("ClassWeaver for <"
0535: + targetClass + "> not initialized ");
0536: return clGen.getJavaClass().getBytes();
0537: }
0538:
0539: private void reset() {
0540: woven = false;
0541: modified = false;
0542:
0543: Iterator it = methods.values().iterator();
0544: while (it.hasNext()) {
0545: MethodWeaver mw = (MethodWeaver) it.next();
0546: methodWeavers.remove(mw.target);
0547: }
0548: methods.clear();
0549: }
0550:
0551: /**
0552: * Verifies if the new class definition doesn't change anything,
0553: * which RedefineClasses can not handle (anything except
0554: * changing method bodies).
0555: *
0556: * @return <CODE>false</CODE> if any incompabilities betwen
0557: * the new and the old class definitions where found,
0558: * otherwise <CODE>true</CODE>.
0559: */
0560: private boolean verifyClassSchema() {
0561: boolean retval = true;
0562:
0563: if (null == clGen || null == bcelClass)
0564: throw new RuntimeException("nothing to verify");
0565:
0566: //System.out.println( "Verifying " + oldClass.getClassName() );
0567: // Get classes
0568: ClassParser cp = new ClassParser(new ByteArrayInputStream(
0569: originalCode), targetClass.getName().replace('.', '/'));
0570: try {
0571: bcelClass = cp.parse();
0572: } catch (java.io.IOException e) {
0573: throw new RuntimeException(e.getMessage());
0574: }
0575: JavaClass newClass = clGen.getJavaClass();
0576:
0577: // Check class name
0578: if (!bcelClass.getClassName().equals(newClass.getClassName())) {
0579: System.out.println("Class name changed ( old: "
0580: + bcelClass.getClassName() + ", new: "
0581: + newClass.getClassName() + ")");
0582: retval = false;
0583: }
0584: // Check class modifiers (access flags)
0585: if (bcelClass.getAccessFlags() != newClass.getAccessFlags()) {
0586: System.out.println("Class modifiers changed (old: "
0587: + bcelClass.getAccessFlags() + ", new: "
0588: + newClass.getAccessFlags() + ")");
0589: retval = false;
0590: }
0591:
0592: // Methods
0593: org.apache.bcel.classfile.Method oldMethods[] = bcelClass
0594: .getMethods();
0595: org.apache.bcel.classfile.Method newMethods[] = newClass
0596: .getMethods();
0597: int oldMethodsLength = oldMethods.length;
0598: // number of methods
0599: if (oldMethodsLength != newMethods.length) {
0600: System.out.println("Number of methods changed (old: "
0601: + oldMethodsLength + ", new: " + newMethods.length
0602: + ")");
0603: retval = false;
0604: } else {
0605: for (int i = 0; i < oldMethodsLength; i++) {
0606: org.apache.bcel.classfile.Method oldMeth = oldMethods[i];
0607: org.apache.bcel.classfile.Method newMeth = newMethods[i];
0608:
0609: // method name
0610: String oldMName = oldMeth.getName();
0611: if (!oldMName.equals(newMeth.getName())) {
0612: System.out.println("Method name changed (old: "
0613: + oldMName + ", new: " + newMeth.getName()
0614: + ")");
0615: retval = false;
0616: }
0617: // method modifiers
0618: if (oldMeth.getAccessFlags() != newMeth
0619: .getAccessFlags()) {
0620: System.out.println("Method modifiers changed for"
0621: + oldMName);
0622: retval = false;
0623: }
0624: // signature
0625: if (!oldMeth.getSignature().equals(
0626: newMeth.getSignature())) {
0627: System.out.println("Method signature changed for "
0628: + oldMName);
0629: retval = false;
0630: }
0631: }
0632: }
0633:
0634: // Check for JVMDI_ERROR_SCHEMA_CHANGE... (Fields)
0635: org.apache.bcel.classfile.Field newFields[] = newClass
0636: .getFields();
0637: org.apache.bcel.classfile.Field oldFields[] = bcelClass
0638: .getFields();
0639: int newFieldsLength = newFields.length;
0640: if (newFieldsLength != oldFields.length) {
0641: System.out.println("Number of fields changed (old "
0642: + oldFields.length + ", new " + newFieldsLength
0643: + ")");
0644: retval = false;
0645: } else {
0646: for (int i = 0; i < newFieldsLength; i++) {
0647: org.apache.bcel.classfile.Field nf = newFields[i];
0648: org.apache.bcel.classfile.Field of = oldFields[i];
0649: String oldName = of.getName();
0650:
0651: // Name
0652: if (!nf.getName().equals(oldName)) {
0653: System.out.println("Field name changed ( new: "
0654: + nf.getName() + ", old: " + oldName + ")");
0655: retval = false;
0656: }
0657: // Access flags
0658: if (nf.getAccessFlags() != of.getAccessFlags()) {
0659: System.out
0660: .println("Field access flags changed for "
0661: + oldName + "( old: "
0662: + of.getAccessFlags() + ", new: "
0663: + nf.getAccessFlags() + ")");
0664: retval = false;
0665: }
0666: // Signature
0667: if (!nf.getSignature().equals(of.getSignature())) {
0668: System.out.println("Field signature changed for "
0669: + oldName + "( old: " + of.getSignature()
0670: + ", new: " + nf.getSignature() + ")");
0671: retval = false;
0672: }
0673: // Constant value (may be 'null')
0674: ConstantValue oldConst = of.getConstantValue();
0675: ConstantValue newConst = nf.getConstantValue();
0676: if (oldConst != newConst) {
0677: if (null == oldConst) {
0678: System.out
0679: .println("Changed constant field to modifiable field "
0680: + oldName);
0681: retval = false;
0682: } else if (null == newConst) {
0683: System.out
0684: .println("Changed modifiable field to constant field "
0685: + oldName);
0686: retval = false;
0687: } else if (!oldConst.toString().equals(
0688: newConst.toString())) {
0689: System.out.println("Changed "
0690: + oldConst.toString() + " to "
0691: + newConst.toString());
0692: retval = false;
0693: }
0694: }
0695: // Attributes
0696: Attribute newAttributes[] = nf.getAttributes();
0697: Attribute oldAttributes[] = of.getAttributes();
0698: int oldAttributesLength = oldAttributes.length;
0699: if (oldAttributesLength != newAttributes.length) {
0700: System.out
0701: .println("Number of field attributes changend (old: "
0702: + oldAttributesLength
0703: + " new: "
0704: + newAttributes.length + ")");
0705: retval = false;
0706: } else {
0707: for (int j = 0; j < oldAttributesLength; j++) {
0708: Attribute oldAttr = oldAttributes[j];
0709: Attribute newAttr = newAttributes[j];
0710: }
0711: }
0712: }
0713: }
0714: return retval;
0715: }
0716:
0717: /**
0718: * Verify bytecodes with the BCEL verifier.
0719: * <p>
0720: * NOTE: Can't be used since even `normal' classes are rejected with Jikes
0721: * RVM 2.3.0.1.
0722: *
0723: */
0724: private void verify() {
0725: if (null == clGen)
0726: throw new RuntimeException("nothing to verify");
0727:
0728: JavaClass jc = clGen.getJavaClass();
0729: Repository.addClass(jc);
0730:
0731: Verifier v = VerifierFactory.getVerifier(jc.getClassName());
0732: checkVerificationResult(v.doPass1(), "1");
0733: checkVerificationResult(v.doPass2(), "2");
0734: MethodWeaver methodWeavers[] = new MethodWeaver[methods.size()];
0735: methods.values().toArray(methodWeavers);
0736: for (int i = 0; i < methods.size(); i++) {
0737: int method_index = methodWeavers[i].getTargetIndex();
0738: checkVerificationResult(v.doPass3a(method_index), "3a");
0739: checkVerificationResult(v.doPass3b(method_index), "3b");
0740: }
0741:
0742: try {
0743: String[] warnings = v.getMessages();
0744: if (warnings.length != 0)
0745: System.err.println("Messages:");
0746: for (int j = 0; j < warnings.length; j++)
0747: System.err.println(warnings[j]);
0748: } catch (Exception e) {
0749: System.err.println("could not verify " + targetClass);
0750: }
0751: }
0752:
0753: /**
0754: * Check bytecode verification result.
0755: *
0756: * @param vr verification result which must be checked
0757: * @param pass verification pass
0758: */
0759: private void checkVerificationResult(VerificationResult vr,
0760: String pass) {
0761: if (vr != VerificationResult.VR_OK) {
0762: System.err.println("Verification failed in pass " + pass
0763: + " for " + targetClass + ".");
0764: System.err.println(vr);
0765: }
0766: }
0767:
0768: /**
0769: * Redefines existing classes, actually only method bodies may be redefined,
0770: * but the JVMDI and JVMTI functions requieres the whole class code. The parameters
0771: * are two arrays of the same size, each entry of one array corresponds to the
0772: * entry with the same index in the other array.
0773: * The class that should be redefined must exist in the JVM (they must be loaded
0774: * in advance).
0775: *
0776: * Required for JVMDI/JVMTI HotSwap advice weaving.
0777: *
0778: * @param cls Array of Class objects that should get redefined
0779: * @param definitions Array of class definitions, which are arrays of bytes,
0780: * the definition is in the same form like in a class file.
0781: * @exception UnmodifiableClassException <CODE>cls</CODE> contains a not modifiable
0782: * class (for example an interface or a class that's not in the class path).
0783: * @exception IlligalClassFormatException <CODE>definitions</CODE> contains a invalide
0784: * class definition
0785: * @exception RuntimeException could not apply operation.
0786: * @exception NullPointerException if <CODE>cls</CODE> is <CODE>null</CODE>.
0787: * @exception IlligalArgumentExceptin <CODE>definitions</CODE> is <CODE>null</CODE>.
0788: */
0789: public static void redefineClasses(Class[] cls, byte[][] definitions) {
0790: HotSwapClassWeaver.redefineClasses(cls, definitions);
0791: }
0792:
0793: /**
0794: * Redefines an existing class, actually only method bodies may be redefined,
0795: * but the JVMDI and JVMTI functions requieres the whole class code.
0796: * The class that should be redefined must exist in the JVM (they must be loaded
0797: * in advance).
0798: *
0799: * Required for JVMDI/JVMTI HotSwap advice weaving.
0800: *
0801: * @param cl Class objects that should get redefined
0802: * @param definition class definition,
0803: * the definition is in the same form like in a class file.
0804: * @exception UnmodifiableClassException <CODE>cl</CODE> is a not modifiable
0805: * class (for example an interface or a class that's not in the class path).
0806: * @exception IlligalClassFormatException <CODE>definition</CODE> is a invalide
0807: * class definition
0808: * @exception RuntimeException could not apply operation.
0809: * @exception NullPointerException if <CODE>cl</CODE> is <CODE>null</CODE>.
0810: * @exception IlligalArgumentExceptin <CODE>definition</CODE> is <CODE>null</CODE>.
0811: */
0812: public static void redefineClass(Class cl, byte[] definition) {
0813: HotSwapClassWeaver.redefineClass(cl, definition);
0814: }
0815:
0816: //----------------------------------------------------------------------
0817:
0818: /**
0819: * Implementation of MethodWeaver. Collects redefinition
0820: * requests and weaves them into the methods bytecode.
0821: *
0822: * Based on MethodWeaver implementation by Johann Gyger.
0823: *
0824: * @see ch.ethz.jvmai.MethodWeaver
0825: */
0826: protected class MethodWeaver implements ch.ethz.jvmai.MethodWeaver {
0827:
0828: /**
0829: * Method bound to this weaver.
0830: */
0831: public final Member target;
0832:
0833: /**
0834: * Method identifier for {@link #target}.
0835: * Generated with {@link HotSwapAdvancedClassWeaver#createNewMethodId ClassWeaver.createNewMethodId}.
0836: */
0837: public final int targetId;
0838:
0839: /**
0840: * Method index (in the array of declared methods of the declaring
0841: * class {@link Class#getDeclaredMethods()}) or -1 if not initialized.
0842: */
0843: private int targetIndex;
0844:
0845: /**
0846: * Indicates how the method should be modified.
0847: * see {@link ch.ethz.jvmai.MethodWeaver}
0848: */
0849: private int status;
0850:
0851: /**
0852: * Redefine advice method.
0853: */
0854: private Member redefineAdvice;
0855:
0856: // /**
0857: // * Watched method calls (not implemented).
0858: // */
0859: // private Map methodCalls;
0860: //
0861: // /**
0862: // * Watched method returns (not implemented).
0863: // */
0864: // private Map methodReturns;
0865:
0866: /**
0867: * Watched field accesses
0868: */
0869: private Map fieldAccessors;
0870:
0871: /**
0872: * Watched field modifications
0873: */
0874: private Map fieldModifiers;
0875:
0876: /**
0877: * BCELs method generator
0878: */
0879: private MethodGen methodGen;
0880:
0881: /**
0882: * BCEL instructions, used during weaving
0883: * process (normalwise set to 'null').
0884: */
0885: private InstructionList instructions;
0886:
0887: /**
0888: * Create a new method weaver. Use the static method
0889: * {@link HotSwapAdvancedClassWeaver#getWeaver(java.lang.reflect.Member) ClassWeaver.getWeaver}to obtain a weaver.
0890: *
0891: * @param target method that will be woven
0892: */
0893: public MethodWeaver(Member target) {
0894: this .target = target;
0895:
0896: targetId = createNewMethodId(target);
0897: targetIndex = -1;
0898: redefineAdvice = null;
0899: methodGen = null;
0900: instructions = null;
0901:
0902: //methodCalls = Collections.synchronizedMap(new LinkedHashMap());
0903: //methodReturns = Collections.synchronizedMap(new LinkedHashMap());
0904: fieldAccessors = Collections
0905: .synchronizedMap(new LinkedHashMap());
0906: fieldModifiers = Collections
0907: .synchronizedMap(new LinkedHashMap());
0908:
0909: status = MWEAVER_STATUS_UNCHANGED;
0910: }
0911:
0912: /**
0913: * Changes the status field to reflect that an watch
0914: * of type <code>'type'</code> is set.
0915: *
0916: * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
0917: */
0918: private void addWatch(int type) {
0919: status |= type;
0920: setModified();
0921: }
0922:
0923: /**
0924: * Changes the status field to reflect that an watch
0925: * of type <code>'type'</code> has to be removed.
0926: *
0927: * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
0928: */
0929: private void removeWatch(int type) {
0930: status &= ~type;
0931: setModified();
0932: }
0933:
0934: /**
0935: * Changes the status field to either add or remove a watch.
0936: *
0937: * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
0938: * @param flag <code>'true'</code> to add the watch, false to remove it.
0939: */
0940: private void setWatch(int type, boolean flag) {
0941: status = flag ? (status | type) : (status & ~type);
0942: setModified();
0943: }
0944:
0945: /**
0946: * Indicates if a watch is set or not.
0947: *
0948: * @param type type of the watch (see ch.ethz.jvmai.MethodWeaver).
0949: * @return <CODE>'true'</CODE> if the watch is set.
0950: */
0951: public boolean hasWatch(int type) {
0952: return (status & type) > 0;
0953: }
0954:
0955: /**
0956: * Returns the method bound to this weaver.
0957: * @return Method bound to this weaver.
0958: */
0959: public Member getTarget() {
0960: return target;
0961: }
0962:
0963: /**
0964: * Returns the identifier of the method bound to this weaver.
0965: * @return identifier of the method bound to this weaver
0966: */
0967: public int getTargetId() {
0968: return targetId;
0969: }
0970:
0971: /**
0972: * Returns the index of the target method in the array, which
0973: * will be returned by its declaring classes <CODE>getDeclaredMethods()
0974: * </CODE> method.
0975: * @return index in the array of declared methods
0976: */
0977: public int getTargetIndex() {
0978: return targetIndex;
0979: }
0980:
0981: /**
0982: * Returns the status of this weaver. The codes are defined
0983: * in {@link ch.ethz.jvmai.MethodWeaver MethodWeaver}.
0984: *
0985: * @return int status of the weaver
0986: */
0987: public int getStatus() {
0988: return status;
0989: }
0990:
0991: /**
0992: * Redefine the method in this weaver.
0993: *
0994: * @param advice method that will be called instead
0995: * or null to reset a redefined method.
0996: */
0997: public void setRedefineAdvice(Member advice) {
0998: if (null != redefineAdvice && null != advice)
0999: throw new RuntimeException(
1000: "Method is already redefined");
1001:
1002: redefineAdvice = advice;
1003: setWatch(MWEAVER_STATUS_REDEFINE, null != advice);
1004: }
1005:
1006: /**
1007: * Enable method entry join point.
1008: *
1009: * @param flag enable/disable
1010: */
1011: public void setMethodEntryEnabled(boolean flag) {
1012: setWatch(MWEAVER_STATUS_ENTRY_ENABLED, flag);
1013: }
1014:
1015: /**
1016: * Enable method entry join point.
1017: *
1018: * @param flag enable/disable
1019: */
1020: public void setMethodExitEnabled(boolean flag) {
1021: setWatch(MWEAVER_STATUS_EXIT_ENABLED, flag);
1022: }
1023:
1024: /**
1025: * Add a method for which a callback should be woven (before each call).
1026: *
1027: * @param m watched method
1028: */
1029: public void addMethodCall(Method m) {
1030: throw new JVMAIRuntimeException(
1031: "Method HotSwapAdvancedClassWeaver.addMethodCall() not implemented");
1032: // String key = m.getDeclaringClass().getName() + "#" + m.getName();
1033: // if( ! methodCalls.containsKey( key ) ) {
1034: // methodCalls.put( key, m );
1035: // addWatch(MWEAVER_STATUS_HAS_METHOD_CALL_WATCH);
1036: // }
1037: }
1038:
1039: /**
1040: * Remove a method for which a callback was woven (before each call).
1041: *
1042: * @param m watched method
1043: */
1044: public void removeMethodCall(Method m) {
1045: throw new JVMAIRuntimeException(
1046: "Method HotSwapAdvancedClassWeaver.removeMethodCall() not implemented");
1047: // String key = m.getDeclaringClass().getName() + "#" + m.getName();
1048: // if( methodCalls.containsKey( key ) ) {
1049: // methodCalls.remove( key );
1050: // if( methodCalls.isEmpty() )
1051: // removeWatch(MWEAVER_STATUS_HAS_METHOD_CALL_WATCH);
1052: // else
1053: // setModified();
1054: // }
1055: }
1056:
1057: /**
1058: * Add a method for which a callback should be woven (after each call).
1059: *
1060: * @param m watched method
1061: */
1062: public void addMethodReturn(Method m) {
1063: throw new JVMAIRuntimeException(
1064: "Method HotSwapAdvancedClassWeaver.addMethodReturn() not implemented");
1065: // String key = m.getDeclaringClass().getName() + "#" + m.getName();
1066: // if( ! methodReturns.containsKey( key ) ) {
1067: // methodReturns.put( key, m );
1068: // addWatch(MWEAVER_STATUS_HAS_METHOD_RETURN_WATCH);
1069: // }
1070: }
1071:
1072: /**
1073: * Remove a method for which a callback was woven (after each call).
1074: *
1075: * @param m watched method
1076: */
1077: public void removeMethodReturn(Method m) {
1078: throw new JVMAIRuntimeException(
1079: "Method HotSwapAdvancedClassWeaver.removeMethodReturn() not implemented");
1080: // String key = m.getDeclaringClass().getName() + "#" + m.getName();
1081: // if( methodReturns.containsKey( key ) ) {
1082: // methodReturns.remove( key );
1083: // if( methodReturns.isEmpty() )
1084: // removeWatch(MWEAVER_STATUS_HAS_METHOD_RETURN_WATCH);
1085: // else
1086: // setModified();
1087: // }
1088: }
1089:
1090: /**
1091: * Add a field for which a callback should be woven (for each access).
1092: *
1093: * @param fw weaver for the watched field
1094: */
1095: public void addFieldAccessor(HotSwapFieldWeaver fw) {
1096: String f = fw.getKey();
1097: //System.out.println("HotSwapClassWeaver.addFieldAccessor(" + f + ") to " + target.getName() );
1098: if (!fieldAccessors.containsKey(f)) {
1099: fieldAccessors.put(f, fw);
1100: addWatch(MWEAVER_STATUS_HAS_FIELD_ACCESS_WATCH);
1101: }
1102: }
1103:
1104: /**
1105: * Remove a field for which a callback was woven (for each access).
1106: *
1107: * @param fw weaver for the watched field
1108: */
1109: public void removeFieldAccessor(HotSwapFieldWeaver fw) {
1110: String f = fw.getKey();
1111: if (fieldAccessors.containsKey(f)) {
1112: fieldAccessors.remove(f);
1113: if (fieldAccessors.isEmpty())
1114: removeWatch(MWEAVER_STATUS_HAS_FIELD_ACCESS_WATCH);
1115: else
1116: setModified();
1117: }
1118: }
1119:
1120: /**
1121: * Add a field for which a callback should be woven (for each modification).
1122: *
1123: * @param fw weaver for the watched field
1124: */
1125: public void addFieldModifier(HotSwapFieldWeaver fw) {
1126: String f = fw.getKey();
1127: //System.out.println("HotSwapClassWeaver.addFieldModifier(" + f + ") to " + target.getName() );
1128: if (!fieldModifiers.containsKey(f)) {
1129: fieldModifiers.put(f, fw);
1130: addWatch(MWEAVER_STATUS_HAS_FIELD_MODIFICATION_WATCH);
1131: }
1132: }
1133:
1134: /**
1135: * Remove a field for which a callback was woven (for each modification).
1136: *
1137: * @param fw weaver for the watched field
1138: */
1139: public void removeFieldModifier(HotSwapFieldWeaver fw) {
1140: String f = fw.getKey();
1141: if (fieldModifiers.containsKey(f)) {
1142: fieldModifiers.remove(f);
1143: if (fieldModifiers.isEmpty())
1144: removeWatch(MWEAVER_STATUS_HAS_FIELD_MODIFICATION_WATCH);
1145: else
1146: setModified();
1147: }
1148: }
1149:
1150: /**
1151: * Weave all registered field accesses/modifications.
1152: */
1153: private void weaveFieldAdvice() {
1154: for (InstructionHandle h = instructions.getStart(); h != null; h = h
1155: .getNext()) {
1156: Instruction instr = h.getInstruction();
1157:
1158: if (instr instanceof FieldInstruction) {
1159: FieldInstruction fi = (FieldInstruction) instr;
1160: String key = fi.getReferenceType(cpGen).toString()
1161: + "#" + fi.getName(cpGen) + "#"
1162: + fi.getSignature(cpGen);
1163: if ((instr instanceof GETSTATIC || instr instanceof GETFIELD)
1164: && fieldAccessors.containsKey(key)) {
1165: // Field Access
1166: HotSwapFieldWeaver fieldWeaver = (HotSwapFieldWeaver) fieldAccessors
1167: .get(key);
1168: // check if this is a watched field access
1169: if (null != fieldWeaver) {
1170: //System.out.println("MethodWeaver.weaveFieldAdvice(): " + target + " GET " + key);
1171:
1172: // weave the callback function call
1173: instructions
1174: .insert(
1175: h,
1176: createFieldAccessAdviceCallback(fieldWeaver));
1177: }
1178: } else if ((instr instanceof PUTSTATIC || instr instanceof PUTFIELD)
1179: && fieldModifiers.containsKey(key)) {
1180: // Field modification
1181: HotSwapFieldWeaver fieldWeaver = (HotSwapFieldWeaver) fieldModifiers
1182: .get(key);
1183: // check if this is a watched field modification
1184: if (null != fieldWeaver) {
1185: // Store new value to a local variable
1186: Type field_type = fi.getFieldType(cpGen);
1187: // The use of the LocalVariableGen is faster
1188: // than just using max local + 1 (Dont no why, but meassurements showed it.
1189: int local_index = methodGen.getMaxLocals() + 1;
1190: instructions.insert(h, InstructionFactory
1191: .createStore(field_type,
1192: local_index));
1193:
1194: //System.out.println("MethodWeaver.weaveFieldAdvice(): " + target + " PUT " + key);
1195: // weaver the callback function call
1196: instructions
1197: .insert(
1198: h,
1199: createFieldModificationAdviceCallback(
1200: "doOnFieldModification",
1201: fieldWeaver,
1202: local_index));
1203: // Load new value
1204: instructions.insert(h,
1205: InstructionFactory.createLoad(
1206: field_type, local_index));
1207: methodGen.setMaxLocals(local_index
1208: + field_type.getSize() - 1);
1209: }
1210: }
1211: }
1212: }
1213: }
1214:
1215: /**
1216: * Weave method redefine advice callback into method body and remove original
1217: * code.
1218: */
1219: private void weaveRedefineAdvice() {
1220: //System.out.println("MethodWeaver.weaveRedefineAdvice(): " + target + " / " + redefineAdvice);
1221:
1222: // get advices class generator
1223: Class advice_class = redefineAdvice.getDeclaringClass();
1224: String adviceClassName = advice_class.getName();
1225: ClassGen adviceGen;
1226: try {
1227: adviceGen = new ClassGen(HotSwapAspectInterfaceImpl
1228: .getBCELClassDefinition(advice_class));
1229: } catch (ClassNotFoundException e) {
1230: throw new JVMAIRuntimeException(
1231: "can not read class file " + advice_class);
1232: }
1233:
1234: // get advices constant pool generator
1235: ConstantPoolGen advice_cpg = adviceGen.getConstantPool();
1236:
1237: // get advices generator (method generator)
1238: org.apache.bcel.classfile.Method[] methods = adviceGen
1239: .getMethods();
1240: String adviceName = redefineAdvice.getName();
1241: MethodGen advice_mg = null;
1242: for (int i = 0; i < methods.length; i++)
1243: if (adviceName.equals(methods[i].getName())) {
1244: advice_mg = new MethodGen(methods[i],
1245: adviceClassName, advice_cpg);
1246: i = methods.length;
1247: }
1248: if (null == advice_mg)
1249: throw new JVMAIRuntimeException("can not find method "
1250: + adviceClassName + "." + adviceName);
1251:
1252: methodGen.removeExceptionHandlers();
1253:
1254: // set targets method generator
1255: instructions = advice_mg.getInstructionList();
1256: methodGen.setInstructionList(instructions);
1257: methodGen.setMaxLocals(advice_mg.getMaxLocals());
1258:
1259: for (InstructionHandle h = instructions.getStart(); h != null; h = h
1260: .getNext()) {
1261: Instruction instr = h.getInstruction();
1262:
1263: // Transform local variable (and parameter) access.
1264: // Decrement each index by 1 because the first parameter was stripped.
1265: if (instr instanceof LocalVariableInstruction) {
1266: LocalVariableInstruction lv_instr = (LocalVariableInstruction) instr;
1267: if (lv_instr.getIndex() == 0)
1268: throw new JVMAIRuntimeException(
1269: "No (implicit) `this' usage allowed in advice method: "
1270: + redefineAdvice);
1271: lv_instr.setIndex(lv_instr.getIndex() - 1);
1272: }
1273:
1274: // Transform constant pool references to constant pool of target class.
1275: // Add missing constant pool entries in target class.
1276: if (instr instanceof CPInstruction) {
1277: CPInstruction cp_instr = (CPInstruction) instr;
1278: org.apache.bcel.classfile.Constant c = advice_cpg
1279: .getConstant(cp_instr.getIndex());
1280: int new_index = cpGen.addConstant(c, advice_cpg);
1281: cp_instr.setIndex(new_index);
1282: }
1283: }
1284:
1285: // Curb exception handlers
1286: CodeExceptionGen[] cegs = advice_mg.getExceptionHandlers();
1287: for (int i = 0; i < cegs.length; i++) {
1288: CodeExceptionGen ceg = cegs[i];
1289: methodGen.addExceptionHandler(ceg.getStartPC(), ceg
1290: .getEndPC(), ceg.getHandlerPC(), ceg
1291: .getCatchType());
1292: }
1293: }
1294:
1295: /**
1296: * Weave method entry advice callback into method body.
1297: */
1298: private void weaveMethodEntryAdvice() {
1299: //System.out.println("MethodWeaver.weaveMethodEntryAdvice(): " + target);
1300: InstructionHandle start = instructions.getStart();
1301:
1302: if (target instanceof Constructor) {
1303:
1304: // JVM requires call to super constructors, to be done first,
1305: // but this works also when the callback is called first.
1306: // check if the constructor calls another constructor
1307: InstructionHandle h = usesSuper();
1308: if (null != h) {
1309: // call must be woven after the other constructor was called.
1310: h = instructions.append(h,
1311: new PUSH(cpGen, targetId));
1312: instructions.append(h, instructionFactory
1313: .createInvoke(JVMAI_CLASS,
1314: "doOnConstructor", Type.VOID,
1315: new Type[] { Type.INT },
1316: Constants.INVOKESTATIC));
1317: } else {
1318: // Push parameter: int methodId
1319: instructions.insert(start,
1320: new PUSH(cpGen, targetId));
1321:
1322: // Call advice
1323: instructions.insert(start, instructionFactory
1324: .createInvoke(JVMAI_CLASS,
1325: "doOnConstructor", Type.VOID,
1326: new Type[] { Type.INT },
1327: Constants.INVOKESTATIC));
1328: }
1329: } else {
1330:
1331: // Push parameter: int methodId
1332: instructions.insert(start, new PUSH(cpGen, targetId));
1333:
1334: // Call advice
1335: instructions.insert(start, instructionFactory
1336: .createInvoke(JVMAI_CLASS, "doOnMethodEntry",
1337: Type.VOID, new Type[] { Type.INT },
1338: Constants.INVOKESTATIC));
1339: }
1340: }
1341:
1342: /**
1343: * Checks if another constructor is called by a constructor.
1344: * The call is the first thing that a constructor does.
1345: * @return InstructionHandle of the call or null if no call
1346: * to another constructor was found
1347: */
1348: private InstructionHandle usesSuper() {
1349: //System.out.println("MethodWeaver.usesSuper(): " + target);
1350: InstructionHandle handle = instructions.getStart();
1351: while (handle != null) {
1352: Instruction inst = handle.getInstruction();
1353: if (inst instanceof INVOKESPECIAL) {
1354: return handle;
1355: }
1356: if (!(inst instanceof StackProducer))
1357: break;
1358: handle = handle.getNext();
1359: }
1360: return null;
1361: }
1362:
1363: /**
1364: * Weave method exit advice callback into method body.
1365: */
1366: private void weaveMethodExitAdvice() {
1367: //System.out.println("MethodWeaver.weaveMethodExitAdvice(): " + target);
1368: InstructionHandle try_start = instructions.getStart();
1369: InstructionHandle try_end = instructions.getEnd();
1370: InstructionHandle real_end;
1371:
1372: // Weave method exit advice (in a finally block)
1373: int local_index = methodGen.getMaxLocals();
1374: InstructionHandle finally_start = instructions
1375: .append(new ASTORE(local_index));
1376: // Push parameter: int methodId
1377: instructions.append(new PUSH(cpGen, targetId));
1378: // Push parameter: int local value index of the return value
1379: // actual local index + 3 because two additional local variables
1380: // will be created below.
1381: instructions.append(new PUSH(cpGen, local_index + 3));
1382: // Call advice
1383: instructions.append(instructionFactory.createInvoke(
1384: JVMAI_CLASS, "doOnMethodExit", Type.VOID,
1385: new Type[] { Type.INT, Type.INT },
1386: Constants.INVOKESTATIC));
1387:
1388: real_end = instructions.append(new RET(local_index++));
1389:
1390: // Insert exception handler before finally block
1391: InstructionHandle catch_start = instructions.insert(
1392: finally_start, new ASTORE(local_index));
1393: instructions.insert(finally_start, new JSR(finally_start));
1394: instructions
1395: .insert(finally_start, new ALOAD(local_index++));
1396: instructions.insert(finally_start, new ATHROW());
1397:
1398: // Jump to finally block before each return
1399: JumpFinallyVisitor visitor = new JumpFinallyVisitor(
1400: instructions, try_start, catch_start,
1401: finally_start, local_index);
1402: visitor.go();
1403: local_index += visitor.getLocalSize();
1404:
1405: methodGen.setMaxLocals(local_index);
1406: methodGen.addExceptionHandler(try_start, catch_start
1407: .getPrev(), catch_start, null);
1408: }
1409:
1410: /**
1411: * Create a field access advice callback that can be woven.
1412: *
1413: * @param fieldWeaver weaver for the field.
1414: * @return List of instructions that make up the callback.
1415: */
1416: private InstructionList createFieldAccessAdviceCallback(
1417: HotSwapFieldWeaver fieldWeaver) {
1418: InstructionList il = new InstructionList();
1419:
1420: // Push parameter: Object owner
1421: if (Modifier.isStatic(fieldWeaver.getTarget()
1422: .getModifiers()))
1423: il.append(InstructionConstants.ACONST_NULL);
1424: else
1425: il.append(InstructionConstants.DUP);
1426:
1427: // Push parameter: int fieldId
1428: il.append(new PUSH(cpGen, fieldWeaver.getTargetId()));
1429:
1430: // Call advice
1431: il.append(instructionFactory.createInvoke(JVMAI_CLASS,
1432: "doOnFieldAccess", Type.VOID, new Type[] {
1433: Type.OBJECT, Type.INT },
1434: Constants.INVOKESTATIC));
1435:
1436: return il;
1437: }
1438:
1439: /**
1440: * Create a field advice callback that can be woven.
1441: *
1442: * @param callbackMethod method that will be called.
1443: * @param fieldWeaver weaver for the field.
1444: * @param slot variable table index of the new value for the field
1445: * @return List of instructions that make up the callback.
1446: */
1447: private InstructionList createFieldModificationAdviceCallback(
1448: String callbackMethod, HotSwapFieldWeaver fieldWeaver,
1449: int slot) {
1450: InstructionList il = new InstructionList();
1451:
1452: // Push parameter: Object owner
1453: if (Modifier.isStatic(fieldWeaver.getTarget()
1454: .getModifiers()))
1455: il.append(InstructionConstants.ACONST_NULL);
1456: else
1457: il.append(InstructionConstants.DUP);
1458:
1459: // Push parameter: int fieldId
1460: int fieldId = fieldWeaver.getTargetId();
1461: il.append(new PUSH(cpGen, fieldId));
1462:
1463: // push parameter: local variable index of the field
1464: il.append(new PUSH(cpGen, slot));
1465:
1466: // Call advice
1467: il.append(instructionFactory.createInvoke(JVMAI_CLASS,
1468: callbackMethod, Type.VOID, new Type[] {
1469: Type.OBJECT, Type.INT, Type.INT },
1470: Constants.INVOKESTATIC));
1471:
1472: return il;
1473: }
1474:
1475: /**
1476: * Weave advice that are associated with the method in this weaver.
1477: *
1478: * Called by {@link HotSwapAdvancedClassWeaver#weaveMethods()}.
1479: */
1480: protected void weaveMethod() {
1481: if (MWEAVER_STATUS_UNCHANGED == status) {
1482: // Remove this weaver from all maps
1483: //System.out.println("MethodWeaverImpl.weaveMethod(): remove " + this.target.getName());
1484: HotSwapAdvancedClassWeaver.methodWeavers
1485: .remove(this .target);
1486: methods.remove(this .target);
1487: } else {
1488: // 1. Prepare
1489: initMethodWeaving();
1490: // 2. Redefine method
1491: if (null != redefineAdvice)
1492: weaveRedefineAdvice();
1493: // 3. Weave field advices
1494: if (hasWatch(MWEAVER_STATUS_HAS_FIELD_ACCESS_WATCH
1495: | MWEAVER_STATUS_HAS_FIELD_MODIFICATION_WATCH))
1496: weaveFieldAdvice();
1497: // 4. Weave method entry advice
1498: if (hasWatch(MWEAVER_STATUS_ENTRY_ENABLED))
1499: weaveMethodEntryAdvice();
1500: // 5. Weaver method exit advice
1501: if (hasWatch(MWEAVER_STATUS_EXIT_ENABLED))
1502: weaveMethodExitAdvice();
1503: // 6. Finish method weaving
1504: finishMethodWeaving();
1505: }
1506: }
1507:
1508: /**
1509: * Prepare for weaving by initializing the corresponding fields.
1510: *
1511: * Called by {@link #weaveMethod()}.
1512: */
1513: private void initMethodWeaving() {
1514: // 1. Get the method name
1515: String methodName;
1516: String signature;
1517: if (target instanceof Constructor) {
1518: // the name of constructors is alway <init>
1519: methodName = "<init>";
1520: signature = JNIUtil.jniSignature((Constructor) target);
1521: } else { // target is a method
1522: methodName = target.getName();
1523: signature = JNIUtil.jniSignature((Method) target);
1524: }
1525:
1526: // 2. Create the BCEL MethodGen object
1527: if (0 > targetIndex) {
1528: // 2.a Try to get the BCEL method object
1529: methodGen = null;
1530: org.apache.bcel.classfile.Method[] meths = clGen
1531: .getMethods();
1532: // Iterate over all member methods of the class
1533: for (int i = 0; i < meths.length; i++) {
1534: // Check for
1535: if (methodName.equalsIgnoreCase(meths[i].getName())
1536: && signature
1537: .equals(meths[i].getSignature())) {
1538: targetIndex = i;
1539: methodGen = new MethodGen(meths[i], methodName,
1540: cpGen);
1541: break;
1542: }
1543: }
1544: } else {
1545: methodGen = new MethodGen(clGen
1546: .getMethodAt(targetIndex), methodName, cpGen);
1547: }
1548:
1549: if (null == methodGen)
1550: throw new RuntimeException("Method " + methodName
1551: + " not found");
1552:
1553: // create BCEL instructions list
1554: instructions = methodGen.getInstructionList();
1555:
1556: }
1557:
1558: /**
1559: * Clean up weaving process and replaces the method definition in ClassWeavers
1560: * class definition.
1561: *
1562: * Called by {@link #weaveMethod()}
1563: */
1564: private void finishMethodWeaving() {
1565: methodGen.setMaxStack();
1566: methodGen.setMaxLocals();
1567: methodGen.removeLocalVariables();
1568: methodGen.removeLineNumbers();
1569: methodGen.removeCodeAttributes();
1570:
1571: clGen.setMethodAt(methodGen.getMethod(), targetIndex);
1572:
1573: // release instructions
1574: instructions.dispose();
1575: instructions = null;
1576: methodGen = null;
1577: }
1578:
1579: /**
1580: * Sets some variables to indicate that the target class
1581: * must be woven at the next call of
1582: * {@link HotSwapAdvancedClassWeaver#commit()}.
1583: * <P>
1584: * This does not set the {@link #status} variable, which must
1585: * be managed too.
1586: */
1587: private void setModified() {
1588: if (!modified) {
1589: // synchronize with commit and resetAll
1590: synchronized (classWeavers) {
1591: modifiedClassWeavers
1592: .add(HotSwapAdvancedClassWeaver.this );
1593: modified = true;
1594: }
1595: }
1596: }
1597:
1598: /**
1599: * Removes all modifications of the target method, so that it
1600: * will be restored to its original state after the next commitment
1601: * ({@link HotSwapAdvancedClassWeaver#commit()}).
1602: */
1603: private void resetMethod() {
1604: setModified();
1605: status = MWEAVER_STATUS_UNCHANGED;
1606:
1607: redefineAdvice = null;
1608: //methodCalls.clear();
1609: //methodReturns.clear();
1610: fieldAccessors.clear();
1611: fieldModifiers.clear();
1612: }
1613:
1614: }
1615: }
1616:
1617: //======================================================================
1618: //
1619: // $Log$
1620: //
|