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.expr.ExprEditor;
022:
023: /**
024: * <code>CtBehavior</code> represents a method, a constructor,
025: * or a static constructor (class initializer).
026: * It is the abstract super class of
027: * <code>CtMethod</code> and <code>CtConstructor</code>.
028: */
029: public abstract class CtBehavior extends CtMember {
030: protected MethodInfo methodInfo;
031:
032: protected CtBehavior(CtClass clazz, MethodInfo minfo) {
033: super (clazz);
034: methodInfo = minfo;
035: }
036:
037: /**
038: * @param isCons true if this is a constructor.
039: */
040: void copy(CtBehavior src, boolean isCons, ClassMap map)
041: throws CannotCompileException {
042: CtClass declaring = declaringClass;
043: MethodInfo srcInfo = src.methodInfo;
044: CtClass srcClass = src.getDeclaringClass();
045: ConstPool cp = declaring.getClassFile2().getConstPool();
046:
047: map = new ClassMap(map);
048: map.put(srcClass.getName(), declaring.getName());
049: try {
050: boolean patch = false;
051: CtClass srcSuper = srcClass.getSuperclass();
052: CtClass destSuper = declaring.getSuperclass();
053: String destSuperName = null;
054: if (srcSuper != null && destSuper != null) {
055: String srcSuperName = srcSuper.getName();
056: destSuperName = destSuper.getName();
057: if (!srcSuperName.equals(destSuperName))
058: if (srcSuperName.equals(CtClass.javaLangObject))
059: patch = true;
060: else
061: map.put(srcSuperName, destSuperName);
062: }
063:
064: methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo,
065: map);
066: if (isCons && patch)
067: methodInfo.setSuperclass(destSuperName);
068: } catch (NotFoundException e) {
069: throw new CannotCompileException(e);
070: } catch (BadBytecode e) {
071: throw new CannotCompileException(e);
072: }
073: }
074:
075: protected void extendToString(StringBuffer buffer) {
076: buffer.append(' ');
077: buffer.append(getName());
078: buffer.append(' ');
079: buffer.append(methodInfo.getDescriptor());
080: }
081:
082: /**
083: * Returns the MethodInfo representing this method/constructor in the
084: * class file.
085: */
086: public MethodInfo getMethodInfo() {
087: declaringClass.checkModify();
088: return methodInfo;
089: }
090:
091: /**
092: * Returns the MethodInfo representing the method/constructor in the
093: * class file (read only).
094: * Normal applications do not need calling this method. Use
095: * <code>getMethodInfo()</code>.
096: *
097: * <p>The <code>MethodInfo</code> object obtained by this method
098: * is read only. Changes to this object might not be reflected
099: * on a class file generated by <code>toBytecode()</code>,
100: * <code>toClass()</code>, etc in <code>CtClass</code>.
101: *
102: * <p>This method is available even if the <code>CtClass</code>
103: * containing this method is frozen. However, if the class is
104: * frozen, the <code>MethodInfo</code> might be also pruned.
105: *
106: * @see #getMethodInfo()
107: * @see CtClass#isFrozen()
108: * @see CtClass#prune()
109: */
110: public MethodInfo getMethodInfo2() {
111: return methodInfo;
112: }
113:
114: /**
115: * Obtains the modifiers of the method/constructor.
116: *
117: * @return modifiers encoded with
118: * <code>javassist.Modifier</code>.
119: * @see Modifier
120: */
121: public int getModifiers() {
122: return AccessFlag.toModifier(methodInfo.getAccessFlags());
123: }
124:
125: /**
126: * Sets the encoded modifiers of the method/constructor.
127: *
128: * <p>Changing the modifiers may cause a problem.
129: * For example, if a non-static method is changed to static,
130: * the method will be rejected by the bytecode verifier.
131: *
132: * @see Modifier
133: */
134: public void setModifiers(int mod) {
135: declaringClass.checkModify();
136: methodInfo.setAccessFlags(AccessFlag.of(mod));
137: }
138:
139: /**
140: * Returns the annotations associated with this method or constructor.
141: *
142: * @return an array of annotation-type objects.
143: * @see #getAvailableAnnotations()
144: * @since 3.1
145: */
146: public Object[] getAnnotations() throws ClassNotFoundException {
147: return getAnnotations(false);
148: }
149:
150: /**
151: * Returns the annotations associated with this method or constructor.
152: * If any annotations are not on the classpath, they are not included
153: * in the returned array.
154: *
155: * @return an array of annotation-type objects.
156: * @see #getAnnotations()
157: * @since 3.3
158: */
159: public Object[] getAvailableAnnotations() {
160: try {
161: return getAnnotations(true);
162: } catch (ClassNotFoundException e) {
163: throw new RuntimeException("Unexpected exception", e);
164: }
165: }
166:
167: private Object[] getAnnotations(boolean ignoreNotFound)
168: throws ClassNotFoundException {
169: MethodInfo mi = getMethodInfo2();
170: AnnotationsAttribute ainfo = (AnnotationsAttribute) mi
171: .getAttribute(AnnotationsAttribute.invisibleTag);
172: AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi
173: .getAttribute(AnnotationsAttribute.visibleTag);
174: return CtClassType.toAnnotationType(ignoreNotFound,
175: getDeclaringClass().getClassPool(), ainfo, ainfo2);
176: }
177:
178: /**
179: * Returns the parameter annotations associated with this method or constructor.
180: *
181: * @return an array of annotation-type objects. The length of the returned array is
182: * equal to the number of the formal parameters. If each parameter has no
183: * annotation, the elements of the returned array are empty arrays.
184: *
185: * @see #getAvailableParameterAnnotations()
186: * @see #getAnnotations()
187: * @since 3.1
188: */
189: public Object[][] getParameterAnnotations()
190: throws ClassNotFoundException {
191: return getParameterAnnotations(false);
192: }
193:
194: /**
195: * Returns the parameter annotations associated with this method or constructor.
196: * If any annotations are not on the classpath, they are not included in the
197: * returned array.
198: *
199: * @return an array of annotation-type objects. The length of the returned array is
200: * equal to the number of the formal parameters. If each parameter has no
201: * annotation, the elements of the returned array are empty arrays.
202: *
203: * @see #getParameterAnnotations()
204: * @see #getAvailableAnnotations()
205: * @since 3.3
206: */
207: public Object[][] getAvailableParameterAnnotations() {
208: try {
209: return getParameterAnnotations(true);
210: } catch (ClassNotFoundException e) {
211: throw new RuntimeException("Unexpected exception", e);
212: }
213: }
214:
215: Object[][] getParameterAnnotations(boolean ignoreNotFound)
216: throws ClassNotFoundException {
217: MethodInfo mi = getMethodInfo2();
218: ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute) mi
219: .getAttribute(ParameterAnnotationsAttribute.invisibleTag);
220: ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute) mi
221: .getAttribute(ParameterAnnotationsAttribute.visibleTag);
222: return CtClassType.toAnnotationType(ignoreNotFound,
223: getDeclaringClass().getClassPool(), ainfo, ainfo2, mi);
224: }
225:
226: /**
227: * Obtains parameter types of this method/constructor.
228: */
229: public CtClass[] getParameterTypes() throws NotFoundException {
230: return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
231: declaringClass.getClassPool());
232: }
233:
234: /**
235: * Obtains the type of the returned value.
236: */
237: CtClass getReturnType0() throws NotFoundException {
238: return Descriptor.getReturnType(methodInfo.getDescriptor(),
239: declaringClass.getClassPool());
240: }
241:
242: /**
243: * Returns the method signature (the parameter types
244: * and the return type).
245: * The method signature is represented by a character string
246: * called method descriptor, which is defined in the JVM specification.
247: * If two methods/constructors have
248: * the same parameter types
249: * and the return type, <code>getSignature()</code> returns the
250: * same string (the return type of constructors is <code>void</code>).
251: *
252: * @see javassist.bytecode.Descriptor
253: */
254: public String getSignature() {
255: return methodInfo.getDescriptor();
256: }
257:
258: /**
259: * Obtains exceptions that this method/constructor may throw.
260: *
261: * @return a zero-length array if there is no throws clause.
262: */
263: public CtClass[] getExceptionTypes() throws NotFoundException {
264: String[] exceptions;
265: ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
266: if (ea == null)
267: exceptions = null;
268: else
269: exceptions = ea.getExceptions();
270:
271: return declaringClass.getClassPool().get(exceptions);
272: }
273:
274: /**
275: * Sets exceptions that this method/constructor may throw.
276: */
277: public void setExceptionTypes(CtClass[] types)
278: throws NotFoundException {
279: declaringClass.checkModify();
280: if (types == null || types.length == 0) {
281: methodInfo.removeExceptionsAttribute();
282: return;
283: }
284:
285: String[] names = new String[types.length];
286: for (int i = 0; i < types.length; ++i)
287: names[i] = types[i].getName();
288:
289: ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
290: if (ea == null) {
291: ea = new ExceptionsAttribute(methodInfo.getConstPool());
292: methodInfo.setExceptionsAttribute(ea);
293: }
294:
295: ea.setExceptions(names);
296: }
297:
298: /**
299: * Returns true if the body is empty.
300: */
301: public abstract boolean isEmpty();
302:
303: /**
304: * Sets a method/constructor body.
305: *
306: * @param src the source code representing the body.
307: * It must be a single statement or block.
308: * If it is <code>null</code>, the substituted
309: * body does nothing except returning zero or null.
310: */
311: public void setBody(String src) throws CannotCompileException {
312: setBody(src, null, null);
313: }
314:
315: /**
316: * Sets a method/constructor body.
317: *
318: * @param src the source code representing the body.
319: * It must be a single statement or block.
320: * If it is <code>null</code>, the substituted
321: * body does nothing except returning zero or null.
322: * @param delegateObj the source text specifying the object
323: * that is called on by <code>$proceed()</code>.
324: * @param delegateMethod the name of the method
325: * that is called by <code>$proceed()</code>.
326: */
327: public void setBody(String src, String delegateObj,
328: String delegateMethod) throws CannotCompileException {
329: declaringClass.checkModify();
330: try {
331: Javac jv = new Javac(declaringClass);
332: if (delegateMethod != null)
333: jv.recordProceed(delegateObj, delegateMethod);
334:
335: Bytecode b = jv.compileBody(this , src);
336: methodInfo.setCodeAttribute(b.toCodeAttribute());
337: methodInfo.setAccessFlags(methodInfo.getAccessFlags()
338: & ~AccessFlag.ABSTRACT);
339: } catch (CompileError e) {
340: throw new CannotCompileException(e);
341: }
342: }
343:
344: static void setBody0(CtClass srcClass, MethodInfo srcInfo,
345: CtClass destClass, MethodInfo destInfo, ClassMap map)
346: throws CannotCompileException {
347: destClass.checkModify();
348:
349: map = new ClassMap(map);
350: map.put(srcClass.getName(), destClass.getName());
351: try {
352: CodeAttribute cattr = srcInfo.getCodeAttribute();
353: if (cattr != null) {
354: ConstPool cp = destInfo.getConstPool();
355: CodeAttribute ca = (CodeAttribute) cattr.copy(cp, map);
356: destInfo.setCodeAttribute(ca);
357: }
358: } catch (CodeAttribute.RuntimeCopyException e) {
359: /* the exception may be thrown by copy() in CodeAttribute.
360: */
361: throw new CannotCompileException(e);
362: }
363:
364: destInfo.setAccessFlags(destInfo.getAccessFlags()
365: & ~AccessFlag.ABSTRACT);
366: }
367:
368: /**
369: * Obtains an attribute with the given name.
370: * If that attribute is not found in the class file, this
371: * method returns null.
372: *
373: * <p>Note that an attribute is a data block specified by
374: * the class file format. It is not an annotation.
375: * See {@link javassist.bytecode.AttributeInfo}.
376: *
377: * @param name attribute name
378: */
379: public byte[] getAttribute(String name) {
380: AttributeInfo ai = methodInfo.getAttribute(name);
381: if (ai == null)
382: return null;
383: else
384: return ai.get();
385: }
386:
387: /**
388: * Adds an attribute. The attribute is saved in the class file.
389: *
390: * <p>Note that an attribute is a data block specified by
391: * the class file format. It is not an annotation.
392: * See {@link javassist.bytecode.AttributeInfo}.
393: *
394: * @param name attribute name
395: * @param data attribute value
396: */
397: public void setAttribute(String name, byte[] data) {
398: declaringClass.checkModify();
399: methodInfo.addAttribute(new AttributeInfo(methodInfo
400: .getConstPool(), name, data));
401: }
402:
403: /**
404: * Declares to use <code>$cflow</code> for this method/constructor.
405: * If <code>$cflow</code> is used, the class files modified
406: * with Javassist requires a support class
407: * <code>javassist.runtime.Cflow</code> at runtime
408: * (other Javassist classes are not required at runtime).
409: *
410: * <p>Every <code>$cflow</code> variable is given a unique name.
411: * For example, if the given name is <code>"Point.paint"</code>,
412: * then the variable is indicated by <code>$cflow(Point.paint)</code>.
413: *
414: * @param name <code>$cflow</code> name. It can include
415: * alphabets, numbers, <code>_</code>,
416: * <code>$</code>, and <code>.</code> (dot).
417: *
418: * @see javassist.runtime.Cflow
419: */
420: public void useCflow(String name) throws CannotCompileException {
421: CtClass cc = declaringClass;
422: cc.checkModify();
423: ClassPool pool = cc.getClassPool();
424: String fname;
425: int i = 0;
426: while (true) {
427: fname = "_cflow$" + i++;
428: try {
429: cc.getDeclaredField(fname);
430: } catch (NotFoundException e) {
431: break;
432: }
433: }
434:
435: pool.recordCflow(name, declaringClass.getName(), fname);
436: try {
437: CtClass type = pool.get("javassist.runtime.Cflow");
438: CtField field = new CtField(type, fname, cc);
439: field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
440: cc.addField(field, CtField.Initializer.byNew(type));
441: insertBefore(fname + ".enter();");
442: String src = fname + ".exit();";
443: insertAfter(src, true);
444: } catch (NotFoundException e) {
445: throw new CannotCompileException(e);
446: }
447: }
448:
449: /**
450: * Declares a new local variable. The scope of this variable is the
451: * whole method body. The initial value of that variable is not set.
452: * The declared variable can be accessed in the code snippet inserted
453: * by <code>insertBefore()</code>, <code>insertAfter()</code>, etc.
454: *
455: * <p>If the second parameter <code>asFinally</code> to
456: * <code>insertAfter()</code> is true, the declared local variable
457: * is not visible from the code inserted by <code>insertAfter()</code>.
458: *
459: * @param name the name of the variable
460: * @param type the type of the variable
461: * @see #insertBefore(String)
462: * @see #insertAfter(String)
463: */
464: public void addLocalVariable(String name, CtClass type)
465: throws CannotCompileException {
466: declaringClass.checkModify();
467: ConstPool cp = methodInfo.getConstPool();
468: CodeAttribute ca = methodInfo.getCodeAttribute();
469: if (ca == null)
470: throw new CannotCompileException("no method body");
471:
472: LocalVariableAttribute va = (LocalVariableAttribute) ca
473: .getAttribute(LocalVariableAttribute.tag);
474: if (va == null) {
475: va = new LocalVariableAttribute(cp);
476: ca.getAttributes().add(va);
477: }
478:
479: int maxLocals = ca.getMaxLocals();
480: String desc = Descriptor.of(type);
481: va.addEntry(0, ca.getCodeLength(), cp.addUtf8Info(name), cp
482: .addUtf8Info(desc), maxLocals);
483: ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc));
484: }
485:
486: /**
487: * Modifies the method/constructor body.
488: *
489: * @param converter specifies how to modify.
490: */
491: public void instrument(CodeConverter converter)
492: throws CannotCompileException {
493: declaringClass.checkModify();
494: ConstPool cp = methodInfo.getConstPool();
495: converter.doit(getDeclaringClass(), methodInfo, cp);
496: }
497:
498: /**
499: * Modifies the method/constructor body.
500: *
501: * @param editor specifies how to modify.
502: */
503: public void instrument(ExprEditor editor)
504: throws CannotCompileException {
505: // if the class is not frozen,
506: // does not turn the modified flag on.
507: if (declaringClass.isFrozen())
508: declaringClass.checkModify();
509:
510: if (editor.doit(declaringClass, methodInfo))
511: declaringClass.checkModify();
512: }
513:
514: /**
515: * Inserts bytecode at the beginning of the body.
516: *
517: * <p>If this object represents a constructor,
518: * the bytecode is inserted before
519: * a constructor in the super class or this class is called.
520: * Therefore, the inserted bytecode is subject to constraints described
521: * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
522: * For example, it cannot access instance fields or methods although
523: * it may assign a value to an instance field directly declared in this
524: * class. Accessing static fields and methods is allowed.
525: * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
526: *
527: * @param src the source code representing the inserted bytecode.
528: * It must be a single statement or block.
529: * @see CtConstructor#insertBeforeBody(String)
530: */
531: public void insertBefore(String src) throws CannotCompileException {
532: declaringClass.checkModify();
533: CodeAttribute ca = methodInfo.getCodeAttribute();
534: if (ca == null)
535: throw new CannotCompileException("no method body");
536:
537: CodeIterator iterator = ca.iterator();
538: Javac jv = new Javac(declaringClass);
539: try {
540: int nvars = jv.recordParams(getParameterTypes(), Modifier
541: .isStatic(getModifiers()));
542: jv.recordParamNames(ca, nvars);
543: jv.recordLocalVariables(ca, 0);
544: jv.compileStmnt(src);
545: Bytecode b = jv.getBytecode();
546: int stack = b.getMaxStack();
547: int locals = b.getMaxLocals();
548:
549: if (stack > ca.getMaxStack())
550: ca.setMaxStack(stack);
551:
552: if (locals > ca.getMaxLocals())
553: ca.setMaxLocals(locals);
554:
555: int pos = iterator.insertEx(b.get());
556: iterator.insert(b.getExceptionTable(), pos);
557: } catch (NotFoundException e) {
558: throw new CannotCompileException(e);
559: } catch (CompileError e) {
560: throw new CannotCompileException(e);
561: } catch (BadBytecode e) {
562: throw new CannotCompileException(e);
563: }
564: }
565:
566: /**
567: * Inserts bytecode at the end of the body.
568: * The bytecode is inserted just before every return insturction.
569: * It is not executed when an exception is thrown.
570: *
571: * @param src the source code representing the inserted bytecode.
572: * It must be a single statement or block.
573: */
574: public void insertAfter(String src) throws CannotCompileException {
575: insertAfter(src, false);
576: }
577:
578: /**
579: * Inserts bytecode at the end of the body.
580: * The bytecode is inserted just before every return insturction.
581: *
582: * @param src the source code representing the inserted bytecode.
583: * It must be a single statement or block.
584: * @param asFinally true if the inserted bytecode is executed
585: * not only when the control normally returns
586: * but also when an exception is thrown.
587: * If this parameter is true, the inserted code cannot
588: * access local variables.
589: */
590: public void insertAfter(String src, boolean asFinally)
591: throws CannotCompileException {
592: declaringClass.checkModify();
593: ConstPool pool = methodInfo.getConstPool();
594: CodeAttribute ca = methodInfo.getCodeAttribute();
595: if (ca == null)
596: throw new CannotCompileException("no method body");
597:
598: CodeIterator iterator = ca.iterator();
599: int retAddr = ca.getMaxLocals();
600: Bytecode b = new Bytecode(pool, 0, retAddr + 1);
601: b.setStackDepth(ca.getMaxStack() + 1);
602: Javac jv = new Javac(b, declaringClass);
603: try {
604: int nvars = jv.recordParams(getParameterTypes(), Modifier
605: .isStatic(getModifiers()));
606: jv.recordParamNames(ca, nvars);
607: CtClass rtype = getReturnType0();
608: int varNo = jv.recordReturnType(rtype, true);
609: jv.recordLocalVariables(ca, 0);
610:
611: int handlerLen = insertAfterHandler(asFinally, b, rtype,
612: varNo);
613:
614: byte[] save = makeSaveCode(pool, rtype, varNo);
615: byte[] restore = makeRestoreCode(b, pool, rtype, varNo);
616:
617: b.addAstore(retAddr);
618: jv.compileStmnt(src);
619: b.addRet(retAddr);
620: ca.setMaxStack(b.getMaxStack());
621: ca.setMaxLocals(b.getMaxLocals());
622:
623: int gapPos = iterator.append(b.get());
624: iterator.append(b.getExceptionTable(), gapPos);
625:
626: if (asFinally)
627: ca.getExceptionTable().add(0, gapPos, gapPos, 0);
628:
629: int gapLen = iterator.getCodeLength() - gapPos - handlerLen;
630: int subr = iterator.getCodeLength() - gapLen;
631:
632: while (iterator.hasNext()) {
633: int pos = iterator.next();
634: if (pos >= subr)
635: break;
636:
637: int c = iterator.byteAt(pos);
638: if (c == Opcode.ARETURN || c == Opcode.IRETURN
639: || c == Opcode.FRETURN || c == Opcode.LRETURN
640: || c == Opcode.DRETURN || c == Opcode.RETURN) {
641: insertJSR(iterator, subr, pos, save, restore);
642: subr = iterator.getCodeLength() - gapLen;
643: }
644: }
645: } catch (NotFoundException e) {
646: throw new CannotCompileException(e);
647: } catch (CompileError e) {
648: throw new CannotCompileException(e);
649: } catch (BadBytecode e) {
650: throw new CannotCompileException(e);
651: }
652: }
653:
654: private byte[] makeSaveCode(ConstPool cp, CtClass rtype, int varNo) {
655: Bytecode b = new Bytecode(cp, 0, 0);
656: if (rtype == CtClass.voidType) {
657: b.addOpcode(Opcode.ACONST_NULL);
658: b.addAstore(varNo);
659: return b.get();
660: } else {
661: b.addStore(varNo, rtype);
662: return b.get();
663: }
664: }
665:
666: private byte[] makeRestoreCode(Bytecode code, ConstPool cp,
667: CtClass rtype, int varNo) {
668: if (rtype == CtClass.voidType) {
669: if (code.getMaxLocals() < 1)
670: code.setMaxLocals(1);
671:
672: return new byte[0];
673: } else {
674: Bytecode b = new Bytecode(cp, 0, 0);
675: b.addLoad(varNo, rtype);
676: return b.get();
677: }
678: }
679:
680: private void insertJSR(CodeIterator iterator, int subr, int pos,
681: byte[] save, byte[] restore) throws BadBytecode {
682: int gapSize = 5 + save.length + restore.length;
683: boolean wide = subr - pos > Short.MAX_VALUE - gapSize - 4;
684: gapSize = iterator.insertGap(pos, wide ? gapSize : gapSize - 2);
685:
686: iterator.write(save, pos);
687: pos += save.length;
688: if (wide) {
689: iterator.writeByte(Opcode.JSR_W, pos);
690: iterator.write32bit(subr - pos + gapSize, pos + 1);
691: pos += 5;
692: } else {
693: iterator.writeByte(Opcode.JSR, pos);
694: iterator.write16bit(subr - pos + gapSize, pos + 1);
695: pos += 3;
696: }
697:
698: iterator.write(restore, pos);
699: }
700:
701: private int insertAfterHandler(boolean asFinally, Bytecode b,
702: CtClass rtype, int returnVarNo) {
703: if (!asFinally)
704: return 0;
705:
706: int var = b.getMaxLocals();
707: b.incMaxLocals(1);
708: int pc = b.currentPc();
709: b.addAstore(var);
710: if (rtype.isPrimitive()) {
711: char c = ((CtPrimitiveType) rtype).getDescriptor();
712: if (c == 'D') {
713: b.addDconst(0.0);
714: b.addDstore(returnVarNo);
715: } else if (c == 'F') {
716: b.addFconst(0);
717: b.addFstore(returnVarNo);
718: } else if (c == 'J') {
719: b.addLconst(0);
720: b.addLstore(returnVarNo);
721: } else if (c != 'V') { // int, boolean, char, short, ...
722: b.addIconst(0);
723: b.addIstore(returnVarNo);
724: }
725: } else {
726: b.addOpcode(Opcode.ACONST_NULL);
727: b.addAstore(returnVarNo);
728: }
729:
730: b.addOpcode(Opcode.JSR);
731: int pc2 = b.currentPc();
732: b.addIndex(0); // correct later
733: b.addAload(var);
734: b.addOpcode(Opcode.ATHROW);
735: int pc3 = b.currentPc();
736: b.write16bit(pc2, pc3 - pc2 + 1);
737: return pc3 - pc;
738: }
739:
740: /* -- OLD version --
741:
742: public void insertAfter(String src) throws CannotCompileException {
743: declaringClass.checkModify();
744: CodeAttribute ca = methodInfo.getCodeAttribute();
745: CodeIterator iterator = ca.iterator();
746: Bytecode b = new Bytecode(methodInfo.getConstPool(),
747: ca.getMaxStack(), ca.getMaxLocals());
748: b.setStackDepth(ca.getMaxStack());
749: Javac jv = new Javac(b, declaringClass);
750: try {
751: jv.recordParams(getParameterTypes(),
752: Modifier.isStatic(getModifiers()));
753: CtClass rtype = getReturnType0();
754: int varNo = jv.recordReturnType(rtype, true);
755: boolean isVoid = rtype == CtClass.voidType;
756: if (isVoid) {
757: b.addOpcode(Opcode.ACONST_NULL);
758: b.addAstore(varNo);
759: jv.compileStmnt(src);
760: }
761: else {
762: b.addStore(varNo, rtype);
763: jv.compileStmnt(src);
764: b.addLoad(varNo, rtype);
765: }
766:
767: byte[] code = b.get();
768: ca.setMaxStack(b.getMaxStack());
769: ca.setMaxLocals(b.getMaxLocals());
770: while (iterator.hasNext()) {
771: int pos = iterator.next();
772: int c = iterator.byteAt(pos);
773: if (c == Opcode.ARETURN || c == Opcode.IRETURN
774: || c == Opcode.FRETURN || c == Opcode.LRETURN
775: || c == Opcode.DRETURN || c == Opcode.RETURN)
776: iterator.insert(pos, code);
777: }
778: }
779: catch (NotFoundException e) {
780: throw new CannotCompileException(e);
781: }
782: catch (CompileError e) {
783: throw new CannotCompileException(e);
784: }
785: catch (BadBytecode e) {
786: throw new CannotCompileException(e);
787: }
788: }
789: */
790:
791: /**
792: * Adds a catch clause that handles an exception thrown in the
793: * body. The catch clause must end with a return or throw statement.
794: *
795: * @param src the source code representing the catch clause.
796: * It must be a single statement or block.
797: * @param exceptionType the type of the exception handled by the
798: * catch clause.
799: */
800: public void addCatch(String src, CtClass exceptionType)
801: throws CannotCompileException {
802: addCatch(src, exceptionType, "$e");
803: }
804:
805: /**
806: * Adds a catch clause that handles an exception thrown in the
807: * body. The catch clause must end with a return or throw statement.
808: *
809: * @param src the source code representing the catch clause.
810: * It must be a single statement or block.
811: * @param exceptionType the type of the exception handled by the
812: * catch clause.
813: * @param exceptionName the name of the variable containing the
814: * caught exception, for example,
815: * <code>$e</code>.
816: */
817: public void addCatch(String src, CtClass exceptionType,
818: String exceptionName) throws CannotCompileException {
819: declaringClass.checkModify();
820: ConstPool cp = methodInfo.getConstPool();
821: CodeAttribute ca = methodInfo.getCodeAttribute();
822: CodeIterator iterator = ca.iterator();
823: Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca
824: .getMaxLocals());
825: b.setStackDepth(1);
826: Javac jv = new Javac(b, declaringClass);
827: try {
828: jv.recordParams(getParameterTypes(), Modifier
829: .isStatic(getModifiers()));
830: int var = jv.recordVariable(exceptionType, exceptionName);
831: b.addAstore(var);
832: jv.compileStmnt(src);
833:
834: int stack = b.getMaxStack();
835: int locals = b.getMaxLocals();
836:
837: if (stack > ca.getMaxStack())
838: ca.setMaxStack(stack);
839:
840: if (locals > ca.getMaxLocals())
841: ca.setMaxLocals(locals);
842:
843: int len = iterator.getCodeLength();
844: int pos = iterator.append(b.get());
845: ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
846: cp.addClassInfo(exceptionType));
847: iterator.append(b.getExceptionTable(), pos);
848: } catch (NotFoundException e) {
849: throw new CannotCompileException(e);
850: } catch (CompileError e) {
851: throw new CannotCompileException(e);
852: }
853: }
854:
855: /* CtConstructor overrides this method.
856: */
857: int getStartPosOfBody(CodeAttribute ca)
858: throws CannotCompileException {
859: return 0;
860: }
861:
862: /**
863: * Inserts bytecode at the specified line in the body.
864: * It is equivalent to:
865: *
866: * <br><code>insertAt(lineNum, true, src)</code>
867: *
868: * <br>See this method as well.
869: *
870: * @param lineNum the line number. The bytecode is inserted at the
871: * beginning of the code at the line specified by this
872: * line number.
873: * @param src the source code representing the inserted bytecode.
874: * It must be a single statement or block.
875: * @return the line number at which the bytecode has been inserted.
876: *
877: * @see CtBehavior#insertAt(int,boolean,String)
878: */
879: public int insertAt(int lineNum, String src)
880: throws CannotCompileException {
881: return insertAt(lineNum, true, src);
882: }
883:
884: /**
885: * Inserts bytecode at the specified line in the body.
886: *
887: * <p>If there is not
888: * a statement at the specified line, the bytecode might be inserted
889: * at the line including the first statement after that line specified.
890: * For example, if there is only a closing brace at that line, the
891: * bytecode would be inserted at another line below.
892: * To know exactly where the bytecode will be inserted, call with
893: * <code>modify</code> set to <code>false</code>.
894: *
895: * @param lineNum the line number. The bytecode is inserted at the
896: * beginning of the code at the line specified by this
897: * line number.
898: * @param modify if false, this method does not insert the bytecode.
899: * It instead only returns the line number at which
900: * the bytecode would be inserted.
901: * @param src the source code representing the inserted bytecode.
902: * It must be a single statement or block.
903: * If modify is false, the value of src can be null.
904: * @return the line number at which the bytecode has been inserted.
905: */
906: public int insertAt(int lineNum, boolean modify, String src)
907: throws CannotCompileException {
908: CodeAttribute ca = methodInfo.getCodeAttribute();
909: if (ca == null)
910: throw new CannotCompileException("no method body");
911:
912: LineNumberAttribute ainfo = (LineNumberAttribute) ca
913: .getAttribute(LineNumberAttribute.tag);
914: if (ainfo == null)
915: throw new CannotCompileException("no line number info");
916:
917: LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum);
918: lineNum = pc.line;
919: int index = pc.index;
920: if (!modify)
921: return lineNum;
922:
923: declaringClass.checkModify();
924: CodeIterator iterator = ca.iterator();
925: Javac jv = new Javac(declaringClass);
926: try {
927: jv.recordLocalVariables(ca, index);
928: jv.recordParams(getParameterTypes(), Modifier
929: .isStatic(getModifiers()));
930: jv.setMaxLocals(ca.getMaxLocals());
931: jv.compileStmnt(src);
932: Bytecode b = jv.getBytecode();
933: int locals = b.getMaxLocals();
934: int stack = b.getMaxStack();
935: ca.setMaxLocals(locals);
936:
937: /* We assume that there is no values in the operand stack
938: * at the position where the bytecode is inserted.
939: */
940: if (stack > ca.getMaxStack())
941: ca.setMaxStack(stack);
942:
943: iterator.insert(index, b.get());
944: iterator.insert(b.getExceptionTable(), index);
945: return lineNum;
946: } catch (NotFoundException e) {
947: throw new CannotCompileException(e);
948: } catch (CompileError e) {
949: throw new CannotCompileException(e);
950: } catch (BadBytecode e) {
951: throw new CannotCompileException(e);
952: }
953: }
954: }
|