All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.lib.javassist.CtBehavior Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
/*
 * 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 com.feilong.lib.javassist;

import com.feilong.lib.javassist.bytecode.AccessFlag;
import com.feilong.lib.javassist.bytecode.AnnotationsAttribute;
import com.feilong.lib.javassist.bytecode.AttributeInfo;
import com.feilong.lib.javassist.bytecode.BadBytecode;
import com.feilong.lib.javassist.bytecode.Bytecode;
import com.feilong.lib.javassist.bytecode.CodeAttribute;
import com.feilong.lib.javassist.bytecode.CodeIterator;
import com.feilong.lib.javassist.bytecode.ConstPool;
import com.feilong.lib.javassist.bytecode.Descriptor;
import com.feilong.lib.javassist.bytecode.ExceptionsAttribute;
import com.feilong.lib.javassist.bytecode.LineNumberAttribute;
import com.feilong.lib.javassist.bytecode.LocalVariableAttribute;
import com.feilong.lib.javassist.bytecode.LocalVariableTypeAttribute;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.bytecode.Opcode;
import com.feilong.lib.javassist.bytecode.ParameterAnnotationsAttribute;
import com.feilong.lib.javassist.bytecode.SignatureAttribute;
import com.feilong.lib.javassist.bytecode.StackMap;
import com.feilong.lib.javassist.bytecode.StackMapTable;
import com.feilong.lib.javassist.compiler.CompileError;
import com.feilong.lib.javassist.compiler.Javac;
import com.feilong.lib.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); } } @Override 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 com.feilong.lib.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 */ @Override 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 */ @Override 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 */ @Override 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 */ @Override 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 */ @Override 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 */ @Override 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 com.feilong.lib.javassist.bytecode.Descriptor * @see #getGenericSignature() */ @Override 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 */ @Override 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 com.feilong.lib.javassist.CtClass#setGenericSignature(String)} * for a code sample. * * @param sig * a new generic signature. * @see com.feilong.lib.javassist.bytecode.SignatureAttribute.MethodSignature#encode() * @since 3.17 */ @Override 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 com.feilong.lib.javassist.bytecode.AttributeInfo}. * * @param name * attribute name */ @Override public byte[] getAttribute(String name){ AttributeInfo ai = methodInfo.getAttribute(name); if (ai == null){ return null; } 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 com.feilong.lib.javassist.bytecode.AttributeInfo}. * * @param name * attribute name * @param data * attribute value */ @Override 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 com.feilong.lib.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(com.feilong.lib.javassist.runtime.Cflow.class.getName()); 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 com.feilong.lib.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); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy