001: /*
002: * @(#)ModifiedMethod.java
003: *
004: * Copyright (C) 2002-2004 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.compiler;
028:
029: import org.apache.bcel.classfile.JavaClass;
030: import org.apache.bcel.classfile.Method;
031: import org.apache.bcel.generic.InstructionList;
032: import org.apache.bcel.generic.MethodGen;
033:
034: /**
035: * Refers to a class file that has been modified with additional logging
036: * statements.
037: *
038: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
039: * @version $Date: 2004/04/15 05:48:25 $
040: * @since December 17, 2002
041: */
042: public class ModifiedMethod {
043: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
044: .getLogger(ModifiedMethod.class);
045: private JavaClass origClass;
046: private Method origMethod;
047: private MethodGen modMethGen;
048: private Method modMeth;
049: private boolean closed = false;
050: private ModifiedInstructionList modInstructions;
051:
052: /**
053: * constant pool index for the name of the class's signature.
054: */
055: private int classSigPoolIndex;
056:
057: /**
058: * constant pool index for the method-ref for invoking the logger.
059: */
060: private int staticMethodPoolIndex;
061:
062: /**
063: * the original class file's checksum.
064: */
065: private long checksum;
066:
067: /**
068: * Reference to this method's index
069: */
070: private final short methodIndex;
071:
072: /**
073: * @throws IllegalStateException if the class file has already been
074: * modified (identified by a class name field).
075: */
076: ModifiedMethod(short methodIndex, int classSigPoolIndex,
077: int staticMethodPoolIndex, JavaClass origC, Method origM,
078: MethodGen mg) {
079: if (mg == null || origC == null || origM == null) {
080: throw new IllegalArgumentException("no null args");
081: }
082: this .methodIndex = methodIndex;
083: this .classSigPoolIndex = classSigPoolIndex;
084: this .staticMethodPoolIndex = staticMethodPoolIndex;
085:
086: this .modMethGen = mg;
087: this .origClass = origC;
088: this .origMethod = origM;
089: }
090:
091: public short getMethodIndex() {
092: return this .methodIndex;
093: }
094:
095: public String getMethodName() {
096: return this .origMethod.getName()
097: + this .origMethod.getSignature();
098: }
099:
100: /**
101: * If the method cannot be modified, then this will return <tt>null</tt>.
102: */
103: public ModifiedInstructionList getInstructionList() {
104: checkClose();
105: if (this .modInstructions == null && canAddMarks()) {
106: createInstructionList();
107:
108: // the above call can create a null list if the generated list
109: // is bad.
110: }
111: return this .modInstructions;
112: }
113:
114: public JavaClass getOriginalClass() {
115: return this .origClass;
116: }
117:
118: public Method getOriginalMethod() {
119: return this .origMethod;
120: }
121:
122: /**
123: * Returns <tt>true</tt> if the method can be modified, otherwise returns
124: * <tt>false</tt>. A method can be modified only if it is not native,
125: * not abstract, and it has a code attribute.
126: */
127: public boolean canAddMarks() {
128: return ModifiedClass.isMarkable(this .origMethod);
129: }
130:
131: //------------------------------------------------------------------------
132:
133: /**
134: * The modified method generator is only valid <i>before</i> a close.
135: */
136: MethodGen getModifiedMethodGen() {
137: checkClose();
138: return this .modMethGen;
139: }
140:
141: /**
142: * This should only be called *after* a close.
143: */
144: Method getNewMethod() {
145: if (!this .closed || this .modMeth == null) {
146: throw new IllegalStateException(
147: "ModifiedMethod has not been closed.");
148: }
149: return this .modMeth;
150: }
151:
152: void close() {
153: checkClose();
154:
155: this .closed = true;
156: if (this .modInstructions != null) {
157: LOG.debug("Setting the modified instruction list.");
158: this .modInstructions.updateInstructionList();
159: this .modMethGen.setMaxLocals();
160: this .modMethGen.setMaxStack();
161: this .modMeth = this .modMethGen.getMethod();
162: this .modInstructions.close();
163: this .modInstructions = null;
164:
165: /*
166: InstructionList finalList = this.modMethGen.getInstructionList();
167: LOG.debug( "Final modified method: ["+this.modMethGen+
168: "], instructions (size="+finalList.getLength()+") ["+finalList+
169: "]." );
170: */
171: this .modMethGen = null;
172: } else if (this .modMeth == null) {
173: this .modMeth = this .modMethGen.getMethod();
174:
175: /*
176: InstructionList finalList = this.modMethGen.getInstructionList();
177: LOG.debug( "Final modified method: ["+this.modMethGen+
178: "], instructions (size="+finalList.getLength()+") ["+finalList+
179: "]." );
180: */
181: this .modMethGen = null;
182: }
183: }
184:
185: //------------------------------------------------------------------------
186:
187: /*
188: * The class cannot be closed when this is called, or else an NPE will
189: * be thrown.
190: */
191: private void createInstructionList() {
192: InstructionList list = getModifiedMethodGen()
193: .getInstructionList();
194: if (ModifiedInstructionList.isValidInstructionList(list)) {
195: this .modInstructions = new ModifiedInstructionList(
196: this .methodIndex, this .classSigPoolIndex,
197: this .staticMethodPoolIndex, list,
198: new ModifiedTargeters(this .modMethGen));
199: } else {
200: LOG.warn("Instruction list for method [" + getMethodName()
201: + "] in class ["
202: + getOriginalClass().getClassName()
203: + "] is invalid.");
204: }
205: // else the list is invalid.
206: }
207:
208: private void checkClose() {
209: if (this .closed) {
210: throw new IllegalStateException("Method has been closed.");
211: }
212: }
213: }
|