001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist;
017:
018: import javassist.bytecode.*;
019: import javassist.compiler.Javac;
020: import javassist.compiler.CompileError;
021: import javassist.CtMethod.ConstParameter;
022:
023: /**
024: * A collection of static methods for creating a <code>CtMethod</code>.
025: * An instance of this class does not make any sense.
026: *
027: * @see CtClass#addMethod(CtMethod)
028: */
029: public class CtNewMethod {
030:
031: /**
032: * Compiles the given source code and creates a method.
033: * The source code must include not only the method body
034: * but the whole declaration, for example,
035: *
036: * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul>
037: *
038: * @param src the source text.
039: * @param declaring the class to which the created method is added.
040: */
041: public static CtMethod make(String src, CtClass declaring)
042: throws CannotCompileException {
043: return make(src, declaring, null, null);
044: }
045:
046: /**
047: * Compiles the given source code and creates a method.
048: * The source code must include not only the method body
049: * but the whole declaration, for example,
050: *
051: * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul>
052: *
053: * <p>If the source code includes <code>$proceed()</code>, then
054: * it is compiled into a method call on the specified object.
055: *
056: * @param src the source text.
057: * @param declaring the class to which the created method is added.
058: * @param delegateObj the source text specifying the object
059: * that is called on by <code>$proceed()</code>.
060: * @param delegateMethod the name of the method
061: * that is called by <code>$proceed()</code>.
062: */
063: public static CtMethod make(String src, CtClass declaring,
064: String delegateObj, String delegateMethod)
065: throws CannotCompileException {
066: Javac compiler = new Javac(declaring);
067: try {
068: if (delegateMethod != null)
069: compiler.recordProceed(delegateObj, delegateMethod);
070:
071: CtMember obj = compiler.compile(src);
072: if (obj instanceof CtMethod)
073: return (CtMethod) obj;
074: } catch (CompileError e) {
075: throw new CannotCompileException(e);
076: }
077:
078: throw new CannotCompileException("not a method");
079: }
080:
081: /**
082: * Creates a public (non-static) method. The created method cannot
083: * be changed to a static method later.
084: *
085: * @param returnType the type of the returned value.
086: * @param mname the method name.
087: * @param parameters a list of the parameter types.
088: * @param exceptions a list of the exception types.
089: * @param body the source text of the method body.
090: * It must be a block surrounded by <code>{}</code>.
091: * If it is <code>null</code>, the created method
092: * does nothing except returning zero or null.
093: * @param declaring the class to which the created method is added.
094: */
095: public static CtMethod make(CtClass returnType, String mname,
096: CtClass[] parameters, CtClass[] exceptions, String body,
097: CtClass declaring) throws CannotCompileException {
098: return make(Modifier.PUBLIC, returnType, mname, parameters,
099: exceptions, body, declaring);
100: }
101:
102: /**
103: * Creates a method.
104: *
105: * @param modifiers access modifiers.
106: * @param returnType the type of the returned value.
107: * @param mname the method name.
108: * @param parameters a list of the parameter types.
109: * @param exceptions a list of the exception types.
110: * @param body the source text of the method body.
111: * It must be a block surrounded by <code>{}</code>.
112: * If it is <code>null</code>, the created method
113: * does nothing except returning zero or null.
114: * @param declaring the class to which the created method is added.
115: *
116: * @see Modifier
117: */
118: public static CtMethod make(int modifiers, CtClass returnType,
119: String mname, CtClass[] parameters, CtClass[] exceptions,
120: String body, CtClass declaring)
121: throws CannotCompileException {
122: try {
123: CtMethod cm = new CtMethod(returnType, mname, parameters,
124: declaring);
125: cm.setModifiers(modifiers);
126: cm.setExceptionTypes(exceptions);
127: cm.setBody(body);
128: return cm;
129: } catch (NotFoundException e) {
130: throw new CannotCompileException(e);
131: }
132: }
133:
134: /**
135: * Creates a copy of a method. This method is provided for creating
136: * a new method based on an existing method.
137: *
138: * @param src the source method.
139: * @param declaring the class to which the created method is added.
140: * @param map the hashtable associating original class names
141: * with substituted names.
142: * It can be <code>null</code>.
143: *
144: * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap)
145: */
146: public static CtMethod copy(CtMethod src, CtClass declaring,
147: ClassMap map) throws CannotCompileException {
148: return new CtMethod(src, declaring, map);
149: }
150:
151: /**
152: * Creates a copy of a method with a new name.
153: * This method is provided for creating
154: * a new method based on an existing method.
155: *
156: * @param src the source method.
157: * @param name the name of the created method.
158: * @param declaring the class to which the created method is added.
159: * @param map the hashtable associating original class names
160: * with substituted names.
161: * It can be <code>null</code>.
162: *
163: * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap)
164: */
165: public static CtMethod copy(CtMethod src, String name,
166: CtClass declaring, ClassMap map)
167: throws CannotCompileException {
168: CtMethod cm = new CtMethod(src, declaring, map);
169: cm.setName(name);
170: return cm;
171: }
172:
173: /**
174: * Creates a public abstract method.
175: *
176: * @param returnType the type of the returned value
177: * @param mname the method name
178: * @param parameters a list of the parameter types
179: * @param exceptions a list of the exception types
180: * @param declaring the class to which the created method is added.
181: *
182: * @see CtMethod#CtMethod(CtClass,String,CtClass[],CtClass)
183: */
184: public static CtMethod abstractMethod(CtClass returnType,
185: String mname, CtClass[] parameters, CtClass[] exceptions,
186: CtClass declaring) throws NotFoundException {
187: CtMethod cm = new CtMethod(returnType, mname, parameters,
188: declaring);
189: cm.setExceptionTypes(exceptions);
190: return cm;
191: }
192:
193: /**
194: * Creates a public getter method. The getter method returns the value
195: * of the specified field in the class to which this method is added.
196: * The created method is initially not static even if the field is
197: * static. Change the modifiers if the method should be static.
198: *
199: * @param methodName the name of the getter
200: * @param field the field accessed.
201: */
202: public static CtMethod getter(String methodName, CtField field)
203: throws CannotCompileException {
204: FieldInfo finfo = field.getFieldInfo2();
205: String fieldType = finfo.getDescriptor();
206: String desc = "()" + fieldType;
207: ConstPool cp = finfo.getConstPool();
208: MethodInfo minfo = new MethodInfo(cp, methodName, desc);
209: minfo.setAccessFlags(AccessFlag.PUBLIC);
210:
211: Bytecode code = new Bytecode(cp, 2, 1);
212: try {
213: String fieldName = finfo.getName();
214: if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) {
215: code.addAload(0);
216: code.addGetfield(Bytecode.THIS, fieldName, fieldType);
217: } else
218: code.addGetstatic(Bytecode.THIS, fieldName, fieldType);
219:
220: code.addReturn(field.getType());
221: } catch (NotFoundException e) {
222: throw new CannotCompileException(e);
223: }
224:
225: minfo.setCodeAttribute(code.toCodeAttribute());
226: return new CtMethod(minfo, field.getDeclaringClass());
227: }
228:
229: /**
230: * Creates a public setter method. The setter method assigns the
231: * value of the first parameter to the specified field
232: * in the class to which this method is added.
233: * The created method is not static even if the field is
234: * static. You may not change it to be static
235: * by <code>setModifiers()</code> in <code>CtBehavior</code>.
236: *
237: * @param methodName the name of the setter
238: * @param field the field accessed.
239: */
240: public static CtMethod setter(String methodName, CtField field)
241: throws CannotCompileException {
242: FieldInfo finfo = field.getFieldInfo2();
243: String fieldType = finfo.getDescriptor();
244: String desc = "(" + fieldType + ")V";
245: ConstPool cp = finfo.getConstPool();
246: MethodInfo minfo = new MethodInfo(cp, methodName, desc);
247: minfo.setAccessFlags(AccessFlag.PUBLIC);
248:
249: Bytecode code = new Bytecode(cp, 3, 3);
250: try {
251: String fieldName = finfo.getName();
252: if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) {
253: code.addAload(0);
254: code.addLoad(1, field.getType());
255: code.addPutfield(Bytecode.THIS, fieldName, fieldType);
256: } else {
257: code.addLoad(1, field.getType());
258: code.addPutstatic(Bytecode.THIS, fieldName, fieldType);
259: }
260:
261: code.addReturn(null);
262: } catch (NotFoundException e) {
263: throw new CannotCompileException(e);
264: }
265:
266: minfo.setCodeAttribute(code.toCodeAttribute());
267: return new CtMethod(minfo, field.getDeclaringClass());
268: }
269:
270: /**
271: * Creates a method forwarding to a delegate in
272: * a super class. The created method calls a method specified
273: * by <code>delegate</code> with all the parameters passed to the
274: * created method. If the delegate method returns a value,
275: * the created method returns that value to the caller.
276: * The delegate method must be declared in a super class.
277: *
278: * <p>The following method is an example of the created method.
279: *
280: * <ul><pre>int f(int p, int q) {
281: * return super.f(p, q);
282: * }</pre></ul>
283: *
284: * <p>The name of the created method can be changed by
285: * <code>setName()</code>.
286: *
287: * @param delegate the method that the created method forwards to.
288: * @param declaring the class to which the created method is
289: * added.
290: */
291: public static CtMethod delegator(CtMethod delegate,
292: CtClass declaring) throws CannotCompileException {
293: try {
294: return delegator0(delegate, declaring);
295: } catch (NotFoundException e) {
296: throw new CannotCompileException(e);
297: }
298: }
299:
300: private static CtMethod delegator0(CtMethod delegate,
301: CtClass declaring) throws CannotCompileException,
302: NotFoundException {
303: MethodInfo deleInfo = delegate.getMethodInfo2();
304: String methodName = deleInfo.getName();
305: String desc = deleInfo.getDescriptor();
306: ConstPool cp = declaring.getClassFile2().getConstPool();
307: MethodInfo minfo = new MethodInfo(cp, methodName, desc);
308: minfo.setAccessFlags(deleInfo.getAccessFlags());
309:
310: ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute();
311: if (eattr != null)
312: minfo.setExceptionsAttribute((ExceptionsAttribute) eattr
313: .copy(cp, null));
314:
315: Bytecode code = new Bytecode(cp, 0, 0);
316: boolean isStatic = Modifier.isStatic(delegate.getModifiers());
317: CtClass deleClass = delegate.getDeclaringClass();
318: CtClass[] params = delegate.getParameterTypes();
319: int s;
320: if (isStatic) {
321: s = code.addLoadParameters(params, 0);
322: code.addInvokestatic(deleClass, methodName, desc);
323: } else {
324: code.addLoad(0, deleClass);
325: s = code.addLoadParameters(params, 1);
326: code.addInvokespecial(deleClass, methodName, desc);
327: }
328:
329: code.addReturn(delegate.getReturnType());
330: code.setMaxLocals(++s);
331: code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
332: minfo.setCodeAttribute(code.toCodeAttribute());
333: return new CtMethod(minfo, declaring);
334: }
335:
336: /**
337: * Creates a wrapped method. The wrapped method receives parameters
338: * in the form of an array of <code>Object</code>.
339: *
340: * <p>The body of the created method is a copy of the body of a method
341: * specified by <code>body</code>. However, it is wrapped in
342: * parameter-conversion code.
343: *
344: * <p>The method specified by <code>body</code> must have this singature:
345: *
346: * <ul><code>Object method(Object[] params, <type> cvalue)
347: * </code></ul>
348: *
349: * <p>The type of the <code>cvalue</code> depends on
350: * <code>constParam</code>.
351: * If <code>constParam</code> is <code>null</code>, the signature
352: * must be:
353: *
354: * <ul><code>Object method(Object[] params)</code></ul>
355: *
356: * <p>The method body copied from <code>body</code> is wrapped in
357: * parameter-conversion code, which converts parameters specified by
358: * <code>parameterTypes</code> into an array of <code>Object</code>.
359: * The returned value is also converted from the <code>Object</code>
360: * type to the type specified by <code>returnType</code>. Thus,
361: * the resulting method body is as follows:
362: *
363: * <ul><pre>Object[] params = new Object[] { p0, p1, ... };
364: * <<i>type</i>> cvalue = <<i>constant-value</i>>;
365: * <i>... copied method body ...</i>
366: * Object result = <<i>returned value</i>>
367: * return (<i><returnType></i>)result;
368: * </pre></ul>
369: *
370: * <p>The variables <code>p0</code>, <code>p2</code>, ... represent
371: * formal parameters of the created method.
372: * The value of <code>cvalue</code> is specified by
373: * <code>constParam</code>.
374: *
375: * <p>If the type of a parameter or a returned value is a primitive
376: * type, then the value is converted into a wrapper object such as
377: * <code>java.lang.Integer</code>. If the type of the returned value
378: * is <code>void</code>, the returned value is discarded.
379: *
380: * <p><i>Example:</i>
381: *
382: * <ul><pre>ClassPool pool = ... ;
383: * CtClass vec = pool.makeClass("intVector");
384: * vec.setSuperclass(pool.get("java.util.Vector"));
385: * CtMethod addMethod = pool.getMethod("Sample", "add0");
386: *
387: * CtClass[] argTypes = { CtClass.intType };
388: * CtMethod m = CtNewMethod.wrapped(CtClass.voidType, "add", argTypes,
389: * null, addMethod, null, vec);
390: * vec.addMethod(m);</pre></ul>
391: *
392: * <p>where the class <code>Sample</code> is as follows:
393: *
394: * <ul><pre>public class Sample extends java.util.Vector {
395: * public Object add0(Object[] args) {
396: * super.addElement(args[0]);
397: * return null;
398: * }
399: * }</pre></ul>
400: *
401: * <p>This program produces a class <code>intVector</code>:
402: *
403: * <ul><pre>public class intVector extends java.util.Vector {
404: * public void add(int p0) {
405: * Object[] args = new Object[] { p0 };
406: * // begin of copied body
407: * super.addElement(args[0]);
408: * Object result = null;
409: * // end
410: * }
411: * }</pre></ul>
412: *
413: * <p>Note that the type of the parameter to <code>add()</code> depends
414: * only on the value of <code>argTypes</code> passed to
415: * <code>CtNewMethod.wrapped()</code>. Thus, it is easy to
416: * modify this program to produce a
417: * <code>StringVector</code> class, which is a vector containing
418: * only <code>String</code> objects, and other vector classes.
419: *
420: * @param returnType the type of the returned value.
421: * @param mname the method name.
422: * @param parameterTypes a list of the parameter types.
423: * @param exceptionTypes a list of the exception types.
424: * @param body the method body
425: * (must not be a static method).
426: * @param constParam the constant parameter
427: * (maybe <code>null</code>).
428: * @param declaring the class to which the created method is
429: * added.
430: */
431: public static CtMethod wrapped(CtClass returnType, String mname,
432: CtClass[] parameterTypes, CtClass[] exceptionTypes,
433: CtMethod body, ConstParameter constParam, CtClass declaring)
434: throws CannotCompileException {
435: return CtNewWrappedMethod.wrapped(returnType, mname,
436: parameterTypes, exceptionTypes, body, constParam,
437: declaring);
438: }
439: }
|