/* * 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.bytecode; import javassist.*; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Code_attribute. * *

To browse the code field of * a Code_attribute structure, * use CodeIterator. * * @see CodeIterator * @see #iterator() */ public class CodeAttribute extends AttributeInfo implements Opcode { /** * The name of this attribute "Code". */ public static final String tag = "Code"; // code[] is stored in AttributeInfo.info. private int maxStack; private int maxLocals; private ExceptionTable exceptions; private List attributes; /** * Constructs a Code_attribute. * * @param cp constant pool table * @param stack max_stack * @param locals max_locals * @param code code[] * @param etable exception_table[] */ public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code, ExceptionTable etable) { super(cp, tag); maxStack = stack; maxLocals = locals; info = code; exceptions = etable; attributes = new ArrayList(); } /** * Constructs a copy of Code_attribute. * Specified class names are replaced during the copy. * * @param cp constant pool table. * @param src source Code attribute. * @param classnames pairs of replaced and substituted * class names. */ private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames) throws BadBytecode, NotFoundException, CannotCompileException { super(cp, tag); maxStack = src.getMaxStack(); maxLocals = src.getMaxLocals(); exceptions = src.getExceptionTable().copy(cp, classnames); attributes = new ArrayList(); List src_attr = src.getAttributes(); int num = src_attr.size(); for (int i = 0; i < num; ++i) { AttributeInfo ai = src_attr.get(i); attributes.add(ai.copy(cp, classnames)); } info = src.copyCode(cp, classnames, exceptions, this); } CodeAttribute(ConstPool cp, int name_id, DataInputStream in) throws IOException { super(cp, name_id, (byte[])null); @SuppressWarnings("unused") int attr_len = in.readInt(); maxStack = in.readUnsignedShort(); maxLocals = in.readUnsignedShort(); int code_len = in.readInt(); info = new byte[code_len]; in.readFully(info); exceptions = new ExceptionTable(cp, in); attributes = new ArrayList(); int num = in.readUnsignedShort(); for (int i = 0; i < num; ++i) attributes.add(AttributeInfo.read(cp, in)); } /** * Makes a copy. Class names are replaced according to the * given Map object. * * @param newCp the constant pool table used by the new copy. * @param classnames pairs of replaced and substituted * class names. * @exception RuntimeCopyException if a BadBytecode * exception is thrown, it is * converted into * RuntimeCopyException. * * @return CodeAttribute object. */ @Override public AttributeInfo copy(ConstPool newCp, Map classnames) throws RuntimeCopyException { try { return new CodeAttribute(newCp, this, classnames); } catch (BadBytecode e) { throw new RuntimeCopyException("bad bytecode. fatal?"); } catch (NotFoundException e) { throw new RuntimeException(e); } catch (CannotCompileException e) { throw new RuntimeException(e); } } /** * An exception that may be thrown by copy() * in CodeAttribute. */ public static class RuntimeCopyException extends RuntimeException { /** default serialVersionUID */ private static final long serialVersionUID = 1L; /** * Constructs an exception. */ public RuntimeCopyException(String s) { super(s); } } /** * Returns the length of this attribute_info * structure. * The returned value is attribute_length + 6. */ @Override public int length() { return 18 + info.length + exceptions.size() * 8 + AttributeInfo.getLength(attributes); } @Override void write(DataOutputStream out) throws IOException { out.writeShort(name); // attribute_name_index out.writeInt(length() - 6); // attribute_length out.writeShort(maxStack); // max_stack out.writeShort(maxLocals); // max_locals out.writeInt(info.length); // code_length out.write(info); // code exceptions.write(out); out.writeShort(attributes.size()); // attributes_count AttributeInfo.writeAll(attributes, out); // attributes } /** * This method is not available. * * @throws java.lang.UnsupportedOperationException always thrown. */ @Override public byte[] get() { throw new UnsupportedOperationException("CodeAttribute.get()"); } /** * This method is not available. * * @throws java.lang.UnsupportedOperationException always thrown. */ @Override public void set(byte[] newinfo) { throw new UnsupportedOperationException("CodeAttribute.set()"); } @Override void renameClass(String oldname, String newname) { AttributeInfo.renameClass(attributes, oldname, newname); } @Override void renameClass(Map classnames) { AttributeInfo.renameClass(attributes, classnames); } @Override void getRefClasses(Map classnames) { AttributeInfo.getRefClasses(attributes, classnames); } /** * Returns the name of the class declaring the method including * this code attribute. */ public String getDeclaringClass() { ConstPool cp = getConstPool(); return cp.getClassName(); } /** * Returns max_stack. */ public int getMaxStack() { return maxStack; } /** * Sets max_stack. */ public void setMaxStack(int value) { maxStack = value; } /** * Computes the maximum stack size and sets max_stack * to the computed size. * * @throws BadBytecode if this method fails in computing. * @return the newly computed value of max_stack */ public int computeMaxStack() throws BadBytecode { maxStack = new CodeAnalyzer(this).computeMaxStack(); return maxStack; } /** * Returns max_locals. */ public int getMaxLocals() { return maxLocals; } /** * Sets max_locals. */ public void setMaxLocals(int value) { maxLocals = value; } /** * Returns code_length. */ public int getCodeLength() { return info.length; } /** * Returns code[]. */ public byte[] getCode() { return info; } /** * Sets code[]. */ void setCode(byte[] newinfo) { super.set(newinfo); } /** * Makes a new iterator for reading this code attribute. */ public CodeIterator iterator() { return new CodeIterator(this); } /** * Returns exception_table[]. */ public ExceptionTable getExceptionTable() { return exceptions; } /** * Returns attributes[]. * It returns a list of AttributeInfo. * A new element can be added to the returned list * and an existing element can be removed from the list. * * @see AttributeInfo */ public List getAttributes() { return attributes; } /** * Returns the attribute with the specified name. * If it is not found, this method returns null. * * @param name attribute name * @return an AttributeInfo object or null. */ public AttributeInfo getAttribute(String name) { return AttributeInfo.lookup(attributes, name); } /** * Adds a stack map table. If another copy of stack map table * is already contained, the old one is removed. * * @param smt the stack map table added to this code attribute. * If it is null, a new stack map is not added. * Only the old stack map is removed. */ public void setAttribute(StackMapTable smt) { AttributeInfo.remove(attributes, StackMapTable.tag); if (smt != null) attributes.add(smt); } /** * Adds a stack map table for J2ME (CLDC). If another copy of stack map table * is already contained, the old one is removed. * * @param sm the stack map table added to this code attribute. * If it is null, a new stack map is not added. * Only the old stack map is removed. * @since 3.12 */ public void setAttribute(StackMap sm) { AttributeInfo.remove(attributes, StackMap.tag); if (sm != null) attributes.add(sm); } /** * Copies code. */ private byte[] copyCode(ConstPool destCp, Map classnames, ExceptionTable etable, CodeAttribute destCa) throws BadBytecode, NotFoundException, CannotCompileException { int len = getCodeLength(); byte[] newCode = new byte[len]; destCa.info = newCode; LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), newCode, destCp, classnames); return LdcEntry.doit(newCode, ldc, etable, destCa); } private static LdcEntry copyCode(byte[] code, int beginPos, int endPos, ConstPool srcCp, byte[] newcode, ConstPool destCp, Map classnameMap) throws BadBytecode, NotFoundException, CannotCompileException { int i2, index; LdcEntry ldcEntry = null; for (int i = beginPos; i < endPos; i = i2) { i2 = CodeIterator.nextOpcode(code, i); byte c = code[i]; newcode[i] = c; switch (c & 0xff) { case LDC_W : case LDC2_W : case GETSTATIC : case PUTSTATIC : case GETFIELD : case PUTFIELD : case INVOKEVIRTUAL : case INVOKESPECIAL : case INVOKESTATIC : case NEW : case ANEWARRAY : case CHECKCAST : case INSTANCEOF : copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); break; case LDC : index = code[i + 1] & 0xff; index = srcCp.copy(index, destCp, classnameMap); if (index < 0x100) newcode[i + 1] = (byte)index; else { newcode[i] = NOP; newcode[i + 1] = NOP; LdcEntry ldc = new LdcEntry(); ldc.where = i; ldc.index = index; ldc.next = ldcEntry; ldcEntry = ldc; } break; case INVOKEINTERFACE : copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); newcode[i + 3] = code[i + 3]; newcode[i + 4] = code[i + 4]; break; case INVOKEDYNAMIC : copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); copyBootstrapMethod(srcCp, destCp, i + 1, code, newcode, classnameMap); newcode[i + 3] = 0; newcode[i + 4] = 0; break; case MULTIANEWARRAY : copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); newcode[i + 3] = code[i + 3]; break; default : while (++i < i2) newcode[i] = code[i]; break; } } return ldcEntry; } /** * Copy the Bootstrap method of the specified index referenced in the source InvokeDynamic directive * to the specified index in the destination Boostrap Attribute.
* if the Bootstrap Attribute does not exist in the destination class, create a new Bootstrap Attribute;
* if the destination Bootstrap Method already exists at the specified index method, * the method at that position will be overwritten, otherwise it will be added * at the end of the destination Bootstrap method. * * @param srcCp the constant pool table of source * @param destCp the constant pool table of destination * @param codeIndex the index of the invoke dynamic first parameter in code array * @param srcCode the code array of source * @param newCode the code array of destination * @param classnameMap pairs of replaced and substituted class names. * * @throws NotFoundException this exception thrown when the class * cannot be found in the default ClassPool * @throws CannotCompileException this exception thrown from the method * {@link #copyInvokeStaticMethod(CtClass, ConstPool, * BootstrapMethodsAttribute.BootstrapMethod, CtClass, Map)} */ private static void copyBootstrapMethod(ConstPool srcCp, ConstPool destCp, int codeIndex, byte[] srcCode, byte[] newCode, Map classnameMap) throws NotFoundException, CannotCompileException { ClassPool classPool = ClassPool.getDefault(); CtClass srcCc = classPool.get(srcCp.getClassName()); CtClass destCc = classPool.get(destCp.getClassName()); ClassFile srcCf = srcCc.getClassFile(); ClassFile destCf = destCc.getClassFile(); BootstrapMethodsAttribute srcBma = (BootstrapMethodsAttribute) srcCf.getAttribute(BootstrapMethodsAttribute.tag); // if source class does not have bootstrap attribute then stop copy if (srcBma == null) { return; } BootstrapMethodsAttribute destBma = (BootstrapMethodsAttribute) destCf.getAttribute(BootstrapMethodsAttribute.tag); int srcCpIndex = ((srcCode[codeIndex] & 0xff) << 8) | (srcCode[codeIndex + 1] & 0xff); int destCpIndex = ((newCode[codeIndex] & 0xff) << 8) | (newCode[codeIndex + 1] & 0xff); int srcBmIndex = srcCp.getInvokeDynamicBootstrap(srcCpIndex); int destBmIndex = destCp.getInvokeDynamicBootstrap(destCpIndex); // if source class does not have bootstrap attribute, then create bootstrap attribute if (destBma == null) { destBma = new BootstrapMethodsAttribute(destCp, new BootstrapMethodsAttribute.BootstrapMethod[0]); destCf.addAttribute(destBma); } BootstrapMethodsAttribute.BootstrapMethod srcBm = srcBma.getMethods()[srcBmIndex]; destBma.addMethod(srcCp, srcBm, destBmIndex, classnameMap); copyInvokeStaticMethod(srcCc, srcCp, srcBm, destCc, classnameMap); } /** * Copy the static methods referenced by the bootstrap method in this class (such as some lambda methods).
* If the source method exists in the destination class, it will be ignored. * * @param srcCc source class * @param srcCp constant pool table of source class * @param srcBm source method to be copied * @param destCc destination class * @param classnameMap irs of replaced and substituted class names. * * @throws CannotCompileException thrown by {@link CtNewMethod#copy(CtMethod, CtClass, ClassMap)} * or{@link CtClass#addMethod(CtMethod)} */ private static void copyInvokeStaticMethod(CtClass srcCc, ConstPool srcCp, BootstrapMethodsAttribute.BootstrapMethod srcBm, CtClass destCc, Map classnameMap) throws CannotCompileException { for (int argument : srcBm.arguments) { ConstInfo constInfo = srcCp.getItem(argument); if (!(constInfo instanceof MethodHandleInfo)) continue; MethodHandleInfo methodHandleInfo = (MethodHandleInfo) constInfo; if (ConstPool.REF_invokeStatic != methodHandleInfo.refKind) continue; String methodRefClassName = srcCp.getMethodrefClassName(methodHandleInfo.refIndex); if (methodRefClassName == null || !methodRefClassName.equals(srcCc.getName())) continue; String staticMethodName = srcCp.getMethodrefName(methodHandleInfo.refIndex); String staticMethodSignature = srcCp.getMethodrefType(methodHandleInfo.refIndex); CtMethod srcMethod = getStaticCtMethod(srcCc, staticMethodName, staticMethodSignature); if (!checkStaticMethodExisted(destCc, staticMethodName, staticMethodSignature)) { ClassMap classMap = new ClassMap(); classMap.putAll(classnameMap); CtMethod ctMethod = CtNewMethod.copy(srcMethod, destCc, classMap); destCc.addMethod(ctMethod); } } } private static CtMethod getStaticCtMethod(CtClass ctClass, String staticMethodName, String staticMethodSignature) { CtMethod srcMethod = null; for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) { if (Modifier.isStatic(declaredMethod.getModifiers()) && declaredMethod.getName().equals(staticMethodName) && declaredMethod.getSignature().equals(staticMethodSignature)) { srcMethod = declaredMethod; break; } } if (srcMethod == null) { throw new RuntimeException("Can not found static method:" + staticMethodName); } return srcMethod; } private static boolean checkStaticMethodExisted(CtClass ctClass, String staticMethodName, String staticMethodSignature) { for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) { if (Modifier.isStatic(declaredMethod.getModifiers()) && declaredMethod.getName().equals(staticMethodName) && declaredMethod.getSignature().equals(staticMethodSignature)) { return true; } } return false; } private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp, byte[] newcode, ConstPool destCp, Map classnameMap) { int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff); index = srcCp.copy(index, destCp, classnameMap); newcode[i] = (byte)(index >> 8); newcode[i + 1] = (byte)index; } static class LdcEntry { LdcEntry next; int where; int index; static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, CodeAttribute ca) throws BadBytecode { if (ldc != null) code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc); /* The original code was the following: while (ldc != null) { int where = ldc.where; code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca); code[where] = (byte)Opcode.LDC_W; ByteArray.write16bit(ldc.index, code, where + 1); ldc = ldc.next; } But this code does not support a large method > 32KB. */ return code; } } /** * Changes the index numbers of the local variables * to append a new parameter. * This method does not update LocalVariableAttribute, * LocalVariableTypeAttribute, * StackMapTable, or StackMap. * These attributes must be explicitly updated. * * @param where the index of the new parameter. * @param size the type size of the new parameter (1 or 2). * * @see LocalVariableAttribute#shiftIndex(int, int) * @see LocalVariableTypeAttribute#shiftIndex(int, int) * @see StackMapTable#insertLocal(int, int, int) * @see StackMap#insertLocal(int, int, int) */ public void insertLocalVar(int where, int size) throws BadBytecode { CodeIterator ci = iterator(); while (ci.hasNext()) shiftIndex(ci, where, size); setMaxLocals(getMaxLocals() + size); } /** * @param lessThan If the index of the local variable is * less than this value, it does not change. * Otherwise, the index is increased. * @param delta the indexes of the local variables are * increased by this value. */ private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { int index = ci.next(); int opcode = ci.byteAt(index); if (opcode < ILOAD) return; else if (opcode < IASTORE) { if (opcode < ILOAD_0) { // iload, lload, fload, dload, aload shiftIndex8(ci, index, opcode, lessThan, delta); } else if (opcode < IALOAD) { // iload_0, ..., aload_3 shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); } else if (opcode < ISTORE) return; else if (opcode < ISTORE_0) { // istore, lstore, ... shiftIndex8(ci, index, opcode, lessThan, delta); } else { // istore_0, ..., astore_3 shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); } } else if (opcode == IINC) { int var = ci.byteAt(index + 1); if (var < lessThan) return; var += delta; if (var < 0x100) ci.writeByte(var, index + 1); else { int plus = (byte)ci.byteAt(index + 2); int pos = ci.insertExGap(3); ci.writeByte(WIDE, pos - 3); ci.writeByte(IINC, pos - 2); ci.write16bit(var, pos - 1); ci.write16bit(plus, pos + 1); } } else if (opcode == RET) shiftIndex8(ci, index, opcode, lessThan, delta); else if (opcode == WIDE) { int var = ci.u16bitAt(index + 2); if (var < lessThan) return; var += delta; ci.write16bit(var, index + 2); } } private static void shiftIndex8(CodeIterator ci, int index, int opcode, int lessThan, int delta) throws BadBytecode { int var = ci.byteAt(index + 1); if (var < lessThan) return; var += delta; if (var < 0x100) ci.writeByte(var, index + 1); else { int pos = ci.insertExGap(2); ci.writeByte(WIDE, pos - 2); ci.writeByte(opcode, pos - 1); ci.write16bit(var, pos); } } private static void shiftIndex0(CodeIterator ci, int index, int opcode, int lessThan, int delta, int opcode_i_0, int opcode_i) throws BadBytecode { int var = (opcode - opcode_i_0) % 4; if (var < lessThan) return; var += delta; if (var < 4) ci.writeByte(opcode + delta, index); else { opcode = (opcode - opcode_i_0) / 4 + opcode_i; if (var < 0x100) { int pos = ci.insertExGap(1); ci.writeByte(opcode, pos - 1); ci.writeByte(var, pos); } else { int pos = ci.insertExGap(3); ci.writeByte(WIDE, pos - 1); ci.writeByte(opcode, pos); ci.write16bit(var, pos + 1); } } } }