📄 ctbehavior.java
字号:
throws CannotCompileException { declaringClass.checkModify(); try { Javac jv = new Javac(declaringClass); if (delegateMethod != null) jv.recordProceed(delegateObj, delegateMethod); Bytecode b = jv.compileBody(this, src); methodInfo.setCodeAttribute(b.toCodeAttribute()); methodInfo.setAccessFlags(methodInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); } catch (CompileError 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); } } catch (CodeAttribute.RuntimeCopyException e) { /* the exception may be thrown by copy() in CodeAttribute. */ throw new CannotCompileException(e); } destInfo.setAccessFlags(destInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); } /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * * <p>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. * * <p>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 <code>$cflow</code> for this method/constructor. * If <code>$cflow</code> is used, the class files modified * with Javassist requires a support class * <code>javassist.runtime.Cflow</code> at runtime * (other Javassist classes are not required at runtime). * * <p>Every <code>$cflow</code> variable is given a unique name. * For example, if the given name is <code>"Point.paint"</code>, * then the variable is indicated by <code>$cflow(Point.paint)</code>. * * @param name <code>$cflow</code> name. It can include * alphabets, numbers, <code>_</code>, * <code>$</code>, and <code>.</code> (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();"); 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 <code>insertBefore()</code>, <code>insertAfter()</code>, etc. * * <p>If the second parameter <code>asFinally</code> to * <code>insertAfter()</code> is true, the declared local variable * is not visible from the code inserted by <code>insertAfter()</code>. * * @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)); } /** * 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. * * @param editor specifies how to modify. */ 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. * * <p>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 <code>insertBeforeBody()</code> in <code>CtConstructor</code>. * * @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 { declaringClass.checkModify(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); CodeIterator iterator = ca.iterator(); Javac jv = new Javac(declaringClass); try { int nvars = jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.recordParamNames(ca, nvars); jv.recordLocalVariables(ca, 0); 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); } 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 { declaringClass.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, declaringClass); 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); int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo); byte[] save = makeSaveCode(pool, rtype, varNo); byte[] restore = makeRestoreCode(b, pool, rtype, varNo); b.addAstore(retAddr); jv.compileStmnt(src); b.addRet(retAddr); ca.setMaxStack(b.getMaxStack()); ca.setMaxLocals(b.getMaxLocals()); int gapPos = iterator.append(b.get()); iterator.append(b.getExceptionTable(), gapPos); if (asFinally) ca.getExceptionTable().add(0, gapPos, gapPos, 0); int gapLen = iterator.getCodeLength() - gapPos - handlerLen; int subr = iterator.getCodeLength() - gapLen; while (iterator.hasNext()) { int pos = iterator.next(); if (pos >= subr) 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) { insertJSR(iterator, subr, pos, save, restore); subr = iterator.getCodeLength() - gapLen; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -