/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.compiler.Javac; import javassist.compiler.CompileError; import javassist.expr.ExprEditor; /** * CtBehavior represents a method, a constructor, * or a static constructor (class initializer). * It is the abstract super class of * CtMethod and CtConstructor. * *

To directly read or modify bytecode, obtain MethodInfo * objects. * * @see #getMethodInfo() */ public abstract class CtBehavior extends CtMember { protected MethodInfo methodInfo; protected CtBehavior(CtClass clazz, MethodInfo minfo) { super(clazz); methodInfo = minfo; } /** * @param isCons true if this is a constructor. */ void copy(CtBehavior src, boolean isCons, ClassMap map) throws CannotCompileException { CtClass declaring = declaringClass; MethodInfo srcInfo = src.methodInfo; CtClass srcClass = src.getDeclaringClass(); ConstPool cp = declaring.getClassFile2().getConstPool(); map = new ClassMap(map); map.put(srcClass.getName(), declaring.getName()); try { boolean patch = false; CtClass srcSuper = srcClass.getSuperclass(); CtClass destSuper = declaring.getSuperclass(); String destSuperName = null; if (srcSuper != null && destSuper != null) { String srcSuperName = srcSuper.getName(); destSuperName = destSuper.getName(); if (!srcSuperName.equals(destSuperName)) if (srcSuperName.equals(CtClass.javaLangObject)) patch = true; else map.putIfNone(srcSuperName, destSuperName); } // a stack map table is copied from srcInfo. methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); if (isCons && patch) methodInfo.setSuperclass(destSuperName); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } protected void extendToString(StringBuffer buffer) { buffer.append(' '); buffer.append(getName()); buffer.append(' '); buffer.append(methodInfo.getDescriptor()); } /** * Returns the method or constructor name followed by parameter types * such as javassist.CtBehavior.stBody(String). * * @since 3.5 */ public abstract String getLongName(); /** * Returns the MethodInfo representing this method/constructor in the * class file. * *

If you modify the bytecode through the returned * MethodInfo object, you might have to explicitly * rebuild a stack map table. Javassist does not automatically * rebuild it for avoiding unnecessary rebuilding. * * @see javassist.bytecode.MethodInfo#rebuildStackMap(ClassPool) */ public MethodInfo getMethodInfo() { declaringClass.checkModify(); return methodInfo; } /** * Returns the MethodInfo representing the method/constructor in the * class file (read only). * Normal applications do not need calling this method. Use * getMethodInfo(). * *

The MethodInfo object obtained by this method * is read only. Changes to this object might not be reflected * on a class file generated by toBytecode(), * toClass(), etc in CtClass. * *

This method is available even if the CtClass * containing this method is frozen. However, if the class is * frozen, the MethodInfo might be also pruned. * * @see #getMethodInfo() * @see CtClass#isFrozen() * @see CtClass#prune() */ public MethodInfo getMethodInfo2() { return methodInfo; } /** * Obtains the modifiers of the method/constructor. * * @return modifiers encoded with * javassist.Modifier. * @see Modifier */ public int getModifiers() { return AccessFlag.toModifier(methodInfo.getAccessFlags()); } /** * Sets the encoded modifiers of the method/constructor. * *

Changing the modifiers may cause a problem. * For example, if a non-static method is changed to static, * the method will be rejected by the bytecode verifier. * * @see Modifier */ public void setModifiers(int mod) { declaringClass.checkModify(); methodInfo.setAccessFlags(AccessFlag.of(mod)); } /** * Returns true if the class has the specified annotation type. * * @param typeName the name of annotation type. * @return true if the annotation is found, * otherwise false. * @since 3.21 */ public boolean hasAnnotation(String typeName) { MethodInfo mi = getMethodInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.hasAnnotationType(typeName, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotation if the class has the specified annotation class. * For example, if an annotation @Author is associated * with this method/constructor, an Author object is returned. * The member values can be obtained by calling methods on * the Author object. * * @param clz the annotation class. * @return the annotation if found, otherwise null. * @since 3.11 */ public Object getAnnotation(Class clz) throws ClassNotFoundException { MethodInfo mi = getMethodInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotations associated with this method or constructor. * * @return an array of annotation-type objects. * @see #getAvailableAnnotations() * @since 3.1 */ public Object[] getAnnotations() throws ClassNotFoundException { return getAnnotations(false); } /** * Returns the annotations associated with this method or constructor. * If any annotations are not on the classpath, they are not included * in the returned array. * * @return an array of annotation-type objects. * @see #getAnnotations() * @since 3.3 */ public Object[] getAvailableAnnotations(){ try{ return getAnnotations(true); } catch (ClassNotFoundException e){ throw new RuntimeException("Unexpected exception", e); } } private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { MethodInfo mi = getMethodInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the parameter annotations associated with this method or constructor. * * @return an array of annotation-type objects. The length of the returned array is * equal to the number of the formal parameters. If each parameter has no * annotation, the elements of the returned array are empty arrays. * * @see #getAvailableParameterAnnotations() * @see #getAnnotations() * @since 3.1 */ public Object[][] getParameterAnnotations() throws ClassNotFoundException { return getParameterAnnotations(false); } /** * Returns the parameter annotations associated with this method or constructor. * If any annotations are not on the classpath, they are not included in the * returned array. * * @return an array of annotation-type objects. The length of the returned array is * equal to the number of the formal parameters. If each parameter has no * annotation, the elements of the returned array are empty arrays. * * @see #getParameterAnnotations() * @see #getAvailableAnnotations() * @since 3.3 */ public Object[][] getAvailableParameterAnnotations(){ try { return getParameterAnnotations(true); } catch(ClassNotFoundException e) { throw new RuntimeException("Unexpected exception", e); } } Object[][] getParameterAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { MethodInfo mi = getMethodInfo2(); ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute) mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag); ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute) mi.getAttribute(ParameterAnnotationsAttribute.visibleTag); return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), ainfo, ainfo2, mi); } /** * Obtains parameter types of this method/constructor. */ public CtClass[] getParameterTypes() throws NotFoundException { return Descriptor.getParameterTypes(methodInfo.getDescriptor(), declaringClass.getClassPool()); } /** * Obtains the type of the returned value. */ CtClass getReturnType0() throws NotFoundException { return Descriptor.getReturnType(methodInfo.getDescriptor(), declaringClass.getClassPool()); } /** * Returns the method signature (the parameter types * and the return type). * The method signature is represented by a character string * called method descriptor, which is defined in the JVM specification. * If two methods/constructors have * the same parameter types * and the return type, getSignature() returns the * same string (the return type of constructors is void). * *

Note that the returned string is not the type signature * contained in the SignatureAttirbute. It is * a descriptor. * * @see javassist.bytecode.Descriptor * @see #getGenericSignature() */ public String getSignature() { return methodInfo.getDescriptor(); } /** * Returns the generic signature of the method. * It represents parameter types including type variables. * * @see SignatureAttribute#toMethodSignature(String) * @since 3.17 */ public String getGenericSignature() { SignatureAttribute sa = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag); return sa == null ? null : sa.getSignature(); } /** * Set the generic signature of the method. * It represents parameter types including type variables. * See {@link javassist.CtClass#setGenericSignature(String)} * for a code sample. * * @param sig a new generic signature. * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode() * @since 3.17 */ public void setGenericSignature(String sig) { declaringClass.checkModify(); methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig)); } /** * Obtains exceptions that this method/constructor may throw. * * @return a zero-length array if there is no throws clause. */ public CtClass[] getExceptionTypes() throws NotFoundException { String[] exceptions; ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); if (ea == null) exceptions = null; else exceptions = ea.getExceptions(); return declaringClass.getClassPool().get(exceptions); } /** * Sets exceptions that this method/constructor may throw. */ public void setExceptionTypes(CtClass[] types) throws NotFoundException { declaringClass.checkModify(); if (types == null || types.length == 0) { methodInfo.removeExceptionsAttribute(); return; } String[] names = new String[types.length]; for (int i = 0; i < types.length; ++i) names[i] = types[i].getName(); ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); if (ea == null) { ea = new ExceptionsAttribute(methodInfo.getConstPool()); methodInfo.setExceptionsAttribute(ea); } ea.setExceptions(names); } /** * Returns true if the body is empty. */ public abstract boolean isEmpty(); /** * Sets a method/constructor body. * * @param src the source code representing the body. * It must be a single statement or block. * If it is null, the substituted * body does nothing except returning zero or null. */ public void setBody(String src) throws CannotCompileException { setBody(src, null, null); } /** * Sets a method/constructor body. * * @param src the source code representing the body. * It must be a single statement or block. * If it is null, the substituted * body does nothing except returning zero or null. * @param delegateObj the source text specifying the object * that is called on by $proceed(). * @param delegateMethod the name of the method * that is called by $proceed(). */ public void setBody(String src, String delegateObj, String delegateMethod) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); try { Javac jv = new Javac(cc); if (delegateMethod != null) jv.recordProceed(delegateObj, delegateMethod); Bytecode b = jv.compileBody(this, src); methodInfo.setCodeAttribute(b.toCodeAttribute()); methodInfo.setAccessFlags(methodInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); declaringClass.rebuildClassFile(); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } static void setBody0(CtClass srcClass, MethodInfo srcInfo, CtClass destClass, MethodInfo destInfo, ClassMap map) throws CannotCompileException { destClass.checkModify(); map = new ClassMap(map); map.put(srcClass.getName(), destClass.getName()); try { CodeAttribute cattr = srcInfo.getCodeAttribute(); if (cattr != null) { ConstPool cp = destInfo.getConstPool(); CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map); destInfo.setCodeAttribute(ca); // a stack map table is copied to destInfo. } } catch (CodeAttribute.RuntimeCopyException e) { /* the exception may be thrown by copy() in CodeAttribute. */ throw new CannotCompileException(e); } destInfo.setAccessFlags(destInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); destClass.rebuildClassFile(); } /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * *

Note that an attribute is a data block specified by * the class file format. It is not an annotation. * See {@link javassist.bytecode.AttributeInfo}. * * @param name attribute name */ public byte[] getAttribute(String name) { AttributeInfo ai = methodInfo.getAttribute(name); if (ai == null) return null; else return ai.get(); } /** * Adds an attribute. The attribute is saved in the class file. * *

Note that an attribute is a data block specified by * the class file format. It is not an annotation. * See {@link javassist.bytecode.AttributeInfo}. * * @param name attribute name * @param data attribute value */ public void setAttribute(String name, byte[] data) { declaringClass.checkModify(); methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(), name, data)); } /** * Declares to use $cflow for this method/constructor. * If $cflow is used, the class files modified * with Javassist requires a support class * javassist.runtime.Cflow at runtime * (other Javassist classes are not required at runtime). * *

Every $cflow variable is given a unique name. * For example, if the given name is "Point.paint", * then the variable is indicated by $cflow(Point.paint). * * @param name $cflow name. It can include * alphabets, numbers, _, * $, and . (dot). * * @see javassist.runtime.Cflow */ public void useCflow(String name) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); ClassPool pool = cc.getClassPool(); String fname; int i = 0; while (true) { fname = "_cflow$" + i++; try { cc.getDeclaredField(fname); } catch(NotFoundException e) { break; } } pool.recordCflow(name, declaringClass.getName(), fname); try { CtClass type = pool.get("javassist.runtime.Cflow"); CtField field = new CtField(type, fname, cc); field.setModifiers(Modifier.PUBLIC | Modifier.STATIC); cc.addField(field, CtField.Initializer.byNew(type)); insertBefore(fname + ".enter();", false); String src = fname + ".exit();"; insertAfter(src, true); } catch (NotFoundException e) { throw new CannotCompileException(e); } } /** * Declares a new local variable. The scope of this variable is the * whole method body. The initial value of that variable is not set. * The declared variable can be accessed in the code snippet inserted * by insertBefore(), insertAfter(), etc. * *

If the second parameter asFinally to * insertAfter() is true, the declared local variable * is not visible from the code inserted by insertAfter(). * * @param name the name of the variable * @param type the type of the variable * @see #insertBefore(String) * @see #insertAfter(String) */ public void addLocalVariable(String name, CtClass type) throws CannotCompileException { declaringClass.checkModify(); ConstPool cp = methodInfo.getConstPool(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( LocalVariableAttribute.tag); if (va == null) { va = new LocalVariableAttribute(cp); ca.getAttributes().add(va); } int maxLocals = ca.getMaxLocals(); String desc = Descriptor.of(type); va.addEntry(0, ca.getCodeLength(), cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals); ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc)); } /** * Inserts a new parameter, which becomes the first parameter. */ public void insertParameter(CtClass type) throws CannotCompileException { declaringClass.checkModify(); String desc = methodInfo.getDescriptor(); String desc2 = Descriptor.insertParameter(type, desc); try { addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc); } catch (BadBytecode e) { throw new CannotCompileException(e); } methodInfo.setDescriptor(desc2); } /** * Appends a new parameter, which becomes the last parameter. */ public void addParameter(CtClass type) throws CannotCompileException { declaringClass.checkModify(); String desc = methodInfo.getDescriptor(); String desc2 = Descriptor.appendParameter(type, desc); int offset = Modifier.isStatic(getModifiers()) ? 0 : 1; try { addParameter2(offset + Descriptor.paramSize(desc), type, desc); } catch (BadBytecode e) { throw new CannotCompileException(e); } methodInfo.setDescriptor(desc2); } private void addParameter2(int where, CtClass type, String desc) throws BadBytecode { CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca != null) { int size = 1; char typeDesc = 'L'; int classInfo = 0; if (type.isPrimitive()) { CtPrimitiveType cpt = (CtPrimitiveType)type; size = cpt.getDataSize(); typeDesc = cpt.getDescriptor(); } else classInfo = methodInfo.getConstPool().addClassInfo(type); ca.insertLocalVar(where, size); LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag); if (va != null) va.shiftIndex(where, size); LocalVariableTypeAttribute lvta = (LocalVariableTypeAttribute)ca.getAttribute(LocalVariableTypeAttribute.tag); if (lvta != null) lvta.shiftIndex(where, size); StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); if (smt != null) smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); if (sm != null) sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); } } /** * Modifies the method/constructor body. * * @param converter specifies how to modify. */ public void instrument(CodeConverter converter) throws CannotCompileException { declaringClass.checkModify(); ConstPool cp = methodInfo.getConstPool(); converter.doit(getDeclaringClass(), methodInfo, cp); } /** * Modifies the method/constructor body. * *

While executing this method, only replace() * in Expr is available for bytecode modification. * Other methods such as insertBefore() may collapse * the bytecode because the ExprEditor loses * its current position. * * @param editor specifies how to modify. * @see javassist.expr.Expr#replace(String) * @see #insertBefore(String) */ public void instrument(ExprEditor editor) throws CannotCompileException { // if the class is not frozen, // does not turn the modified flag on. if (declaringClass.isFrozen()) declaringClass.checkModify(); if (editor.doit(declaringClass, methodInfo)) declaringClass.checkModify(); } /** * Inserts bytecode at the beginning of the body. * *

If this object represents a constructor, * the bytecode is inserted before * a constructor in the super class or this class is called. * Therefore, the inserted bytecode is subject to constraints described * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed). * For example, it cannot access instance fields or methods although * it may assign a value to an instance field directly declared in this * class. Accessing static fields and methods is allowed. * Use insertBeforeBody() in CtConstructor. * * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * @see CtConstructor#insertBeforeBody(String) */ public void insertBefore(String src) throws CannotCompileException { insertBefore(src, true); } private void insertBefore(String src, boolean rebuild) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); CodeIterator iterator = ca.iterator(); Javac jv = new Javac(cc); try { int nvars = jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.recordParamNames(ca, nvars); jv.recordLocalVariables(ca, 0); jv.recordType(getReturnType0()); jv.compileStmnt(src); Bytecode b = jv.getBytecode(); int stack = b.getMaxStack(); int locals = b.getMaxLocals(); if (stack > ca.getMaxStack()) ca.setMaxStack(stack); if (locals > ca.getMaxLocals()) ca.setMaxLocals(locals); int pos = iterator.insertEx(b.get()); iterator.insert(b.getExceptionTable(), pos); if (rebuild) methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } /** * Inserts bytecode at the end of the body. * The bytecode is inserted just before every return insturction. * It is not executed when an exception is thrown. * * @param src the source code representing the inserted bytecode. * It must be a single statement or block. */ public void insertAfter(String src) throws CannotCompileException { insertAfter(src, false); } /** * Inserts bytecode at the end of the body. * The bytecode is inserted just before every return insturction. * * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * @param asFinally true if the inserted bytecode is executed * not only when the control normally returns * but also when an exception is thrown. * If this parameter is true, the inserted code cannot * access local variables. */ public void insertAfter(String src, boolean asFinally) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); ConstPool pool = methodInfo.getConstPool(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); CodeIterator iterator = ca.iterator(); int retAddr = ca.getMaxLocals(); Bytecode b = new Bytecode(pool, 0, retAddr + 1); b.setStackDepth(ca.getMaxStack() + 1); Javac jv = new Javac(b, cc); try { int nvars = jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.recordParamNames(ca, nvars); CtClass rtype = getReturnType0(); int varNo = jv.recordReturnType(rtype, true); jv.recordLocalVariables(ca, 0); // finally clause for exceptions int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo, jv, src); int handlerPos = iterator.getCodeLength(); if (asFinally) ca.getExceptionTable().add(getStartPosOfBody(ca), handlerPos, handlerPos, 0); int adviceLen = 0; int advicePos = 0; boolean noReturn = true; while (iterator.hasNext()) { int pos = iterator.next(); if (pos >= handlerPos) break; int c = iterator.byteAt(pos); if (c == Opcode.ARETURN || c == Opcode.IRETURN || c == Opcode.FRETURN || c == Opcode.LRETURN || c == Opcode.DRETURN || c == Opcode.RETURN) { if (noReturn) { // finally clause for normal termination adviceLen = insertAfterAdvice(b, jv, src, pool, rtype, varNo); handlerPos = iterator.append(b.get()); iterator.append(b.getExceptionTable(), handlerPos); advicePos = iterator.getCodeLength() - adviceLen; handlerLen = advicePos - handlerPos; noReturn = false; } insertGoto(iterator, advicePos, pos); advicePos = iterator.getCodeLength() - adviceLen; handlerPos = advicePos - handlerLen; } } if (noReturn) { handlerPos = iterator.append(b.get()); iterator.append(b.getExceptionTable(), handlerPos); } ca.setMaxStack(b.getMaxStack()); ca.setMaxLocals(b.getMaxLocals()); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } private int insertAfterAdvice(Bytecode code, Javac jv, String src, ConstPool cp, CtClass rtype, int varNo) throws CompileError { int pc = code.currentPc(); if (rtype == CtClass.voidType) { code.addOpcode(Opcode.ACONST_NULL); code.addAstore(varNo); jv.compileStmnt(src); code.addOpcode(Opcode.RETURN); if (code.getMaxLocals() < 1) code.setMaxLocals(1); } else { code.addStore(varNo, rtype); jv.compileStmnt(src); code.addLoad(varNo, rtype); if (rtype.isPrimitive()) code.addOpcode(((CtPrimitiveType)rtype).getReturnOp()); else code.addOpcode(Opcode.ARETURN); } return code.currentPc() - pc; } /* * assert subr > pos */ private void insertGoto(CodeIterator iterator, int subr, int pos) throws BadBytecode { iterator.setMark(subr); // the gap length might be a multiple of 4. iterator.writeByte(Opcode.NOP, pos); boolean wide = subr + 2 - pos > Short.MAX_VALUE; int len = wide ? 4 : 2; CodeIterator.Gap gap = iterator.insertGapAt(pos, len, false); pos = gap.position + gap.length - len; int offset = iterator.getMark() - pos; if (wide) { iterator.writeByte(Opcode.GOTO_W, pos); iterator.write32bit(offset, pos + 1); } else if (offset <= Short.MAX_VALUE) { iterator.writeByte(Opcode.GOTO, pos); iterator.write16bit(offset, pos + 1); } else { if (gap.length < 4) { CodeIterator.Gap gap2 = iterator.insertGapAt(gap.position, 2, false); pos = gap2.position + gap2.length + gap.length - 4; } iterator.writeByte(Opcode.GOTO_W, pos); iterator.write32bit(iterator.getMark() - pos, pos + 1); } } /* insert a finally clause */ private int insertAfterHandler(boolean asFinally, Bytecode b, CtClass rtype, int returnVarNo, Javac javac, String src) throws CompileError { if (!asFinally) return 0; int var = b.getMaxLocals(); b.incMaxLocals(1); int pc = b.currentPc(); b.addAstore(var); // store an exception if (rtype.isPrimitive()) { char c = ((CtPrimitiveType)rtype).getDescriptor(); if (c == 'D') { b.addDconst(0.0); b.addDstore(returnVarNo); } else if (c == 'F') { b.addFconst(0); b.addFstore(returnVarNo); } else if (c == 'J') { b.addLconst(0); b.addLstore(returnVarNo); } else if (c == 'V') { b.addOpcode(Opcode.ACONST_NULL); b.addAstore(returnVarNo); } else { // int, boolean, char, short, ... b.addIconst(0); b.addIstore(returnVarNo); } } else { b.addOpcode(Opcode.ACONST_NULL); b.addAstore(returnVarNo); } javac.compileStmnt(src); b.addAload(var); b.addOpcode(Opcode.ATHROW); return b.currentPc() - pc; } /* -- OLD version -- public void insertAfter(String src) throws CannotCompileException { declaringClass.checkModify(); CodeAttribute ca = methodInfo.getCodeAttribute(); CodeIterator iterator = ca.iterator(); Bytecode b = new Bytecode(methodInfo.getConstPool(), ca.getMaxStack(), ca.getMaxLocals()); b.setStackDepth(ca.getMaxStack()); Javac jv = new Javac(b, declaringClass); try { jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); CtClass rtype = getReturnType0(); int varNo = jv.recordReturnType(rtype, true); boolean isVoid = rtype == CtClass.voidType; if (isVoid) { b.addOpcode(Opcode.ACONST_NULL); b.addAstore(varNo); jv.compileStmnt(src); } else { b.addStore(varNo, rtype); jv.compileStmnt(src); b.addLoad(varNo, rtype); } byte[] code = b.get(); ca.setMaxStack(b.getMaxStack()); ca.setMaxLocals(b.getMaxLocals()); while (iterator.hasNext()) { int pos = iterator.next(); int c = iterator.byteAt(pos); if (c == Opcode.ARETURN || c == Opcode.IRETURN || c == Opcode.FRETURN || c == Opcode.LRETURN || c == Opcode.DRETURN || c == Opcode.RETURN) iterator.insert(pos, code); } } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } */ /** * Adds a catch clause that handles an exception thrown in the * body. The catch clause must end with a return or throw statement. * * @param src the source code representing the catch clause. * It must be a single statement or block. * @param exceptionType the type of the exception handled by the * catch clause. */ public void addCatch(String src, CtClass exceptionType) throws CannotCompileException { addCatch(src, exceptionType, "$e"); } /** * Adds a catch clause that handles an exception thrown in the * body. The catch clause must end with a return or throw statement. * * @param src the source code representing the catch clause. * It must be a single statement or block. * @param exceptionType the type of the exception handled by the * catch clause. * @param exceptionName the name of the variable containing the * caught exception, for example, * $e. */ public void addCatch(String src, CtClass exceptionType, String exceptionName) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); ConstPool cp = methodInfo.getConstPool(); CodeAttribute ca = methodInfo.getCodeAttribute(); CodeIterator iterator = ca.iterator(); Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals()); b.setStackDepth(1); Javac jv = new Javac(b, cc); try { jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); int var = jv.recordVariable(exceptionType, exceptionName); b.addAstore(var); jv.compileStmnt(src); int stack = b.getMaxStack(); int locals = b.getMaxLocals(); if (stack > ca.getMaxStack()) ca.setMaxStack(stack); if (locals > ca.getMaxLocals()) ca.setMaxLocals(locals); int len = iterator.getCodeLength(); int pos = iterator.append(b.get()); ca.getExceptionTable().add(getStartPosOfBody(ca), len, len, cp.addClassInfo(exceptionType)); iterator.append(b.getExceptionTable(), pos); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } /* CtConstructor overrides this method. */ int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { return 0; } /** * Inserts bytecode at the specified line in the body. * It is equivalent to: * *
insertAt(lineNum, true, src) * *
See this method as well. * * @param lineNum the line number. The bytecode is inserted at the * beginning of the code at the line specified by this * line number. * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * @return the line number at which the bytecode has been inserted. * * @see CtBehavior#insertAt(int,boolean,String) */ public int insertAt(int lineNum, String src) throws CannotCompileException { return insertAt(lineNum, true, src); } /** * Inserts bytecode at the specified line in the body. * *

If there is not * a statement at the specified line, the bytecode might be inserted * at the line including the first statement after that line specified. * For example, if there is only a closing brace at that line, the * bytecode would be inserted at another line below. * To know exactly where the bytecode will be inserted, call with * modify set to false. * * @param lineNum the line number. The bytecode is inserted at the * beginning of the code at the line specified by this * line number. * @param modify if false, this method does not insert the bytecode. * It instead only returns the line number at which * the bytecode would be inserted. * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * If modify is false, the value of src can be null. * @return the line number at which the bytecode has been inserted. */ public int insertAt(int lineNum, boolean modify, String src) throws CannotCompileException { CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); LineNumberAttribute ainfo = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); if (ainfo == null) throw new CannotCompileException("no line number info"); LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum); lineNum = pc.line; int index = pc.index; if (!modify) return lineNum; CtClass cc = declaringClass; cc.checkModify(); CodeIterator iterator = ca.iterator(); Javac jv = new Javac(cc); try { jv.recordLocalVariables(ca, index); jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.setMaxLocals(ca.getMaxLocals()); jv.compileStmnt(src); Bytecode b = jv.getBytecode(); int locals = b.getMaxLocals(); int stack = b.getMaxStack(); ca.setMaxLocals(locals); /* We assume that there is no values in the operand stack * at the position where the bytecode is inserted. */ if (stack > ca.getMaxStack()) ca.setMaxStack(stack); index = iterator.insertAt(index, b.get()); iterator.insert(b.getExceptionTable(), index); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); return lineNum; } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } }