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